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/.github/workflows/build.yml b/.github/workflows/build.yml index 4a0f775..548ae74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,21 +2,29 @@ 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: - name: Build on Linux + build_and_test_on_linux: + name: Build and Test on Linux runs-on: ${{ matrix.os }} strategy: matrix: @@ -24,6 +32,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,88 +43,69 @@ 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 apt-get -y install uuid-dev pkg-config doxygen graphviz llvm-10 + run: | + sudo wget https://apt.llvm.org/llvm.sh + sudo chmod +x llvm.sh + 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' - env: - CC: ${{ matrix.compiler.cc }} - CXX: ${{ matrix.compiler.cxx }} run: | - thread_count=`sudo cat /proc/cpuinfo| grep "processor"| wc -l` 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.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 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: | - 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 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 + 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 - 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 + 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++ } + 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: | - 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 + 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 - 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 \ No newline at end of file + sudo cmake --build . --target staticscript document -- -j $THREAD_COUNT + sudo ctest --extra-verbose \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..deb2f67 --- /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 + 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.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 + 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 91a8de7..a139901 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,12 +21,31 @@ 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) include_directories(include) -add_subdirectory(builtin) +add_subdirectory(lib) + +add_subdirectory(src) + +add_executable( + staticscript + $ + $ + $ + $ + $ + $ + $ +) + +add_dependencies(staticscript antlr4_static) + +target_link_libraries(staticscript PRIVATE antlr4_static ${llvm_libs}) + +enable_testing() -add_subdirectory(lib) \ No newline at end of file +add_subdirectory(tests) \ No newline at end of file diff --git a/README-zh_CN.md b/README-zh_CN.md index 7de7a53..e9ae58c 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -1,7 +1,183 @@ -# 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&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=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 +``` + +## 语言特性概要 + +### 变量和常量的声明 + +下面是一些变量声明 + +```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..258f5e2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,181 @@ -# StaticScript +

StaticScript

