From 48c4e88b6474005071ffa668906b3c3bef541945 Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Fri, 20 Nov 2020 19:32:29 +0800 Subject: [PATCH 01/17] feat: add optimization --- .github/workflows/build.yml | 10 +-- builtin/CMakeLists.txt | 4 +- builtin/ss_io.c | 12 ++-- builtin/ss_io.h | 12 ++-- builtin/ss_string.c | 32 +++++----- builtin/ss_string.h | 32 +++++----- cmake/AddLLVM.cmake | 9 +-- examples/all.ss | 2 +- include/AST/ASTBuilder.h | 2 +- include/AST/Node.h | 2 +- include/CodeGen/Builtin.h | 2 +- include/CodeGen/IRGenerator.h | 4 +- include/CodeGen/Pass.h | 2 +- include/Entity/Scope.h | 4 +- include/Exception/CodeGenException.h | 11 ---- include/Exception/CompilerException.h | 20 ------ include/Exception/DriverException.h | 11 ---- include/Exception/SemanticException.h | 11 ---- include/Optimization/Optimizer.h | 32 ++++++++++ include/Sema/ReferenceResolver.h | 2 +- include/Sema/ScopeScanner.h | 2 +- include/Sema/SemanticValidator.h | 2 +- include/Sema/TypeChecker.h | 2 +- include/{Util => Support}/Alias.h | 8 +++ include/Support/Exception.h | 44 ++++++++++++++ include/{Util => Support}/Find.h | 2 +- include/Support/LLVM.h | 87 +++++++++++++++++++++++++++ include/{Util => Support}/Output.h | 2 +- lib/CMakeLists.txt | 2 + lib/CodeGen/Builtin.cpp | 2 +- lib/CodeGen/CMakeLists.txt | 2 +- lib/Driver/CMakeLists.txt | 2 +- lib/Driver/Driver.cpp | 69 +++++++++++++++------ lib/Optimization/CMakeLists.txt | 5 ++ lib/Optimization/Optimizer.cpp | 74 +++++++++++++++++++++++ 35 files changed, 374 insertions(+), 147 deletions(-) delete mode 100644 include/Exception/CodeGenException.h delete mode 100644 include/Exception/CompilerException.h delete mode 100644 include/Exception/DriverException.h delete mode 100644 include/Exception/SemanticException.h create mode 100644 include/Optimization/Optimizer.h rename include/{Util => Support}/Alias.h (92%) create mode 100644 include/Support/Exception.h rename include/{Util => Support}/Find.h (89%) create mode 100644 include/Support/LLVM.h rename include/{Util => Support}/Output.h (97%) create mode 100644 lib/Optimization/CMakeLists.txt create mode 100644 lib/Optimization/Optimizer.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4a0f775..b9f0d65 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,9 @@ jobs: path: ${{ github.workspace }}/thirdparty key: ${{ matrix.os }}-${{ matrix.compiler.cc }}-antlr - name: Install Prerequirements - run: sudo apt-get -y install uuid-dev pkg-config doxygen graphviz llvm-10 + run: | + sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" + sudo apt-get -y install uuid-dev pkg-config doxygen graphviz - name: Install Antlr4 and Antlr4 Runtime if: steps.cache-antlr.outputs.cache-hit != 'true' env: @@ -66,7 +68,7 @@ jobs: cd cmake-build-debug sudo cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $thread_count - for file in ../examples/*ss + for file in ../examples/*.ss do sudo ./lib/staticscript $file --emit-llvm -o ss-ir.ll sudo ./lib/staticscript $file -o ss-obj.o @@ -104,7 +106,7 @@ jobs: cd cmake-build-debug sudo cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $thread_count - for file in ../examples/*ss + for file in ../examples/*.ss do sudo ./lib/staticscript $file --emit-llvm -o ss-ir.ll sudo ./lib/staticscript $file -o ss-obj.o @@ -112,7 +114,7 @@ jobs: cd ../cmake-build-release sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $thread_count - for file in ../examples/*ss + for file in ../examples/*.ss do sudo ./lib/staticscript $file --emit-llvm -o ss-ir.ll sudo ./lib/staticscript $file -o ss-obj.o diff --git a/builtin/CMakeLists.txt b/builtin/CMakeLists.txt index 10011d7..1ae269b 100644 --- a/builtin/CMakeLists.txt +++ b/builtin/CMakeLists.txt @@ -3,8 +3,8 @@ set(BUILTIN_BITCODES ss_string.bc ss_io.bc) add_custom_command( OUTPUT ${BUILTIN_BITCODES} - COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_string.c -std=c99 -Os -o ss_string.bc - COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_io.c -std=c99 -Os -o ss_io.bc + COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_string.c -std=c99 -O2 -o ss_string.bc + COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_io.c -std=c99 -O2 -o ss_io.bc DEPENDS ${BUILTIN_SOURCES} ) diff --git a/builtin/ss_io.c b/builtin/ss_io.c index 1308659..abf04ef 100644 --- a/builtin/ss_io.c +++ b/builtin/ss_io.c @@ -1,6 +1,6 @@ #include "ss_io.h" -ss_string *KEEPALIVE ss_integer2string(long number) { +ss_string *ss_integer2string(long number) { size_t capacity = ss_string_get_capacity_with_size(20); ss_string *str = ss_string_create_with_capacity(capacity); snprintf(str->buffer, 20, "%ld", number); @@ -8,22 +8,22 @@ ss_string *KEEPALIVE ss_integer2string(long number) { return str; } -long KEEPALIVE ss_string2integer(ss_string *str) { +long ss_string2integer(ss_string *str) { return strtol(str->buffer, NULL, 10); } -void KEEPALIVE ss_print_integer(long number) { +void ss_print_integer(long number) { printf("%ld", number); } -void KEEPALIVE ss_println_integer(long number) { +void ss_println_integer(long number) { printf("%ld\n", number); } -void KEEPALIVE ss_print_string(ss_string *str) { +void ss_print_string(ss_string *str) { printf("%s", str->buffer); } -void KEEPALIVE ss_println_string(ss_string *str) { +void ss_println_string(ss_string *str) { printf("%s\n", str->buffer); } \ No newline at end of file diff --git a/builtin/ss_io.h b/builtin/ss_io.h index 4f09a92..851f62e 100644 --- a/builtin/ss_io.h +++ b/builtin/ss_io.h @@ -5,14 +5,14 @@ #include "ss_base.h" #include "ss_string.h" -ss_string *KEEPALIVE ss_integer2string(long number); +ss_string *ss_integer2string(long number); -long KEEPALIVE ss_string2integer(ss_string *str); +long ss_string2integer(ss_string *str); -void KEEPALIVE ss_print_integer(long number); +void ss_print_integer(long number); -void KEEPALIVE ss_println_integer(long number); +void ss_println_integer(long number); -void KEEPALIVE ss_print_string(ss_string *str); +void ss_print_string(ss_string *str); -void KEEPALIVE ss_println_string(ss_string *str); \ No newline at end of file +void ss_println_string(ss_string *str); \ No newline at end of file diff --git a/builtin/ss_string.c b/builtin/ss_string.c index d50b623..12f9bda 100644 --- a/builtin/ss_string.c +++ b/builtin/ss_string.c @@ -1,6 +1,6 @@ #include "ss_string.h" -size_t KEEPALIVE ss_string_get_capacity_with_size(size_t size) { +size_t ss_string_get_capacity_with_size(size_t size) { if (size == 0) { return 16; } else if (size < 16) { @@ -12,7 +12,7 @@ size_t KEEPALIVE ss_string_get_capacity_with_size(size_t size) { } } -ss_string *KEEPALIVE ss_string_create_with_capacity(size_t capacity) { +ss_string *ss_string_create_with_capacity(size_t capacity) { ss_string *str = (ss_string *) malloc(sizeof(ss_string)); if (!str) { return NULL; @@ -24,7 +24,7 @@ ss_string *KEEPALIVE ss_string_create_with_capacity(size_t capacity) { return str; } -ss_string *KEEPALIVE ss_string_create(const char *literal) { +ss_string *ss_string_create(const char *literal) { size_t size = strlen(literal); size_t capacity = ss_string_get_capacity_with_size(size); ss_string *str = ss_string_create_with_capacity(capacity); @@ -36,16 +36,16 @@ ss_string *KEEPALIVE ss_string_create(const char *literal) { return str; } -void KEEPALIVE ss_string_delete(ss_string *str) { +void ss_string_delete(ss_string *str) { free(str->buffer); free(str); } -size_t KEEPALIVE ss_string_get_size(ss_string *str) { +size_t ss_string_get_size(ss_string *str) { return str->size; } -long KEEPALIVE ss_string_grow_with_capacity(ss_string *str, size_t new_capacity) { +long ss_string_grow_with_capacity(ss_string *str, size_t new_capacity) { char *new_buffer = (char *) calloc(new_capacity, 1); if (!new_buffer) { return -1; @@ -58,7 +58,7 @@ long KEEPALIVE ss_string_grow_with_capacity(ss_string *str, size_t new_capacity) return 0; } -long KEEPALIVE ss_string_grow(ss_string *str) { +long ss_string_grow(ss_string *str) { if (str->capacity < 32) { str->capacity += 16; } else { @@ -67,7 +67,7 @@ long KEEPALIVE ss_string_grow(ss_string *str) { return ss_string_grow_with_capacity(str, str->capacity); } -long KEEPALIVE ss_string_append(ss_string *dest, ss_string *src) { +long ss_string_append(ss_string *dest, ss_string *src) { size_t src_size = strlen(src->buffer); size_t needed_size = dest->size + src_size; size_t needed_capacity = ss_string_get_capacity_with_size(needed_size); @@ -81,7 +81,7 @@ long KEEPALIVE ss_string_append(ss_string *dest, ss_string *src) { return 0; } -long KEEPALIVE ss_string_prepend(ss_string *dest, ss_string *src) { +long ss_string_prepend(ss_string *dest, ss_string *src) { size_t src_size = strlen(src->buffer); size_t needed_size = dest->size + src_size; size_t needed_capacity = ss_string_get_capacity_with_size(needed_size); @@ -106,7 +106,7 @@ ss_string *ss_string_concat(ss_string *str1, ss_string *str2) { return str; } -ss_string *KEEPALIVE ss_string_slice(ss_string *str, ssize_t from, ssize_t to) { +ss_string *ss_string_slice(ss_string *str, ssize_t from, ssize_t to) { if (from < 0) { from = str->size + from; } @@ -124,12 +124,12 @@ ss_string *KEEPALIVE ss_string_slice(ss_string *str, ssize_t from, ssize_t to) { return new_str; } -long KEEPALIVE ss_string_equals(ss_string *str1, ss_string *str2) { +long ss_string_equals(ss_string *str1, ss_string *str2) { size_t max_size = str1->size > str2->size ? str1->size : str2->size; return strncmp(str1->buffer, str2->buffer, max_size); } -ssize_t KEEPALIVE ss_string_index_of_with_literal(ss_string *str, const char *literal) { +ssize_t ss_string_index_of_with_literal(ss_string *str, const char *literal) { char *sub = strstr(str->buffer, literal); if (!sub) { return -1; @@ -137,11 +137,11 @@ ssize_t KEEPALIVE ss_string_index_of_with_literal(ss_string *str, const char *li return sub - str->buffer; } -ssize_t KEEPALIVE ss_string_index_of(ss_string *str, ss_string *substr) { +ssize_t ss_string_index_of(ss_string *str, ss_string *substr) { return ss_string_index_of_with_literal(str, substr->buffer); } -long KEEPALIVE ss_string_trim_left(ss_string *str) { +long ss_string_trim_left(ss_string *str) { size_t i = 0; while (isspace(str->buffer[i])) { i += 1; @@ -158,7 +158,7 @@ long KEEPALIVE ss_string_trim_left(ss_string *str) { return 0; } -void KEEPALIVE ss_string_trim_right(ss_string *str) { +void ss_string_trim_right(ss_string *str) { ssize_t i = str->size - 1; while (i >= 0 && isspace(str->buffer[i])) { str->buffer[i] = 0; @@ -167,7 +167,7 @@ void KEEPALIVE ss_string_trim_right(ss_string *str) { str->size = i + 1; } -long KEEPALIVE ss_string_trim(ss_string *str) { +long ss_string_trim(ss_string *str) { ss_string_trim_right(str); return ss_string_trim_left(str); } diff --git a/builtin/ss_string.h b/builtin/ss_string.h index 644e47a..765ef4d 100644 --- a/builtin/ss_string.h +++ b/builtin/ss_string.h @@ -12,36 +12,36 @@ typedef struct { size_t capacity; } ss_string; -size_t KEEPALIVE ss_string_get_capacity_with_size(size_t size); +size_t ss_string_get_capacity_with_size(size_t size); -ss_string *KEEPALIVE ss_string_create_with_capacity(size_t capacity); +ss_string *ss_string_create_with_capacity(size_t capacity); -ss_string *KEEPALIVE ss_string_create(const char *literal); +ss_string *ss_string_create(const char *literal); -void KEEPALIVE ss_string_delete(ss_string *str); +void ss_string_delete(ss_string *str); -size_t KEEPALIVE ss_string_get_size(ss_string *str); +size_t ss_string_get_size(ss_string *str); -long KEEPALIVE ss_string_grow_with_capacity(ss_string *str, size_t new_capacity); +long ss_string_grow_with_capacity(ss_string *str, size_t new_capacity); -long KEEPALIVE ss_string_grow(ss_string *str); +long ss_string_grow(ss_string *str); -long KEEPALIVE ss_string_append(ss_string *dest, ss_string *src); +long ss_string_append(ss_string *dest, ss_string *src); -long KEEPALIVE ss_string_prepend(ss_string *dest, ss_string *src); +long ss_string_prepend(ss_string *dest, ss_string *src); ss_string *ss_string_concat(ss_string *str1, ss_string *str2); -ss_string *KEEPALIVE ss_string_slice(ss_string *str, ssize_t from, ssize_t to); +ss_string *ss_string_slice(ss_string *str, ssize_t from, ssize_t to); -long KEEPALIVE ss_string_equals(ss_string *str1, ss_string *str2); +long ss_string_equals(ss_string *str1, ss_string *str2); -ssize_t KEEPALIVE ss_string_index_of_with_literal(ss_string *str, const char *literal); +ssize_t ss_string_index_of_with_literal(ss_string *str, const char *literal); -ssize_t KEEPALIVE ss_string_index_of(ss_string *str, ss_string *substr); +ssize_t ss_string_index_of(ss_string *str, ss_string *substr); -long KEEPALIVE ss_string_trim_left(ss_string *str); +long ss_string_trim_left(ss_string *str); -void KEEPALIVE ss_string_trim_right(ss_string *str); +void ss_string_trim_right(ss_string *str); -long KEEPALIVE ss_string_trim(ss_string *str); +long ss_string_trim(ss_string *str); diff --git a/cmake/AddLLVM.cmake b/cmake/AddLLVM.cmake index af7f9fa..e1ef7ee 100644 --- a/cmake/AddLLVM.cmake +++ b/cmake/AddLLVM.cmake @@ -1,11 +1,8 @@ -if (CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) - find_package(LLVM 10 REQUIRED CONFIG) -elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) +if (CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) list(APPEND CMAKE_PREFIX_PATH /usr/local/opt/llvm/lib/cmake/llvm) - find_package(LLVM 11 REQUIRED CONFIG) endif () -find_package(LLVM REQUIRED CONFIG) +find_package(LLVM 11 REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") @@ -13,4 +10,4 @@ include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) -llvm_map_components_to_libnames(llvm_libs support core irreader linker nativecodegen) +llvm_map_components_to_libnames(llvm_libs support core irreader linker native codegen nativecodegen Passes ObjCARCOpts) diff --git a/examples/all.ss b/examples/all.ss index 31365f4..41e858e 100644 --- a/examples/all.ss +++ b/examples/all.ss @@ -25,5 +25,5 @@ function func(a: number, b: number): number { return x + y; } -func(1, 2); +ss_println_integer(func(1, 2)); test(); \ No newline at end of file diff --git a/include/AST/ASTBuilder.h b/include/AST/ASTBuilder.h index b036e1b..a1d59c3 100644 --- a/include/AST/ASTBuilder.h +++ b/include/AST/ASTBuilder.h @@ -7,7 +7,7 @@ #include "AST/TypeNode.h" #include "AST/ExprNode.h" #include "AST/ModuleNode.h" -#include "Util/Alias.h" +#include "Support/Alias.h" /// AST构建器 class ASTBuilder final : public StaticScriptParserVisitor { diff --git a/include/AST/Node.h b/include/AST/Node.h index 6201174..fbec2ba 100644 --- a/include/AST/Node.h +++ b/include/AST/Node.h @@ -1,7 +1,7 @@ #pragma once #include -#include "Util/Alias.h" +#include "Support/Alias.h" class ASTVisitor; diff --git a/include/CodeGen/Builtin.h b/include/CodeGen/Builtin.h index 4b7fe84..34d658b 100644 --- a/include/CodeGen/Builtin.h +++ b/include/CodeGen/Builtin.h @@ -2,7 +2,7 @@ #include #include "config.h" -#include "Util/Alias.h" +#include "Support/Alias.h" #include diff --git a/include/CodeGen/IRGenerator.h b/include/CodeGen/IRGenerator.h index e5c3580..30c4636 100644 --- a/include/CodeGen/IRGenerator.h +++ b/include/CodeGen/IRGenerator.h @@ -3,8 +3,8 @@ #include "StaticScriptLexer.h" #include "Sema/ASTVisitor.h" #include "CodeGen/Builtin.h" -#include "Util/Alias.h" -#include "Exception/CodeGenException.h" +#include "Support/Alias.h" +#include "Support/Exception.h" class IRGenerator : public ASTVisitor { public: diff --git a/include/CodeGen/Pass.h b/include/CodeGen/Pass.h index d9ef476..3d9612f 100644 --- a/include/CodeGen/Pass.h +++ b/include/CodeGen/Pass.h @@ -5,7 +5,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "Util/Alias.h" +#include "Support/Alias.h" /// 移除终结指令后面的指令 struct EraseInstsAfterTerminatorPass : public llvm::FunctionPass { diff --git a/include/Entity/Scope.h b/include/Entity/Scope.h index 8377ec2..6bdce14 100644 --- a/include/Entity/Scope.h +++ b/include/Entity/Scope.h @@ -2,8 +2,8 @@ #include #include "AST/DeclNode.h" -#include "Util/Alias.h" -#include "Util/Find.h" +#include "Support/Alias.h" +#include "Support/Find.h" class TopLevelScope; diff --git a/include/Exception/CodeGenException.h b/include/Exception/CodeGenException.h deleted file mode 100644 index 5d6f371..0000000 --- a/include/Exception/CodeGenException.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Exception/CompilerException.h" - -/// IR生成异常 -class CodeGenException : public CompilerException { -public: - explicit CodeGenException(const String &message) : CompilerException(message) {} - - ~CodeGenException() noexcept override = default; -}; \ No newline at end of file diff --git a/include/Exception/CompilerException.h b/include/Exception/CompilerException.h deleted file mode 100644 index 807a7b5..0000000 --- a/include/Exception/CompilerException.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include -#include "Util/Alias.h" - -/// 编译异常 -class CompilerException : public std::exception { -public: - ~CompilerException() noexcept override = default; - - explicit CompilerException(String message) : message(std::move(message)) {} - - [[nodiscard]] const char *what() const noexcept override { - return message.c_str(); - } - -protected: - String message; -}; \ No newline at end of file diff --git a/include/Exception/DriverException.h b/include/Exception/DriverException.h deleted file mode 100644 index 2a5c811..0000000 --- a/include/Exception/DriverException.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Exception/CompilerException.h" - -/// 编译驱动器异常 -class DriverException : public CompilerException { -public: - explicit DriverException(const String &message) : CompilerException(message) {} - - ~DriverException() noexcept override = default; -}; \ No newline at end of file diff --git a/include/Exception/SemanticException.h b/include/Exception/SemanticException.h deleted file mode 100644 index 08050e3..0000000 --- a/include/Exception/SemanticException.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "Exception/CompilerException.h" - -/// 语义分析异常 -class SemanticException : public CompilerException { -public: - explicit SemanticException(const String &message) : CompilerException(message) {} - - ~SemanticException() noexcept override = default; -}; \ No newline at end of file diff --git a/include/Optimization/Optimizer.h b/include/Optimization/Optimizer.h new file mode 100644 index 0000000..8a8db06 --- /dev/null +++ b/include/Optimization/Optimizer.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "Support/Alias.h" +#include "Support/LLVM.h" +#include "Support/Exception.h" + +class Optimizer final { +public: + explicit Optimizer(LLVMModule &module, unsigned optLevel, unsigned sizeLevel); + + void optimize(); + +private: + void preoptimize(); + + void addOptimizationPasses(llvm::legacy::PassManager &passManager, + llvm::legacy::FunctionPassManager &functionPassManager, + llvm::TargetMachine *targetMachine) const; + + static void addStandardLinkPasses(llvm::legacy::PassManagerBase &passManager); + + LLVMModule &module; + unsigned optLevel; + unsigned sizeLevel; +}; \ No newline at end of file diff --git a/include/Sema/ReferenceResolver.h b/include/Sema/ReferenceResolver.h index 8590eaf..3219185 100644 --- a/include/Sema/ReferenceResolver.h +++ b/include/Sema/ReferenceResolver.h @@ -2,7 +2,7 @@ #include "Entity/Scope.h" #include "Sema/ASTVisitor.h" -#include "Exception/SemanticException.h" +#include "Support/Exception.h" /// 引用消解器 class ReferenceResolver final : public ASTVisitor { diff --git a/include/Sema/ScopeScanner.h b/include/Sema/ScopeScanner.h index 4c0beb9..34182c5 100644 --- a/include/Sema/ScopeScanner.h +++ b/include/Sema/ScopeScanner.h @@ -2,7 +2,7 @@ #include "Entity/Scope.h" #include "Sema/ASTVisitor.h" -#include "Exception/SemanticException.h" +#include "Support/Exception.h" /// 作用域扫描器 class ScopeScanner final : public ASTVisitor { diff --git a/include/Sema/SemanticValidator.h b/include/Sema/SemanticValidator.h index 9eec9bb..1b424cd 100644 --- a/include/Sema/SemanticValidator.h +++ b/include/Sema/SemanticValidator.h @@ -2,7 +2,7 @@ #include "StaticScriptLexer.h" #include "Sema/ASTVisitor.h" -#include "Exception/SemanticException.h" +#include "Support/Exception.h" /// 语义合法性验证器 class SemanticValidator : public ASTVisitor { diff --git a/include/Sema/TypeChecker.h b/include/Sema/TypeChecker.h index 076dcd7..72f2dd4 100644 --- a/include/Sema/TypeChecker.h +++ b/include/Sema/TypeChecker.h @@ -2,7 +2,7 @@ #include "StaticScriptLexer.h" #include "Sema/ASTVisitor.h" -#include "Exception/SemanticException.h" +#include "Support/Exception.h" /// 类型检查器 class TypeChecker : public ASTVisitor { diff --git a/include/Util/Alias.h b/include/Support/Alias.h similarity index 92% rename from include/Util/Alias.h rename to include/Support/Alias.h index e0cb505..e89f569 100644 --- a/include/Util/Alias.h +++ b/include/Support/Alias.h @@ -43,6 +43,9 @@ using Stack = std::stack; template using Map = std::map; +template +using UniquePtr = std::unique_ptr; + template using SharedPtr = std::shared_ptr; @@ -52,6 +55,11 @@ using SharedPtrVector = Vector>; template using SharedPtrMap = Map>; +template +inline UniquePtr makeUnique(Args &&...args) { + return std::make_unique(std::forward(args)...); +} + template inline SharedPtr makeShared(Args &&...args) { return std::make_shared(std::forward(args)...); diff --git a/include/Support/Exception.h b/include/Support/Exception.h new file mode 100644 index 0000000..51de8c8 --- /dev/null +++ b/include/Support/Exception.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include "Support/Alias.h" + +/// 编译异常 +class CompilerException : public std::exception { +public: + ~CompilerException() noexcept override = default; + + explicit CompilerException(String message) : message(std::move(message)) {} + + [[nodiscard]] const char *what() const noexcept override { + return message.c_str(); + } + +protected: + String message; +}; + +/// 语义分析异常 +class SemanticException : public CompilerException { +public: + explicit SemanticException(const String &message) : CompilerException(message) {} + + ~SemanticException() noexcept override = default; +}; + +/// IR生成异常 +class CodeGenException : public CompilerException { +public: + explicit CodeGenException(const String &message) : CompilerException(message) {} + + ~CodeGenException() noexcept override = default; +}; + +/// 编译驱动器异常 +class DriverException : public CompilerException { +public: + explicit DriverException(const String &message) : CompilerException(message) {} + + ~DriverException() noexcept override = default; +}; \ No newline at end of file diff --git a/include/Util/Find.h b/include/Support/Find.h similarity index 89% rename from include/Util/Find.h rename to include/Support/Find.h index 5f66448..73f1555 100644 --- a/include/Util/Find.h +++ b/include/Support/Find.h @@ -1,4 +1,4 @@ -#include "Util/Alias.h" +#include "Support/Alias.h" template inline SharedPtr mapFind(const SharedPtrMap &map, const K &key) { diff --git a/include/Support/LLVM.h b/include/Support/LLVM.h new file mode 100644 index 0000000..36de1e9 --- /dev/null +++ b/include/Support/LLVM.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include "Support/Exception.h" + +inline void initLLVMTarget() { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); +} + +inline void initLLVMPasses() { + llvm::PassRegistry ®istry = *llvm::PassRegistry::getPassRegistry(); + llvm::initializeCore(registry); + llvm::initializeCoroutines(registry); + llvm::initializeScalarOpts(registry); + llvm::initializeObjCARCOpts(registry); + llvm::initializeVectorization(registry); + llvm::initializeIPO(registry); + llvm::initializeAnalysis(registry); + llvm::initializeTransformUtils(registry); + llvm::initializeInstCombine(registry); + llvm::initializeAggressiveInstCombine(registry); + llvm::initializeInstrumentation(registry); + llvm::initializeTarget(registry); + llvm::initializeExpandMemCmpPassPass(registry); + llvm::initializeScalarizeMaskedMemIntrinPass(registry); + llvm::initializeCodeGenPreparePass(registry); + llvm::initializeAtomicExpandPass(registry); + llvm::initializeRewriteSymbolsLegacyPassPass(registry); + llvm::initializeWinEHPreparePass(registry); + llvm::initializeDwarfEHPreparePass(registry); + llvm::initializeSafeStackLegacyPassPass(registry); + llvm::initializeSjLjEHPreparePass(registry); + llvm::initializePreISelIntrinsicLoweringLegacyPassPass(registry); + llvm::initializeGlobalMergePass(registry); + llvm::initializeIndirectBrExpandPassPass(registry); + llvm::initializeInterleavedLoadCombinePass(registry); + llvm::initializeInterleavedAccessPass(registry); + llvm::initializeEntryExitInstrumenterPass(registry); + llvm::initializePostInlineEntryExitInstrumenterPass(registry); + llvm::initializeUnreachableBlockElimLegacyPassPass(registry); + llvm::initializeExpandReductionsPass(registry); + llvm::initializeWasmEHPreparePass(registry); + llvm::initializeWriteBitcodePassPass(registry); + llvm::initializeHardwareLoopsPass(registry); + llvm::initializeTypePromotionPass(registry); +} + +inline void initLLVMCodeGen() { + static llvm::codegen::RegisterCodeGenFlags CFG; +} + +inline llvm::CodeGenOpt::Level getCodeGenOptLevel(unsigned optLevel = 2) { + switch (optLevel) { + case 1: + return llvm::CodeGenOpt::Less; + case 2: + return llvm::CodeGenOpt::Default; + case 3: + return llvm::CodeGenOpt::Aggressive; + default: + return llvm::CodeGenOpt::None; + } +} + +inline String getTargetTriple() { + return llvm::sys::getDefaultTargetTriple(); +} + +inline llvm::TargetMachine *getTargetMachine(unsigned optLevel = 2) { + const String &targetTriple = getTargetTriple(); + String error; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(targetTriple, error); + if (!target) { + throw DriverException(error); + } + return target->createTargetMachine(targetTriple, + llvm::codegen::getCPUStr(), + llvm::codegen::getFeaturesStr(), + llvm::codegen::InitTargetOptionsFromCodeGenFlags(), + llvm::codegen::getExplicitRelocModel(), + llvm::codegen::getExplicitCodeModel(), + getCodeGenOptLevel(optLevel)); +} \ No newline at end of file diff --git a/include/Util/Output.h b/include/Support/Output.h similarity index 97% rename from include/Util/Output.h rename to include/Support/Output.h index c27d31c..1149490 100644 --- a/include/Util/Output.h +++ b/include/Support/Output.h @@ -1,7 +1,7 @@ #pragma once #include -#include "Util/Alias.h" +#include "Support/Alias.h" template static inline void print(std::ostream &outputStream, T &&content) { diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9daba98..9dd313c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(Entity) add_subdirectory(AST) add_subdirectory(Sema) add_subdirectory(CodeGen) +add_subdirectory(Optimization) add_subdirectory(Driver) add_executable( @@ -11,6 +12,7 @@ add_executable( $ $ $ + $ $ ) diff --git a/lib/CodeGen/Builtin.cpp b/lib/CodeGen/Builtin.cpp index a669bcf..5dabb27 100644 --- a/lib/CodeGen/Builtin.cpp +++ b/lib/CodeGen/Builtin.cpp @@ -1,5 +1,5 @@ #include "CodeGen/Builtin.h" -#include "Exception/CodeGenException.h" +#include "Support/Exception.h" void BuiltinString::initialize(LLVMModule &module, LLVMContext &context) { llvm::SMDiagnostic error; diff --git a/lib/CodeGen/CMakeLists.txt b/lib/CodeGen/CMakeLists.txt index c0eea2e..d849645 100644 --- a/lib/CodeGen/CMakeLists.txt +++ b/lib/CodeGen/CMakeLists.txt @@ -4,4 +4,4 @@ set(SRC Builtin.cpp ${CODEGEN_SRC} Pass.cpp) add_library(codegen OBJECT ${SRC}) -add_dependencies(codegen ast entity sema builtin) \ No newline at end of file +add_dependencies(codegen sema builtin) \ No newline at end of file diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index 101985d..a6de52e 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -4,4 +4,4 @@ set(SRC Driver.cpp) add_library(driver OBJECT ${SRC}) -add_dependencies(driver ast sema codegen) \ No newline at end of file +add_dependencies(driver optimization) \ No newline at end of file diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index f6b3688..5cdce93 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include "StaticScriptLexer.h" #include "StaticScriptParser.h" @@ -9,15 +10,29 @@ #include "Sema/TypeChecker.h" #include "Sema/SemanticValidator.h" #include "CodeGen/IRGenerator.h" -#include "Exception/DriverException.h" +#include "Optimization/Optimizer.h" +#include "Support/LLVM.h" +#include "Support/Exception.h" int drive(int argc, char **argv) { + llvm::InitLLVM X(argc, argv); llvm::cl::getRegisteredOptions().clear(); llvm::cl::OptionCategory generalOptsCat("General Options"); llvm::cl::OptionCategory genericOptsCat("Generic Options"); llvm::cl::opt inputFilename(llvm::cl::Positional, llvm::cl::desc("")); - llvm::cl::opt outputFilename("o", llvm::cl::value_desc("output file"), llvm::cl::desc("Write output to "), llvm::cl::cat(generalOptsCat)); + llvm::cl::opt outputFilename("o", llvm::cl::value_desc("output file"), llvm::cl::desc("Write output to "), + llvm::cl::cat(generalOptsCat)); llvm::cl::opt emitLLVM("emit-llvm", llvm::cl::desc("Generate the LLVM representation for input file"), llvm::cl::cat(generalOptsCat)); + llvm::cl::opt optLevelO0("O0", llvm::cl::desc("Optimization level 0. Similar to clang -O0"), + llvm::cl::cat(generalOptsCat)); + llvm::cl::opt optLevelO1("O1", llvm::cl::desc("Optimization level 1. Similar to clang -O1"), llvm::cl::cat(generalOptsCat)); + llvm::cl::opt optLevelO2("O2", llvm::cl::init(true), llvm::cl::desc("Optimization level 2. Similar to clang -O2"), + llvm::cl::cat(generalOptsCat)); + llvm::cl::opt optLevelOs("Os", llvm::cl::desc("Like -O2 with extra optimizations for size. Similar to clang -Os"), + llvm::cl::cat(generalOptsCat)); + llvm::cl::opt optLevelOz("Oz", llvm::cl::desc("Like -Os but reduces code size further. Similar to clang -Oz"), + llvm::cl::cat(generalOptsCat)); + llvm::cl::opt optLevelO3("O3", llvm::cl::desc("Optimization level 3. Similar to clang -O3"), llvm::cl::cat(generalOptsCat)); llvm::cl::opt showHelp("help", llvm::cl::desc("Display available options"), llvm::cl::cat(genericOptsCat)); llvm::cl::opt showHelpAlias("h", llvm::cl::desc("Alias for --help"), llvm::cl::cat(genericOptsCat)); llvm::cl::opt showVersion("version", llvm::cl::desc("Display the version of this program"), llvm::cl::cat(genericOptsCat)); @@ -44,7 +59,24 @@ int drive(int argc, char **argv) { outputFilename.setValue(emitLLVM ? "ss-ir.ll" : "ss-out.o"); } - if(!llvm::sys::fs::exists(inputFilename)) { + unsigned optLevel = 0; + unsigned sizeLevel = 0; + + if (optLevelO3) { + optLevel = 3; + } else if (optLevelOz) { + optLevel = 2; + sizeLevel = 2; + } else if (optLevelOs) { + optLevel = 2; + sizeLevel = 1; + } else if (optLevelO2) { + optLevel = 2; + } else if (optLevelO1) { + optLevel = 1; + } + + if (!llvm::sys::fs::exists(inputFilename)) { throw DriverException("Not found input file"); } std::ifstream fin(inputFilename.data()); @@ -78,25 +110,21 @@ int drive(int argc, char **argv) { LLVMModule &llvmModule = generator->getLLVMModule(); - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - - const String &targetTriple = llvm::sys::getDefaultTargetTriple(); - llvmModule.setTargetTriple(targetTriple); + // 初始化LLVM相关 + initLLVMTarget(); + initLLVMPasses(); + initLLVMCodeGen(); - String targetErrorMsg; - const llvm::Target *target = llvm::TargetRegistry::lookupTarget(targetTriple, targetErrorMsg); + // 设置target triple + llvmModule.setTargetTriple(getTargetTriple()); - if (!target) { - throw DriverException(targetErrorMsg); - } + // 优化 + SharedPtr optimizer = makeShared(llvmModule, optLevel, sizeLevel); + optimizer->optimize(); - String cpu = "generic"; - String features; - llvm::TargetOptions opt; - auto rm = llvm::Optional(); - llvm::TargetMachine *targetMachine = target->createTargetMachine(targetTriple, cpu, features, opt, rm); + llvm::TargetMachine *targetMachine = getTargetMachine(optLevel); + // 设置data layout llvmModule.setDataLayout(targetMachine->createDataLayout()); std::error_code ec; @@ -122,7 +150,8 @@ int drive(int argc, char **argv) { return 0; } -int main(int argc, char *argv[]) { + +int main(int argc, char **argv) { try { return drive(argc, argv); } catch (const SemanticException &e) { @@ -134,4 +163,4 @@ int main(int argc, char *argv[]) { } catch (const std::exception &e) { llvm::errs() << "[Unknown Exception] " << e.what() << '\n'; } -} +} \ No newline at end of file diff --git a/lib/Optimization/CMakeLists.txt b/lib/Optimization/CMakeLists.txt new file mode 100644 index 0000000..c0aa9df --- /dev/null +++ b/lib/Optimization/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SRC Optimizer.cpp) + +add_library(optimization OBJECT ${SRC}) + +add_dependencies(optimization codegen) \ No newline at end of file diff --git a/lib/Optimization/Optimizer.cpp b/lib/Optimization/Optimizer.cpp new file mode 100644 index 0000000..fd53208 --- /dev/null +++ b/lib/Optimization/Optimizer.cpp @@ -0,0 +1,74 @@ +#include "Optimization/Optimizer.h" + +Optimizer::Optimizer(LLVMModule &module, unsigned int optLevel, unsigned int sizeLevel) : module(module), + optLevel(optLevel), + sizeLevel(sizeLevel) {} + +void Optimizer::optimize() { + preoptimize(); + + llvm::Triple moduleTriple(getTargetTriple()); + + llvm::TargetLibraryInfoImpl tlii(moduleTriple); + + llvm::codegen::setFunctionAttributes(llvm::codegen::getCPUStr(), llvm::codegen::getFeaturesStr(), module); + + llvm::legacy::PassManager passes; + + llvm::TargetMachine *targetMachine = getTargetMachine(optLevel); + + passes.add(new llvm::TargetLibraryInfoWrapperPass(tlii)); + + passes.add(llvm::createTargetTransformInfoWrapperPass(targetMachine->getTargetIRAnalysis())); + + llvm::legacy::FunctionPassManager funcPasses(&module); + + funcPasses.add(llvm::createTargetTransformInfoWrapperPass(targetMachine->getTargetIRAnalysis())); + + addStandardLinkPasses(passes); + + addOptimizationPasses(passes, funcPasses, targetMachine); + + funcPasses.doInitialization(); + for (LLVMFunction &function : module) { + funcPasses.run(function); + } + funcPasses.doFinalization(); + + passes.add(llvm::createVerifierPass()); + + passes.run(module); +} + +void Optimizer::preoptimize() { + for (LLVMFunction &function : module) { + if (function.getName() != "main" && !function.isDeclaration()) { + function.setLinkage(LLVMFunction::LinkageTypes::InternalLinkage); + } + } +} + +void Optimizer::addOptimizationPasses(llvm::legacy::PassManager &passManager, llvm::legacy::FunctionPassManager &functionPassManager, + llvm::TargetMachine *targetMachine) const { + llvm::PassManagerBuilder builder; + builder.OptLevel = optLevel; + builder.SizeLevel = sizeLevel; + if (optLevel > 1) { + builder.Inliner = llvm::createFunctionInliningPass(optLevel, sizeLevel, false); + } else { + builder.Inliner = llvm::createAlwaysInlinerLegacyPass(); + } + builder.DisableUnrollLoops = optLevel == 0; + builder.LoopVectorize = optLevel > 1 && sizeLevel < 2; + builder.SLPVectorize = optLevel > 1 && sizeLevel < 2; + targetMachine->adjustPassManager(builder); + builder.populateFunctionPassManager(functionPassManager); + builder.populateModulePassManager(passManager); +} + +void Optimizer::addStandardLinkPasses(llvm::PassManagerBase &passManager) { + llvm::PassManagerBuilder Builder; + Builder.VerifyInput = true; + Builder.Inliner = llvm::createFunctionInliningPass(); + Builder.populateLTOPassManager(passManager); +} \ No newline at end of file From e0156a3d79d20d6c888b55ada40507368bd59aca Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sun, 29 Nov 2020 14:51:59 +0800 Subject: [PATCH 02/17] feat: support array type and optimize project structure --- CMakeLists.txt | 6 +- ROADMAP-zh_CN.md | 1 + builtin/CMakeLists.txt | 6 +- builtin/ss_array.c | 166 ++++++++++++++ builtin/ss_array.h | 160 ++++++++++++++ builtin/ss_base.h | 3 - builtin/ss_error.c | 11 + builtin/ss_error.h | 49 +++++ builtin/ss_io.h | 3 +- builtin/ss_string.c | 72 +----- builtin/ss_string.h | 48 +++- examples/array.ss | 17 ++ grammar/StaticScriptLexer.g4 | 2 + grammar/StaticScriptParser.g4 | 49 +++-- include/AST/ASTBuilder.h | 35 +-- include/AST/DeclNode.h | 14 +- include/AST/ExprNode.h | 61 ++++-- include/AST/Node.h | 1 + include/AST/TypeNode.h | 35 --- include/CodeGen/Builtin.h | 64 +++++- include/CodeGen/IRGenerator.h | 14 +- include/CodeGen/Pass.h | 6 +- include/{config.h.in => Config/Config.h.in} | 6 +- include/Entity/Type.h | 87 ++++++++ include/Optimization/Optimizer.h | 9 +- include/Sema/ASTVisitor.h | 14 +- include/Sema/ReferenceResolver.h | 3 +- include/Sema/ScopeScanner.h | 7 +- include/Sema/SemanticValidator.h | 33 --- include/Sema/TypeChecker.h | 11 +- include/Support/Alias.h | 62 ------ include/Support/Error.h | 69 ++++++ include/Support/Exception.h | 44 ---- include/Support/Find.h | 2 + include/Support/LLVM.h | 57 ++++- include/Support/Output.h | 45 ---- lib/AST/ASTBuilder.cpp | 136 +++++++----- lib/AST/CMakeLists.txt | 2 +- lib/AST/DeclNode.cpp | 22 +- lib/AST/ExprNode.cpp | 32 ++- lib/AST/TypeNode.cpp | 14 -- lib/CodeGen/Builtin.cpp | 113 +++++++--- lib/CodeGen/DeclCodeGen.cpp | 27 +-- lib/CodeGen/ExprCodeGen.cpp | 229 ++++++++++++++++++-- lib/CodeGen/IRGenerator.cpp | 56 ++++- lib/Driver/CMakeLists.txt | 4 +- lib/Driver/Driver.cpp | 35 +-- lib/Entity/CMakeLists.txt | 2 +- lib/Entity/Type.cpp | 85 ++++++++ lib/Sema/ASTVisitor.cpp | 19 +- lib/Sema/ReferenceResolver.cpp | 12 +- lib/Sema/ScopeScanner.cpp | 12 +- lib/Sema/SemanticValidator.cpp | 93 ++------ lib/Sema/TypeChecker.cpp | 175 +++++++++++---- 54 files changed, 1626 insertions(+), 714 deletions(-) create mode 100644 builtin/ss_array.c create mode 100644 builtin/ss_array.h delete mode 100644 builtin/ss_base.h create mode 100644 builtin/ss_error.c create mode 100644 builtin/ss_error.h create mode 100644 examples/array.ss delete mode 100644 include/AST/TypeNode.h rename include/{config.h.in => Config/Config.h.in} (78%) create mode 100644 include/Entity/Type.h create mode 100644 include/Support/Error.h delete mode 100644 include/Support/Exception.h delete mode 100644 include/Support/Output.h delete mode 100644 lib/AST/TypeNode.cpp create mode 100644 lib/Entity/Type.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 91a8de7..e70d003 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,10 @@ project(StaticScript CXX) set(CMAKE_CXX_STANDARD 17) +if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(DEBUG_MODE true) +endif () + set(PROJECT_VERSION_MAJOR 0) set(PROJECT_VERSION_MINOR 0) set(PROJECT_VERSION_PATCH 1) @@ -17,7 +21,7 @@ include(AddLLVM) include(AddDoxygen) -configure_file(include/config.h.in ${PROJECT_BINARY_DIR}/include/config.h) +configure_file(include/Config/Config.h.in ${PROJECT_BINARY_DIR}/include/Config/Config.h) include_directories(${PROJECT_BINARY_DIR}/include) diff --git a/ROADMAP-zh_CN.md b/ROADMAP-zh_CN.md index 50867b4..2e6b344 100644 --- a/ROADMAP-zh_CN.md +++ b/ROADMAP-zh_CN.md @@ -13,6 +13,7 @@ - [x] 变量/常量类型推导 - [x] 变量/常量赋值类型检查 - [x] 函数传参/返回类型检查 + - [x] 数组元素类型一致检查 - [ ] 语义合法性检查 - [x] break/continue语句只能出现在循环里 - [x] return语句只能出现在函数里 diff --git a/builtin/CMakeLists.txt b/builtin/CMakeLists.txt index 1ae269b..c69eeb8 100644 --- a/builtin/CMakeLists.txt +++ b/builtin/CMakeLists.txt @@ -1,9 +1,11 @@ -set(BUILTIN_SOURCES ss_string.c ss_io.c) -set(BUILTIN_BITCODES ss_string.bc ss_io.bc) +set(BUILTIN_SOURCES ss_error.c ss_string.c ss_array.c ss_io.c) +set(BUILTIN_BITCODES ss_error.bc ss_string.bc ss_array.bc ss_io.bc) add_custom_command( OUTPUT ${BUILTIN_BITCODES} + COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_error.c -std=c99 -O2 -o ss_error.bc COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_string.c -std=c99 -O2 -o ss_string.bc + COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_array.c -std=c99 -O2 -o ss_array.bc COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_io.c -std=c99 -O2 -o ss_io.bc DEPENDS ${BUILTIN_SOURCES} ) diff --git a/builtin/ss_array.c b/builtin/ss_array.c new file mode 100644 index 0000000..58ac07b --- /dev/null +++ b/builtin/ss_array.c @@ -0,0 +1,166 @@ +#include "ss_array.h" + +ss_array *ss_array_create_integer_array(ss_error **error) { + return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(long), error); +} + +ss_array *ss_array_create_boolean_array(ss_error **error) { + return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(bool), error); +} + +ss_array *ss_array_create_string_array(ss_error **error) { + return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(ss_string *), error); +} + +ss_array *ss_array_create_array_array(ss_error **error) { + ss_array *arr = ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(ss_array *), error); + if (!arr) { + return NULL; + } + arr->is_nd = true; + return arr; +} + +ss_array *ss_array_create_integer_array_with_literal(long literal_list[], size_t size, ss_error **error) { + ss_array_create_array_with_literal(long, false) +} + +ss_array *ss_array_create_boolean_array_with_literal(bool literal_list[], size_t size, ss_error **error) { + ss_array_create_array_with_literal(bool, false) +} + +ss_array *ss_array_create_string_array_with_literal(ss_string *literal_list[], size_t size, ss_error **error) { + ss_array_create_array_with_literal(ss_string *, false) +} + +ss_array *ss_array_create_array_array_with_literal(ss_array *literal_list[], size_t size, ss_error **error) { + ss_array_create_array_with_literal(ss_array *, true) +} + +void ss_array_delete(ss_array *arr) { + if (arr != NULL) { + if (arr->buffer != NULL) { + free(arr->buffer); + } + free(arr); + } +} + +size_t ss_array_get_size(ss_array *arr, ss_error **error) { + if (!arr) { + *error = ss_error_create(NULL_ARRAY_PTR_CODE, NULL_ARRAY_PTR_DESC); + return 0; + } + return arr->size; +} + +bool ss_array_is_nd_array(ss_array *arr, ss_error **error) { + if (!arr) { + *error = ss_error_create(NULL_ARRAY_PTR_CODE, NULL_ARRAY_PTR_DESC); + return false; + } + return arr->is_nd; +} + +void ss_array_push_integer(ss_array *arr, long new_element, ss_error **error) { + ss_array_push_detect() + ss_array_push(long) +} + +void ss_array_push_boolean(ss_array *arr, bool new_element, ss_error **error) { + ss_array_push_detect() + ss_array_push(bool) +} + +void ss_array_push_string(ss_array *arr, ss_string *new_element, ss_error **error) { + ss_array_push_detect() + ss_array_push(ss_string *) +} + +void ss_array_push_array(ss_array *arr, ss_array *new_element, ss_error **error) { + ss_array_push_detect() + ss_array_push(ss_array *) +} + +long ss_array_pop_integer(ss_array *arr, ss_error **error) { + ss_array_pop_detect(0) + ss_array_pop(long) +} + +bool ss_array_pop_boolean(ss_array *arr, ss_error **error) { + ss_array_pop_detect(false) + ss_array_pop(bool) +} + +ss_string *ss_array_pop_string(ss_array *arr, ss_error **error) { + ss_array_pop_detect(NULL) + ss_array_pop(ss_string *) +} + +ss_array *ss_array_pop_array(ss_array *arr, ss_error **error) { + ss_array_pop_detect(NULL) + ss_array_pop(ss_array *) +} + +long ss_array_get_integer(ss_array *arr, size_t index, ss_error **error) { + ss_array_get_detect(0) + ss_array_get(long) +} + +bool ss_array_get_boolean(ss_array *arr, size_t index, ss_error **error) { + ss_array_get_detect(false) + ss_array_get(bool) +} + +ss_string *ss_array_get_string(ss_array *arr, size_t index, ss_error **error) { + ss_array_get_detect(NULL) + ss_array_get(ss_string *) +} + +ss_array *ss_array_get_array(ss_array *arr, size_t index, ss_error **error) { + ss_array_get_detect(NULL) + ss_array_get(ss_array *) +} + +void ss_array_set_integer(ss_array *arr, size_t index, long element, ss_error **error) { + ss_array_set_detect() + ss_array_set(long) +} + +void ss_array_set_boolean(ss_array *arr, size_t index, bool element, ss_error **error) { + ss_array_set_detect() + ss_array_set(bool) +} + +void ss_array_set_string(ss_array *arr, size_t index, ss_string *element, ss_error **error) { + ss_array_set_detect() + ss_array_set(ss_string *) +} + +void ss_array_set_array(ss_array *arr, size_t index, ss_array *element, ss_error **error) { + ss_array_set_detect() + ss_array_set(ss_array *) +} + + +ss_array *ss_array_slice(ss_array *arr, ssize_t from, ssize_t to, ss_error **error) { + if (from < 0) { + from = arr->size + from; + } + if (to < 0) { + to = arr->size + to; + } + if (to < from) { + *error = ss_error_create(ARRAY_SLICE_TO_LT_FROM_CODE, ARRAY_SLICE_TO_LT_FROM_DESC); + return NULL; + } + size_t slice_size = to - from; + size_t new_capacity = ss_array_get_capacity_with_size(slice_size); + ss_array *new_arr = ss_array_create(new_capacity, arr->element_size, error); + if (!new_arr) { + return NULL; + } + memcpy(new_arr->buffer, arr->buffer + from * arr->element_size, slice_size * arr->element_size); + new_arr->size = slice_size; + return new_arr; +} \ No newline at end of file diff --git a/builtin/ss_array.h b/builtin/ss_array.h new file mode 100644 index 0000000..0d45603 --- /dev/null +++ b/builtin/ss_array.h @@ -0,0 +1,160 @@ +#pragma once + +#include +#include +#include +#include "ss_string.h" +#include "ss_error.h" + +#define SS_ARRAY_INIT_CAPACITY 8 + +typedef struct { + void *buffer; + size_t size; + size_t capacity; + size_t element_size; + bool is_nd; +} ss_array; + +static size_t ss_array_get_capacity_with_size(size_t size) { + return 1u << (size_t) ceil(log2(size)); +} + +static void ss_array_grow(ss_array *arr, ss_error **error) { + size_t new_capacity = arr->capacity * 2; + void *new_buffer = realloc(arr->buffer, arr->element_size * new_capacity); + if (!new_buffer) { + *error = ss_error_create(REALLOC_FAILED_CODE, REALLOC_FAILED_DESC); + return; + } + arr->buffer = new_buffer; + arr->capacity = new_capacity; +} + +static ss_array *ss_array_create(size_t capacity, size_t element_size, ss_error **error) { + ss_array *arr = (ss_array *) malloc(sizeof(ss_array)); + if (!arr) { + *error = ss_error_create(MALLOC_FAILED_CODE, MALLOC_FAILED_DESC); + return NULL; + } + arr->buffer = calloc(capacity, element_size); + if (!arr->buffer) { + *error = ss_error_create(CALLOC_FAILED_CODE, CALLOC_FAILED_DESC); + return NULL; + } + arr->size = 0; + arr->capacity = capacity; + arr->element_size = element_size; + arr->is_nd = false; + return arr; +} + +ss_array *ss_array_create_integer_array(ss_error **error); + +ss_array *ss_array_create_boolean_array(ss_error **error); + +ss_array *ss_array_create_string_array(ss_error **error); + +ss_array *ss_array_create_array_array(ss_error **error); + +#define ss_array_create_array_with_literal(type, nd_status) \ + size_t capacity = ss_array_get_capacity_with_size(size); \ + ss_array *arr = ss_array_create(capacity, sizeof(type), error); \ + if (!arr) { \ + return NULL; \ + } \ + memcpy(arr->buffer, literal_list, arr->element_size * size); \ + arr->size = size; \ + arr->is_nd = nd_status; \ + return arr; + +ss_array *ss_array_create_integer_array_with_literal(long literal_list[], size_t size, ss_error **error); + +ss_array *ss_array_create_boolean_array_with_literal(bool literal_list[], size_t size, ss_error **error); + +ss_array *ss_array_create_string_array_with_literal(ss_string *literal_list[], size_t size, ss_error **error); + +ss_array *ss_array_create_array_array_with_literal(ss_array *literal_list[], size_t size, ss_error **error); + +void ss_array_delete(ss_array *arr); + +size_t ss_array_get_size(ss_array *arr, ss_error **error); + +bool ss_array_is_nd_array(ss_array *arr, ss_error **error); + +#define ss_array_push_detect() \ + if (arr->size + 1 >= arr->capacity) { \ + ss_array_grow(arr, error); \ + if (*error != NULL) { \ + return; \ + } \ + } + +#define ss_array_push(type) \ + type *buffer = (type *) arr->buffer; \ + buffer[arr->size++] = new_element; \ + + +void ss_array_push_integer(ss_array *arr, long new_element, ss_error **error); + +void ss_array_push_boolean(ss_array *arr, bool new_element, ss_error **error); + +void ss_array_push_string(ss_array *arr, ss_string *new_element, ss_error **error); + +void ss_array_push_array(ss_array *arr, ss_array *new_element, ss_error **error); + +#define ss_array_pop_detect(zero_value) \ + if (arr->size == 0) { \ + *error = ss_error_create(EMPTY_ARRAY_POP_CODE, EMPTY_ARRAY_POP_DESC); \ + return zero_value; \ + } + +#define ss_array_pop(type) \ + type *buffer = (type *) arr->buffer; \ + return buffer[--arr->size]; + +long ss_array_pop_integer(ss_array *arr, ss_error **error); + +bool ss_array_pop_boolean(ss_array *arr, ss_error **error); + +ss_string *ss_array_pop_string(ss_array *arr, ss_error **error); + +ss_array *ss_array_pop_array(ss_array *arr, ss_error **error); + +#define ss_array_get_detect(zero_value) \ + if (index >= arr->size) { \ + *error = ss_error_create(ARRAY_INDEX_GE_SIZE_CODE, ARRAY_INDEX_GE_SIZE_DESC); \ + return zero_value; \ + } + +#define ss_array_get(type) \ + type *buffer = (type *) arr->buffer; \ + return buffer[index]; + +long ss_array_get_integer(ss_array *arr, size_t index, ss_error **error); + +bool ss_array_get_boolean(ss_array *arr, size_t index, ss_error **error); + +ss_string *ss_array_get_string(ss_array *arr, size_t index, ss_error **error); + +ss_array *ss_array_get_array(ss_array *arr, size_t index, ss_error **error); + +#define ss_array_set_detect() \ + if (index >= arr->size) { \ + *error = ss_error_create(ARRAY_INDEX_GE_SIZE_CODE, ARRAY_INDEX_GE_SIZE_DESC); \ + return; \ + } + +#define ss_array_set(type) \ + type *buffer = (type *)arr->buffer; \ + buffer[index] = element; + +void ss_array_set_integer(ss_array *arr, size_t index, long element, ss_error **error); + +void ss_array_set_boolean(ss_array *arr, size_t index, bool element, ss_error **error); + +void ss_array_set_string(ss_array *arr, size_t index, ss_string *element, ss_error **error); + +void ss_array_set_array(ss_array *arr, size_t index, ss_array *element, ss_error **error); + +ss_array *ss_array_slice(ss_array *arr, ssize_t from, ssize_t to, ss_error **error); \ No newline at end of file diff --git a/builtin/ss_base.h b/builtin/ss_base.h deleted file mode 100644 index ab81189..0000000 --- a/builtin/ss_base.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#define KEEPALIVE __attribute__((used)) diff --git a/builtin/ss_error.c b/builtin/ss_error.c new file mode 100644 index 0000000..a57030d --- /dev/null +++ b/builtin/ss_error.c @@ -0,0 +1,11 @@ +#include "ss_error.h" + +void ss_exit_if_error(ss_error *error) { + if (error) { + printf("\033[0;31m""[StaticScript Runtime Error]:\n\tcode: %d\n\tdescription: %s\n""\033[0m", error->code, error->description); + ss_error_delete(error); + abort(); + } else { + ss_error_delete(error); + } +} \ No newline at end of file diff --git a/builtin/ss_error.h b/builtin/ss_error.h new file mode 100644 index 0000000..17167b8 --- /dev/null +++ b/builtin/ss_error.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +typedef struct { + int code; + const char *description; +} ss_error; + + +#define MALLOC_FAILED_CODE 100001 +#define CALLOC_FAILED_CODE 100002 +#define REALLOC_FAILED_CODE 100003 + +#define MALLOC_FAILED_DESC "malloc分配内存失败" +#define CALLOC_FAILED_DESC "calloc分配内存失败" +#define REALLOC_FAILED_DESC "realloc分配内存失败" + + +#define NULL_ARRAY_PTR_CODE 200001 +#define EMPTY_ARRAY_POP_CODE 200002 +#define ARRAY_INDEX_GE_SIZE_CODE 200003 +#define ARRAY_SLICE_TO_LT_FROM_CODE 200004 + +#define NULL_ARRAY_PTR_DESC "数组指针为NULL" +#define EMPTY_ARRAY_POP_DESC "空数组不能执行pop操作" +#define ARRAY_INDEX_GE_SIZE_DESC "索引不得大于等于数组长度" +#define ARRAY_SLICE_TO_LT_FROM_DESC "数组执行slice操作时to不得小于from" + + +static ss_error *ss_error_create(int code, const char *description) { + ss_error *error = (ss_error *) malloc(sizeof(ss_error)); + if (!error) { + printf("\033[0;31m""[StaticScript Runtime Error]: Create Error Object Failed\n""\033[0m"); + abort(); + } + error->code = code; + error->description = description; + return error; +} + +static void ss_error_delete(ss_error *error) { + if (error != NULL) { + free(error); + } +} + +void ss_exit_if_error(ss_error *error); \ No newline at end of file diff --git a/builtin/ss_io.h b/builtin/ss_io.h index 851f62e..e6057bc 100644 --- a/builtin/ss_io.h +++ b/builtin/ss_io.h @@ -1,8 +1,7 @@ #pragma once -#include #include -#include "ss_base.h" +#include #include "ss_string.h" ss_string *ss_integer2string(long number); diff --git a/builtin/ss_string.c b/builtin/ss_string.c index 12f9bda..561b579 100644 --- a/builtin/ss_string.c +++ b/builtin/ss_string.c @@ -1,29 +1,5 @@ #include "ss_string.h" -size_t ss_string_get_capacity_with_size(size_t size) { - if (size == 0) { - return 16; - } else if (size < 16) { - return 32; - } else if (size < 32) { - return 48; - } else { - return size * 3 / 2; - } -} - -ss_string *ss_string_create_with_capacity(size_t capacity) { - ss_string *str = (ss_string *) malloc(sizeof(ss_string)); - if (!str) { - return NULL; - } - str->buffer = (char *) calloc(capacity, 1); - str->capacity = capacity; - str->size = 0; - memset(str->buffer, 0, capacity); - return str; -} - ss_string *ss_string_create(const char *literal) { size_t size = strlen(literal); size_t capacity = ss_string_get_capacity_with_size(size); @@ -45,53 +21,29 @@ size_t ss_string_get_size(ss_string *str) { return str->size; } -long ss_string_grow_with_capacity(ss_string *str, size_t new_capacity) { - char *new_buffer = (char *) calloc(new_capacity, 1); - if (!new_buffer) { - return -1; - } - memset(new_buffer, 0, new_capacity); - memcpy(new_buffer, str->buffer, str->size); - free(str->buffer); - str->buffer = new_buffer; - str->capacity = new_capacity; - return 0; -} - -long ss_string_grow(ss_string *str) { - if (str->capacity < 32) { - str->capacity += 16; - } else { - str->capacity = str->capacity * 3 / 2; - } - return ss_string_grow_with_capacity(str, str->capacity); -} - long ss_string_append(ss_string *dest, ss_string *src) { - size_t src_size = strlen(src->buffer); - size_t needed_size = dest->size + src_size; + size_t needed_size = dest->size + src->size; size_t needed_capacity = ss_string_get_capacity_with_size(needed_size); if (dest->capacity < needed_capacity) { if (ss_string_grow_with_capacity(dest, needed_capacity) == -1) { return -1; } } - strncat(dest->buffer, src->buffer, src_size); + strncat(dest->buffer, src->buffer, src->size); dest->size = needed_size; return 0; } long ss_string_prepend(ss_string *dest, ss_string *src) { - size_t src_size = strlen(src->buffer); - size_t needed_size = dest->size + src_size; + size_t needed_size = dest->size + src->size; size_t needed_capacity = ss_string_get_capacity_with_size(needed_size); if (dest->capacity < needed_capacity) { if (ss_string_grow_with_capacity(dest, needed_capacity) == -1) { return -1; } } - memmove(dest->buffer + src_size, dest->buffer, src_size + 1); - memcpy(dest->buffer, src->buffer, src_size); + memmove(dest->buffer + src->size, dest->buffer, src->size + 1); + memcpy(dest->buffer, src->buffer, src->size); dest->size = needed_size; return 0; } @@ -125,22 +77,20 @@ ss_string *ss_string_slice(ss_string *str, ssize_t from, ssize_t to) { } long ss_string_equals(ss_string *str1, ss_string *str2) { - size_t max_size = str1->size > str2->size ? str1->size : str2->size; - return strncmp(str1->buffer, str2->buffer, max_size); + if (str1->size != str2->size) { + return -1; + } + return strncmp(str1->buffer, str2->buffer, str1->size); } -ssize_t ss_string_index_of_with_literal(ss_string *str, const char *literal) { - char *sub = strstr(str->buffer, literal); +ssize_t ss_string_index_of(ss_string *str, ss_string *substr) { + char *sub = strstr(str->buffer, substr->buffer); if (!sub) { return -1; } return sub - str->buffer; } -ssize_t ss_string_index_of(ss_string *str, ss_string *substr) { - return ss_string_index_of_with_literal(str, substr->buffer); -} - long ss_string_trim_left(ss_string *str) { size_t i = 0; while (isspace(str->buffer[i])) { diff --git a/builtin/ss_string.h b/builtin/ss_string.h index 765ef4d..4f2e2ea 100644 --- a/builtin/ss_string.h +++ b/builtin/ss_string.h @@ -4,7 +4,6 @@ #include #include #include -#include "ss_base.h" typedef struct { char *buffer; @@ -12,9 +11,44 @@ typedef struct { size_t capacity; } ss_string; -size_t ss_string_get_capacity_with_size(size_t size); - -ss_string *ss_string_create_with_capacity(size_t capacity); +static size_t ss_string_get_capacity_with_size(size_t size) { + if (size == 0) { + return 16; + } else if (size < 16) { + return 32; + } else if (size < 32) { + return 48; + } else { + return size * 3 / 2; + } +} + +static ss_string *ss_string_create_with_capacity(size_t capacity) { + ss_string *str = (ss_string *) malloc(sizeof(ss_string)); + if (!str) { + return NULL; + } + str->buffer = (char *) calloc(capacity, 1); + str->capacity = capacity; + str->size = 0; + memset(str->buffer, 0, capacity); + return str; +} + +static long ss_string_grow_with_capacity(ss_string *str, size_t new_capacity) { + char *new_buffer = (char *)realloc(str->buffer, new_capacity); + if (!new_buffer) { + return -1; + } + str->buffer = new_buffer; + str->capacity = new_capacity; + return 0; +} + +static long ss_string_grow(ss_string *str) { + size_t new_capacity = str->capacity < 32 ? str->capacity + 16 : str->capacity * 3 / 2; + return ss_string_grow_with_capacity(str, new_capacity); +} ss_string *ss_string_create(const char *literal); @@ -22,10 +56,6 @@ void ss_string_delete(ss_string *str); size_t ss_string_get_size(ss_string *str); -long ss_string_grow_with_capacity(ss_string *str, size_t new_capacity); - -long ss_string_grow(ss_string *str); - long ss_string_append(ss_string *dest, ss_string *src); long ss_string_prepend(ss_string *dest, ss_string *src); @@ -36,8 +66,6 @@ ss_string *ss_string_slice(ss_string *str, ssize_t from, ssize_t to); long ss_string_equals(ss_string *str1, ss_string *str2); -ssize_t ss_string_index_of_with_literal(ss_string *str, const char *literal); - ssize_t ss_string_index_of(ss_string *str, ss_string *substr); long ss_string_trim_left(ss_string *str); diff --git a/examples/array.ss b/examples/array.ss new file mode 100644 index 0000000..083bd41 --- /dev/null +++ b/examples/array.ss @@ -0,0 +1,17 @@ +let arr1: number[]; +let arr2: number[] = []; +let arr3: number[][]; +let arr4: number[][] = []; + +let arr5 = [1,2]; +let arr6 = [[1,2],[3,4]]; +let arr7 = [[],[1,2]]; +let arr8 = [[[0],[0],[]],[[]]]; + +let arr: number[][][] = [[[1,2], [1,2,3,4], [5,6,7]]]; +++arr[0][1][3]; +let ele = arr[0][1][3]; +ss_println_integer(ele); + +arr[0][0][0] += arr[0][2][2]; +ss_println_integer(arr[0][0][0]); \ No newline at end of file diff --git a/grammar/StaticScriptLexer.g4 b/grammar/StaticScriptLexer.g4 index 22f11cd..1118bb8 100644 --- a/grammar/StaticScriptLexer.g4 +++ b/grammar/StaticScriptLexer.g4 @@ -22,6 +22,8 @@ Return: 'return'; // 分隔符 OpenParen: '('; CloseParen: ')'; +OpenBracket: '['; +CloseBracket: ']'; OpenBrace: '{'; CloseBrace: '}'; Colon: ':'; diff --git a/grammar/StaticScriptParser.g4 b/grammar/StaticScriptParser.g4 index aac1cb4..b110c36 100644 --- a/grammar/StaticScriptParser.g4 +++ b/grammar/StaticScriptParser.g4 @@ -53,20 +53,29 @@ variableDeclarator : Identifier typeAnnotation? variableInitializer? ; +variableInitializer + : Assign expression + ; + typeAnnotation - : Colon builtinType + : Colon type ; -builtinType +type + : arrayType + | atomicType + ; + +arrayType + : atomicType (OpenBracket CloseBracket)+ + ; + +atomicType : Boolean | Number | String ; -variableInitializer - : Assign expression - ; - functionDeclaration : Function Identifier OpenParen parameterList? CloseParen typeAnnotation? functionBody ; @@ -79,10 +88,6 @@ functionBody : compoundStatement ; -argumentList - : expressionList - ; - compoundStatement : OpenBrace statements? CloseBrace ; @@ -92,9 +97,7 @@ expressionStatement ; expression - : literal # LiteralExpr - | Identifier # IdentifierExpr - | OpenParen expression CloseParen # ParenExpr + : base=expression (OpenBracket index+=expression CloseBracket)+ # ArraySubscriptExpr | callExpression # CallExpr | expression uop=(PlusPlus | MinusMinus) # PostfixUnaryExpr | uop=(Not | BitNot | Plus | Minus | PlusPlus | MinusMinus) expression # PrefixUnaryExpr @@ -109,16 +112,32 @@ expression | expression bop=And expression # BinaryExpr | expression bop=Or expression # BinaryExpr | expression bop=(Assign | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | ModulusAssign | LeftShiftAssign | RightShiftAssign | BitAndAssign | BitXorAssign | BitOrAssign | AndAssign | OrAssign) expression # BinaryExpr + | Identifier # IdentifierExpr + | literal # LiteralExpr + | OpenParen expression CloseParen # ParenExpr ; callExpression : Identifier OpenParen argumentList? CloseParen ; +argumentList + : expressionList + ; + literal : BooleanLiteral | IntegerLiteral | StringLiteral + | arrayLiteral + ; + +arrayLiteral + : OpenBracket expressionList? CloseBracket + ; + +expressionList + : expression (Comma expression)* ; selectionStatement @@ -147,10 +166,6 @@ forInit | expressionList ; -expressionList - : expression (Comma expression)* - ; - jumpStatement : continueStatement | breakStatement diff --git a/include/AST/ASTBuilder.h b/include/AST/ASTBuilder.h index a1d59c3..96e39d0 100644 --- a/include/AST/ASTBuilder.h +++ b/include/AST/ASTBuilder.h @@ -1,13 +1,12 @@ #pragma once #include -#include "StaticScriptParserBaseVisitor.h" +#include "StaticScriptParserVisitor.h" +#include "Entity/Type.h" #include "AST/DeclNode.h" #include "AST/StmtNode.h" -#include "AST/TypeNode.h" #include "AST/ExprNode.h" #include "AST/ModuleNode.h" -#include "Support/Alias.h" /// AST构建器 class ASTBuilder final : public StaticScriptParserVisitor { @@ -34,11 +33,15 @@ class ASTBuilder final : public StaticScriptParserVisitor { antlrcpp::Any visitVariableDeclarator(StaticScriptParser::VariableDeclaratorContext *ctx) override; + antlrcpp::Any visitVariableInitializer(StaticScriptParser::VariableInitializerContext *ctx) override; + antlrcpp::Any visitTypeAnnotation(StaticScriptParser::TypeAnnotationContext *ctx) override; - antlrcpp::Any visitVariableInitializer(StaticScriptParser::VariableInitializerContext *ctx) override; + antlrcpp::Any visitType(StaticScriptParser::TypeContext *ctx) override; - antlrcpp::Any visitBuiltinType(StaticScriptParser::BuiltinTypeContext *ctx) override; + antlrcpp::Any visitArrayType(StaticScriptParser::ArrayTypeContext *ctx) override; + + antlrcpp::Any visitAtomicType(StaticScriptParser::AtomicTypeContext *ctx) override; antlrcpp::Any visitFunctionDeclaration(StaticScriptParser::FunctionDeclarationContext *ctx) override; @@ -46,21 +49,13 @@ class ASTBuilder final : public StaticScriptParserVisitor { antlrcpp::Any visitFunctionBody(StaticScriptParser::FunctionBodyContext *ctx) override; - antlrcpp::Any visitCallExpression(StaticScriptParser::CallExpressionContext *ctx) override; - - antlrcpp::Any visitArgumentList(StaticScriptParser::ArgumentListContext *ctx) override; - antlrcpp::Any visitCompoundStatement(StaticScriptParser::CompoundStatementContext *ctx) override; antlrcpp::Any visitExpressionStatement(StaticScriptParser::ExpressionStatementContext *ctx) override; antlrcpp::Any visitExpression(StaticScriptParser::ExpressionContext *ctx); - antlrcpp::Any visitLiteralExpr(StaticScriptParser::LiteralExprContext *ctx) override; - - antlrcpp::Any visitIdentifierExpr(StaticScriptParser::IdentifierExprContext *ctx) override; - - antlrcpp::Any visitParenExpr(StaticScriptParser::ParenExprContext *ctx) override; + antlrcpp::Any visitArraySubscriptExpr(StaticScriptParser::ArraySubscriptExprContext *ctx) override; antlrcpp::Any visitCallExpr(StaticScriptParser::CallExprContext *ctx) override; @@ -70,8 +65,20 @@ class ASTBuilder final : public StaticScriptParserVisitor { antlrcpp::Any visitBinaryExpr(StaticScriptParser::BinaryExprContext *ctx) override; + antlrcpp::Any visitIdentifierExpr(StaticScriptParser::IdentifierExprContext *ctx) override; + + antlrcpp::Any visitLiteralExpr(StaticScriptParser::LiteralExprContext *ctx) override; + + antlrcpp::Any visitParenExpr(StaticScriptParser::ParenExprContext *ctx) override; + + antlrcpp::Any visitCallExpression(StaticScriptParser::CallExpressionContext *ctx) override; + + antlrcpp::Any visitArgumentList(StaticScriptParser::ArgumentListContext *ctx) override; + antlrcpp::Any visitLiteral(StaticScriptParser::LiteralContext *ctx) override; + antlrcpp::Any visitArrayLiteral(StaticScriptParser::ArrayLiteralContext *ctx) override; + antlrcpp::Any visitSelectionStatement(StaticScriptParser::SelectionStatementContext *ctx) override; antlrcpp::Any visitIfStatement(StaticScriptParser::IfStatementContext *ctx) override; diff --git a/include/AST/DeclNode.h b/include/AST/DeclNode.h index cf0f3d4..899ddb5 100644 --- a/include/AST/DeclNode.h +++ b/include/AST/DeclNode.h @@ -1,7 +1,7 @@ #pragma once +#include "Entity/Type.h" #include "AST/Node.h" -#include "AST/TypeNode.h" #include "AST/StmtNode.h" #include "AST/ExprNode.h" @@ -23,13 +23,13 @@ class VarDeclNode : public DeclNode { VarDeclNode( VarModifier modifier, String name, - const SharedPtr &type + const SharedPtr &type ); VarDeclNode( VarModifier modifier, String name, - const SharedPtr &type, + const SharedPtr &type, const SharedPtr &initVal ); @@ -43,7 +43,7 @@ class VarDeclNode : public DeclNode { VarModifier modifier; String name; - SharedPtr type = nullptr; + SharedPtr type = nullptr; SharedPtr initVal = nullptr; // 当前变量声明的alloca/load ir @@ -53,7 +53,7 @@ class VarDeclNode : public DeclNode { /// 函数参数声明节点 class ParmVarDeclNode : public VarDeclNode { public: - ParmVarDeclNode(const String &name, const SharedPtr &type); + ParmVarDeclNode(const String &name, const SharedPtr &type); ~ParmVarDeclNode() override = default; @@ -68,7 +68,7 @@ class FunctionDeclNode : public DeclNode { FunctionDeclNode( String name, const SharedPtrVector ¶ms, - const SharedPtr &returnType, + const SharedPtr &returnType, const SharedPtr &body ); @@ -80,7 +80,7 @@ class FunctionDeclNode : public DeclNode { String name; SharedPtrVector params; - SharedPtr returnType = nullptr; + SharedPtr returnType = nullptr; SharedPtr body = nullptr; SharedPtr internalScope = nullptr; diff --git a/include/AST/ExprNode.h b/include/AST/ExprNode.h index 87683b0..d257c7a 100644 --- a/include/AST/ExprNode.h +++ b/include/AST/ExprNode.h @@ -1,7 +1,7 @@ #pragma once +#include "Entity/Type.h" #include "AST/Node.h" -#include "AST/TypeNode.h" #include "AST/StmtNode.h" class FunctionDeclNode; @@ -11,28 +11,34 @@ class ExprNode : public Node { public: ExprNode() = default; - explicit ExprNode(const SharedPtr &type); + explicit ExprNode(const SharedPtr &type); ~ExprNode() override = default; - SharedPtr inferType = nullptr; + SharedPtr inferType = nullptr; // 当前表达式的ir LLVMValue *code = nullptr; }; /// 字面量表达式节点 -class LiteralExprNode : public ExprNode { +class LiteralExprNode: public ExprNode { public: - explicit LiteralExprNode(const SharedPtr &type); - LiteralExprNode() = default; - ~LiteralExprNode() override = default; + explicit LiteralExprNode(const SharedPtr &type); +}; + +/// 原子字面量表达式节点 +class AtomicLiteralExprNode : public LiteralExprNode { +public: + explicit AtomicLiteralExprNode(const SharedPtr &type); + + ~AtomicLiteralExprNode() override = default; }; /// 布尔值字面量表达式节点 -class BooleanLiteralExprNode : public LiteralExprNode { +class BooleanLiteralExprNode : public AtomicLiteralExprNode { public: explicit BooleanLiteralExprNode(bool literal); @@ -44,23 +50,23 @@ class BooleanLiteralExprNode : public LiteralExprNode { }; /// 整数字面量表达式节点 -class IntegerLiteralExprNode : public LiteralExprNode { +class IntegerLiteralExprNode : public AtomicLiteralExprNode { public: - explicit IntegerLiteralExprNode(int literal); + explicit IntegerLiteralExprNode(long literal); ~IntegerLiteralExprNode() override = default; void accept(const SharedPtr &visitor) override; - int literal; + long literal; }; /// 字符串字面量表达式节点 -class StringLiteralExprNode : public LiteralExprNode { +class StringLiteralExprNode : public AtomicLiteralExprNode { public: - explicit StringLiteralExprNode(String literal); + explicit StringLiteralExprNode(); - StringLiteralExprNode() = default; + explicit StringLiteralExprNode(String literal); ~StringLiteralExprNode() override = default; @@ -69,6 +75,20 @@ class StringLiteralExprNode : public LiteralExprNode { String literal; }; +/// 数组字面量表达式 +class ArrayLiteralExprNode: public LiteralExprNode { +public: + explicit ArrayLiteralExprNode(); + + explicit ArrayLiteralExprNode(const SharedPtr &type); + + ~ArrayLiteralExprNode() override = default; + + void accept(const SharedPtr &visitor) override; + + SharedPtrVector elements; +}; + /// 标识符表达式节点 class IdentifierExprNode : public ExprNode { public: @@ -131,3 +151,16 @@ class BinaryOperatorExprNode : public ExprNode { unsigned int opCode; SharedPtr lhs = nullptr, rhs = nullptr; }; + +/// 数组下标表达式 +class ArraySubscriptExprNode: public ExprNode { +public: + ArraySubscriptExprNode(const SharedPtr &baseExpr, const SharedPtrVector &indexExprs); + + ~ArraySubscriptExprNode() override = default; + + void accept(const SharedPtr &visitor) override; + + SharedPtr baseExpr = nullptr; + SharedPtrVector indexExprs; +}; \ No newline at end of file diff --git a/include/AST/Node.h b/include/AST/Node.h index fbec2ba..986c37b 100644 --- a/include/AST/Node.h +++ b/include/AST/Node.h @@ -2,6 +2,7 @@ #include #include "Support/Alias.h" +#include "Support/LLVM.h" class ASTVisitor; diff --git a/include/AST/TypeNode.h b/include/AST/TypeNode.h deleted file mode 100644 index 063385d..0000000 --- a/include/AST/TypeNode.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "AST/Node.h" - -enum class TypeKind { - Boolean, Integer, String -}; - -/// 类型节点 -class TypeNode : public Node { -public: - explicit TypeNode(TypeKind kind); - - ~TypeNode() override = default; - - virtual bool isBuiltin() const = 0; - - TypeKind kind; -}; - -/// 内建类型节点 -class BuiltinTypeNode : public TypeNode { -public: - explicit BuiltinTypeNode(TypeKind kind); - - ~BuiltinTypeNode() override = default; - - bool isBuiltin() const override; - - void accept(const SharedPtr &visitor) override; - - static inline const SharedPtr BOOLEAN_TYPE = makeShared(TypeKind::Boolean); - static inline const SharedPtr INTEGER_TYPE = makeShared(TypeKind::Integer); - static inline const SharedPtr STRING_TYPE = makeShared(TypeKind::String); -}; diff --git a/include/CodeGen/Builtin.h b/include/CodeGen/Builtin.h index 34d658b..fedac1e 100644 --- a/include/CodeGen/Builtin.h +++ b/include/CodeGen/Builtin.h @@ -1,21 +1,32 @@ #pragma once -#include -#include "config.h" +#include "Config/Config.h" #include "Support/Alias.h" -#include - +#include "Support/Error.h" +#include "Support/LLVM.h" class Builtin { public: static void initialize(LLVMModule &module, LLVMContext &context); }; +class BuiltinError { +public: + static void linkModule(LLVMModule &module, LLVMContext &context); + + static void getTypeAndFunction(LLVMModule &module); + + static inline llvm::PointerType *type = nullptr; + static inline LLVMFunction *exitIfErrorFunc = nullptr; +}; + class BuiltinString { public: - static void initialize(LLVMModule &module, LLVMContext &context); + static void linkModule(LLVMModule &module, LLVMContext &context); - static inline LLVMType *type = nullptr; + static void getTypeAndFunction(LLVMModule &module); + + static inline llvm::PointerType *type = nullptr; static inline LLVMFunction *createFunc = nullptr; static inline LLVMFunction *deleteFunc = nullptr; static inline LLVMFunction *getSizeFunc = nullptr; @@ -30,9 +41,48 @@ class BuiltinString { static inline LLVMFunction *trimFunc = nullptr; }; +class BuiltinArray { +public: + static void linkModule(LLVMModule &module, LLVMContext &context); + + static void getTypeAndFunction(LLVMModule &module); + + static inline llvm::PointerType *type = nullptr; + static inline LLVMFunction *createIntegerArrayFunc = nullptr; + static inline LLVMFunction *createBooleanArrayFunc = nullptr; + static inline LLVMFunction *createStringArrayFunc = nullptr; + static inline LLVMFunction *createArrayArrayFunc = nullptr; + static inline LLVMFunction *createIntegerArrayWithLiteralFunc = nullptr; + static inline LLVMFunction *createBooleanArrayWithLiteralFunc = nullptr; + static inline LLVMFunction *createStringArrayWithLiteralFunc = nullptr; + static inline LLVMFunction *createArrayArrayWithLiteralFunc = nullptr; + static inline LLVMFunction *deleteFunc = nullptr; + static inline LLVMFunction *getSizeFunc = nullptr; + static inline LLVMFunction *isNDArrayFunc = nullptr; + static inline LLVMFunction *pushIntegerFunc = nullptr; + static inline LLVMFunction *pushBooleanFunc = nullptr; + static inline LLVMFunction *pushStringFunc = nullptr; + static inline LLVMFunction *pushArrayFunc = nullptr; + static inline LLVMFunction *popIntegerFunc = nullptr; + static inline LLVMFunction *popBooleanFunc = nullptr; + static inline LLVMFunction *popStringFunc = nullptr; + static inline LLVMFunction *popArrayFunc = nullptr; + static inline LLVMFunction *getIntegerFunc = nullptr; + static inline LLVMFunction *getBooleanFunc = nullptr; + static inline LLVMFunction *getStringFunc = nullptr; + static inline LLVMFunction *getArrayFunc = nullptr; + static inline LLVMFunction *setIntegerFunc = nullptr; + static inline LLVMFunction *setBooleanFunc = nullptr; + static inline LLVMFunction *setStringFunc = nullptr; + static inline LLVMFunction *setArrayFunc = nullptr; + static inline LLVMFunction *sliceFunc = nullptr; +}; + class BuiltinIO { public: - static void initialize(LLVMModule &module, LLVMContext &context); + static void linkModule(LLVMModule &module, LLVMContext &context); + + static void getTypeAndFunction(LLVMModule &module); static inline LLVMFunction *integer2stringFunc = nullptr; static inline LLVMFunction *string2integerFunc = nullptr; diff --git a/include/CodeGen/IRGenerator.h b/include/CodeGen/IRGenerator.h index 30c4636..5271300 100644 --- a/include/CodeGen/IRGenerator.h +++ b/include/CodeGen/IRGenerator.h @@ -1,10 +1,12 @@ #pragma once +#include #include "StaticScriptLexer.h" #include "Sema/ASTVisitor.h" #include "CodeGen/Builtin.h" #include "Support/Alias.h" -#include "Support/Exception.h" +#include "Support/Error.h" +#include "Support/LLVM.h" class IRGenerator : public ASTVisitor { public: @@ -26,6 +28,8 @@ class IRGenerator : public ASTVisitor { void visit(const SharedPtr &strLiteralExpr) override; + void visit(const SharedPtr &arrayLiteralExpr) override; + void visit(const SharedPtr &varExpr) override; void visit(const SharedPtr &callExpr) override; @@ -34,6 +38,8 @@ class IRGenerator : public ASTVisitor { void visit(const SharedPtr &bopExpr) override; + void visit(const SharedPtr &asExpr) override; + void visit(const SharedPtr &exprStmt) override; void visit(const SharedPtr &compStmt) override; @@ -59,7 +65,9 @@ class IRGenerator : public ASTVisitor { } private: - LLVMType *getType(const SharedPtr &builtinType); + LLVMType *getType(const SharedPtr &inputType); + + void setArrayElement(const SharedPtr &asExpr, LLVMValue *valueCode); inline void setFuncInsertPoint(LLVMFunction *func) { LLVMBasicBlock *curBB = &(func->getBasicBlockList().back()); @@ -75,7 +83,7 @@ class IRGenerator : public ASTVisitor { void emitBranch(LLVMBasicBlock *targetBB); LLVMContext llvmContext; - LLVMIRBuilder llvmIRBuilder; + llvm::IRBuilder<> llvmIRBuilder; SharedPtr llvmModule; LLVMFunction *mainFn = nullptr; diff --git a/include/CodeGen/Pass.h b/include/CodeGen/Pass.h index 3d9612f..4d674af 100644 --- a/include/CodeGen/Pass.h +++ b/include/CodeGen/Pass.h @@ -1,11 +1,7 @@ #pragma once -#include "llvm/Pass.h" -#include "llvm/IR/Function.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "Support/Alias.h" +#include "Support/LLVM.h" /// 移除终结指令后面的指令 struct EraseInstsAfterTerminatorPass : public llvm::FunctionPass { diff --git a/include/config.h.in b/include/Config/Config.h.in similarity index 78% rename from include/config.h.in rename to include/Config/Config.h.in index 59f1328..1eb7446 100644 --- a/include/config.h.in +++ b/include/Config/Config.h.in @@ -4,10 +4,12 @@ #cmakedefine CMAKE_HOST_WIN32 @CMAKE_HOST_WIN32@ #if defined(CMAKE_HOST_UNIX) && !defined(CMAKE_HOST_APPLE) - #define CMAKE_HOST_LINUX 1 + #define CMAKE_HOST_LINUX true #endif #cmakedefine PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ #cmakedefine PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@ #cmakedefine PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@ -#cmakedefine PROJECT_VERSION "@PROJECT_VERSION@" \ No newline at end of file +#cmakedefine PROJECT_VERSION "@PROJECT_VERSION@" + +#cmakedefine DEBUG_MODE @DEBUG_MODE@ \ No newline at end of file diff --git a/include/Entity/Type.h b/include/Entity/Type.h new file mode 100644 index 0000000..0ff0977 --- /dev/null +++ b/include/Entity/Type.h @@ -0,0 +1,87 @@ +#pragma once + +#include "Support/Alias.h" +#include "Support/Error.h" + +/// 原子类型的种类, Unknown只用于空数组的元素类型 +enum class AtomicTypeKind { + Boolean, Integer, String, Unknown +}; + +/// 类型节点 +class Type { +public: + virtual ~Type() = default; + + [[nodiscard]] virtual bool isAtomic() const = 0; + + [[nodiscard]] virtual bool isArray() const = 0; + + [[nodiscard]] virtual bool isUnknown() const = 0; + + [[nodiscard]] bool equals(const SharedPtr &rhs) const; +}; + +/// 原子类型节点 +class AtomicType : public Type { +public: + explicit AtomicType(AtomicTypeKind kind); + + explicit AtomicType() = default; + + ~AtomicType() override = default; + + [[nodiscard]] bool isAtomic() const override; + + [[nodiscard]] bool isArray() const override; + + [[nodiscard]] bool isUnknown() const override; + + [[nodiscard]] AtomicTypeKind getKind() const; + + bool operator==(const AtomicType &rhs) = delete; + + bool operator!=(const AtomicType &rhs) = delete; + + [[nodiscard]] bool equals(const SharedPtr &rhs) const; + + static inline const SharedPtr BOOLEAN_TYPE = makeShared(AtomicTypeKind::Boolean); // NOLINT + static inline const SharedPtr INTEGER_TYPE = makeShared(AtomicTypeKind::Integer); // NOLINT + static inline const SharedPtr STRING_TYPE = makeShared(AtomicTypeKind::String); // NOLINT + static inline const SharedPtr UNKNOWN_TYPE = makeShared(AtomicTypeKind::Unknown); // NOLINT +private: + AtomicTypeKind kind = AtomicTypeKind::Unknown; +}; + +/// 数组类型节点 +class ArrayType : public Type { +public: + static SharedPtr createNDArrayType(const SharedPtr &atomicType, size_t depth); + + explicit ArrayType(const SharedPtr &elementType); + + explicit ArrayType() = default; + + ~ArrayType() override = default; + + [[nodiscard]] bool isAtomic() const override; + + [[nodiscard]] bool isArray() const override; + + [[nodiscard]] bool isUnknown() const override; + + [[nodiscard]] const SharedPtr &getElementType() const; + + [[nodiscard]] size_t getDepth() const; + + void setElementType(const SharedPtr &eleType); + + bool operator==(const ArrayType &rhs) = delete; + + bool operator!=(const ArrayType &rhs) = delete; + + [[nodiscard]] bool equals(const SharedPtr &rhs) const; +private: + SharedPtr elementType = AtomicType::UNKNOWN_TYPE; + size_t depth = 1; +}; \ No newline at end of file diff --git a/include/Optimization/Optimizer.h b/include/Optimization/Optimizer.h index 8a8db06..25818a0 100644 --- a/include/Optimization/Optimizer.h +++ b/include/Optimization/Optimizer.h @@ -1,15 +1,8 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include #include "Support/Alias.h" #include "Support/LLVM.h" -#include "Support/Exception.h" +#include "Support/Error.h" class Optimizer final { public: diff --git a/include/Sema/ASTVisitor.h b/include/Sema/ASTVisitor.h index 9ec2cc9..fc78b49 100644 --- a/include/Sema/ASTVisitor.h +++ b/include/Sema/ASTVisitor.h @@ -1,11 +1,14 @@ #pragma once #include -#include "AST/TypeNode.h" +#include "Entity/Type.h" #include "AST/DeclNode.h" #include "AST/StmtNode.h" #include "AST/ExprNode.h" #include "AST/ModuleNode.h" +#include "Support/Alias.h" +#include "Support/Error.h" +#include "Support/LLVM.h" /// AST遍历器 class ASTVisitor : public std::enable_shared_from_this { @@ -14,8 +17,6 @@ class ASTVisitor : public std::enable_shared_from_this { virtual void visit(const SharedPtr &module); - virtual void visit(const SharedPtr &builtinType); - virtual void visit(const SharedPtr &varDecl); virtual void visit(const SharedPtr ¶mVarDecl); @@ -28,6 +29,8 @@ class ASTVisitor : public std::enable_shared_from_this { virtual void visit(const SharedPtr &strLiteralExpr); + virtual void visit(const SharedPtr &arrayLiteralExpr); + virtual void visit(const SharedPtr &varExpr); virtual void visit(const SharedPtr &callExpr); @@ -36,6 +39,8 @@ class ASTVisitor : public std::enable_shared_from_this { virtual void visit(const SharedPtr &bopExpr); + virtual void visit(const SharedPtr &asExpr); + virtual void visit(const SharedPtr &exprStmt); virtual void visit(const SharedPtr &compStmt); @@ -55,7 +60,10 @@ class ASTVisitor : public std::enable_shared_from_this { virtual void visit(const SharedPtr &breakStmt); virtual void visit(const SharedPtr &returnStmt); +}; +/// 带作用域的AST遍历器 +class ASTVisitorWithScope: public ASTVisitor { protected: /** * @brief 将作用域压栈 diff --git a/include/Sema/ReferenceResolver.h b/include/Sema/ReferenceResolver.h index 3219185..3a9af83 100644 --- a/include/Sema/ReferenceResolver.h +++ b/include/Sema/ReferenceResolver.h @@ -2,10 +2,9 @@ #include "Entity/Scope.h" #include "Sema/ASTVisitor.h" -#include "Support/Exception.h" /// 引用消解器 -class ReferenceResolver final : public ASTVisitor { +class ReferenceResolver final : public ASTVisitorWithScope { public: void visit(const SharedPtr &module) override; diff --git a/include/Sema/ScopeScanner.h b/include/Sema/ScopeScanner.h index 34182c5..a780943 100644 --- a/include/Sema/ScopeScanner.h +++ b/include/Sema/ScopeScanner.h @@ -2,10 +2,9 @@ #include "Entity/Scope.h" #include "Sema/ASTVisitor.h" -#include "Support/Exception.h" /// 作用域扫描器 -class ScopeScanner final : public ASTVisitor { +class ScopeScanner final : public ASTVisitorWithScope { public: void visit(const SharedPtr &module) override; @@ -21,6 +20,8 @@ class ScopeScanner final : public ASTVisitor { void visit(const SharedPtr &strLiteralExpr) override; + void visit(const SharedPtr &arrayLiteralExpr) override; + void visit(const SharedPtr &varExpr) override; void visit(const SharedPtr &callExpr) override; @@ -29,6 +30,8 @@ class ScopeScanner final : public ASTVisitor { void visit(const SharedPtr &bopExpr) override; + void visit(const SharedPtr &asExpr) override; + void visit(const SharedPtr &exprStmt) override; void visit(const SharedPtr &compStmt) override; diff --git a/include/Sema/SemanticValidator.h b/include/Sema/SemanticValidator.h index 1b424cd..3197612 100644 --- a/include/Sema/SemanticValidator.h +++ b/include/Sema/SemanticValidator.h @@ -2,52 +2,19 @@ #include "StaticScriptLexer.h" #include "Sema/ASTVisitor.h" -#include "Support/Exception.h" /// 语义合法性验证器 class SemanticValidator : public ASTVisitor { public: - void visit(const SharedPtr &module) override; - - void visit(const SharedPtr &builtinType) override; - void visit(const SharedPtr &varDecl) override; - void visit(const SharedPtr ¶mVarDecl) override; - void visit(const SharedPtr &funcDecl) override; - void visit(const SharedPtr &boolLiteralExpr) override; - - void visit(const SharedPtr &intLiteralExpr) override; - - void visit(const SharedPtr &strLiteralExpr) override; - - void visit(const SharedPtr &varExpr) override; - - void visit(const SharedPtr &callExpr) override; - void visit(const SharedPtr &uopExpr) override; void visit(const SharedPtr &bopExpr) override; - void visit(const SharedPtr &exprStmt) override; - - void visit(const SharedPtr &compStmt) override; - - void visit(const SharedPtr &varDeclStmt) override; - - void visit(const SharedPtr &funcDeclStmt) override; - - void visit(const SharedPtr &ifStmt) override; - - void visit(const SharedPtr &whileStmt) override; - - void visit(const SharedPtr &forStmt) override; - void visit(const SharedPtr &continueStmt) override; void visit(const SharedPtr &breakStmt) override; - - void visit(const SharedPtr &returnStmt) override; }; diff --git a/include/Sema/TypeChecker.h b/include/Sema/TypeChecker.h index 72f2dd4..29dda29 100644 --- a/include/Sema/TypeChecker.h +++ b/include/Sema/TypeChecker.h @@ -2,14 +2,13 @@ #include "StaticScriptLexer.h" #include "Sema/ASTVisitor.h" -#include "Support/Exception.h" /// 类型检查器 class TypeChecker : public ASTVisitor { public: void visit(const SharedPtr &varDecl) override; - void visit(const SharedPtr &varExpr) override; + void visit(const SharedPtr &asExpr) override; void visit(const SharedPtr &callExpr) override; @@ -17,6 +16,10 @@ class TypeChecker : public ASTVisitor { void visit(const SharedPtr &bopExpr) override; + void visit(const SharedPtr &varExpr) override; + + void visit(const SharedPtr &arrayLiteralExpr) override; + void visit(const SharedPtr &ifStmt) override; void visit(const SharedPtr &whileStmt) override; @@ -24,4 +27,8 @@ class TypeChecker : public ASTVisitor { void visit(const SharedPtr &forStmt) override; void visit(const SharedPtr &returnStmt) override; + +private: + /// 把显式的声明类型递归赋回给初始值的类型, 用于CodeGen + static void assignTypeForArrayLiteral(const SharedPtr &arrayLiteral, const SharedPtr &type); }; \ No newline at end of file diff --git a/include/Support/Alias.h b/include/Support/Alias.h index e89f569..ee6b0b1 100644 --- a/include/Support/Alias.h +++ b/include/Support/Alias.h @@ -5,32 +5,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include using String = std::string; @@ -74,39 +48,3 @@ template inline SharedPtr dynPtrCast(const SharedPtr

&ptr) { return std::dynamic_pointer_cast(ptr); } - -using LLVMValue = llvm::Value; -using LLVMContext = llvm::LLVMContext; -using LLVMIRBuilder = llvm::IRBuilder<>; -using LLVMModule = llvm::Module; -using LLVMConstantInt = llvm::ConstantInt; -using LLVMAPInt = llvm::APInt; -using LLVMType = llvm::Type; -using LLVMFunctionType = llvm::FunctionType; -using LLVMFunction = llvm::Function; -using LLVMBasicBlock = llvm::BasicBlock; -using LLVMGlobalValue = llvm::GlobalValue; -using LLVMGlobalVariable = llvm::GlobalVariable; - -template -inline bool LLVMIsa(const Y &val) { - return llvm::isa(val); -} - -template -inline X* LLVMCast(const Y &val) { - return llvm::cast(val); -} - -template -inline X* LLVMDynCast(const Y &val) { - return llvm::dyn_cast(val); -} - -inline bool LLVMVerifyFunction(const LLVMFunction &func) { - return llvm::verifyFunction(func); -} - -inline bool LLVMVerifyModule(const LLVMModule &module) { - return llvm::verifyModule(module); -} diff --git a/include/Support/Error.h b/include/Support/Error.h new file mode 100644 index 0000000..04af9d8 --- /dev/null +++ b/include/Support/Error.h @@ -0,0 +1,69 @@ +#pragma once + +#include "Config/Config.h" +#include "Support/LLVM.h" + +inline void reportError(const llvm::Twine &reason) { +#ifdef DEBUG_MODE + llvm::report_fatal_error(reason); +#else + llvm::errs() << reason << '\n'; + abort(); +#endif +} + +inline void reportOnError(bool condition, const llvm::Twine &reason) { + if (condition) { + reportError(reason); + } +} + +inline void reportTypeError(const llvm::Twine &reason) { + reportError(llvm::formatv("[Compilation Type Error] {0}", reason)); +} + +inline void reportOnTypeError(bool condition, const llvm::Twine &reason) { + if (condition) { + reportTypeError(reason); + } +} + +inline void reportSemanticError(const llvm::Twine &reason) { + reportError(llvm::formatv("[Compilation Semantic Error] {0}", reason)); +} + +inline void reportOnSemanticError(bool condition, const llvm::Twine &reason) { + if (condition) { + reportSemanticError(reason); + } +} + +inline void reportLinkError(const llvm::Twine &reason) { + reportError(llvm::formatv("[Compilation Link Error] {0}", reason)); +} + +inline void reportOnLinkError(bool condition, const llvm::Twine &reason) { + if (condition) { + reportLinkError(reason); + } +} + +inline void reportCodeGenError(const llvm::Twine &reason) { + reportError(llvm::formatv("[Compilation CodeGen Error] {0}", reason)); +} + +inline void reportOnCodeGenError(bool condition, const llvm::Twine &reason) { + if (condition) { + reportCodeGenError(reason); + } +} + +inline void reportDriverError(const llvm::Twine &reason) { + reportError(llvm::formatv("[Compilation Driver Error] {0}", reason)); +} + +inline void reportOnDriverError(bool condition, const llvm::Twine &reason) { + if (condition) { + reportDriverError(reason); + } +} \ No newline at end of file diff --git a/include/Support/Exception.h b/include/Support/Exception.h deleted file mode 100644 index 51de8c8..0000000 --- a/include/Support/Exception.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include "Support/Alias.h" - -/// 编译异常 -class CompilerException : public std::exception { -public: - ~CompilerException() noexcept override = default; - - explicit CompilerException(String message) : message(std::move(message)) {} - - [[nodiscard]] const char *what() const noexcept override { - return message.c_str(); - } - -protected: - String message; -}; - -/// 语义分析异常 -class SemanticException : public CompilerException { -public: - explicit SemanticException(const String &message) : CompilerException(message) {} - - ~SemanticException() noexcept override = default; -}; - -/// IR生成异常 -class CodeGenException : public CompilerException { -public: - explicit CodeGenException(const String &message) : CompilerException(message) {} - - ~CodeGenException() noexcept override = default; -}; - -/// 编译驱动器异常 -class DriverException : public CompilerException { -public: - explicit DriverException(const String &message) : CompilerException(message) {} - - ~DriverException() noexcept override = default; -}; \ No newline at end of file diff --git a/include/Support/Find.h b/include/Support/Find.h index 73f1555..eaca646 100644 --- a/include/Support/Find.h +++ b/include/Support/Find.h @@ -1,3 +1,5 @@ +#pragma once + #include "Support/Alias.h" template diff --git a/include/Support/LLVM.h b/include/Support/LLVM.h index 36de1e9..1e899d9 100644 --- a/include/Support/LLVM.h +++ b/include/Support/LLVM.h @@ -1,9 +1,60 @@ #pragma once +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include "Support/Exception.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Support/Error.h" + +using LLVMValue = llvm::Value; +using LLVMContext = llvm::LLVMContext; +using LLVMModule = llvm::Module; +using LLVMConstantInt = llvm::ConstantInt; +using LLVMType = llvm::Type; +using LLVMFunctionType = llvm::FunctionType; +using LLVMFunction = llvm::Function; +using LLVMBasicBlock = llvm::BasicBlock; +using LLVMGlobalValue = llvm::GlobalValue; +using LLVMGlobalVariable = llvm::GlobalVariable; + +void reportOnDriverError(bool condition, const llvm::Twine &reason); inline void initLLVMTarget() { llvm::InitializeNativeTarget(); @@ -74,9 +125,7 @@ inline llvm::TargetMachine *getTargetMachine(unsigned optLevel = 2) { const String &targetTriple = getTargetTriple(); String error; const llvm::Target *target = llvm::TargetRegistry::lookupTarget(targetTriple, error); - if (!target) { - throw DriverException(error); - } + reportOnDriverError(!target, error); return target->createTargetMachine(targetTriple, llvm::codegen::getCPUStr(), llvm::codegen::getFeaturesStr(), diff --git a/include/Support/Output.h b/include/Support/Output.h deleted file mode 100644 index 1149490..0000000 --- a/include/Support/Output.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include "Support/Alias.h" - -template -static inline void print(std::ostream &outputStream, T &&content) { - outputStream << content; -} - -template -static inline void print(std::ostream &outputStream, T &&content, U &&...restContent) { - outputStream << content; - print(outputStream, restContent...); -} - -template -inline void outPrint(T &&...content) { - print(std::cout, content...); -} - -inline void outPrintln() { - std::cout << std::endl; -} - -template -inline void outPrintln(T &&...content) { - print(std::cout, content...); - outPrintln(); -} - -template -inline void errPrint(T &&...content) { - print(std::cerr, content...); -} - -inline void errPrintln() { - std::cerr << std::endl; -} - -template -inline void errPrintln(T &&...content) { - print(std::cerr, content...); - errPrintln(); -} diff --git a/lib/AST/ASTBuilder.cpp b/lib/AST/ASTBuilder.cpp index 06d865b..c55bb1d 100644 --- a/lib/AST/ASTBuilder.cpp +++ b/lib/AST/ASTBuilder.cpp @@ -112,30 +112,45 @@ antlrcpp::Any ASTBuilder::visitVariableDeclarator(StaticScriptParser::VariableDe return varDecl; } +antlrcpp::Any ASTBuilder::visitVariableInitializer(StaticScriptParser::VariableInitializerContext *ctx) { + return visitExpression(ctx->expression()); +} + antlrcpp::Any ASTBuilder::visitTypeAnnotation(StaticScriptParser::TypeAnnotationContext *ctx) { - return visitBuiltinType(ctx->builtinType()); + return visitType(ctx->type()); } -antlrcpp::Any ASTBuilder::visitBuiltinType(StaticScriptParser::BuiltinTypeContext *ctx) { - if (ctx->Boolean()) { - return BuiltinTypeNode::BOOLEAN_TYPE; - } else if (ctx->Number()) { - return BuiltinTypeNode::INTEGER_TYPE; +antlrcpp::Any ASTBuilder::visitType(StaticScriptParser::TypeContext *ctx) { + SharedPtr type; + if (ctx->arrayType()) { + type = visitArrayType(ctx->arrayType()).as>(); + } else { + type = visitAtomicType(ctx->atomicType()).as>(); } - return BuiltinTypeNode::STRING_TYPE; + return type; } -antlrcpp::Any ASTBuilder::visitVariableInitializer(StaticScriptParser::VariableInitializerContext *ctx) { - return visitExpression(ctx->expression()); +antlrcpp::Any ASTBuilder::visitArrayType(StaticScriptParser::ArrayTypeContext *ctx) { + const SharedPtr &atomicType = visitAtomicType(ctx->atomicType()); + size_t depth = ctx->OpenBracket().size(); + return ArrayType::createNDArrayType(atomicType, depth); +} + +antlrcpp::Any ASTBuilder::visitAtomicType(StaticScriptParser::AtomicTypeContext *ctx) { + if (ctx->Boolean()) { + return AtomicType::BOOLEAN_TYPE; + } else if (ctx->Number()) { + return AtomicType::INTEGER_TYPE; + } + return AtomicType::STRING_TYPE; } antlrcpp::Any ASTBuilder::visitFunctionDeclaration(StaticScriptParser::FunctionDeclarationContext *ctx) { String name = ctx->Identifier()->getText(); SharedPtrVector params; - SharedPtr returnType; + SharedPtr returnType; if (ctx->parameterList()) { - antlrcpp::Any paramsAny = visitParameterList(ctx->parameterList()); - params = paramsAny.as>(); + params = visitParameterList(ctx->parameterList()).as>(); } if (ctx->typeAnnotation()) { returnType = visitTypeAnnotation(ctx->typeAnnotation()); @@ -152,7 +167,7 @@ antlrcpp::Any ASTBuilder::visitParameterList(StaticScriptParser::ParameterListCo SharedPtrVector params; for (size_t i = 0; i < ctx->Identifier().size(); ++i) { String name = ctx->Identifier(i)->getText(); - SharedPtr type = visitTypeAnnotation(ctx->typeAnnotation(i)); + SharedPtr type = visitTypeAnnotation(ctx->typeAnnotation(i)); SharedPtr param = makeShared(name, type); param->bindChildrenInversely(); params.push_back(param); @@ -164,22 +179,6 @@ antlrcpp::Any ASTBuilder::visitFunctionBody(StaticScriptParser::FunctionBodyCont return visitCompoundStatement(ctx->compoundStatement()); } -antlrcpp::Any ASTBuilder::visitCallExpression(StaticScriptParser::CallExpressionContext *ctx) { - String calleeName = ctx->Identifier()->getText(); - SharedPtrVector args; - if (ctx->argumentList()) { - antlrcpp::Any argsAny = visitArgumentList(ctx->argumentList()); - args = argsAny.as>(); - } - SharedPtr callExpr = makeShared(calleeName, args); - callExpr->bindChildrenInversely(); - return callExpr; -} - -antlrcpp::Any ASTBuilder::visitArgumentList(StaticScriptParser::ArgumentListContext *ctx) { - return visitExpressionList(ctx->expressionList()); -} - antlrcpp::Any ASTBuilder::visitCompoundStatement(StaticScriptParser::CompoundStatementContext *ctx) { SharedPtrVector childStmts; if (ctx->statements()) { @@ -201,15 +200,9 @@ antlrcpp::Any ASTBuilder::visitExpressionStatement(StaticScriptParser::Expressio antlrcpp::Any ASTBuilder::visitExpression(StaticScriptParser::ExpressionContext *ctx) { SharedPtr expr; antlrcpp::Any exprAny; - if (auto literalExprCtx = dynamic_cast(ctx)) { - exprAny = visitLiteralExpr(literalExprCtx); - expr = exprAny.as>(); - } else if (auto idExprCtx = dynamic_cast(ctx)) { - exprAny = visitIdentifierExpr(idExprCtx); - expr = exprAny.as>(); - } else if (auto parenExprCtx = dynamic_cast(ctx)) { - exprAny = visitParenExpr(parenExprCtx); - expr = exprAny.as>(); + if (auto arraySubscriptExprCtx = dynamic_cast(ctx)) { + exprAny = visitArraySubscriptExpr(arraySubscriptExprCtx); + expr = exprAny.as>(); } else if (auto callExprCtx = dynamic_cast(ctx)) { exprAny = visitCallExpr(callExprCtx); expr = exprAny.as>(); @@ -222,21 +215,27 @@ antlrcpp::Any ASTBuilder::visitExpression(StaticScriptParser::ExpressionContext } else if (auto binaryExprCtx = dynamic_cast(ctx)) { exprAny = visitBinaryExpr(binaryExprCtx); expr = exprAny.as>(); + } else if (auto idExprCtx = dynamic_cast(ctx)) { + exprAny = visitIdentifierExpr(idExprCtx); + expr = exprAny.as>(); + } else if (auto literalExprCtx = dynamic_cast(ctx)) { + exprAny = visitLiteralExpr(literalExprCtx); + expr = exprAny.as>(); + } else if (auto parenExprCtx = dynamic_cast(ctx)) { + exprAny = visitParenExpr(parenExprCtx); + expr = exprAny.as>(); } expr->bindChildrenInversely(); return expr; } -antlrcpp::Any ASTBuilder::visitLiteralExpr(StaticScriptParser::LiteralExprContext *ctx) { - return visitLiteral(ctx->literal()); -} - -antlrcpp::Any ASTBuilder::visitIdentifierExpr(StaticScriptParser::IdentifierExprContext *ctx) { - return makeShared(ctx->Identifier()->getText()); -} - -antlrcpp::Any ASTBuilder::visitParenExpr(StaticScriptParser::ParenExprContext *ctx) { - return visitExpression(ctx->expression()); +antlrcpp::Any ASTBuilder::visitArraySubscriptExpr(StaticScriptParser::ArraySubscriptExprContext *ctx) { + SharedPtr baseExpr = visitExpression(ctx->base); + SharedPtrVector indexExprs(ctx->index.size()); + for (size_t i = 0; i < ctx->index.size(); ++i) { + indexExprs[i] = visitExpression(ctx->index[i]).as>(); + } + return makeShared(baseExpr, indexExprs); } antlrcpp::Any ASTBuilder::visitCallExpr(StaticScriptParser::CallExprContext *ctx) { @@ -245,7 +244,7 @@ antlrcpp::Any ASTBuilder::visitCallExpr(StaticScriptParser::CallExprContext *ctx antlrcpp::Any ASTBuilder::visitPostfixUnaryExpr(StaticScriptParser::PostfixUnaryExprContext *ctx) { SharedPtr subExpr = visitExpression(ctx->expression()); - SharedPtr expr= makeShared(ctx->uop->getType(), subExpr); + SharedPtr expr = makeShared(ctx->uop->getType(), subExpr); expr->isPostfix = true; return expr; } @@ -261,6 +260,33 @@ antlrcpp::Any ASTBuilder::visitBinaryExpr(StaticScriptParser::BinaryExprContext return makeShared(ctx->bop->getType(), lhs, rhs); } +antlrcpp::Any ASTBuilder::visitIdentifierExpr(StaticScriptParser::IdentifierExprContext *ctx) { + return makeShared(ctx->Identifier()->getText()); +} + +antlrcpp::Any ASTBuilder::visitLiteralExpr(StaticScriptParser::LiteralExprContext *ctx) { + return visitLiteral(ctx->literal()); +} + +antlrcpp::Any ASTBuilder::visitParenExpr(StaticScriptParser::ParenExprContext *ctx) { + return visitExpression(ctx->expression()); +} + +antlrcpp::Any ASTBuilder::visitCallExpression(StaticScriptParser::CallExpressionContext *ctx) { + String calleeName = ctx->Identifier()->getText(); + SharedPtrVector args; + if (ctx->argumentList()) { + args = visitArgumentList(ctx->argumentList()).as>(); + } + SharedPtr callExpr = makeShared(calleeName, args); + callExpr->bindChildrenInversely(); + return callExpr; +} + +antlrcpp::Any ASTBuilder::visitArgumentList(StaticScriptParser::ArgumentListContext *ctx) { + return visitExpressionList(ctx->expressionList()); +} + antlrcpp::Any ASTBuilder::visitLiteral(StaticScriptParser::LiteralContext *ctx) { SharedPtr literalExpr; if (ctx->BooleanLiteral()) { @@ -269,14 +295,24 @@ antlrcpp::Any ASTBuilder::visitLiteral(StaticScriptParser::LiteralContext *ctx) } else if (ctx->IntegerLiteral()) { int literal = std::stoi(ctx->IntegerLiteral()->getText()); literalExpr = makeShared(literal); - } else { + } else if (ctx->StringLiteral()) { String literal = ctx->StringLiteral()->getText(); literal = literal.substr(1, literal.size() - 2); literalExpr = makeShared(literal); + } else if (ctx->arrayLiteral()) { + literalExpr = visitArrayLiteral(ctx->arrayLiteral()).as>(); } return literalExpr; } +antlrcpp::Any ASTBuilder::visitArrayLiteral(StaticScriptParser::ArrayLiteralContext *ctx) { + SharedPtr arrayLiteralExpr = makeShared(); + if (ctx->expressionList()) { + arrayLiteralExpr->elements = visitExpressionList(ctx->expressionList()).as>(); + } + return arrayLiteralExpr; +} + antlrcpp::Any ASTBuilder::visitSelectionStatement(StaticScriptParser::SelectionStatementContext *ctx) { return visitIfStatement(ctx->ifStatement()); } diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 884f874..afe6e64 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -1,4 +1,4 @@ -set(SRC DeclNode.cpp TypeNode.cpp StmtNode.cpp ExprNode.cpp ModuleNode.cpp ASTBuilder.cpp) +set(SRC DeclNode.cpp StmtNode.cpp ExprNode.cpp ModuleNode.cpp ASTBuilder.cpp) add_library(ast OBJECT ${SRC}) diff --git a/lib/AST/DeclNode.cpp b/lib/AST/DeclNode.cpp index 239b3e6..37bd37e 100644 --- a/lib/AST/DeclNode.cpp +++ b/lib/AST/DeclNode.cpp @@ -7,12 +7,12 @@ VarDeclNode::VarDeclNode() : modifier(VarModifier::Let) {} VarDeclNode::VarDeclNode( VarModifier modifier, String name, - const SharedPtr &type + const SharedPtr &type ) : modifier(modifier), name(std::move(name)), type(type) {} VarDeclNode::VarDeclNode(VarModifier modifier, String name, - const SharedPtr &type, + const SharedPtr &type, const SharedPtr &initVal ) : modifier(modifier), name(std::move(name)), @@ -25,9 +25,6 @@ void VarDeclNode::accept(const SharedPtr &visitor) { void VarDeclNode::bindChildrenInversely() { auto self = shared_from_this(); - if (type) { - type->parent = self; - } if (initVal) { initVal->parent = self; } @@ -39,7 +36,7 @@ bool VarDeclNode::isConstant() const { ParmVarDeclNode::ParmVarDeclNode( const String &name, - const SharedPtr &type + const SharedPtr &type ) : VarDeclNode(VarModifier::Param, name, type) {} void ParmVarDeclNode::accept(const SharedPtr &visitor) { @@ -48,12 +45,12 @@ void ParmVarDeclNode::accept(const SharedPtr &visitor) { SharedPtrMap FunctionDeclNode::getBuiltinFunctions() { SharedPtrMap functions; - SharedPtr integerArg = makeShared("number", BuiltinTypeNode::INTEGER_TYPE); - SharedPtr strArg = makeShared("str", BuiltinTypeNode::STRING_TYPE); + SharedPtr integerArg = makeShared("number", AtomicType::INTEGER_TYPE); + SharedPtr strArg = makeShared("str", AtomicType::STRING_TYPE); SharedPtrVector integerArgs{integerArg}; SharedPtrVector strArgs{strArg}; - functions["ss_integer2string"] = makeShared("ss_integer2string", integerArgs, BuiltinTypeNode::STRING_TYPE, nullptr); - functions["ss_string2integer"] = makeShared("ss_string2integer", strArgs, BuiltinTypeNode::INTEGER_TYPE, nullptr); + functions["ss_integer2string"] = makeShared("ss_integer2string", integerArgs, AtomicType::STRING_TYPE, nullptr); + functions["ss_string2integer"] = makeShared("ss_string2integer", strArgs, AtomicType::INTEGER_TYPE, nullptr); functions["ss_print_integer"] = makeShared("ss_print_integer", integerArgs, nullptr, nullptr); functions["ss_println_integer"] = makeShared("ss_println_integer", integerArgs, nullptr, nullptr); functions["ss_print_string"] = makeShared("ss_print_string", strArgs, nullptr, nullptr); @@ -64,7 +61,7 @@ SharedPtrMap FunctionDeclNode::getBuiltinFunctions() { FunctionDeclNode::FunctionDeclNode( String name, const SharedPtrVector ¶ms, - const SharedPtr &returnType, + const SharedPtr &returnType, const SharedPtr &body ) : name(std::move(name)), params(params), @@ -80,8 +77,5 @@ void FunctionDeclNode::bindChildrenInversely() { for (const SharedPtr ¶m: params) { param->parent = self; } - if (returnType) { - returnType->parent = self; - } body->parent = self; } diff --git a/lib/AST/ExprNode.cpp b/lib/AST/ExprNode.cpp index 7ecbb95..c43fb11 100644 --- a/lib/AST/ExprNode.cpp +++ b/lib/AST/ExprNode.cpp @@ -1,30 +1,41 @@ #include #include "AST/ExprNode.h" -#include "AST/DeclNode.h" #include "Sema/ASTVisitor.h" -ExprNode::ExprNode(const SharedPtr &type) : inferType(type) {} +ExprNode::ExprNode(const SharedPtr &type) : inferType(type) {} -LiteralExprNode::LiteralExprNode(const SharedPtr &type) : ExprNode(type) {} +LiteralExprNode::LiteralExprNode(const SharedPtr &type) : ExprNode(type) {} -IntegerLiteralExprNode::IntegerLiteralExprNode(int literal) : literal(literal), LiteralExprNode(BuiltinTypeNode::INTEGER_TYPE) {} +AtomicLiteralExprNode::AtomicLiteralExprNode(const SharedPtr &type) : LiteralExprNode(type) {} + +IntegerLiteralExprNode::IntegerLiteralExprNode(long literal) : literal(literal), AtomicLiteralExprNode(AtomicType::INTEGER_TYPE) {} void IntegerLiteralExprNode::accept(const SharedPtr &visitor) { visitor->visit(staticPtrCast(shared_from_this())); } -BooleanLiteralExprNode::BooleanLiteralExprNode(bool literal) : literal(literal), LiteralExprNode(BuiltinTypeNode::BOOLEAN_TYPE) {} +BooleanLiteralExprNode::BooleanLiteralExprNode(bool literal) : literal(literal), AtomicLiteralExprNode(AtomicType::BOOLEAN_TYPE) {} void BooleanLiteralExprNode::accept(const SharedPtr &visitor) { visitor->visit(staticPtrCast(shared_from_this())); } -StringLiteralExprNode::StringLiteralExprNode(String literal) : literal(std::move(literal)), LiteralExprNode(BuiltinTypeNode::STRING_TYPE) {} +StringLiteralExprNode::StringLiteralExprNode() : AtomicLiteralExprNode(AtomicType::STRING_TYPE) {} + +StringLiteralExprNode::StringLiteralExprNode(String literal) : literal(std::move(literal)), AtomicLiteralExprNode(AtomicType::STRING_TYPE) {} void StringLiteralExprNode::accept(const SharedPtr &visitor) { visitor->visit(staticPtrCast(shared_from_this())); } +ArrayLiteralExprNode::ArrayLiteralExprNode() : LiteralExprNode(AtomicType::UNKNOWN_TYPE) {} + +ArrayLiteralExprNode::ArrayLiteralExprNode(const SharedPtr &type) : LiteralExprNode(type) {} + +void ArrayLiteralExprNode::accept(const SharedPtr &visitor) { + visitor->visit(staticPtrCast(shared_from_this())); +} + IdentifierExprNode::IdentifierExprNode(String name) : name(std::move(name)) {} void IdentifierExprNode::accept(const SharedPtr &visitor) { @@ -70,6 +81,11 @@ void BinaryOperatorExprNode::bindChildrenInversely() { lhs->parent = rhs->parent = shared_from_this(); } +void ArraySubscriptExprNode::accept(const SharedPtr &visitor) { + visitor->visit(staticPtrCast(shared_from_this())); +} - - +ArraySubscriptExprNode::ArraySubscriptExprNode( + const SharedPtr &baseExpr, + const SharedPtrVector &indexExprs +) : baseExpr(baseExpr), indexExprs(indexExprs) {} diff --git a/lib/AST/TypeNode.cpp b/lib/AST/TypeNode.cpp deleted file mode 100644 index f09ac67..0000000 --- a/lib/AST/TypeNode.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "AST/TypeNode.h" -#include "Sema/ASTVisitor.h" - -TypeNode::TypeNode(TypeKind kind) : kind(kind) {} - -BuiltinTypeNode::BuiltinTypeNode(TypeKind kind) : TypeNode(kind) {} - -bool BuiltinTypeNode::isBuiltin() const { - return true; -} - -void BuiltinTypeNode::accept(const SharedPtr &visitor) { - visitor->visit(staticPtrCast(shared_from_this())); -} diff --git a/lib/CodeGen/Builtin.cpp b/lib/CodeGen/Builtin.cpp index 5dabb27..2dca4b6 100644 --- a/lib/CodeGen/Builtin.cpp +++ b/lib/CodeGen/Builtin.cpp @@ -1,19 +1,42 @@ #include "CodeGen/Builtin.h" -#include "Support/Exception.h" - -void BuiltinString::initialize(LLVMModule &module, LLVMContext &context) { - llvm::SMDiagnostic error; - String stringBitcodeFilename = PROJECT_BINARY_DIR"/builtin/ss_string.bc"; - if (!llvm::sys::fs::exists(stringBitcodeFilename)) { - throw CodeGenException("Not found " + stringBitcodeFilename); - } - std::unique_ptr stringModule = llvm::parseIRFile(stringBitcodeFilename, error, context); - if (!stringModule) { - throw CodeGenException("Parse bitcode file of the string module failed"); - } - if (llvm::Linker::linkModules(module, std::move(stringModule))) { - throw CodeGenException("Link string module failed"); - } + +void Builtin::initialize(LLVMModule &module, LLVMContext &context) { + BuiltinError::linkModule(module, context); + BuiltinString::linkModule(module, context); + BuiltinArray::linkModule(module, context); + BuiltinIO::linkModule(module, context); + + BuiltinError::getTypeAndFunction(module); + BuiltinString::getTypeAndFunction(module); + BuiltinArray::getTypeAndFunction(module); + BuiltinIO::getTypeAndFunction(module); +} + +#define BUILTIN_LINK_MODULE(moduleName) \ + llvm::SMDiagnostic error; \ + String bitcodeFilename = PROJECT_BINARY_DIR"/builtin/ss_"#moduleName".bc"; \ + bool bitcodeExist = llvm::sys::fs::exists(bitcodeFilename); \ + reportOnLinkError(!bitcodeExist, "Not found " + bitcodeFilename); \ + std::unique_ptr bitcodeModule = llvm::parseIRFile(bitcodeFilename, error, context); \ + reportOnLinkError(!bitcodeModule, "Parse bitcode file of the "#moduleName" module failed"); \ + bool linkFailed = llvm::Linker::linkModules(module, std::move(bitcodeModule)); \ + reportOnLinkError(linkFailed, "Link "#moduleName" module failed"); + +void BuiltinError::linkModule(LLVMModule &module, LLVMContext &context) { + BUILTIN_LINK_MODULE(error) +} + +void BuiltinError::getTypeAndFunction(LLVMModule &module) { + llvm::StructType *errStructType = module.getTypeByName("struct.ss_error"); + type = errStructType->getPointerTo(); + exitIfErrorFunc = module.getFunction("ss_exit_if_error"); +} + +void BuiltinString::linkModule(LLVMModule &module, LLVMContext &context) { + BUILTIN_LINK_MODULE(string) +} + +void BuiltinString::getTypeAndFunction(LLVMModule &module) { llvm::StructType *strStructType = module.getTypeByName("struct.ss_string"); type = strStructType->getPointerTo(); createFunc = module.getFunction("ss_string_create"); @@ -30,19 +53,48 @@ void BuiltinString::initialize(LLVMModule &module, LLVMContext &context) { trimFunc = module.getFunction("ss_string_trim"); } -void BuiltinIO::initialize(LLVMModule &module, LLVMContext &context) { - llvm::SMDiagnostic error; - String ioBitcodeFilename = PROJECT_BINARY_DIR"/builtin/ss_io.bc"; - if (!llvm::sys::fs::exists(ioBitcodeFilename)) { - throw CodeGenException("Not found " + ioBitcodeFilename); - } - std::unique_ptr ioModule = llvm::parseIRFile(ioBitcodeFilename, error, context); - if (!ioModule) { - throw CodeGenException("Parse bitcode file of the io module failed"); - } - if (llvm::Linker::linkModules(module, std::move(ioModule))) { - throw CodeGenException("Link io module failed"); - } +void BuiltinArray::linkModule(LLVMModule &module, LLVMContext &context) { + BUILTIN_LINK_MODULE(array) +} + +void BuiltinArray::getTypeAndFunction(LLVMModule &module) { + llvm::StructType *arrStructType = module.getTypeByName("struct.ss_array"); + type = arrStructType->getPointerTo(); + createIntegerArrayFunc = module.getFunction("ss_array_create_integer_array"); + createBooleanArrayFunc = module.getFunction("ss_array_create_boolean_array"); + createStringArrayFunc = module.getFunction("ss_array_create_string_array"); + createArrayArrayFunc = module.getFunction("ss_array_create_array_array"); + createIntegerArrayWithLiteralFunc = module.getFunction("ss_array_create_integer_array_with_literal"); + createBooleanArrayWithLiteralFunc = module.getFunction("ss_array_create_boolean_array_with_literal"); + createStringArrayWithLiteralFunc = module.getFunction("ss_array_create_string_array_with_literal"); + createArrayArrayWithLiteralFunc = module.getFunction("ss_array_create_array_array_with_literal"); + deleteFunc = module.getFunction("ss_array_delete"); + getSizeFunc = module.getFunction("ss_array_get_size"); + isNDArrayFunc = module.getFunction("ss_array_is_nd_array"); + pushIntegerFunc = module.getFunction("ss_array_push_integer"); + pushBooleanFunc = module.getFunction("ss_array_push_boolean"); + pushStringFunc = module.getFunction("ss_array_push_string"); + pushArrayFunc = module.getFunction("ss_array_push_array"); + popIntegerFunc = module.getFunction("ss_array_pop_integer"); + popBooleanFunc = module.getFunction("ss_array_pop_boolean"); + popStringFunc = module.getFunction("ss_array_pop_string"); + popArrayFunc = module.getFunction("ss_array_pop_array"); + getIntegerFunc = module.getFunction("ss_array_get_integer"); + getBooleanFunc = module.getFunction("ss_array_get_boolean"); + getStringFunc = module.getFunction("ss_array_get_string"); + getArrayFunc = module.getFunction("ss_array_get_array"); + setIntegerFunc = module.getFunction("ss_array_set_integer"); + setBooleanFunc = module.getFunction("ss_array_set_boolean"); + setStringFunc = module.getFunction("ss_array_set_string"); + setArrayFunc = module.getFunction("ss_array_set_array"); + sliceFunc = module.getFunction("ss_array_slice"); +} + +void BuiltinIO::linkModule(LLVMModule &module, LLVMContext &context) { + BUILTIN_LINK_MODULE(io) +} + +void BuiltinIO::getTypeAndFunction(LLVMModule &module) { integer2stringFunc = module.getFunction("ss_integer2string"); string2integerFunc = module.getFunction("ss_string2integer"); printIntegerFunc = module.getFunction("ss_print_integer"); @@ -50,8 +102,3 @@ void BuiltinIO::initialize(LLVMModule &module, LLVMContext &context) { printStringFunc = module.getFunction("ss_print_string"); printlnStringFunc = module.getFunction("ss_println_string"); } - -void Builtin::initialize(LLVMModule &module, LLVMContext &context) { - BuiltinString::initialize(module, context); - BuiltinIO::initialize(module, context); -} diff --git a/lib/CodeGen/DeclCodeGen.cpp b/lib/CodeGen/DeclCodeGen.cpp index 9402a44..4e97e21 100644 --- a/lib/CodeGen/DeclCodeGen.cpp +++ b/lib/CodeGen/DeclCodeGen.cpp @@ -8,19 +8,20 @@ void IRGenerator::visit(const SharedPtr &varDecl) { bool hasInitVal = bool(varDecl->initVal); // 区分全局变量和局部变量 if (varDecl->scope->isTopLevel()) { - // 变量初始值为字面量 bool isLiteralInit = bool(dynPtrCast(varDecl->initVal)); - // 是否为字符串类型变量 - bool isStringVar = varDecl->type == BuiltinTypeNode::STRING_TYPE; + bool isBoolean = varDecl->type->equals(AtomicType::BOOLEAN_TYPE); + bool isInteger = varDecl->type->equals(AtomicType::INTEGER_TYPE); + bool isStringVar = varDecl->type->equals(AtomicType::STRING_TYPE); + bool isArrayVar = varDecl->type->isArray(); llvm::Constant *initializer; - if (isStringVar) { + if (isStringVar || isArrayVar) { initializer = llvm::ConstantPointerNull::getNullValue(type); + } else if (isLiteralInit) { + // 初始值为布尔和整数字面量 + initializer = llvm::cast(varDecl->initVal->code); } else { - if (isLiteralInit) { - initializer = LLVMCast(varDecl->initVal->code); - } else { - initializer = llvm::ConstantInt::get(type, 0); - } + // 初始值为布尔和整数类型的表达式 + initializer = llvm::ConstantInt::get(type, 0); } auto *gVar = new LLVMGlobalVariable( *llvmModule, @@ -31,12 +32,12 @@ void IRGenerator::visit(const SharedPtr &varDecl) { varDecl->name ); uint64_t alignment = 8; - if (varDecl->type == BuiltinTypeNode::BOOLEAN_TYPE) { + if (isBoolean) { alignment = 1; } gVar->setAlignment(llvm::MaybeAlign(alignment)); - // 如果为字符串或者有非字面量的初始值 - if (isStringVar || (hasInitVal && !isLiteralInit)) { + // 在main函数中store字符串和数组的指针值或者非字面量的表达式值 + if (isStringVar || isArrayVar || (hasInitVal && !isLiteralInit)) { llvmIRBuilder.CreateStore(varDecl->initVal->code, gVar); } varDecl->code = gVar; @@ -84,5 +85,5 @@ void IRGenerator::visit(const SharedPtr &funcDecl) { llvmIRBuilder.CreateRetVoid(); } } - LLVMVerifyFunction(*func); + llvm::verifyFunction(*func); } \ No newline at end of file diff --git a/lib/CodeGen/ExprCodeGen.cpp b/lib/CodeGen/ExprCodeGen.cpp index 0997f68..539f3cb 100644 --- a/lib/CodeGen/ExprCodeGen.cpp +++ b/lib/CodeGen/ExprCodeGen.cpp @@ -20,6 +20,167 @@ void IRGenerator::visit(const SharedPtr &strLiteralExpr) strLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinString::createFunc, argLiteral); } +void IRGenerator::visit(const SharedPtr &arrayLiteralExpr) { + SharedPtr arrayType = staticPtrCast(arrayLiteralExpr->inferType); + SharedPtr elementType = arrayType->getElementType(); + + LLVMValue *errorAlloca = llvmIRBuilder.CreateAlloca(BuiltinError::type); + llvmIRBuilder.CreateStore(llvm::ConstantPointerNull::get(BuiltinError::type), errorAlloca); + if (arrayLiteralExpr->elements.empty()) { + LLVMFunction *createFunc = nullptr; + if (elementType->equals(AtomicType::BOOLEAN_TYPE)) { + createFunc = BuiltinArray::createBooleanArrayFunc; + } else if (elementType->equals(AtomicType::INTEGER_TYPE)) { + createFunc = BuiltinArray::createIntegerArrayFunc; + } else if (elementType->equals(AtomicType::STRING_TYPE)) { + createFunc = BuiltinArray::createStringArrayFunc; + } else if (elementType->isArray()) { + createFunc = BuiltinArray::createArrayArrayFunc; + } + arrayLiteralExpr->code = llvmIRBuilder.CreateCall(createFunc, errorAlloca); + } else { + // TODO: 优化策略: 当size < 7时, 使用memset+store替代memcpy + size_t size = arrayLiteralExpr->elements.size(); + LLVMValue *sizeLoad = llvmIRBuilder.getInt64(size); + LLVMFunction *memcpyFunc = llvm::Intrinsic::getDeclaration( + llvmModule.get(), + llvm::Intrinsic::memcpy, + Vector{ + llvmIRBuilder.getInt8PtrTy(), + llvmIRBuilder.getInt8PtrTy(), + llvmIRBuilder.getInt64Ty() + } + ); + reportOnCodeGenError(!memcpyFunc, "Not found llvm.memcpy intrinsic function"); + if (elementType->equals(AtomicType::BOOLEAN_TYPE)) { + // 避免使用vector + Vector literalVector(size); + for (size_t i = 0; i < size; ++i) { + literalVector[i] = staticPtrCast(arrayLiteralExpr->elements[i])->literal; + } + llvm::Constant *literalConstants = llvm::ConstantDataArray::get(llvmContext, literalVector); + auto *globalLiteralConstants = new LLVMGlobalVariable( + *llvmModule, + literalConstants->getType(), + true, + LLVMGlobalValue::PrivateLinkage, + literalConstants + ); + LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( + llvm::ArrayType::get(llvmIRBuilder.getInt8Ty(), size) + ); + LLVMValue *castedLiteralListLoad = llvmIRBuilder.CreateBitCast(literalListAlloca, llvmIRBuilder.getInt8PtrTy()); + LLVMValue *castedLiteralConstants = llvmIRBuilder.CreateBitCast(globalLiteralConstants, llvmIRBuilder.getInt8PtrTy()); + Vector memcpyArgs{ + castedLiteralListLoad, + castedLiteralConstants, + llvmIRBuilder.getInt64(sizeof(unsigned char) * size), + llvmIRBuilder.getFalse() + }; + llvmIRBuilder.CreateCall(memcpyFunc, memcpyArgs); + LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( + literalListAlloca, + Vector{ + llvmIRBuilder.getInt64(0), + llvmIRBuilder.getInt64(0) + } + ); + Vector createArgs{literalListPtr, sizeLoad, errorAlloca}; + arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::createBooleanArrayWithLiteralFunc, createArgs); + } else if (elementType->equals(AtomicType::INTEGER_TYPE)) { + Vector literalVector(size); + for (size_t i = 0; i < size; ++i) { + literalVector[i] = staticPtrCast(arrayLiteralExpr->elements[i])->literal; + } + llvm::Constant *literalConstants = llvm::ConstantDataArray::get(llvmContext, literalVector); + auto *globalLiteralConstants = new LLVMGlobalVariable( + *llvmModule, + literalConstants->getType(), + true, + LLVMGlobalValue::PrivateLinkage, + literalConstants + ); + LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( + llvm::ArrayType::get(llvmIRBuilder.getInt64Ty(), size) + ); + LLVMValue *castedLiteralListLoad = llvmIRBuilder.CreateBitCast(literalListAlloca, llvmIRBuilder.getInt8PtrTy()); + LLVMValue *castedLiteralConstants = llvmIRBuilder.CreateBitCast(globalLiteralConstants, llvmIRBuilder.getInt8PtrTy()); + Vector memcpyArgs{ + castedLiteralListLoad, + castedLiteralConstants, + llvmIRBuilder.getInt64(sizeof(long) * size), + llvmIRBuilder.getFalse() + }; + llvmIRBuilder.CreateCall(memcpyFunc, memcpyArgs); + LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( + literalListAlloca, + Vector{ + llvmIRBuilder.getInt64(0), + llvmIRBuilder.getInt64(0) + } + ); + Vector createArgs{literalListPtr, sizeLoad, errorAlloca}; + arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::createIntegerArrayWithLiteralFunc, createArgs); + } else if (elementType->equals(AtomicType::STRING_TYPE)) { + ASTVisitor::visit(arrayLiteralExpr); + + LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( + llvm::ArrayType::get(BuiltinString::type, size) + ); + + for (size_t i = 0; i < size; ++i) { + LLVMValue *literalItemPtr = llvmIRBuilder.CreateInBoundsGEP( + literalListAlloca, Vector{ + llvmIRBuilder.getInt64(0), + llvmIRBuilder.getInt64(i) + } + ); + llvmIRBuilder.CreateStore(arrayLiteralExpr->elements[i]->code, literalItemPtr); + } + + LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( + literalListAlloca, + Vector{ + llvmIRBuilder.getInt64(0), + llvmIRBuilder.getInt64(0) + } + ); + + Vector createArgs{literalListPtr, sizeLoad, errorAlloca}; + arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::createStringArrayWithLiteralFunc, createArgs); + } else if (elementType->isArray()) { + ASTVisitor::visit(arrayLiteralExpr); + + LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( + llvm::ArrayType::get(BuiltinArray::type, size) + ); + + for (size_t i = 0; i < size; ++i) { + LLVMValue *literalItemPtr = llvmIRBuilder.CreateInBoundsGEP( + literalListAlloca, Vector{ + llvmIRBuilder.getInt64(0), + llvmIRBuilder.getInt64(i) + } + ); + llvmIRBuilder.CreateStore(arrayLiteralExpr->elements[i]->code, literalItemPtr); + } + + LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( + literalListAlloca, + Vector{ + llvmIRBuilder.getInt64(0), + llvmIRBuilder.getInt64(0) + } + ); + + Vector createArgs{literalListPtr, sizeLoad, errorAlloca}; + arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::createArrayArrayWithLiteralFunc, createArgs); + } + } + LLVMValue *errorLoad = llvmIRBuilder.CreateLoad(errorAlloca); + llvmIRBuilder.CreateCall(BuiltinError::exitIfErrorFunc, errorLoad); +} + void IRGenerator::visit(const SharedPtr &varExpr) { // 按右值处理 varExpr->code = llvmIRBuilder.CreateLoad(varExpr->refVarDecl->code); @@ -28,11 +189,9 @@ void IRGenerator::visit(const SharedPtr &varExpr) { void IRGenerator::visit(const SharedPtr &callExpr) { ASTVisitor::visit(callExpr); llvm::Function *calleeFunc = llvmModule->getFunction(callExpr->calleeName); - if (!calleeFunc) { - throw CodeGenException("没有找到函数"); - } + reportOnCodeGenError(!calleeFunc, "Function '" + callExpr->calleeName + "' is not found"); if (calleeFunc->arg_size() != callExpr->args.size()) { - throw CodeGenException("函数参数传递不正确"); + reportCodeGenError("The number of parameters of function declaration and arguments of call expression does not match"); } Vector argsV; for (size_t i = 0, e = callExpr->args.size(); i != e; ++i) { @@ -49,8 +208,7 @@ void IRGenerator::visit(const SharedPtr &uopExpr) { switch (uopExpr->opCode) { case StaticScriptLexer::PlusPlus: case StaticScriptLexer::MinusMinus: { - const SharedPtr &subVarExpr = staticPtrCast(subExpr); - LLVMValue *value = llvmIRBuilder.CreateLoad(subVarExpr->refVarDecl->code); + LLVMValue *value = subExpr->code; LLVMValue *newValue; LLVMConstantInt *one = llvmIRBuilder.getInt64(1); @@ -59,13 +217,12 @@ void IRGenerator::visit(const SharedPtr &uopExpr) { } else { newValue = llvmIRBuilder.CreateNSWSub(value, one); } - if (uopExpr->isPostfix) { - llvmIRBuilder.CreateStore(newValue, subVarExpr->refVarDecl->code); - uopExpr->code = value; - } else { + if (const auto &subVarExpr = dynPtrCast(subExpr)) { llvmIRBuilder.CreateStore(newValue, subVarExpr->refVarDecl->code); - uopExpr->code = newValue; + } else if (const auto &asExpr = dynPtrCast(subExpr)) { + setArrayElement(asExpr, newValue); } + uopExpr->code = uopExpr->isPostfix ? value : newValue; break; } case StaticScriptLexer::Not: { @@ -90,13 +247,16 @@ void IRGenerator::visit(const SharedPtr &uopExpr) { void IRGenerator::visit(const SharedPtr &bopExpr) { unsigned int bopCode = bopExpr->opCode; + + /// 平凡赋值: lhs是左值, rhs是右值 + /// 符合赋值: lhs是左值也是右值, rhs是右值; `lhs op= rhs` 相当于 `lhs = lhs op rhs` if (bopCode == StaticScriptLexer::Assign) { bopExpr->rhs->accept(shared_from_this()); } else { ASTVisitor::visit(bopExpr); } - const SharedPtr &type = bopExpr->lhs->inferType; + const SharedPtr &type = bopExpr->lhs->inferType; LLVMValue *lhsCode = bopExpr->lhs->code; LLVMValue *rhsCode = bopExpr->rhs->code; LLVMValue *targetCode = nullptr; @@ -104,10 +264,10 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { switch (bopCode) { case StaticScriptLexer::Plus: case StaticScriptLexer::PlusAssign: { - if (type == BuiltinTypeNode::STRING_TYPE) { + if (type->equals(AtomicType::STRING_TYPE)) { Vector argsV{lhsCode, rhsCode}; targetCode = llvmIRBuilder.CreateCall(BuiltinString::concatFunc, argsV); - } else if (type == BuiltinTypeNode::INTEGER_TYPE) { + } else if (type->equals(AtomicType::INTEGER_TYPE)) { targetCode = llvmIRBuilder.CreateNSWAdd(lhsCode, rhsCode); } break; @@ -184,7 +344,7 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { break; } case StaticScriptLexer::Equals: { - if (type == BuiltinTypeNode::STRING_TYPE) { + if (type->equals(AtomicType::STRING_TYPE)) { Vector argsV{lhsCode, rhsCode}; LLVMValue *relationship = llvmIRBuilder.CreateCall(BuiltinString::equalsFunc, argsV); targetCode = llvmIRBuilder.CreateICmpEQ(relationship, llvmIRBuilder.getInt64(0)); @@ -194,7 +354,7 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { break; } case StaticScriptLexer::NotEquals: { - if (type == BuiltinTypeNode::STRING_TYPE) { + if (type->equals(AtomicType::STRING_TYPE)) { Vector argsV{lhsCode, rhsCode}; LLVMValue *relationship = llvmIRBuilder.CreateCall(BuiltinString::equalsFunc, argsV); targetCode = llvmIRBuilder.CreateICmpNE(relationship, llvmIRBuilder.getInt64(0)); @@ -207,15 +367,42 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { break; } if (bopCode >= StaticScriptLexer::Assign && bopCode <= StaticScriptLexer::OrAssign) { - // 在语义阶段保证lhs类型为IdentifierExprNode + // 在语义阶段保证lhs类型为IdentifierExprNode或者ArraySubscriptExprNode // 按左值处理 - SharedPtr leftVarExpr = staticPtrCast(bopExpr->lhs); if (bopCode == StaticScriptLexer::Assign) { - llvmIRBuilder.CreateStore(rhsCode, leftVarExpr->refVarDecl->code); - targetCode = llvmIRBuilder.CreateLoad(leftVarExpr->refVarDecl->code); - } else { + targetCode = rhsCode; + } + if (const auto &leftVarExpr = dynPtrCast(bopExpr->lhs)) { llvmIRBuilder.CreateStore(targetCode, leftVarExpr->refVarDecl->code); + } else if (const auto &leftAsExpr = dynPtrCast(bopExpr->lhs)) { + setArrayElement(leftAsExpr, targetCode); } } bopExpr->code = targetCode; +} + +void IRGenerator::visit(const SharedPtr &asExpr) { + ASTVisitor::visit(asExpr); + SharedPtr iterType = asExpr->baseExpr->inferType; + LLVMValue *iterCode = asExpr->baseExpr->code; + LLVMValue *errorAlloca = llvmIRBuilder.CreateAlloca(BuiltinError::type); + llvmIRBuilder.CreateStore(llvm::ConstantPointerNull::get(BuiltinError::type), errorAlloca); + for (const SharedPtr &indexExpr: asExpr->indexExprs) { + LLVMFunction *getFunc = nullptr; + SharedPtr iterEleType = staticPtrCast(iterType)->getElementType(); + if (iterEleType->equals(AtomicType::BOOLEAN_TYPE)) { + getFunc = BuiltinArray::getBooleanFunc; + } else if (iterEleType->equals(AtomicType::INTEGER_TYPE)) { + getFunc = BuiltinArray::getIntegerFunc; + } else if (iterEleType->equals(AtomicType::STRING_TYPE)) { + getFunc = BuiltinArray::getStringFunc; + } else if (iterEleType->isArray()) { + getFunc = BuiltinArray::getArrayFunc; + } + iterType = iterEleType; + iterCode = llvmIRBuilder.CreateCall(getFunc, Vector{iterCode, indexExpr->code, errorAlloca}); + LLVMValue *errorLoad = llvmIRBuilder.CreateLoad(errorAlloca); + llvmIRBuilder.CreateCall(BuiltinError::exitIfErrorFunc, errorLoad); + } + asExpr->code = iterCode; } \ No newline at end of file diff --git a/lib/CodeGen/IRGenerator.cpp b/lib/CodeGen/IRGenerator.cpp index ca1e8c7..794c44f 100644 --- a/lib/CodeGen/IRGenerator.cpp +++ b/lib/CodeGen/IRGenerator.cpp @@ -7,7 +7,7 @@ void IRGenerator::resolve(const SharedPtr &module) { llvmModule = makeShared(module->filename, llvmContext); Builtin::initialize(*llvmModule, llvmContext); ASTVisitor::resolve(module); - LLVMVerifyModule(*llvmModule); + llvm::verifyModule(*llvmModule); runPasses(*llvmModule); } @@ -20,17 +20,21 @@ void IRGenerator::visit(const SharedPtr &module) { ASTVisitor::visit(module); setFuncInsertPoint(mainFn); llvmIRBuilder.CreateRet(llvmIRBuilder.getInt64(0)); - LLVMVerifyFunction(*mainFn); + llvm::verifyFunction(*mainFn); } -LLVMType *IRGenerator::getType(const SharedPtr &builtinType) { +LLVMType *IRGenerator::getType(const SharedPtr &inputType) { LLVMType *type = llvmIRBuilder.getVoidTy(); - if (builtinType == BuiltinTypeNode::BOOLEAN_TYPE) { - type = llvmIRBuilder.getInt1Ty(); - } else if (builtinType == BuiltinTypeNode::INTEGER_TYPE) { - type = llvmIRBuilder.getInt64Ty(); - } else if (builtinType == BuiltinTypeNode::STRING_TYPE) { - type = BuiltinString::type; + if (inputType) { + if (inputType->equals(AtomicType::BOOLEAN_TYPE)) { + type = llvmIRBuilder.getInt1Ty(); + } else if (inputType->equals(AtomicType::INTEGER_TYPE)) { + type = llvmIRBuilder.getInt64Ty(); + } else if (inputType->equals(AtomicType::STRING_TYPE)) { + type = BuiltinString::type; + } else if (inputType->isArray()) { + type = BuiltinArray::type; + } } return type; } @@ -59,3 +63,37 @@ void IRGenerator::emitBranch(LLVMBasicBlock *targetBB) { } llvmIRBuilder.ClearInsertionPoint(); } + +void IRGenerator::setArrayElement(const SharedPtr &asExpr, LLVMValue *valueCode) { + size_t indexExprsSize = asExpr->indexExprs.size(); + SharedPtr iterType = asExpr->baseExpr->inferType; + LLVMValue *iterBaseCode = asExpr->baseExpr->code; + LLVMValue *errorAlloca = llvmIRBuilder.CreateAlloca(BuiltinError::type); + llvmIRBuilder.CreateStore(llvm::ConstantPointerNull::get(BuiltinError::type), errorAlloca); + for (size_t i = 0; i < indexExprsSize; ++i) { + LLVMFunction *accessFunc = nullptr; + bool accessByGet = i < indexExprsSize - 1; + const SharedPtr &iterEleType = staticPtrCast(iterType)->getElementType(); + if (iterEleType->equals(AtomicType::BOOLEAN_TYPE)) { + accessFunc = accessByGet ? BuiltinArray::getBooleanFunc : BuiltinArray::setBooleanFunc; + } else if (iterEleType->equals(AtomicType::INTEGER_TYPE)) { + accessFunc = accessByGet ? BuiltinArray::getIntegerFunc : BuiltinArray::setIntegerFunc; + } else if (iterEleType->equals(AtomicType::STRING_TYPE)) { + accessFunc = accessByGet ? BuiltinArray::getStringFunc : BuiltinArray::setStringFunc; + } else if (iterEleType->isArray()) { + accessFunc = accessByGet ? BuiltinArray::getArrayFunc : BuiltinArray::setArrayFunc; + } + iterType = iterEleType; + Vector accessArgs{ + iterBaseCode, + asExpr->indexExprs[i]->code, + errorAlloca + }; + if (!accessByGet) { + accessArgs.insert(accessArgs.begin() + 2, valueCode); + } + iterBaseCode = llvmIRBuilder.CreateCall(accessFunc, accessArgs); + LLVMValue *errorLoad = llvmIRBuilder.CreateLoad(errorAlloca); + llvmIRBuilder.CreateCall(BuiltinError::exitIfErrorFunc, errorLoad); + } +} diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt index a6de52e..8760232 100644 --- a/lib/Driver/CMakeLists.txt +++ b/lib/Driver/CMakeLists.txt @@ -4,4 +4,6 @@ set(SRC Driver.cpp) add_library(driver OBJECT ${SRC}) -add_dependencies(driver optimization) \ No newline at end of file +#add_dependencies(driver optimization) + +add_dependencies(driver sema) \ No newline at end of file diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 5cdce93..0819369 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1,7 +1,4 @@ #include -#include -#include -#include #include "StaticScriptLexer.h" #include "StaticScriptParser.h" #include "AST/ASTBuilder.h" @@ -11,10 +8,11 @@ #include "Sema/SemanticValidator.h" #include "CodeGen/IRGenerator.h" #include "Optimization/Optimizer.h" +#include "Support/Alias.h" #include "Support/LLVM.h" -#include "Support/Exception.h" +#include "Support/Error.h" -int drive(int argc, char **argv) { +int main(int argc, char **argv) { llvm::InitLLVM X(argc, argv); llvm::cl::getRegisteredOptions().clear(); llvm::cl::OptionCategory generalOptsCat("General Options"); @@ -77,12 +75,10 @@ int drive(int argc, char **argv) { } if (!llvm::sys::fs::exists(inputFilename)) { - throw DriverException("Not found input file"); + reportDriverError("Not found input file"); } std::ifstream fin(inputFilename.data()); - if (!fin) { - throw DriverException("Open input file failed"); - } + reportOnDriverError(!fin, "Open input file failed"); antlr4::ANTLRInputStream inputStream(fin); StaticScriptLexer lexer(&inputStream); @@ -130,9 +126,7 @@ int drive(int argc, char **argv) { std::error_code ec; llvm::raw_fd_ostream dest(outputFilename, ec, llvm::sys::fs::OF_None); - if (ec) { - throw DriverException("Could not create file: " + ec.message()); - } + reportOnDriverError(bool(ec),"Could not create file: " + ec.message()); if (emitLLVM) { dest << llvmModule; @@ -140,7 +134,7 @@ int drive(int argc, char **argv) { llvm::legacy::PassManager pass; auto fileType = llvm::CGFT_ObjectFile; if (targetMachine->addPassesToEmitFile(pass, dest, nullptr, fileType)) { - throw DriverException("TargetMachine can't emit a object file"); + reportDriverError("TargetMachine can't emit a object file"); } pass.run(llvmModule); } @@ -148,19 +142,4 @@ int drive(int argc, char **argv) { dest.close(); fin.close(); return 0; -} - - -int main(int argc, char **argv) { - try { - return drive(argc, argv); - } catch (const SemanticException &e) { - llvm::errs() << "[Semantic Exception] " << e.what() << '\n'; - } catch (const CodeGenException &e) { - llvm::errs() << "[CodeGen Exception] " << e.what() << '\n'; - } catch (const DriverException &e) { - llvm::errs() << "[Driver Exception] " << e.what() << '\n'; - } catch (const std::exception &e) { - llvm::errs() << "[Unknown Exception] " << e.what() << '\n'; - } } \ No newline at end of file diff --git a/lib/Entity/CMakeLists.txt b/lib/Entity/CMakeLists.txt index ebe5a94..a50c118 100644 --- a/lib/Entity/CMakeLists.txt +++ b/lib/Entity/CMakeLists.txt @@ -1,3 +1,3 @@ -set(SRC Scope.cpp) +set(SRC Type.cpp Scope.cpp) add_library(entity OBJECT ${SRC}) \ No newline at end of file diff --git a/lib/Entity/Type.cpp b/lib/Entity/Type.cpp new file mode 100644 index 0000000..b38d6ae --- /dev/null +++ b/lib/Entity/Type.cpp @@ -0,0 +1,85 @@ +#include "Entity/Type.h" + +bool Type::equals(const SharedPtr &rhs) const { + // 如果rhs为nullptr, 直接返回false + if (!rhs) { + return false; + } + // 如果比较的两类型不属于同类, 直接返回false + if (isAtomic() && rhs->isArray() || isArray() && rhs->isAtomic()) { + return false; + } + if (SharedPtr atomicType = dynPtrCast(rhs)) { + const auto *type = dynamic_cast(this); + return type->equals(atomicType); + } else { + SharedPtr arrayType = dynPtrCast(rhs); + const auto *type = dynamic_cast(this); + return type->equals(arrayType); + } +} + +AtomicType::AtomicType(AtomicTypeKind kind) : kind(kind) {} + +bool AtomicType::isAtomic() const { + return true; +} + +bool AtomicType::isArray() const { + return false; +} + +bool AtomicType::isUnknown() const { + return kind == AtomicTypeKind::Unknown; +} + +AtomicTypeKind AtomicType::getKind() const { + return kind; +} + +bool AtomicType::equals(const SharedPtr &rhs) const { + return kind == rhs->kind; +} + +SharedPtr ArrayType::createNDArrayType(const SharedPtr &atomicType, size_t depth) { + reportOnTypeError(depth == 0, "Dimension of multidimensional array must be positive"); + SharedPtr type = atomicType; + for (size_t i = 0; i < depth; ++i) { + type = makeShared(type); + } + return staticPtrCast(type); +} + +ArrayType::ArrayType(const SharedPtr &elementType) : elementType(elementType) { + if (elementType->isArray()) { + depth = staticPtrCast(elementType)->depth + 1; + } +} + +bool ArrayType::isAtomic() const { + return false; +} + +bool ArrayType::isArray() const { + return true; +} + +bool ArrayType::isUnknown() const { + return elementType->isUnknown(); +} + +const SharedPtr &ArrayType::getElementType() const { + return elementType; +} + +void ArrayType::setElementType(const SharedPtr &eleType) { + elementType = eleType; +} + +bool ArrayType::equals(const SharedPtr &rhs) const { + return elementType->equals(rhs->elementType); +} + +size_t ArrayType::getDepth() const { + return depth; +} diff --git a/lib/Sema/ASTVisitor.cpp b/lib/Sema/ASTVisitor.cpp index ad33fd0..765ee8f 100644 --- a/lib/Sema/ASTVisitor.cpp +++ b/lib/Sema/ASTVisitor.cpp @@ -1,6 +1,6 @@ #include "Sema/ASTVisitor.h" -void ASTVisitor::resolve(const SharedPtr &module) { +void ASTVisitor::resolve(const SharedPtr &module) { module->accept(shared_from_this()); } @@ -10,10 +10,8 @@ void ASTVisitor::visit(const SharedPtr &module) { } } -void ASTVisitor::visit(const SharedPtr &builtinType) {} - void ASTVisitor::visit(const SharedPtr &varDecl) { - if(varDecl->initVal) { + if (varDecl->initVal) { varDecl->initVal->accept(shared_from_this()); } } @@ -33,6 +31,12 @@ void ASTVisitor::visit(const SharedPtr &intLiteralExpr) void ASTVisitor::visit(const SharedPtr &strLiteralExpr) {} +void ASTVisitor::visit(const SharedPtr &arrayLiteralExpr) { + for (const SharedPtr &expr : arrayLiteralExpr->elements) { + expr->accept(shared_from_this()); + } +} + void ASTVisitor::visit(const SharedPtr &varExpr) {} void ASTVisitor::visit(const SharedPtr &callExpr) { @@ -50,6 +54,13 @@ void ASTVisitor::visit(const SharedPtr &bopExpr) { bopExpr->rhs->accept(shared_from_this()); } +void ASTVisitor::visit(const SharedPtr &asExpr) { + asExpr->baseExpr->accept(shared_from_this()); + for (const SharedPtr &indexExpr:asExpr->indexExprs) { + indexExpr->accept(shared_from_this()); + } +} + void ASTVisitor::visit(const SharedPtr &exprStmt) { exprStmt->expr->accept(shared_from_this()); } diff --git a/lib/Sema/ReferenceResolver.cpp b/lib/Sema/ReferenceResolver.cpp index 3fa68c1..109bab0 100644 --- a/lib/Sema/ReferenceResolver.cpp +++ b/lib/Sema/ReferenceResolver.cpp @@ -8,7 +8,7 @@ void ReferenceResolver::visit(const SharedPtr &module) { void ReferenceResolver::visit(const SharedPtr &varDecl) { if (getCurrentScope()->hasVariable(varDecl->name)) { - throw SemanticException("变量重复定义: " + varDecl->name); + reportSemanticError("Variable repeated definition: " + varDecl->name); } else { getCurrentScope()->addVariable(varDecl->name, varDecl); } @@ -17,7 +17,7 @@ void ReferenceResolver::visit(const SharedPtr &varDecl) { void ReferenceResolver::visit(const SharedPtr ¶mVarDecl) { if (getCurrentScope()->hasVariable(paramVarDecl->name)) { - throw SemanticException("变量重复定义: " + paramVarDecl->name); + reportSemanticError("Repeated definition of function parameters: " + paramVarDecl->name); } else { getCurrentScope()->addVariable(paramVarDecl->name, paramVarDecl); } @@ -26,7 +26,7 @@ void ReferenceResolver::visit(const SharedPtr ¶mVarDecl) { void ReferenceResolver::visit(const SharedPtr &funcDecl) { const SharedPtr &topLevelScope = dynPtrCast(getCurrentScope()); if (topLevelScope->hasFunction(funcDecl->name)) { - throw SemanticException("函数重复定义: " + funcDecl->name); + reportSemanticError("Function repeated definition: " + funcDecl->name); } else { topLevelScope->addFunction(funcDecl->name, funcDecl); } @@ -40,7 +40,7 @@ void ReferenceResolver::visit(const SharedPtr &varExpr) { if (varDecl) { varExpr->refVarDecl = varDecl; } else { - throw SemanticException("变量未定义: " + varExpr->name); + reportSemanticError("Variable undefined: " + varExpr->name); } } @@ -49,7 +49,7 @@ void ReferenceResolver::visit(const SharedPtr &callExpr) { if (funcDecl) { callExpr->refFuncDecl = funcDecl; } else { - throw SemanticException("函数未定义: " + callExpr->calleeName); + reportSemanticError("Function undefined: " + callExpr->calleeName); } ASTVisitor::visit(callExpr); } @@ -89,7 +89,7 @@ void ReferenceResolver::visit(const SharedPtr &returnStmt) { funcDecl->refReturnStmt = returnStmt; } else { // 这里本应该是语义合法性检查器的责任, 但是类型检查器要使用到refFuncDecl信息,所以提前到这里做 - throw SemanticException("return语句不在函数内"); + reportSemanticError("The return statement muse be in a function"); } ASTVisitor::visit(returnStmt); } diff --git a/lib/Sema/ScopeScanner.cpp b/lib/Sema/ScopeScanner.cpp index 1ad7e0f..13d8c96 100644 --- a/lib/Sema/ScopeScanner.cpp +++ b/lib/Sema/ScopeScanner.cpp @@ -22,7 +22,7 @@ void ScopeScanner::visit(const SharedPtr &funcDecl) { if (topLevelScope) { funcDecl->scope = topLevelScope; } else { - throw SemanticException("不允许函数嵌套"); + reportSemanticError("Nested functions are not allowed"); } // 函数参数作用域: 在外层作作用域与内层作用域之间的辅助作用域 SharedPtr paramScope = LocalScope::create(topLevelScope); @@ -45,6 +45,11 @@ void ScopeScanner::visit(const SharedPtr &strLiteralExpr) strLiteralExpr->scope = getCurrentScope(); } +void ScopeScanner::visit(const SharedPtr &arrayLiteralExpr) { + arrayLiteralExpr->scope = getCurrentScope(); + ASTVisitor::visit(arrayLiteralExpr); +} + void ScopeScanner::visit(const SharedPtr &varExpr) { varExpr->scope = getCurrentScope(); } @@ -64,6 +69,11 @@ void ScopeScanner::visit(const SharedPtr &bopExpr) { ASTVisitor::visit(bopExpr); } +void ScopeScanner::visit(const SharedPtr &asExpr) { + asExpr->scope = getCurrentScope(); + ASTVisitor::visit(asExpr); +} + void ScopeScanner::visit(const SharedPtr &exprStmt) { exprStmt->scope = getCurrentScope(); ASTVisitor::visit(exprStmt); diff --git a/lib/Sema/SemanticValidator.cpp b/lib/Sema/SemanticValidator.cpp index 3e8a938..2b2195c 100644 --- a/lib/Sema/SemanticValidator.cpp +++ b/lib/Sema/SemanticValidator.cpp @@ -1,63 +1,34 @@ #include "Sema/SemanticValidator.h" -void SemanticValidator::visit(const SharedPtr &module) { - ASTVisitor::visit(module); -} - -void SemanticValidator::visit(const SharedPtr &builtinType) { - ASTVisitor::visit(builtinType); -} - void SemanticValidator::visit(const SharedPtr &varDecl) { ASTVisitor::visit(varDecl); if (varDecl->isConstant()) { + reportOnSemanticError(!varDecl->initVal, "Constant must have an initial value"); if (!dynPtrCast(varDecl->initVal)) { - throw SemanticException("常量初始值暂时只支持字面量"); + reportSemanticError("Constant initializers only support literal quantities"); } } } -void SemanticValidator::visit(const SharedPtr ¶mVarDecl) { - ASTVisitor::visit(paramVarDecl); -} - void SemanticValidator::visit(const SharedPtr &funcDecl) { ASTVisitor::visit(funcDecl); if (funcDecl->returnType) { - if (!funcDecl->refReturnStmt) { - throw SemanticException("有返回类型的函数必须有return语句"); - } + reportOnSemanticError( + !funcDecl->refReturnStmt, + "A function with a return type must have a return statement" + ); } else if (funcDecl->name == "main") { - throw SemanticException("函数名不能为main"); + reportSemanticError("Function name cannot be 'main'"); } } -void SemanticValidator::visit(const SharedPtr &boolLiteralExpr) { - ASTVisitor::visit(boolLiteralExpr); -} - -void SemanticValidator::visit(const SharedPtr &intLiteralExpr) { - ASTVisitor::visit(intLiteralExpr); -} - -void SemanticValidator::visit(const SharedPtr &strLiteralExpr) { - ASTVisitor::visit(strLiteralExpr); -} - -void SemanticValidator::visit(const SharedPtr &varExpr) { - ASTVisitor::visit(varExpr); -} - -void SemanticValidator::visit(const SharedPtr &callExpr) { - ASTVisitor::visit(callExpr); -} - void SemanticValidator::visit(const SharedPtr &uopExpr) { ASTVisitor::visit(uopExpr); // 只能对变量进行自增自减 if (uopExpr->opCode == StaticScriptLexer::PlusPlus || uopExpr->opCode == StaticScriptLexer::MinusMinus) { - if (!dynPtrCast(uopExpr->subExpr)) { - throw SemanticException("自增自减运算符只允许对变量进行运算"); + if (!dynPtrCast(uopExpr->subExpr) && + !dynPtrCast(uopExpr->subExpr)) { + reportSemanticError("The increment and decrement operator only allows operations on variables or array elements"); } } } @@ -65,57 +36,25 @@ void SemanticValidator::visit(const SharedPtr &uopExpr) { void SemanticValidator::visit(const SharedPtr &bopExpr) { ASTVisitor::visit(bopExpr); if (bopExpr->opCode >= StaticScriptLexer::Assign && bopExpr->opCode <= StaticScriptLexer::OrAssign) { - SharedPtr varExpr = dynPtrCast(bopExpr->lhs); - if (varExpr) { + if (const auto &varExpr = dynPtrCast(bopExpr->lhs)) { if (varExpr->refVarDecl->isConstant()) { - throw SemanticException("不允许对常量赋值"); + reportSemanticError("Assignment to a constant is not allowed"); } + } else if (const auto &asExpr = dynPtrCast(bopExpr->lhs)) { } else { - throw SemanticException("只允许对变量赋值, 不允许对表达式赋值"); + reportSemanticError("Only variables or array elements are allowed to be assigned rather than expressions"); } } } -void SemanticValidator::visit(const SharedPtr &exprStmt) { - ASTVisitor::visit(exprStmt); -} - -void SemanticValidator::visit(const SharedPtr &compStmt) { - ASTVisitor::visit(compStmt); -} - -void SemanticValidator::visit(const SharedPtr &varDeclStmt) { - ASTVisitor::visit(varDeclStmt); -} - -void SemanticValidator::visit(const SharedPtr &funcDeclStmt) { - ASTVisitor::visit(funcDeclStmt); -} - -void SemanticValidator::visit(const SharedPtr &ifStmt) { - ASTVisitor::visit(ifStmt); -} - -void SemanticValidator::visit(const SharedPtr &whileStmt) { - ASTVisitor::visit(whileStmt); -} - -void SemanticValidator::visit(const SharedPtr &forStmt) { - ASTVisitor::visit(forStmt); -} - void SemanticValidator::visit(const SharedPtr &continueStmt) { if (!continueStmt->refIterationStmt) { - throw SemanticException("continue语句语句只能出现在循环里"); + reportSemanticError("A continue statement can only appear in a loop"); } } void SemanticValidator::visit(const SharedPtr &breakStmt) { if (!breakStmt->refIterationStmt) { - throw SemanticException("break语句只能出现在循环里"); + reportSemanticError("A break statement can only appear in a loop"); } } - -void SemanticValidator::visit(const SharedPtr &returnStmt) { - ASTVisitor::visit(returnStmt); -} diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index 554b30e..82ba2bf 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -1,35 +1,66 @@ #include "Sema/TypeChecker.h" +#include void TypeChecker::visit(const SharedPtr &varDecl) { if (varDecl->initVal) { varDecl->initVal->accept(shared_from_this()); + SharedPtr &initValInferType = varDecl->initVal->inferType; if (varDecl->type) { - if (varDecl->type != varDecl->initVal->inferType) { - throw SemanticException("变量声明类型与初始值类型不匹配: " + varDecl->name); + if (!varDecl->type->equals(initValInferType)) { + if (varDecl->type->isArray() && initValInferType->isArray() && initValInferType->isUnknown()) { + } else { + reportTypeError("The variable declaration type does not match the initial value type: " + varDecl->name); + } } + } else if (!initValInferType) { + reportTypeError("Unknown variable initialization type"); } else { - varDecl->type = varDecl->initVal->inferType; - } - } else { - if (varDecl->isConstant()) { - throw SemanticException("const常量必须有初始值"); + if (initValInferType->isArray() && initValInferType->isUnknown()) { + reportTypeError( + "When an empty array literal is used as the initial value of a variable, the variable must explicitly specify the type"); + } + varDecl->type = initValInferType; } - if (!varDecl->type) { - throw SemanticException("变量未指定类型: " + varDecl->name); + if (varDecl->type->isArray()) { + // 把显式的声明类型递归赋回给初始值的类型 + assignTypeForArrayLiteral(staticPtrCast(varDecl->initVal), staticPtrCast(varDecl->type)); } + } else { + reportOnTypeError(!varDecl->type, "Variable has no type specified: " + varDecl->name); // 如果变量声明时未指定初始值, 则默认为其类型对应的零值 - if (varDecl->type == BuiltinTypeNode::BOOLEAN_TYPE) { - varDecl->initVal = makeShared(false); - } else if (varDecl->type == BuiltinTypeNode::INTEGER_TYPE) { - varDecl->initVal = makeShared(0); - } else { - varDecl->initVal = makeShared(); + if (varDecl->type->isAtomic()) { + if (varDecl->type->equals(AtomicType::BOOLEAN_TYPE)) { + varDecl->initVal = makeShared(false); + } else if (varDecl->type->equals(AtomicType::INTEGER_TYPE)) { + varDecl->initVal = makeShared(0); + } else if (varDecl->type->equals(AtomicType::STRING_TYPE)) { + varDecl->initVal = makeShared(); + } + } else if (varDecl->type->isArray()) { + varDecl->initVal = makeShared(varDecl->type); } } } -void TypeChecker::visit(const SharedPtr &varExpr) { - varExpr->inferType = varExpr->refVarDecl->type; +void TypeChecker::visit(const SharedPtr &asExpr) { + ASTVisitor::visit(asExpr); + if (!asExpr->baseExpr->inferType->isArray()) { + reportTypeError("Base of array subscript expression is not array type"); + } + for (const SharedPtr &indexExpr : asExpr->indexExprs) { + if (!indexExpr->inferType->equals(AtomicType::INTEGER_TYPE)) { + reportTypeError( + "The subscript of an array subscript expression can only be a natural number. The current subscript is not of integer type"); + } + if (auto intLiteral = dynPtrCast(indexExpr); intLiteral && intLiteral->literal < 0) { + reportTypeError("The subscript of an array subscript expression can only be a natural number. The current subscript is less than 0"); + } + } + SharedPtr iterType = asExpr->baseExpr->inferType; + for (size_t i = 0; i < asExpr->indexExprs.size(); ++i) { + iterType = staticPtrCast(iterType)->getElementType(); + } + asExpr->inferType = iterType; } void TypeChecker::visit(const SharedPtr &callExpr) { @@ -37,11 +68,16 @@ void TypeChecker::visit(const SharedPtr &callExpr) { size_t argsSize = callExpr->args.size(); size_t paramsSize = callExpr->refFuncDecl->params.size(); if (argsSize != paramsSize) { - throw SemanticException("调用" + callExpr->calleeName + "函数时参数数量不匹配"); + reportTypeError("The number of arguments did not match when calling '" + callExpr->calleeName + "' function"); } for (size_t i = 0; i < argsSize; ++i) { if (callExpr->args[i]->inferType != callExpr->refFuncDecl->params[i]->type) { - throw SemanticException("调用" + callExpr->calleeName + "函数时第" + std::to_string(i + 1) + "个参数类型不匹配"); + reportTypeError( + "The type of the " + + std::to_string(i + 1) + + "-th parameter does not match when calling '" + + callExpr->calleeName + + "' function"); } } callExpr->inferType = callExpr->refFuncDecl->returnType; @@ -51,18 +87,18 @@ void TypeChecker::visit(const SharedPtr &uopExpr) { ASTVisitor::visit(uopExpr); unsigned int uop = uopExpr->opCode; - const SharedPtr &inferType = uopExpr->subExpr->inferType; + const SharedPtr &inferType = uopExpr->subExpr->inferType; // 分别检查!运算符和其他运算符的算子类型 if (uop == StaticScriptLexer::Not) { // !运算符的算子必须是布尔类型 - if (inferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("!运算符只允许对布尔值进行运算"); + if (!inferType->equals(AtomicType::BOOLEAN_TYPE)) { + reportTypeError("Operator '!' allows only Boolean operations"); } } else { // 其他运算符的算子必须是整数类型 - if (inferType != BuiltinTypeNode::INTEGER_TYPE) { - throw SemanticException("当前一元运算符只允许对整数进行运算"); + if (!inferType->equals(AtomicType::INTEGER_TYPE)) { + reportTypeError("The current unary operator only allows operations on integers"); } } uopExpr->inferType = inferType; @@ -72,72 +108,121 @@ void TypeChecker::visit(const SharedPtr &bopExpr) { ASTVisitor::visit(bopExpr); unsigned int bop = bopExpr->opCode; - const SharedPtr &leftInferType = bopExpr->lhs->inferType; - const SharedPtr &rightInferType = bopExpr->rhs->inferType; + const SharedPtr &leftInferType = bopExpr->lhs->inferType; + const SharedPtr &rightInferType = bopExpr->rhs->inferType; // 所有的二元运算符只支持运算同类型的表达式 if (leftInferType != rightInferType) { - throw SemanticException("二元运算符" + std::to_string(bop) + "只支持运算同类型的表达式"); + reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports expressions of the same type"); } if (bop == StaticScriptLexer::Plus || bop == StaticScriptLexer::PlusAssign) { - if (leftInferType != BuiltinTypeNode::INTEGER_TYPE && - leftInferType != BuiltinTypeNode::STRING_TYPE) { - throw SemanticException("加运算只支持整数类型和字符串类型"); + if (!leftInferType->equals(AtomicType::INTEGER_TYPE) && + !leftInferType->equals(AtomicType::STRING_TYPE)) { + reportTypeError("Add string and integer only operation types are supported"); } } else if (bop >= StaticScriptLexer::Minus && bop <= StaticScriptLexer::GreaterThanEquals || bop >= StaticScriptLexer::MinusAssign && bop <= StaticScriptLexer::RightShiftAssign) { - if (leftInferType != BuiltinTypeNode::INTEGER_TYPE) { - throw SemanticException("二元运算符" + std::to_string(bop) + "只支持整数类型"); + if (!leftInferType->equals(AtomicType::INTEGER_TYPE)) { + reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports integer type"); } } else if (bop == StaticScriptLexer::And || bop == StaticScriptLexer::Or || bop == StaticScriptLexer::AndAssign || bop == StaticScriptLexer::OrAssign) { - if (leftInferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("二元运算符" + std::to_string(bop) + "只支持布尔类型"); + if (!leftInferType->equals(AtomicType::BOOLEAN_TYPE)) { + reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports boolean type"); } } // 设置二元运算表达式的类型 if (bop >= StaticScriptLexer::LessThan && bop <= StaticScriptLexer::NotEquals) { - bopExpr->inferType = BuiltinTypeNode::BOOLEAN_TYPE; + bopExpr->inferType = AtomicType::BOOLEAN_TYPE; } else { bopExpr->inferType = leftInferType; } } +void TypeChecker::visit(const SharedPtr &varExpr) { + varExpr->inferType = varExpr->refVarDecl->type; +} + +void TypeChecker::visit(const SharedPtr &arrayLiteralExpr) { + ASTVisitor::visit(arrayLiteralExpr); + if (!arrayLiteralExpr->elements.empty()) { + // 寻找非unknown的element + auto element = std::find_if(arrayLiteralExpr->elements.begin(), arrayLiteralExpr->elements.end(), [](const SharedPtr &element) { + return !element->inferType->isUnknown(); + }); + SharedPtr elementType; + if (element != arrayLiteralExpr->elements.end()) { + elementType = (*element)->inferType; + for (const SharedPtr &expr : arrayLiteralExpr->elements) { + if (!elementType->equals(expr->inferType)) { + if (elementType->isArray() && expr->inferType->isArray() && expr->inferType->isUnknown()) { + if (staticPtrCast(elementType)->getDepth() != staticPtrCast(expr->inferType)->getDepth()) { + reportTypeError("Inconsistent depth of multidimensional array elements"); + } + } else { + reportTypeError("Inconsistent array element type"); + } + } + } + } else { + elementType = arrayLiteralExpr->elements[0]->inferType; + for (const SharedPtr &expr : arrayLiteralExpr->elements) { + if (staticPtrCast(elementType)->getDepth() != staticPtrCast(expr->inferType)->getDepth()) { + reportTypeError("Inconsistent depth of multidimensional array elements"); + } + } + } + arrayLiteralExpr->inferType = makeShared(elementType); + } else { + arrayLiteralExpr->inferType = makeShared(); + } +} + void TypeChecker::visit(const SharedPtr &ifStmt) { ASTVisitor::visit(ifStmt); - if (ifStmt->condition->inferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("if语句条件暂时只支持boolean类型"); + if (!ifStmt->condition->inferType->equals(AtomicType::BOOLEAN_TYPE)) { + reportTypeError("The condition of if statement only supports boolean type"); } } void TypeChecker::visit(const SharedPtr &whileStmt) { ASTVisitor::visit(whileStmt); - if (whileStmt->condition->inferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("while语句条件暂时只支持boolean类型"); + if (!whileStmt->condition->inferType->equals(AtomicType::BOOLEAN_TYPE)) { + reportTypeError("The condition of while statement only supports boolean type temporarily"); } } void TypeChecker::visit(const SharedPtr &forStmt) { ASTVisitor::visit(forStmt); - if (forStmt->condition && - forStmt->condition->inferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("for语句条件暂时只支持boolean类型"); + if (forStmt->condition && !forStmt->condition->inferType->equals(AtomicType::BOOLEAN_TYPE)) { + reportTypeError("The condition of for statement only supports boolean type temporarily"); } } void TypeChecker::visit(const SharedPtr &returnStmt) { ASTVisitor::visit(returnStmt); if (returnStmt->returnExpr) { - if (returnStmt->returnExpr->inferType != returnStmt->refFuncDecl->returnType) { - throw SemanticException("return返回值类型与函数返回值类型不匹配"); + if (!returnStmt->returnExpr->inferType->equals(returnStmt->refFuncDecl->returnType)) { + reportTypeError("The return value type does not match the function return value type"); } } else { if (returnStmt->refFuncDecl->returnType) { - throw SemanticException("return语句缺少返回值"); + reportTypeError("The return statement is missing a return value"); + } + } +} + +void TypeChecker::assignTypeForArrayLiteral(const SharedPtr &arrayLiteral, const SharedPtr &type) { + arrayLiteral->inferType = type; + for (const SharedPtr &itemExpr : arrayLiteral->elements) { + if (const SharedPtr &subArrayLiteral = dynPtrCast(itemExpr)) { + assignTypeForArrayLiteral(subArrayLiteral, staticPtrCast(type->getElementType())); + } else { + itemExpr->inferType = type->getElementType(); } } } From e37a0bb986b3cd8954c19f09ca49ee722e23b33d Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sun, 29 Nov 2020 16:52:55 +0800 Subject: [PATCH 03/17] docs: add more detail in README.md and README-zh_CN.md --- README-zh_CN.md | 131 ++++++++++++++++++++++++++++++++++++++++++++++-- README.md | 130 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 253 insertions(+), 8 deletions(-) diff --git a/README-zh_CN.md b/README-zh_CN.md index 7de7a53..6ebd8ca 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -1,7 +1,130 @@ -# StaticScript +

StaticScript

-> StaticScript是一门语法类似于TypeScript的编程语言. +
-## Status +StaticScript是一门类TypeScript的静态强类型编程语言 -![Build](https://github.com/StaticScript/StaticScript/workflows/Build/badge.svg) \ No newline at end of file +![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=flat-square) +![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=flat-square) +![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=flat-square) + +![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=flat-square) +![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=flat-square) +![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=flat-square) +![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=flat-square) + +![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=flat-square) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=flat-square) +![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=flat-square) + +[English](./README.md) | 简体中文 + +
+ + +## 语言特性概要 + +### 变量和常量的声明 + +下面是一些变量声明 + +```typescript +let flag: boolean = true; +let count: number = 20; +let content: string = "Hello World"; +``` + +得益于StaticScript的类型推导特性, 我们可以把上面的变量声明写成下面这样, 它们是等效的 + +```typescript +let flag = true; +let count = 20; +let content = "Hello World"; +``` + +StaticScript的编译器可以巧妙地从变量的初始值推导出变量的类型 + +除了使用`let`声明变量外,还可以使用`const`声明常量 + +```typescript +const name = "StaticScript"; +const age = 1; +const developing = true; +``` + +`let`和`const`的区别在于`const`声明的常量不能被修改 + +### 变量运算 + +可以使用多种多样的运算符对变量执行操作,包括算术运算、按位运算、逻辑运算、关系运算、赋值和字符串连接 + +```typescript +let a = 1; +let b = 2; + +// 加减乘除 +let sum = a + b; +let diff = a - b; +let product = a * b; +let quotient = a / b; + +a = a << 1; // 等效于 `a <<= 1` +b = b >> 1; // 等效于 `b >>= 1` + +let year = "2020", month = "08", day = "06"; +let birthday = year + "/" + month + "/" + day; +``` + +### 控制流 + +```typescript +let a = 1; +let b = 100; +if (a < b) { + ss_println_string("b更大"); +} else { + ss_println_string("b不比a大"); +} + + +let max = a; +if (a < b) { + max = b; +} +``` + +### 循环 + +StaticScript支持使用`while`语句和`for`语句执行一些重复的操作 + +```typescript +// 计算[1, 100]间所有偶数的和 +let sum1 = 0; +let i = 1; +while(i <= 100) { + if (i % 2 == 0) { + sum1 += i; + } +} + + +// 计算[1, 100]间所有整数的和 +let sum2 = 0; +for(let i = 1; i <= 100; i++) { + sum2 += i; +} +``` + +### 函数 + +StaticScript支持在顶级范围内定义函数并在任何作用域内使用函数 + +```typescript +function add(a: number, b: number): number { + return a + b; +} +``` + +StaticScript可以通过return语句的表达式来推断返回类型, 因此上面的函数可以省略返回类型 + +需要注意的是,函数的参数类型必须显式声明, 不能省略 \ No newline at end of file diff --git a/README.md b/README.md index 7df9ffd..bbab4c1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,129 @@ -# StaticScript +

StaticScript

-> StaticScript is a statically typed and strongly typed programming language, syntactically like TypeScript. +
-## Status +StaticScript is a statically typed and strongly typed programming language, syntactically like TypeScript. -![Build](https://github.com/StaticScript/StaticScript/workflows/Build/badge.svg) \ No newline at end of file +![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=flat-square) +![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=flat-square) +![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=flat-square) + +![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=flat-square) +![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=flat-square) +![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=flat-square) +![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=flat-square) + +![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=flat-square) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=flat-square) +![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=flat-square) + +English | [简体中文](./README-zh_CN.md) + +
+ + +## Language Features Summary + +### Variable and Constant Declaration + +Here are some variable declarations. + +```typescript +let flag: boolean = true; +let count: number = 20; +let content: string = "Hello World"; +``` + +Thanks to the type inference feature of StaticScript, we can write the above variable declaration as follows. They are exactly equivalent. + +```typescript +let flag = true; +let count = 20; +let content = "Hello World"; +``` + +The compiler of StaticScript cleverly deduced the type of the variable from the initial value. + +In addition to using `let` to declare variables, you can also use `const` to declare constants. + +```typescript +const name = "StaticScript"; +const age = 1; +const developing = true; +``` + +The difference between `let` and `const` is that constants declared with `const` cannot be modified. + +### Variable Evaluation + +You can use a wealth of operators to perform operations on variables, including arithmetic operations, bitwise operations, logical operations, relational operations, assignments, and string concatenation. + +```typescript +let a = 1; +let b = 2; + +// add, subtract, multiply and divide +let sum = a + b; +let diff = a - b; +let product = a * b; +let quotient = a / b; + +a = a << 1; // equivalent to `a <<= 1` +b = b >> 1; // equivalent to `b >>= 1` + +let year = "2020", month = "08", day = "06"; +let birthday = year + "/" + month + "/" + day; +``` + +### Control Flow + +```typescript +let a = 1; +let b = 100; +if (a < b) { + ss_println_string("b is bigger"); +} else { + ss_println_string("b is not bigger"); +} + + +let max = a; +if (a < b) { + max = b; +} +``` + +### Loops +StaticScript supports using `while` statement and `for` statement to do some repetitive things. + +```typescript +// Calculate the sum of all even numbers between [1, 100] +let sum1 = 0; +let i = 1; +while(i <= 100) { + if (i % 2 == 0) { + sum1 += i; + } +} + + +// Calculate the sum of all integers between [1, 100] +let sum2 = 0; +for(let i = 1; i <= 100; i++) { + sum2 += i; +} +``` + +### Function + +StaticScript supports defining functions in the top level scope and using function in any scope. + +```typescript +function add(a: number, b: number): number { + return a + b; +} +``` + +The above function can omit the return type because StaticScript can deduce the return type through the expression of the return statement. + +It is important to note that the parameter types of functions must be explicitly declared. From 2921c3f4386c8f93fc37395deb0dc113c4ea6eea Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sun, 29 Nov 2020 17:09:18 +0800 Subject: [PATCH 04/17] chore: improve badges in README.md and README-zh_CN.md --- README-zh_CN.md | 24 ++++++++++++------------ README.md | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README-zh_CN.md b/README-zh_CN.md index 6ebd8ca..5ea832e 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -4,18 +4,18 @@ StaticScript是一门类TypeScript的静态强类型编程语言 -![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=flat-square) -![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=flat-square) -![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=flat-square) - -![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=flat-square) -![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=flat-square) -![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=flat-square) -![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=flat-square) - -![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=flat-square) -![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=flat-square) -![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=flat-square) +![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=for-the-badge) +![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=for-the-badge) +![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=for-the-badge) + +![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=for-the-badge) +![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=for-the-badge) + +![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) [English](./README.md) | 简体中文 diff --git a/README.md b/README.md index bbab4c1..43bc506 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,18 @@ StaticScript is a statically typed and strongly typed programming language, syntactically like TypeScript. -![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=flat-square) -![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=flat-square) -![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=flat-square) - -![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=flat-square) -![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=flat-square) -![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=flat-square) -![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=flat-square) - -![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=flat-square) -![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=flat-square) -![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=flat-square) +![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=for-the-badge) +![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=for-the-badge) +![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=for-the-badge) + +![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=for-the-badge) +![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=for-the-badge) + +![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) English | [简体中文](./README-zh_CN.md) From 17a1b429658bfbd53fb6b7eac7ba4761e59034fa Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sun, 29 Nov 2020 17:25:27 +0800 Subject: [PATCH 05/17] fix: fix linguist issue --- .gitattributes | 2 ++ builtin/.gitattributes | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 .gitattributes create mode 100644 builtin/.gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a840b5f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.h linguist-language=cpp +*.cpp linguist-language=cpp \ No newline at end of file diff --git a/builtin/.gitattributes b/builtin/.gitattributes new file mode 100644 index 0000000..4f230f7 --- /dev/null +++ b/builtin/.gitattributes @@ -0,0 +1,2 @@ +*.h linguist-language=c +*.c linguist-language=c \ No newline at end of file From c25e69c5511e78b3a594a7a00cd3c42996d3d188 Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Mon, 30 Nov 2020 21:57:53 +0800 Subject: [PATCH 06/17] test: add more test cases and improve test process --- .github/workflows/{build.yml => ci.yml} | 109 ++++++++++++----- CMakeLists.txt | 19 ++- builtin/CMakeLists.txt | 16 --- examples/all.ss | 29 ----- examples/array.ss | 17 --- examples/iteration.ss | 7 -- examples/operators.ss | 148 ----------------------- examples/variable.ss | 15 --- grammar/StaticScriptParser.g4 | 2 +- include/AST/DeclNode.h | 18 +++ include/CodeGen/Builtin.h | 5 +- include/CodeGen/IRGenerator.h | 4 +- include/Config/Config.h.in | 6 +- include/Entity/Scope.h | 2 +- include/Entity/Type.h | 4 + {builtin => lib}/.gitattributes | 0 lib/CMakeLists.txt | 32 +++-- {builtin => lib}/ss_array.c | 0 {builtin => lib}/ss_array.h | 0 {builtin => lib}/ss_error.c | 7 ++ {builtin => lib}/ss_error.h | 5 +- {builtin => lib}/ss_io.c | 0 {builtin => lib}/ss_io.h | 0 {builtin => lib}/ss_string.c | 0 {builtin => lib}/ss_string.h | 0 {lib => src}/AST/ASTBuilder.cpp | 0 {lib => src}/AST/CMakeLists.txt | 0 {lib => src}/AST/DeclNode.cpp | 42 +++++-- {lib => src}/AST/ExprNode.cpp | 0 {lib => src}/AST/ModuleNode.cpp | 0 {lib => src}/AST/StmtNode.cpp | 0 src/CMakeLists.txt | 6 + {lib => src}/CodeGen/Builtin.cpp | 30 ++++- {lib => src}/CodeGen/CMakeLists.txt | 2 +- {lib => src}/CodeGen/DeclCodeGen.cpp | 0 {lib => src}/CodeGen/ExprCodeGen.cpp | 0 {lib => src}/CodeGen/IRGenerator.cpp | 4 +- {lib => src}/CodeGen/Pass.cpp | 0 {lib => src}/CodeGen/StmtCodeGen.cpp | 0 {lib => src}/Driver/CMakeLists.txt | 4 +- {lib => src}/Driver/Driver.cpp | 3 +- {lib => src}/Entity/CMakeLists.txt | 0 {lib => src}/Entity/Scope.cpp | 2 +- {lib => src}/Entity/Type.cpp | 0 {lib => src}/Optimization/CMakeLists.txt | 0 {lib => src}/Optimization/Optimizer.cpp | 0 {lib => src}/Sema/ASTVisitor.cpp | 0 {lib => src}/Sema/CMakeLists.txt | 0 {lib => src}/Sema/ReferenceResolver.cpp | 0 {lib => src}/Sema/ScopeScanner.cpp | 0 {lib => src}/Sema/SemanticValidator.cpp | 0 {lib => src}/Sema/TypeChecker.cpp | 4 +- tests/all.ss | 18 +++ tests/array.ss | 54 +++++++++ {examples => tests}/function.ss | 6 +- tests/iteration.ss | 6 + tests/operators.ss | 85 +++++++++++++ {examples => tests}/selection.ss | 4 +- tests/variable.ss | 31 +++++ 59 files changed, 435 insertions(+), 311 deletions(-) rename .github/workflows/{build.yml => ci.yml} (53%) delete mode 100644 builtin/CMakeLists.txt delete mode 100644 examples/all.ss delete mode 100644 examples/array.ss delete mode 100644 examples/iteration.ss delete mode 100644 examples/operators.ss delete mode 100644 examples/variable.ss rename {builtin => lib}/.gitattributes (100%) rename {builtin => lib}/ss_array.c (100%) rename {builtin => lib}/ss_array.h (100%) rename {builtin => lib}/ss_error.c (69%) rename {builtin => lib}/ss_error.h (93%) rename {builtin => lib}/ss_io.c (100%) rename {builtin => lib}/ss_io.h (100%) rename {builtin => lib}/ss_string.c (100%) rename {builtin => lib}/ss_string.h (100%) rename {lib => src}/AST/ASTBuilder.cpp (100%) rename {lib => src}/AST/CMakeLists.txt (100%) rename {lib => src}/AST/DeclNode.cpp (69%) rename {lib => src}/AST/ExprNode.cpp (100%) rename {lib => src}/AST/ModuleNode.cpp (100%) rename {lib => src}/AST/StmtNode.cpp (100%) create mode 100644 src/CMakeLists.txt rename {lib => src}/CodeGen/Builtin.cpp (83%) rename {lib => src}/CodeGen/CMakeLists.txt (81%) rename {lib => src}/CodeGen/DeclCodeGen.cpp (100%) rename {lib => src}/CodeGen/ExprCodeGen.cpp (100%) rename {lib => src}/CodeGen/IRGenerator.cpp (96%) rename {lib => src}/CodeGen/Pass.cpp (100%) rename {lib => src}/CodeGen/StmtCodeGen.cpp (100%) rename {lib => src}/Driver/CMakeLists.txt (65%) rename {lib => src}/Driver/Driver.cpp (97%) rename {lib => src}/Entity/CMakeLists.txt (100%) rename {lib => src}/Entity/Scope.cpp (97%) rename {lib => src}/Entity/Type.cpp (100%) rename {lib => src}/Optimization/CMakeLists.txt (100%) rename {lib => src}/Optimization/Optimizer.cpp (100%) rename {lib => src}/Sema/ASTVisitor.cpp (100%) rename {lib => src}/Sema/CMakeLists.txt (100%) rename {lib => src}/Sema/ReferenceResolver.cpp (100%) rename {lib => src}/Sema/ScopeScanner.cpp (100%) rename {lib => src}/Sema/SemanticValidator.cpp (100%) rename {lib => src}/Sema/TypeChecker.cpp (98%) create mode 100644 tests/all.ss create mode 100644 tests/array.ss rename {examples => tests}/function.ss (79%) create mode 100644 tests/iteration.ss create mode 100644 tests/operators.ss rename {examples => tests}/selection.ss (69%) create mode 100644 tests/variable.ss diff --git a/.github/workflows/build.yml b/.github/workflows/ci.yml similarity index 53% rename from .github/workflows/build.yml rename to .github/workflows/ci.yml index b9f0d65..5c50525 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Build +name: Continuous Integration on: push: @@ -43,7 +43,7 @@ jobs: CC: ${{ matrix.compiler.cc }} CXX: ${{ matrix.compiler.cxx }} run: | - thread_count=`sudo cat /proc/cpuinfo| grep "processor"| wc -l` + echo "THREAD_COUNT=$(sudo cat /proc/cpuinfo| grep "processor"| wc -l)" >> $GITHUB_ENV sudo mkdir -p thirdparty/antlr && cd thirdparty/antlr sudo wget https://www.antlr.org/download/antlr-4.8-complete.jar sudo wget -O ${{ runner.temp }}/antlr4-src.zip https://www.antlr.org/download/antlr4-cpp-runtime-4.8-source.zip @@ -52,7 +52,7 @@ jobs: sudo mkdir build && cd build sudo mkdir -p ${{ github.workspace }}/thirdparty/antlr-runtime sudo cmake .. -DANTLR4_INSTALL=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/thirdparty/antlr-runtime - sudo cmake --build . --target install -- -j $thread_count + sudo cmake --build . --target install -- -j $THREAD_COUNT - name: Move Antlr Runtime into /usr/local run: | cd ${{ github.workspace }}/thirdparty/antlr-runtime @@ -63,24 +63,27 @@ jobs: CC: ${{ matrix.compiler.cc }} CXX: ${{ matrix.compiler.cxx }} run: | - thread_count=`sudo cat /proc/cpuinfo| grep "processor"| wc -l` sudo mkdir cmake-build-debug cmake-build-release cd cmake-build-debug sudo cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. - sudo cmake --build . --target staticscript document -- -j $thread_count - for file in ../examples/*.ss - do - sudo ./lib/staticscript $file --emit-llvm -o ss-ir.ll - sudo ./lib/staticscript $file -o ss-obj.o - done + sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT cd ../cmake-build-release sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. - sudo cmake --build . --target staticscript document -- -j $thread_count - for file in ../examples/*.ss - do - sudo ./lib/staticscript $file --emit-llvm -o ss-ir.ll - sudo ./lib/staticscript $file -o ss-obj.o - done + sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT + - name: Upload Debug Executable File + uses: actions/upload-artifact@v2 + with: + name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug + path: | + cmake-build-debug/staticscript + cmake-build-debug/lib/*.bc + - name: Upload Release Executable File + uses: actions/upload-artifact@v2 + with: + name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release + path: | + cmake-build-release/staticscript + cmake-build-release/lib/*.bc build_on_macos: name: Build on macOS @@ -101,21 +104,73 @@ jobs: CC: ${{ matrix.compiler.cc }} CXX: ${{ matrix.compiler.cxx }} run: | - thread_count=`sudo sysctl -n machdep.cpu.thread_count` + echo "THREAD_COUNT=$(sudo sysctl -n machdep.cpu.thread_count)" >> $GITHUB_ENV sudo mkdir cmake-build-debug cmake-build-release cd cmake-build-debug sudo cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. - sudo cmake --build . --target staticscript document -- -j $thread_count - for file in ../examples/*.ss - do - sudo ./lib/staticscript $file --emit-llvm -o ss-ir.ll - sudo ./lib/staticscript $file -o ss-obj.o - done + sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT cd ../cmake-build-release sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. - sudo cmake --build . --target staticscript document -- -j $thread_count - for file in ../examples/*.ss + sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT + - name: Upload Debug Executable File + uses: actions/upload-artifact@v2 + with: + name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug + path: | + cmake-build-debug/staticscript + cmake-build-debug/lib/*.bc + - name: Upload Release Executable File + uses: actions/upload-artifact@v2 + with: + name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release + path: | + cmake-build-release/staticscript + cmake-build-release/lib/*.bc + + test: + name: Test + needs: [ build_on_linux, build_on_macos ] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-18.04, ubuntu-20.04, macos-10.15, macos-11.0 ] + compiler: + - { cc: gcc, cxx: g++ } + - { cc: clang, cxx: clang++ } + steps: + - name: Fetch Codebase + uses: actions/checkout@v2 + - name: Download Debug Executable File + uses: actions/download-artifact@v2 + with: + name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug + path: executables/debug + - name: Download Release Executable File + uses: actions/download-artifact@v2 + with: + name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release + path: executables/release + - name: Test Exmaple Code on Debug Compiler + working-directory: executables/debug + run: | + chmod +x ./staticscript + for file in ${{ github.workspace }}/tests/*.ss + do + base=$(basename $file) + sudo ./staticscript $file -L lib --emit-llvm -o $base.ll + sudo ./staticscript $file -L lib -o $base.o + sudo clang $base.o -lm -o $base.exe + sudo ./$base.exe + done + - name: Test Exmaple Code on Release Compiler + working-directory: executables/release + run: | + chmod +x ./staticscript + for file in ${{ github.workspace }}/tests/*.ss do - sudo ./lib/staticscript $file --emit-llvm -o ss-ir.ll - sudo ./lib/staticscript $file -o ss-obj.o + base=$(basename $file) + sudo ./staticscript $file -L lib --emit-llvm -o $base.ll + sudo ./staticscript $file -L lib -o $base.o + sudo clang $base.o -lm -o $base.exe + sudo ./$base.exe done \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e70d003..c420c46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,21 @@ include_directories(${PROJECT_BINARY_DIR}/include) include_directories(include) -add_subdirectory(builtin) +add_subdirectory(lib) -add_subdirectory(lib) \ No newline at end of file +add_subdirectory(src) + +add_executable( + staticscript + $ + $ + $ + $ + $ + $ + $ +) + +add_dependencies(staticscript antlr4_static) + +target_link_libraries(staticscript PRIVATE antlr4_static ${llvm_libs}) \ No newline at end of file diff --git a/builtin/CMakeLists.txt b/builtin/CMakeLists.txt deleted file mode 100644 index c69eeb8..0000000 --- a/builtin/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -set(BUILTIN_SOURCES ss_error.c ss_string.c ss_array.c ss_io.c) -set(BUILTIN_BITCODES ss_error.bc ss_string.bc ss_array.bc ss_io.bc) - -add_custom_command( - OUTPUT ${BUILTIN_BITCODES} - COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_error.c -std=c99 -O2 -o ss_error.bc - COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_string.c -std=c99 -O2 -o ss_string.bc - COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_array.c -std=c99 -O2 -o ss_array.bc - COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/builtin/ss_io.c -std=c99 -O2 -o ss_io.bc - DEPENDS ${BUILTIN_SOURCES} -) - -add_custom_target( - builtin - DEPENDS ${BUILTIN_BITCODES} -) \ No newline at end of file diff --git a/examples/all.ss b/examples/all.ss deleted file mode 100644 index 41e858e..0000000 --- a/examples/all.ss +++ /dev/null @@ -1,29 +0,0 @@ -let v = 20 + 1; -const s = "string content"; -let f = true; - -function test() { - ss_print_string(s); - return; -} - -function func(a: number, b: number): number { - let s = "StaticScript"; - let x = a + 1; - let y = b - 1; - if (x < y) { - let temp = x; - x = y; - y = temp; - } - for(let i = 0; i < 100; i = i + 1) { - y = y + i - x; - if (i > 50) { - break; - } - } - return x + y; -} - -ss_println_integer(func(1, 2)); -test(); \ No newline at end of file diff --git a/examples/array.ss b/examples/array.ss deleted file mode 100644 index 083bd41..0000000 --- a/examples/array.ss +++ /dev/null @@ -1,17 +0,0 @@ -let arr1: number[]; -let arr2: number[] = []; -let arr3: number[][]; -let arr4: number[][] = []; - -let arr5 = [1,2]; -let arr6 = [[1,2],[3,4]]; -let arr7 = [[],[1,2]]; -let arr8 = [[[0],[0],[]],[[]]]; - -let arr: number[][][] = [[[1,2], [1,2,3,4], [5,6,7]]]; -++arr[0][1][3]; -let ele = arr[0][1][3]; -ss_println_integer(ele); - -arr[0][0][0] += arr[0][2][2]; -ss_println_integer(arr[0][0][0]); \ No newline at end of file diff --git a/examples/iteration.ss b/examples/iteration.ss deleted file mode 100644 index eec9ccc..0000000 --- a/examples/iteration.ss +++ /dev/null @@ -1,7 +0,0 @@ -let x = 1; -for(let i = 0; i < 10; i = i + 1) { - x = x + i; - if (x > 10) { - break; - } -} \ No newline at end of file diff --git a/examples/operators.ss b/examples/operators.ss deleted file mode 100644 index c5e0b76..0000000 --- a/examples/operators.ss +++ /dev/null @@ -1,148 +0,0 @@ -let n_1 = 1; -let n_2 = n_1; -let n_3 = 23 + --n_1; -ss_println_integer(n_1); // 0 -ss_println_integer(n_2); // 1 -ss_println_integer(n_3); // 23 - -let n_4 = n_1 - ++n_2; -ss_println_integer(n_2); // 2 -ss_println_integer(n_4); // -2 - -let n_5 = n_2 - -1; -ss_println_integer(n_5); // 3 - -let b = true; -if (!b) { - ss_println_string("!b is true"); -} else { - ss_println_string("!b is false"); -} - -let n_6 = ~100; -ss_println_integer(n_6); // -101 - -let n_7 = 15 % 4; -ss_println_integer(n_7); // 3 - -let n_8 = 1 << 2; -ss_println_integer(n_8); // 4 - -let n_9 = 4 >> 2; -ss_println_integer(n_9); // 1 - -ss_println_integer(99&100); // 96 -ss_println_integer(99|100); // 103 -ss_println_integer(99^100); // 7 - -if(true && true) { - ss_println_string("pass"); -} else { - ss_println_string("failed"); -} -if(true && false) { - ss_println_string("failed"); -} else { - ss_println_string("pass"); -} -if(false && true) { - ss_println_string("failed"); -} else { - ss_println_string("pass"); -} -if(false && false) { - ss_println_string("failed"); -} else { - ss_println_string("pass"); -} - -let n_10 = 10; -n_10 += 100; -ss_println_integer(n_10); // 110 - -let n_11 = 102; -n_11 -= 90; -ss_println_integer(n_11); // 12 - -let n_12 = 4; -n_12 *= 8; -ss_println_integer(n_12); // 32 - -let n_13 = 3; -n_13 /= 2; -ss_println_integer(n_13); // 1 - -let n_14 = 9; -n_14 %= 2; -ss_println_integer(n_14); // 1 - -let n_15 = 9; -n_15 <<= 2; -ss_println_integer(n_15); // 36 - -let n_16 = 36; -n_16 >>= 2; -ss_println_integer(n_16); // 9 - -let n_17 = 9; -n_17 &= 2; -ss_println_integer(n_17); // 0 - -let n_18 = 9; -n_18 ^= 2; -ss_println_integer(n_18); // 11 - -let n_19 = 9; -n_19 |= 2; -ss_println_integer(n_19); // 11 - -let b_2 = true; -b_2 &&= false; -if (b_2) { - ss_println_string("failed"); -} else { - ss_println_string("pass"); -} - -let b_3 = true; -b_3 ||= false; -if (b_3) { - ss_println_string("pass"); -} else { - ss_println_string("failed"); -} - -if ("123" == "123") { - ss_println_string("pass"); -} else { - ss_println_string("failed"); -} - -if ("123" != "1234") { - ss_println_string("pass"); -} else { - ss_println_string("failed"); -} - -if (1 == 1) { - ss_println_string("pass"); -} else { - ss_println_string("failed"); -} -if (1 != 2) { - ss_println_string("pass"); -} else { - ss_println_string("failed"); -} - -if (true != false) { - ss_println_string("pass"); -} else { - ss_println_string("failed"); -} - -if (false == false) { - ss_println_string("pass"); -} else { - ss_println_string("failed"); -} \ No newline at end of file diff --git a/examples/variable.ss b/examples/variable.ss deleted file mode 100644 index f296526..0000000 --- a/examples/variable.ss +++ /dev/null @@ -1,15 +0,0 @@ -let xn: number; -let xb: boolean; -let xs: string; -let a: number = 1; -let b: boolean = false; -let c: string = "string content"; -let d = 1; -let e = false; -let f = "string content"; -const g: number = 1; -const h: boolean = false; -const i: string = "string content"; -const j = 1; -const k = false; -const l = "string content"; diff --git a/grammar/StaticScriptParser.g4 b/grammar/StaticScriptParser.g4 index b110c36..8bb06b9 100644 --- a/grammar/StaticScriptParser.g4 +++ b/grammar/StaticScriptParser.g4 @@ -97,7 +97,7 @@ expressionStatement ; expression - : base=expression (OpenBracket index+=expression CloseBracket)+ # ArraySubscriptExpr + : base=expression (OpenBracket index+=expression CloseBracket)+ # ArraySubscriptExpr | callExpression # CallExpr | expression uop=(PlusPlus | MinusMinus) # PostfixUnaryExpr | uop=(Not | BitNot | Plus | Minus | PlusPlus | MinusMinus) expression # PrefixUnaryExpr diff --git a/include/AST/DeclNode.h b/include/AST/DeclNode.h index 899ddb5..d42c514 100644 --- a/include/AST/DeclNode.h +++ b/include/AST/DeclNode.h @@ -20,6 +20,11 @@ class VarDeclNode : public DeclNode { public: explicit VarDeclNode(); + VarDeclNode( + VarModifier modifier, + const SharedPtr &type + ); + VarDeclNode( VarModifier modifier, String name, @@ -53,6 +58,8 @@ class VarDeclNode : public DeclNode { /// 函数参数声明节点 class ParmVarDeclNode : public VarDeclNode { public: + explicit ParmVarDeclNode(const SharedPtr &type); + ParmVarDeclNode(const String &name, const SharedPtr &type); ~ParmVarDeclNode() override = default; @@ -65,6 +72,17 @@ class FunctionDeclNode : public DeclNode { public: static SharedPtrMap getBuiltinFunctions(); + FunctionDeclNode( + String name, + const SharedPtrVector ¶ms + ); + + FunctionDeclNode( + String name, + const SharedPtrVector ¶ms, + const SharedPtr &returnType + ); + FunctionDeclNode( String name, const SharedPtrVector ¶ms, diff --git a/include/CodeGen/Builtin.h b/include/CodeGen/Builtin.h index fedac1e..8b3afa2 100644 --- a/include/CodeGen/Builtin.h +++ b/include/CodeGen/Builtin.h @@ -7,7 +7,9 @@ class Builtin { public: - static void initialize(LLVMModule &module, LLVMContext &context); + static void initialize(LLVMModule &module, LLVMContext &context, const llvm::Twine &libDirArg); + + static inline String libDir = STATICSCRIPT_LIB_DIR; // NOLINT }; class BuiltinError { @@ -18,6 +20,7 @@ class BuiltinError { static inline llvm::PointerType *type = nullptr; static inline LLVMFunction *exitIfErrorFunc = nullptr; + static inline LLVMFunction *assertFunc = nullptr; }; class BuiltinString { diff --git a/include/CodeGen/IRGenerator.h b/include/CodeGen/IRGenerator.h index 5271300..2727d45 100644 --- a/include/CodeGen/IRGenerator.h +++ b/include/CodeGen/IRGenerator.h @@ -8,11 +8,11 @@ #include "Support/Error.h" #include "Support/LLVM.h" -class IRGenerator : public ASTVisitor { +class IRGenerator final: public ASTVisitor { public: explicit IRGenerator(); - void resolve(const SharedPtr &module) override; + void resolve(const SharedPtr &module, const llvm::Twine &libDir); void visit(const SharedPtr &module) override; diff --git a/include/Config/Config.h.in b/include/Config/Config.h.in index 1eb7446..1564653 100644 --- a/include/Config/Config.h.in +++ b/include/Config/Config.h.in @@ -12,4 +12,8 @@ #cmakedefine PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@ #cmakedefine PROJECT_VERSION "@PROJECT_VERSION@" -#cmakedefine DEBUG_MODE @DEBUG_MODE@ \ No newline at end of file +#cmakedefine DEBUG_MODE @DEBUG_MODE@ + +#define STATICSCRIPT_INSTALL_DIR "/usr/local/staticscript" +#define STATICSCRIPT_BIN_DIR STATICSCRIPT_INSTALL_DIR"/bin" +#define STATICSCRIPT_LIB_DIR STATICSCRIPT_INSTALL_DIR"/lib" \ No newline at end of file diff --git a/include/Entity/Scope.h b/include/Entity/Scope.h index 6bdce14..b543517 100644 --- a/include/Entity/Scope.h +++ b/include/Entity/Scope.h @@ -63,7 +63,7 @@ class TopLevelScope : public Scope { private: SharedPtrMap functions; - SharedPtrMap builtinFunctions = FunctionDeclNode::getBuiltinFunctions(); + SharedPtrMap libFunctions = FunctionDeclNode::getBuiltinFunctions(); }; /// 局部作用域 diff --git a/include/Entity/Type.h b/include/Entity/Type.h index 0ff0977..a4fb3b0 100644 --- a/include/Entity/Type.h +++ b/include/Entity/Type.h @@ -19,6 +19,10 @@ class Type { [[nodiscard]] virtual bool isUnknown() const = 0; + bool operator==(const Type &rhs) = delete; + + bool operator!=(const Type &rhs) = delete; + [[nodiscard]] bool equals(const SharedPtr &rhs) const; }; diff --git a/builtin/.gitattributes b/lib/.gitattributes similarity index 100% rename from builtin/.gitattributes rename to lib/.gitattributes diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9dd313c..80d4dd8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,21 +1,17 @@ -add_subdirectory(Entity) -add_subdirectory(AST) -add_subdirectory(Sema) -add_subdirectory(CodeGen) -add_subdirectory(Optimization) -add_subdirectory(Driver) +set(BUILTIN_LIB_SOURCES ss_error.c ss_string.c ss_array.c ss_io.c) +set(BUILTIN_LIB_BITCODES ss_error.bc ss_string.bc ss_array.bc ss_io.bc) -add_executable( - staticscript - $ - $ - $ - $ - $ - $ - $ +add_custom_command( + OUTPUT ${BUILTIN_LIB_BITCODES} + COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/lib/ss_error.c -std=c99 -O2 -o ss_error.bc + COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/lib/ss_string.c -std=c99 -O2 -o ss_string.bc + COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/lib/ss_array.c -std=c99 -O2 -o ss_array.bc + COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/lib/ss_io.c -std=c99 -O2 -o ss_io.bc + COMMAND sudo mkdir -p /usr/local/staticscript/lib && sudo cp ss_*.bc /usr/local/staticscript/lib + DEPENDS ${BUILTIN_LIB_SOURCES} ) -add_dependencies(staticscript antlr4_static) - -target_link_libraries(staticscript PRIVATE antlr4_static ${llvm_libs}) \ No newline at end of file +add_custom_target( + lib + DEPENDS ${BUILTIN_LIB_BITCODES} +) \ No newline at end of file diff --git a/builtin/ss_array.c b/lib/ss_array.c similarity index 100% rename from builtin/ss_array.c rename to lib/ss_array.c diff --git a/builtin/ss_array.h b/lib/ss_array.h similarity index 100% rename from builtin/ss_array.h rename to lib/ss_array.h diff --git a/builtin/ss_error.c b/lib/ss_error.c similarity index 69% rename from builtin/ss_error.c rename to lib/ss_error.c index a57030d..86e79e9 100644 --- a/builtin/ss_error.c +++ b/lib/ss_error.c @@ -8,4 +8,11 @@ void ss_exit_if_error(ss_error *error) { } else { ss_error_delete(error); } +} + +void ss_assert(bool condition) { + if (!condition) { + printf("\033[0;31m""assert failed\n""\033[0m"); + abort(); + } } \ No newline at end of file diff --git a/builtin/ss_error.h b/lib/ss_error.h similarity index 93% rename from builtin/ss_error.h rename to lib/ss_error.h index 17167b8..d09b519 100644 --- a/builtin/ss_error.h +++ b/lib/ss_error.h @@ -2,6 +2,7 @@ #include #include +#include typedef struct { int code; @@ -46,4 +47,6 @@ static void ss_error_delete(ss_error *error) { } } -void ss_exit_if_error(ss_error *error); \ No newline at end of file +void ss_exit_if_error(ss_error *error); + +void ss_assert(bool condition); \ No newline at end of file diff --git a/builtin/ss_io.c b/lib/ss_io.c similarity index 100% rename from builtin/ss_io.c rename to lib/ss_io.c diff --git a/builtin/ss_io.h b/lib/ss_io.h similarity index 100% rename from builtin/ss_io.h rename to lib/ss_io.h diff --git a/builtin/ss_string.c b/lib/ss_string.c similarity index 100% rename from builtin/ss_string.c rename to lib/ss_string.c diff --git a/builtin/ss_string.h b/lib/ss_string.h similarity index 100% rename from builtin/ss_string.h rename to lib/ss_string.h diff --git a/lib/AST/ASTBuilder.cpp b/src/AST/ASTBuilder.cpp similarity index 100% rename from lib/AST/ASTBuilder.cpp rename to src/AST/ASTBuilder.cpp diff --git a/lib/AST/CMakeLists.txt b/src/AST/CMakeLists.txt similarity index 100% rename from lib/AST/CMakeLists.txt rename to src/AST/CMakeLists.txt diff --git a/lib/AST/DeclNode.cpp b/src/AST/DeclNode.cpp similarity index 69% rename from lib/AST/DeclNode.cpp rename to src/AST/DeclNode.cpp index 37bd37e..07c220d 100644 --- a/lib/AST/DeclNode.cpp +++ b/src/AST/DeclNode.cpp @@ -4,6 +4,11 @@ VarDeclNode::VarDeclNode() : modifier(VarModifier::Let) {} +VarDeclNode::VarDeclNode( + VarModifier modifier, + const SharedPtr &type +) : modifier(modifier), type(type) {} + VarDeclNode::VarDeclNode( VarModifier modifier, String name, @@ -34,6 +39,10 @@ bool VarDeclNode::isConstant() const { return modifier == VarModifier::Const; } +ParmVarDeclNode::ParmVarDeclNode( + const SharedPtr &type +) : VarDeclNode(VarModifier::Param, type) {} + ParmVarDeclNode::ParmVarDeclNode( const String &name, const SharedPtr &type @@ -45,19 +54,36 @@ void ParmVarDeclNode::accept(const SharedPtr &visitor) { SharedPtrMap FunctionDeclNode::getBuiltinFunctions() { SharedPtrMap functions; - SharedPtr integerArg = makeShared("number", AtomicType::INTEGER_TYPE); - SharedPtr strArg = makeShared("str", AtomicType::STRING_TYPE); + SharedPtr booleanArg = makeShared(AtomicType::BOOLEAN_TYPE); + SharedPtr integerArg = makeShared(AtomicType::INTEGER_TYPE); + SharedPtr strArg = makeShared(AtomicType::STRING_TYPE); + SharedPtrVector booleanArgs{booleanArg}; SharedPtrVector integerArgs{integerArg}; SharedPtrVector strArgs{strArg}; - functions["ss_integer2string"] = makeShared("ss_integer2string", integerArgs, AtomicType::STRING_TYPE, nullptr); - functions["ss_string2integer"] = makeShared("ss_string2integer", strArgs, AtomicType::INTEGER_TYPE, nullptr); - functions["ss_print_integer"] = makeShared("ss_print_integer", integerArgs, nullptr, nullptr); - functions["ss_println_integer"] = makeShared("ss_println_integer", integerArgs, nullptr, nullptr); - functions["ss_print_string"] = makeShared("ss_print_string", strArgs, nullptr, nullptr); - functions["ss_println_string"] = makeShared("ss_println_string", strArgs, nullptr, nullptr); + functions["ss_integer2string"] = makeShared("ss_integer2string", integerArgs, AtomicType::STRING_TYPE); + functions["ss_string2integer"] = makeShared("ss_string2integer", strArgs, AtomicType::INTEGER_TYPE); + functions["ss_print_integer"] = makeShared("ss_print_integer", integerArgs); + functions["ss_println_integer"] = makeShared("ss_println_integer", integerArgs); + functions["ss_print_string"] = makeShared("ss_print_string", strArgs); + functions["ss_println_string"] = makeShared("ss_println_string", strArgs); + functions["ss_assert"] = makeShared("ss_assert", booleanArgs); return functions; } +FunctionDeclNode::FunctionDeclNode( + String name, + const SharedPtrVector ¶ms +) : name(std::move(name)), + params(params) {} + +FunctionDeclNode::FunctionDeclNode( + String name, + const SharedPtrVector ¶ms, + const SharedPtr &returnType +) : name(std::move(name)), + params(params), + returnType(returnType) {} + FunctionDeclNode::FunctionDeclNode( String name, const SharedPtrVector ¶ms, diff --git a/lib/AST/ExprNode.cpp b/src/AST/ExprNode.cpp similarity index 100% rename from lib/AST/ExprNode.cpp rename to src/AST/ExprNode.cpp diff --git a/lib/AST/ModuleNode.cpp b/src/AST/ModuleNode.cpp similarity index 100% rename from lib/AST/ModuleNode.cpp rename to src/AST/ModuleNode.cpp diff --git a/lib/AST/StmtNode.cpp b/src/AST/StmtNode.cpp similarity index 100% rename from lib/AST/StmtNode.cpp rename to src/AST/StmtNode.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..669ebde --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(Entity) +add_subdirectory(AST) +add_subdirectory(Sema) +add_subdirectory(CodeGen) +add_subdirectory(Optimization) +add_subdirectory(Driver) \ No newline at end of file diff --git a/lib/CodeGen/Builtin.cpp b/src/CodeGen/Builtin.cpp similarity index 83% rename from lib/CodeGen/Builtin.cpp rename to src/CodeGen/Builtin.cpp index 2dca4b6..26d3ef9 100644 --- a/lib/CodeGen/Builtin.cpp +++ b/src/CodeGen/Builtin.cpp @@ -1,6 +1,29 @@ #include "CodeGen/Builtin.h" -void Builtin::initialize(LLVMModule &module, LLVMContext &context) { +void Builtin::initialize(LLVMModule &module, LLVMContext &context, const llvm::Twine &libDirArg) { + llvm::SmallString<256> libPath; + if (!libDirArg.str().empty()) { + libDirArg.toVector(libPath); + if (!llvm::sys::path::is_absolute(libPath)) { + if (llvm::sys::fs::make_absolute(libPath)) { + reportLinkError("Parse the specified library directory failed"); + } + } + if (!llvm::sys::fs::exists(libPath)) { + reportLinkError("The specified library directory does not exist"); + } + if (!llvm::sys::fs::is_directory(libPath)) { + reportLinkError("The specified library directory is not a directory"); + } + libDir = libPath.str(); + } else { + llvm::sys::fs::current_path(libPath); + llvm::sys::path::append(libPath, "lib"); + if (llvm::sys::fs::exists(libPath) && llvm::sys::fs::is_directory(libPath)) { + libDir = libPath.str(); + } + } + BuiltinError::linkModule(module, context); BuiltinString::linkModule(module, context); BuiltinArray::linkModule(module, context); @@ -14,7 +37,7 @@ void Builtin::initialize(LLVMModule &module, LLVMContext &context) { #define BUILTIN_LINK_MODULE(moduleName) \ llvm::SMDiagnostic error; \ - String bitcodeFilename = PROJECT_BINARY_DIR"/builtin/ss_"#moduleName".bc"; \ + String bitcodeFilename = Builtin::libDir + "/ss_"#moduleName".bc"; \ bool bitcodeExist = llvm::sys::fs::exists(bitcodeFilename); \ reportOnLinkError(!bitcodeExist, "Not found " + bitcodeFilename); \ std::unique_ptr bitcodeModule = llvm::parseIRFile(bitcodeFilename, error, context); \ @@ -30,6 +53,7 @@ void BuiltinError::getTypeAndFunction(LLVMModule &module) { llvm::StructType *errStructType = module.getTypeByName("struct.ss_error"); type = errStructType->getPointerTo(); exitIfErrorFunc = module.getFunction("ss_exit_if_error"); + assertFunc = module.getFunction("ss_assert"); } void BuiltinString::linkModule(LLVMModule &module, LLVMContext &context) { @@ -101,4 +125,4 @@ void BuiltinIO::getTypeAndFunction(LLVMModule &module) { printlnIntegerFunc = module.getFunction("ss_println_integer"); printStringFunc = module.getFunction("ss_print_string"); printlnStringFunc = module.getFunction("ss_println_string"); -} +} \ No newline at end of file diff --git a/lib/CodeGen/CMakeLists.txt b/src/CodeGen/CMakeLists.txt similarity index 81% rename from lib/CodeGen/CMakeLists.txt rename to src/CodeGen/CMakeLists.txt index d849645..e99731f 100644 --- a/lib/CodeGen/CMakeLists.txt +++ b/src/CodeGen/CMakeLists.txt @@ -4,4 +4,4 @@ set(SRC Builtin.cpp ${CODEGEN_SRC} Pass.cpp) add_library(codegen OBJECT ${SRC}) -add_dependencies(codegen sema builtin) \ No newline at end of file +add_dependencies(codegen sema lib) \ No newline at end of file diff --git a/lib/CodeGen/DeclCodeGen.cpp b/src/CodeGen/DeclCodeGen.cpp similarity index 100% rename from lib/CodeGen/DeclCodeGen.cpp rename to src/CodeGen/DeclCodeGen.cpp diff --git a/lib/CodeGen/ExprCodeGen.cpp b/src/CodeGen/ExprCodeGen.cpp similarity index 100% rename from lib/CodeGen/ExprCodeGen.cpp rename to src/CodeGen/ExprCodeGen.cpp diff --git a/lib/CodeGen/IRGenerator.cpp b/src/CodeGen/IRGenerator.cpp similarity index 96% rename from lib/CodeGen/IRGenerator.cpp rename to src/CodeGen/IRGenerator.cpp index 794c44f..d08c0c2 100644 --- a/lib/CodeGen/IRGenerator.cpp +++ b/src/CodeGen/IRGenerator.cpp @@ -3,9 +3,9 @@ IRGenerator::IRGenerator() : llvmIRBuilder(llvmContext) {} -void IRGenerator::resolve(const SharedPtr &module) { +void IRGenerator::resolve(const SharedPtr &module, const llvm::Twine &libDir) { llvmModule = makeShared(module->filename, llvmContext); - Builtin::initialize(*llvmModule, llvmContext); + Builtin::initialize(*llvmModule, llvmContext, libDir); ASTVisitor::resolve(module); llvm::verifyModule(*llvmModule); runPasses(*llvmModule); diff --git a/lib/CodeGen/Pass.cpp b/src/CodeGen/Pass.cpp similarity index 100% rename from lib/CodeGen/Pass.cpp rename to src/CodeGen/Pass.cpp diff --git a/lib/CodeGen/StmtCodeGen.cpp b/src/CodeGen/StmtCodeGen.cpp similarity index 100% rename from lib/CodeGen/StmtCodeGen.cpp rename to src/CodeGen/StmtCodeGen.cpp diff --git a/lib/Driver/CMakeLists.txt b/src/Driver/CMakeLists.txt similarity index 65% rename from lib/Driver/CMakeLists.txt rename to src/Driver/CMakeLists.txt index 8760232..a6de52e 100644 --- a/lib/Driver/CMakeLists.txt +++ b/src/Driver/CMakeLists.txt @@ -4,6 +4,4 @@ set(SRC Driver.cpp) add_library(driver OBJECT ${SRC}) -#add_dependencies(driver optimization) - -add_dependencies(driver sema) \ No newline at end of file +add_dependencies(driver optimization) \ No newline at end of file diff --git a/lib/Driver/Driver.cpp b/src/Driver/Driver.cpp similarity index 97% rename from lib/Driver/Driver.cpp rename to src/Driver/Driver.cpp index 0819369..cdacedb 100644 --- a/lib/Driver/Driver.cpp +++ b/src/Driver/Driver.cpp @@ -21,6 +21,7 @@ int main(int argc, char **argv) { llvm::cl::opt outputFilename("o", llvm::cl::value_desc("output file"), llvm::cl::desc("Write output to "), llvm::cl::cat(generalOptsCat)); llvm::cl::opt emitLLVM("emit-llvm", llvm::cl::desc("Generate the LLVM representation for input file"), llvm::cl::cat(generalOptsCat)); + llvm::cl::opt libDir("L", llvm::cl::desc("Specify lib library directory")); llvm::cl::opt optLevelO0("O0", llvm::cl::desc("Optimization level 0. Similar to clang -O0"), llvm::cl::cat(generalOptsCat)); llvm::cl::opt optLevelO1("O1", llvm::cl::desc("Optimization level 1. Similar to clang -O1"), llvm::cl::cat(generalOptsCat)); @@ -102,7 +103,7 @@ int main(int argc, char **argv) { validator->resolve(module); // 中间代码生成 SharedPtr generator = makeShared(); - generator->resolve(module); + generator->resolve(module, libDir); LLVMModule &llvmModule = generator->getLLVMModule(); diff --git a/lib/Entity/CMakeLists.txt b/src/Entity/CMakeLists.txt similarity index 100% rename from lib/Entity/CMakeLists.txt rename to src/Entity/CMakeLists.txt diff --git a/lib/Entity/Scope.cpp b/src/Entity/Scope.cpp similarity index 97% rename from lib/Entity/Scope.cpp rename to src/Entity/Scope.cpp index b596fb2..5ea7bf6 100644 --- a/lib/Entity/Scope.cpp +++ b/src/Entity/Scope.cpp @@ -39,7 +39,7 @@ SharedPtr TopLevelScope::resolveVariable(const String &name) { SharedPtr TopLevelScope::resolveFunction(const String &name) { SharedPtr function = mapFind(functions, name); if (!function) { - return mapFind(builtinFunctions, name); + return mapFind(libFunctions, name); } return function; } diff --git a/lib/Entity/Type.cpp b/src/Entity/Type.cpp similarity index 100% rename from lib/Entity/Type.cpp rename to src/Entity/Type.cpp diff --git a/lib/Optimization/CMakeLists.txt b/src/Optimization/CMakeLists.txt similarity index 100% rename from lib/Optimization/CMakeLists.txt rename to src/Optimization/CMakeLists.txt diff --git a/lib/Optimization/Optimizer.cpp b/src/Optimization/Optimizer.cpp similarity index 100% rename from lib/Optimization/Optimizer.cpp rename to src/Optimization/Optimizer.cpp diff --git a/lib/Sema/ASTVisitor.cpp b/src/Sema/ASTVisitor.cpp similarity index 100% rename from lib/Sema/ASTVisitor.cpp rename to src/Sema/ASTVisitor.cpp diff --git a/lib/Sema/CMakeLists.txt b/src/Sema/CMakeLists.txt similarity index 100% rename from lib/Sema/CMakeLists.txt rename to src/Sema/CMakeLists.txt diff --git a/lib/Sema/ReferenceResolver.cpp b/src/Sema/ReferenceResolver.cpp similarity index 100% rename from lib/Sema/ReferenceResolver.cpp rename to src/Sema/ReferenceResolver.cpp diff --git a/lib/Sema/ScopeScanner.cpp b/src/Sema/ScopeScanner.cpp similarity index 100% rename from lib/Sema/ScopeScanner.cpp rename to src/Sema/ScopeScanner.cpp diff --git a/lib/Sema/SemanticValidator.cpp b/src/Sema/SemanticValidator.cpp similarity index 100% rename from lib/Sema/SemanticValidator.cpp rename to src/Sema/SemanticValidator.cpp diff --git a/lib/Sema/TypeChecker.cpp b/src/Sema/TypeChecker.cpp similarity index 98% rename from lib/Sema/TypeChecker.cpp rename to src/Sema/TypeChecker.cpp index 82ba2bf..14dcc0d 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/src/Sema/TypeChecker.cpp @@ -71,7 +71,7 @@ void TypeChecker::visit(const SharedPtr &callExpr) { reportTypeError("The number of arguments did not match when calling '" + callExpr->calleeName + "' function"); } for (size_t i = 0; i < argsSize; ++i) { - if (callExpr->args[i]->inferType != callExpr->refFuncDecl->params[i]->type) { + if (!callExpr->args[i]->inferType->equals(callExpr->refFuncDecl->params[i]->type)) { reportTypeError( "The type of the " + std::to_string(i + 1) + @@ -112,7 +112,7 @@ void TypeChecker::visit(const SharedPtr &bopExpr) { const SharedPtr &rightInferType = bopExpr->rhs->inferType; // 所有的二元运算符只支持运算同类型的表达式 - if (leftInferType != rightInferType) { + if (!leftInferType->equals(rightInferType)) { reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports expressions of the same type"); } diff --git a/tests/all.ss b/tests/all.ss new file mode 100644 index 0000000..2ee6819 --- /dev/null +++ b/tests/all.ss @@ -0,0 +1,18 @@ +function fibonacci(n: number): number { + if(n <= 1) { + return 1; + } + return fibonacci(n - 1) + fibonacci(n - 2); +} + +ss_assert(fibonacci(10) == 89); +ss_assert(fibonacci(11) == 144); +ss_assert(fibonacci(12) == 233); +ss_assert(fibonacci(13) == 377); +ss_assert(fibonacci(14) == 610); +ss_assert(fibonacci(15) == 987); +ss_assert(fibonacci(16) == 1597); +ss_assert(fibonacci(17) == 2584); +ss_assert(fibonacci(18) == 4181); +ss_assert(fibonacci(19) == 6765); +ss_assert(fibonacci(20) == 10946); \ No newline at end of file diff --git a/tests/array.ss b/tests/array.ss new file mode 100644 index 0000000..64f2b9e --- /dev/null +++ b/tests/array.ss @@ -0,0 +1,54 @@ +let arr1: number[]; +let arr2: number[] = []; +let arr3: number[][]; +let arr4: number[][] = []; + +let arr5 = [1,2]; +let arr6 = [[1,2],[3,4]]; +let arr7 = [[],[1,2]]; +let arr8 = [[[1],[2],[]],[[]]]; + +ss_assert(arr5[0] == 1); +ss_assert(arr5[1] == 2); +ss_assert(arr6[0][0] == 1); +ss_assert(arr6[0][1] == 2); +ss_assert(arr6[1][0] == 3); +ss_assert(arr6[1][1] == 4); +ss_assert(arr7[1][0] == 1); +ss_assert(arr7[1][1] == 2); +ss_assert(arr8[0][0][0] == 1); +ss_assert(arr8[0][1][0] == 2); + +let arr: number[][][] = [[[1,2,3,4], [5,6,7,8], [9,10,11,12]], [[100,200,300,400], [201,202,203,204], [205,206,207,208]]]; +for(let i = 0; i < 2; i++) { + for(let j = 0; j < 3; j++) { + for(let k = 0; k < 4; k++) { + arr[i][j][k] += 1; + } + } +} + +ss_assert(arr[0][0][0] == 2); +ss_assert(arr[0][0][1] == 3); +ss_assert(arr[0][0][2] == 4); +ss_assert(arr[0][0][3] == 5); +ss_assert(arr[0][1][0] == 6); +ss_assert(arr[0][1][1] == 7); +ss_assert(arr[0][1][2] == 8); +ss_assert(arr[0][1][3] == 9); +ss_assert(arr[0][2][0] == 10); +ss_assert(arr[0][2][1] == 11); +ss_assert(arr[0][2][2] == 12); +ss_assert(arr[0][2][3] == 13); +ss_assert(arr[1][0][0] == 101); +ss_assert(arr[1][0][1] == 201); +ss_assert(arr[1][0][2] == 301); +ss_assert(arr[1][0][3] == 401); +ss_assert(arr[1][1][0] == 202); +ss_assert(arr[1][1][1] == 203); +ss_assert(arr[1][1][2] == 204); +ss_assert(arr[1][1][3] == 205); +ss_assert(arr[1][2][0] == 206); +ss_assert(arr[1][2][1] == 207); +ss_assert(arr[1][2][2] == 208); +ss_assert(arr[1][2][3] == 209); \ No newline at end of file diff --git a/examples/function.ss b/tests/function.ss similarity index 79% rename from examples/function.ss rename to tests/function.ss index 76b2766..ed423b6 100644 --- a/examples/function.ss +++ b/tests/function.ss @@ -22,6 +22,6 @@ function lessThan(a: number, b: number): boolean { nop(); nopRet(); -add(1, 2); -max(1, 2); -lessThan(1, 2); +ss_assert(add(1, 2) == 3); +ss_assert(max(1, 2) == 2); +ss_assert(lessThan(1, 2)); diff --git a/tests/iteration.ss b/tests/iteration.ss new file mode 100644 index 0000000..f4cd60b --- /dev/null +++ b/tests/iteration.ss @@ -0,0 +1,6 @@ +let sum = 1; +for(let i = 0; i < 100; i++) { + sum += i; +} + +ss_assert(sum == 4951); \ No newline at end of file diff --git a/tests/operators.ss b/tests/operators.ss new file mode 100644 index 0000000..8983fc8 --- /dev/null +++ b/tests/operators.ss @@ -0,0 +1,85 @@ +let n_1 = 1; +let n_2 = n_1; +let n_3 = 23 + --n_1; +ss_assert(n_1 == 0); +ss_assert(n_2 == 1); +ss_assert(n_3 == 23); + +let n_4 = n_1 - ++n_2; +ss_assert(n_2 == 2); +ss_assert(n_4 == -2); + +let n_5 = n_2 - -1; +ss_assert(n_5 == 3); + +let b = false; +ss_assert(!b); + +let n_6 = ~100; +ss_assert(n_6 == -101); + +let n_7 = 15 % 4; +ss_assert(n_7 == 3); + +let n_8 = 1 << 2; +ss_assert(n_8 == 4); + +let n_9 = 4 >> 2; +ss_assert(n_9 == 1); + +ss_assert((99 & 100) == 96); +ss_assert((99 | 100) == 103); +ss_assert((99 ^ 100) == 7); + +ss_assert(true && true); +ss_assert(!(true && false)); +ss_assert(!(false && true)); +ss_assert(!(false && false)); + +let n_10 = 10; +n_10 += 100; +ss_assert(n_10 == 110); + +let n_11 = 102; +n_11 -= 90; +ss_assert(n_11 == 12); + +let n_12 = 4; +n_12 *= 8; +ss_assert(n_12 == 32); + +let n_13 = 3; +n_13 /= 2; +ss_assert(n_13 == 1); + +let n_14 = 9; +n_14 %= 2; +ss_assert(n_14 == 1); + +let n_15 = 9; +n_15 <<= 2; +ss_assert(n_15 == 36); + +let n_16 = 36; +n_16 >>= 2; +ss_assert(n_16 == 9); + +let n_17 = 9; +n_17 &= 2; +ss_assert(n_17 == 0); + +let n_18 = 9; +n_18 ^= 2; +ss_assert(n_18 == 11); + +let n_19 = 9; +n_19 |= 2; +ss_assert(n_19 == 11); + +let b_2 = true; +b_2 &&= false; +ss_assert(!b_2); + +let b_3 = true; +b_3 ||= false; +ss_assert(b_3); \ No newline at end of file diff --git a/examples/selection.ss b/tests/selection.ss similarity index 69% rename from examples/selection.ss rename to tests/selection.ss index fc39b9b..6fcadd9 100644 --- a/examples/selection.ss +++ b/tests/selection.ss @@ -4,4 +4,6 @@ if (x < y) { x = x + 1; } else { y = y + 1; -} \ No newline at end of file +} +ss_assert(x == 1); +ss_assert(y == 1); \ No newline at end of file diff --git a/tests/variable.ss b/tests/variable.ss new file mode 100644 index 0000000..579d8af --- /dev/null +++ b/tests/variable.ss @@ -0,0 +1,31 @@ +let xn: number; +let xb: boolean; +let xs: string; +let a: number = 1; +let b: boolean = false; +let c: string = "string content"; +let d = 1; +let e = false; +let f = "string content"; +const g: number = 1; +const h: boolean = false; +const i: string = "string content"; +const j = 1; +const k = false; +const l = "string content"; + +ss_assert(xn == 0); +ss_assert(xb == false); +ss_assert(xs == ""); +ss_assert(a == 1); +ss_assert(b == false); +ss_assert(c == "string content"); +ss_assert(d == 1); +ss_assert(e == false); +ss_assert(f == "string content"); +ss_assert(g == 1); +ss_assert(h == false); +ss_assert(i == "string content"); +ss_assert(j == 1); +ss_assert(k == false); +ss_assert(l == "string content"); \ No newline at end of file From f35775ea6ea024abe8c6fd32c04d4c5363e2dfbe Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Thu, 3 Dec 2020 18:17:52 +0800 Subject: [PATCH 07/17] feat: support float pointer number(64-bits) --- .github/workflows/ci.yml | 4 +- cmake/AddANTLR.cmake | 2 +- grammar/StaticScriptLexer.g4 | 550 +++++++++++++++++++++++++++++++--- grammar/StaticScriptParser.g4 | 17 +- include/AST/ASTBuilder.h | 2 +- include/AST/ExprNode.h | 43 ++- include/CodeGen/Builtin.h | 10 + include/CodeGen/IRGenerator.h | 12 +- include/Entity/Type.h | 106 +++++-- include/Sema/ASTVisitor.h | 2 + include/Sema/ScopeScanner.h | 2 + include/Support/Alias.h | 6 + lib/CMakeLists.txt | 1 - lib/ss_array.c | 28 ++ lib/ss_array.h | 12 + lib/ss_io.c | 20 ++ lib/ss_io.h | 8 + src/AST/ASTBuilder.cpp | 45 ++- src/AST/DeclNode.cpp | 16 +- src/AST/ExprNode.cpp | 22 +- src/CodeGen/Builtin.cpp | 15 + src/CodeGen/DeclCodeGen.cpp | 53 ++-- src/CodeGen/ExprCodeGen.cpp | 367 +++++++++++++---------- src/CodeGen/IRGenerator.cpp | 34 ++- src/CodeGen/StmtCodeGen.cpp | 12 +- src/Entity/Type.cpp | 145 +++++++-- src/Sema/ASTVisitor.cpp | 2 + src/Sema/ScopeScanner.cpp | 4 + src/Sema/TypeChecker.cpp | 236 +++++++++------ tests/array.ss | 10 +- tests/float.ss | 5 + 31 files changed, 1357 insertions(+), 434 deletions(-) create mode 100644 tests/float.ss diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c50525..3fa7c3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,9 @@ jobs: key: ${{ matrix.os }}-${{ matrix.compiler.cc }}-antlr - name: Install Prerequirements run: | - sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" + sudo wget https://apt.llvm.org/llvm.sh + sudo chmod +x llvm.sh + sudo ./llvm.sh 11 sudo apt-get -y install uuid-dev pkg-config doxygen graphviz - name: Install Antlr4 and Antlr4 Runtime if: steps.cache-antlr.outputs.cache-hit != 'true' diff --git a/cmake/AddANTLR.cmake b/cmake/AddANTLR.cmake index 36da73f..8ef2245 100644 --- a/cmake/AddANTLR.cmake +++ b/cmake/AddANTLR.cmake @@ -3,7 +3,7 @@ list(APPEND CMAKE_PREFIX_PATH /usr/local/lib/cmake/antlr4) if (CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) set(ANTLR4_JAR_LOCATION ${PROJECT_SOURCE_DIR}/thirdparty/antlr/antlr-4.8-complete.jar) elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) - set(ANTLR4_JAR_LOCATION /usr/local/opt/antlr/antlr-4.8-complete.jar) + set(ANTLR4_JAR_LOCATION /usr/local/opt/antlr/antlr-4.9-complete.jar) endif () find_package(antlr4-runtime REQUIRED) diff --git a/grammar/StaticScriptLexer.g4 b/grammar/StaticScriptLexer.g4 index 1118bb8..e43c8ae 100644 --- a/grammar/StaticScriptLexer.g4 +++ b/grammar/StaticScriptLexer.g4 @@ -1,11 +1,14 @@ lexer grammar StaticScriptLexer; +channels { ERROR } + options { language = Cpp; } // 关键字 Boolean: 'boolean'; +Integer: 'int'; Number: 'number'; String: 'string'; Let: 'let'; @@ -31,58 +34,79 @@ SemiColon: ';'; Comma: ','; // 一元操作符 -PlusPlus: '++'; // number->number -MinusMinus: '--'; // number->number -Not: '!'; // boolean->boolean -BitNot: '~'; // number->number +PlusPlus: '++'; // number->number +MinusMinus: '--'; // number->number +Not: '!'; // boolean->boolean +BitNot: '~'; // int->int // 二元操作符 -Plus: '+'; // (number|string)->(number|string): Plus既是一元操作符也是二元操作符 -Minus: '-'; // number->number: Minus既是一元操作符也是二元操作符 -Multiply: '*'; // number->number -Divide: '/'; // number->number -Modulus: '%'; // number->number -BitAnd: '&'; // number->number -BitXOr: '^'; // number->number -BitOr: '|'; // number->number -LeftShift: '<<'; // number->number -RightShift: '>>'; // number->number -LessThan: '<'; // number->boolean -GreaterThan: '>'; // number->boolean -LessThanEquals: '<='; // number->boolean -GreaterThanEquals: '>='; // number->boolean -Equals: '=='; // (number|boolean|string)->boolean -NotEquals: '!='; // (number|boolean|string)->boolean -And: '&&'; // boolean->boolean -Or: '||'; // boolean->boolean -Assign: '='; // (number|boolean|string)->(number|boolean|string) -PlusAssign: '+='; // (number|string)->(number|string) -MinusAssign: '-='; // number->number -MultiplyAssign: '*='; // number->number -DivideAssign: '/='; // number->number -ModulusAssign: '%='; // number->number -BitAndAssign: '&='; // number->number -BitXorAssign: '^='; // number->number -BitOrAssign: '|='; // number->number -LeftShiftAssign: '<<='; // number->number -RightShiftAssign: '>>='; // number->number -AndAssign: '&&='; // boolean->boolean -OrAssign: '||='; // boolean->boolean - -// 字面量 +Plus: '+'; // (integer|number|string)->(integer|number|string): Plus既是一元操作符也是二元操作符 +Minus: '-'; // number->number: Minus既是一元操作符也是二元操作符 +Multiply: '*'; // number->number +Divide: '/'; // number->number +Modulus: '%'; // number->number +BitAnd: '&'; // integer->integer +BitXOr: '^'; // integer->integer +BitOr: '|'; // integer->integer +ShiftLeft: '<<'; // integer->integer +ArithmeticShiftRight: '>>'; // integer->integer +LogicalShiftRight: '>>>'; // integer->integer +LessThan: '<'; // number->boolean +GreaterThan: '>'; // number->boolean +LessThanEquals: '<='; // number->boolean +GreaterThanEquals: '>='; // number->boolean +Equals: '=='; // (integer|number|boolean|string)->boolean +NotEquals: '!='; // (integer|number|boolean|string)->boolean +And: '&&'; // boolean->boolean +Or: '||'; // boolean->boolean +Assign: '='; // (integer|number|boolean|string)->(integer|number|boolean|string) +PlusAssign: '+='; // (number|string)->(number|string) +MinusAssign: '-='; // number->number +MultiplyAssign: '*='; // number->number +DivideAssign: '/='; // number->number +ModulusAssign: '%='; // number->number +BitAndAssign: '&='; // integer->integer +BitXorAssign: '^='; // integer->integer +BitOrAssign: '|='; // integer->integer +ShiftLeftAssign: '<<='; // integer->integer +ArithmeticShiftRightAssign: '>>='; // integer->integer +LogicalShiftRightAssign: '>>>='; // integer->integer +AndAssign: '&&='; // boolean->boolean +OrAssign: '||='; // boolean->boolean + +// null字面量 +NullLiteral: 'null'; + +// 布尔字面量 BooleanLiteral: 'true' | 'false'; -IntegerLiteral: '0' | [1-9] [0-9]*; + +// 数字字面量 +DecimalIntegerLiteral: '0' | [1-9] [0-9]*; +HexIntegerLiteral: '0' [xX] HexDigit+; +OctalIntegerLiteral: '0' [oO] [0-7]+; +BinaryIntegerLiteral: '0' [bB] [01]+; + +FloatLiteral + : DecimalIntegerLiteral '.' [0-9]* ExponentPart? + | '.' [0-9]+ ExponentPart? + | DecimalIntegerLiteral ExponentPart + ; + +// 字符串字面量 StringLiteral: '"' StringCharacter* '"'; WhiteSpaces: [\t\u000B\u000C\u0020\u00A0]+ -> channel(HIDDEN); LineTerminator: [\r\n\u2028\u2029] -> channel(HIDDEN); // 标识符 -Identifier: Letter LetterOrDigit*; +Identifier: IdentifierStart IdentifierPart*; // 注释 MultiLineComment: '/*' .*? '*/' -> channel(HIDDEN); SingleLineComment: '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN); +// 错误 +UnexpectedCharacter: . -> channel(ERROR); + // Fragment规则 fragment StringCharacter : ~["\\\r\n] @@ -92,6 +116,10 @@ fragment StringCharacter fragment EscapeSequence : CharacterEscapeSequence + | '0' // no digit ahead! TODO + | HexEscapeSequence + | UnicodeEscapeSequence + | ExtendedUnicodeEscapeSequence ; fragment CharacterEscapeSequence @@ -99,21 +127,453 @@ fragment CharacterEscapeSequence | NonEscapeCharacter ; +fragment HexEscapeSequence + : 'x' HexDigit HexDigit + ; + +fragment UnicodeEscapeSequence + : 'u' HexDigit HexDigit HexDigit HexDigit + ; + +fragment ExtendedUnicodeEscapeSequence + : 'u' '{' HexDigit+ '}' + ; + fragment SingleEscapeCharacter - : ['"\\bfnrtv] + : ["\\bfnrtv] ; fragment NonEscapeCharacter - : ~['"\\bfnrtv0-9xu\r\n] + : ~["\\bfnrtv0-9xu\r\n] + ; + +fragment EscapeCharacter + : SingleEscapeCharacter + | [0-9] + | [xu] ; fragment LineContinuation : '\\' [\r\n\u2028\u2029] ; -fragment LetterOrDigit - : Letter - | [0-9] +fragment HexDigit + : [0-9a-fA-F] + ; + +fragment ExponentPart + : [eE] [+-]? [0-9]+ + ; + +fragment IdentifierPart + : IdentifierStart + | UnicodeCombiningMark + | UnicodeDigit + | UnicodeConnectorPunctuation + | '\u200C' + | '\u200D' + ; + +fragment IdentifierStart + : UnicodeLetter + | [$_] + | '\\' UnicodeEscapeSequence + ; + +fragment UnicodeLetter + : [\u0041-\u005A] + | [\u0061-\u007A] + | [\u00AA] + | [\u00B5] + | [\u00BA] + | [\u00C0-\u00D6] + | [\u00D8-\u00F6] + | [\u00F8-\u021F] + | [\u0222-\u0233] + | [\u0250-\u02AD] + | [\u02B0-\u02B8] + | [\u02BB-\u02C1] + | [\u02D0-\u02D1] + | [\u02E0-\u02E4] + | [\u02EE] + | [\u037A] + | [\u0386] + | [\u0388-\u038A] + | [\u038C] + | [\u038E-\u03A1] + | [\u03A3-\u03CE] + | [\u03D0-\u03D7] + | [\u03DA-\u03F3] + | [\u0400-\u0481] + | [\u048C-\u04C4] + | [\u04C7-\u04C8] + | [\u04CB-\u04CC] + | [\u04D0-\u04F5] + | [\u04F8-\u04F9] + | [\u0531-\u0556] + | [\u0559] + | [\u0561-\u0587] + | [\u05D0-\u05EA] + | [\u05F0-\u05F2] + | [\u0621-\u063A] + | [\u0640-\u064A] + | [\u0671-\u06D3] + | [\u06D5] + | [\u06E5-\u06E6] + | [\u06FA-\u06FC] + | [\u0710] + | [\u0712-\u072C] + | [\u0780-\u07A5] + | [\u0905-\u0939] + | [\u093D] + | [\u0950] + | [\u0958-\u0961] + | [\u0985-\u098C] + | [\u098F-\u0990] + | [\u0993-\u09A8] + | [\u09AA-\u09B0] + | [\u09B2] + | [\u09B6-\u09B9] + | [\u09DC-\u09DD] + | [\u09DF-\u09E1] + | [\u09F0-\u09F1] + | [\u0A05-\u0A0A] + | [\u0A0F-\u0A10] + | [\u0A13-\u0A28] + | [\u0A2A-\u0A30] + | [\u0A32-\u0A33] + | [\u0A35-\u0A36] + | [\u0A38-\u0A39] + | [\u0A59-\u0A5C] + | [\u0A5E] + | [\u0A72-\u0A74] + | [\u0A85-\u0A8B] + | [\u0A8D] + | [\u0A8F-\u0A91] + | [\u0A93-\u0AA8] + | [\u0AAA-\u0AB0] + | [\u0AB2-\u0AB3] + | [\u0AB5-\u0AB9] + | [\u0ABD] + | [\u0AD0] + | [\u0AE0] + | [\u0B05-\u0B0C] + | [\u0B0F-\u0B10] + | [\u0B13-\u0B28] + | [\u0B2A-\u0B30] + | [\u0B32-\u0B33] + | [\u0B36-\u0B39] + | [\u0B3D] + | [\u0B5C-\u0B5D] + | [\u0B5F-\u0B61] + | [\u0B85-\u0B8A] + | [\u0B8E-\u0B90] + | [\u0B92-\u0B95] + | [\u0B99-\u0B9A] + | [\u0B9C] + | [\u0B9E-\u0B9F] + | [\u0BA3-\u0BA4] + | [\u0BA8-\u0BAA] + | [\u0BAE-\u0BB5] + | [\u0BB7-\u0BB9] + | [\u0C05-\u0C0C] + | [\u0C0E-\u0C10] + | [\u0C12-\u0C28] + | [\u0C2A-\u0C33] + | [\u0C35-\u0C39] + | [\u0C60-\u0C61] + | [\u0C85-\u0C8C] + | [\u0C8E-\u0C90] + | [\u0C92-\u0CA8] + | [\u0CAA-\u0CB3] + | [\u0CB5-\u0CB9] + | [\u0CDE] + | [\u0CE0-\u0CE1] + | [\u0D05-\u0D0C] + | [\u0D0E-\u0D10] + | [\u0D12-\u0D28] + | [\u0D2A-\u0D39] + | [\u0D60-\u0D61] + | [\u0D85-\u0D96] + | [\u0D9A-\u0DB1] + | [\u0DB3-\u0DBB] + | [\u0DBD] + | [\u0DC0-\u0DC6] + | [\u0E01-\u0E30] + | [\u0E32-\u0E33] + | [\u0E40-\u0E46] + | [\u0E81-\u0E82] + | [\u0E84] + | [\u0E87-\u0E88] + | [\u0E8A] + | [\u0E8D] + | [\u0E94-\u0E97] + | [\u0E99-\u0E9F] + | [\u0EA1-\u0EA3] + | [\u0EA5] + | [\u0EA7] + | [\u0EAA-\u0EAB] + | [\u0EAD-\u0EB0] + | [\u0EB2-\u0EB3] + | [\u0EBD-\u0EC4] + | [\u0EC6] + | [\u0EDC-\u0EDD] + | [\u0F00] + | [\u0F40-\u0F6A] + | [\u0F88-\u0F8B] + | [\u1000-\u1021] + | [\u1023-\u1027] + | [\u1029-\u102A] + | [\u1050-\u1055] + | [\u10A0-\u10C5] + | [\u10D0-\u10F6] + | [\u1100-\u1159] + | [\u115F-\u11A2] + | [\u11A8-\u11F9] + | [\u1200-\u1206] + | [\u1208-\u1246] + | [\u1248] + | [\u124A-\u124D] + | [\u1250-\u1256] + | [\u1258] + | [\u125A-\u125D] + | [\u1260-\u1286] + | [\u1288] + | [\u128A-\u128D] + | [\u1290-\u12AE] + | [\u12B0] + | [\u12B2-\u12B5] + | [\u12B8-\u12BE] + | [\u12C0] + | [\u12C2-\u12C5] + | [\u12C8-\u12CE] + | [\u12D0-\u12D6] + | [\u12D8-\u12EE] + | [\u12F0-\u130E] + | [\u1310] + | [\u1312-\u1315] + | [\u1318-\u131E] + | [\u1320-\u1346] + | [\u1348-\u135A] + | [\u13A0-\u13B0] + | [\u13B1-\u13F4] + | [\u1401-\u1676] + | [\u1681-\u169A] + | [\u16A0-\u16EA] + | [\u1780-\u17B3] + | [\u1820-\u1877] + | [\u1880-\u18A8] + | [\u1E00-\u1E9B] + | [\u1EA0-\u1EE0] + | [\u1EE1-\u1EF9] + | [\u1F00-\u1F15] + | [\u1F18-\u1F1D] + | [\u1F20-\u1F39] + | [\u1F3A-\u1F45] + | [\u1F48-\u1F4D] + | [\u1F50-\u1F57] + | [\u1F59] + | [\u1F5B] + | [\u1F5D] + | [\u1F5F-\u1F7D] + | [\u1F80-\u1FB4] + | [\u1FB6-\u1FBC] + | [\u1FBE] + | [\u1FC2-\u1FC4] + | [\u1FC6-\u1FCC] + | [\u1FD0-\u1FD3] + | [\u1FD6-\u1FDB] + | [\u1FE0-\u1FEC] + | [\u1FF2-\u1FF4] + | [\u1FF6-\u1FFC] + | [\u207F] + | [\u2102] + | [\u2107] + | [\u210A-\u2113] + | [\u2115] + | [\u2119-\u211D] + | [\u2124] + | [\u2126] + | [\u2128] + | [\u212A-\u212D] + | [\u212F-\u2131] + | [\u2133-\u2139] + | [\u2160-\u2183] + | [\u3005-\u3007] + | [\u3021-\u3029] + | [\u3031-\u3035] + | [\u3038-\u303A] + | [\u3041-\u3094] + | [\u309D-\u309E] + | [\u30A1-\u30FA] + | [\u30FC-\u30FE] + | [\u3105-\u312C] + | [\u3131-\u318E] + | [\u31A0-\u31B7] + | [\u3400-\u4DBF] + | [\u4E00-\u9FFF] + | [\uA000-\uA48C] + | [\uAC00] + | [\uD7A3] + | [\uF900-\uFA2D] + | [\uFB00-\uFB06] + | [\uFB13-\uFB17] + | [\uFB1D] + | [\uFB1F-\uFB28] + | [\uFB2A-\uFB36] + | [\uFB38-\uFB3C] + | [\uFB3E] + | [\uFB40-\uFB41] + | [\uFB43-\uFB44] + | [\uFB46-\uFBB1] + | [\uFBD3-\uFD3D] + | [\uFD50-\uFD8F] + | [\uFD92-\uFDC7] + | [\uFDF0-\uFDFB] + | [\uFE70-\uFE72] + | [\uFE74] + | [\uFE76-\uFEFC] + | [\uFF21-\uFF3A] + | [\uFF41-\uFF5A] + | [\uFF66-\uFFBE] + | [\uFFC2-\uFFC7] + | [\uFFCA-\uFFCF] + | [\uFFD2-\uFFD7] + | [\uFFDA-\uFFDC] + ; + +fragment UnicodeCombiningMark + : [\u0300-\u034E] + | [\u0360-\u0362] + | [\u0483-\u0486] + | [\u0591-\u05A1] + | [\u05A3-\u05B9] + | [\u05BB-\u05BD] + | [\u05BF] + | [\u05C1-\u05C2] + | [\u05C4] + | [\u064B-\u0655] + | [\u0670] + | [\u06D6-\u06DC] + | [\u06DF-\u06E4] + | [\u06E7-\u06E8] + | [\u06EA-\u06ED] + | [\u0711] + | [\u0730-\u074A] + | [\u07A6-\u07B0] + | [\u0901-\u0903] + | [\u093C] + | [\u093E-\u094D] + | [\u0951-\u0954] + | [\u0962-\u0963] + | [\u0981-\u0983] + | [\u09BC-\u09C4] + | [\u09C7-\u09C8] + | [\u09CB-\u09CD] + | [\u09D7] + | [\u09E2-\u09E3] + | [\u0A02] + | [\u0A3C] + | [\u0A3E-\u0A42] + | [\u0A47-\u0A48] + | [\u0A4B-\u0A4D] + | [\u0A70-\u0A71] + | [\u0A81-\u0A83] + | [\u0ABC] + | [\u0ABE-\u0AC5] + | [\u0AC7-\u0AC9] + | [\u0ACB-\u0ACD] + | [\u0B01-\u0B03] + | [\u0B3C] + | [\u0B3E-\u0B43] + | [\u0B47-\u0B48] + | [\u0B4B-\u0B4D] + | [\u0B56-\u0B57] + | [\u0B82-\u0B83] + | [\u0BBE-\u0BC2] + | [\u0BC6-\u0BC8] + | [\u0BCA-\u0BCD] + | [\u0BD7] + | [\u0C01-\u0C03] + | [\u0C3E-\u0C44] + | [\u0C46-\u0C48] + | [\u0C4A-\u0C4D] + | [\u0C55-\u0C56] + | [\u0C82-\u0C83] + | [\u0CBE-\u0CC4] + | [\u0CC6-\u0CC8] + | [\u0CCA-\u0CCD] + | [\u0CD5-\u0CD6] + | [\u0D02-\u0D03] + | [\u0D3E-\u0D43] + | [\u0D46-\u0D48] + | [\u0D4A-\u0D4D] + | [\u0D57] + | [\u0D82-\u0D83] + | [\u0DCA] + | [\u0DCF-\u0DD4] + | [\u0DD6] + | [\u0DD8-\u0DDF] + | [\u0DF2-\u0DF3] + | [\u0E31] + | [\u0E34-\u0E3A] + | [\u0E47-\u0E4E] + | [\u0EB1] + | [\u0EB4-\u0EB9] + | [\u0EBB-\u0EBC] + | [\u0EC8-\u0ECD] + | [\u0F18-\u0F19] + | [\u0F35] + | [\u0F37] + | [\u0F39] + | [\u0F3E-\u0F3F] + | [\u0F71-\u0F84] + | [\u0F86-\u0F87] + | [\u0F90-\u0F97] + | [\u0F99-\u0FBC] + | [\u0FC6] + | [\u102C-\u1032] + | [\u1036-\u1039] + | [\u1056-\u1059] + | [\u17B4-\u17D3] + | [\u18A9] + | [\u20D0-\u20DC] + | [\u20E1] + | [\u302A-\u302F] + | [\u3099-\u309A] + | [\uFB1E] + | [\uFE20-\uFE23] + ; + +fragment UnicodeDigit + : [\u0030-\u0039] + | [\u0660-\u0669] + | [\u06F0-\u06F9] + | [\u0966-\u096F] + | [\u09E6-\u09EF] + | [\u0A66-\u0A6F] + | [\u0AE6-\u0AEF] + | [\u0B66-\u0B6F] + | [\u0BE7-\u0BEF] + | [\u0C66-\u0C6F] + | [\u0CE6-\u0CEF] + | [\u0D66-\u0D6F] + | [\u0E50-\u0E59] + | [\u0ED0-\u0ED9] + | [\u0F20-\u0F29] + | [\u1040-\u1049] + | [\u1369-\u1371] + | [\u17E0-\u17E9] + | [\u1810-\u1819] + | [\uFF10-\uFF19] ; -fragment Letter : [a-zA-Z_] ; +fragment UnicodeConnectorPunctuation + : [\u005F] + | [\u203F-\u2040] + | [\u30FB] + | [\uFE33-\uFE34] + | [\uFE4D-\uFE4F] + | [\uFF3F] + | [\uFF65] + ; diff --git a/grammar/StaticScriptParser.g4 b/grammar/StaticScriptParser.g4 index 8bb06b9..1dab858 100644 --- a/grammar/StaticScriptParser.g4 +++ b/grammar/StaticScriptParser.g4 @@ -63,15 +63,16 @@ typeAnnotation type : arrayType - | atomicType + | basicType ; arrayType - : atomicType (OpenBracket CloseBracket)+ + : basicType (OpenBracket CloseBracket)+ ; -atomicType +basicType : Boolean + | Integer | Number | String ; @@ -103,7 +104,7 @@ expression | uop=(Not | BitNot | Plus | Minus | PlusPlus | MinusMinus) expression # PrefixUnaryExpr | expression bop=(Multiply | Divide | Modulus) expression # BinaryExpr | expression bop=(Plus | Minus) expression # BinaryExpr - | expression bop=(LeftShift | RightShift) expression # BinaryExpr + | expression bop=(ShiftLeft | ArithmeticShiftRight | LogicalShiftRight) expression # BinaryExpr | expression bop=(LessThan | GreaterThan | LessThanEquals | GreaterThanEquals) expression # BinaryExpr | expression bop=(Equals | NotEquals) expression # BinaryExpr | expression bop=BitAnd expression # BinaryExpr @@ -111,7 +112,7 @@ expression | expression bop=BitOr expression # BinaryExpr | expression bop=And expression # BinaryExpr | expression bop=Or expression # BinaryExpr - | expression bop=(Assign | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | ModulusAssign | LeftShiftAssign | RightShiftAssign | BitAndAssign | BitXorAssign | BitOrAssign | AndAssign | OrAssign) expression # BinaryExpr + | expression bop=(Assign | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | ModulusAssign | ShiftLeftAssign | ArithmeticShiftRightAssign | LogicalShiftRightAssign | BitAndAssign | BitXorAssign | BitOrAssign | AndAssign | OrAssign) expression # BinaryExpr | Identifier # IdentifierExpr | literal # LiteralExpr | OpenParen expression CloseParen # ParenExpr @@ -127,7 +128,11 @@ argumentList literal : BooleanLiteral - | IntegerLiteral + | DecimalIntegerLiteral + | HexIntegerLiteral + | OctalIntegerLiteral + | BinaryIntegerLiteral + | FloatLiteral | StringLiteral | arrayLiteral ; diff --git a/include/AST/ASTBuilder.h b/include/AST/ASTBuilder.h index 96e39d0..47d2a1e 100644 --- a/include/AST/ASTBuilder.h +++ b/include/AST/ASTBuilder.h @@ -41,7 +41,7 @@ class ASTBuilder final : public StaticScriptParserVisitor { antlrcpp::Any visitArrayType(StaticScriptParser::ArrayTypeContext *ctx) override; - antlrcpp::Any visitAtomicType(StaticScriptParser::AtomicTypeContext *ctx) override; + antlrcpp::Any visitBasicType(StaticScriptParser::BasicTypeContext *ctx) override; antlrcpp::Any visitFunctionDeclaration(StaticScriptParser::FunctionDeclarationContext *ctx) override; diff --git a/include/AST/ExprNode.h b/include/AST/ExprNode.h index d257c7a..a2e95c9 100644 --- a/include/AST/ExprNode.h +++ b/include/AST/ExprNode.h @@ -15,30 +15,22 @@ class ExprNode : public Node { ~ExprNode() override = default; - SharedPtr inferType = nullptr; + SharedPtr type = nullptr; // 当前表达式的ir LLVMValue *code = nullptr; }; /// 字面量表达式节点 -class LiteralExprNode: public ExprNode { +class LiteralExprNode : public ExprNode { public: LiteralExprNode() = default; explicit LiteralExprNode(const SharedPtr &type); }; -/// 原子字面量表达式节点 -class AtomicLiteralExprNode : public LiteralExprNode { -public: - explicit AtomicLiteralExprNode(const SharedPtr &type); - - ~AtomicLiteralExprNode() override = default; -}; - /// 布尔值字面量表达式节点 -class BooleanLiteralExprNode : public AtomicLiteralExprNode { +class BooleanLiteralExprNode : public LiteralExprNode { public: explicit BooleanLiteralExprNode(bool literal); @@ -50,7 +42,7 @@ class BooleanLiteralExprNode : public AtomicLiteralExprNode { }; /// 整数字面量表达式节点 -class IntegerLiteralExprNode : public AtomicLiteralExprNode { +class IntegerLiteralExprNode : public LiteralExprNode { public: explicit IntegerLiteralExprNode(long literal); @@ -61,8 +53,20 @@ class IntegerLiteralExprNode : public AtomicLiteralExprNode { long literal; }; +/// 浮点数字面量表达式节点 +class FloatLiteralExprNode : public LiteralExprNode { +public: + explicit FloatLiteralExprNode(double literal); + + ~FloatLiteralExprNode() override = default; + + void accept(const SharedPtr &visitor) override; + + double literal; +}; + /// 字符串字面量表达式节点 -class StringLiteralExprNode : public AtomicLiteralExprNode { +class StringLiteralExprNode : public LiteralExprNode { public: explicit StringLiteralExprNode(); @@ -76,7 +80,7 @@ class StringLiteralExprNode : public AtomicLiteralExprNode { }; /// 数组字面量表达式 -class ArrayLiteralExprNode: public LiteralExprNode { +class ArrayLiteralExprNode : public LiteralExprNode { public: explicit ArrayLiteralExprNode(); @@ -153,7 +157,7 @@ class BinaryOperatorExprNode : public ExprNode { }; /// 数组下标表达式 -class ArraySubscriptExprNode: public ExprNode { +class ArraySubscriptExprNode : public ExprNode { public: ArraySubscriptExprNode(const SharedPtr &baseExpr, const SharedPtrVector &indexExprs); @@ -163,4 +167,11 @@ class ArraySubscriptExprNode: public ExprNode { SharedPtr baseExpr = nullptr; SharedPtrVector indexExprs; -}; \ No newline at end of file +}; + +//class ArithmeticExprNode; +//class BitwiseExprNode; +//class ComparisonExprNode; +//class LogicalExprNode; +//class AssignmentExprNode; +//class StringOperationExprNode; diff --git a/include/CodeGen/Builtin.h b/include/CodeGen/Builtin.h index 8b3afa2..48e56be 100644 --- a/include/CodeGen/Builtin.h +++ b/include/CodeGen/Builtin.h @@ -52,10 +52,12 @@ class BuiltinArray { static inline llvm::PointerType *type = nullptr; static inline LLVMFunction *createIntegerArrayFunc = nullptr; + static inline LLVMFunction *createFloatArrayFunc = nullptr; static inline LLVMFunction *createBooleanArrayFunc = nullptr; static inline LLVMFunction *createStringArrayFunc = nullptr; static inline LLVMFunction *createArrayArrayFunc = nullptr; static inline LLVMFunction *createIntegerArrayWithLiteralFunc = nullptr; + static inline LLVMFunction *createFloatArrayWithLiteralFunc = nullptr; static inline LLVMFunction *createBooleanArrayWithLiteralFunc = nullptr; static inline LLVMFunction *createStringArrayWithLiteralFunc = nullptr; static inline LLVMFunction *createArrayArrayWithLiteralFunc = nullptr; @@ -63,18 +65,22 @@ class BuiltinArray { static inline LLVMFunction *getSizeFunc = nullptr; static inline LLVMFunction *isNDArrayFunc = nullptr; static inline LLVMFunction *pushIntegerFunc = nullptr; + static inline LLVMFunction *pushFloatFunc = nullptr; static inline LLVMFunction *pushBooleanFunc = nullptr; static inline LLVMFunction *pushStringFunc = nullptr; static inline LLVMFunction *pushArrayFunc = nullptr; static inline LLVMFunction *popIntegerFunc = nullptr; + static inline LLVMFunction *popFloatFunc = nullptr; static inline LLVMFunction *popBooleanFunc = nullptr; static inline LLVMFunction *popStringFunc = nullptr; static inline LLVMFunction *popArrayFunc = nullptr; static inline LLVMFunction *getIntegerFunc = nullptr; + static inline LLVMFunction *getFloatFunc = nullptr; static inline LLVMFunction *getBooleanFunc = nullptr; static inline LLVMFunction *getStringFunc = nullptr; static inline LLVMFunction *getArrayFunc = nullptr; static inline LLVMFunction *setIntegerFunc = nullptr; + static inline LLVMFunction *setFloatFunc = nullptr; static inline LLVMFunction *setBooleanFunc = nullptr; static inline LLVMFunction *setStringFunc = nullptr; static inline LLVMFunction *setArrayFunc = nullptr; @@ -89,8 +95,12 @@ class BuiltinIO { static inline LLVMFunction *integer2stringFunc = nullptr; static inline LLVMFunction *string2integerFunc = nullptr; + static inline LLVMFunction *float2stringFunc = nullptr; + static inline LLVMFunction *string2floatFunc = nullptr; static inline LLVMFunction *printIntegerFunc = nullptr; static inline LLVMFunction *printlnIntegerFunc = nullptr; + static inline LLVMFunction *printFloatFunc = nullptr; + static inline LLVMFunction *printlnFloatFunc = nullptr; static inline LLVMFunction *printStringFunc = nullptr; static inline LLVMFunction *printlnStringFunc = nullptr; }; diff --git a/include/CodeGen/IRGenerator.h b/include/CodeGen/IRGenerator.h index 2727d45..546f903 100644 --- a/include/CodeGen/IRGenerator.h +++ b/include/CodeGen/IRGenerator.h @@ -8,7 +8,7 @@ #include "Support/Error.h" #include "Support/LLVM.h" -class IRGenerator final: public ASTVisitor { +class IRGenerator final : public ASTVisitor { public: explicit IRGenerator(); @@ -26,6 +26,8 @@ class IRGenerator final: public ASTVisitor { void visit(const SharedPtr &intLiteralExpr) override; + void visit(const SharedPtr &floatLiteralExpr) override; + void visit(const SharedPtr &strLiteralExpr) override; void visit(const SharedPtr &arrayLiteralExpr) override; @@ -69,6 +71,14 @@ class IRGenerator final: public ASTVisitor { void setArrayElement(const SharedPtr &asExpr, LLVMValue *valueCode); + inline LLVMValue *float2integer(LLVMValue *val) { + return llvmIRBuilder.CreateFPToSI(val, llvmIRBuilder.getInt64Ty()); + } + + inline LLVMValue *integer2float(LLVMValue *val) { + return llvmIRBuilder.CreateSIToFP(val, llvmIRBuilder.getDoubleTy()); + } + inline void setFuncInsertPoint(LLVMFunction *func) { LLVMBasicBlock *curBB = &(func->getBasicBlockList().back()); llvmIRBuilder.SetInsertPoint(curBB); diff --git a/include/Entity/Type.h b/include/Entity/Type.h index a4fb3b0..67ad23f 100644 --- a/include/Entity/Type.h +++ b/include/Entity/Type.h @@ -1,66 +1,101 @@ #pragma once +#include #include "Support/Alias.h" #include "Support/Error.h" -/// 原子类型的种类, Unknown只用于空数组的元素类型 -enum class AtomicTypeKind { - Boolean, Integer, String, Unknown +/// 基础类型的种类, Unknown只用于空数组的元素类型 +enum class BasicTypeKind { + Boolean, Integer, Float, String, Unknown }; +class ArrayType; + /// 类型节点 -class Type { +class Type : public std::enable_shared_from_this { public: virtual ~Type() = default; - [[nodiscard]] virtual bool isAtomic() const = 0; + [[nodiscard]] virtual bool isBasic() const = 0; - [[nodiscard]] virtual bool isArray() const = 0; + [[nodiscard]] virtual bool isBoolean() const = 0; + + [[nodiscard]] virtual bool isInteger() const = 0; + + [[nodiscard]] virtual bool isFloat() const = 0; + + [[nodiscard]] virtual bool isNumber() const = 0; + + [[nodiscard]] virtual bool isString() const = 0; [[nodiscard]] virtual bool isUnknown() const = 0; + [[nodiscard]] virtual bool isArray() const = 0; + + [[nodiscard]] virtual SharedPtr asArray() const = 0; + bool operator==(const Type &rhs) = delete; bool operator!=(const Type &rhs) = delete; + /// 判断类型是否相等[无方向](note: equals < sameAs < compatible) [[nodiscard]] bool equals(const SharedPtr &rhs) const; + + /// 判断类型是否相同[无方向](note: equals < sameAs < compatible) + [[nodiscard]] bool sameAs(const SharedPtr &rhs) const; + + /// 判断类型是否兼容[有方向](note: equals < sameAs < compatible) + [[nodiscard]] bool compatible(const SharedPtr &rhs) const; }; -/// 原子类型节点 -class AtomicType : public Type { +/// 基础类型节点 +class BasicType : public Type { public: - explicit AtomicType(AtomicTypeKind kind); + explicit BasicType(BasicTypeKind kind); - explicit AtomicType() = default; + explicit BasicType() = default; - ~AtomicType() override = default; + ~BasicType() override = default; - [[nodiscard]] bool isAtomic() const override; + [[nodiscard]] bool isBasic() const override; - [[nodiscard]] bool isArray() const override; + [[nodiscard]] bool isBoolean() const override; + + [[nodiscard]] bool isInteger() const override; + + [[nodiscard]] bool isFloat() const override; + + [[nodiscard]] bool isNumber() const override; + + [[nodiscard]] bool isString() const override; [[nodiscard]] bool isUnknown() const override; - [[nodiscard]] AtomicTypeKind getKind() const; + [[nodiscard]] bool isArray() const override; + + [[nodiscard]] SharedPtr asArray() const override; + + [[nodiscard]] BasicTypeKind getKind() const; - bool operator==(const AtomicType &rhs) = delete; + bool operator==(const BasicType &rhs) = delete; - bool operator!=(const AtomicType &rhs) = delete; + bool operator!=(const BasicType &rhs) = delete; - [[nodiscard]] bool equals(const SharedPtr &rhs) const; + [[nodiscard]] bool equals(const SharedPtr &rhs) const; - static inline const SharedPtr BOOLEAN_TYPE = makeShared(AtomicTypeKind::Boolean); // NOLINT - static inline const SharedPtr INTEGER_TYPE = makeShared(AtomicTypeKind::Integer); // NOLINT - static inline const SharedPtr STRING_TYPE = makeShared(AtomicTypeKind::String); // NOLINT - static inline const SharedPtr UNKNOWN_TYPE = makeShared(AtomicTypeKind::Unknown); // NOLINT + static inline const SharedPtr BOOLEAN_TYPE = makeShared(BasicTypeKind::Boolean); // NOLINT + static inline const SharedPtr INTEGER_TYPE = makeShared(BasicTypeKind::Integer); // NOLINT + static inline const SharedPtr FLOAT_TYPE = makeShared(BasicTypeKind::Float); // NOLINT + static inline const SharedPtr STRING_TYPE = makeShared(BasicTypeKind::String); // NOLINT + static inline const SharedPtr UNKNOWN_TYPE = makeShared(BasicTypeKind::Unknown); // NOLINT private: - AtomicTypeKind kind = AtomicTypeKind::Unknown; + BasicTypeKind kind = BasicTypeKind::Unknown; }; /// 数组类型节点 class ArrayType : public Type { public: - static SharedPtr createNDArrayType(const SharedPtr &atomicType, size_t depth); + static SharedPtr createNDArrayType(const SharedPtr &basicType, size_t depth); explicit ArrayType(const SharedPtr &elementType); @@ -68,24 +103,39 @@ class ArrayType : public Type { ~ArrayType() override = default; - [[nodiscard]] bool isAtomic() const override; + [[nodiscard]] bool isBasic() const override; - [[nodiscard]] bool isArray() const override; + [[nodiscard]] bool isBoolean() const override; + + [[nodiscard]] bool isInteger() const override; + + [[nodiscard]] bool isFloat() const override; + + [[nodiscard]] bool isNumber() const override; + + [[nodiscard]] bool isString() const override; [[nodiscard]] bool isUnknown() const override; + [[nodiscard]] bool isArray() const override; + + [[nodiscard]] SharedPtr asArray() const override; + + /// 获取数组的元素类型, 多维数组则获取子数组的类型 [[nodiscard]] const SharedPtr &getElementType() const; - [[nodiscard]] size_t getDepth() const; + /// 获取多维数组最内层的元素类型 + [[nodiscard]] SharedPtr getBasicElementType() const; - void setElementType(const SharedPtr &eleType); + [[nodiscard]] size_t getDepth() const; bool operator==(const ArrayType &rhs) = delete; bool operator!=(const ArrayType &rhs) = delete; [[nodiscard]] bool equals(const SharedPtr &rhs) const; + private: - SharedPtr elementType = AtomicType::UNKNOWN_TYPE; + SharedPtr elementType = BasicType::UNKNOWN_TYPE; size_t depth = 1; }; \ No newline at end of file diff --git a/include/Sema/ASTVisitor.h b/include/Sema/ASTVisitor.h index fc78b49..6adea99 100644 --- a/include/Sema/ASTVisitor.h +++ b/include/Sema/ASTVisitor.h @@ -27,6 +27,8 @@ class ASTVisitor : public std::enable_shared_from_this { virtual void visit(const SharedPtr &intLiteralExpr); + virtual void visit(const SharedPtr &floatLiteralExpr); + virtual void visit(const SharedPtr &strLiteralExpr); virtual void visit(const SharedPtr &arrayLiteralExpr); diff --git a/include/Sema/ScopeScanner.h b/include/Sema/ScopeScanner.h index a780943..a49c86f 100644 --- a/include/Sema/ScopeScanner.h +++ b/include/Sema/ScopeScanner.h @@ -18,6 +18,8 @@ class ScopeScanner final : public ASTVisitorWithScope { void visit(const SharedPtr &intLiteralExpr) override; + void visit(const SharedPtr &floatLiteralExpr) override; + void visit(const SharedPtr &strLiteralExpr) override; void visit(const SharedPtr &arrayLiteralExpr) override; diff --git a/include/Support/Alias.h b/include/Support/Alias.h index ee6b0b1..9872581 100644 --- a/include/Support/Alias.h +++ b/include/Support/Alias.h @@ -48,3 +48,9 @@ template inline SharedPtr dynPtrCast(const SharedPtr

&ptr) { return std::dynamic_pointer_cast(ptr); } + + +template +inline SharedPtr constPtrCast(const SharedPtr

&ptr) { + return std::const_pointer_cast(ptr); +} \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 80d4dd8..090fedd 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -7,7 +7,6 @@ add_custom_command( COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/lib/ss_string.c -std=c99 -O2 -o ss_string.bc COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/lib/ss_array.c -std=c99 -O2 -o ss_array.bc COMMAND clang -c -emit-llvm ${PROJECT_SOURCE_DIR}/lib/ss_io.c -std=c99 -O2 -o ss_io.bc - COMMAND sudo mkdir -p /usr/local/staticscript/lib && sudo cp ss_*.bc /usr/local/staticscript/lib DEPENDS ${BUILTIN_LIB_SOURCES} ) diff --git a/lib/ss_array.c b/lib/ss_array.c index 58ac07b..868ab2e 100644 --- a/lib/ss_array.c +++ b/lib/ss_array.c @@ -4,6 +4,10 @@ ss_array *ss_array_create_integer_array(ss_error **error) { return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(long), error); } +ss_array *ss_array_create_float_array(ss_error **error) { + return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(double), error); +} + ss_array *ss_array_create_boolean_array(ss_error **error) { return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(bool), error); } @@ -25,6 +29,10 @@ ss_array *ss_array_create_integer_array_with_literal(long literal_list[], size_t ss_array_create_array_with_literal(long, false) } +ss_array *ss_array_create_float_array_with_literal(double literal_list[], size_t size, ss_error **error) { + ss_array_create_array_with_literal(double, false) +} + ss_array *ss_array_create_boolean_array_with_literal(bool literal_list[], size_t size, ss_error **error) { ss_array_create_array_with_literal(bool, false) } @@ -67,6 +75,11 @@ void ss_array_push_integer(ss_array *arr, long new_element, ss_error **error) { ss_array_push(long) } +void ss_array_push_float(ss_array *arr, double new_element, ss_error **error) { + ss_array_push_detect() + ss_array_push(double) +} + void ss_array_push_boolean(ss_array *arr, bool new_element, ss_error **error) { ss_array_push_detect() ss_array_push(bool) @@ -87,6 +100,11 @@ long ss_array_pop_integer(ss_array *arr, ss_error **error) { ss_array_pop(long) } +double ss_array_pop_float(ss_array *arr, ss_error **error) { + ss_array_pop_detect(0) + ss_array_pop(double) +} + bool ss_array_pop_boolean(ss_array *arr, ss_error **error) { ss_array_pop_detect(false) ss_array_pop(bool) @@ -107,6 +125,11 @@ long ss_array_get_integer(ss_array *arr, size_t index, ss_error **error) { ss_array_get(long) } +double ss_array_get_float(ss_array *arr, size_t index, ss_error **error) { + ss_array_get_detect(0) + ss_array_get(double) +} + bool ss_array_get_boolean(ss_array *arr, size_t index, ss_error **error) { ss_array_get_detect(false) ss_array_get(bool) @@ -127,6 +150,11 @@ void ss_array_set_integer(ss_array *arr, size_t index, long element, ss_error ** ss_array_set(long) } +void ss_array_set_float(ss_array *arr, size_t index, double element, ss_error **error) { + ss_array_set_detect() + ss_array_set(double) +} + void ss_array_set_boolean(ss_array *arr, size_t index, bool element, ss_error **error) { ss_array_set_detect() ss_array_set(bool) diff --git a/lib/ss_array.h b/lib/ss_array.h index 0d45603..f2d87d4 100644 --- a/lib/ss_array.h +++ b/lib/ss_array.h @@ -51,6 +51,8 @@ static ss_array *ss_array_create(size_t capacity, size_t element_size, ss_error ss_array *ss_array_create_integer_array(ss_error **error); +ss_array *ss_array_create_float_array(ss_error **error); + ss_array *ss_array_create_boolean_array(ss_error **error); ss_array *ss_array_create_string_array(ss_error **error); @@ -70,6 +72,8 @@ ss_array *ss_array_create_array_array(ss_error **error); ss_array *ss_array_create_integer_array_with_literal(long literal_list[], size_t size, ss_error **error); +ss_array *ss_array_create_float_array_with_literal(double literal_list[], size_t size, ss_error **error); + ss_array *ss_array_create_boolean_array_with_literal(bool literal_list[], size_t size, ss_error **error); ss_array *ss_array_create_string_array_with_literal(ss_string *literal_list[], size_t size, ss_error **error); @@ -97,6 +101,8 @@ bool ss_array_is_nd_array(ss_array *arr, ss_error **error); void ss_array_push_integer(ss_array *arr, long new_element, ss_error **error); +void ss_array_push_float(ss_array *arr, double new_element, ss_error **error); + void ss_array_push_boolean(ss_array *arr, bool new_element, ss_error **error); void ss_array_push_string(ss_array *arr, ss_string *new_element, ss_error **error); @@ -115,6 +121,8 @@ void ss_array_push_array(ss_array *arr, ss_array *new_element, ss_error **error) long ss_array_pop_integer(ss_array *arr, ss_error **error); +double ss_array_pop_float(ss_array *arr, ss_error **error); + bool ss_array_pop_boolean(ss_array *arr, ss_error **error); ss_string *ss_array_pop_string(ss_array *arr, ss_error **error); @@ -133,6 +141,8 @@ ss_array *ss_array_pop_array(ss_array *arr, ss_error **error); long ss_array_get_integer(ss_array *arr, size_t index, ss_error **error); +double ss_array_get_float(ss_array *arr, size_t index, ss_error **error); + bool ss_array_get_boolean(ss_array *arr, size_t index, ss_error **error); ss_string *ss_array_get_string(ss_array *arr, size_t index, ss_error **error); @@ -151,6 +161,8 @@ ss_array *ss_array_get_array(ss_array *arr, size_t index, ss_error **error); void ss_array_set_integer(ss_array *arr, size_t index, long element, ss_error **error); +void ss_array_set_float(ss_array *arr, size_t index, double element, ss_error **error); + void ss_array_set_boolean(ss_array *arr, size_t index, bool element, ss_error **error); void ss_array_set_string(ss_array *arr, size_t index, ss_string *element, ss_error **error); diff --git a/lib/ss_io.c b/lib/ss_io.c index abf04ef..1d55c41 100644 --- a/lib/ss_io.c +++ b/lib/ss_io.c @@ -8,10 +8,22 @@ ss_string *ss_integer2string(long number) { return str; } +ss_string *ss_float2string(double number) { + size_t capacity = ss_string_get_capacity_with_size(20); + ss_string *str = ss_string_create_with_capacity(capacity); + snprintf(str->buffer, 20, "%lf", number); + str->size = strlen(str->buffer); + return str; +} + long ss_string2integer(ss_string *str) { return strtol(str->buffer, NULL, 10); } +double ss_string2float(ss_string *str) { + return strtod(str->buffer, NULL); +} + void ss_print_integer(long number) { printf("%ld", number); } @@ -20,6 +32,14 @@ void ss_println_integer(long number) { printf("%ld\n", number); } +void ss_print_float(double number) { + printf("%lf", number); +} + +void ss_println_float(double number) { + printf("%lf\n", number); +} + void ss_print_string(ss_string *str) { printf("%s", str->buffer); } diff --git a/lib/ss_io.h b/lib/ss_io.h index e6057bc..890983e 100644 --- a/lib/ss_io.h +++ b/lib/ss_io.h @@ -6,12 +6,20 @@ ss_string *ss_integer2string(long number); +ss_string *ss_float2string(double number); + long ss_string2integer(ss_string *str); +double ss_string2float(ss_string *str); + void ss_print_integer(long number); void ss_println_integer(long number); +void ss_print_float(double number); + +void ss_println_float(double number); + void ss_print_string(ss_string *str); void ss_println_string(ss_string *str); \ No newline at end of file diff --git a/src/AST/ASTBuilder.cpp b/src/AST/ASTBuilder.cpp index c55bb1d..afcf639 100644 --- a/src/AST/ASTBuilder.cpp +++ b/src/AST/ASTBuilder.cpp @@ -1,5 +1,7 @@ #include "AST/ASTBuilder.h" +// TODO: bindChildrenInversely + ASTBuilder::ASTBuilder(String filename) : filename(std::move(filename)) {} antlrcpp::Any ASTBuilder::visitModule(StaticScriptParser::ModuleContext *ctx) { @@ -125,24 +127,26 @@ antlrcpp::Any ASTBuilder::visitType(StaticScriptParser::TypeContext *ctx) { if (ctx->arrayType()) { type = visitArrayType(ctx->arrayType()).as>(); } else { - type = visitAtomicType(ctx->atomicType()).as>(); + type = visitBasicType(ctx->basicType()).as>(); } return type; } antlrcpp::Any ASTBuilder::visitArrayType(StaticScriptParser::ArrayTypeContext *ctx) { - const SharedPtr &atomicType = visitAtomicType(ctx->atomicType()); + const SharedPtr &basicType = visitBasicType(ctx->basicType()); size_t depth = ctx->OpenBracket().size(); - return ArrayType::createNDArrayType(atomicType, depth); + return ArrayType::createNDArrayType(basicType, depth); } -antlrcpp::Any ASTBuilder::visitAtomicType(StaticScriptParser::AtomicTypeContext *ctx) { +antlrcpp::Any ASTBuilder::visitBasicType(StaticScriptParser::BasicTypeContext *ctx) { if (ctx->Boolean()) { - return AtomicType::BOOLEAN_TYPE; + return BasicType::BOOLEAN_TYPE; + } else if (ctx->Integer()) { + return BasicType::INTEGER_TYPE; } else if (ctx->Number()) { - return AtomicType::INTEGER_TYPE; + return BasicType::FLOAT_TYPE; } - return AtomicType::STRING_TYPE; + return BasicType::STRING_TYPE; } antlrcpp::Any ASTBuilder::visitFunctionDeclaration(StaticScriptParser::FunctionDeclarationContext *ctx) { @@ -292,15 +296,36 @@ antlrcpp::Any ASTBuilder::visitLiteral(StaticScriptParser::LiteralContext *ctx) if (ctx->BooleanLiteral()) { bool literal = ctx->BooleanLiteral()->getText() == "true"; literalExpr = makeShared(literal); - } else if (ctx->IntegerLiteral()) { - int literal = std::stoi(ctx->IntegerLiteral()->getText()); - literalExpr = makeShared(literal); } else if (ctx->StringLiteral()) { String literal = ctx->StringLiteral()->getText(); literal = literal.substr(1, literal.size() - 2); literalExpr = makeShared(literal); } else if (ctx->arrayLiteral()) { literalExpr = visitArrayLiteral(ctx->arrayLiteral()).as>(); + } else if (ctx->FloatLiteral()) { + double literal = std::stod(ctx->FloatLiteral()->getText()); + literalExpr = makeShared(literal); + } else { + int base = 10; + String literalStr; + if (ctx->DecimalIntegerLiteral()) { + base = 10; + literalStr = ctx->DecimalIntegerLiteral()->getText(); + } else if (ctx->HexIntegerLiteral()) { + base = 16; + literalStr = ctx->HexIntegerLiteral()->getText(); + literalStr = literalStr.substr(2, literalStr.size() - 2); + } else if (ctx->OctalIntegerLiteral()) { + base = 8; + literalStr = ctx->OctalIntegerLiteral()->getText(); + literalStr = literalStr.substr(2, literalStr.size() - 2); + } else if (ctx->BinaryIntegerLiteral()) { + base = 2; + literalStr = ctx->BinaryIntegerLiteral()->getText(); + literalStr = literalStr.substr(2, literalStr.size() - 2); + } + long literal = std::stol(literalStr, nullptr, base); + literalExpr = makeShared(literal); } return literalExpr; } diff --git a/src/AST/DeclNode.cpp b/src/AST/DeclNode.cpp index 07c220d..5351c9d 100644 --- a/src/AST/DeclNode.cpp +++ b/src/AST/DeclNode.cpp @@ -54,16 +54,22 @@ void ParmVarDeclNode::accept(const SharedPtr &visitor) { SharedPtrMap FunctionDeclNode::getBuiltinFunctions() { SharedPtrMap functions; - SharedPtr booleanArg = makeShared(AtomicType::BOOLEAN_TYPE); - SharedPtr integerArg = makeShared(AtomicType::INTEGER_TYPE); - SharedPtr strArg = makeShared(AtomicType::STRING_TYPE); + SharedPtr booleanArg = makeShared(BasicType::BOOLEAN_TYPE); + SharedPtr integerArg = makeShared(BasicType::INTEGER_TYPE); + SharedPtr floatArg = makeShared(BasicType::FLOAT_TYPE); + SharedPtr strArg = makeShared(BasicType::STRING_TYPE); SharedPtrVector booleanArgs{booleanArg}; SharedPtrVector integerArgs{integerArg}; + SharedPtrVector floatArgs{floatArg}; SharedPtrVector strArgs{strArg}; - functions["ss_integer2string"] = makeShared("ss_integer2string", integerArgs, AtomicType::STRING_TYPE); - functions["ss_string2integer"] = makeShared("ss_string2integer", strArgs, AtomicType::INTEGER_TYPE); + functions["ss_integer2string"] = makeShared("ss_integer2string", integerArgs, BasicType::STRING_TYPE); + functions["ss_string2integer"] = makeShared("ss_string2integer", strArgs, BasicType::INTEGER_TYPE); + functions["ss_float2string"] = makeShared("ss_float2string", floatArgs, BasicType::STRING_TYPE); + functions["ss_string2float"] = makeShared("ss_string2float", strArgs, BasicType::FLOAT_TYPE); functions["ss_print_integer"] = makeShared("ss_print_integer", integerArgs); functions["ss_println_integer"] = makeShared("ss_println_integer", integerArgs); + functions["ss_print_float"] = makeShared("ss_print_float", floatArgs); + functions["ss_println_float"] = makeShared("ss_println_float", floatArgs); functions["ss_print_string"] = makeShared("ss_print_string", strArgs); functions["ss_println_string"] = makeShared("ss_println_string", strArgs); functions["ss_assert"] = makeShared("ss_assert", booleanArgs); diff --git a/src/AST/ExprNode.cpp b/src/AST/ExprNode.cpp index c43fb11..711029b 100644 --- a/src/AST/ExprNode.cpp +++ b/src/AST/ExprNode.cpp @@ -2,33 +2,37 @@ #include "AST/ExprNode.h" #include "Sema/ASTVisitor.h" -ExprNode::ExprNode(const SharedPtr &type) : inferType(type) {} +ExprNode::ExprNode(const SharedPtr &type) : type(type) {} LiteralExprNode::LiteralExprNode(const SharedPtr &type) : ExprNode(type) {} -AtomicLiteralExprNode::AtomicLiteralExprNode(const SharedPtr &type) : LiteralExprNode(type) {} +BooleanLiteralExprNode::BooleanLiteralExprNode(bool literal) : literal(literal), LiteralExprNode(BasicType::BOOLEAN_TYPE) {} -IntegerLiteralExprNode::IntegerLiteralExprNode(long literal) : literal(literal), AtomicLiteralExprNode(AtomicType::INTEGER_TYPE) {} +void BooleanLiteralExprNode::accept(const SharedPtr &visitor) { + visitor->visit(staticPtrCast(shared_from_this())); +} + +IntegerLiteralExprNode::IntegerLiteralExprNode(long literal) : literal(literal), LiteralExprNode(BasicType::INTEGER_TYPE) {} void IntegerLiteralExprNode::accept(const SharedPtr &visitor) { visitor->visit(staticPtrCast(shared_from_this())); } -BooleanLiteralExprNode::BooleanLiteralExprNode(bool literal) : literal(literal), AtomicLiteralExprNode(AtomicType::BOOLEAN_TYPE) {} +FloatLiteralExprNode::FloatLiteralExprNode(double literal) : literal(literal), LiteralExprNode(BasicType::FLOAT_TYPE) {} -void BooleanLiteralExprNode::accept(const SharedPtr &visitor) { - visitor->visit(staticPtrCast(shared_from_this())); +void FloatLiteralExprNode::accept(const SharedPtr &visitor) { + visitor->visit(staticPtrCast(shared_from_this())); } -StringLiteralExprNode::StringLiteralExprNode() : AtomicLiteralExprNode(AtomicType::STRING_TYPE) {} +StringLiteralExprNode::StringLiteralExprNode() : LiteralExprNode(BasicType::STRING_TYPE) {} -StringLiteralExprNode::StringLiteralExprNode(String literal) : literal(std::move(literal)), AtomicLiteralExprNode(AtomicType::STRING_TYPE) {} +StringLiteralExprNode::StringLiteralExprNode(String literal) : literal(std::move(literal)), LiteralExprNode(BasicType::STRING_TYPE) {} void StringLiteralExprNode::accept(const SharedPtr &visitor) { visitor->visit(staticPtrCast(shared_from_this())); } -ArrayLiteralExprNode::ArrayLiteralExprNode() : LiteralExprNode(AtomicType::UNKNOWN_TYPE) {} +ArrayLiteralExprNode::ArrayLiteralExprNode() : LiteralExprNode(BasicType::UNKNOWN_TYPE) {} ArrayLiteralExprNode::ArrayLiteralExprNode(const SharedPtr &type) : LiteralExprNode(type) {} diff --git a/src/CodeGen/Builtin.cpp b/src/CodeGen/Builtin.cpp index 26d3ef9..34a61cd 100644 --- a/src/CodeGen/Builtin.cpp +++ b/src/CodeGen/Builtin.cpp @@ -17,6 +17,7 @@ void Builtin::initialize(LLVMModule &module, LLVMContext &context, const llvm::T } libDir = libPath.str(); } else { + // 在当前执行目录下搜索lib目录 llvm::sys::fs::current_path(libPath); llvm::sys::path::append(libPath, "lib"); if (llvm::sys::fs::exists(libPath) && llvm::sys::fs::is_directory(libPath)) { @@ -24,6 +25,10 @@ void Builtin::initialize(LLVMModule &module, LLVMContext &context, const llvm::T } } + if (!llvm::sys::fs::exists(libDir)) { + reportLinkError("The library directory '" + libDir + "'does not exist"); + } + BuiltinError::linkModule(module, context); BuiltinString::linkModule(module, context); BuiltinArray::linkModule(module, context); @@ -85,10 +90,12 @@ void BuiltinArray::getTypeAndFunction(LLVMModule &module) { llvm::StructType *arrStructType = module.getTypeByName("struct.ss_array"); type = arrStructType->getPointerTo(); createIntegerArrayFunc = module.getFunction("ss_array_create_integer_array"); + createFloatArrayFunc = module.getFunction("ss_array_create_float_array"); createBooleanArrayFunc = module.getFunction("ss_array_create_boolean_array"); createStringArrayFunc = module.getFunction("ss_array_create_string_array"); createArrayArrayFunc = module.getFunction("ss_array_create_array_array"); createIntegerArrayWithLiteralFunc = module.getFunction("ss_array_create_integer_array_with_literal"); + createFloatArrayWithLiteralFunc = module.getFunction("ss_array_create_float_array_with_literal"); createBooleanArrayWithLiteralFunc = module.getFunction("ss_array_create_boolean_array_with_literal"); createStringArrayWithLiteralFunc = module.getFunction("ss_array_create_string_array_with_literal"); createArrayArrayWithLiteralFunc = module.getFunction("ss_array_create_array_array_with_literal"); @@ -96,18 +103,22 @@ void BuiltinArray::getTypeAndFunction(LLVMModule &module) { getSizeFunc = module.getFunction("ss_array_get_size"); isNDArrayFunc = module.getFunction("ss_array_is_nd_array"); pushIntegerFunc = module.getFunction("ss_array_push_integer"); + pushFloatFunc = module.getFunction("ss_array_push_float"); pushBooleanFunc = module.getFunction("ss_array_push_boolean"); pushStringFunc = module.getFunction("ss_array_push_string"); pushArrayFunc = module.getFunction("ss_array_push_array"); popIntegerFunc = module.getFunction("ss_array_pop_integer"); + popFloatFunc = module.getFunction("ss_array_pop_float"); popBooleanFunc = module.getFunction("ss_array_pop_boolean"); popStringFunc = module.getFunction("ss_array_pop_string"); popArrayFunc = module.getFunction("ss_array_pop_array"); getIntegerFunc = module.getFunction("ss_array_get_integer"); + getFloatFunc = module.getFunction("ss_array_get_float"); getBooleanFunc = module.getFunction("ss_array_get_boolean"); getStringFunc = module.getFunction("ss_array_get_string"); getArrayFunc = module.getFunction("ss_array_get_array"); setIntegerFunc = module.getFunction("ss_array_set_integer"); + setFloatFunc = module.getFunction("ss_array_set_float"); setBooleanFunc = module.getFunction("ss_array_set_boolean"); setStringFunc = module.getFunction("ss_array_set_string"); setArrayFunc = module.getFunction("ss_array_set_array"); @@ -121,8 +132,12 @@ void BuiltinIO::linkModule(LLVMModule &module, LLVMContext &context) { void BuiltinIO::getTypeAndFunction(LLVMModule &module) { integer2stringFunc = module.getFunction("ss_integer2string"); string2integerFunc = module.getFunction("ss_string2integer"); + float2stringFunc = module.getFunction("ss_float2string"); + string2floatFunc = module.getFunction("ss_string2float"); printIntegerFunc = module.getFunction("ss_print_integer"); printlnIntegerFunc = module.getFunction("ss_println_integer"); + printFloatFunc = module.getFunction("ss_print_float"); + printlnFloatFunc = module.getFunction("ss_println_float"); printStringFunc = module.getFunction("ss_print_string"); printlnStringFunc = module.getFunction("ss_println_string"); } \ No newline at end of file diff --git a/src/CodeGen/DeclCodeGen.cpp b/src/CodeGen/DeclCodeGen.cpp index 4e97e21..33caf22 100644 --- a/src/CodeGen/DeclCodeGen.cpp +++ b/src/CodeGen/DeclCodeGen.cpp @@ -4,24 +4,36 @@ void IRGenerator::visit(const SharedPtr &varDecl) { ASTVisitor::visit(varDecl); LLVMType *type = getType(varDecl->type); - // 变量有初始值 - bool hasInitVal = bool(varDecl->initVal); + LLVMValue *varInitCode = varDecl->initVal->code; // 区分全局变量和局部变量 if (varDecl->scope->isTopLevel()) { bool isLiteralInit = bool(dynPtrCast(varDecl->initVal)); - bool isBoolean = varDecl->type->equals(AtomicType::BOOLEAN_TYPE); - bool isInteger = varDecl->type->equals(AtomicType::INTEGER_TYPE); - bool isStringVar = varDecl->type->equals(AtomicType::STRING_TYPE); - bool isArrayVar = varDecl->type->isArray(); + bool declBoolean = varDecl->type->isBoolean(); + bool declFloat = varDecl->type->isFloat(); + bool initFloat = varDecl->initVal->type->isFloat(); + bool declString = varDecl->type->isString(); + bool declArray = varDecl->type->isArray(); llvm::Constant *initializer; - if (isStringVar || isArrayVar) { + if (declString || declArray) { initializer = llvm::ConstantPointerNull::getNullValue(type); } else if (isLiteralInit) { - // 初始值为布尔和整数字面量 - initializer = llvm::cast(varDecl->initVal->code); + if (declFloat && initFloat) { + initializer = llvm::cast(varInitCode); + } else if (declFloat && !initFloat) { + initializer = llvm::cast(integer2float(varInitCode)); + } else if (!declFloat && initFloat) { + initializer = llvm::cast(float2integer(varInitCode)); + } else { + initializer = llvm::cast(varInitCode); + } } else { - // 初始值为布尔和整数类型的表达式 - initializer = llvm::ConstantInt::get(type, 0); + if (declFloat) { + // 初始值为浮点表达式 + initializer = llvm::ConstantFP::get(type, 0.0); + } else { + // 初始值为布尔和整数表达式 + initializer = llvm::ConstantInt::get(type, 0); + } } auto *gVar = new LLVMGlobalVariable( *llvmModule, @@ -32,20 +44,25 @@ void IRGenerator::visit(const SharedPtr &varDecl) { varDecl->name ); uint64_t alignment = 8; - if (isBoolean) { + if (declBoolean) { alignment = 1; } gVar->setAlignment(llvm::MaybeAlign(alignment)); // 在main函数中store字符串和数组的指针值或者非字面量的表达式值 - if (isStringVar || isArrayVar || (hasInitVal && !isLiteralInit)) { - llvmIRBuilder.CreateStore(varDecl->initVal->code, gVar); + if (declString || declArray) { + llvmIRBuilder.CreateStore(varInitCode, gVar); + } else if (!isLiteralInit) { + if (declFloat && !initFloat) { + varInitCode = integer2float(varInitCode); + } else if (!declFloat && initFloat) { + varInitCode = float2integer(varInitCode); + } + llvmIRBuilder.CreateStore(varInitCode, gVar); } varDecl->code = gVar; } else { LLVMValue *alloca = llvmIRBuilder.CreateAlloca(type); - if (hasInitVal) { - llvmIRBuilder.CreateStore(varDecl->initVal->code, alloca); - } + llvmIRBuilder.CreateStore(varInitCode, alloca); varDecl->code = alloca; } } @@ -57,7 +74,7 @@ void IRGenerator::visit(const SharedPtr ¶mVarDecl) { void IRGenerator::visit(const SharedPtr &funcDecl) { Vector argsType; - for (const SharedPtr ¶m: funcDecl->params) { + for (const auto ¶m: funcDecl->params) { argsType.push_back(getType(param->type)); } LLVMType *returnType = getType(funcDecl->returnType); diff --git a/src/CodeGen/ExprCodeGen.cpp b/src/CodeGen/ExprCodeGen.cpp index 539f3cb..106b28a 100644 --- a/src/CodeGen/ExprCodeGen.cpp +++ b/src/CodeGen/ExprCodeGen.cpp @@ -1,9 +1,7 @@ #include "CodeGen/IRGenerator.h" void IRGenerator::visit(const SharedPtr &boolLiteralExpr) { - boolLiteralExpr->code = boolLiteralExpr->literal ? - llvmIRBuilder.getTrue() : - llvmIRBuilder.getFalse(); + boolLiteralExpr->code = boolLiteralExpr->literal ? llvmIRBuilder.getTrue() : llvmIRBuilder.getFalse(); } void IRGenerator::visit(const SharedPtr &intLiteralExpr) { @@ -14,25 +12,84 @@ void IRGenerator::visit(const SharedPtr &intLiteralExpr) ); } +void IRGenerator::visit(const SharedPtr &floatLiteralExpr) { + floatLiteralExpr->code = llvm::ConstantFP::get(llvmIRBuilder.getDoubleTy(), floatLiteralExpr->literal); +} + void IRGenerator::visit(const SharedPtr &strLiteralExpr) { llvm::Constant *literal = llvmIRBuilder.CreateGlobalString(strLiteralExpr->literal); llvm::Value *argLiteral = llvmIRBuilder.CreatePointerCast(literal, llvmIRBuilder.getInt8PtrTy()); strLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinString::createFunc, argLiteral); } +#define GenerateBasicArray(LiteralType, UpperCaseType, AllocaType) \ + llvm::Constant *literalConstants = llvm::ConstantDataArray::get(llvmContext, literalVector); \ + auto *globalLiteralConstants = new LLVMGlobalVariable( \ + *llvmModule, \ + literalConstants->getType(), \ + true, \ + LLVMGlobalValue::PrivateLinkage, \ + literalConstants \ + ); \ + LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( \ + llvm::ArrayType::get(llvmIRBuilder.get##AllocaType##Ty(), elementSize) \ + ); \ + LLVMValue *castedLiteralListLoad = llvmIRBuilder.CreateBitCast(literalListAlloca, llvmIRBuilder.getInt8PtrTy()); \ + LLVMValue *castedLiteralConstants = llvmIRBuilder.CreateBitCast(globalLiteralConstants, llvmIRBuilder.getInt8PtrTy()); \ + Vector memcpyArgs{ \ + castedLiteralListLoad, \ + castedLiteralConstants, \ + llvmIRBuilder.getInt64(sizeof(LiteralType) * elementSize), \ + llvmIRBuilder.getFalse() \ + }; \ + llvmIRBuilder.CreateCall(memcpyFunc, memcpyArgs); \ + LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( \ + literalListAlloca, \ + Vector{ \ + llvmIRBuilder.getInt64(0), \ + llvmIRBuilder.getInt64(0) \ + } \ + ); \ + Vector createArgs{literalListPtr, elementSizeCode, errorAlloca}; \ + arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::create##UpperCaseType##ArrayWithLiteralFunc, createArgs); \ + +#define GenerateComplexArray(UpperCaseType) \ + LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( \ + llvm::ArrayType::get(Builtin##UpperCaseType::type, elementSize) \ + ); \ + for (size_t i = 0; i < elementSize; ++i) { \ + LLVMValue *literalItemPtr = llvmIRBuilder.CreateInBoundsGEP( \ + literalListAlloca, Vector{ \ + llvmIRBuilder.getInt64(0), \ + llvmIRBuilder.getInt64(i) \ + } \ + ); \ + llvmIRBuilder.CreateStore(arrayLiteralExpr->elements[i]->code, literalItemPtr); \ + } \ + LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( \ + literalListAlloca, \ + Vector{ \ + llvmIRBuilder.getInt64(0), \ + llvmIRBuilder.getInt64(0) \ + } \ + ); \ + Vector createArgs{literalListPtr, elementSizeCode, errorAlloca}; \ + arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::create##UpperCaseType##ArrayWithLiteralFunc, createArgs); \ + void IRGenerator::visit(const SharedPtr &arrayLiteralExpr) { - SharedPtr arrayType = staticPtrCast(arrayLiteralExpr->inferType); - SharedPtr elementType = arrayType->getElementType(); + const SharedPtr &elementType = arrayLiteralExpr->type->asArray()->getElementType(); LLVMValue *errorAlloca = llvmIRBuilder.CreateAlloca(BuiltinError::type); llvmIRBuilder.CreateStore(llvm::ConstantPointerNull::get(BuiltinError::type), errorAlloca); if (arrayLiteralExpr->elements.empty()) { LLVMFunction *createFunc = nullptr; - if (elementType->equals(AtomicType::BOOLEAN_TYPE)) { + if (elementType->isBoolean()) { createFunc = BuiltinArray::createBooleanArrayFunc; - } else if (elementType->equals(AtomicType::INTEGER_TYPE)) { + } else if (elementType->isInteger()) { createFunc = BuiltinArray::createIntegerArrayFunc; - } else if (elementType->equals(AtomicType::STRING_TYPE)) { + } else if (elementType->isFloat()) { + createFunc = BuiltinArray::createFloatArrayFunc; + } else if (elementType->isString()) { createFunc = BuiltinArray::createStringArrayFunc; } else if (elementType->isArray()) { createFunc = BuiltinArray::createArrayArrayFunc; @@ -40,8 +97,8 @@ void IRGenerator::visit(const SharedPtr &arrayLiteralExpr) arrayLiteralExpr->code = llvmIRBuilder.CreateCall(createFunc, errorAlloca); } else { // TODO: 优化策略: 当size < 7时, 使用memset+store替代memcpy - size_t size = arrayLiteralExpr->elements.size(); - LLVMValue *sizeLoad = llvmIRBuilder.getInt64(size); + size_t elementSize = arrayLiteralExpr->elements.size(); + LLVMValue *elementSizeCode = llvmIRBuilder.getInt64(elementSize); LLVMFunction *memcpyFunc = llvm::Intrinsic::getDeclaration( llvmModule.get(), llvm::Intrinsic::memcpy, @@ -52,129 +109,36 @@ void IRGenerator::visit(const SharedPtr &arrayLiteralExpr) } ); reportOnCodeGenError(!memcpyFunc, "Not found llvm.memcpy intrinsic function"); - if (elementType->equals(AtomicType::BOOLEAN_TYPE)) { + if (elementType->isBoolean()) { // 避免使用vector - Vector literalVector(size); - for (size_t i = 0; i < size; ++i) { + Vector literalVector(elementSize); + for (size_t i = 0; i < elementSize; ++i) { literalVector[i] = staticPtrCast(arrayLiteralExpr->elements[i])->literal; } - llvm::Constant *literalConstants = llvm::ConstantDataArray::get(llvmContext, literalVector); - auto *globalLiteralConstants = new LLVMGlobalVariable( - *llvmModule, - literalConstants->getType(), - true, - LLVMGlobalValue::PrivateLinkage, - literalConstants - ); - LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( - llvm::ArrayType::get(llvmIRBuilder.getInt8Ty(), size) - ); - LLVMValue *castedLiteralListLoad = llvmIRBuilder.CreateBitCast(literalListAlloca, llvmIRBuilder.getInt8PtrTy()); - LLVMValue *castedLiteralConstants = llvmIRBuilder.CreateBitCast(globalLiteralConstants, llvmIRBuilder.getInt8PtrTy()); - Vector memcpyArgs{ - castedLiteralListLoad, - castedLiteralConstants, - llvmIRBuilder.getInt64(sizeof(unsigned char) * size), - llvmIRBuilder.getFalse() - }; - llvmIRBuilder.CreateCall(memcpyFunc, memcpyArgs); - LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( - literalListAlloca, - Vector{ - llvmIRBuilder.getInt64(0), - llvmIRBuilder.getInt64(0) - } - ); - Vector createArgs{literalListPtr, sizeLoad, errorAlloca}; - arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::createBooleanArrayWithLiteralFunc, createArgs); - } else if (elementType->equals(AtomicType::INTEGER_TYPE)) { - Vector literalVector(size); - for (size_t i = 0; i < size; ++i) { + GenerateBasicArray(unsigned char, Boolean, Int8) + } else if (elementType->isInteger()) { + Vector literalVector(elementSize); + for (size_t i = 0; i < elementSize; ++i) { literalVector[i] = staticPtrCast(arrayLiteralExpr->elements[i])->literal; } - llvm::Constant *literalConstants = llvm::ConstantDataArray::get(llvmContext, literalVector); - auto *globalLiteralConstants = new LLVMGlobalVariable( - *llvmModule, - literalConstants->getType(), - true, - LLVMGlobalValue::PrivateLinkage, - literalConstants - ); - LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( - llvm::ArrayType::get(llvmIRBuilder.getInt64Ty(), size) - ); - LLVMValue *castedLiteralListLoad = llvmIRBuilder.CreateBitCast(literalListAlloca, llvmIRBuilder.getInt8PtrTy()); - LLVMValue *castedLiteralConstants = llvmIRBuilder.CreateBitCast(globalLiteralConstants, llvmIRBuilder.getInt8PtrTy()); - Vector memcpyArgs{ - castedLiteralListLoad, - castedLiteralConstants, - llvmIRBuilder.getInt64(sizeof(long) * size), - llvmIRBuilder.getFalse() - }; - llvmIRBuilder.CreateCall(memcpyFunc, memcpyArgs); - LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( - literalListAlloca, - Vector{ - llvmIRBuilder.getInt64(0), - llvmIRBuilder.getInt64(0) - } - ); - Vector createArgs{literalListPtr, sizeLoad, errorAlloca}; - arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::createIntegerArrayWithLiteralFunc, createArgs); - } else if (elementType->equals(AtomicType::STRING_TYPE)) { - ASTVisitor::visit(arrayLiteralExpr); - - LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( - llvm::ArrayType::get(BuiltinString::type, size) - ); - - for (size_t i = 0; i < size; ++i) { - LLVMValue *literalItemPtr = llvmIRBuilder.CreateInBoundsGEP( - literalListAlloca, Vector{ - llvmIRBuilder.getInt64(0), - llvmIRBuilder.getInt64(i) - } - ); - llvmIRBuilder.CreateStore(arrayLiteralExpr->elements[i]->code, literalItemPtr); + GenerateBasicArray(long, Integer, Int64) + } else if (elementType->isFloat()) { + Vector literalVector(elementSize); + for (size_t i = 0; i < elementSize; ++i) { + const SharedPtr &element = arrayLiteralExpr->elements[i]; + if (const auto &floatElement = dynPtrCast(element)) { + literalVector[i] = floatElement->literal; + } else if (const auto &intElement = dynPtrCast(element)) { + literalVector[i] = intElement->literal; + } } - - LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( - literalListAlloca, - Vector{ - llvmIRBuilder.getInt64(0), - llvmIRBuilder.getInt64(0) - } - ); - - Vector createArgs{literalListPtr, sizeLoad, errorAlloca}; - arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::createStringArrayWithLiteralFunc, createArgs); + GenerateBasicArray(double, Float, Double) + } else if (elementType->isString()) { + ASTVisitor::visit(arrayLiteralExpr); + GenerateComplexArray(String) } else if (elementType->isArray()) { ASTVisitor::visit(arrayLiteralExpr); - - LLVMValue *literalListAlloca = llvmIRBuilder.CreateAlloca( - llvm::ArrayType::get(BuiltinArray::type, size) - ); - - for (size_t i = 0; i < size; ++i) { - LLVMValue *literalItemPtr = llvmIRBuilder.CreateInBoundsGEP( - literalListAlloca, Vector{ - llvmIRBuilder.getInt64(0), - llvmIRBuilder.getInt64(i) - } - ); - llvmIRBuilder.CreateStore(arrayLiteralExpr->elements[i]->code, literalItemPtr); - } - - LLVMValue *literalListPtr = llvmIRBuilder.CreateInBoundsGEP( - literalListAlloca, - Vector{ - llvmIRBuilder.getInt64(0), - llvmIRBuilder.getInt64(0) - } - ); - - Vector createArgs{literalListPtr, sizeLoad, errorAlloca}; - arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::createArrayArrayWithLiteralFunc, createArgs); + GenerateComplexArray(Array) } } LLVMValue *errorLoad = llvmIRBuilder.CreateLoad(errorAlloca); @@ -188,14 +152,25 @@ void IRGenerator::visit(const SharedPtr &varExpr) { void IRGenerator::visit(const SharedPtr &callExpr) { ASTVisitor::visit(callExpr); + const SharedPtrVector &args = callExpr->args; + const SharedPtrVector ¶ms = callExpr->refFuncDecl->params; + size_t argsSize = args.size(); llvm::Function *calleeFunc = llvmModule->getFunction(callExpr->calleeName); reportOnCodeGenError(!calleeFunc, "Function '" + callExpr->calleeName + "' is not found"); - if (calleeFunc->arg_size() != callExpr->args.size()) { + if (argsSize != calleeFunc->arg_size()) { reportCodeGenError("The number of parameters of function declaration and arguments of call expression does not match"); } Vector argsV; - for (size_t i = 0, e = callExpr->args.size(); i != e; ++i) { - argsV.push_back(callExpr->args[i]->code); + for (size_t i = 0, e = argsSize; i != e; ++i) { + LLVMValue *argV = args[i]->code; + if (args[i]->type->isInteger() && params[i]->type->isFloat()) { + // 将整型实参转为浮点型 + argV = integer2float(argV); + } else if (args[i]->type->isFloat() && params[i]->type->isInteger()) { + // 将浮点型实参转为整型 + argV = float2integer(argV); + } + argsV.push_back(argV); } callExpr->code = llvmIRBuilder.CreateCall(calleeFunc, argsV); } @@ -205,17 +180,27 @@ void IRGenerator::visit(const SharedPtr &uopExpr) { SharedPtr subExpr = uopExpr->subExpr; + bool isFloat = uopExpr->subExpr->type->isFloat(); + switch (uopExpr->opCode) { case StaticScriptLexer::PlusPlus: case StaticScriptLexer::MinusMinus: { LLVMValue *value = subExpr->code; LLVMValue *newValue; - LLVMConstantInt *one = llvmIRBuilder.getInt64(1); - - if (uopExpr->opCode == StaticScriptLexer::PlusPlus) { - newValue = llvmIRBuilder.CreateNSWAdd(value, one); + if (isFloat) { + llvm::Constant *one = llvm::ConstantFP::get(llvmIRBuilder.getDoubleTy(), 1.0); + if (uopExpr->opCode == StaticScriptLexer::PlusPlus) { + newValue = llvmIRBuilder.CreateFAdd(value, one); + } else { + newValue = llvmIRBuilder.CreateFSub(value, one); + } } else { - newValue = llvmIRBuilder.CreateNSWSub(value, one); + LLVMConstantInt *one = llvmIRBuilder.getInt64(1); + if (uopExpr->opCode == StaticScriptLexer::PlusPlus) { + newValue = llvmIRBuilder.CreateNSWAdd(value, one); + } else { + newValue = llvmIRBuilder.CreateNSWSub(value, one); + } } if (const auto &subVarExpr = dynPtrCast(subExpr)) { llvmIRBuilder.CreateStore(newValue, subVarExpr->refVarDecl->code); @@ -238,7 +223,11 @@ void IRGenerator::visit(const SharedPtr &uopExpr) { break; } case StaticScriptLexer::Minus: { - uopExpr->code = llvmIRBuilder.CreateNSWNeg(subExpr->code); + if (isFloat) { + uopExpr->code = llvmIRBuilder.CreateFNeg(subExpr->code); + } else { + uopExpr->code = llvmIRBuilder.CreateNSWNeg(subExpr->code); + } break; } } @@ -247,49 +236,76 @@ void IRGenerator::visit(const SharedPtr &uopExpr) { void IRGenerator::visit(const SharedPtr &bopExpr) { unsigned int bopCode = bopExpr->opCode; - - /// 平凡赋值: lhs是左值, rhs是右值 - /// 符合赋值: lhs是左值也是右值, rhs是右值; `lhs op= rhs` 相当于 `lhs = lhs op rhs` + // 平凡赋值: lhs是左值, rhs是右值 + // 复合赋值: lhs是左值也是右值, rhs是右值; `lhs op= rhs` 相当于 `lhs = lhs op rhs` if (bopCode == StaticScriptLexer::Assign) { bopExpr->rhs->accept(shared_from_this()); } else { ASTVisitor::visit(bopExpr); } - const SharedPtr &type = bopExpr->lhs->inferType; + const SharedPtr &leftType = bopExpr->lhs->type; + const SharedPtr &rightType = bopExpr->rhs->type; LLVMValue *lhsCode = bopExpr->lhs->code; LLVMValue *rhsCode = bopExpr->rhs->code; LLVMValue *targetCode = nullptr; + SharedPtr type = leftType; + + if (leftType->isInteger() && rightType->isFloat()) { + type = rightType; + lhsCode = integer2float(lhsCode); + } else if (leftType->isFloat() && rightType->isInteger()) { + rhsCode = integer2float(rhsCode); + } + switch (bopCode) { case StaticScriptLexer::Plus: case StaticScriptLexer::PlusAssign: { - if (type->equals(AtomicType::STRING_TYPE)) { + if (type->isString()) { Vector argsV{lhsCode, rhsCode}; targetCode = llvmIRBuilder.CreateCall(BuiltinString::concatFunc, argsV); - } else if (type->equals(AtomicType::INTEGER_TYPE)) { + } else if (type->isInteger()) { targetCode = llvmIRBuilder.CreateNSWAdd(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFAdd(lhsCode, rhsCode); } break; } case StaticScriptLexer::Minus: case StaticScriptLexer::MinusAssign: { - targetCode = llvmIRBuilder.CreateNSWSub(lhsCode, rhsCode); + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateNSWSub(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFSub(lhsCode, rhsCode); + } break; } case StaticScriptLexer::Multiply: case StaticScriptLexer::MultiplyAssign: { - targetCode = llvmIRBuilder.CreateNSWMul(lhsCode, rhsCode); + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateNSWMul(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFMul(lhsCode, rhsCode); + } break; } case StaticScriptLexer::Divide: case StaticScriptLexer::DivideAssign: { - targetCode = llvmIRBuilder.CreateSDiv(lhsCode, rhsCode); + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateSDiv(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFDiv(lhsCode, rhsCode); + } break; } case StaticScriptLexer::Modulus: case StaticScriptLexer::ModulusAssign: { - targetCode = llvmIRBuilder.CreateSRem(lhsCode, rhsCode); + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateSRem(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFRem(lhsCode, rhsCode); + } break; } case StaticScriptLexer::BitAnd: @@ -307,16 +323,21 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { targetCode = llvmIRBuilder.CreateOr(lhsCode, rhsCode); break; } - case StaticScriptLexer::LeftShift: - case StaticScriptLexer::LeftShiftAssign: { + case StaticScriptLexer::ShiftLeft: + case StaticScriptLexer::ShiftLeftAssign: { targetCode = llvmIRBuilder.CreateShl(lhsCode, rhsCode); break; } - case StaticScriptLexer::RightShift: - case StaticScriptLexer::RightShiftAssign: { + case StaticScriptLexer::ArithmeticShiftRight: + case StaticScriptLexer::ArithmeticShiftRightAssign: { targetCode = llvmIRBuilder.CreateAShr(lhsCode, rhsCode); break; } + case StaticScriptLexer::LogicalShiftRight: + case StaticScriptLexer::LogicalShiftRightAssign: { + targetCode = llvmIRBuilder.CreateLShr(lhsCode, rhsCode); + break; + } case StaticScriptLexer::And: case StaticScriptLexer::AndAssign: { targetCode = llvmIRBuilder.CreateAnd(lhsCode, rhsCode); @@ -328,38 +349,58 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { break; } case StaticScriptLexer::LessThan: { - targetCode = llvmIRBuilder.CreateICmpSLT(lhsCode, rhsCode); + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpSLT(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOLE(lhsCode, rhsCode); + } break; } case StaticScriptLexer::GreaterThan: { - targetCode = llvmIRBuilder.CreateICmpSGT(lhsCode, rhsCode); + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpSGT(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOGT(lhsCode, rhsCode); + } break; } case StaticScriptLexer::LessThanEquals: { - targetCode = llvmIRBuilder.CreateICmpSLE(lhsCode, rhsCode); + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpSLE(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOLE(lhsCode, rhsCode); + } break; } case StaticScriptLexer::GreaterThanEquals: { - targetCode = llvmIRBuilder.CreateICmpSGE(lhsCode, rhsCode); + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpSGE(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOGE(lhsCode, rhsCode); + } break; } case StaticScriptLexer::Equals: { - if (type->equals(AtomicType::STRING_TYPE)) { + if (type->isString()) { Vector argsV{lhsCode, rhsCode}; LLVMValue *relationship = llvmIRBuilder.CreateCall(BuiltinString::equalsFunc, argsV); targetCode = llvmIRBuilder.CreateICmpEQ(relationship, llvmIRBuilder.getInt64(0)); - } else { + } else if (type->isBoolean() || type->isInteger()) { targetCode = llvmIRBuilder.CreateICmpEQ(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOEQ(lhsCode, rhsCode); } break; } case StaticScriptLexer::NotEquals: { - if (type->equals(AtomicType::STRING_TYPE)) { + if (type->isString()) { Vector argsV{lhsCode, rhsCode}; LLVMValue *relationship = llvmIRBuilder.CreateCall(BuiltinString::equalsFunc, argsV); targetCode = llvmIRBuilder.CreateICmpNE(relationship, llvmIRBuilder.getInt64(0)); - } else { + } else if (type->isBoolean() || type->isInteger()) { targetCode = llvmIRBuilder.CreateICmpNE(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpONE(lhsCode, rhsCode); } break; } @@ -376,6 +417,8 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { llvmIRBuilder.CreateStore(targetCode, leftVarExpr->refVarDecl->code); } else if (const auto &leftAsExpr = dynPtrCast(bopExpr->lhs)) { setArrayElement(leftAsExpr, targetCode); + } else { + reportCodeGenError("No assignment code was generated"); } } bopExpr->code = targetCode; @@ -383,18 +426,20 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { void IRGenerator::visit(const SharedPtr &asExpr) { ASTVisitor::visit(asExpr); - SharedPtr iterType = asExpr->baseExpr->inferType; + SharedPtr iterType = asExpr->baseExpr->type; LLVMValue *iterCode = asExpr->baseExpr->code; LLVMValue *errorAlloca = llvmIRBuilder.CreateAlloca(BuiltinError::type); llvmIRBuilder.CreateStore(llvm::ConstantPointerNull::get(BuiltinError::type), errorAlloca); for (const SharedPtr &indexExpr: asExpr->indexExprs) { LLVMFunction *getFunc = nullptr; - SharedPtr iterEleType = staticPtrCast(iterType)->getElementType(); - if (iterEleType->equals(AtomicType::BOOLEAN_TYPE)) { + SharedPtr iterEleType = iterType->asArray()->getElementType(); + if (iterEleType->isBoolean()) { getFunc = BuiltinArray::getBooleanFunc; - } else if (iterEleType->equals(AtomicType::INTEGER_TYPE)) { + } else if (iterEleType->isInteger()) { getFunc = BuiltinArray::getIntegerFunc; - } else if (iterEleType->equals(AtomicType::STRING_TYPE)) { + } else if (iterEleType->isFloat()) { + getFunc = BuiltinArray::getFloatFunc; + } else if (iterEleType->isString()) { getFunc = BuiltinArray::getStringFunc; } else if (iterEleType->isArray()) { getFunc = BuiltinArray::getArrayFunc; diff --git a/src/CodeGen/IRGenerator.cpp b/src/CodeGen/IRGenerator.cpp index d08c0c2..55872c2 100644 --- a/src/CodeGen/IRGenerator.cpp +++ b/src/CodeGen/IRGenerator.cpp @@ -26,11 +26,13 @@ void IRGenerator::visit(const SharedPtr &module) { LLVMType *IRGenerator::getType(const SharedPtr &inputType) { LLVMType *type = llvmIRBuilder.getVoidTy(); if (inputType) { - if (inputType->equals(AtomicType::BOOLEAN_TYPE)) { + if (inputType->isBoolean()) { type = llvmIRBuilder.getInt1Ty(); - } else if (inputType->equals(AtomicType::INTEGER_TYPE)) { + } else if (inputType->isInteger()) { type = llvmIRBuilder.getInt64Ty(); - } else if (inputType->equals(AtomicType::STRING_TYPE)) { + } else if (inputType->isFloat()) { + type = llvmIRBuilder.getDoubleTy(); + } else if (inputType->isString()) { type = BuiltinString::type; } else if (inputType->isArray()) { type = BuiltinArray::type; @@ -66,22 +68,24 @@ void IRGenerator::emitBranch(LLVMBasicBlock *targetBB) { void IRGenerator::setArrayElement(const SharedPtr &asExpr, LLVMValue *valueCode) { size_t indexExprsSize = asExpr->indexExprs.size(); - SharedPtr iterType = asExpr->baseExpr->inferType; + SharedPtr iterType = asExpr->baseExpr->type; LLVMValue *iterBaseCode = asExpr->baseExpr->code; LLVMValue *errorAlloca = llvmIRBuilder.CreateAlloca(BuiltinError::type); llvmIRBuilder.CreateStore(llvm::ConstantPointerNull::get(BuiltinError::type), errorAlloca); for (size_t i = 0; i < indexExprsSize; ++i) { LLVMFunction *accessFunc = nullptr; - bool accessByGet = i < indexExprsSize - 1; - const SharedPtr &iterEleType = staticPtrCast(iterType)->getElementType(); - if (iterEleType->equals(AtomicType::BOOLEAN_TYPE)) { - accessFunc = accessByGet ? BuiltinArray::getBooleanFunc : BuiltinArray::setBooleanFunc; - } else if (iterEleType->equals(AtomicType::INTEGER_TYPE)) { - accessFunc = accessByGet ? BuiltinArray::getIntegerFunc : BuiltinArray::setIntegerFunc; - } else if (iterEleType->equals(AtomicType::STRING_TYPE)) { - accessFunc = accessByGet ? BuiltinArray::getStringFunc : BuiltinArray::setStringFunc; + bool accessBySet = i == indexExprsSize - 1; + const SharedPtr &iterEleType = iterType->asArray()->getElementType(); + if (iterEleType->isBoolean()) { + accessFunc = accessBySet ? BuiltinArray::setBooleanFunc : BuiltinArray::getBooleanFunc; + } else if (iterEleType->isInteger()) { + accessFunc = accessBySet ? BuiltinArray::setIntegerFunc : BuiltinArray::getIntegerFunc; + } else if (iterEleType->isFloat()) { + accessFunc = accessBySet ? BuiltinArray::setFloatFunc : BuiltinArray::getFloatFunc; + } else if (iterEleType->isString()) { + accessFunc = accessBySet ? BuiltinArray::setStringFunc : BuiltinArray::getStringFunc; } else if (iterEleType->isArray()) { - accessFunc = accessByGet ? BuiltinArray::getArrayFunc : BuiltinArray::setArrayFunc; + accessFunc = accessBySet ? BuiltinArray::setArrayFunc : BuiltinArray::getArrayFunc; } iterType = iterEleType; Vector accessArgs{ @@ -89,7 +93,9 @@ void IRGenerator::setArrayElement(const SharedPtr &asExp asExpr->indexExprs[i]->code, errorAlloca }; - if (!accessByGet) { + // 准备给多维数组最内层的元素赋值 + if (accessBySet) { + // getFunc和setFunc参数不同, setFunc的第三个参数是需要赋的值 accessArgs.insert(accessArgs.begin() + 2, valueCode); } iterBaseCode = llvmIRBuilder.CreateCall(accessFunc, accessArgs); diff --git a/src/CodeGen/StmtCodeGen.cpp b/src/CodeGen/StmtCodeGen.cpp index 0cc240f..dad5c9e 100644 --- a/src/CodeGen/StmtCodeGen.cpp +++ b/src/CodeGen/StmtCodeGen.cpp @@ -121,8 +121,18 @@ void IRGenerator::visit(const SharedPtr &breakStmt) { // TODO: 处理函数中间的return语句 void IRGenerator::visit(const SharedPtr &returnStmt) { ASTVisitor::visit(returnStmt); + const auto &returnExpr = returnStmt->returnExpr; + const auto &returnType = returnStmt->refFuncDecl->returnType; if (returnStmt->returnExpr) { - llvmIRBuilder.CreateRet(returnStmt->returnExpr->code); + if (returnExpr->type->isInteger() && returnType->isFloat()) { + // 将整型返回值转为浮点型 + returnExpr->code = integer2float(returnExpr->code); + } else if (returnExpr->type->isFloat() && returnType->isInteger()) { + // 将浮点返回值转为整型 + returnExpr->code = float2integer(returnExpr->code); + } + + llvmIRBuilder.CreateRet(returnExpr->code); } else { llvmIRBuilder.CreateRetVoid(); } diff --git a/src/Entity/Type.cpp b/src/Entity/Type.cpp index b38d6ae..8eaf394 100644 --- a/src/Entity/Type.cpp +++ b/src/Entity/Type.cpp @@ -6,12 +6,12 @@ bool Type::equals(const SharedPtr &rhs) const { return false; } // 如果比较的两类型不属于同类, 直接返回false - if (isAtomic() && rhs->isArray() || isArray() && rhs->isAtomic()) { + if (isBasic() && rhs->isArray() || isArray() && rhs->isBasic()) { return false; } - if (SharedPtr atomicType = dynPtrCast(rhs)) { - const auto *type = dynamic_cast(this); - return type->equals(atomicType); + if (SharedPtr basicType = dynPtrCast(rhs)) { + const auto *type = dynamic_cast(this); + return type->equals(basicType); } else { SharedPtr arrayType = dynPtrCast(rhs); const auto *type = dynamic_cast(this); @@ -19,31 +19,105 @@ bool Type::equals(const SharedPtr &rhs) const { } } -AtomicType::AtomicType(AtomicTypeKind kind) : kind(kind) {} +// 相同类型(包括相等类型和数字类型) +bool Type::sameAs(const SharedPtr &rhs) const { + // 右操作子为空直接返回false + if (!rhs) { + return false; + } + // 相等的类型是兼容的 + if (equals(rhs)) { + return true; + } + // 整数和浮点数是兼容的 + if (isNumber() && rhs->isNumber()) { + return true; + } + return false; +} -bool AtomicType::isAtomic() const { +// 左操作子: 形式上的类型(左值的类型(变量声明类型), 函数形参类型) +// 右操作子: 实际的类型(右值的类型(变量赋值类型), 函数实参类型) +// 兼容是有方向的, 左操作子兼容右操作子并不一定代表右操作子兼容左操作子 +bool Type::compatible(const SharedPtr &rhs) const { + // 右操作子为空直接返回false + if (!rhs) { + return false; + } + // 同类的类型是兼容的 + if (equals(rhs) || sameAs(rhs)) { + return true; + } + if (isArray() && rhs->isArray()) { + const auto &arrayType = asArray(); + const auto &rhsArrayType = rhs->asArray(); + // 左右操作子都是数组, 且右操作子为空树组, 左操作子深度不小于右操作子深度, 左操作子兼容右操作子(反之不成立) + if (rhs->isUnknown() && arrayType->getDepth() >= rhsArrayType->getDepth()) { + return true; + } + const auto &basicType = arrayType->getBasicElementType(); + const auto &rhsBasicType = rhsArrayType->getBasicElementType(); + // 内层元素都是数字的多维数组相互兼容 + if (basicType->isNumber() && rhsBasicType->isNumber() && arrayType->getDepth() == rhsArrayType->getDepth()) { + return true; + } + } else if (isArray() && rhs->isBasic()) { + // 左操作子为数组, 右操作子为unknown基础类型, 左操作子兼容右操作子(反之不成立) + if (rhs->isUnknown()) { + return true; + } + } + return false; +} + +BasicType::BasicType(BasicTypeKind kind) : kind(kind) {} + +bool BasicType::isBasic() const { return true; } -bool AtomicType::isArray() const { +bool BasicType::isBoolean() const { + return kind == BasicTypeKind::Boolean; +} + +bool BasicType::isInteger() const { + return kind == BasicTypeKind::Integer; +} + +bool BasicType::isFloat() const { + return kind == BasicTypeKind::Float; +} + +bool BasicType::isNumber() const { + return kind == BasicTypeKind::Integer || kind == BasicTypeKind::Float; +} + +bool BasicType::isString() const { + return kind == BasicTypeKind::String; +} + +bool BasicType::isUnknown() const { + return kind == BasicTypeKind::Unknown; +} + +bool BasicType::isArray() const { return false; } -bool AtomicType::isUnknown() const { - return kind == AtomicTypeKind::Unknown; +SharedPtr BasicType::asArray() const { + return nullptr; } -AtomicTypeKind AtomicType::getKind() const { +BasicTypeKind BasicType::getKind() const { return kind; } -bool AtomicType::equals(const SharedPtr &rhs) const { +bool BasicType::equals(const SharedPtr &rhs) const { return kind == rhs->kind; } -SharedPtr ArrayType::createNDArrayType(const SharedPtr &atomicType, size_t depth) { - reportOnTypeError(depth == 0, "Dimension of multidimensional array must be positive"); - SharedPtr type = atomicType; +SharedPtr ArrayType::createNDArrayType(const SharedPtr &basicType, size_t depth) { + SharedPtr type = basicType; for (size_t i = 0; i < depth; ++i) { type = makeShared(type); } @@ -52,28 +126,59 @@ SharedPtr ArrayType::createNDArrayType(const SharedPtr &a ArrayType::ArrayType(const SharedPtr &elementType) : elementType(elementType) { if (elementType->isArray()) { - depth = staticPtrCast(elementType)->depth + 1; + depth = elementType->asArray()->depth + 1; } } -bool ArrayType::isAtomic() const { +bool ArrayType::isBasic() const { + return false; +} + +bool ArrayType::isBoolean() const { + return false; +} + +bool ArrayType::isInteger() const { + return false; +} + +bool ArrayType::isFloat() const { return false; } +bool ArrayType::isNumber() const { + return false; +} + +bool ArrayType::isString() const { + return false; +} + +bool ArrayType::isUnknown() const { + return getBasicElementType()->isUnknown(); +} + bool ArrayType::isArray() const { return true; } -bool ArrayType::isUnknown() const { - return elementType->isUnknown(); +SharedPtr ArrayType::asArray() const { + return constPtrCast(staticPtrCast(shared_from_this())); } const SharedPtr &ArrayType::getElementType() const { return elementType; } -void ArrayType::setElementType(const SharedPtr &eleType) { - elementType = eleType; +SharedPtr ArrayType::getBasicElementType() const { + if (elementType->isBasic()) { + return elementType; + } + SharedPtr iterType = elementType; + while (iterType->isArray()) { + iterType = iterType->asArray()->elementType; + } + return iterType; } bool ArrayType::equals(const SharedPtr &rhs) const { diff --git a/src/Sema/ASTVisitor.cpp b/src/Sema/ASTVisitor.cpp index 765ee8f..5ae68d2 100644 --- a/src/Sema/ASTVisitor.cpp +++ b/src/Sema/ASTVisitor.cpp @@ -29,6 +29,8 @@ void ASTVisitor::visit(const SharedPtr &boolLiteralExpr) void ASTVisitor::visit(const SharedPtr &intLiteralExpr) {} +void ASTVisitor::visit(const SharedPtr &floatLiteralExpr) {} + void ASTVisitor::visit(const SharedPtr &strLiteralExpr) {} void ASTVisitor::visit(const SharedPtr &arrayLiteralExpr) { diff --git a/src/Sema/ScopeScanner.cpp b/src/Sema/ScopeScanner.cpp index 13d8c96..15201ad 100644 --- a/src/Sema/ScopeScanner.cpp +++ b/src/Sema/ScopeScanner.cpp @@ -41,6 +41,10 @@ void ScopeScanner::visit(const SharedPtr &intLiteralExpr intLiteralExpr->scope = getCurrentScope(); } +void ScopeScanner::visit(const SharedPtr &floatLiteralExpr) { + floatLiteralExpr->scope = getCurrentScope(); +} + void ScopeScanner::visit(const SharedPtr &strLiteralExpr) { strLiteralExpr->scope = getCurrentScope(); } diff --git a/src/Sema/TypeChecker.cpp b/src/Sema/TypeChecker.cpp index 14dcc0d..554f476 100644 --- a/src/Sema/TypeChecker.cpp +++ b/src/Sema/TypeChecker.cpp @@ -2,53 +2,57 @@ #include void TypeChecker::visit(const SharedPtr &varDecl) { - if (varDecl->initVal) { - varDecl->initVal->accept(shared_from_this()); - SharedPtr &initValInferType = varDecl->initVal->inferType; - if (varDecl->type) { - if (!varDecl->type->equals(initValInferType)) { - if (varDecl->type->isArray() && initValInferType->isArray() && initValInferType->isUnknown()) { - } else { - reportTypeError("The variable declaration type does not match the initial value type: " + varDecl->name); - } + SharedPtr &varDeclType = varDecl->type; + SharedPtr &varInitVal = varDecl->initVal; + if (varInitVal) { + varInitVal->accept(shared_from_this()); + const SharedPtr &initValType = varInitVal->type; + if (varDeclType) { + if (!varDeclType->compatible(initValType)) { + reportTypeError("The variable declaration type does not match the initial value type: " + varDecl->name); } - } else if (!initValInferType) { + } else if (!initValType) { reportTypeError("Unknown variable initialization type"); } else { - if (initValInferType->isArray() && initValInferType->isUnknown()) { + if (initValType->isArray() && initValType->isUnknown()) { reportTypeError( - "When an empty array literal is used as the initial value of a variable, the variable must explicitly specify the type"); + "When an empty array literal is used as the initial value of a variable, the variable must explicitly specify the type" + ); } - varDecl->type = initValInferType; + varDeclType = initValType; } - if (varDecl->type->isArray()) { + if (varDeclType->isArray()) { // 把显式的声明类型递归赋回给初始值的类型 - assignTypeForArrayLiteral(staticPtrCast(varDecl->initVal), staticPtrCast(varDecl->type)); + const auto &initArrayLiteral = staticPtrCast(varInitVal); + const auto &varArrayType = staticPtrCast(varDeclType); + assignTypeForArrayLiteral(initArrayLiteral, varArrayType); } } else { - reportOnTypeError(!varDecl->type, "Variable has no type specified: " + varDecl->name); + reportOnTypeError(!varDeclType, "Variable has no type specified: " + varDecl->name); // 如果变量声明时未指定初始值, 则默认为其类型对应的零值 - if (varDecl->type->isAtomic()) { - if (varDecl->type->equals(AtomicType::BOOLEAN_TYPE)) { - varDecl->initVal = makeShared(false); - } else if (varDecl->type->equals(AtomicType::INTEGER_TYPE)) { - varDecl->initVal = makeShared(0); - } else if (varDecl->type->equals(AtomicType::STRING_TYPE)) { - varDecl->initVal = makeShared(); - } - } else if (varDecl->type->isArray()) { - varDecl->initVal = makeShared(varDecl->type); + SharedPtr initVal; + if (varDeclType->isBoolean()) { + initVal = makeShared(false); + } else if (varDeclType->isInteger()) { + initVal = makeShared(0); + } else if (varDeclType->isFloat()) { + initVal = makeShared(0.0); + } else if (varDeclType->isString()) { + initVal = makeShared(); + } else if (varDeclType->isArray()) { + initVal = makeShared(varDeclType); } + varInitVal = initVal; } } void TypeChecker::visit(const SharedPtr &asExpr) { ASTVisitor::visit(asExpr); - if (!asExpr->baseExpr->inferType->isArray()) { + if (!asExpr->baseExpr->type->isArray()) { reportTypeError("Base of array subscript expression is not array type"); } for (const SharedPtr &indexExpr : asExpr->indexExprs) { - if (!indexExpr->inferType->equals(AtomicType::INTEGER_TYPE)) { + if (!indexExpr->type->isInteger()) { reportTypeError( "The subscript of an array subscript expression can only be a natural number. The current subscript is not of integer type"); } @@ -56,173 +60,215 @@ void TypeChecker::visit(const SharedPtr &asExpr) { reportTypeError("The subscript of an array subscript expression can only be a natural number. The current subscript is less than 0"); } } - SharedPtr iterType = asExpr->baseExpr->inferType; + SharedPtr iterType = asExpr->baseExpr->type; for (size_t i = 0; i < asExpr->indexExprs.size(); ++i) { - iterType = staticPtrCast(iterType)->getElementType(); + iterType = iterType->asArray()->getElementType(); } - asExpr->inferType = iterType; + asExpr->type = iterType; } void TypeChecker::visit(const SharedPtr &callExpr) { ASTVisitor::visit(callExpr); - size_t argsSize = callExpr->args.size(); - size_t paramsSize = callExpr->refFuncDecl->params.size(); + const SharedPtrVector &args = callExpr->args; + const SharedPtrVector ¶ms = callExpr->refFuncDecl->params; + size_t argsSize = args.size(); + size_t paramsSize = params.size(); if (argsSize != paramsSize) { reportTypeError("The number of arguments did not match when calling '" + callExpr->calleeName + "' function"); } for (size_t i = 0; i < argsSize; ++i) { - if (!callExpr->args[i]->inferType->equals(callExpr->refFuncDecl->params[i]->type)) { + if (!params[i]->type->compatible(args[i]->type)) { reportTypeError( "The type of the " + std::to_string(i + 1) + "-th parameter does not match when calling '" + callExpr->calleeName + - "' function"); + "' function" + ); } } - callExpr->inferType = callExpr->refFuncDecl->returnType; + callExpr->type = callExpr->refFuncDecl->returnType; } void TypeChecker::visit(const SharedPtr &uopExpr) { ASTVisitor::visit(uopExpr); unsigned int uop = uopExpr->opCode; - const SharedPtr &inferType = uopExpr->subExpr->inferType; + const SharedPtr &type = uopExpr->subExpr->type; - // 分别检查!运算符和其他运算符的算子类型 - if (uop == StaticScriptLexer::Not) { - // !运算符的算子必须是布尔类型 - if (!inferType->equals(AtomicType::BOOLEAN_TYPE)) { - reportTypeError("Operator '!' allows only Boolean operations"); + switch (uop) { + case StaticScriptLexer::Plus: + case StaticScriptLexer::Minus: + case StaticScriptLexer::PlusPlus: + case StaticScriptLexer::MinusMinus: { + if (!type->isNumber()) { + reportTypeError("The current unary operator only allows operations on numbers"); + } + break; } - } else { - // 其他运算符的算子必须是整数类型 - if (!inferType->equals(AtomicType::INTEGER_TYPE)) { - reportTypeError("The current unary operator only allows operations on integers"); + case StaticScriptLexer::Not: { + // !运算符的算子必须是布尔类型 + if (!type->isBoolean()) { + reportTypeError("Operator '!' allows only Boolean operations"); + } + break; + } + case StaticScriptLexer::BitNot: { + if (!type->isInteger()) { + reportTypeError("The current unary operator only allows operations on integers"); + } + break; } + default: + break; } - uopExpr->inferType = inferType; + uopExpr->type = type; } void TypeChecker::visit(const SharedPtr &bopExpr) { ASTVisitor::visit(bopExpr); unsigned int bop = bopExpr->opCode; - const SharedPtr &leftInferType = bopExpr->lhs->inferType; - const SharedPtr &rightInferType = bopExpr->rhs->inferType; + const SharedPtr &leftType = bopExpr->lhs->type; + const SharedPtr &rightType = bopExpr->rhs->type; - // 所有的二元运算符只支持运算同类型的表达式 - if (!leftInferType->equals(rightInferType)) { + // 二元运算符只支持基础类型 + if (!leftType->isBasic() || !rightType->isBasic()) { + reportTypeError("Binary operators only supports basic types(boolean/integer/float/string"); + } + + // 所有的二元运算符只支持运算同类类型的表达式) + if (!leftType->sameAs(rightType)) { reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports expressions of the same type"); } + // 检查算数类型是否符合运算符要求 if (bop == StaticScriptLexer::Plus || bop == StaticScriptLexer::PlusAssign) { - if (!leftInferType->equals(AtomicType::INTEGER_TYPE) && - !leftInferType->equals(AtomicType::STRING_TYPE)) { + if (!leftType->isNumber() && + !leftType->isString()) { reportTypeError("Add string and integer only operation types are supported"); } - } else if (bop >= StaticScriptLexer::Minus && bop <= StaticScriptLexer::GreaterThanEquals || - bop >= StaticScriptLexer::MinusAssign && bop <= StaticScriptLexer::RightShiftAssign) { - if (!leftInferType->equals(AtomicType::INTEGER_TYPE)) { - reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports integer type"); + } else if (bop >= StaticScriptLexer::Minus && bop <= StaticScriptLexer::Modulus || + bop >= StaticScriptLexer::MinusAssign && bop <= StaticScriptLexer::ModulusAssign || + bop >= StaticScriptLexer::LessThan && bop <= StaticScriptLexer::GreaterThanEquals) { + if (!leftType->isNumber()) { + reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports number type"); + } + } else if ( + bop >= StaticScriptLexer::BitAnd && bop <= StaticScriptLexer::LogicalShiftRight || + bop >= StaticScriptLexer::BitAndAssign && bop <= StaticScriptLexer::LogicalShiftRightAssign + ) { + if (!leftType->isInteger()) { + reportTypeError("Bitwise operator '" + std::to_string(bop) + "' only supports integer type"); } } else if (bop == StaticScriptLexer::And || bop == StaticScriptLexer::Or || bop == StaticScriptLexer::AndAssign || bop == StaticScriptLexer::OrAssign) { - if (!leftInferType->equals(AtomicType::BOOLEAN_TYPE)) { + if (!leftType->isBoolean()) { reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports boolean type"); } } // 设置二元运算表达式的类型 if (bop >= StaticScriptLexer::LessThan && bop <= StaticScriptLexer::NotEquals) { - bopExpr->inferType = AtomicType::BOOLEAN_TYPE; + bopExpr->type = BasicType::BOOLEAN_TYPE; + } else if (leftType->isFloat() || rightType->isFloat()) { + bopExpr->type = BasicType::FLOAT_TYPE; } else { - bopExpr->inferType = leftInferType; + bopExpr->type = leftType; } } void TypeChecker::visit(const SharedPtr &varExpr) { - varExpr->inferType = varExpr->refVarDecl->type; + varExpr->type = varExpr->refVarDecl->type; } void TypeChecker::visit(const SharedPtr &arrayLiteralExpr) { ASTVisitor::visit(arrayLiteralExpr); + // 数组元素非空则寻找该数组的元素类型 if (!arrayLiteralExpr->elements.empty()) { // 寻找非unknown的element - auto element = std::find_if(arrayLiteralExpr->elements.begin(), arrayLiteralExpr->elements.end(), [](const SharedPtr &element) { - return !element->inferType->isUnknown(); - }); - SharedPtr elementType; - if (element != arrayLiteralExpr->elements.end()) { - elementType = (*element)->inferType; - for (const SharedPtr &expr : arrayLiteralExpr->elements) { - if (!elementType->equals(expr->inferType)) { - if (elementType->isArray() && expr->inferType->isArray() && expr->inferType->isUnknown()) { - if (staticPtrCast(elementType)->getDepth() != staticPtrCast(expr->inferType)->getDepth()) { - reportTypeError("Inconsistent depth of multidimensional array elements"); - } - } else { - reportTypeError("Inconsistent array element type"); - } + auto primaryElement = std::find_if( + arrayLiteralExpr->elements.begin(), + arrayLiteralExpr->elements.end(), + [](const SharedPtr &element) { + return !element->type->isUnknown(); + } + ); + SharedPtr primaryElementType; + // 如果找到了非unknown的element + if (primaryElement != arrayLiteralExpr->elements.end()) { + primaryElementType = (*primaryElement)->type; + for (const SharedPtr &element : arrayLiteralExpr->elements) { + if (!primaryElementType->compatible(element->type)) { + reportTypeError("Inconsistent array element type"); + } else if (element->type->isFloat()) { + // 如果数组中有一个元素为浮点型, 那么所有的元素都设置为浮点型 + primaryElementType = BasicType::FLOAT_TYPE; } } } else { - elementType = arrayLiteralExpr->elements[0]->inferType; - for (const SharedPtr &expr : arrayLiteralExpr->elements) { - if (staticPtrCast(elementType)->getDepth() != staticPtrCast(expr->inferType)->getDepth()) { - reportTypeError("Inconsistent depth of multidimensional array elements"); + // 所有元素类型都是unknown, 即多维数组 + primaryElementType = arrayLiteralExpr->elements[0]->type; + // 判断多维数组的子数组深度是否一致 + for (const SharedPtr &element : arrayLiteralExpr->elements) { + if (primaryElementType->asArray()->getDepth() != element->type->asArray()->getDepth()) { + reportTypeError("Inconsistent depth of multidimensional empty array elements"); } } } - arrayLiteralExpr->inferType = makeShared(elementType); + arrayLiteralExpr->type = makeShared(primaryElementType); } else { - arrayLiteralExpr->inferType = makeShared(); + // 空数组则默认为其元素类型为unknown + arrayLiteralExpr->type = makeShared(); } } void TypeChecker::visit(const SharedPtr &ifStmt) { ASTVisitor::visit(ifStmt); - if (!ifStmt->condition->inferType->equals(AtomicType::BOOLEAN_TYPE)) { + if (!ifStmt->condition->type->isBoolean()) { reportTypeError("The condition of if statement only supports boolean type"); } } void TypeChecker::visit(const SharedPtr &whileStmt) { ASTVisitor::visit(whileStmt); - if (!whileStmt->condition->inferType->equals(AtomicType::BOOLEAN_TYPE)) { + if (!whileStmt->condition->type->isBoolean()) { reportTypeError("The condition of while statement only supports boolean type temporarily"); } } void TypeChecker::visit(const SharedPtr &forStmt) { ASTVisitor::visit(forStmt); - if (forStmt->condition && !forStmt->condition->inferType->equals(AtomicType::BOOLEAN_TYPE)) { + if (forStmt->condition && !forStmt->condition->type->isBoolean()) { reportTypeError("The condition of for statement only supports boolean type temporarily"); } } void TypeChecker::visit(const SharedPtr &returnStmt) { ASTVisitor::visit(returnStmt); - if (returnStmt->returnExpr) { - if (!returnStmt->returnExpr->inferType->equals(returnStmt->refFuncDecl->returnType)) { + const auto &returnExpr = returnStmt->returnExpr; + auto &returnType = returnStmt->refFuncDecl->returnType; + if (returnExpr) { + if (!returnType) { + returnType = returnExpr->type; + } else if (!returnExpr->type->sameAs(returnType)) { reportTypeError("The return value type does not match the function return value type"); } - } else { - if (returnStmt->refFuncDecl->returnType) { - reportTypeError("The return statement is missing a return value"); - } + } else if (returnType) { + reportTypeError("The return statement is missing a return value"); } } void TypeChecker::assignTypeForArrayLiteral(const SharedPtr &arrayLiteral, const SharedPtr &type) { - arrayLiteral->inferType = type; + arrayLiteral->type = type; for (const SharedPtr &itemExpr : arrayLiteral->elements) { - if (const SharedPtr &subArrayLiteral = dynPtrCast(itemExpr)) { - assignTypeForArrayLiteral(subArrayLiteral, staticPtrCast(type->getElementType())); + const SharedPtr &elementType = type->getElementType(); + if (const auto &subArrayLiteral = dynPtrCast(itemExpr)) { + assignTypeForArrayLiteral(subArrayLiteral, elementType->asArray()); } else { - itemExpr->inferType = type->getElementType(); + itemExpr->type = elementType; } } } diff --git a/tests/array.ss b/tests/array.ss index 64f2b9e..c4c8481 100644 --- a/tests/array.ss +++ b/tests/array.ss @@ -51,4 +51,12 @@ ss_assert(arr[1][1][3] == 205); ss_assert(arr[1][2][0] == 206); ss_assert(arr[1][2][1] == 207); ss_assert(arr[1][2][2] == 208); -ss_assert(arr[1][2][3] == 209); \ No newline at end of file +ss_assert(arr[1][2][3] == 209); + + +let floatArr: number[][] = [[1, 2.3], [1.2, 5]]; + +ss_assert(floatArr[0][0] == 1); +ss_assert(floatArr[0][1] == 2.3); +ss_assert(floatArr[1][0] == 1.2); +ss_assert(floatArr[1][1] == 5); \ No newline at end of file diff --git a/tests/float.ss b/tests/float.ss new file mode 100644 index 0000000..332900d --- /dev/null +++ b/tests/float.ss @@ -0,0 +1,5 @@ +let f1 = 1.1; +let f2 = f1 + 1.23; + +ss_assert(f2 == 2.33); + From b951b1bc14e90e7c5dc44c396dbd091a552b227a Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Thu, 3 Dec 2020 21:44:46 +0800 Subject: [PATCH 08/17] fix: fix conversion problem between integer and floating point when assigning values --- src/CodeGen/ExprCodeGen.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/CodeGen/ExprCodeGen.cpp b/src/CodeGen/ExprCodeGen.cpp index 106b28a..a27bdd2 100644 --- a/src/CodeGen/ExprCodeGen.cpp +++ b/src/CodeGen/ExprCodeGen.cpp @@ -252,7 +252,7 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { SharedPtr type = leftType; - if (leftType->isInteger() && rightType->isFloat()) { + if (leftType->isInteger() && rightType->isFloat() && bopCode != StaticScriptLexer::Assign) { type = rightType; lhsCode = integer2float(lhsCode); } else if (leftType->isFloat() && rightType->isInteger()) { @@ -409,10 +409,21 @@ void IRGenerator::visit(const SharedPtr &bopExpr) { } if (bopCode >= StaticScriptLexer::Assign && bopCode <= StaticScriptLexer::OrAssign) { // 在语义阶段保证lhs类型为IdentifierExprNode或者ArraySubscriptExprNode - // 按左值处理 + + // 简单赋值时直接把targetCode设置为rhsCode if (bopCode == StaticScriptLexer::Assign) { targetCode = rhsCode; } + + // 赋值/算术类型复合赋值时整型与浮点型的转换机制 + if (bopCode >= StaticScriptLexer::Assign && bopCode <= StaticScriptLexer::ModulusAssign) { + if (leftType->isInteger() && rightType->isFloat()) { + targetCode = float2integer(targetCode); + } else if (leftType->isFloat() && rightType->isInteger()) { + targetCode = integer2float(targetCode); + } + } + if (const auto &leftVarExpr = dynPtrCast(bopExpr->lhs)) { llvmIRBuilder.CreateStore(targetCode, leftVarExpr->refVarDecl->code); } else if (const auto &leftAsExpr = dynPtrCast(bopExpr->lhs)) { From 4dab3b9174f1374cdcc7cb77390bc3754a5044aa Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Fri, 4 Dec 2020 15:04:09 +0800 Subject: [PATCH 09/17] chore: update antlr version --- .github/workflows/ci.yml | 25 +++++++++++-------------- cmake/AddANTLR.cmake | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fa7c3b..bbad00e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,9 @@ jobs: compiler: - { cc: gcc, cxx: g++ } - { cc: clang, cxx: clang++ } + env: + CC: ${{ matrix.compiler.cc }} + CXX: ${{ matrix.compiler.cxx }} steps: - name: Fetch Codebase uses: actions/checkout@v2 @@ -32,7 +35,7 @@ jobs: uses: actions/cache@v2 with: path: ${{ github.workspace }}/thirdparty - key: ${{ matrix.os }}-${{ matrix.compiler.cc }}-antlr + key: ${{ matrix.os }}-${{ matrix.compiler.cc }}-antlr4.9 - name: Install Prerequirements run: | sudo wget https://apt.llvm.org/llvm.sh @@ -41,30 +44,24 @@ jobs: sudo apt-get -y install uuid-dev pkg-config doxygen graphviz - name: Install Antlr4 and Antlr4 Runtime if: steps.cache-antlr.outputs.cache-hit != 'true' - env: - CC: ${{ matrix.compiler.cc }} - CXX: ${{ matrix.compiler.cxx }} run: | - echo "THREAD_COUNT=$(sudo cat /proc/cpuinfo| grep "processor"| wc -l)" >> $GITHUB_ENV sudo mkdir -p thirdparty/antlr && cd thirdparty/antlr - sudo wget https://www.antlr.org/download/antlr-4.8-complete.jar - sudo wget -O ${{ runner.temp }}/antlr4-src.zip https://www.antlr.org/download/antlr4-cpp-runtime-4.8-source.zip + sudo wget https://www.antlr.org/download/antlr-4.9-complete.jar + sudo wget -O ${{ runner.temp }}/antlr4-src.zip https://www.antlr.org/download/antlr4-cpp-runtime-4.9-source.zip cd ${{ runner.temp }} sudo unzip antlr4-src.zip sudo mkdir build && cd build sudo mkdir -p ${{ github.workspace }}/thirdparty/antlr-runtime sudo cmake .. -DANTLR4_INSTALL=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/thirdparty/antlr-runtime - sudo cmake --build . --target install -- -j $THREAD_COUNT + sudo cmake --build . --target install -- -j 2 - name: Move Antlr Runtime into /usr/local run: | cd ${{ github.workspace }}/thirdparty/antlr-runtime sudo cp -r include/* /usr/local/include/ sudo cp -r lib/* /usr/local/lib/ - name: CMake Build - env: - CC: ${{ matrix.compiler.cc }} - CXX: ${{ matrix.compiler.cxx }} run: | + echo "THREAD_COUNT=$(sudo cat /proc/cpuinfo| grep "processor"| wc -l)" >> $GITHUB_ENV sudo mkdir cmake-build-debug cmake-build-release cd cmake-build-debug sudo cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. @@ -96,15 +93,15 @@ jobs: compiler: - { cc: gcc, cxx: g++ } - { cc: clang, cxx: clang++ } + env: + CC: ${{ matrix.compiler.cc }} + CXX: ${{ matrix.compiler.cxx }} steps: - name: Install Prerequirements run: brew install antlr antlr4-cpp-runtime doxygen graphviz - name: Fetch Codebase uses: actions/checkout@v2 - name: CMake Build - env: - CC: ${{ matrix.compiler.cc }} - CXX: ${{ matrix.compiler.cxx }} run: | echo "THREAD_COUNT=$(sudo sysctl -n machdep.cpu.thread_count)" >> $GITHUB_ENV sudo mkdir cmake-build-debug cmake-build-release diff --git a/cmake/AddANTLR.cmake b/cmake/AddANTLR.cmake index 8ef2245..296bfdd 100644 --- a/cmake/AddANTLR.cmake +++ b/cmake/AddANTLR.cmake @@ -1,7 +1,7 @@ list(APPEND CMAKE_PREFIX_PATH /usr/local/lib/cmake/antlr4) if (CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) - set(ANTLR4_JAR_LOCATION ${PROJECT_SOURCE_DIR}/thirdparty/antlr/antlr-4.8-complete.jar) + set(ANTLR4_JAR_LOCATION ${PROJECT_SOURCE_DIR}/thirdparty/antlr/antlr-4.9-complete.jar) elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) set(ANTLR4_JAR_LOCATION /usr/local/opt/antlr/antlr-4.9-complete.jar) endif () From d7cd7f838adc9a6dc281f09fe5dc896e22e14837 Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sat, 5 Dec 2020 22:27:55 +0800 Subject: [PATCH 10/17] fix: fix some bugs and add some test cases bug 1: The problem of integers and floats conversion bug 2: `curFn` is not set correctly when multiple function declarations exist --- lib/ss_io.c | 4 +- src/CodeGen/DeclCodeGen.cpp | 37 ++++++++-------- src/CodeGen/ExprCodeGen.cpp | 14 +++--- src/CodeGen/IRGenerator.cpp | 1 + tests/float.ss | 61 +++++++++++++++++++++++-- tests/variable.ss | 88 ++++++++++++++++++++++++------------- 6 files changed, 145 insertions(+), 60 deletions(-) diff --git a/lib/ss_io.c b/lib/ss_io.c index 1d55c41..f558867 100644 --- a/lib/ss_io.c +++ b/lib/ss_io.c @@ -33,11 +33,11 @@ void ss_println_integer(long number) { } void ss_print_float(double number) { - printf("%lf", number); + printf("%f", number); } void ss_println_float(double number) { - printf("%lf\n", number); + printf("%f\n", number); } void ss_print_string(ss_string *str) { diff --git a/src/CodeGen/DeclCodeGen.cpp b/src/CodeGen/DeclCodeGen.cpp index 33caf22..9f0a973 100644 --- a/src/CodeGen/DeclCodeGen.cpp +++ b/src/CodeGen/DeclCodeGen.cpp @@ -5,24 +5,28 @@ void IRGenerator::visit(const SharedPtr &varDecl) { ASTVisitor::visit(varDecl); LLVMType *type = getType(varDecl->type); LLVMValue *varInitCode = varDecl->initVal->code; + + bool isLiteralInit = bool(dynPtrCast(varDecl->initVal)); + bool declBoolean = varDecl->type->isBoolean(); + bool declFloat = varDecl->type->isFloat(); + bool initFloat = varDecl->initVal->type->isFloat(); + bool declString = varDecl->type->isString(); + bool declArray = varDecl->type->isArray(); + + if (declFloat && !initFloat) { + varInitCode = integer2float(varInitCode); + } else if (!declFloat && initFloat) { + varInitCode = float2integer(varInitCode); + } + // 区分全局变量和局部变量 if (varDecl->scope->isTopLevel()) { - bool isLiteralInit = bool(dynPtrCast(varDecl->initVal)); - bool declBoolean = varDecl->type->isBoolean(); - bool declFloat = varDecl->type->isFloat(); - bool initFloat = varDecl->initVal->type->isFloat(); - bool declString = varDecl->type->isString(); - bool declArray = varDecl->type->isArray(); llvm::Constant *initializer; if (declString || declArray) { initializer = llvm::ConstantPointerNull::getNullValue(type); } else if (isLiteralInit) { - if (declFloat && initFloat) { + if (declFloat) { initializer = llvm::cast(varInitCode); - } else if (declFloat && !initFloat) { - initializer = llvm::cast(integer2float(varInitCode)); - } else if (!declFloat && initFloat) { - initializer = llvm::cast(float2integer(varInitCode)); } else { initializer = llvm::cast(varInitCode); } @@ -49,14 +53,8 @@ void IRGenerator::visit(const SharedPtr &varDecl) { } gVar->setAlignment(llvm::MaybeAlign(alignment)); // 在main函数中store字符串和数组的指针值或者非字面量的表达式值 - if (declString || declArray) { - llvmIRBuilder.CreateStore(varInitCode, gVar); - } else if (!isLiteralInit) { - if (declFloat && !initFloat) { - varInitCode = integer2float(varInitCode); - } else if (!declFloat && initFloat) { - varInitCode = float2integer(varInitCode); - } + // 全局变量初始值为字面量时不需要在store varInitCode + if (declString || declArray || !isLiteralInit) { llvmIRBuilder.CreateStore(varInitCode, gVar); } varDecl->code = gVar; @@ -103,4 +101,5 @@ void IRGenerator::visit(const SharedPtr &funcDecl) { } } llvm::verifyFunction(*func); + curFn = mainFn; } \ No newline at end of file diff --git a/src/CodeGen/ExprCodeGen.cpp b/src/CodeGen/ExprCodeGen.cpp index a27bdd2..9dfe1ec 100644 --- a/src/CodeGen/ExprCodeGen.cpp +++ b/src/CodeGen/ExprCodeGen.cpp @@ -76,6 +76,7 @@ void IRGenerator::visit(const SharedPtr &strLiteralExpr) Vector createArgs{literalListPtr, elementSizeCode, errorAlloca}; \ arrayLiteralExpr->code = llvmIRBuilder.CreateCall(BuiltinArray::create##UpperCaseType##ArrayWithLiteralFunc, createArgs); \ + void IRGenerator::visit(const SharedPtr &arrayLiteralExpr) { const SharedPtr &elementType = arrayLiteralExpr->type->asArray()->getElementType(); @@ -119,7 +120,12 @@ void IRGenerator::visit(const SharedPtr &arrayLiteralExpr) } else if (elementType->isInteger()) { Vector literalVector(elementSize); for (size_t i = 0; i < elementSize; ++i) { - literalVector[i] = staticPtrCast(arrayLiteralExpr->elements[i])->literal; + const SharedPtr &element = arrayLiteralExpr->elements[i]; + if (const auto &floatElement = dynPtrCast(element)) { + literalVector[i] = floatElement->literal; + } else if (const auto &intElement = dynPtrCast(element)) { + literalVector[i] = intElement->literal; + } } GenerateBasicArray(long, Integer, Int64) } else if (elementType->isFloat()) { @@ -235,10 +241,8 @@ void IRGenerator::visit(const SharedPtr &uopExpr) { void IRGenerator::visit(const SharedPtr &bopExpr) { unsigned int bopCode = bopExpr->opCode; - - // 平凡赋值: lhs是左值, rhs是右值 - // 复合赋值: lhs是左值也是右值, rhs是右值; `lhs op= rhs` 相当于 `lhs = lhs op rhs` - if (bopCode == StaticScriptLexer::Assign) { + // 变量被赋值时只需要visit lhs + if (bopCode == StaticScriptLexer::Assign && dynPtrCast(bopExpr->lhs)) { bopExpr->rhs->accept(shared_from_this()); } else { ASTVisitor::visit(bopExpr); diff --git a/src/CodeGen/IRGenerator.cpp b/src/CodeGen/IRGenerator.cpp index 55872c2..3b89659 100644 --- a/src/CodeGen/IRGenerator.cpp +++ b/src/CodeGen/IRGenerator.cpp @@ -66,6 +66,7 @@ void IRGenerator::emitBranch(LLVMBasicBlock *targetBB) { llvmIRBuilder.ClearInsertionPoint(); } +// 设置数组元素的值, 务必确保baseExpr已经被visit过 void IRGenerator::setArrayElement(const SharedPtr &asExpr, LLVMValue *valueCode) { size_t indexExprsSize = asExpr->indexExprs.size(); SharedPtr iterType = asExpr->baseExpr->type; diff --git a/tests/float.ss b/tests/float.ss index 332900d..a78f130 100644 --- a/tests/float.ss +++ b/tests/float.ss @@ -1,5 +1,60 @@ -let f1 = 1.1; -let f2 = f1 + 1.23; +let a1 = 1; +let a2 = 2.0; +let a3 = a1 + a2; -ss_assert(f2 == 2.33); +ss_assert(a3 == 3); +ss_assert(a3 == 3.0); +// =-----------------------------------------------= + +let b1 = 1; +let b2: number = b1; + +ss_assert(b2 == 1.0); + +let b3 = 1.2; +let b4: int = b3; + +ss_assert(b4 == 1); + +// =-----------------------------------------------= + +let c1 = 1; + +function addMore(n: number) { + return n + 1.2; +} + +ss_assert(addMore(c1) == 2.2); + +let c3 = 1.234; + +function floor(n: int): int { + return n; +} + +ss_assert(floor(c3) == 1); + +// =-----------------------------------------------= + +let arr1: number[] = [1, 2, 3, 4, 5]; +for(let i = 0; i < 5; i++) { + arr1[i] += 1.2; +} +ss_assert(arr1[0] == 2.2); +ss_assert(arr1[1] == 3.2); +ss_assert(arr1[2] == 4.2); +ss_assert(arr1[3] == 5.2); +ss_assert(arr1[4] == 6.2); + +let arr2: number[] = [2.2, 4.4, 8.8, 16.16, 32.32]; + +for(let j = 0; j < 5; j++) { + arr2[j] += 8; +} + +ss_assert(arr2[0] == 10.2); +ss_assert(arr2[1] == 12.4); +ss_assert(arr2[2] == 16.8); +ss_assert(arr2[3] == 24.16); +ss_assert(arr2[4] == 40.32); diff --git a/tests/variable.ss b/tests/variable.ss index 579d8af..7e32e02 100644 --- a/tests/variable.ss +++ b/tests/variable.ss @@ -1,31 +1,57 @@ -let xn: number; -let xb: boolean; -let xs: string; -let a: number = 1; -let b: boolean = false; -let c: string = "string content"; -let d = 1; -let e = false; -let f = "string content"; -const g: number = 1; -const h: boolean = false; -const i: string = "string content"; -const j = 1; -const k = false; -const l = "string content"; - -ss_assert(xn == 0); -ss_assert(xb == false); -ss_assert(xs == ""); -ss_assert(a == 1); -ss_assert(b == false); -ss_assert(c == "string content"); -ss_assert(d == 1); -ss_assert(e == false); -ss_assert(f == "string content"); -ss_assert(g == 1); -ss_assert(h == false); -ss_assert(i == "string content"); -ss_assert(j == 1); -ss_assert(k == false); -ss_assert(l == "string content"); \ No newline at end of file +let ai: int; +let af: number; +let ab: boolean; +let as: string; + +ss_assert(ai == 0); +ss_assert(af == 0); +ss_assert(ab == false); +ss_assert(as == ""); + +// =-----------------------------------------------= + +let bi: int = 1; +let bf: number = 1.0; +let bb: boolean = false; +let bs: string = "string content"; + +ss_assert(bi == 1); +ss_assert(bf == 1.0); +ss_assert(bb == false); +ss_assert(bs == "string content"); + +// =-----------------------------------------------= + +let ci = 1; +let cf = 1.0; +let cb = false; +let cs = "string content"; + +ss_assert(ci == 1); +ss_assert(cf == 1.0); +ss_assert(cb == false); +ss_assert(cs == "string content"); + +// =-----------------------------------------------= + +const di: int = 1; +const df: number = 1.0; +const db: boolean = false; +const ds: string = "string content"; + +ss_assert(di == 1); +ss_assert(df == 1.0); +ss_assert(db == false); +ss_assert(ds == "string content"); + +// =-----------------------------------------------= + +const ei = 1; +const ef = 1.0; +const eb = false; +const es = "string content"; + +ss_assert(ei == 1); +ss_assert(ef == 1.0); +ss_assert(eb == false); +ss_assert(es == "string content"); \ No newline at end of file From 1a5479abce50f332c9b880317a895544ddc84a90 Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sun, 6 Dec 2020 00:07:13 +0800 Subject: [PATCH 11/17] fix: fix no EOF in parser grammar and add more test cases --- grammar/StaticScriptParser.g4 | 2 +- src/Driver/Driver.cpp | 6 ++-- tests/array.ss | 13 ++++++++- tests/{all.ss => fib.ss} | 0 tests/selection.ss | 55 ++++++++++++++++++++++++++++++----- 5 files changed, 64 insertions(+), 12 deletions(-) rename tests/{all.ss => fib.ss} (100%) diff --git a/grammar/StaticScriptParser.g4 b/grammar/StaticScriptParser.g4 index 1dab858..39bdee4 100644 --- a/grammar/StaticScriptParser.g4 +++ b/grammar/StaticScriptParser.g4 @@ -6,7 +6,7 @@ options { } module - : statements? + : statements? EOF ; statements diff --git a/src/Driver/Driver.cpp b/src/Driver/Driver.cpp index cdacedb..0508391 100644 --- a/src/Driver/Driver.cpp +++ b/src/Driver/Driver.cpp @@ -69,10 +69,12 @@ int main(int argc, char **argv) { } else if (optLevelOs) { optLevel = 2; sizeLevel = 1; - } else if (optLevelO2) { - optLevel = 2; } else if (optLevelO1) { optLevel = 1; + } else if (optLevelO0) { + optLevel = 0; + } else { + optLevel = 2; } if (!llvm::sys::fs::exists(inputFilename)) { diff --git a/tests/array.ss b/tests/array.ss index c4c8481..14e15ac 100644 --- a/tests/array.ss +++ b/tests/array.ss @@ -59,4 +59,15 @@ let floatArr: number[][] = [[1, 2.3], [1.2, 5]]; ss_assert(floatArr[0][0] == 1); ss_assert(floatArr[0][1] == 2.3); ss_assert(floatArr[1][0] == 1.2); -ss_assert(floatArr[1][1] == 5); \ No newline at end of file +ss_assert(floatArr[1][1] == 5); + +for(let i = 0; i < 2; i++) { + for(let j = 0; j < 2; j++) { + floatArr[i][j] += 8; + } +} + +ss_assert(floatArr[0][0] == 9); +ss_assert(floatArr[0][1] == 10.3); +ss_assert(floatArr[1][0] == 9.2); +ss_assert(floatArr[1][1] == 13); \ No newline at end of file diff --git a/tests/all.ss b/tests/fib.ss similarity index 100% rename from tests/all.ss rename to tests/fib.ss diff --git a/tests/selection.ss b/tests/selection.ss index 6fcadd9..471aaa5 100644 --- a/tests/selection.ss +++ b/tests/selection.ss @@ -1,9 +1,48 @@ -let x: number = 1; -let y: number = 0; -if (x < y) { - x = x + 1; -} else { - y = y + 1; +function process(n: int): int { + if (n < 100) { + if (n < 30) { + return 1; + } else if (n < 60) { + return 2; + } else { + return 3; + } + } else if (n < 200) { + if (n < 130) { + return 4; + } else if (n < 160) { + return 5; + } else { + return 6; + } + } else if (n < 300) { + if (n < 230) { + return 7; + } else if (n < 260) { + return 8; + } else { + return 9; + } + } else { + if (n < 330) { + return 10; + } else if (n < 360) { + return 11; + } else { + return 12; + } + } } -ss_assert(x == 1); -ss_assert(y == 1); \ No newline at end of file + +ss_assert(process(20) == 1); +ss_assert(process(50) == 2); +ss_assert(process(70) == 3); +ss_assert(process(120) == 4); +ss_assert(process(150) == 5); +ss_assert(process(170) == 6); +ss_assert(process(220) == 7); +ss_assert(process(250) == 8); +ss_assert(process(270) == 9); +ss_assert(process(320) == 10); +ss_assert(process(350) == 11); +ss_assert(process(370) == 12); \ No newline at end of file From 0d9887bb3f7ec59c0545df6b2b2f5460a0355ccf Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Tue, 8 Dec 2020 12:05:25 +0800 Subject: [PATCH 12/17] fix: enhance type checking for array --- include/CodeGen/Builtin.h | 1 + include/Entity/Type.h | 14 ++++++++---- lib/ss_array.c | 33 +++++++++++++++++----------- lib/ss_array.h | 21 ++++++++++++------ src/CodeGen/Builtin.cpp | 1 + src/Entity/Type.cpp | 45 ++++++++++++++++++++++----------------- src/Sema/TypeChecker.cpp | 20 +++++++++-------- 7 files changed, 84 insertions(+), 51 deletions(-) diff --git a/include/CodeGen/Builtin.h b/include/CodeGen/Builtin.h index 48e56be..f77b252 100644 --- a/include/CodeGen/Builtin.h +++ b/include/CodeGen/Builtin.h @@ -64,6 +64,7 @@ class BuiltinArray { static inline LLVMFunction *deleteFunc = nullptr; static inline LLVMFunction *getSizeFunc = nullptr; static inline LLVMFunction *isNDArrayFunc = nullptr; + static inline LLVMFunction *isFloatArrayFunc = nullptr; static inline LLVMFunction *pushIntegerFunc = nullptr; static inline LLVMFunction *pushFloatFunc = nullptr; static inline LLVMFunction *pushBooleanFunc = nullptr; diff --git a/include/Entity/Type.h b/include/Entity/Type.h index 67ad23f..c485a25 100644 --- a/include/Entity/Type.h +++ b/include/Entity/Type.h @@ -32,20 +32,22 @@ class Type : public std::enable_shared_from_this { [[nodiscard]] virtual bool isArray() const = 0; + [[nodiscard]] virtual bool isNumberArray() const = 0; + [[nodiscard]] virtual SharedPtr asArray() const = 0; bool operator==(const Type &rhs) = delete; bool operator!=(const Type &rhs) = delete; - /// 判断类型是否相等[无方向](note: equals < sameAs < compatible) + /// 判断类型是否相等[无方向](note: equals < sameAs < compatibleWith) [[nodiscard]] bool equals(const SharedPtr &rhs) const; - /// 判断类型是否相同[无方向](note: equals < sameAs < compatible) + /// 判断类型是否相同[无方向](note: equals < sameAs < compatibleWith) [[nodiscard]] bool sameAs(const SharedPtr &rhs) const; - /// 判断类型是否兼容[有方向](note: equals < sameAs < compatible) - [[nodiscard]] bool compatible(const SharedPtr &rhs) const; + /// 判断类型是否兼容[有方向](note: equals < sameAs < compatibleWith) + [[nodiscard]] bool compatibleWith(const SharedPtr &rhs) const; }; /// 基础类型节点 @@ -73,6 +75,8 @@ class BasicType : public Type { [[nodiscard]] bool isArray() const override; + [[nodiscard]] bool isNumberArray() const override; + [[nodiscard]] SharedPtr asArray() const override; [[nodiscard]] BasicTypeKind getKind() const; @@ -119,6 +123,8 @@ class ArrayType : public Type { [[nodiscard]] bool isArray() const override; + [[nodiscard]] bool isNumberArray() const override; + [[nodiscard]] SharedPtr asArray() const override; /// 获取数组的元素类型, 多维数组则获取子数组的类型 diff --git a/lib/ss_array.c b/lib/ss_array.c index 868ab2e..ad0e78b 100644 --- a/lib/ss_array.c +++ b/lib/ss_array.c @@ -1,48 +1,47 @@ #include "ss_array.h" ss_array *ss_array_create_integer_array(ss_error **error) { - return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(long), error); + return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(long), IntegerType, error); } ss_array *ss_array_create_float_array(ss_error **error) { - return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(double), error); + return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(double), FloatType, error); } ss_array *ss_array_create_boolean_array(ss_error **error) { - return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(bool), error); + return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(bool), BooleanType, error); } ss_array *ss_array_create_string_array(ss_error **error) { - return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(ss_string *), error); + return ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(ss_string *), StringType, error); } ss_array *ss_array_create_array_array(ss_error **error) { - ss_array *arr = ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(ss_array *), error); + ss_array *arr = ss_array_create(SS_ARRAY_INIT_CAPACITY, sizeof(ss_array *), ArrayType, error); if (!arr) { return NULL; } - arr->is_nd = true; return arr; } ss_array *ss_array_create_integer_array_with_literal(long literal_list[], size_t size, ss_error **error) { - ss_array_create_array_with_literal(long, false) + ss_array_create_array_with_literal(long, IntegerType) } ss_array *ss_array_create_float_array_with_literal(double literal_list[], size_t size, ss_error **error) { - ss_array_create_array_with_literal(double, false) + ss_array_create_array_with_literal(double, FloatType) } ss_array *ss_array_create_boolean_array_with_literal(bool literal_list[], size_t size, ss_error **error) { - ss_array_create_array_with_literal(bool, false) + ss_array_create_array_with_literal(bool, BooleanType) } ss_array *ss_array_create_string_array_with_literal(ss_string *literal_list[], size_t size, ss_error **error) { - ss_array_create_array_with_literal(ss_string *, false) + ss_array_create_array_with_literal(ss_string *, StringType) } ss_array *ss_array_create_array_array_with_literal(ss_array *literal_list[], size_t size, ss_error **error) { - ss_array_create_array_with_literal(ss_array *, true) + ss_array_create_array_with_literal(ss_array *, ArrayType) } void ss_array_delete(ss_array *arr) { @@ -67,7 +66,15 @@ bool ss_array_is_nd_array(ss_array *arr, ss_error **error) { *error = ss_error_create(NULL_ARRAY_PTR_CODE, NULL_ARRAY_PTR_DESC); return false; } - return arr->is_nd; + return arr->element_type == ArrayType; +} + +bool ss_array_is_float_array(ss_array *arr, ss_error **error) { + if (!arr) { + *error = ss_error_create(NULL_ARRAY_PTR_CODE, NULL_ARRAY_PTR_DESC); + return false; + } + return arr->element_type == FloatType; } void ss_array_push_integer(ss_array *arr, long new_element, ss_error **error) { @@ -184,7 +191,7 @@ ss_array *ss_array_slice(ss_array *arr, ssize_t from, ssize_t to, ss_error **err } size_t slice_size = to - from; size_t new_capacity = ss_array_get_capacity_with_size(slice_size); - ss_array *new_arr = ss_array_create(new_capacity, arr->element_size, error); + ss_array *new_arr = ss_array_create(new_capacity, arr->element_size, arr->element_type, error); if (!new_arr) { return NULL; } diff --git a/lib/ss_array.h b/lib/ss_array.h index f2d87d4..19ea5ac 100644 --- a/lib/ss_array.h +++ b/lib/ss_array.h @@ -8,12 +8,20 @@ #define SS_ARRAY_INIT_CAPACITY 8 +enum ElementType { + BooleanType = 1, + IntegerType = 3, + FloatType = 5, + StringType = 7, + ArrayType = 9 +}; + typedef struct { void *buffer; size_t size; size_t capacity; size_t element_size; - bool is_nd; + enum ElementType element_type; } ss_array; static size_t ss_array_get_capacity_with_size(size_t size) { @@ -31,7 +39,7 @@ static void ss_array_grow(ss_array *arr, ss_error **error) { arr->capacity = new_capacity; } -static ss_array *ss_array_create(size_t capacity, size_t element_size, ss_error **error) { +static ss_array *ss_array_create(size_t capacity, size_t element_size, enum ElementType element_type, ss_error **error) { ss_array *arr = (ss_array *) malloc(sizeof(ss_array)); if (!arr) { *error = ss_error_create(MALLOC_FAILED_CODE, MALLOC_FAILED_DESC); @@ -45,7 +53,7 @@ static ss_array *ss_array_create(size_t capacity, size_t element_size, ss_error arr->size = 0; arr->capacity = capacity; arr->element_size = element_size; - arr->is_nd = false; + arr->element_type = element_type; return arr; } @@ -59,15 +67,14 @@ ss_array *ss_array_create_string_array(ss_error **error); ss_array *ss_array_create_array_array(ss_error **error); -#define ss_array_create_array_with_literal(type, nd_status) \ +#define ss_array_create_array_with_literal(type, element_type) \ size_t capacity = ss_array_get_capacity_with_size(size); \ - ss_array *arr = ss_array_create(capacity, sizeof(type), error); \ + ss_array *arr = ss_array_create(capacity, sizeof(type), element_type, error); \ if (!arr) { \ return NULL; \ } \ memcpy(arr->buffer, literal_list, arr->element_size * size); \ arr->size = size; \ - arr->is_nd = nd_status; \ return arr; ss_array *ss_array_create_integer_array_with_literal(long literal_list[], size_t size, ss_error **error); @@ -86,6 +93,8 @@ size_t ss_array_get_size(ss_array *arr, ss_error **error); bool ss_array_is_nd_array(ss_array *arr, ss_error **error); +bool ss_array_is_float_array(ss_array *arr, ss_error **error); + #define ss_array_push_detect() \ if (arr->size + 1 >= arr->capacity) { \ ss_array_grow(arr, error); \ diff --git a/src/CodeGen/Builtin.cpp b/src/CodeGen/Builtin.cpp index 34a61cd..6918604 100644 --- a/src/CodeGen/Builtin.cpp +++ b/src/CodeGen/Builtin.cpp @@ -102,6 +102,7 @@ void BuiltinArray::getTypeAndFunction(LLVMModule &module) { deleteFunc = module.getFunction("ss_array_delete"); getSizeFunc = module.getFunction("ss_array_get_size"); isNDArrayFunc = module.getFunction("ss_array_is_nd_array"); + isFloatArrayFunc = module.getFunction("ss_array_is_float_array"); pushIntegerFunc = module.getFunction("ss_array_push_integer"); pushFloatFunc = module.getFunction("ss_array_push_float"); pushBooleanFunc = module.getFunction("ss_array_push_boolean"); diff --git a/src/Entity/Type.cpp b/src/Entity/Type.cpp index 8eaf394..ec777c5 100644 --- a/src/Entity/Type.cpp +++ b/src/Entity/Type.cpp @@ -19,7 +19,10 @@ bool Type::equals(const SharedPtr &rhs) const { } } -// 相同类型(包括相等类型和数字类型) +// 相同类型 +// 1. 相等的类型是相同类型 +// 2. 基础整型和基础浮点型是相同类型 +// 3. 数组与空数组是相同类型 bool Type::sameAs(const SharedPtr &rhs) const { // 右操作子为空直接返回false if (!rhs) { @@ -29,17 +32,24 @@ bool Type::sameAs(const SharedPtr &rhs) const { if (equals(rhs)) { return true; } - // 整数和浮点数是兼容的 + // 整数和浮点数是同类的 if (isNumber() && rhs->isNumber()) { return true; } + if (isArray() && rhs->isUnknown()) { + // 1. 左操作子为数组, 右操作子为unknown基础类型, 左操作子兼容右操作子(反之不成立) + // 2. 左右操作子都是数组, 且右操作子为空树组, 左操作子深度不小于右操作子深度, 左操作子兼容右操作子(反之不成立) + if (rhs->isBasic() || rhs->isArray() && asArray()->getDepth() >= rhs->asArray()->getDepth()) { + return true; + } + } return false; } // 左操作子: 形式上的类型(左值的类型(变量声明类型), 函数形参类型) // 右操作子: 实际的类型(右值的类型(变量赋值类型), 函数实参类型) // 兼容是有方向的, 左操作子兼容右操作子并不一定代表右操作子兼容左操作子 -bool Type::compatible(const SharedPtr &rhs) const { +bool Type::compatibleWith(const SharedPtr &rhs) const { // 右操作子为空直接返回false if (!rhs) { return false; @@ -48,22 +58,8 @@ bool Type::compatible(const SharedPtr &rhs) const { if (equals(rhs) || sameAs(rhs)) { return true; } - if (isArray() && rhs->isArray()) { - const auto &arrayType = asArray(); - const auto &rhsArrayType = rhs->asArray(); - // 左右操作子都是数组, 且右操作子为空树组, 左操作子深度不小于右操作子深度, 左操作子兼容右操作子(反之不成立) - if (rhs->isUnknown() && arrayType->getDepth() >= rhsArrayType->getDepth()) { - return true; - } - const auto &basicType = arrayType->getBasicElementType(); - const auto &rhsBasicType = rhsArrayType->getBasicElementType(); - // 内层元素都是数字的多维数组相互兼容 - if (basicType->isNumber() && rhsBasicType->isNumber() && arrayType->getDepth() == rhsArrayType->getDepth()) { - return true; - } - } else if (isArray() && rhs->isBasic()) { - // 左操作子为数组, 右操作子为unknown基础类型, 左操作子兼容右操作子(反之不成立) - if (rhs->isUnknown()) { + if (isNumberArray() && rhs->isNumberArray()) { + if (asArray()->getDepth() == rhs->asArray()->getDepth()) { return true; } } @@ -116,6 +112,10 @@ bool BasicType::equals(const SharedPtr &rhs) const { return kind == rhs->kind; } +bool BasicType::isNumberArray() const { + return false; +} + SharedPtr ArrayType::createNDArrayType(const SharedPtr &basicType, size_t depth) { SharedPtr type = basicType; for (size_t i = 0; i < depth; ++i) { @@ -188,3 +188,10 @@ bool ArrayType::equals(const SharedPtr &rhs) const { size_t ArrayType::getDepth() const { return depth; } + +bool ArrayType::isNumberArray() const { + if (getBasicElementType()->isNumber()) { + return true; + } + return false; +} diff --git a/src/Sema/TypeChecker.cpp b/src/Sema/TypeChecker.cpp index 554f476..574104a 100644 --- a/src/Sema/TypeChecker.cpp +++ b/src/Sema/TypeChecker.cpp @@ -6,9 +6,10 @@ void TypeChecker::visit(const SharedPtr &varDecl) { SharedPtr &varInitVal = varDecl->initVal; if (varInitVal) { varInitVal->accept(shared_from_this()); + const auto &initArrayLiteral = dynPtrCast(varInitVal); const SharedPtr &initValType = varInitVal->type; if (varDeclType) { - if (!varDeclType->compatible(initValType)) { + if (!(varDeclType->sameAs(initValType) || initArrayLiteral && varDeclType->compatibleWith(initValType))) { reportTypeError("The variable declaration type does not match the initial value type: " + varDecl->name); } } else if (!initValType) { @@ -21,11 +22,9 @@ void TypeChecker::visit(const SharedPtr &varDecl) { } varDeclType = initValType; } - if (varDeclType->isArray()) { - // 把显式的声明类型递归赋回给初始值的类型 - const auto &initArrayLiteral = staticPtrCast(varInitVal); - const auto &varArrayType = staticPtrCast(varDeclType); - assignTypeForArrayLiteral(initArrayLiteral, varArrayType); + // 根据显式类型或推导类型规化数组元素类型 + if (initArrayLiteral) { + assignTypeForArrayLiteral(initArrayLiteral, varDeclType->asArray()); } } else { reportOnTypeError(!varDeclType, "Variable has no type specified: " + varDecl->name); @@ -77,7 +76,7 @@ void TypeChecker::visit(const SharedPtr &callExpr) { reportTypeError("The number of arguments did not match when calling '" + callExpr->calleeName + "' function"); } for (size_t i = 0; i < argsSize; ++i) { - if (!params[i]->type->compatible(args[i]->type)) { + if (!params[i]->type->sameAs(args[i]->type)) { reportTypeError( "The type of the " + std::to_string(i + 1) + @@ -201,8 +200,11 @@ void TypeChecker::visit(const SharedPtr &arrayLiteralExpr) if (primaryElement != arrayLiteralExpr->elements.end()) { primaryElementType = (*primaryElement)->type; for (const SharedPtr &element : arrayLiteralExpr->elements) { - if (!primaryElementType->compatible(element->type)) { - reportTypeError("Inconsistent array element type"); + if (!primaryElementType->compatibleWith(element->type)) { + // 数字型数组视为一致的元素类型 + if (!(primaryElementType->isNumberArray() && element->type->isNumberArray())) { + reportTypeError("Inconsistent array element type"); + } } else if (element->type->isFloat()) { // 如果数组中有一个元素为浮点型, 那么所有的元素都设置为浮点型 primaryElementType = BasicType::FLOAT_TYPE; From ef9d59adc31aa3a658fb221e0eaa19f3c3882a5c Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sat, 19 Dec 2020 11:31:31 +0800 Subject: [PATCH 13/17] feat: add link process --- .github/workflows/ci.yml | 6 ++++-- src/Driver/Driver.cpp | 29 +++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bbad00e..d5e9c3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,7 +157,8 @@ jobs: do base=$(basename $file) sudo ./staticscript $file -L lib --emit-llvm -o $base.ll - sudo ./staticscript $file -L lib -o $base.o + sudo ./staticscript $file -L lib -c -o $base.o + sudo ./staticscript $file -L lib -o $base.exe sudo clang $base.o -lm -o $base.exe sudo ./$base.exe done @@ -169,7 +170,8 @@ jobs: do base=$(basename $file) sudo ./staticscript $file -L lib --emit-llvm -o $base.ll - sudo ./staticscript $file -L lib -o $base.o + sudo ./staticscript $file -L lib -c -o $base.o + sudo ./staticscript $file -L lib -o $base.exe sudo clang $base.o -lm -o $base.exe sudo ./$base.exe done \ No newline at end of file diff --git a/src/Driver/Driver.cpp b/src/Driver/Driver.cpp index 0508391..756b1b4 100644 --- a/src/Driver/Driver.cpp +++ b/src/Driver/Driver.cpp @@ -1,3 +1,4 @@ +#include #include #include "StaticScriptLexer.h" #include "StaticScriptParser.h" @@ -21,6 +22,7 @@ int main(int argc, char **argv) { llvm::cl::opt outputFilename("o", llvm::cl::value_desc("output file"), llvm::cl::desc("Write output to "), llvm::cl::cat(generalOptsCat)); llvm::cl::opt emitLLVM("emit-llvm", llvm::cl::desc("Generate the LLVM representation for input file"), llvm::cl::cat(generalOptsCat)); + llvm::cl::opt noLink("c", llvm::cl::desc("Generate the target object file"), llvm::cl::cat(generalOptsCat)); llvm::cl::opt libDir("L", llvm::cl::desc("Specify lib library directory")); llvm::cl::opt optLevelO0("O0", llvm::cl::desc("Optimization level 0. Similar to clang -O0"), llvm::cl::cat(generalOptsCat)); @@ -54,8 +56,18 @@ int main(int argc, char **argv) { return 0; } + llvm::StringRef inputBasename = llvm::sys::path::stem(inputFilename); + + String outputIRFilename; + String outputObjFilename; + String outputExeFilename; + if (outputFilename.empty()) { - outputFilename.setValue(emitLLVM ? "ss-ir.ll" : "ss-out.o"); + outputIRFilename = llvm::formatv("{0}-ir.ll", inputBasename); + outputObjFilename = llvm::formatv("{0}-out.o", inputBasename); + outputExeFilename = llvm::formatv("{0}-out.exe", inputBasename); + } else { + outputIRFilename = outputObjFilename = outputExeFilename = outputFilename; } unsigned optLevel = 0; @@ -126,10 +138,10 @@ int main(int argc, char **argv) { // 设置data layout llvmModule.setDataLayout(targetMachine->createDataLayout()); - std::error_code ec; - llvm::raw_fd_ostream dest(outputFilename, ec, llvm::sys::fs::OF_None); - reportOnDriverError(bool(ec),"Could not create file: " + ec.message()); + std::error_code ec; + llvm::raw_fd_ostream dest(emitLLVM ? outputIRFilename : outputObjFilename, ec, llvm::sys::fs::OF_None); + reportOnDriverError(bool(ec), "Could not create file: " + ec.message()); if (emitLLVM) { dest << llvmModule; @@ -144,5 +156,14 @@ int main(int argc, char **argv) { dest.flush(); dest.close(); fin.close(); + + if (!emitLLVM && !noLink) { + String linkCmd = llvm::formatv("clang {0} -lm -O2 -o {1}", outputObjFilename, outputExeFilename); + system(linkCmd.data()); + if (outputFilename.empty()) { + String rmCmd = llvm::formatv("rm -f {0}", outputObjFilename); + system(rmCmd.data()); + } + } return 0; } \ No newline at end of file From 0d94797fad41ead9db8bcca09dee81fc5a39ef5a Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sat, 19 Dec 2020 15:30:14 +0800 Subject: [PATCH 14/17] chore: add release workflow --- .github/workflows/{ci.yml => build.yml} | 22 ++-- .github/workflows/release.yml | 130 ++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 7 deletions(-) rename .github/workflows/{ci.yml => build.yml} (95%) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/build.yml similarity index 95% rename from .github/workflows/ci.yml rename to .github/workflows/build.yml index d5e9c3d..b7911cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/build.yml @@ -1,18 +1,26 @@ -name: Continuous Integration +name: Build on: push: + branches: + - '**' paths-ignore: - 'README.md' - 'README-zh_CN.md' - 'ROADMAP-zh_CN.md' - 'LICENSE' + tags-ignore: + - '**' pull_request: + branches: + - '**' paths-ignore: - 'README.md' - 'README-zh_CN.md' - 'ROADMAP-zh_CN.md' - 'LICENSE' + tags-ignore: + - '**' jobs: build_on_linux: @@ -69,14 +77,14 @@ jobs: cd ../cmake-build-release sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT - - name: Upload Debug Executable File + - name: Upload Debug File uses: actions/upload-artifact@v2 with: name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug path: | cmake-build-debug/staticscript cmake-build-debug/lib/*.bc - - name: Upload Release Executable File + - name: Upload Release File uses: actions/upload-artifact@v2 with: name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release @@ -111,14 +119,14 @@ jobs: cd ../cmake-build-release sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT - - name: Upload Debug Executable File + - name: Upload Debug File uses: actions/upload-artifact@v2 with: name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug path: | cmake-build-debug/staticscript cmake-build-debug/lib/*.bc - - name: Upload Release Executable File + - name: Upload Release File uses: actions/upload-artifact@v2 with: name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release @@ -139,12 +147,12 @@ jobs: steps: - name: Fetch Codebase uses: actions/checkout@v2 - - name: Download Debug Executable File + - name: Download Debug File uses: actions/download-artifact@v2 with: name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug path: executables/debug - - name: Download Release Executable File + - name: Download Release File uses: actions/download-artifact@v2 with: name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f55c7ca --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,130 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + build_on_ubuntu_before_release: + name: Build on Ubuntu Before Release + runs-on: ubuntu-20.04 + env: + CC: clang + CXX: clang++ + steps: + - name: Fetch Codebase + uses: actions/checkout@v2 + - name: Cache Antlr4 and Antlr4 Runtime + id: cache-antlr + uses: actions/cache@v2 + with: + path: ${{ github.workspace }}/thirdparty + key: ubuntu-20.04-clang-antlr4.9 + - name: Install Prerequirements + run: | + sudo wget https://apt.llvm.org/llvm.sh + sudo chmod +x llvm.sh + sudo ./llvm.sh 11 + sudo apt-get -y install uuid-dev pkg-config doxygen graphviz + - name: Install Antlr4 and Antlr4 Runtime + if: steps.cache-antlr.outputs.cache-hit != 'true' + run: | + sudo mkdir -p thirdparty/antlr && cd thirdparty/antlr + sudo wget https://www.antlr.org/download/antlr-4.9-complete.jar + sudo wget -O ${{ runner.temp }}/antlr4-src.zip https://www.antlr.org/download/antlr4-cpp-runtime-4.9-source.zip + cd ${{ runner.temp }} + sudo unzip antlr4-src.zip + sudo mkdir build && cd build + sudo mkdir -p ${{ github.workspace }}/thirdparty/antlr-runtime + sudo cmake .. -DANTLR4_INSTALL=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/thirdparty/antlr-runtime + sudo cmake --build . --target install -- -j 2 + - name: Move Antlr Runtime into /usr/local + run: | + cd ${{ github.workspace }}/thirdparty/antlr-runtime + sudo cp -r include/* /usr/local/include/ + sudo cp -r lib/* /usr/local/lib/ + - name: CMake Build + run: | + echo "THREAD_COUNT=$(sudo cat /proc/cpuinfo| grep "processor"| wc -l)" >> $GITHUB_ENV + sudo mkdir cmake-build-release + cd cmake-build-release + sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. + sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT + sudo mkdir bin && sudo cp staticscript bin/ + - name: Upload Release File + uses: actions/upload-artifact@v2 + with: + name: staticscript-ubuntu-release + path: | + cmake-build-release/bin/staticscript + cmake-build-release/lib/*.bc + + build_on_macos_before_release: + name: Build on macOS Before Release + runs-on: macos-10.15 + env: + CC: clang + CXX: clang++ + steps: + - name: Install Prerequirements + run: brew install antlr antlr4-cpp-runtime doxygen graphviz + - name: Fetch Codebase + uses: actions/checkout@v2 + - name: CMake Build + run: | + echo "THREAD_COUNT=$(sudo sysctl -n machdep.cpu.thread_count)" >> $GITHUB_ENV + sudo mkdir cmake-build-debug cmake-build-release + cd cmake-build-release + sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. + sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT + sudo mkdir bin && sudo cp staticscript bin/ + - name: Upload Release File + uses: actions/upload-artifact@v2 + with: + name: staticscript-macos-release + path: | + cmake-build-release/bin/staticscript + cmake-build-release/lib/*.bc + + release: + name: Release + needs: [ build_on_ubuntu_before_release, build_on_macos_before_release ] + runs-on: ubuntu-20.04 + steps: + - name: Fetch Codebase + uses: actions/checkout@v2 + - name: Download Release File + uses: actions/download-artifact@v2 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: false + prerelease: false + - name: Compress Release Attachments + run: | + sudo zip -r staticscript-ubuntu-release.zip staticscript-ubuntu-release + sudo zip -r staticscript-macos-release.zip staticscript-macos-release + - name: Upload Ubuntu Release Attachment + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./staticscript-ubuntu-release.zip + asset_name: staticscript-ubuntu-release.zip + asset_content_type: application/zip + - name: Upload macOS Release Attachment + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./staticscript-macos-release.zip + asset_name: staticscript-macos-release.zip + asset_content_type: application/zip \ No newline at end of file From aa4f0c495e2b82924f49658459b6421ee9b4a599 Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sat, 19 Dec 2020 17:28:29 +0800 Subject: [PATCH 15/17] chore: add install script and modify README --- README-zh_CN.md | 73 ++++++++++++++++++++++++++++++++++++++------ README.md | 72 +++++++++++++++++++++++++++++++++++++------ install-macos.sh | 23 ++++++++++++++ install-ubuntu.sh | 17 +++++++++++ tests/.gitattributes | 1 + 5 files changed, 166 insertions(+), 20 deletions(-) create mode 100644 install-macos.sh create mode 100644 install-ubuntu.sh create mode 100644 tests/.gitattributes diff --git a/README-zh_CN.md b/README-zh_CN.md index 5ea832e..dacadf8 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -4,23 +4,76 @@ StaticScript是一门类TypeScript的静态强类型编程语言 -![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=for-the-badge) -![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=for-the-badge) -![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=for-the-badge) +![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=flat-square) +![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=flat-square) +![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=flat-square) -![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) -![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) -![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=for-the-badge) -![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=for-the-badge) +![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=flat-square&color=brightgreen) +![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=flat-square&color=brightgreen) +![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=flat-square) +![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=flat-square) -![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) -![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) -![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=flat-square&color=brightgreen) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=flat-square&color=brightgreen) +![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=flat-square&color=brightgreen) [English](./README.md) | 简体中文 +## 安装 + +### Ubuntu + +```shell +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/StaticScript/StaticScript/master/install-ubuntu.sh)" +``` +或者 +```shell +wget https://raw.githubusercontent.com/StaticScript/StaticScript/master/install-ubuntu.sh +sudo chmod +x install-ubuntu.sh +sudo /bin/bash install-ubuntu.sh +``` + +> 对于其他的Linux发行版, 你需要修改安装脚本才能正常安装 +> +> 安装脚本需要sudo权限 + +### macOS + +```shell +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/StaticScript/StaticScript/master/install-macos.sh)" +``` +或者 +```shell +wget https://raw.githubusercontent.com/StaticScript/StaticScript/master/install-macos.sh +sudo chmod +x install-macos.sh +sudo /bin/bash install-macos.sh +``` + +> 安装脚本需要sudo权限 + +### Windows + +**_暂不支持_** + + +## 使用 + +首先你编写像下面这样一个合法的StaticScript代码文件 +```typescript +// test.ss + +let content: string = "Hello World"; + +ss_println_string(content); +``` + +然后在命令行里执行下面这样的命令 +```shell +staticscript test.ss -o test +./test +``` ## 语言特性概要 diff --git a/README.md b/README.md index 43bc506..b6e6424 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,75 @@ StaticScript is a statically typed and strongly typed programming language, syntactically like TypeScript. -![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=for-the-badge) -![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=for-the-badge) -![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=for-the-badge) +![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=flat-square) +![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=flat-square) +![License](https://img.shields.io/github/license/StaticScript/StaticScript?style=flat-square) -![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) -![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) -![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=for-the-badge) -![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=for-the-badge) +![GitHub Repo stars](https://img.shields.io/github/stars/StaticScript/StaticScript?style=flat-square&color=brightgreen) +![GitHub forks](https://img.shields.io/github/forks/StaticScript/StaticScript?style=flat-square&color=brightgreen) +![GitHub issues](https://img.shields.io/github/issues-raw/StaticScript/StaticScript?style=flat-square) +![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/StaticScript/StaticScript?style=flat-square) -![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) -![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) -![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=for-the-badge&color=brightgreen) +![GitHub Repository Size](https://img.shields.io/github/repo-size/StaticScript/StaticScript?style=flat-square&color=brightgreen) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/StaticScript/StaticScript?style=flat-square&color=brightgreen) +![GitHub top language](https://img.shields.io/github/languages/top/StaticScript/StaticScript?style=flat-square&color=brightgreen) English | [简体中文](./README-zh_CN.md) +## Install + +### Install on Ubuntu + +```shell +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/StaticScript/StaticScript/master/install-ubuntu.sh)" +``` +Or +```shell +wget https://raw.githubusercontent.com/StaticScript/StaticScript/master/install-ubuntu.sh +sudo chmod +x install-ubuntu.sh +sudo /bin/bash install-ubuntu.sh +``` + +> For other linux distributions, you may need to modify the installation script to install properly. +> +> The installation script may request administrator privileges. + +### Install on macOS + +```shell +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/StaticScript/StaticScript/master/install-macos.sh)" +``` +Or +```shell +wget https://raw.githubusercontent.com/StaticScript/StaticScript/master/install-macos.sh +sudo chmod +x install-macos.sh +sudo /bin/bash install-macos.sh +``` +> The installation script may request administrator privileges. + +### Install on Windows + +**_Temporarily not supported_** + + +## Usage + +First you need to write a legal StaticScript code file as following. +```typescript +// test.ss + +let content: string = "Hello World"; + +ss_println_string(content); +``` + +Then execute the following command from the command line. +```shell +staticscript test.ss -o test +./test +``` ## Language Features Summary diff --git a/install-macos.sh b/install-macos.sh new file mode 100644 index 0000000..05cf22b --- /dev/null +++ b/install-macos.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +if [[ ! $(command -v clang) ]]; then + sudo xcode-select --install +fi + +if [[ ! $(command -v wget) ]]; then + brew install wget +fi + +sudo wget https://github.com/StaticScript/StaticScript/releases/latest/download/staticscript-macos-release.zip + +sudo unzip staticscript-macos-release.zip + +sudo rm -f staticscript-macos-release.zip + +sudo mv staticscript-macos-release /usr/local/staticscript + +sudo chmod +x /usr/local/staticscript/bin/staticscript + +sudo ln -s /usr/local/staticscript/bin/staticscript /usr/local/bin/staticscript + +sudo ln -s /usr/local/staticscript/bin/staticscript /usr/local/bin/ssc diff --git a/install-ubuntu.sh b/install-ubuntu.sh new file mode 100644 index 0000000..dd0848e --- /dev/null +++ b/install-ubuntu.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +sudo apt-get -y install clang wget unzip + +sudo wget https://github.com/StaticScript/StaticScript/releases/latest/download/staticscript-ubuntu-release.zip + +sudo unzip staticscript-macos-release.zip + +sudo rm -f staticscript-macos-release.zip + +sudo mv staticscript-macos-release /usr/local/staticscript + +sudo chmod +x /usr/local/staticscript/bin/staticscript + +sudo ln -s /usr/local/staticscript/bin/staticscript /usr/local/bin/staticscript + +sudo ln -s /usr/local/staticscript/bin/staticscript /usr/local/bin/ssc diff --git a/tests/.gitattributes b/tests/.gitattributes new file mode 100644 index 0000000..47d6ee2 --- /dev/null +++ b/tests/.gitattributes @@ -0,0 +1 @@ +*.ss linguist-language=TypeScript \ No newline at end of file From 0b2aee015dccbe1672830420e3aa8b8c4ada5cc2 Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Sat, 19 Dec 2020 22:23:00 +0800 Subject: [PATCH 16/17] chore: modify slogan in README --- README-zh_CN.md | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README-zh_CN.md b/README-zh_CN.md index dacadf8..e9ae58c 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -2,7 +2,7 @@

-StaticScript是一门类TypeScript的静态强类型编程语言 +StaticScript是一门类TypeScript的静态编译型语言 ![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=flat-square) ![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=flat-square) @@ -60,7 +60,7 @@ sudo /bin/bash install-macos.sh ## 使用 -首先你编写像下面这样一个合法的StaticScript代码文件 +首先编写像下面这样一个合法的StaticScript代码文件 ```typescript // test.ss diff --git a/README.md b/README.md index b6e6424..258f5e2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
-StaticScript is a statically typed and strongly typed programming language, syntactically like TypeScript. +StaticScript is a statically typed programming language, syntactically like TypeScript. ![Github Workflow Status](https://img.shields.io/github/workflow/status/StaticScript/StaticScript/Build?style=flat-square) ![Platform](https://img.shields.io/badge/platform-linux--64%20%7C%20macos--64-brightgreen?style=flat-square) From 18f67f914af921ca5bc045b7dd07fb9441316edf Mon Sep 17 00:00:00 2001 From: ApsarasX Date: Mon, 17 May 2021 21:21:34 +0800 Subject: [PATCH 17/17] chore: upgrade to LLVM 12 --- .github/workflows/build.yml | 98 +++++------------------------------ .github/workflows/release.yml | 6 +-- CMakeLists.txt | 6 ++- cmake/AddANTLR.cmake | 4 +- cmake/AddLLVM.cmake | 2 +- include/Support/LLVM.h | 36 +++---------- install-macos.sh | 0 install-ubuntu.sh | 0 src/CodeGen/Builtin.cpp | 6 +-- tests/CMakeLists.txt | 11 ++++ 10 files changed, 45 insertions(+), 124 deletions(-) mode change 100644 => 100755 install-macos.sh mode change 100644 => 100755 install-ubuntu.sh create mode 100644 tests/CMakeLists.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b7911cf..548ae74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,8 +23,8 @@ on: - '**' jobs: - build_on_linux: - name: Build on Linux + build_and_test_on_linux: + name: Build and Test on Linux runs-on: ${{ matrix.os }} strategy: matrix: @@ -48,14 +48,14 @@ jobs: run: | sudo wget https://apt.llvm.org/llvm.sh sudo chmod +x llvm.sh - sudo ./llvm.sh 11 + sudo ./llvm.sh sudo apt-get -y install uuid-dev pkg-config doxygen graphviz - name: Install Antlr4 and Antlr4 Runtime if: steps.cache-antlr.outputs.cache-hit != 'true' run: | sudo mkdir -p thirdparty/antlr && cd thirdparty/antlr - sudo wget https://www.antlr.org/download/antlr-4.9-complete.jar - sudo wget -O ${{ runner.temp }}/antlr4-src.zip https://www.antlr.org/download/antlr4-cpp-runtime-4.9-source.zip + sudo wget https://www.antlr.org/download/antlr-4.9.2-complete.jar + sudo wget -O ${{ runner.temp }}/antlr4-src.zip https://www.antlr.org/download/antlr4-cpp-runtime-4.9.2-source.zip cd ${{ runner.temp }} sudo unzip antlr4-src.zip sudo mkdir build && cd build @@ -74,30 +74,18 @@ jobs: cd cmake-build-debug sudo cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT + sudo ctest --extra-verbose cd ../cmake-build-release sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT - - name: Upload Debug File - uses: actions/upload-artifact@v2 - with: - name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug - path: | - cmake-build-debug/staticscript - cmake-build-debug/lib/*.bc - - name: Upload Release File - uses: actions/upload-artifact@v2 - with: - name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release - path: | - cmake-build-release/staticscript - cmake-build-release/lib/*.bc + sudo ctest --extra-verbose - build_on_macos: - name: Build on macOS + build_and_test_on_macos: + name: Build and Test on macOS runs-on: ${{ matrix.os }} strategy: matrix: - os: [ macos-10.15, macos-11.0 ] + os: [ macos-10.15 ] compiler: - { cc: gcc, cxx: g++ } - { cc: clang, cxx: clang++ } @@ -116,70 +104,8 @@ jobs: cd cmake-build-debug sudo cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT + sudo ctest --extra-verbose cd ../cmake-build-release sudo cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-w" -G "Unix Makefiles" .. sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT - - name: Upload Debug File - uses: actions/upload-artifact@v2 - with: - name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug - path: | - cmake-build-debug/staticscript - cmake-build-debug/lib/*.bc - - name: Upload Release File - uses: actions/upload-artifact@v2 - with: - name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release - path: | - cmake-build-release/staticscript - cmake-build-release/lib/*.bc - - test: - name: Test - needs: [ build_on_linux, build_on_macos ] - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-18.04, ubuntu-20.04, macos-10.15, macos-11.0 ] - compiler: - - { cc: gcc, cxx: g++ } - - { cc: clang, cxx: clang++ } - steps: - - name: Fetch Codebase - uses: actions/checkout@v2 - - name: Download Debug File - uses: actions/download-artifact@v2 - with: - name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-debug - path: executables/debug - - name: Download Release File - uses: actions/download-artifact@v2 - with: - name: staticscript-${{ matrix.os }}-${{ matrix.compiler.cc }}-release - path: executables/release - - name: Test Exmaple Code on Debug Compiler - working-directory: executables/debug - run: | - chmod +x ./staticscript - for file in ${{ github.workspace }}/tests/*.ss - do - base=$(basename $file) - sudo ./staticscript $file -L lib --emit-llvm -o $base.ll - sudo ./staticscript $file -L lib -c -o $base.o - sudo ./staticscript $file -L lib -o $base.exe - sudo clang $base.o -lm -o $base.exe - sudo ./$base.exe - done - - name: Test Exmaple Code on Release Compiler - working-directory: executables/release - run: | - chmod +x ./staticscript - for file in ${{ github.workspace }}/tests/*.ss - do - base=$(basename $file) - sudo ./staticscript $file -L lib --emit-llvm -o $base.ll - sudo ./staticscript $file -L lib -c -o $base.o - sudo ./staticscript $file -L lib -o $base.exe - sudo clang $base.o -lm -o $base.exe - sudo ./$base.exe - done \ No newline at end of file + sudo ctest --extra-verbose \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f55c7ca..deb2f67 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,14 +25,14 @@ jobs: run: | sudo wget https://apt.llvm.org/llvm.sh sudo chmod +x llvm.sh - sudo ./llvm.sh 11 + sudo ./llvm.sh sudo apt-get -y install uuid-dev pkg-config doxygen graphviz - name: Install Antlr4 and Antlr4 Runtime if: steps.cache-antlr.outputs.cache-hit != 'true' run: | sudo mkdir -p thirdparty/antlr && cd thirdparty/antlr - sudo wget https://www.antlr.org/download/antlr-4.9-complete.jar - sudo wget -O ${{ runner.temp }}/antlr4-src.zip https://www.antlr.org/download/antlr4-cpp-runtime-4.9-source.zip + sudo wget https://www.antlr.org/download/antlr-4.9.2-complete.jar + sudo wget -O ${{ runner.temp }}/antlr4-src.zip https://www.antlr.org/download/antlr4-cpp-runtime-4.9.2-source.zip cd ${{ runner.temp }} sudo unzip antlr4-src.zip sudo mkdir build && cd build diff --git a/CMakeLists.txt b/CMakeLists.txt index c420c46..a139901 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,4 +44,8 @@ add_executable( add_dependencies(staticscript antlr4_static) -target_link_libraries(staticscript PRIVATE antlr4_static ${llvm_libs}) \ No newline at end of file +target_link_libraries(staticscript PRIVATE antlr4_static ${llvm_libs}) + +enable_testing() + +add_subdirectory(tests) \ No newline at end of file diff --git a/cmake/AddANTLR.cmake b/cmake/AddANTLR.cmake index 296bfdd..8d6bce9 100644 --- a/cmake/AddANTLR.cmake +++ b/cmake/AddANTLR.cmake @@ -1,9 +1,9 @@ list(APPEND CMAKE_PREFIX_PATH /usr/local/lib/cmake/antlr4) if (CMAKE_HOST_SYSTEM_NAME STREQUAL Linux) - set(ANTLR4_JAR_LOCATION ${PROJECT_SOURCE_DIR}/thirdparty/antlr/antlr-4.9-complete.jar) + set(ANTLR4_JAR_LOCATION ${PROJECT_SOURCE_DIR}/thirdparty/antlr/antlr-4.9.2-complete.jar) elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) - set(ANTLR4_JAR_LOCATION /usr/local/opt/antlr/antlr-4.9-complete.jar) + set(ANTLR4_JAR_LOCATION /usr/local/opt/antlr/antlr-4.9.2-complete.jar) endif () find_package(antlr4-runtime REQUIRED) diff --git a/cmake/AddLLVM.cmake b/cmake/AddLLVM.cmake index e1ef7ee..76a6ba8 100644 --- a/cmake/AddLLVM.cmake +++ b/cmake/AddLLVM.cmake @@ -2,7 +2,7 @@ if (CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin) list(APPEND CMAKE_PREFIX_PATH /usr/local/opt/llvm/lib/cmake/llvm) endif () -find_package(LLVM 11 REQUIRED CONFIG) +find_package(LLVM 12 REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") diff --git a/include/Support/LLVM.h b/include/Support/LLVM.h index 1e899d9..1db3e20 100644 --- a/include/Support/LLVM.h +++ b/include/Support/LLVM.h @@ -65,39 +65,19 @@ inline void initLLVMTarget() { inline void initLLVMPasses() { llvm::PassRegistry ®istry = *llvm::PassRegistry::getPassRegistry(); llvm::initializeCore(registry); - llvm::initializeCoroutines(registry); + llvm::initializeTransformUtils(registry); llvm::initializeScalarOpts(registry); llvm::initializeObjCARCOpts(registry); llvm::initializeVectorization(registry); - llvm::initializeIPO(registry); - llvm::initializeAnalysis(registry); - llvm::initializeTransformUtils(registry); llvm::initializeInstCombine(registry); llvm::initializeAggressiveInstCombine(registry); + llvm::initializeIPO(registry); llvm::initializeInstrumentation(registry); + llvm::initializeAnalysis(registry); + llvm::initializeCoroutines(registry); + llvm::initializeCodeGen(registry); + llvm::initializeGlobalISel(registry); llvm::initializeTarget(registry); - llvm::initializeExpandMemCmpPassPass(registry); - llvm::initializeScalarizeMaskedMemIntrinPass(registry); - llvm::initializeCodeGenPreparePass(registry); - llvm::initializeAtomicExpandPass(registry); - llvm::initializeRewriteSymbolsLegacyPassPass(registry); - llvm::initializeWinEHPreparePass(registry); - llvm::initializeDwarfEHPreparePass(registry); - llvm::initializeSafeStackLegacyPassPass(registry); - llvm::initializeSjLjEHPreparePass(registry); - llvm::initializePreISelIntrinsicLoweringLegacyPassPass(registry); - llvm::initializeGlobalMergePass(registry); - llvm::initializeIndirectBrExpandPassPass(registry); - llvm::initializeInterleavedLoadCombinePass(registry); - llvm::initializeInterleavedAccessPass(registry); - llvm::initializeEntryExitInstrumenterPass(registry); - llvm::initializePostInlineEntryExitInstrumenterPass(registry); - llvm::initializeUnreachableBlockElimLegacyPassPass(registry); - llvm::initializeExpandReductionsPass(registry); - llvm::initializeWasmEHPreparePass(registry); - llvm::initializeWriteBitcodePassPass(registry); - llvm::initializeHardwareLoopsPass(registry); - llvm::initializeTypePromotionPass(registry); } inline void initLLVMCodeGen() { @@ -118,7 +98,7 @@ inline llvm::CodeGenOpt::Level getCodeGenOptLevel(unsigned optLevel = 2) { } inline String getTargetTriple() { - return llvm::sys::getDefaultTargetTriple(); + return llvm::sys::getProcessTriple(); } inline llvm::TargetMachine *getTargetMachine(unsigned optLevel = 2) { @@ -129,7 +109,7 @@ inline llvm::TargetMachine *getTargetMachine(unsigned optLevel = 2) { return target->createTargetMachine(targetTriple, llvm::codegen::getCPUStr(), llvm::codegen::getFeaturesStr(), - llvm::codegen::InitTargetOptionsFromCodeGenFlags(), + llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple(targetTriple)), llvm::codegen::getExplicitRelocModel(), llvm::codegen::getExplicitCodeModel(), getCodeGenOptLevel(optLevel)); diff --git a/install-macos.sh b/install-macos.sh old mode 100644 new mode 100755 diff --git a/install-ubuntu.sh b/install-ubuntu.sh old mode 100644 new mode 100755 diff --git a/src/CodeGen/Builtin.cpp b/src/CodeGen/Builtin.cpp index 6918604..a7b7503 100644 --- a/src/CodeGen/Builtin.cpp +++ b/src/CodeGen/Builtin.cpp @@ -55,7 +55,7 @@ void BuiltinError::linkModule(LLVMModule &module, LLVMContext &context) { } void BuiltinError::getTypeAndFunction(LLVMModule &module) { - llvm::StructType *errStructType = module.getTypeByName("struct.ss_error"); + llvm::StructType *errStructType = llvm::StructType::getTypeByName(module.getContext(), "struct.ss_error"); type = errStructType->getPointerTo(); exitIfErrorFunc = module.getFunction("ss_exit_if_error"); assertFunc = module.getFunction("ss_assert"); @@ -66,7 +66,7 @@ void BuiltinString::linkModule(LLVMModule &module, LLVMContext &context) { } void BuiltinString::getTypeAndFunction(LLVMModule &module) { - llvm::StructType *strStructType = module.getTypeByName("struct.ss_string"); + llvm::StructType *strStructType = llvm::StructType::getTypeByName(module.getContext(), "struct.ss_string"); type = strStructType->getPointerTo(); createFunc = module.getFunction("ss_string_create"); deleteFunc = module.getFunction("ss_string_delete"); @@ -87,7 +87,7 @@ void BuiltinArray::linkModule(LLVMModule &module, LLVMContext &context) { } void BuiltinArray::getTypeAndFunction(LLVMModule &module) { - llvm::StructType *arrStructType = module.getTypeByName("struct.ss_array"); + llvm::StructType *arrStructType = llvm::StructType::getTypeByName(module.getContext(), "struct.ss_array"); type = arrStructType->getPointerTo(); createIntegerArrayFunc = module.getFunction("ss_array_create_integer_array"); createFloatArrayFunc = module.getFunction("ss_array_create_float_array"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..befe0a8 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB TEST_CASES_FILE *.ss) + +set(LINK_LIBRARY_PATH ${PROJECT_BINARY_DIR}/lib) + +foreach(filepath ${TEST_CASES_FILE}) + string(REGEX REPLACE ".+/(.+)\\..*" "\\1" filename ${filepath}) + add_test( + NAME test_${filename} + COMMAND staticscript ${filepath} -L ${LINK_LIBRARY_PATH} + ) +endforeach() \ No newline at end of file