├── .clang-format ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── bin └── antlr-4.9.1-complete.jar ├── compile-grammar.sh ├── compile.sh ├── docker.sh ├── run.sh └── src ├── CLIManager.hpp ├── JIT.hpp ├── ObjectEmitter.hpp ├── exceptions ├── NotImplementedException.hpp └── SyntaxErrorException.hpp ├── grammar ├── .gitignore ├── FooLexer.g4 ├── FooParser.g4 ├── Visitor.hpp └── visitors │ └── instructions.cpp ├── logic ├── Helper.cpp ├── Helper.hpp ├── Scope.cpp └── Scope.hpp └── main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | UseTab: Never 4 | BreakBeforeBraces: Allman 5 | AllowShortIfStatementsOnASingleLine: Never 6 | IndentCaseLabels: false 7 | ColumnLimit: 0 8 | DerivePointerAlignment: false 9 | PointerAlignment: Right 10 | AccessModifierOffset: -4 11 | AlignConsecutiveMacros: true 12 | IndentPPDirectives: BeforeHash 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # MacOS generated 35 | **/.DS_Store 36 | 37 | # CMake 38 | CMakeLists.txt.user 39 | CMakeCache.txt 40 | CMakeFiles 41 | CMakeScripts 42 | Testing 43 | Makefile 44 | cmake_install.cmake 45 | install_manifest.txt 46 | compile_commands.json 47 | CTestTestfile.cmake 48 | _deps 49 | build/ 50 | build-*/ 51 | test/ 52 | dist/ 53 | 54 | # IDEs config 55 | .vscode/ 56 | .idea/ 57 | 58 | # Grammar Runtime 59 | src/grammar/runtime 60 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | os: linux 3 | dist: bionic 4 | sudo: required 5 | 6 | addons: 7 | apt: 8 | update: true 9 | 10 | before_install: 11 | - sudo apt-get install -y --no-install-recommends libedit-dev 12 | - wget https://apt.llvm.org/llvm.sh 13 | - chmod +x llvm.sh 14 | - sudo ./llvm.sh 12 15 | script: 16 | - ./compile.sh 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7 FATAL_ERROR) 2 | project(compiler) 3 | 4 | find_package(LLVM 12 REQUIRED CONFIG) 5 | 6 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 7 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 8 | 9 | set(CMAKE_CXX_STANDARD 14) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | include_directories(${LLVM_INCLUDE_DIRS}) 13 | add_definitions(${LLVM_DEFINITIONS}) 14 | 15 | include(ExternalProject) 16 | 17 | set(ANTLR4_ROOT ${CMAKE_CURRENT_BINARY_DIR}/antlr4_runtime/src/antlr4_runtime) 18 | set(ANTLR4_INCLUDE_DIRS ${ANTLR4_ROOT}/runtime/Cpp/runtime/src) 19 | set(ANTLR4_GIT_REPOSITORY https://github.com/antlr/antlr4.git) 20 | 21 | if(NOT DEFINED ANTLR4_TAG) 22 | # Set to branch name to keep library updated at the cost of needing to rebuild after 'clean' 23 | # Set to commit hash to keep the build stable and does not need to rebuild after 'clean' 24 | set(ANTLR4_TAG master) 25 | endif() 26 | 27 | if(${CMAKE_GENERATOR} MATCHES "Visual Studio.*") 28 | set(ANTLR4_OUTPUT_DIR ${ANTLR4_ROOT}/runtime/Cpp/dist/$(Configuration)) 29 | elseif(${CMAKE_GENERATOR} MATCHES "Xcode.*") 30 | set(ANTLR4_OUTPUT_DIR ${ANTLR4_ROOT}/runtime/Cpp/dist/$(CONFIGURATION)) 31 | else() 32 | set(ANTLR4_OUTPUT_DIR ${ANTLR4_ROOT}/runtime/Cpp/dist) 33 | endif() 34 | 35 | if(MSVC) 36 | set(ANTLR4_STATIC_LIBRARIES 37 | ${ANTLR4_OUTPUT_DIR}/antlr4-runtime-static.lib) 38 | set(ANTLR4_SHARED_LIBRARIES 39 | ${ANTLR4_OUTPUT_DIR}/antlr4-runtime.lib) 40 | set(ANTLR4_RUNTIME_LIBRARIES 41 | ${ANTLR4_OUTPUT_DIR}/antlr4-runtime.dll) 42 | else() 43 | set(ANTLR4_STATIC_LIBRARIES 44 | ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.a) 45 | if(MINGW) 46 | set(ANTLR4_SHARED_LIBRARIES 47 | ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.dll.a) 48 | set(ANTLR4_RUNTIME_LIBRARIES 49 | ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.dll) 50 | elseif(CYGWIN) 51 | set(ANTLR4_SHARED_LIBRARIES 52 | ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.dll.a) 53 | set(ANTLR4_RUNTIME_LIBRARIES 54 | ${ANTLR4_OUTPUT_DIR}/cygantlr4-runtime-4.9.2.dll) 55 | elseif(APPLE) 56 | set(ANTLR4_RUNTIME_LIBRARIES 57 | ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.dylib) 58 | else() 59 | set(ANTLR4_RUNTIME_LIBRARIES 60 | ${ANTLR4_OUTPUT_DIR}/libantlr4-runtime.so) 61 | endif() 62 | endif() 63 | 64 | 65 | if(${CMAKE_GENERATOR} MATCHES ".* Makefiles") 66 | # This avoids 67 | # 'warning: jobserver unavailable: using -j1. Add '+' to parent make rule.' 68 | set(ANTLR4_BUILD_COMMAND $(MAKE)) 69 | elseif(${CMAKE_GENERATOR} MATCHES "Visual Studio.*") 70 | set(ANTLR4_BUILD_COMMAND 71 | ${CMAKE_COMMAND} 72 | --build . 73 | --config $(Configuration) 74 | --target) 75 | elseif(${CMAKE_GENERATOR} MATCHES "Xcode.*") 76 | set(ANTLR4_BUILD_COMMAND 77 | ${CMAKE_COMMAND} 78 | --build . 79 | --config $(CONFIGURATION) 80 | --target) 81 | else() 82 | set(ANTLR4_BUILD_COMMAND 83 | ${CMAKE_COMMAND} 84 | --build . 85 | --target) 86 | endif() 87 | 88 | if(NOT DEFINED ANTLR4_WITH_STATIC_CRT) 89 | set(ANTLR4_WITH_STATIC_CRT ON) 90 | endif() 91 | 92 | if(ANTLR4_ZIP_REPOSITORY) 93 | ExternalProject_Add( 94 | antlr4_runtime 95 | PREFIX antlr4_runtime 96 | URL ${ANTLR4_ZIP_REPOSITORY} 97 | DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR} 98 | BUILD_COMMAND "" 99 | BUILD_IN_SOURCE 1 100 | SOURCE_DIR ${ANTLR4_ROOT} 101 | SOURCE_SUBDIR runtime/Cpp 102 | CMAKE_CACHE_ARGS 103 | -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} 104 | -DWITH_STATIC_CRT:BOOL=${ANTLR4_WITH_STATIC_CRT} 105 | # -DCMAKE_CXX_STANDARD:STRING=17 # if desired, compile the runtime with a different C++ standard 106 | # -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} # alternatively, compile the runtime with the same C++ standard as the outer project 107 | INSTALL_COMMAND "" 108 | EXCLUDE_FROM_ALL 1) 109 | else() 110 | ExternalProject_Add( 111 | antlr4_runtime 112 | PREFIX antlr4_runtime 113 | GIT_REPOSITORY ${ANTLR4_GIT_REPOSITORY} 114 | GIT_TAG ${ANTLR4_TAG} 115 | DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR} 116 | BUILD_COMMAND "" 117 | BUILD_IN_SOURCE 1 118 | SOURCE_DIR ${ANTLR4_ROOT} 119 | SOURCE_SUBDIR runtime/Cpp 120 | CMAKE_CACHE_ARGS 121 | -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} 122 | -DWITH_STATIC_CRT:BOOL=${ANTLR4_WITH_STATIC_CRT} 123 | # -DCMAKE_CXX_STANDARD:STRING=17 # if desired, compile the runtime with a different C++ standard 124 | # -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} # alternatively, compile the runtime with the same C++ standard as the outer project 125 | INSTALL_COMMAND "" 126 | EXCLUDE_FROM_ALL 1) 127 | endif() 128 | 129 | # Separate build step as rarely people want both 130 | set(ANTLR4_BUILD_DIR ${ANTLR4_ROOT}) 131 | if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0") 132 | # CMake 3.14 builds in above's SOURCE_SUBDIR when BUILD_IN_SOURCE is true 133 | set(ANTLR4_BUILD_DIR ${ANTLR4_ROOT}/runtime/Cpp) 134 | endif() 135 | 136 | ExternalProject_Add_Step( 137 | antlr4_runtime 138 | build_static 139 | COMMAND ${ANTLR4_BUILD_COMMAND} antlr4_static 140 | # Depend on target instead of step (a custom command) 141 | # to avoid running dependent steps concurrently 142 | DEPENDS antlr4_runtime 143 | BYPRODUCTS ${ANTLR4_STATIC_LIBRARIES} 144 | EXCLUDE_FROM_MAIN 1 145 | WORKING_DIRECTORY ${ANTLR4_BUILD_DIR}) 146 | ExternalProject_Add_StepTargets(antlr4_runtime build_static) 147 | 148 | add_library(antlr4_static STATIC IMPORTED) 149 | add_dependencies(antlr4_static antlr4_runtime-build_static) 150 | set_target_properties(antlr4_static PROPERTIES 151 | IMPORTED_LOCATION ${ANTLR4_STATIC_LIBRARIES}) 152 | 153 | ExternalProject_Add_Step( 154 | antlr4_runtime 155 | build_shared 156 | COMMAND ${ANTLR4_BUILD_COMMAND} antlr4_shared 157 | # Depend on target instead of step (a custom command) 158 | # to avoid running dependent steps concurrently 159 | DEPENDS antlr4_runtime 160 | BYPRODUCTS ${ANTLR4_SHARED_LIBRARIES} ${ANTLR4_RUNTIME_LIBRARIES} 161 | EXCLUDE_FROM_MAIN 1 162 | WORKING_DIRECTORY ${ANTLR4_BUILD_DIR}) 163 | ExternalProject_Add_StepTargets(antlr4_runtime build_shared) 164 | 165 | add_library(antlr4_shared SHARED IMPORTED) 166 | add_dependencies(antlr4_shared antlr4_runtime-build_shared) 167 | set_target_properties(antlr4_shared PROPERTIES 168 | IMPORTED_LOCATION ${ANTLR4_RUNTIME_LIBRARIES}) 169 | if(ANTLR4_SHARED_LIBRARIES) 170 | set_target_properties(antlr4_shared PROPERTIES 171 | IMPORTED_IMPLIB ${ANTLR4_SHARED_LIBRARIES}) 172 | endif() 173 | 174 | message("LLVM_INCLUDE_DIRS: ${LLVM_INCLUDE_DIRS}") 175 | file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") 176 | 177 | add_executable(${PROJECT_NAME} ${SOURCES}) 178 | 179 | target_include_directories(${PROJECT_NAME} PUBLIC include src ${ANTLR4_INCLUDE_DIRS}) 180 | 181 | set(TARGET_WebAssembly WebAssemblyCodeGen WebAssemblyAsmParser WebAssemblyDesc WebAssemblyInfo) 182 | set(TARGET_XCore XCoreCodeGen XCoreDesc XCoreInfo) 183 | set(TARGET_SystemZ SystemZCodeGen SystemZAsmParser SystemZDesc SystemZInfo) 184 | set(TARGET_Sparc SparcCodeGen SparcAsmParser SparcDesc SparcInfo) 185 | set(TARGET_RISCV RISCVCodeGen RISCVAsmParser RISCVDesc RISCVInfo) 186 | set(TARGET_PowerPC PowerPCCodeGen PowerPCAsmParser PowerPCDesc PowerPCInfo) 187 | set(TARGET_NVPTX NVPTXCodeGen NVPTXDesc NVPTXInfo) 188 | set(TARGET_MSP430 MSP430CodeGen MSP430AsmParser MSP430Desc MSP430Info) 189 | set(TARGET_Mips MipsCodeGen MipsAsmParser MipsDesc MipsInfo) 190 | set(TARGET_Lanai LanaiCodeGen LanaiAsmParser LanaiDesc LanaiInfo) 191 | set(TARGET_Hexagon HexagonCodeGen HexagonAsmParser HexagonDesc HexagonInfo) 192 | set(TARGET_BPF BPFCodeGen BPFAsmParser BPFDesc BPFInfo) 193 | set(TARGET_ARM ARMCodeGen ARMAsmParser ARMDesc ARMUtils ARMInfo) 194 | set(TARGET_AMDGPU AMDGPUCodeGen AMDGPUAsmParser AMDGPUDesc AMDGPUUtils AMDGPUInfo) 195 | set(TARGET_X86 X86CodeGen X86AsmParser X86Desc X86Info) 196 | set(TARGET_AArch64 AArch64CodeGen AArch64AsmParser AArch64Desc AArch64Utils AArch64Info) 197 | set(TARGET_AVR AVRCodeGen AVRAsmParser AVRDesc AVRInfo) 198 | 199 | set(TARGETS_TO_BUILD "WebAssembly" "XCore" "SystemZ" "Sparc" "RISCV" "PowerPC" "NVPTX" "MSP430" "Mips" "Lanai" "Hexagon" "BPF" "ARM" "AMDGPU" "X86" "AArch64" "AVR") 200 | 201 | set(LLVM_TARGETS) 202 | foreach (target IN ITEMS ${TARGETS_TO_BUILD}) 203 | list(APPEND LLVM_TARGETS "${TARGET_${target}}") 204 | endforeach(target) 205 | 206 | llvm_map_components_to_libnames(llvm_libs 207 | TextAPI 208 | OrcJIT 209 | JITLink 210 | ObjectYAML 211 | WindowsManifest 212 | Coverage 213 | TableGen 214 | LTO 215 | Passes 216 | ObjCARCOpts 217 | Coroutines 218 | LibDriver 219 | XRay 220 | ${LLVM_TARGETS} 221 | MIRParser 222 | ipo 223 | Instrumentation 224 | Vectorize 225 | Linker 226 | IRReader 227 | AsmParser 228 | Symbolize 229 | DebugInfoPDB 230 | FuzzMutate 231 | LineEditor 232 | MCA 233 | DebugInfoGSYM 234 | GlobalISel 235 | SelectionDAG 236 | AsmPrinter 237 | DebugInfoDWARF 238 | MCJIT 239 | Interpreter 240 | ExecutionEngine 241 | RuntimeDyld 242 | CodeGen 243 | Target 244 | ScalarOpts 245 | InstCombine 246 | AggressiveInstCombine 247 | TransformUtils 248 | BitWriter 249 | Analysis 250 | ProfileData 251 | DlltoolDriver 252 | Option 253 | Object 254 | MCParser 255 | MC 256 | DebugInfoCodeView 257 | DebugInfoMSF 258 | BitReader 259 | Core 260 | Remarks 261 | BinaryFormat 262 | BitstreamReader 263 | Support 264 | Demangle 265 | ) 266 | 267 | target_link_libraries(${PROJECT_NAME} antlr4_static ${llvm_libs}) 268 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:bionic 2 | 3 | RUN set -xe && \ 4 | apt-get update && \ 5 | apt-get install -y --no-install-recommends \ 6 | ca-certificates \ 7 | lsb-release \ 8 | wget \ 9 | software-properties-common \ 10 | gpg-agent \ 11 | libedit-dev \ 12 | uuid-dev \ 13 | zlib1g-dev \ 14 | openjdk-8-jre-headless \ 15 | build-essential \ 16 | pkg-config \ 17 | cmake \ 18 | git 19 | 20 | RUN set -xe && \ 21 | wget https://apt.llvm.org/llvm.sh && \ 22 | chmod +x llvm.sh && \ 23 | ./llvm.sh 12 24 | 25 | RUN set -xe && \ 26 | git config --global user.email "johndoe@example.com" && \ 27 | git config --global user.name "John Doe" 28 | 29 | WORKDIR /usr/src/app 30 | 31 | COPY . . 32 | 33 | CMD bash 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nathanael Demacon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLVM + ANTLR4 Starter Project 2 | Starter project for ANTLR4 and LLVM C++ project. 3 | 4 | ## Prerequisite 5 | - LLVM **12** 6 | - Java (for ANTLR4) 7 | - git 8 | 9 | ### Install prerequisite libraries 10 | #### Debian / Ubuntu 11 | ```bash 12 | sudo apt-get update && \ 13 | sudo apt-get install -y --no-install-recommends \ 14 | ca-certificates \ 15 | lsb-release \ 16 | wget \ 17 | software-properties-common \ 18 | gpg-agent \ 19 | libedit-dev \ 20 | uuid-dev \ 21 | zlib1g-dev \ 22 | openjdk-8-jre-headless \ 23 | build-essential \ 24 | pkg-config \ 25 | cmake \ 26 | git && \ 27 | wget https://apt.llvm.org/llvm.sh && \ 28 | chmod +x llvm.sh && \ 29 | sudo ./llvm.sh 12 30 | ``` 31 | 32 | ## Functionalities 33 | - Object Emitter 34 | - JIT 35 | - CLI 36 | 37 | ## CLI 38 | The code of this repository comes with a pre-configured CLI for fast iteration. 39 | 40 | ``` 41 | Usage: compiler [OPTIONS] 42 | 43 | Options: 44 | -h, --help Get usage and available options 45 | -c, --compile Outputs to an object file. By default the program is using the JIT 46 | -o Filename used along with --compile option. Default is output.o 47 | --print-llvm Print generated LLVM bytecode 48 | ``` 49 | 50 | ## Development 51 | To start using the project, first clone it: 52 | ```bash 53 | git clone git@github.com:quantumsheep/llvm-antlr4-starter.git 54 | ``` 55 | For non-SSH users: 56 | ```bash 57 | git clone https://github.com/quantumsheep/llvm-antlr4-starter.git 58 | ``` 59 | 60 | Grammar and visitors and located in `src/grammar`. 61 | 62 | Some scripts are available to ease development: 63 | - `compile.sh`: Compile the sources using CMake 64 | - `compile-grammar.sh`: Generate structures from ANTL4 grammar files 65 | - `docker.sh`: Launch a coding environment inside Docker with everything pre-installed 66 | - `run.sh`: Run the program (located at `build/compiler` once compiled) 67 | -------------------------------------------------------------------------------- /bin/antlr-4.9.1-complete.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quantumsheep/llvm-antlr4-starter/3d6ffe2405423e0f4b8388cd4039e47f9e0a0715/bin/antlr-4.9.1-complete.jar -------------------------------------------------------------------------------- /compile-grammar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | current_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" 4 | 5 | grammar_directory="$current_directory/src/grammar" 6 | 7 | java -jar "$current_directory/bin/antlr-4.9.1-complete.jar" -Dlanguage=Cpp -no-listener -visitor -o "$grammar_directory/runtime" "$grammar_directory/FooLexer.g4" "$grammar_directory/FooParser.g4" 8 | 9 | for file in $grammar_directory/runtime/*; do 10 | if [[ $file == *.h ]]; then 11 | sed -i.bak '1,3d' "$file" 12 | elif [[ $file == *.cpp ]]; then 13 | sed -i.bak '1,4d' "$file" 14 | fi 15 | done 16 | 17 | rm $grammar_directory/runtime/*.bak 18 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./compile-grammar.sh 4 | 5 | mkdir -p build 6 | cd build 7 | cmake .. 8 | cmake --build . 9 | -------------------------------------------------------------------------------- /docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | docker build . -t llvm-antlr4-starter-dev 6 | 7 | docker run --rm -it \ 8 | --mount type=bind,source="$(pwd)"/,target=/usr/src/app/ \ 9 | llvm-antlr4-starter-dev 10 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./build/compiler "$@" 4 | -------------------------------------------------------------------------------- /src/CLIManager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace FooLang 8 | { 9 | class CLIManager 10 | { 11 | public: 12 | std::string program; 13 | std::vector args; 14 | 15 | CLIManager(int argc, char **argv) : program(argv[0]) 16 | { 17 | for (int i = 1; i < argc; i++) 18 | { 19 | args.push_back(argv[i]); 20 | } 21 | } 22 | 23 | std::vector::const_iterator find(const std::string &arg) const 24 | { 25 | return std::find(args.begin(), args.end(), arg); 26 | } 27 | 28 | bool hasOption(const std::string &option) const 29 | { 30 | return this->find(option) != args.end(); 31 | } 32 | 33 | std::string getOptionValue(const std::string &option, const std::string &defaultValue = "") const 34 | { 35 | auto it = this->find(option); 36 | 37 | if (it == args.end()) 38 | return defaultValue; 39 | 40 | it++; 41 | 42 | if (it == args.end()) 43 | return defaultValue; 44 | 45 | return *it; 46 | } 47 | }; 48 | } // namespace FooLang 49 | -------------------------------------------------------------------------------- /src/JIT.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace FooLang 11 | { 12 | class JIT 13 | { 14 | private: 15 | std::unique_ptr lljit; 16 | 17 | public: 18 | JIT(std::unique_ptr _lljit) : lljit(std::move(_lljit)) {} 19 | 20 | template ::value && std::is_function>::value>> 21 | llvm::Expected lookup(const std::string &name) 22 | { 23 | auto symbol = this->lljit->lookup(name); 24 | 25 | if (!symbol) 26 | { 27 | return symbol.takeError(); 28 | } 29 | 30 | return (T)symbol.get().getAddress(); 31 | } 32 | 33 | template ::value>> 34 | inline llvm::Expected lookup(const std::string &name) 35 | { 36 | return this->lookup(name); 37 | } 38 | 39 | static llvm::Expected create(std::unique_ptr &module, std::unique_ptr &context) 40 | { 41 | auto lljit_ptr = llvm::orc::LLJITBuilder().create(); 42 | auto &lljit = lljit_ptr.get(); 43 | 44 | auto &jd = lljit->getMainJITDylib(); 45 | 46 | llvm::orc::MangleAndInterner Mangle(lljit->getExecutionSession(), lljit->getDataLayout()); 47 | 48 | jd.define( 49 | llvm::orc::absoluteSymbols({{Mangle("printf"), llvm::JITEvaluatedSymbol::fromPointer(&printf)}})); 50 | 51 | jd.addGenerator(llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess('_'))); 52 | 53 | if (!lljit) 54 | { 55 | return lljit_ptr.takeError(); 56 | } 57 | 58 | if (auto err = lljit->addIRModule(llvm::orc::ThreadSafeModule(std::move(module), std::move(context)))) 59 | { 60 | return std::move(err); 61 | } 62 | 63 | return JIT(std::move(lljit)); 64 | } 65 | }; 66 | } // namespace FooLang 67 | -------------------------------------------------------------------------------- /src/ObjectEmitter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace FooLang 13 | { 14 | class ObjectEmitter 15 | { 16 | public: 17 | static void emit(const std::unique_ptr &module, const std::string &filename, std::string &error) 18 | { 19 | auto targetTriple = llvm::sys::getDefaultTargetTriple(); 20 | 21 | auto target = llvm::TargetRegistry::lookupTarget(targetTriple, error); 22 | 23 | if (!target) 24 | { 25 | return; 26 | } 27 | 28 | auto cpu = "generic"; 29 | auto features = ""; 30 | 31 | llvm::TargetOptions opt; 32 | auto RM = llvm::Optional(); 33 | auto targetMachine = target->createTargetMachine(targetTriple, cpu, features, opt, RM); 34 | 35 | module->setDataLayout(targetMachine->createDataLayout()); 36 | module->setTargetTriple(targetTriple); 37 | 38 | std::error_code ec; 39 | llvm::raw_fd_ostream dest(filename, ec, llvm::sys::fs::OF_None); 40 | 41 | if (ec) 42 | { 43 | error = "Could not open file: " + ec.message(); 44 | return; 45 | } 46 | 47 | llvm::legacy::PassManager pass; 48 | auto FileType = llvm::CGFT_ObjectFile; 49 | 50 | if (targetMachine->addPassesToEmitFile(pass, dest, nullptr, FileType)) 51 | { 52 | error = "TargetMachine can't emit a file of this type"; 53 | return; 54 | } 55 | 56 | pass.run(*module); 57 | dest.flush(); 58 | } 59 | }; 60 | } // namespace FooLang 61 | -------------------------------------------------------------------------------- /src/exceptions/NotImplementedException.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace FooLang 4 | { 5 | class NotImplementedException : public std::logic_error 6 | { 7 | public: 8 | NotImplementedException() : std::logic_error("Functionnality not yet implemented.") {} 9 | }; 10 | } // namespace FooLang 11 | -------------------------------------------------------------------------------- /src/exceptions/SyntaxErrorException.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace FooLang 4 | { 5 | class SyntaxErrorException : public std::logic_error 6 | { 7 | public: 8 | SyntaxErrorException() : std::logic_error("Syntax error.") {} 9 | }; 10 | } // namespace FooLang 11 | -------------------------------------------------------------------------------- /src/grammar/.gitignore: -------------------------------------------------------------------------------- 1 | .antlr 2 | *.class 3 | *.java 4 | *.interp 5 | *.tokens 6 | *.log 7 | -------------------------------------------------------------------------------- /src/grammar/FooLexer.g4: -------------------------------------------------------------------------------- 1 | lexer grammar FooLexer; 2 | 3 | ToImplement: 'to implement'; 4 | 5 | WhiteSpace: [ \t]+ -> skip; 6 | LineTerminator: [\r\n] -> channel(HIDDEN); 7 | -------------------------------------------------------------------------------- /src/grammar/FooParser.g4: -------------------------------------------------------------------------------- 1 | parser grammar FooParser; 2 | 3 | options { 4 | tokenVocab = FooLexer; 5 | } 6 | 7 | instructions: instruction*; 8 | 9 | instruction: ToImplement; 10 | -------------------------------------------------------------------------------- /src/grammar/Visitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../logic/Scope.hpp" 4 | #include "exceptions/SyntaxErrorException.hpp" 5 | #include "runtime/FooLexer.h" 6 | #include "runtime/FooParserBaseVisitor.h" 7 | 8 | #include "../logic/Helper.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace antlr4; 17 | 18 | namespace FooLang 19 | { 20 | class Visitor 21 | { 22 | public: 23 | std::unique_ptr llvm_context; 24 | std::unique_ptr llvm_module; 25 | llvm::IRBuilder<> builder; 26 | std::string path; 27 | 28 | std::map variables; 29 | 30 | Visitor(const std::string &path) : llvm_context(std::make_unique()), 31 | llvm_module(std::make_unique(path, *this->llvm_context)), 32 | builder(*this->llvm_context), 33 | path(path) {} 34 | 35 | void start() 36 | { 37 | std::ifstream stream; 38 | stream.open(path); 39 | 40 | ANTLRInputStream input(stream); 41 | FooLexer lexer(&input); 42 | CommonTokenStream tokens(&lexer); 43 | FooParser parser(&tokens); 44 | 45 | if (lexer.getNumberOfSyntaxErrors() || parser.getNumberOfSyntaxErrors()) 46 | { 47 | throw SyntaxErrorException(); 48 | } 49 | 50 | this->visitInstructions(parser.instructions()); 51 | } 52 | 53 | void visitInstructions(FooParser::InstructionsContext *context); 54 | 55 | void visitInstruction(FooParser::InstructionContext *context); 56 | }; 57 | } // namespace FooLang 58 | -------------------------------------------------------------------------------- /src/grammar/visitors/instructions.cpp: -------------------------------------------------------------------------------- 1 | #include "../Visitor.hpp" 2 | 3 | #include "exceptions/NotImplementedException.hpp" 4 | 5 | using namespace FooLang; 6 | 7 | void Visitor::visitInstructions(FooParser::InstructionsContext *context) 8 | { 9 | for (auto &instructionContext : context->instruction()) 10 | { 11 | this->visitInstruction(instructionContext); 12 | } 13 | } 14 | 15 | void Visitor::visitInstruction(FooParser::InstructionContext *context) 16 | { 17 | throw NotImplementedException(); 18 | } 19 | -------------------------------------------------------------------------------- /src/logic/Helper.cpp: -------------------------------------------------------------------------------- 1 | #include "Helper.hpp" 2 | 3 | llvm::Value *FooLang::Helpers::printf( 4 | const std::unique_ptr &llvm_module, 5 | llvm::IRBuilder<> &builder, 6 | const std::string &format, 7 | const std::vector &values) 8 | { 9 | auto &llvm_context = llvm_module->getContext(); 10 | 11 | auto printf_type = llvm::FunctionType::get(llvm::Type::getVoidTy(llvm_context), {llvm::Type::getInt8PtrTy(llvm_context)}, true); 12 | auto function = llvm_module->getOrInsertFunction("printf", printf_type, llvm::AttributeList().addAttribute(llvm_context, 1U, llvm::Attribute::NoAlias)); 13 | 14 | auto constant = llvm::ConstantDataArray::getString(llvm_context, format, true); 15 | auto global = new llvm::GlobalVariable(*llvm_module, llvm::ArrayType::get(llvm::Type::getInt8Ty(llvm_context), format.size() + 1), true, llvm::GlobalValue::PrivateLinkage, constant, ".str"); 16 | global->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); 17 | global->setAlignment(llvm::Align(1)); 18 | 19 | auto zero = llvm::ConstantInt::get(llvm::Type::getInt64Ty(llvm_context), 0); 20 | 21 | std::vector args = { 22 | builder.CreateGEP(global, {zero, zero}), 23 | }; 24 | 25 | args.insert(args.end(), values.begin(), values.end()); 26 | 27 | return builder.CreateCall(function, args); 28 | } 29 | -------------------------------------------------------------------------------- /src/logic/Helper.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace FooLang 4 | { 5 | namespace Helpers 6 | { 7 | 8 | llvm::Value *printf( 9 | const std::unique_ptr &llvm_module, 10 | llvm::IRBuilder<> &builder, 11 | const std::string &format, 12 | const std::vector &values = {}); 13 | 14 | } // namespace Helpers 15 | } // namespace FooLang 16 | -------------------------------------------------------------------------------- /src/logic/Scope.cpp: -------------------------------------------------------------------------------- 1 | #include "Scope.hpp" 2 | 3 | using namespace FooLang; 4 | 5 | llvm::Value *Scope::setVariable(const std::string &name, llvm::Value *value) 6 | { 7 | this->variables[name] = value; 8 | return value; 9 | } 10 | 11 | llvm::Value *Scope::getVariable(const std::string &name) 12 | { 13 | try 14 | { 15 | return this->variables.at(name); 16 | } 17 | catch (std::out_of_range) 18 | { 19 | return nullptr; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/logic/Scope.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace FooLang 6 | { 7 | class Scope 8 | { 9 | public: 10 | std::unordered_map variables; 11 | 12 | llvm::Function *currentFunction = nullptr; 13 | 14 | Scope(llvm::Function *_currentFunction = nullptr) : currentFunction(_currentFunction) {} 15 | 16 | llvm::Value *setVariable(const std::string &name, llvm::Value *); 17 | 18 | llvm::Value *getVariable(const std::string &name); 19 | }; 20 | } // namespace FooLang 21 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "CLIManager.hpp" 2 | #include "JIT.hpp" 3 | #include "ObjectEmitter.hpp" 4 | #include "grammar/Visitor.hpp" 5 | 6 | #include 7 | 8 | int run(const FooLang::CLIManager &cli, FooLang::Visitor &visitor) 9 | { 10 | auto jit = FooLang::JIT::create(visitor.llvm_module, visitor.llvm_context); 11 | 12 | auto entry = jit->lookup("main"); 13 | if (!entry) 14 | { 15 | llvm::errs() << entry.takeError(); 16 | return 1; 17 | } 18 | 19 | return entry.get()(); 20 | } 21 | 22 | int compile(const FooLang::CLIManager &cli, FooLang::Visitor &visitor) 23 | { 24 | std::string error; 25 | FooLang::ObjectEmitter::emit(visitor.llvm_module, cli.getOptionValue("-o", "output.o"), error); 26 | 27 | if (!error.empty()) 28 | { 29 | llvm::errs() << error; 30 | return 1; 31 | } 32 | 33 | return 0; 34 | } 35 | 36 | int main(int argc, char **argv) 37 | { 38 | FooLang::CLIManager cli(argc, argv); 39 | 40 | if (argc < 2 || cli.hasOption("--help") || cli.hasOption("-h")) 41 | { 42 | std::cerr << "Usage: compiler [--help,-h] [--print-llvm] [--compile,-c [-o ]]" << std::endl; 43 | return 1; 44 | } 45 | 46 | llvm::InitializeAllTargetInfos(); 47 | llvm::InitializeAllTargets(); 48 | llvm::InitializeAllTargetMCs(); 49 | llvm::InitializeAllAsmParsers(); 50 | llvm::InitializeAllAsmPrinters(); 51 | 52 | FooLang::Visitor visitor(argv[1]); 53 | visitor.start(); 54 | 55 | if (cli.hasOption("--print-llvm")) 56 | { 57 | visitor.llvm_module->print(llvm::outs(), nullptr); 58 | std::cout << std::endl; 59 | } 60 | 61 | if (cli.hasOption("--compile") || cli.hasOption("-c")) 62 | { 63 | compile(cli, visitor); 64 | } 65 | else 66 | { 67 | return run(cli, visitor); 68 | } 69 | 70 | return 0; 71 | } 72 | --------------------------------------------------------------------------------