├── .cargo └── config.toml ├── .config └── nextest.toml ├── .devcontainer ├── devcontainer.json └── first-run-notice.txt ├── .gitattributes ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── book.yml │ ├── brew_release.yml │ ├── docker_dev.yml │ ├── npmpkg.yml │ ├── release.yml │ ├── test.yml │ └── wasm.yml ├── .gitignore ├── .gitmodules ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── Dockerfile.dev ├── LICENSE ├── Makefile ├── README.md ├── book ├── .gitignore ├── book.toml ├── mdbook-admonish.css ├── mermaid-init.js ├── mermaid.min.js └── src │ ├── About.md │ ├── CONTRIBUTING-CN.md │ ├── SUMMARY.md │ ├── blogs │ ├── README.md │ ├── aboutpl.md │ ├── gc_for_beginner.md │ ├── lsp_and_salsa.md │ └── performance_optimization.md │ ├── compiler │ ├── README.md │ ├── ast.md │ ├── flow.md │ ├── generic.md │ └── parser.md │ ├── compiler_theory │ ├── README.md │ └── top-down_parsing.md │ ├── dev-prepare.md │ ├── lsp │ ├── 2022-12-08-13-20-37.png │ ├── 2022-12-08-13-22-04.png │ ├── README.md │ ├── design.md │ └── diagnostic.md │ ├── performance.md │ ├── references │ ├── README.md │ ├── array.md │ ├── basic.md │ ├── closure.md │ ├── closure_lsp.png │ ├── deconstruct.md │ ├── generic.md │ ├── interface.md │ ├── macro.md │ ├── method.md │ ├── module.md │ ├── operator │ │ ├── README.md │ │ └── tyops.md │ ├── tuple.md │ └── union.md │ ├── systemlib │ ├── 2023-01-24-23-23-55.png │ ├── 2023-01-24-23-25-06.png │ ├── README.md │ ├── bdw.png │ ├── eva.md │ ├── gc.md │ ├── immix.md │ ├── immix.png │ ├── planglib.md │ ├── stackmap.md │ └── vm.md │ └── tutorial │ ├── 2022-10-23-00-17-08.png │ ├── README.md │ ├── basicproject.md │ ├── example │ ├── Kagari.toml │ └── main.pi │ ├── installation.md │ └── vscsupport.md ├── build.rs ├── codecov.yml ├── deb ├── DEBIAN │ └── postinst └── apt.yaml ├── imgs ├── 2024-02-21-11-46-55.png ├── 2024-02-21-11-50-11.png └── 2024-02-21-11-50-25.png ├── internal_macro ├── Cargo.toml ├── README.md └── src │ ├── add_symbol_macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ ├── comment_macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ ├── fmt_macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ ├── lib.rs │ ├── node_macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ ├── range_macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs │ └── test_parser_macro │ ├── Cargo.toml │ └── src │ └── lib.rs ├── kagari ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── naive_coro.md ├── pl_linker ├── Cargo.toml ├── build.rs └── src │ ├── apple.rs │ ├── lib.rs │ └── linker.rs ├── planglib ├── core │ ├── Kagari.lock │ ├── Kagari.toml │ ├── __private.pi │ ├── builtin.pi │ ├── coro.pi │ ├── dtoa.pi │ ├── eq.pi │ ├── gc.pi │ ├── hash.pi │ ├── hash │ │ ├── hasher.pi │ │ └── pl_hash.pi │ ├── ord.pi │ ├── panic.pi │ ├── process.pi │ └── string.pi └── std │ ├── Kagari.lock │ ├── Kagari.toml │ ├── __private.pi │ ├── buf.pi │ ├── chan.pi │ ├── cols │ ├── arr.pi │ └── hashtable.pi │ ├── err.pi │ ├── future.pi │ ├── future │ ├── delay.pi │ ├── executor.pi │ └── primitives.pi │ ├── io.pi │ ├── iter.pi │ ├── json │ └── encode.pi │ ├── libc.pi │ ├── libuv.pi │ ├── math.pi │ ├── mutex.pi │ ├── rand.pi │ ├── slice.pi │ ├── stdbuiltin.pi │ ├── string.pi │ ├── task.pi │ ├── task │ ├── delay.pi │ ├── dns.pi │ ├── executor.pi │ ├── helper.pi │ ├── http.pi │ ├── reactor.pi │ ├── tcp.pi │ └── udp.pi │ ├── thread.pi │ ├── time.pi │ └── userrand.pi ├── plc.scoop ├── rust-toolchain.toml ├── setup-llvm.sh ├── src ├── ast │ ├── accumulators.rs │ ├── builder │ │ ├── llvmbuilder.rs │ │ ├── mod.rs │ │ └── no_op_builder.rs │ ├── compiler.rs │ ├── compiler │ │ ├── jit.rs │ │ ├── options.rs │ │ └── progress.rs │ ├── ctx.rs │ ├── ctx │ │ ├── builtins.rs │ │ ├── cast.rs │ │ ├── completion.rs │ │ ├── generic.rs │ │ ├── lsp.rs │ │ └── references.rs │ ├── diag.rs │ ├── expects │ │ ├── arr.pi.expect │ │ ├── buf.pi.expect │ │ ├── builtin.pi.expect │ │ ├── chan.pi.expect │ │ ├── dtoa.pi.expect │ │ ├── eq.pi.expect │ │ ├── err.pi.expect │ │ ├── executor.pi.expect │ │ ├── gc.pi.expect │ │ ├── hash.pi.expect │ │ ├── hasher.pi.expect │ │ ├── helper.pi.expect │ │ ├── hinttest.expect │ │ ├── io.pi.expect │ │ ├── iter.pi.expect │ │ ├── libc.pi.expect │ │ ├── libuv.pi.expect │ │ ├── m1.pi.expect │ │ ├── m2.pi.expect │ │ ├── match_diag.pi.expect │ │ ├── mutex.pi.expect │ │ ├── ord.pi.expect │ │ ├── panic.pi.expect │ │ ├── pl_hash.pi.expect │ │ ├── reactor.pi.expect │ │ ├── slice.pi.expect │ │ ├── stdbuiltin.pi.expect │ │ ├── string.pi.expect │ │ ├── task.pi.expect │ │ ├── test_diag.pi.expect │ │ ├── thread.pi.expect │ │ └── trait_diag.pi.expect │ ├── fmt.rs │ ├── jit_config.rs │ ├── macros.rs │ ├── mod.rs │ ├── node │ │ ├── README.md │ │ ├── cast.rs │ │ ├── comment.rs │ │ ├── control.rs │ │ ├── error.rs │ │ ├── function.rs │ │ ├── function │ │ │ └── generator.rs │ │ ├── global.rs │ │ ├── implement.rs │ │ ├── interface.rs │ │ ├── intermediate_node.rs │ │ ├── macro_nodes.rs │ │ ├── mod.rs │ │ ├── node_result.rs │ │ ├── operator.rs │ │ ├── pkg.rs │ │ ├── pointer.rs │ │ ├── primary.rs │ │ ├── program.rs │ │ ├── program │ │ │ └── salsa_structs.rs │ │ ├── ret.rs │ │ ├── statement.rs │ │ ├── string_literal.rs │ │ ├── tuple.rs │ │ ├── types.rs │ │ └── union.rs │ ├── plmod.rs │ ├── pltype.rs │ ├── pltype │ │ ├── method.rs │ │ └── tpdocs.rs │ ├── range.rs │ ├── test.rs │ ├── tokens.rs │ └── traits.rs ├── db.rs ├── flow │ ├── display.rs │ ├── mod.rs │ └── test.rs ├── inference │ └── mod.rs ├── jar.rs ├── lib.rs ├── lsp │ ├── config.rs │ ├── dispatcher.rs │ ├── fake_thread_pool.rs │ ├── helpers.rs │ ├── lspserver.rs │ ├── mem_docs.rs │ ├── mod.rs │ ├── semantic_tokens.rs │ ├── text.rs │ └── wasm.rs ├── main.rs ├── nomparser │ ├── README.md │ ├── array.rs │ ├── cast.rs │ ├── comment.rs │ ├── constval.rs │ ├── control.rs │ ├── control │ │ └── _match.rs │ ├── error.rs │ ├── expression.rs │ ├── function.rs │ ├── grammar.ebnf │ ├── helper.rs │ ├── identifier.rs │ ├── implement.rs │ ├── macro_parse.rs │ ├── macros.rs │ ├── mod.rs │ ├── pkg.rs │ ├── program.rs │ ├── statement.rs │ ├── string_literal.rs │ ├── structure.rs │ ├── types.rs │ └── union.rs ├── repl │ ├── completer.rs │ ├── editor.rs │ ├── mod.rs │ ├── repl_cmd.rs │ └── test.rs ├── utils │ ├── README.md │ ├── mod.rs │ ├── plc_new.rs │ └── read_config.rs └── version.rs ├── test ├── Kagari.lock ├── Kagari.toml ├── arr_bounds │ ├── Kagari.toml │ └── main.pi ├── fmt │ ├── Kagari.toml │ └── test_fmt.pi ├── gcbench │ ├── Kagari.toml │ ├── README.md │ └── main.pi ├── lsp │ ├── Kagari.toml │ ├── mod.pi │ ├── mod2.pi │ ├── test_completion.pi │ └── trait1.pi ├── lsp_diag │ ├── Kagari.toml │ ├── m1.pi │ ├── m2.pi │ ├── match_diag.pi │ ├── test_diag.pi │ └── trait_diag.pi ├── lsp_incremental │ ├── Kagari.toml │ ├── main.pi │ ├── module1.pi │ └── module2.pi ├── main.pi ├── mod1.pi ├── mod2.pi ├── project2 │ ├── Kagari.toml │ ├── main.pi │ └── main │ │ └── test.pi ├── sub │ └── mod.pi ├── tail │ ├── Kagari.toml │ └── main.pi ├── test │ ├── _hashtable.pi │ ├── _io.pi │ ├── _match.pi │ ├── arr.pi │ ├── closure.pi │ ├── compiletime_reflection.pi │ ├── deconstruct.pi │ ├── fixed_point.pi │ ├── flow.pi │ ├── fntype.pi │ ├── future_test.pi │ ├── generic.pi │ ├── global.pi │ ├── ifel.pi │ ├── inference.pi │ ├── iter.pi │ ├── list.pi │ ├── macros.pi │ ├── map.pi │ ├── method.pi │ ├── module.pi │ ├── multi_trait.pi │ ├── multi_trait_A.pi │ ├── multi_trait_st.pi │ ├── print.pi │ ├── rand.pi │ ├── simple.pi │ ├── sort_test.pi │ ├── st.pi │ ├── std_test.pi │ ├── str.pi │ ├── sub_module.pi │ ├── time.pi │ ├── traits.pi │ ├── tree.pi │ ├── tuple.pi │ └── union.pi ├── tmod.pi ├── tmod1.pi └── tmod2.pi └── vm ├── Cargo.toml ├── build.rs └── src ├── compiler_rt.rs ├── gc └── mod.rs ├── lib.rs ├── libcwrap └── mod.rs ├── logger └── mod.rs ├── mutex └── mod.rs └── time └── mod.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.wasm32-unknown-unknown] 2 | # Increase the stack size to 8MB, the default is 1MB 3 | # This is to prevent stack overflow panics in the wasm code while running. 4 | rustflags = [ 5 | "-C", "link-args=-z stack-size=8000000", 6 | ] -------------------------------------------------------------------------------- /.config/nextest.toml: -------------------------------------------------------------------------------- 1 | [profile.ci] 2 | # Don't fail fast in CI to run the full test suite. 3 | fail-fast = false 4 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile 3 | { 4 | "name": "Existing Dockerfile", 5 | "build": { 6 | // Sets the run context to one level up instead of the .devcontainer folder. 7 | "context": "..", 8 | // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. 9 | "dockerfile": "../Dockerfile.dev" 10 | }, 11 | "features": { 12 | }, 13 | 14 | // Features to add to the dev container. More info: https://containers.dev/features. 15 | // "features": {}, 16 | 17 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 18 | // "forwardPorts": [], 19 | 20 | // Uncomment the next line to run commands after the container is created. 21 | // "postCreateCommand": "cat /etc/os-release", 22 | 23 | // Configure tool-specific properties. 24 | // "customizations": {}, 25 | 26 | // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. 27 | "remoteUser": "root" 28 | } 29 | -------------------------------------------------------------------------------- /.devcontainer/first-run-notice.txt: -------------------------------------------------------------------------------- 1 | 👋 Welcome to Codespaces! You are on our Pivot-Lang's dev image. 2 | 3 | You need to run `make devlinux && source ~/.bashrc` on the project root before start developing. 4 | 5 | For any cargo build issues, please try `cargo clean`, and rerun the build command. 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto 3 | *.pi eol=lf 4 | *.sh eol=lf -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # ["Chronostasys"] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ["https://cdn.mo2.leezeeyee.com/pay.jpg"] 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "04:00" # UTC 8 | labels: 9 | - "dependencies" 10 | commit-message: 11 | prefix: "bump" 12 | open-pull-requests-limit: 10 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | labels: 18 | - "dependencies" 19 | commit-message: 20 | prefix: "chore(ci)" 21 | -------------------------------------------------------------------------------- /.github/workflows/docker_dev.yml: -------------------------------------------------------------------------------- 1 | name: docker_dev 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - release/* 7 | paths: 8 | - '**.rs' 9 | - '**/Cargo.*' 10 | - '.github/workflows/docker_dev.yml' 11 | - '**.pi' 12 | - '**/Kagari.*' 13 | - 'codecov.yml' 14 | 15 | 16 | jobs: 17 | artifacts: 18 | name: docker_dev 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | submodules: true 24 | - 25 | name: Set up QEMU 26 | uses: docker/setup-qemu-action@v3 27 | - 28 | name: Set up Docker Buildx 29 | uses: docker/setup-buildx-action@v3 30 | - 31 | name: Login to ali registry 32 | uses: docker/login-action@v3 33 | with: 34 | registry: registry.cn-hangzhou.aliyuncs.com 35 | username: ${{ secrets.DOCKERHUB_USERNAME }} 36 | password: ${{ secrets.DOCKERHUB_TOKEN }} 37 | - 38 | name: Build and push 39 | uses: docker/build-push-action@v5 40 | with: 41 | context: . 42 | push: true 43 | file: ./Dockerfile.dev 44 | tags: registry.cn-hangzhou.aliyuncs.com/pivot_lang/pivot_lang_dev:latest 45 | -------------------------------------------------------------------------------- /.github/workflows/npmpkg.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package to npmjs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - release/* 7 | - wasm/* 8 | paths: 9 | - '**.rs' 10 | - '**/Cargo.*' 11 | - '.github/workflows/npmpkg.yml' 12 | - 'codecov.yml' 13 | workflow_dispatch: 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | submodules: true 21 | - uses: dsherret/rust-toolchain-file@v1 22 | name: Install Rust (stable) 23 | - name: install rust 24 | run: cargo install wasm-pack --locked 25 | - name: build wasm 26 | run: wasm-pack build --target bundler --no-default-features --scope pivot-lang --release --locked 27 | # Setup .npmrc file to publish to npm 28 | - uses: actions/setup-node@v3 29 | with: 30 | node-version: '16.x' 31 | registry-url: 'https://registry.npmjs.org' 32 | - run: | 33 | cd pkg 34 | npm version prerelease -preid $(git rev-parse --short "$GITHUB_SHA") 35 | npm publish --access public || true 36 | env: 37 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/wasm.yml: -------------------------------------------------------------------------------- 1 | name: wasm-check 2 | on: 3 | pull_request: 4 | paths: 5 | - '**.rs' 6 | - '**/Cargo.*' 7 | - '.github/workflows/wasm.yml' 8 | - '**.pi' 9 | - '**/Kagari.*' 10 | - 'codecov.yml' 11 | push: 12 | branches: 13 | - "master" 14 | - "release/*" 15 | - "staging" 16 | - "trying" 17 | paths: 18 | - '**.rs' 19 | - '**/Cargo.*' 20 | - '.github/workflows/wasm.yml' 21 | - '**.pi' 22 | - '**/Kagari.*' 23 | - 'codecov.yml' 24 | 25 | 26 | concurrency: 27 | group: ${{ github.workflow }}-${{ github.ref }} 28 | cancel-in-progress: true 29 | 30 | jobs: 31 | wasm-test: 32 | name: Wasm Build Check 33 | runs-on: ubuntu-latest 34 | env: 35 | CARGO_TERM_COLOR: always 36 | RUST_BACKTRACE: 1 37 | 38 | steps: 39 | - uses: jetli/wasm-pack-action@v0.4.0 40 | - uses: actions/checkout@v4 41 | with: 42 | submodules: true 43 | - name: test wasm 44 | run: wasm-pack build --target bundler --no-default-features --scope pivot-lang -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *.plb 3 | *.piv 4 | .VSCodeCounter 5 | # ignore all ll file except alloc.ll 6 | *.ll 7 | !alloc*.ll 8 | *.out 9 | *.ilk 10 | *.exe 11 | *.pdb 12 | 13 | test.cpp 14 | *.bc 15 | .idea 16 | *.o 17 | *.plist 18 | out.* 19 | *.html 20 | *.asm 21 | *out 22 | *.dSYM 23 | *.dot 24 | dots 25 | *testfile 26 | .DS_Store 27 | !7zr.exe 28 | *.info 29 | 30 | CMakeLists.txt.user 31 | CMakeCache.txt 32 | CMakeFiles 33 | CMakeScripts 34 | Testing 35 | Makefile 36 | cmake_install.cmake 37 | install_manifest.txt 38 | compile_commands.json 39 | CTestTestfile.cmake 40 | _deps 41 | 42 | *.a 43 | *.dylib 44 | *.s 45 | test.c 46 | test.bash 47 | .cmake 48 | .vscode/c_cpp_properties.json 49 | build/build.ninja 50 | *.proj* 51 | *.vcxproj* 52 | *.sln 53 | *.dir 54 | Debug 55 | Release 56 | kagari/test_dir 57 | planglib/thirdparty 58 | immix/llvm/cmake-build-debug/build.ninja 59 | testout* 60 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "immix"] 2 | path = immix 3 | url = https://github.com/Chronostasys/immix.git 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "rust-lang.rust-analyzer", 8 | "pivot-langauthors.pivot-lang-support", 9 | "vadimcn.vscode-lldb", 10 | "ms-vscode.cpptools" 11 | ], 12 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 13 | "unwantedRecommendations": [ 14 | ], 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.sourceDirectory": "${workspaceFolder}/immix/llvm", 3 | "C_Cpp.autoAddFileAssociations": false, 4 | "C_Cpp.errorSquiggles": "enabled", 5 | "files.watcherExclude": { 6 | "**/.git/objects/**": true, 7 | "**/.git/subtree-cache/**": true, 8 | "**/node_modules/*/**": true, 9 | "**/.hg/store/**": true, 10 | "**/target/**": true, 11 | "**/book/book/**": true, 12 | }, 13 | "lldb.displayFormat": "auto", 14 | "lldb.showDisassembly": "auto", 15 | "lldb.dereferencePointers": true, 16 | "lldb.consoleMode": "commands", 17 | "makefile.configureOnOpen": true, 18 | "vs-browser.localProxyServer.cookieDomainRewrite": true, 19 | "vs-browser.localProxyServer.enabled": true, 20 | "vs-browser.localProxyServer.forceLocation": true, 21 | "vs-browser.proxyMode": true, 22 | // "rust-analyzer.check.targets": [ 23 | // "wasm32-unknown-unknown", 24 | // ], 25 | // "rust-analyzer.cargo.target": "wasm32-unknown-unknown", 26 | // "rust-analyzer.cargo.noDefaultFeatures": true, 27 | // "rust-analyzer.check.noDefaultFeatures": true 28 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "compile main.pi", 6 | "command": "plc", 7 | "args": ["${workspaceFolder}/test/main.pi"], 8 | "type": "shell", 9 | "dependsOn":["compile vm"] 10 | }, 11 | { 12 | "label": "compile vm", 13 | "command": "cargo", 14 | "args": ["build", "--manifest-path", "${workspaceFolder}/vm/Cargo.toml", "--release"], 15 | "type": "shell", 16 | }, 17 | { 18 | "label": "install plc", 19 | "command": "cargo", 20 | "args": ["install", "--path", "${workspaceFolder}"], 21 | "type": "shell", 22 | }, 23 | { 24 | "label": "install debug plc", 25 | "command": "cargo", 26 | "args": ["install", "--path", "${workspaceFolder}", "--debug"], 27 | "type": "shell", 28 | }, 29 | { 30 | "label": "windows link", 31 | "command": "clang", 32 | "args": ["-o", "${workspaceFolder}/out.plb.out", "-g", "-O0", "${workspaceFolder}/out.plb.o","${workspaceFolder}/vm/target/release/vm.lib", 33 | "-lws2_32", "-lbcrypt","-luserenv","-ladvapi32"], 34 | "type": "shell", 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to pivot-lang 2 | 3 | Thanks a lot for contributing to this project! 4 | 5 | The following is a set of guidelines for contributing to [pivot-lang][1]. 6 | 7 | If you are new to pivot-lang, you can join our [qq group](https://jq.qq.com/?_wv=1027&k=I5vdShVl) to discuss with us. 8 | 9 | **Since the project is young**: consider those best practices prone to change. Please suggest improvements! 10 | 11 | [1]: https://github.com/Pivot-Studio/pivot-lang 12 | 13 | ## Basics 14 | 15 | ### License 16 | 17 | The project uses the [MIT][l1] license. By contributing to this project you agree to license 18 | your changes under this license. 19 | 20 | [l1]: https://opensource.org/licenses/MIT 21 | 22 | 23 | ## What to do 24 | 25 | ### Issues 26 | 27 | There is plenty of [features missing][i1] and possibly bugs might be already there. Feel free to add new [issues][i2] 28 | and to wrangle over those already [open][i3] and help fixing them. 29 | 30 | [i1]: https://github.com/Pivot-Studio/pivot-lang/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement 31 | [i2]: https://github.com/Pivot-Studio/pivot-lang/issues 32 | [i3]: https://github.com/Pivot-Studio/pivot-lang/issues?q=is%3Aopen+is%3Aissue 33 | 34 | ### Code 35 | 36 | Implementing new features is always welcome! 37 | 38 | ### Tests 39 | 40 | It is strongly suggested to provide test along changes so the coverage stays around the **85%**, helping to 41 | get to full coverage is pretty welcome. 42 | 43 | please make sure you passed all tests(by running `cargo test --all`) before submitting a PR. 44 | 45 | ### Benchmark 46 | 47 | Help in making sure the code does not have performance regression, by improving the benchmark suite or just by 48 | running it weekly, is welcome as well. 49 | 50 | ### Documentation 51 | 52 | See the [documentation][d1] site. 53 | 54 | [d1]: https://pivotlang.tech/ 55 | 56 | ## Style 57 | 58 | ### Issue style 59 | 60 | Try to write at least 3 short paragraphs describing what were you trying to achieve, what is not working and 61 | the step by step actions that lead to the unwanted outcome. 62 | 63 | If possible provide: 64 | 65 | - a code snippet or a link to a [gist][is1] showcasing the problem, if is a library usage issue. 66 | - a backtrace, if it is a crash. 67 | - a sample source project, if it is a compiling issue. 68 | 69 | [is1]: https://gist.github.com 70 | 71 | ### Coding style 72 | 73 | The normal rust coding style is checked by [rustfmt][cs1]. 74 | Readable code is the first step on having good and safe libraries. 75 | 76 | To avoid slight differences appearing in nightly versions, please 77 | use the following command to run rustfmt: `cargo +stable fmt` 78 | 79 | [cs1]: https://github.com/rust-lang-nursery/rustfmt -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 AS runner 2 | RUN apt update 3 | RUN apt install -y git libxml2 build-essential libunwind-dev 4 | RUN useradd -ms /bin/bash pivot && echo "pivot:pivot" | chpasswd 5 | RUN mkdir -p /home/pivot 6 | WORKDIR /home/pivot 7 | COPY ./target/release/plc /usr/bin/plc 8 | COPY ./planglib /home/pivot/pl/planglib 9 | COPY ./target/release/libvm.a /home/pivot/pl/libvm.a 10 | COPY ./target/release/libvm.so /home/pivot/pl/libvm.so 11 | COPY ./test /home/pivot/pltest 12 | RUN chown -R pivot:pivot /home/pivot 13 | USER pivot 14 | ENV KAGARI_LIB_ROOT=/home/pivot/pl/planglib 15 | ENV PL_ROOT=/home/pivot/pl 16 | -------------------------------------------------------------------------------- /Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/base:ubuntu AS runner 2 | COPY ./.devcontainer/first-run-notice.txt /tmp/scripts/ 3 | RUN mv -f /tmp/scripts/first-run-notice.txt /usr/local/etc/vscode-dev-containers/ 4 | COPY ./setup-llvm.sh . 5 | RUN apt-get update && apt-get install -y gnupg2 software-properties-common cmake 6 | RUN apt-get update && apt-get install -y lsb-release wget && apt-get clean all 7 | RUN ./setup-llvm.sh 8 | RUN apt update 9 | RUN apt install -y git libxml2 build-essential libunwind-dev 10 | RUN apt-get update 11 | 12 | # Get Ubuntu packages 13 | RUN apt-get install -y \ 14 | build-essential \ 15 | curl 16 | 17 | # Update new packages 18 | RUN apt-get update 19 | 20 | 21 | # Get Rust 22 | RUN curl https://sh.rustup.rs -sSf | bash -s -- -y 23 | ENV PATH="/root/.cargo/bin:${PATH}" 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 The pivot-lang Authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: devlinux devmac vm install fmt test clean bench 2 | devlinux: 3 | @echo "export KAGARI_LIB_ROOT=$$(pwd)/planglib" >> ~/.bashrc 4 | @echo "export PL_ROOT=$$(pwd)/target/release/" >> ~/.bashrc 5 | @source ~/.bashrc 6 | @echo "环境变量已加入~/.bashrc,请重启终端和vsc应用更改" 7 | 8 | devmac: 9 | @echo "export KAGARI_LIB_ROOT=$$(pwd)/planglib" >> ~/.bash_profile 10 | @echo "export PL_ROOT=$$(pwd)/target/release/" >> ~/.bash_profile 11 | @source ~/.bash_profile 12 | @echo "环境变量已加入~/.bashrc,请重启终端和vsc应用更改" 13 | 14 | vm: 15 | @cd vm && cargo build --release --locked 16 | 17 | vmdebug: 18 | @mkdir -p target/release 19 | @cd vm && cargo build 20 | @cp target/debug/libvm.a target/release/libvm.a 21 | @touch target/debug/libvm.so && cp target/debug/libvm.so target/release/libvm.so 22 | @touch target/debug/libvm.dylib && cp target/debug/libvm.dylib target/release/libvm.dylib 23 | @touch target/debug/libuv.so && cp target/debug/libuv.so target/release/libuv.so 24 | @touch target/debug/libuv.dylib && cp target/debug/libuv.dylib target/release/libuv.dylib 25 | 26 | install: 27 | @cargo install --path=. --locked 28 | 29 | fmt: 30 | @cargo +stable fmt 31 | 32 | test: vmdebug 33 | @cargo nextest run --workspace --all-features 34 | 35 | clean: 36 | @rm -rf out* 37 | @cd target && rm -f *.ll && rm -f *.bc && rm -rf *.dSYM && rm -f testout* && rm -f out* && rm -f *.o && rm -rf test* 38 | 39 | bench: 40 | @cd immix && cargo bench 41 | @cd vm && cargo bench 42 | 43 | bench-simple-gc: 44 | @cd vm && cargo bench --features=simple_gc --no-default-features 45 | 46 | cmake-clean: 47 | @find . -name CMakeCache.txt -type f -delete 48 | 49 | lsp-wasm: 50 | @wasm-pack build --target bundler --no-default-features --scope pivot-lang 51 | 52 | renew-expect: 53 | @UPDATE_EXPECT=1 cargo test --all 54 | 55 | mdbook-install: 56 | @cargo install mdbook 57 | @cargo install mdbook-mermaid 58 | @cargo install mdbook-admonish 59 | @cargo install mdbook-linkcheck 60 | @cargo install mdbook-toc 61 | 62 | init-submodules: 63 | @git submodule update --init --recursive 64 | 65 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["The pivot-lang Authors"] 3 | language = "en" 4 | multilingual = true 5 | src = "src" 6 | title = "Pivot Lang" 7 | 8 | [build] 9 | create-missing = false 10 | 11 | [output.html] 12 | git-repository-url = "https://github.com/Pivot-Studio/pivot-lang" 13 | git-repository-icon = "fa-github" 14 | edit-url-template = "https://github.com/Pivot-Studio/pivot-lang/edit/master/book/{path}" 15 | additional-css = ["././mdbook-admonish.css"] 16 | additional-js = ["mermaid.min.js", "mermaid-init.js"] 17 | site-url = "/docs/" 18 | 19 | [preprocessor] 20 | 21 | [preprocessor.admonish] 22 | command = "mdbook-admonish" 23 | assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install` 24 | 25 | [preprocessor.mermaid] 26 | command = "mdbook-mermaid" 27 | 28 | [preprocessor.toc] 29 | command = "mdbook-toc" 30 | renderer = ["html"] -------------------------------------------------------------------------------- /book/mermaid-init.js: -------------------------------------------------------------------------------- 1 | mermaid.initialize({startOnLoad:true}); 2 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [About](./About.md) 4 | - [Performance](./performance.md) 5 | 6 | ## Tutorial 7 | 8 | - [Quick Start](./tutorial/README.md) 9 | - [Installation](./tutorial/installation.md) 10 | - [Basic Project](./tutorial/basicproject.md) 11 | - [VSC Support](./tutorial/vscsupport.md) 12 | - [References](./references/README.md) 13 | - [Basic](./references/basic.md) 14 | - [Operator](./references/operator/README.md) 15 | - [Type Operator](./references/operator/tyops.md) 16 | - [Array](./references/array.md) 17 | - [Closure](./references/closure.md) 18 | - [Module](./references/module.md) 19 | - [Method](./references/method.md) 20 | - [Trait](./references/interface.md) 21 | - [Generic](./references/generic.md) 22 | - [Tuple](./references/tuple.md) 23 | - [Deconstruct](./references/deconstruct.md) 24 | - [Union](./references/union.md) 25 | - [Macro](./references/macro.md) 26 | 27 | ## How the project works internally 28 | 29 | - [Dev Prepare](./dev-prepare.md) 30 | - [Compiler](./compiler/README.md) 31 | - [Parser](./compiler/parser.md) 32 | - [AST](./compiler/ast.md) 33 | - [Flow Chart](compiler/flow.md) 34 | - [Language Server](./lsp/README.md) 35 | - [Design](./lsp/design.md) 36 | - [Diagnostic](./lsp/diagnostic.md) 37 | - [System library](./systemlib/README.md) 38 | - [vm](./systemlib/vm.md) 39 | - [gc](./systemlib/gc.md) 40 | - [Immix Gc](./systemlib/immix.md) 41 | - [Stack Map](./systemlib/stackmap.md) 42 | - [Evacuation](./systemlib/eva.md) 43 | - [planglib](./systemlib/planglib.md) 44 | - [compiler theory](./compiler_theory/README.md) 45 | - [top-down parsing](./compiler_theory/top-down_parsing.md) 46 | 47 | ## CONTRIBUTING 48 | 49 | - [Contributing](./CONTRIBUTING-CN.md) 50 | 51 | ## Blogs 52 | 53 | - [Blogs](./blogs/README.md) 54 | - [About pivot lang](blogs/aboutpl.md) 55 | - [lsp and salsa](blogs/lsp_and_salsa.md) 56 | - [GC basic tutorial](blogs/gc_for_beginner.md) 57 | - [performance optimization](blogs/performance_optimization.md) 58 | -------------------------------------------------------------------------------- /book/src/blogs/README.md: -------------------------------------------------------------------------------- 1 | # Blogs 2 | 3 | Some development blogs. 4 | -------------------------------------------------------------------------------- /book/src/blogs/aboutpl.md: -------------------------------------------------------------------------------- 1 | # 聊一聊pivot-lang 2 | 3 | 这是这个项目的第一篇博客,这篇博客里我准备讲一讲这个项目的意义还有它的发展方向,还有一些写的过程中的有趣的话题。 4 | 5 | 这个项目希望创造出一种好用的类rust的新编程语言,他要具有大部分先进的特性,并且避免掉rust太难学习的缺点。 6 | 7 | 目前已经完成的部分和rust非常的像,但是已经有一些功能上出现区别,比如模块化。模块化表面上和rust有点像,但是其实完全不一样,要说类似其实反而和go更类似一些,不过也有很大区别。 8 | 我们预计之后的一些高级功能,比如代数类型、模式匹配和一些别的特殊语法糖上,我们会和rust有巨大分歧,这方面还敬请期待。 9 | 10 | ## History 11 | 12 | 项目大概是2022年9月中旬开始的,一开始的时候我们几乎完全手写了第一版lexer和parser。当时那一部分代码大部分是[@RINNE-TAN]写的,因为我那个时候还不会rust,只能对[@RINNE-TAN]的代码进行拙劣的模仿。我们初期争论比较大的点是是否该用lr分析法的工具,而不是ll分析手写递归下降。最后我们还是觉得ll可控性更好一些,选择了手写。但是就写了个开头,我们就发现这玩意写到后边可维护性会有很大问题,简直是依托__。 13 | 14 | 所以我们开始重新考虑一些自动或者半自动的工具,比如antlr、lalrpop之类的。这些工具有个很严重的问题,就是他们往往需要在特殊格式里编写一部分源代码,而写这部分代码的时候是没有代码提示的。这对于其他的语言应该好一些,但是这对于rust来说是相当致命的,尤其是在我们没有人是rust大佬的情况下。 15 | 16 | 这个时候伟大的[@RINNE-TAN]找到了[nom](https://github.com/Geal/nom),虽然这个东西上手有点不习惯,但是熟练之后被证明究极好用,于是我们果断重写了之前的lexer和parser的代码。我代码量大概就是这个时候开始反超[@RINNE-TAN]的,因为重构几乎都是我搞的。 17 | 18 | 之后我们十一之前加班加点,希望在10.1期间作出一个有一些基础功能的版本,这期间有很多人都参与了代码编写,不过总体来说这一阶段的工作是比较简单的。唯一一个相对难一点的功能是@CjiW做的函数相关的功能,函数至今也是我们代码生成中最复杂的模块之一。 19 | 20 | 十一之后我们加入了大量lsp相关的功能,还有debug支持。这两个都是之前我做的项目中比较少或者没有涉及过的,意外的是这两个功能的实现都比较顺利。 21 | 22 | 顺带一提,项目里很多奇怪的东西还有一些文件的命名多少沾点二次元,这都是托了[@RINNE-TAN]大爷的福。 23 | 24 | 再后来,我们的高级功能开始提上日程,[@RINNE-TAN]几乎实现了泛型功能的所有代码,这部分逻辑十分复杂,因为涉及到自动泛型推断和代码膨胀等技术,是目前编译器中最复杂的功能之一。谢谢你,[@RINNE-TAN]! 25 | 26 | 顺便提一句,[@RINNE-TAN]现在是单身,如果有人对找程序员男朋友感兴趣,请抓紧 27 | 28 | [@RINNE-TAN]: https://github.com/RINNE-TAN 29 | 30 | ## Future 31 | 32 | 接下来一段时间的首要任务是完善泛型功能,让impl快能加泛型,接口也支持泛型。然后,就是代数类型和模式匹配,这些任务预计寒假能搞完。之后的高级功能就是闭包和协程支持,然后需要完善系统库,gc支持多线程。如果非常顺利的话,寒假结束前有机会完成到协程。 33 | 项目现在非常缺人,如果你对开发pl感兴趣,请联系我们。 34 | -------------------------------------------------------------------------------- /book/src/blogs/gc_for_beginner.md: -------------------------------------------------------------------------------- 1 | # ~~牛马都能看懂的~~GC原理介绍 2 | 3 | 本文不要求你有任何高深的底层知识,本文的书写宗旨是让一个牛马都能看懂GC的基本原理 4 | 5 | ## 故事 6 | 7 | 首先,想象你是一个大学生,和三个舍友一起住在一间宿舍里。你们四个人都有自己的床,自己的书桌,自己的衣柜,自己的书架,自己的电脑,自己的手机,自己的鞋柜,自己的鞋子,自己的衣服。。。 8 | 9 | 一切都很美好,直到你们发现你们的宿舍里的垃圾日益变多,整个宿舍变得更加脏乱,逐渐不适宜居住了。于是你们决定开始定期清理垃圾。 10 | 11 | 怎么定义一个物品是不是垃圾呢?你们想了一种方法:以每个人为原点出发,寻找所有你可能直接 12 | 使用的物品,然后再从这些物品出发,寻找所有这些物品可能会用到的其他物品,依此类推,直到所有直接或间接可能被使用的物品 13 | 都被找到,剩下的物品就是不会再被用到的物品,也就是垃圾。 14 | 15 | 位了清理垃圾,一开始你们选择值日制度,每天轮流值日,值日的人负责清理宿舍里的垃圾,清理完毕后,将垃圾扔到宿舍外的垃圾桶里。这样,宿舍里的垃圾就被清理干净了,宿舍也变得干净了。 16 | 17 | 但是后来你们发现这样做有一个问题,就是每天都要有人值日,而且值日的人要花费很多时间清理垃圾,而且有些人垃圾多 18 | 有些人垃圾少,垃圾少的人和垃圾多的人都要花费同样的时间清理垃圾,这样就不公平了。于是你们决定改进一下,改成 19 | 每个人都有自己的垃圾桶,所有人都可以随时清理自己的垃圾,这样就不用每天都有人值日了,而且每个人清理垃圾的时间 20 | 也可以岔开,对于大家都方便。 21 | 22 | 这套机制稳定的运行了一段时间,直到你们发现宿舍里有一个人特别的懒,他从来不清理自己的垃圾,他的 23 | 垃圾逐渐堆积,甚至有时候会散布到宿舍的其他地方,这样宿舍里的垃圾又开始变多了,宿舍又变得脏乱了。 24 | 25 | 这个时候宿舍里一个有洁癖的人就受不了了,他帮助懒人清理了他的垃圾,然而懒人发现之后 26 | 非但没有感谢,反而指责他丢掉了他还要继续使用的东西。两人爆发了严重的争吵。 27 | 28 | 最后为了避免这个问题,宿舍经过讨论找到了一个完美的解决方案:每个人在不方便清理自己垃圾的时候,都需要 29 | 请一个保姆来代替自己清理垃圾,在自己方便的时候再把保姆送走。 30 | 31 | 最后,你们宿舍终于变成了一个高效、少吵架的宿舍。 32 | 33 | ## 问题 34 | 35 | 这个故事里的意象都象征着什么?你看懂了吗? 36 | -------------------------------------------------------------------------------- /book/src/blogs/lsp_and_salsa.md: -------------------------------------------------------------------------------- 1 | # 代码分析和差量计算 2 | 3 | 在[lsp的文档](https://pivotlang.tech/lsp/design.html)中我简单介绍了差量计算在lsp模块中起到的优化作用,这里我将会详细介绍一下这个过程,希望能帮没接触过差量计算的小伙伴了解为何差量计算对于lsp的计算任务来说如此重要,以及什么情况下适合使用差量计算。 4 | 5 | 6 | ## 纯函数 7 | 差量计算中的基础对象一般是纯函数,纯函数的定义是:对于给定的输入,总是会有相同的输出,而且**不会有任何副作用**。 8 | 请注意其中**不会有任何副作用**这一点,如果要建立一个正确工作差量计算模型,必须要保证这一点。 9 | 10 | ### 带有副作用的函数为什么会影响差量计算正确性? 11 | 12 | 试想以下场景:我有两个参与差量计算的基础函数`A`和`B`,`A`不是纯函数,它会修改全局变量`a`,每次运行他会把`a`加一。 13 | 那么在某次计算过程中,`A`的输入与上次相同,`B`有变化,这导致`B`被重新执行了,而`A`直接使用上次缓存的计算结果,跳过了这次计算。那么这会导致本次计算全局变量`a`没有被`A`修改。这导致一个很严重的问题:**即使用差量计算之后相比使用之前,相同情况下计算的影响不一样**。 14 | 假设上方例子中不使用差量计算,那么`A`会被重新执行一次,这样`a`相比差量计算情况就会被多加一,**正确的差量计算模型不应该对系统状态有影响**,所以如果要使用差量计算,应该保证所有参与计算的函数都是没有副作用的纯函数。 15 | 16 | ### 一些容易被忽略的副作用情况 17 | 尽管纯函数的定义比较简单,上方的例子也比较直观,但是实际生产中其实很多函数都是有副作用的,而且很多初学者可能并不能完全分析出这些函数 18 | 的副作用。这里特别说一个容易被忽略的副作用情况: 19 | 20 | * 修改被差量计算框架缓存的函数输出结果 21 | 22 | 在你分析一个函数有没有副作用的时候应该记住:被缓存的纯函数输出也属于全局状态,和全局变量没有本质区别。所以修改这些被缓存值也是有副作用的。修改这些值很可能在无意中发生:比如函数`A`的输出中有个类型指针,它输出后作为输入传给了函数`B`,函数`B`修改了这个类型指针指向的值,这样虽然看起来没有进行和全局变量相关的操作,但是实际上这个类型指针指向的值就是被缓存的函数`A`的输出,这种行为修改了缓存状态,是有副作用的。 23 | 24 | ## 差量计算系统设计原则 25 | 有了上方纯函数相关的知识,我们可以总结出一个正确的差量计算系统需要遵循的两条原则: 26 | 27 | - 差量计算系统中,一个函数的输出应该是**只读**的,应该避免在别的函数中修改对应值 28 | - 差量计算函数不能修改**任何**全局状态 29 | 30 | ## 实战举例--lsp引用查找功能设计 31 | lsp引用查找是一个绝大部分语言插件都有的功能,它被使用的也很多,大部分程序员同学应该每天都会用到很多次。这个功能底层实现是比较简单的,如果不考虑差量计算的话: 32 | 33 | > 在符号表中所有能被引用的符号中多存一个`refs`链表,每次该符号被使用,往链表中加入被使用的位置。最后在接收到`find reference`请求的时候,查找到对应的符号,返回`refs`链表即可。 34 | 35 | 但是这么设计在差量计算的时候会遇到问题:假设我们差量计算的最小单位是`compile_file`函数,它的意义是编译一个文件,那么这个函数输出的编译结果中就应该包含该文件中定义的所有的符号,自然也包括这些符号的`refs`信息。然而,很多符号是可以被跨文件使用的,比如全局变量,这些符号的`refs`信息是跨文件的,所以这些符号的`refs`信息可能会在对别的文件调用`compile_file`函数时被修改,这样就违反了差量计算系统的第原则:**差量计算函数不能修改任何全局状态**。 36 | 如果我们直接用这种设计来进行差量计算的话: 37 | > 假设有文件`A`和文件`B`,`A`中有全局变量`a`,`B`中使用了全局变量`a`,第一次编译是全量编译,生成了正确的`refs`信息。第二次编译的时候,用户只修改了`B`,所以`A`的编译被跳过,复用了上次结果。这时请注意:`a`的`refs`链表是包含上次编译产生的完整结果的,而不是只有`A`文件中的引用信息,这一切都是因为`B`在编译的时候可能改`A`的编译结果,往`a`的`refs`里加值。 38 | 39 | 所以我们需要重新设计这个功能,我们需要把`refs`信息从符号中移除,放到一个与编译文件绑定的`refs`表中,这个表可以是`map[str]vec`类型,他只记录当前文件中符号的引用信息。当lsp收到`find reference`请求的时候,先找到对应的符号,然后再遍历所有文件的编译输出,找到对应符号在各个`refs`表中的记录,并且汇总。这样就可以保证差量计算的正确性了。 40 | 41 | 42 | ## 总结 43 | 我不是很擅长写文章,不知道这篇表达的清不清楚,如果能帮助到你就再好不过了。文章篇幅限制,这篇博客里跳过了差量计算框架的使用细节。 44 | 如果你对这方面感兴趣,可以参考我们的[源代码](https://github.com/Pivot-Studio/pivot-lang)。 45 | -------------------------------------------------------------------------------- /book/src/compiler/ast.md: -------------------------------------------------------------------------------- 1 | # AST 2 | 3 | 抽象语法树是目前编译器中最复杂的部分,它是编译器的中间表示,也是编译器的核心。本节将介绍AST的设计和实现。 4 | 5 | ## AST的设计 6 | 7 | 基本上,所有源代码中的基础单位都会对应抽象语法树中的一个节点。抽象语法树有很多类型的节点,他们可能会相互引用。 8 | 9 | 所有的节点都必须实现`Node` trait,这个trait定义了节点的基本行为。 10 | 11 | ```rust,no_run,noplayground 12 | {{#include ../../../src/ast/node/mod.rs:node}} 13 | ``` 14 | 15 | 一般来说,所有的节点都需要加入`NodeEnum`中,并且使用`#[node]`proc macro进行修饰。 16 | 同时需要在`fmt.rs`中加入format相关的函数。 17 | 18 | 如果你想学习如何添加新的节点,可以先从简单的节点开始,比如`BoolConstNode` 19 | 20 | ```rust,no_run,noplayground 21 | {{#include ../../../src/ast/node/primary.rs:bool}} 22 | ``` 23 | 24 | 25 | 你可能注意到了,`Node`trait继承了`RangeTrait`,这个trait定义了节点的位置信息。 26 | 27 | ```rust,no_run,noplayground 28 | {{#include ../../../src/ast/node/mod.rs:range}} 29 | ``` 30 | 一般来说,`RangeTrait`的实现通过`#[node]`宏来自动生成,你不需要手动实现它。 31 | 32 | `Node`接口中的`print`函数用于打印节点的信息,它会被用于调试。`print`打印的结果和`tree`的输出非常像,你需要用一些工具函数来 33 | 格式化输出。以`ifnode`的`print`函数为例: 34 | 35 | ```rust 36 | {{#include ../../../src/ast/node/control.rs:print}} 37 | ``` 38 | 39 | `emit`函数是生成llvm代码的核心,它会调用llvm api构造自己对应的llvm ir。在编译的时候,最上层节点的`emit`会被调用, 40 | 该函数会递归的调用自己的子节点的`emit`函数,最终生成整个程序的llvm ir。 41 | 下方是`ifnode`的`emit`函数: 42 | 43 | ```rust,no_run,noplayground 44 | {{#include ../../../src/ast/node/control.rs:emit}} 45 | ``` 46 | 47 | emit函数的第一个参数是节点自身,第二个参数是编译上下文。编译上下文中会包含一些需要透传的信息,比如符号表,lsp参数等, 48 | 第三个参数是`builder`,用于生成中间代码。目前`builder`只有llvm的实现。 49 | 50 | emit函数的返回值比较复杂,它是一个`Result`枚举类型,它的`Ok`类型中包含一个`Option`的`PLValue`--这是代表该节点的运算结果, 51 | 一般只有表达式节点有这个值,statement节点这里会返回`None`,除此之外还包含一个`Option`的`PLType`,这是代表该节点返回值的类型, 52 | 最后一个是一个`TerminatorEnum`,用于分析某个路径是否有终结语句(break,continue,return,panic等)。返回值的`Err`类型是一个`PLDiag`,这个类型是用于报告错误的,包含了所有的错误信息。一般来说,在返回之前错误就会被加到编译上下文中,所以调用者不需要对其进行 53 | 任何处理。 54 | 55 | 56 | ## 打印AST结构 57 | 58 | plc命令行工具有打印ast的功能,你可以使用`plc xxx.pi --printast`命令来打印ast结构。 59 | 下方是一个ast打印结果的样例: 60 | 61 | ```ast 62 | ... 63 | file: /Users/bobli/src/pivot-lang/test/sub/mod.pi 64 | ProgramNode 65 | └─ FuncDefNode 66 | ├─ id: name 67 | ├─ TypeNameNode 68 | │ └─ ExternIdNode 69 | │ └─ VarNode: void 70 | └─ StatementsNode 71 | └─ RetNode 72 | file: /Users/bobli/src/pivot-lang/test/mod2.pi 73 | ProgramNode 74 | ├─ UseNode 75 | │ ├─ VarNode: sub 76 | │ └─ VarNode: mod 77 | ├─ FuncDefNode 78 | │ ├─ id: test_mod 79 | │ ├─ TypedIdentifierNode 80 | │ │ ├─ id: args 81 | │ │ └─ TypeNameNode 82 | │ │ └─ ExternIdNode 83 | │ │ └─ VarNode: i64 84 | │ ├─ TypeNameNode 85 | │ │ └─ ExternIdNode 86 | │ │ └─ VarNode: void 87 | │ └─ StatementsNode 88 | │ └─ RetNode 89 | └─ StructDefNode 90 | ├─ id: Mod2 91 | └─ TypedIdentifierNode 92 | ├─ id: y 93 | └─ TypeNameNode 94 | └─ ExternIdNode 95 | └─ VarNode: bool 96 | ... 97 | ``` 98 | -------------------------------------------------------------------------------- /book/src/compiler/flow.md: -------------------------------------------------------------------------------- 1 | # Flow Chart 2 | 3 | 这是一个附加功能,它将为每个函数生成流程图,以 `.dot` 文件格式输出,`.dot` 文件可通过 4 | - [Graphviz](https://graphviz.org/) 5 | - [Graphviz Online](https://dreampuf.github.io/GraphvizOnline) 6 | 7 | 查看。 8 | 9 | ## 依赖 10 | 11 | - [petgraph](https://github.com/petgraph/petgraph) 12 | 13 | ## 实现 14 | 15 | 流程图的生成包含两个步骤: 16 | 17 | - 由 AST 生成 `图` 数据结构 18 | - 根据 `图` 生成 `.dot文件` 19 | 20 | ### 图的生成 21 | 22 | 我们以函数为单位生成 `graph` ,而 AST 根节点为 `ProgramNode`,因此我们需要遍历其 `fntypes` 23 | ,逐个生成 `graph` 最终得到一个 `Vec` : 24 | 25 | ```rust,no_run,noplayground 26 | {{#include ../../../src/flow/mod.rs:creategraphs}} 27 | ``` 28 | 29 | 接下来实现 `from_ast()` 函数,它接收一个 `NodeEnum`(这是一个 `Statements` 节点), 返回一个完整的 `Graph`,具体分为两步: 30 | - 初步构建图(`build_graph()`) 31 | - 去除不必要节点(`0入度节点`,`虚节点`,`空节点`)) 32 | ```rust,no_run,noplayground 33 | {{#include ../../../src/flow/mod.rs:fromast}} 34 | ``` 35 | 主要介绍构建图的环节。 36 | 37 | 38 | 39 | 定义图的 `节点` 与 `边` : 40 | 41 | ```rust,no_run,noplayground 42 | {{#include ../../../src/flow/mod.rs:nodeandedge}} 43 | ``` 44 | 45 | `build_graph()`函数以 `Statement` 为单位,针对不同节点构建不同的图结构,为了方便的连接节点,我们定义了 `GraphContext` 用于存储上下文信息: 46 | 47 | ```rust,no_run,noplayground 48 | {{#include ../../../src/flow/mod.rs:GraphContext}} 49 | ``` 50 | 51 | 每次调用 `build_graph()` 前,我们需要为构建部分提供两个 `锚点(local_source, local_sink)` ,第一次调用时,`锚点` 即为起点和终点, 52 | 以后每次调用前,均需构建两个虚节点,作为`锚点`(虚节点之后将被去掉)。 53 | 54 | 对于不涉及分支、循环、跳转的简单语句,图结构较为简单: 55 | 56 | ``` 57 | local_source -> current -> local_sink 58 | 59 | local_source ------------> local_sink // 注释 60 | 61 | local_source ---> ERR ---> local_sink // 错误 62 | ``` 63 | 64 | 分支及循环语句则较为复杂(以 `IF语句` 为例): 65 | ``` 66 | IF: /--Y--> sub_source -----> [...body...] ------> sub_sink ->-\ 67 | / \ 68 | local_source -> cond local_sink 69 | \ / 70 | \--N--> sub_source1 -> Option<[...els...]> -> sub_sink ->-/ 71 | 72 | ``` 73 | 74 | `if.body` 及 `if.els` 部分可以通过递归调用 `build_graph()` 构建,但是需要先生成两个虚节点,并**暂时**赋给 `ctx`,构建完毕后, 75 | `ctx` 的 `local_source/sink`需要还原 76 | 77 | 对于语句块,对每个语句调用 `build_graph()` ,每次将 `sub_source` 更改为 `sub_sink` ,`sub_sink` 则重新创建: 78 | ```rust,no_run,noplayground 79 | {{#include ../../../src/flow/mod.rs:stsloop}} 80 | ``` 81 | 82 | ### .dot 文件生成 83 | 84 | 我们只需按dot语法格式生成图的点/边的信息即可。下面是一个简单的dot文件: 85 | 86 | ``` 87 | digraph _pointer_struct__name_params { 88 | D0 [shape=box, style=rounded, label="begin", fontname=""]; 89 | {rank = sink; D1 [shape=box, style=rounded, label="end", fontname=""];} 90 | D4 [shape=box, label="return\l", fontname=""]; 91 | D4 -> D1; 92 | D0 -> D4; 93 | } 94 | ``` 95 | 可以在[Graphviz Online](https://dreampuf.github.io/GraphvizOnline)查看对应流程图。 -------------------------------------------------------------------------------- /book/src/compiler/generic.md: -------------------------------------------------------------------------------- 1 | # Generic 2 | ```mermaid 3 | graph TD 4 | DefStruct-->StructAddSymbol-->StructDef-->NodeEmit 5 | subgraph DefStruct 6 | Def("Struct X< T >{y:T}") 7 | end 8 | subgraph StructAddSymbol 9 | subgraph SetUpGenericMap 10 | subgraph PLType::STType 11 | GenericMap 12 | Fields 13 | end 14 | subgraph PLType::Generic 15 | CurrentType 16 | end 17 | None 18 | EmptyVec("vec![]") 19 | CurrentType-->None 20 | Fields-->EmptyVec 21 | GenericMap--T-->PLType::Generic 22 | end 23 | %% GenLLVMCode 24 | subgraph GenLLVMCode 25 | Code("生成占位符,不带泛型签名,A = type {}") 26 | end 27 | AddType("ctx.add_type(X)") 28 | SetUpGenericMap-->GenLLVMCode-->AddType 29 | end 30 | 31 | subgraph StructDef 32 | AddGeneric(将泛型T加入ctx中泛型表) 33 | GetType("ctx.get_type(X)") 34 | FieldGetType 35 | subgraph EmitStructDef 36 | AddGeneric-->GetType--X-->FieldGetType--fields-->GenVtable 37 | end 38 | end 39 | 40 | subgraph NodeEmit 41 | HightLight 42 | end 43 | 44 | ``` 45 | ```mermaid 46 | graph TD 47 | subgraph StructInit 48 | Init("A{x:1+3}") 49 | end 50 | ``` 51 | ```mermaid 52 | graph TD 53 | subgraph TypeNameNode 54 | GenericParam 55 | subgraph ExidNode 56 | ModName 57 | subgraph VarNode 58 | ID 59 | end 60 | end 61 | end 62 | subgraph API 63 | ExidNode--Mod1::Struct1-->GetOriginType-->HasGenericParam{HasGenericParam}--yes-->GetGenericType 64 | HasGenericParam--no-->RET 65 | GenericParam--"i64,bool"-->GetGenericType-->SetUpGeneric 66 | SetUpGeneric-->GenericInfer 67 | SetUpGeneric-->FieldRecursiveEq 68 | subgraph GetType 69 | GenericInfer 70 | end 71 | subgraph EqOrInfer 72 | FieldRecursiveEq 73 | end 74 | GenericInfer--"Mod1::Struct1{i64|bool}"-->RET 75 | FieldRecursiveEq-->RET 76 | end 77 | ``` 78 | -------------------------------------------------------------------------------- /book/src/compiler/parser.md: -------------------------------------------------------------------------------- 1 | # Parser 2 | 3 | parser源代码位置位于`src/nomparser`目录下,包含了词法分析和语法分析部分。 4 | 5 | 6 | ## nom 7 | 8 | [nom](https://github.com/Geal/nom)是一个用rust编写的parser combinator库,它不像lr分析器一样提供生成代码的功能,而是 9 | 提供一组函数,这些函数可以用来组合出各种parser。 10 | 11 | 相比于lr分析器,nom的优点是它的parser combinator非常灵活,熟练后可以快速组合出各种parser, 12 | 而且可自定义性非常的强,看起来也很直观,相比很多ir生成器的语法并没有复杂多少,但是带来了更好的 13 | 语法支持(一般的ir分析生成器的语法定义文件不会有编程语言那么好的语法支持)。 14 | 15 | 会使用nom是读懂编译器parser代码的重要前提,这里强烈推荐两个nom文档: 16 | 17 | - [Nom Recipes](https://github.com/Geal/nom/blob/main/doc/nom_recipes.md) 18 | - [choosing a combinator](https://github.com/Geal/nom/blob/main/doc/choosing_a_combinator.md) 19 | 20 | ## parser结构 21 | 22 | parser的主要功能是使用递归下降法将pivot-lang源代码转换为ast。如果你不了解递归下降法,可以先看看[这篇文章](https://ruslanspivak.com/lsbasi-part1/)。 23 | 24 | 对于pivot lang的每一条语法规则,都会在parser里对应一个分析函数,这些分析函数可能会调用其他分析函数,最终最上层的分析函数可以将完整的源代码转换为ast。 25 | 26 | pivot lang的完整语法规则见[这里](../../../#grammar) 27 | 28 | parser最顶层的函数是`parse`,它接受一个源文件输出一个AST根节点。 29 | 30 | ```rust,no_run,noplayground 31 | {{#include ../../../src/nomparser/mod.rs:parse}} 32 | ``` 33 | -------------------------------------------------------------------------------- /book/src/compiler_theory/README.md: -------------------------------------------------------------------------------- 1 | # 编译原理 2 | 3 | 这个章节会介绍一些编译原理的基础知识,方便新手入门 4 | 5 | -------------------------------------------------------------------------------- /book/src/dev-prepare.md: -------------------------------------------------------------------------------- 1 | # 环境准备 2 | 3 | ## Rust 4 | 5 | 可以在项目的根目录下的`rust-toolchain`文件中查看当前项目使用的rust版本,如果你的rust版本不是这个版本,可以使用rustup安装这个版本的rust。 6 | 7 | ```bash 8 | rustup install $(cat rust-toolchain) 9 | ``` 10 | 11 | 国内如果没代理安装rust比较困难,建议使用清华源进行安装,见[此处](https://mirrors.tuna.tsinghua.edu.cn/help/rustup/) 12 | 13 | 安装完毕后,可以进行cargo换源设置,防止依赖无法下载,见[此处](https://mirrors.tuna.tsinghua.edu.cn/help/crates.io-index.git/) 14 | 15 | ## LLVM 16 | 17 | Pivot-Lang目前使用LLVM 18作为后端,所以需要安装LLVM。如果你使用的是MacOS,可以使用brew安装LLVM。 18 | 19 | ```bash 20 | brew install llvm@18 21 | ``` 22 | 23 | 如果你使用的是ubuntu,可以使用[这里](https://github.com/Pivot-Studio/setup-llvm/blob/main/scripts/install_llvm.sh)的脚本进行安装 24 | 25 | ## CMake 26 | 27 | 我们的垃圾回收模块使用了LLVM中的StackMap功能,需要使用CMake进行编译。 28 | 29 | ## 环境变量 30 | 31 | 开发项目需要正确配置一些环境变量才能让你保证开发时正确跑通测试。 32 | 33 | 运行下方命令将会把项目需要的一些环境变量加入到你的初始化脚本中 34 | 35 | ```bash 36 | make devlinux # linux 37 | make devmac # mac 38 | ``` 39 | 40 | 注意这些命令都只需要跑一次 41 | 42 | ## 测试是否成功配置开发环境 43 | 44 | 上方步骤完成后,可以运行下方命令进行测试 45 | 46 | ```bash 47 | make test 48 | ``` 49 | 50 | 如果上方命令都成功执行,那么恭喜你,你已经成功配置了开发环境 51 | 52 | ## 使用github codespace 进行开发 53 | 54 | 使用github codespace进行开发的环境配置较为简单,但是请注意费用问题。 55 | 56 | 点击以下链接即可创建一个包含pl开发环境的codespace 57 | 58 | [create codespace](https://github.com/codespaces/new?machine=standardLinux32gb&repo=535925143&ref=master&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=SouthEastAsia) 59 | 60 | 创建完毕后,建议使用本地vscode打开codespace中的项目进行开发 61 | 62 | ## 常见问题 63 | 64 | ### No suitable version of LLVM was found system-wide or pointed 65 | 66 | 需要设置llvm环境变量,如果你使用的是ubuntu,可以执行如下脚本. 67 | 68 | ```bash 69 | echo "export LLVM_SYS_180_PREFIX=/usr/lib/llvm-18" >> ~/.bashrc 70 | source ~/.bashrc 71 | ``` 72 | 73 | 如果是macOS通过`brew install llvm@16`安装,则需要设置环境变量: 74 | 75 | ```bash 76 | echo "export LLVM_SYS_180_PREFIX=$(brew --prefix llvm@18)" >> ~/.bashrc 77 | source ~/.bashrc 78 | ``` 79 | 80 | ### Could NOT find ZLIB (missing: ZLIB_LIBRARY ZLIB_INCLUDE_DIR) 81 | 82 | 缺少zlibdev造成的,如果你是ubuntu机器,使用下方命令进行安装: 83 | 84 | ```bash 85 | sudo apt install zlib1g-dev 86 | ``` 87 | 88 | ### Unknown linker flag: -lzstd 89 | 90 | 在Mac上出现时,可能是缺少`LIBRARY_PATH`环境变量 91 | 92 | ```bash 93 | echo "export LIBRARY_PATH=$LIBRARY_PATH:$(brew --prefix zstd)/lib" >> ~/.bashrc 94 | source ~/.bashrc 95 | ``` 96 | -------------------------------------------------------------------------------- /book/src/lsp/2022-12-08-13-20-37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/book/src/lsp/2022-12-08-13-20-37.png -------------------------------------------------------------------------------- /book/src/lsp/2022-12-08-13-22-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/book/src/lsp/2022-12-08-13-22-04.png -------------------------------------------------------------------------------- /book/src/lsp/README.md: -------------------------------------------------------------------------------- 1 | # Language Server 2 | 3 | Pivot Lang的Language Server(以下简称LSP)是一个用于为编译器提供语法支持的组件,它同时被用于在编译期间生成诊断信息。 4 | 5 | > 基本上,lsp能够为所有的现代代码编辑器提供服务,但是目前我们只为vsc提供官方支持。如果想在别的编辑器中使用lsp,可能需要自己写一个 6 | > 简单的客户端插件。 7 | 8 | 9 | ### ⚠️注意事项 10 | **有一些函数功能纯粹,可能被用在很多无法预料的地方,如果在这些函数中操作非幂等的lsp相关功能很可能导致lsp最后工作时出现错误,请尽量避免**! 11 | 12 | 目前大部分lsp的功能接口都是幂等的。`semantic_token`和`doc_symbol`不幂等 -------------------------------------------------------------------------------- /book/src/lsp/design.md: -------------------------------------------------------------------------------- 1 | # 设计(design) 2 | 3 | pivot-lang的lsp功能被内置于编译器中,它是以差量计算(incremental)为前提设计的。 4 | 目前整个lsp程序几乎是完全单线程的,但得益于我们的差量计算,它仍然具有不错的性能。 5 | 6 | 7 | ## 1. 差量计算(incremental) 8 | 9 | 差量计算是指在编译器中,当源代码发生修改时,我们只对发生变化的部分进行重新分析,而不是对整个项目全部重新进行计算。 10 | 11 | pivot-lang的差量计算是基于rust的[salsa](https://github.com/salsa-rs/salsa)库实现的。 12 | 我们使用的版本是仍然处于预览阶段的`salsa_2022`。 13 | 14 | pl中的差量计算的最小复用单元是`Module`,即一个源文件。 15 | 16 | 在plc作为lsp运行时,所有的lsp功能相关计算会在`TextDocumentEdit`事件发生时进行,之后如果不进行文本编辑,所有的 17 | lsp请求都会直接从缓存中读取结果。 18 | 19 | 20 | ## 2. 差量计算举例 21 | 22 | 假设我们有一个pl项目,其中有三个文件:`a.pi`、`b.pi`、`c.pi`。 23 | 其中`a.pi`和`b.pi`都引用了`c.pi`中的函数`f`,并且`a.pi`中还使用了`b.pi`中的函数`g`。 24 | 25 | 此时,当我们用vsc打开此pl项目,vsc会启动plc进行分析。如果没使用差量计算,那么plc分析流程如下: 26 | 27 | ``` 28 | 尝试分析a.pi-> 29 | 依赖c.pi-> 30 | 分析c.pi-> 31 | 返回继续分析a.pi-> 32 | 依赖b.pi-> 33 | 分析b.pi-> 34 | 依赖c.pi-> 35 | 分析c.pi-> 36 | 返回继续分析b.pi-> 37 | 返回继续分析a.pi-> 38 | 完成 39 | ``` 40 | 41 | 可以看到`c.pi`被分析了两次,这是不必要的。差量分析在这一步中可以优化掉第二次对`c.pi`的分析。 42 | 43 | 然后,假设我们在`a.pi`中添加了一个字符,那么如果没采用差量分析法,所有的模块都会被重新分析一遍。而差量分析法只会对`a.pi`进行重新分析。 44 | 45 | 接着,如果我们改动了`b.pi`,那么差量分析法会对`b.pi`和`a.pi`进行重新分析,而不会对`c.pi`进行重新分析。**即:每次修改文件时,只会对该文件以及依赖该文件的文件进行重新分析。** 46 | 47 | ## 3. 验证差量计算是否正常工作 48 | 49 | 我们可以在vsc的选项中找到一个plc的`Log Level`选项,将它设置成`2`之后就能看到plc的`info`日志了。 50 | ![](2022-12-08-13-20-37.png) 51 | 52 | > 日志可以在vsc的outut中选择`pivot-lang language server`查看 53 | > ![](2022-12-08-13-22-04.png) 54 | 55 | 这里会在每次重新计算的时候输出对应的log。 56 | 57 | -------------------------------------------------------------------------------- /book/src/lsp/diagnostic.md: -------------------------------------------------------------------------------- 1 | # Diagnostic 2 | 3 | 诊断信息是个非常重要的功能,它可以帮助我们在编写代码的时候发现错误和可能有问题的地方,从而提高我们的编码效率。 4 | 5 | 为了让用户体验尽可能的好,我们的lsp分析需要尽量容忍用户的错误输入,尽可能多的分析出用户代码中的问题 6 | 7 | ## Fault Tolerance 8 | 9 | 错误容忍是生成好的诊断信息的前提。在pivot-lang的中,我们分别在两个层面上实现了错误容忍: 10 | 11 | - parser 12 | - ast 13 | 14 | ### Parser的错误容忍 15 | 16 | nom parser架构中,如果出现了一个无法被识别的语句,整个分析器就会终止分析输出错误。这对于错误容忍的要求来说是无法被接受的。所以 17 | 我们的编译器不使用nom parser的默认错误处理机制,任何parser阶段产生的nom error都应该被视作**bug**,我们应该尽可能的避免这种情况。 18 | 19 | 在parse过程中,如果一些错误语句能非常明显的被识别为一个语法的未完成项(且没有歧义),我们应该将它识别为该语法类型的Node,并且在Node上加一个 20 | flag标识它不完整(常常是`is_complete`),这样在ast阶段我们就能输出对应的诊断信息。 21 | 22 | 对于最常见的基础语法单位`statement`和`top_statement`,parser提供了一个helper函数`except`,能够在遇到不可被识别的错误语句的时候 23 | 将该“块”语句识别为`ErrNode`,以方便后续的分析正常进行。 24 | 25 | > !!!**注意**:ErrNode虽然很好用,但是它只能输出很宽泛的诊断信息(比如无法识别该语句),它是最后的错误容忍手段,应该尽量避免使用它 26 | 27 | 28 | ### AST的错误容忍 29 | 30 | 由于parse阶段的时候能够容忍错误的语句,对于一些语法错误,ast节点只需要检查自身的完整性就能够输出诊断信息了。而对于语义错误(例如类型不匹配),我们需要在ast阶段 31 | 进行分析并获取结果。这些操作目前是在各个节点的`emit`函数里进行的。所有的`emit`函数都返回`NodeResult`类型,如果该节点的`emit`中出现了错误, 32 | 分析将会中断,其对应的错误信息会被添加到`ctx`(编译上下文)中并且作为`error`返回。上层函数如果遇到自己依赖的函数报错**一定不能重复添加该错误至`ctx`中**, 33 | 否则会导致错误信息重复输出。上层函数处理自己的依赖报错有两种情况: 34 | - 直接停止分析并将该错误传递给自己的上级 35 | - 忽略该错误继续进行分析 36 | 37 | 一般来说,大部分的expression和statement都会采用第一种方案,而statement block则会采用第二种方案 38 | 39 | -------------------------------------------------------------------------------- /book/src/performance.md: -------------------------------------------------------------------------------- 1 | # Performance 2 | 3 | 实验证明在经过我们的数次性能优化之后,Pivot Lang的性能已经在很多场景下超越Golang。优化之后的Immix GC在内存分配和回收上的性能也有了很大的提升,能够 4 | 在大部分场景下击败别的GC算法。 5 | 6 | ## GC Benchmark 7 | 8 | 我们使用知名的`BdwGC`的benchmark代码进行了测试,将Pivot Lang版本与Golang版本以及BdwGC原版进行了对比。 9 | 10 | 源代码以及结果见:[Pivot Lang GC Benchmark](https://github.com/Chronostasys/gcbench) 11 | 12 | ## RealLife application performance 13 | 14 | 我们使用Pivot Lang实现了有名的[ray tracing in one weekend](https://github.com/Pivot-Studio/rtweekend-pl)项目, 15 | 渲染速度相比于[golang实现](https://github.com/hunterloftis/oneweekend)有明显性能优势。 16 | 17 | > 请注意golang的实现代码中的参数与原书不完全一样,测试是在调整成一样的参数之后进行的。 18 | -------------------------------------------------------------------------------- /book/src/references/README.md: -------------------------------------------------------------------------------- 1 | # References 2 | 3 | 语言功能的参考文档。 4 | -------------------------------------------------------------------------------- /book/src/references/array.md: -------------------------------------------------------------------------------- 1 | # Pivot Lang数组使用说明 2 | 3 | 在Pivot Lang中,数组是一种重要的数据结构,可以用来存储多个相同类型的数据。以下是一些基本的数组操作: 4 | 5 | ## 创建数组 6 | 7 | 在Pivot Lang中,我们可以使用以下方式创建一个数组: 8 | 9 | ```pivot 10 | let a1:[i64] = [1]; 11 | let array = arr::from_slice([1, 2, 3]); 12 | let b = a1[0]; 13 | a1[0] = 100; 14 | ``` 15 | 16 | 这里,我们创建了一个名为`a1`的数组,它包含一个元素`1`。然后,我们使用`arr::from_slice`函数从一个切片创建了一个名为`array`的数组,它包含三个元素`1`、`2`和`3`。 17 | 18 | 原生的熟组是不能增加或减少元素的,但是可以使用`arr::from_slice`函数从一个切片创建一个数组容器,这个容器是可以增加或减少元素的。 19 | 20 | ## 访问数组元素 21 | 22 | 我们可以使用`get`方法来访问数组中的元素: 23 | 24 | ```pivot 25 | let a2 = array.get(2); 26 | ``` 27 | 28 | 这里,我们获取了`array`数组中索引为`2`的元素,并将其值赋给了变量`a2`。 29 | 30 | ## 修改数组元素 31 | 32 | 我们可以使用`set`方法来修改数组中的元素: 33 | 34 | ```pivot 35 | array.set(2, 100); 36 | ``` 37 | 38 | 这里,我们将`array`数组中索引为`2`的元素的值设置为了`100`。 39 | 40 | ## 添加元素到数组 41 | 42 | 我们可以使用`push`方法来向数组中添加元素: 43 | 44 | ```pivot 45 | array.push(4); 46 | array.push(5); 47 | ``` 48 | 49 | 这里,我们向`array`数组中添加了两个元素`4`和`5`。 50 | 51 | ## 从数组中移除元素 52 | 53 | 我们可以使用`pop`方法来从数组中移除元素: 54 | 55 | ```pivot 56 | let a5 = array.pop(); 57 | ``` 58 | 59 | 这里,我们从`array`数组中移除了最后一个元素,并将其值赋给了变量`a5`。 60 | 61 | ## 遍历数组 62 | 63 | 我们可以使用`iter`方法来获取一个数组的迭代器,然后使用`next`方法来遍历数组中的元素: 64 | 65 | ```pivot 66 | let iter = array.iter(); 67 | for let i = iter.next(); i is i64; i = iter.next() { 68 | // 处理元素i 69 | } 70 | ``` 71 | 72 | 这里,我们获取了`array`数组的迭代器,然后使用一个循环来遍历数组中的元素。 73 | -------------------------------------------------------------------------------- /book/src/references/basic.md: -------------------------------------------------------------------------------- 1 | 2 | # Pivot Lang基础语法和运算 3 | 4 | ## 函数定义 5 | 6 | 使用`fn`关键字定义函数,函数可以有返回值,也可以没有。 7 | 8 | 例如: 9 | 10 | - `pub fn test_warn() void`定义了一个没有返回值的函数 11 | - `pub fn test_vm_link() i64`定义了一个返回值为i64类型的函数 12 | 13 | ## 变量定义和赋值 14 | 15 | 使用`let`关键字定义变量,并可以直接赋值。 16 | 17 | 例如: 18 | 19 | - `let x = 1;`定义了一个i64类型变量x并赋值为1 20 | - `let test: i8 = 1;`定义了一个i8类型的变量test并赋值为1 21 | 22 | ```admonish tip 23 | 在`let`语句中如果不标注左边变量的类型,编译器会尝试自动推导出变量的类型。 24 | 25 | ``` 26 | 27 | ## 运算 28 | 29 | 支持基本的算术运算(加减乘除)、位运算(与或非异或左移右移)等。 30 | 31 | 例如: 32 | 33 | - `test = -test;`是取反运算 34 | - `let test2: i8 = test + 1;`是加法运算 35 | - `let b = test | 1;`是位或运算 36 | 37 | ## 函数调用 38 | 39 | 直接使用函数名调用函数。 40 | 41 | 例如: 42 | 43 | - `test_vm();`调用了test_vm函数 44 | 45 | ## 指针操作 46 | 47 | 使用`&`获取变量的地址,使用`*`获取指针指向的值。 48 | 49 | 例如: 50 | 51 | - `let b = &a;`获取了变量a的地址 52 | - `*b = 100;`修改了指针b指向的值 53 | 54 | ## 循环 55 | 56 | 使用`while`和`for`关键字进行循环。 57 | 58 | 例如: 59 | 60 | - `while i < 7 {...}`是一个while循环 61 | - `for let i = 0; i <= 10; i = i + 1 {...}`是一个for循环 62 | 63 | ## 条件判断 64 | 65 | 使用`if`和`else`关键字进行条件判断。 66 | 67 | 例如: 68 | 69 | - `if i == 3 {...}`是一个if条件判断 70 | 71 | ## 跳出循环 72 | 73 | 使用`break`关键字跳出循环。 74 | 75 | 例如: 76 | 77 | - `if i == 5 {break;}`在i等于5时跳出循环 78 | 79 | ## 跳过当前循环 80 | 81 | 使用`continue`关键字跳过当前循环。 82 | 83 | 例如: 84 | 85 | - `if i == 3 {i = 5;continue;}`在i等于3时跳过当前循环 86 | 87 | ## 逻辑运算 88 | 89 | 支持基本的逻辑运算(与或非)。 90 | 91 | 例如: 92 | 93 | - `let x = (false && true_with_panic()) || (true || !true_with_panic());`是一个复杂的逻辑运算 94 | -------------------------------------------------------------------------------- /book/src/references/closure.md: -------------------------------------------------------------------------------- 1 | # Pivot Lang闭包使用说明 2 | 3 | 在Pivot Lang中,闭包是一种特殊的函数,它可以捕获和使用其外部作用域中的变量。以下是一些基本的闭包操作: 4 | 5 | ## 创建闭包 6 | 7 | 在Pivot Lang中,我们可以使用以下方式创建一个闭包: 8 | 9 | ```pivot 10 | let a = |a: i64| => i64 { 11 | let c = b; 12 | return c; 13 | }; 14 | ``` 15 | 16 | 这里,我们创建了一个名为`a`的闭包,它接受一个`i64`类型的参数,返回一个`i64`类型的结果。在闭包体内,我们使用了外部作用域中的变量`b`。 17 | 18 | ## 调用闭包 19 | 20 | 我们可以像调用普通函数一样调用闭包: 21 | 22 | ```pivot 23 | let re = a(2); 24 | ``` 25 | 26 | 这里,我们调用了闭包`a`,并将结果赋值给了变量`re`。 27 | 28 | ## 闭包的类型推断 29 | 30 | 在Pivot Lang中,一些情况下闭包的类型可以被推断出来。例如,我们可以将一个闭包作为函数的参数: 31 | 32 | ```pivot 33 | test_type_infer(|a| => { 34 | let c = b; 35 | return c; 36 | }); 37 | ``` 38 | 39 | 这里,我们将一个闭包作为`test_type_infer`函数的参数。这个闭包接受一个参数`a`,并返回一个结果。在闭包体内,我们使用了外部作用域中的变量`b`。 40 | 41 | ## 返回闭包的函数 42 | 43 | 在Pivot Lang中,函数可以返回一个闭包: 44 | 45 | ```pivot 46 | fn test_ret_closure() |i64| => i64 { 47 | let b = 1; 48 | return |a: i64| => i64 { 49 | let c = b; 50 | return c; 51 | }; 52 | } 53 | ``` 54 | 55 | 这里,我们定义了一个名为`test_ret_closure`的函数,它返回一个闭包。这个闭包接受一个`i64`类型的参数,返回一个`i64`类型的结果。在闭包体内,我们使用了函数作用域中的变量`b`。 56 | 57 | ## LSP特殊支持 58 | 59 | 所有闭包内部的被捕获变量都会被下划线标注,以方便区分 60 | 61 | ![closure lsp](closure_lsp.png) 62 | -------------------------------------------------------------------------------- /book/src/references/closure_lsp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/book/src/references/closure_lsp.png -------------------------------------------------------------------------------- /book/src/references/deconstruct.md: -------------------------------------------------------------------------------- 1 | # Deconstruct 2 | 3 | 解构指的是用类似构造复杂类型的语法获取复杂类型中的部分值。一般来说,解构语法会用于结构体和元组。 4 | 5 | ## Table of Contents 6 | 7 | 8 | 9 | 10 | 11 | ## 元组解构 12 | 13 | ```pl 14 | let (a, b, c) = (1, 2, 3); 15 | (a, b, c): (i32, i32, i32) = (1, 2, 3); 16 | (a) = (1,); 17 | ``` 18 | 19 | 20 | ```admonish warning 21 | 元组解构的时候目标元素个数必须与源元素个数相同,否则会报错。 22 | 23 | ``` 24 | 25 | ```pl 26 | let (a, b, c) = (1, 2); // 出错! 27 | (a) = (1, 2); // 出错! 28 | ``` 29 | 30 | ## 结构体解构 31 | 32 | 结构体解构与元组解构类似,只是解构的时候需要指定字段名。 33 | 34 | ```pl 35 | struct Point { 36 | x: i32; 37 | y: i32; 38 | } 39 | 40 | let { x:xx, y:yy } = Point { x: 1, y: 2 }; 41 | ``` 42 | 43 | ```admonish tip 44 | 结构体解构的时候,如果字段名与变量名相同,可以省略字段名。 45 | 46 | ``` 47 | 48 | ```pl 49 | struct Point { 50 | x: i32; 51 | y: i32; 52 | } 53 | 54 | let { x, y } = Point { x: 1, y: 2 }; // 省略字段名 55 | let yy:i32; 56 | { x, y:yy } = Point { x: 1, y: 2 }; 57 | ``` 58 | 59 | 对于结构体解构,你不需要像元组一样解构所有的字段。 60 | 61 | ```pl 62 | struct Point { 63 | x: i32; 64 | y: i32; 65 | } 66 | 67 | let { x } = Point { x: 1, y: 2 }; // 只解构 x 字段 68 | ``` 69 | 70 | ## 嵌套解构 71 | 72 | 解构语法可以嵌套使用。 73 | 74 | ```pl 75 | struct Point { 76 | x: i32; 77 | y: i32; 78 | } 79 | 80 | struct Line { 81 | start: Point; 82 | end: Point; 83 | } 84 | 85 | let { start: { x:xx, y:yy }, end: { x:xx2, y:yy2 } } = Line { start: Point { x: 1, y: 2 }, end: Point { x: 3, y: 4 } }; 86 | ``` 87 | 88 | 元组和结构体也可以相互嵌套 89 | 90 | ```pl 91 | 92 | struct Point { 93 | x: i32; 94 | y: i32; 95 | } 96 | 97 | struct Line { 98 | start: Point; 99 | end: Point; 100 | } 101 | 102 | let (a, b, { start }) = (1, 2, Line { start: Point { x: 1, y: 2 }, end: Point { x: 3, y: 4 } }); 103 | 104 | ``` 105 | 106 | -------------------------------------------------------------------------------- /book/src/references/generic.md: -------------------------------------------------------------------------------- 1 | 2 | # Pivot Lang 泛型语法和功能 3 | 4 | 在 Pivot Lang 中,泛型是一种定义可以处理多种类型的代码的方式。泛型可以用于定义函数、结构体和trait。在定义时,泛型参数被指定为特定的标识符,这个标识符可以在定义中的任何地方使用。在使用泛型定义的代码时,泛型参数被具体的类型替换。 5 | 6 | 以下是一些使用泛型的例子: 7 | 8 | ## 1. 在结构体中使用泛型: 9 | 10 | ```pivot 11 | pub struct HashTable|V> { 12 | buckets:[Option>]; 13 | salt:u64; 14 | entries:u64; 15 | } 16 | ``` 17 | 18 | 在这个定义中,`K`和`V`是泛型参数。`K`必须实现`Hash`和`Eq`这两个trait,`V`没有特定的要求。`HashTable`有三个字段:`buckets`、`salt`和`entries`。`buckets`是一个数组,它的元素类型是`Option>`,这是一个使用泛型参数`K`和`V`的类型。 19 | 20 | ## 2. 在方法中使用泛型: 21 | 22 | ```pivot 23 | impl |V> HashTable { 24 | pub fn insert(k:K,v:V) void { 25 | // ... 26 | } 27 | } 28 | ``` 29 | 30 | 在这个方法中,`K`和`V`是泛型参数。这个方法接受两个参数,一个是类型为`K`的键,一个是类型为`V`的值。 31 | 32 | ## 3. 在普通函数中使用泛型: 33 | 34 | ```pivot 35 | pub fn new_hash_table|V>(bucket_size:u64,salt:u64) HashTable { 36 | // ... 37 | } 38 | ``` 39 | 40 | 在这个函数中,`K`和`V`是泛型参数。这个函数接受两个参数,一个是桶的大小,一个是盐值。它返回一个`HashTable`类型的值。 41 | 42 | ## 4. 使用trait约束: 43 | 44 | 在Pivot Lang中,可以使用trait约束泛型参数。例如,`K:Hash+Eq`表示`K`必须实现`Hash`和`Eq`这两个trait。 45 | 46 | 泛型提供了代码重用的强大工具,使得可以编写一段代码来处理多种类型,而不是为每种类型都写一段几乎相同的代码。这不仅可以减少代码量,也可以提高代码的可读性和可维护性。 47 | -------------------------------------------------------------------------------- /book/src/references/interface.md: -------------------------------------------------------------------------------- 1 | 2 | # Pivot Lang Trait 语法和功能 3 | 4 | 在 Pivot Lang 中,trait 是一种定义共享行为的方式。一个 trait 可以由多个方法组成,这些方法定义了实现该 trait 的类型应具有的行为。 5 | 6 | ## Trait 定义 7 | 8 | 一个 trait 是使用 `trait` 关键字定义的,后面跟着 trait 的名称和一个包含方法签名的代码块。例如: 9 | 10 | ```pivot 11 | trait B { 12 | fn b() i64; 13 | } 14 | ``` 15 | 16 | 这定义了一个名为 `B` 的 trait,它有一个返回 `i64` 的方法 `b`。 17 | 18 | ## 复合 Traits 19 | 20 | 一个 trait 可以要求实现它的类型也实现其他的 traits。这是通过 `:` 操作符完成的。例如: 21 | 22 | ```pivot 23 | trait C: A+B { 24 | fn c() void; 25 | } 26 | ``` 27 | 28 | 这定义了一个名为 `C` 的 trait,要求实现它的类型也要实现 `A` 和 `B` 这两个 traits。 29 | 30 | ## Trait 实现 31 | 32 | 一个类型通过为 trait 中的所有方法提供定义来实现一个 trait。这是通过 `impl` 关键字完成的。例如: 33 | 34 | ```pivot 35 | impl B for test_struct { 36 | fn b() i64 { 37 | return 1000; 38 | } 39 | } 40 | ``` 41 | 42 | 这段代码为类型 `test_struct` 实现了 trait `B`。方法 `b` 被定义为返回 `1000`。 43 | 44 | 关于 `impl` 的更多信息,请参阅 [method](method.md)。 45 | 46 | ## Trait 使用 47 | 48 | 一个 trait 可以被用作变量和函数参数的类型。这允许代码与实现了该 trait 的任何类型一起工作。例如: 49 | 50 | ```pivot 51 | let c: C; 52 | ``` 53 | 54 | 这段代码声明了一个类型为 `C` 的变量 `c`。任何实现了 trait `C` 的类型都可以被赋值给 `c`。 55 | 56 | ## 结论 57 | 58 | Pivot Lang 中的 traits 提供了一种定义类型间共享行为的方式。它们允许代码重用和多态,使得语言更加灵活和强大。 59 | -------------------------------------------------------------------------------- /book/src/references/macro.md: -------------------------------------------------------------------------------- 1 | # Macro 2 | 3 | ```admonish warning 4 | pl的宏系统目前不稳定,可能存在很多bug,使用时请注意。 5 | ``` 6 | 7 | TODO 8 | -------------------------------------------------------------------------------- /book/src/references/method.md: -------------------------------------------------------------------------------- 1 | # Method 2 | 3 | method就是隶属于某个结构体的函数,它们与普通函数**没有**本质区别。 4 | 所有的method都必须在impl块里声明,且method都会隐式的有个`self`参数,该参数是`impl`类型的指针 5 | 6 | ## Method Example 7 | 8 | 最简单的添加method的例子: 9 | 10 | ```pivot-lang 11 | {{#include ../../../test/test/method.pi:impl}} 12 | ``` 13 | 14 | 在一个包中,可以定义该包中结构体的`method` . 15 | 16 | 调用method的时候,使用`.`即可 17 | 18 | ```pivot-lang 19 | let a = A{}; 20 | a.method(); 21 | ``` 22 | 23 | ## Extension Method 24 | 25 | 实际上,如果一个结构体不在当前包中定义,我们可以仍然可以定义它实现 **当前包中** `Trait` 的 `method` 。这种方法被称为`extension method`。 26 | 27 | ```pivot-lang 28 | pub trait Eq { 29 | fn eq(r:*S) bool; 30 | } 31 | 32 | 33 | impl > Eq<[T]> for [T] { 34 | fn eq(r:*[T]) bool { 35 | if arr_len(*self) != arr_len(*r) { 36 | return false; 37 | } 38 | for let i = 0; i < arr_len(*self); i = i + 1 { 39 | if !(*self)[i].eq(&(*r)[i]) { 40 | return false; 41 | } 42 | } 43 | return true; 44 | } 45 | } 46 | ``` 47 | 48 | 当然,你也可以合法的为本包中的结构体实现子包(或本包)中的`Trait`,但是这种情况下的`method`不属于`extension method`。 49 | 50 | ### 与普通method的区别 51 | 52 | `extension method`与普通`method`的区别在于其作用域,普通`method`的作用域与其所在的结构体相同,只要出现了该结构体类型就一定可以使用他的 53 | `method`(这里的出现不止指显示的引入结构体,通过函数返回值等方式间接的获取该类型的值也算)。 54 | 55 | 而`extension method`的作用域则是在`Trait`所在的包中,要使用`extension method`,必须直接或者间接的依赖了该`Trait`所在的包。 56 | -------------------------------------------------------------------------------- /book/src/references/module.md: -------------------------------------------------------------------------------- 1 | # Module 2 | 3 | 模块化 4 | 5 | ## 模块的划分和使用 6 | 7 | 总体来说,pl的模块化与rust类似,但是规则比rust简单。 8 | 任何一个pl文件都是一个模块,模块的名字就是文件名。一个pl项目的根目录一定有`Kagari.toml`配置文件,之后所有该项目 9 | 模块的路径都是从该文件所在的目录开始计算的。 10 | 11 | 举个例子,如果我的pl项目有以下的目录结构: 12 | 13 | ```text 14 | test 15 | ├── Kagari.toml 16 | ├── mod1.pi 17 | ├── main.pi 18 | └── sub 19 | └── mod.pi 20 | 21 | ``` 22 | 23 | 如果`main.pi`想使用`mod1.pi`或者`mod.pi`中的函数,那么可以这样写: 24 | 25 | ```pivot-lang 26 | use mod1; 27 | use sub::mod; 28 | 29 | fn main() void { 30 | mod1::func(); 31 | mod::func(); 32 | return; 33 | } 34 | ``` 35 | 如果`mod.pi`想使用`mod1.pi`中的函数,那么可以这样写: 36 | 37 | ```pivot-lang 38 | use mod1; 39 | 40 | fn main() void { 41 | mod1::func(); 42 | return; 43 | } 44 | 45 | ``` 46 | 47 | 48 | ```admonish warning 49 | pl的模块**不支持**循环引用 50 | 51 | ``` 52 | ```admonish warning 53 | 目前pl引入的所有模块必须对应到相对的pi文件(不能对应目录),且引入同名的模块是UB。 54 | 55 | ``` 56 | 57 | ## 单独引入和全引入 58 | 59 | pl的模块引入支持单独引入某些符号或者全引入。 60 | 61 | ```pivot-lang 62 | use mod1::func1; // 单独引入 63 | use mod2::*; // 全引入 64 | ``` 65 | 请注意,单独引入仍然会形成父子模块关系,会对[extension method](method.md#extension-method)的作用域产生影响。 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ## 引用另一个pl项目 75 | 目前只支持引用本地的pl项目,引用的方式是在`Kagari.toml`中添加`[deps]` 76 | 77 | ```toml 78 | [deps] 79 | sub3 = { path = "sub2" } 80 | ``` 81 | 82 | 使用时,引用的项目模块会在`deps`中定义的命名空间之下: 83 | 84 | ```pivot-lang 85 | use sub3::lib; 86 | 87 | fn main() void { 88 | lib::func(); 89 | return; 90 | } 91 | 92 | ``` 93 | -------------------------------------------------------------------------------- /book/src/references/operator/README.md: -------------------------------------------------------------------------------- 1 | # Operators 2 | 3 | 本节收录了所有的运算符。 4 | 5 | ## 算术运算符 6 | 7 | | 运算符 | 描述 | 示例 | 8 | | --- | --- | --- | 9 | | `+` | 加法 | `1 + 1` | 10 | | `-` | 减法 | `1 - 1` | 11 | | `*` | 乘法 | `1 * 1` | 12 | | `/` | 除法 | `1 / 1` | 13 | | `%` | 取余 | `1 % 1` | 14 | 15 | ## 位运算符 16 | 17 | | 运算符 | 描述 | 示例 | 18 | | --- | --- | --- | 19 | | `&` | 与 | `1 & 1` | 20 | | `\|` | 或 | `1 \| 1` | 21 | | `^` | 异或 | `1 ^ 1` | 22 | | `<<` | 左移 | `1 << 1` | 23 | | `>>` | 右移 | `1 >> 1` | 24 | 25 | ## 逻辑运算符 26 | 27 | | 运算符 | 描述 | 示例 | 28 | | --- | --- | --- | 29 | | `&&` | 逻辑与 | `true && false` | 30 | | `\|\|` | 逻辑或 | `true \|\| false` | 31 | | `!` | 逻辑非 | `!true` | 32 | 33 | ## 比较运算符 34 | 35 | | 运算符 | 描述 | 示例 | 36 | | --- | --- | --- | 37 | | `==` | 等于 | `1 == 1` | 38 | | `!=` | 不等于 | `1 != 1` | 39 | | `>` | 大于 | `1 > 1` | 40 | | `<` | 小于 | `1 < 1` | 41 | | `>=` | 大于等于 | `1 >= 1` | 42 | | `<=` | 小于等于 | `1 <= 1` | 43 | 44 | ## 赋值运算符 45 | 46 | | 运算符 | 描述 | 示例 | 47 | | --- | --- | --- | 48 | | `=` | 赋值 | `a = 1` | 49 | 50 | ## 类型运算符 51 | 52 | 详见[类型运算符](./tyops.md)。 53 | 54 | | 运算符 | 描述 | 示例 | 55 | | --- | --- | --- | 56 | | `as` | 类型转换 | `1 as i32` | 57 | | `is` | 类型判断 | `1 is i32` | 58 | | `impl` | 实现 | `a impl TestTrait?` | 59 | 60 | ## 其他运算符 61 | 62 | | 运算符 | 描述 | 示例 | 63 | | --- | --- | --- | 64 | | `&` | 取地址 | `&a` | 65 | | `*` | 取值 | `*a` | 66 | | `()` | 函数调用 | `test_vm()` | 67 | | `[]` | 索引 | `a[1]` | 68 | | `.` | 成员访问 | `a.b` | 69 | | `:` | 类型标注 | `let a: i32 = 1` | 70 | | `;` | 语句结束 | `let a = 1;` | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /book/src/references/tuple.md: -------------------------------------------------------------------------------- 1 | 2 | # Pivot Lang 元组语法和功能 3 | 4 | 在 Pivot Lang 中,元组是一种可以包含不同类型元素的数据类型。元组使用圆括号`()`包围元素,元素之间使用逗号`,`分隔。元组的元素可以通过`.`后跟索引的方式访问。 5 | 6 | 以下是一些使用元组的例子: 7 | 8 | ## 1. 定义一个空元组: 9 | 10 | ```pivot 11 | let d = (); 12 | ``` 13 | 14 | ## 2. 定义一个只有一个元素的元组: 15 | 16 | ```pivot 17 | let a = (1,); 18 | ``` 19 | 20 | 注意,只有一个元素的元组需要在元素后面加上逗号`,`,否则它会被认为是一个普通的值,而不是元组。 21 | 22 | ## 3. 定义一个包含多个元素的元组: 23 | 24 | ```pivot 25 | let b = (1, 2); 26 | ``` 27 | 28 | ## 4. 定义一个包含元组的元组: 29 | 30 | ```pivot 31 | let c = (1, 2, (3,)); 32 | ``` 33 | 34 | 5. 访问元组的元素: 35 | 36 | ```pivot 37 | let e = a.0; 38 | let f = c.2; 39 | ``` 40 | 41 | 在这些例子中,`a.0`表示元组`a`的第一个元素,`c.2`表示元组`c`的第三个元素。 42 | -------------------------------------------------------------------------------- /book/src/references/union.md: -------------------------------------------------------------------------------- 1 | # Pivot Lang的Union语法 2 | 3 | 在Pivot Lang中,Union类型允许你将多种不同的类型组合在一起。你可以定义一个Union类型,它可以包含任何你需要的类型。以下是一些Union类型的定义: 4 | 5 | ```pivot 6 | type A = f32 | T; 7 | type B = i32 | A; 8 | type D = *i32 | f64; 9 | ``` 10 | 11 | 在上述代码中,`A`是一个泛型Union类型,它可以是`f32`或者任何其他类型`T`。`B`也是一个泛型Union类型,它可以是`i32`或者类型`A`。`D`是一个Union类型,它可以是`*i32`或者`f64`。 12 | 你可以使用Union类型来定义变量,如下所示: 13 | 14 | ```pivot 15 | let c: A = a; 16 | let aa: B = d; 17 | let ff: D = ≫ 18 | ``` 19 | 20 | 你也可以使用Union类型在函数中作为返回值,如下所示: 21 | 22 | ```pivot 23 | fn test_ret_union() Option { 24 | return 101; 25 | } 26 | ``` 27 | 28 | 你还可以在Union类型上定义方法,如下所示: 29 | 30 | ```pivot 31 | impl A { 32 | fn name() void { 33 | let f = (*self) as f32!; 34 | f = 100.0; 35 | return; 36 | } 37 | } 38 | ``` 39 | 40 | 此外,你还可以在函数中使用Union类型作为参数,如下所示: 41 | 42 | ```pivot 43 | fn generic(a: Option) void { 44 | let c = a as R!; 45 | a = c; 46 | return; 47 | } 48 | ``` 49 | 50 | ## Union与其子类型转化 51 | 52 | ### 隐式类型转化 53 | 54 | 隐式转化主要出现在赋值操作中。例如: 55 | 56 | ```rust 57 | let c: A = a; 58 | ``` 59 | 60 | 在这行代码中,变量 `a` 的类型是 `i128`,在赋值给 `c` 时,它被隐式转化为 `A` 类型。这是因为 `A` 类型包含了 `i128` 类型,所以 `i128` 类型可以被隐式转化为 `A` 类型。 61 | 62 | ### 显式类型转化 63 | 64 | 显式类型转化主要通过 `as` 关键字进行。例如: 65 | 66 | ```rust 67 | let h = c as i128?; 68 | ``` 69 | 70 | 在这行代码中,变量 `c` 的类型是 `A`,使用 `as` 关键字将其显式转化为 `Option` 类型。因为 `A` 类型包含了 `i128` 类型,但是她不一定是 `i128` 类型,所以 `A` 类型可以被安全的显式转化为 `Option` 类型。 71 | 72 | ### 强制类型转化 73 | 74 | 强制类型转化通过 `as ... !` 符号进行。例如: 75 | 76 | ```rust 77 | let i = c!; 78 | ``` 79 | 80 | 在这行代码中,变量 `c` 的类型是 `A`,使用 `!` 符号将其强制转化为 `i128` 类型。这是因为 `A` 类型包含了 `i128` 类型,所以 `A` 类型可以被强制转化为 `i128` 类型。 81 | -------------------------------------------------------------------------------- /book/src/systemlib/2023-01-24-23-23-55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/book/src/systemlib/2023-01-24-23-23-55.png -------------------------------------------------------------------------------- /book/src/systemlib/2023-01-24-23-25-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/book/src/systemlib/2023-01-24-23-25-06.png -------------------------------------------------------------------------------- /book/src/systemlib/README.md: -------------------------------------------------------------------------------- 1 | # SystemLib 2 | pl的系统库一部分是pl代码一部分是rust代码,rust部分在项目的`vm`目录中,pl部分在`planglib`目录中。 3 | 4 | -------------------------------------------------------------------------------- /book/src/systemlib/bdw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/book/src/systemlib/bdw.png -------------------------------------------------------------------------------- /book/src/systemlib/gc.md: -------------------------------------------------------------------------------- 1 | # GC 2 | 3 | pivot-lang 是一门使用gc进行内存管理的语言。 4 | 5 | pivot-lang 的gc目前是使用rust写的,采用一种叫做`immix`[\[1\]]的mark region算法。 6 | 7 | 在以前的版本中,我们使用的是我们现在叫做`simple gc`的垃圾回收算法,他是一个简单的mark-sweep算法。 8 | 它由于性能问题和不支持多线程等原因最终被`immix`取代。 9 | 10 | 11 | [\[1\]]: https://www.cs.utexas.edu/users/speedway/DaCapo/papers/immix-pldi-2008.pdf 12 | 13 | ## Immix GC 14 | 15 | immix gc是一种mark region算法,它是一个精确的gc算法。但是请注意,它的精确建立在使用它 16 | 的项目提供的特殊支持之上。可以认为目前我们的immix gc实现 __是为pivot-lang量身定制__ 的。pl编译器为了和我们的immix gc配合,会在编译时专门生成一些额外的代码,如果缺少这些代码,immix gc将无法正常工作。 17 | 所以虽然理论上我们可以将我们的immix gc用到其他项目中,这么做的效益很可能并不是很高-- 18 | 缺少编译器的支持,使用者将需要手动添加那些额外的代码。 19 | 20 | immix gc的实现代码在[这里](https://github.com/Chronostasys/immix)。 21 | -------------------------------------------------------------------------------- /book/src/systemlib/immix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/book/src/systemlib/immix.png -------------------------------------------------------------------------------- /book/src/systemlib/planglib.md: -------------------------------------------------------------------------------- 1 | # planglib 2 | planglib目录下的每一个文件夹都是一个系统模块,在编译的时候会自动被加入依赖中,不需要在配置文件中特殊配置。 3 | 4 | ## planglib如何在编译期间被找到 5 | plc编译器在编译时会试图寻找`KAGARI_LIB_ROOT`环境变量,并且将该变量视为`planglib`的根目录 6 | 7 | > 不设置或错误设置`KAGARI_LIB_ROOT`环境变量可能导致无法进行编译或者代码分析 8 | 9 | 如果你是plang开发者,你可以手动在`~/.bashrc`或者`~/.bash_profile`中加入以下代码: 10 | 11 | ```bash 12 | export KAGARI_LIB_ROOT=/planglib 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /book/src/systemlib/vm.md: -------------------------------------------------------------------------------- 1 | # VM 2 | 放rust写的给pivot-lang使用的函数 3 | 4 | 所有导出的函数需要加`#[is_runtime]`,所有导出结构体需要加`#[repr(C)]` 5 | 6 | 7 | ## JIT invalid memory access issue 8 | 9 | 在jit模式下使用runtime函数可能会出现`invalid memory access`错误, 10 | 这个问题本质是rust编译的时候会优化掉不使用的module,导致jit时找不到对应runtime函数。所以建议每个 11 | mod加一个叫做`reg`的函数,里边**必须用到你会使用的所有结构体**,这样在需要jit测试的时候调用使用模块的`reg`函数,对应代码就不会被优化掉了。 12 | 13 | 14 | ## 使用`is_runtime`导出rust函数 15 | 16 | 一个被`is_runtime`标记的rust函数在编译到静态库之后,在pivot-lang中声明对应的函数,即可像正常函数一样调用。例如: 17 | 18 | Rust: 19 | ```rust 20 | #[is_runtime] 21 | fn printi64ln(i: i64) { 22 | println!("{}", i); 23 | } 24 | ``` 25 | 26 | Pivot Lang: 27 | ```pivot-lang 28 | fn printi64ln(i: i64) void 29 | 30 | fn main() void { 31 | printi64ln(1) 32 | return 33 | } 34 | ``` 35 | 36 | > !!!**注意事项**:`is_runtime`标记的函数不能有modifier(比如`pub`,`unsafe`),但是被`is_runtime`标记的`impl`块中的函数不受此限制。 37 | > ```rust,ignore 38 | > struct MyStruct; 39 | > #[is_runtime("struct")] 40 | > impl MyStruct { 41 | > pub fn myfunc1() { 42 | > // ... 43 | > } 44 | > } 45 | > ``` 46 | > 标记impl块时,导出的函数名称会变为`{structname}__{fnname}`的形式,函数允许使用receiver。更多高级用法参见`is_runtime`的rust doc 47 | -------------------------------------------------------------------------------- /book/src/tutorial/2022-10-23-00-17-08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/book/src/tutorial/2022-10-23-00-17-08.png -------------------------------------------------------------------------------- /book/src/tutorial/README.md: -------------------------------------------------------------------------------- 1 | # Quick Start: A short introduction to the language 2 | 3 | > 重要:Pivot lang尚属于早期开发阶段,可能会经常发生breaking change,因此不建议在生产环境中使用。 4 | 5 | 本教程将会从安装出发,简单介绍Pivot lang的语法,以及一些基本的使用规则。 6 | -------------------------------------------------------------------------------- /book/src/tutorial/basicproject.md: -------------------------------------------------------------------------------- 1 | # 基础项目 2 | 3 | ## 两种编译模型介绍 4 | 5 | Pivot Lang存在两种不同的编译方案: 6 | 7 | - 静态编译:编译器会将源码编译成一个可执行文件,能给在操作系统上原生运行 8 | - jit编译:编译器会将源码编译成一个字节码文件,然后在运行时使用编译器动态生成可执行代码 9 | 10 | 目前这两种方案使用的编译器是同一个可执行文件(plc),然而他们在依赖和功能上存在一些差别, 11 | 下方是一个简单的对比图: 12 | 13 | | | jit | 静态编译 | 14 | | ---------------------------- | --- | -------- | 15 | | 完整的pivot lang功能支持 | ✅ | ✅ | 16 | | 生成可执行文件 | ❌ | ✅ | 17 | | 启动速度 | ❌ | ✅ | 18 | | 运行时优化 | ✅ | ❌ | 19 | | 支持debug | ❌ | ✅ | 20 | | stackmap支持 | ✅ | ✅ | 21 | 22 | ## 项目结构 23 | 24 | 一个最基础的pivot lang项目由一个配置文件和一个源文件组成。 25 | 26 | 你可以使用`plc new test`来在test 目录下生成他们。 27 | 28 | 配置文件用于指定项目的一些基本信息,源文件用于编写pivot lang代码。其结构如下: 29 | 30 | ``` 31 | test 32 | ├── Kagari.toml 33 | └── main.pi 34 | ``` 35 | 36 | ### 配置文件 37 | 38 | 一个pl项目的根目录必须有一个名为`Kagari.toml`的配置文件。示例配置文件的内容如下: 39 | 40 | ```toml 41 | {{#include example/Kagari.toml}} 42 | ``` 43 | 44 | entry指定了该项目的入口文件,即编译器将从该文件开始编译。如果缺少该配置`plc`将无法编译该项目 45 | 46 | ### 源文件 47 | 48 | 示例项目中的`main.pi`为源文件。其内容如下: 49 | 50 | ```pl 51 | {{#include example/main.pi}} 52 | ``` 53 | 54 | 源文件的后缀名必须为`.pi`。 55 | 在示例中,我们调用了一个系统库中的函数`print_s`,该函数用于打印一个字符串类型的值并换行。此源代码编译后执行会输出`Hello World`。 56 | 57 | ## 编译 58 | 59 | 如果你已经安装了`plc`,那么你可以在项目根目录下执行`plc main.pi`命令来编译该项目。此指令会生成一个名叫`out`的文件, 60 | 还有一些中间文件(在target目录下)。 61 | 62 | ```admonish tip title="如果你想尝试jit模式的话" 63 | 编译时带上`--jit`参数,如`plc main.pi --jit`,这样编译出来的文件会带有`bc`后缀,如`out.bc`。 64 | 65 | 编译后输入`plc run out.bc`可以jit运行该项目 66 | 67 | ``` 68 | -------------------------------------------------------------------------------- /book/src/tutorial/example/Kagari.toml: -------------------------------------------------------------------------------- 1 | entry = "main.pi" 2 | project = "main" -------------------------------------------------------------------------------- /book/src/tutorial/example/main.pi: -------------------------------------------------------------------------------- 1 | use std::io; 2 | pub fn main() i64 { 3 | io::print_s("Hello, world!\n"); 4 | return 0; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /book/src/tutorial/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | 4 | 5 | ## Windows 6 | 7 | windows用户可以使用[scoop](https://scoop.sh/)来安装pivot lang编译器。 8 | 9 | **请注意,windows环境下的pivot lang编译器目前只支持x64架构,而且依赖MSVC环境。 10 | 你需要安装[Visual Studio](https://visualstudio.microsoft.com/zh-hans/),并在 11 | 安装时勾选C++开发环境**。 12 | 13 | ```powershell 14 | scoop bucket add pivot https://github.com/Pivot-Studio/scoop 15 | scoop install plc 16 | ``` 17 | 18 | 19 | ## Linux 20 | 21 | 目前我们对架构为amd64的Ubuntu 20.04 LTS 和 Ubuntu 22.04 LTS提供了apt包。 22 | 首先你需要添加我们的apt源的gpg key: 23 | ```bash 24 | sudo apt update 25 | sudo apt install wget gnupg 26 | wget -O - https://pivotlang.tech/apt/public.key | sudo apt-key add - 27 | ``` 28 | 然后添加我们的apt源: 29 | ```bash 30 | sudo touch chmod +777 /etc/apt/sources.list.d/pl.list 31 | sudo chmod +777 /etc/apt/sources.list.d/pl.list 32 | sudo echo "deb [arch=amd64] https://pivotlang.tech/apt/repo focal main 33 | # deb-src [arch=amd64] https://pivotlang.tech/apt/repo focal main 34 | deb [arch=amd64] https://pivotlang.tech/apt/repo jammy main 35 | # deb-src [arch=amd64] https://pivotlang.tech/apt/repo jammy main">/etc/apt/sources.list.d/pl.list 36 | sudo apt update 37 | ``` 38 | 最后安装pivot lang编译器: 39 | ```bash 40 | sudo apt install pivot-lang 41 | ``` 42 | 你可以运行`plc`来检查是否安装成功。 43 | 44 | 安装完成后请按照提示设置环境变量 45 | 46 | 47 | ## MacOS 48 | 49 | MacOS可以使用`homebrew`进行安装。 50 | 51 | 首先你需要添加我们的homebrew tap: 52 | ```bash 53 | brew tap pivot-studio/tap 54 | ``` 55 | 56 | 然后安装pivot lang编译器: 57 | ```bash 58 | brew install pivot-lang 59 | ``` 60 | 61 | 安装完成后请按照提示设置环境变量 62 | 63 | ## Docker 64 | 65 | 66 | 使用下方命令可以启动一个docker容器,然后在容器中使用pivot lang编译器: 67 | ```bash 68 | docker run -it --rm registry.cn-hangzhou.aliyuncs.com/pivot_lang/pivot_lang:latest /bin/bash 69 | ``` 70 | -------------------------------------------------------------------------------- /book/src/tutorial/vscsupport.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Code support 2 | 3 | 我们建议开发者使用Visual Studio Code作为开发工具,因为我们提供了丰富的插件支持。 4 | 5 | 6 | ```admonish note 7 | 您可能也注意到了,我们的语言服务器可以被编译成webassembly并直接运行在浏览器中。如果你想只在在浏览器中体验 8 | pivot-lang,可以访问[https://pivotlang.tech](https://pivotlang.tech)。 9 | 10 | ``` 11 | 12 | ## vsc插件安装 13 | 在vsc插件市场搜索`pivot-lang support`,安装第一个即可 14 | ![](2022-10-23-00-17-08.png) 15 | 16 | 17 | ```admonish note 18 | pivot-lang support插件依赖于plc命令,你必须确保plc文件安装路径在环境变量`PATH`中 19 | 20 | ``` 21 | 22 | 23 | ## 支持功能 24 | - [x] vsc debug 25 | - [x] 断点 26 | - [x] 变量表 27 | - [x] 函数参数 28 | - [x] 普通变量 29 | - [x] 代码高亮 30 | - [x] lsp支持 31 | - [x] 错误提示 32 | - [x] 代码提示 33 | - [x] 普通变量 34 | - [x] 函数参数 35 | - [x] 函数 36 | - [x] 类型 37 | - [x] 模块 38 | - [x] 代码跳转 39 | - [x] 普通变量 40 | - [x] 函数参数 41 | - [x] 函数 42 | - [x] 类型 43 | - [x] 模块 44 | - [x] 引用查找 45 | - [x] 普通变量 46 | - [x] 函数参数 47 | - [x] 函数 48 | - [x] 类型 49 | - [x] 语法高亮 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use vergen::EmitBuilder; 2 | 3 | pub fn main() { 4 | // NOTE: This will output everything, and requires all features enabled. 5 | // NOTE: See the EmitBuilder documentation for configuration options. 6 | EmitBuilder::builder() 7 | .all_build() 8 | .all_cargo() 9 | .all_git() 10 | .all_rustc() 11 | .all_sysinfo() 12 | .emit() 13 | .unwrap(); 14 | } 15 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: 85% 6 | threshold: 1% 7 | patch: 8 | default: 9 | # basic 10 | target: 50% 11 | threshold: 1% 12 | ignore: 13 | - "src/lsp/mod.rs" 14 | - "src/lsp/helpers.rs" 15 | - "src/lsp/dispatcher.rs" 16 | - "src/main.rs" 17 | - "src/lsp/lspserver.rs" 18 | - "src/lsp/wasm.rs" 19 | - "src/lsp/wasm.rs" 20 | - "src/repl/editor.rs" 21 | - "src/repl/*.rs" 22 | -------------------------------------------------------------------------------- /deb/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | GREEN='\033[0;32m' 2 | NC='\033[0m' 3 | RED='\033[0;31m' 4 | 5 | if [ "$1" = configure ]; then 6 | chmod +rw -R /pl 7 | echo "${GREEN}" 8 | echo "-------------------------------------------------------------------" 9 | echo "Successfully installed pivot-lang compiler!" 10 | echo "the pivot-lang compiler is now available as 'plc' command" 11 | echo "${RED}在开始使用plc之前,您需要设定两个环境变量" 12 | echo "请使用以下命令在当前终端中配置环境变量:" 13 | echo " $ export KAGARI_LIB_ROOT=/pl/planglib" 14 | echo " $ export PL_ROOT=/pl" 15 | echo "如果您希望以后新创建的终端能直接使用plc,请将以上两行命令添加到~/.bashrc文件中" 16 | echo "-------------------------------------------------------------------" 17 | echo "${NC}" 18 | fi -------------------------------------------------------------------------------- /deb/apt.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: apt-repo 6 | name: apt-repo 7 | namespace: lang 8 | spec: 9 | ports: 10 | - name: "8110" 11 | port: 8110 12 | targetPort: 8110 13 | selector: 14 | app: apt-repo 15 | status: 16 | loadBalancer: {} 17 | 18 | --- 19 | apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | labels: 23 | app: apt-repo 24 | name: apt-repo 25 | namespace: lang 26 | spec: 27 | replicas: 1 28 | selector: 29 | matchLabels: 30 | app: apt-repo 31 | strategy: 32 | type: RollingUpdate 33 | template: 34 | metadata: 35 | labels: 36 | app: apt-repo 37 | spec: 38 | priorityClassName: high-priority 39 | containers: 40 | - image: registry.cn-hangzhou.aliyuncs.com/pivotstudio/lang-apt:{{.build_tag}} 41 | name: apt-repo 42 | ports: 43 | - containerPort: 8110 44 | resources: 45 | limits: 46 | cpu: 100m 47 | memory: 100Mi 48 | requests: 49 | cpu: 100m 50 | memory: 50Mi 51 | restartPolicy: Always 52 | status: {} 53 | 54 | -------------------------------------------------------------------------------- /imgs/2024-02-21-11-46-55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/imgs/2024-02-21-11-46-55.png -------------------------------------------------------------------------------- /imgs/2024-02-21-11-50-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/imgs/2024-02-21-11-50-11.png -------------------------------------------------------------------------------- /imgs/2024-02-21-11-50-25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/imgs/2024-02-21-11-50-25.png -------------------------------------------------------------------------------- /internal_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "internal_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | log = "0.4" 10 | lazy_static = "1.4" 11 | 12 | llvm-sys = { version = "180", optional = true } 13 | [dependencies.add_symbol_macro] 14 | path = "src/add_symbol_macro" 15 | [dependencies.range_macro] 16 | path = "src/range_macro" 17 | [dependencies.fmt_macro] 18 | path = "src/fmt_macro" 19 | [dependencies.comment_macro] 20 | path = "src/comment_macro" 21 | [dependencies.test_parser_macro] 22 | path = "src/test_parser_macro" 23 | [dependencies.node_macro] 24 | path = "src/node_macro" 25 | 26 | [features] 27 | default = ["jit"] 28 | jit = ["llvm-sys"] 29 | static = [] -------------------------------------------------------------------------------- /internal_macro/README.md: -------------------------------------------------------------------------------- 1 | # internal macro 2 | 3 | 内部使用的proc macro库 4 | 5 | 6 | ## 内容 7 | 8 | - [add_symbol_macro](src/add_symbol_macro) 用于导出rust函数给pivot-lang使用的宏(jit或静态编译皆可) 9 | - [range_macro](src/range_macro/) 用于生成range的宏,用于帮助ast映射源码位置 10 | - [test_parser_macro](src/test_parser_macro/) 用于测试parser的宏 11 | -------------------------------------------------------------------------------- /internal_macro/src/add_symbol_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "add_symbol_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | syn = {version = "1.0",features = ["full"]} 10 | quote = "1.0" 11 | 12 | 13 | [lib] 14 | proc-macro = true 15 | -------------------------------------------------------------------------------- /internal_macro/src/comment_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "comment_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | syn = {version = "1.0",features = ["full"]} 10 | quote = "1.0" 11 | 12 | [lib] 13 | proc-macro = true 14 | -------------------------------------------------------------------------------- /internal_macro/src/comment_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::{parse::Parser, parse_macro_input, DeriveInput}; 4 | #[proc_macro_attribute] 5 | pub fn comments(_args: TokenStream, input: TokenStream) -> TokenStream { 6 | let mut ast = parse_macro_input!(input as DeriveInput); 7 | if let syn::Data::Struct(ref mut struct_data) = &mut ast.data { 8 | if let syn::Fields::Named(fields) = &mut struct_data.fields { 9 | fields.named.push( 10 | syn::Field::parse_named 11 | .parse2(quote! { pub comments : Vec>> }) 12 | .unwrap(), 13 | ); 14 | } 15 | quote! { 16 | #ast 17 | } 18 | .into() 19 | } else { 20 | panic!("`range` has to be used with structs ") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /internal_macro/src/fmt_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fmt_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | syn = {version = "1.0",features = ["full"]} 10 | quote = "1.0" 11 | 12 | [lib] 13 | proc-macro = true 14 | -------------------------------------------------------------------------------- /internal_macro/src/fmt_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | #[proc_macro_attribute] 5 | pub fn fmt(_args: TokenStream, input: TokenStream) -> TokenStream { 6 | let mut ast = parse_macro_input!(input as DeriveInput); 7 | match &mut ast.data { 8 | syn::Data::Struct(_) => { 9 | let ident = &ast.ident; 10 | let fnid = format_ident!("{}", to_snake(format_ident!("parse{}", ident).to_string())); 11 | quote! { 12 | #ast 13 | impl crate::ast::node::FmtTrait for #ident { 14 | fn format(&self, builder: &mut crate::ast::fmt::FmtBuilder) { 15 | builder.#fnid(self); 16 | } 17 | } 18 | } 19 | .into() 20 | } 21 | _ => panic!("`format` has to be used with structs "), 22 | } 23 | } 24 | 25 | fn to_snake(id: String) -> String { 26 | let mut res = String::new(); 27 | let mut last = '_'; 28 | for c in id.chars() { 29 | if c.is_uppercase() { 30 | if last != '_' { 31 | res.push('_'); 32 | } 33 | res.push(c.to_ascii_lowercase()); 34 | } else { 35 | res.push(c); 36 | } 37 | last = c; 38 | } 39 | res 40 | } 41 | -------------------------------------------------------------------------------- /internal_macro/src/node_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "node_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | syn = {version = "1.0",features = ["full"]} 10 | quote = "1.0" 11 | 12 | [lib] 13 | proc-macro = true -------------------------------------------------------------------------------- /internal_macro/src/range_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "range_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | syn = {version = "1.0",features = ["full"]} 10 | quote = "1.0" 11 | 12 | [lib] 13 | proc-macro = true 14 | -------------------------------------------------------------------------------- /internal_macro/src/range_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::{parse::Parser, parse_macro_input, DeriveInput}; 4 | #[proc_macro_attribute] 5 | pub fn range(_args: TokenStream, input: TokenStream) -> TokenStream { 6 | let mut ast = parse_macro_input!(input as DeriveInput); 7 | match &mut ast.data { 8 | syn::Data::Struct(ref mut struct_data) => { 9 | if let syn::Fields::Named(fields) = &mut struct_data.fields { 10 | fields.named.push( 11 | syn::Field::parse_named 12 | .parse2(quote! { pub range : crate::ast::range::Range }) 13 | .unwrap(), 14 | ); 15 | } 16 | let ident = &ast.ident; 17 | quote! { 18 | #ast 19 | impl crate::ast::node::RangeTrait for #ident { 20 | fn range(&self) -> crate::ast::range::Range { 21 | self.range 22 | } 23 | } 24 | } 25 | .into() 26 | } 27 | _ => panic!("`range` has to be used with structs "), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /internal_macro/src/test_parser_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_parser_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | syn = {version = "1.0",features = ["full"]} 10 | quote = "1.0" 11 | 12 | [lib] 13 | proc-macro = true 14 | -------------------------------------------------------------------------------- /internal_macro/src/test_parser_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use std::{ 4 | collections::hash_map::DefaultHasher, 5 | hash::{Hash, Hasher}, 6 | }; 7 | use syn::{parse_macro_input, ItemFn}; 8 | #[proc_macro_attribute] 9 | pub fn test_parser(args: TokenStream, input: TokenStream) -> TokenStream { 10 | let ast = parse_macro_input!(input as ItemFn); 11 | let args = parse_macro_input!(args as syn::LitStr); 12 | let mut hasher = DefaultHasher::new(); 13 | args.token().to_string().hash(&mut hasher); 14 | let test_fn = format_ident!( 15 | "parser_test_err_{}_{}", 16 | ast.sig.ident.to_string(), 17 | hasher.finish() 18 | ); 19 | let original_fn = format_ident!("{}", ast.sig.ident.to_string()); 20 | quote! { 21 | #ast 22 | #[test] 23 | fn #test_fn() { 24 | let arg = #args; 25 | let span = Span::from(arg); 26 | match #original_fn(span) { 27 | Err(e) => panic!("{:?}",e), 28 | Ok(span) => { 29 | if (span.0.len()!=0) { 30 | panic!("span is not empty get {:?}",span.0); 31 | } 32 | }, 33 | } 34 | } 35 | } 36 | .into() 37 | } 38 | #[proc_macro_attribute] 39 | pub fn test_parser_error(args: TokenStream, input: TokenStream) -> TokenStream { 40 | let ast = parse_macro_input!(input as ItemFn); 41 | let args = parse_macro_input!(args as syn::LitStr); 42 | let mut hasher = DefaultHasher::new(); 43 | args.token().to_string().hash(&mut hasher); 44 | let test_fn = format_ident!( 45 | "parser_test_err_{}_{}", 46 | ast.sig.ident.to_string(), 47 | hasher.finish() 48 | ); 49 | let original_fn = format_ident!("{}", ast.sig.ident.to_string()); 50 | quote! { 51 | #ast 52 | #[test] 53 | fn #test_fn() { 54 | let arg = #args; 55 | let span = Span::from(arg); 56 | if let Ok(span) = #original_fn(span) { 57 | if (span.0.len()==0) { 58 | panic!("expected err but get ok , input is {:?}",arg); 59 | } 60 | } 61 | } 62 | } 63 | .into() 64 | } 65 | -------------------------------------------------------------------------------- /kagari/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kagari" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | fs_extra = "1.3.0" 10 | 11 | 12 | [lib] 13 | path = "src/lib.rs" 14 | -------------------------------------------------------------------------------- /kagari/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | path::{Path, PathBuf}, 3 | process::{Command, Output}, 4 | }; 5 | 6 | pub fn download_repo( 7 | repo_url: &str, 8 | target_dir: &str, 9 | ) -> (Option>, PathBuf) { 10 | let branch = "default"; 11 | let sub_dir = repo_url 12 | .strip_prefix("http://") 13 | .or_else(|| repo_url.strip_prefix("https://")) 14 | .expect("Invalid repo url") 15 | .trim_end_matches(".git"); 16 | let target_dir = Path::new(target_dir).join(sub_dir).join(branch); 17 | if !target_dir.exists() { 18 | std::fs::create_dir_all(&target_dir).expect("Failed to create target directory"); 19 | } else { 20 | return ( 21 | Some( 22 | Command::new("git") 23 | .arg("pull") 24 | .stdout(std::process::Stdio::null()) 25 | .stderr(std::process::Stdio::null()) 26 | .current_dir(target_dir.clone()) 27 | .spawn() 28 | .expect("git pull failed") 29 | .wait_with_output(), 30 | ), 31 | target_dir, 32 | ); 33 | } 34 | ( 35 | Some( 36 | Command::new("git") 37 | .arg("clone") 38 | .arg(repo_url) 39 | .arg(".") 40 | .stdout(std::process::Stdio::null()) 41 | .stderr(std::process::Stdio::null()) 42 | .current_dir(target_dir.clone()) 43 | .spawn() 44 | .expect("git clone failed") 45 | .wait_with_output(), 46 | ), 47 | target_dir, 48 | ) 49 | } 50 | 51 | pub fn cp_to_hash_dir(repo_dir: &str, head: &str) -> PathBuf { 52 | // checkout to head 53 | _ = Command::new("git") 54 | .current_dir(repo_dir) 55 | .arg("checkout") 56 | .arg(head) 57 | .output() 58 | .unwrap(); 59 | let re = Command::new("git") 60 | .current_dir(repo_dir) 61 | .arg("rev-parse") 62 | .arg(head) 63 | .output() 64 | .unwrap(); 65 | let hash = std::str::from_utf8(&re.stdout); 66 | let path = Path::new(repo_dir) 67 | .parent() 68 | .unwrap() 69 | .join(hash.unwrap().trim()); 70 | if !path.exists() { 71 | std::fs::create_dir_all(&path).expect("Failed to create target directory"); 72 | // copy repo_dir to hash_dir using std lib 73 | fs_extra::dir::copy( 74 | repo_dir, 75 | path.clone(), 76 | &fs_extra::dir::CopyOptions::new().content_only(true), 77 | ) 78 | .unwrap(); 79 | } 80 | path 81 | } 82 | -------------------------------------------------------------------------------- /kagari/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /naive_coro.md: -------------------------------------------------------------------------------- 1 | # 单线程协程调度器 2 | 3 | * 每次切换协程时需要保存当前协程的栈给gc,gc有一个表key是协程的bp指针,value是协程的stackmap 4 | * 协程要退出的时候,需要把自己的栈从gc中删除 5 | * 每次go其实都是将任务加入到调度器的任务队列中 6 | * 主线程运行调度器逻辑,调度器一直试图执行(resume)对列里的协程,直到队列为空 7 | 8 | 9 | resume 返回闭包 -------------------------------------------------------------------------------- /pl_linker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pl_linker" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | lld_rs = { version = "180.0.0", default-features = false, git="https://github.com/Pivot-Studio/lld-rs.git", branch="main" } 10 | thiserror = "1.0.38" 11 | mun_target = { git="https://github.com/mun-lang/mun.git" } 12 | once_cell = { version = "1.4.0" } 13 | parking_lot = { version = "0.12.0", default-features = false } 14 | 15 | 16 | [lib] 17 | name = "pl_linker" 18 | path = "src/lib.rs" 19 | -------------------------------------------------------------------------------- /pl_linker/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | #[cfg(target_os = "macos")] 3 | println!("cargo:rustc-link-lib=dylib=xar"); 4 | } 5 | -------------------------------------------------------------------------------- /pl_linker/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod apple; 2 | pub mod linker; 3 | pub extern crate mun_target; 4 | -------------------------------------------------------------------------------- /planglib/core/Kagari.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/planglib/core/Kagari.lock -------------------------------------------------------------------------------- /planglib/core/Kagari.toml: -------------------------------------------------------------------------------- 1 | project = "core" 2 | entry = "__private.pi" 3 | -------------------------------------------------------------------------------- /planglib/core/__private.pi: -------------------------------------------------------------------------------- 1 | use core::gc; 2 | use core::panic; 3 | use core::builtin; 4 | use core::hash::pl_hash; 5 | use core::hash::hasher; 6 | use core::hash; 7 | use core::eq; 8 | use core::coro; 9 | use core::process; 10 | use core::ord; 11 | use core::dtoa; 12 | use core::string; 13 | // use core::cols::hashtable; 14 | -------------------------------------------------------------------------------- /planglib/core/builtin.pi: -------------------------------------------------------------------------------- 1 | pub struct None { 2 | } 3 | pub type Option = T|None; 4 | 5 | 6 | impl Option { 7 | pub fn map(f:|T|=>R) Option { 8 | if (*self) is None { 9 | return None{}; 10 | } 11 | let v = (*self) as T!; 12 | return f(v); 13 | } 14 | pub fn flatmap(f:|T|=>Option) Option { 15 | if (*self) is None { 16 | return None{}; 17 | } 18 | let v = (*self) as T!; 19 | return f(v); 20 | } 21 | } 22 | pub use core::string::ToString; 23 | impl ToString for Option { 24 | fn to_string() gc::string { 25 | let v = *self; 26 | if v is None { 27 | return "None"; 28 | } else { 29 | let ret = "Some("; 30 | ret.append((v as T!).to_string()); 31 | ret.append(")"); 32 | return ret; 33 | } 34 | } 35 | } 36 | 37 | pub use core::gc::string; 38 | pub use core::gc::string_from_bytes; 39 | pub use core::ord::Ord; 40 | 41 | pub type Result = T|E; 42 | 43 | 44 | impl Result { 45 | pub fn map(f:|T|=>R) Result { 46 | if (*self) is E { 47 | return (*self) as E!; 48 | } 49 | let v = (*self) as T!; 50 | return f(v); 51 | } 52 | 53 | pub fn flatmap(f:|T|=>Result) Result { 54 | if (*self) is E { 55 | return (*self) as E!; 56 | } 57 | let v = (*self) as T!; 58 | return f(v); 59 | } 60 | 61 | pub fn map_err(f:|E|=>R) Result { 62 | if (*self) is T { 63 | return (*self) as T!; 64 | } 65 | let v = (*self) as E!; 66 | return f(v); 67 | } 68 | 69 | pub fn flatmap_err(f:|E|=>Result) Result { 70 | if (*self) is T { 71 | return (*self) as T!; 72 | } 73 | let v = (*self) as E!; 74 | return f(v); 75 | } 76 | } 77 | 78 | impl ToString for Result { 79 | fn to_string() gc::string { 80 | let v = *self; 81 | if v is E { 82 | let ret = "Err("; 83 | ret.append((v as E!).to_string()); 84 | ret.append(")"); 85 | return ret; 86 | } else { 87 | let ret = "Ok("; 88 | ret.append((v as T!).to_string()); 89 | ret.append(")"); 90 | return ret; 91 | } 92 | } 93 | } 94 | 95 | pub use core::hash::Hash; 96 | pub use core::eq::Eq; 97 | -------------------------------------------------------------------------------- /planglib/core/dtoa.pi: -------------------------------------------------------------------------------- 1 | fn vm_dtoa(f: f64, rec: *u8)void; 2 | fn vm_ftoa(f: f32, rec: *u8)void; 3 | 4 | pub fn dtoa(f: f64) gc::string { 5 | let bs = [u8*80;]; 6 | vm_dtoa(f, &bs[0]); 7 | return gc::string_from_bytes(bs); 8 | } 9 | 10 | pub fn ftoa(f: f32) gc::string { 11 | let bs = [u8*64;]; 12 | vm_ftoa(f, &bs[0]); 13 | return gc::string_from_bytes(bs); 14 | } -------------------------------------------------------------------------------- /planglib/core/eq.pi: -------------------------------------------------------------------------------- 1 | pub trait Eq { 2 | fn eq(r:*S) bool; 3 | } 4 | 5 | 6 | impl Eq for gc::string { 7 | fn eq(r:*gc::string) bool { 8 | return gc::string_eq(*self, *r); 9 | } 10 | } 11 | 12 | 13 | 14 | impl > Eq<[T]> for [T] { 15 | fn eq(r:*[T]) bool { 16 | if arr_len(*self) != arr_len(*r) { 17 | return false; 18 | } 19 | for let i = 0; i < arr_len(*self); i = i + 1 { 20 | if !(*self)[i].eq(&(*r)[i]) { 21 | return false; 22 | } 23 | } 24 | return true; 25 | } 26 | } 27 | 28 | 29 | impl Eq for i128 { 30 | fn eq(r:*i128) bool { 31 | return *r == *self; 32 | } 33 | } 34 | 35 | 36 | impl Eq for i64 { 37 | fn eq(r:*i64) bool { 38 | return *r == *self; 39 | } 40 | } 41 | 42 | impl Eq for i32 { 43 | fn eq(r:*i32) bool { 44 | return *r == *self; 45 | } 46 | } 47 | 48 | 49 | impl Eq for i16 { 50 | fn eq(r:*i16) bool { 51 | return *r == *self; 52 | } 53 | } 54 | 55 | 56 | impl Eq for i8 { 57 | fn eq(r:*i8) bool { 58 | return *r == *self; 59 | } 60 | } 61 | 62 | 63 | impl Eq for u128 { 64 | fn eq(r:*u128) bool { 65 | return *r == *self; 66 | } 67 | } 68 | 69 | 70 | impl Eq for u64 { 71 | fn eq(r:*u64) bool { 72 | return *r == *self; 73 | } 74 | } 75 | 76 | impl Eq for u32 { 77 | fn eq(r:*u32) bool { 78 | return *r == *self; 79 | } 80 | } 81 | 82 | 83 | impl Eq for u16 { 84 | fn eq(r:*u16) bool { 85 | return *r == *self; 86 | } 87 | } 88 | 89 | 90 | impl Eq for u8 { 91 | fn eq(r:*u8) bool { 92 | return *r == *self; 93 | } 94 | } 95 | 96 | impl Eq for char { 97 | fn eq(r:*char) bool { 98 | return *r == *self; 99 | } 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /planglib/core/hash/hasher.pi: -------------------------------------------------------------------------------- 1 | 2 | pub trait Hasher64 { 3 | fn append(data:[u8]) void; 4 | fn finish() u64; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /planglib/core/hash/pl_hash.pi: -------------------------------------------------------------------------------- 1 | use core::hash::hasher::Hasher64; 2 | 3 | /// Paul Larson hash function 4 | /// https://stackoverflow.com/questions/628790/have-a-good-hash-function-for-a-c-hash-table 5 | /// 6 | /// # Arguments 7 | /// 8 | /// * `data` - data to hash 9 | /// * `salt` - salt to use, should be a random number, used to prevent hash collision attacks 10 | fn hash(data:[u8], salt:u64) u64 { 11 | let hash = salt; 12 | 13 | for let i = 0; i < arr_len(data); i = i + 1 { 14 | hash = hash*101 + data[i] as u64; 15 | } 16 | return hash; 17 | } 18 | 19 | 20 | 21 | pub struct PaulLarsonHasher { 22 | hash: u64; 23 | } 24 | 25 | 26 | impl Hasher64 for PaulLarsonHasher { 27 | fn finish() u64 { 28 | return self.hash; 29 | } 30 | 31 | fn append(bytes: [u8]) void { 32 | self.hash = hash(bytes, self.hash); 33 | return; 34 | } 35 | } 36 | 37 | pub fn new_paul_larson_hasher(salt:u64) PaulLarsonHasher { 38 | return PaulLarsonHasher{hash:salt}; 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /planglib/core/panic.pi: -------------------------------------------------------------------------------- 1 | /// # pl_panic 2 | /// This function will print a backtrace 3 | /// and exit the current process immediatly with code 1 . 4 | /// the backtrace may not work in jit mod 5 | /// 6 | /// The implementation of this function is in the vm crate, 7 | /// using `libunwind`. So linking to `libunwind` is required during 8 | /// static linking. 9 | /// 10 | /// In the future, the implementation of this function may be changed, 11 | /// to remove the dependency on `libunwind`. 12 | pub fn pl_panic() void; 13 | 14 | pub fn assert(b: bool) void { 15 | if !b { 16 | pl_panic(); 17 | } 18 | return; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /planglib/core/process.pi: -------------------------------------------------------------------------------- 1 | pub fn exit_now(code:i64) void; -------------------------------------------------------------------------------- /planglib/std/Kagari.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/planglib/std/Kagari.lock -------------------------------------------------------------------------------- /planglib/std/Kagari.toml: -------------------------------------------------------------------------------- 1 | project = "std" 2 | entry = "__private.pi" 3 | -------------------------------------------------------------------------------- /planglib/std/__private.pi: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::stdbuiltin; 3 | use std::iter; 4 | use std::cols::arr; 5 | use std::future; 6 | use std::chan; 7 | use std::mutex; 8 | use std::cols::hashtable; 9 | use std::libc; 10 | use std::buf; 11 | use std::err; 12 | use std::thread; 13 | use std::rand; 14 | use std::userrand; 15 | use std::math; 16 | use std::time; 17 | use std::string; 18 | use std::slice; 19 | use std::task; 20 | use std::libuv; 21 | use std::task::reactor; 22 | use std::task::executor; 23 | use std::task::helper; 24 | use std::task::delay; 25 | use std::task::tcp; 26 | use std::task::udp; 27 | use std::task::http; 28 | use std::task::dns; 29 | use std::json::encode; 30 | -------------------------------------------------------------------------------- /planglib/std/buf.pi: -------------------------------------------------------------------------------- 1 | use std::libc::*; 2 | 3 | 4 | 5 | /// A buffer of bytes. 6 | struct Buffer { 7 | data:[u8]; 8 | len:u64; 9 | } 10 | 11 | impl Buffer { 12 | /// # cap 13 | /// Return the capacity of the buffer. 14 | pub fn cap() u64 { 15 | let cap = arr_len(self.data); 16 | return cap as u64; 17 | } 18 | /// Grow the buffer to the given size. 19 | pub fn grow(size:u64) void { 20 | // if the buffer is already big enough, do nothing 21 | if self.cap() >= size { 22 | return; 23 | } 24 | // otehrwise malloc a new buffer 25 | let new_data = [u8*size as i64;]; 26 | // copy the old data into the new buffer 27 | arr_copy(self.data, new_data, self.len as i64); 28 | // change the buffer to point to the new data 29 | self.data = new_data; 30 | return; 31 | } 32 | 33 | /// write the given data to the buffer. 34 | pub fn write(data:*u8, len:u64) void { 35 | if len == 0 { 36 | return; 37 | } 38 | // grow the buffer to fit the new data 39 | self.grow(self.len + len); 40 | // copy the new data into the buffer 41 | let ptr = &self.data[self.len as i64 - 1]; 42 | let offset_len = arr_len(self.data) - self.len as i64; 43 | let offset_arr = arr_from_raw(ptr, offset_len); 44 | let src_arr = arr_from_raw(data, len as i64); 45 | arr_copy(src_arr, offset_arr, len as i64); 46 | // update the length of the buffer 47 | self.len = self.len + len; 48 | return; 49 | } 50 | 51 | /// read the given number of bytes from the buffer. 52 | pub fn read(len:u64) Result<[u8]| InvalidReadRangeErr> { 53 | if len == 0 { 54 | return [u8*0;]; 55 | } 56 | // if the buffer is too small, return nothing 57 | if self.len < len { 58 | return InvalidReadRangeErr{}; 59 | } 60 | // otherwise, copy the data out of the buffer 61 | 62 | let dst_arr:[u8] = [u8*len as i64;]; 63 | arr_copy(self.data, dst_arr, len as i64); 64 | // return the data 65 | return dst_arr; 66 | } 67 | pub fn raw_ptr() *u8 { 68 | return &self.data[0]; 69 | } 70 | pub fn set_len(len:u64) void { 71 | self.len = len; 72 | return; 73 | } 74 | } 75 | 76 | use std::err::Error; 77 | 78 | pub struct InvalidReadRangeErr { 79 | } 80 | 81 | 82 | 83 | impl Error for InvalidReadRangeErr { 84 | fn msg() string { 85 | return "Invalid read range"; 86 | } 87 | } 88 | 89 | 90 | 91 | pub fn new_buffer(size:i64) Buffer { 92 | return Buffer{data:[u8*size;]}; 93 | } 94 | -------------------------------------------------------------------------------- /planglib/std/err.pi: -------------------------------------------------------------------------------- 1 | pub trait Error { 2 | fn msg() string; 3 | } 4 | -------------------------------------------------------------------------------- /planglib/std/future.pi: -------------------------------------------------------------------------------- 1 | pub use std::future::primitives::*; 2 | pub use std::future::executor::*; 3 | pub use std::future::delay::*; 4 | -------------------------------------------------------------------------------- /planglib/std/future/delay.pi: -------------------------------------------------------------------------------- 1 | use std::future::primitives::*; 2 | use std::thread; 3 | 4 | pub struct Delay { 5 | sec:u64; 6 | start:u64; 7 | } 8 | 9 | fn unixtime() u64; 10 | 11 | pub fn delay(sec:u64) Delay { 12 | return Delay{sec:sec, start:unixtime()}; 13 | } 14 | 15 | use std::io; 16 | 17 | impl Future<()> for Delay{ 18 | fn poll(wk:Waker) Poll<()> { 19 | if unixtime() - self.start >= self.sec { 20 | return Ready<()>{}; 21 | } 22 | thread::spawn(||=>{ 23 | let sec = self.sec - (unixtime() - self.start); 24 | if sec <= 0 { 25 | wk.wake(); 26 | return; 27 | } 28 | thread::sleep(sec); 29 | wk.wake(); 30 | return; 31 | }); 32 | return Pending{}; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /planglib/std/future/executor.pi: -------------------------------------------------------------------------------- 1 | use std::future::primitives::*; 2 | use std::chan; 3 | 4 | struct Executor { 5 | pub ch:*chan::Chan<|Waker|=>void>; 6 | } 7 | 8 | 9 | pub fn new_executor() Executor { 10 | 11 | 12 | return Executor { 13 | ch: &chan::channel<|Waker|=>void>(10 as u64), 14 | }; 15 | } 16 | 17 | use std::io; 18 | 19 | impl Executor { 20 | pub fn start_exec_loop() void { 21 | while true { 22 | let work: |Waker|=>void = self.ch.recv(); 23 | let waker = Waker{ 24 | wake: || => void { 25 | self.ch.send(work); 26 | return; 27 | }, 28 | }; 29 | work(waker); 30 | } 31 | return; 32 | } 33 | 34 | pub fn spawn(future:Future) void { 35 | self.ch.send(|w|=>{ 36 | future.poll(w); 37 | return; 38 | }); 39 | return; 40 | } 41 | } 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /planglib/std/future/primitives.pi: -------------------------------------------------------------------------------- 1 | use std::chan; 2 | pub struct Waker { 3 | pub wake:||=>void; 4 | } 5 | 6 | pub struct Pending {} 7 | 8 | pub struct Ready { 9 | v: T; 10 | } 11 | 12 | pub type Poll = Ready | Pending; 13 | pub trait Future { 14 | fn poll(wk: Waker) Poll; 15 | } 16 | 17 | 18 | pub trait FutureExt { 19 | fn continue_with(f:|T| => Future) Future; 20 | } 21 | 22 | 23 | pub struct FnFuture { 24 | f:|Waker|=>Poll; 25 | re:Poll; 26 | } 27 | 28 | impl Future for FnFuture { 29 | fn poll(wk: Waker) Poll { 30 | if self.re is Ready { 31 | return self.re; 32 | } 33 | let re = self.f(wk); 34 | self.re = re; 35 | return re; 36 | } 37 | } 38 | 39 | use std::io; 40 | 41 | 42 | impl FutureExt for Future { 43 | fn continue_with(f:|T|=>Future) Future { 44 | let re = Pending{} as Poll; 45 | let ff = FnFuture{}; 46 | ff.re = re; 47 | ff.f = |wk:Waker|=> { 48 | let re = self.poll(Waker{wake:||=>{ 49 | let re = self.poll(Waker{}) as Ready!; 50 | let re1 = f(re.v); 51 | let result = re1.poll(wk); 52 | ff.re = result; 53 | if result is Ready{ 54 | wk.wake(); 55 | } 56 | return; 57 | }}); 58 | if re is Ready { 59 | let re = f((re as Ready!).v); 60 | let result = re.poll(wk); 61 | ff.re = result; 62 | return result; 63 | } 64 | return Pending{} as Poll; 65 | }; 66 | return ff as Future; 67 | } 68 | } 69 | 70 | pub fn nothing_future() Future<()> { 71 | 72 | return FnFuture<()>{f:|_wk:Waker|=>{ 73 | return Ready<()>{} as Poll<()>; 74 | }, re:Ready<()>{} as Poll<()>}; 75 | } 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /planglib/std/iter.pi: -------------------------------------------------------------------------------- 1 | pub trait Iterator { 2 | fn next() Option; 3 | } 4 | 5 | 6 | pub trait IntoIter { 7 | fn into_iter() Iterator; 8 | } 9 | 10 | 11 | impl IntoIter for [T] { 12 | gen fn into_iter() Iterator { 13 | let l = arr_len(*self); 14 | for let i = 0; i < l; i = i + 1 { 15 | let x = (*self)[i]; 16 | yield return x; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /planglib/std/libc.pi: -------------------------------------------------------------------------------- 1 | pub fn LibC__read(fd:i32,buf:*u8, size:u64) i64; 2 | pub fn LibC__write(fd:i32, buf:*u8, size:u64) i64; 3 | pub fn LibC__open(path:*u8, byte_len:i64, flags:i32) i32; 4 | pub fn LibC__close(fd:i32) i32; 5 | 6 | 7 | const STDIN_FILENO:i32; 8 | const STDOUT_FILENO:i32; 9 | const STDERR_FILENO:i32; 10 | 11 | const O_RDONLY:i32; 12 | const O_WRONLY:i32; 13 | const O_RDWR:i32; 14 | const O_CREAT:i32; 15 | 16 | 17 | 18 | pub fn pl_clock_gettime(sec:*i64, nsec:*u32) void; 19 | 20 | 21 | pub fn LibC__memcpy(dest:*u8, src:*u8, n:u64) *u8; 22 | 23 | /// `buf`: `*u8` - buffer to fill with random data 24 | /// `buflen`: `u64` - length of buffer(in bytes) 25 | /// `flags`: `u32` - flags to pass to getrandom syscall 26 | /// - `0`: `GRND_RANDOM` - use /dev/random 27 | /// - `1`: `GRND_NONBLOCK` - return EAGAIN if not enough entropy 28 | pub fn LibC__getrandom(buf:*u8, buflen:u64, flags:u32) i64; 29 | 30 | use std::libuv::*; 31 | 32 | pub fn LibC__addrinfo_ai_addr(ai:*addrinfo) *u8; 33 | -------------------------------------------------------------------------------- /planglib/std/mutex.pi: -------------------------------------------------------------------------------- 1 | pub struct MutexHandle{} 2 | 3 | 4 | pub struct Mutex{ 5 | handle: **MutexHandle; 6 | } 7 | 8 | pub fn create_mutex(mutex: **MutexHandle) u64; 9 | 10 | pub fn lock_mutex(mutex: *MutexHandle) u64; 11 | 12 | pub fn new_mutex() Mutex{ 13 | let m = Mutex{}; 14 | m.handle = &&MutexHandle{}; 15 | create_mutex(m.handle); 16 | return m; 17 | } 18 | 19 | impl Mutex{ 20 | pub fn lock() u64{ 21 | return lock_mutex(*self.handle); 22 | } 23 | 24 | pub fn unlock() u64{ 25 | return unlock_mutex(*self.handle); 26 | } 27 | } 28 | 29 | 30 | pub fn unlock_mutex(mutex: *MutexHandle) u64; 31 | 32 | 33 | pub struct CondVar{} 34 | 35 | pub fn create_condvar(condvar: **CondVar) u64; 36 | 37 | pub fn condvar_wait(condvar: *CondVar, mutex: *MutexHandle) u64; 38 | 39 | pub fn condvar_notify(condvar: *CondVar) u64; 40 | 41 | pub fn condvar_notify_all(condvar: *CondVar) u64; 42 | 43 | pub fn drop_condvar(condvar: *CondVar) u64; -------------------------------------------------------------------------------- /planglib/std/rand.pi: -------------------------------------------------------------------------------- 1 | use std::libc::*; 2 | use core::panic; 3 | pub fn randi64() i64 { 4 | let ret = 0; 5 | let ptr = unsafe_cast(&ret); 6 | // 生成的随机数存储在ptr,8 bytes,使用/dev/random 7 | // 0阻塞,1非阻塞 8 | LibC__getrandom(ptr, 8 as u64, 0 as u32); 9 | return ret; 10 | } 11 | 12 | /// 生成 `[0, max)` 的随机数,`max` 必须大于 `0`,否则 `panic` 13 | pub fn randi64n(max: i64) i64 { 14 | panic::assert(max > 0); 15 | let ret = (randi64() as u64) % (max as u64); 16 | return ret as i64; 17 | } 18 | 19 | /// 生成 `[min, max)` 的随机数,`min` 必须小于 `max`,否则 `panic` 20 | pub fn randi64r(min: i64, max: i64) i64 { 21 | panic::assert(min < max); 22 | let ret = randi64n(max - min) + min; 23 | return ret; 24 | } -------------------------------------------------------------------------------- /planglib/std/task.pi: -------------------------------------------------------------------------------- 1 | pub trait Task { 2 | fn poll(wk:||=>void) Option; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /planglib/std/task/delay.pi: -------------------------------------------------------------------------------- 1 | use std::task::Task; 2 | use std::task::reactor; 3 | use std::task::executor; 4 | 5 | 6 | 7 | struct DelayTask { 8 | first:bool; 9 | ready:bool; 10 | delay:u64; 11 | } 12 | 13 | use std::io; 14 | impl Task<()> for DelayTask { 15 | fn poll(wk:||=>void) Option<()> { 16 | if self.first { 17 | self.first = false; 18 | reactor::GLOBAL_REACTOR.add_timer(self.delay , 0 as u64, || => { 19 | self.ready = true; 20 | wk(); 21 | return; 22 | }); 23 | } 24 | 25 | if self.ready { 26 | return () as Option<()>; 27 | } 28 | return None{} as Option<()>; 29 | } 30 | } 31 | 32 | pub fn delay(delay:u64) Task<()> { 33 | return DelayTask { 34 | first:true, 35 | ready:false, 36 | delay:delay, 37 | } as Task<()>; 38 | } -------------------------------------------------------------------------------- /planglib/std/task/dns.pi: -------------------------------------------------------------------------------- 1 | use std::task::Task; 2 | use std::task::reactor; 3 | use std::libuv; 4 | 5 | pub struct DNSResolver { 6 | } 7 | 8 | pub fn new_dns_resolver() DNSResolver { 9 | return DNSResolver{}; 10 | } 11 | 12 | struct ResolveTask { 13 | first: bool; 14 | ready: bool; 15 | hostname: string; 16 | ip: string; 17 | } 18 | 19 | impl Task for ResolveTask { 20 | fn poll(wk:||=>void) Option { 21 | if self.first { 22 | self.first = false; 23 | let resolve_cb = |ip:string| => { 24 | if self.ready { 25 | return; 26 | } 27 | self.ip = ip; 28 | self.ready = true; 29 | wk(); 30 | return; 31 | }; 32 | reactor::GLOBAL_REACTOR.dns_resolve(self.hostname, resolve_cb); 33 | } 34 | 35 | if self.ready { 36 | return self.ip as Option; 37 | } 38 | return None{} as Option; 39 | } 40 | } 41 | 42 | impl DNSResolver { 43 | pub fn resolve_async(hostname: string) Task { 44 | let resolve_task = ResolveTask{ 45 | first: true, 46 | ready: false, 47 | hostname: hostname, 48 | ip: "", 49 | }; 50 | return resolve_task as Task; 51 | } 52 | } -------------------------------------------------------------------------------- /planglib/std/task/executor.pi: -------------------------------------------------------------------------------- 1 | use std::chan; 2 | use std::task::Task; 3 | struct SingleThreadExecutor { 4 | pub ch:*chan::Chan<| ||=>void |=>void>; 5 | } 6 | 7 | 8 | var GLOBAL_EXECUTOR = new_executor(&chan::channel<|||=>void |=>void>(10 as u64)); 9 | 10 | 11 | pub fn new_executor(queue: *chan::Chan<|||=>void |=>void>) SingleThreadExecutor { 12 | return SingleThreadExecutor { 13 | ch: queue, 14 | }; 15 | } 16 | 17 | use std::io; 18 | impl SingleThreadExecutor { 19 | pub fn start_exec_loop() void { 20 | while true { 21 | let work: | ||=>void |=>void = self.ch.recv(); 22 | let waker = || => void { 23 | self.ch.send(work); 24 | return; 25 | }; 26 | work(waker); 27 | } 28 | return; 29 | } 30 | 31 | pub fn spawn(task:Task) void { 32 | self.ch.send(|wk|=>{ 33 | task.poll(wk); 34 | return; 35 | }); 36 | return; 37 | } 38 | } 39 | 40 | 41 | pub fn start_exec_loop() void { 42 | GLOBAL_EXECUTOR.start_exec_loop(); 43 | return; 44 | } 45 | 46 | 47 | pub fn spawn_task(task:Task) void { 48 | GLOBAL_EXECUTOR.spawn(task); 49 | return; 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /planglib/std/task/helper.pi: -------------------------------------------------------------------------------- 1 | use std::task::Task; 2 | use std::task::executor; 3 | use std::task::reactor; 4 | use std::thread; 5 | use std::mutex; 6 | 7 | 8 | pub fn spawn_async_main(t:Task) void { 9 | // let tk = f(); 10 | thread::spawn(||=>{ 11 | let f = async || => { 12 | await t; 13 | reactor::stop_global_reactor(); 14 | return (); 15 | }; 16 | executor::GLOBAL_EXECUTOR.spawn(f()); 17 | executor::GLOBAL_EXECUTOR.start_exec_loop(); 18 | return; 19 | }); 20 | reactor::start_global_reactor(); 21 | return; 22 | } 23 | 24 | 25 | struct AdapterTask { 26 | task:Task; 27 | wk:||=>void; 28 | ready:bool; 29 | mtx:mutex::Mutex; 30 | } 31 | 32 | impl Task for AdapterTask { 33 | fn poll(wk:||=>void) Option { 34 | if self.ready { 35 | let r = self.task.poll(|| => { 36 | return; 37 | }); 38 | return r; 39 | } 40 | self.mtx.lock(); 41 | if !self.ready { 42 | self.wk = ||=>{ 43 | self.mtx.unlock(); 44 | wk(); 45 | return; 46 | }; 47 | } else { 48 | self.mtx.unlock(); 49 | wk(); 50 | let r = self.task.poll(|| => { 51 | return; 52 | }); 53 | return r; 54 | } 55 | self.mtx.unlock(); 56 | let r = self.task.poll(|| => { 57 | return; 58 | }); 59 | return r; 60 | } 61 | } 62 | 63 | 64 | pub fn spawn(tk:Task) Task { 65 | let t = AdapterTask { 66 | task:tk, 67 | ready:false, 68 | mtx:mutex::new_mutex(), 69 | }; 70 | t.wk = ||=>{ 71 | t.mtx.unlock(); 72 | return; 73 | }; 74 | tk.poll(||=>{ 75 | t.mtx.lock(); 76 | t.ready = true; 77 | t.wk(); 78 | 79 | return; 80 | }); 81 | return t as Task; 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /planglib/std/thread.pi: -------------------------------------------------------------------------------- 1 | fn new_thread(f: *||=>void, sp:i64) void; 2 | 3 | 4 | pub fn spawn(f:||=>void) void { 5 | let p = &f; 6 | // gc::pin(p); 7 | new_thread(p,asm_sp()); 8 | return; 9 | } 10 | 11 | pub fn sleep(secs: u64) void; -------------------------------------------------------------------------------- /plc.scoop: -------------------------------------------------------------------------------- 1 | {{ 2 | "version": "{0}", 3 | "homepage": "https://pivotlang.tech", 4 | "license": "MIT", 5 | "description": "Pivot Lang Compiler", 6 | "bin": [ 7 | "win64\\plc.exe" 8 | ], 9 | "architecture": {{ 10 | "64bit": {{ 11 | "url": "https://github.com/Pivot-Studio/pivot-lang/releases/download/{0}/{1}", 12 | "type": "tar.xz", 13 | "sha256": "{2}" 14 | }} 15 | }}, 16 | "post_install": [ 17 | "setx KAGARI_LIB_ROOT \"$dir\\win64\\planglib\"", 18 | "setx PL_ROOT \"$dir\\win64\"", 19 | "$env:KAGARI_LIB_ROOT=\"$dir\\win64\\planglib\"", 20 | "$env:PL_ROOT=\"$dir\\win64\"" 21 | ], 22 | "post_uninstall":[ 23 | "[Environment]::SetEnvironmentVariable('KAGARI_LIB_ROOT', $null, 'User')", 24 | "[Environment]::SetEnvironmentVariable('PL_ROOT', $null, 'User')" 25 | ] 26 | }} -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.81.0" 3 | components = ["rustfmt", "clippy"] -------------------------------------------------------------------------------- /setup-llvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script install the correct LLVM release for Debian & Ubuntu distros. 4 | 5 | set -eux 6 | 7 | LLVM_VERSION=18 8 | DISTRO=$(lsb_release -is) 9 | VERSION=$(lsb_release -sr) 10 | DIST_VERSION="${DISTRO}_${VERSION}" 11 | 12 | # Find the right repository name for the distro and version 13 | case "$DIST_VERSION" in 14 | Debian_9* ) REPO_NAME="deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-$LLVM_VERSION main" ;; 15 | Debian_10* ) REPO_NAME="deb http://apt.llvm.org/buster/ llvm-toolchain-buster-$LLVM_VERSION main" ;; 16 | Debian_unstable ) REPO_NAME="deb http://apt.llvm.org/unstable/ llvm-toolchain-$LLVM_VERSION main" ;; 17 | Debian_testing ) REPO_NAME="deb http://apt.llvm.org/unstable/ llvm-toolchain-$LLVM_VERSION main" ;; 18 | Ubuntu_16.04 ) REPO_NAME="deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-$LLVM_VERSION main" ;; 19 | Ubuntu_18.04 ) REPO_NAME="deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-$LLVM_VERSION main" ;; 20 | Ubuntu_18.10 ) REPO_NAME="deb http://apt.llvm.org/cosmic/ llvm-toolchain-cosmic-$LLVM_VERSION main" ;; 21 | Ubuntu_19.04 ) REPO_NAME="deb http://apt.llvm.org/disco/ llvm-toolchain-disco-$LLVM_VERSION main" ;; 22 | Ubuntu_19.10 ) REPO_NAME="deb http://apt.llvm.org/eoan/ llvm-toolchain-eoan-$LLVM_VERSION main" ;; 23 | Ubuntu_20.04 ) REPO_NAME="deb http://apt.llvm.org/focal/ llvm-toolchain-focal-$LLVM_VERSION main" ;; 24 | Ubuntu_22.04 ) REPO_NAME="deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-$LLVM_VERSION main" ;; 25 | Ubuntu_22.10 ) REPO_NAME="deb http://apt.llvm.org/kinetic/ llvm-toolchain-kinetic-$LLVM_VERSION main" ;; 26 | Ubuntu_24.04 ) REPO_NAME="deb http://apt.llvm.org/noble/ llvm-toolchain-noble-$LLVM_VERSION main" ;; 27 | Ubuntu_24.10 ) REPO_NAME="deb http://apt.llvm.org/oracular/ llvm-toolchain-oracular-$LLVM_VERSION main" ;; 28 | * ) 29 | echo "Distribution '$DISTRO' in version '$VERSION' is not supported by this script (${DIST_VERSION})." 30 | exit 2 31 | esac 32 | 33 | # Add the right repository for the distro 34 | wget -qO - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 35 | add-apt-repository "${REPO_NAME}" --yes 36 | apt-get update 37 | 38 | # Install required packages 39 | apt-get install -y llvm-$LLVM_VERSION llvm-$LLVM_VERSION-* liblld-$LLVM_VERSION* libpolly-$LLVM_VERSION-dev -------------------------------------------------------------------------------- /src/ast/accumulators.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use lsp_types::{ 4 | CodeLens, CompletionItem, DocumentSymbol, GotoDefinitionResponse, Hover, InlayHint, Location, 5 | SemanticTokens, SignatureHelp, TextEdit, 6 | }; 7 | 8 | use super::diag::PLDiag; 9 | 10 | #[salsa::accumulator] 11 | pub struct Diagnostics(pub (String, Vec)); 12 | 13 | #[salsa::accumulator] 14 | pub struct PLReferences(pub Vec); 15 | 16 | #[salsa::accumulator] 17 | pub struct GotoDef(pub GotoDefinitionResponse); 18 | 19 | #[salsa::accumulator] 20 | pub struct Completions(pub Vec); 21 | 22 | #[salsa::accumulator] 23 | pub struct PLSemanticTokens(pub SemanticTokens); 24 | 25 | #[salsa::accumulator] 26 | pub struct PLCodeLens(pub CodeLens); 27 | 28 | #[salsa::accumulator] 29 | pub struct PLHover(pub Hover); 30 | 31 | #[salsa::accumulator] 32 | pub struct ModBuffer(pub PLModBuffer); 33 | 34 | #[derive(Debug, Clone)] 35 | pub struct PLModBuffer { 36 | pub path: PathBuf, 37 | pub buf: Vec, 38 | pub name: String, 39 | } 40 | 41 | #[salsa::accumulator] 42 | pub struct PLFormat(pub Vec); 43 | #[salsa::accumulator] 44 | pub struct Hints(pub Vec); 45 | #[salsa::accumulator] 46 | pub struct DocSymbols(pub Vec); 47 | 48 | #[salsa::accumulator] 49 | pub struct PLSignatureHelp(pub SignatureHelp); 50 | -------------------------------------------------------------------------------- /src/ast/compiler/jit.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CString; 2 | 3 | use inkwell::support; 4 | 5 | use inkwell::OptimizationLevel; 6 | 7 | use std::path::Path; 8 | 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 10 | pub enum EngineType { 11 | // MCJit, 12 | #[default] 13 | OrcJit, 14 | } 15 | 16 | impl From<&str> for EngineType { 17 | fn from(s: &str) -> Self { 18 | match s { 19 | "orc" => EngineType::OrcJit, 20 | _ => panic!("unknown engine type"), 21 | } 22 | } 23 | } 24 | 25 | /// # run 26 | /// 27 | /// jit run a module 28 | /// 29 | /// ## Arguments 30 | /// 31 | /// * `p` - module path 32 | /// * `opt` - optimization level 33 | pub fn run(p: &Path, opt: OptimizationLevel, engine: EngineType) -> i32 { 34 | support::enable_llvm_pretty_stack_trace(); 35 | // expected to be called by memory manager on stackmap section initialized 36 | extern "C" fn gc_init(map: *mut u8) { 37 | immix::gc_init(map); 38 | } 39 | match engine { 40 | EngineType::OrcJit => { 41 | let cstr = CString::new(p.to_str().unwrap()).unwrap(); 42 | let cstr_p = cstr.as_ptr(); 43 | unsafe { immix::CreateAndRunPLOrcJITEngine(cstr_p, opt as u32, gc_init) } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/ast/compiler/options.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "llvm")] 2 | use inkwell::OptimizationLevel; 3 | 4 | /// Options preserves additional args to do additional works during parsing AST 5 | #[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Copy)] 6 | pub struct Options { 7 | pub asm: bool, 8 | pub genir: bool, 9 | /// printast will print out the generated AST and ends up the further processes. 10 | pub printast: bool, 11 | /// flow will print out the generated AST and ends up the further processes. 12 | pub flow: bool, 13 | pub optimization: HashOptimizationLevel, 14 | pub fmt: bool, 15 | pub jit: bool, 16 | pub debug: bool, 17 | pub print_escape: bool, 18 | } 19 | 20 | #[repr(u32)] 21 | #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] 22 | pub enum HashOptimizationLevel { 23 | None = 0, 24 | Less = 1, 25 | Default = 2, 26 | Aggressive = 3, 27 | } 28 | 29 | pub fn convert(optimization: u64) -> HashOptimizationLevel { 30 | match optimization { 31 | 0 => HashOptimizationLevel::None, 32 | 1 => HashOptimizationLevel::Less, 33 | 2 => HashOptimizationLevel::Default, 34 | 3 => HashOptimizationLevel::Aggressive, 35 | _ => panic!("optimization level must be 0-3"), 36 | } 37 | } 38 | 39 | impl Default for HashOptimizationLevel { 40 | /// Returns the default value for `OptimizationLevel`, namely `OptimizationLevel::Default`. 41 | fn default() -> Self { 42 | HashOptimizationLevel::Default 43 | } 44 | } 45 | 46 | #[cfg(feature = "llvm")] 47 | impl HashOptimizationLevel { 48 | pub fn to_llvm(self) -> OptimizationLevel { 49 | match self { 50 | HashOptimizationLevel::None => OptimizationLevel::None, 51 | HashOptimizationLevel::Less => OptimizationLevel::Less, 52 | HashOptimizationLevel::Default => OptimizationLevel::Default, 53 | HashOptimizationLevel::Aggressive => OptimizationLevel::Aggressive, 54 | } 55 | } 56 | } 57 | 58 | /// # ActionType 59 | /// lsp action type 60 | #[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] 61 | pub enum ActionType { 62 | GotoDef, 63 | FindReferences, 64 | Diagnostic, 65 | Hover, 66 | Compile, 67 | PrintAst, 68 | Flow, 69 | Fmt, 70 | LspFmt, 71 | Hint, 72 | DocSymbol, 73 | SignatureHelp, 74 | #[cfg(test)] 75 | Completion, 76 | #[cfg(target_arch = "wasm32")] 77 | SemanticTokensFull, 78 | } 79 | -------------------------------------------------------------------------------- /src/ast/compiler/progress.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "llvm")] 2 | use crate::compiler::Options; 3 | use console::Emoji; 4 | use std::time::Duration; 5 | 6 | use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; 7 | use lazy_static::lazy_static; 8 | 9 | lazy_static! { 10 | pub static ref COMPILE_PROGRESS: ProgressBar = ProgressBar::hidden(); 11 | pub static ref CHECK_PROGRESS: ProgressBar = ProgressBar::hidden(); 12 | } 13 | 14 | pub(crate) static CHECK: Emoji<'_, '_> = Emoji("🧐 ", ":-)"); 15 | 16 | pub(crate) static LOOKING_GLASS: Emoji<'_, '_> = Emoji("🔍 ", ":-)"); 17 | 18 | pub(crate) static TRUCK: Emoji<'_, '_> = Emoji("🚚 ", ":-)"); 19 | 20 | pub(crate) static CLIP: Emoji<'_, '_> = Emoji("🔗 ", ":-)"); 21 | 22 | pub(crate) static SPARKLE: Emoji<'_, '_> = Emoji("✨ ", ":-)"); 23 | 24 | lazy_static! { 25 | pub(crate) static ref PROGRESS_STYLE: ProgressStyle = ProgressStyle::with_template( 26 | "{prefix:.bold} {spinner} [{bar:40.cyan/blue}] {wide_msg:.green} ({elapsed})", 27 | ) 28 | .unwrap(); 29 | pub(crate) static ref MSG_PROGRESS_STYLE: ProgressStyle = 30 | ProgressStyle::with_template("{prefix:.bold} {spinner} {wide_msg:.green} ({elapsed})",) 31 | .unwrap(); 32 | } 33 | 34 | #[cfg(feature = "llvm")] 35 | pub fn prepare_progressbar(pb: &indicatif::ProgressBar, op: Options, prefix: String) { 36 | pb.enable_steady_tick(Duration::from_millis(50)); 37 | pb.set_style(PROGRESS_STYLE.clone()); 38 | 39 | let is_present_only = op.printast || op.flow; 40 | if is_present_only { 41 | pb.set_draw_target(ProgressDrawTarget::hidden()); 42 | } else { 43 | pb.set_draw_target(ProgressDrawTarget::stderr()); 44 | } 45 | pb.set_prefix(prefix); 46 | } 47 | -------------------------------------------------------------------------------- /src/ast/expects/arr.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/buf.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/builtin.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/chan.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/dtoa.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/eq.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/err.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/executor.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/gc.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/hash.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/hasher.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/helper.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/io.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/iter.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/libc.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/libuv.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/m1.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/m2.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/mutex.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/ord.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/panic.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/pl_hash.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/reactor.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/slice.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/stdbuiltin.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/string.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/task.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/expects/thread.pi.expect: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /src/ast/jit_config.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "llvm")] 2 | use std::sync::atomic::AtomicBool; 3 | pub static IS_JIT: AtomicBool = AtomicBool::new(false); 4 | -------------------------------------------------------------------------------- /src/ast/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod accumulators; 2 | pub mod builder; 3 | pub mod compiler; 4 | pub mod ctx; 5 | pub mod diag; 6 | pub mod fmt; 7 | pub mod jit_config; 8 | pub mod macros; 9 | pub mod node; 10 | pub mod plmod; 11 | pub mod pltype; 12 | pub mod range; 13 | pub mod test; 14 | pub mod tokens; 15 | pub mod traits; 16 | -------------------------------------------------------------------------------- /src/ast/node/README.md: -------------------------------------------------------------------------------- 1 | # node 2 | 3 | 所有ast节点 4 | 5 | ## 注意事项 6 | 7 | - 帮助函数写[mod.rs](mod.rs)里 8 | - alloc的时候请调用封装的`alloc`函数 9 | - 尽量调用封装的`position_at_end`函数来改变builder指向,除非你知道你在干什么 10 | - 在生成无法对应到源码的指令时,必须使用`ctx.nodebug_builder`生成指令,而不是`ctx.builder` 11 | - 如果你不使用emit结果,请不要使用`ret.emit(ctx,builder)?`这种带问号的语句,应该使用`_ = ret.emit(ctx,builder)`,这样才能做到尽可能的分析出用户的错误 12 | 13 | -------------------------------------------------------------------------------- /src/ast/node/comment.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::ast::ctx::Ctx; 3 | use internal_macro::node; 4 | use lsp_types::SemanticTokenType; 5 | 6 | #[node] 7 | pub struct CommentNode { 8 | pub comment: String, 9 | pub is_doc: bool, // use "///" (is_doc:true) 10 | } 11 | 12 | impl PrintTrait for CommentNode { 13 | fn print(&self, tabs: usize, end: bool, mut line: Vec) { 14 | deal_line(tabs, &mut line, end); 15 | tab(tabs, line.clone(), end); 16 | println!("CommentNode: {}", self.comment); 17 | } 18 | } 19 | 20 | impl Node for CommentNode { 21 | fn emit<'a, 'b>( 22 | &mut self, 23 | ctx: &'b mut Ctx<'a>, 24 | _builder: &'b BuilderEnum<'a, '_>, 25 | ) -> NodeResult { 26 | ctx.push_semantic_token(self.range, SemanticTokenType::COMMENT, 0); 27 | Ok(Default::default()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ast/node/error.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::ast::{ctx::Ctx, diag::ErrorCode}; 3 | 4 | use colored::Colorize; 5 | use internal_macro::node; 6 | 7 | #[node] 8 | pub struct ErrorNode { 9 | pub msg: String, 10 | pub src: String, 11 | pub code: ErrorCode, 12 | } 13 | 14 | impl PrintTrait for ErrorNode { 15 | fn print(&self, tabs: usize, end: bool, mut line: Vec) { 16 | deal_line(tabs, &mut line, end); 17 | tab(tabs, line.clone(), end); 18 | println!("ErrorNode: {}", self.msg.red()); 19 | tab(tabs + 1, line, true); 20 | println!("Src: {}", format!("{:?}", self.src).red()); 21 | } 22 | } 23 | 24 | impl Node for ErrorNode { 25 | fn emit<'a, 'b>( 26 | &mut self, 27 | ctx: &'b mut Ctx<'a>, 28 | _builder: &'b BuilderEnum<'a, '_>, 29 | ) -> NodeResult { 30 | let err = ctx.add_diag(self.range.new_err(self.code)); 31 | ctx.generate_completion_if(ctx.should_gen(self.range), || ctx.completion_alternatives()); 32 | 33 | Err(err) 34 | } 35 | } 36 | 37 | /// # StErrorNode 38 | /// 表现一个因为缺少分号而错误的statement 39 | #[node] 40 | pub struct StErrorNode { 41 | pub err: ErrorNode, 42 | pub st: Box, 43 | } 44 | 45 | impl PrintTrait for StErrorNode { 46 | fn print(&self, tabs: usize, end: bool, mut line: Vec) { 47 | deal_line(tabs, &mut line, end); 48 | tab(tabs, line.clone(), end); 49 | println!("StErrorNode"); 50 | self.st.print(tabs + 1, false, line.clone()); 51 | self.err.print(tabs + 1, true, line); 52 | } 53 | } 54 | 55 | impl Node for StErrorNode { 56 | fn emit<'a, 'b>( 57 | &mut self, 58 | ctx: &'b mut Ctx<'a>, 59 | builder: &'b BuilderEnum<'a, '_>, 60 | ) -> NodeResult { 61 | _ = self.st.emit(ctx, builder); 62 | self.err.emit(ctx, builder) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ast/node/intermediate_node.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::{ctx::Ctx, range::Range}; 2 | use internal_macro::node; 3 | 4 | use crate::ast::builder::BuilderEnum; 5 | 6 | use super::node_result::NodeResult; 7 | use super::{Node, PrintTrait}; 8 | 9 | #[node] 10 | pub struct IntermediateNode { 11 | value: NodeResult, 12 | } 13 | 14 | // this node will never be shared between threads 15 | unsafe impl Send for IntermediateNode {} 16 | unsafe impl Sync for IntermediateNode {} 17 | 18 | impl IntermediateNode { 19 | pub fn new(value: NodeResult) -> Self { 20 | Self { 21 | value, 22 | range: Range::default(), 23 | } 24 | } 25 | } 26 | 27 | impl PrintTrait for IntermediateNode { 28 | fn print(&self, _tabs: usize, _end: bool, _line: Vec) { 29 | unreachable!() 30 | } 31 | } 32 | 33 | impl Node for IntermediateNode { 34 | fn emit<'a, 'b>( 35 | &mut self, 36 | _ctx: &'b mut Ctx<'a>, 37 | _builder: &'b BuilderEnum<'a, '_>, 38 | ) -> NodeResult { 39 | self.value.clone() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/ast/node/pointer.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use super::node_result::NodeResultBuilder; 4 | use super::*; 5 | 6 | use crate::ast::builder::BuilderEnum; 7 | use crate::ast::builder::IRBuilder; 8 | use crate::ast::{ctx::Ctx, diag::ErrorCode}; 9 | use internal_macro::node; 10 | 11 | #[node(comment)] 12 | pub struct PointerOpNode { 13 | pub value: Box, 14 | pub op: PointerOpEnum, 15 | } 16 | 17 | #[derive(Clone, PartialEq, Eq, Debug)] 18 | pub enum PointerOpEnum { 19 | Deref, 20 | Addr, 21 | } 22 | 23 | impl PrintTrait for PointerOpNode { 24 | fn print(&self, tabs: usize, end: bool, mut line: Vec) { 25 | deal_line(tabs, &mut line, end); 26 | tab(tabs, line.clone(), end); 27 | println!("PointerOpNode"); 28 | self.value.print(tabs + 1, true, line.clone()); 29 | } 30 | } 31 | 32 | impl Node for PointerOpNode { 33 | fn emit<'a, 'b>( 34 | &mut self, 35 | ctx: &'b mut Ctx<'a>, 36 | builder: &'b BuilderEnum<'a, '_>, 37 | ) -> NodeResult { 38 | let v = self.value.emit(ctx, builder)?.get_value(); 39 | let v = v.unwrap(); 40 | let value = v.get_value(); 41 | let mut tp = v.get_ty(); 42 | let btp = tp.clone(); 43 | let value = match self.op { 44 | PointerOpEnum::Deref => { 45 | if let PLType::Pointer(tp1) = &*btp.borrow() { 46 | tp = tp1.clone(); 47 | builder.build_load(value, "deref", &btp.borrow(), ctx) 48 | } else { 49 | return Err(ctx.add_diag(self.range.new_err(ErrorCode::NOT_A_POINTER))); 50 | } 51 | } 52 | PointerOpEnum::Addr => { 53 | // let old_tp = tp.clone().unwrap(); 54 | let oldtp = tp.clone(); 55 | tp = Arc::new(RefCell::new(PLType::Pointer(tp))); 56 | let mut val = value; 57 | if !builder.is_ptr(v.get_value()) { 58 | // if not a pointer, then alloc a new tmp var 59 | let var = builder.alloc("var", &oldtp.borrow(), ctx, None); 60 | builder.build_store(var, value); 61 | val = var; 62 | } 63 | let v = builder.alloc("addr", &tp.borrow(), ctx, None); 64 | builder.build_store(v, val); 65 | v 66 | } 67 | }; 68 | value.new_output(tp).to_result() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/ast/node/string_literal.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::{ 2 | ctx::Ctx, 3 | node::{deal_line, tab}, 4 | pltype::PriType, 5 | }; 6 | 7 | use crate::ast::builder::BuilderEnum; 8 | use crate::ast::builder::IRBuilder; 9 | use internal_macro::node; 10 | use lsp_types::SemanticTokenType; 11 | use ustr::ustr; 12 | 13 | use super::{node_result::NodeResultBuilder, Node, NodeResult, PrintTrait}; 14 | 15 | #[node] 16 | pub struct StringNode { 17 | pub content: String, 18 | } 19 | 20 | impl PrintTrait for StringNode { 21 | fn print(&self, tabs: usize, end: bool, mut line: Vec) { 22 | deal_line(tabs, &mut line, end); 23 | tab(tabs, line.clone(), end); 24 | println!("StringNode: {:?}", self.content); 25 | } 26 | } 27 | 28 | impl Node for StringNode { 29 | fn emit<'a, 'b>( 30 | &mut self, 31 | ctx: &'b mut Ctx<'a>, 32 | builder: &'b BuilderEnum<'a, '_>, 33 | ) -> NodeResult { 34 | ctx.push_semantic_token(self.range, SemanticTokenType::STRING, 0); 35 | let v = builder.const_string(&self.content); 36 | let tp = ctx 37 | .plmod 38 | .submods 39 | .get(&ustr("gc")) 40 | .map(|m| m.types.get(&ustr("string")).unwrap().clone()) 41 | .unwrap_or_else(|| ctx.plmod.types.get(&ustr("string")).unwrap().clone()); 42 | let alloca = builder.alloc("string", &tp.borrow(), ctx, None); 43 | let len = builder 44 | .build_struct_gep(alloca, 1, "len", &tp.borrow(), ctx) 45 | .unwrap(); 46 | let byte_len = builder 47 | .build_struct_gep(alloca, 2, "byte_len", &tp.borrow(), ctx) 48 | .unwrap(); 49 | let read_arr = builder 50 | .build_struct_gep(alloca, 3, "real_arr_str", &tp.borrow(), ctx) 51 | .unwrap(); 52 | builder.build_store(read_arr, v); 53 | 54 | builder.build_store( 55 | len, 56 | builder.int_value(&PriType::I64, self.content.chars().count() as _, true), 57 | ); 58 | builder.build_store( 59 | byte_len, 60 | builder.int_value(&PriType::I64, self.content.len() as u64, true), 61 | ); 62 | alloca.new_output(tp.typ).set_const().to_result() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/ast/pltype/tpdocs.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::node::NodeEnum; 2 | 3 | use super::{FNValue, PLType, STType}; 4 | 5 | impl PLType { 6 | pub fn get_docs(&self) -> Option { 7 | match self { 8 | PLType::Fn(f) => f.get_docs_string(), 9 | PLType::Struct(s) | PLType::Trait(s) => s.get_docs_string(), 10 | PLType::Unknown => Some("A type cannot be inferred".to_string()), 11 | _ => Some(self.get_kind_name().to_string()), 12 | } 13 | } 14 | } 15 | 16 | pub trait DocAnnotatable { 17 | fn get_docs(&self) -> Option<&Vec>>; 18 | fn get_docs_string(&self) -> Option { 19 | if let Some(docs) = self.get_docs() { 20 | let mut string = String::new(); 21 | for doc in docs { 22 | if let NodeEnum::Comment(c) = &**doc { 23 | string.push_str(&c.comment); 24 | string.push('\n'); 25 | } 26 | } 27 | Some(string) 28 | } else { 29 | None 30 | } 31 | } 32 | } 33 | 34 | impl DocAnnotatable for FNValue { 35 | fn get_docs(&self) -> Option<&Vec>> { 36 | Option::Some(self.doc.as_ref()) 37 | } 38 | } 39 | 40 | impl DocAnnotatable for STType { 41 | fn get_docs(&self) -> Option<&Vec>> { 42 | Option::Some(self.doc.as_ref()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/ast/traits.rs: -------------------------------------------------------------------------------- 1 | use ustr::Ustr; 2 | 3 | use super::{ 4 | node::types::CustomTypeNode, 5 | pltype::{ClosureType, FNValue, PlaceHolderType, STType, UnionType}, 6 | }; 7 | 8 | pub trait CustomType { 9 | fn get_path(&self) -> Ustr; 10 | fn get_name(&self) -> Ustr; 11 | fn get_range(&self) -> Range; 12 | } 13 | 14 | macro_rules! impl_custom_type { 15 | ($t:ty) => { 16 | 17 | impl CustomType for $t { 18 | fn get_path(&self) -> Ustr { 19 | self.path.clone() 20 | } 21 | fn get_name(&self) -> Ustr { 22 | self.name.clone() 23 | } 24 | fn get_range(&self) -> Range { 25 | self.range 26 | } 27 | } 28 | }; 29 | () => { 30 | 31 | }; 32 | ($($t:ty),*) => { 33 | $(impl_custom_type!($t);)* 34 | }; 35 | ($($t:ty,)*) => { 36 | $(impl_custom_type!($t);)* 37 | }; 38 | } 39 | use crate::ast::range::Range; 40 | impl_custom_type!(UnionType, STType, FNValue, PlaceHolderType, CustomTypeNode); 41 | 42 | impl CustomType for ClosureType { 43 | fn get_path(&self) -> Ustr { 44 | Ustr::from("") 45 | } 46 | fn get_name(&self) -> Ustr { 47 | Ustr::from("closure") 48 | } 49 | fn get_range(&self) -> Range { 50 | self.range 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/jar.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, sync::Arc}; 2 | 3 | use ustr::Ustr; 4 | 5 | use crate::ast::{plmod::Mod, pltype::PLType}; 6 | 7 | pub trait PivotDB { 8 | fn set_ref_str(&self, ref_str: Option); 9 | fn get_ref_str(&self) -> Option; 10 | fn add_module(&self, name: Ustr, plmod: Mod); 11 | fn get_module(&self, name: Ustr) -> Option; 12 | fn add_tp_to_mod(&self, name: Ustr, tpname: Ustr, pltype: Arc>); 13 | } 14 | 15 | #[salsa::db] 16 | pub trait Db: salsa::Database + PivotDB {} 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "wasm32")] 2 | mod jar; 3 | #[cfg(target_arch = "wasm32")] 4 | pub use jar::*; 5 | #[cfg(target_arch = "wasm32")] 6 | mod ast; 7 | #[cfg(target_arch = "wasm32")] 8 | mod db; 9 | #[cfg(target_arch = "wasm32")] 10 | mod flow; 11 | #[cfg(target_arch = "wasm32")] 12 | mod lsp; 13 | #[cfg(target_arch = "wasm32")] 14 | mod nomparser; 15 | #[cfg(target_arch = "wasm32")] 16 | mod utils; 17 | 18 | #[cfg(target_arch = "wasm32")] 19 | mod inference; 20 | -------------------------------------------------------------------------------- /src/lsp/config.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use lsp_types::{SemanticTokenType, SemanticTokensLegend}; 3 | 4 | use crate::lsp::semantic_tokens::SUPPORTED_MODIFIERS; 5 | 6 | lazy_static! { 7 | pub static ref SEMANTIC_LEGEND: SemanticTokensLegend = { 8 | lsp_types::SemanticTokensLegend { 9 | token_types: vec![ 10 | SemanticTokenType::NAMESPACE, 11 | SemanticTokenType::TYPE, 12 | SemanticTokenType::CLASS, 13 | SemanticTokenType::ENUM, 14 | SemanticTokenType::INTERFACE, 15 | SemanticTokenType::STRUCT, 16 | SemanticTokenType::TYPE_PARAMETER, 17 | SemanticTokenType::FUNCTION, 18 | SemanticTokenType::METHOD, 19 | SemanticTokenType::PROPERTY, 20 | SemanticTokenType::MACRO, 21 | SemanticTokenType::VARIABLE, 22 | SemanticTokenType::PARAMETER, 23 | SemanticTokenType::ENUM_MEMBER, 24 | SemanticTokenType::STRING, 25 | SemanticTokenType::NUMBER, 26 | SemanticTokenType::KEYWORD, 27 | SemanticTokenType::MODIFIER, 28 | SemanticTokenType::COMMENT, 29 | SemanticTokenType::REGEXP, 30 | SemanticTokenType::OPERATOR, 31 | ], 32 | token_modifiers: SUPPORTED_MODIFIERS.to_vec(), 33 | } 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/lsp/dispatcher.rs: -------------------------------------------------------------------------------- 1 | use lsp_server::{ExtractError, Message, Request, RequestId}; 2 | 3 | pub struct Dispatcher(Message); 4 | 5 | impl Dispatcher { 6 | pub fn new(msg: Message) -> Self { 7 | Dispatcher(msg) 8 | } 9 | pub fn on(&self, mut f: F) -> &Dispatcher 10 | where 11 | R: lsp_types::request::Request, 12 | R::Params: serde::de::DeserializeOwned, 13 | F: FnMut(RequestId, R::Params), 14 | { 15 | if let Message::Request(req) = self.0.clone() { 16 | let params = cast::(req).map_err(|_| ()); 17 | if let Ok(params) = params { 18 | log::info!("req: {}", R::METHOD); 19 | f(params.0, params.1); 20 | } 21 | } 22 | self 23 | } 24 | pub fn on_noti(&self, mut f: F) -> &Dispatcher 25 | where 26 | R: lsp_types::notification::Notification, 27 | R::Params: serde::de::DeserializeOwned, 28 | F: FnMut(R::Params), 29 | { 30 | if let Message::Notification(req) = self.0.clone() { 31 | let params = cast_noti::(req).map_err(|_| ()); 32 | if let Ok(params) = params { 33 | log::info!("noti: {}", R::METHOD); 34 | f(params); 35 | } 36 | } 37 | self 38 | } 39 | } 40 | 41 | fn cast(req: Request) -> Result<(RequestId, R::Params), ExtractError> 42 | where 43 | R: lsp_types::request::Request, 44 | R::Params: serde::de::DeserializeOwned, 45 | { 46 | req.extract(R::METHOD) 47 | } 48 | 49 | fn cast_noti( 50 | req: lsp_server::Notification, 51 | ) -> Result> 52 | where 53 | R: lsp_types::notification::Notification, 54 | R::Params: serde::de::DeserializeOwned, 55 | { 56 | req.extract(R::METHOD) 57 | } 58 | -------------------------------------------------------------------------------- /src/lsp/fake_thread_pool.rs: -------------------------------------------------------------------------------- 1 | //! A fake thread pool that executes jobs immediately on the current thread. 2 | //! 3 | //! This is used for wasm32 targets where threads are not supported. 4 | 5 | pub(crate) struct ThreadPool {} 6 | 7 | impl ThreadPool { 8 | pub fn new(n: usize) -> Self { 9 | Self {} 10 | } 11 | 12 | pub fn execute(&self, job: F) 13 | where 14 | F: FnOnce() + Send + 'static, 15 | { 16 | job(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lsp/mod.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | pub mod dispatcher; 3 | pub mod helpers; 4 | #[cfg(not(target_arch = "wasm32"))] 5 | mod lspserver; 6 | pub mod mem_docs; 7 | pub mod semantic_tokens; 8 | pub mod text; 9 | #[cfg(not(target_arch = "wasm32"))] 10 | pub use lspserver::*; 11 | #[cfg(target_arch = "wasm32")] 12 | pub mod wasm; 13 | -------------------------------------------------------------------------------- /src/nomparser/README.md: -------------------------------------------------------------------------------- 1 | # nomparser 2 | 3 | Nomparser uses nom to scan and parse the source code into an AST. It takes charges on both a lexer and a parser. 4 | The nomparser is an LL parser, which uses the SDD(syntax-directed-definition), which combines the parser and syntax tree construction. 5 | 6 | The syntax of pivot-language is specified in [grammar.enbf](./grammar.ebnf), and the respective parsing handler could be searched by the snake case of EBNF item. 7 | 8 | 9 | ## 注意事项 10 | 11 | - 记得使用`delspace`去除前后空格 12 | - 语言的token用`tag_token`来读取,而不是`tag` 13 | - parser理论上不该返回错误,任何语法错误情况都应该生成`ErrorNode`。如果parser返回了错误,将会导致编译panic。 14 | -------------------------------------------------------------------------------- /src/nomparser/array.rs: -------------------------------------------------------------------------------- 1 | use internal_macro::test_parser; 2 | use nom::sequence::pair; 3 | use nom::{ 4 | combinator::{map_res, opt}, 5 | multi::{many0, separated_list0}, 6 | sequence::{delimited, tuple}, 7 | IResult, 8 | }; 9 | 10 | use crate::nomparser::Span; 11 | use crate::{ 12 | ast::node::types::ArrayInitNode, 13 | ast::tokens::TokenType, 14 | ast::{diag::ErrorCode, node::error::ErrorNode}, 15 | }; 16 | 17 | use super::*; 18 | 19 | #[test_parser("[1,2,3]")] 20 | #[test_parser("[i8*capicity;]")] 21 | #[test_parser("[i32*expected_capicity*2;2,3,4]")] 22 | #[test_parser( 23 | "[ 24 | 1, 25 | 2, 26 | x 27 | ]" 28 | )] 29 | pub fn array_init(input: Span) -> IResult> { 30 | map_res( 31 | tuple(( 32 | tag_token_symbol_ex(TokenType::LBRACKET), 33 | opt(pair( 34 | type_name, 35 | delimited( 36 | tag_token_symbol_ex(TokenType::MUL), 37 | general_exp, 38 | tag_token_symbol_ex(TokenType::SEMI), 39 | ), 40 | )), 41 | separated_list0( 42 | tag_token_symbol_ex(TokenType::COMMA), 43 | del_newline_or_space!(general_exp), 44 | ), 45 | tag_token_symbol_ex(TokenType::RBRACKET), 46 | )), 47 | // lb and rb are used to mark the boundaries of an arry. 48 | |((_, lb), tp, exps, (_, rb))| { 49 | let range = lb.start.to(rb.end); 50 | res_enum(ArrayInitNode { exps, range, tp }.into()) 51 | }, 52 | )(input) 53 | } 54 | 55 | #[test_parser("[123]")] 56 | #[test_parser("[index]")] 57 | pub fn array_element_op(input: Span) -> IResult>)> { 58 | delspace(map_res( 59 | tuple(( 60 | tag_token_symbol_ex(TokenType::LBRACKET), 61 | opt(general_exp), 62 | tag_token_symbol_ex(TokenType::RBRACKET), 63 | many0(comment), 64 | )), 65 | |(_, idx, (_, rr), com)| { 66 | if let Some(idx) = idx { 67 | Ok::<_, ()>((ComplexOp::Index(idx), com)) 68 | } else { 69 | Ok::<_, ()>(( 70 | ComplexOp::Index(Box::new(NodeEnum::Err(ErrorNode { 71 | msg: String::from("Nedded index for array element access"), 72 | src: String::from("[]"), 73 | code: ErrorCode::NEEDED_INDEX_FOR_ARRAY_ELEMENT_ACCESS, 74 | range: rr, 75 | }))), 76 | com, 77 | )) 78 | } 79 | }, 80 | ))(input) 81 | } 82 | -------------------------------------------------------------------------------- /src/nomparser/comment.rs: -------------------------------------------------------------------------------- 1 | use crate::nomparser::Span; 2 | use crate::{ast::node::comment::CommentNode, ast::range::Range}; 3 | use internal_macro::{test_parser, test_parser_error}; 4 | use nom::{ 5 | branch::alt, 6 | bytes::complete::{tag, take_until}, 7 | combinator::{map_res, rest}, 8 | sequence::{pair, terminated}, 9 | IResult, InputTake, 10 | }; 11 | 12 | use super::*; 13 | 14 | #[test_parser("//123")] 15 | #[test_parser("//123\t")] 16 | #[test_parser("//123 \u{1234}")] 17 | #[test_parser("/// 123\n")] 18 | #[test_parser("//12\r //3")] 19 | #[test_parser("//12\r \\//3")] 20 | #[test_parser_error("//12\r\n /3")] 21 | #[test_parser("/// 123\r 456")] 22 | #[test_parser_error("/// 12\n 3 ///")] 23 | #[test_parser_error("/ / 123\n")] 24 | #[test_parser_error("//123 \n \n ///")] 25 | pub fn comment(input: Span) -> IResult> { 26 | map_res( 27 | pair( 28 | alt((tag("///"), tag("//"))), 29 | alt((terminated(take_until("\n"), tag("\n")), rest)), 30 | ), 31 | |(a, c): (Span, Span)| { 32 | res_enum( 33 | CommentNode { 34 | comment: c.trim_end_matches('\r').to_string(), 35 | range: Range::new(&input, &c.take_split(c.len()).0), 36 | is_doc: a.contains("///"), 37 | } 38 | .into(), 39 | ) 40 | }, 41 | )(input.clone()) 42 | } 43 | -------------------------------------------------------------------------------- /src/nomparser/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! del_newline_or_space { 3 | ($e:expr) => { 4 | nom::sequence::delimited( 5 | nom::multi::many0(nom::branch::alt(( 6 | nom::bytes::complete::tag("\n"), 7 | nom::bytes::complete::tag("\r\n"), 8 | nom::bytes::complete::tag(" "), 9 | nom::bytes::complete::tag("\t"), 10 | ))), 11 | $e, 12 | nom::multi::many0(nom::branch::alt(( 13 | nom::bytes::complete::tag("\n"), 14 | nom::bytes::complete::tag("\r\n"), 15 | nom::bytes::complete::tag(" "), 16 | nom::bytes::complete::tag("\t"), 17 | ))), 18 | ) 19 | }; 20 | } 21 | 22 | /// parse_bin_ops matches an expression, which is consisted by 23 | /// the exp type separated by one of ops. 24 | macro_rules! parse_bin_ops { 25 | ($exp:ident, $($op:ident),*) => { 26 | delspace(map( 27 | tuple(( 28 | $exp, 29 | many0(tuple(( 30 | alt(( 31 | $( 32 | tag_token_symbol_ex(TokenType::$op), 33 | )* 34 | )), 35 | $exp, 36 | ))), 37 | )), 38 | create_bin, 39 | )) 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /src/nomparser/pkg.rs: -------------------------------------------------------------------------------- 1 | use nom::{ 2 | combinator::{map_res, opt}, 3 | multi::separated_list1, 4 | sequence::{preceded, tuple}, 5 | IResult, 6 | }; 7 | 8 | use crate::nomparser::Span; 9 | use crate::{ast::node::pkg::UseNode, ast::tokens::TokenType}; 10 | use internal_macro::{test_parser, test_parser_error}; 11 | 12 | use super::*; 13 | 14 | #[test_parser("use a::b")] 15 | #[test_parser("use a::")] 16 | #[test_parser("use a")] 17 | #[test_parser("use a:")] 18 | #[test_parser_error("usea")] 19 | #[test_parser_error("usea:")] 20 | pub fn use_statement(input: Span) -> IResult> { 21 | map_res( 22 | modifiable( 23 | preceded( 24 | tag_token_word(TokenType::USE), 25 | delspace(tuple(( 26 | separated_list1(tag_token_symbol(TokenType::DOUBLE_COLON), identifier), 27 | opt(preceded( 28 | tag_token_symbol(TokenType::DOUBLE_COLON), 29 | tag_token_symbol(TokenType::MUL), 30 | )), 31 | // the options are used for error toleration 32 | opt(tag_token_symbol(TokenType::DOUBLE_COLON)), 33 | opt(tag_token_symbol(TokenType::COLON)), 34 | ))), 35 | ), 36 | TokenType::PUB, 37 | ), 38 | |(modifier, (ns, all, opt, opt2))| { 39 | let mut range = ns 40 | .first() 41 | .unwrap() 42 | .range() 43 | .start 44 | .to(ns.last().unwrap().range().end); 45 | if let Some(opt) = opt { 46 | range = range.start.to(opt.1.end); 47 | } 48 | if let Some(opt2) = opt2 { 49 | range = range.start.to(opt2.1.end); 50 | } 51 | res_enum(NodeEnum::UseNode(UseNode { 52 | namespace: ns, 53 | range, 54 | complete: opt.is_none() && opt2.is_none(), 55 | singlecolon: opt2.is_some(), 56 | modifier, 57 | all_import: all.is_some(), 58 | })) 59 | }, 60 | )(input) 61 | } 62 | -------------------------------------------------------------------------------- /src/nomparser/union.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | helper::{modifiable, res_enum, semi_stmt, tag_token_symbol_ex}, 3 | identifier::identifier, 4 | types::{generic_type_def, type_name}, 5 | Span, 6 | }; 7 | use crate::ast::node::RangeTrait; 8 | use crate::ast::{ 9 | node::{union::UnionDefNode, NodeEnum}, 10 | tokens::TokenType, 11 | }; 12 | use internal_macro::test_parser; 13 | use nom::{ 14 | combinator::{map_res, opt}, 15 | multi::separated_list1, 16 | sequence::tuple, 17 | IResult, Parser, 18 | }; 19 | 20 | #[test_parser("pub type A = B | C")] 21 | #[test_parser("type A=B")] 22 | #[test_parser( 23 | "pub 24 | type A 25 | = 26 | B | C" 27 | )] 28 | fn union(input: Span) -> IResult> { 29 | map_res( 30 | modifiable( 31 | tuple(( 32 | tag_token_symbol_ex(TokenType::TYPE), 33 | identifier, 34 | opt(generic_type_def), 35 | tag_token_symbol_ex(TokenType::ASSIGN), 36 | separated_list1(tag_token_symbol_ex(TokenType::TYPE_OR), type_name), 37 | )), 38 | TokenType::PUB, 39 | ), 40 | |(modifier, ((_, st_r), name, generics, _, sum_types))| { 41 | let range = st_r.start.to(sum_types.last().unwrap().range().end); 42 | res_enum( 43 | UnionDefNode { 44 | modifier, 45 | name: *name, 46 | generics, 47 | sum_types, 48 | range, 49 | } 50 | .into(), 51 | ) 52 | }, 53 | )(input) 54 | } 55 | 56 | /// # union 57 | /// 58 | /// union is also called `sum type` in other languages 59 | /// 60 | /// ## Syntax 61 | /// 62 | /// ```pl 63 | /// type A = B | C | D; 64 | /// type A = B | C | D; 65 | /// ``` 66 | pub fn union_stmt(input: Span) -> IResult> { 67 | semi_stmt(union, union).parse(input) 68 | } 69 | -------------------------------------------------------------------------------- /src/repl/repl_cmd.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::{Parser, Subcommand}; 4 | 5 | /// REPL Command Line Interface 6 | #[derive(Parser)] 7 | #[command(version, about, long_about = None, name = "@repl", bin_name = "@repl")] 8 | pub struct REPLCli { 9 | #[command(subcommand)] 10 | pub command: Commands, 11 | } 12 | 13 | #[derive(Subcommand)] 14 | pub enum Commands { 15 | /// loads a Pivot Lang project into repl 16 | Load { 17 | /// project path 18 | proj_path: PathBuf, 19 | /// sets the project name 20 | #[arg(long = "as")] 21 | _as: String, 22 | }, 23 | /// loads the dependencies of a Pivot Lang project into repl 24 | LoadDeps { 25 | /// project path 26 | proj_path: PathBuf, 27 | }, 28 | /// reload a file 29 | Reload { 30 | /// file path 31 | file_path: PathBuf, 32 | }, 33 | /// Watch a dir, perform a hot reload on file change 34 | Watch { 35 | /// directory 36 | dir: String, 37 | }, 38 | /// Print current config 39 | Config, 40 | /// Print symbol table 41 | Symbol, 42 | } 43 | 44 | #[cfg(test)] 45 | #[test] 46 | fn test_repl_cli() { 47 | REPLCli::try_parse_from(["@repl", "load", r#""test""#, "--as", "proj"]).unwrap(); 48 | REPLCli::try_parse_from(["@repl", "load-deps", r#""test""#]).unwrap(); 49 | } 50 | -------------------------------------------------------------------------------- /src/repl/test.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | struct TestEditor { 4 | input: VecDeque, 5 | err_expects: VecDeque, 6 | } 7 | 8 | impl TestEditor { 9 | fn new(input: VecDeque, err_expects: VecDeque) -> Self { 10 | Self { input, err_expects } 11 | } 12 | } 13 | 14 | impl super::editor::TermEditor for TestEditor { 15 | fn readline(&mut self, _prompt: &str) -> Result { 16 | self.input 17 | .pop_front() 18 | .ok_or(rustyline::error::ReadlineError::Eof) 19 | } 20 | 21 | fn set_helper(&mut self, _helper: Option) {} 22 | 23 | fn load_history + ?Sized>( 24 | &mut self, 25 | _path: &P, 26 | ) -> Result<(), rustyline::error::ReadlineError> { 27 | Ok(()) 28 | } 29 | 30 | fn save_history + ?Sized>( 31 | &mut self, 32 | _path: &P, 33 | ) -> Result<(), rustyline::error::ReadlineError> { 34 | Ok(()) 35 | } 36 | 37 | fn add_history_entry + Into>( 38 | &mut self, 39 | _line: S, 40 | ) -> Result { 41 | Ok(true) 42 | } 43 | fn assert_err(&mut self, err: bool) { 44 | let expect = self.err_expects.pop_front().unwrap(); 45 | assert_eq!( 46 | err, expect, 47 | "expect err: {}, got: {}. buffer remains: {:#?}", 48 | expect, err, self.input 49 | ); 50 | } 51 | } 52 | 53 | #[test] 54 | fn test_repl() { 55 | let rl = TestEditor::new( 56 | VecDeque::from(vec![ 57 | "let a = 1".to_owned(), 58 | "a".to_owned(), 59 | "b".to_owned(), 60 | "let a = 2;".to_owned(), 61 | "use std::cols::hashtable;".to_owned(), 62 | "@repl load test --as test".to_owned(), 63 | "use test::test::std_test;".to_owned(), 64 | "std_test::test_std()".to_owned(), 65 | "@repl load-deps test".to_owned(), 66 | "use project2::main::test".to_owned(), 67 | "test::test()".to_owned(), 68 | "@repl reload test/test/std_test.pi".to_owned(), 69 | "std_test::test_std()".to_owned(), 70 | "@repl watch test".to_owned(), 71 | "std_test::test_std()".to_owned(), 72 | "@repl config".to_owned(), 73 | "@repl symbol".to_owned(), 74 | ]), 75 | VecDeque::from(vec![ 76 | false, false, true, false, false, false, false, false, false, false, false, false, 77 | false, false, false, false, false, 78 | ]), 79 | ); 80 | super::start_repl(rl); 81 | } 82 | -------------------------------------------------------------------------------- /src/utils/README.md: -------------------------------------------------------------------------------- 1 | # utils 2 | 3 | 目前只有个测试文件 -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod plc_new; 2 | pub mod read_config; 3 | 4 | use std::{ 5 | collections::hash_map::DefaultHasher, 6 | hash::{Hash, Hasher}, 7 | io, 8 | path::{Path, PathBuf}, 9 | }; 10 | 11 | use lsp_types::Url; 12 | 13 | pub fn get_hash_code(obj: T) -> u64 14 | where 15 | T: Hash, 16 | { 17 | let mut hasher = DefaultHasher::new(); 18 | obj.hash(&mut hasher); 19 | hasher.finish() 20 | } 21 | 22 | pub fn url_from_path(file: &str) -> Url { 23 | { 24 | if file.starts_with("@__repl") { 25 | return Url::parse("httss://example.com").unwrap(); 26 | } 27 | #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] 28 | return Url::from_file_path(file).unwrap(); 29 | #[cfg(not(any(unix, windows, target_os = "redox", target_os = "wasi")))] 30 | { 31 | if file.starts_with("http") { 32 | return Url::parse(file).unwrap(); 33 | } 34 | return Url::parse("httss://example.com").unwrap(); 35 | } 36 | } 37 | } 38 | 39 | pub fn canonicalize>(path: P) -> io::Result { 40 | #[cfg(target_arch = "wasm32")] 41 | return Ok(path.as_ref().to_path_buf()); 42 | #[cfg(not(target_arch = "wasm32"))] 43 | { 44 | if path.as_ref().starts_with("@__repl__") { 45 | return Ok(path.as_ref().to_path_buf()); 46 | } 47 | dunce::canonicalize(path) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/Kagari.lock: -------------------------------------------------------------------------------- 1 | [pl_test] 2 | name = "pl_test" 3 | 4 | [pl_test.git] 5 | url = "https://github.com/Pivot-Studio/pl_test.git" 6 | commit = "ffcf2de5b5301ece8aab3e5b89d8b99b6d17d8de" 7 | -------------------------------------------------------------------------------- /test/Kagari.toml: -------------------------------------------------------------------------------- 1 | project = "project1" 2 | entry = "main.pi" 3 | 4 | assert_index_out_of_bounds = true 5 | 6 | [deps] 7 | project2 = { path = "project2" } 8 | # pl_test = { git = "https://github.com/Pivot-Studio/pl_test.git", head = "main" } 9 | -------------------------------------------------------------------------------- /test/arr_bounds/Kagari.toml: -------------------------------------------------------------------------------- 1 | entry = "main.pi" 2 | project = "arr_bounds" 3 | 4 | assert_index_out_of_bounds = true 5 | -------------------------------------------------------------------------------- /test/arr_bounds/main.pi: -------------------------------------------------------------------------------- 1 | fn main() i64 { 2 | let a = [1]; 3 | let d = a[1]; 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /test/fmt/Kagari.toml: -------------------------------------------------------------------------------- 1 | project = "test" 2 | entry = "test_fmt.pi" -------------------------------------------------------------------------------- /test/gcbench/Kagari.toml: -------------------------------------------------------------------------------- 1 | entry = "main.pi" 2 | project = "gcbench" 3 | -------------------------------------------------------------------------------- /test/gcbench/README.md: -------------------------------------------------------------------------------- 1 | # GC Benchmark 2 | 3 | This is a simple benchmark for testing our gc performance. 4 | -------------------------------------------------------------------------------- /test/gcbench/main.pi: -------------------------------------------------------------------------------- 1 | pub fn main() i64 { 2 | // bench(); 3 | for let i = 0; i < 30; i = i + 1 { 4 | bench(); 5 | } 6 | let start = millitime(); 7 | for let i = 0; i < 10; i = i + 1 { 8 | bench(); 9 | } 10 | let end = millitime(); 11 | println!("time: ", (end - start)/10); 12 | 13 | let stw = gc::DioGC__get_stw_num(); 14 | println!("gc stw: ", stw); 15 | return 0; 16 | } 17 | 18 | 19 | fn millitime() i64; 20 | 21 | fn bench() void { 22 | let root = &GCTestObj{}; 23 | populate(*K_LONG_LIVED_TREE_DEPTH, root); 24 | let d = *K_MIN_TREE_DEPTH; 25 | // println!(d); 26 | while d <= *K_MAX_TREE_DEPTH { 27 | // println!(d); 28 | time_construct(d); 29 | d = d + 2; 30 | } 31 | 32 | keep_on_stack(root); 33 | return; 34 | } 35 | 36 | fn keep_on_stack(p:*GCTestObj) void; 37 | 38 | fn time_construct(depth: i64) void { 39 | // println!(depth); 40 | let i_num_iters = num_iters(depth); 41 | // println!(i_num_iters); 42 | for let i = 0; i < i_num_iters; i = i + 1 { 43 | let temp_tree = make_tree(depth); 44 | populate(depth, temp_tree); 45 | } 46 | for let i = 0; i < i_num_iters; i = i + 1 { 47 | make_tree(depth); 48 | } 49 | return; 50 | } 51 | 52 | 53 | 54 | 55 | fn tree_size(i: i64) i64 { 56 | 57 | return (1<<(i+1)) -1; 58 | } 59 | 60 | 61 | var K_STRETCH_TREE_DEPTH = &18; 62 | var K_LONG_LIVED_TREE_DEPTH = & 16; 63 | var K_MIN_TREE_DEPTH = & 4; 64 | var K_MAX_TREE_DEPTH = & 16; 65 | 66 | 67 | fn num_iters(i:i64) i64 { 68 | 69 | return 2 * tree_size(*K_STRETCH_TREE_DEPTH) / tree_size(i); 70 | } 71 | 72 | struct GCTestObj { 73 | b:* GCTestObj; 74 | d:u64; 75 | e:*GCTestObj; 76 | d0:u64; 77 | d1:u64; 78 | d2:u64; 79 | d3:u64; 80 | d4:u64; 81 | d5:u64; 82 | d6:u64; 83 | d7:u64; 84 | d8:u64; 85 | d9:u64; 86 | d10:u64; 87 | d11:u64; 88 | 89 | 90 | } 91 | 92 | fn populate(idepth: i64, thisnode: * GCTestObj) void { 93 | if idepth <= 0 { 94 | return; 95 | } 96 | thisnode.b = &GCTestObj{}; 97 | thisnode.e = &GCTestObj{}; 98 | populate(idepth-1, thisnode.b); 99 | populate(idepth-1, thisnode.e); 100 | return; 101 | } 102 | 103 | 104 | fn make_tree(idepth: i64) * GCTestObj { 105 | if idepth <= 0 { 106 | return &GCTestObj{}; 107 | } 108 | let left = make_tree(idepth-1); 109 | let right = make_tree(idepth-1); 110 | let thisnode = &GCTestObj{b: left, e: right}; 111 | return thisnode; 112 | } 113 | -------------------------------------------------------------------------------- /test/lsp/Kagari.toml: -------------------------------------------------------------------------------- 1 | project = "test" 2 | entry = "test_completion.pi" 3 | 4 | -------------------------------------------------------------------------------- /test/lsp/mod.pi: -------------------------------------------------------------------------------- 1 | /// # content 2 | struct name { 3 | 4 | } 5 | pub struct pubname { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /test/lsp/mod2.pi: -------------------------------------------------------------------------------- 1 | use test::mod; 2 | 3 | fn name() void { 4 | let a = mod::name{}; 5 | return; 6 | } 7 | 8 | 9 | 10 | fn test_sig_help(i:i64, ii:bool) void { 11 | test_sig_help(i, ii) 12 | return; 13 | } -------------------------------------------------------------------------------- /test/lsp/test_completion.pi: -------------------------------------------------------------------------------- 1 | struct test { 2 | a:bool; 3 | b:f64; 4 | c:i64; 5 | d:; 6 | } 7 | 8 | fn name(args:test) void { 9 | args. 10 | t 11 | return; 12 | } 13 | 14 | struct test1 { 15 | a:bool; 16 | b:f64; 17 | c:i64; 18 | } 19 | 20 | use 21 | 22 | fn name() void { 23 | 24 | return; 25 | } 26 | 27 | fn name1() void { 28 | let a = 10; 29 | return; 30 | } 31 | 32 | // 此文件中尽量不要改动已有代码,最好在后方添加。改动已有代码可能导致测试不通过 33 | 34 | use test::mod; 35 | 36 | struct a { 37 | n:m 38 | m:mod:: 39 | o:mod::name 40 | } 41 | use test::mod2; 42 | use test::trait1; 43 | 44 | use core::panic; 45 | 46 | fn traitname() void { 47 | let st = trait1::testSt2{}; 48 | let a:trait1::TestTrait = st; 49 | let d = st.a; 50 | a.name(); 51 | trait1::new(); 52 | name(panic::assert(true)); 53 | return; 54 | } 55 | 56 | var GLOB = &1; 57 | -------------------------------------------------------------------------------- /test/lsp/trait1.pi: -------------------------------------------------------------------------------- 1 | 2 | 3 | trait TestTrait { 4 | fn name() void; 5 | } 6 | 7 | 8 | struct testSt { 9 | 10 | } 11 | 12 | impl TestTrait for testSt { 13 | fn name2() void { 14 | return; 15 | } 16 | } 17 | 18 | struct testSt2 { 19 | a:bool; 20 | } 21 | 22 | 23 | impl TestTrait for testSt2 { 24 | fn name() void { 25 | return; 26 | } 27 | } 28 | 29 | fn new() void { 30 | 31 | return; 32 | } 33 | -------------------------------------------------------------------------------- /test/lsp_diag/Kagari.toml: -------------------------------------------------------------------------------- 1 | project = "test" 2 | entry = "test_diag.pi" 3 | 4 | -------------------------------------------------------------------------------- /test/lsp_diag/m1.pi: -------------------------------------------------------------------------------- 1 | use test::m2; -------------------------------------------------------------------------------- /test/lsp_diag/m2.pi: -------------------------------------------------------------------------------- 1 | use test::test_diag; -------------------------------------------------------------------------------- /test/lsp_diag/match_diag.pi: -------------------------------------------------------------------------------- 1 | 2 | 3 | fn test_match() void { 4 | match 1 { 5 | 1 => { 6 | return; 7 | } 8 | "dasda" => { 9 | return; 10 | } 11 | i64(_) => { 12 | return; 13 | } 14 | {a:0} => {} 15 | true => {} 16 | 1.0 => {} 17 | _ => { 18 | return; 19 | } 20 | } 21 | 22 | match true { 23 | false => { 24 | 25 | } 26 | _ => { 27 | } 28 | } 29 | 30 | let a = 1 as Option; 31 | match a { 32 | i64(x) => { 33 | return; 34 | } 35 | 1 => { 36 | return; 37 | } 38 | f64(_) => { 39 | return; 40 | } 41 | None(_) => { 42 | return; 43 | } 44 | } 45 | 46 | match 'a' { 47 | 'b' => { 48 | 49 | } 50 | "s" => { 51 | return; 52 | } 53 | _ => { 54 | } 55 | } 56 | 57 | match 1.0 { 58 | 1.1 => { 59 | 60 | } 61 | 1 => {} 62 | TestTrait(a) => { 63 | 64 | } 65 | _ => { 66 | } 67 | } 68 | 69 | match (1,2) { 70 | (1,,2,) => { 71 | return; 72 | } 73 | (1,_,_) => { 74 | return; 75 | } 76 | () => {} 77 | (a,()a, dasdas) => { 78 | return; 79 | } 80 | (_,2) => { 81 | return; 82 | } 83 | } 84 | 85 | 86 | let aa: TestTrait; 87 | match aa { 88 | pattern => { 89 | 90 | } 91 | _ => { 92 | } 93 | } 94 | 95 | match (1,2,3) { 96 | TestTrait(a) => { 97 | 98 | } 99 | _ => { 100 | } 101 | } 102 | let aaa = TestST{a:1}; 103 | match aaa { 104 | {b:a} => { 105 | 106 | } 107 | _ => { 108 | } 109 | } 110 | 111 | 112 | 113 | return; 114 | } 115 | 116 | 117 | trait TestTrait { 118 | } 119 | 120 | struct TestST { 121 | a:i64; 122 | 123 | } -------------------------------------------------------------------------------- /test/lsp_incremental/Kagari.toml: -------------------------------------------------------------------------------- 1 | entry = "main.pi" 2 | project = "lsp_incremental" 3 | -------------------------------------------------------------------------------- /test/lsp_incremental/main.pi: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use lsp_incremental::module1; 3 | fn main() i64 { 4 | io::print_s("Hello World\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /test/lsp_incremental/module1.pi: -------------------------------------------------------------------------------- 1 | use lsp_incremental::module2; -------------------------------------------------------------------------------- /test/lsp_incremental/module2.pi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pivot-Studio/pivot-lang/e81101f319f8186fd52a1c21a9796a2396cd2d48/test/lsp_incremental/module2.pi -------------------------------------------------------------------------------- /test/main.pi: -------------------------------------------------------------------------------- 1 | use project1::test::_match; 2 | use project1::test::time; 3 | use project1::test::print; 4 | use project1::test::ifel; 5 | use project1::test::generic; 6 | use project1::test::st; 7 | use project1::test::method; 8 | use project1::test::global; 9 | use project1::test::traits; 10 | use project1::test::simple; 11 | use project1::test::flow; 12 | use project1::test::compiletime_reflection::test_compile_time_reflection; 13 | use project1::test::list; 14 | use project1::test::module; 15 | use project1::test::str; 16 | use project1::test::macros; 17 | use project1::test::union; 18 | use project1::test::multi_trait; 19 | use project1::test::tuple; 20 | use project1::test::fntype; 21 | use project1::test::closure; 22 | use project1::test::map; 23 | use project1::test::tree; 24 | use project1::test::fixed_point; 25 | use project1::test::sort_test; 26 | use project1::tmod2; 27 | // use pl_test::main; 28 | use project1::test::deconstruct; 29 | use project1::test::st::*; 30 | use project1::test::arr; 31 | use project1::test::iter; 32 | use project1::test::inference; 33 | use project1::test::rand; 34 | use project1::test::_hashtable; 35 | use project1::test::_io; 36 | // use project1::test::future_test; 37 | use project1::test::std_test; 38 | 39 | 40 | 41 | 42 | async fn main() Task<()> { 43 | macros::test_macros(); 44 | ifel::test_if_else(); 45 | generic::test_generic(); 46 | method::test_impl(); 47 | test_struct(); 48 | global::test_global(); 49 | traits::test_trait(); 50 | simple::test_simple(); 51 | flow::test_flow(); 52 | list::test_list(); 53 | // main::simple_test(); 54 | module::test_module(); 55 | str::test_string(); 56 | union::test_union(); 57 | multi_trait::test_multi_trait(); 58 | deconstruct::test_deconstruct(); 59 | tuple::test_tuple(); 60 | fntype::test_fntype()(); 61 | closure::test_closure(); 62 | tree::test_eva(); 63 | map::test_map(); 64 | fixed_point::test_fixed_point(); 65 | generic::ret_generic1(); 66 | test_compile_time_reflection(); 67 | iter::test_generator(); 68 | inference::test_inference(); 69 | rand::test_rand(); 70 | sort_test::test_sort(); 71 | arr::test_arr(); 72 | _hashtable::test_hashtable(); 73 | _io::test_io(); 74 | // future_test::test_future(); 75 | std_test::test_std(); 76 | // await std_test::test_udp(); 77 | 78 | await std_test::test_nested_async_closure(); 79 | await std_test::test_nested_async_closure_in_normal_f(); 80 | await std_test::test_nested_async_closure_in_normal_f2(); 81 | await std_test::test_delay(); 82 | await std_test::test_http(); 83 | await std_test::test_async_loop(); 84 | // await std_test::test_dns(); 85 | 86 | return (); 87 | } 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /test/mod1.pi: -------------------------------------------------------------------------------- 1 | use project1::mod2; 2 | pub use core::panic::assert; 3 | 4 | pub fn test_mod(args: i64) void { 5 | let d: mod2::Mod2 = mod2::Mod2{}; 6 | let dd = Mod1{}; 7 | dd.y.y = true; 8 | assert(dd.y.y); 9 | printi64ln(999999); 10 | mod2::ret_opt(); 11 | return; 12 | } 13 | 14 | // pub fn printi64ln(i: i64) void; 15 | 16 | //415642154 17 | pub struct Mod1 { 18 | x: i64; 19 | y: T; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /test/mod2.pi: -------------------------------------------------------------------------------- 1 | use project1::sub::mod; 2 | pub fn test_mod(args: i64) void { 3 | return; 4 | } 5 | 6 | pub struct Mod2 { 7 | pub y: bool; 8 | } 9 | 10 | impl Eq for Mod2 { 11 | fn eq(other:*Mod2) bool { 12 | return true; 13 | } 14 | } 15 | 16 | 17 | struct name { 18 | t:T; 19 | } 20 | 21 | 22 | 23 | pub fn ret_opt() name> { 24 | let b = 1; 25 | let a:Option = b; 26 | let aa:name> = name{t:a}; 27 | return aa; 28 | } 29 | -------------------------------------------------------------------------------- /test/project2/Kagari.toml: -------------------------------------------------------------------------------- 1 | project = "project2" 2 | entry = "main.pi" 3 | -------------------------------------------------------------------------------- /test/project2/main.pi: -------------------------------------------------------------------------------- 1 | use project2::main::test; 2 | 3 | pub fn test() void { 4 | return; 5 | } -------------------------------------------------------------------------------- /test/project2/main/test.pi: -------------------------------------------------------------------------------- 1 | pub fn test() void { 2 | return; 3 | } -------------------------------------------------------------------------------- /test/sub/mod.pi: -------------------------------------------------------------------------------- 1 | pub fn name() void { 2 | return; 3 | } -------------------------------------------------------------------------------- /test/tail/Kagari.toml: -------------------------------------------------------------------------------- 1 | entry = "main.pi" 2 | project = "tail" 3 | -------------------------------------------------------------------------------- /test/tail/main.pi: -------------------------------------------------------------------------------- 1 | /// tail call optimization test 2 | pub fn main() i64 { 3 | let re = fib_tail(400000); 4 | println!(re); 5 | return 0; 6 | } 7 | 8 | 9 | fn fib_tail_helper(n:i64, a:i64, b:i64) i64 { 10 | if n == 0 { 11 | return a; 12 | } else { 13 | return fib_tail_helper(n - 1, b, a + b); 14 | } 15 | } 16 | 17 | fn fib_tail(n:i64) i64 { 18 | return fib_tail_helper(n, 0, 1); 19 | } -------------------------------------------------------------------------------- /test/test/_hashtable.pi: -------------------------------------------------------------------------------- 1 | use core::hash::pl_hash::*; 2 | use std::cols::hashtable; 3 | use core::panic; 4 | use core::eq::Eq; 5 | 6 | pub fn test_hashtable() i64 { 7 | 8 | 9 | 10 | let table1 = hashtable::new_hash_table(10 as u64, 100 as u64); 11 | table1.insert(1, ""); 12 | 13 | let table; 14 | table = hashtable::new_hash_table(10 as u64, 100 as u64); 15 | table.insert("hi", "World"); 16 | table.insert("tj", "love hj"); 17 | let v = table.get("hi") as string!; 18 | panic::assert(v.eq(&"World")); 19 | v = table.get("tj") as string!; 20 | panic::assert(v.eq(&"love hj")); 21 | table.insert("hi", "World2"); 22 | v = table.get("hi") as string!; 23 | panic::assert(v.eq(&"World2")); 24 | // count len 25 | let count = 0; 26 | let iter = table.into_iter(); 27 | for let re = iter.next();!re is None ; re = iter.next() { 28 | count = count + 1; 29 | } 30 | panic::assert(count.eq(&2)); 31 | let str = "1"; 32 | for let i = 0; i < 10; i = i + 1 { 33 | str.append("1"); 34 | table.insert(str, "World2"); 35 | } 36 | // println!(table.bucket_size()); 37 | let i_table = hashtable::new_hash_table(10 as u64, 100 as u64); 38 | i_table.insert(1, 2); 39 | i_table.insert(3, 4); 40 | let one = i_table.get(1) as i64!; 41 | let three = i_table.get(3) as i64!; 42 | panic::assert(one.eq(&2)); 43 | panic::assert(three.eq(&4)); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /test/test/_io.pi: -------------------------------------------------------------------------------- 1 | use std::io; 2 | // use std::cols::hashtable::new_hash_table; 3 | 4 | pub fn test_io() i64 { 5 | let fd = io::open_read("./Cargo.toml"); 6 | let _s = fd.read_all_as_string() as string!; 7 | 8 | 9 | fd.close(); 10 | 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /test/test/_match.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | struct name { 3 | a: i64; 4 | b: i64; 5 | } 6 | 7 | pub fn test_match() void { 8 | let a = 1 as Option; 9 | 10 | match a { 11 | i64(i) => { 12 | panic::assert(i == 1); 13 | println!(i); 14 | } 15 | _ => { 16 | panic::pl_panic(); 17 | println!(222); 18 | } 19 | } 20 | 21 | let n = name{ a: 1, b: 2 }; 22 | 23 | match n { 24 | { a: 1, b: 2 } => { 25 | println!(1); 26 | } 27 | _ => { 28 | panic::pl_panic(); 29 | println!(2); 30 | } 31 | }; 32 | 33 | match (a,n) { 34 | (None(_),{ a: 1, b: 2 }) => { 35 | panic::pl_panic(); 36 | println!(1); 37 | } 38 | (i64(2),{ a: 1, b: 2 }) => { 39 | panic::pl_panic(); 40 | println!(2); 41 | } 42 | _ => { 43 | println!(3); 44 | } 45 | }; 46 | 47 | let s = "fdsafasdad"; 48 | match s { 49 | "fdsafasdad" => { 50 | println!(1); 51 | } 52 | _ => { 53 | panic::pl_panic(); 54 | println!(2); 55 | } 56 | }; 57 | return; 58 | } -------------------------------------------------------------------------------- /test/test/arr.pi: -------------------------------------------------------------------------------- 1 | use std::cols::arr; 2 | use core::panic::*; 3 | use std::iter::IntoIter; 4 | pub fn test_arr() void { 5 | let array = arr::from_slice([1, 2, 3]); 6 | array.set(2, 100); 7 | let a2 = array.get(2); 8 | assert(a2 == 100); 9 | array.push(4); 10 | array.push(5); 11 | let a4 = array.get(4); 12 | assert(a4 == 5); 13 | let a5 = array.pop(); 14 | assert(a5 == 5); 15 | let iter = array.iter(); 16 | let count = 0; 17 | for let i = iter.next(); i is i64; i = iter.next() { 18 | let tmp = i as i64!; 19 | if count == 0 { 20 | assert(tmp == 1); 21 | } else if count == 1 { 22 | assert(tmp == 2); 23 | } else if count == 2 { 24 | assert(tmp == 100); 25 | } else if count == 3 { 26 | assert(tmp == 4); 27 | } 28 | count = count + 1; 29 | } 30 | iter = [1, 2, 3].into_iter(); 31 | count = 0; 32 | for let i = iter.next(); i is i64; i = iter.next() { 33 | let tmp = i as i64!; 34 | if count == 0 { 35 | assert(tmp == 1); 36 | } else if count == 1 { 37 | assert(tmp == 2); 38 | } else if count == 2 { 39 | assert(tmp == 3); 40 | } else { 41 | panic::pl_panic(); 42 | } 43 | count = count + 1; 44 | } 45 | return; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /test/test/closure.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | pub fn test_closure() i64 { 3 | let b = 1; 4 | let bb = 100; 5 | test_type_infer(|a| => { 6 | let c = b; 7 | let aa = |a| => { 8 | let c = bb; 9 | return c; 10 | }; 11 | let re = aa(2); 12 | panic::assert(re == 100); 13 | panic::assert(c == 1); 14 | panic::assert(a==100); 15 | return c; 16 | }); 17 | let fff = |a:|i64| => i64| => { 18 | return; 19 | }; 20 | let ddd = |a| => { 21 | return; 22 | }; 23 | fff = ddd; 24 | let eee = |a| => { 25 | let c = b; 26 | let aa = |a| => { 27 | let c = bb; 28 | return c; 29 | }; 30 | let re = aa(2); 31 | panic::assert(re == 100); 32 | return c; 33 | }; 34 | fff(eee); 35 | 36 | let f = test_ret_closure(); 37 | let d = f(2); 38 | panic::assert(d == 1); 39 | return 0; 40 | } 41 | 42 | fn test_type_infer(f: |i64| => i64) void { 43 | f(100); 44 | return; 45 | } 46 | 47 | fn test_ret_closure() |i64| => i64 { 48 | let b = 1; 49 | let bb = 100; 50 | let a = |a: i64| => i64 { 51 | let c = b; 52 | let aa = |a: i64| => i64 { 53 | let c = bb; 54 | return c; 55 | }; 56 | let re = aa(2); 57 | panic::assert(re == 100); 58 | b = 10001; 59 | return c; 60 | }; 61 | a(0); 62 | panic::assert(b == 10001); 63 | b = 1; 64 | return a; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /test/test/compiletime_reflection.pi: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use core::panic::assert; 3 | use project1::tmod2; 4 | 5 | pub fn test_compile_time_reflection() void { 6 | let n = tmod2::mod1_name{}; 7 | assert(!n.f); 8 | assert(!n.dd); 9 | name(&n); 10 | io::print_s("\n"); 11 | assert(n.f); 12 | assert(n.dd); 13 | // println!(n, "你好", true, 10); 14 | io::print_s("\n"); 15 | let a = test_s{}; 16 | 17 | let b = a impl test_t?; 18 | 19 | panic::assert(b); 20 | 21 | test_g(a); 22 | return; 23 | } 24 | 25 | trait test_t { 26 | fn dosth(); 27 | } 28 | 29 | struct test_s { 30 | 31 | } 32 | 33 | impl test_t for test_s { 34 | fn dosth() { 35 | 36 | return ; 37 | } 38 | } 39 | 40 | fn test_g(a:T) void { 41 | if a impl test_t? { 42 | let b = a impl test_t!; 43 | b.dosth(); 44 | } else { 45 | panic::pl_panic(); 46 | } 47 | if let b = a impl test_t { 48 | b.dosth(); 49 | return; 50 | }else { 51 | panic::pl_panic(); 52 | } 53 | panic::pl_panic(); 54 | return; 55 | } 56 | 57 | 58 | fn name(t:*T) void { 59 | let s = fullnameof(); 60 | io::print_s(s); 61 | io::print_s("\n"); 62 | let b = true; 63 | forfields(t, { 64 | match_type(_field,{ 65 | _value = true; 66 | }); 67 | b = true; 68 | let d = _field_name; 69 | io::print_s(_field_name); 70 | io::print_s("\n"); 71 | forfields(_field, { 72 | io::print_s(_field_name); 73 | io::print_s("\n"); 74 | }); 75 | }); 76 | return; 77 | } 78 | -------------------------------------------------------------------------------- /test/test/deconstruct.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | struct DecTest { 3 | aa: bool; 4 | bb: i128; 5 | cc: (i64, i64); 6 | dd: *DecTest; 7 | } 8 | 9 | pub fn test_deconstruct() void { 10 | let (a, (b, c)) = (1, (2, 3)); 11 | let d = a + b + c; 12 | panic::assert(d == 1 + 2 + 3); 13 | let complex = DecTest{ 14 | aa: true, 15 | bb: 123 as i128, 16 | cc: (1, 2), 17 | dd: &DecTest{ 18 | aa: true, 19 | bb: 123 as i128, 20 | cc: (1, 2) 21 | } 22 | }; 23 | let ({aa, bb, cc: (cc1, cc2), dd}, e) = (complex, 100); 24 | panic::assert(cc1 + cc2 + e == 1 + 2 + 100); 25 | {aa, cc: (cc1, cc2)} = DecTest{ 26 | aa: false, 27 | cc: (3, 4) 28 | }; 29 | let (d1,d2) = (1,2); 30 | (d1,d2) = (3,4); 31 | panic::assert(d1 == 3); 32 | panic::assert(d2 == 4); 33 | 34 | 35 | panic::assert(!aa); 36 | panic::assert(cc1 + cc2 == 3 + 4); 37 | return; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /test/test/fixed_point.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | pub fn test_fixed_point() void { 3 | let g = |f, x| => { 4 | if x == 0 { 5 | return 1; 6 | } 7 | return x * f(x - 1); 8 | }; 9 | let fact = Y(g); 10 | for let x = 0; x < 10; x = x + 1 { 11 | panic::assert(fact(x) == fact_recursion(x)); 12 | } 13 | return; 14 | } 15 | 16 | struct Func { 17 | f: |Func, A| => F; 18 | } 19 | 20 | impl Func { 21 | fn call(f: Func, x: A) F { 22 | return self.f(f, x); 23 | } 24 | 25 | } 26 | 27 | fn Y(g: ||A| => R, A| => R) |A| => R { 28 | return |x| => { 29 | return |f, x| => { 30 | return f.call(f, x); 31 | }(Func{ 32 | f: |f, x| => { 33 | return g(|x| => { 34 | return f.call(f, x); 35 | }, x); 36 | } 37 | }, x); 38 | }; 39 | } 40 | 41 | fn fact_recursion(x: i64) i64 { 42 | if x == 0 { 43 | return 1; 44 | } 45 | return x * fact_recursion(x - 1); 46 | } 47 | 48 | -------------------------------------------------------------------------------- /test/test/flow.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | pub fn true_with_pnanic() bool { 3 | panic::pl_panic(); 4 | return true; 5 | } 6 | 7 | pub fn test_loop() void { 8 | let i = 0; 9 | while i < 7 { 10 | if i == 3 { 11 | i = 5; 12 | continue; 13 | } 14 | if i == 5 { 15 | break; 16 | } 17 | i = i + 1; 18 | } 19 | panic::assert(i == 5); 20 | let j = 0; 21 | for let i = 0; i <= 10; i = i + 1 { 22 | j = j + i; 23 | } 24 | panic::assert(j == 55); 25 | return; 26 | } 27 | 28 | pub fn test_minimal_eval() void { 29 | let x = (false && true_with_pnanic()) || (true || !true_with_pnanic()); 30 | if (true || true_with_pnanic()) && (false && !true_with_pnanic() || x) { 31 | } else { 32 | panic::pl_panic(); 33 | } 34 | for let i = 10; i < 15 && (i > 100 || true); i = i + 1 { 35 | } 36 | let i = 0; 37 | while i > 100 || (i < 5 && i <= 6) { 38 | i = i + 1; 39 | } 40 | return; 41 | } 42 | 43 | pub fn test_flow() void { 44 | test_minimal_eval(); 45 | test_loop(); 46 | return; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /test/test/fntype.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | fn fn_as_param(f: |i64, i64| => i64) |i64, i64| => i64 { 3 | panic::assert(f(1, 2) == 3); 4 | return f; 5 | } 6 | 7 | fn add(i: i64, j: i64) i64 { 8 | return i + j; 9 | } 10 | 11 | pub fn test_fntype() || => f { 12 | let f = add; 13 | let fc: |i64, i64| => i64 = f; 14 | let re = fn_as_param(fc)(2, 2); 15 | panic::assert(re == 4); 16 | let ff: || => f = test_ret_f; 17 | let f1 = test_ret_f().f(100, 2); 18 | panic::assert(f1 == 102); 19 | return ff; 20 | } 21 | 22 | fn test_ret_f() f { 23 | let d: |i64, i64| => i64 = add; 24 | return f{ 25 | f: d 26 | }; 27 | } 28 | 29 | pub struct f { 30 | f: |i64, i64| => i64; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /test/test/future_test.pi: -------------------------------------------------------------------------------- 1 | use std::future; 2 | use std::future::FutureExt; 3 | use std::thread; 4 | 5 | 6 | pub fn test_future() void { 7 | let exe = future::new_executor(); 8 | thread::spawn(|| => { 9 | exe.start_exec_loop(); 10 | return; 11 | }); 12 | let f:future::Future<()> = future::delay(1 as u64); 13 | let ff1 = f.continue_with(|r:()| => { 14 | println!("Hello from future"); 15 | return future::nothing_future(); 16 | }).continue_with(|r:()| => { 17 | println!("continue Hello from future"); 18 | return future::nothing_future(); 19 | }); 20 | 21 | let f2:future::Future<()> = future::delay(1 as u64); 22 | let ff2 = f2.continue_with(|r:()| => { 23 | println!("Hello from future2"); 24 | return future::nothing_future(); 25 | }).continue_with(|r:()| => { 26 | println!("continue Hello from future2"); 27 | return future::nothing_future(); 28 | }); 29 | exe.spawn(ff1); 30 | exe.spawn(ff2); 31 | 32 | thread::sleep(2 as u64); 33 | return; 34 | } 35 | -------------------------------------------------------------------------------- /test/test/global.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | var a = init_a(); 3 | pub struct name { 4 | a: i64; 5 | b: i64; 6 | } 7 | 8 | fn init_a() *i64 { 9 | let a = 100; 10 | return &a; 11 | } 12 | 13 | var b = &name{ 14 | a: 1 15 | }; 16 | pub fn change_global() void { 17 | b.a = 100; 18 | return; 19 | } 20 | 21 | pub fn test_global() void { 22 | panic::assert(*a == 100); 23 | change_global(); 24 | panic::assert(b.a == 100); 25 | return; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /test/test/ifel.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | 3 | pub fn test_if_else() void { 4 | test_if_only(true); 5 | test_if_else_ret(true); 6 | test_if_ret_only(true); 7 | test_else_ret_only(true); 8 | test_not_ret(true); 9 | test_if_let(23456); 10 | return; 11 | } 12 | fn test_if_let(t:T) void { 13 | let d= 1234 as Option; 14 | if let a = d as i64 { 15 | panic::assert(a == 1234); 16 | }else { 17 | panic::pl_panic(); 18 | } 19 | 20 | if false { 21 | panic::pl_panic(); 22 | } else if let tt = t as i64 { 23 | panic::assert(tt == 23456); 24 | } else { 25 | panic::pl_panic(); 26 | } 27 | 28 | return; 29 | } 30 | 31 | 32 | 33 | 34 | fn test_not_equal() void { 35 | if 1 != 2 { 36 | return; 37 | } 38 | return; 39 | } 40 | 41 | pub fn test_if_only(judge: bool) void { 42 | if judge { 43 | return; 44 | } 45 | return; 46 | } 47 | 48 | pub fn test_if_else_ret(judge: bool) void { 49 | if judge { 50 | return; 51 | } else { 52 | return; 53 | } 54 | } 55 | 56 | pub fn test_if_ret_only(judge: bool) void { 57 | if judge { 58 | return; 59 | } else { 60 | } 61 | return; 62 | } 63 | 64 | pub fn test_else_ret_only(judge: bool) void { 65 | if judge { 66 | } else { 67 | return; 68 | } 69 | return; 70 | } 71 | 72 | pub fn test_not_ret(judge: bool) void { 73 | if judge { 74 | } else { 75 | } 76 | return; 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /test/test/list.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | pub struct List { 3 | val: i64; 4 | next: *List; 5 | } 6 | 7 | pub fn test_list() void { 8 | let head = List{}; 9 | let walk = &head; 10 | for let i = 0; i < 10; i = i + 1 { 11 | let new = List{ 12 | val: i 13 | }; 14 | walk.next = &new; 15 | walk = walk.next; 16 | } 17 | walk = &head; 18 | for let i = 0; i < 10; i = i + 1 { 19 | walk = walk.next; 20 | panic::assert(walk.val == i); 21 | } 22 | return; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /test/test/macros.pi: -------------------------------------------------------------------------------- 1 | macro test { 2 | ($a:@id) => { 3 | io::print_s($a); 4 | }; 5 | ($($a:@id = $b:@expr,)*) => { 6 | $( 7 | test!($a); 8 | io::printi64ln($b); 9 | )* 10 | 11 | }; 12 | } 13 | 14 | use std::io; 15 | 16 | pub fn test_macros() void { 17 | let a = "hello"; 18 | test!(a); 19 | return; 20 | } -------------------------------------------------------------------------------- /test/test/map.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | pub fn test_map() void { 3 | // count = 5 4 | // 1, 0, 1, 1, 1 5 | // 1, 1, 0, 1, 1 6 | // 1, 0, 0, 1, 1 7 | // 0, 1, 1, 0, 0 8 | // 1, 0, 0, 1, 1 9 | let mp = [[1, 0, 1, 1, 1], [1, 1, 0, 1, 1], [1, 0, 0, 1, 1], [0, 1, 1, 0, 0], [1, 0, 0, 1, 1]]; 10 | panic::assert(count(mp) == 5); 11 | return; 12 | } 13 | 14 | pub fn count(mp: [[i64]]) i64 { 15 | let res = 0; 16 | let w = 5; 17 | let h = 5; 18 | let dfs: |i64, i64| => void; 19 | dfs = |i, j| => { 20 | if i == -1 || i == h || j == -1 || j == w || mp[i][j] == 0 { 21 | return; 22 | } 23 | mp[i][j] = 0; 24 | dfs(i + 1, j); 25 | dfs(i, j + 1); 26 | dfs(i - 1, j); 27 | dfs(i, j - 1); 28 | return; 29 | }; 30 | for let i = 0; i < h; i = i + 1 { 31 | for let j = 0; j < w; j = j + 1 { 32 | if mp[i][j] == 0 { 33 | continue; 34 | } 35 | res = res + 1; 36 | dfs(i, j); 37 | } 38 | } 39 | return res; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /test/test/method.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | pub struct impl_struct { 3 | x: i64; 4 | } 5 | 6 | /// ANCHOR: impl 7 | impl impl_struct { 8 | pub fn add1() void { 9 | self.x = self.x + 1; 10 | return; 11 | } 12 | 13 | pub fn set(x: i64) void { 14 | self.x = x; 15 | return; 16 | } 17 | 18 | } 19 | 20 | /// ANCHOR_END: impl 21 | pub fn test_impl() void { 22 | let x = impl_struct{ 23 | x: 99 24 | }; 25 | x.set(114514); 26 | panic::assert(x.x == 114514); 27 | x.add1(); 28 | panic::assert(x.x == 114515); 29 | let p1 = Point{ 30 | x: 1, 31 | y: 2 32 | }; 33 | let p2 = Point{ 34 | x: true, 35 | y: false 36 | }; 37 | let p3 = p1.mixup(p2); 38 | let p4 = p2.mixup(p1); 39 | panic::assert(p3.x == 1); 40 | panic::assert(!p3.y); 41 | panic::assert(p4.y == 2); 42 | panic::assert(p4.x); 43 | return; 44 | } 45 | 46 | struct Point { 47 | x: X; 48 | y: Y; 49 | } 50 | 51 | impl Point { 52 | fn mixup(other: Point) Point { 53 | return Point{ 54 | x: self.x, 55 | y: other.y 56 | }; 57 | } 58 | 59 | } 60 | 61 | // 目前推断是根据字段推断,如果没字段则会有bug 62 | // struct A {} 63 | // impl A { 64 | // fn abc() void { 65 | // return; 66 | // } 67 | // } 68 | -------------------------------------------------------------------------------- /test/test/module.pi: -------------------------------------------------------------------------------- 1 | use project1::test::sub_module; 2 | pub fn test_module() void { 3 | let x = sub_module::A{}; 4 | sub_module::testfn(x); 5 | x.testimpl(); 6 | let y = sub_module::test_generic(x); 7 | y.testimpl(); 8 | let z = sub_module::B{ 9 | x: x 10 | }; 11 | let a = sub_module::WithGeneric{ 12 | x: z 13 | }; 14 | return; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /test/test/multi_trait.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | use project1::test::multi_trait_st::test_struct; 3 | use project1::test::multi_trait_A::A; 4 | trait B { 5 | fn b() i64; 6 | 7 | } 8 | 9 | trait C: A+B { 10 | fn c() void; 11 | 12 | } 13 | 14 | pub fn test_multi_trait() void { 15 | let t = test_struct{}; 16 | let c: C; 17 | c = t as C; 18 | let d = c is test_struct; 19 | panic::assert(d); 20 | let _casted_0 = c as test_struct!; 21 | let tt = test_generic_struct{}; 22 | let cc: B; 23 | cc = tt; 24 | let dd = cc is test_generic_struct; 25 | panic::assert(dd); 26 | let _casted_1 = cc as test_generic_struct!; 27 | let x = c.a(); 28 | let y = c.b(); 29 | panic::assert(x); 30 | panic::assert(y == 1000); 31 | panic::assert(trait_with_generic(t) == 1000); 32 | cc = c as B; 33 | dd = cc is test_struct; 34 | panic::assert(dd); 35 | y = cc.b(); 36 | panic::assert(y == 1000); 37 | return; 38 | } 39 | 40 | impl C for test_struct { 41 | fn c() void { 42 | return; 43 | } 44 | 45 | } 46 | 47 | impl B for test_struct { 48 | fn b() i64 { 49 | return 1000; 50 | } 51 | 52 | } 53 | 54 | fn trait_with_generic(x: T) i64 { 55 | x.a(); 56 | let i = x.b(); 57 | return i; 58 | } 59 | 60 | 61 | impl B for test_generic_struct { 62 | fn b() i64 { 63 | return 1000; 64 | } 65 | } 66 | 67 | 68 | struct test_generic_struct { 69 | 70 | } 71 | -------------------------------------------------------------------------------- /test/test/multi_trait_A.pi: -------------------------------------------------------------------------------- 1 | use project1::test::multi_trait_st::test_struct; 2 | pub trait A { 3 | fn a() bool; 4 | 5 | } 6 | 7 | impl A for test_struct { 8 | fn a() bool { 9 | return true; 10 | } 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /test/test/multi_trait_st.pi: -------------------------------------------------------------------------------- 1 | pub struct test_struct { 2 | } 3 | 4 | -------------------------------------------------------------------------------- /test/test/print.pi: -------------------------------------------------------------------------------- 1 | use std::cols::hashtable::*; 2 | use std::cols::arr::*; 3 | 4 | 5 | 6 | pub fn test_print() i64 { 7 | let a = 0.0; 8 | println!(a); 9 | let m = "🌏Hello, 世界!"; 10 | println!(m); 11 | let cs = m.chars(); 12 | println!(cs); 13 | let h = new_hash_table(10 as u64, 1 as u64); 14 | h.insert(1, 2); 15 | h.insert(2, 3); 16 | println!(-0); 17 | println!(456 as i32); 18 | println!(-789 as i16); 19 | println!(12 as i8); 20 | println!(123); 21 | println!(456 as u32); 22 | println!(789 as u16); 23 | println!(12 as u8); 24 | println!(123.456); 25 | println!(-789.012); 26 | println!(1.0/3.0); 27 | println!(-0.123 as f32); 28 | println!(-10.0); 29 | println!([1, 2, 3, 4]); 30 | println!(from_slice([1, 2, 3, 4])); 31 | println!(h); 32 | println!(1,2,3); 33 | println!(123); 34 | let b = '😀'; 35 | println!(b); 36 | println!(0.000093999999999999994); 37 | return 0; 38 | } -------------------------------------------------------------------------------- /test/test/rand.pi: -------------------------------------------------------------------------------- 1 | use std::rand; 2 | use std::userrand; 3 | use core::panic::*; 4 | use std::io; 5 | use std::math; 6 | 7 | pub fn test_rand() void { 8 | let rng = userrand::new(12); 9 | // test_ppm(); // 生成ppm,查看分布 10 | for let i = 0; i < 10000; i = i + 1 { 11 | let r1 = rand::randi64n(5); 12 | assert(r1 >= 0 && r1 < 5); 13 | let r2 = rand::randi64r(2, 9); 14 | assert(r2 >= 2 && r2 < 9); 15 | let r3 = rng.randi64(); 16 | assert(r3 <= *userrand::RAND_MAX && r3 >= *userrand::RAND_MIN); 17 | let r6 = rng.randf64(); 18 | assert(r6 <= 1.0 && r6 >= 0.0); 19 | let r4 = rng.randi64n(10); 20 | assert(r4 < 10 && r4 >= 0); 21 | let r5 = rng.randi64r(5, 10); 22 | assert(r5 < 10 && r5 >= 5); 23 | let r7 = rng.randf64r(5.0, 10.0); 24 | assert(r7 <= 10.0 && r7 >= 5.0); 25 | } 26 | return; 27 | } 28 | 29 | pub fn test_ppm() void { 30 | let size = 1024; 31 | init_ppm(size, size); 32 | let num = size * size / 64; 33 | let rng = userrand::new(0); 34 | for let i = 0; i < num; i = i + 1 { 35 | let r = rng.randi64() as u64; 36 | // let r = rand::randi64() as u64; 37 | gen_line(r); 38 | } 39 | return; 40 | } 41 | 42 | fn init_ppm(width: i64, height: i64) void { 43 | io::print_s("P3\n"); 44 | print_i64(width); 45 | print_s(" "); 46 | print_i64(height); 47 | print_s("\n255\n"); 48 | return; 49 | } 50 | 51 | /// if bit i is 0, then the pixel is black 52 | fn gen_line(data: u64) void { 53 | for let i: u64 = 0; i < 64; i = i + 1 { 54 | if data & 1 == 0 { 55 | print_s("0 0 0\n"); 56 | } else { 57 | print_s("255 255 255\n"); 58 | } 59 | data = data >> 1; 60 | } 61 | return; 62 | 63 | } -------------------------------------------------------------------------------- /test/test/simple.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | pub fn test_warn() void { 3 | return; 4 | let x = 1; 5 | panic::pl_panic(); 6 | } 7 | 8 | pub fn test_primitives() void { 9 | let test: i8 = 1; 10 | test = -test; 11 | let test2: i8 = test + 1; 12 | let testf32: f32 = 1.1; 13 | testf32 = -testf32; 14 | testf32 = testf32 + 1.2; 15 | let utest: u8 = 1; 16 | utest = utest + 2; 17 | test = 127 as i8; 18 | panic::assert(test == 0x7f); 19 | panic::assert(test == 0o177); 20 | panic::assert(test == 0b0111_1111); 21 | test = -test - 1; 22 | panic::assert(test == 0x80); 23 | panic::assert(test == 0o200); 24 | panic::assert(test == 0b1000_0000); 25 | panic::assert(test as i64 == -128); 26 | panic::assert(test as i64 == 0xffff_ffff_ffff_ff80); 27 | panic::assert(test as i64 == 0o1777777777777777777600); 28 | panic::assert(test as i64 == 0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_10000000); 29 | let b = test | 1; 30 | panic::assert(b as i64 == -127); 31 | let c = test & 1; 32 | panic::assert(c == 0); 33 | let d = test ^ 1; 34 | panic::assert(d as i64 == -127); 35 | let e = test << 1; 36 | panic::assert(e as i64 == 0); 37 | let f = test >> 1; 38 | panic::assert(f as i64 == -64); 39 | let g = test as u8 >> 1; 40 | panic::assert(g as i64 == 64); 41 | let h = ~test ; 42 | panic::assert(h as i64 == 127); 43 | return; 44 | } 45 | 46 | pub fn test_vm_link() i64; 47 | 48 | pub fn test_vm() void { 49 | panic::assert(test_vm_link() == 66); 50 | return; 51 | } 52 | 53 | pub fn test_pointer() void { 54 | let a = 1; 55 | let b = &a; 56 | *b = 100; 57 | panic::assert(*b == 100); 58 | panic::assert(a == 100); 59 | return; 60 | } 61 | 62 | pub fn test_simple() void { 63 | test_vm(); 64 | test_primitives(); 65 | test_warn(); 66 | test_pointer(); 67 | return; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /test/test/sort_test.pi: -------------------------------------------------------------------------------- 1 | use core::ord::*; 2 | use core::panic::*; 3 | use core::eq::*; 4 | 5 | pub fn test_sort() void { 6 | let arr = [3, 9, 1, 4, 5, 8, 7, 6, 2, 10]; 7 | 8 | arr.sort(); 9 | assert(arr.eq(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); 10 | return; 11 | } -------------------------------------------------------------------------------- /test/test/str.pi: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use core::panic; 3 | use project1::test::macros::test; 4 | pub fn test_string() void { 5 | let s = "hello world!\n"; 6 | test!(s = 1+2*(3+4), s = 5*2,); 7 | test!(s); 8 | panic::assert(s.len() == 13); 9 | panic::assert(s.byte_len() == 13); 10 | let ss = "你好啊!\n"; 11 | panic::assert(ss.len() == 5); 12 | panic::assert(ss.byte_len() == 13); 13 | s.append(ss); 14 | println!(s.len()); 15 | panic::assert(s.len() == 18); 16 | panic::assert(s.byte_len() == 26); 17 | io::print_s(s); 18 | return; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /test/test/sub_module.pi: -------------------------------------------------------------------------------- 1 | pub struct A { 2 | } 3 | 4 | pub struct B { 5 | x: A; 6 | } 7 | 8 | pub struct WithGeneric { 9 | x: T; 10 | } 11 | 12 | pub fn testfn(x: A) void { 13 | return; 14 | } 15 | 16 | impl A { 17 | pub fn testimpl() void { 18 | return; 19 | } 20 | 21 | } 22 | 23 | pub fn test_generic(x: T) T { 24 | return x; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /test/test/time.pi: -------------------------------------------------------------------------------- 1 | use std::time; 2 | 3 | 4 | pub fn test_time() i64 { 5 | let t = time::now(); 6 | 7 | let aa = 0; 8 | for let i = 0; i < 1000; i = i + 1 { 9 | aa = aa + i; 10 | } 11 | let ep = t.elapsed(); 12 | println!(ep.secs()); 13 | println!(ep.sub_sec_nanos()); 14 | println!(ep); 15 | return 0; 16 | } -------------------------------------------------------------------------------- /test/test/tree.pi: -------------------------------------------------------------------------------- 1 | struct Node { 2 | left: Option<*Node>; 3 | right: Option<*Node>; 4 | } 5 | 6 | fn build_tree() Node { 7 | let n = &Node{}; 8 | let orin = n; 9 | for let i = 0; i < 10; i = i + 1 { 10 | n.left = &Node{}; 11 | n.right = &Node{}; 12 | if i - i / 2 * 2 == 1 { 13 | n.left = &Node{}; 14 | n = n.left as *Node!; 15 | } else { 16 | n.right = &Node{}; 17 | n = n.right as *Node!; 18 | } 19 | } 20 | n = orin; 21 | for let i = 0; i < 10; i = i + 1 { 22 | if i - i / 2 * 2 == 1 { 23 | n = n.left as *Node!; 24 | } else { 25 | n = n.right as *Node!; 26 | } 27 | } 28 | return *orin; 29 | } 30 | 31 | /// 测试驱逐算法 32 | /// 33 | /// 这里应该会触发驱逐算法 34 | /// 35 | /// 如果我们没有正确处理引用关系或者gc存在bug,很可能会导致segfault 36 | pub fn test_eva() void { 37 | let n = build_tree(); 38 | for let i = 0; i < 10; i = i + 1 { 39 | if i - i / 2 * 2 == 1 { 40 | n = *n.left as *Node!; 41 | } else { 42 | n = *n.right as *Node!; 43 | } 44 | } 45 | return; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /test/test/tuple.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | pub fn test_tuple() void { 3 | let d: () = (); 4 | let a = (1,); 5 | // tuple 6 | let g = (1); 7 | // i64 8 | let b = (1, 2); 9 | let c: (i64, i64, (i64)) = (1, 2, (3,)); 10 | let e = a.0; 11 | let f = c.2; 12 | panic::assert(f.0 == 3); 13 | println!((1,2)); 14 | return; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /test/test/union.pi: -------------------------------------------------------------------------------- 1 | use core::panic; 2 | type A = f32 | T; 3 | type B = i32 | A; 4 | type D = *i32 | f64; 5 | pub fn test_union() i64 { 6 | let g: Option = None{}; 7 | panic::assert(g is None); 8 | g = 1 as Option; 9 | let a: i128 = 1; 10 | let b: f32 = 1.0; 11 | let c: A = a; 12 | let h = c as i128?; 13 | let re = h.map(|a: i128| => i64 { 14 | let b = a as i64; 15 | return b + 100; 16 | }); 17 | let re1 = re as i64!; 18 | panic::assert(re1 == 101); 19 | let i = h as i128!; 20 | panic::assert(i == 1); 21 | let d = c; 22 | d = b as A; 23 | let e = a as f32; 24 | e = 100.1 as f32; 25 | let f = e as i64; 26 | panic::assert(f == 100); 27 | let aa: B = d; 28 | let bb = aa as i32?; 29 | panic::assert(bb is None); 30 | let cc = aa as A!; 31 | cc.name(); 32 | let dd = cc as f32!; 33 | panic::assert(dd == 1.0); 34 | let j = test_ret_union() as i64!; 35 | panic::assert(j == 101); 36 | let gg = 1 as i32; 37 | let ff: D = ≫ 38 | panic::assert(ff is *i32); 39 | let hh = ff as *i32!; 40 | *hh = 101; 41 | let ii = ff as *i32!; 42 | panic::assert(*ii == 101); 43 | return 0; 44 | } 45 | 46 | fn test_ret_union() Option { 47 | return 101; 48 | } 49 | 50 | impl A { 51 | fn name() void { 52 | let f = (*self) as f32!; 53 | f = 100.0 as f32; 54 | return; 55 | } 56 | 57 | } 58 | 59 | type Name = i32 | i64; 60 | fn generic(a: Option) void { 61 | let c = a as R!; 62 | a = c as Option; 63 | return; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /test/tmod.pi: -------------------------------------------------------------------------------- 1 | pub struct mod_struct { 2 | } 3 | 4 | -------------------------------------------------------------------------------- /test/tmod1.pi: -------------------------------------------------------------------------------- 1 | use project1::tmod; 2 | pub trait namet { 3 | fn nametf() void; 4 | 5 | } 6 | 7 | impl namet for tmod::mod_struct { 8 | fn nametf() void { 9 | return; 10 | } 11 | 12 | } 13 | 14 | pub fn name(a:tmod::mod_struct) void { 15 | 16 | return; 17 | } 18 | 19 | // TODO 20 | impl tmod::mod_struct { 21 | } 22 | 23 | impl namet for i64 { 24 | fn nametf() void { 25 | return; 26 | } 27 | 28 | } 29 | 30 | -------------------------------------------------------------------------------- /test/tmod2.pi: -------------------------------------------------------------------------------- 1 | use project1::tmod::mod_struct; 2 | use project1::tmod1::namet; 3 | pub fn test_mod_impl() void{ 4 | let a = 1; 5 | 1.nametf(); 6 | a.nametf(); 7 | let n = tmod::mod_struct{}; 8 | tmod1::name(n); 9 | n.nametf(); 10 | return ; 11 | } 12 | 13 | 14 | pub struct mod1_name { 15 | pub f:bool; 16 | pub dd:bool; 17 | pub ddd:tname; 18 | pub s:gc::string; 19 | } 20 | 21 | pub struct tname { 22 | fjdkslfjsl:bool; 23 | } 24 | -------------------------------------------------------------------------------- /vm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | internal_macro = { path = "../internal_macro",default-features = false } 10 | backtrace = "0.3" 11 | immix = { path = "../immix", default-features = false, features = ["llvm_stackmap", "auto_gc", "madv_dontneed", "conservative_stack_scan", "c-api"] } 12 | # env_logger = "0.10" 13 | log = { version = "0.4", features = ["std"] } 14 | libc = "0.2" 15 | bytecount = "0.6.3" 16 | context = "3.0.0" 17 | thread-priority = "1.2.0" 18 | [target.'cfg(windows)'.dependencies] 19 | winapi = { version = "0.3", features = ["winuser", "wincrypt", "ws2def"] } 20 | 21 | [dev-dependencies] 22 | rand = "0.8" 23 | vm = { path = ".", features = ["unittest"] } 24 | 25 | [build-dependencies] 26 | cc = "1.0" 27 | cmake = "0.1" 28 | 29 | [lib] 30 | name = "vm" 31 | path = "src/lib.rs" 32 | crate-type = ["staticlib", "lib", "cdylib"] 33 | 34 | [features] 35 | default = ["static", "immix", "nolog", "jitdylib"] 36 | jitdylib = [] 37 | static = ["internal_macro/static"] 38 | immix = [] 39 | nolog = ["log/release_max_level_off"] 40 | unittest = [] 41 | -------------------------------------------------------------------------------- /vm/src/compiler_rt.rs: -------------------------------------------------------------------------------- 1 | //! walkaround for windows, see https://discourse.llvm.org/t/orc-jit-on-windows-cannot-find-divti3/78676/2 2 | use internal_macro::is_runtime; 3 | 4 | #[is_runtime] 5 | fn __divti3(a: i128, b: i128) -> i128 { 6 | a / b 7 | } 8 | 9 | #[is_runtime] 10 | fn __modti3(a: i128, b: i128) -> i128 { 11 | a % b 12 | } 13 | -------------------------------------------------------------------------------- /vm/src/gc/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "immix")] 2 | pub use immix::c_api::*; 3 | -------------------------------------------------------------------------------- /vm/src/mutex/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::Cell, 3 | mem, 4 | sync::{Condvar, Mutex, MutexGuard}, 5 | }; 6 | 7 | use internal_macro::is_runtime; 8 | 9 | struct MutexContainer { 10 | mutex: Mutex<()>, 11 | guard: Cell>>, 12 | } 13 | pub struct OpaqueMutex { 14 | _data: [usize; 0], 15 | } 16 | 17 | #[is_runtime] 18 | fn create_mutex(mutex: *mut *mut OpaqueMutex) -> u64 { 19 | // immix::pin(mutex.cast()); 20 | // immix::gc_keep_live_pinned(mutex.cast()); 21 | *mutex = Box::into_raw(Box::new(MutexContainer { 22 | mutex: Mutex::new(()), 23 | guard: Cell::new(None), 24 | })) 25 | .cast(); 26 | // eprintln!("mutex: {:p} -> {:p}", mutex,*mutex); 27 | fn drop_mutex_f(mutex: *mut u8) { 28 | unsafe { 29 | drop(Box::from_raw(mutex.cast::())); 30 | } 31 | } 32 | immix::gc_register_finalizer(mutex as _, (*mutex) as _, drop_mutex_f); 33 | 0 34 | } 35 | 36 | #[is_runtime] 37 | fn lock_mutex(mutex: *mut OpaqueMutex) -> u64 { 38 | // eprintln!("lock mutex: {:p}", mutex); 39 | let container: &MutexContainer = &*mutex.cast(); 40 | // immix::thread_stuck_start(); 41 | let lock: MutexGuard<'static, _> = mem::transmute(container.mutex.lock().unwrap()); 42 | // immix::thread_stuck_end(); 43 | container.guard.set(Some(lock)); 44 | 0 45 | } 46 | 47 | #[is_runtime] 48 | fn unlock_mutex(mutex: *mut OpaqueMutex) -> u64 { 49 | let container: &MutexContainer = &*mutex.cast(); 50 | if container.mutex.try_lock().is_ok() { 51 | return !0; //can't unlock an unlocked mutex 52 | } else { 53 | container.guard.set(None); 54 | } 55 | 0 56 | } 57 | 58 | #[is_runtime] 59 | fn create_condvar(cv: *mut *mut Condvar) -> u64 { 60 | let condvar = Box::leak(Box::new(Condvar::new())); 61 | *cv = condvar; 62 | 0 63 | } 64 | 65 | #[is_runtime] 66 | fn drop_condvar(cond: *mut Condvar) -> u64 { 67 | drop(Box::from_raw(cond)); 68 | 0 69 | } 70 | 71 | #[is_runtime] 72 | fn condvar_wait(cond: *mut Condvar, mutex: *mut OpaqueMutex) -> u64 { 73 | // eprintln!("condvar wait mutex: {:p}", mutex); 74 | let container: &MutexContainer = &*mutex.cast(); 75 | let lock = container.guard.replace(None).unwrap(); 76 | let cond = unsafe { &*cond }; 77 | let lock = cond.wait::<()>(lock).unwrap(); 78 | container.guard.set(Some(lock)); 79 | 0 80 | } 81 | 82 | #[is_runtime] 83 | fn condvar_notify(cond: *mut Condvar) -> u64 { 84 | // eprintln!("condvar notify"); 85 | let cond = unsafe { &*cond }; 86 | cond.notify_one(); 87 | 0 88 | } 89 | 90 | #[is_runtime] 91 | fn condvar_notify_all(cond: *mut Condvar) -> u64 { 92 | let cond = unsafe { &*cond }; 93 | cond.notify_all(); 94 | 0 95 | } 96 | -------------------------------------------------------------------------------- /vm/src/time/mod.rs: -------------------------------------------------------------------------------- 1 | use internal_macro::is_runtime; 2 | 3 | #[is_runtime] 4 | fn unixtime() -> u64 { 5 | std::time::SystemTime::now() 6 | .duration_since(std::time::UNIX_EPOCH) 7 | .unwrap() 8 | .as_secs() 9 | } 10 | 11 | #[is_runtime] 12 | fn pl_clock_gettime(sec: *mut i64, nano: *mut u32) { 13 | let t = std::time::SystemTime::now() 14 | .duration_since(std::time::UNIX_EPOCH) 15 | .unwrap(); 16 | unsafe { 17 | *sec = t.as_secs() as i64; 18 | *nano = t.subsec_nanos(); 19 | } 20 | } 21 | --------------------------------------------------------------------------------