-> StaticScript is a statically typed and strongly typed programming language, syntactically like TypeScript. +
-## Status +StaticScript is a statically 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&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=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 + +### 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. 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 deleted file mode 100644 index 10011d7..0000000 --- a/builtin/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -set(BUILTIN_SOURCES ss_string.c ss_io.c) -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 - DEPENDS ${BUILTIN_SOURCES} -) - -add_custom_target( - builtin - DEPENDS ${BUILTIN_BITCODES} -) \ 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_io.c b/builtin/ss_io.c deleted file mode 100644 index 1308659..0000000 --- a/builtin/ss_io.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "ss_io.h" - -ss_string *KEEPALIVE 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); - str->size = strlen(str->buffer); - return str; -} - -long KEEPALIVE ss_string2integer(ss_string *str) { - return strtol(str->buffer, NULL, 10); -} - -void KEEPALIVE ss_print_integer(long number) { - printf("%ld", number); -} - -void KEEPALIVE ss_println_integer(long number) { - printf("%ld\n", number); -} - -void KEEPALIVE ss_print_string(ss_string *str) { - printf("%s", str->buffer); -} - -void KEEPALIVE 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 deleted file mode 100644 index 4f09a92..0000000 --- a/builtin/ss_io.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include -#include "ss_base.h" -#include "ss_string.h" - -ss_string *KEEPALIVE ss_integer2string(long number); - -long KEEPALIVE ss_string2integer(ss_string *str); - -void KEEPALIVE ss_print_integer(long number); - -void KEEPALIVE ss_println_integer(long number); - -void KEEPALIVE ss_print_string(ss_string *str); - -void KEEPALIVE ss_println_string(ss_string *str); \ No newline at end of file diff --git a/builtin/ss_string.c b/builtin/ss_string.c deleted file mode 100644 index d50b623..0000000 --- a/builtin/ss_string.c +++ /dev/null @@ -1,173 +0,0 @@ -#include "ss_string.h" - -size_t KEEPALIVE 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 *KEEPALIVE 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 *KEEPALIVE 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); - if (!str) { - return NULL; - } - memcpy(str->buffer, literal, size); - str->size = size; - return str; -} - -void KEEPALIVE ss_string_delete(ss_string *str) { - free(str->buffer); - free(str); -} - -size_t KEEPALIVE ss_string_get_size(ss_string *str) { - return str->size; -} - -long KEEPALIVE 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 KEEPALIVE 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 KEEPALIVE 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); - if (dest->capacity < needed_capacity) { - if (ss_string_grow_with_capacity(dest, needed_capacity) == -1) { - return -1; - } - } - strncat(dest->buffer, src->buffer, src_size); - dest->size = needed_size; - return 0; -} - -long KEEPALIVE 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); - 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); - dest->size = needed_size; - return 0; -} - -ss_string *ss_string_concat(ss_string *str1, ss_string *str2) { - size_t size = str1->size + str2->size; - size_t capacity = ss_string_get_capacity_with_size(size); - ss_string *str = ss_string_create_with_capacity(capacity); - memcpy(str->buffer, str1->buffer, str1->size); - memcpy(str->buffer + str1->size, str2->buffer, str2->size); - str->size = size; - return str; -} - -ss_string *KEEPALIVE ss_string_slice(ss_string *str, ssize_t from, ssize_t to) { - if (from < 0) { - from = str->size + from; - } - if (to < 0) { - to = str->size + to; - } - if (to < from) { - return NULL; - } - size_t slice_size = to - from; - size_t new_capacity = ss_string_get_capacity_with_size(slice_size); - ss_string *new_str = ss_string_create_with_capacity(new_capacity); - memcpy(new_str->buffer, str->buffer + from, slice_size); - new_str->size = slice_size; - return new_str; -} - -long KEEPALIVE 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) { - char *sub = strstr(str->buffer, literal); - if (!sub) { - return -1; - } - return sub - str->buffer; -} - -ssize_t KEEPALIVE 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) { - size_t i = 0; - while (isspace(str->buffer[i])) { - i += 1; - } - char *new_buffer = (char *) calloc(str->capacity, 1); - if (!new_buffer) { - return -1; - } - memset(new_buffer, 0, str->capacity); - memcpy(new_buffer, str->buffer + i, str->size - i); - free(str->buffer); - str->buffer = new_buffer; - str->size -= i; - return 0; -} - -void KEEPALIVE ss_string_trim_right(ss_string *str) { - ssize_t i = str->size - 1; - while (i >= 0 && isspace(str->buffer[i])) { - str->buffer[i] = 0; - i -= 1; - } - str->size = i + 1; -} - -long KEEPALIVE 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 deleted file mode 100644 index 644e47a..0000000 --- a/builtin/ss_string.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "ss_base.h" - -typedef struct { - char *buffer; - size_t size; - size_t capacity; -} ss_string; - -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 *KEEPALIVE ss_string_create(const char *literal); - -void KEEPALIVE ss_string_delete(ss_string *str); - -size_t KEEPALIVE ss_string_get_size(ss_string *str); - -long KEEPALIVE ss_string_grow_with_capacity(ss_string *str, size_t new_capacity); - -long KEEPALIVE ss_string_grow(ss_string *str); - -long KEEPALIVE ss_string_append(ss_string *dest, ss_string *src); - -long KEEPALIVE 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); - -long KEEPALIVE 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 KEEPALIVE ss_string_index_of(ss_string *str, ss_string *substr); - -long KEEPALIVE ss_string_trim_left(ss_string *str); - -void KEEPALIVE ss_string_trim_right(ss_string *str); - -long KEEPALIVE ss_string_trim(ss_string *str); diff --git a/cmake/AddANTLR.cmake b/cmake/AddANTLR.cmake index 36da73f..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.8-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.8-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 af7f9fa..76a6ba8 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 12 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 deleted file mode 100644 index 31365f4..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; -} - -func(1, 2); -test(); \ 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/selection.ss b/examples/selection.ss deleted file mode 100644 index fc39b9b..0000000 --- a/examples/selection.ss +++ /dev/null @@ -1,7 +0,0 @@ -let x: number = 1; -let y: number = 0; -if (x < y) { - x = x + 1; -} else { - y = y + 1; -} \ 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/StaticScriptLexer.g4 b/grammar/StaticScriptLexer.g4 index 22f11cd..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'; @@ -22,6 +25,8 @@ Return: 'return'; // 分隔符 OpenParen: '('; CloseParen: ')'; +OpenBracket: '['; +CloseBracket: ']'; OpenBrace: '{'; CloseBrace: '}'; Colon: ':'; @@ -29,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] @@ -90,6 +116,10 @@ fragment StringCharacter fragment EscapeSequence : CharacterEscapeSequence + | '0' // no digit ahead! TODO + | HexEscapeSequence + | UnicodeEscapeSequence + | ExtendedUnicodeEscapeSequence ; fragment CharacterEscapeSequence @@ -97,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 aac1cb4..39bdee4 100644 --- a/grammar/StaticScriptParser.g4 +++ b/grammar/StaticScriptParser.g4 @@ -6,7 +6,7 @@ options { } module - : statements? + : statements? EOF ; statements @@ -53,20 +53,30 @@ variableDeclarator : Identifier typeAnnotation? variableInitializer? ; +variableInitializer + : Assign expression + ; + typeAnnotation - : Colon builtinType + : Colon type ; -builtinType +type + : arrayType + | basicType + ; + +arrayType + : basicType (OpenBracket CloseBracket)+ + ; + +basicType : Boolean + | Integer | Number | String ; -variableInitializer - : Assign expression - ; - functionDeclaration : Function Identifier OpenParen parameterList? CloseParen typeAnnotation? functionBody ; @@ -79,10 +89,6 @@ functionBody : compoundStatement ; -argumentList - : expressionList - ; - compoundStatement : OpenBrace statements? CloseBrace ; @@ -92,15 +98,13 @@ 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 | 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 @@ -108,17 +112,37 @@ 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 ; callExpression : Identifier OpenParen argumentList? CloseParen ; +argumentList + : expressionList + ; + literal : BooleanLiteral - | IntegerLiteral + | DecimalIntegerLiteral + | HexIntegerLiteral + | OctalIntegerLiteral + | BinaryIntegerLiteral + | FloatLiteral | StringLiteral + | arrayLiteral + ; + +arrayLiteral + : OpenBracket expressionList? CloseBracket + ; + +expressionList + : expression (Comma expression)* ; selectionStatement @@ -147,10 +171,6 @@ forInit | expressionList ; -expressionList - : expression (Comma expression)* - ; - jumpStatement : continueStatement | breakStatement diff --git a/include/AST/ASTBuilder.h b/include/AST/ASTBuilder.h index b036e1b..47d2a1e 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 "Util/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 visitBasicType(StaticScriptParser::BasicTypeContext *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..d42c514 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" @@ -20,16 +20,21 @@ class VarDeclNode : public DeclNode { public: explicit VarDeclNode(); + VarDeclNode( + VarModifier modifier, + const SharedPtr &type + ); + 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 +48,7 @@ class VarDeclNode : public DeclNode { VarModifier modifier; String name; - SharedPtr type = nullptr; + SharedPtr type = nullptr; SharedPtr initVal = nullptr; // 当前变量声明的alloca/load ir @@ -53,7 +58,9 @@ class VarDeclNode : public DeclNode { /// 函数参数声明节点 class ParmVarDeclNode : public VarDeclNode { public: - ParmVarDeclNode(const String &name, const SharedPtr &type); + explicit ParmVarDeclNode(const SharedPtr &type); + + ParmVarDeclNode(const String &name, const SharedPtr &type); ~ParmVarDeclNode() override = default; @@ -65,10 +72,21 @@ 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, - const SharedPtr &returnType, + const SharedPtr &returnType, const SharedPtr &body ); @@ -80,7 +98,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..a2e95c9 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,11 +11,11 @@ class ExprNode : public Node { public: ExprNode() = default; - explicit ExprNode(const SharedPtr &type); + explicit ExprNode(const SharedPtr &type); ~ExprNode() override = default; - SharedPtr inferType = nullptr; + SharedPtr type = nullptr; // 当前表达式的ir LLVMValue *code = nullptr; @@ -24,11 +24,9 @@ class ExprNode : public Node { /// 字面量表达式节点 class LiteralExprNode : public ExprNode { public: - explicit LiteralExprNode(const SharedPtr &type); - LiteralExprNode() = default; - ~LiteralExprNode() override = default; + explicit LiteralExprNode(const SharedPtr &type); }; /// 布尔值字面量表达式节点 @@ -46,21 +44,33 @@ class BooleanLiteralExprNode : public LiteralExprNode { /// 整数字面量表达式节点 class IntegerLiteralExprNode : public LiteralExprNode { public: - explicit IntegerLiteralExprNode(int literal); + explicit IntegerLiteralExprNode(long literal); ~IntegerLiteralExprNode() override = default; void accept(const SharedPtr &visitor) override; - int literal; + 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 LiteralExprNode { public: - explicit StringLiteralExprNode(String literal); + explicit StringLiteralExprNode(); - StringLiteralExprNode() = default; + explicit StringLiteralExprNode(String literal); ~StringLiteralExprNode() override = default; @@ -69,6 +79,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 +155,23 @@ 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; +}; + +//class ArithmeticExprNode; +//class BitwiseExprNode; +//class ComparisonExprNode; +//class LogicalExprNode; +//class AssignmentExprNode; +//class StringOperationExprNode; diff --git a/include/AST/Node.h b/include/AST/Node.h index 6201174..986c37b 100644 --- a/include/AST/Node.h +++ b/include/AST/Node.h @@ -1,7 +1,8 @@ #pragma once #include -#include "Util/Alias.h" +#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 4b7fe84..f77b252 100644 --- a/include/CodeGen/Builtin.h +++ b/include/CodeGen/Builtin.h @@ -1,21 +1,35 @@ #pragma once -#include -#include "config.h" -#include "Util/Alias.h" -#include - +#include "Config/Config.h" +#include "Support/Alias.h" +#include "Support/Error.h" +#include "Support/LLVM.h" 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 { +public: + static void linkModule(LLVMModule &module, LLVMContext &context); + + static void getTypeAndFunction(LLVMModule &module); + + static inline llvm::PointerType *type = nullptr; + static inline LLVMFunction *exitIfErrorFunc = nullptr; + static inline LLVMFunction *assertFunc = 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,14 +44,64 @@ 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 *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; + 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; + 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; + 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; + 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 e5c3580..546f903 100644 --- a/include/CodeGen/IRGenerator.h +++ b/include/CodeGen/IRGenerator.h @@ -1,16 +1,18 @@ #pragma once +#include #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/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; @@ -24,8 +26,12 @@ class IRGenerator : 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; + void visit(const SharedPtr &varExpr) override; void visit(const SharedPtr &callExpr) override; @@ -34,6 +40,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 +67,17 @@ class IRGenerator : public ASTVisitor { } private: - LLVMType *getType(const SharedPtr &builtinType); + LLVMType *getType(const SharedPtr &inputType); + + 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()); @@ -75,7 +93,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 d9ef476..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 "Util/Alias.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 59% rename from include/config.h.in rename to include/Config/Config.h.in index 59f1328..1564653 100644 --- a/include/config.h.in +++ b/include/Config/Config.h.in @@ -4,10 +4,16 @@ #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@ + +#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 8377ec2..b543517 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; @@ -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 new file mode 100644 index 0000000..c485a25 --- /dev/null +++ b/include/Entity/Type.h @@ -0,0 +1,147 @@ +#pragma once + +#include +#include "Support/Alias.h" +#include "Support/Error.h" + +/// 基础类型的种类, Unknown只用于空数组的元素类型 +enum class BasicTypeKind { + Boolean, Integer, Float, String, Unknown +}; + +class ArrayType; + +/// 类型节点 +class Type : public std::enable_shared_from_this { +public: + virtual ~Type() = default; + + [[nodiscard]] virtual bool isBasic() 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 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 < compatibleWith) + [[nodiscard]] bool equals(const SharedPtr &rhs) const; + + /// 判断类型是否相同[无方向](note: equals < sameAs < compatibleWith) + [[nodiscard]] bool sameAs(const SharedPtr &rhs) const; + + /// 判断类型是否兼容[有方向](note: equals < sameAs < compatibleWith) + [[nodiscard]] bool compatibleWith(const SharedPtr &rhs) const; +}; + +/// 基础类型节点 +class BasicType : public Type { +public: + explicit BasicType(BasicTypeKind kind); + + explicit BasicType() = default; + + ~BasicType() override = default; + + [[nodiscard]] bool isBasic() 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]] bool isNumberArray() const override; + + [[nodiscard]] SharedPtr asArray() const override; + + [[nodiscard]] BasicTypeKind getKind() const; + + bool operator==(const BasicType &rhs) = delete; + + bool operator!=(const BasicType &rhs) = delete; + + [[nodiscard]] bool equals(const SharedPtr &rhs) const; + + 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: + BasicTypeKind kind = BasicTypeKind::Unknown; +}; + +/// 数组类型节点 +class ArrayType : public Type { +public: + static SharedPtr createNDArrayType(const SharedPtr &basicType, size_t depth); + + explicit ArrayType(const SharedPtr &elementType); + + explicit ArrayType() = default; + + ~ArrayType() override = default; + + [[nodiscard]] bool isBasic() 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]] bool isNumberArray() const override; + + [[nodiscard]] SharedPtr asArray() const override; + + /// 获取数组的元素类型, 多维数组则获取子数组的类型 + [[nodiscard]] const SharedPtr &getElementType() const; + + /// 获取多维数组最内层的元素类型 + [[nodiscard]] SharedPtr getBasicElementType() const; + + [[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 = BasicType::UNKNOWN_TYPE; + size_t depth = 1; +}; \ No newline at end of file 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..25818a0 --- /dev/null +++ b/include/Optimization/Optimizer.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Support/Alias.h" +#include "Support/LLVM.h" +#include "Support/Error.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/ASTVisitor.h b/include/Sema/ASTVisitor.h index 9ec2cc9..6adea99 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); @@ -26,8 +27,12 @@ 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); + virtual void visit(const SharedPtr &varExpr); virtual void visit(const SharedPtr &callExpr); @@ -36,6 +41,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 +62,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 8590eaf..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 "Exception/SemanticException.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 4c0beb9..a49c86f 100644 --- a/include/Sema/ScopeScanner.h +++ b/include/Sema/ScopeScanner.h @@ -2,10 +2,9 @@ #include "Entity/Scope.h" #include "Sema/ASTVisitor.h" -#include "Exception/SemanticException.h" /// 作用域扫描器 -class ScopeScanner final : public ASTVisitor { +class ScopeScanner final : public ASTVisitorWithScope { public: void visit(const SharedPtr &module) override; @@ -19,8 +18,12 @@ class ScopeScanner 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; + void visit(const SharedPtr &varExpr) override; void visit(const SharedPtr &callExpr) override; @@ -29,6 +32,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 9eec9bb..3197612 100644 --- a/include/Sema/SemanticValidator.h +++ b/include/Sema/SemanticValidator.h @@ -2,52 +2,19 @@ #include "StaticScriptLexer.h" #include "Sema/ASTVisitor.h" -#include "Exception/SemanticException.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 076dcd7..29dda29 100644 --- a/include/Sema/TypeChecker.h +++ b/include/Sema/TypeChecker.h @@ -2,14 +2,13 @@ #include "StaticScriptLexer.h" #include "Sema/ASTVisitor.h" -#include "Exception/SemanticException.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 new file mode 100644 index 0000000..9872581 --- /dev/null +++ b/include/Support/Alias.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include + +using String = std::string; + +template +using Vector = std::vector; + +template +using Stack = std::stack; + +template +using Map = std::map; + +template +using UniquePtr = std::unique_ptr; + +template +using SharedPtr = std::shared_ptr; + +template +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)...); +} + +template +inline SharedPtr staticPtrCast(const SharedPtr

&ptr) { + return std::static_pointer_cast(ptr); +} + +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/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/Util/Find.h b/include/Support/Find.h similarity index 84% rename from include/Util/Find.h rename to include/Support/Find.h index 5f66448..eaca646 100644 --- a/include/Util/Find.h +++ b/include/Support/Find.h @@ -1,4 +1,6 @@ -#include "Util/Alias.h" +#pragma once + +#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..1db3e20 --- /dev/null +++ b/include/Support/LLVM.h @@ -0,0 +1,116 @@ +#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 +#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(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); +} + +inline void initLLVMPasses() { + llvm::PassRegistry ®istry = *llvm::PassRegistry::getPassRegistry(); + llvm::initializeCore(registry); + llvm::initializeTransformUtils(registry); + llvm::initializeScalarOpts(registry); + llvm::initializeObjCARCOpts(registry); + llvm::initializeVectorization(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); +} + +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::getProcessTriple(); +} + +inline llvm::TargetMachine *getTargetMachine(unsigned optLevel = 2) { + const String &targetTriple = getTargetTriple(); + String error; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(targetTriple, error); + reportOnDriverError(!target, error); + return target->createTargetMachine(targetTriple, + llvm::codegen::getCPUStr(), + llvm::codegen::getFeaturesStr(), + llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple(targetTriple)), + llvm::codegen::getExplicitRelocModel(), + llvm::codegen::getExplicitCodeModel(), + getCodeGenOptLevel(optLevel)); +} \ No newline at end of file diff --git a/include/Util/Alias.h b/include/Util/Alias.h deleted file mode 100644 index e0cb505..0000000 --- a/include/Util/Alias.h +++ /dev/null @@ -1,104 +0,0 @@ -#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 -#include -#include -#include -#include -#include -#include - -using String = std::string; - -template -using Vector = std::vector; - -template -using Stack = std::stack; - -template -using Map = std::map; - -template -using SharedPtr = std::shared_ptr; - -template -using SharedPtrVector = Vector>; - -template -using SharedPtrMap = Map>; - -template -inline SharedPtr makeShared(Args &&...args) { - return std::make_shared(std::forward(args)...); -} - -template -inline SharedPtr staticPtrCast(const SharedPtr

