├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .vscode ├── configurationCache.log ├── dryrun.log ├── launch.json └── targets.log ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── crates ├── blazex │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ ├── lib.rs │ │ ├── main.rs │ │ └── test.rs │ └── stdlib │ │ ├── file_system.c │ │ ├── io.c │ │ ├── os.c │ │ ├── string.c │ │ └── test.c ├── bzxc_lexer │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── literals.rs │ │ └── logical.rs ├── bzxc_llvm │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── oop.rs ├── bzxc_parser │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── parse_result.rs │ │ └── parser │ │ ├── arith_expr.rs │ │ ├── array_expr.rs │ │ ├── atom.rs │ │ ├── c_object.rs │ │ ├── c_to_bzx_obj.rs │ │ ├── call.rs │ │ ├── call_access_expr.rs │ │ ├── class_def.rs │ │ ├── class_init.rs │ │ ├── comp_expr.rs │ │ ├── expr.rs │ │ ├── extern_def.rs │ │ ├── factor.rs │ │ ├── for_expr.rs │ │ ├── fun_def.rs │ │ ├── if_expr.rs │ │ ├── index_expr.rs │ │ ├── mod.rs │ │ ├── obj_expr.rs │ │ ├── obj_prop_expr.rs │ │ ├── power.rs │ │ ├── statement.rs │ │ ├── statements.rs │ │ ├── term.rs │ │ ├── var_expr.rs │ │ └── while_expr.rs ├── bzxc_shared │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── bzxc_type_system │ ├── Cargo.toml │ └── src │ ├── annotate.rs │ ├── constraint.rs │ ├── lib.rs │ ├── llvm_node.rs │ ├── substitution.rs │ ├── type_env.rs │ └── unifier.rs ├── rust-toolchain └── tests └── main └── main.bzx /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests and publish if commit message contains "release" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | release: 13 | name: Run Tests 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | include: 18 | - os: ubuntu-latest 19 | artifact_name: blazex 20 | exe: main 21 | # - os: windows-latest 22 | # artifact_name: blazex.exe 23 | # exe: main.exe 24 | steps: 25 | - name: Checkout code 26 | uses: actions/checkout@v2 27 | - name: Build project 28 | run: make install 29 | - name: Run Tests 30 | run: cargo test -- --test-threads=1 31 | - name: Archive into tar.xz 32 | if: contains(github.event.head_commit.message, 'release') 33 | run: tar cf - ~/.blazex/ | xz -z - > blazex-{{ matrix.os }}.tar.xz 34 | - name: Bump version and push tag/create release point 35 | if: contains(github.event.head_commit.message, 'release') 36 | id: bump_version 37 | uses: anothrNick/github-tag-action@master 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | WITH_V: true 41 | DEFAULT_BUMP: patch 42 | - name: Upload binary to release 43 | if: contains(github.event.head_commit.message, 'release') 44 | uses: svenstaro/upload-release-action@v1-release 45 | with: 46 | repo_token: ${{ secrets.GITHUB_TOKEN }} 47 | file: blazex-${{ matrix.os }}.tar.xz 48 | asset_name: ${{ matrix.artifact_name }} 49 | tag: ${{ steps.bump_version.outputs.new_tag }} 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Rust Build directory 2 | /target 3 | 4 | # Ignore object files 5 | *.o 6 | 7 | # Ignore Executables 8 | *.out 9 | 10 | *.a 11 | 12 | cmake-build-* 13 | 14 | /blazex/ -------------------------------------------------------------------------------- /.vscode/configurationCache.log: -------------------------------------------------------------------------------- 1 | {"buildTargets":["build"],"launchTargets":[],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":[],"compilerArgs":[]},"fileIndex":[]}} -------------------------------------------------------------------------------- /.vscode/dryrun.log: -------------------------------------------------------------------------------- 1 | make --dry-run --always-make --keep-going --print-directory 2 | make: Entering directory '/home/romeah/Desktop/blazex' 3 | 4 | mkdir -p /home/romeah/.blazex 5 | cd /home/romeah/.blazex && \ 6 | wget https://github.com/llvm/llvm-project/releases/download/llvmorg-11.1.0/llvm-11.1.0.src.tar.xz && \ 7 | tar xJf llvm-11.1.0.src.tar.xz && \ 8 | rm -rf llvm-11.1.0.src.tar.xz && \ 9 | mkdir -p llvm-11.1.0.src/build && \ 10 | cd llvm-11.1.0.src/build && \ 11 | cmake .. && \ 12 | cmake --build . && \ 13 | cmake --build . --target install && \ 14 | cmake -DCMAKE_INSTALL_PREFIX=/home/romeah/.blazex/llvm-11.1.0 -P cmake_install.cmake && \ 15 | rm -rf llvm-11.1.0.src 16 | cargo build --locked --target x86_64-unknown-linux-gnu --release 17 | mkdir -p "/home/romeah/.blazex/bin" 18 | cp "./target/x86_64-unknown-linux-gnu/release/blazex" "/home/romeah/.blazex/bin/blazex" 19 | strip "/home/romeah/.blazex/bin/blazex" 20 | make: Leaving directory '/home/romeah/Desktop/blazex' 21 | 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "launch", 7 | "name": "Compile", 8 | "cargo": { 9 | "args": ["build", "--release"], 10 | "filter": { 11 | "kind": "bin", 12 | "name": "blazex" 13 | } 14 | }, 15 | "args": ["examples/main.bzx", "-l"], 16 | "cwd": "${workspaceFolder}" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/targets.log: -------------------------------------------------------------------------------- 1 | make all --print-data-base --no-builtin-variables --no-builtin-rules --question 2 | make: *** No rule to make target 'all'. Stop. 3 | 4 | # GNU Make 4.3 5 | # Built for x86_64-pc-linux-gnu 6 | # Copyright (C) 1988-2020 Free Software Foundation, Inc. 7 | # License GPLv3+: GNU GPL version 3 or later 8 | # This is free software: you are free to change and redistribute it. 9 | # There is NO WARRANTY, to the extent permitted by law. 10 | 11 | # Make data base, printed on Wed Dec 22 14:34:56 2021 12 | 13 | # Variables 14 | 15 | # environment 16 | GDK_BACKEND = x11 17 | # environment 18 | LC_ALL = C 19 | # environment 20 | VSCODE_IPC_HOOK_EXTHOST = /run/user/1000/vscode-ipc-28ba6725-f53a-4977-beba-5f4794cdfee2.sock 21 | # environment 22 | MANDATORY_PATH = /usr/share/gconf/cutefish-xsession.mandatory.path 23 | # environment 24 | LC_NAME = en_IN 25 | # environment 26 | LC_NUMERIC = en_US.UTF-8 27 | # environment 28 | VSCODE_CWD = /home/romeah 29 | # environment 30 | NVM_DIR = /home/romeah/.config/nvm 31 | # environment 32 | LC_ADDRESS = en_IN 33 | # default 34 | MAKE_COMMAND := make 35 | # environment 36 | QT_ACCESSIBILITY = 1 37 | # automatic 38 | @D = $(patsubst %/,%,$(dir $@)) 39 | # environment 40 | GDK_DPI_SCALE = 1 41 | # environment 42 | VSCODE_HANDLES_UNCAUGHT_ERRORS = true 43 | # default 44 | .VARIABLES := 45 | # environment 46 | PWD = /home/romeah/Desktop/blazex 47 | # automatic 48 | %D = $(patsubst %/,%,$(dir $%)) 49 | # environment 50 | XDG_DATA_DIRS = /usr/share/cutefish-xsession:/usr/local/share/:/usr/share/ 51 | # automatic 52 | ^D = $(patsubst %/,%,$(dir $^)) 53 | # environment 54 | VSCODE_LOG_STACK = false 55 | # automatic 56 | %F = $(notdir $%) 57 | # environment 58 | NVM_INC = /home/romeah/.config/nvm/versions/node/v15.0.0/include/node 59 | # environment 60 | VSCODE_CODE_CACHE_PATH = /home/romeah/.config/Code/CachedData/ccbaa2d27e38e5afa3e5c21c1c7bef4657064247 61 | # environment 62 | XDG_SESSION_PATH = /org/freedesktop/DisplayManager/Session0 63 | # environment 64 | LANG = C 65 | # environment 66 | XAUTHORITY = /home/romeah/.Xauthority 67 | # default 68 | .LOADED := 69 | # default 70 | .INCLUDE_DIRS = /usr/local/include /usr/include /usr/include 71 | # makefile 72 | MAKEFLAGS = pqrR 73 | # makefile 74 | CURDIR := /home/romeah/Desktop/blazex 75 | # environment 76 | VSCODE_PIPE_LOGGING = true 77 | # environment 78 | APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL = true 79 | # environment 80 | LESSOPEN = | /usr/bin/lesspipe %s 81 | # automatic 82 | *D = $(patsubst %/,%,$(dir $*)) 83 | # environment 84 | MFLAGS = -pqrR 85 | # environment 86 | SSH_AUTH_SOCK = /tmp/ssh-oJdJhgNvP7Y7/agent.774 87 | # default 88 | .SHELLFLAGS := -c 89 | # environment 90 | NVM_BIN = /home/romeah/.config/nvm/versions/node/v15.0.0/bin 91 | # environment 92 | XDG_CONFIG_DIRS = /etc/xdg/xdg-cutefish-xsession:/etc/xdg 93 | # automatic 94 | +D = $(patsubst %/,%,$(dir $+)) 95 | # environment 96 | VSCODE_CRASH_REPORTER_START_OPTIONS = {"companyName":"Microsoft","productName":"VSCode","submitURL":"appcenter://code?aid=fba07a4d-84bd-4fc8-a125-9640fc8ce171&uid=3d68ac4c-8afc-4903-ba9c-a0b26ae10084&iid=3d68ac4c-8afc-4903-ba9c-a0b26ae10084&sid=3d68ac4c-8afc-4903-ba9c-a0b26ae10084","uploadToServer":true} 97 | # environment 98 | XDG_SESSION_DESKTOP = Cutefish 99 | # makefile (from 'Makefile', line 1) 100 | MAKEFILE_LIST := Makefile 101 | # automatic 102 | @F = $(notdir $@) 103 | # environment 104 | LC_COLLATE = en_US.UTF-8 105 | # environment 106 | VSCODE_VERBOSE_LOGGING = true 107 | # makefile (from 'Makefile', line 1) 108 | LLVM_SYS_110_PREFIX := /home/romeah/.blazex/llvm-11.1.0 109 | # environment 110 | VSCODE_PID = 1293 111 | # environment 112 | XDG_SESSION_TYPE = x11 113 | # automatic 114 | ?D = $(patsubst %/,%,$(dir $?)) 115 | # environment 116 | XDG_DATA_HOME = /home/romeah/.local/share 117 | # environment 118 | SESSION_MANAGER = 119 | # automatic 120 | *F = $(notdir $*) 121 | # environment 122 | QT_QPA_PLATFORMTHEME = cutefish 123 | # environment 124 | CHROME_DESKTOP = code-url-handler.desktop 125 | # environment 126 | DBUS_SESSION_BUS_ADDRESS = unix:path=/run/user/1000/bus 127 | # automatic 128 | 28 | 29 | USAGE: 30 | blazex [FLAGS] [OPTIONS] 31 | 32 | For more information try --help 33 | ``` 34 | 35 | ## Example 36 | 37 | - Printing the famous "Hello World" 38 | 39 | ```bzx 40 | extern variadic fun printf(string): int; 41 | printf("Hello World!") @ yep as simple as that 42 | ``` 43 | 44 | - Comments 45 | 46 | ```bzx 47 | @ single line comment 48 | @@ 49 | multi-line comment 50 | @@ 51 | ``` 52 | 53 | - Creating and calling functions 54 | 55 | ```bzx 56 | fun sum(a, b) { 57 | var c = a + b; 58 | return c; 59 | } 60 | 61 | println("%i", sum(2, 2)); 62 | ``` 63 | 64 | - Working around with objects 65 | 66 | ```bzx 67 | var obj = { 68 | prop: 5 @ properties should be Identifier or there will be Invalid Syntax Error 69 | } 70 | 71 | println("%i", obj.prop); @ accessing object property 72 | 73 | obj.prop = 10; @ editing object property value 74 | println("%i", obj.prop) @ 10 75 | ``` 76 | 77 | - Classes 78 | 79 | ```bzx 80 | class Main { 81 | var a = 10; @ this is a property 82 | 83 | @ this is constructor 84 | fun() { 85 | soul.a = 5; @ soul is the current object it's operating on 86 | } 87 | 88 | @ this is a method 89 | fun sum_to_a(b) { 90 | soul.a = soul.a + b; 91 | return soul.a; 92 | } 93 | } 94 | 95 | var ins = new Main(); @ creating/initializing a class, returns a object with the properties 96 | 97 | println("%i", ins.sum_to_a(5)); 98 | ``` 99 | 100 | ## Dependencies 101 | 102 | - llvm_sys (Interacting with LLVM) 103 | - codespan-reporting (Errors) 104 | - mimalloc (Memory allocation) 105 | - structopt (Argument parsing) 106 | - notify (Look for file changes) 107 | 108 | ## Contributing 109 | 110 | - Fork the repository 111 | - Create a branch with the patch/feature you want 112 | - Make Changes to the code 113 | - Commit the code (Use the [Emoji Commit Style](https://github.com/BlazifyOrg/pretty-commits)) and the message should ** 114 | NOT** contain the word "release" 115 | - Finally, push the code and make a pull request 116 | 117 | ## Project Structure 118 | 119 | | Crate | Description | 120 | |:---------------------------------------------:|:--------------------------:| 121 | | [blazex](crates/blazex) | The binary | 122 | | [bzxc_lexer](crates/bzxc_lexer) | Lexer for Tokenizing | 123 | | [bzxc_parser](crates/bzxc_parser) | Parser for AST Tree | 124 | | [bzxc_type_system](crates/bzxc_type_system) | Type System | 125 | | [bzxc_llvm](crates/bzxc_llvm) | LLVM IR Code Generation | 126 | | [bzxc_shared](crates/bzxc_shared) | Things Shared among crates | 127 | 128 | ## TODO 129 | 130 | - [x] Type System 131 | - [x] LLVM 132 | - [x] Errors 133 | - [x] Lexer 134 | - [x] Parser 135 | - [x] Type System 136 | - [x] LLVM 137 | - [x] Reading from file 138 | - [x] Lexer 139 | - [x] Parser 140 | - [x] AST 141 | 142 | ## Author 143 | 144 | - [Ronit "RoMeAh" Rahaman](https://blazify.tech/team/) 145 | -------------------------------------------------------------------------------- /crates/blazex/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blazex" 3 | version = "0.0.1" 4 | authors = ["RoMeAh "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | mimalloc = { version = "0.1.25", default_features = false } 11 | structopt = "0.3.21" 12 | notify = "4.0.17" 13 | llvm-sys = "100.0" 14 | bzxc_lexer = { path = "../bzxc_lexer" } 15 | bzxc_parser = { path = "../bzxc_parser" } 16 | bzxc_type_system = { path = "../bzxc_type_system" } 17 | bzxc_llvm = { path = "../bzxc_llvm" } 18 | bzxc_shared = { path = "../bzxc_shared" } 19 | 20 | [build-dependencies] 21 | cc = "1.0" -------------------------------------------------------------------------------- /crates/blazex/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs}; 2 | 3 | fn main() { 4 | println!("cargo:rerun-if-changed=build.rs"); 5 | let files = rerunners("stdlib".to_string()); 6 | let mut out_dir = env::current_exe().ok().unwrap(); 7 | out_dir.pop(); 8 | out_dir.pop(); 9 | out_dir.pop(); 10 | out_dir.push("stdlib"); 11 | 12 | if !out_dir.exists() { 13 | fs::create_dir(&out_dir).unwrap(); 14 | } 15 | cc::Build::new() 16 | .files(files) 17 | .warnings(true) 18 | .extra_warnings(true) 19 | .flag_if_supported("-Wno-unused-result") 20 | .out_dir(out_dir.as_path()) 21 | .opt_level(3) 22 | .warnings_into_errors(true) 23 | .compile("libblazex.a"); 24 | } 25 | 26 | fn rerunners(path: String) -> Vec { 27 | let mut vec = vec![]; 28 | for entry in fs::read_dir(path).unwrap() { 29 | let entry = entry.unwrap(); 30 | let path = entry.path(); 31 | if path.is_file() { 32 | vec.push(path.to_str().unwrap().to_string()); 33 | println!("cargo:rerun-if-changed={}", path.display()); 34 | } else { 35 | vec.extend(rerunners(entry.path().to_str().unwrap().to_string())); 36 | } 37 | } 38 | 39 | vec 40 | } 41 | -------------------------------------------------------------------------------- /crates/blazex/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | 3 | use std::env; 4 | use std::process::Command; 5 | 6 | use llvm_sys::core::{ 7 | LLVMContextCreate, LLVMContextDispose, LLVMCreateBuilderInContext, 8 | LLVMCreateFunctionPassManager, LLVMCreateModuleProviderForExistingModule, LLVMDisposeBuilder, 9 | LLVMDisposeMessage, LLVMDisposeModule, LLVMDumpModule, LLVMInitializeFunctionPassManager, 10 | LLVMModuleCreateWithNameInContext, LLVMSetDataLayout, LLVMSetTarget, 11 | }; 12 | use llvm_sys::error_handling::LLVMEnablePrettyStackTrace; 13 | use llvm_sys::target::{ 14 | LLVMCopyStringRepOfTargetData, LLVM_InitializeAllAsmParsers, LLVM_InitializeAllAsmPrinters, 15 | LLVM_InitializeAllTargetInfos, LLVM_InitializeAllTargetMCs, LLVM_InitializeAllTargets, 16 | }; 17 | use llvm_sys::target_machine::LLVMCodeGenFileType::LLVMObjectFile; 18 | use llvm_sys::target_machine::LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive; 19 | use llvm_sys::target_machine::LLVMCodeModel::LLVMCodeModelDefault; 20 | use llvm_sys::target_machine::LLVMRelocMode::LLVMRelocDefault; 21 | use llvm_sys::target_machine::{ 22 | LLVMCreateTargetDataLayout, LLVMCreateTargetMachine, LLVMGetDefaultTargetTriple, 23 | LLVMGetHostCPUFeatures, LLVMGetTargetFromTriple, LLVMTargetMachineEmitToFile, LLVMTargetRef, 24 | }; 25 | use llvm_sys::transforms::scalar::{ 26 | LLVMAddBasicAliasAnalysisPass, LLVMAddCFGSimplificationPass, LLVMAddGVNPass, 27 | LLVMAddInstructionCombiningPass, LLVMAddReassociatePass, 28 | }; 29 | use llvm_sys::transforms::util::LLVMAddPromoteMemoryToRegisterPass; 30 | 31 | pub mod test; 32 | 33 | use bzxc_lexer::Lexer; 34 | use bzxc_llvm::Compiler; 35 | use bzxc_parser::parser::Parser; 36 | use bzxc_shared::to_c_str; 37 | use bzxc_type_system::TypeSystem; 38 | 39 | pub unsafe fn compile( 40 | file_name: String, 41 | cnt: String, 42 | is_quiet: bool, 43 | watch: bool, 44 | no_std: bool, 45 | out_file: String, 46 | llvm: bool, 47 | ) -> i32 { 48 | if !is_quiet { 49 | println!("----BlazeX compiler----"); 50 | println!("Version: {}", env!("CARGO_PKG_VERSION")); 51 | println!("File: {}", file_name); 52 | } 53 | 54 | let name = Box::leak(file_name.to_owned().into_boxed_str()); 55 | let content = Box::leak(cnt.to_owned().into_boxed_str()); 56 | let lexed = Lexer::new(name, content).lex(); 57 | let mut tokens = vec![]; 58 | match lexed { 59 | Ok(lexed) => { 60 | tokens.extend(lexed); 61 | } 62 | Err(error) => { 63 | error.prettify(); 64 | if !watch { 65 | return 1; 66 | } 67 | } 68 | } 69 | 70 | let parsed = Parser::new(tokens).parse(); 71 | if parsed.error.is_some() || parsed.node.is_none() { 72 | parsed.error.unwrap().prettify(); 73 | if !watch { 74 | return 1; 75 | } 76 | } 77 | 78 | let context = LLVMContextCreate(); 79 | let llvm_node = TypeSystem::new(parsed.node.unwrap(), context).llvm_node(); 80 | 81 | let module = LLVMModuleCreateWithNameInContext(to_c_str(name).as_ptr(), context); 82 | let builder = LLVMCreateBuilderInContext(context); 83 | 84 | let fpm = LLVMCreateFunctionPassManager(LLVMCreateModuleProviderForExistingModule(module)); 85 | LLVMAddInstructionCombiningPass(fpm); 86 | LLVMAddReassociatePass(fpm); 87 | LLVMAddGVNPass(fpm); 88 | LLVMAddCFGSimplificationPass(fpm); 89 | LLVMAddBasicAliasAnalysisPass(fpm); 90 | LLVMAddPromoteMemoryToRegisterPass(fpm); 91 | LLVMInitializeFunctionPassManager(fpm); 92 | 93 | LLVMEnablePrettyStackTrace(); 94 | 95 | Compiler::init(context, builder, module, fpm, llvm_node).compile_main(); 96 | if llvm { 97 | LLVMDumpModule(module); 98 | } 99 | 100 | LLVM_InitializeAllTargetInfos(); 101 | LLVM_InitializeAllTargets(); 102 | LLVM_InitializeAllTargetMCs(); 103 | LLVM_InitializeAllAsmParsers(); 104 | LLVM_InitializeAllAsmPrinters(); 105 | 106 | let mut errors = MaybeUninit::uninit(); 107 | let mut target: MaybeUninit = MaybeUninit::uninit(); 108 | let mut ret = LLVMGetTargetFromTriple( 109 | LLVMGetDefaultTargetTriple(), 110 | target.as_mut_ptr(), 111 | errors.as_mut_ptr(), 112 | ); 113 | if ret == 1 { 114 | LLVMDisposeMessage(errors.assume_init()); 115 | } 116 | let machine = LLVMCreateTargetMachine( 117 | target.assume_init(), 118 | LLVMGetDefaultTargetTriple(), 119 | to_c_str("generic").as_ptr(), 120 | LLVMGetHostCPUFeatures(), 121 | LLVMCodeGenLevelAggressive, 122 | LLVMRelocDefault, 123 | LLVMCodeModelDefault, 124 | ); 125 | 126 | LLVMSetTarget(module, LLVMGetDefaultTargetTriple()); 127 | let datalayout = LLVMCreateTargetDataLayout(machine); 128 | let datalayout_str = LLVMCopyStringRepOfTargetData(datalayout); 129 | LLVMSetDataLayout(module, datalayout_str); 130 | LLVMDisposeMessage(datalayout_str); 131 | 132 | ret = LLVMTargetMachineEmitToFile( 133 | machine, 134 | module, 135 | to_c_str(out_file.as_str()).as_ptr() as *mut _, 136 | LLVMObjectFile, 137 | errors.as_mut_ptr(), 138 | ); 139 | if ret == 1 { 140 | LLVMDisposeMessage(errors.assume_init()); 141 | } 142 | 143 | LLVMDisposeBuilder(builder); 144 | LLVMDisposeModule(module); 145 | LLVMContextDispose(context); 146 | 147 | let mut dir = env::current_exe().ok().unwrap(); 148 | dir.pop(); 149 | if dir.ends_with("bin") { 150 | dir.pop(); 151 | } else { 152 | dir.pop(); 153 | dir.pop(); 154 | dir.pop(); 155 | dir.push("blazex"); 156 | } 157 | dir.push("stdlib"); 158 | println!("{:#?}", dir); 159 | 160 | let mut args = vec![ 161 | out_file.clone(), 162 | format!("-o{}", out_file.replace(".o", ".out")), 163 | ]; 164 | 165 | if !no_std { 166 | assert!(dir.is_dir()); 167 | args.push(format!("{}/libblazex.a", dir.to_str().unwrap())); 168 | } 169 | Command::new("clang-10").args(&args[..]).status().unwrap(); 170 | std::fs::remove_file(out_file.clone()).unwrap(); 171 | println!("Compiled executable to {}", out_file.replace(".o", ".out")); 172 | 173 | return 0; 174 | } 175 | -------------------------------------------------------------------------------- /crates/blazex/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | #[global_allocator] 14 | static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; 15 | 16 | use blazex::compile; 17 | use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; 18 | use std::path::PathBuf; 19 | use std::process::exit; 20 | use std::sync::mpsc::channel; 21 | use std::time::Duration; 22 | use structopt::StructOpt; 23 | 24 | /* 25 | * Arguments Struct for CLI Argument Parsing 26 | */ 27 | #[derive(StructOpt)] 28 | struct CmdParams { 29 | /* 30 | * Path to the BlazeX Source code 31 | */ 32 | #[structopt(parse(from_os_str))] 33 | pub path: PathBuf, 34 | 35 | /* 36 | * Name of compiled file (Default: input_file.bze) 37 | */ 38 | #[structopt(parse(from_os_str), long, short = "o")] 39 | pub out: Option, 40 | 41 | /* 42 | * Whether there should be any logging in console (Default: false) 43 | */ 44 | #[structopt(long, short = "q")] 45 | pub quiet: bool, 46 | 47 | /* 48 | * Whether the compiler should compile/run on file changes (Default: false) 49 | */ 50 | #[structopt(long, short = "w")] 51 | pub watch: bool, 52 | 53 | /* 54 | * Spit LLVM or not 55 | */ 56 | #[structopt(long, short = "l")] 57 | pub llvm: bool, 58 | 59 | /* 60 | * Whether it should not link to stdlib or not 61 | */ 62 | #[structopt(long, short = "no-std")] 63 | pub no_std: bool, 64 | } 65 | 66 | /* 67 | * Entry Point of the Compiler 68 | */ 69 | fn main() { 70 | let cmd_params = CmdParams::from_args(); 71 | let file_name = cmd_params.path.as_os_str().to_str().unwrap().to_string(); 72 | if !file_name.ends_with(".bzx") { 73 | eprintln!("Unexpected file {}", file_name); 74 | exit(1); 75 | } 76 | let is_quiet = cmd_params.quiet; 77 | let out_file = if let Some(out) = cmd_params.out { 78 | if out.ends_with(".o") { 79 | out.as_os_str().to_str().unwrap().to_string() 80 | } else { 81 | out.as_os_str().to_str().unwrap().to_string() + ".o" 82 | } 83 | } else { 84 | file_name.clone().replace(".bzx", ".o") 85 | }; 86 | let watch = cmd_params.watch; 87 | let spit_ll = cmd_params.llvm; 88 | let no_std = cmd_params.no_std; 89 | 90 | /* 91 | * Compiling to Object File 92 | */ 93 | let compile_with_config = || { 94 | let cnt = std::fs::read_to_string(file_name.clone()).expect("could not read file"); 95 | unsafe { 96 | compile( 97 | file_name.clone(), 98 | cnt, 99 | is_quiet, 100 | watch, 101 | no_std, 102 | out_file.clone(), 103 | spit_ll, 104 | ) 105 | } 106 | }; 107 | 108 | let init = compile_with_config(); 109 | if !watch { 110 | exit(init) 111 | }; 112 | 113 | let (tx, rx) = channel(); 114 | 115 | let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap(); 116 | 117 | watcher 118 | .watch(file_name.clone(), RecursiveMode::Recursive) 119 | .unwrap(); 120 | 121 | /* 122 | * Triggering the compiler on file change 123 | */ 124 | loop { 125 | match rx.recv() { 126 | Ok(DebouncedEvent::Write(_)) => { 127 | println!("\u{001b}[32;1mChange Detected!\u{001b}[0m"); 128 | compile_with_config(); 129 | } 130 | Ok(_) => {} 131 | Err(e) => { 132 | eprintln!("Watch error: {:?}", e); 133 | if !watch { 134 | exit(1); 135 | } 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /crates/blazex/src/test.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::process::Command; 4 | 5 | #[test] 6 | fn compile() { 7 | let mut test_dir = std::env::current_dir().unwrap(); 8 | test_dir.pop(); 9 | test_dir.pop(); 10 | test_dir.push("tests"); 11 | let parent_folder = std::fs::read_dir(test_dir).unwrap(); 12 | for folder in parent_folder { 13 | if folder.is_err() { 14 | continue; 15 | } 16 | 17 | let child_folder = std::fs::read_dir(folder.unwrap().path()); 18 | if child_folder.is_err() { 19 | continue; 20 | } 21 | 22 | for file in child_folder.unwrap() { 23 | if file.is_err() { 24 | continue; 25 | } 26 | 27 | let file = file.unwrap(); 28 | let file_name = file.file_name().into_string().unwrap(); 29 | if !file_name.ends_with(".bzx") { 30 | continue; 31 | } 32 | 33 | let file_path = file.path().into_os_string().into_string().unwrap(); 34 | let out_file = file_path.replace(".bzx", ".o"); 35 | let cnt = std::fs::read_to_string(file_path).unwrap(); 36 | unsafe { 37 | let res = 38 | super::super::compile(file_name, cnt, false, false, false, out_file, false); 39 | assert_eq!(res, 0); 40 | } 41 | } 42 | } 43 | } 44 | 45 | #[test] 46 | fn run() { 47 | let mut test_dir = std::env::current_dir().unwrap(); 48 | test_dir.pop(); 49 | test_dir.pop(); 50 | test_dir.push("tests"); 51 | let parent_folder = std::fs::read_dir(test_dir).unwrap(); 52 | for folder in parent_folder { 53 | if folder.is_err() { 54 | continue; 55 | } 56 | 57 | let child_folder = std::fs::read_dir(folder.unwrap().path()); 58 | if child_folder.is_err() { 59 | continue; 60 | } 61 | 62 | for file in child_folder.unwrap() { 63 | if file.is_err() { 64 | continue; 65 | } 66 | 67 | let file = file.unwrap(); 68 | let file_name = file.file_name().into_string().unwrap(); 69 | let file_path = file.path().into_os_string().into_string().unwrap(); 70 | 71 | if file_name.ends_with(".bzx") { 72 | continue; 73 | } 74 | 75 | println!("Running {}", file_path); 76 | 77 | let cmd = Command::new(format!("{}", file_path)) 78 | .output() 79 | .expect(&format!("failed to execute {}", file_name)); 80 | assert!(cmd.status.success()); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /crates/blazex/stdlib/file_system.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define _CRT_SECURE_NO_WARNINGS 6 | 7 | 8 | char *read_file(const char *path) { 9 | FILE *fp = fopen(path, "r"); 10 | if (fp == NULL) { 11 | return NULL; 12 | } 13 | 14 | fseek(fp, 0, SEEK_END); 15 | long size = ftell(fp); 16 | fseek(fp, 0, SEEK_SET); 17 | 18 | char *content = malloc(size + 1); 19 | fread(content, 1, size, fp); 20 | content[size] = '\0'; 21 | 22 | fclose(fp); 23 | 24 | return content; 25 | } 26 | 27 | int write_file(const char *path, const char *content) { 28 | FILE *fp = fopen(path, "w"); 29 | if (fp == NULL) { 30 | return -1; 31 | } 32 | 33 | fwrite(content, 1, strlen(content), fp); 34 | fclose(fp); 35 | 36 | return 0; 37 | } 38 | 39 | int delete_file(const char *path) { 40 | return remove(path); 41 | } 42 | -------------------------------------------------------------------------------- /crates/blazex/stdlib/io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int print(const char *fmt, ...) { 6 | va_list args; 7 | va_start(args, fmt); 8 | vprintf(fmt, args); 9 | va_end(args); 10 | 11 | return 0; 12 | } 13 | 14 | int println(const char *fmt, ...) { 15 | va_list args; 16 | va_start(args, fmt); 17 | vprintf(fmt, args); 18 | va_end(args); 19 | 20 | printf("\n"); 21 | 22 | return 0; 23 | } 24 | 25 | int input_int() { 26 | int i; 27 | scanf("%d", &i); 28 | return i; 29 | } 30 | 31 | float input_float() { 32 | float f; 33 | scanf("%f", &f); 34 | return f; 35 | } 36 | 37 | char input_char() { 38 | char c; 39 | scanf("%c", &c); 40 | return c; 41 | } 42 | 43 | char *input_string() { 44 | char *str = (char *) malloc(sizeof(char) * 100); 45 | scanf("%[^\n]%*c", str); 46 | return str; 47 | } 48 | -------------------------------------------------------------------------------- /crates/blazex/stdlib/os.c: -------------------------------------------------------------------------------- 1 | char *platform() { 2 | #ifdef _WIN32 3 | return "win32"; 4 | #elif _WIN64 5 | return "win64"; 6 | #elif __APPLE__ || __MACH__ 7 | return "darwin"; 8 | #elif __linux__ 9 | return "linux"; 10 | #elif __unix__ 11 | return "unix"; 12 | #elif __posix 13 | return "posix"; 14 | #elif __FreeBSD__ 15 | return "freebsd"; 16 | #elif __OpenBSD__ 17 | return "openbsd"; 18 | #elif __NetBSD__ 19 | return "netbsd"; 20 | #elif __DragonFly__ 21 | return "dragonfly"; 22 | #elif __sun 23 | return "sunos"; 24 | #else 25 | return "unknown"; 26 | #endif 27 | } -------------------------------------------------------------------------------- /crates/blazex/stdlib/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char *str_concat(char *s1, char *s2) { 5 | char *s = malloc(strlen(s1) + strlen(s2) + 1); 6 | strcpy(s, s1); 7 | strcat(s, s2); 8 | return s; 9 | } 10 | 11 | char get_char_at(char *s, int i) { 12 | return s[i]; 13 | } -------------------------------------------------------------------------------- /crates/blazex/stdlib/test.c: -------------------------------------------------------------------------------- 1 | struct Obj { 2 | int a; 3 | }; 4 | 5 | struct Obj *accept_obj(struct Obj *x) { 6 | struct Obj *r = x; 7 | return r; 8 | } 9 | -------------------------------------------------------------------------------- /crates/bzxc_lexer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bzxc_lexer" 3 | version = "0.1.0" 4 | authors = ["RoMeAh "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | bzxc_shared = { path = "../bzxc_shared" } -------------------------------------------------------------------------------- /crates/bzxc_lexer/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | mod literals; 15 | mod logical; 16 | use bzxc_shared::{to_static_str, Error, Position, Token, Tokens}; 17 | 18 | /* 19 | * Returns all the keywords in the language 20 | */ 21 | pub fn get_keywords() -> Vec { 22 | vec![ 23 | string("val"), 24 | string("var"), 25 | string("and"), 26 | string("or"), 27 | string("not"), 28 | string("if"), 29 | string("else"), 30 | string("for"), 31 | string("to"), 32 | string("step"), 33 | string("while"), 34 | string("fun"), 35 | string("return"), 36 | string("class"), 37 | string("new"), 38 | string("extern"), 39 | string("soul"), 40 | string("static"), 41 | string("void"), 42 | string("int"), 43 | string("float"), 44 | string("bool"), 45 | string("string"), 46 | string("char"), 47 | string("CObject"), 48 | string("CArray"), 49 | string("CToBzxObject"), 50 | ] 51 | } 52 | 53 | /* 54 | * Retuns a array of all numbers from 0 to 9 55 | */ 56 | pub(crate) fn get_number() -> Vec { 57 | vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 58 | } 59 | 60 | /* 61 | * Converts str to String 62 | */ 63 | fn string(str: &str) -> String { 64 | return String::from(str); 65 | } 66 | 67 | /* 68 | * Return all ascii charecters 69 | */ 70 | pub(crate) fn get_ascii_letters() -> Vec<&'static str> { 71 | "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 72 | .split("") 73 | .collect::>() 74 | } 75 | 76 | /* 77 | * Returns all ascii charecters with numbers 78 | */ 79 | pub(crate) fn get_ascii_letters_and_digits() -> Vec<&'static str> { 80 | let mut vec = get_ascii_letters(); 81 | vec.extend(get_number().iter().map(|x| to_static_str(x.to_string()))); 82 | vec 83 | } 84 | 85 | /* 86 | * Goes through the file and lexes into a Array of token 87 | */ 88 | pub struct Lexer { 89 | pub file_name: String, 90 | pub text: String, 91 | pub current_char: Option, 92 | pub position: Position, 93 | } 94 | 95 | impl Lexer { 96 | /* 97 | * Creates a new Lexer Instance 98 | */ 99 | pub fn new(file_name: &'static str, text: &'static str) -> Lexer { 100 | let lexer = Lexer { 101 | file_name: String::from(file_name), 102 | text: String::from(text), 103 | current_char: Some(text.chars().collect::>()[0]), 104 | position: Position::new(0, file_name, text), 105 | }; 106 | lexer 107 | } 108 | 109 | /* 110 | * Advance to the next charecter is present 111 | */ 112 | fn advance(&mut self) { 113 | self.position.advance(); 114 | if self.text.len() > self.position.index { 115 | let split: Vec = self.text.chars().collect::>(); 116 | self.current_char = Some(split[self.position.index]); 117 | } else { 118 | self.current_char = None; 119 | } 120 | } 121 | 122 | /* 123 | * Lex all charecters into a array of tokens 124 | */ 125 | pub fn lex(&mut self) -> Result, Error> { 126 | let mut tokens: Vec = vec![]; 127 | 128 | while self.current_char.is_some() { 129 | let start = self.position.clone(); 130 | let mut end = self.position.clone(); 131 | end.advance(); 132 | 133 | if [' ', '\t', '\r'].contains(&self.current_char.unwrap()) { 134 | self.advance(); 135 | continue; 136 | } 137 | 138 | if ['\n', ';'].contains(&self.current_char.unwrap()) { 139 | let pos_start = self.position.clone(); 140 | self.advance(); 141 | tokens.push(Token::new( 142 | Tokens::Newline, 143 | pos_start, 144 | self.position.clone(), 145 | )); 146 | continue; 147 | } 148 | 149 | let token = match self.current_char.unwrap() { 150 | '(' => Tokens::LeftParenthesis, 151 | ')' => Tokens::RightParenthesis, 152 | '{' => Tokens::LeftCurlyBraces, 153 | '}' => Tokens::RightCurlyBraces, 154 | '[' => Tokens::LeftSquareBraces, 155 | ']' => Tokens::RightSquareBraces, 156 | ':' => Tokens::Colon, 157 | ',' => Tokens::Comma, 158 | '.' => Tokens::Dot, 159 | _ => Tokens::Unknown, 160 | }; 161 | 162 | let mut token_is_unknown = false; 163 | if token == Tokens::Unknown { 164 | match self.current_char.unwrap() { 165 | '+' => tokens.push(self.make_arith_ops(Tokens::Plus, Tokens::PlusEquals)), 166 | '-' => tokens.push(self.make_arith_ops(Tokens::Minus, Tokens::MinusEquals)), 167 | '*' => { 168 | tokens.push(self.make_arith_ops(Tokens::Multiply, Tokens::MultiplyEquals)) 169 | } 170 | '/' => tokens.push(self.make_arith_ops(Tokens::Divide, Tokens::DivideEquals)), 171 | '%' => tokens.push(self.make_arith_ops(Tokens::Modulo, Tokens::ModuloEquals)), 172 | '^' => tokens.push(self.make_arith_ops(Tokens::Power, Tokens::PowerEquals)), 173 | '@' => self.skip_comment(), 174 | '"' => tokens.push(self.make_string()), 175 | '!' => tokens.push(self.make_not()), 176 | '<' => tokens.push(self.make_less_than()), 177 | '>' => tokens.push(self.make_greater_than()), 178 | '=' => tokens.push(self.make_equals()), 179 | '\'' => tokens.push(self.make_char()?), 180 | '|' => tokens.push(self.make_or()?), 181 | '&' => tokens.push(self.make_and()?), 182 | _ => { 183 | let no = self.current_char.unwrap().to_digit(36); 184 | if no.is_some() { 185 | if get_number().contains(&no.unwrap()) 186 | || self.current_char.unwrap() == '.' 187 | { 188 | tokens.push(self.make_number()); 189 | } else if get_ascii_letters() 190 | .contains(&self.current_char.unwrap().to_string().as_str()) 191 | { 192 | tokens.push(self.make_identifiers()); 193 | } else { 194 | token_is_unknown = true; 195 | } 196 | } else { 197 | token_is_unknown = true; 198 | } 199 | } 200 | } 201 | } else { 202 | tokens.push(Token::new(token, start, end)); 203 | self.advance(); 204 | } 205 | 206 | if token_is_unknown { 207 | let start_1 = self.position.clone(); 208 | self.position.advance(); 209 | let char = self.current_char.unwrap().to_string(); 210 | return Err(Error::new( 211 | "Illegal Character", 212 | start_1, 213 | self.position.clone(), 214 | Box::leak( 215 | format!("Unexpected Character '{}'", char) 216 | .to_owned() 217 | .into_boxed_str(), 218 | ), 219 | )); 220 | } 221 | } 222 | 223 | tokens.push(Token::new( 224 | Tokens::EOF, 225 | self.position.clone(), 226 | self.position.clone(), 227 | )); 228 | Ok(tokens) 229 | } 230 | 231 | /* 232 | * Makes a PLUS or PLUS_EQUALS 233 | */ 234 | fn make_arith_ops(&mut self, no_eq: Tokens, eq: Tokens) -> Token { 235 | let start = self.position.clone(); 236 | self.advance(); 237 | 238 | if self.current_char.unwrap_or(' ') == '=' { 239 | self.advance(); 240 | return Token::new(eq, start, self.position); 241 | } 242 | 243 | return Token::new(no_eq, start, self.position); 244 | } 245 | 246 | /* 247 | * Makes a Identifier or Keyword Token 248 | */ 249 | fn make_identifiers(&mut self) -> Token { 250 | let mut identifier = String::new(); 251 | let start = self.position.clone(); 252 | 253 | while self.current_char.is_some() { 254 | if !get_ascii_letters_and_digits() 255 | .contains(&self.current_char.unwrap().to_string().as_str()) 256 | { 257 | break; 258 | } 259 | identifier.push(self.current_char.unwrap()); 260 | self.advance(); 261 | } 262 | 263 | let identifier_type = if get_keywords().contains(&identifier) { 264 | Tokens::Keyword(to_static_str(identifier)) 265 | } else if identifier == "true".to_string() || identifier == "false".to_string() { 266 | Tokens::Boolean(identifier.parse().ok().unwrap()) 267 | } else { 268 | Tokens::Identifier(to_static_str(identifier)) 269 | }; 270 | Token::new(identifier_type, start, self.position.clone()) 271 | } 272 | 273 | /* 274 | * Returns Nothing but skips through comments 275 | */ 276 | pub fn skip_comment(&mut self) { 277 | self.advance(); 278 | 279 | if self.current_char.unwrap() == '@' { 280 | while self.current_char.is_some() { 281 | self.advance(); 282 | if self.current_char.unwrap() == '@' { 283 | self.advance(); 284 | if self.current_char.unwrap() == '@' { 285 | break; 286 | } 287 | } 288 | } 289 | } 290 | 291 | while self.current_char.is_some() { 292 | if self.current_char.unwrap() == '\n' { 293 | break; 294 | } 295 | self.advance(); 296 | } 297 | 298 | self.advance(); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /crates/bzxc_lexer/src/literals.rs: -------------------------------------------------------------------------------- 1 | use bzxc_shared::{to_static_str, Error, Token, Tokens}; 2 | 3 | use crate::Lexer; 4 | 5 | impl Lexer { 6 | /* 7 | * Makes a number token 8 | */ 9 | pub(crate) fn make_number(&mut self) -> Token { 10 | let mut str_num = String::new(); 11 | let mut dot_count = 0; 12 | let start = self.position.clone(); 13 | 14 | while self.current_char.is_some() { 15 | if self.current_char.unwrap().to_digit(36).is_none() 16 | && self.current_char.unwrap() != '.' 17 | { 18 | break; 19 | } 20 | if self.current_char.unwrap() == '.' { 21 | dot_count += 1; 22 | } 23 | str_num.push(self.current_char.unwrap()); 24 | self.advance(); 25 | } 26 | 27 | return if dot_count > 0 { 28 | Token::new( 29 | Tokens::Float(str_num.parse::().unwrap()), 30 | start, 31 | self.position.clone(), 32 | ) 33 | } else { 34 | Token::new( 35 | Tokens::Int(str_num.parse::().unwrap()), 36 | start, 37 | self.position.clone(), 38 | ) 39 | }; 40 | } 41 | 42 | /* 43 | * Makes a String Token 44 | */ 45 | pub(crate) fn make_string(&mut self) -> Token { 46 | let mut str_raw = String::new(); 47 | let start = self.position.clone(); 48 | let mut escape = false; 49 | self.advance(); 50 | 51 | while self.current_char.is_some() || escape { 52 | if self.current_char.unwrap() == '"' { 53 | break; 54 | } 55 | if escape { 56 | if self.current_char.unwrap() == 'n' { 57 | str_raw.push('\n'); 58 | } else if self.current_char.unwrap() == 't' { 59 | str_raw.push('\t'); 60 | } else { 61 | str_raw.push(self.current_char.unwrap()); 62 | } 63 | } else { 64 | if self.current_char.unwrap() == '\\' { 65 | escape = true; 66 | self.advance(); 67 | continue; 68 | } else { 69 | str_raw.push(self.current_char.unwrap()); 70 | } 71 | } 72 | 73 | self.advance(); 74 | escape = false; 75 | } 76 | 77 | self.advance(); 78 | 79 | Token::new( 80 | Tokens::String(to_static_str(str_raw)), 81 | start, 82 | self.position.clone(), 83 | ) 84 | } 85 | 86 | /* 87 | * Makes a charecter token 88 | */ 89 | pub(crate) fn make_char(&mut self) -> Result { 90 | let start = self.position.clone(); 91 | 92 | self.advance(); 93 | let new_char = self.current_char; 94 | self.advance(); 95 | 96 | if self.current_char.unwrap_or(' ') != '\'' { 97 | return Err(Error::new( 98 | "Expected Character", 99 | start, 100 | self.position.clone(), 101 | "Expected Character \"'\" because chars are unicode characters.", 102 | )); 103 | } 104 | 105 | self.advance(); 106 | 107 | Ok(Token::new( 108 | Tokens::Char(new_char.unwrap()), 109 | start, 110 | self.position.clone(), 111 | )) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /crates/bzxc_lexer/src/logical.rs: -------------------------------------------------------------------------------- 1 | use bzxc_shared::{Error, Token, Tokens}; 2 | 3 | use crate::Lexer; 4 | 5 | impl Lexer { 6 | /* 7 | * Makes a DOUBLE_EQUALS | ARROW | EQUALS Token 8 | */ 9 | pub(crate) fn make_equals(&mut self) -> Token { 10 | let start = self.position.clone(); 11 | self.advance(); 12 | 13 | if self.current_char.unwrap_or(' ') == '=' { 14 | self.advance(); 15 | return Token::new(Tokens::DoubleEquals, start, self.position.clone()); 16 | } 17 | 18 | Token::new(Tokens::Equals, start, self.position.clone()) 19 | } 20 | 21 | /* 22 | * Makes a LESS_THAN or LESS_THAN_EQUALS Token 23 | */ 24 | pub(crate) fn make_less_than(&mut self) -> Token { 25 | let start = self.position.clone(); 26 | self.advance(); 27 | 28 | if self.current_char.unwrap_or(' ') == '=' { 29 | self.advance(); 30 | return Token::new(Tokens::LessThanEquals, start, self.position.clone()); 31 | } 32 | 33 | Token::new(Tokens::LessThan, start, self.position.clone()) 34 | } 35 | 36 | /* 37 | * Makes a GREATER_THAN or GREATER_THAN_EQUALS Token 38 | */ 39 | pub(crate) fn make_greater_than(&mut self) -> Token { 40 | let start = self.position.clone(); 41 | self.advance(); 42 | 43 | if self.current_char.unwrap_or(' ') == '=' { 44 | self.advance(); 45 | return Token::new(Tokens::GreaterThanEquals, start, self.position.clone()); 46 | } 47 | 48 | Token::new(Tokens::GreaterThan, start, self.position.clone()) 49 | } 50 | 51 | /* 52 | * Makes a NOT or NOT_EQUALS Token 53 | */ 54 | pub(crate) fn make_not(&mut self) -> Token { 55 | let start = self.position.clone(); 56 | self.advance(); 57 | 58 | if self.current_char.unwrap_or(' ') == '=' { 59 | self.advance(); 60 | return Token::new(Tokens::NotEquals, start, self.position.clone()); 61 | } 62 | 63 | Token::new(Tokens::Keyword("not"), start, self.position.clone()) 64 | } 65 | 66 | /* 67 | * Makes a NOT Token 68 | */ 69 | pub(crate) fn make_or(&mut self) -> Result { 70 | let start = self.position.clone(); 71 | self.advance(); 72 | 73 | if self.current_char.unwrap_or(' ') == '|' { 74 | self.advance(); 75 | return Ok(Token::new( 76 | Tokens::Keyword("or"), 77 | start, 78 | self.position.clone(), 79 | )); 80 | } 81 | 82 | Err(Error::new( 83 | "Expected Character", 84 | start, 85 | self.position.clone(), 86 | "Expected one more '|'", 87 | )) 88 | } 89 | 90 | /* 91 | * Makes a AND Token 92 | */ 93 | pub(crate) fn make_and(&mut self) -> Result { 94 | let start = self.position.clone(); 95 | self.advance(); 96 | 97 | if self.current_char.unwrap_or(' ') == '&' { 98 | self.advance(); 99 | return Ok(Token::new( 100 | Tokens::Keyword("and"), 101 | start, 102 | self.position.clone(), 103 | )); 104 | } 105 | 106 | Err(Error::new( 107 | "Expected Character", 108 | start, 109 | self.position.clone(), 110 | "Expected one more '&'", 111 | )) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /crates/bzxc_llvm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bzxc_llvm" 3 | version = "0.1.0" 4 | authors = ["RoMeAh "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | llvm-sys = "100.0" 11 | bzxc_shared = { path = "../bzxc_shared" } -------------------------------------------------------------------------------- /crates/bzxc_llvm/src/oop.rs: -------------------------------------------------------------------------------- 1 | use llvm_sys::core::{ 2 | LLVMBuildInsertValue, LLVMBuildStore, LLVMBuildStructGEP, LLVMConstNull, LLVMFunctionType, 3 | LLVMGetArrayLength, LLVMGetElementType, LLVMGetReturnType, LLVMGetUndef, LLVMPointerType, 4 | LLVMStructGetTypeAtIndex, LLVMTypeOf, 5 | }; 6 | use llvm_sys::prelude::{LLVMTypeRef, LLVMValueRef}; 7 | 8 | use bzxc_shared::{to_c_str, LLVMNode}; 9 | 10 | use crate::Compiler; 11 | 12 | impl Compiler { 13 | pub(super) unsafe fn create_obj( 14 | &mut self, 15 | ty: LLVMTypeRef, 16 | properties: Vec<(String, LLVMNode)>, 17 | ) -> LLVMValueRef { 18 | let aligner_ty = LLVMGetElementType(LLVMStructGetTypeAtIndex(LLVMGetElementType(ty), 0)); 19 | 20 | let aligner = LLVMGetArrayLength(aligner_ty) as usize as u32; 21 | 22 | let aligner_val = LLVMConstNull(aligner_ty); 23 | let aligner_ptr = self.create_entry_block_alloca("aligner", aligner_ty); 24 | LLVMBuildStore(self.builder, aligner_val, aligner_ptr); 25 | 26 | let struct_val = LLVMBuildInsertValue( 27 | self.builder, 28 | LLVMGetUndef(LLVMGetElementType(ty)), 29 | aligner_ptr, 30 | 0, 31 | to_c_str("c_to_bzx_obj_load").as_ptr(), 32 | ); 33 | 34 | let ptr = self.create_entry_block_alloca("obj", LLVMTypeOf(struct_val)); 35 | LLVMBuildStore(self.builder, struct_val, ptr); 36 | 37 | for (i, (name, val)) in properties.iter().enumerate() { 38 | let idx = i + 1; 39 | self.objects.insert((name.clone(), aligner), idx); 40 | LLVMBuildStore( 41 | self.builder, 42 | self.compile(val.clone()), 43 | LLVMBuildStructGEP( 44 | self.builder, 45 | ptr, 46 | idx as u32, 47 | to_c_str("struct_gep").as_ptr(), 48 | ), 49 | ); 50 | } 51 | 52 | ptr 53 | } 54 | 55 | pub(super) unsafe fn obj_property( 56 | &mut self, 57 | object: LLVMValueRef, 58 | property: String, 59 | ) -> LLVMValueRef { 60 | let i = self 61 | .objects 62 | .get(&( 63 | property, 64 | LLVMGetArrayLength(LLVMGetElementType(LLVMStructGetTypeAtIndex( 65 | LLVMGetElementType(LLVMTypeOf(object)), 66 | 0, 67 | ))) as usize as u32, 68 | )) 69 | .unwrap(); 70 | 71 | LLVMBuildStructGEP( 72 | self.builder, 73 | object, 74 | *i as u32, 75 | to_c_str("obj_load").as_ptr(), 76 | ) 77 | } 78 | 79 | pub(super) unsafe fn class_method( 80 | &mut self, 81 | class: String, 82 | klass: LLVMTypeRef, 83 | method: LLVMNode, 84 | ) -> LLVMValueRef { 85 | match method { 86 | LLVMNode::Fun { 87 | body, 88 | name, 89 | params, 90 | ty, 91 | } => { 92 | let mut n_params = vec![("soul".to_string(), klass)]; 93 | n_params.extend(params); 94 | 95 | let mut pty = n_params 96 | .iter() 97 | .map(|(_, ty)| ty.clone()) 98 | .collect::>(); 99 | 100 | let ty = LLVMFunctionType( 101 | LLVMGetReturnType(LLVMGetElementType(ty)), 102 | pty.as_mut_ptr(), 103 | pty.len() as u32, 104 | 0, 105 | ); 106 | self.compile(LLVMNode::Fun { 107 | body, 108 | name: format!("{}%{}", class, name), 109 | params: n_params.clone(), 110 | ty: LLVMPointerType(ty, 0), 111 | }) 112 | } 113 | _ => unreachable!(), 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /crates/bzxc_parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bzxc_parser" 3 | version = "0.1.0" 4 | authors = ["RoMeAh "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | bzxc_shared = { path = "../bzxc_shared" } -------------------------------------------------------------------------------- /crates/bzxc_parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | pub mod parse_result; 15 | pub mod parser; 16 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parse_result.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use bzxc_shared::{Error, Node}; 15 | 16 | /* 17 | * Result returned after statement(s) are parsed by the error 18 | */ 19 | #[derive(Debug, Clone)] 20 | pub struct ParseResult { 21 | pub node: Option, 22 | pub error: Option, 23 | pub advance_count: usize, 24 | pub to_reverse_count: usize, 25 | } 26 | 27 | impl ParseResult { 28 | /* 29 | * Creates a new instance of Parse Result 30 | */ 31 | pub fn new() -> ParseResult { 32 | ParseResult { 33 | node: None, 34 | error: None, 35 | advance_count: 0, 36 | to_reverse_count: 0, 37 | } 38 | } 39 | 40 | /* 41 | * Registers node and error of a result into the current 42 | */ 43 | pub fn register(&mut self, res: ParseResult) -> Option { 44 | self.advance_count += res.advance_count; 45 | if res.error.is_some() { 46 | self.error = res.error.clone(); 47 | }; 48 | res.node 49 | } 50 | 51 | /* 52 | * Register a Result if there is no error 53 | */ 54 | pub fn try_register(&mut self, res: ParseResult) -> Option { 55 | if res.error.is_some() { 56 | self.to_reverse_count = res.advance_count; 57 | return None; 58 | }; 59 | self.register(res) 60 | } 61 | 62 | /* 63 | * Advance the Node Array Index by one 64 | */ 65 | pub fn register_advancement(&mut self) { 66 | self.advance_count += 1; 67 | } 68 | 69 | /* 70 | * Return a Result with a node 71 | */ 72 | pub fn success(&mut self, node: Node) -> ParseResult { 73 | self.node = Some(node); 74 | self.clone() 75 | } 76 | 77 | /* 78 | * Return a Result with a error 79 | */ 80 | pub fn failure(&mut self, error: Error) -> ParseResult { 81 | self.error = Some(error); 82 | self.clone() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/arith_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parse a arithmetic expression 21 | */ 22 | pub(crate) fn arith_expr(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | 25 | let mut left = res.register(self.term()); 26 | if res.error.is_some() { 27 | return res; 28 | } 29 | 30 | while [Tokens::Plus, Tokens::Minus].contains(&self.current_token.value) { 31 | let op_token = self.current_token.clone(); 32 | res.register_advancement(); 33 | self.advance(); 34 | 35 | let right = res.register(self.term()); 36 | if res.error.is_some() { 37 | return res; 38 | } 39 | 40 | left = Option::from(Node::BinaryNode { 41 | left: Box::new(left.clone().unwrap()), 42 | right: Box::new(right.clone().unwrap()), 43 | op_token, 44 | }); 45 | } 46 | 47 | res.success(left.unwrap()) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/array_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a array 21 | */ 22 | pub(crate) fn array_expr(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let mut element_nodes: Vec = vec![]; 25 | let token = self.current_token.clone(); 26 | let pos_start = self.current_token.pos_start.clone(); 27 | 28 | if self.current_token.value != Tokens::LeftSquareBraces { 29 | return res.failure(Error::new( 30 | "Invalid syntax", 31 | pos_start, 32 | token.pos_end, 33 | "'[' was expected.", 34 | )); 35 | } 36 | 37 | res.register_advancement(); 38 | self.advance(); 39 | 40 | if self.current_token.value == Tokens::RightSquareBraces { 41 | res.register_advancement(); 42 | self.advance(); 43 | } else { 44 | let mut expr = res.register(self.expr()); 45 | if res.error.is_some() { 46 | return res.failure(Error::new( 47 | "Invalid Syntax", 48 | pos_start, 49 | token.pos_end, 50 | "Expected ']', 'var', 'if', 'for', 'while', 'fun', int, float, identifier, '+', '-', '(', '[' or 'NOT'" 51 | )); 52 | } 53 | 54 | element_nodes.push(expr.unwrap()); 55 | while self.current_token.value == Tokens::Comma { 56 | res.register_advancement(); 57 | self.advance(); 58 | 59 | expr = res.register(self.expr()); 60 | if res.error.is_some() { 61 | return res; 62 | } 63 | element_nodes.push(expr.unwrap()); 64 | } 65 | 66 | if self.current_token.value != Tokens::RightSquareBraces { 67 | return res.failure(Error::new( 68 | "Invalid Syntax", 69 | pos_start, 70 | token.pos_end, 71 | "Expected ']' or ','.", 72 | )); 73 | } 74 | res.register_advancement(); 75 | self.advance(); 76 | } 77 | 78 | res.success(Node::ArrayNode { element_nodes }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/atom.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Token, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a atom expression 21 | */ 22 | pub(crate) fn atom(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let token = self.current_token.clone(); 25 | 26 | if let Tokens::Int(_) | Tokens::Float(_) = token.value { 27 | res.register_advancement(); 28 | self.advance(); 29 | return res.success(Node::NumberNode { 30 | token: token.clone(), 31 | }); 32 | } else if let Tokens::Boolean(_) = token.value { 33 | res.register_advancement(); 34 | self.advance(); 35 | return res.success(Node::BooleanNode { 36 | token: token.clone(), 37 | }); 38 | } else if let Tokens::String(_) = token.value { 39 | res.register_advancement(); 40 | self.advance(); 41 | return res.success(Node::StringNode { 42 | token: token.clone(), 43 | }); 44 | } else if let Tokens::Char(_) = token.value { 45 | res.register_advancement(); 46 | self.advance(); 47 | return res.success(Node::CharNode { 48 | token: token.clone(), 49 | }); 50 | } else if let Tokens::Identifier(_) = token.value { 51 | let var_expr = res.register(self.var_expr()); 52 | if res.error.is_some() { 53 | return res; 54 | } 55 | return res.success(var_expr.unwrap()); 56 | } else if token.value == Tokens::LeftParenthesis { 57 | res.register_advancement(); 58 | self.advance(); 59 | let expr = res.register(self.expr()); 60 | if res.error.is_some() { 61 | return res; 62 | } 63 | if self.current_token.clone().value != Tokens::RightParenthesis { 64 | return res.failure(Error::new( 65 | "Invalid Syntax", 66 | self.current_token.clone().pos_start, 67 | self.current_token.clone().pos_end, 68 | "Expected ')'", 69 | )); 70 | } 71 | 72 | res.register_advancement(); 73 | self.advance(); 74 | return res.success(expr.unwrap()); 75 | } else if token.value == Tokens::LeftSquareBraces { 76 | let array_expr = res.register(self.array_expr()); 77 | if res.error.is_some() { 78 | return res; 79 | } 80 | return res.success(array_expr.unwrap()); 81 | } else if token.value == Tokens::LeftCurlyBraces { 82 | let obj_expr = res.register(self.obj_expr()); 83 | if res.error.is_some() { 84 | return res; 85 | } 86 | return res.success(obj_expr.unwrap()); 87 | } else if token.value == Tokens::Keyword("if") { 88 | let if_expr = res.register(self.if_expr()); 89 | if res.error.is_some() { 90 | return res; 91 | } 92 | return res.success(if_expr.unwrap()); 93 | } else if token.value == Tokens::Keyword("while") { 94 | let while_expr = res.register(self.while_expr()); 95 | if res.error.is_some() { 96 | return res; 97 | } 98 | return res.success(while_expr.unwrap()); 99 | } else if token.value == Tokens::Keyword("for") { 100 | let for_expr = res.register(self.for_expr()); 101 | if res.error.is_some() { 102 | return res; 103 | } 104 | return res.success(for_expr.unwrap()); 105 | } else if token.value == Tokens::Keyword("fun") { 106 | let fun_def = res.register(self.fun_def()); 107 | if res.error.is_some() { 108 | return res; 109 | } 110 | return res.success(fun_def.unwrap()); 111 | } else if token.value == Tokens::Keyword("class") { 112 | let class_def = res.register(self.class_def()); 113 | if res.error.is_some() { 114 | return res; 115 | } 116 | return res.success(class_def.unwrap()); 117 | } else if token.value == Tokens::Keyword("new") { 118 | let class_init = res.register(self.class_init()); 119 | if res.error.is_some() { 120 | return res; 121 | } 122 | return res.success(class_init.unwrap()); 123 | } else if token.value == Tokens::Keyword("soul") { 124 | let token = Token::new( 125 | Tokens::Identifier("soul"), 126 | self.current_token.pos_start, 127 | self.current_token.pos_end, 128 | ); 129 | self.advance(); 130 | res.register_advancement(); 131 | 132 | return res.success(Node::VarAccessNode { token }); 133 | } else if token.value == Tokens::Keyword("extern") { 134 | let extern_def = res.register(self.extern_def()); 135 | if res.error.is_some() { 136 | return res; 137 | } 138 | return res.success(extern_def.unwrap()); 139 | } else if token.value == Tokens::Keyword("CObject") { 140 | let c_object_def = res.register(self.c_object()); 141 | if res.error.is_some() { 142 | return res; 143 | } 144 | return res.success(c_object_def.unwrap()); 145 | } else if token.value == Tokens::Keyword("CToBzxObject") { 146 | let c_object_def = res.register(self.c_to_bzx_object()); 147 | if res.error.is_some() { 148 | return res; 149 | } 150 | return res.success(c_object_def.unwrap()); 151 | } else if let Tokens::Keyword(_) = token.value { 152 | self.advance(); 153 | res.register_advancement(); 154 | 155 | return res.success(Node::TypeKeyword { token }); 156 | } 157 | 158 | res.failure(Error::new( 159 | "Invalid Syntax", 160 | token.pos_start, 161 | token.pos_end, 162 | "A Int, Float, String, Char, Keyword, Identifier, '+', '-', '(', etc was Expected", 163 | )) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/c_object.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | pub(crate) fn c_object(&mut self) -> ParseResult { 20 | let mut res = ParseResult::new(); 21 | if self.current_token.value != Tokens::Keyword("CObject") { 22 | return res.failure(Error::new( 23 | "Syntax error", 24 | self.current_token.pos_start.clone(), 25 | self.current_token.pos_end.clone(), 26 | "Expected CObject", 27 | )); 28 | } 29 | 30 | self.advance(); 31 | res.register_advancement(); 32 | 33 | if self.current_token.value != Tokens::LeftParenthesis { 34 | return res.failure(Error::new( 35 | "Syntax error", 36 | self.current_token.pos_start.clone(), 37 | self.current_token.pos_end.clone(), 38 | "Expected (", 39 | )); 40 | } 41 | 42 | self.advance(); 43 | res.register_advancement(); 44 | 45 | let obj = res.register(self.expr()); 46 | if res.error.is_some() { 47 | return res; 48 | } 49 | 50 | if self.current_token.value != Tokens::RightParenthesis { 51 | return res.failure(Error::new( 52 | "Syntax error", 53 | self.current_token.pos_start.clone(), 54 | self.current_token.pos_end.clone(), 55 | "Expected )", 56 | )); 57 | } 58 | 59 | res.register_advancement(); 60 | self.advance(); 61 | 62 | res.success(Node::CObject { 63 | object: Box::new(obj.unwrap()), 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/c_to_bzx_obj.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | pub(crate) fn c_to_bzx_object(&mut self) -> ParseResult { 20 | let mut res = ParseResult::new(); 21 | if self.current_token.value != Tokens::Keyword("CToBzxObject") { 22 | return res.failure(Error::new( 23 | "Syntax error", 24 | self.current_token.pos_start.clone(), 25 | self.current_token.pos_end.clone(), 26 | "Expected CObject", 27 | )); 28 | } 29 | 30 | self.advance(); 31 | res.register_advancement(); 32 | 33 | if self.current_token.value != Tokens::LeftParenthesis { 34 | return res.failure(Error::new( 35 | "Syntax error", 36 | self.current_token.pos_start.clone(), 37 | self.current_token.pos_end.clone(), 38 | "Expected (", 39 | )); 40 | } 41 | 42 | self.advance(); 43 | res.register_advancement(); 44 | 45 | let obj = res.register(self.expr()); 46 | if res.error.is_some() { 47 | return res; 48 | } 49 | 50 | if self.current_token.value == Tokens::Comma { 51 | self.advance(); 52 | res.register_advancement(); 53 | } else { 54 | return res.failure(Error::new( 55 | "Syntax error", 56 | self.current_token.pos_start.clone(), 57 | self.current_token.pos_end.clone(), 58 | "Expected ,", 59 | )); 60 | } 61 | 62 | let obj_type = res.register(self.expr()); 63 | if res.error.is_some() { 64 | return res; 65 | } 66 | 67 | if self.current_token.value != Tokens::RightParenthesis { 68 | return res.failure(Error::new( 69 | "Syntax error", 70 | self.current_token.pos_start.clone(), 71 | self.current_token.pos_end.clone(), 72 | "Expected )", 73 | )); 74 | } 75 | 76 | res.register_advancement(); 77 | self.advance(); 78 | 79 | res.success(Node::CToBzxObject { 80 | object: Box::new(obj.unwrap()), 81 | bzx_object: Box::new(obj_type.unwrap()), 82 | }) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/call.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | 17 | impl Parser { 18 | /* 19 | * Parses a function call 20 | */ 21 | pub(crate) fn call(&mut self) -> ParseResult { 22 | let mut res = ParseResult::new(); 23 | let atom = res.register(self.obj_prop_expr()); 24 | if res.error.is_some() { 25 | return res; 26 | } 27 | 28 | self.call_access_expr(atom, res) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/call_access_expr.rs: -------------------------------------------------------------------------------- 1 | use bzxc_shared::{Error, Node, Tokens}; 2 | 3 | use crate::parse_result::ParseResult; 4 | 5 | use super::Parser; 6 | 7 | impl Parser { 8 | pub(crate) fn call_access_expr( 9 | &mut self, 10 | expr: Option, 11 | mut res: ParseResult, 12 | ) -> ParseResult { 13 | if self.current_token.value == Tokens::Dot { 14 | self.advance(); 15 | res.register_advancement(); 16 | 17 | if let Tokens::Identifier(_) = self.current_token.value { 18 | } else { 19 | return res.failure(Error::new( 20 | "Invalid Syntax", 21 | self.current_token.pos_start.clone(), 22 | self.current_token.pos_end.clone(), 23 | "Expected identifier", 24 | )); 25 | } 26 | 27 | let mut id = self.current_token.clone(); 28 | 29 | res.register_advancement(); 30 | self.advance(); 31 | 32 | let mut l; 33 | 34 | if self.current_token.value == Tokens::Equals { 35 | res.register_advancement(); 36 | self.advance(); 37 | 38 | let val = res.register(self.expr()); 39 | if res.error.is_some() { 40 | return res; 41 | } 42 | 43 | return res.success(Node::ObjectPropEdit { 44 | object: Box::new(expr.clone().unwrap()), 45 | property: id, 46 | new_val: Box::new(val.unwrap()), 47 | }); 48 | } else if self.current_token.value == Tokens::LeftParenthesis { 49 | let mut arg_nodes: Vec = vec![]; 50 | res.register_advancement(); 51 | self.advance(); 52 | 53 | if self.current_token.value == Tokens::RightParenthesis { 54 | res.register_advancement(); 55 | self.advance(); 56 | } else { 57 | let expr = res.register(self.expr()); 58 | if res.error.is_some() { 59 | return res.failure(Error::new( 60 | "Invalid Syntax", 61 | self.current_token.pos_start.clone(), 62 | self.current_token.pos_end.clone(), 63 | "Expected ')', 'var', int, float, identifier, '+', '-' or ','", 64 | )); 65 | } 66 | arg_nodes.push(expr.unwrap()); 67 | 68 | while self.current_token.value == Tokens::Comma { 69 | res.register_advancement(); 70 | self.advance(); 71 | 72 | let expr = res.register(self.expr()); 73 | if res.error.is_some() { 74 | return res.failure(Error::new( 75 | "Invalid Syntax", 76 | self.current_token.pos_start.clone(), 77 | self.current_token.pos_end.clone(), 78 | "Expected ')', 'var', int, float, identifier, '+', '-' or ','", 79 | )); 80 | } 81 | arg_nodes.push(expr.unwrap()); 82 | } 83 | 84 | if self.current_token.value != Tokens::RightParenthesis { 85 | return res.failure(Error::new( 86 | "Invalid Syntax", 87 | self.current_token.pos_start.clone(), 88 | self.current_token.pos_end.clone(), 89 | "Expected ')' or ','", 90 | )); 91 | } 92 | res.register_advancement(); 93 | self.advance(); 94 | } 95 | 96 | l = Node::ObjectMethodCall { 97 | object: Box::new(expr.clone().unwrap()), 98 | property: id, 99 | args: arg_nodes, 100 | } 101 | } else { 102 | l = Node::ObjectPropAccess { 103 | object: Box::new(expr.clone().unwrap()), 104 | property: id, 105 | }; 106 | } 107 | 108 | while self.current_token.value == Tokens::Dot { 109 | self.advance(); 110 | res.register_advancement(); 111 | 112 | if let Tokens::Identifier(_) = self.current_token.value { 113 | } else { 114 | return res.failure(Error::new( 115 | "Invalid Syntax", 116 | self.current_token.pos_start.clone(), 117 | self.current_token.pos_end.clone(), 118 | "Expected identifier", 119 | )); 120 | } 121 | 122 | id = self.current_token.clone(); 123 | 124 | res.register_advancement(); 125 | self.advance(); 126 | 127 | if self.current_token.value == Tokens::Equals { 128 | res.register_advancement(); 129 | self.advance(); 130 | 131 | let expr = res.register(self.expr()); 132 | if res.error.is_some() { 133 | return res; 134 | } 135 | 136 | return res.success(Node::ObjectPropEdit { 137 | object: Box::new(l), 138 | property: id, 139 | new_val: Box::new(expr.unwrap()), 140 | }); 141 | } else if self.current_token.value == Tokens::LeftParenthesis { 142 | let mut arg_nodes: Vec = vec![]; 143 | res.register_advancement(); 144 | self.advance(); 145 | 146 | if self.current_token.value == Tokens::RightParenthesis { 147 | res.register_advancement(); 148 | self.advance(); 149 | } else { 150 | let expr = res.register(self.expr()); 151 | if res.error.is_some() { 152 | return res.failure(Error::new( 153 | "Invalid Syntax", 154 | self.current_token.pos_start.clone(), 155 | self.current_token.pos_end.clone(), 156 | "Expected ')', 'var', int, float, identifier, '+', '-' or ','", 157 | )); 158 | } 159 | arg_nodes.push(expr.unwrap()); 160 | 161 | while self.current_token.value == Tokens::Comma { 162 | res.register_advancement(); 163 | self.advance(); 164 | 165 | let expr = res.register(self.expr()); 166 | if res.error.is_some() { 167 | return res.failure(Error::new( 168 | "Invalid Syntax", 169 | self.current_token.pos_start.clone(), 170 | self.current_token.pos_end.clone(), 171 | "Expected ')', 'var', int, float, identifier, '+', '-' or ','", 172 | )); 173 | } 174 | arg_nodes.push(expr.unwrap()); 175 | } 176 | 177 | if self.current_token.value != Tokens::RightParenthesis { 178 | return res.failure(Error::new( 179 | "Invalid Syntax", 180 | self.current_token.pos_start.clone(), 181 | self.current_token.pos_end.clone(), 182 | "Expected ')' or ','", 183 | )); 184 | } 185 | res.register_advancement(); 186 | self.advance(); 187 | } 188 | 189 | l = Node::ObjectMethodCall { 190 | object: Box::new(l), 191 | property: id, 192 | args: arg_nodes, 193 | } 194 | } else { 195 | l = Node::ObjectPropAccess { 196 | object: Box::new(l), 197 | property: id, 198 | }; 199 | } 200 | } 201 | return res.success(l); 202 | } else if self.current_token.value == Tokens::LeftParenthesis { 203 | let mut arg_nodes: Vec = vec![]; 204 | res.register_advancement(); 205 | self.advance(); 206 | 207 | if self.current_token.value == Tokens::RightParenthesis { 208 | res.register_advancement(); 209 | self.advance(); 210 | } else { 211 | let expr = res.register(self.expr()); 212 | if res.error.is_some() { 213 | return res.failure(Error::new( 214 | "Invalid Syntax", 215 | self.current_token.pos_start.clone(), 216 | self.current_token.pos_end.clone(), 217 | "Expected ')', 'var', int, float, identifier, '+', '-' or ','", 218 | )); 219 | } 220 | arg_nodes.push(expr.unwrap()); 221 | 222 | while self.current_token.value == Tokens::Comma { 223 | res.register_advancement(); 224 | self.advance(); 225 | 226 | let expr = res.register(self.expr()); 227 | if res.error.is_some() { 228 | return res.failure(Error::new( 229 | "Invalid Syntax", 230 | self.current_token.pos_start.clone(), 231 | self.current_token.pos_end.clone(), 232 | "Expected ')', 'var', int, float, identifier, '+', '-' or ','", 233 | )); 234 | } 235 | arg_nodes.push(expr.unwrap()); 236 | } 237 | 238 | if self.current_token.value != Tokens::RightParenthesis { 239 | return res.failure(Error::new( 240 | "Invalid Syntax", 241 | self.current_token.pos_start.clone(), 242 | self.current_token.pos_end.clone(), 243 | "Expected ')' or ','", 244 | )); 245 | } 246 | res.register_advancement(); 247 | self.advance(); 248 | } 249 | return res.success(Node::CallNode { 250 | node_to_call: Box::new(expr.clone().unwrap()), 251 | args: arg_nodes, 252 | }); 253 | } else if self.current_token.value == Tokens::LeftSquareBraces { 254 | res.register_advancement(); 255 | self.advance(); 256 | 257 | let idx = res.register(self.expr()); 258 | if res.error.is_some() { 259 | return res; 260 | } 261 | 262 | if self.current_token.value != Tokens::RightSquareBraces { 263 | return res.failure(Error::new( 264 | "Invalid Syntax", 265 | self.current_token.pos_start.clone(), 266 | self.current_token.pos_end.clone(), 267 | "Expected ']'", 268 | )); 269 | } 270 | 271 | res.register_advancement(); 272 | self.advance(); 273 | 274 | return res.success(Node::ArrayAcess { 275 | array: Box::new(expr.unwrap()), 276 | index: Box::new(idx.unwrap()), 277 | }); 278 | } 279 | 280 | res.success(expr.unwrap()) 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/class_def.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Token, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a class definition 21 | */ 22 | pub(crate) fn class_def(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | 25 | if self.current_token.value != Tokens::Keyword("class") { 26 | return res.failure(Error::new( 27 | "Syntax Error", 28 | self.current_token.pos_start, 29 | self.current_token.pos_end, 30 | "Expected 'class'", 31 | )); 32 | } 33 | 34 | self.advance(); 35 | res.register_advancement(); 36 | 37 | let name = self.current_token.clone(); 38 | if matches!(name.value, Tokens::Identifier(_)) { 39 | res.register_advancement(); 40 | self.advance(); 41 | } else { 42 | return res.failure(Error::new( 43 | "Syntax Error", 44 | self.current_token.pos_start, 45 | self.current_token.pos_end, 46 | "Expected 'identifier'", 47 | )); 48 | } 49 | 50 | let mut constructor_def = false; 51 | let mut methods = vec![]; 52 | let mut static_members = vec![]; 53 | 54 | let mut constructor = ( 55 | vec![], 56 | Box::new(Node::Statements { 57 | statements: vec![Node::ReturnNode { 58 | value: Box::new(Some(Node::VarAccessNode { 59 | token: Token::new( 60 | Tokens::Identifier("soul"), 61 | self.current_token.pos_start, 62 | self.current_token.pos_end, 63 | ), 64 | })), 65 | }], 66 | }), 67 | ); 68 | let mut properties = vec![]; 69 | 70 | if self.current_token.value != Tokens::LeftCurlyBraces { 71 | return res.failure(Error::new( 72 | "Syntax Error", 73 | self.current_token.pos_start, 74 | self.current_token.pos_end, 75 | "Expected '{'", 76 | )); 77 | } 78 | 79 | self.advance(); 80 | res.register_advancement(); 81 | 82 | while self.current_token.value != Tokens::RightCurlyBraces { 83 | if self.current_token.value == Tokens::Newline { 84 | self.advance(); 85 | res.register_advancement(); 86 | continue; 87 | } 88 | 89 | let mut is_static = false; 90 | if self.current_token.value == Tokens::Keyword("static") { 91 | is_static = true; 92 | self.advance(); 93 | res.register_advancement(); 94 | } 95 | 96 | let statement = res.try_register(self.statement()); 97 | if res.error.is_some() { 98 | return res; 99 | } 100 | if statement.is_none() { 101 | self.reverse(res.to_reverse_count as usize); 102 | continue; 103 | } 104 | 105 | match statement.clone().unwrap() { 106 | Node::VarAssignNode { 107 | name, 108 | value, 109 | reassignable: _, 110 | } => { 111 | if is_static { 112 | static_members.push((name, *value)); 113 | } else { 114 | properties.push((name, *value)); 115 | } 116 | } 117 | Node::FunDef { 118 | name, 119 | arg_tokens, 120 | body_node, 121 | } => { 122 | // if name is none and there is no constructor, then it is a constructor orelse it is a function and if static keyword is present it is static member 123 | if name.is_none() { 124 | if !constructor_def { 125 | constructor_def = true; 126 | constructor = (arg_tokens, body_node); 127 | } else { 128 | return res.failure(Error::new( 129 | "Syntax Error", 130 | self.current_token.pos_start, 131 | self.current_token.pos_end, 132 | "Expected '}'", 133 | )); 134 | } 135 | } else { 136 | if is_static { 137 | static_members.push(( 138 | name.unwrap(), 139 | Node::FunDef { 140 | name, 141 | arg_tokens, 142 | body_node, 143 | }, 144 | )); 145 | } else { 146 | methods.push((name.unwrap(), arg_tokens, *body_node)); 147 | } 148 | } 149 | } 150 | _ => { 151 | return res.failure(Error::new( 152 | "Syntax Error", 153 | self.current_token.pos_start, 154 | self.current_token.pos_end, 155 | "Expected properties or methods", 156 | )) 157 | } 158 | } 159 | } 160 | 161 | if self.current_token.value != Tokens::RightCurlyBraces { 162 | return res.failure(Error::new( 163 | "Syntax Error", 164 | self.current_token.pos_start, 165 | self.current_token.pos_end, 166 | "Expected '}'", 167 | )); 168 | } 169 | 170 | self.advance(); 171 | res.register_advancement(); 172 | 173 | res.success(Node::ClassDefNode { 174 | constructor, 175 | methods, 176 | name, 177 | properties, 178 | static_members, 179 | }) 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/class_init.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a class initialization expression 21 | */ 22 | pub(crate) fn class_init(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let mut constructor_params: Vec = vec![]; 25 | 26 | if self.current_token.value != Tokens::Keyword("new") { 27 | return res.failure(Error::new( 28 | "Invalid Syntax", 29 | self.current_token.pos_start.clone(), 30 | self.current_token.pos_end.clone(), 31 | "Expected 'new'", 32 | )); 33 | } 34 | 35 | res.register_advancement(); 36 | self.advance(); 37 | 38 | if let Tokens::Identifier(_) = self.current_token.value { 39 | } else { 40 | return res.failure(Error::new( 41 | "Invalid Syntax", 42 | self.current_token.pos_start.clone(), 43 | self.current_token.pos_end.clone(), 44 | "Expected identifier", 45 | )); 46 | } 47 | 48 | let name = self.current_token.clone(); 49 | 50 | res.register_advancement(); 51 | self.advance(); 52 | 53 | if self.current_token.value == Tokens::LeftParenthesis { 54 | res.register_advancement(); 55 | self.advance(); 56 | 57 | if self.current_token.value == Tokens::RightParenthesis { 58 | res.register_advancement(); 59 | self.advance(); 60 | } else { 61 | let expr = res.register(self.expr()); 62 | if res.error.is_some() { 63 | return res.failure(Error::new( 64 | "Invalid Syntax", 65 | self.current_token.pos_start.clone(), 66 | self.current_token.pos_end.clone(), 67 | "Expected ')', 'var', int, float, identifier, '+', '-' or ','", 68 | )); 69 | } 70 | constructor_params.push(expr.unwrap()); 71 | 72 | while self.current_token.value == Tokens::Comma { 73 | res.register_advancement(); 74 | self.advance(); 75 | 76 | let expr = res.register(self.expr()); 77 | if res.error.is_some() { 78 | return res.failure(Error::new( 79 | "Invalid Syntax", 80 | self.current_token.pos_start.clone(), 81 | self.current_token.pos_end.clone(), 82 | "Expected ')', 'var', int, float, identifier, '+', '-' or ','", 83 | )); 84 | } 85 | constructor_params.push(expr.unwrap()); 86 | } 87 | 88 | if self.current_token.value != Tokens::RightParenthesis { 89 | return res.failure(Error::new( 90 | "Invalid Syntax", 91 | self.current_token.pos_start.clone(), 92 | self.current_token.pos_end.clone(), 93 | "Expected ')' or ','", 94 | )); 95 | } 96 | res.register_advancement(); 97 | self.advance(); 98 | } 99 | } else { 100 | return res.failure(Error::new( 101 | "Invalid Syntax", 102 | self.current_token.pos_start.clone(), 103 | self.current_token.pos_end.clone(), 104 | "Expected '('", 105 | )); 106 | } 107 | 108 | res.success(Node::ClassInitNode { 109 | name, 110 | constructor_params, 111 | }) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/comp_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parse a computed expression 21 | */ 22 | pub(crate) fn comp_expr(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let pos_start = self.current_token.clone().pos_start; 25 | 26 | if self.current_token.value == Tokens::Keyword("not") { 27 | let op_token = self.current_token.clone(); 28 | res.register_advancement(); 29 | self.advance(); 30 | 31 | let node = res.register(self.comp_expr()); 32 | if res.error.is_some() { 33 | return res; 34 | } 35 | 36 | return res.success(Node::UnaryNode { 37 | node: Box::new(node.clone().unwrap()), 38 | op_token: op_token.clone(), 39 | }); 40 | } 41 | 42 | let mut left = res.register(self.arith_expr()); 43 | if res.error.is_some() { 44 | return res; 45 | } 46 | 47 | while [ 48 | Tokens::DoubleEquals, 49 | Tokens::NotEquals, 50 | Tokens::LessThan, 51 | Tokens::LessThanEquals, 52 | Tokens::GreaterThan, 53 | Tokens::GreaterThanEquals, 54 | ] 55 | .contains(&self.current_token.value) 56 | { 57 | let op_token = self.current_token.clone(); 58 | res.register_advancement(); 59 | self.advance(); 60 | 61 | let right = res.register(self.arith_expr()); 62 | if res.error.is_some() { 63 | return res; 64 | } 65 | left = Option::from(Node::BinaryNode { 66 | left: Box::new(left.clone().unwrap()), 67 | right: Box::new(right.clone().unwrap()), 68 | op_token, 69 | }); 70 | } 71 | 72 | if res.error.is_some() { 73 | return res.failure(Error::new( 74 | "Invalid Syntax", 75 | pos_start, 76 | self.current_token.pos_end.clone(), 77 | "A Int or Float or Identifier, '+', '-', '(', 'not', '!' was Expected", 78 | )); 79 | } 80 | res.success(left.unwrap()) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parse a expression 21 | */ 22 | pub(crate) fn expr(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let pos_start = self.current_token.clone().pos_start; 25 | 26 | if self.current_token.value == Tokens::Keyword("val") 27 | || self.current_token.value == Tokens::Keyword("var") 28 | { 29 | let var_type: String = self.current_token.value.into_string(); 30 | res.register_advancement(); 31 | self.advance(); 32 | 33 | if let Tokens::Identifier(_) = self.current_token.value { 34 | } else { 35 | return res.failure(Error::new( 36 | "Invalid Syntax Error", 37 | self.current_token.pos_start.clone(), 38 | self.current_token.pos_end.clone(), 39 | "Expected Identifier", 40 | )); 41 | } 42 | 43 | let var_name = self.current_token.clone(); 44 | res.register_advancement(); 45 | self.advance(); 46 | 47 | if self.current_token.value != Tokens::Equals { 48 | return res.failure(Error::new( 49 | "Invalid Syntax", 50 | self.current_token.pos_start.clone(), 51 | self.current_token.pos_end.clone(), 52 | "Expected '='", 53 | )); 54 | } 55 | 56 | res.register_advancement(); 57 | self.advance(); 58 | 59 | let expr = res.register(self.expr()); 60 | if res.error.is_some() { 61 | return res; 62 | } 63 | 64 | let reassignable = if var_type == String::from("var") { 65 | true 66 | } else { 67 | false 68 | }; 69 | return res.success(Node::VarAssignNode { 70 | name: var_name.clone(), 71 | value: Box::new(expr.unwrap()), 72 | reassignable, 73 | }); 74 | } 75 | 76 | let mut left = res.register(self.comp_expr()); 77 | if res.error.is_some() { 78 | return res; 79 | } 80 | 81 | while self.current_token.value == Tokens::Keyword("and") 82 | || self.current_token.value == Tokens::Keyword("or") 83 | { 84 | let op_token = self.current_token.clone(); 85 | res.register_advancement(); 86 | self.advance(); 87 | let right = res.register(self.comp_expr()); 88 | if res.error.is_some() { 89 | return res; 90 | } 91 | left = Option::from(Node::BinaryNode { 92 | left: Box::new(left.clone().unwrap()), 93 | right: Box::new(right.clone().unwrap()), 94 | op_token, 95 | }); 96 | } 97 | 98 | if res.error.is_some() { 99 | return res.failure(Error::new( 100 | "Invalid Syntax", 101 | pos_start, 102 | self.current_token.pos_end.clone(), 103 | "Expected 'var', int, float, identifier, '+', '-' or '('", 104 | )); 105 | } 106 | 107 | res.success(left.unwrap()) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/extern_def.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parse a extern function declaration 21 | */ 22 | pub(crate) fn extern_def(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let pos_start = self.current_token.pos_start.clone(); 25 | 26 | if self.current_token.value != Tokens::Keyword("extern") { 27 | return res.failure(Error::new( 28 | pos_start.file_name, 29 | pos_start, 30 | self.current_token.pos_end, 31 | "Expected extern keyword", 32 | )); 33 | } 34 | 35 | self.advance(); 36 | res.register_advancement(); 37 | 38 | let expr = res.register(self.expr()); 39 | if res.error.is_some() { 40 | return res; 41 | } 42 | 43 | let name = self.current_token.clone(); 44 | if matches!(self.current_token.value, Tokens::Identifier(_)) { 45 | self.advance(); 46 | res.register_advancement(); 47 | } else { 48 | return res.failure(Error::new( 49 | pos_start.file_name, 50 | pos_start, 51 | self.current_token.pos_end, 52 | "Expected identifier", 53 | )); 54 | } 55 | 56 | if self.current_token.value != Tokens::LeftParenthesis { 57 | return res.failure(Error::new( 58 | pos_start.file_name, 59 | pos_start, 60 | self.current_token.pos_end, 61 | "Expected (", 62 | )); 63 | } 64 | 65 | self.advance(); 66 | res.register_advancement(); 67 | 68 | let mut args = Vec::new(); 69 | let mut var_args = false; 70 | while self.current_token.value != Tokens::RightParenthesis { 71 | if self.current_token.value == Tokens::Dot { 72 | self.advance(); 73 | res.register_advancement(); 74 | 75 | if self.current_token.value != Tokens::Dot { 76 | return res.failure(Error::new( 77 | pos_start.file_name, 78 | pos_start, 79 | self.current_token.pos_end, 80 | "Expected .", 81 | )); 82 | } 83 | 84 | self.advance(); 85 | res.register_advancement(); 86 | 87 | if self.current_token.value != Tokens::Dot { 88 | return res.failure(Error::new( 89 | pos_start.file_name, 90 | pos_start, 91 | self.current_token.pos_end, 92 | "Expected .", 93 | )); 94 | } 95 | 96 | self.advance(); 97 | res.register_advancement(); 98 | 99 | var_args = true; 100 | break; 101 | } 102 | 103 | let arg = res.register(self.expr()); 104 | if res.error.is_some() { 105 | return res; 106 | } 107 | args.push(arg.unwrap()); 108 | 109 | if self.current_token.value != Tokens::Comma { 110 | break; 111 | } 112 | 113 | self.advance(); 114 | res.register_advancement(); 115 | } 116 | 117 | if self.current_token.value != Tokens::RightParenthesis { 118 | return res.failure(Error::new( 119 | pos_start.file_name, 120 | pos_start, 121 | self.current_token.pos_end, 122 | "Expected )", 123 | )); 124 | } 125 | 126 | res.register_advancement(); 127 | self.advance(); 128 | 129 | res.success(Node::ExternNode { 130 | name, 131 | arg_tokens: args, 132 | return_type: Box::new(expr.unwrap()), 133 | var_args, 134 | }) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/factor.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parse factor expressions 21 | */ 22 | pub(crate) fn factor(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let token = self.current_token.clone(); 25 | 26 | if [Tokens::Plus, Tokens::Minus].contains(&self.current_token.value) { 27 | res.register_advancement(); 28 | self.advance(); 29 | let factor = res.register(self.factor()); 30 | if res.error.is_some() { 31 | return res; 32 | } 33 | return res.success(Node::UnaryNode { 34 | op_token: token.clone(), 35 | node: Box::new(factor.clone().unwrap()), 36 | }); 37 | } 38 | self.power() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/for_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a For loop 21 | */ 22 | pub(crate) fn for_expr(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | if self.current_token.value != Tokens::Keyword("for") { 25 | return res.failure(Error::new( 26 | "Invalid Syntax", 27 | self.current_token.pos_start.clone(), 28 | self.current_token.pos_end.clone(), 29 | "Expected 'for'", 30 | )); 31 | } 32 | 33 | res.register_advancement(); 34 | self.advance(); 35 | 36 | if let Tokens::Identifier(_) = self.current_token.value { 37 | } else { 38 | return res.failure(Error::new( 39 | "Invalid Syntax", 40 | self.current_token.pos_start.clone(), 41 | self.current_token.pos_end.clone(), 42 | "Expected Identifier", 43 | )); 44 | } 45 | 46 | let var_name = self.current_token.clone(); 47 | res.register_advancement(); 48 | self.advance(); 49 | 50 | if self.current_token.value != Tokens::Equals { 51 | return res.failure(Error::new( 52 | "Invalid Syntax", 53 | self.current_token.pos_start.clone(), 54 | self.current_token.pos_end.clone(), 55 | "Expected '='", 56 | )); 57 | } 58 | 59 | res.register_advancement(); 60 | self.advance(); 61 | 62 | let init_expr = res.register(self.expr()); 63 | if res.error.is_some() { 64 | return res; 65 | } 66 | 67 | if self.current_token.value != Tokens::Keyword("to") { 68 | return res.failure(Error::new( 69 | "Invalid Syntax", 70 | self.current_token.pos_start.clone(), 71 | self.current_token.pos_end.clone(), 72 | "Expected 'to'", 73 | )); 74 | } 75 | 76 | res.register_advancement(); 77 | self.advance(); 78 | 79 | let end_expr = res.register(self.expr()); 80 | if res.error.is_some() { 81 | return res; 82 | } 83 | 84 | if self.current_token.value != Tokens::Keyword("step") { 85 | return res.failure(Error::new( 86 | "Invalid Syntax", 87 | self.current_token.pos_start, 88 | self.current_token.pos_end, 89 | "Expected 'step' keyword", 90 | )); 91 | } 92 | 93 | res.register_advancement(); 94 | self.advance(); 95 | let expr = res.register(self.expr()); 96 | if res.error.is_some() { 97 | return res; 98 | } 99 | let step = expr.unwrap(); 100 | 101 | if self.current_token.value != Tokens::LeftCurlyBraces { 102 | return res.failure(Error::new( 103 | "Invalid Syntax", 104 | self.current_token.pos_start.clone(), 105 | self.current_token.pos_end.clone(), 106 | "Expected '{'", 107 | )); 108 | } 109 | 110 | res.register_advancement(); 111 | self.advance(); 112 | 113 | let body = res.register(self.statements()); 114 | if res.error.is_some() { 115 | return res; 116 | } 117 | 118 | if self.current_token.value != Tokens::RightCurlyBraces { 119 | return res.failure(Error::new( 120 | "Invalid Syntax", 121 | self.current_token.pos_start.clone(), 122 | self.current_token.pos_end.clone(), 123 | "Expected '}'", 124 | )); 125 | } 126 | 127 | res.register_advancement(); 128 | self.advance(); 129 | 130 | res.success(Node::ForNode { 131 | var_name_token: var_name, 132 | start_value: Box::new(init_expr.clone().unwrap()), 133 | end_value: Box::new(end_expr.clone().unwrap()), 134 | body_node: Box::new(body.clone().unwrap()), 135 | step_value_node: Box::new(step.clone()), 136 | }) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/fun_def.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Token, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a function definition 21 | */ 22 | pub(crate) fn fun_def(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | if self.current_token.value != Tokens::Keyword("fun") { 25 | return res.failure(Error::new( 26 | "Invalid Syntax", 27 | self.current_token.pos_start.clone(), 28 | self.current_token.pos_end.clone(), 29 | "Expected 'fun'", 30 | )); 31 | } 32 | 33 | res.register_advancement(); 34 | self.advance(); 35 | 36 | let mut fun_name: Option = None; 37 | if let Tokens::Identifier(_) = self.current_token.value { 38 | fun_name = Some(self.current_token.clone()); 39 | 40 | res.register_advancement(); 41 | self.advance(); 42 | 43 | if self.current_token.value != Tokens::LeftParenthesis { 44 | return res.failure(Error::new( 45 | "Invalid Syntax", 46 | self.current_token.pos_start.clone(), 47 | self.current_token.pos_end.clone(), 48 | "Expected '('", 49 | )); 50 | } 51 | } else if self.current_token.value != Tokens::LeftParenthesis { 52 | return res.failure(Error::new( 53 | "Invalid Syntax", 54 | self.current_token.pos_start.clone(), 55 | self.current_token.pos_end.clone(), 56 | "Expected '(' or identifier", 57 | )); 58 | } 59 | 60 | res.register_advancement(); 61 | self.advance(); 62 | 63 | let mut args_name_tokens: Vec = vec![]; 64 | if let Tokens::Identifier(_) = self.current_token.value { 65 | let name = self.current_token.clone(); 66 | args_name_tokens.push(name); 67 | 68 | res.register_advancement(); 69 | self.advance(); 70 | 71 | while self.current_token.value == Tokens::Comma { 72 | res.register_advancement(); 73 | self.advance(); 74 | 75 | if let Tokens::Identifier(_) = self.current_token.value { 76 | let new_arg_token = self.current_token.clone(); 77 | args_name_tokens.push(new_arg_token); 78 | res.register_advancement(); 79 | self.advance(); 80 | } else { 81 | return res.failure(Error::new( 82 | "Invalid Syntax", 83 | self.current_token.pos_start.clone(), 84 | self.current_token.pos_end.clone(), 85 | "Expected Identifier", 86 | )); 87 | } 88 | } 89 | 90 | if self.current_token.value != Tokens::RightParenthesis { 91 | return res.failure(Error::new( 92 | "Invalid Syntax", 93 | self.current_token.pos_start.clone(), 94 | self.current_token.pos_end.clone(), 95 | "Expected ')' or ','", 96 | )); 97 | } 98 | } else if self.current_token.value != Tokens::RightParenthesis { 99 | return res.failure(Error::new( 100 | "Invalid Syntax", 101 | self.current_token.pos_start.clone(), 102 | self.current_token.pos_end.clone(), 103 | "Expected ')' or identifier", 104 | )); 105 | } 106 | 107 | res.register_advancement(); 108 | self.advance(); 109 | 110 | if self.current_token.value != Tokens::LeftCurlyBraces { 111 | return res.failure(Error::new( 112 | "Invalid Syntax", 113 | self.current_token.pos_start.clone(), 114 | self.current_token.pos_end.clone(), 115 | "Expected '{'", 116 | )); 117 | } 118 | self.advance(); 119 | res.register_advancement(); 120 | 121 | let body_node = res.register(self.statements()); 122 | if res.error.is_some() { 123 | return res; 124 | } 125 | 126 | if self.current_token.value != Tokens::RightCurlyBraces { 127 | return res.failure(Error::new( 128 | "Invalid Syntax", 129 | self.current_token.pos_start.clone(), 130 | self.current_token.pos_end.clone(), 131 | "Expected '}'", 132 | )); 133 | } 134 | self.advance(); 135 | res.register_advancement(); 136 | 137 | res.success(Node::FunDef { 138 | name: fun_name, 139 | body_node: Box::new(body_node.clone().unwrap()), 140 | arg_tokens: args_name_tokens, 141 | }) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/if_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a If Expresion 21 | */ 22 | pub(crate) fn if_expr(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | if self.current_token.value != Tokens::Keyword("if") { 25 | return res.failure(Error::new( 26 | "Invalid Syntax", 27 | self.current_token.pos_start.clone(), 28 | self.current_token.pos_end.clone(), 29 | "Expected 'if'", 30 | )); 31 | } 32 | 33 | res.register_advancement(); 34 | self.advance(); 35 | 36 | let mut cases: Vec<(Node, Node)> = vec![]; 37 | let mut else_case: Option = None; 38 | 39 | let first_condition = res.register(self.expr()); 40 | if res.error.is_some() { 41 | return res; 42 | } 43 | 44 | if self.current_token.value != Tokens::LeftCurlyBraces { 45 | return res.failure(Error::new( 46 | "Invalid Syntax", 47 | self.current_token.pos_start.clone(), 48 | self.current_token.pos_end.clone(), 49 | "Expected '{'", 50 | )); 51 | } 52 | 53 | res.register_advancement(); 54 | self.advance(); 55 | 56 | let first_expr = res.register(self.statements()); 57 | if res.error.is_some() { 58 | return res; 59 | } 60 | 61 | cases.push((first_condition.unwrap(), first_expr.unwrap())); 62 | 63 | if self.current_token.value != Tokens::RightCurlyBraces { 64 | return res.failure(Error::new( 65 | "Invalid Syntax", 66 | self.current_token.pos_start.clone(), 67 | self.current_token.pos_end.clone(), 68 | "Expected '}'", 69 | )); 70 | } 71 | self.advance(); 72 | res.register_advancement(); 73 | 74 | while self.current_token.value == Tokens::Keyword("else") { 75 | res.register_advancement(); 76 | self.advance(); 77 | 78 | if self.current_token.value == Tokens::Keyword("if") { 79 | res.register_advancement(); 80 | self.advance(); 81 | 82 | let condition = res.register(self.expr()); 83 | if res.error.is_some() { 84 | return res; 85 | } 86 | 87 | if self.current_token.value != Tokens::LeftCurlyBraces { 88 | return res.failure(Error::new( 89 | "Invalid Syntax", 90 | self.current_token.pos_start.clone(), 91 | self.current_token.pos_end.clone(), 92 | "Expected '{'", 93 | )); 94 | } 95 | 96 | res.register_advancement(); 97 | self.advance(); 98 | 99 | let else_if = res.register(self.statements()); 100 | if res.error.is_some() { 101 | return res; 102 | } 103 | 104 | cases.push((condition.unwrap(), else_if.unwrap())); 105 | 106 | if self.current_token.value != Tokens::RightCurlyBraces { 107 | return res.failure(Error::new( 108 | "Invalid Syntax", 109 | self.current_token.pos_start.clone(), 110 | self.current_token.pos_end.clone(), 111 | "Expected '}'", 112 | )); 113 | } 114 | res.register_advancement(); 115 | self.advance(); 116 | } else { 117 | if self.current_token.value != Tokens::LeftCurlyBraces { 118 | return res.failure(Error::new( 119 | "Invalid Syntax", 120 | self.current_token.pos_start.clone(), 121 | self.current_token.pos_end.clone(), 122 | "Expected '}'", 123 | )); 124 | } 125 | self.advance(); 126 | res.register_advancement(); 127 | 128 | let else_ = res.register(self.statements()); 129 | if res.error.is_some() { 130 | return res; 131 | } 132 | 133 | else_case = Some(else_.unwrap()); 134 | if self.current_token.value != Tokens::RightCurlyBraces { 135 | return res.failure(Error::new( 136 | "Invalid Syntax", 137 | self.current_token.pos_start.clone(), 138 | self.current_token.pos_end.clone(), 139 | "Expected '}'", 140 | )); 141 | } 142 | res.register_advancement(); 143 | self.advance(); 144 | break; 145 | } 146 | } 147 | res.success(Node::IfNode { 148 | cases, 149 | else_case: Box::new(else_case.clone()), 150 | }) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/index_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | 17 | impl Parser { 18 | /* 19 | * Parse indexing of arrays 20 | */ 21 | pub(crate) fn index_expr(&mut self) -> ParseResult { 22 | let mut res = ParseResult::new(); 23 | let atom = res.register(self.atom()); 24 | if res.error.is_some() { 25 | return res; 26 | } 27 | 28 | self.call_access_expr(atom, res) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use crate::parse_result::ParseResult; 15 | use bzxc_shared::{Error, Token, Tokens}; 16 | 17 | mod arith_expr; 18 | mod array_expr; 19 | mod atom; 20 | mod c_object; 21 | mod c_to_bzx_obj; 22 | mod call; 23 | mod call_access_expr; 24 | mod class_def; 25 | mod class_init; 26 | mod comp_expr; 27 | mod expr; 28 | mod extern_def; 29 | mod factor; 30 | mod for_expr; 31 | mod fun_def; 32 | mod if_expr; 33 | mod index_expr; 34 | mod obj_expr; 35 | mod obj_prop_expr; 36 | mod power; 37 | mod statement; 38 | mod statements; 39 | mod term; 40 | mod var_expr; 41 | mod while_expr; 42 | 43 | /* 44 | * Parses Tokens into a Statements Node with child nodes 45 | */ 46 | #[derive(Debug, Clone)] 47 | pub struct Parser { 48 | pub tokens: Vec, 49 | pub token_index: usize, 50 | pub current_token: Token, 51 | } 52 | 53 | impl Parser { 54 | /* 55 | * Creates a new Parser instance 56 | */ 57 | pub fn new(tokens: Vec) -> Parser { 58 | let current_token = tokens.clone()[0].clone(); 59 | Parser { 60 | tokens, 61 | token_index: 0, 62 | current_token, 63 | } 64 | } 65 | 66 | /* 67 | * Parses tokens into a node 68 | */ 69 | pub fn parse(&mut self) -> ParseResult { 70 | let mut res = self.statements(); 71 | if res.error.is_none() && self.current_token.value != Tokens::EOF { 72 | return res.failure(Error::new( 73 | "Invalid Syntax", 74 | self.current_token.pos_start.clone(), 75 | self.current_token.pos_end.clone(), 76 | "Expected Operators, Variables, Functions, etc but found none", 77 | )); 78 | } 79 | res 80 | } 81 | 82 | /* 83 | * Advances to the next token 84 | */ 85 | fn advance(&mut self) { 86 | self.token_index += 1; 87 | self.update_current_token(); 88 | } 89 | 90 | /* 91 | * Updates the current token based upon the token index 92 | */ 93 | fn update_current_token(&mut self) { 94 | if self.token_index < self.tokens.len() { 95 | self.current_token = self.tokens.clone()[self.clone().token_index].clone(); 96 | } 97 | } 98 | 99 | /* 100 | * Reverse tokens by provided offset 101 | */ 102 | fn reverse(&mut self, cnt: usize) { 103 | self.token_index -= cnt; 104 | self.update_current_token(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/obj_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Error, Node, Token, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a object expression 21 | */ 22 | pub(crate) fn obj_expr(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let pos_start = self.current_token.clone().pos_start; 25 | let mut properties: Vec<(Token, Node)> = vec![]; 26 | 27 | if self.current_token.value != Tokens::LeftCurlyBraces { 28 | return res.failure(Error::new( 29 | "Invalid syntax", 30 | pos_start, 31 | self.current_token.clone().pos_end, 32 | "'{' was expected.", 33 | )); 34 | } 35 | 36 | self.advance(); 37 | res.register_advancement(); 38 | 39 | if self.current_token.value == Tokens::Newline { 40 | res.register_advancement(); 41 | self.advance(); 42 | } 43 | 44 | if self.current_token.value == Tokens::RightCurlyBraces { 45 | res.register_advancement(); 46 | self.advance(); 47 | } else { 48 | let mut expr = res.register(self.expr()); 49 | if res.error.is_some() { 50 | return res.failure(Error::new( 51 | "Invalid syntax", 52 | pos_start, 53 | self.current_token.pos_end, 54 | "'}', 'key' was expected.", 55 | )); 56 | } 57 | 58 | let mut tok; 59 | if let Node::VarAccessNode { token } = expr.unwrap() { 60 | tok = token; 61 | } else { 62 | return res.failure(Error::new( 63 | "Invalid syntax", 64 | pos_start, 65 | self.current_token.clone().pos_end, 66 | "string was expected.", 67 | )); 68 | } 69 | 70 | if self.current_token.value != Tokens::Colon { 71 | return res.failure(Error::new( 72 | "Invalid syntax", 73 | pos_start, 74 | self.current_token.clone().pos_end, 75 | "':' was expected.", 76 | )); 77 | } 78 | 79 | res.register_advancement(); 80 | self.advance(); 81 | 82 | expr = res.register(self.expr()); 83 | if res.error.is_some() { 84 | return res; 85 | } 86 | 87 | properties.push((tok, expr.unwrap())); 88 | 89 | while self.current_token.value == Tokens::Comma { 90 | self.advance(); 91 | res.register_advancement(); 92 | 93 | if self.current_token.value == Tokens::Newline { 94 | res.register_advancement(); 95 | self.advance(); 96 | } 97 | 98 | expr = res.register(self.expr()); 99 | if res.error.is_some() { 100 | return res.failure(Error::new( 101 | "Invalid syntax", 102 | pos_start, 103 | self.current_token.pos_end, 104 | "'}' ',', 'key' was expected.", 105 | )); 106 | } 107 | 108 | if let Node::VarAccessNode { token } = expr.unwrap() { 109 | tok = token; 110 | } else { 111 | return res.failure(Error::new( 112 | "Invalid syntax", 113 | pos_start, 114 | self.current_token.clone().pos_end, 115 | "string was expected.", 116 | )); 117 | } 118 | 119 | if self.current_token.value != Tokens::Colon { 120 | return res.failure(Error::new( 121 | "Invalid syntax", 122 | pos_start, 123 | self.current_token.clone().pos_end, 124 | "':' was expected.", 125 | )); 126 | } 127 | 128 | res.register_advancement(); 129 | self.advance(); 130 | 131 | expr = res.register(self.expr()); 132 | if res.error.is_some() { 133 | return res; 134 | } 135 | 136 | properties.push((tok, expr.unwrap())); 137 | } 138 | 139 | if self.current_token.value == Tokens::Newline { 140 | self.advance(); 141 | res.register_advancement() 142 | } 143 | 144 | if self.current_token.value != Tokens::RightCurlyBraces { 145 | return res.failure(Error::new( 146 | "Invalid syntax", 147 | pos_start, 148 | self.current_token.clone().pos_end, 149 | "'}', ',' was expected.", 150 | )); 151 | } 152 | 153 | res.register_advancement(); 154 | self.advance(); 155 | } 156 | 157 | res.success(Node::ObjectDefNode { properties }) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/obj_prop_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | 17 | impl Parser { 18 | /* 19 | * Parses a object property expression 20 | */ 21 | pub(crate) fn obj_prop_expr(&mut self) -> ParseResult { 22 | let mut res = ParseResult::new(); 23 | let expr = res.register(self.index_expr()); 24 | if res.error.is_some() { 25 | return res; 26 | } 27 | 28 | self.call_access_expr(expr, res) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/power.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parses a power expression 21 | */ 22 | pub(crate) fn power(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let mut left = res.register(self.call()); 25 | if res.error.is_some() { 26 | return res; 27 | } 28 | 29 | while self.current_token.value == Tokens::Power { 30 | let op_token = self.current_token.clone(); 31 | res.register_advancement(); 32 | self.advance(); 33 | 34 | let right = res.register(self.factor()); 35 | if res.error.is_some() { 36 | return res; 37 | } 38 | 39 | left = Option::from(Node::BinaryNode { 40 | left: Box::new(left.clone().unwrap()), 41 | right: Box::new(right.clone().unwrap()), 42 | op_token, 43 | }); 44 | } 45 | 46 | res.success(left.unwrap()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/statement.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parse a statement 21 | */ 22 | pub(crate) fn statement(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | 25 | if self.current_token.value == Tokens::Keyword("return") { 26 | res.register_advancement(); 27 | self.advance(); 28 | 29 | let expr = res.try_register(self.expr()); 30 | if expr.is_none() { 31 | self.reverse(res.to_reverse_count as usize); 32 | } 33 | 34 | return res.success(Node::ReturnNode { 35 | value: Box::new(expr), 36 | }); 37 | } 38 | 39 | let expr = res.register(self.expr()); 40 | if res.error.is_some() { 41 | return res; 42 | } 43 | res.success(expr.unwrap()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/statements.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use crate::parse_result::ParseResult; 15 | use bzxc_shared::Node; 16 | use bzxc_shared::Tokens; 17 | 18 | use super::Parser; 19 | 20 | impl Parser { 21 | /* 22 | * Parse Statements 23 | */ 24 | pub(crate) fn statements(&mut self) -> ParseResult { 25 | let mut res = ParseResult::new(); 26 | let mut statements: Vec = vec![]; 27 | 28 | while self.current_token.value == Tokens::Newline { 29 | res.register_advancement(); 30 | self.advance(); 31 | } 32 | 33 | let mut statement = res.try_register(self.statement()); 34 | if res.error.is_some() { 35 | return res; 36 | }; 37 | if let Some(ref smt) = statement { 38 | statements.push(smt.clone()); 39 | } else { 40 | self.reverse(res.to_reverse_count); 41 | } 42 | let mut more_statements = statement.is_some(); 43 | 44 | loop { 45 | let mut newline_ct = 0; 46 | while self.current_token.value == Tokens::Newline { 47 | res.register_advancement(); 48 | self.advance(); 49 | newline_ct += 1; 50 | } 51 | 52 | if newline_ct == 0 { 53 | more_statements = false; 54 | } 55 | 56 | if !more_statements { 57 | break; 58 | } 59 | statement = res.try_register(self.statement()); 60 | if statement.is_none() { 61 | self.reverse(res.to_reverse_count as usize); 62 | more_statements = false; 63 | continue; 64 | } 65 | statements.push(statement.unwrap()) 66 | } 67 | res.success(Node::Statements { statements }) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/term.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::Parser; 15 | use crate::parse_result::ParseResult; 16 | use bzxc_shared::{Node, Tokens}; 17 | 18 | impl Parser { 19 | /* 20 | * Parse a term expression 21 | */ 22 | pub(crate) fn term(&mut self) -> ParseResult { 23 | let mut res = ParseResult::new(); 24 | let mut left = res.register(self.factor()); 25 | if res.error.is_some() { 26 | return res; 27 | } 28 | 29 | while [Tokens::Multiply, Tokens::Divide, Tokens::Modulo].contains(&self.current_token.value) 30 | { 31 | let op_token = self.current_token.clone(); 32 | res.register_advancement(); 33 | self.advance(); 34 | 35 | let right = res.register(self.factor()); 36 | if res.error.is_some() { 37 | return res; 38 | } 39 | 40 | left = Option::from(Node::BinaryNode { 41 | left: Box::new(left.clone().unwrap()), 42 | right: Box::new(right.clone().unwrap()), 43 | op_token, 44 | }); 45 | } 46 | 47 | res.success(left.unwrap()) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/var_expr.rs: -------------------------------------------------------------------------------- 1 | use bzxc_shared::{Error, Node, Tokens}; 2 | 3 | use crate::parse_result::ParseResult; 4 | 5 | use super::Parser; 6 | 7 | impl Parser { 8 | /* 9 | * Variable related expressions 10 | */ 11 | pub(crate) fn var_expr(&mut self) -> ParseResult { 12 | let mut res = ParseResult::new(); 13 | 14 | if let Tokens::Identifier(_) = self.current_token.value { 15 | } else { 16 | return res.failure(Error::new( 17 | "Invalid Syntax", 18 | self.current_token.pos_start.clone(), 19 | self.current_token.pos_end.clone(), 20 | "Expected Identifier", 21 | )); 22 | } 23 | 24 | let tok = self.current_token.clone(); 25 | self.advance(); 26 | res.register_advancement(); 27 | 28 | let type_tok = self.current_token.clone(); 29 | if [ 30 | Tokens::Equals, 31 | Tokens::PlusEquals, 32 | Tokens::MinusEquals, 33 | Tokens::MultiplyEquals, 34 | Tokens::DivideEquals, 35 | Tokens::PowerEquals, 36 | Tokens::ModuloEquals, 37 | ] 38 | .contains(&type_tok.value) 39 | { 40 | res.register_advancement(); 41 | self.advance(); 42 | 43 | let expr = res.register(self.expr()); 44 | if res.error.is_some() { 45 | return res; 46 | } 47 | 48 | return res.success(Node::VarReassignNode { 49 | name: tok, 50 | typee: type_tok, 51 | value: Box::new(expr.unwrap()), 52 | }); 53 | } 54 | 55 | res.success(Node::VarAccessNode { token: tok }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /crates/bzxc_parser/src/parser/while_expr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use bzxc_shared::{Error, Node, Tokens}; 15 | 16 | use super::Parser; 17 | use crate::parse_result::ParseResult; 18 | 19 | impl Parser { 20 | /* 21 | * Parses a while loop 22 | */ 23 | pub(crate) fn while_expr(&mut self) -> ParseResult { 24 | let mut res = ParseResult::new(); 25 | if self.current_token.value != Tokens::Keyword("while") { 26 | return res.failure(Error::new( 27 | "Invalid Syntax", 28 | self.current_token.pos_start.clone(), 29 | self.current_token.pos_end.clone(), 30 | "Expected 'while'", 31 | )); 32 | } 33 | 34 | res.register_advancement(); 35 | self.advance(); 36 | 37 | let condition_node = res.register(self.expr()); 38 | if res.error.is_some() { 39 | return res; 40 | } 41 | 42 | if self.current_token.value != Tokens::LeftCurlyBraces { 43 | return res.failure(Error::new( 44 | "Invalid Syntax", 45 | self.current_token.pos_start.clone(), 46 | self.current_token.pos_end.clone(), 47 | "Expected '{'", 48 | )); 49 | } 50 | 51 | res.register_advancement(); 52 | self.advance(); 53 | 54 | let body_node = res.register(self.statements()); 55 | if res.error.is_some() { 56 | return res; 57 | } 58 | 59 | if self.current_token.value != Tokens::RightCurlyBraces { 60 | return res.failure(Error::new( 61 | "Invalid Syntax", 62 | self.current_token.pos_start.clone(), 63 | self.current_token.pos_end.clone(), 64 | "Expected '}'", 65 | )); 66 | } 67 | 68 | res.register_advancement(); 69 | self.advance(); 70 | 71 | res.success(Node::WhileNode { 72 | condition_node: Box::new(condition_node.clone().unwrap()), 73 | body_node: Box::new(body_node.clone().unwrap()), 74 | }) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /crates/bzxc_shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bzxc_shared" 3 | version = "0.1.0" 4 | authors = ["RoMeAh "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | codespan-reporting = "0.11.1" 11 | llvm-sys = "100.0" 12 | -------------------------------------------------------------------------------- /crates/bzxc_type_system/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bzxc_type_system" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | bzxc_shared = { path = "../bzxc_shared" } 10 | llvm-sys = "100.0" -------------------------------------------------------------------------------- /crates/bzxc_type_system/src/annotate.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | /* 4 | * Copyright 2020 to 2021 BlazifyOrg 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | use bzxc_shared::{Binder, Node, Tokens, Type, TypedNode}; 16 | 17 | use crate::TypeSystem; 18 | 19 | impl TypeSystem { 20 | pub(crate) fn annotate(&mut self, node: Node) -> TypedNode { 21 | match node.clone() { 22 | Node::Statements { statements } => TypedNode::Statements( 23 | statements 24 | .iter() 25 | .map(|statement| self.annotate(statement.clone())) 26 | .collect(), 27 | ), 28 | Node::NumberNode { token } => match token.value { 29 | Tokens::Int(i) => TypedNode::Int { 30 | ty: Type::fresh_var(), 31 | val: i, 32 | }, 33 | Tokens::Float(f) => TypedNode::Float { 34 | ty: Type::fresh_var(), 35 | val: f, 36 | }, 37 | _ => unreachable!(), 38 | }, 39 | Node::BooleanNode { token } => TypedNode::Boolean { 40 | ty: Type::fresh_var(), 41 | val: token.value.into_boolean(), 42 | }, 43 | Node::CharNode { token } => TypedNode::Char { 44 | ty: Type::fresh_var(), 45 | val: token.value.into_char(), 46 | }, 47 | Node::StringNode { token } => TypedNode::String { 48 | ty: Type::fresh_var(), 49 | val: token.value.into_string(), 50 | }, 51 | Node::UnaryNode { node, op_token } => TypedNode::Unary { 52 | ty: Type::fresh_var(), 53 | val: box self.annotate(*node), 54 | op_token, 55 | }, 56 | Node::BinaryNode { 57 | left, 58 | op_token, 59 | right, 60 | } => TypedNode::Binary { 61 | ty: Type::fresh_var(), 62 | left: box self.annotate(*left.clone()), 63 | right: box self.annotate(*right.clone()), 64 | op_token, 65 | }, 66 | Node::VarAccessNode { token } => match self.type_env.get(token.value.into_string()) { 67 | Some(ty) => TypedNode::Var { 68 | ty, 69 | name: token.value.into_string(), 70 | }, 71 | None => { 72 | println!("{:?}", self.type_env); 73 | panic!("No var found {}", token.value.into_string()); 74 | } 75 | }, 76 | Node::VarAssignNode { name, value, .. } => { 77 | let val = self.annotate(*value); 78 | let ty = val.get_type(); 79 | self.type_env.set(name.value.into_string(), ty.clone()); 80 | TypedNode::Let { 81 | ty, 82 | name: name.value.into_string(), 83 | val: box val, 84 | } 85 | } 86 | Node::FunDef { 87 | arg_tokens, 88 | body_node, 89 | name, 90 | } => { 91 | let ty = Type::fresh_var(); 92 | 93 | let name = if let Some(tok) = name { 94 | self.type_env.set(tok.value.into_string(), ty.clone()); 95 | tok.value.into_string() 96 | } else { 97 | "%anonymous%".to_string() 98 | }; 99 | 100 | self.type_env.push_scope(); 101 | let mut binders = vec![]; 102 | for arg in arg_tokens { 103 | let ty = Type::fresh_var(); 104 | 105 | self.type_env.set(arg.value.into_string(), ty.clone()); 106 | let binder = Binder { 107 | ty, 108 | name: arg.value.into_string(), 109 | }; 110 | binders.push(binder); 111 | } 112 | 113 | let fun = TypedNode::Fun { 114 | ty, 115 | name, 116 | params: binders, 117 | body: box self.annotate(*body_node), 118 | }; 119 | 120 | self.type_env.pop_scope(); 121 | 122 | fun 123 | } 124 | Node::CallNode { args, node_to_call } => TypedNode::Call { 125 | ty: Type::fresh_var(), 126 | fun: box self.annotate(*node_to_call), 127 | args: args.iter().map(|x| self.annotate(x.clone())).collect(), 128 | }, 129 | Node::ReturnNode { value } => { 130 | let val = box if let Some(val) = *value.clone() { 131 | self.annotate(val) 132 | } else { 133 | TypedNode::Null { ty: Type::Null } 134 | }; 135 | TypedNode::Return { 136 | ty: val.get_type(), 137 | val, 138 | } 139 | } 140 | Node::IfNode { cases, else_case } => TypedNode::If { 141 | ty: Type::fresh_var(), 142 | cases: cases 143 | .iter() 144 | .map(|x| { 145 | self.type_env.push_scope(); 146 | let val = (self.annotate(x.0.clone()), self.annotate(x.1.clone())); 147 | self.type_env.pop_scope(); 148 | val 149 | }) 150 | .collect(), 151 | else_case: if let Some(n) = *else_case.clone() { 152 | self.type_env.push_scope(); 153 | let val = box self.annotate(n); 154 | self.type_env.pop_scope(); 155 | Some(val) 156 | } else { 157 | None 158 | }, 159 | }, 160 | Node::WhileNode { 161 | condition_node, 162 | body_node, 163 | } => { 164 | self.type_env.push_scope(); 165 | let val = TypedNode::While { 166 | ty: Type::fresh_var(), 167 | cond: box self.annotate(*condition_node), 168 | body: box self.annotate(*body_node), 169 | }; 170 | self.type_env.pop_scope(); 171 | val 172 | } 173 | Node::ForNode { 174 | var_name_token, 175 | start_value, 176 | end_value, 177 | step_value_node, 178 | body_node, 179 | } => { 180 | self.type_env.push_scope(); 181 | let val = TypedNode::For { 182 | ty: Type::fresh_var(), 183 | var: var_name_token.value.into_string(), 184 | start: { 185 | let start = box self.annotate(*start_value); 186 | self.type_env 187 | .set(var_name_token.value.into_string(), start.get_type()); 188 | start 189 | }, 190 | end: box self.annotate(*end_value), 191 | step: box self.annotate(*step_value_node), 192 | body: box self.annotate(*body_node), 193 | }; 194 | self.type_env.pop_scope(); 195 | val 196 | } 197 | Node::ArrayNode { element_nodes } => TypedNode::Array { 198 | ty: Type::fresh_var(), 199 | elements: element_nodes 200 | .iter() 201 | .map(|x| self.annotate(x.clone())) 202 | .collect(), 203 | }, 204 | Node::ArrayAcess { array, index } => TypedNode::Index { 205 | ty: Type::fresh_var(), 206 | array: box self.annotate(*array), 207 | idx: box self.annotate(*index), 208 | }, 209 | Node::VarReassignNode { name, typee, value } => { 210 | let name = name.value.into_string(); 211 | let val = box self.annotate(*value); 212 | let prev = self.type_env.get(name.clone()).unwrap().clone(); 213 | 214 | TypedNode::ReLet { 215 | ty: val.get_type(), 216 | name, 217 | val, 218 | prev, 219 | } 220 | } 221 | Node::ObjectDefNode { properties } => TypedNode::Object { 222 | ty: Type::fresh_var(), 223 | properties: { 224 | let mut tree = BTreeMap::new(); 225 | 226 | for (name, node) in properties { 227 | tree.insert(name.value.into_string(), self.annotate(node.clone())); 228 | } 229 | tree.clone() 230 | }, 231 | }, 232 | Node::ObjectPropAccess { object, property } => TypedNode::ObjectAccess { 233 | ty: Type::fresh_var(), 234 | object: box self.annotate(*object), 235 | property: property.value.into_string(), 236 | }, 237 | Node::ObjectPropEdit { 238 | object, 239 | property, 240 | new_val, 241 | } => TypedNode::ObjectEdit { 242 | ty: Type::fresh_var(), 243 | object: box self.annotate(*object), 244 | property: property.value.into_string(), 245 | new_val: box self.annotate(*new_val), 246 | }, 247 | Node::ObjectMethodCall { 248 | object, 249 | property, 250 | args, 251 | } => TypedNode::ObjectMethodCall { 252 | ty: Type::fresh_var(), 253 | object: box self.annotate(*object), 254 | property: property.value.into_string(), 255 | args: args.iter().map(|x| self.annotate(x.clone())).collect(), 256 | }, 257 | Node::ClassDefNode { 258 | methods: mthds, 259 | properties: props, 260 | constructor, 261 | name, 262 | static_members: st_mthds, 263 | } => { 264 | let mut properties = BTreeMap::new(); 265 | let mut methods = BTreeMap::new(); 266 | let mut static_members = BTreeMap::new(); 267 | 268 | for (name, value) in st_mthds { 269 | static_members.insert(name.value.into_string(), self.annotate(value.clone())); 270 | } 271 | 272 | for (name, node) in props { 273 | properties.insert(name.value.into_string(), self.annotate(node.clone())); 274 | } 275 | 276 | let obj_ty = Type::fresh_var(); 277 | let ty = Type::Class(box obj_ty.clone()); 278 | 279 | let static_obj = TypedNode::Object { 280 | ty: Type::fresh_var(), 281 | properties: static_members, 282 | }; 283 | self.type_env 284 | .set(name.value.into_string(), static_obj.get_type()); 285 | self.type_env.push_scope(); 286 | self.type_env.set("soul".to_string(), obj_ty.clone()); 287 | 288 | for (name, args, body) in mthds { 289 | methods.insert( 290 | name.value.into_string(), 291 | self.annotate(Node::FunDef { 292 | name: Some(name), 293 | body_node: box body, 294 | arg_tokens: args, 295 | }), 296 | ); 297 | } 298 | 299 | self.class_env.insert(name.value.into_string(), ty.clone()); 300 | 301 | let mut params = vec![]; 302 | let mut params_ty = vec![]; 303 | for arg in constructor.0 { 304 | let ty = Type::fresh_var(); 305 | params_ty.push(ty.clone()); 306 | self.type_env.set(arg.value.into_string(), ty.clone()); 307 | params.push(Binder { 308 | ty, 309 | name: arg.value.into_string(), 310 | }); 311 | } 312 | 313 | let val = TypedNode::Class { 314 | ty, 315 | name: name.value.into_string(), 316 | properties, 317 | constructor: box TypedNode::Fun { 318 | ty: Type::Fun(params_ty, box Type::Null), 319 | name: "%constructor%".to_string(), 320 | params, 321 | body: box self.annotate(*constructor.1), 322 | }, 323 | methods, 324 | static_obj: box static_obj, 325 | }; 326 | 327 | self.type_env.pop_scope(); 328 | 329 | val 330 | } 331 | Node::ClassInitNode { 332 | name, 333 | constructor_params, 334 | } => TypedNode::ClassInit { 335 | ty: Type::fresh_var(), 336 | class: self 337 | .class_env 338 | .get(&*name.value.into_string()) 339 | .unwrap() 340 | .clone(), 341 | constructor_params: constructor_params 342 | .iter() 343 | .map(|x| self.annotate(x.clone())) 344 | .collect(), 345 | }, 346 | Node::ExternNode { 347 | name, 348 | arg_tokens, 349 | return_type, 350 | var_args, 351 | } => { 352 | self.type_env.push_scope(); 353 | let name = name.value.into_string(); 354 | let mut params = vec![]; 355 | for arg in arg_tokens { 356 | params.push(self.annotate(arg.clone())); 357 | } 358 | 359 | let ret = self.annotate(*return_type); 360 | 361 | let fun = TypedNode::Extern { 362 | ty: Type::fresh_var(), 363 | name: name.clone(), 364 | args: params, 365 | return_type: box ret, 366 | var_args, 367 | }; 368 | self.type_env.pop_scope(); 369 | self.type_env.set(name, fun.get_type()); 370 | 371 | fun 372 | } 373 | Node::CObject { object } => TypedNode::CObject { 374 | ty: Type::fresh_var(), 375 | object: box self.annotate(*object), 376 | }, 377 | Node::CToBzxObject { object, bzx_object } => TypedNode::CToBzxObject { 378 | ty: self.annotate(*bzx_object).get_type(), 379 | object: box self.annotate(*object), 380 | }, 381 | Node::TypeKeyword { token } => match token.value.into_string().as_str() { 382 | "int" => TypedNode::Int { 383 | ty: Type::Int, 384 | val: 0, 385 | }, 386 | "float" => TypedNode::Float { 387 | ty: Type::Float, 388 | val: 0.0, 389 | }, 390 | "bool" => TypedNode::Boolean { 391 | ty: Type::Boolean, 392 | val: false, 393 | }, 394 | "char" => TypedNode::Char { 395 | ty: Type::Char, 396 | val: '\0', 397 | }, 398 | "string" => TypedNode::String { 399 | ty: Type::String, 400 | val: String::new(), 401 | }, 402 | "void" => TypedNode::Null { ty: Type::Null }, 403 | _ => unreachable!(), 404 | }, 405 | } 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /crates/bzxc_type_system/src/constraint.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, HashMap}; 2 | 3 | use bzxc_shared::{Tokens, Type, TypedNode}; 4 | 5 | use crate::TypeSystem; 6 | 7 | /* 8 | * Copyright 2020 to 2021 BlazifyOrg 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | #[derive(Debug, Clone)] 20 | pub struct Constraint(pub Type, pub Type); 21 | 22 | impl TypeSystem { 23 | pub(crate) fn collect(&mut self, node: TypedNode) -> Vec { 24 | match node { 25 | TypedNode::Statements(stmts) => stmts 26 | .iter() 27 | .map(|x| self.collect(x.clone())) 28 | .collect::>>() 29 | .concat(), 30 | TypedNode::Int { ty, .. } => { 31 | vec![Constraint(ty, Type::Int)] 32 | } 33 | TypedNode::Float { ty, .. } => { 34 | vec![Constraint(ty, Type::Float)] 35 | } 36 | TypedNode::Boolean { ty, .. } => { 37 | vec![Constraint(ty, Type::Boolean)] 38 | } 39 | TypedNode::Char { ty, .. } => vec![Constraint(ty, Type::Char)], 40 | TypedNode::String { ty, .. } => vec![Constraint(ty, Type::String)], 41 | TypedNode::Unary { ty, val, .. } => { 42 | let mut constr = self.collect(*val.clone()); 43 | constr.push(Constraint(ty, val.get_type())); 44 | constr 45 | } 46 | TypedNode::Binary { 47 | ty, 48 | left, 49 | right, 50 | op_token, 51 | } => { 52 | let mut constr = self.collect(*left.clone()); 53 | constr.extend(self.collect(*right.clone())); 54 | constr.push(Constraint(left.get_type(), right.get_type())); 55 | constr.push(Constraint( 56 | ty.clone(), 57 | match op_token.value { 58 | Tokens::Plus 59 | | Tokens::Minus 60 | | Tokens::Multiply 61 | | Tokens::Divide 62 | | Tokens::Modulo => left.get_type(), 63 | _ => Type::Boolean, 64 | }, 65 | )); 66 | constr 67 | } 68 | TypedNode::Let { ty, val, .. } => { 69 | let mut constr = self.collect(*val.clone()); 70 | constr.push(Constraint(ty, val.get_type())); 71 | constr 72 | } 73 | TypedNode::ReLet { ty, prev, val, .. } => { 74 | let mut constr = self.collect(*val.clone()); 75 | constr.push(Constraint(prev.clone(), val.get_type())); 76 | constr.push(Constraint(ty, prev)); 77 | constr 78 | } 79 | TypedNode::Fun { 80 | ty, params, body, .. 81 | } => { 82 | let mut constr = self.collect(*body.clone()); 83 | constr.push(Constraint( 84 | ty, 85 | Type::Fun( 86 | params.iter().map(|x| x.ty.clone()).collect(), 87 | box body.get_type(), 88 | ), 89 | )); 90 | 91 | constr 92 | } 93 | TypedNode::Extern { 94 | ty, 95 | return_type, 96 | args, 97 | .. 98 | } => { 99 | let mut constr = vec![]; 100 | 101 | let mut param_ty = vec![]; 102 | for arg in args { 103 | constr.extend(self.collect(arg.clone())); 104 | param_ty.push(arg.get_type()); 105 | } 106 | 107 | constr.extend(self.collect(*return_type.clone())); 108 | 109 | constr.push(Constraint( 110 | ty, 111 | Type::Fun(param_ty, box return_type.get_type().clone()), 112 | )); 113 | 114 | constr 115 | } 116 | TypedNode::Call { ty, fun, args } => { 117 | let mut constr = self.collect(*fun.clone()); 118 | let mut args_ty = vec![]; 119 | for arg in args.clone() { 120 | constr.extend(self.collect(arg.clone())); 121 | args_ty.push(arg.get_type()); 122 | } 123 | constr.push(Constraint(fun.get_type(), Type::Fun(args_ty, box ty))); 124 | constr 125 | } 126 | TypedNode::Return { ty, val } => { 127 | let mut constr = self.collect(*val.clone()); 128 | constr.push(Constraint(ty, val.get_type())); 129 | constr 130 | } 131 | TypedNode::If { 132 | ty, 133 | cases, 134 | else_case, 135 | } => { 136 | let mut constr = vec![]; 137 | for (cond, body) in cases { 138 | constr.extend(self.collect(cond.clone())); 139 | constr.push(Constraint(Type::Boolean, cond.get_type())); 140 | constr.extend(self.collect(body.clone())); 141 | constr.push(Constraint(ty.clone(), body.get_type())); 142 | } 143 | 144 | if let Some(tn) = else_case { 145 | constr.push(Constraint(ty.clone(), tn.get_type())); 146 | constr.extend(self.collect(*tn)); 147 | } 148 | 149 | return constr; 150 | } 151 | TypedNode::For { 152 | ty, 153 | start, 154 | end, 155 | step, 156 | body, 157 | .. 158 | } => { 159 | let mut constr = self.collect(*start.clone()); 160 | constr.push(Constraint(ty, body.get_type())); 161 | constr.push(Constraint(start.get_type(), end.get_type())); 162 | constr.push(Constraint(start.get_type(), step.get_type())); 163 | constr.extend(self.collect(*end)); 164 | constr.extend(self.collect(*step)); 165 | constr.extend(self.collect(*body)); 166 | constr 167 | } 168 | TypedNode::While { ty, cond, body } => { 169 | let mut constr = self.collect(*body.clone()); 170 | constr.push(Constraint(ty, body.get_type())); 171 | constr.extend(self.collect(*cond.clone())); 172 | constr.push(Constraint(Type::Boolean, cond.get_type())); 173 | constr 174 | } 175 | TypedNode::Array { ty, elements } => { 176 | let elem_ty = if let Some(elem) = elements.first() { 177 | elem.get_type() 178 | } else { 179 | Type::Null 180 | }; 181 | let mut constr = self.collect(elements.first().unwrap().clone()); 182 | 183 | for element in elements.iter().skip(1).collect::>() { 184 | constr.push(Constraint(elem_ty.clone(), element.get_type())); 185 | constr.extend(self.collect(element.clone())); 186 | } 187 | 188 | constr.push(Constraint( 189 | ty.clone(), 190 | Type::Array(box elem_ty.clone(), elements.len() as u32), 191 | )); 192 | constr 193 | } 194 | TypedNode::Index { ty, array, idx } => { 195 | let mut constr = self.collect(*array.clone()); 196 | constr.extend(self.collect(*idx.clone())); 197 | constr.push(Constraint(idx.get_type(), Type::Int)); 198 | constr.push(Constraint(array.get_type(), Type::Array(box ty, 0))); 199 | constr 200 | } 201 | TypedNode::Object { ty, properties } => { 202 | let mut constr = vec![]; 203 | let mut tree = BTreeMap::new(); 204 | for (name, node) in properties { 205 | constr.extend(self.collect(node.clone())); 206 | tree.insert(name.clone(), node.get_type()); 207 | } 208 | constr.push(Constraint(ty, Type::create_obj(tree))); 209 | constr 210 | } 211 | TypedNode::CObject { ty, object } => { 212 | let mut constr = self.collect(*object.clone()); 213 | constr.push(Constraint(ty, object.get_type())); 214 | constr 215 | } 216 | TypedNode::CToBzxObject { ty, object } => { 217 | let mut constr = self.collect(*object.clone()); 218 | constr.push(Constraint(ty, object.get_type())); 219 | constr 220 | } 221 | TypedNode::ObjectAccess { 222 | ty, 223 | property, 224 | object, 225 | } => { 226 | let mut constr = self.collect(*object.clone()); 227 | constr.push(Constraint( 228 | object.get_type(), 229 | Type::Object(BTreeMap::from([(property, ty)])), 230 | )); 231 | constr 232 | } 233 | TypedNode::ObjectEdit { 234 | ty, 235 | property, 236 | object, 237 | new_val, 238 | } => { 239 | let mut constr = self.collect(*object.clone()); 240 | constr.extend(self.collect(*new_val.clone())); 241 | constr.push(Constraint(ty.clone(), new_val.get_type())); 242 | constr.push(Constraint( 243 | object.get_type(), 244 | Type::Object(BTreeMap::from([(property, new_val.get_type())])), 245 | )); 246 | constr 247 | } 248 | TypedNode::ObjectMethodCall { 249 | ty, 250 | object, 251 | args, 252 | property, 253 | } => { 254 | let mut constr = self.collect(*object.clone()); 255 | let mut args_ty = vec![]; 256 | for arg in args.clone() { 257 | constr.extend(self.collect(arg.clone())); 258 | args_ty.push(arg.get_type()); 259 | } 260 | constr.push(Constraint( 261 | object.get_type(), 262 | Type::Object(BTreeMap::from([(property, Type::Fun(args_ty, box ty))])), 263 | )); 264 | constr 265 | } 266 | TypedNode::Class { 267 | ty, 268 | constructor, 269 | methods, 270 | properties, 271 | name: _, 272 | static_obj, 273 | } => { 274 | let mut constr = self.collect(*static_obj.clone()); 275 | let mut tree = BTreeMap::new(); 276 | 277 | let mut methods_tree = HashMap::new(); 278 | methods_tree.insert("constructor".to_string(), constructor.get_type()); 279 | 280 | for (name, val) in properties { 281 | tree.insert(name.clone(), val.get_type()); 282 | constr.extend(self.collect(val.clone())); 283 | } 284 | 285 | let obj = Type::create_obj(tree); 286 | 287 | constr.push(Constraint(ty, Type::Class(box obj))); 288 | 289 | constr.extend(self.collect(*constructor.clone())); 290 | for (name, val) in methods { 291 | methods_tree.insert(name.clone(), val.get_type()); 292 | constr.extend(self.collect(val.clone())); 293 | } 294 | self.methods.insert( 295 | Type::Array(box Type::Int, Type::last_aligner() as u32), 296 | methods_tree, 297 | ); 298 | 299 | constr 300 | } 301 | TypedNode::ClassInit { 302 | ty, 303 | class, 304 | constructor_params, 305 | } => { 306 | let mut constr = vec![]; 307 | let mut params = vec![]; 308 | 309 | for param in constructor_params { 310 | constr.extend(self.collect(param.clone())); 311 | params.push(param.get_type()); 312 | } 313 | 314 | constr.push(Constraint(Type::Class(box ty.clone()), class)); 315 | 316 | constr 317 | } 318 | _ => vec![], 319 | } 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /crates/bzxc_type_system/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | #![feature(box_syntax, box_patterns)] 14 | #![allow(unused_variables)] 15 | 16 | use llvm_sys::prelude::LLVMContextRef; 17 | use std::collections::HashMap; 18 | 19 | use bzxc_shared::{LLVMNode, Node, Type}; 20 | use type_env::TypeEnv; 21 | 22 | mod annotate; 23 | mod constraint; 24 | mod llvm_node; 25 | mod substitution; 26 | mod type_env; 27 | mod unifier; 28 | 29 | pub struct TypeSystem { 30 | node: Node, 31 | methods: HashMap>, 32 | type_env: TypeEnv, 33 | class_env: HashMap, 34 | pub context: LLVMContextRef, 35 | } 36 | 37 | impl TypeSystem { 38 | pub fn new(node: Node, context: LLVMContextRef) -> Self { 39 | TypeSystem { 40 | node, 41 | methods: HashMap::new(), 42 | type_env: TypeEnv::new(), 43 | class_env: HashMap::new(), 44 | context, 45 | } 46 | } 47 | 48 | pub fn llvm_node(&mut self) -> LLVMNode { 49 | let annotation = self.annotate(self.node.clone()); 50 | let constraints = self.collect(annotation.clone()); 51 | let substitution = self.unify(constraints.clone()); 52 | self.gen(substitution, annotation) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /crates/bzxc_type_system/src/llvm_node.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | use bzxc_shared::{LLVMNode, Type, TypedNode}; 14 | 15 | use crate::substitution::Substitution; 16 | use crate::TypeSystem; 17 | 18 | impl TypeSystem { 19 | pub(crate) fn gen(&self, subs: Substitution, node: TypedNode) -> LLVMNode { 20 | let llvm = |ty: Type| ty.llvm(self.context, subs.0.clone()); 21 | match node { 22 | TypedNode::Statements(stmts) => LLVMNode::Statements( 23 | stmts 24 | .iter() 25 | .map(|x| self.gen(subs.clone(), x.clone())) 26 | .collect(), 27 | ), 28 | TypedNode::Int { ty, val } => LLVMNode::Int { ty: llvm(ty), val }, 29 | TypedNode::Float { ty, val } => LLVMNode::Float { ty: llvm(ty), val }, 30 | TypedNode::Boolean { ty, val } => LLVMNode::Boolean { ty: llvm(ty), val }, 31 | TypedNode::Char { ty, val } => LLVMNode::Char { ty: llvm(ty), val }, 32 | TypedNode::String { ty, val } => LLVMNode::String { ty: llvm(ty), val }, 33 | TypedNode::Unary { ty, val, op_token } => LLVMNode::Unary { 34 | ty: llvm(ty), 35 | op_token, 36 | val: box self.gen(subs, *val), 37 | }, 38 | TypedNode::Binary { 39 | ty, 40 | left, 41 | right, 42 | op_token, 43 | } => LLVMNode::Binary { 44 | ty: llvm(ty), 45 | left: box self.gen(subs.clone(), *left), 46 | right: box self.gen(subs, *right), 47 | op_token, 48 | }, 49 | TypedNode::Fun { 50 | ty, 51 | name, 52 | params, 53 | body, 54 | } => LLVMNode::Fun { 55 | body: box self.gen(subs.clone(), *body), 56 | ty: llvm(ty), 57 | name, 58 | params: params 59 | .iter() 60 | .map(|x| (x.name.clone(), llvm(x.ty.clone()))) 61 | .collect(), 62 | }, 63 | TypedNode::Let { ty, name, val } | TypedNode::ReLet { ty, name, val, .. } => { 64 | LLVMNode::Let { 65 | ty: llvm(ty), 66 | name, 67 | val: box self.gen(subs, *val), 68 | } 69 | } 70 | 71 | TypedNode::Var { ty, name } => LLVMNode::Var { ty: llvm(ty), name }, 72 | TypedNode::Call { ty, fun, args } => LLVMNode::Call { 73 | ty: llvm(ty), 74 | args: args 75 | .iter() 76 | .map(|arg| self.gen(subs.clone(), arg.clone())) 77 | .collect(), 78 | fun: box self.gen(subs, *fun), 79 | }, 80 | TypedNode::Return { ty, val } => LLVMNode::Return { 81 | ty: llvm(ty), 82 | val: box self.gen(subs, *val), 83 | }, 84 | TypedNode::Null { ty } => LLVMNode::Null { ty: llvm(ty) }, 85 | TypedNode::If { 86 | ty, 87 | cases, 88 | else_case, 89 | } => LLVMNode::If { 90 | ty: llvm(ty), 91 | cases: cases 92 | .iter() 93 | .map(|(cond, body)| { 94 | ( 95 | self.gen(subs.clone(), cond.clone()), 96 | self.gen(subs.clone(), body.clone()), 97 | ) 98 | }) 99 | .collect(), 100 | else_case: if let Some(els) = else_case { 101 | Some(box self.gen(subs, *els)) 102 | } else { 103 | None 104 | }, 105 | }, 106 | TypedNode::While { ty, cond, body } => LLVMNode::While { 107 | ty: llvm(ty), 108 | cond: box self.gen(subs.clone(), *cond), 109 | body: box self.gen(subs, *body), 110 | }, 111 | TypedNode::For { 112 | ty, 113 | var, 114 | start, 115 | end, 116 | step, 117 | body, 118 | } => LLVMNode::For { 119 | ty: llvm(ty), 120 | body: box self.gen(subs.clone(), *body), 121 | var, 122 | end: box self.gen(subs.clone(), *end), 123 | start: box self.gen(subs.clone(), *start), 124 | step: box self.gen(subs.clone(), *step), 125 | }, 126 | TypedNode::Array { ty, elements } => LLVMNode::Array { 127 | ty: llvm(ty), 128 | elements: elements 129 | .iter() 130 | .map(|elem| self.gen(subs.clone(), elem.clone())) 131 | .collect(), 132 | }, 133 | TypedNode::Index { ty, array, idx } => LLVMNode::Index { 134 | ty: llvm(ty), 135 | array: box self.gen(subs.clone(), *array), 136 | idx: box self.gen(subs.clone(), *idx), 137 | }, 138 | TypedNode::Object { ty, properties } => LLVMNode::Object { 139 | ty: llvm(ty), 140 | properties: properties 141 | .iter() 142 | .map(|(name, node)| (name.clone(), self.gen(subs.clone(), node.clone()))) 143 | .collect(), 144 | }, 145 | TypedNode::CObject { ty, object } => LLVMNode::CObject { 146 | ty: llvm(ty), 147 | object: box self.gen(subs.clone(), *object), 148 | }, 149 | TypedNode::CToBzxObject { ty, object } => LLVMNode::CToBzxObject { 150 | ty: llvm(ty), 151 | object: box self.gen(subs.clone(), *object), 152 | }, 153 | TypedNode::ObjectAccess { 154 | ty, 155 | object, 156 | property, 157 | } => LLVMNode::ObjectAccess { 158 | ty: llvm(ty), 159 | object: box self.gen(subs, *object), 160 | property, 161 | }, 162 | TypedNode::ObjectEdit { 163 | ty, 164 | property, 165 | object, 166 | new_val, 167 | } => LLVMNode::ObjectEdit { 168 | ty: llvm(ty), 169 | new_val: box self.gen(subs.clone(), *new_val), 170 | object: box self.gen(subs.clone(), *object), 171 | property, 172 | }, 173 | TypedNode::ObjectMethodCall { 174 | ty, 175 | property, 176 | object, 177 | args, 178 | } => LLVMNode::ObjectMethodCall { 179 | ty: llvm(ty), 180 | property, 181 | args: args 182 | .iter() 183 | .map(|x| self.gen(subs.clone(), x.clone()).clone()) 184 | .collect(), 185 | object: box self.gen(subs, *object), 186 | }, 187 | TypedNode::Class { 188 | ty, 189 | properties, 190 | methods, 191 | constructor, 192 | name, 193 | static_obj, 194 | } => LLVMNode::Class { 195 | ty: llvm(ty), 196 | name, 197 | constructor: box self.gen(subs.clone(), *constructor), 198 | properties: properties 199 | .iter() 200 | .map(|(name, node)| (name.clone(), self.gen(subs.clone(), node.clone()))) 201 | .collect(), 202 | methods: methods 203 | .iter() 204 | .map(|(name, node)| (name.clone(), self.gen(subs.clone(), node.clone()))) 205 | .collect(), 206 | static_obj: box self.gen(subs.clone(), *static_obj), 207 | }, 208 | TypedNode::ClassInit { 209 | ty, 210 | class, 211 | constructor_params, 212 | } => LLVMNode::ClassInit { 213 | ty: llvm(ty), 214 | class: llvm(class), 215 | constructor_params: constructor_params 216 | .iter() 217 | .map(|x| self.gen(subs.clone(), x.clone())) 218 | .collect(), 219 | }, 220 | TypedNode::Extern { 221 | return_type, 222 | name, 223 | args, 224 | ty, 225 | var_args, 226 | } => LLVMNode::Extern { 227 | ty: llvm(ty), 228 | return_type: box self.gen(subs.clone(), *return_type), 229 | name, 230 | args: args 231 | .iter() 232 | .map(|x| self.gen(subs.clone(), x.clone())) 233 | .collect(), 234 | var_args, 235 | }, 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /crates/bzxc_type_system/src/substitution.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | use std::collections::BTreeMap; 14 | 15 | use bzxc_shared::Type; 16 | 17 | use crate::constraint::Constraint; 18 | 19 | #[derive(Debug, Clone, PartialEq, PartialOrd)] 20 | pub struct Substitution(pub BTreeMap); 21 | impl Substitution { 22 | pub fn empty() -> Self { 23 | Substitution(BTreeMap::new()) 24 | } 25 | 26 | pub fn apply(&self, constraints: Vec) -> Vec { 27 | constraints 28 | .iter() 29 | .map(|constraint| { 30 | Constraint( 31 | self.apply_ty(constraint.0.clone()), 32 | self.apply_ty(constraint.1.clone()), 33 | ) 34 | }) 35 | .collect() 36 | } 37 | 38 | pub fn apply_ty(&self, ty: Type) -> Type { 39 | self.0.iter().fold(ty.clone(), |result, solution| { 40 | let (ty, solution_type) = solution; 41 | if let Type::Var(tvar) = ty { 42 | self.substitute_tvar(result, *tvar, solution_type.clone()) 43 | } else { 44 | unreachable!(); 45 | } 46 | }) 47 | } 48 | 49 | fn substitute_tvar(&self, ty: Type, tvar: i32, sol_ty: Type) -> Type { 50 | match ty { 51 | Type::Fun(params, ret) => Type::Fun( 52 | params 53 | .iter() 54 | .map(|x| self.substitute_tvar(x.clone(), tvar, sol_ty.clone())) 55 | .collect(), 56 | box self.substitute_tvar(*ret.clone(), tvar, sol_ty.clone()), 57 | ), 58 | Type::Array(ty, size) => Type::Array(box self.substitute_tvar(*ty, tvar, sol_ty), size), 59 | Type::Object(ty) => Type::Object( 60 | ty.iter() 61 | .map(|(name, ty)| { 62 | ( 63 | name.clone(), 64 | self.substitute_tvar(ty.clone(), tvar, sol_ty.clone()), 65 | ) 66 | }) 67 | .collect(), 68 | ), 69 | Type::Class(obj) => Type::Class(box self.substitute_tvar(*obj, tvar, sol_ty)), 70 | Type::Var(tvar2) => { 71 | if tvar == tvar2 { 72 | sol_ty 73 | } else { 74 | ty 75 | } 76 | } 77 | _ => ty, 78 | } 79 | } 80 | 81 | pub fn compose(&mut self, other: Substitution) -> Substitution { 82 | let mut self_substituded: BTreeMap = self 83 | .0 84 | .clone() 85 | .into_iter() 86 | .map(|(k, s)| (k, other.apply_ty(s))) 87 | .collect(); 88 | self_substituded.extend(other.0); 89 | Substitution(self_substituded) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /crates/bzxc_type_system/src/type_env.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | use std::collections::HashMap; 14 | 15 | use bzxc_shared::Type; 16 | 17 | #[derive(Debug, Clone)] 18 | pub struct TypeEnv(Vec>); 19 | 20 | impl TypeEnv { 21 | pub fn new() -> Self { 22 | Self(vec![HashMap::new()]) 23 | } 24 | 25 | pub fn set(&mut self, k: String, v: Type) { 26 | self.0.last_mut().unwrap().insert(k, v); 27 | } 28 | 29 | pub fn get(&self, k: String) -> Option { 30 | for map in self.0.iter().rev() { 31 | if let Some(v) = map.get(&k) { 32 | return Some(v.clone()); 33 | } 34 | } 35 | 36 | None 37 | } 38 | 39 | pub fn push_scope(&mut self) { 40 | self.0.push(HashMap::new()); 41 | } 42 | 43 | pub fn pop_scope(&mut self) { 44 | self.0.pop(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /crates/bzxc_type_system/src/unifier.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 to 2021 BlazifyOrg 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | use std::collections::BTreeMap; 14 | 15 | use bzxc_shared::Type; 16 | 17 | use crate::{constraint::Constraint, substitution::Substitution, TypeSystem}; 18 | 19 | impl TypeSystem { 20 | pub(crate) fn unify(&mut self, constraints: Vec) -> Substitution { 21 | if constraints.is_empty() { 22 | Substitution::empty() 23 | } else { 24 | let mut it = constraints.into_iter(); 25 | let mut subst = self.unify_one(it.next().unwrap()); 26 | let subst_tail = subst.apply(it.collect()); 27 | let subst_tail: Substitution = self.unify(subst_tail); 28 | subst.compose(subst_tail) 29 | } 30 | } 31 | 32 | pub fn unify_one(&mut self, constraint: Constraint) -> Substitution { 33 | match (constraint.0, constraint.1) { 34 | (Type::Fun(params, ret1), Type::Fun(args, ret2)) => { 35 | let mut constraints = vec![]; 36 | 37 | for i in 0..params.len() { 38 | constraints.push(Constraint( 39 | params.get(i).unwrap().clone(), 40 | args.get(i).unwrap().clone(), 41 | )); 42 | } 43 | 44 | constraints.push(Constraint(*ret1.clone(), *ret2.clone())); 45 | 46 | self.unify(constraints) 47 | } 48 | (Type::Class(ty1), Type::Class(ty2)) => self.unify_one(Constraint(*ty1, *ty2)), 49 | (Type::Array(ty1, _), Type::Array(ty2, _)) => self.unify_one(Constraint(*ty1, *ty2)), 50 | (Type::Object(tree1), Type::Object(tree2)) => { 51 | let main_tree; 52 | let other_tree; 53 | 54 | if tree1.len() > tree2.len() { 55 | main_tree = tree1; 56 | other_tree = tree2; 57 | } else { 58 | main_tree = tree2; 59 | other_tree = tree1; 60 | }; 61 | 62 | let a = main_tree.get("%alignment%"); 63 | let b = other_tree.get("%alignment%"); 64 | 65 | let mut constr = vec![]; 66 | for (name, ty1) in &other_tree { 67 | if name.as_str() == "%alignment%" { 68 | continue; 69 | } 70 | let mut ty2 = main_tree.get(name); 71 | 72 | if ty2.is_none() { 73 | ty2 = self 74 | .methods 75 | .get(a.unwrap_or_else(|| b.unwrap())) 76 | .unwrap_or_else(|| self.methods.get(b.unwrap()).unwrap()) 77 | .get(name); 78 | } 79 | 80 | constr.push(Constraint(ty1.clone(), ty2.unwrap().clone())); 81 | } 82 | 83 | self.unify(constr) 84 | } 85 | (Type::Var(tvar), ty) => self.unify_var(tvar, ty), 86 | (ty, Type::Var(tvar)) => self.unify_var(tvar, ty), 87 | (a, b) => { 88 | if a == b { 89 | Substitution::empty() 90 | } else { 91 | panic!("Cannot unify {:#?} with {:#?}", a, b) 92 | } 93 | } 94 | } 95 | } 96 | 97 | pub fn unify_var(&mut self, tvar: i32, ty: Type) -> Substitution { 98 | match ty.clone() { 99 | Type::Var(tvar2) => { 100 | if tvar == tvar2 { 101 | Substitution::empty() 102 | } else { 103 | Substitution(BTreeMap::from([(Type::Var(tvar), ty)])) 104 | } 105 | } 106 | _ => { 107 | if self.occurs(tvar, ty.clone()) { 108 | panic!("circular type") 109 | } else { 110 | let methods = self.methods.clone(); 111 | for (x, class) in methods { 112 | for (y, method) in class { 113 | if method == Type::Var(tvar) { 114 | self.methods 115 | .get_mut(&x.clone()) 116 | .unwrap() 117 | .insert(y.clone(), ty.clone()); 118 | } 119 | } 120 | } 121 | Substitution(BTreeMap::from([(Type::Var(tvar), ty)])) 122 | } 123 | } 124 | } 125 | } 126 | 127 | fn occurs(&self, tvar: i32, ty: Type) -> bool { 128 | match ty { 129 | Type::Fun(p, r) => { 130 | p.iter() 131 | .map(|x| self.occurs(tvar, x.clone())) 132 | .collect::>() 133 | .contains(&true) 134 | | self.occurs(tvar, *r.clone()) 135 | } 136 | Type::Array(arr, _) => self.occurs(tvar, *arr), 137 | Type::Class(klass) => self.occurs(tvar, *klass), 138 | Type::Object(obj) => obj 139 | .iter() 140 | .map(|(_, ty)| self.occurs(tvar, ty.clone())) 141 | .collect::>() 142 | .contains(&true), 143 | Type::Var(tvar2) => tvar == tvar2, 144 | _ => false, 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly -------------------------------------------------------------------------------- /tests/main/main.bzx: -------------------------------------------------------------------------------- 1 | extern int print(string, ...) 2 | extern int println(string, ...) 3 | extern float input_float() 4 | 5 | class A { 6 | var x = 0.0 7 | fun() { soul.x = 0.0 } 8 | fun a() { soul.x = input_float() } 9 | fun b() { soul.x = soul.x * 6.0 } 10 | fun c() { println("%f", soul.x) } 11 | } 12 | 13 | var a = new A(); 14 | a.a() 15 | a.b() 16 | a.c() --------------------------------------------------------------------------------