├── .gitignore ├── pull_request_template.md ├── CONTRIBUTING.md ├── tests ├── rockffitestlib.so ├── integration_tests.rs ├── token_test.rs └── tokenizer_tests.rs ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── examples ├── ffi.rck ├── sieve.rck └── mandelbrot.rck ├── test_utils ├── Cargo.toml └── src │ └── lib.rs ├── .github ├── actions │ ├── install_llvm │ │ └── action.yml │ └── install_valgrind │ │ └── action.yml ├── workflows │ ├── audit.yml │ ├── coverage.yml │ ├── main.yml │ └── release.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── src ├── lib.rs ├── compiler │ ├── break_visitor.rs │ ├── bool.rs │ ├── numeric.rs │ ├── grouping.rs │ ├── load.rs │ ├── utils.rs │ ├── string.rs │ ├── func_decl_vistor.rs │ ├── extern_visitor.rs │ ├── program.rs │ ├── unary.rs │ ├── identifier.rs │ ├── while_visitor.rs │ ├── value.rs │ ├── conditional.rs │ ├── variable.rs │ ├── scope.rs │ ├── assignment.rs │ ├── func_call.rs │ ├── binary.rs │ └── mod.rs ├── llvm │ ├── llvm_error.rs │ ├── typ.rs │ ├── utils.rs │ ├── basic_block.rs │ ├── mod.rs │ ├── value.rs │ ├── function.rs │ ├── engine.rs │ ├── pass_manager.rs │ ├── module.rs │ ├── context.rs │ └── builder.rs ├── main.rs ├── expression.rs ├── visitor.rs ├── token.rs ├── tokenizer.rs └── parser.rs ├── stdlib ├── Cargo.toml └── src │ └── lib.rs ├── Cargo.toml ├── MIT-LICENSE.txt ├── wix ├── License.rtf └── main.wxs ├── README.md ├── CODE_OF_CONDUCT.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.idea 3 | playground.rck 4 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | ## Proposed Changes 4 | 5 | - 6 | - 7 | - 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 1. Fork. 2 | 2. Create a pull request. 3 | 3. Make sure all automated checks pass. 4 | -------------------------------------------------------------------------------- /tests/rockffitestlib.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jarkonik/rocklang/HEAD/tests/rockffitestlib.so -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "coverage-gutters.coverageBaseDir": "target/tarpaulin", 3 | "coverage-gutters.showLineCoverage": true 4 | } 5 | -------------------------------------------------------------------------------- /examples/ffi.rck: -------------------------------------------------------------------------------- 1 | load("./tests/rockffitestlib.so") 2 | rockffitestlib = extern("rockffitest") 3 | 4 | print("2 + 3 is ") 5 | print(string(rockffitestlib(2, 3))) 6 | print("\n") 7 | -------------------------------------------------------------------------------- /test_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_utils" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /.github/actions/install_llvm/action.yml: -------------------------------------------------------------------------------- 1 | name: "Install llvm" 2 | runs: 3 | using: "composite" 4 | steps: 5 | - name: Install LLVM 6 | uses: KyleMayes/install-llvm-action@v1.5.0 7 | with: 8 | version: "13.0" 9 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | #[macro_use] 3 | extern crate test_utils; 4 | 5 | mod llvm; 6 | mod visitor; 7 | 8 | pub mod compiler; 9 | pub mod expression; 10 | pub mod parser; 11 | pub mod token; 12 | pub mod tokenizer; 13 | -------------------------------------------------------------------------------- /stdlib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stdlib" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [lib] 11 | name = "stdlib" 12 | -------------------------------------------------------------------------------- /src/compiler/break_visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::visitor::BreakVisitor; 2 | 3 | use super::{Compiler, CompilerResult, Value}; 4 | 5 | impl BreakVisitor> for Compiler { 6 | fn visit_break(&mut self) -> CompilerResult { 7 | Ok(Value::Break) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.github/actions/install_valgrind/action.yml: -------------------------------------------------------------------------------- 1 | name: "Install valgrind" 2 | runs: 3 | using: "composite" 4 | steps: 5 | - name: Install valgrind 6 | shell: bash 7 | run: sudo apt install valgrind 8 | - name: Install cargo valgrind 9 | shell: bash 10 | run: cargo install cargo-valgrind 11 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | jobs: 6 | audit: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - uses: actions-rs/audit-check@v1 11 | with: 12 | token: ${{ secrets.GITHUB_TOKEN }} 13 | -------------------------------------------------------------------------------- /src/compiler/bool.rs: -------------------------------------------------------------------------------- 1 | use crate::visitor::BoolVisitor; 2 | 3 | use super::{Compiler, CompilerResult, Value}; 4 | 5 | impl BoolVisitor> for Compiler { 6 | fn visit_bool(&mut self, expr: &bool) -> CompilerResult { 7 | Ok(Value::Bool(self.context.const_bool(*expr))) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/llvm/llvm_error.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::error::Error; 3 | 4 | #[derive(Clone, Debug)] 5 | pub struct LLVMError {} 6 | 7 | impl fmt::Display for LLVMError { 8 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 9 | write!(f, "LLVMError") 10 | } 11 | } 12 | 13 | impl Error for LLVMError {} 14 | -------------------------------------------------------------------------------- /src/compiler/numeric.rs: -------------------------------------------------------------------------------- 1 | use crate::visitor::NumericVisitor; 2 | 3 | use super::{Compiler, CompilerResult, Value}; 4 | 5 | impl NumericVisitor> for Compiler { 6 | fn visit_numeric(&mut self, expr: &f64) -> CompilerResult { 7 | Ok(Value::Numeric(self.context.const_double(*expr))) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/compiler/grouping.rs: -------------------------------------------------------------------------------- 1 | use crate::visitor::{GroupingVisitor, Visitor}; 2 | 3 | use super::{Compiler, CompilerResult, Value}; 4 | 5 | impl GroupingVisitor> for Compiler { 6 | fn visit_grouping(&mut self, expr: &crate::expression::Grouping) -> CompilerResult { 7 | self.walk(&*expr.0) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/llvm/typ.rs: -------------------------------------------------------------------------------- 1 | use llvm::core::LLVMPointerType; 2 | 3 | extern crate llvm_sys as llvm; 4 | 5 | #[derive(Debug, Clone, Copy)] 6 | pub struct Type(pub *mut llvm::LLVMType); 7 | 8 | impl Type { 9 | pub fn new(typ: *mut llvm::LLVMType) -> Self { 10 | Type(typ) 11 | } 12 | 13 | pub fn pointer_type(&self, address_space: u32) -> Type { 14 | Type(unsafe { LLVMPointerType(self.0, address_space) }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/llvm/utils.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | borrow::Cow, 3 | ffi::{CStr, CString}, 4 | }; 5 | 6 | pub fn c_str(mut s: &str) -> Cow { 7 | if s.is_empty() { 8 | s = "\0"; 9 | } 10 | 11 | if !s.chars().rev().any(|ch| ch == '\0') { 12 | return Cow::from(CString::new(s).expect("unreachable since null bytes are checked")); 13 | } 14 | 15 | unsafe { Cow::from(CStr::from_ptr(s.as_ptr() as *const _)) } 16 | } 17 | -------------------------------------------------------------------------------- /src/compiler/load.rs: -------------------------------------------------------------------------------- 1 | use crate::visitor::LoadVisitor; 2 | 3 | use super::{Compiler, CompilerError, CompilerResult, Value}; 4 | 5 | impl LoadVisitor> for Compiler { 6 | fn visit_load(&mut self, name: &str) -> CompilerResult { 7 | if self.context.load_libary_permanently(name).is_err() { 8 | Err(CompilerError::LoadLibaryError(name.to_string()))? 9 | } else { 10 | Ok(Value::Void) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Watch coverage", 8 | "type": "shell", 9 | "group": "test", 10 | "command": "cargo watch -w src -w tests -x 'tarpaulin --timeout 120 --skip-clean --ignore-tests --output-dir target/tarpaulin -o Lcov'", 11 | "isBackground": true 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/llvm/basic_block.rs: -------------------------------------------------------------------------------- 1 | use llvm::core::LLVMGetBasicBlockParent; 2 | 3 | use super::Function; 4 | 5 | extern crate llvm_sys as llvm; 6 | 7 | #[derive(Clone, Copy, Debug)] 8 | pub struct BasicBlock(pub *mut llvm::LLVMBasicBlock); 9 | 10 | #[allow(dead_code)] 11 | impl BasicBlock { 12 | pub fn new(block: *mut llvm::LLVMBasicBlock) -> Self { 13 | BasicBlock(block) 14 | } 15 | 16 | pub fn get_parent(&self) -> Function { 17 | Function::from(unsafe { LLVMGetBasicBlockParent(self.0) }) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/llvm/mod.rs: -------------------------------------------------------------------------------- 1 | // TODO: Move to a crate ,remove allow(dead_code) 2 | 3 | mod basic_block; 4 | mod builder; 5 | mod context; 6 | mod engine; 7 | mod function; 8 | mod llvm_error; 9 | mod module; 10 | mod pass_manager; 11 | mod typ; 12 | mod utils; 13 | mod value; 14 | 15 | pub use basic_block::BasicBlock; 16 | pub use builder::Builder; 17 | pub use builder::Cmp; 18 | pub use context::Context; 19 | pub use engine::Engine; 20 | pub use function::Function; 21 | pub use llvm_error::LLVMError; 22 | pub use module::Module; 23 | pub use pass_manager::PassManager; 24 | pub use typ::Type; 25 | pub use value::Value; 26 | -------------------------------------------------------------------------------- /examples/sieve.rck: -------------------------------------------------------------------------------- 1 | mem_set = (vec: vec, val: number, n: number): vec => { 2 | i = 0 3 | while i < n { 4 | vec_set(vec, i, val) 5 | i = i + 1 6 | } 7 | vec 8 | } 9 | 10 | sieve = (n: number): void => { 11 | v = vec_new() 12 | prime = mem_set(v, 1, n + 1) 13 | 14 | p = 2 15 | 16 | while p * p <= n { 17 | if vec_get(prime, p) == 1 { 18 | i = p * p 19 | while i <= n { 20 | vec_set(prime, i, 0) 21 | i = i + p 22 | } 23 | } 24 | 25 | p = p + 1 26 | } 27 | 28 | p = 2 29 | 30 | while p <= n { 31 | if vec_get(prime, p) == 1 { 32 | print(string(p)) 33 | print("\n") 34 | } 35 | 36 | p = p + 1 37 | } 38 | } 39 | 40 | sieve(10) 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/compiler/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | llvm::{self, Context}, 3 | parser, 4 | }; 5 | 6 | pub fn get_llvm_type(context: &Context, typ: &parser::Type) -> llvm::Type { 7 | match typ { 8 | parser::Type::Vector => context.void_type().pointer_type(0), 9 | parser::Type::Numeric => context.double_type(), 10 | parser::Type::Function => todo!(), 11 | parser::Type::Void => context.void_type(), 12 | parser::Type::Ptr => context.void_type().pointer_type(0), 13 | parser::Type::String => context.void_type().pointer_type(0), 14 | parser::Type::Bool => context.i1_type(), 15 | parser::Type::CString => context.i8_type().pointer_type(0), 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | test: 6 | name: coverage 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout repository 10 | uses: actions/checkout@v2 11 | 12 | - uses: ./.github/actions/install_llvm 13 | 14 | - name: Install tarpaulin 15 | run: cargo install cargo-tarpaulin 16 | shell: bash 17 | 18 | - name: Generate code coverage 19 | run: | 20 | cargo tarpaulin --ignore-tests --verbose --all-features --workspace --timeout 120 --out Xml 21 | 22 | - name: Upload to codecov.io 23 | uses: codecov/codecov-action@v2 24 | with: 25 | fail_ci_if_error: true 26 | -------------------------------------------------------------------------------- /src/compiler/string.rs: -------------------------------------------------------------------------------- 1 | use crate::visitor::StringVisitor; 2 | 3 | use super::{Compiler, CompilerResult, Value}; 4 | use crate::compiler::LLVMCompiler; 5 | 6 | impl StringVisitor> for Compiler { 7 | fn visit_string(&mut self, expr: &str) -> CompilerResult { 8 | let with_newlines = expr.to_string().replace("\\n", "\n"); 9 | 10 | let string_from_c_string = self.module.get_function("string_from_c_string").unwrap(); 11 | let ptr = self 12 | .builder 13 | .build_global_string_ptr(with_newlines.as_str(), ""); 14 | let string = Value::String(self.builder.build_call(&string_from_c_string, &[ptr], "")); 15 | self.track_maybe_orphaned(string); 16 | 17 | Ok(string) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/llvm/value.rs: -------------------------------------------------------------------------------- 1 | use llvm::core::LLVMSetInitializer; 2 | 3 | extern crate llvm_sys as llvm; 4 | 5 | trait LLVMValue { 6 | fn value(&self) -> *mut llvm::LLVMValue; 7 | } 8 | 9 | trait Initializer: LLVMValue { 10 | fn set_initializer(&self, value: T) { 11 | unsafe { 12 | LLVMSetInitializer(self.value(), value.value()); 13 | } 14 | } 15 | } 16 | 17 | #[derive(Debug, Clone, Copy)] 18 | pub struct Value(pub *mut llvm::LLVMValue); 19 | 20 | impl From<*mut llvm::LLVMValue> for Value { 21 | fn from(ptr: *mut llvm::LLVMValue) -> Self { 22 | Value(ptr) 23 | } 24 | } 25 | 26 | impl LLVMValue for Value { 27 | fn value(&self) -> *mut llvm::LLVMValue { 28 | self.0 29 | } 30 | } 31 | 32 | impl Initializer for Value {} 33 | -------------------------------------------------------------------------------- /src/llvm/function.rs: -------------------------------------------------------------------------------- 1 | extern crate llvm_sys as llvm; 2 | 3 | use llvm_sys::{ 4 | analysis::{LLVMVerifierFailureAction, LLVMVerifyFunction}, 5 | core::LLVMGetParam, 6 | }; 7 | 8 | use super::{LLVMError, Value}; 9 | 10 | #[derive(Debug, Clone, Copy)] 11 | pub struct Function(pub *mut llvm::LLVMValue); 12 | 13 | impl Function { 14 | pub fn get_param(&self, idx: u32) -> Value { 15 | Value::from(unsafe { LLVMGetParam(self.0, idx) }) 16 | } 17 | 18 | pub fn verify_function(&self) -> Result<(), LLVMError> { 19 | let result = unsafe { 20 | LLVMVerifyFunction(self.0, LLVMVerifierFailureAction::LLVMPrintMessageAction) 21 | }; 22 | if result == 0 { 23 | Ok(()) 24 | } else { 25 | Err(LLVMError {}) 26 | } 27 | } 28 | 29 | pub fn from(ptr: *mut llvm_sys::LLVMValue) -> Function { 30 | Function(ptr) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/compiler/func_decl_vistor.rs: -------------------------------------------------------------------------------- 1 | use crate::{llvm, visitor::FuncDeclVisitor}; 2 | 3 | use super::{utils::get_llvm_type, Compiler, CompilerResult, Value}; 4 | 5 | impl FuncDeclVisitor> for Compiler { 6 | fn visit_func_decl(&mut self, expr: &crate::expression::FuncDecl) -> CompilerResult { 7 | let types: Vec = expr 8 | .params 9 | .iter() 10 | .map(|arg| get_llvm_type(&self.context, &arg.typ)) 11 | .collect(); 12 | 13 | let fun_type = self.context.function_type( 14 | get_llvm_type(&self.context, &expr.return_type), 15 | &types, 16 | false, 17 | ); 18 | 19 | let fun = Value::Function { 20 | return_type: expr.return_type, 21 | typ: fun_type, 22 | val: self.module.add_function("", fun_type), 23 | }; 24 | 25 | Ok(fun) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/llvm/engine.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use llvm::execution_engine::{ 4 | LLVMDisposeExecutionEngine, LLVMGetFunctionAddress, LLVMOpaqueExecutionEngine, 5 | }; 6 | 7 | use super::{utils::c_str, LLVMError, Module}; 8 | 9 | extern crate llvm_sys as llvm; 10 | 11 | pub struct Engine(*mut llvm::execution_engine::LLVMOpaqueExecutionEngine); 12 | 13 | impl Drop for Engine { 14 | fn drop(&mut self) { 15 | unsafe { LLVMDisposeExecutionEngine(self.0) } 16 | } 17 | } 18 | 19 | impl Engine { 20 | pub fn new(module: &Module) -> Result { 21 | module.create_engine() 22 | } 23 | 24 | pub fn from(ptr: *mut LLVMOpaqueExecutionEngine) -> Self { 25 | Engine(ptr) 26 | } 27 | 28 | pub fn call(&self, name: &str) { 29 | unsafe { 30 | let addr = LLVMGetFunctionAddress(self.0, c_str(name).as_ptr()); 31 | let f: extern "C" fn() = mem::transmute(addr); 32 | f(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/compiler/extern_visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::{llvm, visitor::ExternVisitor}; 2 | 3 | use super::{utils::get_llvm_type, Compiler, CompilerResult, Value}; 4 | 5 | impl ExternVisitor> for Compiler { 6 | fn visit_extern(&mut self, extern_stmt: &crate::expression::Extern) -> CompilerResult { 7 | let types: Vec = extern_stmt 8 | .types 9 | .iter() 10 | .map(|typ| get_llvm_type(&self.context, typ)) 11 | .collect(); 12 | 13 | let fun_type = self.context.function_type( 14 | get_llvm_type(&self.context, &extern_stmt.return_type), 15 | &types, 16 | false, 17 | ); 18 | let fun = self 19 | .module 20 | .add_function(extern_stmt.name.as_str(), fun_type); 21 | 22 | Ok(Value::Function { 23 | val: fun, 24 | typ: fun_type, 25 | return_type: extern_stmt.return_type, 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rocklang" 3 | version = "0.2.8" 4 | edition = "2021" 5 | authors = ["Jaroslaw Konik"] 6 | license = "MIT" 7 | description = "JIT-compiled programming language." 8 | documentation = "https://jarkonik.github.io/rocklang/" 9 | homepage = "https://jarkonik.github.io/rocklang/" 10 | repository = "https://github.com/jarkonik/rocklang" 11 | 12 | # See more keys and their definitions at 13 | # https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [workspace] 16 | members = [ 17 | "stdlib", 18 | "test_utils" 19 | ] 20 | 21 | [dependencies] 22 | serde = { version = "1.0.130", features = ["derive"] } 23 | serde_json = "1.0.59" 24 | assert-json-diff = "2.0.1" 25 | stdlib = { path = "./stdlib" } 26 | llvm-sys = "130" 27 | backtrace = "0.3" 28 | test_utils = { path = "./test_utils" } 29 | libc = "0.2" 30 | anyhow = "1.0" 31 | 32 | [dev-dependencies] 33 | assert_cmd = "0.10" 34 | predicates = "2.1.0" 35 | pretty_assertions = "1.2" 36 | indoc = "1.0" 37 | mockall = "0.11" 38 | 39 | # [profile.release] 40 | # debug = true 41 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Jaroslaw Konik 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/compiler/program.rs: -------------------------------------------------------------------------------- 1 | use crate::compiler::LLVMCompiler; 2 | use crate::{ 3 | parser::Program, 4 | visitor::{ProgramVisitor, Visitor}, 5 | }; 6 | 7 | use super::{Compiler, CompilerResult, Value, MAIN_FUNCTION}; 8 | 9 | impl ProgramVisitor> for Compiler { 10 | fn visit_program(&mut self, program: Program) -> CompilerResult { 11 | self.enter_scope(); 12 | self.init_builtins(); 13 | 14 | let main_fun = self.module.add_function( 15 | MAIN_FUNCTION, 16 | self.context 17 | .function_type(self.context.void_type(), &[], false), 18 | ); 19 | let block = self.context.append_basic_block(&main_fun, ""); 20 | self.builder.position_builder_at_end(&block); 21 | 22 | for stmt in program.body { 23 | self.release_maybe_orphaned(); 24 | self.walk(&stmt)?; 25 | } 26 | self.exit_scope()?; 27 | 28 | self.builder.build_ret_void(); 29 | 30 | self.verify_function(main_fun)?; 31 | 32 | if self.optimization { 33 | self.pass_manager.run(&main_fun); 34 | }; 35 | 36 | Ok(Value::Void) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/compiler/unary.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | expression, 3 | parser::{self, Span}, 4 | visitor::{UnaryVisitor, Visitor}, 5 | }; 6 | 7 | use super::{Compiler, CompilerError, CompilerResult, Value}; 8 | 9 | impl UnaryVisitor> for Compiler { 10 | fn visit_unary( 11 | &mut self, 12 | expr: &crate::expression::Unary, 13 | span: Span, 14 | ) -> CompilerResult { 15 | match &expr.operator { 16 | expression::Operator::Minus => { 17 | let r = match self.walk(&expr.right)? { 18 | Value::Numeric(p) => p, 19 | val => Err(CompilerError::TypeError { 20 | expected: parser::Type::Numeric, 21 | actual: val.get_type(), 22 | span, 23 | })?, 24 | }; 25 | 26 | Ok(Value::Numeric(self.builder.build_fneg(r, ""))) 27 | } 28 | operator => Err(CompilerError::WrongOperator { 29 | expected: expression::Operator::Minus, 30 | actual: operator.clone(), 31 | span, 32 | })?, 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /wix/License.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}{\f1\fnil\fcharset0 Courier New;}} 2 | {\*\generator Riched20 10.0.15063}\viewkind4\uc1 3 | \pard\sa180\fs24\lang9 Copyright (c) 2021 Jaroslaw Konik\par 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\par 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par 6 | \f1 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\f0\par 7 | } 8 | 9 | -------------------------------------------------------------------------------- /src/compiler/identifier.rs: -------------------------------------------------------------------------------- 1 | use crate::visitor::IdentifierVisitor; 2 | 3 | use super::{variable::Variable, Compiler, CompilerError, CompilerResult, LLVMCompiler, Value}; 4 | 5 | impl IdentifierVisitor> for Compiler { 6 | fn visit_identifier(&mut self, expr: &str) -> CompilerResult { 7 | let var = self.get_var(expr); 8 | 9 | let val = match var { 10 | Some(var) => { 11 | let val = self 12 | .builder 13 | .build_load(&var.llvm_type(&self.context), &var.into(), ""); 14 | Some(match var { 15 | Variable::String(_) => Value::String(val), 16 | Variable::Numeric(_) => Value::Numeric(val), 17 | Variable::Bool(_) => Value::Bool(val), 18 | Variable::Function { 19 | typ, 20 | return_type, 21 | val, 22 | } => Value::Function { 23 | val, 24 | typ, 25 | return_type, 26 | }, 27 | Variable::Vec(_) => Value::Vec(val), 28 | Variable::Ptr(_) => Value::Ptr(val), 29 | }) 30 | } 31 | None => self.get_param(expr), 32 | } 33 | .ok_or_else(|| CompilerError::UndefinedIdentifier(expr.to_string()))?; 34 | 35 | Ok(val) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/llvm/pass_manager.rs: -------------------------------------------------------------------------------- 1 | extern crate llvm_sys as llvm; 2 | 3 | use llvm::core::LLVMDisposePassManager; 4 | use llvm_sys::{ 5 | core::{ 6 | LLVMCreateFunctionPassManager, LLVMInitializeFunctionPassManager, 7 | LLVMRunFunctionPassManager, 8 | }, 9 | transforms::{scalar::*, util::LLVMAddPromoteMemoryToRegisterPass}, 10 | }; 11 | 12 | use super::{Function, Module}; 13 | 14 | pub struct PassManager(*mut llvm::LLVMPassManager); 15 | 16 | impl Drop for PassManager { 17 | fn drop(&mut self) { 18 | unsafe { LLVMDisposePassManager(self.0) }; 19 | } 20 | } 21 | 22 | impl PassManager { 23 | pub fn new(module: &Module) -> Self { 24 | let prov = module.create_module_provider(); 25 | 26 | let res = PassManager(unsafe { LLVMCreateFunctionPassManager(prov) }); 27 | unsafe { 28 | LLVMAddInstructionCombiningPass(res.0); 29 | LLVMAddReassociatePass(res.0); 30 | LLVMAddGVNPass(res.0); 31 | LLVMAddCFGSimplificationPass(res.0); 32 | LLVMAddBasicAliasAnalysisPass(res.0); 33 | LLVMAddPromoteMemoryToRegisterPass(res.0); 34 | LLVMAddInstructionCombiningPass(res.0); 35 | LLVMAddReassociatePass(res.0); 36 | LLVMInitializeFunctionPassManager(res.0); 37 | } 38 | res 39 | } 40 | 41 | pub fn run(&self, fun: &Function) { 42 | unsafe { 43 | LLVMRunFunctionPassManager(self.0, fun.0); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/mandelbrot.rck: -------------------------------------------------------------------------------- 1 | WIDTH = 30 2 | HEIGHT = 30 3 | 4 | complexprint = (c: vec): void => { 5 | print(string(vec_get(c, 0))) 6 | print("+") 7 | print(string(vec_get(c, 1))) 8 | print("i") 9 | print("\n") 10 | } 11 | 12 | complexadd = (a: vec, b: vec): vec => { 13 | r = vec_get(a, 0) + vec_get(b, 0) 14 | img = vec_get(a, 1) + vec_get(b, 1) 15 | 16 | res = vec_new() 17 | vec_set(res, 0, r) 18 | vec_set(res, 1, img) 19 | 20 | res 21 | } 22 | 23 | 24 | complexmul = (a: vec, b: vec): vec => { 25 | r = vec_get(a, 0) * vec_get(b, 0) - vec_get(a, 1) * vec_get(b, 1) 26 | img = vec_get(a, 0) * vec_get(b, 1) + vec_get(a, 1) * vec_get(b, 0) 27 | 28 | res = vec_new() 29 | vec_set(res, 0, r) 30 | vec_set(res, 1, img) 31 | 32 | res 33 | } 34 | 35 | y = 0 36 | while y < HEIGHT { 37 | x = 0 38 | while x < WIDTH { 39 | c = vec_new() 40 | halfwidth = WIDTH * 0.5 41 | halfheight = HEIGHT * 0.5 42 | 43 | rx = y - halfwidth 44 | ry = x - halfheight 45 | vec_set(c, 0, rx * 0.1) 46 | vec_set(c, 1, ry * 0.1) 47 | 48 | i = 0 49 | 50 | acc = vec_new() 51 | vec_set(acc, 0, 0) 52 | vec_set(acc, 1, 0) 53 | 54 | isstable = 1 55 | 56 | while i < 50 { 57 | acc = complexadd(complexmul(acc, acc), c) 58 | if sqrt(vec_get(acc, 0) * vec_get(acc, 0) + vec_get(acc, 1) * vec_get(acc, 1)) > 2 { 59 | isstable = 0 60 | } 61 | if sqrt(vec_get(acc, 0) * vec_get(acc, 0) + vec_get(acc, 1) * vec_get(acc, 1)) < -2 { 62 | isstable = 0 63 | } 64 | i = i + 1 65 | } 66 | 67 | if isstable == 1 { 68 | print("*") 69 | } else { 70 | print(".") 71 | } 72 | 73 | x = x + 1 74 | } 75 | y = y + 1 76 | print("\n") 77 | } 78 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use rocklang::compiler::{Compile, Compiler}; 3 | 4 | use rocklang::parser::{Parse, Parser}; 5 | use rocklang::tokenizer::{Tokenize, Tokenizer}; 6 | use std::error::Error; 7 | use std::fmt; 8 | use std::{env, fs}; 9 | 10 | #[derive(Clone, Debug)] 11 | pub struct InputError {} 12 | 13 | impl fmt::Display for InputError { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 15 | write!(f, "Input error") 16 | } 17 | } 18 | 19 | impl Error for InputError {} 20 | 21 | #[cfg(not(tarpaulin_include))] 22 | fn main() -> Result<()> { 23 | let args: Vec = env::args().collect(); 24 | if args.len() < 2 { 25 | Err(InputError {})?; 26 | } 27 | let filename = &args[1]; 28 | 29 | let mut dump_ir = false; 30 | let mut no_opt = false; 31 | let mut dump_ast = false; 32 | 33 | for arg in std::env::args() { 34 | match arg.as_str() { 35 | "--ir" => dump_ir = true, 36 | "--no-opt" => no_opt = true, 37 | "--ast" => dump_ast = true, 38 | _ => (), 39 | } 40 | } 41 | 42 | let source = fs::read_to_string(filename).expect("Error reading input file"); 43 | 44 | let mut tokenizer = Tokenizer::new(source); 45 | let tokens = tokenizer.tokenize()?; 46 | 47 | let mut parser = Parser::new(tokens); 48 | let ast = parser.parse()?; 49 | 50 | if dump_ast { 51 | let json = serde_json::to_string_pretty(&ast).expect("Serialization error"); 52 | println!("{}", json); 53 | return Ok(()); 54 | } 55 | 56 | let mut compiler = Compiler::new(ast).expect("Compiler initialization error"); 57 | if no_opt { 58 | compiler.turn_off_optimization(); 59 | } 60 | 61 | compiler.compile()?; 62 | 63 | if dump_ir { 64 | compiler.dump_ir(); 65 | } else { 66 | compiler.run(); 67 | } 68 | 69 | Ok(()) 70 | } 71 | -------------------------------------------------------------------------------- /src/compiler/while_visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::Span; 2 | use crate::visitor::WhileVisitor; 3 | use crate::{llvm, parser}; 4 | 5 | use super::{Compiler, CompilerError, CompilerResult, Value}; 6 | use crate::compiler::LLVMCompiler; 7 | use crate::visitor::Visitor; 8 | 9 | impl WhileVisitor> for Compiler { 10 | fn visit_while( 11 | &mut self, 12 | expr: &crate::expression::While, 13 | span: Span, 14 | ) -> CompilerResult { 15 | let predicate = match self.walk(&expr.predicate)? { 16 | Value::Bool(b) => b, 17 | val => Err(CompilerError::TypeError { 18 | expected: parser::Type::Bool, 19 | actual: val.get_type(), 20 | span, 21 | })?, 22 | }; 23 | 24 | let fun = self.builder().get_insert_block().get_parent(); 25 | 26 | let loop_block = self.context().append_basic_block(&fun, "loop"); 27 | let after_loop_block = self.context().append_basic_block(&fun, "afterloop"); 28 | self.after_loop_blocks.push(after_loop_block); 29 | 30 | self.builder 31 | .build_cond_br(&predicate, &loop_block, &after_loop_block); 32 | 33 | self.builder.position_builder_at_end(&loop_block); 34 | 35 | self.enter_scope(); 36 | 37 | let mut is_break = false; 38 | for stmt in &expr.body { 39 | self.release_maybe_orphaned(); 40 | if let Value::Break = self.walk(stmt)? { 41 | is_break = true; 42 | break; 43 | }; 44 | } 45 | self.exit_scope().unwrap(); 46 | 47 | let pred: llvm::Value = self.walk(&expr.predicate)?.into(); 48 | if is_break { 49 | self.builder.build_br(&after_loop_block); 50 | } else { 51 | self.builder 52 | .build_cond_br(&pred, &loop_block, &after_loop_block); 53 | } 54 | self.builder.position_builder_at_end(&after_loop_block); 55 | self.after_loop_blocks.pop(); 56 | 57 | Ok(Value::Void) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::prelude::*; 2 | use predicates::prelude::*; 3 | use std::process::Command; 4 | 5 | #[test] 6 | #[cfg_attr(tarpaulin, ignore)] 7 | fn prime_sieve() -> Result<(), Box> { 8 | let mut cmd = Command::cargo_bin("rocklang")?; 9 | 10 | cmd.arg("examples/sieve.rck"); 11 | cmd.assert() 12 | .success() 13 | .stdout(predicate::eq("2\n3\n5\n7\n").normalize()); 14 | 15 | Ok(()) 16 | } 17 | 18 | #[test] 19 | #[cfg_attr(tarpaulin, ignore)] 20 | fn mandelbrot() -> Result<(), Box> { 21 | let mut cmd = Command::cargo_bin("rocklang")?; 22 | 23 | cmd.arg("examples/mandelbrot.rck"); 24 | cmd.assert().success().stdout( 25 | predicate::eq( 26 | "...............*.............. 27 | ...............*.............. 28 | ...............*.............. 29 | ..............***............. 30 | .............*****............ 31 | .............*****............ 32 | .............*****............ 33 | ..............***............. 34 | .............*****............ 35 | ...........*********.......... 36 | .........*************........ 37 | ..........***********......... 38 | .........*************........ 39 | .......*****************...... 40 | .......*****************...... 41 | .....*...*************...*.... 42 | .........*************........ 43 | ..........***********......... 44 | ..........*****.*****......... 45 | .............................. 46 | .............................. 47 | .............................. 48 | .............................. 49 | .............................. 50 | .............................. 51 | .............................. 52 | .............................. 53 | .............................. 54 | .............................. 55 | .............................. 56 | ", 57 | ) 58 | .normalize(), 59 | ); 60 | 61 | Ok(()) 62 | } 63 | 64 | #[test] 65 | #[cfg_attr(tarpaulin, ignore)] 66 | fn ffi() -> Result<(), Box> { 67 | let mut cmd = Command::cargo_bin("rocklang")?; 68 | 69 | cmd.arg("examples/ffi.rck"); 70 | cmd.assert() 71 | .success() 72 | .stdout(predicate::eq("2 + 3 is 5\n").normalize()); 73 | 74 | Ok(()) 75 | } 76 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: ./.github/actions/install_llvm 12 | - uses: actions-rs/toolchain@v1 13 | with: 14 | profile: minimal 15 | toolchain: stable 16 | override: true 17 | - uses: actions-rs/cargo@v1 18 | with: 19 | command: check 20 | 21 | valgrind_examples: 22 | name: Test examples with valgrind 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: ./.github/actions/install_llvm 27 | - uses: ./.github/actions/install_valgrind 28 | - name: Run examples 29 | shell: bash 30 | run: for file in examples/*; do cargo valgrind run $file || exit 1; done 31 | 32 | test: 33 | name: Test Suite 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v2 37 | - uses: ./.github/actions/install_llvm 38 | - uses: actions-rs/toolchain@v1 39 | with: 40 | profile: minimal 41 | toolchain: stable 42 | override: true 43 | - uses: actions-rs/cargo@v1 44 | with: 45 | command: test 46 | 47 | fmt: 48 | name: Rustfmt 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v2 52 | - uses: actions-rs/toolchain@v1 53 | with: 54 | profile: minimal 55 | toolchain: stable 56 | override: true 57 | - run: rustup component add rustfmt 58 | - uses: actions-rs/cargo@v1 59 | with: 60 | command: fmt 61 | args: --all -- --check 62 | 63 | clippy: 64 | name: Clippy 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v2 68 | - uses: ./.github/actions/install_llvm 69 | - uses: actions-rs/toolchain@v1 70 | with: 71 | profile: minimal 72 | toolchain: stable 73 | override: true 74 | - run: rustup component add clippy 75 | - uses: actions-rs/cargo@v1 76 | with: 77 | command: clippy 78 | args: -- -D warnings 79 | -------------------------------------------------------------------------------- /src/expression.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::Param; 2 | use crate::parser::Span; 3 | use crate::parser::Type; 4 | use serde::Serialize; 5 | 6 | #[derive(Serialize, Debug, Clone)] 7 | pub enum Operator { 8 | Plus, 9 | LessOrEqual, 10 | Less, 11 | Minus, 12 | Asterisk, 13 | Slash, 14 | Equal, 15 | Mod, 16 | NotEqual, 17 | Greater, 18 | GreaterOrEqual, 19 | } 20 | 21 | #[derive(Serialize, Debug, Clone)] 22 | pub struct Binary { 23 | pub left: Box, 24 | pub operator: Operator, 25 | pub right: Box, 26 | } 27 | 28 | #[derive(Serialize, Debug, Clone)] 29 | pub struct Conditional { 30 | pub predicate: Box, 31 | pub body: Vec, 32 | pub else_body: Vec, 33 | } 34 | 35 | #[derive(Serialize, Debug, Clone)] 36 | pub struct Assignment { 37 | pub left: Box, 38 | pub right: Box, 39 | } 40 | 41 | #[derive(Serialize, Debug, Clone)] 42 | pub struct While { 43 | pub predicate: Box, 44 | pub body: Vec, 45 | } 46 | 47 | #[derive(Serialize, Debug, Clone)] 48 | pub struct Unary { 49 | pub operator: Operator, 50 | pub right: Box, 51 | } 52 | 53 | #[derive(Serialize, Debug, Clone)] 54 | pub struct FuncCall { 55 | pub calee: Box, 56 | pub args: Vec, 57 | } 58 | 59 | #[derive(Serialize, Debug, Clone)] 60 | pub struct FuncDecl { 61 | pub params: Vec, 62 | pub body: Vec, 63 | pub return_type: Type, 64 | } 65 | 66 | #[derive(Serialize, Debug, Clone)] 67 | pub struct Extern { 68 | pub types: Vec, 69 | pub return_type: Type, 70 | pub name: String, 71 | } 72 | 73 | #[derive(Serialize, Debug, Clone)] 74 | pub struct Grouping(pub Box); 75 | 76 | #[derive(Serialize, Debug, Clone)] 77 | pub enum Expression { 78 | Break, 79 | Bool(bool), 80 | String(String), 81 | Identifier(String), 82 | Numeric(f64), 83 | Conditional(Conditional), 84 | Assignment(Assignment), 85 | Binary(Binary), 86 | While(While), 87 | Unary(Unary), 88 | Grouping(Grouping), 89 | FuncCall(FuncCall), 90 | FuncDecl(FuncDecl), 91 | Load(String), 92 | Extern(Extern), 93 | } 94 | 95 | #[derive(Debug, Serialize, Clone)] 96 | pub struct Node { 97 | pub expression: Expression, 98 | pub span: Span, 99 | } 100 | -------------------------------------------------------------------------------- /src/visitor.rs: -------------------------------------------------------------------------------- 1 | use crate::expression::{self, Node}; 2 | use crate::parser::{Program, Span}; 3 | 4 | pub trait BinaryVisitor { 5 | fn visit_binary(&mut self, expr: &expression::Binary, span: Span) -> T; 6 | } 7 | 8 | pub trait FuncCallVisitor { 9 | fn visit_func_call(&mut self, expr: &expression::FuncCall, span: Span) -> T; 10 | } 11 | 12 | pub trait NumericVisitor { 13 | fn visit_numeric(&mut self, expr: &f64) -> T; 14 | } 15 | 16 | pub trait StringVisitor { 17 | fn visit_string(&mut self, expr: &str) -> T; 18 | } 19 | 20 | pub trait ProgramVisitor { 21 | fn visit_program(&mut self, program: Program) -> T; 22 | } 23 | 24 | pub trait AssignmentVisitor { 25 | fn visit_assignment(&mut self, expr: &expression::Assignment, span: Span) -> T; 26 | } 27 | 28 | pub trait ConditionalVisitor { 29 | fn visit_conditional(&mut self, expr: &expression::Conditional, span: Span) -> T; 30 | } 31 | 32 | pub trait UnaryVisitor { 33 | fn visit_unary(&mut self, expr: &expression::Unary, span: Span) -> T; 34 | } 35 | 36 | pub trait GroupingVisitor { 37 | fn visit_grouping(&mut self, expr: &expression::Grouping) -> T; 38 | } 39 | 40 | pub trait WhileVisitor { 41 | fn visit_while(&mut self, expr: &expression::While, span: Span) -> T; 42 | } 43 | 44 | pub trait IdentifierVisitor { 45 | fn visit_identifier(&mut self, expr: &str) -> T; 46 | } 47 | 48 | pub trait BoolVisitor { 49 | fn visit_bool(&mut self, expr: &bool) -> T; 50 | } 51 | 52 | pub trait BreakVisitor { 53 | fn visit_break(&mut self) -> T; 54 | } 55 | 56 | pub trait FuncDeclVisitor { 57 | fn visit_func_decl(&mut self, body: &expression::FuncDecl) -> T; 58 | } 59 | 60 | pub trait LoadVisitor { 61 | fn visit_load(&mut self, name: &str) -> T; 62 | } 63 | 64 | pub trait ExternVisitor { 65 | fn visit_extern(&mut self, name: &expression::Extern) -> T; 66 | } 67 | 68 | pub trait Visitor: 69 | BinaryVisitor 70 | + FuncCallVisitor 71 | + NumericVisitor 72 | + StringVisitor 73 | + ProgramVisitor 74 | + AssignmentVisitor 75 | + ConditionalVisitor 76 | + UnaryVisitor 77 | + GroupingVisitor 78 | + WhileVisitor 79 | + IdentifierVisitor 80 | + BoolVisitor 81 | + BreakVisitor 82 | + FuncDeclVisitor 83 | + LoadVisitor 84 | + ExternVisitor 85 | { 86 | fn walk(&mut self, node: &Node) -> T; 87 | } 88 | -------------------------------------------------------------------------------- /tests/token_test.rs: -------------------------------------------------------------------------------- 1 | use rocklang::token::TokenKind; 2 | 3 | macro_rules! assert_format_string { 4 | ($token:ident, $results:expr) => { 5 | let token = TokenKind::$token; 6 | 7 | let token_name = format!("{}", token); 8 | assert_eq!(token_name, $results); 9 | }; 10 | } 11 | 12 | macro_rules! assert_angle_brackets_format_string { 13 | ($token:ident, $first_param:expr, $results:expr ) => { 14 | let token = TokenKind::$token($first_param); 15 | 16 | let token_name = format!("{}", token); 17 | assert_eq!(token_name, $results); 18 | }; 19 | } 20 | 21 | #[test] 22 | fn it_assert_token_types_formatng() { 23 | assert_angle_brackets_format_string!(Identifier, "ident".to_string(), ""); 24 | assert_angle_brackets_format_string!(Numeric, 10.0, ""); 25 | assert_angle_brackets_format_string!(String, "string".to_string(), ""); 26 | assert_format_string!(LeftParen, ""); 27 | assert_format_string!(NotEqual, ""); 28 | assert_format_string!(LeftParen, ""); 29 | assert_format_string!(RightParen, ""); 30 | assert_format_string!(Slash, ""); 31 | assert_format_string!(Plus, ""); 32 | assert_format_string!(Minus, ""); 33 | assert_format_string!(Asterisk, ""); 34 | assert_format_string!(Equal, ""); 35 | assert_format_string!(LCurly, ""); 36 | assert_format_string!(RCurly, ""); 37 | assert_format_string!(If, ""); 38 | assert_format_string!(While, ""); 39 | assert_format_string!(True, ""); 40 | assert_format_string!(False, ""); 41 | assert_format_string!(DoubleEqual, ""); 42 | assert_format_string!(Percent, ""); 43 | assert_format_string!(Exclamation, ""); 44 | assert_format_string!(Break, ""); 45 | assert_format_string!(Eof, ""); 46 | assert_format_string!(Comma, ""); 47 | assert_format_string!(Arrow, ""); 48 | assert_format_string!(Less, ""); 49 | assert_format_string!(LessOrEqual, ""); 50 | assert_format_string!(Greater, ""); 51 | assert_format_string!(GreaterOrEqual, ""); 52 | assert_format_string!(Or, ""); 53 | assert_format_string!(And, ""); 54 | assert_format_string!(Else, ""); 55 | assert_format_string!(Colon, ""); 56 | } 57 | -------------------------------------------------------------------------------- /src/compiler/value.rs: -------------------------------------------------------------------------------- 1 | use crate::llvm::Context; 2 | use crate::llvm::{self}; 3 | use crate::parser; 4 | 5 | #[derive(Debug, Clone, Copy)] 6 | pub enum Value { 7 | Void, 8 | String(llvm::Value), 9 | CString(llvm::Value), 10 | Numeric(llvm::Value), 11 | Bool(llvm::Value), 12 | Function { 13 | val: llvm::Function, 14 | typ: llvm::Type, 15 | return_type: parser::Type, 16 | }, 17 | Vec(llvm::Value), 18 | Break, 19 | Ptr(llvm::Value), 20 | } 21 | 22 | impl From for llvm::Value { 23 | fn from(v: Value) -> Self { 24 | match v { 25 | Value::Void | Value::Break => unreachable!(), 26 | Value::String(lv) => lv, 27 | Value::Numeric(lv) => lv, 28 | Value::Bool(lv) => lv, 29 | Value::Function { val, .. } => llvm::Value(val.0), 30 | Value::Vec(lv) => lv, 31 | Value::Ptr(lv) => lv, 32 | Value::CString(_) => todo!(), 33 | } 34 | } 35 | } 36 | 37 | impl From<&Value> for llvm::Value { 38 | fn from(v: &Value) -> Self { 39 | match *v { 40 | Value::Void | Value::Break => unreachable!(), 41 | Value::String(lv) => lv, 42 | Value::Numeric(lv) => lv, 43 | Value::Bool(lv) => lv, 44 | Value::Function { val, .. } => llvm::Value(val.0), 45 | Value::Vec(lv) => lv, 46 | Value::Ptr(lv) => lv, 47 | Value::CString(_) => todo!(), 48 | } 49 | } 50 | } 51 | 52 | impl Value { 53 | pub fn llvm_type(&self, context: &Context) -> llvm::Type { 54 | match self { 55 | Value::Numeric(_) => context.double_type(), 56 | Value::Bool(_) => context.i1_type(), 57 | Value::Ptr(_) => context.void_type().pointer_type(0), 58 | Value::String(_) => context.void_type().pointer_type(0), 59 | Value::Vec(_) => context.void_type().pointer_type(0), 60 | Value::Function { typ, .. } => typ.pointer_type(0), 61 | Value::Void | Value::Break => unreachable!(), 62 | Value::CString(_) => todo!(), 63 | } 64 | } 65 | 66 | pub fn get_type(&self) -> parser::Type { 67 | match self { 68 | Value::Void | Value::Break => parser::Type::Void, 69 | Value::Numeric(_) => parser::Type::Numeric, 70 | Value::Bool(_) => parser::Type::Bool, 71 | Value::Ptr(_) => parser::Type::Ptr, 72 | Value::String(_) => parser::Type::String, 73 | Value::Vec(_) => parser::Type::Vector, 74 | Value::Function { .. } => parser::Type::Function, 75 | Value::CString(_) => parser::Type::CString, 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/compiler/conditional.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | expression, 3 | parser::{self, Span}, 4 | visitor::ConditionalVisitor, 5 | }; 6 | 7 | use super::{Compiler, CompilerError, CompilerResult, LLVMCompiler, Value}; 8 | 9 | fn compile_conditional( 10 | compiler: &mut T, 11 | expr: &expression::Conditional, 12 | span: Span, 13 | ) -> CompilerResult { 14 | let predicate = match compiler.walk(&expr.predicate)? { 15 | Value::Bool(b) => b, 16 | expr => Err(CompilerError::TypeError { 17 | expected: parser::Type::Bool, 18 | actual: expr.get_type(), 19 | span, 20 | })?, 21 | }; 22 | 23 | let fun = compiler.builder().get_insert_block().get_parent(); 24 | 25 | let then_block = compiler.context().append_basic_block(&fun, "then"); 26 | let else_block = compiler.context().append_basic_block(&fun, "else"); 27 | let after_if_block = compiler.context().append_basic_block(&fun, "afterif"); 28 | 29 | compiler 30 | .builder() 31 | .build_cond_br(&predicate, &then_block, &else_block); 32 | 33 | compiler.builder().position_builder_at_end(&then_block); 34 | compiler.enter_scope(); 35 | 36 | let mut is_break = false; 37 | for stmt in &expr.body { 38 | compiler.release_maybe_orphaned(); 39 | if let Value::Break = compiler.walk(stmt)? { 40 | is_break = true; 41 | break; 42 | } 43 | } 44 | compiler.exit_scope().unwrap(); 45 | 46 | if is_break { 47 | compiler 48 | .builder() 49 | .create_br(compiler.after_loop_blocks().last().unwrap()); 50 | } else { 51 | compiler.builder().create_br(&after_if_block); 52 | } 53 | 54 | compiler.builder().position_builder_at_end(&else_block); 55 | compiler.enter_scope(); 56 | 57 | let mut is_break = false; 58 | for stmt in &expr.else_body { 59 | compiler.release_maybe_orphaned(); 60 | if let Value::Break = compiler.walk(stmt)? { 61 | is_break = true; 62 | break; 63 | } 64 | } 65 | compiler.exit_scope().unwrap(); 66 | if is_break { 67 | compiler 68 | .builder() 69 | .create_br(compiler.after_loop_blocks().last().unwrap()); 70 | } else { 71 | compiler.builder().create_br(&after_if_block); 72 | } 73 | compiler.builder().position_builder_at_end(&after_if_block); 74 | 75 | Ok(Value::Void) 76 | } 77 | 78 | impl ConditionalVisitor> for Compiler { 79 | fn visit_conditional( 80 | &mut self, 81 | expr: &crate::expression::Conditional, 82 | span: Span, 83 | ) -> CompilerResult { 84 | compile_conditional(self, expr, span) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/llvm/module.rs: -------------------------------------------------------------------------------- 1 | extern crate llvm_sys as llvm; 2 | 3 | use std::{ffi::CStr, fmt::Display, mem}; 4 | 5 | use libc::c_void; 6 | use llvm::{ 7 | core::{LLVMCreateModuleProviderForExistingModule, LLVMDumpModule, LLVMPrintModuleToString}, 8 | execution_engine::{LLVMCreateExecutionEngineForModule, LLVMLinkInMCJIT}, 9 | target::{LLVM_InitializeNativeAsmPrinter, LLVM_InitializeNativeTarget}, 10 | }; 11 | use llvm_sys::core::{LLVMAddFunction, LLVMAddGlobal, LLVMGetNamedFunction}; 12 | 13 | use super::{utils::c_str, Context, Engine, Function, LLVMError, Type, Value}; 14 | 15 | pub struct Module(*mut llvm::LLVMModule); 16 | 17 | #[allow(dead_code)] 18 | impl Module { 19 | pub fn new(name: &str, context: &Context) -> Self { 20 | context.create_module(name) 21 | } 22 | 23 | pub fn add_global(&self, typ: Type, name: &str) -> Value { 24 | Value::from(unsafe { LLVMAddGlobal(self.0, typ.0, c_str(name).as_ptr()) }) 25 | } 26 | 27 | pub fn add_function(&self, name: &str, function_type: Type) -> Function { 28 | Function::from(unsafe { LLVMAddFunction(self.0, c_str(name).as_ptr(), function_type.0) }) 29 | } 30 | 31 | pub fn get_function(&self, name: &str) -> Option { 32 | let fun = unsafe { LLVMGetNamedFunction(self.0, c_str(name).as_ptr()) }; 33 | if fun.is_null() { 34 | None 35 | } else { 36 | Some(Function::from(fun)) 37 | } 38 | } 39 | 40 | #[allow(dead_code)] 41 | pub fn dump(&self) { 42 | unsafe { LLVMDumpModule(self.0) } 43 | } 44 | 45 | pub fn from_context(ptr: *mut llvm::LLVMModule) -> Module { 46 | Module(ptr) 47 | } 48 | 49 | pub fn create_engine(&self) -> Result { 50 | let engine = Engine::from(unsafe { 51 | let mut ee = mem::zeroed(); 52 | let mut out = mem::zeroed(); 53 | 54 | LLVMLinkInMCJIT(); 55 | LLVM_InitializeNativeTarget(); 56 | LLVM_InitializeNativeAsmPrinter(); 57 | 58 | if LLVMCreateExecutionEngineForModule(&mut ee, self.0, &mut out) != 0 { 59 | Err(LLVMError {})?; 60 | }; 61 | ee 62 | }); 63 | Ok(engine) 64 | } 65 | 66 | pub fn create_module_provider(&self) -> *mut llvm::LLVMModuleProvider { 67 | unsafe { LLVMCreateModuleProviderForExistingModule(self.0) } 68 | } 69 | } 70 | 71 | impl Display for Module { 72 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { 73 | let raw_ptr = unsafe { LLVMPrintModuleToString(self.0) }; 74 | let str = unsafe { CStr::from_ptr(raw_ptr).to_str() }; 75 | fmt.write_str(str.unwrap())?; 76 | unsafe { libc::free(raw_ptr as *mut c_void) }; 77 | Ok(()) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/tokenizer_tests.rs: -------------------------------------------------------------------------------- 1 | use rocklang::token::TokenKind; 2 | use rocklang::tokenizer::TokenizerError; 3 | use rocklang::tokenizer::{Tokenize, Tokenizer}; 4 | 5 | macro_rules! assert_token_kind_of { 6 | ($expected: pat, $actual: expr) => { 7 | assert!(matches!($actual.kind, $expected)); 8 | }; 9 | } 10 | 11 | #[test] 12 | fn it_tokenizes_all_tokens() { 13 | let mut tokenizer = Tokenizer::new(String::from( 14 | "< > () + - * % \"test\" ! != || && = == => { } , 15 | // comment 16 | / identifier identifier123 23123.321 123 <= >= :", 17 | )); 18 | let tokens = tokenizer.tokenize().unwrap(); 19 | assert_token_kind_of!(TokenKind::Less, tokens[0]); 20 | assert_token_kind_of!(TokenKind::Greater, tokens[1]); 21 | assert_token_kind_of!(TokenKind::LeftParen, tokens[2]); 22 | assert_token_kind_of!(TokenKind::RightParen, tokens[3]); 23 | assert_token_kind_of!(TokenKind::Plus, tokens[4]); 24 | assert_token_kind_of!(TokenKind::Minus, tokens[5]); 25 | assert_token_kind_of!(TokenKind::Asterisk, tokens[6]); 26 | assert_token_kind_of!(TokenKind::Percent, tokens[7]); 27 | assert_token_kind_of!(TokenKind::String(_), tokens[8]); 28 | assert_token_kind_of!(TokenKind::Exclamation, tokens[9]); 29 | assert_token_kind_of!(TokenKind::NotEqual, tokens[10]); 30 | assert_token_kind_of!(TokenKind::Or, tokens[11]); 31 | assert_token_kind_of!(TokenKind::And, tokens[12]); 32 | assert_token_kind_of!(TokenKind::Equal, tokens[13]); 33 | assert_token_kind_of!(TokenKind::DoubleEqual, tokens[14]); 34 | assert_token_kind_of!(TokenKind::Arrow, tokens[15]); 35 | assert_token_kind_of!(TokenKind::LCurly, tokens[16]); 36 | assert_token_kind_of!(TokenKind::RCurly, tokens[17]); 37 | assert_token_kind_of!(TokenKind::Comma, tokens[18]); 38 | assert_token_kind_of!(TokenKind::Slash, tokens[19]); 39 | assert_token_kind_of!(TokenKind::Identifier(_), tokens[20]); 40 | assert_token_kind_of!(TokenKind::Identifier(_), tokens[21]); 41 | assert_token_kind_of!(TokenKind::Numeric(_), tokens[22]); 42 | assert_token_kind_of!(TokenKind::Numeric(_), tokens[23]); 43 | assert_token_kind_of!(TokenKind::LessOrEqual, tokens[24]); 44 | assert_token_kind_of!(TokenKind::GreaterOrEqual, tokens[25]); 45 | assert_token_kind_of!(TokenKind::Colon, tokens[26]); 46 | assert_token_kind_of!(TokenKind::Eof, tokens[27]); 47 | assert_eq!(28, tokens.len()); 48 | } 49 | 50 | #[test] 51 | fn it_returns_error_for_unexpected_character() { 52 | let mut tokenizer = Tokenizer::new(String::from("~")); 53 | assert!(matches!( 54 | tokenizer.tokenize(), 55 | Err(TokenizerError { chr: '~', line: 1 }), 56 | )); 57 | } 58 | 59 | #[test] 60 | fn tokenizer_error_display() { 61 | let error = TokenizerError { chr: 'a', line: 55 }; 62 | assert_eq!("unexpected character 'a'", format!("{}", error)); 63 | } 64 | -------------------------------------------------------------------------------- /src/compiler/variable.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | llvm::{self, Context}, 3 | parser, 4 | }; 5 | 6 | #[derive(Debug, Clone, Copy)] 7 | pub enum Variable { 8 | String(llvm::Value), 9 | Numeric(llvm::Value), 10 | Bool(llvm::Value), 11 | Function { 12 | val: llvm::Function, 13 | typ: llvm::Type, 14 | return_type: parser::Type, 15 | }, 16 | Vec(llvm::Value), 17 | Ptr(llvm::Value), 18 | } 19 | 20 | impl From for llvm::Value { 21 | fn from(v: Variable) -> Self { 22 | match v { 23 | Variable::String(lv) => lv, 24 | Variable::Numeric(lv) => lv, 25 | Variable::Bool(lv) => lv, 26 | Variable::Function { val, .. } => llvm::Value(val.0), 27 | Variable::Vec(lv) => lv, 28 | Variable::Ptr(lv) => lv, 29 | } 30 | } 31 | } 32 | 33 | impl From<&Variable> for llvm::Value { 34 | fn from(v: &Variable) -> Self { 35 | match *v { 36 | Variable::String(lv) => lv, 37 | Variable::Numeric(lv) => lv, 38 | Variable::Bool(lv) => lv, 39 | Variable::Function { val, .. } => llvm::Value(val.0), 40 | Variable::Vec(lv) => lv, 41 | Variable::Ptr(lv) => lv, 42 | } 43 | } 44 | } 45 | impl Variable { 46 | pub fn llvm_type(&self, context: &Context) -> llvm::Type { 47 | match self { 48 | Variable::Numeric(_) => context.double_type(), 49 | Variable::Bool(_) => context.i1_type(), 50 | Variable::Ptr(_) => context.void_type().pointer_type(0), 51 | Variable::String(_) => context.void_type().pointer_type(0), 52 | Variable::Vec(_) => context.void_type().pointer_type(0), 53 | Variable::Function { typ, .. } => typ.pointer_type(0), 54 | } 55 | } 56 | 57 | pub fn get_type(&self) -> parser::Type { 58 | match self { 59 | Variable::Numeric(_) => parser::Type::Numeric, 60 | Variable::Bool(_) => parser::Type::Bool, 61 | Variable::Ptr(_) => parser::Type::Ptr, 62 | Variable::String(_) => parser::Type::String, 63 | Variable::Vec(_) => parser::Type::Vector, 64 | Variable::Function { .. } => parser::Type::Function, 65 | } 66 | } 67 | 68 | pub fn set_value(&mut self, ptr: llvm::Value) { 69 | match self { 70 | Variable::String(v) => { 71 | v.0 = ptr.0; 72 | } 73 | Variable::Numeric(v) => { 74 | v.0 = ptr.0; 75 | } 76 | Variable::Bool(v) => { 77 | v.0 = ptr.0; 78 | } 79 | Variable::Function { val, .. } => { 80 | val.0 = ptr.0; 81 | } 82 | Variable::Vec(v) => { 83 | v.0 = ptr.0; 84 | } 85 | Variable::Ptr(v) => { 86 | v.0 = ptr.0; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::fmt::Display; 3 | 4 | use crate::parser::Span; 5 | 6 | #[derive(Clone, Debug)] 7 | pub enum TokenKind { 8 | DoubleEqual, 9 | Else, 10 | Less, 11 | LessOrEqual, 12 | Comma, 13 | Break, 14 | True, 15 | False, 16 | NotEqual, 17 | Percent, 18 | LeftParen, 19 | LCurly, 20 | RCurly, 21 | While, 22 | If, 23 | RightParen, 24 | Slash, 25 | Plus, 26 | Minus, 27 | Asterisk, 28 | Equal, 29 | Arrow, 30 | Exclamation, 31 | Or, 32 | And, 33 | Greater, 34 | GreaterOrEqual, 35 | Colon, 36 | String(String), 37 | Identifier(String), 38 | Numeric(f64), 39 | Load, 40 | Extern, 41 | Eof, 42 | } 43 | 44 | #[derive(Clone, Debug)] 45 | pub struct Token { 46 | pub kind: TokenKind, 47 | pub span: Span, 48 | } 49 | 50 | fn token_name(token: &TokenKind) -> &str { 51 | match token { 52 | TokenKind::NotEqual => "NotEqual", 53 | TokenKind::LeftParen => "LeftParen", 54 | TokenKind::RightParen => "RightParen", 55 | TokenKind::Slash => "Slash", 56 | TokenKind::Identifier { .. } => "Identifier", 57 | TokenKind::Numeric { .. } => "Numeric", 58 | TokenKind::Plus => "Plus", 59 | TokenKind::Minus => "Minus", 60 | TokenKind::Asterisk => "Asterisk", 61 | TokenKind::Equal => "Equal", 62 | TokenKind::LCurly => "LCurly", 63 | TokenKind::RCurly => "RCurly", 64 | TokenKind::If => "If", 65 | TokenKind::While => "While", 66 | TokenKind::True => "True", 67 | TokenKind::False => "False", 68 | TokenKind::DoubleEqual => "DoubleEqual", 69 | TokenKind::Percent => "Percent", 70 | TokenKind::Exclamation => "Exclamation", 71 | TokenKind::Break => "Break", 72 | TokenKind::String { .. } => "String", 73 | TokenKind::Eof => "Eof", 74 | TokenKind::Comma => "Comma", 75 | TokenKind::Arrow => "Arrow", 76 | TokenKind::Less => "Less", 77 | TokenKind::LessOrEqual => "LessOrEqual", 78 | TokenKind::Greater => "Greater", 79 | TokenKind::GreaterOrEqual => "GreaterOrEqual", 80 | TokenKind::Or => "Or", 81 | TokenKind::And => "And", 82 | TokenKind::Else => "Else", 83 | TokenKind::Colon => "Colon", 84 | TokenKind::Load => "Load", 85 | TokenKind::Extern => "Extern", 86 | } 87 | } 88 | 89 | impl Display for TokenKind { 90 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 91 | match &self { 92 | TokenKind::Numeric(value) => { 93 | write!(f, "<{}({})>", token_name(self), value) 94 | } 95 | TokenKind::Identifier(literal) | TokenKind::String(literal) => { 96 | write!(f, "<{}({})>", token_name(self), literal) 97 | } 98 | _ => write!(f, "<{}>", token_name(self)), 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/compiler/scope.rs: -------------------------------------------------------------------------------- 1 | use crate::llvm::{Builder, Context, Module}; 2 | use std::collections::HashMap; 3 | 4 | use super::{variable::Variable, CompilerResult, Value}; 5 | 6 | pub struct Scope { 7 | env: HashMap, 8 | params: HashMap, 9 | } 10 | 11 | impl Scope { 12 | pub fn new() -> Self { 13 | Scope { 14 | params: HashMap::new(), 15 | env: HashMap::new(), 16 | } 17 | } 18 | 19 | pub fn get(&self, literal: &str) -> Option<&Variable> { 20 | self.env.get(literal) 21 | } 22 | 23 | pub fn set(&mut self, literal: &str, val: Variable) { 24 | if self.env.contains_key(literal) { 25 | panic!() 26 | } 27 | self.env.insert(literal.to_string(), val); 28 | } 29 | 30 | pub fn release_references( 31 | &self, 32 | context: &Context, 33 | module: &Module, 34 | builder: &Builder, 35 | ) -> CompilerResult<()> { 36 | for (_, var) in self.env.iter() { 37 | match var { 38 | Variable::String(val) => { 39 | let release = module.get_function("release_string_reference").unwrap(); 40 | builder.build_call( 41 | &release, 42 | &[builder.build_load(&var.llvm_type(context), val, "")], 43 | "", 44 | ); 45 | } 46 | Variable::Vec(val) => { 47 | let release = module.get_function("release_vec_reference").unwrap(); 48 | builder.build_call( 49 | &release, 50 | &[builder.build_load(&var.llvm_type(context), val, "")], 51 | "", 52 | ); 53 | } 54 | Variable::Numeric(_) 55 | | Variable::Bool(_) 56 | | Variable::Function { .. } 57 | | Variable::Ptr(_) => {} 58 | } 59 | } 60 | for (_, var) in self.params.iter() { 61 | match var { 62 | Value::String(val) => { 63 | let release = module.get_function("release_string_reference").unwrap(); 64 | builder.build_call(&release, &[*val], ""); 65 | } 66 | Value::Vec(val) => { 67 | let release = module.get_function("release_vec_reference").unwrap(); 68 | builder.build_call(&release, &[*val], ""); 69 | } 70 | Value::Numeric(_) | Value::Bool(_) | Value::Function { .. } | Value::Ptr(_) => {} 71 | Value::Void => unreachable!(), 72 | Value::Break => unreachable!(), 73 | Value::CString(_) => todo!(), 74 | } 75 | } 76 | Ok(()) 77 | } 78 | 79 | pub fn set_param(&mut self, name: &str, val: Value) { 80 | if self.env.contains_key(name) { 81 | panic!() 82 | } 83 | self.params.insert(name.to_string(), val); 84 | } 85 | 86 | pub fn get_param(&self, name: &str) -> Option<&Value> { 87 | self.params.get(name) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /stdlib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::RefCell, 3 | ffi::{CStr, CString}, 4 | rc::Rc, 5 | }; 6 | 7 | /// # Safety 8 | /// 9 | /// loads raw ptr 10 | pub unsafe extern "C" fn string_from_c_string(ptr: *const i8) -> *const RefCell { 11 | let c_str = CStr::from_ptr(ptr); 12 | let rc = Rc::new(RefCell::new(c_str.to_str().unwrap().to_string())); 13 | Rc::into_raw(rc) 14 | } 15 | 16 | /// # Safety 17 | /// 18 | /// loads raw ptr 19 | pub unsafe extern "C" fn c_string_from_string(ptr: *const RefCell) -> *const i8 { 20 | let rc = Rc::from_raw(ptr); 21 | let ptr = { 22 | let string = rc.try_borrow().unwrap(); 23 | let c_string = CString::new(string.to_owned()).unwrap(); 24 | let ptr = c_string.as_ptr(); 25 | std::mem::forget(c_string); 26 | ptr 27 | }; 28 | std::mem::forget(rc); 29 | ptr 30 | } 31 | 32 | pub extern "C" fn string(num: f64) -> *const RefCell { 33 | let rc = Rc::new(RefCell::new(num.to_string())); 34 | Rc::into_raw(rc) 35 | } 36 | 37 | /// # Safety 38 | /// 39 | /// loads raw ptr 40 | pub unsafe extern "C" fn print(ptr: *const RefCell) { 41 | let rc = Rc::from_raw(ptr); 42 | { 43 | let string = rc.try_borrow().unwrap(); 44 | print!("{}", string); 45 | } 46 | std::mem::forget(rc); 47 | } 48 | 49 | /// # Safety 50 | /// 51 | /// loads raw ptr 52 | pub unsafe extern "C" fn release_string_reference(ptr: *const RefCell) { 53 | Rc::decrement_strong_count(ptr); 54 | } 55 | 56 | /// # Safety 57 | /// 58 | /// loads raw ptr 59 | pub unsafe extern "C" fn inc_string_reference(ptr: *const RefCell) { 60 | Rc::increment_strong_count(ptr); 61 | } 62 | 63 | /// # Safety 64 | /// 65 | /// loads raw ptr 66 | pub unsafe extern "C" fn inc_vec_reference(ptr: *const RefCell>) { 67 | Rc::increment_strong_count(ptr); 68 | } 69 | 70 | pub extern "C" fn vec_new() -> *const RefCell> { 71 | let rc = Rc::new(RefCell::new(Vec::new())); 72 | Rc::into_raw(rc) 73 | } 74 | 75 | /// # Safety 76 | /// 77 | /// loads raw ptr 78 | pub unsafe extern "C" fn vec_set(ptr: *const RefCell>, idx: f64, val: f64) { 79 | let rc = Rc::from_raw(ptr); 80 | { 81 | let mut vec = rc.try_borrow_mut().unwrap(); 82 | if (idx as usize) >= vec.len() { 83 | vec.resize((idx as usize) + 1, 0.); 84 | } 85 | vec[idx as usize] = val; 86 | } 87 | std::mem::forget(rc); 88 | } 89 | 90 | /// # Safety 91 | /// 92 | /// loads raw ptr 93 | pub unsafe extern "C" fn vec_get(ptr: *const RefCell>, idx: f64) -> f64 { 94 | let rc = Rc::from_raw(ptr); 95 | let val = { 96 | let vec = rc.borrow(); 97 | if (idx as usize) < vec.len() { 98 | vec[idx as usize] 99 | } else { 100 | 0. 101 | } 102 | }; 103 | std::mem::forget(rc); 104 | val 105 | } 106 | 107 | /// # Safety 108 | /// 109 | /// loads raw ptr 110 | pub unsafe extern "C" fn vec_len(ptr: *const RefCell>) -> f64 { 111 | let rc = Rc::from_raw(ptr); 112 | let val = { 113 | let vec = rc.borrow(); 114 | vec.len() 115 | }; 116 | std::mem::forget(rc); 117 | val as f64 118 | } 119 | 120 | /// # Safety 121 | /// 122 | /// loads raw ptr 123 | pub unsafe extern "C" fn release_vec_reference(ptr: *const RefCell>) { 124 | Rc::decrement_strong_count(ptr); 125 | } 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rock 2 | 3 | ⚠️The project is at a very early stage, lots of things will not work. 4 | 5 | [![Github all releases](https://img.shields.io/github/downloads/jarkonik/rocklang/total.svg)](https://GitHub.com/jarkonik/rocklang/releases/) 6 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?hosted_button_id=Y25KDXY4LJYQ2) 7 | [![Continuous integration](https://github.com/jarkonik/rocklang/actions/workflows/main.yml/badge.svg)](https://github.com/jarkonik/rocklang/actions/workflows/main.yml) 8 | [![codecov](https://codecov.io/gh/jarkonik/rocklang/branch/main/graph/badge.svg?token=DW07IRWGG0)](https://codecov.io/gh/jarkonik/rocklang) 9 | [![Rocklang 10 | Discord](https://badgen.net/discord/members/NK3baHRTve)](https://discord.gg/NK3baHRTve) 11 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 12 | 13 | 14 | 15 | JIT-compiled programming language. 16 | 17 | ## Getting started 18 | 19 | ### Linux 20 | 21 | 1. Copy and paste the following command in a shell: 22 | ``` 23 | sudo sh -c 'curl -L https://github.com/jarkonik/rocklang/releases/latest/download/rocklang-ubuntu-latest -o /usr/local/bin/rocklang && chmod +x /usr/local/bin/rocklang' 24 | ``` 25 | 2. Create a file named `main.rck` in a directory of your choice, with following 26 | content: 27 | ``` 28 | print("Hello from rocklang") 29 | ``` 30 | 3. While being in the same directory, that you've created the source file in, run `rocklang main.rck` from a shell. 31 | 4. You should see text `Hello from rocklang` printed in your terminal. 32 | 33 | ### Windows 34 | 35 | 1. [Download and run the installer.](https://github.com/jarkonik/rocklang/releases/latest/download/rocklang-windows-latest.msi) 36 | 2. Create a file named `main.rck` in a directory of your choice, with the following 37 | content: 38 | ``` 39 | print("Hello from rocklang") 40 | ``` 41 | 3. While being in the same directory, that you've created the source file in, run `rocklang main.rck` from PowerShell or 42 | Command Prompt. 43 | 4. You should see text `Hello from rocklang` printed in your terminal. 44 | 45 | ### MacOS 46 | 47 | 1. Copy and paste the following command in a shell: 48 | ``` 49 | sudo sh -c 'curl -L https://github.com/jarkonik/rocklang/releases/latest/download/rocklang-macos-latest -o /usr/local/bin/rocklang && chmod +x /usr/local/bin/rocklang' 50 | ``` 51 | 2. Create a file named `main.rck` in a directory of your choice, with the following 52 | content: 53 | ``` 54 | print("Hello from rocklang") 55 | ``` 56 | 3. While being in the same directory, that you've created the source file in, run `rocklang main.rck` from a shell. 57 | 4. You should see text `Hello from rocklang` printed in your terminal. 58 | 59 | ## Example 60 | 61 | Sample implementation of Sieve of Eratosthenes written in Rock 62 | 63 | ```c 64 | mem_set = (vec: vec, val: number, n: number): vec => { 65 | i = 0 66 | while i < n { 67 | vec_set(vec, i, val) 68 | i = i + 1 69 | } 70 | vec 71 | } 72 | 73 | sieve = (n: number): void => { 74 | v = vec_new() 75 | prime = mem_set(v, 1, n + 1) 76 | 77 | p = 2 78 | 79 | while p * p <= n { 80 | if vec_get(prime, p) == 1 { 81 | i = p * p 82 | while i <= n { 83 | vec_set(prime, i, 0) 84 | i = i + p 85 | } 86 | } 87 | 88 | p = p + 1 89 | } 90 | 91 | p = 2 92 | 93 | while p <= n { 94 | if vec_get(prime, p) == 1 { 95 | print(string(p)) 96 | print("\n") 97 | } 98 | 99 | p = p + 1 100 | } 101 | } 102 | 103 | sieve(10) 104 | ``` 105 | 106 | ## Building from source 107 | 1. Install Rust compiler that supports Rust Edition 2021, along with `cargo` tool, in your favorite fashion. 108 | 2. Install llvm 13 109 | 3. Run `cargo build` to build binaries or `cargo run examples/sieve.rck` to run a sample program. 110 | 111 | ## License 112 | 113 | This project is licensed under the terms of the MIT license. 114 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build, bump tag version and release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v[0-9]+.*" 7 | 8 | jobs: 9 | release: 10 | name: Create release 11 | runs-on: ubuntu-latest 12 | outputs: 13 | upload_url: ${{ steps.create_release.outputs.upload_url }} 14 | steps: 15 | - name: Create release 16 | id: create_release 17 | uses: actions/create-release@v1 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | tag_name: ${{ github.ref }} 22 | release_name: Release ${{ github.ref }} 23 | draft: false 24 | prerelease: false 25 | 26 | release_assets: 27 | name: Release assets 28 | needs: release 29 | runs-on: ${{ matrix.config.os }} 30 | strategy: 31 | matrix: 32 | config: 33 | - os: ubuntu-latest 34 | - os: macos-latest 35 | - os: windows-latest 36 | steps: 37 | - name: Checkout code 38 | uses: actions/checkout@v1 39 | 40 | - name: Install llvm when platform != Windows 41 | if: ${{ runner.os != 'Windows' }} 42 | uses: ./.github/actions/install_llvm 43 | 44 | - name: Download llvm when platform == Windows 45 | if: ${{ runner.os == 'Windows' }} 46 | run: Invoke-WebRequest -OutFile llvm.7z https://github.com/vovkos/llvm-package-windows/releases/download/llvm-master/llvm-13.0.0-windows-amd64-msvc15-libcmt.7z 47 | shell: pwsh 48 | 49 | - name: Extract llvm when platform == Windows 50 | if: ${{ runner.os == 'Windows' }} 51 | run: 7z.exe x .\llvm.7z -oC:\ -y 52 | shell: pwsh 53 | 54 | - name: Build project when platform == Windows 55 | env: 56 | LLVM_SYS_130_PREFIX: 'C:\llvm-13.0.0-windows-amd64-msvc15-libcmt' 57 | if: ${{ runner.os == 'Windows' }} 58 | run: cargo build --release --locked 59 | 60 | - name: Build project when platform != windows 61 | if: ${{ runner.os != 'Windows' }} 62 | run: cargo build --release --locked 63 | 64 | - name: Upload release assets when platform != Windows 65 | if: ${{ runner.os != 'Windows' }} 66 | uses: actions/upload-release-asset@v1 67 | env: 68 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 69 | with: 70 | upload_url: ${{ needs.release.outputs.upload_url }} 71 | asset_name: rocklang-${{ matrix.config.os }} 72 | asset_path: target/release/rocklang 73 | asset_content_type: application/octet-stream 74 | 75 | - name: Upload release assets when platform == Windows 76 | if: ${{ runner.os == 'Windows' }} 77 | uses: actions/upload-release-asset@v1 78 | env: 79 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 80 | with: 81 | upload_url: ${{ needs.release.outputs.upload_url }} 82 | asset_name: rocklang-${{ matrix.config.os }}.exe 83 | asset_path: target/release/rocklang.exe 84 | asset_content_type: application/octet-stream 85 | 86 | - name: Install wix when platform == Windows 87 | if: ${{ runner.os == 'Windows' }} 88 | run: cargo install cargo-wix 89 | 90 | - name: Create wix installer when platform == Windows 91 | if: ${{ runner.os == 'Windows' }} 92 | env: 93 | LLVM_SYS_130_PREFIX: 'C:\llvm-13.0.0-windows-amd64-msvc15-libcmt' 94 | run: cargo wix -o target/wix/rocklang.msi -p rocklang 95 | 96 | - name: Upload installer when platform == Windows 97 | if: ${{ runner.os == 'Windows' }} 98 | uses: actions/upload-release-asset@v1 99 | env: 100 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 101 | with: 102 | upload_url: ${{ needs.release.outputs.upload_url }} 103 | asset_name: rocklang-${{ matrix.config.os }}.msi 104 | asset_path: target/wix/rocklang.msi 105 | asset_content_type: application/octet-stream 106 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug unit tests in library 'stdlib'", 11 | "cargo": { 12 | "args": ["test", "--no-run", "--lib", "--package=stdlib"], 13 | "filter": { 14 | "name": "stdlib", 15 | "kind": "lib" 16 | } 17 | }, 18 | "args": [], 19 | "cwd": "${workspaceFolder}" 20 | }, 21 | { 22 | "type": "lldb", 23 | "request": "launch", 24 | "name": "Debug unit tests in library 'rocklang'", 25 | "cargo": { 26 | "args": ["test", "--no-run", "--lib", "--package=rocklang"], 27 | "filter": { 28 | "name": "rocklang", 29 | "kind": "lib" 30 | } 31 | }, 32 | "args": [], 33 | "cwd": "${workspaceFolder}" 34 | }, 35 | { 36 | "type": "lldb", 37 | "request": "launch", 38 | "name": "Debug executable 'rocklang'", 39 | "cargo": { 40 | "args": ["build", "--bin=rocklang", "--package=rocklang"], 41 | "filter": { 42 | "name": "rocklang", 43 | "kind": "bin" 44 | } 45 | }, 46 | "args": [], 47 | "cwd": "${workspaceFolder}" 48 | }, 49 | { 50 | "type": "lldb", 51 | "request": "launch", 52 | "name": "Debug unit tests in executable 'rocklang'", 53 | "cargo": { 54 | "args": ["test", "--no-run", "--bin=rocklang", "--package=rocklang"], 55 | "filter": { 56 | "name": "rocklang", 57 | "kind": "bin" 58 | } 59 | }, 60 | "args": [], 61 | "cwd": "${workspaceFolder}" 62 | }, 63 | { 64 | "type": "lldb", 65 | "request": "launch", 66 | "name": "Debug integration test 'tokenizer_tests'", 67 | "cargo": { 68 | "args": [ 69 | "test", 70 | "--no-run", 71 | "--test=tokenizer_tests", 72 | "--package=rocklang" 73 | ], 74 | "filter": { 75 | "name": "tokenizer_tests", 76 | "kind": "test" 77 | } 78 | }, 79 | "args": [], 80 | "cwd": "${workspaceFolder}" 81 | }, 82 | { 83 | "type": "lldb", 84 | "request": "launch", 85 | "name": "Debug integration test 'compiler_tests'", 86 | "cargo": { 87 | "args": [ 88 | "test", 89 | "--no-run", 90 | "--test=compiler_tests", 91 | "--package=rocklang" 92 | ], 93 | "filter": { 94 | "name": "compiler_tests", 95 | "kind": "test" 96 | } 97 | }, 98 | "args": [], 99 | "cwd": "${workspaceFolder}" 100 | }, 101 | { 102 | "type": "lldb", 103 | "request": "launch", 104 | "name": "Debug integration test 'integration_tests'", 105 | "cargo": { 106 | "args": [ 107 | "test", 108 | "--no-run", 109 | "--test=integration_tests", 110 | "--package=rocklang" 111 | ], 112 | "filter": { 113 | "name": "integration_tests", 114 | "kind": "test" 115 | } 116 | }, 117 | "args": [], 118 | "cwd": "${workspaceFolder}" 119 | }, 120 | { 121 | "type": "lldb", 122 | "request": "launch", 123 | "name": "Debug integration test 'parser_tests'", 124 | "cargo": { 125 | "args": [ 126 | "test", 127 | "--no-run", 128 | "--test=parser_tests", 129 | "--package=rocklang" 130 | ], 131 | "filter": { 132 | "name": "parser_tests", 133 | "kind": "test" 134 | } 135 | }, 136 | "args": [], 137 | "cwd": "${workspaceFolder}" 138 | }, 139 | { 140 | "type": "lldb", 141 | "request": "launch", 142 | "name": "Debug integration test 'token_test'", 143 | "cargo": { 144 | "args": ["test", "--no-run", "--test=token_test", "--package=rocklang"], 145 | "filter": { 146 | "name": "token_test", 147 | "kind": "test" 148 | } 149 | }, 150 | "args": [], 151 | "cwd": "${workspaceFolder}" 152 | } 153 | ] 154 | } 155 | -------------------------------------------------------------------------------- /src/llvm/context.rs: -------------------------------------------------------------------------------- 1 | extern crate llvm_sys as llvm; 2 | 3 | use std::ffi::c_void; 4 | 5 | use llvm::core::*; 6 | use llvm::support::LLVMAddSymbol; 7 | use llvm::support::LLVMLoadLibraryPermanently; 8 | 9 | use super::utils::c_str; 10 | use super::BasicBlock; 11 | use super::Builder; 12 | use super::Function; 13 | use super::LLVMError; 14 | use super::Module; 15 | use super::Type; 16 | use super::Value; 17 | 18 | pub struct Context(*mut llvm::LLVMContext); 19 | 20 | impl Drop for Context { 21 | fn drop(&mut self) { 22 | unsafe { 23 | LLVMContextDispose(self.0); 24 | } 25 | } 26 | } 27 | 28 | impl Default for Context { 29 | fn default() -> Self { 30 | Context::new() 31 | } 32 | } 33 | 34 | #[allow(dead_code)] 35 | impl Context { 36 | pub fn new() -> Self { 37 | Context(unsafe { LLVMContextCreate() }) 38 | } 39 | 40 | pub fn i64_type(&self) -> Type { 41 | Type::new(unsafe { LLVMInt64TypeInContext(self.0) }) 42 | } 43 | 44 | pub fn const_u64_to_ptr(&self, val: Value, tp: Type) -> Value { 45 | Value::from(unsafe { LLVMConstIntToPtr(val.0, tp.0) }) 46 | } 47 | 48 | pub fn function_type(&self, return_type: Type, param_types: &[Type], is_var_arg: bool) -> Type { 49 | let mut args: Vec<*mut llvm::LLVMType> = param_types.iter().map(|t| t.0).collect(); 50 | Type(unsafe { 51 | LLVMFunctionType( 52 | return_type.0, 53 | args.as_mut_ptr(), 54 | param_types.len().try_into().unwrap(), 55 | if is_var_arg { 1 } else { 0 }, 56 | ) 57 | }) 58 | } 59 | 60 | pub fn array_type(&self, el_type: Type, el_count: u32) -> Type { 61 | Type(unsafe { LLVMArrayType(el_type.0, el_count) }) 62 | } 63 | 64 | pub fn i32_type(&self) -> Type { 65 | Type(unsafe { LLVMInt32TypeInContext(self.0) }) 66 | } 67 | 68 | pub fn float_type(&self) -> Type { 69 | Type(unsafe { LLVMFloatTypeInContext(self.0) }) 70 | } 71 | 72 | pub fn double_type(&self) -> Type { 73 | Type(unsafe { LLVMDoubleTypeInContext(self.0) }) 74 | } 75 | 76 | pub fn void_type(&self) -> Type { 77 | Type(unsafe { LLVMVoidTypeInContext(self.0) }) 78 | } 79 | 80 | pub fn i8_type(&self) -> Type { 81 | Type(unsafe { LLVMIntTypeInContext(self.0, 8) }) 82 | } 83 | 84 | pub fn u64_type(&self) -> Type { 85 | Type(unsafe { LLVMIntTypeInContext(self.0, 64) }) 86 | } 87 | 88 | pub fn i1_type(&self) -> Type { 89 | Type(unsafe { LLVMIntTypeInContext(self.0, 1) }) 90 | } 91 | 92 | pub fn create_basic_block(&self, name: &str) -> BasicBlock { 93 | BasicBlock::new(unsafe { LLVMCreateBasicBlockInContext(self.0, c_str(name).as_ptr()) }) 94 | } 95 | 96 | pub fn append_basic_block(&self, function: &Function, name: &str) -> BasicBlock { 97 | BasicBlock::new(unsafe { 98 | LLVMAppendBasicBlockInContext(self.0, function.0, c_str(name).as_ptr()) 99 | }) 100 | } 101 | 102 | pub fn const_float(&self, value: f32) -> Value { 103 | Value::from(unsafe { LLVMConstReal(self.float_type().0, value.into()) }) 104 | } 105 | 106 | pub fn const_double(&self, value: f64) -> Value { 107 | Value::from(unsafe { LLVMConstReal(self.double_type().0, value) }) 108 | } 109 | 110 | pub fn add_symbol(&self, name: &str, f: *mut c_void) { 111 | unsafe { LLVMAddSymbol(c_str(name).as_ptr(), f) }; 112 | } 113 | 114 | pub fn const_i8(&self, value: i8) -> Value { 115 | Value::from(unsafe { LLVMConstInt(self.i8_type().0, value as u64, 0) }) 116 | } 117 | 118 | pub fn const_u64(&self, value: u64) -> Value { 119 | Value::from(unsafe { LLVMConstInt(self.u64_type().0, value, 1) }) 120 | } 121 | 122 | pub fn const_bool(&self, value: bool) -> Value { 123 | Value::from(unsafe { LLVMConstInt(self.i1_type().0, if value { 1 } else { 0 }, 0) }) 124 | } 125 | 126 | pub fn load_libary_permanently(&self, name: &str) -> Result<(), LLVMError> { 127 | unsafe { 128 | if LLVMLoadLibraryPermanently(c_str(name).as_ptr()) != 0 { 129 | Err(LLVMError {})?; 130 | } 131 | } 132 | Ok(()) 133 | } 134 | 135 | pub fn create_builder(&self) -> Builder { 136 | Builder::from(unsafe { LLVMCreateBuilderInContext(self.0) }) 137 | } 138 | 139 | pub fn create_module(&self, name: &str) -> Module { 140 | Module::from_context(unsafe { 141 | LLVMModuleCreateWithNameInContext(c_str(name).as_ptr(), self.0) 142 | }) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | konikjar@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /src/llvm/builder.rs: -------------------------------------------------------------------------------- 1 | extern crate llvm_sys as llvm; 2 | 3 | use llvm::{core::*, LLVMBuilder}; 4 | 5 | use super::{utils::c_str, BasicBlock, Context, Function, Type, Value}; 6 | 7 | pub struct Builder(*mut llvm::LLVMBuilder); 8 | pub enum Cmp { 9 | Less, 10 | LessOrEqual, 11 | Equal, 12 | NotEqual, 13 | Greater, 14 | GreaterOrEqual, 15 | } 16 | 17 | impl Drop for Builder { 18 | fn drop(&mut self) { 19 | unsafe { 20 | LLVMDisposeBuilder(self.0); 21 | } 22 | } 23 | } 24 | 25 | #[allow(dead_code)] 26 | impl Builder { 27 | pub fn new(context: &Context) -> Self { 28 | context.create_builder() 29 | } 30 | 31 | pub fn from(ptr: *mut LLVMBuilder) -> Self { 32 | Builder(ptr) 33 | } 34 | 35 | pub fn build_cond_br(&self, iff: &Value, then: &BasicBlock, els: &BasicBlock) -> Value { 36 | Value::from(unsafe { LLVMBuildCondBr(self.0, iff.0, then.0, els.0) }) 37 | } 38 | 39 | pub fn build_br(&self, dest: &BasicBlock) -> Value { 40 | Value::from(unsafe { LLVMBuildBr(self.0, dest.0) }) 41 | } 42 | 43 | pub fn build_alloca(&self, el_type: Type, name: &str) -> Value { 44 | Value::from(unsafe { LLVMBuildAlloca(self.0, el_type.0, c_str(name).as_ptr()) }) 45 | } 46 | 47 | pub fn create_store(&self, val: Value, ptr: &Value) -> Value { 48 | Value::from(unsafe { LLVMBuildStore(self.0, val.0, ptr.0) }) 49 | } 50 | 51 | pub fn build_malloc(&self, el_type: Type, name: &str) -> Value { 52 | Value::from(unsafe { LLVMBuildMalloc(self.0, el_type.0, c_str(name).as_ptr()) }) 53 | } 54 | 55 | pub fn build_load(&self, typ: &Type, ptr: &Value, name: &str) -> Value { 56 | Value::from(unsafe { LLVMBuildLoad2(self.0, typ.0, ptr.0, c_str(name).as_ptr()) }) 57 | } 58 | 59 | pub fn build_add(&self, lhs: Value, rhs: Value, name: &str) -> Value { 60 | Value::from(unsafe { LLVMBuildAdd(self.0, lhs.0, rhs.0, c_str(name).as_ptr()) }) 61 | } 62 | 63 | pub fn build_fadd(&self, lhs: Value, rhs: Value, name: &str) -> Value { 64 | Value::from(unsafe { LLVMBuildFAdd(self.0, lhs.0, rhs.0, c_str(name).as_ptr()) }) 65 | } 66 | 67 | pub fn build_fsub(&self, lhs: Value, rhs: Value, name: &str) -> Value { 68 | Value::from(unsafe { LLVMBuildFSub(self.0, lhs.0, rhs.0, c_str(name).as_ptr()) }) 69 | } 70 | 71 | pub fn build_fdiv(&self, lhs: Value, rhs: Value, name: &str) -> Value { 72 | Value::from(unsafe { LLVMBuildFDiv(self.0, lhs.0, rhs.0, c_str(name).as_ptr()) }) 73 | } 74 | 75 | pub fn build_frem(&self, lhs: Value, rhs: Value, name: &str) -> Value { 76 | Value::from(unsafe { LLVMBuildFRem(self.0, lhs.0, rhs.0, c_str(name).as_ptr()) }) 77 | } 78 | 79 | pub fn build_fmul(&self, lhs: Value, rhs: Value, name: &str) -> Value { 80 | Value::from(unsafe { LLVMBuildFMul(self.0, lhs.0, rhs.0, c_str(name).as_ptr()) }) 81 | } 82 | 83 | pub fn build_fneg(&self, rhs: Value, name: &str) -> Value { 84 | Value::from(unsafe { LLVMBuildFNeg(self.0, rhs.0, c_str(name).as_ptr()) }) 85 | } 86 | 87 | pub fn build_fcmp(&self, lhs: Value, rhs: Value, operator: Cmp, name: &str) -> Value { 88 | Value::from(unsafe { 89 | LLVMBuildFCmp( 90 | self.0, 91 | match operator { 92 | Cmp::LessOrEqual => llvm::LLVMRealPredicate::LLVMRealOLE, 93 | Cmp::Less => llvm::LLVMRealPredicate::LLVMRealOLT, 94 | Cmp::GreaterOrEqual => llvm::LLVMRealPredicate::LLVMRealOGE, 95 | Cmp::Greater => llvm::LLVMRealPredicate::LLVMRealOGT, 96 | Cmp::Equal => llvm::LLVMRealPredicate::LLVMRealOEQ, 97 | Cmp::NotEqual => llvm::LLVMRealPredicate::LLVMRealONE, 98 | }, 99 | lhs.0, 100 | rhs.0, 101 | c_str(name).as_ptr(), 102 | ) 103 | }) 104 | } 105 | 106 | pub fn build_free(&self, value: Value) -> Value { 107 | Value::from(unsafe { LLVMBuildFree(self.0, value.0) }) 108 | } 109 | 110 | pub fn create_br(&self, basic_block: &BasicBlock) -> Value { 111 | Value::from(unsafe { LLVMBuildBr(self.0, basic_block.0) }) 112 | } 113 | 114 | pub fn position_builder_at_end(&self, block: &BasicBlock) { 115 | unsafe { LLVMPositionBuilderAtEnd(self.0, block.0) } 116 | } 117 | 118 | pub fn build_ret(&self, value: Value) -> Value { 119 | Value::from(unsafe { LLVMBuildRet(self.0, value.0) }) 120 | } 121 | 122 | pub fn build_ret_void(&self) -> Value { 123 | Value::from(unsafe { LLVMBuildRetVoid(self.0) }) 124 | } 125 | 126 | pub fn get_insert_block(&self) -> BasicBlock { 127 | BasicBlock(unsafe { LLVMGetInsertBlock(self.0) }) 128 | } 129 | 130 | pub fn build_bitcast(&self, value: &Value, dest_type: Type, name: &str) -> Value { 131 | Value::from(unsafe { LLVMBuildBitCast(self.0, value.0, dest_type.0, c_str(name).as_ptr()) }) 132 | } 133 | 134 | pub fn build_call(&self, func: &Function, args: &[Value], name: &str) -> Value { 135 | let mut args: Vec<*mut llvm::LLVMValue> = args.iter().map(|t| t.0).collect(); 136 | 137 | Value::from(unsafe { 138 | // TODO: dont use deprecated api 139 | #[allow(deprecated)] 140 | LLVMBuildCall( 141 | self.0, 142 | func.0, 143 | args.as_mut_ptr(), 144 | args.len().try_into().unwrap(), 145 | c_str(name).as_ptr(), 146 | ) 147 | }) 148 | } 149 | 150 | pub fn build_global_string_ptr(&self, str: &str, name: &str) -> Value { 151 | Value::from(unsafe { 152 | LLVMBuildGlobalStringPtr(self.0, c_str(str).as_ptr(), c_str(name).as_ptr()) 153 | }) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /test_utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! assert_eq_ir { 3 | ($result:expr, $valid:expr) => { 4 | assert_eq!( 5 | $result, 6 | concat!( 7 | indoc!( 8 | r#" 9 | ; ModuleID = 'main' 10 | source_filename = "main""#, 11 | ), 12 | "\n", 13 | indoc!($valid) 14 | ) 15 | ) 16 | }; 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! node { 21 | ($expr: expr) => { 22 | Node { 23 | expression: $expr, 24 | span: Span::default(), 25 | } 26 | }; 27 | } 28 | 29 | #[macro_export] 30 | macro_rules! boxed_node { 31 | ($expr: expr) => { 32 | Box::new(node!($expr)) 33 | }; 34 | } 35 | 36 | #[macro_export] 37 | macro_rules! in_main_function { 38 | ($context:expr, $module:expr, $builder:expr, $expression:expr) => { 39 | let main_fun = $module.add_function( 40 | MAIN_FUNCTION, 41 | $context.function_type($context.void_type(), &[], false), 42 | ); 43 | let block = $context.append_basic_block(&main_fun, ""); 44 | $builder.position_builder_at_end(&block); 45 | $expression 46 | $builder.build_ret_void(); 47 | 48 | main_fun.verify_function().unwrap_or_else(|_x| { 49 | println!("IR Dump:"); 50 | println!("{}", $module); 51 | assert!(false, "Function verification failed") 52 | }); 53 | }; 54 | } 55 | 56 | #[macro_export] 57 | macro_rules! mock_compiler { 58 | () => { 59 | struct Compiler { 60 | } 61 | 62 | mock! { 63 | Compiler { } 64 | 65 | impl NumericVisitor> for Compiler { 66 | fn visit_numeric(&mut self, expr: &f64) -> CompilerResult; 67 | } 68 | 69 | impl BinaryVisitor> for Compiler { 70 | fn visit_binary(&mut self, expr: &expression::Binary, span: Span) -> CompilerResult; 71 | } 72 | 73 | impl IdentifierVisitor> for Compiler { 74 | fn visit_identifier(&mut self, expr: &str) -> CompilerResult; 75 | } 76 | 77 | impl FuncCallVisitor> for Compiler { 78 | fn visit_func_call(&mut self, expr: &expression::FuncCall, span: Span) -> CompilerResult; 79 | } 80 | 81 | impl FuncDeclVisitor> for Compiler { 82 | fn visit_func_decl(&mut self, body: &expression::FuncDecl) -> CompilerResult; 83 | } 84 | 85 | impl StringVisitor> for Compiler { 86 | fn visit_string(&mut self, expr: &str) -> CompilerResult; 87 | } 88 | 89 | impl ProgramVisitor> for Compiler { 90 | fn visit_program(&mut self, program: parser::Program) -> CompilerResult; 91 | } 92 | 93 | impl AssignmentVisitor> for Compiler { 94 | fn visit_assignment(&mut self, expr: &expression::Assignment, span: Span) -> CompilerResult; 95 | } 96 | 97 | impl ConditionalVisitor> for Compiler { 98 | fn visit_conditional(&mut self, expr: &expression::Conditional, span: Span) -> CompilerResult; 99 | } 100 | 101 | impl UnaryVisitor> for Compiler { 102 | fn visit_unary(&mut self, expr: &expression::Unary, span: Span) -> CompilerResult; 103 | } 104 | 105 | impl GroupingVisitor> for Compiler { 106 | fn visit_grouping(&mut self, expr: &expression::Grouping) -> CompilerResult; 107 | } 108 | 109 | impl WhileVisitor> for Compiler { 110 | fn visit_while(&mut self, expr: &expression::While, span: Span) -> CompilerResult; 111 | } 112 | 113 | impl BoolVisitor> for Compiler { 114 | fn visit_bool(&mut self, expr: &bool) -> CompilerResult; 115 | } 116 | 117 | impl BreakVisitor> for Compiler { 118 | fn visit_break(&mut self) -> CompilerResult; 119 | } 120 | 121 | impl LoadVisitor> for Compiler { 122 | fn visit_load(&mut self, name: &str) -> CompilerResult; 123 | } 124 | 125 | impl ExternVisitor> for Compiler { 126 | fn visit_extern(&mut self, name: &expression::Extern) -> CompilerResult; 127 | } 128 | 129 | impl Visitor> for Compiler { 130 | fn walk(&mut self, expr: &expression::Node) -> CompilerResult; 131 | } 132 | 133 | impl LLVMCompiler for Compiler { 134 | fn builder(&self) -> &Builder; 135 | fn context(&self) -> &Context; 136 | fn module(&self) -> &Module; 137 | fn enter_scope(&mut self); 138 | fn exit_scope(&mut self) -> CompilerResult<()>; 139 | fn get_var(&self, name: &str) -> Option; 140 | fn after_loop_blocks(&self) -> &Vec; 141 | fn track_maybe_orphaned(&mut self, val: Value); 142 | fn release_maybe_orphaned(&mut self); 143 | fn get_builtin(&self, name: &str) -> Option; 144 | fn set_var(&mut self, name: &str, val: Variable); 145 | fn build_function( 146 | &mut self, 147 | fun_compiler_val: Value, 148 | expr: &expression::FuncDecl, 149 | ) -> Result<(), CompilerError>; 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/tokenizer.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::Span; 2 | use crate::token::{Token, TokenKind}; 3 | use std::error::Error; 4 | use std::fmt; 5 | 6 | pub trait Tokenize { 7 | fn tokenize(&mut self) -> Result<&Vec>; 8 | } 9 | 10 | pub struct Tokenizer { 11 | source: String, 12 | current: usize, 13 | column: usize, 14 | tokens: Vec, 15 | line: usize, 16 | } 17 | 18 | impl Tokenize for Tokenizer { 19 | fn tokenize(&mut self) -> Result<&Vec> { 20 | self.current = 0; 21 | self.line = 1; 22 | self.column = 1; 23 | self.tokens.clear(); 24 | 25 | while !self.at_end() { 26 | self.scan_token()?; 27 | } 28 | 29 | self.add_token(TokenKind::Eof); 30 | 31 | Ok(&self.tokens) 32 | } 33 | } 34 | 35 | #[derive(Clone, Debug)] 36 | pub struct TokenizerError { 37 | pub chr: char, 38 | pub line: usize, 39 | } 40 | 41 | impl Error for TokenizerError {} 42 | 43 | impl fmt::Display for TokenizerError { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | write!(f, "unexpected character '{}'", self.chr) 46 | } 47 | } 48 | 49 | type Result = std::result::Result; 50 | 51 | impl Tokenizer { 52 | pub fn new(source: String) -> Self { 53 | Tokenizer { 54 | source, 55 | current: 0, 56 | line: 0, 57 | column: 0, 58 | tokens: Vec::new(), 59 | } 60 | } 61 | 62 | fn scan_token(&mut self) -> Result<()> { 63 | let chr = self.advance(); 64 | match chr { 65 | ' ' | '\r' | '\t' => (), 66 | '\n' => { 67 | self.line += 1; 68 | self.column = 0; 69 | } 70 | '<' => { 71 | if '=' == self.peek() { 72 | self.advance(); 73 | self.add_token(TokenKind::LessOrEqual) 74 | } else { 75 | self.add_token(TokenKind::Less) 76 | } 77 | } 78 | '>' => { 79 | if '=' == self.peek() { 80 | self.advance(); 81 | self.add_token(TokenKind::GreaterOrEqual) 82 | } else { 83 | self.add_token(TokenKind::Greater) 84 | } 85 | } 86 | '(' => self.add_token(TokenKind::LeftParen), 87 | ')' => self.add_token(TokenKind::RightParen), 88 | '+' => self.add_token(TokenKind::Plus), 89 | '-' => self.add_token(TokenKind::Minus), 90 | '*' => self.add_token(TokenKind::Asterisk), 91 | '%' => self.add_token(TokenKind::Percent), 92 | '"' => self.string(), 93 | '!' => { 94 | if '=' == self.peek() { 95 | self.advance(); 96 | self.add_token(TokenKind::NotEqual); 97 | } else { 98 | self.add_token(TokenKind::Exclamation); 99 | } 100 | } 101 | '|' if self.advance() == '|' => { 102 | self.add_token(TokenKind::Or); 103 | } 104 | '&' if self.advance() == '&' => { 105 | self.add_token(TokenKind::And); 106 | } 107 | '=' => match self.peek() { 108 | '=' => { 109 | self.add_token(TokenKind::DoubleEqual); 110 | self.advance(); 111 | } 112 | '>' => { 113 | self.add_token(TokenKind::Arrow); 114 | self.advance(); 115 | } 116 | _ => self.add_token(TokenKind::Equal), 117 | }, 118 | '{' => self.add_token(TokenKind::LCurly), 119 | '}' => self.add_token(TokenKind::RCurly), 120 | ',' => self.add_token(TokenKind::Comma), 121 | ':' => self.add_token(TokenKind::Colon), 122 | '/' => { 123 | if '/' == self.peek() { 124 | while self.peek() != '\n' && !self.at_end() { 125 | self.advance(); 126 | } 127 | } else { 128 | self.add_token(TokenKind::Slash); 129 | } 130 | } 131 | c if c.is_alphabetic() => self.identifier(), 132 | c if c.is_numeric() => self.numeric(), 133 | chr => { 134 | return Err(TokenizerError { 135 | chr, 136 | line: self.line, 137 | }) 138 | } 139 | }; 140 | Ok(()) 141 | } 142 | 143 | fn string(&mut self) { 144 | let mut literal = String::new(); 145 | 146 | while self.peek() != '"' { 147 | let chr = self.advance(); 148 | literal.push(chr); 149 | } 150 | self.advance(); 151 | self.add_token(TokenKind::String(literal)); 152 | } 153 | 154 | fn numeric(&mut self) { 155 | let mut literal = String::new(); 156 | literal.push(self.previous()); 157 | 158 | loop { 159 | let chr = self.peek(); 160 | 161 | if chr.is_numeric() || chr == '.' { 162 | literal.push(chr); 163 | self.advance(); 164 | } else { 165 | break; 166 | } 167 | } 168 | 169 | self.add_token(TokenKind::Numeric( 170 | literal.parse().expect("Error parsing number"), 171 | )); 172 | } 173 | 174 | fn identifier(&mut self) { 175 | let mut literal = String::new(); 176 | literal.push(self.previous()); 177 | 178 | loop { 179 | let chr = self.peek(); 180 | 181 | if chr.is_alphanumeric() || chr == '_' { 182 | literal.push(chr); 183 | self.advance(); 184 | } else { 185 | break; 186 | } 187 | } 188 | 189 | match literal.as_str() { 190 | "if" => self.add_token(TokenKind::If), 191 | "while" => self.add_token(TokenKind::While), 192 | "true" => self.add_token(TokenKind::True), 193 | "false" => self.add_token(TokenKind::False), 194 | "break" => self.add_token(TokenKind::Break), 195 | "else" => self.add_token(TokenKind::Else), 196 | "load" => self.add_token(TokenKind::Load), 197 | "extern" => self.add_token(TokenKind::Extern), 198 | _ => self.add_token(TokenKind::Identifier(literal)), 199 | }; 200 | } 201 | 202 | fn add_token(&mut self, kind: TokenKind) { 203 | self.tokens.push(Token { 204 | kind, 205 | span: Span { 206 | line: self.line as u32, 207 | column: self.column as u32, 208 | }, 209 | }); 210 | } 211 | 212 | fn advance(&mut self) -> char { 213 | let chr = self.peek(); 214 | self.column += 1; 215 | self.current += 1; 216 | chr 217 | } 218 | 219 | fn previous(&mut self) -> char { 220 | let chr = self.source.chars().nth(self.current - 1).unwrap(); 221 | chr 222 | } 223 | 224 | fn peek(&mut self) -> char { 225 | self.source.chars().nth(self.current).unwrap() 226 | } 227 | 228 | fn at_end(&self) -> bool { 229 | self.current >= self.source.chars().count() 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/compiler/assignment.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | expression::{self, Expression}, 3 | parser::Span, 4 | visitor::AssignmentVisitor, 5 | }; 6 | 7 | use super::{variable::Variable, Compiler, CompilerError, CompilerResult, LLVMCompiler, Value}; 8 | 9 | fn compile_assignment( 10 | compiler: &mut T, 11 | expr: &crate::expression::Assignment, 12 | span: Span, 13 | ) -> CompilerResult { 14 | let right = compiler.walk(&expr.right)?; 15 | 16 | let ptr = if let Expression::Identifier(name) = &expr.left.expression { 17 | match compiler.get_var(name) { 18 | Some(ptr) => ptr.into(), 19 | None => compiler 20 | .builder() 21 | .build_alloca(right.llvm_type(compiler.context()), ""), 22 | } 23 | } else { 24 | Err(CompilerError::NonIdentifierAssignment { span: span.clone() })? 25 | }; 26 | 27 | if let Expression::Identifier(name) = &expr.left.expression { 28 | match compiler.get_var(name) { 29 | Some(mut var) => { 30 | match var { 31 | Variable::String(val) => { 32 | let release = compiler 33 | .module() 34 | .get_function("release_string_reference") 35 | .unwrap(); 36 | compiler.builder().build_call( 37 | &release, 38 | &[compiler.builder().build_load( 39 | &var.llvm_type(compiler.context()), 40 | &val, 41 | "", 42 | )], 43 | "", 44 | ); 45 | } 46 | Variable::Vec(val) => { 47 | let release = compiler 48 | .module() 49 | .get_function("release_vec_reference") 50 | .unwrap(); 51 | 52 | compiler.builder().build_call( 53 | &release, 54 | &[compiler.builder().build_load( 55 | &var.llvm_type(compiler.context()), 56 | &val, 57 | "", 58 | )], 59 | "", 60 | ); 61 | } 62 | Variable::Numeric(_) 63 | | Variable::Bool(_) 64 | | Variable::Function { .. } 65 | | Variable::Ptr(_) => {} 66 | } 67 | compiler.builder().create_store(right.into(), &ptr); 68 | var.set_value(ptr); 69 | } 70 | None => { 71 | let var = match right { 72 | Value::String(_) => Variable::String(ptr), 73 | Value::Numeric(_) => Variable::Numeric(ptr), 74 | Value::Bool(_) => Variable::Bool(ptr), 75 | Value::Function { 76 | return_type, 77 | typ, 78 | val, 79 | } => Variable::Function { 80 | val, 81 | typ, 82 | return_type, 83 | }, 84 | Value::Vec(_) => Variable::Vec(ptr), 85 | Value::Ptr(_) => Variable::Ptr(ptr), 86 | Value::Void | Value::Break => Err(CompilerError::VoidAssignment)?, 87 | Value::CString(_) => todo!(), 88 | }; 89 | 90 | compiler.builder().create_store(right.into(), &ptr); 91 | compiler.set_var(name, var); 92 | } 93 | } 94 | 95 | match right { 96 | Value::String(val) => { 97 | let release = compiler 98 | .module() 99 | .get_function("inc_string_reference") 100 | .unwrap(); 101 | 102 | compiler.builder().build_call(&release, &[val], ""); 103 | } 104 | Value::Numeric(_) => {} 105 | Value::Bool(_) => {} 106 | Value::Function { .. } => {} 107 | Value::Vec(val) => { 108 | let release = compiler.module().get_function("inc_vec_reference").unwrap(); 109 | 110 | compiler.builder().build_call(&release, &[val], ""); 111 | } 112 | Value::Ptr(_) => {} 113 | Value::Void | Value::Break => Err(CompilerError::VoidAssignment)?, 114 | Value::CString(_) => todo!(), 115 | }; 116 | } else { 117 | Err(CompilerError::NonIdentifierAssignment { span })? 118 | } 119 | 120 | if let expression::Expression::FuncDecl(e) = &expr.right.expression { 121 | compiler.build_function(right, e)? 122 | } 123 | 124 | Ok(Value::Void) 125 | } 126 | 127 | impl AssignmentVisitor> for Compiler { 128 | fn visit_assignment( 129 | &mut self, 130 | expr: &crate::expression::Assignment, 131 | span: Span, 132 | ) -> CompilerResult { 133 | compile_assignment(self, expr, span) 134 | } 135 | } 136 | 137 | #[cfg(test)] 138 | mod test { 139 | use mockall::{ 140 | mock, 141 | predicate::{self, *}, 142 | }; 143 | 144 | use indoc::indoc; 145 | use pretty_assertions::assert_eq; 146 | 147 | use super::*; 148 | use crate::compiler::MAIN_FUNCTION; 149 | use crate::llvm; 150 | use crate::llvm::{Builder, Context, Module}; 151 | use crate::parser; 152 | use crate::parser::Span; 153 | use crate::{compiler::Variable, expression::Node}; 154 | use crate::{expression, visitor::*}; 155 | 156 | mock_compiler!(); 157 | 158 | #[test] 159 | fn test_numeric_assignment() -> Result<(), CompilerError> { 160 | let context = Context::new(); 161 | let module = context.create_module("main"); 162 | let builder = context.create_builder(); 163 | let mut compiler = MockCompiler::new(); 164 | compiler.expect_context().return_const(context); 165 | compiler.expect_builder().return_const(builder); 166 | compiler.expect_get_var().return_const_st(None); 167 | 168 | let const_double = Value::Numeric(compiler.context().const_double(3.0)); 169 | compiler.expect_walk().return_const_st(Ok(const_double)); 170 | compiler 171 | .expect_set_var() 172 | .with( 173 | predicate::eq("test"), 174 | predicate::function(|x| matches!(x, Variable::Numeric(_))), 175 | ) 176 | .return_const(()); 177 | 178 | let val: Value; 179 | in_main_function!(compiler.context(), module, compiler.builder(), { 180 | val = compile_assignment( 181 | &mut compiler, 182 | &expression::Assignment { 183 | left: Box::new(Node { 184 | expression: expression::Expression::Identifier("test".to_string()), 185 | span: Default::default(), 186 | }), 187 | right: Box::new(Node { 188 | expression: expression::Expression::Numeric(2.0), 189 | span: Default::default(), 190 | }), 191 | }, 192 | Span::default(), 193 | )?; 194 | 195 | assert!(matches!(val, Value::Void)); 196 | }); 197 | 198 | assert_eq_ir!( 199 | module.to_string(), 200 | r#" 201 | 202 | define void @main() { 203 | %1 = alloca double, align 8 204 | store double 3.000000e+00, double* %1, align 8 205 | ret void 206 | } 207 | "# 208 | ); 209 | 210 | Ok(()) 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /wix/main.wxs: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 | 43 | 53 | 54 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 71 | 72 | 77 | 78 | 79 | 80 | 81 | 89 | 90 | 91 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 112 | 116 | 117 | 118 | 119 | 120 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 153 | 154 | 155 | 156 | 157 | 158 | 162 | 163 | 164 | 165 | 173 | 174 | 175 | 176 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /src/compiler/func_call.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | expression::{self, Node}, 3 | llvm, 4 | parser::{self, Span}, 5 | visitor::FuncCallVisitor, 6 | }; 7 | 8 | use super::{variable::Variable, Compiler, CompilerError, CompilerResult, LLVMCompiler, Value}; 9 | 10 | fn compile_args( 11 | compiler: &mut T, 12 | args: &[Node], 13 | ) -> CompilerResult> { 14 | args.iter() 15 | .map(|arg| { 16 | let val = match compiler.walk(arg)? { 17 | Value::Void | Value::Break => Err(CompilerError::VoidAssignment)?, 18 | Value::String(n) => n, 19 | Value::Numeric(n) => n, 20 | Value::Bool(n) => n, 21 | Value::Vec(n) => n, 22 | Value::Ptr(n) => n, 23 | Value::Function { val, .. } => llvm::Value(val.0), 24 | Value::CString(n) => n, 25 | }; 26 | 27 | Ok(val) 28 | }) 29 | .collect() 30 | } 31 | 32 | fn compile_func_call( 33 | compiler: &mut T, 34 | expr: &expression::FuncCall, 35 | span: Span, 36 | ) -> CompilerResult { 37 | let name = match expr.calee.expression { 38 | expression::Expression::Identifier(ref name) => Ok(name.clone()), 39 | _ => unreachable!(), 40 | }?; 41 | 42 | let builtin = compiler.get_builtin(&name); 43 | 44 | let var = match builtin { 45 | Some(b) => b, 46 | None => compiler 47 | .get_var(&name) 48 | .ok_or(CompilerError::UndefinedIdentifier(name))?, 49 | }; 50 | 51 | let args = compile_args(compiler, &expr.args)?; 52 | 53 | let builder = compiler.builder(); 54 | 55 | match var { 56 | Variable::Function { 57 | return_type, val, .. 58 | } => { 59 | let llvm_value = builder.build_call(&val, &args, ""); 60 | 61 | let val = match return_type { 62 | parser::Type::Numeric => Value::Numeric(llvm_value), 63 | parser::Type::Vector => { 64 | let value = Value::Vec(llvm_value); 65 | compiler.track_maybe_orphaned(value); 66 | value 67 | } 68 | parser::Type::Void => Value::Void, 69 | parser::Type::Function => todo!(), 70 | parser::Type::Ptr => Value::Ptr(llvm_value), 71 | parser::Type::Bool => Value::Bool(llvm_value), 72 | parser::Type::String => { 73 | let value = Value::String(llvm_value); 74 | compiler.track_maybe_orphaned(value); 75 | value 76 | } 77 | parser::Type::CString => Value::CString(llvm_value), 78 | }; 79 | 80 | Ok(val) 81 | } 82 | val => Err(CompilerError::TypeError { 83 | expected: parser::Type::Function, 84 | actual: val.get_type(), 85 | span, 86 | }), 87 | } 88 | } 89 | 90 | impl FuncCallVisitor> for Compiler { 91 | fn visit_func_call( 92 | &mut self, 93 | expr: &expression::FuncCall, 94 | span: Span, 95 | ) -> CompilerResult { 96 | compile_func_call(self, expr, span) 97 | } 98 | } 99 | 100 | #[cfg(test)] 101 | mod test { 102 | use super::*; 103 | use crate::compiler::{utils::get_llvm_type, MAIN_FUNCTION}; 104 | use crate::parser::{Span, Type}; 105 | use crate::visitor::*; 106 | use crate::{ 107 | compiler::{CompilerError, CompilerResult, LLVMCompiler, Value, Variable}, 108 | expression::{Expression, FuncCall}, 109 | llvm::{Builder, Context, Module}, 110 | }; 111 | use indoc::indoc; 112 | use mockall::{ 113 | mock, 114 | predicate::{self, *}, 115 | }; 116 | use pretty_assertions::assert_eq; 117 | 118 | mock_compiler!(); 119 | 120 | macro_rules! test_func_call { 121 | ($return_type: expr, $arg_types: expr, $args: expr) => {{ 122 | let context = Context::new(); 123 | let module = context.create_module("main"); 124 | let builder = context.create_builder(); 125 | let mut compiler = MockCompiler::new(); 126 | 127 | compiler.expect_context().return_const(context); 128 | compiler.expect_builder().return_const(builder); 129 | compiler.expect_module().return_const(module); 130 | 131 | let fun_type = compiler.context().function_type( 132 | get_llvm_type(&compiler.context(), &$return_type), 133 | &$arg_types 134 | .iter() 135 | .map(|t| get_llvm_type(&compiler.context(), t)) 136 | .collect::>(), 137 | false, 138 | ); 139 | let fun = compiler.module().add_function("", fun_type); 140 | 141 | let fun_value = Variable::Function { 142 | return_type: $return_type, 143 | typ: fun_type, 144 | val: fun, 145 | }; 146 | 147 | compiler 148 | .expect_get_builtin() 149 | .with(predicate::eq("test_fun")) 150 | .times(1) 151 | .return_const_st(Some(fun_value.clone())); 152 | 153 | let const_double = Value::Numeric(compiler.context().const_double(3.)); 154 | 155 | compiler 156 | .expect_walk() 157 | .returning_st(move |x| match x.expression { 158 | Expression::Numeric(_) => Ok(const_double), 159 | _ => todo!(), 160 | }); 161 | 162 | let val: Value; 163 | in_main_function!(compiler.context(), compiler.module(), compiler.builder(), { 164 | let func_call = FuncCall { 165 | calee: boxed_node!(Expression::Identifier("test_fun".to_string())), 166 | args: $args, 167 | }; 168 | val = compile_func_call(&mut compiler, &func_call, Span::default())?; 169 | }); 170 | 171 | (compiler.module().to_string(), val) 172 | }}; 173 | } 174 | 175 | #[test] 176 | fn test_func_void_no_args_call() -> Result<(), CompilerError> { 177 | let (ir, return_value) = test_func_call!(Type::Void, vec![], vec![]); 178 | assert!(matches!(return_value, Value::Void)); 179 | assert_eq_ir!( 180 | ir, 181 | r#" 182 | 183 | declare void @0() 184 | 185 | define void @main() { 186 | call void @0() 187 | ret void 188 | } 189 | "# 190 | ); 191 | Ok(()) 192 | } 193 | 194 | #[test] 195 | fn test_func_numeric_no_args_call() -> Result<(), CompilerError> { 196 | let (ir, return_value) = test_func_call!(Type::Numeric, vec![], vec![]); 197 | assert!(matches!(return_value, Value::Numeric(_))); 198 | assert_eq_ir!( 199 | ir, 200 | r#" 201 | 202 | declare double @0() 203 | 204 | define void @main() { 205 | %1 = call double @0() 206 | ret void 207 | } 208 | "# 209 | ); 210 | Ok(()) 211 | } 212 | 213 | #[test] 214 | fn test_func_boolean_no_args_call() -> Result<(), CompilerError> { 215 | let (ir, return_value) = test_func_call!(Type::Bool, vec![], vec![]); 216 | assert!(matches!(return_value, Value::Bool(_))); 217 | assert_eq_ir!( 218 | ir, 219 | r#" 220 | 221 | declare i1 @0() 222 | 223 | define void @main() { 224 | %1 = call i1 @0() 225 | ret void 226 | } 227 | "# 228 | ); 229 | Ok(()) 230 | } 231 | 232 | #[test] 233 | fn test_func_ptr_no_args_call() -> Result<(), CompilerError> { 234 | let (ir, return_value) = test_func_call!(Type::Ptr, vec![], vec![]); 235 | assert!(matches!(return_value, Value::Ptr(_))); 236 | assert_eq_ir!( 237 | ir, 238 | r#" 239 | 240 | declare void* @0() 241 | 242 | define void @main() { 243 | %1 = call void* @0() 244 | ret void 245 | } 246 | "# 247 | ); 248 | Ok(()) 249 | } 250 | 251 | #[test] 252 | fn test_func_one_arg() -> Result<(), CompilerError> { 253 | let (ir, return_value) = test_func_call!( 254 | Type::Void, 255 | vec![Type::Numeric], 256 | vec![node!(Expression::Numeric(3.0))] 257 | ); 258 | assert!(matches!(return_value, Value::Void)); 259 | assert_eq_ir!( 260 | ir, 261 | r#" 262 | 263 | declare void @0(double) 264 | 265 | define void @main() { 266 | call void @0(double 3.000000e+00) 267 | ret void 268 | } 269 | "# 270 | ); 271 | Ok(()) 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/compiler/binary.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | expression::{self}, 3 | parser::Span, 4 | visitor::BinaryVisitor, 5 | }; 6 | 7 | use super::{value::Value, Compiler, CompilerError, CompilerResult, LLVMCompiler}; 8 | 9 | fn compile_binary( 10 | compiler: &mut T, 11 | expr: &expression::Binary, 12 | span: Span, 13 | ) -> CompilerResult { 14 | let lhs = match compiler.walk(&expr.left)? { 15 | Value::Numeric(n) => n, 16 | expr => Err(CompilerError::TypeError { 17 | expected: crate::parser::Type::Numeric, 18 | actual: expr.get_type(), 19 | span: span.clone(), 20 | })?, 21 | }; 22 | 23 | let rhs = match compiler.walk(&expr.right)? { 24 | Value::Numeric(n) => n, 25 | expr => Err(CompilerError::TypeError { 26 | expected: crate::parser::Type::Numeric, 27 | actual: expr.get_type(), 28 | span, 29 | })?, 30 | }; 31 | 32 | match expr.operator { 33 | expression::Operator::Plus => { 34 | Ok(Value::Numeric(compiler.builder().build_fadd(lhs, rhs, ""))) 35 | } 36 | expression::Operator::Minus => { 37 | Ok(Value::Numeric(compiler.builder().build_fsub(lhs, rhs, ""))) 38 | } 39 | expression::Operator::Asterisk => { 40 | Ok(Value::Numeric(compiler.builder().build_fmul(lhs, rhs, ""))) 41 | } 42 | expression::Operator::Slash => { 43 | Ok(Value::Numeric(compiler.builder().build_fdiv(lhs, rhs, ""))) 44 | } 45 | expression::Operator::Mod => { 46 | Ok(Value::Numeric(compiler.builder().build_frem(lhs, rhs, ""))) 47 | } 48 | expression::Operator::Equal => Ok(Value::Bool(compiler.builder().build_fcmp( 49 | lhs, 50 | rhs, 51 | crate::llvm::Cmp::Equal, 52 | "", 53 | ))), 54 | expression::Operator::NotEqual => Ok(Value::Bool(compiler.builder().build_fcmp( 55 | lhs, 56 | rhs, 57 | crate::llvm::Cmp::NotEqual, 58 | "", 59 | ))), 60 | expression::Operator::Less => Ok(Value::Bool(compiler.builder().build_fcmp( 61 | lhs, 62 | rhs, 63 | crate::llvm::Cmp::Less, 64 | "", 65 | ))), 66 | expression::Operator::Greater => Ok(Value::Bool(compiler.builder().build_fcmp( 67 | lhs, 68 | rhs, 69 | crate::llvm::Cmp::Greater, 70 | "", 71 | ))), 72 | expression::Operator::LessOrEqual => Ok(Value::Bool(compiler.builder().build_fcmp( 73 | lhs, 74 | rhs, 75 | crate::llvm::Cmp::LessOrEqual, 76 | "", 77 | ))), 78 | expression::Operator::GreaterOrEqual => Ok(Value::Bool(compiler.builder().build_fcmp( 79 | lhs, 80 | rhs, 81 | crate::llvm::Cmp::GreaterOrEqual, 82 | "", 83 | ))), 84 | } 85 | } 86 | 87 | impl BinaryVisitor> for Compiler { 88 | fn visit_binary(&mut self, expr: &expression::Binary, span: Span) -> CompilerResult { 89 | compile_binary(self, expr, span) 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod test { 95 | use mockall::{mock, predicate::*}; 96 | 97 | use indoc::indoc; 98 | use pretty_assertions::assert_eq; 99 | 100 | use super::*; 101 | use crate::compiler::Variable; 102 | use crate::compiler::MAIN_FUNCTION; 103 | use crate::expression::Node; 104 | use crate::llvm; 105 | use crate::llvm::{Builder, Context, Module}; 106 | use crate::parser; 107 | use crate::visitor::*; 108 | 109 | mock_compiler!(); 110 | 111 | fn test_binary_operation( 112 | operator: expression::Operator, 113 | ) -> Result<(String, Value), CompilerError> { 114 | let context = Context::new(); 115 | let module = context.create_module("main"); 116 | let builder = context.create_builder(); 117 | let mut compiler = MockCompiler::new(); 118 | compiler.expect_context().return_const(context); 119 | compiler.expect_builder().return_const(builder); 120 | 121 | let const_double = Value::Numeric(compiler.context().const_double(3.0)); 122 | compiler.expect_walk().return_const_st(Ok(const_double)); 123 | 124 | let val: Value; 125 | in_main_function!(compiler.context(), module, compiler.builder(), { 126 | val = compile_binary( 127 | &mut compiler, 128 | &expression::Binary { 129 | left: Box::new(Node { 130 | expression: expression::Expression::Numeric(6.0), 131 | span: Default::default(), 132 | }), 133 | operator, 134 | right: Box::new(Node { 135 | expression: expression::Expression::Numeric(2.0), 136 | span: Default::default(), 137 | }), 138 | }, 139 | Span::default(), 140 | )?; 141 | 142 | match val { 143 | Value::Numeric(val) => { 144 | let ptr = compiler 145 | .builder() 146 | .build_alloca(compiler.context().double_type(), ""); 147 | compiler.builder().create_store(val, &ptr); 148 | } 149 | Value::Bool(val) => { 150 | let ptr = compiler 151 | .builder() 152 | .build_alloca(compiler.context().i1_type(), ""); 153 | compiler.builder().create_store(val, &ptr); 154 | } 155 | _ => assert!(false, "Unexpected value"), 156 | } 157 | }); 158 | 159 | Ok((module.to_string(), val)) 160 | } 161 | 162 | #[test] 163 | fn test_addition() -> Result<(), CompilerError> { 164 | let (ir, val) = test_binary_operation(expression::Operator::Plus)?; 165 | assert!(matches!(val, Value::Numeric(_))); 166 | assert_eq_ir!( 167 | ir, 168 | r#" 169 | 170 | define void @main() { 171 | %1 = alloca double, align 8 172 | store double 6.000000e+00, double* %1, align 8 173 | ret void 174 | } 175 | "# 176 | ); 177 | Ok(()) 178 | } 179 | 180 | #[test] 181 | fn test_subtraction() -> Result<(), CompilerError> { 182 | let (ir, val) = test_binary_operation(expression::Operator::Minus)?; 183 | assert!(matches!(val, Value::Numeric(_))); 184 | assert_eq_ir!( 185 | ir, 186 | r#" 187 | 188 | define void @main() { 189 | %1 = alloca double, align 8 190 | store double 0.000000e+00, double* %1, align 8 191 | ret void 192 | } 193 | "# 194 | ); 195 | Ok(()) 196 | } 197 | 198 | #[test] 199 | fn test_multiplication() -> Result<(), CompilerError> { 200 | let (ir, val) = test_binary_operation(expression::Operator::Asterisk)?; 201 | assert!(matches!(val, Value::Numeric(_))); 202 | assert_eq_ir!( 203 | ir, 204 | r#" 205 | 206 | define void @main() { 207 | %1 = alloca double, align 8 208 | store double 9.000000e+00, double* %1, align 8 209 | ret void 210 | } 211 | "# 212 | ); 213 | Ok(()) 214 | } 215 | 216 | #[test] 217 | fn test_division() -> Result<(), CompilerError> { 218 | let (ir, val) = test_binary_operation(expression::Operator::Slash)?; 219 | assert!(matches!(val, Value::Numeric(_))); 220 | assert_eq_ir!( 221 | ir, 222 | r#" 223 | 224 | define void @main() { 225 | %1 = alloca double, align 8 226 | store double 1.000000e+00, double* %1, align 8 227 | ret void 228 | } 229 | "# 230 | ); 231 | Ok(()) 232 | } 233 | 234 | #[test] 235 | fn test_remainder() -> Result<(), CompilerError> { 236 | let (ir, val) = test_binary_operation(expression::Operator::Mod)?; 237 | assert!(matches!(val, Value::Numeric(_))); 238 | assert_eq_ir!( 239 | ir, 240 | r#" 241 | 242 | define void @main() { 243 | %1 = alloca double, align 8 244 | store double 0.000000e+00, double* %1, align 8 245 | ret void 246 | } 247 | "# 248 | ); 249 | Ok(()) 250 | } 251 | 252 | #[test] 253 | fn test_equality() -> Result<(), CompilerError> { 254 | let (ir, val) = test_binary_operation(expression::Operator::Equal)?; 255 | assert!(matches!(val, Value::Bool(_))); 256 | assert_eq_ir!( 257 | ir, 258 | r#" 259 | 260 | define void @main() { 261 | %1 = alloca i1, align 1 262 | store i1 true, i1* %1, align 1 263 | ret void 264 | } 265 | "# 266 | ); 267 | Ok(()) 268 | } 269 | 270 | #[test] 271 | fn test_not_equal() -> Result<(), CompilerError> { 272 | let (ir, val) = test_binary_operation(expression::Operator::NotEqual)?; 273 | assert!(matches!(val, Value::Bool(_))); 274 | assert_eq_ir!( 275 | ir, 276 | r#" 277 | 278 | define void @main() { 279 | %1 = alloca i1, align 1 280 | store i1 false, i1* %1, align 1 281 | ret void 282 | } 283 | "# 284 | ); 285 | Ok(()) 286 | } 287 | 288 | #[test] 289 | fn test_less() -> Result<(), CompilerError> { 290 | let (ir, val) = test_binary_operation(expression::Operator::Less)?; 291 | assert!(matches!(val, Value::Bool(_))); 292 | assert_eq_ir!( 293 | ir, 294 | r#" 295 | 296 | define void @main() { 297 | %1 = alloca i1, align 1 298 | store i1 false, i1* %1, align 1 299 | ret void 300 | } 301 | "# 302 | ); 303 | Ok(()) 304 | } 305 | 306 | #[test] 307 | fn test_less_or_equal() -> Result<(), CompilerError> { 308 | let (ir, val) = test_binary_operation(expression::Operator::LessOrEqual)?; 309 | assert!(matches!(val, Value::Bool(_))); 310 | assert_eq_ir!( 311 | ir, 312 | r#" 313 | 314 | define void @main() { 315 | %1 = alloca i1, align 1 316 | store i1 true, i1* %1, align 1 317 | ret void 318 | } 319 | "# 320 | ); 321 | Ok(()) 322 | } 323 | 324 | #[test] 325 | fn test_greater() -> Result<(), CompilerError> { 326 | let (ir, val) = test_binary_operation(expression::Operator::Greater)?; 327 | assert!(matches!(val, Value::Bool(_))); 328 | assert_eq_ir!( 329 | ir, 330 | r#" 331 | 332 | define void @main() { 333 | %1 = alloca i1, align 1 334 | store i1 false, i1* %1, align 1 335 | ret void 336 | } 337 | "# 338 | ); 339 | Ok(()) 340 | } 341 | 342 | #[test] 343 | fn test_greater_or_equal() -> Result<(), CompilerError> { 344 | let (ir, val) = test_binary_operation(expression::Operator::GreaterOrEqual)?; 345 | assert!(matches!(val, Value::Bool(_))); 346 | assert_eq_ir!( 347 | ir, 348 | r#" 349 | 350 | define void @main() { 351 | %1 = alloca i1, align 1 352 | store i1 true, i1* %1, align 1 353 | ret void 354 | } 355 | "# 356 | ); 357 | Ok(()) 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.17.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "0.7.18" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "ansi_term" 31 | version = "0.12.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 34 | dependencies = [ 35 | "winapi", 36 | ] 37 | 38 | [[package]] 39 | name = "anyhow" 40 | version = "1.0.61" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8" 43 | 44 | [[package]] 45 | name = "assert-json-diff" 46 | version = "2.0.2" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" 49 | dependencies = [ 50 | "serde", 51 | "serde_json", 52 | ] 53 | 54 | [[package]] 55 | name = "assert_cmd" 56 | version = "0.10.2" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "b7ac5c260f75e4e4ba87b7342be6edcecbcb3eb6741a0507fda7ad115845cc65" 59 | dependencies = [ 60 | "escargot", 61 | "predicates 1.0.8", 62 | "predicates-core", 63 | "predicates-tree", 64 | ] 65 | 66 | [[package]] 67 | name = "autocfg" 68 | version = "1.1.0" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 71 | 72 | [[package]] 73 | name = "backtrace" 74 | version = "0.3.66" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" 77 | dependencies = [ 78 | "addr2line", 79 | "cc", 80 | "cfg-if", 81 | "libc", 82 | "miniz_oxide", 83 | "object", 84 | "rustc-demangle", 85 | ] 86 | 87 | [[package]] 88 | name = "cc" 89 | version = "1.0.73" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 92 | 93 | [[package]] 94 | name = "cfg-if" 95 | version = "1.0.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 98 | 99 | [[package]] 100 | name = "ctor" 101 | version = "0.1.23" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" 104 | dependencies = [ 105 | "quote", 106 | "syn", 107 | ] 108 | 109 | [[package]] 110 | name = "diff" 111 | version = "0.1.13" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" 114 | 115 | [[package]] 116 | name = "difference" 117 | version = "2.0.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" 120 | 121 | [[package]] 122 | name = "difflib" 123 | version = "0.4.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 126 | 127 | [[package]] 128 | name = "downcast" 129 | version = "0.11.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" 132 | 133 | [[package]] 134 | name = "either" 135 | version = "1.7.0" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" 138 | 139 | [[package]] 140 | name = "escargot" 141 | version = "0.3.1" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "19db1f7e74438642a5018cdf263bb1325b2e792f02dd0a3ca6d6c0f0d7b1d5a5" 144 | dependencies = [ 145 | "serde", 146 | "serde_json", 147 | ] 148 | 149 | [[package]] 150 | name = "float-cmp" 151 | version = "0.9.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" 154 | dependencies = [ 155 | "num-traits", 156 | ] 157 | 158 | [[package]] 159 | name = "fragile" 160 | version = "1.2.1" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "85dcb89d2b10c5f6133de2efd8c11959ce9dbb46a2f7a4cab208c4eeda6ce1ab" 163 | 164 | [[package]] 165 | name = "gimli" 166 | version = "0.26.2" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" 169 | 170 | [[package]] 171 | name = "indoc" 172 | version = "1.0.6" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" 175 | 176 | [[package]] 177 | name = "itertools" 178 | version = "0.10.3" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 181 | dependencies = [ 182 | "either", 183 | ] 184 | 185 | [[package]] 186 | name = "itoa" 187 | version = "1.0.2" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" 190 | 191 | [[package]] 192 | name = "lazy_static" 193 | version = "1.4.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 196 | 197 | [[package]] 198 | name = "libc" 199 | version = "0.2.126" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" 202 | 203 | [[package]] 204 | name = "llvm-sys" 205 | version = "130.0.4" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "bdb6ea20e8a348f6db0b43a7f009fa7d981d22edf4cbe2e0c7b2247dbb25be61" 208 | dependencies = [ 209 | "cc", 210 | "lazy_static", 211 | "libc", 212 | "regex", 213 | "semver", 214 | ] 215 | 216 | [[package]] 217 | name = "memchr" 218 | version = "2.5.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 221 | 222 | [[package]] 223 | name = "miniz_oxide" 224 | version = "0.5.3" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" 227 | dependencies = [ 228 | "adler", 229 | ] 230 | 231 | [[package]] 232 | name = "mockall" 233 | version = "0.11.2" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "e2be9a9090bc1cac2930688fa9478092a64c6a92ddc6ae0692d46b37d9cab709" 236 | dependencies = [ 237 | "cfg-if", 238 | "downcast", 239 | "fragile", 240 | "lazy_static", 241 | "mockall_derive", 242 | "predicates 2.1.1", 243 | "predicates-tree", 244 | ] 245 | 246 | [[package]] 247 | name = "mockall_derive" 248 | version = "0.11.2" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "86d702a0530a0141cf4ed147cf5ec7be6f2c187d4e37fcbefc39cf34116bfe8f" 251 | dependencies = [ 252 | "cfg-if", 253 | "proc-macro2", 254 | "quote", 255 | "syn", 256 | ] 257 | 258 | [[package]] 259 | name = "normalize-line-endings" 260 | version = "0.3.0" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" 263 | 264 | [[package]] 265 | name = "num-traits" 266 | version = "0.2.15" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 269 | dependencies = [ 270 | "autocfg", 271 | ] 272 | 273 | [[package]] 274 | name = "object" 275 | version = "0.29.0" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" 278 | dependencies = [ 279 | "memchr", 280 | ] 281 | 282 | [[package]] 283 | name = "output_vt100" 284 | version = "0.1.3" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" 287 | dependencies = [ 288 | "winapi", 289 | ] 290 | 291 | [[package]] 292 | name = "pest" 293 | version = "2.2.1" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "69486e2b8c2d2aeb9762db7b4e00b0331156393555cff467f4163ff06821eef8" 296 | dependencies = [ 297 | "thiserror", 298 | "ucd-trie", 299 | ] 300 | 301 | [[package]] 302 | name = "predicates" 303 | version = "1.0.8" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" 306 | dependencies = [ 307 | "difference", 308 | "predicates-core", 309 | ] 310 | 311 | [[package]] 312 | name = "predicates" 313 | version = "2.1.1" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" 316 | dependencies = [ 317 | "difflib", 318 | "float-cmp", 319 | "itertools", 320 | "normalize-line-endings", 321 | "predicates-core", 322 | "regex", 323 | ] 324 | 325 | [[package]] 326 | name = "predicates-core" 327 | version = "1.0.3" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" 330 | 331 | [[package]] 332 | name = "predicates-tree" 333 | version = "1.0.5" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" 336 | dependencies = [ 337 | "predicates-core", 338 | "termtree", 339 | ] 340 | 341 | [[package]] 342 | name = "pretty_assertions" 343 | version = "1.2.1" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" 346 | dependencies = [ 347 | "ansi_term", 348 | "ctor", 349 | "diff", 350 | "output_vt100", 351 | ] 352 | 353 | [[package]] 354 | name = "proc-macro2" 355 | version = "1.0.42" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" 358 | dependencies = [ 359 | "unicode-ident", 360 | ] 361 | 362 | [[package]] 363 | name = "quote" 364 | version = "1.0.20" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" 367 | dependencies = [ 368 | "proc-macro2", 369 | ] 370 | 371 | [[package]] 372 | name = "regex" 373 | version = "1.6.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 376 | dependencies = [ 377 | "aho-corasick", 378 | "memchr", 379 | "regex-syntax", 380 | ] 381 | 382 | [[package]] 383 | name = "regex-syntax" 384 | version = "0.6.27" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 387 | 388 | [[package]] 389 | name = "rocklang" 390 | version = "0.2.8" 391 | dependencies = [ 392 | "anyhow", 393 | "assert-json-diff", 394 | "assert_cmd", 395 | "backtrace", 396 | "indoc", 397 | "libc", 398 | "llvm-sys", 399 | "mockall", 400 | "predicates 2.1.1", 401 | "pretty_assertions", 402 | "serde", 403 | "serde_json", 404 | "stdlib", 405 | "test_utils", 406 | ] 407 | 408 | [[package]] 409 | name = "rustc-demangle" 410 | version = "0.1.21" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 413 | 414 | [[package]] 415 | name = "ryu" 416 | version = "1.0.10" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" 419 | 420 | [[package]] 421 | name = "semver" 422 | version = "0.11.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" 425 | dependencies = [ 426 | "semver-parser", 427 | ] 428 | 429 | [[package]] 430 | name = "semver-parser" 431 | version = "0.10.2" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" 434 | dependencies = [ 435 | "pest", 436 | ] 437 | 438 | [[package]] 439 | name = "serde" 440 | version = "1.0.141" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "7af873f2c95b99fcb0bd0fe622a43e29514658873c8ceba88c4cb88833a22500" 443 | dependencies = [ 444 | "serde_derive", 445 | ] 446 | 447 | [[package]] 448 | name = "serde_derive" 449 | version = "1.0.141" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "75743a150d003dd863b51dc809bcad0d73f2102c53632f1e954e738192a3413f" 452 | dependencies = [ 453 | "proc-macro2", 454 | "quote", 455 | "syn", 456 | ] 457 | 458 | [[package]] 459 | name = "serde_json" 460 | version = "1.0.82" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" 463 | dependencies = [ 464 | "itoa", 465 | "ryu", 466 | "serde", 467 | ] 468 | 469 | [[package]] 470 | name = "stdlib" 471 | version = "0.1.0" 472 | 473 | [[package]] 474 | name = "syn" 475 | version = "1.0.98" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" 478 | dependencies = [ 479 | "proc-macro2", 480 | "quote", 481 | "unicode-ident", 482 | ] 483 | 484 | [[package]] 485 | name = "termtree" 486 | version = "0.2.4" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" 489 | 490 | [[package]] 491 | name = "test_utils" 492 | version = "0.1.0" 493 | 494 | [[package]] 495 | name = "thiserror" 496 | version = "1.0.32" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" 499 | dependencies = [ 500 | "thiserror-impl", 501 | ] 502 | 503 | [[package]] 504 | name = "thiserror-impl" 505 | version = "1.0.32" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" 508 | dependencies = [ 509 | "proc-macro2", 510 | "quote", 511 | "syn", 512 | ] 513 | 514 | [[package]] 515 | name = "ucd-trie" 516 | version = "0.1.4" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" 519 | 520 | [[package]] 521 | name = "unicode-ident" 522 | version = "1.0.2" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" 525 | 526 | [[package]] 527 | name = "winapi" 528 | version = "0.3.9" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 531 | dependencies = [ 532 | "winapi-i686-pc-windows-gnu", 533 | "winapi-x86_64-pc-windows-gnu", 534 | ] 535 | 536 | [[package]] 537 | name = "winapi-i686-pc-windows-gnu" 538 | version = "0.4.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 541 | 542 | [[package]] 543 | name = "winapi-x86_64-pc-windows-gnu" 544 | version = "0.4.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 547 | -------------------------------------------------------------------------------- /src/compiler/mod.rs: -------------------------------------------------------------------------------- 1 | mod assignment; 2 | mod binary; 3 | mod bool; 4 | mod break_visitor; 5 | mod conditional; 6 | mod extern_visitor; 7 | mod func_call; 8 | mod func_decl_vistor; 9 | mod grouping; 10 | mod identifier; 11 | mod load; 12 | mod numeric; 13 | mod program; 14 | mod scope; 15 | mod string; 16 | mod unary; 17 | mod utils; 18 | mod value; 19 | mod variable; 20 | mod while_visitor; 21 | 22 | use crate::expression; 23 | use crate::expression::Expression; 24 | use crate::llvm; 25 | use crate::llvm::Builder; 26 | use crate::llvm::Context; 27 | use crate::llvm::Function; 28 | use crate::llvm::Module; 29 | use crate::llvm::Type; 30 | use crate::parser; 31 | use crate::parser::Program; 32 | use crate::parser::Span; 33 | use crate::visitor::*; 34 | use std::collections::HashMap; 35 | use std::error::Error; 36 | use std::ffi::c_void; 37 | use std::fmt; 38 | 39 | use self::scope::Scope; 40 | pub use self::value::Value; 41 | use self::variable::Variable; 42 | 43 | #[derive(Clone, Debug)] 44 | pub enum CompilerError { 45 | VoidAssignment, 46 | NonIdentifierAssignment { 47 | span: Span, 48 | }, 49 | TypeError { 50 | expected: parser::Type, 51 | actual: parser::Type, 52 | span: Span, 53 | }, 54 | EngineInitError, 55 | UndefinedIdentifier(String), 56 | LLVMError(String), 57 | LoadLibaryError(String), 58 | WrongOperator { 59 | expected: expression::Operator, 60 | actual: expression::Operator, 61 | span: Span, 62 | }, 63 | } 64 | 65 | impl fmt::Display for CompilerError { 66 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 67 | let msg = match self { 68 | CompilerError::TypeError { 69 | expected, 70 | actual, 71 | span, 72 | } => { 73 | format!( 74 | "type error expected {}, but got {} at {}", 75 | expected, actual, span 76 | ) 77 | } 78 | CompilerError::EngineInitError => "engine init error".to_string(), 79 | CompilerError::UndefinedIdentifier(x) => { 80 | format!("undefined identifier {}", x) 81 | } 82 | CompilerError::LLVMError(err) => format!("llvm error: {}", err), 83 | CompilerError::LoadLibaryError(lib) => format!("error loading {}", lib), 84 | CompilerError::VoidAssignment => "void assignment".to_string(), 85 | CompilerError::NonIdentifierAssignment { span } => { 86 | format!("non identifier assignment at {}", span) 87 | } 88 | CompilerError::WrongOperator { 89 | expected, 90 | actual, 91 | span, 92 | } => format!( 93 | "wrong operator, expected {:#?}, but got {:#?} at {}", 94 | expected, actual, span 95 | ), 96 | }; 97 | write!(f, "{}", msg) 98 | } 99 | } 100 | 101 | impl Error for CompilerError {} 102 | 103 | type CompilerResult = Result; 104 | 105 | const MAIN_FUNCTION: &str = "main"; 106 | 107 | pub trait Compile: ProgramVisitor> { 108 | fn compile(&mut self) -> CompilerResult; 109 | } 110 | 111 | pub struct Compiler { 112 | after_loop_blocks: Vec, 113 | maybe_orphaned: Vec, 114 | program: Program, 115 | engine: llvm::Engine, 116 | context: llvm::Context, 117 | module: llvm::Module, 118 | builder: llvm::Builder, 119 | pass_manager: llvm::PassManager, 120 | optimization: bool, 121 | scopes: Vec, 122 | builtins: HashMap, 123 | } 124 | 125 | impl Visitor> for Compiler { 126 | fn walk(&mut self, node: &crate::expression::Node) -> CompilerResult { 127 | let span = node.span.clone(); 128 | match &node.expression { 129 | Expression::Binary(expr) => self.visit_binary(expr, span), 130 | Expression::Unary(expr) => self.visit_unary(expr, span), 131 | Expression::FuncCall(expr) => self.visit_func_call(expr, span), 132 | Expression::Numeric(expr) => self.visit_numeric(expr), 133 | Expression::Assignment(expr) => self.visit_assignment(expr, span), 134 | Expression::Identifier(expr) => self.visit_identifier(expr), 135 | Expression::Conditional(expr) => self.visit_conditional(expr, span), 136 | Expression::String(expr) => self.visit_string(expr), 137 | Expression::Bool(expr) => self.visit_bool(expr), 138 | Expression::Break => self.visit_break(), 139 | Expression::While(expr) => self.visit_while(expr, span), 140 | Expression::FuncDecl(expr) => self.visit_func_decl(expr), 141 | Expression::Load(expr) => self.visit_load(expr), 142 | Expression::Extern(expr) => self.visit_extern(expr), 143 | Expression::Grouping(expr) => self.visit_grouping(expr), 144 | } 145 | } 146 | } 147 | 148 | impl Compile for Compiler { 149 | fn compile(&mut self) -> CompilerResult { 150 | self.visit_program(self.program.clone()) 151 | } 152 | } 153 | 154 | impl Compiler { 155 | fn verify_function(&mut self, fun: Function) -> CompilerResult<()> { 156 | if fun.verify_function().is_ok() { 157 | Ok(()) 158 | } else { 159 | println!("{}", self.ir_string()); 160 | Err(CompilerError::LLVMError(self.ir_string())) 161 | } 162 | } 163 | 164 | pub fn dump_ir(&self) { 165 | println!("{}", self.module); 166 | } 167 | 168 | pub fn ir_string(&self) -> String { 169 | format!("{}", self.module) 170 | } 171 | 172 | pub fn run(&self) { 173 | self.engine.call(MAIN_FUNCTION); 174 | } 175 | 176 | pub fn turn_off_optimization(&mut self) { 177 | self.optimization = false; 178 | } 179 | 180 | pub fn new(program: Program) -> CompilerResult { 181 | let context = llvm::Context::new(); 182 | let module = llvm::Module::new("main", &context); 183 | let builder = llvm::Builder::new(&context); 184 | let engine = match llvm::Engine::new(&module) { 185 | Ok(e) => e, 186 | Err(_) => Err(CompilerError::EngineInitError {})?, 187 | }; 188 | let pass_manager = llvm::PassManager::new(&module); 189 | 190 | Ok(Compiler { 191 | after_loop_blocks: Vec::new(), 192 | maybe_orphaned: Vec::new(), 193 | builtins: HashMap::new(), 194 | scopes: vec![], 195 | program, 196 | context, 197 | module, 198 | builder, 199 | engine, 200 | pass_manager, 201 | optimization: true, 202 | }) 203 | } 204 | 205 | fn init_builtin(&mut self, name: &str, typ: Type, fun: *mut c_void, return_type: parser::Type) { 206 | self.context.add_symbol(name, fun); 207 | let val = self.module.add_function(name, typ); 208 | self.builtins.insert( 209 | name.to_string(), 210 | Variable::Function { 211 | val, 212 | typ, 213 | return_type, 214 | }, 215 | ); 216 | } 217 | 218 | fn init_builtins(&mut self) { 219 | let string_type = self.context.function_type( 220 | self.context.void_type().pointer_type(0), 221 | &[self.context.double_type()], 222 | false, 223 | ); 224 | self.init_builtin( 225 | "string", 226 | string_type, 227 | stdlib::string as *mut c_void, 228 | parser::Type::String, 229 | ); 230 | 231 | let print_type = self.context.function_type( 232 | self.context.void_type(), 233 | &[self.context.void_type().pointer_type(0)], 234 | false, 235 | ); 236 | self.init_builtin( 237 | "print", 238 | print_type, 239 | stdlib::print as *mut c_void, 240 | parser::Type::Void, 241 | ); 242 | 243 | self.init_builtin( 244 | "release_string_reference", 245 | self.context.function_type( 246 | self.context.void_type(), 247 | &[self.context.void_type().pointer_type(0)], 248 | false, 249 | ), 250 | stdlib::release_string_reference as *mut c_void, 251 | parser::Type::Void, 252 | ); 253 | 254 | self.init_builtin( 255 | "inc_string_reference", 256 | self.context.function_type( 257 | self.context.void_type(), 258 | &[self.context.void_type().pointer_type(0)], 259 | false, 260 | ), 261 | stdlib::inc_string_reference as *mut c_void, 262 | parser::Type::Void, 263 | ); 264 | 265 | self.init_builtin( 266 | "inc_vec_reference", 267 | self.context.function_type( 268 | self.context.void_type(), 269 | &[self.context.void_type().pointer_type(0)], 270 | false, 271 | ), 272 | stdlib::inc_vec_reference as *mut c_void, 273 | parser::Type::Void, 274 | ); 275 | 276 | self.init_builtin( 277 | "release_vec_reference", 278 | self.context.function_type( 279 | self.context.void_type(), 280 | &[self.context.void_type().pointer_type(0)], 281 | false, 282 | ), 283 | stdlib::release_vec_reference as *mut c_void, 284 | parser::Type::Void, 285 | ); 286 | 287 | self.init_builtin( 288 | "c_string_from_string", 289 | self.context.function_type( 290 | self.context.i8_type().pointer_type(0), 291 | &[self.context.void_type().pointer_type(0)], 292 | false, 293 | ), 294 | stdlib::c_string_from_string as *mut c_void, 295 | parser::Type::CString, 296 | ); 297 | 298 | self.init_builtin( 299 | "string_from_c_string", 300 | self.context.function_type( 301 | self.context.void_type().pointer_type(0), 302 | &[self.context.i8_type().pointer_type(0)], 303 | false, 304 | ), 305 | stdlib::string_from_c_string as *mut c_void, 306 | parser::Type::String, 307 | ); 308 | 309 | let vec_new_type = 310 | self.context 311 | .function_type(self.context.void_type().pointer_type(0), &[], false); 312 | self.init_builtin( 313 | "vec_new", 314 | vec_new_type, 315 | stdlib::vec_new as *mut c_void, 316 | parser::Type::Vector, 317 | ); 318 | 319 | let vec_set_type = self.context.function_type( 320 | self.context.void_type(), 321 | &[ 322 | self.context.void_type().pointer_type(0), 323 | self.context.double_type(), 324 | self.context.double_type(), 325 | ], 326 | false, 327 | ); 328 | self.init_builtin( 329 | "vec_set", 330 | vec_set_type, 331 | stdlib::vec_set as *mut c_void, 332 | parser::Type::Void, 333 | ); 334 | 335 | let vec_get_type = self.context.function_type( 336 | self.context.double_type(), 337 | &[ 338 | self.context.void_type().pointer_type(0), 339 | self.context.double_type(), 340 | ], 341 | false, 342 | ); 343 | self.init_builtin( 344 | "vec_get", 345 | vec_get_type, 346 | stdlib::vec_get as *mut c_void, 347 | parser::Type::Numeric, 348 | ); 349 | 350 | let vec_len_type = self.context.function_type( 351 | self.context.double_type(), 352 | &[self.context.void_type().pointer_type(0)], 353 | false, 354 | ); 355 | self.init_builtin( 356 | "vec_len", 357 | vec_len_type, 358 | stdlib::vec_len as *mut c_void, 359 | parser::Type::Numeric, 360 | ); 361 | 362 | let sqrt_type = self.context.function_type( 363 | self.context.double_type(), 364 | &[self.context.double_type()], 365 | false, 366 | ); 367 | let val = self.module.add_function("sqrt", sqrt_type); 368 | self.builtins.insert( 369 | "sqrt".to_string(), 370 | Variable::Function { 371 | val, 372 | typ: sqrt_type, 373 | return_type: parser::Type::Numeric, 374 | }, 375 | ); 376 | } 377 | 378 | fn set_param(&mut self, name: &str, val: Value) { 379 | self.scopes.last_mut().unwrap().set_param(name, val); 380 | } 381 | 382 | fn get_param(&self, expr: &str) -> Option { 383 | for scope in self.scopes.iter().rev() { 384 | if let Some(val) = scope.get_param(expr) { 385 | return Some(*val); 386 | } 387 | } 388 | None 389 | } 390 | } 391 | 392 | trait LLVMCompiler: Visitor> { 393 | fn builder(&self) -> &Builder; 394 | fn context(&self) -> &Context; 395 | fn module(&self) -> &Module; 396 | fn enter_scope(&mut self); 397 | fn exit_scope(&mut self) -> CompilerResult<()>; 398 | fn after_loop_blocks(&self) -> &Vec; 399 | fn get_var(&self, name: &str) -> Option; 400 | fn get_builtin(&self, name: &str) -> Option; 401 | fn track_maybe_orphaned(&mut self, val: Value); 402 | fn release_maybe_orphaned(&mut self); 403 | fn set_var(&mut self, name: &str, val: Variable); 404 | fn build_function( 405 | &mut self, 406 | fun_compiler_val: Value, 407 | expr: &expression::FuncDecl, 408 | ) -> Result<(), CompilerError>; 409 | } 410 | 411 | impl LLVMCompiler for Compiler { 412 | fn builder(&self) -> &Builder { 413 | &self.builder 414 | } 415 | 416 | fn context(&self) -> &Context { 417 | &self.context 418 | } 419 | 420 | fn module(&self) -> &Module { 421 | &self.module 422 | } 423 | 424 | fn enter_scope(&mut self) { 425 | self.scopes.push(Scope::new()); 426 | } 427 | 428 | fn exit_scope(&mut self) -> CompilerResult<()> { 429 | let scope = self.scopes.pop().unwrap(); 430 | self.release_maybe_orphaned(); 431 | scope.release_references(self.context(), self.module(), self.builder()) 432 | } 433 | 434 | fn get_var(&self, name: &str) -> Option { 435 | for scope in self.scopes.iter().rev() { 436 | if let Some(val) = scope.get(name) { 437 | return Some(*val); 438 | } 439 | } 440 | None 441 | } 442 | 443 | fn get_builtin(&self, name: &str) -> Option { 444 | self.builtins.get(name).copied() 445 | } 446 | 447 | fn track_maybe_orphaned(&mut self, val: Value) { 448 | self.maybe_orphaned.push(val); 449 | } 450 | 451 | fn release_maybe_orphaned(&mut self) { 452 | while let Some(val) = self.maybe_orphaned.pop() { 453 | match val { 454 | Value::Void => todo!(), 455 | Value::String(v) => { 456 | let release = self 457 | .module 458 | .get_function("release_string_reference") 459 | .unwrap(); 460 | self.builder.build_call(&release, &[v], ""); 461 | } 462 | Value::Numeric(_) => todo!(), 463 | Value::Bool(_) => todo!(), 464 | Value::Function { .. } => todo!(), 465 | Value::Vec(v) => { 466 | let release = self.module.get_function("release_vec_reference").unwrap(); 467 | self.builder.build_call(&release, &[v], ""); 468 | } 469 | Value::Break => todo!(), 470 | Value::Ptr(_) => todo!(), 471 | Value::CString(_) => todo!(), 472 | } 473 | } 474 | } 475 | 476 | fn set_var(&mut self, name: &str, val: Variable) { 477 | self.scopes.last_mut().unwrap().set(name, val); 478 | } 479 | 480 | fn build_function( 481 | &mut self, 482 | fun_compiler_val: Value, 483 | expr: &expression::FuncDecl, 484 | ) -> Result<(), CompilerError> { 485 | let fun = match fun_compiler_val { 486 | Value::Function { val, .. } => val, 487 | Value::Void => todo!(), 488 | Value::String(_) => todo!(), 489 | Value::Numeric(_) => todo!(), 490 | Value::Bool(_) => todo!(), 491 | Value::Vec(_) => todo!(), 492 | Value::Break => todo!(), 493 | Value::Ptr(_) => todo!(), 494 | Value::CString(_) => todo!(), 495 | }; 496 | 497 | let curr = self.builder.get_insert_block(); 498 | 499 | let block = self.context.append_basic_block(&fun, "entry"); 500 | self.builder.position_builder_at_end(&block); 501 | 502 | self.enter_scope(); 503 | 504 | for (i, param) in expr.params.iter().enumerate() { 505 | let val = fun.get_param(i.try_into().unwrap()); 506 | 507 | let val = match param.typ { 508 | parser::Type::String => { 509 | let release = self.module.get_function("inc_vec_reference").unwrap(); 510 | self.builder.build_call(&release, &[val], ""); 511 | 512 | Value::String(val) 513 | } 514 | parser::Type::Numeric => Value::Numeric(val), 515 | parser::Type::Bool => Value::Bool(val), 516 | parser::Type::Vector => { 517 | let release = self.module.get_function("inc_vec_reference").unwrap(); 518 | self.builder.build_call(&release, &[val], ""); 519 | 520 | Value::Vec(val) 521 | } 522 | parser::Type::Void => todo!(), 523 | parser::Type::Function => todo!(), 524 | parser::Type::Ptr => todo!(), 525 | parser::Type::CString => todo!(), 526 | }; 527 | self.set_param(param.name.as_str(), val); 528 | } 529 | 530 | let mut last_val = Value::Void; 531 | 532 | for stmt in expr.body.clone() { 533 | self.release_maybe_orphaned(); 534 | last_val = self.walk(&stmt)?; 535 | } 536 | 537 | let ret_val = match last_val { 538 | Value::Void => None, 539 | Value::Numeric(n) => Some(n), 540 | Value::Vec(n) => { 541 | let release = self.module.get_function("inc_vec_reference").unwrap(); 542 | self.builder.build_call(&release, &[n], ""); 543 | Some(n) 544 | } 545 | Value::String(n) => { 546 | let release = self.module.get_function("inc_string_reference").unwrap(); 547 | self.builder.build_call(&release, &[n], ""); 548 | 549 | Some(n) 550 | } 551 | Value::Bool(_) => todo!(), 552 | Value::Function { .. } => todo!(), 553 | Value::Break => todo!(), 554 | Value::Ptr(_) => todo!(), 555 | Value::CString(_) => todo!(), 556 | }; 557 | 558 | self.exit_scope()?; 559 | 560 | match ret_val { 561 | Some(v) => self.builder.build_ret(v), 562 | None => self.builder.build_ret_void(), 563 | }; 564 | 565 | self.builder.position_builder_at_end(&curr); 566 | 567 | self.verify_function(fun)?; 568 | 569 | if self.optimization { 570 | self.pass_manager.run(&fun); 571 | } 572 | 573 | Ok(()) 574 | } 575 | 576 | fn after_loop_blocks(&self) -> &Vec { 577 | &self.after_loop_blocks 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::expression::{self, Node}; 2 | use crate::expression::{Expression, Operator}; 3 | use crate::token::{Token, TokenKind}; 4 | use backtrace::Backtrace; 5 | use serde::Serialize; 6 | use std::error::Error; 7 | use std::fmt::Display; 8 | 9 | macro_rules! consume { 10 | ($self: ident,$kind: pat) => {{ 11 | let token = $self.advance(); 12 | if matches!(token.kind, $kind) { 13 | Ok(()) 14 | } else { 15 | Err(ParserError::SyntaxError { 16 | token: token.clone(), 17 | backtrace: Backtrace::new(), 18 | }) 19 | } 20 | }}; 21 | } 22 | 23 | #[derive(Clone, Debug)] 24 | pub enum ParserError { 25 | SyntaxError { token: Token, backtrace: Backtrace }, 26 | } 27 | impl Display for ParserError { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { 29 | match self { 30 | ParserError::SyntaxError { 31 | token, 32 | backtrace: _, 33 | } => { 34 | write!( 35 | f, 36 | "Syntax error: unexpected token {} at {}", 37 | token.kind, token.span 38 | ) 39 | } 40 | } 41 | } 42 | } 43 | impl Error for ParserError {} 44 | 45 | type Result = std::result::Result; 46 | 47 | #[derive(Copy, Clone, Serialize, Debug)] 48 | pub enum Type { 49 | Numeric, 50 | Bool, 51 | Vector, 52 | Void, 53 | Function, 54 | Ptr, 55 | String, 56 | CString, 57 | } 58 | 59 | impl Display for Type { 60 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 61 | let name = match self { 62 | Type::Numeric => "Numeric", 63 | Type::Bool => "Bool", 64 | Type::Vector => "Vector", 65 | Type::Void => "Void", 66 | Type::Function => "Function", 67 | Type::Ptr => "Ptr", 68 | Type::String => "String", 69 | Type::CString => "CString", 70 | }; 71 | write!(f, "{}", name) 72 | } 73 | } 74 | 75 | #[derive(Clone, Serialize, Debug)] 76 | pub struct Param { 77 | pub typ: Type, 78 | pub name: String, 79 | } 80 | 81 | #[derive(Default, Serialize, Clone)] 82 | pub struct Program { 83 | pub body: Vec, 84 | } 85 | 86 | pub trait Parse { 87 | fn parse(&mut self) -> Result; 88 | } 89 | 90 | pub struct Parser { 91 | tokens: Vec, 92 | current: usize, 93 | } 94 | 95 | #[derive(Debug, Default, Serialize, Clone)] 96 | pub struct Span { 97 | pub line: u32, 98 | pub column: u32, 99 | } 100 | 101 | impl Display for Span { 102 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 103 | write!(f, "{}:{}", self.line, self.column) 104 | } 105 | } 106 | 107 | impl Parse for Parser { 108 | fn parse(&mut self) -> Result { 109 | let mut statements: Vec = Vec::new(); 110 | 111 | while !self.at_end() { 112 | statements.push(self.expression()?); 113 | } 114 | 115 | Ok(Program { body: statements }) 116 | } 117 | } 118 | 119 | impl Parser { 120 | pub fn new(tokens: &[Token]) -> Self { 121 | Parser { 122 | tokens: tokens.to_vec(), 123 | current: 0, 124 | } 125 | } 126 | 127 | fn expression(&mut self) -> Result { 128 | self.while_loop() 129 | } 130 | 131 | fn while_loop(&mut self) -> Result { 132 | match self.peek().kind { 133 | TokenKind::While => { 134 | self.advance(); 135 | let predicate = self.expression()?; 136 | 137 | match self.advance().kind { 138 | TokenKind::LCurly => (), 139 | _ => { 140 | return Err(ParserError::SyntaxError { 141 | token: self.previous().clone(), 142 | backtrace: Backtrace::new(), 143 | }) 144 | } 145 | }; 146 | 147 | let mut body: Vec = Vec::new(); 148 | 149 | loop { 150 | match self.peek().kind { 151 | TokenKind::RCurly => { 152 | self.advance(); 153 | break; 154 | } 155 | _ => { 156 | body.push(self.expression()?); 157 | } 158 | } 159 | } 160 | 161 | Ok(self.node(Expression::While(expression::While { 162 | predicate: Box::new(predicate), 163 | body, 164 | }))) 165 | } 166 | _ => self.conditional(), 167 | } 168 | } 169 | 170 | fn node(&mut self, expression: Expression) -> Node { 171 | Node { 172 | expression, 173 | span: self.previous().span.clone(), 174 | } 175 | } 176 | 177 | fn conditional(&mut self) -> Result { 178 | match self.peek().kind { 179 | TokenKind::If => { 180 | self.advance(); 181 | let predicate = self.expression()?; 182 | 183 | match self.advance().kind { 184 | TokenKind::LCurly => (), 185 | _ => { 186 | return Err(ParserError::SyntaxError { 187 | token: self.previous().clone(), 188 | backtrace: Backtrace::new(), 189 | }) 190 | } 191 | }; 192 | 193 | let mut body: Vec = Vec::new(); 194 | let mut else_body: Vec = Vec::new(); 195 | 196 | loop { 197 | match self.peek().kind { 198 | TokenKind::RCurly => { 199 | self.advance(); 200 | break; 201 | } 202 | _ => { 203 | body.push(self.expression()?); 204 | } 205 | } 206 | } 207 | 208 | if let TokenKind::Else = self.peek().kind { 209 | self.advance(); 210 | 211 | match self.advance().kind { 212 | TokenKind::LCurly => (), 213 | _ => { 214 | return Err(ParserError::SyntaxError { 215 | token: self.previous().clone(), 216 | backtrace: Backtrace::new(), 217 | }) 218 | } 219 | }; 220 | 221 | loop { 222 | match self.peek().kind { 223 | TokenKind::RCurly => { 224 | self.advance(); 225 | break; 226 | } 227 | _ => { 228 | else_body.push(self.expression()?); 229 | } 230 | } 231 | } 232 | } 233 | 234 | Ok(self.node(Expression::Conditional(expression::Conditional { 235 | predicate: Box::new(predicate), 236 | body, 237 | else_body, 238 | }))) 239 | } 240 | _ => self.assignment(), 241 | } 242 | } 243 | 244 | fn assignment(&mut self) -> Result { 245 | let mut expr = self.equality()?; 246 | 247 | while let TokenKind::Equal = self.peek().kind { 248 | self.advance(); 249 | let right = self.equality()?; 250 | expr = self.node(Expression::Assignment(expression::Assignment { 251 | left: Box::new(expr), 252 | right: Box::new(right), 253 | })) 254 | } 255 | 256 | Ok(expr) 257 | } 258 | 259 | fn equality(&mut self) -> Result { 260 | let mut expr = self.addition_or_modulo()?; 261 | 262 | loop { 263 | match self.peek().kind { 264 | TokenKind::DoubleEqual => { 265 | self.advance(); 266 | let right = self.addition_or_modulo()?; 267 | expr = self.node(Expression::Binary(expression::Binary { 268 | left: Box::new(expr), 269 | operator: Operator::Equal, 270 | right: Box::new(right), 271 | })); 272 | } 273 | TokenKind::NotEqual => { 274 | self.advance(); 275 | let right = self.addition_or_modulo()?; 276 | expr = self.node(Expression::Binary(expression::Binary { 277 | left: Box::new(expr), 278 | operator: Operator::NotEqual, 279 | right: Box::new(right), 280 | })); 281 | } 282 | TokenKind::LessOrEqual => { 283 | self.advance(); 284 | let right = self.addition_or_modulo()?; 285 | expr = self.node(Expression::Binary(expression::Binary { 286 | left: Box::new(expr), 287 | operator: Operator::LessOrEqual, 288 | right: Box::new(right), 289 | })); 290 | } 291 | TokenKind::Less => { 292 | self.advance(); 293 | let right = self.addition_or_modulo()?; 294 | expr = self.node(Expression::Binary(expression::Binary { 295 | left: Box::new(expr), 296 | operator: Operator::Less, 297 | right: Box::new(right), 298 | })); 299 | } 300 | TokenKind::Greater => { 301 | self.advance(); 302 | let right = self.addition_or_modulo()?; 303 | expr = self.node(Expression::Binary(expression::Binary { 304 | left: Box::new(expr), 305 | operator: Operator::Greater, 306 | right: Box::new(right), 307 | })); 308 | } 309 | TokenKind::GreaterOrEqual => { 310 | self.advance(); 311 | let right = self.addition_or_modulo()?; 312 | expr = self.node(Expression::Binary(expression::Binary { 313 | left: Box::new(expr), 314 | operator: Operator::GreaterOrEqual, 315 | right: Box::new(right), 316 | })); 317 | } 318 | _ => break, 319 | } 320 | } 321 | 322 | Ok(expr) 323 | } 324 | 325 | fn addition_or_modulo(&mut self) -> Result { 326 | let mut expr = self.factor()?; 327 | 328 | loop { 329 | match self.peek().kind { 330 | TokenKind::Plus => { 331 | self.advance(); 332 | let right = self.factor()?; 333 | 334 | expr = self.node(Expression::Binary(expression::Binary { 335 | left: Box::new(expr), 336 | operator: Operator::Plus, 337 | right: Box::new(right), 338 | })); 339 | } 340 | TokenKind::Minus => { 341 | self.advance(); 342 | let right = self.factor()?; 343 | expr = self.node(Expression::Binary(expression::Binary { 344 | left: Box::new(expr), 345 | operator: Operator::Minus, 346 | right: Box::new(right), 347 | })); 348 | } 349 | TokenKind::Percent => { 350 | self.advance(); 351 | let right = self.factor()?; 352 | expr = self.node(Expression::Binary(expression::Binary { 353 | left: Box::new(expr), 354 | operator: Operator::Mod, 355 | right: Box::new(right), 356 | })); 357 | } 358 | _ => break, 359 | } 360 | } 361 | Ok(expr) 362 | } 363 | 364 | fn factor(&mut self) -> Result { 365 | let mut expr = self.unary()?; 366 | 367 | loop { 368 | match self.peek().kind { 369 | TokenKind::Asterisk => { 370 | self.advance(); 371 | let right = self.unary()?; 372 | expr = self.node(Expression::Binary(expression::Binary { 373 | left: Box::new(expr), 374 | operator: Operator::Asterisk, 375 | right: Box::new(right), 376 | })); 377 | } 378 | TokenKind::Slash => { 379 | self.advance(); 380 | let right = self.unary()?; 381 | expr = self.node(Expression::Binary(expression::Binary { 382 | left: Box::new(expr), 383 | operator: Operator::Slash, 384 | right: Box::new(right), 385 | })); 386 | } 387 | _ => break, 388 | } 389 | } 390 | 391 | Ok(expr) 392 | } 393 | 394 | fn unary(&mut self) -> Result { 395 | match self.peek().kind { 396 | TokenKind::Minus => { 397 | self.advance(); 398 | let right = self.unary()?; 399 | Ok(self.node(Expression::Unary(expression::Unary { 400 | operator: Operator::Minus, 401 | right: Box::new(right), 402 | }))) 403 | } 404 | _ => self.extern_stmt(), 405 | } 406 | } 407 | 408 | fn extern_stmt(&mut self) -> Result { 409 | match self.peek().kind { 410 | TokenKind::Extern => { 411 | self.advance(); 412 | 413 | if !matches!(self.advance().kind, TokenKind::Less) { 414 | return Err(ParserError::SyntaxError { 415 | token: self.previous().clone(), 416 | backtrace: Backtrace::new(), 417 | }); 418 | }; 419 | 420 | let mut types = vec![]; 421 | 422 | loop { 423 | let token = self.advance().clone(); 424 | match token.kind { 425 | TokenKind::Identifier(ref s) => { 426 | types.push(self.type_from_literal(s)?); 427 | } 428 | TokenKind::Comma => (), 429 | TokenKind::Greater => { 430 | break; 431 | } 432 | _ => { 433 | return Err(ParserError::SyntaxError { 434 | token: self.previous().clone(), 435 | backtrace: Backtrace::new(), 436 | }); 437 | } 438 | } 439 | } 440 | 441 | if !matches!(self.advance().kind, TokenKind::LeftParen) { 442 | return Err(ParserError::SyntaxError { 443 | token: self.previous().clone(), 444 | backtrace: Backtrace::new(), 445 | }); 446 | }; 447 | 448 | let name = if let TokenKind::String(s) = &self.advance().kind { 449 | s.to_string() 450 | } else { 451 | return Err(ParserError::SyntaxError { 452 | token: self.previous().clone(), 453 | backtrace: Backtrace::new(), 454 | }); 455 | }; 456 | 457 | if !matches!(self.advance().kind, TokenKind::RightParen) { 458 | return Err(ParserError::SyntaxError { 459 | token: self.previous().clone(), 460 | backtrace: Backtrace::new(), 461 | }); 462 | }; 463 | 464 | let return_type = match types.last() { 465 | Some(v) => *v, 466 | _ => { 467 | return Err(ParserError::SyntaxError { 468 | token: self.previous().clone(), 469 | backtrace: Backtrace::new(), 470 | }); 471 | } 472 | }; 473 | 474 | Ok(self.node(Expression::Extern(expression::Extern { 475 | types: types[0..types.len() - 1].to_vec(), 476 | return_type, 477 | name, 478 | }))) 479 | } 480 | _ => self.func_declr(), 481 | } 482 | } 483 | 484 | fn func_declr(&mut self) -> Result { 485 | match self.peek().kind { 486 | TokenKind::LeftParen => { 487 | let current = self.current; 488 | 489 | self.advance(); 490 | 491 | let mut params: Vec = Vec::new(); 492 | 493 | loop { 494 | match self.advance().clone().kind { 495 | TokenKind::Identifier(name_literal) => match self.advance().kind { 496 | TokenKind::Colon => { 497 | let token = self.advance().clone(); 498 | match token.kind { 499 | TokenKind::Identifier(type_literal) => { 500 | params.push(Param { 501 | name: name_literal.to_string(), 502 | typ: self.type_from_literal(&type_literal)?, 503 | }); 504 | } 505 | _ => { 506 | return Err(ParserError::SyntaxError { 507 | token: self.previous().clone(), 508 | backtrace: Backtrace::new(), 509 | }) 510 | } 511 | } 512 | } 513 | _ => { 514 | self.current = current; 515 | return self.func_call(); 516 | } 517 | }, 518 | TokenKind::Comma => {} 519 | TokenKind::RightParen => break, 520 | _ => { 521 | self.current = current; 522 | return self.func_call(); 523 | } 524 | } 525 | } 526 | 527 | match self.advance().kind { 528 | TokenKind::Colon => (), 529 | _ => { 530 | return Err(ParserError::SyntaxError { 531 | token: self.previous().clone(), 532 | backtrace: Backtrace::new(), 533 | }) 534 | } 535 | } 536 | 537 | let return_type = match &self.advance().kind { 538 | TokenKind::Identifier(type_literal) => match type_literal.as_str() { 539 | "number" => Type::Numeric, 540 | "vec" => Type::Vector, 541 | "void" => Type::Void, 542 | _ => { 543 | return Err(ParserError::SyntaxError { 544 | token: self.previous().clone(), 545 | backtrace: Backtrace::new(), 546 | }) 547 | } 548 | }, 549 | _ => { 550 | return Err(ParserError::SyntaxError { 551 | token: self.previous().clone(), 552 | backtrace: Backtrace::new(), 553 | }) 554 | } 555 | }; 556 | 557 | match self.advance().kind { 558 | TokenKind::Arrow => (), 559 | _ => { 560 | return Err(ParserError::SyntaxError { 561 | token: self.previous().clone(), 562 | backtrace: Backtrace::new(), 563 | }) 564 | } 565 | } 566 | 567 | match self.advance().kind { 568 | TokenKind::LCurly => (), 569 | _ => { 570 | return Err(ParserError::SyntaxError { 571 | token: self.previous().clone(), 572 | backtrace: Backtrace::new(), 573 | }) 574 | } 575 | } 576 | 577 | let mut body: Vec = Vec::new(); 578 | 579 | loop { 580 | match self.peek().kind { 581 | TokenKind::RCurly => { 582 | self.advance(); 583 | break; 584 | } 585 | _ => { 586 | body.push(self.expression()?); 587 | } 588 | } 589 | } 590 | Ok(self.node(Expression::FuncDecl(expression::FuncDecl { 591 | body, 592 | params, 593 | 594 | return_type, 595 | }))) 596 | } 597 | _ => self.func_call(), 598 | } 599 | } 600 | 601 | fn func_call(&mut self) -> Result { 602 | let mut expr = self.load()?; 603 | 604 | while let TokenKind::LeftParen = self.peek().kind { 605 | match expr.expression { 606 | Expression::Identifier { .. } => { 607 | self.advance(); 608 | let mut args: Vec = Vec::new(); 609 | 610 | loop { 611 | if let TokenKind::RightParen = self.peek().kind { 612 | self.advance(); 613 | break; 614 | } 615 | args.push(self.expression()?); 616 | if let TokenKind::RightParen = self.peek().kind { 617 | self.advance(); 618 | break; 619 | } 620 | consume!(self, TokenKind::Comma)?; 621 | } 622 | 623 | expr = self.node(Expression::FuncCall(expression::FuncCall { 624 | calee: Box::new(expr), 625 | args, 626 | })); 627 | } 628 | _ => { 629 | return Err(ParserError::SyntaxError { 630 | token: self.peek().clone(), 631 | backtrace: Backtrace::new(), 632 | }) 633 | } 634 | }; 635 | } 636 | Ok(expr) 637 | } 638 | 639 | fn load(&mut self) -> Result { 640 | match self.peek().kind { 641 | TokenKind::Load => { 642 | self.advance(); 643 | 644 | if !matches!(self.advance().kind, TokenKind::LeftParen) { 645 | return Err(ParserError::SyntaxError { 646 | token: self.previous().clone(), 647 | backtrace: Backtrace::new(), 648 | }); 649 | }; 650 | 651 | let name = if let TokenKind::String(s) = &self.advance().kind { 652 | s.to_string() 653 | } else { 654 | return Err(ParserError::SyntaxError { 655 | token: self.previous().clone(), 656 | backtrace: Backtrace::new(), 657 | }); 658 | }; 659 | 660 | if !matches!(self.advance().kind, TokenKind::RightParen) { 661 | return Err(ParserError::SyntaxError { 662 | token: self.previous().clone(), 663 | backtrace: Backtrace::new(), 664 | }); 665 | }; 666 | 667 | Ok(self.node(Expression::Load(name))) 668 | } 669 | _ => self.primary(), 670 | } 671 | } 672 | 673 | fn primary(&mut self) -> Result { 674 | let token = self.advance().clone(); 675 | match &token.kind { 676 | TokenKind::Numeric(val) => Ok(self.node(Expression::Numeric(*val))), 677 | TokenKind::LeftParen => { 678 | let expr = Expression::Grouping(expression::Grouping(Box::new(self.expression()?))); 679 | 680 | let token = self.advance(); 681 | 682 | match token.kind { 683 | TokenKind::RightParen => (), 684 | _ => { 685 | return Err(ParserError::SyntaxError { 686 | token: token.clone(), 687 | backtrace: Backtrace::new(), 688 | }) 689 | } 690 | }; 691 | 692 | Ok(self.node(expr)) 693 | } 694 | TokenKind::Identifier(literal) => { 695 | Ok(self.node(Expression::Identifier(literal.to_string()))) 696 | } 697 | TokenKind::String(literal) => Ok(self.node(Expression::String(literal.to_string()))), 698 | TokenKind::True => Ok(self.node(Expression::Bool(true))), 699 | TokenKind::False => Ok(self.node(Expression::Bool(false))), 700 | TokenKind::Break => Ok(self.node(Expression::Break)), 701 | _ => Err(ParserError::SyntaxError { 702 | token: token.clone(), 703 | backtrace: Backtrace::new(), 704 | }), 705 | } 706 | } 707 | 708 | fn previous(&self) -> &Token { 709 | &self.tokens[self.current - 1] 710 | } 711 | 712 | fn peek(&self) -> &Token { 713 | &self.tokens[self.current] 714 | } 715 | 716 | fn advance(&mut self) -> &Token { 717 | if !self.at_end() { 718 | self.current += 1; 719 | } 720 | self.previous() 721 | } 722 | 723 | fn type_from_literal(&mut self, type_literal: &str) -> Result { 724 | match type_literal { 725 | "void" => Ok(Type::Void), 726 | "string" => Ok(Type::String), 727 | "cstring" => Ok(Type::CString), 728 | "number" => Ok(Type::Numeric), 729 | "vec" => Ok(Type::Vector), 730 | "fun" => Ok(Type::Function), 731 | "ptr" => Ok(Type::Ptr), 732 | _ => Err(ParserError::SyntaxError { 733 | token: self.previous().clone(), 734 | backtrace: Backtrace::new(), 735 | }), 736 | } 737 | } 738 | 739 | fn at_end(&mut self) -> bool { 740 | matches!(self.peek().kind, TokenKind::Eof) 741 | } 742 | } 743 | 744 | #[cfg(test)] 745 | mod test { 746 | use super::*; 747 | 748 | macro_rules! token { 749 | ($expr: expr) => { 750 | Token { 751 | kind: $expr, 752 | span: Span::default(), 753 | } 754 | }; 755 | } 756 | 757 | macro_rules! assert_is_err { 758 | ($val: expr) => { 759 | assert!(matches!($val, Err(_))); 760 | }; 761 | } 762 | 763 | #[test] 764 | fn dont_allow_args_without_commas_in_between() { 765 | let mut parser = Parser::new(&[ 766 | token!(TokenKind::Identifier("test".to_string())), 767 | token!(TokenKind::LeftParen), 768 | token!(TokenKind::Identifier("a".to_string())), 769 | token!(TokenKind::Identifier("b".to_string())), 770 | token!(TokenKind::RightParen), 771 | ]); 772 | 773 | assert_is_err!(parser.parse()); 774 | } 775 | } 776 | --------------------------------------------------------------------------------