&ptr) { - return std::static_pointer_cast(ptr); -} - -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/Util/Output.h b/include/Util/Output.h deleted file mode 100644 index c27d31c..0000000 --- a/include/Util/Output.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include "Util/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/install-macos.sh b/install-macos.sh new file mode 100755 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 100755 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/lib/.gitattributes b/lib/.gitattributes new file mode 100644 index 0000000..4f230f7 --- /dev/null +++ b/lib/.gitattributes @@ -0,0 +1,2 @@ +*.h linguist-language=c +*.c linguist-language=c \ No newline at end of file diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt deleted file mode 100644 index 884f874..0000000 --- a/lib/AST/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(SRC DeclNode.cpp TypeNode.cpp StmtNode.cpp ExprNode.cpp ModuleNode.cpp ASTBuilder.cpp) - -add_library(ast OBJECT ${SRC}) - -add_dependencies(ast parser entity) \ No newline at end of file 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/CMakeLists.txt b/lib/CMakeLists.txt index 9daba98..090fedd 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,19 +1,16 @@ -add_subdirectory(Entity) -add_subdirectory(AST) -add_subdirectory(Sema) -add_subdirectory(CodeGen) -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 + 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/lib/CodeGen/Builtin.cpp b/lib/CodeGen/Builtin.cpp deleted file mode 100644 index a669bcf..0000000 --- a/lib/CodeGen/Builtin.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "CodeGen/Builtin.h" -#include "Exception/CodeGenException.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"); - } - llvm::StructType *strStructType = module.getTypeByName("struct.ss_string"); - type = strStructType->getPointerTo(); - createFunc = module.getFunction("ss_string_create"); - deleteFunc = module.getFunction("ss_string_delete"); - getSizeFunc = module.getFunction("ss_string_get_size"); - concatFunc = module.getFunction("ss_string_concat"); - appendFunc = module.getFunction("ss_string_append"); - prependFunc = module.getFunction("ss_string_prepend"); - sliceFunc = module.getFunction("ss_string_slice"); - equalsFunc = module.getFunction("ss_string_equals"); - indexOfFunc = module.getFunction("ss_string_index_of"); - trimLeftFunc = module.getFunction("ss_string_trim_left"); - trimRightFunc = module.getFunction("ss_string_trim_right"); - 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"); - } - integer2stringFunc = module.getFunction("ss_integer2string"); - string2integerFunc = module.getFunction("ss_string2integer"); - printIntegerFunc = module.getFunction("ss_print_integer"); - printlnIntegerFunc = module.getFunction("ss_println_integer"); - 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/ExprCodeGen.cpp b/lib/CodeGen/ExprCodeGen.cpp deleted file mode 100644 index 0997f68..0000000 --- a/lib/CodeGen/ExprCodeGen.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include "CodeGen/IRGenerator.h" - -void IRGenerator::visit(const SharedPtr &boolLiteralExpr) { - boolLiteralExpr->code = boolLiteralExpr->literal ? - llvmIRBuilder.getTrue() : - llvmIRBuilder.getFalse(); -} - -void IRGenerator::visit(const SharedPtr &intLiteralExpr) { - intLiteralExpr->code = LLVMConstantInt::get( - llvmIRBuilder.getInt64Ty(), - intLiteralExpr->literal, - true - ); -} - -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); -} - -void IRGenerator::visit(const SharedPtr &varExpr) { - // 按右值处理 - varExpr->code = llvmIRBuilder.CreateLoad(varExpr->refVarDecl->code); -} - -void IRGenerator::visit(const SharedPtr &callExpr) { - ASTVisitor::visit(callExpr); - llvm::Function *calleeFunc = llvmModule->getFunction(callExpr->calleeName); - if (!calleeFunc) { - throw CodeGenException("没有找到函数"); - } - if (calleeFunc->arg_size() != callExpr->args.size()) { - throw CodeGenException("函数参数传递不正确"); - } - Vector argsV; - for (size_t i = 0, e = callExpr->args.size(); i != e; ++i) { - argsV.push_back(callExpr->args[i]->code); - } - callExpr->code = llvmIRBuilder.CreateCall(calleeFunc, argsV); -} - -void IRGenerator::visit(const SharedPtr &uopExpr) { - ASTVisitor::visit(uopExpr); - - SharedPtr subExpr = uopExpr->subExpr; - - switch (uopExpr->opCode) { - case StaticScriptLexer::PlusPlus: - case StaticScriptLexer::MinusMinus: { - const SharedPtr &subVarExpr = staticPtrCast(subExpr); - LLVMValue *value = llvmIRBuilder.CreateLoad(subVarExpr->refVarDecl->code); - LLVMValue *newValue; - LLVMConstantInt *one = llvmIRBuilder.getInt64(1); - - if (uopExpr->opCode == StaticScriptLexer::PlusPlus) { - newValue = llvmIRBuilder.CreateNSWAdd(value, one); - } else { - newValue = llvmIRBuilder.CreateNSWSub(value, one); - } - if (uopExpr->isPostfix) { - llvmIRBuilder.CreateStore(newValue, subVarExpr->refVarDecl->code); - uopExpr->code = value; - } else { - llvmIRBuilder.CreateStore(newValue, subVarExpr->refVarDecl->code); - uopExpr->code = newValue; - } - break; - } - case StaticScriptLexer::Not: { - uopExpr->code = llvmIRBuilder.CreateXor(subExpr->code, llvmIRBuilder.getTrue()); - break; - } - case StaticScriptLexer::BitNot: { - uopExpr->code = llvmIRBuilder.CreateXor(subExpr->code, llvmIRBuilder.getInt64(-1)); - break; - } - case StaticScriptLexer::Plus: { - uopExpr->code = subExpr->code; - break; - } - case StaticScriptLexer::Minus: { - uopExpr->code = llvmIRBuilder.CreateNSWNeg(subExpr->code); - break; - } - } -} - -void IRGenerator::visit(const SharedPtr &bopExpr) { - unsigned int bopCode = bopExpr->opCode; - - if (bopCode == StaticScriptLexer::Assign) { - bopExpr->rhs->accept(shared_from_this()); - } else { - ASTVisitor::visit(bopExpr); - } - - const SharedPtr &type = bopExpr->lhs->inferType; - LLVMValue *lhsCode = bopExpr->lhs->code; - LLVMValue *rhsCode = bopExpr->rhs->code; - LLVMValue *targetCode = nullptr; - - switch (bopCode) { - case StaticScriptLexer::Plus: - case StaticScriptLexer::PlusAssign: { - if (type == BuiltinTypeNode::STRING_TYPE) { - Vector argsV{lhsCode, rhsCode}; - targetCode = llvmIRBuilder.CreateCall(BuiltinString::concatFunc, argsV); - } else if (type == BuiltinTypeNode::INTEGER_TYPE) { - targetCode = llvmIRBuilder.CreateNSWAdd(lhsCode, rhsCode); - } - break; - } - case StaticScriptLexer::Minus: - case StaticScriptLexer::MinusAssign: { - targetCode = llvmIRBuilder.CreateNSWSub(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::Multiply: - case StaticScriptLexer::MultiplyAssign: { - targetCode = llvmIRBuilder.CreateNSWMul(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::Divide: - case StaticScriptLexer::DivideAssign: { - targetCode = llvmIRBuilder.CreateSDiv(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::Modulus: - case StaticScriptLexer::ModulusAssign: { - targetCode = llvmIRBuilder.CreateSRem(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::BitAnd: - case StaticScriptLexer::BitAndAssign: { - targetCode = llvmIRBuilder.CreateAnd(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::BitXOr: - case StaticScriptLexer::BitXorAssign: { - targetCode = llvmIRBuilder.CreateXor(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::BitOr: - case StaticScriptLexer::BitOrAssign: { - targetCode = llvmIRBuilder.CreateOr(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::LeftShift: - case StaticScriptLexer::LeftShiftAssign: { - targetCode = llvmIRBuilder.CreateShl(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::RightShift: - case StaticScriptLexer::RightShiftAssign: { - targetCode = llvmIRBuilder.CreateAShr(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::And: - case StaticScriptLexer::AndAssign: { - targetCode = llvmIRBuilder.CreateAnd(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::Or: - case StaticScriptLexer::OrAssign: { - targetCode = llvmIRBuilder.CreateOr(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::LessThan: { - targetCode = llvmIRBuilder.CreateICmpSLT(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::GreaterThan: { - targetCode = llvmIRBuilder.CreateICmpSGT(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::LessThanEquals: { - targetCode = llvmIRBuilder.CreateICmpSLE(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::GreaterThanEquals: { - targetCode = llvmIRBuilder.CreateICmpSGE(lhsCode, rhsCode); - break; - } - case StaticScriptLexer::Equals: { - if (type == BuiltinTypeNode::STRING_TYPE) { - Vector argsV{lhsCode, rhsCode}; - LLVMValue *relationship = llvmIRBuilder.CreateCall(BuiltinString::equalsFunc, argsV); - targetCode = llvmIRBuilder.CreateICmpEQ(relationship, llvmIRBuilder.getInt64(0)); - } else { - targetCode = llvmIRBuilder.CreateICmpEQ(lhsCode, rhsCode); - } - break; - } - case StaticScriptLexer::NotEquals: { - if (type == BuiltinTypeNode::STRING_TYPE) { - Vector argsV{lhsCode, rhsCode}; - LLVMValue *relationship = llvmIRBuilder.CreateCall(BuiltinString::equalsFunc, argsV); - targetCode = llvmIRBuilder.CreateICmpNE(relationship, llvmIRBuilder.getInt64(0)); - } else { - targetCode = llvmIRBuilder.CreateICmpNE(lhsCode, rhsCode); - } - break; - } - default: - break; - } - if (bopCode >= StaticScriptLexer::Assign && bopCode <= StaticScriptLexer::OrAssign) { - // 在语义阶段保证lhs类型为IdentifierExprNode - // 按左值处理 - SharedPtr leftVarExpr = staticPtrCast(bopExpr->lhs); - if (bopCode == StaticScriptLexer::Assign) { - llvmIRBuilder.CreateStore(rhsCode, leftVarExpr->refVarDecl->code); - targetCode = llvmIRBuilder.CreateLoad(leftVarExpr->refVarDecl->code); - } else { - llvmIRBuilder.CreateStore(targetCode, leftVarExpr->refVarDecl->code); - } - } - bopExpr->code = targetCode; -} \ No newline at end of file diff --git a/lib/CodeGen/IRGenerator.cpp b/lib/CodeGen/IRGenerator.cpp deleted file mode 100644 index ca1e8c7..0000000 --- a/lib/CodeGen/IRGenerator.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "CodeGen/IRGenerator.h" -#include "CodeGen/Pass.h" - -IRGenerator::IRGenerator() : llvmIRBuilder(llvmContext) {} - -void IRGenerator::resolve(const SharedPtr &module) { - llvmModule = makeShared(module->filename, llvmContext); - Builtin::initialize(*llvmModule, llvmContext); - ASTVisitor::resolve(module); - LLVMVerifyModule(*llvmModule); - runPasses(*llvmModule); -} - -void IRGenerator::visit(const SharedPtr &module) { - LLVMFunctionType *mainFnType = LLVMFunctionType::get(llvmIRBuilder.getInt64Ty(), false); - mainFn = LLVMFunction::Create(mainFnType, LLVMFunction::ExternalLinkage, "main", llvmModule.get()); - curFn = mainFn; - LLVMBasicBlock *mainEntryBlock = LLVMBasicBlock::Create(llvmContext, "entry", mainFn); - llvmIRBuilder.SetInsertPoint(mainEntryBlock); - ASTVisitor::visit(module); - setFuncInsertPoint(mainFn); - llvmIRBuilder.CreateRet(llvmIRBuilder.getInt64(0)); - LLVMVerifyFunction(*mainFn); -} - -LLVMType *IRGenerator::getType(const SharedPtr &builtinType) { - 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; - } - return type; -} - -void IRGenerator::emitBlock(LLVMBasicBlock *bb, bool isFinished) { - LLVMBasicBlock *curBB = llvmIRBuilder.GetInsertBlock(); - emitBranch(bb); - if (isFinished && bb->use_empty()) { - delete bb; - return; - } - if (curBB && curBB->getParent()) { - curFn->getBasicBlockList().insertAfter(curBB->getIterator(), bb); - } else { - curFn->getBasicBlockList().push_back(bb); - } - llvmIRBuilder.SetInsertPoint(bb); -} - -void IRGenerator::emitBranch(LLVMBasicBlock *targetBB) { - LLVMBasicBlock *curBB = llvmIRBuilder.GetInsertBlock(); - if (!curBB || curBB->getTerminator()) { - - } else { - llvmIRBuilder.CreateBr(targetBB); - } - llvmIRBuilder.ClearInsertionPoint(); -} diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp deleted file mode 100644 index f6b3688..0000000 --- a/lib/Driver/Driver.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include -#include -#include "StaticScriptLexer.h" -#include "StaticScriptParser.h" -#include "AST/ASTBuilder.h" -#include "Sema/ScopeScanner.h" -#include "Sema/ReferenceResolver.h" -#include "Sema/TypeChecker.h" -#include "Sema/SemanticValidator.h" -#include "CodeGen/IRGenerator.h" -#include "Exception/DriverException.h" - -int drive(int argc, char **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 emitLLVM("emit-llvm", llvm::cl::desc("Generate the LLVM representation for input file"), 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)); - llvm::cl::opt showVersionAlias("v", llvm::cl::desc("Alias for --version"), llvm::cl::cat(genericOptsCat)); - - llvm::cl::ParseCommandLineOptions(argc, argv, "StaticScript Compiler\n"); - - if (showHelp || showHelpAlias) { - llvm::cl::PrintHelpMessage(false, true); - return 0; - } - - if (showVersion || showVersionAlias) { - llvm::outs() << "StaticScript v" << PROJECT_VERSION << '\n'; - return 0; - } - - if (inputFilename.empty()) { - llvm::cl::PrintHelpMessage(false, true); - return 0; - } - - if (outputFilename.empty()) { - outputFilename.setValue(emitLLVM ? "ss-ir.ll" : "ss-out.o"); - } - - if(!llvm::sys::fs::exists(inputFilename)) { - throw DriverException("Not found input file"); - } - std::ifstream fin(inputFilename.data()); - if (!fin) { - throw DriverException("Open input file failed"); - } - - antlr4::ANTLRInputStream inputStream(fin); - StaticScriptLexer lexer(&inputStream); - antlr4::CommonTokenStream tokenStream(&lexer); - tokenStream.fill(); - StaticScriptParser parser(&tokenStream); - antlr4::tree::ParseTree *tree = parser.module(); - ASTBuilder builder(inputFilename.data()); - SharedPtr module = builder.visit(tree); - // 语义分析: 1. 作用域扫描 - SharedPtr scanner = makeShared(); - scanner->resolve(module); - // 语义分析: 2. 引用消解 - SharedPtr resolver = makeShared(); - resolver->resolve(module); - // 语义分析: 3. 类型检查 - SharedPtr checker = makeShared(); - checker->resolve(module); - // 语义分析: 4. 正确性检查 - SharedPtr validator = makeShared(); - validator->resolve(module); - // 中间代码生成 - SharedPtr generator = makeShared(); - generator->resolve(module); - - LLVMModule &llvmModule = generator->getLLVMModule(); - - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - - const String &targetTriple = llvm::sys::getDefaultTargetTriple(); - llvmModule.setTargetTriple(targetTriple); - - String targetErrorMsg; - const llvm::Target *target = llvm::TargetRegistry::lookupTarget(targetTriple, targetErrorMsg); - - if (!target) { - throw DriverException(targetErrorMsg); - } - - String cpu = "generic"; - String features; - llvm::TargetOptions opt; - auto rm = llvm::Optional(); - llvm::TargetMachine *targetMachine = target->createTargetMachine(targetTriple, cpu, features, opt, rm); - - llvmModule.setDataLayout(targetMachine->createDataLayout()); - - 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()); - } - - if (emitLLVM) { - dest << llvmModule; - } else { - 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"); - } - pass.run(llvmModule); - } - dest.flush(); - 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'; - } -} diff --git a/lib/Sema/SemanticValidator.cpp b/lib/Sema/SemanticValidator.cpp deleted file mode 100644 index 3e8a938..0000000 --- a/lib/Sema/SemanticValidator.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#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()) { - if (!dynPtrCast(varDecl->initVal)) { - throw SemanticException("常量初始值暂时只支持字面量"); - } - } -} - -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语句"); - } - } else if (funcDecl->name == "main") { - throw SemanticException("函数名不能为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("自增自减运算符只允许对变量进行运算"); - } - } -} - -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 (varExpr->refVarDecl->isConstant()) { - throw SemanticException("不允许对常量赋值"); - } - } else { - throw SemanticException("只允许对变量赋值, 不允许对表达式赋值"); - } - } -} - -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语句语句只能出现在循环里"); - } -} - -void SemanticValidator::visit(const SharedPtr &breakStmt) { - if (!breakStmt->refIterationStmt) { - throw SemanticException("break语句只能出现在循环里"); - } -} - -void SemanticValidator::visit(const SharedPtr &returnStmt) { - ASTVisitor::visit(returnStmt); -} diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp deleted file mode 100644 index 554b30e..0000000 --- a/lib/Sema/TypeChecker.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "Sema/TypeChecker.h" - -void TypeChecker::visit(const SharedPtr &varDecl) { - if (varDecl->initVal) { - varDecl->initVal->accept(shared_from_this()); - if (varDecl->type) { - if (varDecl->type != varDecl->initVal->inferType) { - throw SemanticException("变量声明类型与初始值类型不匹配: " + varDecl->name); - } - } else { - varDecl->type = varDecl->initVal->inferType; - } - } else { - if (varDecl->isConstant()) { - throw SemanticException("const常量必须有初始值"); - } - if (!varDecl->type) { - throw SemanticException("变量未指定类型: " + 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(); - } - } -} - -void TypeChecker::visit(const SharedPtr &varExpr) { - varExpr->inferType = varExpr->refVarDecl->type; -} - -void TypeChecker::visit(const SharedPtr &callExpr) { - ASTVisitor::visit(callExpr); - size_t argsSize = callExpr->args.size(); - size_t paramsSize = callExpr->refFuncDecl->params.size(); - if (argsSize != paramsSize) { - throw SemanticException("调用" + callExpr->calleeName + "函数时参数数量不匹配"); - } - 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) + "个参数类型不匹配"); - } - } - callExpr->inferType = callExpr->refFuncDecl->returnType; -} - -void TypeChecker::visit(const SharedPtr &uopExpr) { - ASTVisitor::visit(uopExpr); - - unsigned int uop = uopExpr->opCode; - const SharedPtr &inferType = uopExpr->subExpr->inferType; - - // 分别检查!运算符和其他运算符的算子类型 - if (uop == StaticScriptLexer::Not) { - // !运算符的算子必须是布尔类型 - if (inferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("!运算符只允许对布尔值进行运算"); - } - } else { - // 其他运算符的算子必须是整数类型 - if (inferType != BuiltinTypeNode::INTEGER_TYPE) { - throw SemanticException("当前一元运算符只允许对整数进行运算"); - } - } - uopExpr->inferType = inferType; -} - -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; - - // 所有的二元运算符只支持运算同类型的表达式 - if (leftInferType != rightInferType) { - throw SemanticException("二元运算符" + std::to_string(bop) + "只支持运算同类型的表达式"); - } - - if (bop == StaticScriptLexer::Plus || bop == StaticScriptLexer::PlusAssign) { - if (leftInferType != BuiltinTypeNode::INTEGER_TYPE && - leftInferType != BuiltinTypeNode::STRING_TYPE) { - throw SemanticException("加运算只支持整数类型和字符串类型"); - } - } 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) + "只支持整数类型"); - } - } 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 (bop >= StaticScriptLexer::LessThan && bop <= StaticScriptLexer::NotEquals) { - bopExpr->inferType = BuiltinTypeNode::BOOLEAN_TYPE; - } else { - bopExpr->inferType = leftInferType; - } -} - -void TypeChecker::visit(const SharedPtr &ifStmt) { - ASTVisitor::visit(ifStmt); - if (ifStmt->condition->inferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("if语句条件暂时只支持boolean类型"); - } -} - -void TypeChecker::visit(const SharedPtr &whileStmt) { - ASTVisitor::visit(whileStmt); - if (whileStmt->condition->inferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("while语句条件暂时只支持boolean类型"); - } -} - -void TypeChecker::visit(const SharedPtr &forStmt) { - ASTVisitor::visit(forStmt); - if (forStmt->condition && - forStmt->condition->inferType != BuiltinTypeNode::BOOLEAN_TYPE) { - throw SemanticException("for语句条件暂时只支持boolean类型"); - } -} - -void TypeChecker::visit(const SharedPtr &returnStmt) { - ASTVisitor::visit(returnStmt); - if (returnStmt->returnExpr) { - if (returnStmt->returnExpr->inferType != returnStmt->refFuncDecl->returnType) { - throw SemanticException("return返回值类型与函数返回值类型不匹配"); - } - } else { - if (returnStmt->refFuncDecl->returnType) { - throw SemanticException("return语句缺少返回值"); - } - } -} diff --git a/lib/ss_array.c b/lib/ss_array.c new file mode 100644 index 0000000..ad0e78b --- /dev/null +++ b/lib/ss_array.c @@ -0,0 +1,201 @@ +#include "ss_array.h" + +ss_array *ss_array_create_integer_array(ss_error **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), FloatType, error); +} + +ss_array *ss_array_create_boolean_array(ss_error **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 *), 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 *), ArrayType, error); + if (!arr) { + return NULL; + } + 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, 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, 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, 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 *, 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 *, ArrayType) +} + +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->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) { + ss_array_push_detect() + 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) +} + +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) +} + +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) +} + +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) +} + +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) +} + +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_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) +} + +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, arr->element_type, 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/lib/ss_array.h b/lib/ss_array.h new file mode 100644 index 0000000..19ea5ac --- /dev/null +++ b/lib/ss_array.h @@ -0,0 +1,181 @@ +#pragma once + +#include +#include +#include +#include "ss_string.h" +#include "ss_error.h" + +#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; + enum ElementType element_type; +} 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, 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); + 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->element_type = element_type; + return arr; +} + +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); + +ss_array *ss_array_create_array_array(ss_error **error); + +#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), element_type, error); \ + if (!arr) { \ + return NULL; \ + } \ + memcpy(arr->buffer, literal_list, arr->element_size * size); \ + arr->size = size; \ + 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_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); + +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); + +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); \ + 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_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); + +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); + +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); + +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); + +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); + +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_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); + +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/lib/ss_error.c b/lib/ss_error.c new file mode 100644 index 0000000..86e79e9 --- /dev/null +++ b/lib/ss_error.c @@ -0,0 +1,18 @@ +#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); + } +} + +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/lib/ss_error.h b/lib/ss_error.h new file mode 100644 index 0000000..d09b519 --- /dev/null +++ b/lib/ss_error.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#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); + +void ss_assert(bool condition); \ No newline at end of file diff --git a/lib/ss_io.c b/lib/ss_io.c new file mode 100644 index 0000000..f558867 --- /dev/null +++ b/lib/ss_io.c @@ -0,0 +1,49 @@ +#include "ss_io.h" + +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); + str->size = strlen(str->buffer); + 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); +} + +void ss_println_integer(long number) { + printf("%ld\n", number); +} + +void ss_print_float(double number) { + printf("%f", number); +} + +void ss_println_float(double number) { + printf("%f\n", number); +} + +void ss_print_string(ss_string *str) { + printf("%s", str->buffer); +} + +void ss_println_string(ss_string *str) { + printf("%s\n", str->buffer); +} \ No newline at end of file diff --git a/lib/ss_io.h b/lib/ss_io.h new file mode 100644 index 0000000..890983e --- /dev/null +++ b/lib/ss_io.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "ss_string.h" + +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/lib/ss_string.c b/lib/ss_string.c new file mode 100644 index 0000000..561b579 --- /dev/null +++ b/lib/ss_string.c @@ -0,0 +1,123 @@ +#include "ss_string.h" + +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); + if (!str) { + return NULL; + } + memcpy(str->buffer, literal, size); + str->size = size; + return str; +} + +void ss_string_delete(ss_string *str) { + free(str->buffer); + free(str); +} + +size_t ss_string_get_size(ss_string *str) { + return str->size; +} + +long ss_string_append(ss_string *dest, ss_string *src) { + 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); + dest->size = needed_size; + return 0; +} + +long ss_string_prepend(ss_string *dest, ss_string *src) { + 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); + dest->size = needed_size; + return 0; +} + +ss_string *ss_string_concat(ss_string *str1, ss_string *str2) { + size_t size = str1->size + str2->size; + size_t capacity = ss_string_get_capacity_with_size(size); + ss_string *str = ss_string_create_with_capacity(capacity); + memcpy(str->buffer, str1->buffer, str1->size); + memcpy(str->buffer + str1->size, str2->buffer, str2->size); + str->size = size; + return str; +} + +ss_string *ss_string_slice(ss_string *str, ssize_t from, ssize_t to) { + if (from < 0) { + from = str->size + from; + } + if (to < 0) { + to = str->size + to; + } + if (to < from) { + return NULL; + } + size_t slice_size = to - from; + size_t new_capacity = ss_string_get_capacity_with_size(slice_size); + ss_string *new_str = ss_string_create_with_capacity(new_capacity); + memcpy(new_str->buffer, str->buffer + from, slice_size); + new_str->size = slice_size; + return new_str; +} + +long ss_string_equals(ss_string *str1, ss_string *str2) { + if (str1->size != str2->size) { + return -1; + } + return strncmp(str1->buffer, str2->buffer, str1->size); +} + +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; +} + +long ss_string_trim_left(ss_string *str) { + size_t i = 0; + while (isspace(str->buffer[i])) { + i += 1; + } + char *new_buffer = (char *) calloc(str->capacity, 1); + if (!new_buffer) { + return -1; + } + memset(new_buffer, 0, str->capacity); + memcpy(new_buffer, str->buffer + i, str->size - i); + free(str->buffer); + str->buffer = new_buffer; + str->size -= i; + return 0; +} + +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; + i -= 1; + } + str->size = i + 1; +} + +long ss_string_trim(ss_string *str) { + ss_string_trim_right(str); + return ss_string_trim_left(str); +} diff --git a/lib/ss_string.h b/lib/ss_string.h new file mode 100644 index 0000000..4f2e2ea --- /dev/null +++ b/lib/ss_string.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include + +typedef struct { + char *buffer; + size_t size; + size_t capacity; +} ss_string; + +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); + +void ss_string_delete(ss_string *str); + +size_t ss_string_get_size(ss_string *str); + +long ss_string_append(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 *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(ss_string *str, ss_string *substr); + +long ss_string_trim_left(ss_string *str); + +void ss_string_trim_right(ss_string *str); + +long ss_string_trim(ss_string *str); diff --git a/lib/AST/ASTBuilder.cpp b/src/AST/ASTBuilder.cpp similarity index 80% rename from lib/AST/ASTBuilder.cpp rename to src/AST/ASTBuilder.cpp index 06d865b..afcf639 100644 --- a/lib/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) { @@ -112,30 +114,47 @@ 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 = visitBasicType(ctx->basicType()).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 &basicType = visitBasicType(ctx->basicType()); + size_t depth = ctx->OpenBracket().size(); + return ArrayType::createNDArrayType(basicType, depth); +} + +antlrcpp::Any ASTBuilder::visitBasicType(StaticScriptParser::BasicTypeContext *ctx) { + if (ctx->Boolean()) { + return BasicType::BOOLEAN_TYPE; + } else if (ctx->Integer()) { + return BasicType::INTEGER_TYPE; + } else if (ctx->Number()) { + return BasicType::FLOAT_TYPE; + } + return BasicType::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 +171,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 +183,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 +204,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 +219,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 +248,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,22 +264,80 @@ 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()) { bool literal = ctx->BooleanLiteral()->getText() == "true"; literalExpr = makeShared(literal); - } 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>(); + } 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; } +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/src/AST/CMakeLists.txt b/src/AST/CMakeLists.txt new file mode 100644 index 0000000..afe6e64 --- /dev/null +++ b/src/AST/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SRC DeclNode.cpp StmtNode.cpp ExprNode.cpp ModuleNode.cpp ASTBuilder.cpp) + +add_library(ast OBJECT ${SRC}) + +add_dependencies(ast parser entity) \ No newline at end of file diff --git a/lib/AST/DeclNode.cpp b/src/AST/DeclNode.cpp similarity index 56% rename from lib/AST/DeclNode.cpp rename to src/AST/DeclNode.cpp index 239b3e6..5351c9d 100644 --- a/lib/AST/DeclNode.cpp +++ b/src/AST/DeclNode.cpp @@ -4,15 +4,20 @@ VarDeclNode::VarDeclNode() : modifier(VarModifier::Let) {} +VarDeclNode::VarDeclNode( + VarModifier modifier, + const SharedPtr &type +) : modifier(modifier), type(type) {} + 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 +30,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; } @@ -37,9 +39,13 @@ 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 + const SharedPtr &type ) : VarDeclNode(VarModifier::Param, name, type) {} void ParmVarDeclNode::accept(const SharedPtr &visitor) { @@ -48,23 +54,46 @@ 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 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, BuiltinTypeNode::STRING_TYPE, nullptr); - functions["ss_string2integer"] = makeShared("ss_string2integer", strArgs, BuiltinTypeNode::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, 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); 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, - const SharedPtr &returnType, + const SharedPtr &returnType, const SharedPtr &body ) : name(std::move(name)), params(params), @@ -80,8 +109,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/src/AST/ExprNode.cpp similarity index 60% rename from lib/AST/ExprNode.cpp rename to src/AST/ExprNode.cpp index 7ecbb95..711029b 100644 --- a/lib/AST/ExprNode.cpp +++ b/src/AST/ExprNode.cpp @@ -1,30 +1,45 @@ #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) : type(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) {} +BooleanLiteralExprNode::BooleanLiteralExprNode(bool literal) : literal(literal), LiteralExprNode(BasicType::BOOLEAN_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), LiteralExprNode(BuiltinTypeNode::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(String literal) : literal(std::move(literal)), LiteralExprNode(BuiltinTypeNode::STRING_TYPE) {} +StringLiteralExprNode::StringLiteralExprNode() : LiteralExprNode(BasicType::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(BasicType::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 +85,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/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/src/CodeGen/Builtin.cpp b/src/CodeGen/Builtin.cpp new file mode 100644 index 0000000..a7b7503 --- /dev/null +++ b/src/CodeGen/Builtin.cpp @@ -0,0 +1,144 @@ +#include "CodeGen/Builtin.h" + +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 { + // 在当前执行目录下搜索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)) { + libDir = libPath.str(); + } + } + + 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); + 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 = 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); \ + 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 = llvm::StructType::getTypeByName(module.getContext(), "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) { + BUILTIN_LINK_MODULE(string) +} + +void BuiltinString::getTypeAndFunction(LLVMModule &module) { + 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"); + getSizeFunc = module.getFunction("ss_string_get_size"); + concatFunc = module.getFunction("ss_string_concat"); + appendFunc = module.getFunction("ss_string_append"); + prependFunc = module.getFunction("ss_string_prepend"); + sliceFunc = module.getFunction("ss_string_slice"); + equalsFunc = module.getFunction("ss_string_equals"); + indexOfFunc = module.getFunction("ss_string_index_of"); + trimLeftFunc = module.getFunction("ss_string_trim_left"); + trimRightFunc = module.getFunction("ss_string_trim_right"); + trimFunc = module.getFunction("ss_string_trim"); +} + +void BuiltinArray::linkModule(LLVMModule &module, LLVMContext &context) { + BUILTIN_LINK_MODULE(array) +} + +void BuiltinArray::getTypeAndFunction(LLVMModule &module) { + 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"); + 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"); + 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"); + 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"); + 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"); + 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/lib/CodeGen/CMakeLists.txt b/src/CodeGen/CMakeLists.txt similarity index 76% rename from lib/CodeGen/CMakeLists.txt rename to src/CodeGen/CMakeLists.txt index c0eea2e..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 ast entity 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 61% rename from lib/CodeGen/DeclCodeGen.cpp rename to src/CodeGen/DeclCodeGen.cpp index 9402a44..9f0a973 100644 --- a/lib/CodeGen/DeclCodeGen.cpp +++ b/src/CodeGen/DeclCodeGen.cpp @@ -4,21 +4,38 @@ void IRGenerator::visit(const SharedPtr &varDecl) { ASTVisitor::visit(varDecl); LLVMType *type = getType(varDecl->type); - // 变量有初始值 - bool hasInitVal = bool(varDecl->initVal); + 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 isStringVar = varDecl->type == BuiltinTypeNode::STRING_TYPE; llvm::Constant *initializer; - if (isStringVar) { + if (declString || declArray) { initializer = llvm::ConstantPointerNull::getNullValue(type); + } else if (isLiteralInit) { + if (declFloat) { + initializer = llvm::cast(varInitCode); + } else { + initializer = llvm::cast(varInitCode); + } } else { - if (isLiteralInit) { - initializer = LLVMCast(varDecl->initVal->code); + if (declFloat) { + // 初始值为浮点表达式 + initializer = llvm::ConstantFP::get(type, 0.0); } else { + // 初始值为布尔和整数表达式 initializer = llvm::ConstantInt::get(type, 0); } } @@ -31,20 +48,19 @@ void IRGenerator::visit(const SharedPtr &varDecl) { varDecl->name ); uint64_t alignment = 8; - if (varDecl->type == BuiltinTypeNode::BOOLEAN_TYPE) { + if (declBoolean) { alignment = 1; } gVar->setAlignment(llvm::MaybeAlign(alignment)); - // 如果为字符串或者有非字面量的初始值 - if (isStringVar || (hasInitVal && !isLiteralInit)) { - llvmIRBuilder.CreateStore(varDecl->initVal->code, gVar); + // 在main函数中store字符串和数组的指针值或者非字面量的表达式值 + // 全局变量初始值为字面量时不需要在store varInitCode + if (declString || declArray || !isLiteralInit) { + 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; } } @@ -56,7 +72,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); @@ -84,5 +100,6 @@ void IRGenerator::visit(const SharedPtr &funcDecl) { llvmIRBuilder.CreateRetVoid(); } } - LLVMVerifyFunction(*func); + llvm::verifyFunction(*func); + curFn = mainFn; } \ No newline at end of file diff --git a/src/CodeGen/ExprCodeGen.cpp b/src/CodeGen/ExprCodeGen.cpp new file mode 100644 index 0000000..9dfe1ec --- /dev/null +++ b/src/CodeGen/ExprCodeGen.cpp @@ -0,0 +1,468 @@ +#include "CodeGen/IRGenerator.h" + +void IRGenerator::visit(const SharedPtr &boolLiteralExpr) { + boolLiteralExpr->code = boolLiteralExpr->literal ? llvmIRBuilder.getTrue() : llvmIRBuilder.getFalse(); +} + +void IRGenerator::visit(const SharedPtr &intLiteralExpr) { + intLiteralExpr->code = LLVMConstantInt::get( + llvmIRBuilder.getInt64Ty(), + intLiteralExpr->literal, + true + ); +} + +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) { + 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->isBoolean()) { + createFunc = BuiltinArray::createBooleanArrayFunc; + } else if (elementType->isInteger()) { + createFunc = BuiltinArray::createIntegerArrayFunc; + } else if (elementType->isFloat()) { + createFunc = BuiltinArray::createFloatArrayFunc; + } else if (elementType->isString()) { + 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 elementSize = arrayLiteralExpr->elements.size(); + LLVMValue *elementSizeCode = llvmIRBuilder.getInt64(elementSize); + 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->isBoolean()) { + // 避免使用vector + Vector literalVector(elementSize); + for (size_t i = 0; i < elementSize; ++i) { + literalVector[i] = staticPtrCast(arrayLiteralExpr->elements[i])->literal; + } + GenerateBasicArray(unsigned char, Boolean, Int8) + } else if (elementType->isInteger()) { + 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; + } + } + 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; + } + } + GenerateBasicArray(double, Float, Double) + } else if (elementType->isString()) { + ASTVisitor::visit(arrayLiteralExpr); + GenerateComplexArray(String) + } else if (elementType->isArray()) { + ASTVisitor::visit(arrayLiteralExpr); + GenerateComplexArray(Array) + } + } + LLVMValue *errorLoad = llvmIRBuilder.CreateLoad(errorAlloca); + llvmIRBuilder.CreateCall(BuiltinError::exitIfErrorFunc, errorLoad); +} + +void IRGenerator::visit(const SharedPtr &varExpr) { + // 按右值处理 + varExpr->code = llvmIRBuilder.CreateLoad(varExpr->refVarDecl->code); +} + +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 (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 = 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); +} + +void IRGenerator::visit(const SharedPtr &uopExpr) { + ASTVisitor::visit(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; + 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 { + 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); + } else if (const auto &asExpr = dynPtrCast(subExpr)) { + setArrayElement(asExpr, newValue); + } + uopExpr->code = uopExpr->isPostfix ? value : newValue; + break; + } + case StaticScriptLexer::Not: { + uopExpr->code = llvmIRBuilder.CreateXor(subExpr->code, llvmIRBuilder.getTrue()); + break; + } + case StaticScriptLexer::BitNot: { + uopExpr->code = llvmIRBuilder.CreateXor(subExpr->code, llvmIRBuilder.getInt64(-1)); + break; + } + case StaticScriptLexer::Plus: { + uopExpr->code = subExpr->code; + break; + } + case StaticScriptLexer::Minus: { + if (isFloat) { + uopExpr->code = llvmIRBuilder.CreateFNeg(subExpr->code); + } else { + uopExpr->code = llvmIRBuilder.CreateNSWNeg(subExpr->code); + } + break; + } + } +} + +void IRGenerator::visit(const SharedPtr &bopExpr) { + unsigned int bopCode = bopExpr->opCode; + // 变量被赋值时只需要visit lhs + if (bopCode == StaticScriptLexer::Assign && dynPtrCast(bopExpr->lhs)) { + bopExpr->rhs->accept(shared_from_this()); + } else { + ASTVisitor::visit(bopExpr); + } + + 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() && bopCode != StaticScriptLexer::Assign) { + type = rightType; + lhsCode = integer2float(lhsCode); + } else if (leftType->isFloat() && rightType->isInteger()) { + rhsCode = integer2float(rhsCode); + } + + switch (bopCode) { + case StaticScriptLexer::Plus: + case StaticScriptLexer::PlusAssign: { + if (type->isString()) { + Vector argsV{lhsCode, rhsCode}; + targetCode = llvmIRBuilder.CreateCall(BuiltinString::concatFunc, argsV); + } 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: { + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateNSWSub(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFSub(lhsCode, rhsCode); + } + break; + } + case StaticScriptLexer::Multiply: + case StaticScriptLexer::MultiplyAssign: { + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateNSWMul(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFMul(lhsCode, rhsCode); + } + break; + } + case StaticScriptLexer::Divide: + case StaticScriptLexer::DivideAssign: { + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateSDiv(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFDiv(lhsCode, rhsCode); + } + break; + } + case StaticScriptLexer::Modulus: + case StaticScriptLexer::ModulusAssign: { + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateSRem(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFRem(lhsCode, rhsCode); + } + break; + } + case StaticScriptLexer::BitAnd: + case StaticScriptLexer::BitAndAssign: { + targetCode = llvmIRBuilder.CreateAnd(lhsCode, rhsCode); + break; + } + case StaticScriptLexer::BitXOr: + case StaticScriptLexer::BitXorAssign: { + targetCode = llvmIRBuilder.CreateXor(lhsCode, rhsCode); + break; + } + case StaticScriptLexer::BitOr: + case StaticScriptLexer::BitOrAssign: { + targetCode = llvmIRBuilder.CreateOr(lhsCode, rhsCode); + break; + } + case StaticScriptLexer::ShiftLeft: + case StaticScriptLexer::ShiftLeftAssign: { + targetCode = llvmIRBuilder.CreateShl(lhsCode, rhsCode); + break; + } + 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); + break; + } + case StaticScriptLexer::Or: + case StaticScriptLexer::OrAssign: { + targetCode = llvmIRBuilder.CreateOr(lhsCode, rhsCode); + break; + } + case StaticScriptLexer::LessThan: { + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpSLT(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOLE(lhsCode, rhsCode); + } + break; + } + case StaticScriptLexer::GreaterThan: { + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpSGT(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOGT(lhsCode, rhsCode); + } + break; + } + case StaticScriptLexer::LessThanEquals: { + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpSLE(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOLE(lhsCode, rhsCode); + } + break; + } + case StaticScriptLexer::GreaterThanEquals: { + if (type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpSGE(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpOGE(lhsCode, rhsCode); + } + break; + } + case StaticScriptLexer::Equals: { + if (type->isString()) { + Vector argsV{lhsCode, rhsCode}; + LLVMValue *relationship = llvmIRBuilder.CreateCall(BuiltinString::equalsFunc, argsV); + targetCode = llvmIRBuilder.CreateICmpEQ(relationship, llvmIRBuilder.getInt64(0)); + } 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->isString()) { + Vector argsV{lhsCode, rhsCode}; + LLVMValue *relationship = llvmIRBuilder.CreateCall(BuiltinString::equalsFunc, argsV); + targetCode = llvmIRBuilder.CreateICmpNE(relationship, llvmIRBuilder.getInt64(0)); + } else if (type->isBoolean() || type->isInteger()) { + targetCode = llvmIRBuilder.CreateICmpNE(lhsCode, rhsCode); + } else if (type->isFloat()) { + targetCode = llvmIRBuilder.CreateFCmpONE(lhsCode, rhsCode); + } + break; + } + default: + break; + } + 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)) { + setArrayElement(leftAsExpr, targetCode); + } else { + reportCodeGenError("No assignment code was generated"); + } + } + bopExpr->code = targetCode; +} + +void IRGenerator::visit(const SharedPtr &asExpr) { + ASTVisitor::visit(asExpr); + 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 = iterType->asArray()->getElementType(); + if (iterEleType->isBoolean()) { + getFunc = BuiltinArray::getBooleanFunc; + } else if (iterEleType->isInteger()) { + getFunc = BuiltinArray::getIntegerFunc; + } else if (iterEleType->isFloat()) { + getFunc = BuiltinArray::getFloatFunc; + } else if (iterEleType->isString()) { + 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/src/CodeGen/IRGenerator.cpp b/src/CodeGen/IRGenerator.cpp new file mode 100644 index 0000000..3b89659 --- /dev/null +++ b/src/CodeGen/IRGenerator.cpp @@ -0,0 +1,106 @@ +#include "CodeGen/IRGenerator.h" +#include "CodeGen/Pass.h" + +IRGenerator::IRGenerator() : llvmIRBuilder(llvmContext) {} + +void IRGenerator::resolve(const SharedPtr &module, const llvm::Twine &libDir) { + llvmModule = makeShared(module->filename, llvmContext); + Builtin::initialize(*llvmModule, llvmContext, libDir); + ASTVisitor::resolve(module); + llvm::verifyModule(*llvmModule); + runPasses(*llvmModule); +} + +void IRGenerator::visit(const SharedPtr &module) { + LLVMFunctionType *mainFnType = LLVMFunctionType::get(llvmIRBuilder.getInt64Ty(), false); + mainFn = LLVMFunction::Create(mainFnType, LLVMFunction::ExternalLinkage, "main", llvmModule.get()); + curFn = mainFn; + LLVMBasicBlock *mainEntryBlock = LLVMBasicBlock::Create(llvmContext, "entry", mainFn); + llvmIRBuilder.SetInsertPoint(mainEntryBlock); + ASTVisitor::visit(module); + setFuncInsertPoint(mainFn); + llvmIRBuilder.CreateRet(llvmIRBuilder.getInt64(0)); + llvm::verifyFunction(*mainFn); +} + +LLVMType *IRGenerator::getType(const SharedPtr &inputType) { + LLVMType *type = llvmIRBuilder.getVoidTy(); + if (inputType) { + if (inputType->isBoolean()) { + type = llvmIRBuilder.getInt1Ty(); + } else if (inputType->isInteger()) { + type = llvmIRBuilder.getInt64Ty(); + } else if (inputType->isFloat()) { + type = llvmIRBuilder.getDoubleTy(); + } else if (inputType->isString()) { + type = BuiltinString::type; + } else if (inputType->isArray()) { + type = BuiltinArray::type; + } + } + return type; +} + +void IRGenerator::emitBlock(LLVMBasicBlock *bb, bool isFinished) { + LLVMBasicBlock *curBB = llvmIRBuilder.GetInsertBlock(); + emitBranch(bb); + if (isFinished && bb->use_empty()) { + delete bb; + return; + } + if (curBB && curBB->getParent()) { + curFn->getBasicBlockList().insertAfter(curBB->getIterator(), bb); + } else { + curFn->getBasicBlockList().push_back(bb); + } + llvmIRBuilder.SetInsertPoint(bb); +} + +void IRGenerator::emitBranch(LLVMBasicBlock *targetBB) { + LLVMBasicBlock *curBB = llvmIRBuilder.GetInsertBlock(); + if (!curBB || curBB->getTerminator()) { + + } else { + llvmIRBuilder.CreateBr(targetBB); + } + llvmIRBuilder.ClearInsertionPoint(); +} + +// 设置数组元素的值, 务必确保baseExpr已经被visit过 +void IRGenerator::setArrayElement(const SharedPtr &asExpr, LLVMValue *valueCode) { + size_t indexExprsSize = asExpr->indexExprs.size(); + 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 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 = accessBySet ? BuiltinArray::setArrayFunc : BuiltinArray::getArrayFunc; + } + iterType = iterEleType; + Vector accessArgs{ + iterBaseCode, + asExpr->indexExprs[i]->code, + errorAlloca + }; + // 准备给多维数组最内层的元素赋值 + if (accessBySet) { + // getFunc和setFunc参数不同, setFunc的第三个参数是需要赋的值 + 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/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 88% rename from lib/CodeGen/StmtCodeGen.cpp rename to src/CodeGen/StmtCodeGen.cpp index 0cc240f..dad5c9e 100644 --- a/lib/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/lib/Driver/CMakeLists.txt b/src/Driver/CMakeLists.txt similarity index 75% rename from lib/Driver/CMakeLists.txt rename to src/Driver/CMakeLists.txt index 101985d..a6de52e 100644 --- a/lib/Driver/CMakeLists.txt +++ b/src/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/src/Driver/Driver.cpp b/src/Driver/Driver.cpp new file mode 100644 index 0000000..756b1b4 --- /dev/null +++ b/src/Driver/Driver.cpp @@ -0,0 +1,169 @@ +#include +#include +#include "StaticScriptLexer.h" +#include "StaticScriptParser.h" +#include "AST/ASTBuilder.h" +#include "Sema/ScopeScanner.h" +#include "Sema/ReferenceResolver.h" +#include "Sema/TypeChecker.h" +#include "Sema/SemanticValidator.h" +#include "CodeGen/IRGenerator.h" +#include "Optimization/Optimizer.h" +#include "Support/Alias.h" +#include "Support/LLVM.h" +#include "Support/Error.h" + +int main(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 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)); + 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)); + llvm::cl::opt showVersionAlias("v", llvm::cl::desc("Alias for --version"), llvm::cl::cat(genericOptsCat)); + + llvm::cl::ParseCommandLineOptions(argc, argv, "StaticScript Compiler\n"); + + if (showHelp || showHelpAlias) { + llvm::cl::PrintHelpMessage(false, true); + return 0; + } + + if (showVersion || showVersionAlias) { + llvm::outs() << "StaticScript v" << PROJECT_VERSION << '\n'; + return 0; + } + + if (inputFilename.empty()) { + llvm::cl::PrintHelpMessage(false, true); + return 0; + } + + llvm::StringRef inputBasename = llvm::sys::path::stem(inputFilename); + + String outputIRFilename; + String outputObjFilename; + String outputExeFilename; + + if (outputFilename.empty()) { + 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; + unsigned sizeLevel = 0; + + if (optLevelO3) { + optLevel = 3; + } else if (optLevelOz) { + optLevel = 2; + sizeLevel = 2; + } else if (optLevelOs) { + optLevel = 2; + sizeLevel = 1; + } else if (optLevelO1) { + optLevel = 1; + } else if (optLevelO0) { + optLevel = 0; + } else { + optLevel = 2; + } + + if (!llvm::sys::fs::exists(inputFilename)) { + reportDriverError("Not found input file"); + } + std::ifstream fin(inputFilename.data()); + reportOnDriverError(!fin, "Open input file failed"); + + antlr4::ANTLRInputStream inputStream(fin); + StaticScriptLexer lexer(&inputStream); + antlr4::CommonTokenStream tokenStream(&lexer); + tokenStream.fill(); + StaticScriptParser parser(&tokenStream); + antlr4::tree::ParseTree *tree = parser.module(); + ASTBuilder builder(inputFilename.data()); + SharedPtr module = builder.visit(tree); + // 语义分析: 1. 作用域扫描 + SharedPtr scanner = makeShared(); + scanner->resolve(module); + // 语义分析: 2. 引用消解 + SharedPtr resolver = makeShared(); + resolver->resolve(module); + // 语义分析: 3. 类型检查 + SharedPtr checker = makeShared(); + checker->resolve(module); + // 语义分析: 4. 正确性检查 + SharedPtr validator = makeShared(); + validator->resolve(module); + // 中间代码生成 + SharedPtr generator = makeShared(); + generator->resolve(module, libDir); + + LLVMModule &llvmModule = generator->getLLVMModule(); + + // 初始化LLVM相关 + initLLVMTarget(); + initLLVMPasses(); + initLLVMCodeGen(); + + // 设置target triple + llvmModule.setTargetTriple(getTargetTriple()); + + // 优化 + SharedPtr optimizer = makeShared(llvmModule, optLevel, sizeLevel); + optimizer->optimize(); + + llvm::TargetMachine *targetMachine = getTargetMachine(optLevel); + + // 设置data layout + llvmModule.setDataLayout(targetMachine->createDataLayout()); + + + 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; + } else { + llvm::legacy::PassManager pass; + auto fileType = llvm::CGFT_ObjectFile; + if (targetMachine->addPassesToEmitFile(pass, dest, nullptr, fileType)) { + reportDriverError("TargetMachine can't emit a object file"); + } + pass.run(llvmModule); + } + 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 diff --git a/lib/Entity/CMakeLists.txt b/src/Entity/CMakeLists.txt similarity index 54% rename from lib/Entity/CMakeLists.txt rename to src/Entity/CMakeLists.txt index ebe5a94..a50c118 100644 --- a/lib/Entity/CMakeLists.txt +++ b/src/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/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/src/Entity/Type.cpp b/src/Entity/Type.cpp new file mode 100644 index 0000000..ec777c5 --- /dev/null +++ b/src/Entity/Type.cpp @@ -0,0 +1,197 @@ +#include "Entity/Type.h" + +bool Type::equals(const SharedPtr &rhs) const { + // 如果rhs为nullptr, 直接返回false + if (!rhs) { + return false; + } + // 如果比较的两类型不属于同类, 直接返回false + if (isBasic() && rhs->isArray() || isArray() && rhs->isBasic()) { + return false; + } + 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); + return type->equals(arrayType); + } +} + +// 相同类型 +// 1. 相等的类型是相同类型 +// 2. 基础整型和基础浮点型是相同类型 +// 3. 数组与空数组是相同类型 +bool Type::sameAs(const SharedPtr &rhs) const { + // 右操作子为空直接返回false + if (!rhs) { + return false; + } + // 相等的类型是兼容的 + 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::compatibleWith(const SharedPtr &rhs) const { + // 右操作子为空直接返回false + if (!rhs) { + return false; + } + // 同类的类型是兼容的 + if (equals(rhs) || sameAs(rhs)) { + return true; + } + if (isNumberArray() && rhs->isNumberArray()) { + if (asArray()->getDepth() == rhs->asArray()->getDepth()) { + return true; + } + } + return false; +} + +BasicType::BasicType(BasicTypeKind kind) : kind(kind) {} + +bool BasicType::isBasic() const { + return true; +} + +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; +} + +SharedPtr BasicType::asArray() const { + return nullptr; +} + +BasicTypeKind BasicType::getKind() const { + return kind; +} + +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) { + type = makeShared(type); + } + return staticPtrCast(type); +} + +ArrayType::ArrayType(const SharedPtr &elementType) : elementType(elementType) { + if (elementType->isArray()) { + depth = elementType->asArray()->depth + 1; + } +} + +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; +} + +SharedPtr ArrayType::asArray() const { + return constPtrCast(staticPtrCast(shared_from_this())); +} + +const SharedPtr &ArrayType::getElementType() const { + return elementType; +} + +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 { + return elementType->equals(rhs->elementType); +} + +size_t ArrayType::getDepth() const { + return depth; +} + +bool ArrayType::isNumberArray() const { + if (getBasicElementType()->isNumber()) { + return true; + } + return false; +} diff --git a/src/Optimization/CMakeLists.txt b/src/Optimization/CMakeLists.txt new file mode 100644 index 0000000..c0aa9df --- /dev/null +++ b/src/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/src/Optimization/Optimizer.cpp b/src/Optimization/Optimizer.cpp new file mode 100644 index 0000000..fd53208 --- /dev/null +++ b/src/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 diff --git a/lib/Sema/ASTVisitor.cpp b/src/Sema/ASTVisitor.cpp similarity index 84% rename from lib/Sema/ASTVisitor.cpp rename to src/Sema/ASTVisitor.cpp index ad33fd0..5ae68d2 100644 --- a/lib/Sema/ASTVisitor.cpp +++ b/src/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()); } } @@ -31,8 +29,16 @@ 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) { + 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 +56,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/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 88% rename from lib/Sema/ReferenceResolver.cpp rename to src/Sema/ReferenceResolver.cpp index 3fa68c1..109bab0 100644 --- a/lib/Sema/ReferenceResolver.cpp +++ b/src/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/src/Sema/ScopeScanner.cpp similarity index 88% rename from lib/Sema/ScopeScanner.cpp rename to src/Sema/ScopeScanner.cpp index 1ad7e0f..15201ad 100644 --- a/lib/Sema/ScopeScanner.cpp +++ b/src/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); @@ -41,10 +41,19 @@ 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(); } +void ScopeScanner::visit(const SharedPtr &arrayLiteralExpr) { + arrayLiteralExpr->scope = getCurrentScope(); + ASTVisitor::visit(arrayLiteralExpr); +} + void ScopeScanner::visit(const SharedPtr &varExpr) { varExpr->scope = getCurrentScope(); } @@ -64,6 +73,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/src/Sema/SemanticValidator.cpp b/src/Sema/SemanticValidator.cpp new file mode 100644 index 0000000..2b2195c --- /dev/null +++ b/src/Sema/SemanticValidator.cpp @@ -0,0 +1,60 @@ +#include "Sema/SemanticValidator.h" + +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)) { + reportSemanticError("Constant initializers only support literal quantities"); + } + } +} + +void SemanticValidator::visit(const SharedPtr &funcDecl) { + ASTVisitor::visit(funcDecl); + if (funcDecl->returnType) { + reportOnSemanticError( + !funcDecl->refReturnStmt, + "A function with a return type must have a return statement" + ); + } else if (funcDecl->name == "main") { + reportSemanticError("Function name cannot be 'main'"); + } +} + +void SemanticValidator::visit(const SharedPtr &uopExpr) { + ASTVisitor::visit(uopExpr); + // 只能对变量进行自增自减 + if (uopExpr->opCode == StaticScriptLexer::PlusPlus || uopExpr->opCode == StaticScriptLexer::MinusMinus) { + if (!dynPtrCast(uopExpr->subExpr) && + !dynPtrCast(uopExpr->subExpr)) { + reportSemanticError("The increment and decrement operator only allows operations on variables or array elements"); + } + } +} + +void SemanticValidator::visit(const SharedPtr &bopExpr) { + ASTVisitor::visit(bopExpr); + if (bopExpr->opCode >= StaticScriptLexer::Assign && bopExpr->opCode <= StaticScriptLexer::OrAssign) { + if (const auto &varExpr = dynPtrCast(bopExpr->lhs)) { + if (varExpr->refVarDecl->isConstant()) { + reportSemanticError("Assignment to a constant is not allowed"); + } + } else if (const auto &asExpr = dynPtrCast(bopExpr->lhs)) { + } else { + reportSemanticError("Only variables or array elements are allowed to be assigned rather than expressions"); + } + } +} + +void SemanticValidator::visit(const SharedPtr &continueStmt) { + if (!continueStmt->refIterationStmt) { + reportSemanticError("A continue statement can only appear in a loop"); + } +} + +void SemanticValidator::visit(const SharedPtr &breakStmt) { + if (!breakStmt->refIterationStmt) { + reportSemanticError("A break statement can only appear in a loop"); + } +} diff --git a/src/Sema/TypeChecker.cpp b/src/Sema/TypeChecker.cpp new file mode 100644 index 0000000..574104a --- /dev/null +++ b/src/Sema/TypeChecker.cpp @@ -0,0 +1,276 @@ +#include "Sema/TypeChecker.h" +#include + +void TypeChecker::visit(const SharedPtr &varDecl) { + SharedPtr &varDeclType = varDecl->type; + 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->sameAs(initValType) || initArrayLiteral && varDeclType->compatibleWith(initValType))) { + reportTypeError("The variable declaration type does not match the initial value type: " + varDecl->name); + } + } else if (!initValType) { + reportTypeError("Unknown variable initialization type"); + } else { + 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" + ); + } + varDeclType = initValType; + } + // 根据显式类型或推导类型规化数组元素类型 + if (initArrayLiteral) { + assignTypeForArrayLiteral(initArrayLiteral, varDeclType->asArray()); + } + } else { + reportOnTypeError(!varDeclType, "Variable has no type specified: " + varDecl->name); + // 如果变量声明时未指定初始值, 则默认为其类型对应的零值 + 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->type->isArray()) { + reportTypeError("Base of array subscript expression is not array type"); + } + for (const SharedPtr &indexExpr : asExpr->indexExprs) { + 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"); + } + 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->type; + for (size_t i = 0; i < asExpr->indexExprs.size(); ++i) { + iterType = iterType->asArray()->getElementType(); + } + asExpr->type = iterType; +} + +void TypeChecker::visit(const SharedPtr &callExpr) { + ASTVisitor::visit(callExpr); + 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 (!params[i]->type->sameAs(args[i]->type)) { + reportTypeError( + "The type of the " + + std::to_string(i + 1) + + "-th parameter does not match when calling '" + + callExpr->calleeName + + "' function" + ); + } + } + callExpr->type = callExpr->refFuncDecl->returnType; +} + +void TypeChecker::visit(const SharedPtr &uopExpr) { + ASTVisitor::visit(uopExpr); + + unsigned int uop = uopExpr->opCode; + const SharedPtr &type = uopExpr->subExpr->type; + + 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; + } + 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->type = type; +} + +void TypeChecker::visit(const SharedPtr &bopExpr) { + ASTVisitor::visit(bopExpr); + + unsigned int bop = bopExpr->opCode; + const SharedPtr &leftType = bopExpr->lhs->type; + const SharedPtr &rightType = bopExpr->rhs->type; + + // 二元运算符只支持基础类型 + 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 (!leftType->isNumber() && + !leftType->isString()) { + reportTypeError("Add string and integer only operation types are supported"); + } + } 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 (!leftType->isBoolean()) { + reportTypeError("Binary operator '" + std::to_string(bop) + "' only supports boolean type"); + } + } + + // 设置二元运算表达式的类型 + if (bop >= StaticScriptLexer::LessThan && bop <= StaticScriptLexer::NotEquals) { + bopExpr->type = BasicType::BOOLEAN_TYPE; + } else if (leftType->isFloat() || rightType->isFloat()) { + bopExpr->type = BasicType::FLOAT_TYPE; + } else { + bopExpr->type = leftType; + } +} + +void TypeChecker::visit(const SharedPtr &varExpr) { + varExpr->type = varExpr->refVarDecl->type; +} + +void TypeChecker::visit(const SharedPtr &arrayLiteralExpr) { + ASTVisitor::visit(arrayLiteralExpr); + // 数组元素非空则寻找该数组的元素类型 + if (!arrayLiteralExpr->elements.empty()) { + // 寻找非unknown的element + 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->compatibleWith(element->type)) { + // 数字型数组视为一致的元素类型 + if (!(primaryElementType->isNumberArray() && element->type->isNumberArray())) { + reportTypeError("Inconsistent array element type"); + } + } else if (element->type->isFloat()) { + // 如果数组中有一个元素为浮点型, 那么所有的元素都设置为浮点型 + primaryElementType = BasicType::FLOAT_TYPE; + } + } + } else { + // 所有元素类型都是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->type = makeShared(primaryElementType); + } else { + // 空数组则默认为其元素类型为unknown + arrayLiteralExpr->type = makeShared(); + } +} + +void TypeChecker::visit(const SharedPtr &ifStmt) { + ASTVisitor::visit(ifStmt); + 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->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->type->isBoolean()) { + reportTypeError("The condition of for statement only supports boolean type temporarily"); + } +} + +void TypeChecker::visit(const SharedPtr &returnStmt) { + ASTVisitor::visit(returnStmt); + 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 (returnType) { + reportTypeError("The return statement is missing a return value"); + } +} + +void TypeChecker::assignTypeForArrayLiteral(const SharedPtr &arrayLiteral, const SharedPtr &type) { + arrayLiteral->type = type; + for (const SharedPtr &itemExpr : arrayLiteral->elements) { + const SharedPtr &elementType = type->getElementType(); + if (const auto &subArrayLiteral = dynPtrCast(itemExpr)) { + assignTypeForArrayLiteral(subArrayLiteral, elementType->asArray()); + } else { + itemExpr->type = elementType; + } + } +} 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 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 diff --git a/tests/array.ss b/tests/array.ss new file mode 100644 index 0000000..14e15ac --- /dev/null +++ b/tests/array.ss @@ -0,0 +1,73 @@ +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); + + +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); + +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/fib.ss b/tests/fib.ss new file mode 100644 index 0000000..2ee6819 --- /dev/null +++ b/tests/fib.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/float.ss b/tests/float.ss new file mode 100644 index 0000000..a78f130 --- /dev/null +++ b/tests/float.ss @@ -0,0 +1,60 @@ +let a1 = 1; +let a2 = 2.0; +let a3 = a1 + a2; + +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/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/tests/selection.ss b/tests/selection.ss new file mode 100644 index 0000000..471aaa5 --- /dev/null +++ b/tests/selection.ss @@ -0,0 +1,48 @@ +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(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 diff --git a/tests/variable.ss b/tests/variable.ss new file mode 100644 index 0000000..7e32e02 --- /dev/null +++ b/tests/variable.ss @@ -0,0 +1,57 @@ +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