├── src ├── error │ ├── mod.rs │ └── kaori_error.rs ├── virtual_machine │ ├── mod.rs │ └── vm_context.rs ├── lexer │ ├── mod.rs │ ├── span.rs │ ├── token.rs │ ├── token_stream.rs │ ├── token_kind.rs │ └── lexer.rs ├── bytecode │ ├── mod.rs │ ├── function.rs │ ├── value.rs │ ├── op_code.rs │ ├── bytecode.rs │ └── emit_bytecode.rs ├── lib.rs ├── semantic │ ├── mod.rs │ ├── hir_ir.rs │ ├── hir_id.rs │ ├── hir_node.rs │ ├── symbol.rs │ ├── symbol_table.rs │ ├── hir_ty.rs │ ├── type.rs │ ├── hir_decl.rs │ ├── hir_stmt.rs │ ├── hir_expr.rs │ ├── type_checker.rs │ └── resolver.rs ├── cfg_ir │ ├── mod.rs │ ├── active_loops.rs │ ├── operand.rs │ ├── cfg_function.rs │ ├── graph_traversal.rs │ ├── cfg_constants.rs │ ├── basic_block.rs │ ├── jump_threading.rs │ ├── liveness_analysis.rs │ ├── instruction.rs │ └── build_cfgs.rs ├── syntax │ ├── mod.rs │ ├── ast_id.rs │ ├── unary_op.rs │ ├── ast_node.rs │ ├── assign_op.rs │ ├── binary_op.rs │ ├── parse_type.rs │ ├── ty.rs │ ├── decl.rs │ ├── stmt.rs │ ├── expr.rs │ ├── parse_declaration.rs │ ├── parser.rs │ ├── parse_statement.rs │ └── parse_expression.rs ├── main.rs └── program.rs ├── rust-toolchain.toml ├── .gitignore ├── test_suite ├── recursive_fib.kr ├── test.kr ├── mutual_recursion.kr ├── iterative_fib.kr └── primitives.kr ├── tests_bench ├── recursive_fib.lua ├── recursive_fib.py ├── iterative_fib.lua └── iterative_fib.py ├── Cargo.toml ├── benches └── vm_bench.rs ├── README.md ├── LICENSE ├── run_test_suite.py ├── benchmark.py └── Cargo.lock /src/error/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod kaori_error; 2 | -------------------------------------------------------------------------------- /src/virtual_machine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod vm; 2 | pub mod vm_context; 3 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-09-30" 3 | components = ["rustfmt", "clippy"] 4 | profile = "minimal" -------------------------------------------------------------------------------- /src/lexer/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::module_inception)] 2 | pub mod lexer; 3 | pub mod span; 4 | 5 | pub mod token; 6 | pub mod token_kind; 7 | pub mod token_stream; 8 | -------------------------------------------------------------------------------- /src/bytecode/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::module_inception)] 2 | pub mod bytecode; 3 | pub mod emit_bytecode; 4 | 5 | pub mod function; 6 | //pub mod instruction; 7 | pub mod op_code; 8 | pub mod value; 9 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(explicit_tail_calls)] 2 | #![allow(incomplete_features)] 3 | 4 | pub mod bytecode; 5 | pub mod cfg_ir; 6 | pub mod error; 7 | pub mod lexer; 8 | pub mod program; 9 | pub mod semantic; 10 | pub mod syntax; 11 | pub mod virtual_machine; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OS / IDE 3 | .DS_Store 4 | .git 5 | 6 | # Rust artifacts 7 | **/target 8 | # Ignore debug info files 9 | **/*.pdb 10 | # Ignore executables 11 | **/*.exe 12 | 13 | # Local files 14 | ./a.out 15 | .env 16 | 17 | 18 | # Added by cargo 19 | 20 | /target 21 | -------------------------------------------------------------------------------- /test_suite/recursive_fib.kr: -------------------------------------------------------------------------------- 1 | fun fib(n: number) -> number { 2 | if n < 2 { 3 | return n; 4 | } else { 5 | return fib(n - 1) + fib(n - 2); 6 | } 7 | 8 | return 0; 9 | } 10 | 11 | 12 | fun main() { 13 | fib(30); 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests_bench/recursive_fib.lua: -------------------------------------------------------------------------------- 1 | local function fib(n) 2 | if n < 2 then 3 | return n 4 | end 5 | return fib(n - 1) + fib(n - 2) 6 | end 7 | 8 | local start = os.clock() 9 | fib(30) 10 | 11 | local elapsed = (os.clock() - start) * 1000 12 | print(elapsed) 13 | -------------------------------------------------------------------------------- /tests_bench/recursive_fib.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def fib(n: int) -> int: 4 | if n < 2: 5 | return n 6 | return fib(n - 1) + fib(n - 2) 7 | 8 | 9 | start = time.perf_counter() 10 | fib(30) 11 | 12 | end = time.perf_counter() 13 | print((end - start) * 1000) 14 | -------------------------------------------------------------------------------- /src/semantic/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod hir_decl; 2 | pub mod hir_expr; 3 | pub mod hir_node; 4 | pub mod hir_stmt; 5 | pub mod hir_ty; 6 | 7 | pub mod hir_id; 8 | pub mod hir_ir; 9 | pub mod resolver; 10 | pub mod symbol; 11 | pub mod symbol_table; 12 | pub mod r#type; 13 | pub mod type_checker; 14 | -------------------------------------------------------------------------------- /test_suite/test.kr: -------------------------------------------------------------------------------- 1 | fun main() { 2 | if 1 > 2 { 3 | print(1); 4 | } else if 2 > 3 { 5 | print(2); 6 | print(3); 7 | print(4); 8 | } else if 3 < 2 { 9 | print(5); 10 | } else { 11 | print(6); 12 | } 13 | 14 | return; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /test_suite/mutual_recursion.kr: -------------------------------------------------------------------------------- 1 | fun foo(n: number) { 2 | print(n); 3 | 4 | if n > 0 { 5 | bar(n - 1); 6 | } 7 | 8 | } 9 | 10 | fun bar(n: number) { 11 | print(n); 12 | 13 | if n > 0 { 14 | foo(n - 1); 15 | } 16 | 17 | } 18 | 19 | 20 | def main() { 21 | print(foo(10)); 22 | } -------------------------------------------------------------------------------- /src/cfg_ir/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_block; 2 | pub mod build_cfgs; 3 | #[allow(clippy::module_inception)] 4 | pub mod cfg_function; 5 | 6 | pub mod graph_traversal; 7 | pub mod instruction; 8 | //pub mod liveness_analysis; 9 | pub mod active_loops; 10 | pub mod cfg_constants; 11 | 12 | pub mod jump_threading; 13 | pub mod operand; 14 | -------------------------------------------------------------------------------- /src/lexer/span.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, Default)] 2 | pub struct Span { 3 | pub start: usize, 4 | pub end: usize, 5 | } 6 | 7 | impl Span { 8 | pub fn merge(left: Span, right: Span) -> Span { 9 | Span { 10 | start: left.start, 11 | end: right.end, 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/syntax/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod expr; 2 | pub mod parser; 3 | 4 | pub mod binary_op; 5 | 6 | pub mod assign_op; 7 | pub mod ast_id; 8 | pub mod ast_node; 9 | pub mod decl; 10 | pub mod parse_declaration; 11 | pub mod parse_expression; 12 | pub mod parse_statement; 13 | pub mod parse_type; 14 | pub mod stmt; 15 | pub mod ty; 16 | pub mod unary_op; 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kaori" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | ariadne = "0.5.1" 8 | ordered-float = "5.1.0" 9 | criterion = "0.5" 10 | mem = "0.5.0" 11 | 12 | 13 | [profile.release] 14 | debug = true 15 | 16 | [[bench]] 17 | name = "vm_bench" 18 | path = "benches/vm_bench.rs" 19 | harness = false -------------------------------------------------------------------------------- /tests_bench/iterative_fib.lua: -------------------------------------------------------------------------------- 1 | local iterations = 1000000 2 | local max_fib = 30 3 | 4 | local start = os.clock() 5 | 6 | for i = 1, iterations do 7 | local a, b = 0, 1 8 | for j = 1, max_fib do 9 | local temp = a + b 10 | a = b 11 | b = temp 12 | end 13 | end 14 | 15 | local elapsed = (os.clock() - start) * 1000 16 | print(elapsed) -------------------------------------------------------------------------------- /tests_bench/iterative_fib.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | iterations = 1_000_000 4 | max_fib = 30 5 | 6 | start = time.perf_counter() 7 | 8 | for i in range(iterations): 9 | a = 0 10 | b = 1 11 | for j in range(max_fib): 12 | temp = a + b 13 | a = b 14 | b = temp 15 | 16 | end = time.perf_counter() 17 | print((end - start) * 1000) 18 | -------------------------------------------------------------------------------- /src/semantic/hir_ir.rs: -------------------------------------------------------------------------------- 1 | use super::{hir_decl::HirDecl, r#type::Types}; 2 | 3 | pub struct HirIr { 4 | pub declarations: Vec, 5 | pub types: Types, 6 | } 7 | 8 | impl HirIr { 9 | pub fn new(declarations: Vec, types: Types) -> Self { 10 | Self { 11 | declarations, 12 | types, 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/syntax/ast_id.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 4 | pub struct AstId(usize); 5 | 6 | impl Default for AstId { 7 | fn default() -> Self { 8 | static COUNTER: AtomicUsize = AtomicUsize::new(0); 9 | Self(COUNTER.fetch_add(1, Ordering::Relaxed)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/semantic/hir_id.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 4 | pub struct HirId(usize); 5 | 6 | impl Default for HirId { 7 | fn default() -> Self { 8 | static COUNTER: AtomicUsize = AtomicUsize::new(0); 9 | Self(COUNTER.fetch_add(1, Ordering::Relaxed)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test_suite/iterative_fib.kr: -------------------------------------------------------------------------------- 1 | fun main() { 2 | $iterations = 1000000; 3 | $max_fib = 30; 4 | 5 | for $i = 0; i < iterations; i += 1 { 6 | $a = 0; 7 | $b = 1; 8 | 9 | for $j = 0; j < max_fib; j += 1 { 10 | $temp = a + b; 11 | a = b; 12 | b = temp; 13 | } 14 | 15 | } 16 | } 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/lexer/token.rs: -------------------------------------------------------------------------------- 1 | use super::{span::Span, token_kind::TokenKind}; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct Token { 5 | pub kind: TokenKind, 6 | pub span: Span, 7 | } 8 | 9 | impl Token { 10 | pub fn new(kind: TokenKind, start: usize, end: usize) -> Token { 11 | Token { 12 | kind, 13 | span: Span { start, end }, 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/bytecode/function.rs: -------------------------------------------------------------------------------- 1 | use super::value::Value; 2 | 3 | pub struct Function { 4 | pub ip: *const u16, 5 | 6 | pub frame_size: u8, 7 | pub constants: Vec, 8 | } 9 | 10 | impl Function { 11 | pub fn new(ip: *const u16, frame_size: u8, constants: Vec) -> Self { 12 | Self { 13 | ip, 14 | frame_size, 15 | constants, 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/syntax/unary_op.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub struct UnaryOp { 5 | pub span: Span, 6 | pub kind: UnaryOpKind, 7 | } 8 | 9 | #[derive(Debug, Clone, Copy)] 10 | pub enum UnaryOpKind { 11 | Negate, 12 | Not, 13 | } 14 | 15 | impl UnaryOp { 16 | pub fn new(kind: UnaryOpKind, span: Span) -> UnaryOp { 17 | UnaryOp { span, kind } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/syntax/ast_node.rs: -------------------------------------------------------------------------------- 1 | use super::{decl::Decl, stmt::Stmt}; 2 | 3 | #[derive(Debug)] 4 | pub enum AstNode { 5 | Declaration(Decl), 6 | Statement(Stmt), 7 | } 8 | 9 | impl From for AstNode { 10 | fn from(node: Decl) -> Self { 11 | Self::Declaration(node) 12 | } 13 | } 14 | 15 | impl From for AstNode { 16 | fn from(node: Stmt) -> Self { 17 | Self::Statement(node) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/semantic/hir_node.rs: -------------------------------------------------------------------------------- 1 | use super::{hir_decl::HirDecl, hir_stmt::HirStmt}; 2 | 3 | #[derive(Debug)] 4 | pub enum HirNode { 5 | Declaration(HirDecl), 6 | Statement(HirStmt), 7 | } 8 | 9 | impl From for HirNode { 10 | fn from(node: HirDecl) -> Self { 11 | Self::Declaration(node) 12 | } 13 | } 14 | 15 | impl From for HirNode { 16 | fn from(node: HirStmt) -> Self { 17 | Self::Statement(node) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/syntax/assign_op.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub struct AssignOp { 5 | pub span: Span, 6 | pub kind: AssignOpKind, 7 | } 8 | 9 | #[derive(Debug, Clone, Copy)] 10 | pub enum AssignOpKind { 11 | Assign, 12 | AddAssign, 13 | SubtractAssign, 14 | MultiplyAssign, 15 | DivideAssign, 16 | ModuloAssign, 17 | } 18 | 19 | impl AssignOp { 20 | pub fn new(kind: AssignOpKind, span: Span) -> AssignOp { 21 | AssignOp { span, kind } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/syntax/binary_op.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub struct BinaryOp { 5 | pub span: Span, 6 | pub kind: BinaryOpKind, 7 | } 8 | 9 | #[derive(Debug, Clone, Copy)] 10 | pub enum BinaryOpKind { 11 | Add, 12 | Subtract, 13 | Multiply, 14 | Divide, 15 | Modulo, 16 | And, 17 | Or, 18 | Equal, 19 | NotEqual, 20 | Greater, 21 | GreaterEqual, 22 | Less, 23 | LessEqual, 24 | } 25 | 26 | impl BinaryOp { 27 | pub fn new(kind: BinaryOpKind, span: Span) -> BinaryOp { 28 | BinaryOp { span, kind } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/cfg_ir/active_loops.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct LoopLabel { 3 | pub increment_bb_index: usize, 4 | pub terminator_bb_index: usize, 5 | } 6 | 7 | #[derive(Default)] 8 | pub struct ActiveLoops { 9 | active_loops: Vec, 10 | } 11 | 12 | impl ActiveLoops { 13 | pub fn push(&mut self, increment_bb_index: usize, terminator_bb_index: usize) { 14 | let active_loop = LoopLabel { 15 | increment_bb_index, 16 | terminator_bb_index, 17 | }; 18 | 19 | self.active_loops.push(active_loop); 20 | } 21 | 22 | pub fn pop(&mut self) { 23 | self.active_loops.pop(); 24 | } 25 | 26 | pub fn top(&self) -> &LoopLabel { 27 | self.active_loops.last().unwrap() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /benches/vm_bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::{Criterion, black_box, criterion_group, criterion_main}; 2 | use kaori::program::run_program; 3 | use std::fs; 4 | 5 | fn bench_execute(c: &mut Criterion) { 6 | c.bench_function("run_code", |b| { 7 | let source_path = "test_suite/recursive_fib.kr"; 8 | let source = match fs::read_to_string(source_path) { 9 | Ok(source) => source, 10 | Err(error) => { 11 | eprintln!("Error reading source file: {}", error); 12 | return; 13 | } 14 | }; 15 | 16 | b.iter(|| { 17 | // Measure only the program execution 18 | 19 | black_box(run_program(&source)); 20 | }); 21 | }); 22 | } 23 | 24 | criterion_group!(benches, bench_execute); 25 | criterion_main!(benches); 26 | -------------------------------------------------------------------------------- /src/bytecode/value.rs: -------------------------------------------------------------------------------- 1 | #[derive(Default, Clone, Copy, Debug)] 2 | pub struct Value(pub u64); 3 | 4 | impl Value { 5 | #[inline(always)] 6 | pub fn number(value: f64) -> Self { 7 | Self(value.to_bits()) 8 | } 9 | 10 | #[inline(always)] 11 | pub fn boolean(value: bool) -> Self { 12 | Self(value as u64) 13 | } 14 | 15 | #[inline(always)] 16 | pub fn function(value: usize) -> Self { 17 | Self(value as u64) 18 | } 19 | 20 | #[inline(always)] 21 | pub fn as_number(&self) -> f64 { 22 | f64::from_bits(self.0) 23 | } 24 | 25 | #[inline(always)] 26 | pub fn as_boolean(&self) -> bool { 27 | self.0 != 0 28 | } 29 | 30 | #[inline(always)] 31 | pub fn as_function(&self) -> usize { 32 | self.0 as usize 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/cfg_ir/operand.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display, Formatter}; 2 | 3 | #[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] 4 | pub enum Operand { 5 | Variable(usize), 6 | Constant(usize), 7 | None, 8 | } 9 | 10 | impl Operand { 11 | pub fn to_i16(self) -> i16 { 12 | match self { 13 | Self::Constant(value) => -((value + 1) as i16), 14 | Self::Variable(value) => value as i16, 15 | Self::None => unreachable!("Tried to convert invalid op"), 16 | } 17 | } 18 | } 19 | 20 | impl Display for Operand { 21 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 22 | match self { 23 | Self::Variable(id) => write!(f, "r{}", id), 24 | Self::Constant(id) => write!(f, "k{}", id), 25 | Self::None => Ok(()), 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/semantic/symbol.rs: -------------------------------------------------------------------------------- 1 | use super::hir_id::HirId; 2 | 3 | #[derive(Clone)] 4 | pub struct Symbol { 5 | pub id: HirId, 6 | pub name: String, 7 | pub kind: SymbolKind, 8 | } 9 | 10 | #[derive(Clone)] 11 | pub enum SymbolKind { 12 | Variable, 13 | Function, 14 | Struct, 15 | } 16 | 17 | impl Symbol { 18 | pub fn variable(id: HirId, name: String) -> Symbol { 19 | Symbol { 20 | id, 21 | name, 22 | kind: SymbolKind::Variable, 23 | } 24 | } 25 | 26 | pub fn function(id: HirId, name: String) -> Symbol { 27 | Symbol { 28 | id, 29 | name, 30 | kind: SymbolKind::Function, 31 | } 32 | } 33 | 34 | pub fn struct_(id: HirId, name: String) -> Symbol { 35 | Symbol { 36 | id, 37 | name, 38 | kind: SymbolKind::Struct, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kaori - Programming Language 2 | 3 | **Kaori** is a statically typed programming language, now implemented fully in **Rust**, previously implemented in Java 17 4 | 5 | See the full details: [Kaori Documentation](https://kaori-lang.github.io/docs/) 6 | 7 | ## Getting Started 8 | 9 | ### Prerequisites 10 | 11 | - [Rust](https://www.rust-lang.org/) (>= 1.70) 12 | - Cargo (comes bundled with Rust) 13 | 14 | ### Installation 15 | 16 | 1. Clone the repository: 17 | 18 | ```bash 19 | git clone 20 | cd kaori 21 | ``` 22 | 23 | 2. Build the project: 24 | 25 | ```bash 26 | cargo build --release 27 | ``` 28 | 29 | 3. (Optional) Install globally: 30 | 31 | ```bash 32 | cargo install --path . 33 | ``` 34 | 35 | ### Building for Production 36 | 37 | Build an optimized release binary: 38 | 39 | ```bash 40 | cargo build --release 41 | ``` 42 | 43 | ## License 44 | 45 | Kaori is released under the **MIT License**. 46 | See the [`LICENSE`](LICENSE) file for more details 47 | -------------------------------------------------------------------------------- /src/cfg_ir/cfg_function.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display, Formatter}; 2 | 3 | use crate::cfg_ir::graph_traversal::reversed_postorder; 4 | 5 | use super::{basic_block::BasicBlock, cfg_constants::CfgConstant}; 6 | 7 | #[derive(Default)] 8 | pub struct CfgFunction { 9 | pub basic_blocks: Vec, 10 | pub constants: Vec, 11 | pub allocated_variables: usize, 12 | } 13 | 14 | impl CfgFunction { 15 | pub fn new( 16 | basic_blocks: Vec, 17 | constants: Vec, 18 | allocated_variables: usize, 19 | ) -> Self { 20 | Self { 21 | basic_blocks, 22 | constants, 23 | allocated_variables, 24 | } 25 | } 26 | } 27 | 28 | impl Display for CfgFunction { 29 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 30 | writeln!(f, "Control Flow Graph:")?; 31 | 32 | writeln!(f, "CONSTANTS: {:?}", self.constants)?; 33 | for index in reversed_postorder(&self.basic_blocks) { 34 | write!(f, "{}", &self.basic_blocks[index])?; 35 | } 36 | 37 | Ok(()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Washington 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/error/kaori_error.rs: -------------------------------------------------------------------------------- 1 | use ariadne::{Color, Label, Report, ReportKind, Source}; 2 | 3 | use crate::lexer::span::Span; 4 | 5 | #[macro_export] 6 | macro_rules! kaori_error { 7 | ($span:expr, $msg:literal $(, $arg:expr)* $(,)?) => { 8 | KaoriError::new( 9 | $span, 10 | format!($msg $(, $arg)*), 11 | ) 12 | }; 13 | } 14 | 15 | pub struct KaoriError { 16 | pub span: Span, 17 | pub message: String, 18 | } 19 | 20 | impl KaoriError { 21 | pub fn new(span: Span, message: String) -> Self { 22 | Self { span, message } 23 | } 24 | 25 | pub fn report(&self, source: &str) { 26 | let file_id = "source"; 27 | 28 | Report::build(ReportKind::Error, (file_id, self.span.start..self.span.end)) 29 | .with_message(&self.message) 30 | .with_label( 31 | Label::new((file_id, self.span.start..self.span.end)) 32 | .with_message(&self.message) 33 | .with_color(Color::Red), 34 | ) 35 | .finish() 36 | .print((file_id, Source::from(source))) 37 | .unwrap(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/cfg_ir/graph_traversal.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use super::basic_block::{BasicBlock, Terminator}; 4 | 5 | pub fn reversed_postorder(basic_blocks: &[BasicBlock]) -> Vec { 6 | let mut visited = HashSet::new(); 7 | let mut postorder = Vec::new(); 8 | 9 | traverse(0, basic_blocks, &mut visited, &mut postorder); 10 | 11 | postorder.reverse(); 12 | 13 | postorder 14 | } 15 | 16 | fn traverse( 17 | index: usize, 18 | basic_blocks: &[BasicBlock], 19 | visited: &mut HashSet, 20 | postorder: &mut Vec, 21 | ) { 22 | if visited.contains(&index) { 23 | return; 24 | } 25 | 26 | visited.insert(index); 27 | 28 | let bb = &basic_blocks[index]; 29 | 30 | match bb.terminator { 31 | Terminator::Branch { 32 | r#true, r#false, .. 33 | } => { 34 | traverse(r#false, basic_blocks, visited, postorder); 35 | traverse(r#true, basic_blocks, visited, postorder); 36 | } 37 | Terminator::Goto(target) => { 38 | traverse(target, basic_blocks, visited, postorder); 39 | } 40 | _ => {} 41 | }; 42 | 43 | postorder.push(index); 44 | } 45 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_imports)] 2 | use std::{env::args, process::ExitCode}; 3 | #[allow(unused_imports)] 4 | use std::{fs, time::Instant}; 5 | 6 | use kaori::program::run_program; 7 | 8 | fn main() -> ExitCode { 9 | let source_to_run = args().nth(1); 10 | 11 | if source_to_run.is_none() { 12 | eprintln!("Error: No path was found for the program's source!"); 13 | return ExitCode::FAILURE; 14 | } 15 | 16 | let source_path = source_to_run.unwrap(); 17 | 18 | if let Ok(source) = fs::read_to_string(source_path) { 19 | if let Err(err) = run_program(&source) { 20 | err.report(&source); 21 | return ExitCode::FAILURE; 22 | } 23 | 24 | return ExitCode::SUCCESS; 25 | } 26 | 27 | eprintln!("Error: Could not read the file by the given path."); 28 | ExitCode::FAILURE 29 | } 30 | 31 | /* 32 | fn main() { 33 | let source_to_run = "test_suite/iterative_fib.kr"; 34 | 35 | match fs::read_to_string(source_to_run) { 36 | Ok(source) => { 37 | if let Err(error) = run_program(source.to_owned()) { 38 | error.report(&source); 39 | } 40 | } 41 | Err(_) => eprintln!("Error: Could not read the file by the given path."), 42 | }; 43 | } 44 | */ 45 | -------------------------------------------------------------------------------- /src/syntax/parse_type.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::kaori_error::KaoriError, kaori_error, lexer::token_kind::TokenKind}; 2 | 3 | use super::{parser::Parser, ty::Ty}; 4 | 5 | impl<'a> Parser<'a> { 6 | pub fn parse_type(&mut self) -> Result { 7 | let span = self.token_stream.span(); 8 | let kind = self.token_stream.token_kind(); 9 | 10 | Ok(match kind { 11 | TokenKind::Identifier => self.parse_identifier_type()?, 12 | TokenKind::Number => { 13 | self.token_stream.advance(); 14 | Ty::number(span) 15 | } 16 | TokenKind::Bool => { 17 | self.token_stream.advance(); 18 | Ty::bool(span) 19 | } 20 | _ => { 21 | return Err(kaori_error!( 22 | span, 23 | "expected a valid type, but found: {}", 24 | kind, 25 | )); 26 | } 27 | }) 28 | } 29 | 30 | fn parse_identifier_type(&mut self) -> Result { 31 | let span = self.token_stream.span(); 32 | let name = self.token_stream.lexeme().to_owned(); 33 | 34 | self.token_stream.consume(TokenKind::Identifier)?; 35 | 36 | let identifier = Ty::identifier(name, span); 37 | 38 | Ok(identifier) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /run_test_suite.py: -------------------------------------------------------------------------------- 1 | """ 2 | run_test_suite.py\n 3 | Test runs the entire interpreter on all sample Kaori programs in `test_suite`.\n 4 | Added by: DrkWithT\n 5 | v0.0.1: Added numbers & fibonacci tests.\n 6 | """ 7 | 8 | from subprocess import run 9 | from pathlib import Path 10 | 11 | ALL_TEST_SOURCES = [ 12 | 'primitives.kr', 13 | 'recursive_fib.kr', 14 | ] 15 | 16 | def try_kaori_tests(test_sources: list[str]) -> bool: 17 | test_suite_dir: Path = Path('test_suite/') 18 | had_failed_test = False 19 | 20 | for test_file in test_sources: 21 | next_test_path = test_suite_dir.joinpath(test_file) 22 | 23 | print(f'Testing source: \'{next_test_path}\'\n') 24 | 25 | test_result = run(['cargo', 'run', f'{next_test_path}'], capture_output=False) 26 | 27 | if test_result.returncode != 0: 28 | print(f'\x1b[1;31mTest failed for source at {next_test_path}\x1b[0m') 29 | had_failed_test = True 30 | else: 31 | print(f'\x1b[1;32mTest passed for source at {next_test_path}\x1b[0m') 32 | 33 | return not had_failed_test 34 | 35 | if __name__ == '__main__': 36 | if try_kaori_tests(ALL_TEST_SOURCES): 37 | print(f'\x1b[1;32mAll tests passed :)\x1b[0m') 38 | exit(0) 39 | else: 40 | print(f'\x1b[1;31mSome tests failed, please check above logs.\x1b[0m') 41 | exit(1) 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /test_suite/primitives.kr: -------------------------------------------------------------------------------- 1 | /* test primitive vars. & operations */ 2 | 3 | fun test_numbers() -> bool { 4 | $a = 10.0; 5 | $b = -2.0; 6 | 7 | // test arithmetic 8 | $sum_a_b = a + b; 9 | $diff_a_b = a - b; 10 | $prod_a_b = a * b; 11 | $quot_a_b = a / b; 12 | 13 | if sum_a_b != 8.0 { 14 | return false; 15 | } 16 | 17 | if diff_a_b != 12.0 { 18 | return false; 19 | } 20 | 21 | if prod_a_b != -20.0 { 22 | return false; 23 | } 24 | 25 | if quot_a_b != -5.0 { 26 | return false; 27 | } 28 | 29 | return true; 30 | } 31 | 32 | 33 | fun test_newton() -> bool { 34 | /* approximate magnitude of the 2-D vector of using Newton's Method */ 35 | $x = 8.0; 36 | $y = 4.0; 37 | 38 | $dist_squ = x * x + y * y; 39 | $guess = 7.25; 40 | 41 | for $iter = 0; iter < 4; iter += 1 { 42 | guess = 0.5 * ( guess + dist_squ / guess ); 43 | } 44 | 45 | /* max allowed error in magnitude approximation */ 46 | $max_elipson = 0.01; 47 | 48 | /* expected answer: D_target = sqrt(80) */ 49 | $target_answer = 8.944; 50 | 51 | print(guess); 52 | print(target_answer); 53 | 54 | return (target_answer - guess) < max_elipson; 55 | } 56 | 57 | fun main() { 58 | /* test arithmetic: 1 is an error */ 59 | if !test_numbers() { 60 | print(1); 61 | } 62 | 63 | /* test complex arithmetic: 1 is an error */ 64 | if !test_newton() { 65 | print(1); 66 | } 67 | 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/cfg_ir/cfg_constants.rs: -------------------------------------------------------------------------------- 1 | use ordered_float::OrderedFloat; 2 | use std::collections::HashMap; 3 | 4 | use super::operand::Operand; 5 | 6 | #[derive(Default)] 7 | pub struct CfgConstants { 8 | pub constants: Vec, 9 | pub constants_index: HashMap, 10 | } 11 | 12 | impl CfgConstants { 13 | fn push_constant(&mut self, constant: CfgConstant) -> Operand { 14 | if let Some(index) = self.constants_index.get(&constant) { 15 | Operand::Constant(*index) 16 | } else { 17 | let index = self.constants_index.len(); 18 | 19 | self.constants_index.insert(constant.to_owned(), index); 20 | 21 | self.constants.push(constant); 22 | 23 | Operand::Constant(index) 24 | } 25 | } 26 | 27 | pub fn push_function(&mut self, value: usize) -> Operand { 28 | self.push_constant(CfgConstant::Function(value)) 29 | } 30 | 31 | pub fn push_string(&mut self, value: String) -> Operand { 32 | self.push_constant(CfgConstant::String(value)) 33 | } 34 | 35 | pub fn push_number(&mut self, value: f64) -> Operand { 36 | self.push_constant(CfgConstant::Number(OrderedFloat(value))) 37 | } 38 | 39 | pub fn push_boolean(&mut self, value: bool) -> Operand { 40 | self.push_constant(CfgConstant::Boolean(value)) 41 | } 42 | } 43 | 44 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 45 | pub enum CfgConstant { 46 | String(String), 47 | Number(OrderedFloat), 48 | Boolean(bool), 49 | Function(usize), 50 | } 51 | -------------------------------------------------------------------------------- /src/syntax/ty.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | use super::ast_id::AstId; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Ty { 7 | pub id: AstId, 8 | pub span: Span, 9 | pub kind: TyKind, 10 | } 11 | 12 | #[derive(Debug, Clone)] 13 | pub enum TyKind { 14 | Function { 15 | parameters: Vec, 16 | return_ty: Box, 17 | }, 18 | Struct { 19 | fields: Vec, 20 | }, 21 | Identifier(String), 22 | Number, 23 | Bool, 24 | } 25 | 26 | impl Ty { 27 | pub fn function(parameters: Vec, return_ty: Ty, span: Span) -> Ty { 28 | Ty { 29 | id: AstId::default(), 30 | span, 31 | kind: TyKind::Function { 32 | parameters, 33 | return_ty: Box::new(return_ty), 34 | }, 35 | } 36 | } 37 | 38 | pub fn struct_(fields: Vec, span: Span) -> Ty { 39 | Ty { 40 | id: AstId::default(), 41 | span, 42 | kind: TyKind::Struct { fields }, 43 | } 44 | } 45 | 46 | pub fn number(span: Span) -> Ty { 47 | Ty { 48 | id: AstId::default(), 49 | span, 50 | kind: TyKind::Number, 51 | } 52 | } 53 | 54 | pub fn bool(span: Span) -> Ty { 55 | Ty { 56 | id: AstId::default(), 57 | span, 58 | kind: TyKind::Bool, 59 | } 60 | } 61 | 62 | pub fn identifier(name: String, span: Span) -> Ty { 63 | Ty { 64 | id: AstId::default(), 65 | span, 66 | kind: TyKind::Identifier(name), 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/semantic/symbol_table.rs: -------------------------------------------------------------------------------- 1 | use super::{hir_id::HirId, symbol::Symbol}; 2 | 3 | pub struct SymbolTable { 4 | pub symbols: Vec, 5 | pub scopes_ptr: Vec, 6 | } 7 | 8 | impl Default for SymbolTable { 9 | fn default() -> Self { 10 | Self { 11 | symbols: Vec::new(), 12 | scopes_ptr: vec![0], 13 | } 14 | } 15 | } 16 | 17 | impl SymbolTable { 18 | pub fn enter_scope(&mut self) { 19 | let ptr = self.symbols.len(); 20 | 21 | self.scopes_ptr.push(ptr); 22 | } 23 | 24 | pub fn exit_scope(&mut self) { 25 | let ptr = self.scopes_ptr.pop().unwrap(); 26 | 27 | while self.symbols.len() > ptr { 28 | self.symbols.pop(); 29 | } 30 | } 31 | 32 | pub fn declare_variable(&mut self, id: HirId, name: String) { 33 | let symbol = Symbol::variable(id, name); 34 | 35 | self.symbols.push(symbol); 36 | } 37 | 38 | pub fn declare_function(&mut self, id: HirId, name: String) { 39 | let symbol = Symbol::function(id, name); 40 | 41 | self.symbols.push(symbol); 42 | } 43 | 44 | pub fn declare_struct(&mut self, id: HirId, name: String) { 45 | let symbol = Symbol::struct_(id, name); 46 | 47 | self.symbols.push(symbol); 48 | } 49 | 50 | pub fn search_current_scope(&self, name: &str) -> Option<&Symbol> { 51 | let ptr = *self.scopes_ptr.last().unwrap(); 52 | 53 | self.symbols[ptr..] 54 | .iter() 55 | .find(|symbol| symbol.name == name) 56 | } 57 | 58 | pub fn search(&self, name: &str) -> Option<&Symbol> { 59 | self.symbols.iter().rev().find(|symbol| symbol.name == name) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/bytecode/op_code.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 2 | #[repr(u16)] 3 | pub enum Opcode { 4 | AddRR = 0, 5 | AddRK = 1, 6 | AddKR = 2, 7 | AddKK = 3, 8 | 9 | SubtractRR = 4, 10 | SubtractRK = 5, 11 | SubtractKR = 6, 12 | SubtractKK = 7, 13 | 14 | MultiplyRR = 8, 15 | MultiplyRK = 9, 16 | MultiplyKR = 10, 17 | MultiplyKK = 11, 18 | 19 | DivideRR = 12, 20 | DivideRK = 13, 21 | DivideKR = 14, 22 | DivideKK = 15, 23 | 24 | ModuloRR = 16, 25 | ModuloRK = 17, 26 | ModuloKR = 18, 27 | ModuloKK = 19, 28 | 29 | EqualRR = 20, 30 | EqualRK = 21, 31 | EqualKR = 22, 32 | EqualKK = 23, 33 | 34 | NotEqualRR = 24, 35 | NotEqualRK = 25, 36 | NotEqualKR = 26, 37 | NotEqualKK = 27, 38 | 39 | GreaterRR = 28, 40 | GreaterRK = 29, 41 | GreaterKR = 30, 42 | GreaterKK = 31, 43 | 44 | GreaterEqualRR = 32, 45 | GreaterEqualRK = 33, 46 | GreaterEqualKR = 34, 47 | GreaterEqualKK = 35, 48 | 49 | LessRR = 36, 50 | LessRK = 37, 51 | LessKR = 38, 52 | LessKK = 39, 53 | 54 | LessEqualRR = 40, 55 | LessEqualRK = 41, 56 | LessEqualKR = 42, 57 | LessEqualKK = 43, 58 | 59 | NegateR = 44, 60 | NegateK = 45, 61 | NotR = 46, 62 | NotK = 47, 63 | 64 | MoveR = 48, 65 | MoveK = 49, 66 | 67 | CallR = 50, 68 | CallK = 51, 69 | ReturnR = 52, 70 | ReturnK = 53, 71 | ReturnVoid = 54, 72 | 73 | Jump = 55, 74 | JumpIfTrueR = 56, 75 | JumpIfTrueK = 57, 76 | JumpIfFalseR = 58, 77 | JumpIfFalseK = 59, 78 | 79 | PrintR = 60, 80 | PrintK = 61, 81 | 82 | Halt = 62, 83 | } 84 | 85 | impl From for Opcode { 86 | fn from(value: u16) -> Self { 87 | unsafe { std::mem::transmute(value) } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/semantic/hir_ty.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | use super::hir_id::HirId; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct HirTy { 7 | pub id: HirId, 8 | pub span: Span, 9 | pub kind: HirTyKind, 10 | } 11 | 12 | #[derive(Debug, Clone, PartialEq, Eq)] 13 | pub enum HirTyKind { 14 | Function { 15 | parameters: Vec, 16 | return_ty: Box, 17 | }, 18 | Struct { 19 | fields: Vec, 20 | }, 21 | TypeRef(HirId), 22 | Number, 23 | Bool, 24 | } 25 | 26 | impl PartialEq for HirTy { 27 | fn eq(&self, other: &Self) -> bool { 28 | self.kind == other.kind 29 | } 30 | } 31 | 32 | impl Eq for HirTy {} 33 | 34 | impl HirTy { 35 | pub fn function(parameters: Vec, return_ty: HirTy, span: Span) -> HirTy { 36 | HirTy { 37 | id: HirId::default(), 38 | span, 39 | kind: HirTyKind::Function { 40 | parameters, 41 | return_ty: Box::new(return_ty), 42 | }, 43 | } 44 | } 45 | 46 | pub fn struct_(fields: Vec, span: Span) -> HirTy { 47 | HirTy { 48 | id: HirId::default(), 49 | span, 50 | kind: HirTyKind::Struct { fields }, 51 | } 52 | } 53 | 54 | pub fn number(span: Span) -> HirTy { 55 | HirTy { 56 | id: HirId::default(), 57 | span, 58 | kind: HirTyKind::Number, 59 | } 60 | } 61 | 62 | pub fn bool(span: Span) -> HirTy { 63 | HirTy { 64 | id: HirId::default(), 65 | span, 66 | kind: HirTyKind::Bool, 67 | } 68 | } 69 | 70 | pub fn type_ref(id: HirId, span: Span) -> HirTy { 71 | HirTy { 72 | id, 73 | span, 74 | kind: HirTyKind::TypeRef(id), 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/cfg_ir/basic_block.rs: -------------------------------------------------------------------------------- 1 | use super::{instruction::Instruction, operand::Operand}; 2 | use core::fmt; 3 | use std::fmt::{Display, Formatter}; 4 | 5 | #[derive(Debug)] 6 | pub struct BasicBlock { 7 | pub index: usize, 8 | pub instructions: Vec, 9 | pub terminator: Terminator, 10 | } 11 | 12 | impl BasicBlock { 13 | pub fn new(index: usize) -> Self { 14 | Self { 15 | index, 16 | instructions: Vec::new(), 17 | terminator: Terminator::None, 18 | } 19 | } 20 | } 21 | 22 | #[derive(Debug, Clone, Copy)] 23 | pub enum Terminator { 24 | Branch { 25 | src: Operand, 26 | r#true: usize, 27 | r#false: usize, 28 | }, 29 | Goto(usize), 30 | Return { 31 | src: Option, 32 | }, 33 | None, 34 | } 35 | 36 | impl Display for Terminator { 37 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 38 | match self { 39 | Terminator::Branch { 40 | src, 41 | r#true, 42 | r#false, 43 | } => { 44 | write!(f, "{}: true -> BB{}; false -> BB{}", src, r#true, r#false) 45 | } 46 | Terminator::Goto(target) => write!(f, "goto BB{}", target), 47 | Terminator::Return { src } => { 48 | if let Some(operand) = src { 49 | write!(f, "return {}", operand) 50 | } else { 51 | write!(f, "return VOID") 52 | } 53 | } 54 | Terminator::None => write!(f, ""), 55 | } 56 | } 57 | } 58 | 59 | impl Display for BasicBlock { 60 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 61 | writeln!(f, "BB{}:", self.index)?; 62 | for instruction in &self.instructions { 63 | writeln!(f, " {}", instruction)?; 64 | } 65 | writeln!(f, " {}", self.terminator) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/lexer/token_stream.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::kaori_error::KaoriError, kaori_error}; 2 | 3 | use super::{span::Span, token::Token, token_kind::TokenKind}; 4 | 5 | #[derive(Clone)] 6 | pub struct TokenStream<'a> { 7 | source: &'a str, 8 | tokens: Vec, 9 | index: usize, 10 | } 11 | 12 | impl<'a> TokenStream<'a> { 13 | pub fn new(source: &'a str, tokens: Vec) -> Self { 14 | Self { 15 | source, 16 | tokens, 17 | index: 0, 18 | } 19 | } 20 | 21 | pub fn at_end(&mut self) -> bool { 22 | self.token_kind() == TokenKind::EndOfFile 23 | } 24 | 25 | pub fn advance(&mut self) { 26 | self.index += 1; 27 | } 28 | 29 | pub fn consume(&mut self, expected: TokenKind) -> Result<(), KaoriError> { 30 | let found = self.token_kind(); 31 | 32 | if expected == found { 33 | self.advance(); 34 | Ok(()) 35 | } else { 36 | let span = self.span(); 37 | 38 | Err(kaori_error!( 39 | span, 40 | "expected {}, but found {}", 41 | expected, 42 | found, 43 | )) 44 | } 45 | } 46 | 47 | pub fn look_ahead(&mut self, expected: &[TokenKind]) -> bool { 48 | for (i, expected) in expected.iter().enumerate() { 49 | let j = self.index + i; 50 | 51 | let matched = match self.tokens.get(j) { 52 | Some(token) => token.kind == *expected, 53 | None => false, 54 | }; 55 | 56 | if !matched { 57 | return false; 58 | } 59 | } 60 | 61 | true 62 | } 63 | 64 | pub fn token_kind(&self) -> TokenKind { 65 | let token = self.tokens.get(self.index).unwrap(); 66 | 67 | token.kind 68 | } 69 | 70 | pub fn span(&self) -> Span { 71 | self.tokens[self.index].span 72 | } 73 | 74 | pub fn lexeme(&self) -> &str { 75 | let span = self.span(); 76 | 77 | &self.source[span.start..span.end] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /benchmark.py: -------------------------------------------------------------------------------- 1 | import time 2 | import subprocess 3 | import platform 4 | import statistics 5 | from pathlib import Path 6 | 7 | folder = Path("tests_bench") 8 | kaori_folder = Path("test_suite") 9 | rust_exe = Path("target/release/kaori") 10 | 11 | if platform.system() == "Windows": 12 | rust_exe = rust_exe.with_suffix(".exe") 13 | 14 | scripts = [ 15 | ("Kaori Iterative", kaori_folder / "iterative_fib.kr", [str(rust_exe)]), 16 | ("Kaori Recursive", kaori_folder / "recursive_fib.kr", [str(rust_exe)]), 17 | ("Lua Iterative", folder / "iterative_fib.lua", ["lua"]), 18 | ("Lua Recursive", folder / "recursive_fib.lua", ["lua"]), 19 | ("Python Iterative", folder / "iterative_fib.py", ["python"]), 20 | ("Python Recursive", folder / "recursive_fib.py", ["python"]), 21 | ("Pypy Iterative", folder / "iterative_fib.py", ["pypy"]), 22 | ("Pypy Recursive", folder / "recursive_fib.py", ["pypy"]), 23 | ] 24 | 25 | 26 | def run_script(path, cmd_list, runs=20, warmups=5): 27 | """ 28 | Run a single benchmark script multiple times and compute mean and relative stddev. 29 | - `runs`: number of measured executions. 30 | - `warmups`: number of ignored warm-up executions. 31 | """ 32 | full_cmd = cmd_list + [str(path)] 33 | 34 | # Warm-up phase (ignore results) 35 | for _ in range(warmups): 36 | subprocess.run(full_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) 37 | 38 | times = [] 39 | for _ in range(runs): 40 | output = subprocess.check_output(full_cmd, stderr=subprocess.DEVNULL) 41 | ms = float(output.strip()) # script prints milliseconds 42 | times.append(ms) 43 | 44 | mean = statistics.mean(times) 45 | stdev = statistics.stdev(times) 46 | pct = (stdev / mean * 100) if mean != 0 else 0.0 47 | return mean, pct 48 | 49 | 50 | if __name__ == "__main__": 51 | print(f"{'Fibonacci':<25} {'Mean (ms)':<12} {'± %':<8}") 52 | print("-" * 48) 53 | 54 | for name, path, cmd_list in scripts: 55 | try: 56 | mean, pct = run_script(path, cmd_list) 57 | print(f"{name:<25} {mean:>9.3f} {pct:>7.2f}%") 58 | except subprocess.CalledProcessError: 59 | print(f"{name:<25} ERROR") 60 | -------------------------------------------------------------------------------- /src/semantic/type.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::hir_id::HirId; 4 | 5 | #[derive(Debug, Default)] 6 | pub struct Types { 7 | types: HashMap, 8 | } 9 | 10 | impl Types { 11 | pub fn get(&self, id: HirId) -> Type { 12 | let ty = self.types.get(&id).unwrap(); 13 | 14 | self.resolve_type(ty) 15 | } 16 | 17 | pub fn insert(&mut self, id: HirId, ty: Type) { 18 | self.types.insert(id, ty); 19 | } 20 | 21 | fn resolve_type(&self, ty: &Type) -> Type { 22 | match ty { 23 | Type::Boolean => Type::Boolean, 24 | Type::String => Type::String, 25 | Type::Number => Type::Number, 26 | Type::Void => Type::Void, 27 | Type::Function { 28 | parameters, 29 | return_ty, 30 | } => { 31 | let parameters = parameters.iter().map(|ty| self.resolve_type(ty)).collect(); 32 | 33 | let return_ty = self.resolve_type(return_ty); 34 | 35 | Type::function(parameters, return_ty) 36 | } 37 | 38 | Type::Struct { fields } => { 39 | let fields = fields 40 | .iter() 41 | .map(|field| self.resolve_type(field)) 42 | .collect(); 43 | 44 | Type::struct_(fields) 45 | } 46 | 47 | Type::TypeRef(id) => self.get(*id), 48 | } 49 | } 50 | } 51 | 52 | #[derive(Debug, PartialEq, Eq, Clone, Default)] 53 | pub enum Type { 54 | Boolean, 55 | String, 56 | Number, 57 | #[default] 58 | Void, 59 | Function { 60 | parameters: Vec, 61 | return_ty: Box, 62 | }, 63 | Struct { 64 | fields: Vec, 65 | }, 66 | TypeRef(HirId), 67 | } 68 | 69 | impl Type { 70 | pub fn function(parameters: Vec, return_ty: Type) -> Type { 71 | Type::Function { 72 | parameters, 73 | return_ty: Box::new(return_ty), 74 | } 75 | } 76 | 77 | pub fn struct_(fields: Vec) -> Type { 78 | Type::Struct { fields } 79 | } 80 | 81 | pub fn type_ref(id: HirId) -> Type { 82 | Type::TypeRef(id) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/semantic/hir_decl.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | use super::{hir_expr::HirExpr, hir_id::HirId, hir_node::HirNode, hir_ty::HirTy}; 4 | 5 | #[derive(Debug)] 6 | pub struct HirDecl { 7 | pub id: HirId, 8 | pub span: Span, 9 | pub kind: HirDeclKind, 10 | } 11 | 12 | #[derive(Debug)] 13 | pub enum HirDeclKind { 14 | Variable { 15 | right: Box, 16 | ty: Option, 17 | }, 18 | Function { 19 | parameters: Vec, 20 | body: Vec, 21 | return_ty: Option, 22 | }, 23 | Struct { 24 | fields: Vec, 25 | }, 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct HirParameter { 30 | pub id: HirId, 31 | pub span: Span, 32 | pub ty: HirTy, 33 | } 34 | 35 | #[derive(Debug)] 36 | pub struct HirField { 37 | pub id: HirId, 38 | pub span: Span, 39 | pub ty: HirTy, 40 | } 41 | 42 | impl HirParameter { 43 | pub fn new(id: HirId, ty: HirTy, span: Span) -> HirParameter { 44 | HirParameter { id, span, ty } 45 | } 46 | } 47 | 48 | impl HirField { 49 | pub fn new(id: HirId, ty: HirTy, span: Span) -> HirField { 50 | HirField { id, span, ty } 51 | } 52 | } 53 | 54 | impl HirDecl { 55 | pub fn struct_(id: HirId, fields: Vec, span: Span) -> HirDecl { 56 | HirDecl { 57 | id, 58 | span, 59 | 60 | kind: HirDeclKind::Struct { fields }, 61 | } 62 | } 63 | 64 | pub fn variable(id: HirId, right: HirExpr, ty: Option, span: Span) -> HirDecl { 65 | HirDecl { 66 | id, 67 | span, 68 | 69 | kind: HirDeclKind::Variable { 70 | right: Box::new(right), 71 | ty, 72 | }, 73 | } 74 | } 75 | 76 | pub fn function( 77 | id: HirId, 78 | parameters: Vec, 79 | body: Vec, 80 | return_ty: Option, 81 | span: Span, 82 | ) -> HirDecl { 83 | HirDecl { 84 | id, 85 | span, 86 | kind: HirDeclKind::Function { 87 | parameters, 88 | body, 89 | return_ty, 90 | }, 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/cfg_ir/jump_threading.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | 3 | use super::{ 4 | basic_block::{BasicBlock, Terminator}, 5 | cfg_function::CfgFunction, 6 | }; 7 | 8 | pub fn run_jump_threading_optimization(cfgs: &mut [CfgFunction]) { 9 | for cfg in cfgs { 10 | let mut visited = HashSet::new(); 11 | let mut nodes = HashMap::new(); 12 | 13 | traverse(0, &mut cfg.basic_blocks, &mut visited, &mut nodes); 14 | } 15 | } 16 | 17 | fn traverse( 18 | index: usize, 19 | basic_blocks: &mut [BasicBlock], 20 | visited: &mut HashSet, 21 | nodes: &mut HashMap, 22 | ) -> usize { 23 | if let Some(index) = visited.get(&index) { 24 | if let Some(index) = nodes.get(index) { 25 | return *index; 26 | }; 27 | 28 | return *index; 29 | } 30 | 31 | visited.insert(index); 32 | 33 | let basic_block = &basic_blocks[index]; 34 | 35 | let (bb_index, terminator) = &match basic_block.terminator { 36 | Terminator::Branch { 37 | src, 38 | r#true, 39 | r#false, 40 | } => { 41 | let terminator = Terminator::Branch { 42 | src, 43 | r#true: traverse(r#true, basic_blocks, visited, nodes), 44 | r#false: traverse(r#false, basic_blocks, visited, nodes), 45 | }; 46 | 47 | (index, terminator) 48 | } 49 | Terminator::Goto(target) => { 50 | if basic_block.instructions.is_empty() { 51 | let terminator = Terminator::Goto(target); 52 | let index = traverse(target, basic_blocks, visited, nodes); 53 | 54 | (index, terminator) 55 | } else { 56 | let target = traverse(target, basic_blocks, visited, nodes); 57 | let terminator = Terminator::Goto(target); 58 | 59 | (index, terminator) 60 | } 61 | } 62 | Terminator::Return { src } => { 63 | let terminator = Terminator::Return { src }; 64 | 65 | (index, terminator) 66 | } 67 | _ => unreachable!(), 68 | }; 69 | 70 | nodes.insert(index, *bb_index); 71 | 72 | let basic_block = &mut basic_blocks[index]; 73 | basic_block.terminator = *terminator; 74 | 75 | *bb_index 76 | } 77 | -------------------------------------------------------------------------------- /src/program.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use crate::{ 4 | bytecode::{bytecode::Bytecode, emit_bytecode::emit_bytecode}, 5 | cfg_ir::{ 6 | build_cfgs::build_cfgs, cfg_function::CfgFunction, 7 | jump_threading::run_jump_threading_optimization, 8 | }, 9 | error::kaori_error::KaoriError, 10 | lexer::{lexer::Lexer, token_stream::TokenStream}, 11 | semantic::{hir_ir::HirIr, resolver::Resolver, type_checker::TypeChecker}, 12 | syntax::{decl::Decl, parser::Parser}, 13 | virtual_machine::vm::run_vm, 14 | }; 15 | 16 | fn run_lexical_analysis(source: &'_ str) -> Result, KaoriError> { 17 | let lexer = Lexer::new(source); 18 | let tokens = lexer.tokenize()?; 19 | 20 | let token_stream = TokenStream::new(source, tokens); 21 | Ok(token_stream) 22 | } 23 | 24 | fn run_syntax_analysis(token_stream: TokenStream) -> Result, KaoriError> { 25 | let mut parser = Parser::new(token_stream); 26 | 27 | let ast = parser.parse()?; 28 | 29 | Ok(ast) 30 | } 31 | 32 | fn run_semantic_analysis(ast: &mut [Decl]) -> Result { 33 | let mut resolver = Resolver::default(); 34 | 35 | let declarations = resolver.resolve(ast)?; 36 | 37 | let type_checker = TypeChecker::default(); 38 | 39 | let types = type_checker.type_check(&declarations)?; 40 | 41 | let hir = HirIr::new(declarations, types); 42 | 43 | Ok(hir) 44 | } 45 | 46 | fn run_optimizations(cfgs: &mut [CfgFunction]) { 47 | run_jump_threading_optimization(cfgs); 48 | } 49 | 50 | pub fn compile_source_code(source: &str) -> Result { 51 | let token_stream = run_lexical_analysis(source)?; 52 | let mut ast = run_syntax_analysis(token_stream)?; 53 | let hir = run_semantic_analysis(&mut ast)?; 54 | let mut cfgs = build_cfgs(&hir.declarations)?; 55 | 56 | run_optimizations(&mut cfgs); 57 | 58 | let bytecode = emit_bytecode(cfgs); 59 | 60 | //println!("{bytecode}"); 61 | 62 | Ok(bytecode) 63 | } 64 | 65 | pub fn run_program(source: &str) -> Result<(), KaoriError> { 66 | // Start timer 67 | 68 | // Compile and run 69 | let bytecode = compile_source_code(source)?; 70 | 71 | let start = Instant::now(); 72 | 73 | run_vm(bytecode.bytes, bytecode.functions); 74 | 75 | // Measure elapsed time 76 | let elapsed = start.elapsed(); 77 | 78 | println!("{}", elapsed.as_secs_f64() * 1000.0); 79 | 80 | Ok(()) 81 | } 82 | -------------------------------------------------------------------------------- /src/syntax/decl.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | use super::{ast_id::AstId, ast_node::AstNode, expr::Expr, ty::Ty}; 4 | 5 | #[derive(Debug)] 6 | pub struct Decl { 7 | pub id: AstId, 8 | pub span: Span, 9 | pub kind: DeclKind, 10 | } 11 | 12 | #[derive(Debug)] 13 | pub enum DeclKind { 14 | Variable { 15 | name: String, 16 | right: Expr, 17 | ty: Option, 18 | }, 19 | Function { 20 | name: String, 21 | parameters: Vec, 22 | body: Vec, 23 | return_ty: Option, 24 | }, 25 | Struct { 26 | name: String, 27 | fields: Vec, 28 | }, 29 | } 30 | 31 | #[derive(Debug)] 32 | pub struct Parameter { 33 | pub id: AstId, 34 | pub span: Span, 35 | pub name: String, 36 | pub ty: Ty, 37 | } 38 | 39 | #[derive(Debug)] 40 | pub struct Field { 41 | pub id: AstId, 42 | pub span: Span, 43 | pub name: String, 44 | pub ty: Ty, 45 | } 46 | 47 | impl Parameter { 48 | pub fn new(name: String, ty: Ty, span: Span) -> Parameter { 49 | Parameter { 50 | id: AstId::default(), 51 | span, 52 | name, 53 | ty, 54 | } 55 | } 56 | } 57 | 58 | impl Field { 59 | pub fn new(name: String, ty: Ty, span: Span) -> Field { 60 | Field { 61 | id: AstId::default(), 62 | span, 63 | name, 64 | ty, 65 | } 66 | } 67 | } 68 | 69 | impl Decl { 70 | pub fn struct_(name: String, fields: Vec, span: Span) -> Decl { 71 | Decl { 72 | id: AstId::default(), 73 | span, 74 | 75 | kind: DeclKind::Struct { name, fields }, 76 | } 77 | } 78 | 79 | pub fn variable(name: String, right: Expr, ty: Option, span: Span) -> Decl { 80 | Decl { 81 | id: AstId::default(), 82 | span, 83 | 84 | kind: DeclKind::Variable { name, right, ty }, 85 | } 86 | } 87 | 88 | pub fn function( 89 | name: String, 90 | parameters: Vec, 91 | body: Vec, 92 | return_ty: Option, 93 | span: Span, 94 | ) -> Decl { 95 | Decl { 96 | id: AstId::default(), 97 | span, 98 | kind: DeclKind::Function { 99 | name, 100 | parameters, 101 | body, 102 | return_ty, 103 | }, 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/virtual_machine/vm_context.rs: -------------------------------------------------------------------------------- 1 | use crate::bytecode::{function::Function, value::Value}; 2 | 3 | pub struct FunctionFrame { 4 | pub size: u8, 5 | pub registers_ptr: *mut Value, 6 | pub constants_ptr: *const Value, 7 | pub return_address: *const u16, 8 | pub return_register: u16, 9 | } 10 | 11 | impl FunctionFrame { 12 | pub fn new( 13 | size: u8, 14 | registers_ptr: *mut Value, 15 | constants_ptr: *const Value, 16 | return_address: *const u16, 17 | return_register: u16, 18 | ) -> Self { 19 | Self { 20 | size, 21 | registers_ptr, 22 | constants_ptr, 23 | return_address, 24 | return_register, 25 | } 26 | } 27 | } 28 | 29 | pub struct VMContext<'a> { 30 | pub functions: &'a [Function], 31 | pub call_stack: Vec, 32 | pub registers: Vec, 33 | pub registers_ptr: *mut Value, 34 | pub constants_ptr: *const Value, 35 | } 36 | 37 | impl<'a> VMContext<'a> { 38 | pub fn new( 39 | functions: &'a [Function], 40 | registers: Vec, 41 | registers_ptr: *mut Value, 42 | constants_ptr: *const Value, 43 | main_frame: FunctionFrame, 44 | ) -> Self { 45 | Self { 46 | functions, 47 | call_stack: vec![main_frame], 48 | registers, 49 | registers_ptr, 50 | constants_ptr, 51 | } 52 | } 53 | 54 | #[inline(always)] 55 | pub fn get_constant_value(&self, index: u16) -> Value { 56 | unsafe { *self.constants_ptr.add(index as usize) } 57 | } 58 | 59 | #[inline(always)] 60 | pub fn get_register_value(&self, index: u16) -> Value { 61 | unsafe { *self.registers_ptr.add(index as usize) } 62 | } 63 | 64 | #[inline(always)] 65 | pub fn set_value(&mut self, index: u16, value: Value) { 66 | unsafe { 67 | *self.registers_ptr.add(index as usize) = value; 68 | } 69 | } 70 | 71 | #[inline(always)] 72 | pub fn pop_frame(&mut self) -> FunctionFrame { 73 | let frame = unsafe { self.call_stack.pop().unwrap_unchecked() }; 74 | 75 | if let Some(frame) = self.call_stack.last() { 76 | self.registers_ptr = frame.registers_ptr; 77 | self.constants_ptr = frame.constants_ptr; 78 | } 79 | 80 | frame 81 | } 82 | 83 | #[inline(always)] 84 | pub fn push_frame( 85 | &mut self, 86 | return_register: u16, 87 | return_address: *const u16, 88 | frame_size: u8, 89 | constants_ptr: *const Value, 90 | ) { 91 | let size = self.call_stack.last().unwrap().size; 92 | 93 | let registers_ptr = unsafe { self.registers_ptr.add(size as usize) }; 94 | 95 | let frame = FunctionFrame::new( 96 | frame_size, 97 | registers_ptr, 98 | constants_ptr, 99 | return_address, 100 | return_register, 101 | ); 102 | 103 | self.registers_ptr = registers_ptr; 104 | self.constants_ptr = constants_ptr; 105 | 106 | self.call_stack.push(frame); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/semantic/hir_stmt.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | use super::{hir_decl::HirDecl, hir_expr::HirExpr, hir_id::HirId, hir_node::HirNode}; 4 | 5 | #[derive(Debug)] 6 | pub struct HirStmt { 7 | pub id: HirId, 8 | pub span: Span, 9 | pub kind: HirStmtKind, 10 | } 11 | 12 | #[derive(Debug)] 13 | pub enum HirStmtKind { 14 | Print(Box), 15 | Branch { 16 | condition: Box, 17 | then_branch: Box, 18 | else_branch: Option>, 19 | }, 20 | Loop { 21 | init: Option, 22 | condition: HirExpr, 23 | block: Box, 24 | increment: Option>, 25 | }, 26 | Block(Vec), 27 | Expression(Box), 28 | Break, 29 | Continue, 30 | Return(Option>), 31 | } 32 | 33 | impl HirStmt { 34 | pub fn print(expr: HirExpr, span: Span) -> HirStmt { 35 | HirStmt { 36 | id: HirId::default(), 37 | span, 38 | kind: HirStmtKind::Print(Box::new(expr)), 39 | } 40 | } 41 | 42 | pub fn branch_( 43 | condition: HirExpr, 44 | then_branch: HirStmt, 45 | else_branch: Option, 46 | span: Span, 47 | ) -> HirStmt { 48 | HirStmt { 49 | id: HirId::default(), 50 | span, 51 | kind: HirStmtKind::Branch { 52 | condition: Box::new(condition), 53 | then_branch: Box::new(then_branch), 54 | else_branch: else_branch.map(Box::new), 55 | }, 56 | } 57 | } 58 | 59 | pub fn loop_( 60 | init: Option, 61 | condition: HirExpr, 62 | block: HirStmt, 63 | increment: Option, 64 | span: Span, 65 | ) -> HirStmt { 66 | HirStmt { 67 | id: HirId::default(), 68 | span, 69 | kind: HirStmtKind::Loop { 70 | init, 71 | condition, 72 | block: Box::new(block), 73 | increment: increment.map(Box::new), 74 | }, 75 | } 76 | } 77 | 78 | pub fn block(nodes: Vec, span: Span) -> HirStmt { 79 | HirStmt { 80 | id: HirId::default(), 81 | span, 82 | kind: HirStmtKind::Block(nodes), 83 | } 84 | } 85 | 86 | pub fn expression(expr: HirExpr, span: Span) -> HirStmt { 87 | HirStmt { 88 | id: HirId::default(), 89 | span, 90 | kind: HirStmtKind::Expression(Box::new(expr)), 91 | } 92 | } 93 | 94 | pub fn break_(span: Span) -> HirStmt { 95 | HirStmt { 96 | id: HirId::default(), 97 | span, 98 | kind: HirStmtKind::Break, 99 | } 100 | } 101 | 102 | pub fn continue_(span: Span) -> HirStmt { 103 | HirStmt { 104 | id: HirId::default(), 105 | span, 106 | kind: HirStmtKind::Continue, 107 | } 108 | } 109 | 110 | pub fn return_(expr: Option, span: Span) -> HirStmt { 111 | HirStmt { 112 | id: HirId::default(), 113 | span, 114 | kind: HirStmtKind::Return(expr.map(Box::new)), 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/syntax/stmt.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | use super::{ast_id::AstId, ast_node::AstNode, decl::Decl, expr::Expr}; 4 | 5 | #[derive(Debug)] 6 | pub struct Stmt { 7 | pub id: AstId, 8 | pub span: Span, 9 | pub kind: StmtKind, 10 | } 11 | 12 | #[derive(Debug)] 13 | pub enum StmtKind { 14 | Print(Box), 15 | If { 16 | condition: Box, 17 | then_branch: Box, 18 | else_branch: Option>, 19 | }, 20 | WhileLoop { 21 | condition: Box, 22 | block: Box, 23 | }, 24 | ForLoop { 25 | init: Box, 26 | condition: Expr, 27 | increment: Box, 28 | block: Box, 29 | }, 30 | Block(Vec), 31 | Expression(Box), 32 | Break, 33 | Continue, 34 | Return(Option>), 35 | } 36 | 37 | impl Stmt { 38 | pub fn print(expression: Expr, span: Span) -> Stmt { 39 | Stmt { 40 | id: AstId::default(), 41 | span, 42 | kind: StmtKind::Print(Box::new(expression)), 43 | } 44 | } 45 | 46 | pub fn if_(condition: Expr, then_branch: Stmt, else_branch: Option, span: Span) -> Stmt { 47 | Stmt { 48 | id: AstId::default(), 49 | span, 50 | kind: StmtKind::If { 51 | condition: Box::new(condition), 52 | then_branch: Box::new(then_branch), 53 | else_branch: else_branch.map(Box::new), 54 | }, 55 | } 56 | } 57 | 58 | pub fn while_loop(condition: Expr, block: Stmt, span: Span) -> Stmt { 59 | Stmt { 60 | id: AstId::default(), 61 | span, 62 | kind: StmtKind::WhileLoop { 63 | condition: Box::new(condition), 64 | block: Box::new(block), 65 | }, 66 | } 67 | } 68 | 69 | pub fn for_loop(init: Decl, condition: Expr, increment: Stmt, block: Stmt, span: Span) -> Stmt { 70 | Stmt { 71 | id: AstId::default(), 72 | span, 73 | kind: StmtKind::ForLoop { 74 | init: Box::new(init), 75 | condition, 76 | increment: Box::new(increment), 77 | block: Box::new(block), 78 | }, 79 | } 80 | } 81 | 82 | pub fn block(nodes: Vec, span: Span) -> Stmt { 83 | Stmt { 84 | id: AstId::default(), 85 | span, 86 | kind: StmtKind::Block(nodes), 87 | } 88 | } 89 | 90 | pub fn expression(expr: Expr, span: Span) -> Stmt { 91 | Stmt { 92 | id: AstId::default(), 93 | span, 94 | kind: StmtKind::Expression(Box::new(expr)), 95 | } 96 | } 97 | 98 | pub fn break_(span: Span) -> Stmt { 99 | Stmt { 100 | id: AstId::default(), 101 | span, 102 | kind: StmtKind::Break, 103 | } 104 | } 105 | 106 | pub fn continue_(span: Span) -> Stmt { 107 | Stmt { 108 | id: AstId::default(), 109 | span, 110 | kind: StmtKind::Continue, 111 | } 112 | } 113 | 114 | pub fn return_(expression: Option, span: Span) -> Stmt { 115 | Stmt { 116 | id: AstId::default(), 117 | span, 118 | kind: StmtKind::Return(expression.map(Box::new)), 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/lexer/token_kind.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug, PartialEq, Copy, Clone)] 4 | pub enum TokenKind { 5 | Plus, 6 | Minus, 7 | Multiply, 8 | Divide, 9 | Modulo, 10 | 11 | Assign, 12 | AddAssign, 13 | SubtractAssign, 14 | MultiplyAssign, 15 | DivideAssign, 16 | ModuloAssign, 17 | 18 | And, 19 | Or, 20 | Not, 21 | NotEqual, 22 | Equal, 23 | Greater, 24 | GreaterEqual, 25 | Less, 26 | LessEqual, 27 | 28 | Dollar, 29 | Comma, 30 | Semicolon, 31 | Colon, 32 | ThinArrow, 33 | 34 | LeftParen, 35 | RightParen, 36 | LeftBrace, 37 | RightBrace, 38 | 39 | // Keywords 40 | Function, 41 | Struct, 42 | For, 43 | While, 44 | Break, 45 | Continue, 46 | If, 47 | Else, 48 | Return, 49 | Print, 50 | True, 51 | False, 52 | Bool, 53 | Number, 54 | 55 | Identifier, 56 | StringLiteral, 57 | NumberLiteral, 58 | 59 | Invalid, 60 | EndOfFile, 61 | } 62 | 63 | impl fmt::Display for TokenKind { 64 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 65 | let name = match self { 66 | TokenKind::Plus => "+", 67 | TokenKind::Minus => "-", 68 | TokenKind::Multiply => "*", 69 | TokenKind::Divide => "/", 70 | TokenKind::Modulo => "%", 71 | 72 | TokenKind::Assign => "=", 73 | TokenKind::AddAssign => "+=", 74 | TokenKind::SubtractAssign => "-=", 75 | TokenKind::MultiplyAssign => "*=", 76 | TokenKind::DivideAssign => "/=", 77 | TokenKind::ModuloAssign => "%=", 78 | 79 | TokenKind::And => "and", 80 | TokenKind::Or => "or", 81 | TokenKind::Not => "not", 82 | TokenKind::NotEqual => "!=", 83 | TokenKind::Equal => "==", 84 | TokenKind::Greater => ">", 85 | TokenKind::GreaterEqual => ">=", 86 | TokenKind::Less => "<", 87 | TokenKind::LessEqual => "<=", 88 | 89 | TokenKind::Dollar => "$", 90 | TokenKind::Comma => ",", 91 | TokenKind::Semicolon => ";", 92 | TokenKind::Colon => ":", 93 | TokenKind::ThinArrow => "->", 94 | 95 | TokenKind::LeftParen => "(", 96 | TokenKind::RightParen => ")", 97 | TokenKind::LeftBrace => "{", 98 | TokenKind::RightBrace => "}", 99 | 100 | TokenKind::Function => "func", 101 | TokenKind::For => "for", 102 | TokenKind::While => "while", 103 | TokenKind::Break => "break", 104 | TokenKind::Continue => "continue", 105 | TokenKind::If => "if", 106 | TokenKind::Else => "else", 107 | TokenKind::Return => "return", 108 | TokenKind::Struct => "struct", 109 | TokenKind::Print => "print", 110 | TokenKind::True => "true", 111 | TokenKind::False => "false", 112 | TokenKind::Bool => "bool", 113 | TokenKind::Number => "number", 114 | 115 | TokenKind::Identifier => "identifier", 116 | TokenKind::StringLiteral => "string", 117 | TokenKind::NumberLiteral => "number", 118 | 119 | TokenKind::Invalid => "invalid", 120 | TokenKind::EndOfFile => "EOF", 121 | }; 122 | 123 | write!(f, "{name}") 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/syntax/expr.rs: -------------------------------------------------------------------------------- 1 | use crate::lexer::span::Span; 2 | 3 | use super::{assign_op::AssignOp, ast_id::AstId, binary_op::BinaryOp, unary_op::UnaryOp}; 4 | 5 | #[derive(Debug)] 6 | pub struct Expr { 7 | pub id: AstId, 8 | pub span: Span, 9 | pub kind: ExprKind, 10 | } 11 | 12 | #[derive(Debug)] 13 | pub enum ExprKind { 14 | Binary { 15 | operator: BinaryOp, 16 | left: Box, 17 | right: Box, 18 | }, 19 | Unary { 20 | right: Box, 21 | operator: UnaryOp, 22 | }, 23 | Assign { 24 | operator: AssignOp, 25 | left: Box, 26 | right: Box, 27 | }, 28 | Identifier(String), 29 | FunctionCall { 30 | callee: Box, 31 | arguments: Vec, 32 | }, 33 | StringLiteral(String), 34 | NumberLiteral(f64), 35 | BooleanLiteral(bool), 36 | } 37 | 38 | impl Expr { 39 | pub fn binary(operator: BinaryOp, left: Expr, right: Expr) -> Expr { 40 | let span = Span::merge(left.span, right.span); 41 | 42 | Expr { 43 | id: AstId::default(), 44 | span, 45 | kind: ExprKind::Binary { 46 | operator, 47 | left: Box::new(left), 48 | right: Box::new(right), 49 | }, 50 | } 51 | } 52 | 53 | pub fn unary(operator: UnaryOp, right: Expr) -> Expr { 54 | let span = Span::merge(operator.span, right.span); 55 | 56 | Expr { 57 | id: AstId::default(), 58 | span, 59 | kind: ExprKind::Unary { 60 | operator, 61 | right: Box::new(right), 62 | }, 63 | } 64 | } 65 | 66 | pub fn assign(operator: AssignOp, left: Expr, right: Expr) -> Expr { 67 | let span = Span::merge(left.span, right.span); 68 | 69 | Expr { 70 | id: AstId::default(), 71 | span, 72 | kind: ExprKind::Assign { 73 | operator, 74 | left: Box::new(left), 75 | right: Box::new(right), 76 | }, 77 | } 78 | } 79 | 80 | pub fn identifier(name: String, span: Span) -> Expr { 81 | Expr { 82 | id: AstId::default(), 83 | span, 84 | kind: ExprKind::Identifier(name), 85 | } 86 | } 87 | 88 | pub fn function_call(callee: Expr, arguments: Vec, span: Span) -> Expr { 89 | let span = Span::merge(callee.span, span); 90 | 91 | Expr { 92 | id: AstId::default(), 93 | span, 94 | kind: ExprKind::FunctionCall { 95 | callee: Box::new(callee), 96 | arguments, 97 | }, 98 | } 99 | } 100 | 101 | pub fn string_literal(value: String, span: Span) -> Expr { 102 | Expr { 103 | id: AstId::default(), 104 | span, 105 | kind: ExprKind::StringLiteral(value), 106 | } 107 | } 108 | 109 | pub fn number_literal(value: f64, span: Span) -> Expr { 110 | Expr { 111 | id: AstId::default(), 112 | span, 113 | kind: ExprKind::NumberLiteral(value), 114 | } 115 | } 116 | 117 | pub fn boolean_literal(value: bool, span: Span) -> Expr { 118 | Expr { 119 | id: AstId::default(), 120 | span, 121 | kind: ExprKind::BooleanLiteral(value), 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/semantic/hir_expr.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | lexer::span::Span, 3 | syntax::{binary_op::BinaryOp, unary_op::UnaryOp}, 4 | }; 5 | 6 | use super::hir_id::HirId; 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct HirExpr { 10 | pub id: HirId, 11 | pub span: Span, 12 | pub kind: HirExprKind, 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | pub enum HirExprKind { 17 | Binary { 18 | operator: BinaryOp, 19 | left: Box, 20 | right: Box, 21 | }, 22 | Unary { 23 | right: Box, 24 | operator: UnaryOp, 25 | }, 26 | Assign { 27 | left: Box, 28 | right: Box, 29 | }, 30 | Variable(HirId), 31 | FunctionCall { 32 | callee: Box, 33 | arguments: Vec, 34 | }, 35 | Function(HirId), 36 | String(String), 37 | Number(f64), 38 | Boolean(bool), 39 | } 40 | 41 | impl HirExpr { 42 | pub fn binary(operator: BinaryOp, left: HirExpr, right: HirExpr, span: Span) -> HirExpr { 43 | HirExpr { 44 | id: HirId::default(), 45 | span, 46 | kind: HirExprKind::Binary { 47 | operator, 48 | left: Box::new(left), 49 | right: Box::new(right), 50 | }, 51 | } 52 | } 53 | 54 | pub fn unary(operator: UnaryOp, right: HirExpr, span: Span) -> HirExpr { 55 | HirExpr { 56 | id: HirId::default(), 57 | span, 58 | kind: HirExprKind::Unary { 59 | operator, 60 | right: Box::new(right), 61 | }, 62 | } 63 | } 64 | 65 | pub fn assign(left: HirExpr, right: HirExpr, span: Span) -> HirExpr { 66 | HirExpr { 67 | id: HirId::default(), 68 | span, 69 | kind: HirExprKind::Assign { 70 | left: Box::new(left), 71 | right: Box::new(right), 72 | }, 73 | } 74 | } 75 | 76 | pub fn variable(id: HirId, span: Span) -> HirExpr { 77 | HirExpr { 78 | id: HirId::default(), 79 | span, 80 | kind: HirExprKind::Variable(id), 81 | } 82 | } 83 | 84 | pub fn function_call(callee: HirExpr, arguments: Vec, span: Span) -> HirExpr { 85 | HirExpr { 86 | id: HirId::default(), 87 | span, 88 | kind: HirExprKind::FunctionCall { 89 | callee: Box::new(callee), 90 | arguments, 91 | }, 92 | } 93 | } 94 | 95 | pub fn function(id: HirId, span: Span) -> HirExpr { 96 | HirExpr { 97 | id: HirId::default(), 98 | span, 99 | kind: HirExprKind::Function(id), 100 | } 101 | } 102 | 103 | pub fn string(value: String, span: Span) -> HirExpr { 104 | HirExpr { 105 | id: HirId::default(), 106 | span, 107 | kind: HirExprKind::String(value), 108 | } 109 | } 110 | 111 | pub fn number(value: f64, span: Span) -> HirExpr { 112 | HirExpr { 113 | id: HirId::default(), 114 | span, 115 | kind: HirExprKind::Number(value), 116 | } 117 | } 118 | 119 | pub fn boolean(value: bool, span: Span) -> HirExpr { 120 | HirExpr { 121 | id: HirId::default(), 122 | span, 123 | kind: HirExprKind::Boolean(value), 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/syntax/parse_declaration.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::kaori_error::KaoriError, 3 | lexer::{span::Span, token_kind::TokenKind}, 4 | }; 5 | 6 | use super::{ 7 | decl::{Decl, Field, Parameter}, 8 | parser::Parser, 9 | }; 10 | 11 | impl<'a> Parser<'a> { 12 | pub fn parse_variable_declaration(&mut self) -> Result { 13 | let span = self.token_stream.span(); 14 | 15 | self.token_stream.consume(TokenKind::Dollar)?; 16 | 17 | let name = self.token_stream.lexeme().to_owned(); 18 | 19 | self.token_stream.consume(TokenKind::Identifier)?; 20 | 21 | let ty = if self.token_stream.token_kind() == TokenKind::Colon { 22 | self.token_stream.consume(TokenKind::Colon)?; 23 | Some(self.parse_type()?) 24 | } else { 25 | None 26 | }; 27 | 28 | self.token_stream.consume(TokenKind::Assign)?; 29 | 30 | let right = self.parse_expression()?; 31 | 32 | Ok(Decl::variable(name, right, ty, span)) 33 | } 34 | 35 | pub fn parse_function_declaration(&mut self) -> Result { 36 | let span = self.token_stream.span(); 37 | 38 | self.token_stream.consume(TokenKind::Function)?; 39 | 40 | let name = self.token_stream.lexeme().to_owned(); 41 | 42 | self.token_stream.consume(TokenKind::Identifier)?; 43 | 44 | self.token_stream.consume(TokenKind::LeftParen)?; 45 | 46 | let parameters = 47 | self.parse_comma_separator(Parser::parse_function_parameter, TokenKind::RightParen)?; 48 | 49 | self.token_stream.consume(TokenKind::RightParen)?; 50 | 51 | let return_ty = if self.token_stream.token_kind() == TokenKind::ThinArrow { 52 | self.token_stream.consume(TokenKind::ThinArrow)?; 53 | 54 | Some(self.parse_type()?) 55 | } else { 56 | None 57 | }; 58 | 59 | let mut body = Vec::new(); 60 | 61 | self.token_stream.consume(TokenKind::LeftBrace)?; 62 | 63 | while !self.token_stream.at_end() && self.token_stream.token_kind() != TokenKind::RightBrace 64 | { 65 | let node = self.parse_ast_node()?; 66 | body.push(node); 67 | } 68 | 69 | self.token_stream.consume(TokenKind::RightBrace)?; 70 | 71 | Ok(Decl::function(name, parameters, body, return_ty, span)) 72 | } 73 | 74 | pub fn parse_function_parameter(&mut self) -> Result { 75 | let start = self.token_stream.span(); 76 | 77 | let name = self.token_stream.lexeme().to_owned(); 78 | self.token_stream.consume(TokenKind::Identifier)?; 79 | self.token_stream.consume(TokenKind::Colon)?; 80 | 81 | let ty = self.parse_type()?; 82 | 83 | let end = self.token_stream.span(); 84 | 85 | let span = Span::merge(start, end); 86 | 87 | Ok(Parameter::new(name, ty, span)) 88 | } 89 | 90 | pub fn parse_struct_field(&mut self) -> Result { 91 | let start = self.token_stream.span(); 92 | 93 | let name = self.token_stream.lexeme().to_owned(); 94 | self.token_stream.consume(TokenKind::Identifier)?; 95 | self.token_stream.consume(TokenKind::Colon)?; 96 | let ty = self.parse_type()?; 97 | 98 | let end = self.token_stream.span(); 99 | 100 | let span = Span::merge(start, end); 101 | 102 | Ok(Field::new(name, ty, span)) 103 | } 104 | 105 | pub fn parse_struct_declaration(&mut self) -> Result { 106 | let span = self.token_stream.span(); 107 | 108 | self.token_stream.consume(TokenKind::Struct)?; 109 | 110 | let name = self.token_stream.lexeme().to_owned(); 111 | 112 | self.token_stream.consume(TokenKind::Identifier)?; 113 | 114 | self.token_stream.consume(TokenKind::LeftBrace)?; 115 | 116 | let fields = 117 | self.parse_comma_separator(Parser::parse_struct_field, TokenKind::RightBrace)?; 118 | 119 | self.token_stream.consume(TokenKind::RightBrace)?; 120 | 121 | Ok(Decl::struct_(name, fields, span)) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/syntax/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::kaori_error::KaoriError, 3 | kaori_error, 4 | lexer::{token_kind::TokenKind, token_stream::TokenStream}, 5 | }; 6 | 7 | use super::{ast_node::AstNode, decl::Decl, stmt::Stmt}; 8 | 9 | pub struct Parser<'a> { 10 | pub token_stream: TokenStream<'a>, 11 | } 12 | 13 | impl<'a> Parser<'a> { 14 | pub fn new(token_stream: TokenStream<'a>) -> Self { 15 | Self { token_stream } 16 | } 17 | 18 | pub fn parse(&mut self) -> Result, KaoriError> { 19 | let mut declarations = Vec::new(); 20 | 21 | while !self.token_stream.at_end() { 22 | let declaration = self.parse_declaration()?; 23 | 24 | let declaration = match declaration { 25 | Some(decl) => Ok(decl), 26 | _ => Err(kaori_error!( 27 | self.token_stream.span(), 28 | "invalid declaration at global scope" 29 | )), 30 | }?; 31 | 32 | declarations.push(declaration); 33 | } 34 | 35 | Ok(declarations) 36 | } 37 | 38 | pub fn parse_ast_node(&mut self) -> Result { 39 | let declaration = self.parse_declaration()?; 40 | 41 | Ok(if let Some(declaration) = declaration { 42 | AstNode::from(declaration) 43 | } else { 44 | let statement = self.parse_statement()?; 45 | AstNode::from(statement) 46 | }) 47 | } 48 | 49 | pub fn parse_declaration(&mut self) -> Result, KaoriError> { 50 | let token_kind = self.token_stream.token_kind(); 51 | 52 | let declaration = match self.token_stream.token_kind() { 53 | TokenKind::Function => Some(self.parse_function_declaration()?), 54 | TokenKind::Struct => Some(self.parse_struct_declaration()?), 55 | TokenKind::Dollar => Some(self.parse_variable_declaration()?), 56 | _ => None, 57 | }; 58 | 59 | #[allow(clippy::single_match)] 60 | match token_kind { 61 | TokenKind::Dollar => { 62 | self.token_stream.consume(TokenKind::Semicolon)?; 63 | } 64 | _ => (), 65 | }; 66 | 67 | Ok(declaration) 68 | } 69 | 70 | pub fn parse_statement(&mut self) -> Result { 71 | let token_kind = self.token_stream.token_kind(); 72 | 73 | let statement = match token_kind { 74 | TokenKind::Print => self.parse_print_statement(), 75 | TokenKind::LeftBrace => self.parse_block_statement(), 76 | TokenKind::If => self.parse_if_statement(), 77 | TokenKind::While => self.parse_while_loop_statement(), 78 | TokenKind::For => self.parse_for_loop_statement(), 79 | TokenKind::Break => self.parse_break_statement(), 80 | TokenKind::Continue => self.parse_continue_statement(), 81 | TokenKind::Return => self.parse_return_statement(), 82 | 83 | _ => { 84 | let statement = self.parse_expression_statement(); 85 | self.token_stream.consume(TokenKind::Semicolon)?; 86 | 87 | statement 88 | } 89 | }?; 90 | 91 | match token_kind { 92 | TokenKind::Print | TokenKind::Break | TokenKind::Continue | TokenKind::Return => { 93 | self.token_stream.consume(TokenKind::Semicolon)?; 94 | } 95 | _ => (), 96 | }; 97 | 98 | Ok(statement) 99 | } 100 | 101 | pub fn parse_comma_separator( 102 | &mut self, 103 | parse_item: fn(&mut Self) -> Result, 104 | terminator: TokenKind, 105 | ) -> Result, KaoriError> { 106 | let mut items = Vec::new(); 107 | 108 | while !self.token_stream.at_end() && self.token_stream.token_kind() != terminator { 109 | let item = parse_item(self)?; 110 | items.push(item); 111 | 112 | if self.token_stream.token_kind() == terminator { 113 | break; 114 | } 115 | 116 | self.token_stream.consume(TokenKind::Comma)?; 117 | } 118 | 119 | Ok(items) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/syntax/parse_statement.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::kaori_error::KaoriError, lexer::token_kind::TokenKind}; 2 | 3 | use super::{parser::Parser, stmt::Stmt}; 4 | 5 | impl<'a> Parser<'a> { 6 | pub fn parse_return_statement(&mut self) -> Result { 7 | let span = self.token_stream.span(); 8 | 9 | self.token_stream.consume(TokenKind::Return)?; 10 | 11 | if self.token_stream.token_kind() == TokenKind::Semicolon { 12 | let expression = None; 13 | 14 | return Ok(Stmt::return_(expression, span)); 15 | } 16 | 17 | let expression = Some(self.parse_expression()?); 18 | 19 | Ok(Stmt::return_(expression, span)) 20 | } 21 | 22 | pub fn parse_continue_statement(&mut self) -> Result { 23 | let span = self.token_stream.span(); 24 | 25 | self.token_stream.consume(TokenKind::Continue)?; 26 | 27 | Ok(Stmt::continue_(span)) 28 | } 29 | 30 | pub fn parse_break_statement(&mut self) -> Result { 31 | let span = self.token_stream.span(); 32 | 33 | self.token_stream.consume(TokenKind::Break)?; 34 | 35 | Ok(Stmt::break_(span)) 36 | } 37 | 38 | pub fn parse_expression_statement(&mut self) -> Result { 39 | let span = self.token_stream.span(); 40 | let expression = self.parse_expression()?; 41 | 42 | Ok(Stmt::expression(expression, span)) 43 | } 44 | 45 | pub fn parse_print_statement(&mut self) -> Result { 46 | let span = self.token_stream.span(); 47 | 48 | self.token_stream.consume(TokenKind::Print)?; 49 | self.token_stream.consume(TokenKind::LeftParen)?; 50 | let expression = self.parse_expression()?; 51 | self.token_stream.consume(TokenKind::RightParen)?; 52 | 53 | Ok(Stmt::print(expression, span)) 54 | } 55 | 56 | pub fn parse_block_statement(&mut self) -> Result { 57 | let span = self.token_stream.span(); 58 | 59 | let mut nodes = Vec::new(); 60 | 61 | self.token_stream.consume(TokenKind::LeftBrace)?; 62 | 63 | while !self.token_stream.at_end() && self.token_stream.token_kind() != TokenKind::RightBrace 64 | { 65 | let node = self.parse_ast_node()?; 66 | nodes.push(node); 67 | } 68 | 69 | self.token_stream.consume(TokenKind::RightBrace)?; 70 | 71 | Ok(Stmt::block(nodes, span)) 72 | } 73 | 74 | pub fn parse_if_statement(&mut self) -> Result { 75 | let span = self.token_stream.span(); 76 | 77 | self.token_stream.consume(TokenKind::If)?; 78 | 79 | let condition = self.parse_expression()?; 80 | 81 | let then_branch = self.parse_block_statement()?; 82 | 83 | if self.token_stream.token_kind() != TokenKind::Else { 84 | return Ok(Stmt::if_(condition, then_branch, None, span)); 85 | } 86 | 87 | self.token_stream.advance(); 88 | 89 | if self.token_stream.token_kind() == TokenKind::If { 90 | let else_branch = Some(self.parse_if_statement()?); 91 | 92 | return Ok(Stmt::if_(condition, then_branch, else_branch, span)); 93 | } 94 | 95 | let else_branch = Some(self.parse_block_statement()?); 96 | 97 | Ok(Stmt::if_(condition, then_branch, else_branch, span)) 98 | } 99 | 100 | pub fn parse_while_loop_statement(&mut self) -> Result { 101 | let span = self.token_stream.span(); 102 | 103 | self.token_stream.consume(TokenKind::While)?; 104 | 105 | let condition = self.parse_expression()?; 106 | let block = self.parse_block_statement()?; 107 | 108 | Ok(Stmt::while_loop(condition, block, span)) 109 | } 110 | 111 | pub fn parse_for_loop_statement(&mut self) -> Result { 112 | let span = self.token_stream.span(); 113 | 114 | self.token_stream.consume(TokenKind::For)?; 115 | 116 | let init = self.parse_variable_declaration()?; 117 | 118 | self.token_stream.consume(TokenKind::Semicolon)?; 119 | 120 | let condition = self.parse_expression()?; 121 | 122 | self.token_stream.consume(TokenKind::Semicolon)?; 123 | 124 | let increment = self.parse_expression_statement()?; 125 | 126 | let block = self.parse_block_statement()?; 127 | 128 | Ok(Stmt::for_loop(init, condition, increment, block, span)) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/cfg_ir/liveness_analysis.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::{ 4 | basic_block::BlockId, 5 | cfg_instruction::CfgInstruction, 6 | cfg_ir::CfgIr, 7 | operand::{Operand, Variable}, 8 | register_allocator::RegisterAllocator, 9 | }; 10 | 11 | type Instruction = usize; 12 | 13 | pub struct LivenessAnalysis<'a> { 14 | cfg_ir: &'a mut CfgIr, 15 | variable_lifetime: HashMap, 16 | register_allocator: RegisterAllocator, 17 | current_instruction: Instruction, 18 | } 19 | 20 | impl<'a> LivenessAnalysis<'a> { 21 | pub fn new(cfg_ir: &'a mut CfgIr) -> Self { 22 | Self { 23 | cfg_ir, 24 | variable_lifetime: HashMap::new(), 25 | register_allocator: RegisterAllocator::new(), 26 | current_instruction: 0, 27 | } 28 | } 29 | 30 | pub fn analyze_cfgs(&mut self) { 31 | /* for cfg in self.cfg_ir.cfgs.as_ref() { 32 | self.analyze_cfg(cfg); 33 | } */ 34 | } 35 | 36 | pub fn analyze_cfg(&mut self, cfg: BlockId) { 37 | println!("\n"); 38 | 39 | self.variable_lifetime.clear(); 40 | } 41 | 42 | fn try_to_free(&mut self, register: usize) { 43 | /* let register_last_instruction = *self.variable_lifetime.get(®ister).unwrap(); 44 | 45 | if instruction == register_last_instruction { 46 | self.register_allocator.free_register(register); 47 | } */ 48 | } 49 | 50 | fn update_variable_lifetime(&mut self, operand: Operand) { 51 | if let Operand::Variable(variable) = operand { 52 | self.variable_lifetime 53 | .insert(variable, self.current_instruction); 54 | } 55 | } 56 | 57 | fn analyze_instructions(&mut self, instructions: &[CfgInstruction]) { 58 | for instruction in instructions { 59 | self.analyze_instruction(instruction); 60 | 61 | self.current_instruction += 1; 62 | } 63 | } 64 | 65 | fn analyze_instruction(&mut self, instruction: &CfgInstruction) { 66 | match instruction { 67 | CfgInstruction::Add { dest, src1, src2 } 68 | | CfgInstruction::Subtract { dest, src1, src2 } 69 | | CfgInstruction::Multiply { dest, src1, src2 } 70 | | CfgInstruction::Divide { dest, src1, src2 } 71 | | CfgInstruction::Modulo { dest, src1, src2 } 72 | | CfgInstruction::Equal { dest, src1, src2 } 73 | | CfgInstruction::NotEqual { dest, src1, src2 } 74 | | CfgInstruction::Greater { dest, src1, src2 } 75 | | CfgInstruction::GreaterEqual { dest, src1, src2 } 76 | | CfgInstruction::Less { dest, src1, src2 } 77 | | CfgInstruction::LessEqual { dest, src1, src2 } 78 | | CfgInstruction::And { dest, src1, src2 } 79 | | CfgInstruction::Or { dest, src1, src2 } => { 80 | self.update_variable_lifetime(*dest); 81 | self.update_variable_lifetime(*src1); 82 | self.update_variable_lifetime(*src2); 83 | } 84 | CfgInstruction::Negate { dest, src } 85 | | CfgInstruction::Not { dest, src } 86 | | CfgInstruction::Move { dest, src } => { 87 | self.update_variable_lifetime(*dest); 88 | self.update_variable_lifetime(*src); 89 | } 90 | CfgInstruction::StringConst { dest, .. } 91 | | CfgInstruction::NumberConst { dest, .. } 92 | | CfgInstruction::BooleanConst { dest, .. } 93 | | CfgInstruction::FunctionConst { dest, .. } => { 94 | self.update_variable_lifetime(*dest); 95 | } 96 | CfgInstruction::Call => {} 97 | 98 | CfgInstruction::Print { src } => {} 99 | } 100 | } 101 | 102 | fn allocate_register(&mut self, instruction: &mut CfgInstruction) { 103 | match instruction { 104 | CfgInstruction::Add { dest, src1, src2 } 105 | | CfgInstruction::Subtract { dest, src1, src2 } 106 | | CfgInstruction::Multiply { dest, src1, src2 } 107 | | CfgInstruction::Divide { dest, src1, src2 } 108 | | CfgInstruction::Modulo { dest, src1, src2 } 109 | | CfgInstruction::Equal { dest, src1, src2 } 110 | | CfgInstruction::NotEqual { dest, src1, src2 } 111 | | CfgInstruction::Greater { dest, src1, src2 } 112 | | CfgInstruction::GreaterEqual { dest, src1, src2 } 113 | | CfgInstruction::Less { dest, src1, src2 } 114 | | CfgInstruction::LessEqual { dest, src1, src2 } 115 | | CfgInstruction::And { dest, src1, src2 } 116 | | CfgInstruction::Or { dest, src1, src2 } => { 117 | self.update_variable_lifetime(*dest); 118 | self.update_variable_lifetime(*src1); 119 | self.update_variable_lifetime(*src2); 120 | } 121 | CfgInstruction::Negate { dest, src } 122 | | CfgInstruction::Not { dest, src } 123 | | CfgInstruction::Move { dest, src } => { 124 | self.update_variable_lifetime(*dest); 125 | self.update_variable_lifetime(*src); 126 | } 127 | CfgInstruction::StringConst { dest, .. } 128 | | CfgInstruction::NumberConst { dest, .. } 129 | | CfgInstruction::BooleanConst { dest, .. } 130 | | CfgInstruction::FunctionConst { dest, .. } => { 131 | self.update_variable_lifetime(*dest); 132 | } 133 | CfgInstruction::Call => {} 134 | CfgInstruction::Print { src } => {} 135 | } 136 | 137 | //println!(" {instruction}"); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/cfg_ir/instruction.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::fmt::{Display, Formatter}; 3 | 4 | use super::operand::Operand; 5 | 6 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 7 | pub enum CfgOpcode { 8 | // Binary arithmetic 9 | Add, 10 | Subtract, 11 | Multiply, 12 | Divide, 13 | Modulo, 14 | 15 | // Comparisons 16 | Equal, 17 | NotEqual, 18 | Greater, 19 | GreaterEqual, 20 | Less, 21 | LessEqual, 22 | 23 | // Unary operations 24 | Negate, 25 | Not, 26 | 27 | // Data movement 28 | Move, 29 | MoveArg, 30 | 31 | // Calls & side effects 32 | Call, 33 | Print, 34 | } 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct Instruction { 38 | pub op_code: CfgOpcode, 39 | pub dest: Operand, 40 | pub src1: Operand, 41 | pub src2: Operand, 42 | } 43 | 44 | impl Instruction { 45 | // --- Binary operations --- 46 | pub fn add(dest: Operand, src1: Operand, src2: Operand) -> Self { 47 | Self { 48 | op_code: CfgOpcode::Add, 49 | dest, 50 | src1, 51 | src2, 52 | } 53 | } 54 | 55 | pub fn subtract(dest: Operand, src1: Operand, src2: Operand) -> Self { 56 | Self { 57 | op_code: CfgOpcode::Subtract, 58 | dest, 59 | src1, 60 | src2, 61 | } 62 | } 63 | 64 | pub fn multiply(dest: Operand, src1: Operand, src2: Operand) -> Self { 65 | Self { 66 | op_code: CfgOpcode::Multiply, 67 | dest, 68 | src1, 69 | src2, 70 | } 71 | } 72 | 73 | pub fn divide(dest: Operand, src1: Operand, src2: Operand) -> Self { 74 | Self { 75 | op_code: CfgOpcode::Divide, 76 | dest, 77 | src1, 78 | src2, 79 | } 80 | } 81 | 82 | pub fn modulo(dest: Operand, src1: Operand, src2: Operand) -> Self { 83 | Self { 84 | op_code: CfgOpcode::Modulo, 85 | dest, 86 | src1, 87 | src2, 88 | } 89 | } 90 | 91 | // --- Comparisons --- 92 | pub fn equal(dest: Operand, src1: Operand, src2: Operand) -> Self { 93 | Self { 94 | op_code: CfgOpcode::Equal, 95 | dest, 96 | src1, 97 | src2, 98 | } 99 | } 100 | 101 | pub fn not_equal(dest: Operand, src1: Operand, src2: Operand) -> Self { 102 | Self { 103 | op_code: CfgOpcode::NotEqual, 104 | dest, 105 | src1, 106 | src2, 107 | } 108 | } 109 | 110 | pub fn greater(dest: Operand, src1: Operand, src2: Operand) -> Self { 111 | Self { 112 | op_code: CfgOpcode::Greater, 113 | dest, 114 | src1, 115 | src2, 116 | } 117 | } 118 | 119 | pub fn greater_equal(dest: Operand, src1: Operand, src2: Operand) -> Self { 120 | Self { 121 | op_code: CfgOpcode::GreaterEqual, 122 | dest, 123 | src1, 124 | src2, 125 | } 126 | } 127 | 128 | pub fn less(dest: Operand, src1: Operand, src2: Operand) -> Self { 129 | Self { 130 | op_code: CfgOpcode::Less, 131 | dest, 132 | src1, 133 | src2, 134 | } 135 | } 136 | 137 | pub fn less_equal(dest: Operand, src1: Operand, src2: Operand) -> Self { 138 | Self { 139 | op_code: CfgOpcode::LessEqual, 140 | dest, 141 | src1, 142 | src2, 143 | } 144 | } 145 | 146 | // --- Unary operations --- 147 | pub fn negate(dest: Operand, src: Operand) -> Self { 148 | Self { 149 | op_code: CfgOpcode::Negate, 150 | dest, 151 | src1: src, 152 | src2: Operand::None, 153 | } 154 | } 155 | 156 | pub fn not(dest: Operand, src: Operand) -> Self { 157 | Self { 158 | op_code: CfgOpcode::Not, 159 | dest, 160 | src1: src, 161 | src2: Operand::None, 162 | } 163 | } 164 | 165 | // --- Moves --- 166 | pub fn move_(dest: Operand, src: Operand) -> Self { 167 | Self { 168 | op_code: CfgOpcode::Move, 169 | dest, 170 | src1: src, 171 | src2: Operand::None, 172 | } 173 | } 174 | 175 | pub fn move_arg(dest: Operand, src: Operand) -> Self { 176 | Self { 177 | op_code: CfgOpcode::MoveArg, 178 | dest, 179 | src1: src, 180 | src2: Operand::None, 181 | } 182 | } 183 | 184 | // --- Calls and effects --- 185 | pub fn call(dest: Operand, src: Operand) -> Self { 186 | Self { 187 | op_code: CfgOpcode::Call, 188 | dest, 189 | src1: src, 190 | src2: Operand::None, 191 | } 192 | } 193 | 194 | pub fn print(src: Operand) -> Self { 195 | Self { 196 | op_code: CfgOpcode::Print, 197 | dest: Operand::None, 198 | src1: src, 199 | src2: Operand::None, 200 | } 201 | } 202 | } 203 | 204 | impl Display for Instruction { 205 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 206 | use CfgOpcode::*; 207 | 208 | match self.op_code { 209 | Add => write!(f, "{} = {} + {}", self.dest, self.src1, self.src2), 210 | Subtract => write!(f, "{} = {} - {}", self.dest, self.src1, self.src2), 211 | Multiply => write!(f, "{} = {} * {}", self.dest, self.src1, self.src2), 212 | Divide => write!(f, "{} = {} / {}", self.dest, self.src1, self.src2), 213 | Modulo => write!(f, "{} = {} % {}", self.dest, self.src1, self.src2), 214 | 215 | Equal => write!(f, "{} = {} == {}", self.dest, self.src1, self.src2), 216 | NotEqual => write!(f, "{} = {} != {}", self.dest, self.src1, self.src2), 217 | Greater => write!(f, "{} = {} > {}", self.dest, self.src1, self.src2), 218 | GreaterEqual => write!(f, "{} = {} >= {}", self.dest, self.src1, self.src2), 219 | Less => write!(f, "{} = {} < {}", self.dest, self.src1, self.src2), 220 | LessEqual => write!(f, "{} = {} <= {}", self.dest, self.src1, self.src2), 221 | 222 | Negate => write!(f, "{} = -{}", self.dest, self.src1), 223 | Not => write!(f, "{} = !{}", self.dest, self.src1), 224 | Move => write!(f, "{} = {}", self.dest, self.src1), 225 | MoveArg => write!(f, "arg({}) = {}", self.dest, self.src1), 226 | Call => write!(f, "{} = call {}", self.dest, self.src1), 227 | Print => write!(f, "print {}", self.src1), 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/bytecode/bytecode.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::fmt::{Display, Formatter}; 3 | 4 | use super::function::Function; 5 | use crate::bytecode::op_code::Opcode; 6 | 7 | pub struct Bytecode { 8 | pub bytes: Vec, 9 | pub functions: Vec, 10 | } 11 | 12 | impl Bytecode { 13 | pub fn new(bytes: Vec, functions: Vec) -> Self { 14 | Self { bytes, functions } 15 | } 16 | } 17 | 18 | impl Display for Bytecode { 19 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 20 | let mut index = 0; 21 | 22 | while index < self.bytes.len() { 23 | let op_code = Opcode::from(self.bytes[index]); 24 | 25 | match op_code { 26 | // === Arithmetic === 27 | Opcode::AddRR 28 | | Opcode::SubtractRR 29 | | Opcode::MultiplyRR 30 | | Opcode::DivideRR 31 | | Opcode::ModuloRR 32 | | Opcode::EqualRR 33 | | Opcode::NotEqualRR 34 | | Opcode::GreaterRR 35 | | Opcode::GreaterEqualRR 36 | | Opcode::LessRR 37 | | Opcode::LessEqualRR => { 38 | writeln!( 39 | f, 40 | "{:?} r{}, r{}, r{}", 41 | op_code, 42 | self.bytes[index + 1], 43 | self.bytes[index + 2], 44 | self.bytes[index + 3] 45 | )?; 46 | index += 4; 47 | } 48 | 49 | Opcode::AddRK 50 | | Opcode::SubtractRK 51 | | Opcode::MultiplyRK 52 | | Opcode::DivideRK 53 | | Opcode::ModuloRK 54 | | Opcode::EqualRK 55 | | Opcode::NotEqualRK 56 | | Opcode::GreaterRK 57 | | Opcode::GreaterEqualRK 58 | | Opcode::LessRK 59 | | Opcode::LessEqualRK => { 60 | writeln!( 61 | f, 62 | "{:?} r{}, r{}, k{}", 63 | op_code, 64 | self.bytes[index + 1], 65 | self.bytes[index + 2], 66 | self.bytes[index + 3] 67 | )?; 68 | index += 4; 69 | } 70 | 71 | Opcode::AddKR 72 | | Opcode::SubtractKR 73 | | Opcode::MultiplyKR 74 | | Opcode::DivideKR 75 | | Opcode::ModuloKR 76 | | Opcode::EqualKR 77 | | Opcode::NotEqualKR 78 | | Opcode::GreaterKR 79 | | Opcode::GreaterEqualKR 80 | | Opcode::LessKR 81 | | Opcode::LessEqualKR => { 82 | writeln!( 83 | f, 84 | "{:?} r{}, k{}, r{}", 85 | op_code, 86 | self.bytes[index + 1], 87 | self.bytes[index + 2], 88 | self.bytes[index + 3] 89 | )?; 90 | index += 4; 91 | } 92 | 93 | Opcode::AddKK 94 | | Opcode::SubtractKK 95 | | Opcode::MultiplyKK 96 | | Opcode::DivideKK 97 | | Opcode::ModuloKK 98 | | Opcode::EqualKK 99 | | Opcode::NotEqualKK 100 | | Opcode::GreaterKK 101 | | Opcode::GreaterEqualKK 102 | | Opcode::LessKK 103 | | Opcode::LessEqualKK => { 104 | writeln!( 105 | f, 106 | "{:?} r{}, k{}, k{}", 107 | op_code, 108 | self.bytes[index + 1], 109 | self.bytes[index + 2], 110 | self.bytes[index + 3] 111 | )?; 112 | index += 4; 113 | } 114 | 115 | // === Unary === 116 | Opcode::NegateR | Opcode::NotR => { 117 | writeln!( 118 | f, 119 | "{:?} r{}, r{}", 120 | op_code, 121 | self.bytes[index + 1], 122 | self.bytes[index + 2] 123 | )?; 124 | index += 3; 125 | } 126 | 127 | Opcode::NegateK | Opcode::NotK => { 128 | writeln!( 129 | f, 130 | "{:?} r{}, k{}", 131 | op_code, 132 | self.bytes[index + 1], 133 | self.bytes[index + 2] 134 | )?; 135 | index += 3; 136 | } 137 | 138 | // === Data movement === 139 | Opcode::MoveR => { 140 | writeln!( 141 | f, 142 | "{:?} r{}, r{}", 143 | op_code, 144 | self.bytes[index + 1], 145 | self.bytes[index + 2] 146 | )?; 147 | index += 3; 148 | } 149 | 150 | Opcode::MoveK => { 151 | writeln!( 152 | f, 153 | "{:?} r{}, k{}", 154 | op_code, 155 | self.bytes[index + 1], 156 | self.bytes[index + 2] 157 | )?; 158 | index += 3; 159 | } 160 | 161 | // === Function and return === 162 | Opcode::CallR => { 163 | writeln!( 164 | f, 165 | "{:?} r{}, r{}", 166 | op_code, 167 | self.bytes[index + 1], 168 | self.bytes[index + 2] 169 | )?; 170 | index += 3; 171 | } 172 | 173 | Opcode::CallK => { 174 | writeln!( 175 | f, 176 | "{:?} r{}, k{}", 177 | op_code, 178 | self.bytes[index + 1], 179 | self.bytes[index + 2] 180 | )?; 181 | index += 3; 182 | } 183 | 184 | Opcode::ReturnR => { 185 | writeln!(f, "{:?} r{}", op_code, self.bytes[index + 1])?; 186 | index += 2; 187 | } 188 | 189 | Opcode::ReturnK => { 190 | writeln!(f, "{:?} k{}", op_code, self.bytes[index + 1])?; 191 | index += 2; 192 | } 193 | 194 | Opcode::ReturnVoid => { 195 | writeln!(f, "{:?}", op_code)?; 196 | index += 1; 197 | } 198 | 199 | // === Control flow === 200 | Opcode::Jump => { 201 | writeln!(f, "{:?} {}", op_code, self.bytes[index + 1] as i16)?; 202 | index += 2; 203 | } 204 | 205 | Opcode::JumpIfTrueR | Opcode::JumpIfFalseR => { 206 | writeln!( 207 | f, 208 | "{:?} r{}, {}", 209 | op_code, 210 | self.bytes[index + 1], 211 | self.bytes[index + 2] as i16 212 | )?; 213 | index += 3; 214 | } 215 | 216 | Opcode::JumpIfTrueK | Opcode::JumpIfFalseK => { 217 | writeln!( 218 | f, 219 | "{:?} k{}, {}", 220 | op_code, 221 | self.bytes[index + 1], 222 | self.bytes[index + 2] as i16 223 | )?; 224 | index += 3; 225 | } 226 | 227 | // === IO === 228 | Opcode::PrintR => { 229 | writeln!(f, "{:?} r{}", op_code, self.bytes[index + 1])?; 230 | index += 2; 231 | } 232 | 233 | Opcode::PrintK => { 234 | writeln!(f, "{:?} k{}", op_code, self.bytes[index + 1])?; 235 | index += 2; 236 | } 237 | 238 | // === Program termination === 239 | Opcode::Halt => { 240 | writeln!(f, "Halt")?; 241 | index += 1; 242 | } 243 | } 244 | } 245 | 246 | Ok(()) 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/lexer/lexer.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::kaori_error::KaoriError, kaori_error}; 2 | 3 | use super::{span::Span, token::Token, token_kind::TokenKind}; 4 | 5 | pub struct Lexer { 6 | source: Vec, 7 | index: usize, 8 | tokens: Vec, 9 | } 10 | 11 | impl Lexer { 12 | pub fn new(source: &str) -> Self { 13 | Self { 14 | source: source.chars().collect(), 15 | index: 0, 16 | tokens: Vec::new(), 17 | } 18 | } 19 | 20 | fn at_end(&mut self) -> bool { 21 | self.index >= self.source.len() 22 | } 23 | 24 | fn look_ahead(&mut self, expected: &str) -> bool { 25 | for (i, expected) in expected.chars().enumerate() { 26 | let j = self.index + i; 27 | 28 | if Some(&expected) != self.source.get(j) { 29 | return false; 30 | } 31 | } 32 | 33 | true 34 | } 35 | 36 | fn white_space(&mut self) { 37 | while !self.at_end() && self.source[self.index].is_whitespace() { 38 | self.index += 1; 39 | } 40 | } 41 | 42 | fn multiline_comment(&mut self) { 43 | self.index += 2; 44 | 45 | while !self.at_end() && !self.look_ahead("*/") { 46 | self.index += 1; 47 | } 48 | 49 | self.index += 2; 50 | } 51 | 52 | fn line_comment(&mut self) { 53 | self.index += 2; 54 | 55 | while !self.at_end() && !self.look_ahead("\n") { 56 | self.index += 1; 57 | } 58 | 59 | self.index += 1; 60 | } 61 | 62 | fn identifier_or_keyword(&mut self) { 63 | let start = self.index; 64 | 65 | while !self.at_end() && self.source[self.index].is_alphabetic() { 66 | self.index += 1; 67 | } 68 | 69 | while !self.at_end() 70 | && (self.source[self.index].is_alphanumeric() || self.source[self.index] == '_') 71 | { 72 | self.index += 1; 73 | } 74 | let sub: String = self.source[start..self.index].iter().collect(); 75 | 76 | let kind = match sub.as_str() { 77 | "if" => TokenKind::If, 78 | "else" => TokenKind::Else, 79 | "while" => TokenKind::While, 80 | "for" => TokenKind::For, 81 | "break" => TokenKind::Break, 82 | "continue" => TokenKind::Continue, 83 | "return" => TokenKind::Return, 84 | "fun" => TokenKind::Function, 85 | "struct" => TokenKind::Struct, 86 | "print" => TokenKind::Print, 87 | "true" => TokenKind::True, 88 | "false" => TokenKind::False, 89 | "bool" => TokenKind::Bool, 90 | "number" => TokenKind::Number, 91 | "and" => TokenKind::And, 92 | "or" => TokenKind::Or, 93 | "not" => TokenKind::Not, 94 | _ => TokenKind::Identifier, 95 | }; 96 | 97 | let end = self.index; 98 | let token = Token::new(kind, start, end); 99 | self.tokens.push(token); 100 | } 101 | 102 | fn number_literal(&mut self) { 103 | let start = self.index; 104 | 105 | while !self.at_end() && self.source[self.index].is_ascii_digit() { 106 | self.index += 1; 107 | } 108 | 109 | if !self.at_end() && self.source[self.index] == '.' { 110 | self.index += 1; 111 | } 112 | 113 | while !self.at_end() && self.source[self.index].is_ascii_digit() { 114 | self.index += 1; 115 | } 116 | 117 | let end = self.index; 118 | let token = Token::new(TokenKind::NumberLiteral, start, end); 119 | 120 | self.tokens.push(token); 121 | } 122 | 123 | fn string_literal(&mut self) -> Result<(), KaoriError> { 124 | let start = self.index; 125 | 126 | self.index += 1; 127 | 128 | while !self.at_end() && self.source[self.index] != '"' { 129 | self.index += 1; 130 | } 131 | 132 | if self.at_end() { 133 | let end = self.index; 134 | let span = Span { start, end }; 135 | 136 | return Err(kaori_error!(span, "invalid unfinished string literal")); 137 | } 138 | 139 | self.index += 1; 140 | 141 | let end = self.index; 142 | let token = Token::new(TokenKind::StringLiteral, start, end); 143 | 144 | self.tokens.push(token); 145 | 146 | Ok(()) 147 | } 148 | 149 | pub fn symbol(&mut self) -> Result<(), KaoriError> { 150 | let start = self.index; 151 | 152 | let curr_char = self.source[self.index]; 153 | 154 | let kind = match curr_char { 155 | '+' => { 156 | if self.look_ahead("+=") { 157 | TokenKind::AddAssign 158 | } else { 159 | TokenKind::Plus 160 | } 161 | } 162 | '-' => { 163 | if self.look_ahead("-=") { 164 | TokenKind::SubtractAssign 165 | } else if self.look_ahead("->") { 166 | TokenKind::ThinArrow 167 | } else { 168 | TokenKind::Minus 169 | } 170 | } 171 | '*' => { 172 | if self.look_ahead("*=") { 173 | TokenKind::MultiplyAssign 174 | } else { 175 | TokenKind::Multiply 176 | } 177 | } 178 | '/' => { 179 | if self.look_ahead("/=") { 180 | TokenKind::DivideAssign 181 | } else { 182 | TokenKind::Divide 183 | } 184 | } 185 | '%' => { 186 | if self.look_ahead("%=") { 187 | TokenKind::ModuloAssign 188 | } else { 189 | TokenKind::Modulo 190 | } 191 | } 192 | '!' if self.look_ahead("!=") => TokenKind::NotEqual, 193 | '=' => { 194 | if self.look_ahead("==") { 195 | TokenKind::Equal 196 | } else { 197 | TokenKind::Assign 198 | } 199 | } 200 | '>' => { 201 | if self.look_ahead(">=") { 202 | TokenKind::GreaterEqual 203 | } else { 204 | TokenKind::Greater 205 | } 206 | } 207 | '<' => { 208 | if self.look_ahead("<=") { 209 | TokenKind::LessEqual 210 | } else { 211 | TokenKind::Less 212 | } 213 | } 214 | '(' => TokenKind::LeftParen, 215 | ')' => TokenKind::RightParen, 216 | '{' => TokenKind::LeftBrace, 217 | '}' => TokenKind::RightBrace, 218 | '$' => TokenKind::Dollar, 219 | ',' => TokenKind::Comma, 220 | ';' => TokenKind::Semicolon, 221 | ':' => TokenKind::Colon, 222 | _ => TokenKind::Invalid, 223 | }; 224 | 225 | if kind == TokenKind::Invalid { 226 | let end = self.index; 227 | let span = Span { start, end }; 228 | 229 | return Err(kaori_error!(span, "{} is not a valid token", curr_char)); 230 | } 231 | 232 | let size = match kind { 233 | TokenKind::AddAssign 234 | | TokenKind::SubtractAssign 235 | | TokenKind::MultiplyAssign 236 | | TokenKind::DivideAssign 237 | | TokenKind::ModuloAssign 238 | | TokenKind::NotEqual 239 | | TokenKind::Equal 240 | | TokenKind::GreaterEqual 241 | | TokenKind::LessEqual 242 | | TokenKind::ThinArrow => 2, 243 | _ => 1, 244 | }; 245 | 246 | self.index += size; 247 | 248 | let end = self.index; 249 | let token = Token::new(kind, start, end); 250 | 251 | self.tokens.push(token); 252 | Ok(()) 253 | } 254 | 255 | pub fn get_next_token(&mut self) -> Result<(), KaoriError> { 256 | let c = self.source[self.index]; 257 | 258 | match c { 259 | '"' => self.string_literal()?, 260 | '/' if self.look_ahead("/*") => self.multiline_comment(), 261 | '/' if self.look_ahead("//") => self.line_comment(), 262 | c if c.is_alphabetic() => self.identifier_or_keyword(), 263 | '0'..='9' => self.number_literal(), 264 | c if c.is_whitespace() => self.white_space(), 265 | _ => self.symbol()?, 266 | }; 267 | Ok(()) 268 | } 269 | 270 | pub fn tokenize(mut self) -> Result, KaoriError> { 271 | while !self.at_end() { 272 | self.get_next_token()?; 273 | } 274 | 275 | let span = Span { 276 | start: self.index - 1, 277 | end: self.index - 1, 278 | }; 279 | 280 | let token = Token { 281 | kind: TokenKind::EndOfFile, 282 | span, 283 | }; 284 | 285 | self.tokens.push(token); 286 | 287 | Ok(self.tokens) 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/syntax/parse_expression.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::kaori_error::KaoriError, kaori_error, lexer::token_kind::TokenKind}; 2 | 3 | use super::{ 4 | assign_op::{AssignOp, AssignOpKind}, 5 | binary_op::{BinaryOp, BinaryOpKind}, 6 | expr::Expr, 7 | parser::Parser, 8 | unary_op::{UnaryOp, UnaryOpKind}, 9 | }; 10 | 11 | impl<'a> Parser<'a> { 12 | fn build_binary_operator(&mut self) -> BinaryOp { 13 | let token_kind = self.token_stream.token_kind(); 14 | let span = self.token_stream.span(); 15 | 16 | let kind = match token_kind { 17 | TokenKind::Plus => BinaryOpKind::Add, 18 | TokenKind::Minus => BinaryOpKind::Subtract, 19 | TokenKind::Multiply => BinaryOpKind::Multiply, 20 | TokenKind::Divide => BinaryOpKind::Divide, 21 | TokenKind::Modulo => BinaryOpKind::Modulo, 22 | TokenKind::And => BinaryOpKind::And, 23 | TokenKind::Or => BinaryOpKind::Or, 24 | TokenKind::Equal => BinaryOpKind::Equal, 25 | TokenKind::NotEqual => BinaryOpKind::NotEqual, 26 | TokenKind::Greater => BinaryOpKind::Greater, 27 | TokenKind::GreaterEqual => BinaryOpKind::GreaterEqual, 28 | TokenKind::Less => BinaryOpKind::Less, 29 | TokenKind::LessEqual => BinaryOpKind::LessEqual, 30 | _ => unreachable!(), 31 | }; 32 | 33 | BinaryOp::new(kind, span) 34 | } 35 | 36 | fn build_unary_operator(&mut self) -> UnaryOp { 37 | let token_kind = self.token_stream.token_kind(); 38 | let span = self.token_stream.span(); 39 | 40 | let kind = match token_kind { 41 | TokenKind::Minus => UnaryOpKind::Negate, 42 | TokenKind::Not => UnaryOpKind::Not, 43 | _ => unreachable!(), 44 | }; 45 | 46 | UnaryOp::new(kind, span) 47 | } 48 | 49 | pub fn parse_expression(&mut self) -> Result { 50 | self.parse_assign() 51 | } 52 | 53 | fn parse_assign(&mut self) -> Result { 54 | let left = self.parse_or()?; 55 | 56 | let kind = self.token_stream.token_kind(); 57 | let span = self.token_stream.span(); 58 | 59 | let operator = match kind { 60 | TokenKind::Assign => AssignOpKind::Assign, 61 | TokenKind::AddAssign => AssignOpKind::AddAssign, 62 | TokenKind::SubtractAssign => AssignOpKind::SubtractAssign, 63 | TokenKind::MultiplyAssign => AssignOpKind::MultiplyAssign, 64 | TokenKind::DivideAssign => AssignOpKind::DivideAssign, 65 | TokenKind::ModuloAssign => AssignOpKind::ModuloAssign, 66 | _ => return Ok(left), 67 | }; 68 | 69 | let operator = AssignOp::new(operator, span); 70 | 71 | self.token_stream.advance(); 72 | 73 | let right = self.parse_or()?; 74 | 75 | Ok(Expr::assign(operator, left, right)) 76 | } 77 | 78 | fn parse_or(&mut self) -> Result { 79 | let mut left = self.parse_and()?; 80 | 81 | while !self.token_stream.at_end() { 82 | let kind = self.token_stream.token_kind(); 83 | 84 | let operator = match kind { 85 | TokenKind::Or => self.build_binary_operator(), 86 | _ => break, 87 | }; 88 | 89 | self.token_stream.advance(); 90 | 91 | let right = self.parse_and()?; 92 | 93 | left = Expr::binary(operator, left, right); 94 | } 95 | 96 | Ok(left) 97 | } 98 | 99 | fn parse_and(&mut self) -> Result { 100 | let mut left = self.parse_equality()?; 101 | 102 | while !self.token_stream.at_end() { 103 | let kind = self.token_stream.token_kind(); 104 | 105 | let operator = match kind { 106 | TokenKind::And => self.build_binary_operator(), 107 | _ => break, 108 | }; 109 | 110 | self.token_stream.advance(); 111 | let right = self.parse_equality()?; 112 | 113 | left = Expr::binary(operator, left, right); 114 | } 115 | 116 | Ok(left) 117 | } 118 | 119 | fn parse_equality(&mut self) -> Result { 120 | let mut left = self.parse_comparison()?; 121 | 122 | while !self.token_stream.at_end() { 123 | let kind = self.token_stream.token_kind(); 124 | 125 | let operator = match kind { 126 | TokenKind::Equal | TokenKind::NotEqual => self.build_binary_operator(), 127 | _ => break, 128 | }; 129 | 130 | self.token_stream.advance(); 131 | let right = self.parse_comparison()?; 132 | 133 | left = Expr::binary(operator, left, right); 134 | } 135 | 136 | Ok(left) 137 | } 138 | 139 | fn parse_comparison(&mut self) -> Result { 140 | let mut left = self.parse_term()?; 141 | 142 | while !self.token_stream.at_end() { 143 | let kind = self.token_stream.token_kind(); 144 | 145 | let operator = match kind { 146 | TokenKind::Greater 147 | | TokenKind::GreaterEqual 148 | | TokenKind::Less 149 | | TokenKind::LessEqual => self.build_binary_operator(), 150 | _ => break, 151 | }; 152 | 153 | self.token_stream.advance(); 154 | let right = self.parse_term()?; 155 | 156 | left = Expr::binary(operator, left, right); 157 | } 158 | 159 | Ok(left) 160 | } 161 | 162 | fn parse_term(&mut self) -> Result { 163 | let mut left = self.parse_factor()?; 164 | 165 | while !self.token_stream.at_end() { 166 | let kind = self.token_stream.token_kind(); 167 | 168 | let operator = match kind { 169 | TokenKind::Plus | TokenKind::Minus => self.build_binary_operator(), 170 | _ => break, 171 | }; 172 | 173 | self.token_stream.advance(); 174 | let right = self.parse_factor()?; 175 | 176 | left = Expr::binary(operator, left, right); 177 | } 178 | 179 | Ok(left) 180 | } 181 | 182 | fn parse_factor(&mut self) -> Result { 183 | let mut left = self.parse_prefix_unary()?; 184 | 185 | while !self.token_stream.at_end() { 186 | let kind = self.token_stream.token_kind(); 187 | 188 | let operator = match kind { 189 | TokenKind::Multiply | TokenKind::Divide | TokenKind::Modulo => { 190 | self.build_binary_operator() 191 | } 192 | _ => break, 193 | }; 194 | 195 | self.token_stream.advance(); 196 | let right = self.parse_prefix_unary()?; 197 | 198 | left = Expr::binary(operator, left, right); 199 | } 200 | 201 | Ok(left) 202 | } 203 | 204 | fn parse_prefix_unary(&mut self) -> Result { 205 | let kind = self.token_stream.token_kind(); 206 | 207 | let operator = match kind { 208 | TokenKind::Plus => { 209 | self.token_stream.advance(); 210 | return self.parse_prefix_unary(); 211 | } 212 | TokenKind::Not => { 213 | let operator = self.build_unary_operator(); 214 | self.token_stream.advance(); 215 | 216 | let right = self.parse_or()?; 217 | 218 | return Ok(Expr::unary(operator, right)); 219 | } 220 | TokenKind::Minus => self.build_unary_operator(), 221 | _ => return self.parse_primary(), 222 | }; 223 | 224 | self.token_stream.advance(); 225 | 226 | let right = self.parse_prefix_unary()?; 227 | 228 | Ok(Expr::unary(operator, right)) 229 | } 230 | 231 | fn parse_primary(&mut self) -> Result { 232 | let kind = self.token_stream.token_kind(); 233 | let span = self.token_stream.span(); 234 | 235 | let primary = match kind { 236 | TokenKind::LeftParen => { 237 | self.token_stream.consume(TokenKind::LeftParen)?; 238 | let expr = self.parse_expression()?; 239 | self.token_stream.consume(TokenKind::RightParen)?; 240 | expr 241 | } 242 | TokenKind::NumberLiteral => { 243 | let value = match self.token_stream.lexeme().parse::() { 244 | Ok(value) => Ok(value), 245 | Err(..) => Err(kaori_error!(span, "expected a valid float to be parsed")), 246 | }?; 247 | 248 | self.token_stream.advance(); 249 | 250 | Expr::number_literal(value, span) 251 | } 252 | TokenKind::True => { 253 | self.token_stream.advance(); 254 | 255 | Expr::boolean_literal(true, span) 256 | } 257 | TokenKind::False => { 258 | self.token_stream.advance(); 259 | 260 | Expr::boolean_literal(false, span) 261 | } 262 | TokenKind::StringLiteral => { 263 | let value = self.token_stream.lexeme().to_owned(); 264 | self.token_stream.advance(); 265 | 266 | Expr::string_literal(value, span) 267 | } 268 | TokenKind::Identifier => self.parse_postfix_unary()?, 269 | _ => { 270 | let span = self.token_stream.span(); 271 | 272 | return Err(kaori_error!( 273 | span, 274 | "expected a valid operand, but found: {}", 275 | kind 276 | )); 277 | } 278 | }; 279 | 280 | Ok(primary) 281 | } 282 | 283 | fn parse_identifier(&mut self) -> Result { 284 | let name = self.token_stream.lexeme().to_owned(); 285 | let span = self.token_stream.span(); 286 | 287 | let identifier = Expr::identifier(name, span); 288 | 289 | self.token_stream.consume(TokenKind::Identifier)?; 290 | 291 | Ok(identifier) 292 | } 293 | 294 | fn parse_postfix_unary(&mut self) -> Result { 295 | let identifier = self.parse_identifier()?; 296 | 297 | let kind = self.token_stream.token_kind(); 298 | 299 | Ok(match kind { 300 | TokenKind::LeftParen => self.parse_function_call(identifier)?, 301 | _ => identifier, 302 | }) 303 | } 304 | 305 | fn parse_function_call(&mut self, callee: Expr) -> Result { 306 | if self.token_stream.token_kind() != TokenKind::LeftParen { 307 | return Ok(callee); 308 | } 309 | 310 | self.token_stream.consume(TokenKind::LeftParen)?; 311 | 312 | let arguments = 313 | self.parse_comma_separator(Parser::parse_expression, TokenKind::RightParen)?; 314 | 315 | let span = self.token_stream.span(); 316 | 317 | self.token_stream.consume(TokenKind::RightParen)?; 318 | 319 | let callee = Expr::function_call(callee, arguments, span); 320 | 321 | self.parse_function_call(callee) 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /src/bytecode/emit_bytecode.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ 4 | bytecode::op_code::Opcode, 5 | cfg_ir::{ 6 | basic_block::{BasicBlock, Terminator}, 7 | cfg_constants::CfgConstant, 8 | cfg_function::CfgFunction, 9 | graph_traversal::reversed_postorder, 10 | instruction::{CfgOpcode, Instruction}, 11 | operand::Operand, 12 | }, 13 | }; 14 | 15 | use super::{bytecode::Bytecode, function::Function, value::Value}; 16 | 17 | pub fn emit_bytecode(cfgs: Vec) -> Bytecode { 18 | let mut instructions = Vec::new(); 19 | let mut functions_start_index = Vec::new(); 20 | 21 | for cfg in &cfgs { 22 | let index = instructions.len(); 23 | functions_start_index.push(index); 24 | 25 | let mut context = CodegenContext::new( 26 | &cfg.basic_blocks, 27 | cfg.allocated_variables, 28 | &mut instructions, 29 | ); 30 | 31 | context.emit_instructions(); 32 | } 33 | 34 | instructions.push(Opcode::Halt as u16); 35 | 36 | let mut functions = Vec::new(); 37 | 38 | for (index, cfg) in cfgs.iter().enumerate() { 39 | let constants = map_cfg_constants(&cfg.constants); 40 | let frame_size = cfg.allocated_variables; 41 | 42 | let ip = unsafe { instructions.as_ptr().add(functions_start_index[index]) }; 43 | 44 | let function = Function::new(ip, frame_size as u8, constants); 45 | 46 | functions.push(function); 47 | } 48 | 49 | Bytecode::new(instructions, functions) 50 | } 51 | 52 | struct CodegenContext<'a> { 53 | basic_blocks: &'a [BasicBlock], 54 | frame_size: usize, 55 | instructions: &'a mut Vec, 56 | } 57 | 58 | impl<'a> CodegenContext<'a> { 59 | fn new( 60 | basic_blocks: &'a [BasicBlock], 61 | frame_size: usize, 62 | instructions: &'a mut Vec, 63 | ) -> Self { 64 | Self { 65 | basic_blocks, 66 | frame_size, 67 | instructions, 68 | } 69 | } 70 | 71 | fn emit_instructions(&mut self) { 72 | let basic_blocks = reversed_postorder(self.basic_blocks); 73 | 74 | let mut pending_backpatch = Vec::new(); 75 | let mut bb_start_index = HashMap::new(); 76 | 77 | for (index, bb_index) in basic_blocks.iter().copied().enumerate() { 78 | bb_start_index.insert(bb_index, self.instructions.len()); 79 | 80 | let next_bb_index = basic_blocks.get(index + 1).copied(); 81 | 82 | self.visit_block(bb_index, next_bb_index, &mut pending_backpatch); 83 | } 84 | 85 | resolve_backpatches(self.instructions, &pending_backpatch, &bb_start_index); 86 | } 87 | 88 | fn visit_block( 89 | &mut self, 90 | index: usize, 91 | next_bb_index: Option, 92 | pending_backpatch: &mut Vec<(usize, usize)>, 93 | ) { 94 | let basic_block = &self.basic_blocks[index]; 95 | 96 | for instruction in &basic_block.instructions { 97 | self.visit_instruction(instruction); 98 | } 99 | 100 | match basic_block.terminator { 101 | Terminator::Branch { 102 | src, 103 | r#true, 104 | r#false, 105 | } => { 106 | if Some(r#true) != next_bb_index { 107 | let index = self.instructions.len(); 108 | pending_backpatch.push((index, r#true)); 109 | 110 | match src { 111 | Operand::Constant(value) => { 112 | self.instructions.push(Opcode::JumpIfTrueK as u16); 113 | self.instructions.push(value as u16); 114 | } 115 | Operand::Variable(value) => { 116 | self.instructions.push(Opcode::JumpIfTrueR as u16); 117 | self.instructions.push(value as u16); 118 | } 119 | _ => {} 120 | } 121 | 122 | self.instructions.push(0); 123 | } 124 | 125 | if Some(r#false) != next_bb_index { 126 | let index = self.instructions.len(); 127 | pending_backpatch.push((index, r#false)); 128 | 129 | match src { 130 | Operand::Constant(value) => { 131 | self.instructions.push(Opcode::JumpIfFalseK as u16); 132 | self.instructions.push(value as u16); 133 | } 134 | Operand::Variable(value) => { 135 | self.instructions.push(Opcode::JumpIfFalseR as u16); 136 | self.instructions.push(value as u16); 137 | } 138 | _ => {} 139 | } 140 | 141 | self.instructions.push(0); 142 | } 143 | } 144 | Terminator::Goto(target) => { 145 | if Some(target) != next_bb_index { 146 | let index = self.instructions.len(); 147 | pending_backpatch.push((index, target)); 148 | 149 | self.instructions.push(Opcode::Jump as u16); 150 | self.instructions.push(0); 151 | } 152 | } 153 | Terminator::Return { src } => { 154 | match src { 155 | Some(Operand::Constant(value)) => { 156 | self.instructions.push(Opcode::ReturnK as u16); 157 | self.instructions.push(value as u16); 158 | } 159 | Some(Operand::Variable(value)) => { 160 | self.instructions.push(Opcode::ReturnR as u16); 161 | self.instructions.push(value as u16); 162 | } 163 | _ => { 164 | self.instructions.push(Opcode::ReturnVoid as u16); 165 | } 166 | }; 167 | } 168 | Terminator::None => {} 169 | }; 170 | } 171 | 172 | fn visit_instruction(&mut self, instruction: &Instruction) { 173 | let Instruction { 174 | op_code, 175 | mut dest, 176 | src1, 177 | src2, 178 | } = *instruction; 179 | use CfgOpcode::*; 180 | use Operand::*; 181 | 182 | let op_code = match (op_code, src1, src2) { 183 | // === Arithmetic === 184 | (Add, Variable(_), Variable(_)) => Opcode::AddRR, 185 | (Add, Variable(_), Constant(_)) => Opcode::AddRK, 186 | (Add, Constant(_), Variable(_)) => Opcode::AddKR, 187 | (Add, Constant(_), Constant(_)) => Opcode::AddKK, 188 | 189 | (Subtract, Variable(_), Variable(_)) => Opcode::SubtractRR, 190 | (Subtract, Variable(_), Constant(_)) => Opcode::SubtractRK, 191 | (Subtract, Constant(_), Variable(_)) => Opcode::SubtractKR, 192 | (Subtract, Constant(_), Constant(_)) => Opcode::SubtractKK, 193 | 194 | (Multiply, Variable(_), Variable(_)) => Opcode::MultiplyRR, 195 | (Multiply, Variable(_), Constant(_)) => Opcode::MultiplyRK, 196 | (Multiply, Constant(_), Variable(_)) => Opcode::MultiplyKR, 197 | (Multiply, Constant(_), Constant(_)) => Opcode::MultiplyKK, 198 | 199 | (Divide, Variable(_), Variable(_)) => Opcode::DivideRR, 200 | (Divide, Variable(_), Constant(_)) => Opcode::DivideRK, 201 | (Divide, Constant(_), Variable(_)) => Opcode::DivideKR, 202 | (Divide, Constant(_), Constant(_)) => Opcode::DivideKK, 203 | 204 | (Modulo, Variable(_), Variable(_)) => Opcode::ModuloRR, 205 | (Modulo, Variable(_), Constant(_)) => Opcode::ModuloRK, 206 | (Modulo, Constant(_), Variable(_)) => Opcode::ModuloKR, 207 | (Modulo, Constant(_), Constant(_)) => Opcode::ModuloKK, 208 | 209 | // === Comparison === 210 | (Equal, Variable(_), Variable(_)) => Opcode::EqualRR, 211 | (Equal, Variable(_), Constant(_)) => Opcode::EqualRK, 212 | (Equal, Constant(_), Variable(_)) => Opcode::EqualKR, 213 | (Equal, Constant(_), Constant(_)) => Opcode::EqualKK, 214 | 215 | (NotEqual, Variable(_), Variable(_)) => Opcode::NotEqualRR, 216 | (NotEqual, Variable(_), Constant(_)) => Opcode::NotEqualRK, 217 | (NotEqual, Constant(_), Variable(_)) => Opcode::NotEqualKR, 218 | (NotEqual, Constant(_), Constant(_)) => Opcode::NotEqualKK, 219 | 220 | (Greater, Variable(_), Variable(_)) => Opcode::GreaterRR, 221 | (Greater, Variable(_), Constant(_)) => Opcode::GreaterRK, 222 | (Greater, Constant(_), Variable(_)) => Opcode::GreaterKR, 223 | (Greater, Constant(_), Constant(_)) => Opcode::GreaterKK, 224 | 225 | (GreaterEqual, Variable(_), Variable(_)) => Opcode::GreaterEqualRR, 226 | (GreaterEqual, Variable(_), Constant(_)) => Opcode::GreaterEqualRK, 227 | (GreaterEqual, Constant(_), Variable(_)) => Opcode::GreaterEqualKR, 228 | (GreaterEqual, Constant(_), Constant(_)) => Opcode::GreaterEqualKK, 229 | 230 | (Less, Variable(_), Variable(_)) => Opcode::LessRR, 231 | (Less, Variable(_), Constant(_)) => Opcode::LessRK, 232 | (Less, Constant(_), Variable(_)) => Opcode::LessKR, 233 | (Less, Constant(_), Constant(_)) => Opcode::LessKK, 234 | 235 | (LessEqual, Variable(_), Variable(_)) => Opcode::LessEqualRR, 236 | (LessEqual, Variable(_), Constant(_)) => Opcode::LessEqualRK, 237 | (LessEqual, Constant(_), Variable(_)) => Opcode::LessEqualKR, 238 | (LessEqual, Constant(_), Constant(_)) => Opcode::LessEqualKK, 239 | 240 | // === Unary === 241 | (Negate, Variable(_), None) => Opcode::NegateR, 242 | (Negate, Constant(_), None) => Opcode::NegateK, 243 | (Not, Variable(_), None) => Opcode::NotR, 244 | (Not, Constant(_), None) => Opcode::NotK, 245 | 246 | // === Data movement === 247 | (Move, Variable(_), None) => Opcode::MoveR, 248 | (Move, Constant(_), None) => Opcode::MoveK, 249 | 250 | (MoveArg, _, None) => { 251 | if let Operand::Variable(value) = dest { 252 | dest = Operand::Variable(self.frame_size + value); 253 | } 254 | 255 | match src1 { 256 | Operand::Variable(..) => Opcode::MoveR, 257 | Operand::Constant(..) => Opcode::MoveK, 258 | None => unreachable!("Invalid operand for move"), 259 | } 260 | } 261 | 262 | // === Function and control === 263 | (Call, Variable(_), _) => Opcode::CallR, 264 | (Call, Constant(_), _) => Opcode::CallK, 265 | 266 | (Print, Variable(_), _) => Opcode::PrintR, 267 | (Print, Constant(_), _) => Opcode::PrintK, 268 | 269 | // === Fallback === 270 | _ => unreachable!( 271 | "Invalid operand combination for {:?}: {:?}, {:?}", 272 | op_code, src1, src2 273 | ), 274 | }; 275 | 276 | self.instructions.push(op_code as u16); 277 | 278 | match dest { 279 | Constant(value) | Variable(value) => self.instructions.push(value as u16), 280 | _ => {} 281 | } 282 | 283 | match src1 { 284 | Constant(value) | Variable(value) => self.instructions.push(value as u16), 285 | _ => {} 286 | } 287 | 288 | match src2 { 289 | Constant(value) | Variable(value) => self.instructions.push(value as u16), 290 | _ => {} 291 | } 292 | } 293 | } 294 | 295 | fn resolve_backpatches( 296 | instructions: &mut [u16], 297 | pending_backpatch: &[(usize, usize)], 298 | bb_start_index: &HashMap, 299 | ) { 300 | for (instruction_index, bb_index) in pending_backpatch.iter().copied() { 301 | let bb_start_index = bb_start_index[&bb_index]; 302 | 303 | let offset = bb_start_index as i16 - instruction_index as i16; 304 | 305 | let instruction = instructions[instruction_index]; 306 | let op_code = Opcode::from(instruction); 307 | 308 | if let Opcode::Jump = op_code { 309 | instructions[instruction_index + 1] = offset as u16; 310 | } else { 311 | instructions[instruction_index + 2] = offset as u16; 312 | } 313 | } 314 | } 315 | 316 | fn map_cfg_constants(constants: &[CfgConstant]) -> Vec { 317 | constants 318 | .iter() 319 | .map(|constant| match constant { 320 | CfgConstant::Boolean(v) => Value::boolean(*v), 321 | CfgConstant::Number(v) => Value::number(**v), 322 | CfgConstant::Function(index) => Value::function(*index), 323 | _ => todo!(), 324 | }) 325 | .collect() 326 | } 327 | -------------------------------------------------------------------------------- /src/semantic/type_checker.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::kaori_error::KaoriError, 3 | kaori_error, 4 | syntax::{binary_op::BinaryOpKind, unary_op::UnaryOpKind}, 5 | }; 6 | 7 | use super::{ 8 | hir_decl::{HirDecl, HirDeclKind}, 9 | hir_expr::{HirExpr, HirExprKind}, 10 | hir_node::HirNode, 11 | hir_stmt::{HirStmt, HirStmtKind}, 12 | hir_ty::{HirTy, HirTyKind}, 13 | r#type::{Type, Types}, 14 | }; 15 | 16 | #[derive(Debug, Default)] 17 | pub struct TypeChecker { 18 | return_ty: Type, 19 | types: Types, 20 | } 21 | 22 | impl TypeChecker { 23 | pub fn type_check(mut self, declarations: &[HirDecl]) -> Result { 24 | for declaration in declarations.iter() { 25 | match &declaration.kind { 26 | HirDeclKind::Function { 27 | parameters, 28 | return_ty, 29 | .. 30 | } => { 31 | let parameters = parameters 32 | .iter() 33 | .map(|parameter| { 34 | let ty = create_type(¶meter.ty); 35 | 36 | self.types.insert(parameter.id, ty.to_owned()); 37 | 38 | ty 39 | }) 40 | .collect(); 41 | 42 | let return_ty = if let Some(ty) = return_ty { 43 | create_type(ty) 44 | } else { 45 | Type::Void 46 | }; 47 | 48 | let ty = Type::function(parameters, return_ty); 49 | 50 | self.types.insert(declaration.id, ty); 51 | } 52 | HirDeclKind::Struct { fields } => { 53 | let fields = fields 54 | .iter() 55 | .map(|field| { 56 | let ty = create_type(&field.ty); 57 | 58 | self.types.insert(field.id, ty.to_owned()); 59 | 60 | ty 61 | }) 62 | .collect(); 63 | 64 | let ty = Type::struct_(fields); 65 | 66 | self.types.insert(declaration.id, ty); 67 | } 68 | _ => (), 69 | } 70 | } 71 | 72 | for declaration in declarations { 73 | self.type_check_declaration(declaration)?; 74 | } 75 | 76 | Ok(self.types) 77 | } 78 | 79 | fn type_check_nodes(&mut self, nodes: &[HirNode]) -> Result<(), KaoriError> { 80 | for node in nodes { 81 | self.type_check_ast_node(node)?; 82 | } 83 | 84 | Ok(()) 85 | } 86 | 87 | fn type_check_ast_node(&mut self, node: &HirNode) -> Result<(), KaoriError> { 88 | match node { 89 | HirNode::Declaration(declaration) => self.type_check_declaration(declaration), 90 | HirNode::Statement(statement) => self.type_check_statement(statement), 91 | }?; 92 | 93 | Ok(()) 94 | } 95 | 96 | fn type_check_declaration(&mut self, declaration: &HirDecl) -> Result<(), KaoriError> { 97 | match &declaration.kind { 98 | HirDeclKind::Variable { right, ty } => { 99 | let right = self.type_check_expression(right)?; 100 | 101 | let ty = match ty { 102 | Some(ty) => { 103 | let ty = create_type(ty); 104 | 105 | if right != ty { 106 | return Err(kaori_error!( 107 | declaration.span, 108 | "expected {:#?} type on variable declaration, but found: {:#?}", 109 | ty, 110 | right 111 | )); 112 | } 113 | 114 | ty 115 | } 116 | _ => right.to_owned(), 117 | }; 118 | 119 | self.types.insert(declaration.id, ty); 120 | } 121 | HirDeclKind::Function { 122 | body, return_ty, .. 123 | } => { 124 | let return_ty = return_ty.as_ref().map_or(Type::Void, create_type); 125 | 126 | self.return_ty = return_ty; 127 | 128 | for node in body { 129 | self.type_check_ast_node(node)?; 130 | } 131 | } 132 | HirDeclKind::Struct { .. } => {} 133 | }; 134 | 135 | Ok(()) 136 | } 137 | 138 | fn type_check_statement(&mut self, statement: &HirStmt) -> Result<(), KaoriError> { 139 | match &statement.kind { 140 | HirStmtKind::Expression(expression) => { 141 | self.type_check_expression(expression)?; 142 | } 143 | HirStmtKind::Print(expression) => { 144 | self.type_check_expression(expression)?; 145 | } 146 | HirStmtKind::Block(nodes) => { 147 | self.type_check_nodes(nodes)?; 148 | } 149 | HirStmtKind::Branch { 150 | condition, 151 | then_branch, 152 | else_branch, 153 | } => { 154 | let condition_ty = self.type_check_expression(condition)?; 155 | 156 | let Type::Boolean = condition_ty else { 157 | return Err(kaori_error!( 158 | condition.span, 159 | "expected a boolean for condition, but found {:#?}", 160 | condition_ty 161 | )); 162 | }; 163 | 164 | self.type_check_statement(then_branch)?; 165 | 166 | if let Some(branch) = else_branch { 167 | self.type_check_statement(branch)?; 168 | } 169 | } 170 | HirStmtKind::Loop { 171 | init, 172 | condition, 173 | block, 174 | increment, 175 | } => { 176 | if let Some(init) = init { 177 | self.type_check_declaration(init)?; 178 | } 179 | 180 | let condition_ty = self.type_check_expression(condition)?; 181 | 182 | let Type::Boolean = condition_ty else { 183 | return Err(kaori_error!( 184 | condition.span, 185 | "expected a boolean for condition, but found {:#?}", 186 | condition_ty 187 | )); 188 | }; 189 | 190 | self.type_check_statement(block)?; 191 | 192 | if let Some(increment) = increment { 193 | self.type_check_statement(increment)?; 194 | } 195 | } 196 | HirStmtKind::Break => {} 197 | HirStmtKind::Continue => {} 198 | HirStmtKind::Return(expression) => { 199 | let ty = match expression { 200 | Some(expression) => self.type_check_expression(expression)?, 201 | None => Type::Void, 202 | }; 203 | 204 | if self.return_ty != ty { 205 | return Err(kaori_error!( 206 | statement.span, 207 | "expected a return type of {:#?}, but found {:#?}", 208 | self.return_ty, 209 | ty 210 | )); 211 | } 212 | } 213 | }; 214 | 215 | Ok(()) 216 | } 217 | 218 | fn type_check_expression(&mut self, expression: &HirExpr) -> Result { 219 | let ty = match &expression.kind { 220 | HirExprKind::Assign { left, right } => { 221 | let right_ty = self.type_check_expression(right)?; 222 | let left_ty = self.type_check_expression(left)?; 223 | 224 | if left_ty == right_ty { 225 | Type::Void 226 | } else { 227 | return Err(kaori_error!( 228 | expression.span, 229 | "expected left and right side of assign operator to be the same type, but found: {:#?} and {:#?}", 230 | left_ty, 231 | right_ty 232 | )); 233 | } 234 | } 235 | HirExprKind::Binary { 236 | operator, 237 | left, 238 | right, 239 | } => { 240 | let left_ty = self.type_check_expression(left)?; 241 | let right_ty = self.type_check_expression(right)?; 242 | 243 | match (&left_ty, operator.kind, &right_ty) { 244 | (Type::Number, BinaryOpKind::Add, Type::Number) => Type::Number, 245 | (Type::Number, BinaryOpKind::Subtract, Type::Number) => Type::Number, 246 | (Type::Number, BinaryOpKind::Multiply, Type::Number) => Type::Number, 247 | (Type::Number, BinaryOpKind::Divide, Type::Number) => Type::Number, 248 | (Type::Number, BinaryOpKind::Modulo, Type::Number) => Type::Number, 249 | 250 | (Type::Boolean, BinaryOpKind::And, Type::Boolean) => Type::Boolean, 251 | (Type::Boolean, BinaryOpKind::Or, Type::Boolean) => Type::Boolean, 252 | 253 | (lhs, BinaryOpKind::Equal, rhs) if lhs == rhs => Type::Boolean, 254 | (lhs, BinaryOpKind::NotEqual, rhs) if lhs == rhs => Type::Boolean, 255 | 256 | (Type::Number, BinaryOpKind::Greater, Type::Number) => Type::Boolean, 257 | (Type::Number, BinaryOpKind::GreaterEqual, Type::Number) => Type::Boolean, 258 | (Type::Number, BinaryOpKind::Less, Type::Number) => Type::Boolean, 259 | (Type::Number, BinaryOpKind::LessEqual, Type::Number) => Type::Boolean, 260 | 261 | _ => { 262 | return Err(kaori_error!( 263 | expression.span, 264 | "expected valid types for {:#?} operator, but found {:#?} and {:#?}", 265 | operator.kind, 266 | left_ty, 267 | right_ty 268 | )); 269 | } 270 | } 271 | } 272 | HirExprKind::Unary { right, operator } => { 273 | let right_ty = self.type_check_expression(right)?; 274 | 275 | match (operator.kind, &right_ty) { 276 | (UnaryOpKind::Negate, Type::Number) => Type::Number, 277 | (UnaryOpKind::Not, Type::Boolean) => Type::Boolean, 278 | _ => { 279 | return Err(kaori_error!( 280 | expression.span, 281 | "expected valid type for {:#?} operator, but found {:#?}", 282 | operator.kind, 283 | right_ty 284 | )); 285 | } 286 | } 287 | } 288 | HirExprKind::FunctionCall { callee, arguments } => { 289 | let Type::Function { 290 | parameters, 291 | return_ty, 292 | } = self.type_check_expression(callee)? 293 | else { 294 | return Err(kaori_error!( 295 | callee.span, 296 | "expected a valid callable in that function call", 297 | )); 298 | }; 299 | 300 | if arguments.len() != parameters.len() { 301 | return Err(kaori_error!( 302 | expression.span, 303 | "expected the same number of arguments and parameters for this function call", 304 | )); 305 | } 306 | 307 | for (argument, parameter) in arguments.iter().zip(parameters) { 308 | let argument = self.type_check_expression(argument)?; 309 | 310 | if argument != parameter { 311 | return Err(kaori_error!( 312 | expression.span, 313 | "expected argument and parameter of the same type, but found: {:#?} and {:#?}", 314 | argument, 315 | parameter 316 | )); 317 | } 318 | } 319 | 320 | *return_ty 321 | } 322 | HirExprKind::Variable(id) => self.types.get(*id), 323 | HirExprKind::Function(id) => self.types.get(*id), 324 | HirExprKind::Boolean(..) => Type::Boolean, 325 | HirExprKind::Number(..) => Type::Number, 326 | HirExprKind::String(..) => Type::String, 327 | }; 328 | 329 | self.types.insert(expression.id, ty.to_owned()); 330 | 331 | Ok(ty) 332 | } 333 | } 334 | 335 | fn create_type(ty: &HirTy) -> Type { 336 | match &ty.kind { 337 | HirTyKind::Function { 338 | parameters, 339 | return_ty, 340 | } => { 341 | let parameters = parameters.iter().map(create_type).collect(); 342 | let return_ty = create_type(return_ty); 343 | 344 | Type::function(parameters, return_ty) 345 | } 346 | HirTyKind::TypeRef(id) => Type::type_ref(*id), 347 | HirTyKind::Struct { fields } => { 348 | let fields = fields.iter().map(create_type).collect(); 349 | 350 | Type::struct_(fields) 351 | } 352 | HirTyKind::Bool => Type::Boolean, 353 | HirTyKind::Number => Type::Number, 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/cfg_ir/build_cfgs.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ 4 | error::kaori_error::KaoriError, 5 | kaori_error, 6 | semantic::{ 7 | hir_decl::{HirDecl, HirDeclKind}, 8 | hir_expr::{HirExpr, HirExprKind}, 9 | hir_id::HirId, 10 | hir_node::HirNode, 11 | hir_stmt::{HirStmt, HirStmtKind}, 12 | }, 13 | syntax::{binary_op::BinaryOpKind, unary_op::UnaryOpKind}, 14 | }; 15 | 16 | use super::{ 17 | active_loops::ActiveLoops, 18 | basic_block::{BasicBlock, Terminator}, 19 | cfg_constants::CfgConstants, 20 | cfg_function::CfgFunction, 21 | instruction::Instruction, 22 | operand::Operand, 23 | }; 24 | 25 | pub fn build_cfgs(declarations: &[HirDecl]) -> Result, KaoriError> { 26 | let mut functions = HashMap::new(); 27 | 28 | for declaration in declarations { 29 | if let HirDeclKind::Function { .. } = &declaration.kind { 30 | let index = functions.len(); 31 | 32 | functions.insert(declaration.id, index); 33 | } 34 | } 35 | 36 | let mut cfgs = Vec::new(); 37 | 38 | for declaration in declarations { 39 | if let HirDeclKind::Function { .. } = &declaration.kind { 40 | let mut ctx = CfgContext::new(&functions); 41 | 42 | ctx.visit_declaration(declaration)?; 43 | 44 | let cfg = CfgFunction::new( 45 | ctx.basic_blocks, 46 | ctx.constants.constants, 47 | ctx.variables.len(), 48 | ); 49 | 50 | cfgs.push(cfg); 51 | } 52 | } 53 | 54 | Ok(cfgs) 55 | } 56 | 57 | pub struct CfgContext<'a> { 58 | index: usize, 59 | variables: HashMap, 60 | constants: CfgConstants, 61 | basic_blocks: Vec, 62 | active_loops: ActiveLoops, 63 | functions: &'a HashMap, 64 | } 65 | 66 | impl<'a> CfgContext<'a> { 67 | pub fn new(functions: &'a HashMap) -> Self { 68 | Self { 69 | index: 0, 70 | variables: HashMap::new(), 71 | constants: CfgConstants::default(), 72 | basic_blocks: Vec::new(), 73 | active_loops: ActiveLoops::default(), 74 | functions, 75 | } 76 | } 77 | 78 | pub fn create_variable(&mut self, id: HirId) -> Operand { 79 | let variable = Operand::Variable(self.variables.len()); 80 | 81 | self.variables.insert(id, variable); 82 | 83 | variable 84 | } 85 | 86 | fn emit_instruction(&mut self, instruction: Instruction) { 87 | let basic_block = &mut self.basic_blocks[self.index]; 88 | 89 | if let Terminator::None = basic_block.terminator { 90 | basic_block.instructions.push(instruction); 91 | }; 92 | } 93 | 94 | fn set_terminator(&mut self, terminator: Terminator) { 95 | let basic_block = &mut self.basic_blocks[self.index]; 96 | 97 | if let Terminator::None = basic_block.terminator { 98 | basic_block.terminator = terminator; 99 | }; 100 | } 101 | 102 | fn create_bb(&mut self) -> usize { 103 | let index = self.basic_blocks.len(); 104 | 105 | let basic_block = BasicBlock::new(index); 106 | 107 | self.basic_blocks.push(basic_block); 108 | 109 | index 110 | } 111 | 112 | fn visit_nodes(&mut self, nodes: &[HirNode]) -> Result<(), KaoriError> { 113 | for node in nodes { 114 | self.visit_ast_node(node)?; 115 | } 116 | 117 | Ok(()) 118 | } 119 | 120 | fn visit_ast_node(&mut self, node: &HirNode) -> Result<(), KaoriError> { 121 | match node { 122 | HirNode::Declaration(declaration) => self.visit_declaration(declaration)?, 123 | HirNode::Statement(statement) => self.visit_statement(statement)?, 124 | }; 125 | 126 | Ok(()) 127 | } 128 | 129 | fn visit_declaration(&mut self, declaration: &HirDecl) -> Result<(), KaoriError> { 130 | match &declaration.kind { 131 | HirDeclKind::Variable { right, .. } => { 132 | let src = self.visit_expression(right); 133 | let dest = self.create_variable(declaration.id); 134 | 135 | let instruction = Instruction::move_(dest, src); 136 | 137 | self.emit_instruction(instruction); 138 | } 139 | 140 | HirDeclKind::Function { 141 | body, 142 | parameters, 143 | return_ty, 144 | } => { 145 | let _entry_bb = self.create_bb(); 146 | 147 | for parameter in parameters { 148 | self.create_variable(parameter.id); 149 | } 150 | 151 | for node in body { 152 | self.visit_ast_node(node)?; 153 | } 154 | 155 | match self.basic_blocks[self.index].terminator { 156 | Terminator::Return { .. } => {} 157 | _ => { 158 | if return_ty.is_some() { 159 | return Err(kaori_error!( 160 | declaration.span, 161 | "expected a return statement" 162 | )); 163 | } 164 | 165 | self.set_terminator(Terminator::Return { src: None }) 166 | } 167 | } 168 | } 169 | HirDeclKind::Struct { .. } => {} 170 | }; 171 | 172 | Ok(()) 173 | } 174 | 175 | fn visit_statement(&mut self, statement: &HirStmt) -> Result<(), KaoriError> { 176 | match &statement.kind { 177 | HirStmtKind::Expression(expression) => { 178 | self.visit_expression(expression); 179 | } 180 | HirStmtKind::Print(expression) => { 181 | let src = self.visit_expression(expression); 182 | 183 | let instruction = Instruction::print(src); 184 | 185 | self.emit_instruction(instruction); 186 | } 187 | HirStmtKind::Block(nodes) => { 188 | self.visit_nodes(nodes)?; 189 | } 190 | HirStmtKind::Branch { 191 | condition, 192 | then_branch, 193 | else_branch, 194 | } => { 195 | let src = self.visit_expression(condition); 196 | 197 | let then_bb = self.create_bb(); 198 | let else_bb = self.create_bb(); 199 | let terminator_block = self.create_bb(); 200 | 201 | self.set_terminator(Terminator::Branch { 202 | src, 203 | r#true: then_bb, 204 | r#false: else_bb, 205 | }); 206 | 207 | self.index = then_bb; 208 | self.visit_statement(then_branch)?; 209 | self.set_terminator(Terminator::Goto(terminator_block)); 210 | 211 | self.index = else_bb; 212 | if let Some(branch) = else_branch { 213 | self.visit_statement(branch)?; 214 | } 215 | self.set_terminator(Terminator::Goto(terminator_block)); 216 | 217 | self.index = terminator_block; 218 | } 219 | HirStmtKind::Loop { 220 | init, 221 | condition, 222 | block, 223 | increment, 224 | } => { 225 | if let Some(init) = init { 226 | self.visit_declaration(init)?; 227 | } 228 | 229 | let condition_bb = self.create_bb(); 230 | let block_bb = self.create_bb(); 231 | let terminator_bb = self.create_bb(); 232 | let increment_bb = self.create_bb(); 233 | 234 | self.set_terminator(Terminator::Goto(condition_bb)); 235 | 236 | self.index = condition_bb; 237 | let src = self.visit_expression(condition); 238 | self.set_terminator(Terminator::Branch { 239 | src, 240 | r#true: block_bb, 241 | r#false: terminator_bb, 242 | }); 243 | 244 | self.index = block_bb; 245 | self.active_loops.push(increment_bb, terminator_bb); 246 | self.visit_statement(block)?; 247 | self.active_loops.pop(); 248 | self.set_terminator(Terminator::Goto(increment_bb)); 249 | 250 | self.index = increment_bb; 251 | if let Some(increment) = increment { 252 | self.visit_statement(increment)?; 253 | } 254 | self.set_terminator(Terminator::Goto(condition_bb)); 255 | 256 | self.index = terminator_bb; 257 | } 258 | HirStmtKind::Break => { 259 | let label = self.active_loops.top(); 260 | 261 | self.set_terminator(Terminator::Goto(label.terminator_bb_index)); 262 | } 263 | HirStmtKind::Continue => { 264 | let label = self.active_loops.top(); 265 | 266 | self.set_terminator(Terminator::Goto(label.increment_bb_index)); 267 | } 268 | HirStmtKind::Return(expr) => { 269 | if let Some(expr) = expr { 270 | let src = self.visit_expression(expr); 271 | 272 | self.set_terminator(Terminator::Return { src: Some(src) }); 273 | } else { 274 | self.set_terminator(Terminator::Return { src: None }); 275 | } 276 | } 277 | }; 278 | 279 | Ok(()) 280 | } 281 | 282 | fn visit_logical_or(&mut self, id: HirId, left: &HirExpr, right: &HirExpr) -> Operand { 283 | let dest = self.create_variable(id); 284 | 285 | let src1 = self.visit_expression(left); 286 | 287 | self.emit_instruction(Instruction::move_(dest, src1)); 288 | 289 | let src2_bb = self.create_bb(); 290 | let terminator = self.create_bb(); 291 | 292 | self.set_terminator(Terminator::Branch { 293 | src: dest, 294 | r#true: terminator, 295 | r#false: src2_bb, 296 | }); 297 | 298 | self.index = src2_bb; 299 | 300 | let src2 = self.visit_expression(right); 301 | 302 | self.emit_instruction(Instruction::move_(dest, src2)); 303 | 304 | self.set_terminator(Terminator::Goto(terminator)); 305 | 306 | self.index = terminator; 307 | 308 | dest 309 | } 310 | 311 | fn visit_logical_and(&mut self, id: HirId, left: &HirExpr, right: &HirExpr) -> Operand { 312 | let dest = self.create_variable(id); 313 | 314 | let src1 = self.visit_expression(left); 315 | 316 | self.emit_instruction(Instruction::move_(dest, src1)); 317 | 318 | let src2_bb = self.create_bb(); 319 | let terminator = self.create_bb(); 320 | 321 | self.set_terminator(Terminator::Branch { 322 | src: dest, 323 | r#true: src2_bb, 324 | r#false: terminator, 325 | }); 326 | 327 | self.index = src2_bb; 328 | 329 | let src2 = self.visit_expression(right); 330 | self.emit_instruction(Instruction::move_(dest, src2)); 331 | self.set_terminator(Terminator::Goto(terminator)); 332 | 333 | self.index = terminator; 334 | 335 | dest 336 | } 337 | 338 | fn visit_expression(&mut self, expression: &HirExpr) -> Operand { 339 | match &expression.kind { 340 | HirExprKind::Assign { left, right } => { 341 | let dest = self.visit_expression(left); 342 | let src = self.visit_expression(right); 343 | 344 | let instruction = Instruction::move_(dest, src); 345 | 346 | self.emit_instruction(instruction); 347 | 348 | dest 349 | } 350 | HirExprKind::Binary { 351 | operator, 352 | left, 353 | right, 354 | } => match operator.kind { 355 | BinaryOpKind::And => self.visit_logical_and(expression.id, left, right), 356 | BinaryOpKind::Or => self.visit_logical_or(expression.id, left, right), 357 | _ => { 358 | let src1 = self.visit_expression(left); 359 | let src2 = self.visit_expression(right); 360 | let dest = self.create_variable(expression.id); 361 | 362 | let instruction = match operator.kind { 363 | BinaryOpKind::Add => Instruction::add(dest, src1, src2), 364 | BinaryOpKind::Subtract => Instruction::subtract(dest, src1, src2), 365 | BinaryOpKind::Multiply => Instruction::multiply(dest, src1, src2), 366 | BinaryOpKind::Divide => Instruction::divide(dest, src1, src2), 367 | BinaryOpKind::Modulo => Instruction::modulo(dest, src1, src2), 368 | 369 | BinaryOpKind::Equal => Instruction::equal(dest, src1, src2), 370 | BinaryOpKind::NotEqual => Instruction::not_equal(dest, src1, src2), 371 | BinaryOpKind::Greater => Instruction::greater(dest, src1, src2), 372 | BinaryOpKind::GreaterEqual => Instruction::greater_equal(dest, src1, src2), 373 | BinaryOpKind::Less => Instruction::less(dest, src1, src2), 374 | BinaryOpKind::LessEqual => Instruction::less_equal(dest, src1, src2), 375 | _ => unreachable!(), 376 | }; 377 | 378 | self.emit_instruction(instruction); 379 | 380 | dest 381 | } 382 | }, 383 | HirExprKind::Unary { right, operator } => { 384 | let src = self.visit_expression(right); 385 | let dest = self.create_variable(expression.id); 386 | 387 | let instruction = match operator.kind { 388 | UnaryOpKind::Negate => Instruction::negate(dest, src), 389 | UnaryOpKind::Not => Instruction::not(dest, src), 390 | }; 391 | 392 | self.emit_instruction(instruction); 393 | 394 | dest 395 | } 396 | HirExprKind::FunctionCall { callee, arguments } => { 397 | let dest = self.create_variable(expression.id); 398 | 399 | let arguments_src = arguments 400 | .iter() 401 | .map(|argument| self.visit_expression(argument)) 402 | .collect::>(); 403 | 404 | let src = self.visit_expression(callee); 405 | 406 | for (index, src) in arguments_src.iter().copied().enumerate() { 407 | let dest = Operand::Variable(index); 408 | 409 | let instruction = Instruction::move_arg(dest, src); 410 | 411 | self.emit_instruction(instruction); 412 | } 413 | 414 | let instruction = Instruction::call(dest, src); 415 | 416 | self.emit_instruction(instruction); 417 | 418 | dest 419 | } 420 | 421 | HirExprKind::Variable(id) => *self 422 | .variables 423 | .get(id) 424 | .expect("Variable not found for HirId"), 425 | 426 | HirExprKind::Function(id) => { 427 | let value = *self 428 | .functions 429 | .get(id) 430 | .expect("FunctionRef points to a missing variable node"); 431 | 432 | self.constants.push_function(value) 433 | } 434 | HirExprKind::String(value) => self.constants.push_string(value.to_owned()), 435 | HirExprKind::Boolean(value) => self.constants.push_boolean(*value), 436 | HirExprKind::Number(value) => self.constants.push_number(*value), 437 | } 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /src/semantic/resolver.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ 4 | error::kaori_error::KaoriError, 5 | kaori_error, 6 | lexer::span::Span, 7 | syntax::{ 8 | assign_op::AssignOpKind, 9 | ast_id::AstId, 10 | ast_node::AstNode, 11 | binary_op::{BinaryOp, BinaryOpKind}, 12 | decl::{Decl, DeclKind, Field, Parameter}, 13 | expr::{Expr, ExprKind}, 14 | stmt::{Stmt, StmtKind}, 15 | ty::{Ty, TyKind}, 16 | }, 17 | }; 18 | 19 | use super::{ 20 | hir_decl::{HirDecl, HirField, HirParameter}, 21 | hir_expr::{HirExpr, HirExprKind}, 22 | hir_id::HirId, 23 | hir_node::HirNode, 24 | hir_stmt::HirStmt, 25 | hir_ty::HirTy, 26 | symbol::SymbolKind, 27 | symbol_table::SymbolTable, 28 | }; 29 | 30 | #[derive(Default)] 31 | pub struct Resolver { 32 | symbol_table: SymbolTable, 33 | active_loops: u8, 34 | local_scope: bool, 35 | ids: HashMap, 36 | } 37 | 38 | impl Resolver { 39 | pub fn enter_function(&mut self) { 40 | self.symbol_table.enter_scope(); 41 | self.local_scope = true; 42 | } 43 | 44 | pub fn exit_function(&mut self) { 45 | self.symbol_table.exit_scope(); 46 | self.local_scope = false; 47 | } 48 | 49 | pub fn generate_hir_id(&mut self, id: AstId) -> HirId { 50 | let hir_id = HirId::default(); 51 | 52 | self.ids.insert(id, hir_id); 53 | 54 | hir_id 55 | } 56 | 57 | fn resolve_main_function(&mut self, declarations: &mut [Decl]) -> Result<(), KaoriError> { 58 | for (index, declaration) in declarations.iter().enumerate() { 59 | if let DeclKind::Function { name, .. } = &declaration.kind 60 | && name == "main" 61 | { 62 | declarations.swap(0, index); 63 | 64 | return Ok(()); 65 | } 66 | } 67 | 68 | Err(kaori_error!( 69 | Span::default(), 70 | "expected a main function to be declared in the program" 71 | )) 72 | } 73 | 74 | fn resolve_declaration_scope(&self, declaration: &Decl) -> Result<(), KaoriError> { 75 | let has_error = match &declaration.kind { 76 | DeclKind::Function { .. } if self.local_scope => true, 77 | DeclKind::Struct { .. } if self.local_scope => true, 78 | DeclKind::Variable { .. } if !self.local_scope => true, 79 | _ => false, 80 | }; 81 | 82 | if has_error { 83 | Err(kaori_error!( 84 | declaration.span, 85 | "expected declaration to be made in the correct scope" 86 | )) 87 | } else { 88 | Ok(()) 89 | } 90 | } 91 | 92 | pub fn resolve(&mut self, declarations: &mut [Decl]) -> Result, KaoriError> { 93 | self.resolve_main_function(declarations)?; 94 | 95 | for declaration in declarations.iter() { 96 | match &declaration.kind { 97 | DeclKind::Function { name, .. } => { 98 | if self.symbol_table.search_current_scope(name).is_some() { 99 | return Err(kaori_error!( 100 | declaration.span, 101 | "{} is already declared", 102 | name 103 | )); 104 | } 105 | 106 | let id = self.generate_hir_id(declaration.id); 107 | 108 | self.symbol_table.declare_function(id, name.to_owned()); 109 | } 110 | DeclKind::Struct { name, .. } => { 111 | if self.symbol_table.search_current_scope(name).is_some() { 112 | return Err(kaori_error!( 113 | declaration.span, 114 | "{} is already declared", 115 | name 116 | )); 117 | } 118 | 119 | let id = self.generate_hir_id(declaration.id); 120 | 121 | self.symbol_table.declare_struct(id, name.to_owned()); 122 | } 123 | _ => (), 124 | }; 125 | } 126 | 127 | let declarations = declarations 128 | .iter() 129 | .map(|declaration| self.resolve_declaration(declaration)) 130 | .collect::, KaoriError>>()?; 131 | 132 | Ok(declarations) 133 | } 134 | 135 | fn resolve_node(&mut self, node: &AstNode) -> Result { 136 | let node = match node { 137 | AstNode::Declaration(declaration) => { 138 | let declaration = self.resolve_declaration(declaration)?; 139 | HirNode::from(declaration) 140 | } 141 | AstNode::Statement(statement) => { 142 | let statement = self.resolve_statement(statement)?; 143 | HirNode::from(statement) 144 | } 145 | }; 146 | 147 | Ok(node) 148 | } 149 | 150 | fn resolve_parameter(&mut self, parameter: &Parameter) -> Result { 151 | if self 152 | .symbol_table 153 | .search_current_scope(¶meter.name) 154 | .is_some() 155 | { 156 | return Err(kaori_error!( 157 | parameter.span, 158 | "function can't have parameters with the same name: {}", 159 | parameter.name, 160 | )); 161 | }; 162 | 163 | let id = HirId::default(); 164 | 165 | self.symbol_table 166 | .declare_variable(id, parameter.name.to_owned()); 167 | 168 | let ty = self.resolve_type(¶meter.ty)?; 169 | 170 | Ok(HirParameter::new(id, ty, parameter.span)) 171 | } 172 | 173 | fn resolve_field(&mut self, field: &Field) -> Result { 174 | if self 175 | .symbol_table 176 | .search_current_scope(&field.name) 177 | .is_some() 178 | { 179 | return Err(kaori_error!( 180 | field.span, 181 | "struct can't have fields with the same name: {}", 182 | field.name, 183 | )); 184 | }; 185 | 186 | let id = HirId::default(); 187 | 188 | self.symbol_table 189 | .declare_variable(id, field.name.to_owned()); 190 | 191 | let ty = self.resolve_type(&field.ty)?; 192 | 193 | Ok(HirField::new(id, ty, field.span)) 194 | } 195 | 196 | fn resolve_declaration(&mut self, declaration: &Decl) -> Result { 197 | self.resolve_declaration_scope(declaration)?; 198 | 199 | let hir_decl = match &declaration.kind { 200 | DeclKind::Variable { name, right, ty } => { 201 | let right = self.resolve_expression(right)?; 202 | 203 | if self.symbol_table.search_current_scope(name).is_some() { 204 | return Err(kaori_error!( 205 | declaration.span, 206 | "{} is already declared", 207 | name 208 | )); 209 | }; 210 | 211 | let id = HirId::default(); 212 | 213 | self.symbol_table.declare_variable(id, name.to_owned()); 214 | 215 | let ty = match ty { 216 | Some(ty) => Some(self.resolve_type(ty)?), 217 | None => None, 218 | }; 219 | 220 | HirDecl::variable(id, right, ty, declaration.span) 221 | } 222 | DeclKind::Function { 223 | parameters, 224 | body, 225 | return_ty, 226 | .. 227 | } => { 228 | self.enter_function(); 229 | 230 | let parameters = parameters 231 | .iter() 232 | .map(|parameter| self.resolve_parameter(parameter)) 233 | .collect::, KaoriError>>()?; 234 | 235 | let body = body 236 | .iter() 237 | .map(|node| self.resolve_node(node)) 238 | .collect::, KaoriError>>()?; 239 | 240 | let return_ty = match return_ty { 241 | Some(ty) => Some(self.resolve_type(ty)?), 242 | _ => None, 243 | }; 244 | 245 | self.exit_function(); 246 | 247 | let id = self.ids.get(&declaration.id).unwrap(); 248 | 249 | HirDecl::function(*id, parameters, body, return_ty, declaration.span) 250 | } 251 | DeclKind::Struct { fields, .. } => { 252 | let fields = fields 253 | .iter() 254 | .map(|field| self.resolve_field(field)) 255 | .collect::, KaoriError>>()?; 256 | 257 | let id = self.ids.get(&declaration.id).unwrap(); 258 | 259 | HirDecl::struct_(*id, fields, declaration.span) 260 | } 261 | }; 262 | 263 | Ok(hir_decl) 264 | } 265 | 266 | fn resolve_statement(&mut self, statement: &Stmt) -> Result { 267 | let hir_stmt = match &statement.kind { 268 | StmtKind::Expression(expression) => { 269 | let expr = self.resolve_expression(expression)?; 270 | 271 | HirStmt::expression(expr, statement.span) 272 | } 273 | StmtKind::Print(expression) => { 274 | let expr = self.resolve_expression(expression)?; 275 | 276 | HirStmt::print(expr, statement.span) 277 | } 278 | StmtKind::Block(nodes) => { 279 | self.symbol_table.enter_scope(); 280 | 281 | let nodes = nodes 282 | .iter() 283 | .map(|node| self.resolve_node(node)) 284 | .collect::, KaoriError>>()?; 285 | 286 | self.symbol_table.exit_scope(); 287 | 288 | HirStmt::block(nodes, statement.span) 289 | } 290 | StmtKind::If { 291 | condition, 292 | then_branch, 293 | else_branch, 294 | } => { 295 | let condition = self.resolve_expression(condition)?; 296 | let then_branch = self.resolve_statement(then_branch)?; 297 | let else_branch = match else_branch { 298 | Some(branch) => Some(self.resolve_statement(branch)?), 299 | _ => None, 300 | }; 301 | 302 | HirStmt::branch_(condition, then_branch, else_branch, statement.span) 303 | } 304 | StmtKind::WhileLoop { condition, block } => { 305 | let init = None; 306 | let condition = self.resolve_expression(condition)?; 307 | let increment = None; 308 | 309 | self.active_loops += 1; 310 | let block = self.resolve_statement(block)?; 311 | self.active_loops -= 1; 312 | 313 | HirStmt::loop_(init, condition, block, increment, statement.span) 314 | } 315 | StmtKind::ForLoop { 316 | init, 317 | condition, 318 | increment, 319 | block, 320 | } => { 321 | self.symbol_table.enter_scope(); 322 | 323 | let init = Some(self.resolve_declaration(init)?); 324 | let condition = self.resolve_expression(condition)?; 325 | let increment = Some(self.resolve_statement(increment)?); 326 | 327 | self.active_loops += 1; 328 | let block = self.resolve_statement(block)?; 329 | self.active_loops -= 1; 330 | 331 | self.symbol_table.exit_scope(); 332 | 333 | HirStmt::loop_(init, condition, block, increment, statement.span) 334 | } 335 | StmtKind::Break => { 336 | if self.active_loops == 0 { 337 | return Err(kaori_error!( 338 | statement.span, 339 | "break statement can't appear outside of loops" 340 | )); 341 | } 342 | 343 | HirStmt::break_(statement.span) 344 | } 345 | StmtKind::Continue => { 346 | if self.active_loops == 0 { 347 | return Err(kaori_error!( 348 | statement.span, 349 | "continue statement can't appear outside of loops" 350 | )); 351 | } 352 | 353 | HirStmt::continue_(statement.span) 354 | } 355 | StmtKind::Return(expr) => { 356 | let expr = match expr { 357 | Some(e) => Some(self.resolve_expression(e)?), 358 | _ => None, 359 | }; 360 | 361 | HirStmt::return_(expr, statement.span) 362 | } 363 | }; 364 | 365 | Ok(hir_stmt) 366 | } 367 | 368 | fn resolve_expression(&self, expression: &Expr) -> Result { 369 | let hir_expr = match &expression.kind { 370 | ExprKind::Assign { 371 | operator, 372 | left, 373 | right, 374 | } => { 375 | let right = self.resolve_expression(right)?; 376 | let left = self.resolve_expression(left)?; 377 | 378 | let HirExprKind::Variable(..) = &left.kind else { 379 | return Err(kaori_error!( 380 | left.span, 381 | "expected a valid left hand side to assign values to" 382 | )); 383 | }; 384 | 385 | let operator_kind = match &operator.kind { 386 | AssignOpKind::AddAssign => BinaryOpKind::Add, 387 | AssignOpKind::SubtractAssign => BinaryOpKind::Subtract, 388 | AssignOpKind::MultiplyAssign => BinaryOpKind::Multiply, 389 | AssignOpKind::DivideAssign => BinaryOpKind::Divide, 390 | AssignOpKind::ModuloAssign => BinaryOpKind::Modulo, 391 | _ => return Ok(HirExpr::assign(left, right, expression.span)), 392 | }; 393 | 394 | let operator = BinaryOp::new(operator_kind, operator.span); 395 | 396 | let right = 397 | HirExpr::binary(operator, left.to_owned(), right.to_owned(), right.span); 398 | 399 | HirExpr::assign(left, right, expression.span) 400 | } 401 | ExprKind::Binary { 402 | left, 403 | right, 404 | operator, 405 | } => { 406 | let left = self.resolve_expression(left)?; 407 | let right = self.resolve_expression(right)?; 408 | 409 | HirExpr::binary(*operator, left, right, expression.span) 410 | } 411 | ExprKind::Unary { right, operator } => { 412 | let right = self.resolve_expression(right)?; 413 | 414 | HirExpr::unary(*operator, right, expression.span) 415 | } 416 | ExprKind::FunctionCall { callee, arguments } => { 417 | let callee = self.resolve_expression(callee)?; 418 | let arguments = arguments 419 | .iter() 420 | .map(|argument| self.resolve_expression(argument)) 421 | .collect::, KaoriError>>()?; 422 | 423 | HirExpr::function_call(callee, arguments, expression.span) 424 | } 425 | ExprKind::NumberLiteral(value) => HirExpr::number(*value, expression.span), 426 | ExprKind::BooleanLiteral(value) => HirExpr::boolean(*value, expression.span), 427 | ExprKind::StringLiteral(value) => HirExpr::string(value.to_owned(), expression.span), 428 | ExprKind::Identifier(name) => { 429 | let Some(symbol) = self.symbol_table.search(name) else { 430 | return Err(kaori_error!(expression.span, "{} is not declared", name)); 431 | }; 432 | 433 | match symbol.kind { 434 | SymbolKind::Function => HirExpr::function(symbol.id, expression.span), 435 | SymbolKind::Variable => HirExpr::variable(symbol.id, expression.span), 436 | SymbolKind::Struct => { 437 | return Err(kaori_error!(expression.span, "{} is not a value", name)); 438 | } 439 | } 440 | } 441 | }; 442 | 443 | Ok(hir_expr) 444 | } 445 | 446 | fn resolve_type(&mut self, ty: &Ty) -> Result { 447 | let hir_ty = match &ty.kind { 448 | TyKind::Function { 449 | parameters, 450 | return_ty, 451 | } => { 452 | let parameters = parameters 453 | .iter() 454 | .map(|parameter| self.resolve_type(parameter)) 455 | .collect::, KaoriError>>()?; 456 | 457 | let return_ty = self.resolve_type(return_ty)?; 458 | 459 | HirTy::function(parameters, return_ty, ty.span) 460 | } 461 | TyKind::Struct { fields } => { 462 | let fields = fields 463 | .iter() 464 | .map(|field| self.resolve_type(field)) 465 | .collect::, KaoriError>>()?; 466 | 467 | HirTy::struct_(fields, ty.span) 468 | } 469 | TyKind::Identifier(name) => { 470 | let Some(symbol) = self.symbol_table.search(name) else { 471 | return Err(kaori_error!(ty.span, "{} type is not declared", name)); 472 | }; 473 | 474 | let SymbolKind::Struct = symbol.kind else { 475 | return Err(kaori_error!(ty.span, "{} is not a valid type", name)); 476 | }; 477 | 478 | HirTy::type_ref(symbol.id, ty.span) 479 | } 480 | TyKind::Bool => HirTy::bool(ty.span), 481 | TyKind::Number => HirTy::number(ty.span), 482 | }; 483 | 484 | Ok(hir_ty) 485 | } 486 | } 487 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.25.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anes" 31 | version = "0.1.6" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 34 | 35 | [[package]] 36 | name = "anstyle" 37 | version = "1.0.13" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 40 | 41 | [[package]] 42 | name = "ariadne" 43 | version = "0.5.1" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "36f5e3dca4e09a6f340a61a0e9c7b61e030c69fc27bf29d73218f7e5e3b7638f" 46 | dependencies = [ 47 | "unicode-width", 48 | "yansi", 49 | ] 50 | 51 | [[package]] 52 | name = "autocfg" 53 | version = "1.5.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 56 | 57 | [[package]] 58 | name = "backtrace" 59 | version = "0.3.76" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" 62 | dependencies = [ 63 | "addr2line", 64 | "cfg-if", 65 | "libc", 66 | "miniz_oxide", 67 | "object", 68 | "rustc-demangle", 69 | "windows-link", 70 | ] 71 | 72 | [[package]] 73 | name = "bumpalo" 74 | version = "3.19.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 77 | 78 | [[package]] 79 | name = "byteorder" 80 | version = "1.5.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 83 | 84 | [[package]] 85 | name = "cast" 86 | version = "0.3.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 89 | 90 | [[package]] 91 | name = "cfg-if" 92 | version = "1.0.3" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 95 | 96 | [[package]] 97 | name = "ciborium" 98 | version = "0.2.2" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 101 | dependencies = [ 102 | "ciborium-io", 103 | "ciborium-ll", 104 | "serde", 105 | ] 106 | 107 | [[package]] 108 | name = "ciborium-io" 109 | version = "0.2.2" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 112 | 113 | [[package]] 114 | name = "ciborium-ll" 115 | version = "0.2.2" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 118 | dependencies = [ 119 | "ciborium-io", 120 | "half", 121 | ] 122 | 123 | [[package]] 124 | name = "clap" 125 | version = "4.5.48" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" 128 | dependencies = [ 129 | "clap_builder", 130 | ] 131 | 132 | [[package]] 133 | name = "clap_builder" 134 | version = "4.5.48" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" 137 | dependencies = [ 138 | "anstyle", 139 | "clap_lex", 140 | ] 141 | 142 | [[package]] 143 | name = "clap_lex" 144 | version = "0.7.5" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 147 | 148 | [[package]] 149 | name = "criterion" 150 | version = "0.5.1" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" 153 | dependencies = [ 154 | "anes", 155 | "cast", 156 | "ciborium", 157 | "clap", 158 | "criterion-plot", 159 | "is-terminal", 160 | "itertools", 161 | "num-traits", 162 | "once_cell", 163 | "oorandom", 164 | "plotters", 165 | "rayon", 166 | "regex", 167 | "serde", 168 | "serde_derive", 169 | "serde_json", 170 | "tinytemplate", 171 | "walkdir", 172 | ] 173 | 174 | [[package]] 175 | name = "criterion-plot" 176 | version = "0.5.0" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 179 | dependencies = [ 180 | "cast", 181 | "itertools", 182 | ] 183 | 184 | [[package]] 185 | name = "crossbeam-deque" 186 | version = "0.8.6" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 189 | dependencies = [ 190 | "crossbeam-epoch", 191 | "crossbeam-utils", 192 | ] 193 | 194 | [[package]] 195 | name = "crossbeam-epoch" 196 | version = "0.9.18" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 199 | dependencies = [ 200 | "crossbeam-utils", 201 | ] 202 | 203 | [[package]] 204 | name = "crossbeam-utils" 205 | version = "0.8.21" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 208 | 209 | [[package]] 210 | name = "crunchy" 211 | version = "0.2.4" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 214 | 215 | [[package]] 216 | name = "either" 217 | version = "1.15.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 220 | 221 | [[package]] 222 | name = "error-chain" 223 | version = "0.10.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" 226 | dependencies = [ 227 | "backtrace", 228 | ] 229 | 230 | [[package]] 231 | name = "gimli" 232 | version = "0.32.3" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" 235 | 236 | [[package]] 237 | name = "half" 238 | version = "2.6.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" 241 | dependencies = [ 242 | "cfg-if", 243 | "crunchy", 244 | ] 245 | 246 | [[package]] 247 | name = "hermit-abi" 248 | version = "0.5.2" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" 251 | 252 | [[package]] 253 | name = "is-terminal" 254 | version = "0.4.16" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" 257 | dependencies = [ 258 | "hermit-abi", 259 | "libc", 260 | "windows-sys 0.59.0", 261 | ] 262 | 263 | [[package]] 264 | name = "itertools" 265 | version = "0.10.5" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 268 | dependencies = [ 269 | "either", 270 | ] 271 | 272 | [[package]] 273 | name = "itoa" 274 | version = "1.0.15" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 277 | 278 | [[package]] 279 | name = "js-sys" 280 | version = "0.3.81" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" 283 | dependencies = [ 284 | "once_cell", 285 | "wasm-bindgen", 286 | ] 287 | 288 | [[package]] 289 | name = "kaori" 290 | version = "0.1.0" 291 | dependencies = [ 292 | "ariadne", 293 | "criterion", 294 | "mem", 295 | "ordered-float", 296 | ] 297 | 298 | [[package]] 299 | name = "libc" 300 | version = "0.2.176" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" 303 | 304 | [[package]] 305 | name = "log" 306 | version = "0.4.28" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 309 | 310 | [[package]] 311 | name = "mem" 312 | version = "0.5.0" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "b50b6159a8e0e40eac611e28eb8055844fd0756c35764adaa159c9ba2a21ad61" 315 | dependencies = [ 316 | "byteorder", 317 | "error-chain", 318 | ] 319 | 320 | [[package]] 321 | name = "memchr" 322 | version = "2.7.6" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 325 | 326 | [[package]] 327 | name = "miniz_oxide" 328 | version = "0.8.9" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 331 | dependencies = [ 332 | "adler2", 333 | ] 334 | 335 | [[package]] 336 | name = "num-traits" 337 | version = "0.2.19" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 340 | dependencies = [ 341 | "autocfg", 342 | ] 343 | 344 | [[package]] 345 | name = "object" 346 | version = "0.37.3" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" 349 | dependencies = [ 350 | "memchr", 351 | ] 352 | 353 | [[package]] 354 | name = "once_cell" 355 | version = "1.21.3" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 358 | 359 | [[package]] 360 | name = "oorandom" 361 | version = "11.1.5" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" 364 | 365 | [[package]] 366 | name = "ordered-float" 367 | version = "5.1.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" 370 | dependencies = [ 371 | "num-traits", 372 | ] 373 | 374 | [[package]] 375 | name = "plotters" 376 | version = "0.3.7" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" 379 | dependencies = [ 380 | "num-traits", 381 | "plotters-backend", 382 | "plotters-svg", 383 | "wasm-bindgen", 384 | "web-sys", 385 | ] 386 | 387 | [[package]] 388 | name = "plotters-backend" 389 | version = "0.3.7" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" 392 | 393 | [[package]] 394 | name = "plotters-svg" 395 | version = "0.3.7" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" 398 | dependencies = [ 399 | "plotters-backend", 400 | ] 401 | 402 | [[package]] 403 | name = "proc-macro2" 404 | version = "1.0.101" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 407 | dependencies = [ 408 | "unicode-ident", 409 | ] 410 | 411 | [[package]] 412 | name = "quote" 413 | version = "1.0.41" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" 416 | dependencies = [ 417 | "proc-macro2", 418 | ] 419 | 420 | [[package]] 421 | name = "rayon" 422 | version = "1.11.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" 425 | dependencies = [ 426 | "either", 427 | "rayon-core", 428 | ] 429 | 430 | [[package]] 431 | name = "rayon-core" 432 | version = "1.13.0" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" 435 | dependencies = [ 436 | "crossbeam-deque", 437 | "crossbeam-utils", 438 | ] 439 | 440 | [[package]] 441 | name = "regex" 442 | version = "1.11.3" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" 445 | dependencies = [ 446 | "aho-corasick", 447 | "memchr", 448 | "regex-automata", 449 | "regex-syntax", 450 | ] 451 | 452 | [[package]] 453 | name = "regex-automata" 454 | version = "0.4.11" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" 457 | dependencies = [ 458 | "aho-corasick", 459 | "memchr", 460 | "regex-syntax", 461 | ] 462 | 463 | [[package]] 464 | name = "regex-syntax" 465 | version = "0.8.6" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" 468 | 469 | [[package]] 470 | name = "rustc-demangle" 471 | version = "0.1.26" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 474 | 475 | [[package]] 476 | name = "rustversion" 477 | version = "1.0.22" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 480 | 481 | [[package]] 482 | name = "ryu" 483 | version = "1.0.20" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 486 | 487 | [[package]] 488 | name = "same-file" 489 | version = "1.0.6" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 492 | dependencies = [ 493 | "winapi-util", 494 | ] 495 | 496 | [[package]] 497 | name = "serde" 498 | version = "1.0.228" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 501 | dependencies = [ 502 | "serde_core", 503 | "serde_derive", 504 | ] 505 | 506 | [[package]] 507 | name = "serde_core" 508 | version = "1.0.228" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 511 | dependencies = [ 512 | "serde_derive", 513 | ] 514 | 515 | [[package]] 516 | name = "serde_derive" 517 | version = "1.0.228" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 520 | dependencies = [ 521 | "proc-macro2", 522 | "quote", 523 | "syn", 524 | ] 525 | 526 | [[package]] 527 | name = "serde_json" 528 | version = "1.0.145" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 531 | dependencies = [ 532 | "itoa", 533 | "memchr", 534 | "ryu", 535 | "serde", 536 | "serde_core", 537 | ] 538 | 539 | [[package]] 540 | name = "syn" 541 | version = "2.0.106" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 544 | dependencies = [ 545 | "proc-macro2", 546 | "quote", 547 | "unicode-ident", 548 | ] 549 | 550 | [[package]] 551 | name = "tinytemplate" 552 | version = "1.2.1" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 555 | dependencies = [ 556 | "serde", 557 | "serde_json", 558 | ] 559 | 560 | [[package]] 561 | name = "unicode-ident" 562 | version = "1.0.19" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" 565 | 566 | [[package]] 567 | name = "unicode-width" 568 | version = "0.1.14" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 571 | 572 | [[package]] 573 | name = "walkdir" 574 | version = "2.5.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 577 | dependencies = [ 578 | "same-file", 579 | "winapi-util", 580 | ] 581 | 582 | [[package]] 583 | name = "wasm-bindgen" 584 | version = "0.2.104" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" 587 | dependencies = [ 588 | "cfg-if", 589 | "once_cell", 590 | "rustversion", 591 | "wasm-bindgen-macro", 592 | "wasm-bindgen-shared", 593 | ] 594 | 595 | [[package]] 596 | name = "wasm-bindgen-backend" 597 | version = "0.2.104" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" 600 | dependencies = [ 601 | "bumpalo", 602 | "log", 603 | "proc-macro2", 604 | "quote", 605 | "syn", 606 | "wasm-bindgen-shared", 607 | ] 608 | 609 | [[package]] 610 | name = "wasm-bindgen-macro" 611 | version = "0.2.104" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" 614 | dependencies = [ 615 | "quote", 616 | "wasm-bindgen-macro-support", 617 | ] 618 | 619 | [[package]] 620 | name = "wasm-bindgen-macro-support" 621 | version = "0.2.104" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" 624 | dependencies = [ 625 | "proc-macro2", 626 | "quote", 627 | "syn", 628 | "wasm-bindgen-backend", 629 | "wasm-bindgen-shared", 630 | ] 631 | 632 | [[package]] 633 | name = "wasm-bindgen-shared" 634 | version = "0.2.104" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" 637 | dependencies = [ 638 | "unicode-ident", 639 | ] 640 | 641 | [[package]] 642 | name = "web-sys" 643 | version = "0.3.81" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" 646 | dependencies = [ 647 | "js-sys", 648 | "wasm-bindgen", 649 | ] 650 | 651 | [[package]] 652 | name = "winapi-util" 653 | version = "0.1.11" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" 656 | dependencies = [ 657 | "windows-sys 0.61.2", 658 | ] 659 | 660 | [[package]] 661 | name = "windows-link" 662 | version = "0.2.1" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 665 | 666 | [[package]] 667 | name = "windows-sys" 668 | version = "0.59.0" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 671 | dependencies = [ 672 | "windows-targets", 673 | ] 674 | 675 | [[package]] 676 | name = "windows-sys" 677 | version = "0.61.2" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 680 | dependencies = [ 681 | "windows-link", 682 | ] 683 | 684 | [[package]] 685 | name = "windows-targets" 686 | version = "0.52.6" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 689 | dependencies = [ 690 | "windows_aarch64_gnullvm", 691 | "windows_aarch64_msvc", 692 | "windows_i686_gnu", 693 | "windows_i686_gnullvm", 694 | "windows_i686_msvc", 695 | "windows_x86_64_gnu", 696 | "windows_x86_64_gnullvm", 697 | "windows_x86_64_msvc", 698 | ] 699 | 700 | [[package]] 701 | name = "windows_aarch64_gnullvm" 702 | version = "0.52.6" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 705 | 706 | [[package]] 707 | name = "windows_aarch64_msvc" 708 | version = "0.52.6" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 711 | 712 | [[package]] 713 | name = "windows_i686_gnu" 714 | version = "0.52.6" 715 | source = "registry+https://github.com/rust-lang/crates.io-index" 716 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 717 | 718 | [[package]] 719 | name = "windows_i686_gnullvm" 720 | version = "0.52.6" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 723 | 724 | [[package]] 725 | name = "windows_i686_msvc" 726 | version = "0.52.6" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 729 | 730 | [[package]] 731 | name = "windows_x86_64_gnu" 732 | version = "0.52.6" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 735 | 736 | [[package]] 737 | name = "windows_x86_64_gnullvm" 738 | version = "0.52.6" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 741 | 742 | [[package]] 743 | name = "windows_x86_64_msvc" 744 | version = "0.52.6" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 747 | 748 | [[package]] 749 | name = "yansi" 750 | version = "1.0.1" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" 753 | --------------------------------------------------------------------------------