├── .cargo └── config.toml ├── .gitignore ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── chisel ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── itempath ├── Cargo.toml └── src │ └── lib.rs ├── justfile ├── print.asm ├── rotth-analysis ├── Cargo.toml └── src │ ├── ctir.rs │ ├── inference.rs │ ├── lib.rs │ ├── tir.rs │ └── typecheck.rs ├── rotth-lexer ├── Cargo.toml └── src │ └── lib.rs ├── rotth-lsp ├── .gitignore ├── .vscode │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── Cargo.toml ├── rotth-lsp │ ├── .vscodeignore │ ├── CHANGELOG.md │ ├── README.md │ ├── dist │ │ └── extension.js │ ├── package.json │ └── yarn.lock └── src │ ├── completion.rs │ ├── lib.rs │ ├── main.rs │ └── semantic_token.rs ├── rotth-parser ├── Cargo.toml └── src │ ├── ast.rs │ ├── ast │ └── parsers.rs │ ├── hir.rs │ ├── lib.rs │ └── types.rs ├── rotth-src ├── core.rh ├── examples │ ├── args.rh │ ├── bind.rh │ ├── bind_nested.rh │ ├── comparsions.rh │ ├── cond.rh │ ├── cond_loop.rh │ ├── const.rh │ ├── count.rh │ ├── divmod.rh │ ├── early_return.rh │ ├── echo.rh │ ├── exit.rh │ ├── fib.rh │ ├── fib_bind.rh │ ├── fib_loop.rh │ ├── foobar.rh │ ├── generic.rh │ ├── generic_struct.rh │ ├── if.rh │ ├── mem.rh │ ├── module_structure │ │ ├── main.rh │ │ └── main │ │ │ └── foo.rh │ ├── parametrised_generic.rh │ ├── pointer_to.rh │ ├── print_string.rh │ ├── recurse.rh │ ├── redefinition.rh │ ├── simple_bind.rh │ ├── simple_cond.rh │ ├── simple_if.rh │ ├── simple_loop.rh │ ├── simple_string.rh │ ├── simplegeneric.rh │ ├── simpleprint.rh │ ├── struct.rh │ ├── utoa.rh │ └── write_u8.rh ├── main.rh ├── rotth.rh ├── std.rh └── syscalls.rh ├── rotth-vscode ├── .gitignore ├── .vscode │ └── launch.json ├── .vscodeignore ├── CHANGELOG.md ├── README.md ├── language-configuration.json ├── package.json └── syntaxes │ └── rotth.tmLanguage.json ├── rotth ├── Cargo.toml └── src │ ├── emit.rs │ ├── emit │ ├── asm.rs │ ├── cranelift.rs │ └── llvm.rs │ ├── eval.rs │ ├── lib.rs │ ├── lir.rs │ ├── lir2.rs │ ├── lir2 │ └── cfg.rs │ └── main.rs ├── rust-toolchain.toml ├── simplearena ├── Cargo.toml └── src │ └── lib.rs └── spanner ├── Cargo.toml └── src └── lib.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-linux-gnu] 2 | rustflags = [ 3 | "-Clink-arg=-fuse-ld=lld", 4 | "-Clink-arg=-Wl,--no-rosegment", 5 | ] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.asm 3 | *.o 4 | /chumsky -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug executable 'rotth'", 11 | "cargo": { 12 | "args": ["build", "--bin=rotth", "--package=rotth"], 13 | "filter": { 14 | "name": "rotth", 15 | "kind": "bin" 16 | } 17 | }, 18 | "args": ["rotth-src/examples/count.rh"], 19 | "cwd": "${workspaceFolder}" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "rotth", 5 | "rotth-lexer", 6 | "rotth-parser", 7 | "rotth-analysis", 8 | "rotth-lsp", 9 | "spanner", 10 | "simplearena", 11 | "chisel", 12 | "itempath", 13 | ] 14 | 15 | [workspace.dependencies] 16 | chumsky = "1.0.0-alpha.6" 17 | logos = "0.14" 18 | ariadne = "0.4" 19 | clap = { version = "4", features = ["derive"] } 20 | smol_str = { version = "0.2", features = ["serde"] } 21 | thiserror = "1.0" 22 | serde = { version = "1.0.197", features = ["derive"] } 23 | tokio = { version = "1.36.0", features = ["fs", "rt-multi-thread"] } 24 | internment = "0.7.4" 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ivan Chinenov 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rotth 2 | ### A stack-cringe language inspired by [Porth](https://gitlab.com/tsoding/porth) 3 | 4 | ## Syntax 5 | ```rotth 6 | include "std.rh" 7 | 8 | proc main : u64 do 9 | "Hello, world!\n" puts 10 | 0 11 | end 12 | ``` 13 | 14 | Rotth has following keywords: 15 | - `include` 16 | - `if` 17 | - `else` 18 | - `proc` 19 | - `while` 20 | - `do` 21 | - `bind` 22 | - `const` 23 | - `end` 24 | - `return` 25 | - `cond` 26 | 27 | ### `proc` 28 | Keyword `proc` declares a procedure. It is followed by procedure name, then it's inputs and outputs separated by the `:` signature separator. 29 | Body of the procedure is terminated by `end` keyword. 30 | ### `if` and `else` 31 | `if` keyword is a primary conditional construct of the language. It must be preceded by an expression of type `bool` and followed by true branch, then by optional `else` branch and finally by `end` terminator. 32 | ### `while do` 33 | `while` is the looping construct. It is followed by loop condition, then `do` keyword, then loop body, then `end`. 34 | ### `const` 35 | `const` followed by name and type, separated by `:`, declares a compile-time constant. It supports limited compile-time evaluation, syscalls and user-defined proc calls are not allowed. 36 | ### `bind` 37 | `bind` is similliar to destructuring in traditional functional languages, it iakes elements from the stack and allows using them as local constants. For example, this is how you can implement `Forth` `rot` word using it: 38 | ```rotth 39 | bind a : u64 b : ptr c : char do 40 | b c a 41 | end 42 | ``` 43 | ### `cond` 44 | Despite it's name `cond` is more similliar to `Rust`'s `match` than to `Lisp`'s `cond`, taking only constants and literal values as patterns to compare against. 45 | ```rotth 46 | some-char cond 47 | 'a' do 48 | "hello\n" 49 | else 50 | 'b' do 51 | "bye\n" 52 | else 53 | 'c' do 54 | "get out\n" 55 | end 56 | ``` -------------------------------------------------------------------------------- /chisel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chisel" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | clap.workspace = true 8 | serde.workspace = true 9 | 10 | tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } 11 | tracing = "0.1.40" 12 | tokio.workspace = true 13 | tower-http = { version = "0.5.2", features = ["trace"] } 14 | axum = "0.7.4" 15 | axum-macros = "0.4.1" 16 | itempath = { path = "../itempath" } 17 | -------------------------------------------------------------------------------- /chisel/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use itempath::ItemPathBuf; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Serialize, Deserialize, Clone)] 7 | pub struct ModuleContent { 8 | pub path: PathBuf, 9 | pub module_path: ItemPathBuf, 10 | pub contents: String, 11 | } 12 | 13 | #[derive(Serialize, Deserialize)] 14 | pub struct ModuleRequest { 15 | pub path: ItemPathBuf, 16 | } 17 | -------------------------------------------------------------------------------- /chisel/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, path::PathBuf, sync::Arc}; 2 | 3 | use axum::{extract::Query, http::StatusCode, routing::get, Extension, Json, Router}; 4 | use chisel::{ModuleContent, ModuleRequest}; 5 | use itempath::ItemPathBuf; 6 | use tokio::{net::TcpListener, sync::RwLock}; 7 | use tower_http::trace::TraceLayer; 8 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 9 | 10 | #[tokio::main] 11 | async fn main() -> std::io::Result<()> { 12 | tracing_subscriber::registry() 13 | .with( 14 | tracing_subscriber::EnvFilter::try_from_default_env() 15 | .unwrap_or_else(|_| "chisel=debug,tower_http=debug,axum::rejection=trace".into()), 16 | ) 17 | .with(tracing_subscriber::fmt::layer()) 18 | .init(); 19 | 20 | let cwd = std::env::current_dir()?; 21 | let root = std::env::var("ROTTH_SRC") 22 | .map(PathBuf::from) 23 | .unwrap_or_else(|_| cwd.join("src")); 24 | let root = Arc::new(root); 25 | let cache: Arc>> = Default::default(); 26 | 27 | let app = Router::new() 28 | .route("/module", get(module)) 29 | .layer(TraceLayer::new_for_http()) 30 | .layer(Extension(cache)) 31 | .layer(Extension(root)); 32 | 33 | let listener = TcpListener::bind("localhost:4269").await?; 34 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 35 | axum::serve(listener, app).await 36 | } 37 | 38 | async fn module( 39 | Query(ModuleRequest { path: request }): Query, 40 | Extension(root): Extension>, 41 | Extension(cache): Extension>>>, 42 | ) -> (StatusCode, Json>) { 43 | let cache_read = cache.read().await; 44 | let content = match cache_read.get(&request) { 45 | Some(c) => c.clone(), 46 | None => { 47 | let mut path = request 48 | .iter() 49 | .fold((*root).clone(), |acc, frag| acc.join(frag.as_str())); 50 | path.set_extension("rh"); 51 | 52 | let Ok(contents) = tokio::fs::read_to_string(&path).await else { 53 | return ( 54 | StatusCode::NOT_FOUND, 55 | Json(Err(path.to_string_lossy().to_string())), 56 | ); 57 | }; 58 | let content = ModuleContent { 59 | path, 60 | module_path: request.clone(), 61 | contents, 62 | }; 63 | drop(cache_read); 64 | cache.write().await.insert(request, content.clone()); 65 | content 66 | } 67 | }; 68 | 69 | (StatusCode::OK, Json(Ok(content))) 70 | } 71 | -------------------------------------------------------------------------------- /itempath/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "itempath" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | smol_str.workspace = true 8 | serde.workspace = true 9 | -------------------------------------------------------------------------------- /itempath/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(iter_intersperse)] 2 | 3 | use std::{ 4 | borrow::Borrow, 5 | fmt::{Debug, Display}, 6 | ops::{Deref, DerefMut}, 7 | }; 8 | 9 | use serde::{de::Visitor, Deserialize, Serialize}; 10 | use smol_str::SmolStr; 11 | 12 | #[derive(PartialEq, Eq, Hash)] 13 | #[repr(transparent)] 14 | pub struct ItemPath { 15 | segments: [SmolStr], 16 | } 17 | 18 | impl PartialEq<&ItemPath> for ItemPathBuf { 19 | fn eq(&self, other: &&ItemPath) -> bool { 20 | self.segments == other.segments 21 | } 22 | } 23 | 24 | impl PartialEq for ItemPathBuf { 25 | fn eq(&self, other: &ItemPath) -> bool { 26 | self.segments == other.segments 27 | } 28 | } 29 | 30 | impl PartialEq for ItemPath { 31 | fn eq(&self, other: &ItemPathBuf) -> bool { 32 | self.segments == other.segments 33 | } 34 | } 35 | 36 | impl ItemPath { 37 | pub fn iter(&'_ self) -> impl Iterator { 38 | self.segments.iter() 39 | } 40 | 41 | pub fn only(&self) -> Option<&str> { 42 | self.segments.first().map(SmolStr::as_str) 43 | } 44 | 45 | pub fn last(&self) -> Option { 46 | self.segments.last().cloned() 47 | } 48 | 49 | pub fn segment_mut(&mut self, n: usize) -> Option<&mut SmolStr> { 50 | self.segments.get_mut(n) 51 | } 52 | 53 | pub fn drop_first(&self) -> Option<&Self> { 54 | self.segments 55 | .split_first() 56 | .and_then(|(_, b)| if b.is_empty() { None } else { Some(b) }) 57 | .map(|s| unsafe { std::mem::transmute::<&[SmolStr], &ItemPath>(s) }) 58 | } 59 | 60 | pub fn parent(&self) -> Option<&ItemPath> { 61 | if self.segments.is_empty() { 62 | None 63 | } else { 64 | let segments = &self.segments[..self.segments.len() - 1]; 65 | let parent = unsafe { std::mem::transmute::<&[SmolStr], &ItemPath>(segments) }; 66 | Some(parent) 67 | } 68 | } 69 | 70 | pub fn join(&self, other: &Self) -> ItemPathBuf { 71 | let mut segments = self.segments.to_owned(); 72 | segments.extend_from_slice(&other.segments); 73 | ItemPathBuf { segments } 74 | } 75 | 76 | pub fn child(&self, segment: impl Into) -> ItemPathBuf { 77 | let mut segments = self.segments.to_owned(); 78 | segments.push(segment.into()); 79 | ItemPathBuf { segments } 80 | } 81 | } 82 | 83 | #[macro_export] 84 | macro_rules! path { 85 | ( $( $s:tt )::+ ) => {{ 86 | let mut path = $crate::ItemPathBuf::new(); 87 | $(path.push(stringify!($s));)* 88 | path 89 | }}; 90 | () => {{ 91 | ItemPathBuf::new() 92 | }}; 93 | } 94 | 95 | #[derive(PartialEq, Eq, Hash, Default, Clone)] 96 | #[repr(transparent)] 97 | pub struct ItemPathBuf { 98 | segments: Vec, 99 | } 100 | 101 | struct ITPVisitor; 102 | 103 | impl<'de> Visitor<'de> for ITPVisitor { 104 | type Value = ItemPathBuf; 105 | 106 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 107 | formatter.write_str("an item path") 108 | } 109 | 110 | fn visit_borrowed_str(self, v: &'de str) -> Result 111 | where 112 | E: serde::de::Error, 113 | { 114 | let mut itp = ItemPathBuf::new(); 115 | for seg in v.split("::") { 116 | itp.push(seg); 117 | } 118 | Ok(itp) 119 | } 120 | 121 | fn visit_string(self, v: String) -> Result 122 | where 123 | E: serde::de::Error, 124 | { 125 | self.visit_borrowed_str(&v) 126 | } 127 | 128 | fn visit_str(self, v: &str) -> Result 129 | where 130 | E: serde::de::Error, 131 | { 132 | self.visit_borrowed_str(v) 133 | } 134 | } 135 | 136 | impl<'de> Deserialize<'de> for ItemPathBuf { 137 | fn deserialize(deserializer: D) -> Result 138 | where 139 | D: serde::Deserializer<'de>, 140 | { 141 | deserializer.deserialize_str(ITPVisitor) 142 | } 143 | } 144 | 145 | impl Serialize for ItemPathBuf { 146 | fn serialize(&self, serializer: S) -> Result 147 | where 148 | S: serde::Serializer, 149 | { 150 | let separator = SmolStr::from("::"); 151 | let segments = self.segments.iter(); 152 | let iter: String = segments 153 | .intersperse(&separator) 154 | .map(ToString::to_string) 155 | .collect(); 156 | serializer.serialize_str(&iter) 157 | } 158 | } 159 | 160 | impl Deref for ItemPathBuf { 161 | type Target = ItemPath; 162 | 163 | fn deref(&self) -> &Self::Target { 164 | unsafe { std::mem::transmute::<&[SmolStr], &ItemPath>(&*self.segments) } 165 | } 166 | } 167 | 168 | impl DerefMut for ItemPathBuf { 169 | fn deref_mut(&mut self) -> &mut Self::Target { 170 | unsafe { std::mem::transmute::<&mut [SmolStr], &mut ItemPath>(&mut *self.segments) } 171 | } 172 | } 173 | 174 | impl> From> for ItemPathBuf { 175 | fn from(segments: Vec) -> Self { 176 | Self { 177 | segments: segments.into_iter().map(Into::into).collect(), 178 | } 179 | } 180 | } 181 | 182 | impl From for ItemPathBuf { 183 | fn from(segment: SmolStr) -> Self { 184 | Self { 185 | segments: vec![segment], 186 | } 187 | } 188 | } 189 | 190 | impl Borrow for ItemPathBuf { 191 | fn borrow(&self) -> &ItemPath { 192 | self.deref() 193 | } 194 | } 195 | 196 | impl ToOwned for ItemPath { 197 | type Owned = ItemPathBuf; 198 | 199 | fn to_owned(&self) -> Self::Owned { 200 | let segments = self.segments.to_owned(); 201 | Self::Owned { segments } 202 | } 203 | } 204 | 205 | impl ItemPathBuf { 206 | pub fn new() -> Self { 207 | Self::default() 208 | } 209 | 210 | pub fn push(&mut self, segment: impl Into) { 211 | self.segments.push(segment.into()) 212 | } 213 | } 214 | 215 | impl Debug for ItemPath { 216 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 217 | let separator = SmolStr::from("::"); 218 | let segments = self.segments.iter(); 219 | let iter = segments.intersperse(&separator); 220 | for s in iter { 221 | write!(f, "{s}")? 222 | } 223 | Ok(()) 224 | } 225 | } 226 | 227 | impl Display for ItemPath { 228 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 229 | Debug::fmt(self, f) 230 | } 231 | } 232 | 233 | impl Debug for ItemPathBuf { 234 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 235 | Debug::fmt(self.deref(), f) 236 | } 237 | } 238 | 239 | impl Display for ItemPathBuf { 240 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 241 | Display::fmt(self.deref(), f) 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | root := justfile_directory() 2 | vscdir := (root + "/rotth-vscode") 3 | rotthdir := (root + "/rotth-src/examples") 4 | compiler := (root + "/rotth-src/rotth") 5 | 6 | default: 7 | @just --list 8 | 9 | assemble FILE: 10 | nasm -g -F dwarf -f elf64 {{root}}/print.asm -o {{root}}/print.o 11 | nasm -g -F dwarf -f elf64 {{FILE}}.asm -o {{FILE}}.o 12 | ld -o {{FILE}} {{FILE}}.o {{root}}/print.o 13 | 14 | build FILE: 15 | cargo run --bin rotth -- -- {{rotthdir}}/{{FILE}}.rh 16 | nasm -g -F dwarf -f elf64 {{root}}/print.asm -o {{root}}/print.o 17 | nasm -g -F dwarf -f elf64 {{rotthdir}}/{{FILE}}.asm -o {{rotthdir}}/{{FILE}}.o 18 | ld -o {{rotthdir}}/{{FILE}} {{rotthdir}}/{{FILE}}.o {{root}}/print.o 19 | 20 | run FILE: (build FILE) 21 | {{rotthdir}}/{{FILE}} 22 | 23 | gdb FILE: (build FILE) 24 | gdb --tui {{rotthdir}}/{{FILE}} 25 | 26 | compiler: 27 | cargo run --bin rotth -- -- {{compiler}}.rh 28 | nasm -g -F dwarf -f elf64 {{root}}/print.asm -o {{root}}/print.o 29 | nasm -g -F dwarf -f elf64 {{compiler}}.asm -o {{compiler}}.o 30 | ld -o {{compiler}} {{compiler}}.o {{root}}/print.o 31 | {{compiler}} 32 | 33 | clean-run FILE: clean (run FILE) 34 | 35 | clean: 36 | find {{rotthdir}} -type f ! -name "*.rh" -delete 37 | 38 | build-extension: 39 | cd {{vscdir}} && yes | vsce package 40 | -------------------------------------------------------------------------------- /print.asm: -------------------------------------------------------------------------------- 1 | section .data 2 | buffer times 32 db 0 3 | 4 | section .text 5 | global print 6 | 7 | print: 8 | call ft_itoa 9 | mov rdi, buffer 10 | call cstrlen 11 | 12 | mov rdi, 1 ; fd 13 | mov rsi, buffer ; buffer 14 | xor rdx, rdx 15 | mov rdx, rax ; count 16 | mov rax, 1 ; write(2) 17 | syscall 18 | 19 | ret 20 | 21 | ;====================================================== 22 | 23 | ; https://tuttlem.github.io/2013/01/08/strlen-implementation-in-nasm.html 24 | 25 | cstrlen: 26 | push rbx ; save any registers that 27 | push rcx ; we will trash in here 28 | 29 | mov rbx, rdi ; rbx = rdi 30 | 31 | xor al, al ; the byte that the scan will 32 | ; compare to is zero 33 | 34 | mov rcx, 0xffffffff ; the maximum number of bytes 35 | 36 | repne scasb ; while [rdi] != al, keep scanning 37 | 38 | sub rdi, rbx ; length = dist2 - dist1 39 | mov rax, rdi ; rax now holds our length 40 | 41 | pop rcx ; restore the saved registers 42 | pop rbx 43 | 44 | ret ; all done! 45 | 46 | ;====================================================== 47 | 48 | ; https://stackoverflow.com/a/54381686/12015230 49 | 50 | ft_itoa: 51 | xor rcx, rcx ;initialize counter 52 | xor r9, r9 ;set neg flag to 0 53 | mov eax, edi ;move number in RAX for DIV instruction 54 | push rbx ;save RBX 55 | mov ebx, 10 56 | 57 | .check_negative: 58 | and edi, 0x80000000 59 | mov rdi, buffer 60 | jz .divide ;number is positive, proceed to main loop 61 | not eax ;else 62 | inc eax ;compute absolute value with binary complement 63 | inc r9 ;set neg flag 64 | 65 | .divide: 66 | xor edx, edx 67 | div ebx 68 | add edx, 48 ;convert int to char 69 | push rdx 70 | inc rcx 71 | cmp eax, 0 72 | jnz .divide 73 | 74 | .check_neg_flag: 75 | cmp r9, 1 76 | jne .buff_string 77 | mov byte[rdi], '-' 78 | 79 | .buff_string: 80 | pop rdx 81 | mov byte[rdi + r9], dl 82 | dec rcx 83 | inc r9 84 | cmp rcx, 0 85 | jnz .buff_string 86 | mov byte[rdi + r9], 10 87 | mov byte[rdi + r9 + 1], 0 88 | pop rbx ;restore RBX 89 | ret -------------------------------------------------------------------------------- /rotth-analysis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rotth-analysis" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | somok = "1.5.0" 10 | simplearena = { path = "../simplearena" } 11 | fnv = "1.0" 12 | rotth-lexer = { path = "../rotth-lexer" } 13 | rotth-parser = { path = "../rotth-parser" } 14 | spanner = { path = "../spanner" } 15 | itempath = { path = "../itempath" } 16 | thiserror.workspace = true 17 | smol_str.workspace = true 18 | -------------------------------------------------------------------------------- /rotth-analysis/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(assert_matches)] 2 | #![feature(iter_intersperse)] 3 | #![feature(box_patterns)] 4 | #![feature(string_remove_matches)] 5 | #![feature(type_alias_impl_trait)] 6 | #![feature(array_windows)] 7 | 8 | use ctir::ConcreteError; 9 | use inference::TypeInfo; 10 | use spanner::Span; 11 | 12 | pub mod ctir; 13 | pub mod inference; 14 | pub mod tir; 15 | pub mod typecheck; 16 | 17 | pub fn error(span: Span, kind: ErrorKind, message: M) -> Result { 18 | Err(Error::new(Some(span), kind, message)) 19 | } 20 | 21 | pub fn concrete_error( 22 | span: Option, 23 | kind: ConcreteError, 24 | message: M, 25 | ) -> Result { 26 | Err(Error::new(span, ErrorKind::Concrete(kind), message)) 27 | } 28 | 29 | #[derive(Debug)] 30 | pub struct Error { 31 | pub span: Option, 32 | pub kind: ErrorKind, 33 | pub message: String, 34 | } 35 | 36 | impl Error { 37 | fn new(span: Option, kind: ErrorKind, message: impl ToString) -> Error { 38 | Error { 39 | span, 40 | kind, 41 | message: message.to_string(), 42 | } 43 | } 44 | } 45 | 46 | #[derive(Debug)] 47 | pub enum ErrorKind { 48 | TypeMismatch { 49 | expected: Vec, 50 | actual: Vec, 51 | }, 52 | UnificationError(String), 53 | UnsupportedOperation, 54 | NotEnoughData, 55 | Undefined, 56 | InvalidMain, 57 | InvalidWhile, 58 | CompStop, 59 | Unexpected, 60 | CallInConst, 61 | Concrete(ConcreteError), 62 | } 63 | -------------------------------------------------------------------------------- /rotth-analysis/src/typecheck.rs: -------------------------------------------------------------------------------- 1 | use crate::inference::{ReifiedType, TermId}; 2 | use rotth_parser::{hir::Hir, types}; 3 | use simplearena::{Heap, Ref}; 4 | use somok::Somok; 5 | use spanner::Spanned; 6 | use std::collections::VecDeque; 7 | 8 | pub trait TypeRepr {} 9 | impl TypeRepr for ReifiedType {} 10 | impl TypeRepr for TermId {} 11 | impl TypeRepr for types::Type {} 12 | 13 | pub trait NodeRepr {} 14 | impl NodeRepr for Spanned {} 15 | 16 | #[derive(Clone, Default)] 17 | pub struct TypeStack { 18 | top: Option, 19 | } 20 | 21 | impl TypeStack { 22 | pub fn push(&mut self, heap: &mut THeap, ty: TermId) { 23 | let frame = TypeFrame { 24 | ty, 25 | prev: self.top.clone(), 26 | }; 27 | self.top = heap.alloc(frame).some(); 28 | } 29 | 30 | pub fn pop(&mut self, heap: &THeap) -> Option { 31 | if let Some(top) = self.top.clone() { 32 | let top = top.deref(heap).unwrap(); 33 | let prev = top.prev.clone(); 34 | self.top = prev; 35 | top.ty.some() 36 | } else { 37 | None 38 | } 39 | } 40 | 41 | pub fn from_iter(tys: impl Iterator, heap: &mut THeap) -> Self { 42 | let mut stack = Self::default(); 43 | for ty in tys { 44 | stack.push(heap, ty) 45 | } 46 | stack 47 | } 48 | 49 | pub fn into_vec(self, heap: &THeap) -> Vec { 50 | let mut res = VecDeque::new(); 51 | let mut next = self.top; 52 | while let Some(top) = next { 53 | let top = top.deref(heap).unwrap(); 54 | res.push_front(top.ty); 55 | next = top.prev.clone() 56 | } 57 | res.into() 58 | } 59 | } 60 | 61 | #[derive(Debug, Clone)] 62 | pub struct TypeFrame { 63 | ty: TermId, 64 | prev: Option, 65 | } 66 | 67 | type TRef = Ref; 68 | pub type THeap = Heap; 69 | -------------------------------------------------------------------------------- /rotth-lexer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rotth-lexer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | logos.workspace = true 10 | smol_str.workspace = true 11 | internment.workspace = true 12 | spanner = { path = "../spanner" } 13 | -------------------------------------------------------------------------------- /rotth-lexer/src/lib.rs: -------------------------------------------------------------------------------- 1 | use internment::Intern; 2 | use logos::{Lexer, Logos}; 3 | use smol_str::SmolStr; 4 | use spanner::Span; 5 | use std::path::PathBuf; 6 | 7 | fn to_char(l: &'_ mut Lexer<'_, Token>) -> Option { 8 | l.slice().chars().nth(1) 9 | } 10 | 11 | fn to_bool(l: &'_ mut Lexer<'_, Token>) -> Option { 12 | match l.slice() { 13 | "true" => Some(true), 14 | "false" => Some(false), 15 | _ => None, 16 | } 17 | } 18 | 19 | fn to_smol_str(l: &'_ mut Lexer<'_, Token>) -> SmolStr { 20 | SmolStr::from(l.slice()) 21 | } 22 | 23 | #[derive(Clone, Hash, PartialEq, Eq, Logos)] 24 | pub enum Token { 25 | #[token("->", priority = 1000000)] 26 | FieldAccess, 27 | #[token("&>", priority = 1000000)] 28 | Ptr, 29 | #[token("&?&")] 30 | CompStop, 31 | #[token("@")] 32 | Read, 33 | #[token("!")] 34 | Write, 35 | #[regex( 36 | r"[()\{\}<>\|\\/#$%^&*\-=+?][()\{\}<>\|\\/!@#$%^&*\-=+?]?", 37 | to_smol_str 38 | )] 39 | Operator(SmolStr), 40 | #[regex( 41 | r"[_A-Za-z][()\{\}<>\|\\/!#$%^&*\-=+_?A-Za-z0-9]*", 42 | to_smol_str, 43 | priority = 0 44 | )] 45 | Word(SmolStr), 46 | #[regex("false|true", to_bool)] 47 | Bool(bool), 48 | #[regex(r#""(?:[^"]|\\")*""#, to_smol_str)] 49 | String(SmolStr), 50 | #[regex(r"'.'", to_char)] 51 | Char(char), 52 | #[regex(r"(-?[1-9][0-9]*)|0", to_smol_str)] 53 | Num(SmolStr), 54 | #[token(":")] 55 | SigSep, 56 | #[token("::")] 57 | PathSep, 58 | #[token("[")] 59 | LBracket, 60 | #[token("]")] 61 | RBracket, 62 | 63 | #[token("module")] 64 | KwModule, 65 | #[token("use")] 66 | KwUse, 67 | #[token("from")] 68 | KwFrom, 69 | #[token("return")] 70 | KwReturn, 71 | #[token("cond")] 72 | KwCond, 73 | #[token("if")] 74 | KwIf, 75 | #[token("else")] 76 | KwElse, 77 | #[token("proc")] 78 | KwProc, 79 | #[token("while")] 80 | KwWhile, 81 | #[token("do")] 82 | KwDo, 83 | #[token("bind")] 84 | KwBind, 85 | #[token("const")] 86 | KwConst, 87 | #[token("var")] 88 | KwVar, 89 | #[token("struct")] 90 | KwStruct, 91 | #[token("cast")] 92 | KwCast, 93 | #[token("end")] 94 | KwEnd, 95 | 96 | #[regex(r";.*\n", logos::skip)] 97 | Comment, 98 | 99 | #[regex(r"\p{Whitespace}+", logos::skip)] 100 | Whitespace, 101 | 102 | Error, 103 | } 104 | 105 | impl std::fmt::Debug for Token { 106 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 107 | match self { 108 | Token::Word(word) => write!(f, "W({word})"), 109 | Token::Read => write!(f, "@"), 110 | Token::Write => write!(f, "!"), 111 | Token::Operator(op) => write!(f, "O({op})"), 112 | Token::Bool(b) => write!(f, "{b}"), 113 | Token::String(str) => write!(f, "{str:?}"), 114 | Token::Char(c) => write!(f, "{c:?}"), 115 | Token::Num(num) => write!(f, "{num}"), 116 | Token::SigSep => write!(f, ":"), 117 | Token::PathSep => write!(f, "::"), 118 | Token::Ptr => write!(f, "&>"), 119 | Token::CompStop => write!(f, "&?&"), 120 | Token::FieldAccess => write!(f, "->"), 121 | Token::LBracket => write!(f, "["), 122 | Token::RBracket => write!(f, "]"), 123 | Token::KwUse => write!(f, "use"), 124 | Token::KwModule => write!(f, "module"), 125 | Token::KwFrom => write!(f, "from"), 126 | Token::KwReturn => write!(f, "return"), 127 | Token::KwCond => write!(f, "cond"), 128 | Token::KwIf => write!(f, "if"), 129 | Token::KwElse => write!(f, "else"), 130 | Token::KwProc => write!(f, "proc"), 131 | Token::KwWhile => write!(f, "while"), 132 | Token::KwDo => write!(f, "do"), 133 | Token::KwBind => write!(f, "bind"), 134 | Token::KwConst => write!(f, "const"), 135 | Token::KwVar => write!(f, "var"), 136 | Token::KwStruct => write!(f, "struct"), 137 | Token::KwCast => write!(f, "cast"), 138 | Token::KwEnd => write!(f, "end"), 139 | Token::Comment => write!(f, ""), 140 | Token::Whitespace => write!(f, ""), 141 | Token::Error => write!(f, ""), 142 | } 143 | } 144 | } 145 | 146 | impl std::fmt::Display for Token { 147 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 148 | ::fmt(self, f) 149 | } 150 | } 151 | 152 | pub fn lex(src: &str, path: Intern) -> Vec<(Token, Span)> { 153 | Token::lexer(src) 154 | .spanned() 155 | .map(|(t, s)| match t { 156 | Ok(t) => (t, Span::new(path, s.start, s.end)), 157 | Err(()) => (Token::Error, Span::new(path, s.start, s.end)), 158 | }) 159 | .collect() 160 | } 161 | -------------------------------------------------------------------------------- /rotth-lsp/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | node_modules 3 | out/ 4 | .pnpm-debug.log 5 | *.vsix -------------------------------------------------------------------------------- /rotth-lsp/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | { 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "name": "Launch Client", 9 | "runtimeExecutable": "${execPath}", 10 | "args": [ 11 | "--extensionDevelopmentPath=${workspaceRoot}" 12 | ], 13 | "outFiles": [ 14 | "${workspaceRoot}/client/out/**/*.js" 15 | ], 16 | "preLaunchTask": { 17 | "type": "npm", 18 | "script": "watch" 19 | }, 20 | "env": { 21 | "SERVER_PATH": "${workspaceRoot}/target/debug/nrs-language-server" 22 | } 23 | }, 24 | // { 25 | // "type": "node", 26 | // "request": "attach", 27 | // "name": "Attach to Server", 28 | // "port": 6009, 29 | // "restart": true, 30 | // "outFiles": ["${workspaceRoot}/server/out/**/*.js"] 31 | // }, 32 | { 33 | "name": "Language Server E2E Test", 34 | "type": "extensionHost", 35 | "request": "launch", 36 | "runtimeExecutable": "${execPath}", 37 | "args": [ 38 | "--extensionDevelopmentPath=${workspaceRoot}", 39 | "--extensionTestsPath=${workspaceRoot}/client/out/test/index", 40 | "${workspaceRoot}/client/testFixture" 41 | ], 42 | "outFiles": [ 43 | "${workspaceRoot}/client/out/test/**/*.js" 44 | ] 45 | } 46 | ], 47 | "compounds": [ 48 | { 49 | "name": "Client + Server", 50 | "configurations": [ 51 | "Launch Client", 52 | "Attach to Server" 53 | ] 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /rotth-lsp/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /rotth-lsp/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "compile", 9 | "group": "build", 10 | "presentation": { 11 | "panel": "dedicated", 12 | "reveal": "never" 13 | }, 14 | "problemMatcher": [ 15 | "$tsc" 16 | ] 17 | }, 18 | { 19 | "type": "npm", 20 | "script": "watch", 21 | "isBackground": true, 22 | "group": { 23 | "kind": "build", 24 | "isDefault": true 25 | }, 26 | "presentation": { 27 | "panel": "dedicated", 28 | "reveal": "never" 29 | }, 30 | "problemMatcher": [ 31 | "$tsc-watch" 32 | ] 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /rotth-lsp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rotth-lsp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | chumsky.workspace = true 10 | 11 | rotth = { path = "../rotth" } 12 | ropey = "1.3.2" 13 | tokio = { version = "1.17.0", features = ["full"] } 14 | tower-lsp = { version = "0.20.0" } 15 | dashmap = "5.1.0" 16 | somok = "1.3.0" 17 | rotth-parser = { path = "../rotth-parser" } 18 | rotth-lexer = { path = "../rotth-lexer" } 19 | spanner = { path = "../spanner" } 20 | smol_str.workspace = true 21 | -------------------------------------------------------------------------------- /rotth-lsp/rotth-lsp/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/** 4 | node_modules/** 5 | src/** 6 | .gitignore 7 | .yarnrc 8 | webpack.config.js 9 | vsc-extension-quickstart.md 10 | **/tsconfig.json 11 | **/.eslintrc.json 12 | **/*.map 13 | **/*.ts 14 | -------------------------------------------------------------------------------- /rotth-lsp/rotth-lsp/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "rotth-lsp" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /rotth-lsp/rotth-lsp/README.md: -------------------------------------------------------------------------------- 1 | # Rotth LSP 2 | 3 | A language server protocol implementation for Rotth -------------------------------------------------------------------------------- /rotth-lsp/rotth-lsp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rotth-lsp", 3 | "displayName": "Rotth LSP", 4 | "description": "Rotth Language Server extension", 5 | "version": "0.0.1", 6 | "engines": { 7 | "vscode": "^1.65.0" 8 | }, 9 | "categories": [ 10 | "Other" 11 | ], 12 | "activationEvents": [ 13 | "onLanguage:rotth" 14 | ], 15 | "main": "./dist/extension.js", 16 | "contributes": { 17 | "configuration": { 18 | "title": "Rotth LSP", 19 | "properties": { 20 | "rotthLSP.command": { 21 | "type": [ 22 | "string" 23 | ], 24 | "default": "rotth-lsp", 25 | "description": "Rotth LSP command" 26 | } 27 | } 28 | } 29 | }, 30 | "scripts": { 31 | "vscode:prepublish": "yarn run package", 32 | "compile": "webpack", 33 | "watch": "webpack --watch", 34 | "package": "webpack --mode production --devtool hidden-source-map", 35 | "compile-tests": "tsc -p . --outDir out", 36 | "watch-tests": "tsc -p . -w --outDir out", 37 | "pretest": "yarn run compile-tests && yarn run compile && yarn run lint", 38 | "lint": "eslint src --ext ts", 39 | "test": "node ./out/test/runTest.js" 40 | }, 41 | "dependencies": { 42 | "vscode-languageclient": "8.0.0-next.12" 43 | }, 44 | "devDependencies": { 45 | "@types/vscode": "^1.65.0", 46 | "@types/glob": "^7.2.0", 47 | "@types/mocha": "^9.1.0", 48 | "@types/node": "14.x", 49 | "@typescript-eslint/eslint-plugin": "^5.12.1", 50 | "@typescript-eslint/parser": "^5.12.1", 51 | "eslint": "^8.9.0", 52 | "glob": "^7.2.0", 53 | "mocha": "^9.2.1", 54 | "typescript": "^4.5.5", 55 | "ts-loader": "^9.2.6", 56 | "webpack": "^5.69.1", 57 | "webpack-cli": "^4.9.2", 58 | "@vscode/test-electron": "^2.1.2" 59 | } 60 | } -------------------------------------------------------------------------------- /rotth-lsp/src/completion.rs: -------------------------------------------------------------------------------- 1 | use rotth_parser::ast::*; 2 | use spanner::Spanned; 3 | 4 | #[derive(Debug)] 5 | pub enum CompleteCompletionItem { 6 | Const(String), 7 | Mem(String), 8 | Proc(String), 9 | } 10 | /// return (need_to_continue_search, founded reference) 11 | pub fn completion(ast: &[Spanned], ident_offset: usize) -> Vec { 12 | let mut res = Vec::default(); 13 | for item in ast.iter() { 14 | match &**item { 15 | TopLevel::Proc(p) => { 16 | if p.name.span.end < ident_offset { 17 | let Spanned { 18 | span: _, 19 | inner: Word(name), 20 | } = &p.name; 21 | res.push(CompleteCompletionItem::Proc(name.to_string())); 22 | } 23 | } 24 | TopLevel::Const(c) => { 25 | if c.name.span.end < ident_offset { 26 | let Spanned { 27 | span: _, 28 | inner: Word(name), 29 | } = &c.name; 30 | res.push(CompleteCompletionItem::Proc(name.to_string())); 31 | } 32 | } 33 | TopLevel::Mem(m) => { 34 | if m.name.span.end < ident_offset { 35 | let Spanned { 36 | span: _, 37 | inner: Word(name), 38 | } = &m.name; 39 | res.push(CompleteCompletionItem::Proc(name.to_string())); 40 | } 41 | } 42 | _ => (), 43 | } 44 | } 45 | res 46 | } 47 | -------------------------------------------------------------------------------- /rotth-lsp/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod completion; 2 | pub mod semantic_token; 3 | -------------------------------------------------------------------------------- /rotth-lsp/src/main.rs: -------------------------------------------------------------------------------- 1 | use chumsky::error::RichReason; 2 | use dashmap::DashMap; 3 | use ropey::Rope; 4 | use rotth_lexer::lex; 5 | use rotth_lsp::completion::{completion, CompleteCompletionItem}; 6 | use rotth_lsp::semantic_token::{semantic_token_from_ast, CompleteSemanticToken, LEGEND_TYPE}; 7 | use rotth_parser::ast::{parse, TopLevel}; 8 | use rotth_parser::ParserError; 9 | use somok::{Leaksome, Somok}; 10 | use spanner::Spanned; 11 | use std::collections::HashSet; 12 | use std::path::{Path, PathBuf}; 13 | use tokio::io::AsyncReadExt; 14 | use tower_lsp::jsonrpc::Result; 15 | use tower_lsp::lsp_types::*; 16 | use tower_lsp::{Client, LanguageServer, LspService, Server}; 17 | 18 | #[derive(Debug, Clone)] 19 | struct TextDocument { 20 | uri: Url, 21 | text: String, 22 | } 23 | 24 | #[derive(Debug)] 25 | struct Backend { 26 | client: Client, 27 | ast_map: DashMap>>, 28 | include_map: DashMap>, 29 | semantic_token_map: DashMap>, 30 | document_map: DashMap, 31 | } 32 | 33 | impl<'s> Backend { 34 | async fn parse_file( 35 | &self, 36 | parent: Option<&Path>, 37 | path: &Path, 38 | ) -> tokio::io::Result> { 39 | let path = if let Some(parent) = parent { 40 | parent.parent().unwrap().join(path).canonicalize() 41 | } else { 42 | path.canonicalize() 43 | } 44 | .unwrap(); 45 | // self.client 46 | // .log_message(MessageType::INFO, format!("Parsing file {:?}", &path)) 47 | // .await; 48 | let text = { 49 | let mut src = String::new(); 50 | let mut file = tokio::fs::File::open(&path).await?; 51 | file.read_to_string(&mut src).await?; 52 | src 53 | }; 54 | let ast = self 55 | .parse_text(TextDocument { 56 | uri: Url::from_file_path(&path).unwrap(), 57 | text, 58 | }) 59 | .await 60 | .unwrap(); 61 | let includes: Vec<(PathBuf, PathBuf)> = ast 62 | .iter() 63 | .filter_map(|i| { 64 | if let TopLevel::Include(inc) = &**i { 65 | let parent = i.span.file.to_path_buf(); 66 | let path = parent.parent().unwrap().join(&*inc.path); 67 | self.include_map 68 | .entry(parent.to_path_buf()) 69 | .or_default() 70 | .insert(path.clone()); 71 | (parent, path).some() 72 | } else { 73 | None 74 | } 75 | }) 76 | .collect(); 77 | self.ast_map.insert(path, ast); 78 | 79 | includes.okay() 80 | } 81 | 82 | async fn parse_text(&self, params: TextDocument) -> Option>> { 83 | let rope = ropey::Rope::from_str(¶ms.text); 84 | self.document_map 85 | .insert(params.uri.to_file_path().unwrap(), rope.clone()); 86 | let path = &*params.uri.to_file_path().unwrap().into_boxed_path().leak(); 87 | 88 | let tokens = lex(¶ms.text, path); 89 | let ast = match parse(tokens) { 90 | Ok(ast) => ast, 91 | Err(ParserError(es)) => { 92 | let _diagnostics = es 93 | .into_iter() 94 | .filter_map(|error| { 95 | let (message, span) = match error { 96 | rotth_parser::Error::Parser(e) => match e.reason() { 97 | RichReason::ExpectedFound { .. } => ( 98 | format!( 99 | "{}, expected {}", 100 | if e.found().is_some() { 101 | "Unexpected token in input" 102 | } else { 103 | "Unexpected end of input" 104 | }, 105 | if e.expected().len() == 0 { 106 | "something else".to_string() 107 | } else { 108 | e.expected() 109 | .map(ToString::to_string) 110 | .collect::>() 111 | .join(", ") 112 | } 113 | ), 114 | *e.span(), 115 | ), 116 | RichReason::Custom(msg) => (msg.to_string(), *e.span()), 117 | RichReason::Many(_) => todo!(), 118 | }, 119 | rotth_parser::Error::Redefinition(e) => { 120 | ("This item is redefined elsewhere".into(), e.redefined_item) 121 | } 122 | rotth_parser::Error::UnresolvedInclude(_) => todo!(), 123 | }; 124 | 125 | || -> Option { 126 | let start_position = offset_to_position(span.start, &rope)?; 127 | let end_position = offset_to_position(span.end, &rope)?; 128 | Some(Diagnostic::new_simple( 129 | Range::new(start_position, end_position), 130 | message, 131 | )) 132 | }() 133 | }) 134 | .collect::>(); 135 | 136 | // todo: unbreak diagnostics 137 | // self.client 138 | // .publish_diagnostics(params.uri.clone(), diagnostics, None) 139 | // .await; 140 | return None; 141 | } // TODO! 142 | }; 143 | 144 | ast.0.some() 145 | } 146 | 147 | async fn on_change(&self, params: TextDocument) { 148 | let ast = if let Some(ast) = self.parse_text(params.clone()).await { 149 | ast 150 | } else { 151 | return; 152 | }; 153 | 154 | self.semantic_token_map.insert( 155 | params.uri.to_file_path().unwrap(), 156 | semantic_token_from_ast(&ast), 157 | ); 158 | 159 | let mut includes = ast 160 | .iter() 161 | .filter_map(|i| { 162 | if let TopLevel::Include(inc) = &**i { 163 | let parent = i.span.file.to_owned(); 164 | let path = parent.parent().unwrap().join(&*inc.path); 165 | self.include_map 166 | .entry(parent.clone()) 167 | .or_default() 168 | .insert(path.clone()); 169 | (parent, path).some() 170 | } else { 171 | None 172 | } 173 | }) 174 | .collect::>(); 175 | self.ast_map.insert(params.uri.to_file_path().unwrap(), ast); 176 | 177 | while let Some((parent, path)) = includes.pop() { 178 | let extend = self.parse_file(Some(&parent), &path).await.unwrap(); 179 | includes.extend(extend) 180 | } 181 | 182 | // self.client 183 | // .log_message( 184 | // MessageType::INFO, 185 | // format!("Parsed AST for {:?}", params.uri.to_string()), 186 | // ) 187 | // .await; 188 | } 189 | } 190 | 191 | #[tower_lsp::async_trait] 192 | impl LanguageServer for Backend { 193 | async fn initialize(&self, _: InitializeParams) -> Result { 194 | Ok(InitializeResult { 195 | server_info: Some(ServerInfo { 196 | name: "rotth-lsp".into(), 197 | version: Some("0.1.0".into()), 198 | }), 199 | capabilities: ServerCapabilities { 200 | definition_provider: Some(OneOf::Left(true)), 201 | // references_provider: Some(OneOf::Left(true)), 202 | // rename_provider: Some(OneOf::Left(true)), 203 | text_document_sync: Some(TextDocumentSyncCapability::Kind( 204 | TextDocumentSyncKind::FULL, 205 | )), 206 | completion_provider: Some(CompletionOptions { 207 | resolve_provider: Some(false), 208 | trigger_characters: None, 209 | work_done_progress_options: Default::default(), 210 | all_commit_characters: None, 211 | }), 212 | semantic_tokens_provider: Some( 213 | SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions( 214 | SemanticTokensRegistrationOptions { 215 | text_document_registration_options: { 216 | TextDocumentRegistrationOptions { 217 | document_selector: Some(vec![DocumentFilter { 218 | language: Some("rotth".to_string()), 219 | scheme: Some("file".to_string()), 220 | pattern: None, 221 | }]), 222 | } 223 | }, 224 | semantic_tokens_options: SemanticTokensOptions { 225 | work_done_progress_options: WorkDoneProgressOptions::default(), 226 | legend: SemanticTokensLegend { 227 | token_types: LEGEND_TYPE.into(), 228 | token_modifiers: vec![], 229 | }, 230 | range: None, 231 | full: Some(SemanticTokensFullOptions::Bool(true)), 232 | }, 233 | static_registration_options: StaticRegistrationOptions::default(), 234 | }, 235 | ), 236 | ), 237 | ..Default::default() 238 | }, 239 | }) 240 | } 241 | 242 | async fn completion(&self, params: CompletionParams) -> Result> { 243 | let path = params 244 | .text_document_position 245 | .text_document 246 | .uri 247 | .to_file_path() 248 | .unwrap(); 249 | self.client 250 | .log_message(MessageType::INFO, format!("Completion in file {:?}", &path)) 251 | .await; 252 | let position = params.text_document_position.position; 253 | let completions = || -> Option> { 254 | let rope = self.document_map.get(&path)?; 255 | let includes = &*self.include_map.get(&path)?; 256 | let mut asts = self.ast_map.get(&path)?.clone(); 257 | for include in includes { 258 | let ast = self.ast_map.get(include)?.clone(); 259 | asts.extend(ast) 260 | } 261 | 262 | let char = rope.try_line_to_char(position.line as usize).ok()?; 263 | let offset = char + position.character as usize; 264 | let completions = completion(&asts, offset); 265 | let mut ret = Vec::with_capacity(completions.len()); 266 | for item in completions { 267 | match item { 268 | CompleteCompletionItem::Const(name) | CompleteCompletionItem::Mem(name) => { 269 | ret.push(CompletionItem { 270 | label: name.clone(), 271 | kind: Some(CompletionItemKind::CONSTANT), 272 | detail: Some(name.clone()), 273 | insert_text: Some(name), 274 | ..Default::default() 275 | }); 276 | } 277 | CompleteCompletionItem::Proc(name) => { 278 | ret.push(CompletionItem { 279 | label: name.clone(), 280 | kind: Some(CompletionItemKind::FUNCTION), 281 | detail: Some(name.clone()), 282 | insert_text: Some(name), 283 | insert_text_format: Some(InsertTextFormat::SNIPPET), 284 | ..Default::default() 285 | }); 286 | } 287 | } 288 | } 289 | Some(ret) 290 | }(); 291 | Ok(completions.map(CompletionResponse::Array)) 292 | } 293 | 294 | async fn initialized(&self, _: InitializedParams) { 295 | self.client 296 | .log_message(MessageType::INFO, "Rotth-LSP initialized!") 297 | .await; 298 | } 299 | 300 | async fn shutdown(&self) -> Result<()> { 301 | self.client 302 | .log_message(MessageType::INFO, "Rotth-LSP shutting down") 303 | .await; 304 | Ok(()) 305 | } 306 | 307 | async fn did_open(&self, params: DidOpenTextDocumentParams) { 308 | self.client 309 | .log_message( 310 | MessageType::INFO, 311 | format!("file opened! {:?}", params.text_document.uri.to_file_path()), 312 | ) 313 | .await; 314 | self.on_change(TextDocument { 315 | uri: params.text_document.uri, 316 | text: params.text_document.text, 317 | }) 318 | .await 319 | } 320 | 321 | async fn did_change(&self, mut params: DidChangeTextDocumentParams) { 322 | self.on_change(TextDocument { 323 | uri: params.text_document.uri, 324 | text: std::mem::take(&mut params.content_changes[0].text), 325 | }) 326 | .await 327 | } 328 | 329 | async fn goto_definition( 330 | &self, 331 | params: GotoDefinitionParams, 332 | ) -> Result> { 333 | let uri = params.text_document_position_params.text_document.uri; 334 | let rope = if let Some(r) = self.document_map.get(&uri.to_file_path().unwrap()) { 335 | r 336 | } else { 337 | return Ok(None); 338 | }; 339 | 340 | let position = params.text_document_position_params.position; 341 | let line = if let Some(l) = rope.get_line(position.line as usize) { 342 | l 343 | } else { 344 | return Ok(None); 345 | }; 346 | 347 | let mut last_space = 0; 348 | let mut word_start = None; 349 | let mut word_end = None; 350 | for (i, c) in line.chars().enumerate() { 351 | if c.is_whitespace() { 352 | if word_start.is_none() { 353 | last_space = i 354 | } else { 355 | word_end = Some(i); 356 | break; 357 | } 358 | } else if i == position.character as usize { 359 | word_start = Some(last_space + 1) 360 | } 361 | } 362 | let word = if let (Some(start), Some(end)) = (word_start, word_end) { 363 | if let Some(line) = line.as_str() { 364 | &line[start..end] 365 | } else { 366 | return Ok(None); 367 | } 368 | } else { 369 | return Ok(None); 370 | }; 371 | 372 | let item = self.ast_map.iter().find_map(|r| { 373 | for item in r.value() { 374 | if item.name().unwrap() == word { 375 | return item.clone().some(); 376 | } 377 | } 378 | None 379 | }); 380 | 381 | let definition = item.and_then(|item| { 382 | let span = &item.span; 383 | let uri = Url::from_file_path(&*span.file).unwrap(); 384 | 385 | let rope = &*self.document_map.get(span.file)?; 386 | 387 | let start_position = offset_to_position(span.start, rope)?; 388 | let end_position = offset_to_position(span.end, rope)?; 389 | 390 | let range = Range::new(start_position, end_position); 391 | 392 | Some(GotoDefinitionResponse::Scalar(Location::new(uri, range))) 393 | }); 394 | Ok(definition) 395 | } 396 | 397 | async fn semantic_tokens_full( 398 | &self, 399 | params: SemanticTokensParams, 400 | ) -> Result> { 401 | let uri = params.text_document.uri.to_file_path().unwrap(); 402 | self.client 403 | .log_message(MessageType::LOG, "semantic_token_full") 404 | .await; 405 | let semantic_tokens = || -> Option> { 406 | let mut im_complete_tokens = self.semantic_token_map.get_mut(&uri)?; 407 | let rope = self.document_map.get(&uri)?; 408 | 409 | im_complete_tokens.sort_by(|a, b| a.start.cmp(&b.start)); 410 | let mut pre_line = 0; 411 | let mut pre_start = 0; 412 | let semantic_tokens = im_complete_tokens 413 | .iter() 414 | .filter_map(|token| { 415 | let line = rope.try_byte_to_line(token.start).ok()? as u32; 416 | let first = rope.try_line_to_char(line as usize).ok()? as u32; 417 | let start = rope.try_byte_to_char(token.start).ok()? as u32 - first; 418 | let delta_line = line - pre_line; 419 | let delta_start = if delta_line == 0 { 420 | start - pre_start 421 | } else { 422 | start 423 | }; 424 | let ret = Some(SemanticToken { 425 | delta_line, 426 | delta_start, 427 | length: token.length as u32, 428 | token_type: token.token_type as u32, 429 | token_modifiers_bitset: 0, 430 | }); 431 | pre_line = line; 432 | pre_start = start; 433 | ret 434 | }) 435 | .collect::>(); 436 | Some(semantic_tokens) 437 | }(); 438 | if let Some(semantic_tokens) = semantic_tokens { 439 | return Ok(Some(SemanticTokensResult::Tokens(SemanticTokens { 440 | result_id: None, 441 | data: semantic_tokens, 442 | }))); 443 | } 444 | Ok(None) 445 | } 446 | } 447 | 448 | #[tokio::main] 449 | async fn main() { 450 | let stdin = tokio::io::stdin(); 451 | let stdout = tokio::io::stdout(); 452 | 453 | let (service, socket) = LspService::new(|client| Backend { 454 | client, 455 | document_map: Default::default(), 456 | semantic_token_map: Default::default(), 457 | ast_map: Default::default(), 458 | include_map: Default::default(), 459 | }); 460 | Server::new(stdin, stdout, socket).serve(service).await; 461 | } 462 | 463 | fn offset_to_position(offset: usize, rope: &Rope) -> Option { 464 | let line = rope.try_char_to_line(offset).ok()?; 465 | let first_char = rope.try_line_to_char(line).ok()?; 466 | let column = offset - first_char; 467 | Some(Position::new(line as u32, column as u32)) 468 | } 469 | -------------------------------------------------------------------------------- /rotth-lsp/src/semantic_token.rs: -------------------------------------------------------------------------------- 1 | use rotth_parser::ast::*; 2 | use somok::Either; 3 | use spanner::Spanned; 4 | use tower_lsp::lsp_types::SemanticTokenType; 5 | 6 | pub const LEGEND_TYPE: &[SemanticTokenType] = &[ 7 | SemanticTokenType::FUNCTION, 8 | SemanticTokenType::VARIABLE, 9 | SemanticTokenType::STRING, 10 | SemanticTokenType::COMMENT, 11 | SemanticTokenType::NUMBER, 12 | SemanticTokenType::KEYWORD, 13 | SemanticTokenType::OPERATOR, 14 | SemanticTokenType::PARAMETER, 15 | SemanticTokenType::TYPE, 16 | SemanticTokenType::REGEXP, 17 | ]; 18 | 19 | #[derive(Debug)] 20 | pub struct CompleteSemanticToken { 21 | pub start: usize, 22 | pub length: usize, 23 | pub token_type: usize, 24 | } 25 | 26 | pub fn semantic_token_from_ast(ast: &[Spanned]) -> Vec { 27 | let mut semantic_tokens = vec![]; 28 | 29 | for item in ast.iter() { 30 | match &**item { 31 | TopLevel::Include(i) => { 32 | push_token(&i.include, &mut semantic_tokens, SemanticTokenType::KEYWORD); 33 | if let Some(Qualifiers { items, from }) = &i.qualifiers { 34 | for item in items { 35 | push_token(item, &mut semantic_tokens, SemanticTokenType::FUNCTION); 36 | } 37 | push_token(from, &mut semantic_tokens, SemanticTokenType::KEYWORD); 38 | } 39 | push_token(&i.path, &mut semantic_tokens, SemanticTokenType::STRING); 40 | } 41 | TopLevel::Proc(p) => { 42 | push_token(&p.proc, &mut semantic_tokens, SemanticTokenType::KEYWORD); 43 | if let Some(Spanned { 44 | span: _, 45 | inner: 46 | Generics { 47 | left_bracket, 48 | tys, 49 | right_bracket, 50 | }, 51 | }) = &p.generics 52 | { 53 | push_token( 54 | left_bracket, 55 | &mut semantic_tokens, 56 | SemanticTokenType::KEYWORD, 57 | ); 58 | for ty in tys { 59 | push_token(ty, &mut semantic_tokens, SemanticTokenType::TYPE); 60 | } 61 | push_token( 62 | right_bracket, 63 | &mut semantic_tokens, 64 | SemanticTokenType::KEYWORD, 65 | ); 66 | }; 67 | push_token(&p.name, &mut semantic_tokens, SemanticTokenType::FUNCTION); 68 | let signature = &p.signature; 69 | for ty in &signature.ins { 70 | push_token(ty, &mut semantic_tokens, SemanticTokenType::TYPE) 71 | } 72 | if let Some(sep) = &signature.sep { 73 | push_token(sep, &mut semantic_tokens, SemanticTokenType::KEYWORD); 74 | } 75 | if let Some(outs) = &signature.outs { 76 | for ty in outs { 77 | push_token(ty, &mut semantic_tokens, SemanticTokenType::TYPE) 78 | } 79 | } 80 | push_token(&p.do_, &mut semantic_tokens, SemanticTokenType::KEYWORD); 81 | for e in &p.body { 82 | push_expr_tokens(e, &mut semantic_tokens); 83 | } 84 | push_token(&p.end, &mut semantic_tokens, SemanticTokenType::KEYWORD); 85 | } 86 | TopLevel::Const(c) => { 87 | push_token(&c.const_, &mut semantic_tokens, SemanticTokenType::KEYWORD); 88 | push_token(&c.name, &mut semantic_tokens, SemanticTokenType::TYPE); 89 | push_token(&c.do_, &mut semantic_tokens, SemanticTokenType::KEYWORD); 90 | for e in &c.body { 91 | push_expr_tokens(e, &mut semantic_tokens); 92 | } 93 | push_token(&c.end, &mut semantic_tokens, SemanticTokenType::KEYWORD); 94 | } 95 | TopLevel::Mem(m) => { 96 | push_token(&m.mem, &mut semantic_tokens, SemanticTokenType::KEYWORD); 97 | push_token(&m.name, &mut semantic_tokens, SemanticTokenType::TYPE); 98 | push_token(&m.do_, &mut semantic_tokens, SemanticTokenType::KEYWORD); 99 | for e in &m.body { 100 | push_expr_tokens(e, &mut semantic_tokens); 101 | } 102 | push_token(&m.end, &mut semantic_tokens, SemanticTokenType::KEYWORD); 103 | } 104 | TopLevel::Var(v) => { 105 | push_token(&v.var, &mut semantic_tokens, SemanticTokenType::KEYWORD); 106 | push_token(&v.name, &mut semantic_tokens, SemanticTokenType::TYPE); 107 | push_token(&v.sep, &mut semantic_tokens, SemanticTokenType::KEYWORD); 108 | push_token(&v.ty, &mut semantic_tokens, SemanticTokenType::TYPE); 109 | } 110 | TopLevel::Struct(s) => { 111 | push_token(&s.struct_, &mut semantic_tokens, SemanticTokenType::KEYWORD); 112 | push_token(&s.name, &mut semantic_tokens, SemanticTokenType::TYPE); 113 | push_token(&s.do_, &mut semantic_tokens, SemanticTokenType::KEYWORD); 114 | for f in &s.body { 115 | push_token(&f.name, &mut semantic_tokens, SemanticTokenType::PARAMETER); 116 | push_token(&f.name, &mut semantic_tokens, SemanticTokenType::KEYWORD); 117 | push_token(&f.ty, &mut semantic_tokens, SemanticTokenType::TYPE); 118 | } 119 | push_token(&s.end, &mut semantic_tokens, SemanticTokenType::KEYWORD); 120 | } 121 | } 122 | } 123 | 124 | semantic_tokens 125 | } 126 | 127 | fn push_expr_tokens(node: &Spanned, tokens: &mut Vec) { 128 | match &node.inner { 129 | Expr::Keyword(_) => push_token(node, tokens, SemanticTokenType::KEYWORD), 130 | Expr::Read(r) => { 131 | push_token(&r.read, tokens, SemanticTokenType::KEYWORD); 132 | push_token(&r.ty, tokens, SemanticTokenType::TYPE); 133 | } 134 | Expr::Write(w) => { 135 | push_token(&w.write, tokens, SemanticTokenType::KEYWORD); 136 | push_token(&w.ty, tokens, SemanticTokenType::TYPE); 137 | } 138 | Expr::CompStop => push_token(node, tokens, SemanticTokenType::KEYWORD), 139 | Expr::Type(_) => push_token(node, tokens, SemanticTokenType::TYPE), 140 | Expr::Bind(b) => { 141 | push_token(&b.bind, tokens, SemanticTokenType::KEYWORD); 142 | for binding in &b.bindings { 143 | match &**binding { 144 | Either::Left(_) => push_token(binding, tokens, SemanticTokenType::VARIABLE), 145 | Either::Right(NameTypePair { name, sep, ty }) => { 146 | push_token(name, tokens, SemanticTokenType::VARIABLE); 147 | push_token(sep, tokens, SemanticTokenType::KEYWORD); 148 | push_token(ty, tokens, SemanticTokenType::TYPE) 149 | } 150 | } 151 | } 152 | push_token(&b.do_, tokens, SemanticTokenType::KEYWORD); 153 | for e in &b.body { 154 | push_expr_tokens(e, tokens) 155 | } 156 | push_token(&b.end, tokens, SemanticTokenType::KEYWORD); 157 | } 158 | Expr::While(w) => { 159 | push_token(&w.while_, tokens, SemanticTokenType::KEYWORD); 160 | for e in &w.cond { 161 | push_expr_tokens(e, tokens); 162 | } 163 | push_token(&w.do_, tokens, SemanticTokenType::KEYWORD); 164 | for e in &w.body { 165 | push_expr_tokens(e, tokens); 166 | } 167 | push_token(&w.end, tokens, SemanticTokenType::KEYWORD); 168 | } 169 | Expr::If(i) => { 170 | push_token(&i.if_, tokens, SemanticTokenType::KEYWORD); 171 | for e in &i.truth { 172 | push_expr_tokens(e, tokens); 173 | } 174 | for e in &i.lie { 175 | push_token(&e.else_, tokens, SemanticTokenType::KEYWORD); 176 | for e in &e.body { 177 | push_expr_tokens(e, tokens); 178 | } 179 | } 180 | push_token(&i.end, tokens, SemanticTokenType::KEYWORD); 181 | } 182 | Expr::Cond(c) => { 183 | push_token(&c.cond, tokens, SemanticTokenType::KEYWORD); 184 | push_token(&c.pat, tokens, SemanticTokenType::REGEXP); 185 | push_token(&c.do_, tokens, SemanticTokenType::KEYWORD); 186 | for e in &c.body { 187 | push_expr_tokens(e, tokens); 188 | } 189 | for b in &c.branches { 190 | push_token(&b.else_, tokens, SemanticTokenType::KEYWORD); 191 | push_token(&b.pat, tokens, SemanticTokenType::PARAMETER); 192 | push_token(&b.do_, tokens, SemanticTokenType::KEYWORD); 193 | for e in &b.body { 194 | push_expr_tokens(e, tokens); 195 | } 196 | } 197 | push_token(&c.end, tokens, SemanticTokenType::KEYWORD); 198 | } 199 | Expr::Cast(c) => { 200 | push_token(&c.cast, tokens, SemanticTokenType::KEYWORD); 201 | push_token(&c.ty, tokens, SemanticTokenType::TYPE); 202 | } 203 | Expr::Word(_) => push_token(node, tokens, SemanticTokenType::FUNCTION), 204 | Expr::Path(_) => push_token(node, tokens, SemanticTokenType::FUNCTION), 205 | Expr::Literal(l) => { 206 | let ty = match l { 207 | Literal::Bool(_) => SemanticTokenType::NUMBER, 208 | Literal::Char(_) => SemanticTokenType::STRING, 209 | Literal::String(_) => SemanticTokenType::STRING, 210 | Literal::Int(_) | Literal::UInt(_) => SemanticTokenType::NUMBER, 211 | }; 212 | push_token(node, tokens, ty); 213 | } 214 | Expr::Var(v) => { 215 | push_token(&v.var, tokens, SemanticTokenType::KEYWORD); 216 | push_token(&v.name, tokens, SemanticTokenType::PARAMETER); 217 | push_token(&v.sep, tokens, SemanticTokenType::KEYWORD); 218 | push_token(&v.ty, tokens, SemanticTokenType::TYPE); 219 | } 220 | Expr::FieldAccess(a) => { 221 | push_token(&a.access, tokens, SemanticTokenType::KEYWORD); 222 | push_token(&a.field, tokens, SemanticTokenType::PARAMETER); 223 | } 224 | } 225 | } 226 | 227 | fn push_token( 228 | node: &Spanned, 229 | tokens: &mut Vec, 230 | typ: SemanticTokenType, 231 | ) { 232 | tokens.push(CompleteSemanticToken { 233 | start: node.span.start, 234 | length: node.span.length(), 235 | token_type: LEGEND_TYPE.iter().position(|item| item == &typ).unwrap(), 236 | }) 237 | } 238 | -------------------------------------------------------------------------------- /rotth-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rotth-parser" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | chumsky.workspace = true 10 | logos.workspace = true 11 | 12 | spanner = { path = "../spanner" } 13 | rotth-lexer = { path = "../rotth-lexer" } 14 | somok = "1.5.0" 15 | fnv = "1.0" 16 | smol_str.workspace = true 17 | internment.workspace = true 18 | thiserror.workspace = true 19 | itempath = { path = "../itempath" } 20 | -------------------------------------------------------------------------------- /rotth-parser/src/ast.rs: -------------------------------------------------------------------------------- 1 | mod parsers; 2 | 3 | use chumsky::{input::Stream, prelude::Input, Parser}; 4 | use fnv::FnvHashMap; 5 | use internment::Intern; 6 | use itempath::{path, ItemPath, ItemPathBuf}; 7 | use rotth_lexer::{lex, Token}; 8 | use smol_str::SmolStr; 9 | use somok::Either; 10 | use spanner::{Span, Spanned}; 11 | use std::{fmt::Debug, rc::Rc}; 12 | 13 | use crate::{types::Type, Error, ParserError, Redefinition}; 14 | 15 | #[derive(Debug, Clone)] 16 | pub struct File(pub Vec>); 17 | 18 | // #[derive(Debug, Clone)] 19 | // pub struct Module { 20 | // procs: Vec>, 21 | // consts: Vec>, 22 | // vars: Vec>, 23 | // structs: Vec>, 24 | // modules: Vec>, 25 | // uses: Vec>, 26 | // } 27 | 28 | #[derive(Debug, Clone)] 29 | pub struct Word(pub SmolStr); 30 | 31 | #[derive(Debug, Clone, Eq, PartialEq)] 32 | pub enum Punctuation { 33 | LBracket, 34 | RBracket, 35 | Colon, 36 | RArrow, 37 | } 38 | 39 | #[derive(Clone)] 40 | pub enum TopLevel { 41 | Proc(Proc), 42 | Const(Const), 43 | Var(Var), 44 | Struct(Struct), 45 | Use(Use), 46 | Module(ModuleDef), 47 | } 48 | 49 | impl TopLevel { 50 | pub fn name(&self) -> SmolStr { 51 | match self { 52 | TopLevel::Proc(Proc { 53 | name: 54 | Spanned { 55 | span: _, 56 | inner: Word(name), 57 | }, 58 | .. 59 | }) => name.clone(), 60 | TopLevel::Const(Const { 61 | name: 62 | Spanned { 63 | span: _, 64 | inner: Word(name), 65 | }, 66 | .. 67 | }) => name.clone(), 68 | TopLevel::Var(Var { 69 | name: 70 | Spanned { 71 | span: _, 72 | inner: Word(name), 73 | }, 74 | .. 75 | }) => name.clone(), 76 | TopLevel::Struct(Struct { 77 | name: 78 | Spanned { 79 | span: _, 80 | inner: Word(name), 81 | }, 82 | .. 83 | }) => name.clone(), 84 | TopLevel::Use(Use { 85 | use_: _, 86 | name: 87 | Spanned { 88 | span: _, 89 | inner: Word(name), 90 | }, 91 | from: _, 92 | path: _, 93 | }) => name.clone(), 94 | TopLevel::Module(ModuleDef { 95 | module: _, 96 | name: 97 | Spanned { 98 | span: _, 99 | inner: Word(name), 100 | }, 101 | }) => name.clone(), 102 | } 103 | } 104 | } 105 | 106 | impl Debug for TopLevel { 107 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 108 | match self { 109 | TopLevel::Proc(arg0) => arg0.fmt(f), 110 | TopLevel::Const(arg0) => arg0.fmt(f), 111 | TopLevel::Var(arg0) => arg0.fmt(f), 112 | TopLevel::Struct(arg0) => arg0.fmt(f), 113 | TopLevel::Use(arg0) => arg0.fmt(f), 114 | TopLevel::Module(arg0) => arg0.fmt(f), 115 | } 116 | } 117 | } 118 | 119 | #[derive(Debug, Clone)] 120 | pub struct Proc { 121 | pub proc: Spanned, 122 | pub generics: Option>, 123 | pub name: Spanned, 124 | pub signature: Spanned, 125 | pub do_: Spanned, 126 | pub body: Vec>, 127 | pub end: Spanned, 128 | } 129 | 130 | #[derive(Debug, Clone, Hash, PartialEq, Eq)] 131 | pub enum Keyword { 132 | Module, 133 | Use, 134 | From, 135 | Return, 136 | Cond, 137 | If, 138 | Else, 139 | Proc, 140 | While, 141 | Do, 142 | Bind, 143 | Const, 144 | Mem, 145 | Var, 146 | Struct, 147 | Cast, 148 | End, 149 | } 150 | 151 | impl std::fmt::Display for Keyword { 152 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 153 | Debug::fmt(self, f) 154 | } 155 | } 156 | 157 | #[derive(Debug, Clone)] 158 | pub struct Mem { 159 | pub mem: Spanned, 160 | pub name: Spanned, 161 | pub do_: Spanned, 162 | pub body: Vec>, 163 | pub end: Spanned, 164 | } 165 | 166 | #[derive(Debug, Clone)] 167 | pub struct Struct { 168 | pub struct_: Spanned, 169 | pub generics: Option>, 170 | pub name: Spanned, 171 | pub do_: Spanned, 172 | pub body: Vec>, 173 | pub end: Spanned, 174 | } 175 | 176 | #[derive(Debug, Clone)] 177 | pub struct Const { 178 | pub const_: Spanned, 179 | pub name: Spanned, 180 | pub signature: Spanned, 181 | pub do_: Spanned, 182 | pub body: Vec>, 183 | pub end: Spanned, 184 | } 185 | 186 | #[derive(Debug, Clone)] 187 | pub struct ModuleDef { 188 | pub module: Spanned, 189 | pub name: Spanned, 190 | } 191 | 192 | #[derive(Debug, Clone)] 193 | pub struct Use { 194 | pub use_: Spanned, 195 | pub name: Spanned, 196 | pub from: Spanned, 197 | pub path: Spanned, 198 | } 199 | 200 | #[derive(Debug, Clone)] 201 | pub struct Qualifiers { 202 | pub items: Vec>, 203 | pub from: Spanned, 204 | } 205 | 206 | #[derive(Debug, Clone)] 207 | pub struct Read { 208 | pub read: Spanned, 209 | pub ty: Spanned, 210 | } 211 | 212 | #[derive(Debug, Clone)] 213 | pub struct Write { 214 | pub write: Spanned, 215 | pub ty: Spanned, 216 | } 217 | 218 | #[derive(Clone)] 219 | pub enum Expr { 220 | CompStop, 221 | Keyword(Keyword), 222 | Type(Type), 223 | 224 | Bind(Bind), 225 | 226 | While(While), 227 | 228 | If(If), 229 | Cond(Box), 230 | 231 | Cast(Cast), 232 | Read(Read), 233 | Write(Write), 234 | 235 | Word(Word), 236 | Path(ItemPathBuf), 237 | 238 | Literal(Literal), 239 | FieldAccess(Box), 240 | 241 | Var(Var), 242 | } 243 | 244 | impl Debug for Expr { 245 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 246 | match self { 247 | Expr::CompStop => write!(f, "&?&"), 248 | Expr::Read(r) => write!(f, "@{:?}", r.ty), 249 | Expr::Write(w) => write!(f, "@{:?}", w.ty), 250 | Expr::Keyword(arg0) => arg0.fmt(f), 251 | Expr::Type(arg0) => arg0.fmt(f), 252 | Expr::Bind(arg0) => arg0.fmt(f), 253 | Expr::While(arg0) => arg0.fmt(f), 254 | Expr::If(arg0) => arg0.fmt(f), 255 | Expr::Cond(arg0) => arg0.fmt(f), 256 | Expr::Cast(arg0) => arg0.fmt(f), 257 | Expr::Word(arg0) => arg0.fmt(f), 258 | Expr::Path(arg0) => arg0.fmt(f), 259 | Expr::Literal(arg0) => arg0.fmt(f), 260 | Expr::FieldAccess(arg0) => arg0.fmt(f), 261 | Expr::Var(arg0) => arg0.fmt(f), 262 | } 263 | } 264 | } 265 | 266 | #[derive(Debug, Clone)] 267 | pub struct Generics { 268 | pub left_bracket: Spanned, 269 | pub tys: Vec>, 270 | pub right_bracket: Spanned, 271 | } 272 | 273 | #[derive(Debug, Clone, Eq, PartialEq)] 274 | pub struct GenericParams { 275 | pub left_bracket: Spanned, 276 | pub tys: Vec>, 277 | pub right_bracket: Spanned, 278 | } 279 | 280 | #[derive(Debug, Clone)] 281 | pub struct FieldAccess { 282 | pub access: Spanned, 283 | pub field: Spanned, 284 | } 285 | 286 | #[derive(Debug, Clone)] 287 | pub struct Var { 288 | pub var: Spanned, 289 | pub name: Spanned, 290 | pub sep: Spanned, 291 | pub ty: Spanned, 292 | } 293 | 294 | #[derive(Debug, Clone)] 295 | pub struct ConstSignature { 296 | pub sep: Spanned, 297 | pub tys: Vec>, 298 | } 299 | 300 | #[derive(Debug, Clone)] 301 | pub struct ProcSignature { 302 | pub ins: Vec>, 303 | pub sep: Option>, 304 | pub outs: Option>>, 305 | } 306 | 307 | #[derive(Debug, Clone)] 308 | pub struct NameTypePair { 309 | pub name: Spanned, 310 | pub sep: Spanned, 311 | pub ty: Spanned, 312 | } 313 | 314 | #[derive(Debug, Clone)] 315 | pub struct While { 316 | pub while_: Spanned, 317 | pub cond: Vec>, 318 | pub do_: Spanned, 319 | pub body: Vec>, 320 | pub end: Spanned, 321 | } 322 | 323 | #[derive(Debug, Clone)] 324 | pub struct Cast { 325 | pub cast: Spanned, 326 | pub ty: Spanned, 327 | } 328 | 329 | #[derive(Debug, Clone)] 330 | pub struct If { 331 | pub if_: Spanned, 332 | pub truth: Vec>, 333 | pub lie: Option, 334 | pub end: Spanned, 335 | } 336 | 337 | #[derive(Debug, Clone)] 338 | pub struct Else { 339 | pub else_: Spanned, 340 | pub body: Vec>, 341 | } 342 | 343 | #[derive(Debug, Clone)] 344 | pub struct Cond { 345 | pub cond: Spanned, 346 | pub pat: Spanned, 347 | pub do_: Spanned, 348 | pub body: Vec>, 349 | pub branches: Vec>, 350 | pub end: Spanned, 351 | } 352 | 353 | #[derive(Debug, Clone)] 354 | pub struct CondBranch { 355 | pub else_: Spanned, 356 | pub pat: Spanned, 357 | pub do_: Spanned, 358 | pub body: Vec>, 359 | } 360 | 361 | #[derive(Debug, Clone)] 362 | pub struct Bind { 363 | pub bind: Spanned, 364 | pub bindings: Vec>>, 365 | pub do_: Spanned, 366 | pub body: Vec>, 367 | pub end: Spanned, 368 | } 369 | 370 | #[derive(Clone)] 371 | pub enum Literal { 372 | Bool(bool), 373 | Int(i64), 374 | UInt(u64), 375 | String(SmolStr), 376 | Char(char), 377 | } 378 | 379 | impl Debug for Literal { 380 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 381 | match self { 382 | Self::Bool(arg0) => arg0.fmt(f), 383 | Self::Int(arg0) => arg0.fmt(f), 384 | Self::UInt(arg0) => arg0.fmt(f), 385 | Self::String(arg0) => arg0.fmt(f), 386 | Self::Char(arg0) => arg0.fmt(f), 387 | } 388 | } 389 | } 390 | 391 | pub fn parse(tokens: Vec<(Token, Span)>) -> Result> { 392 | let len = tokens.len(); 393 | let path = tokens.first().unwrap().1.file; 394 | parsers::file() 395 | .parse(Stream::from_iter(tokens).spanned(Span::point(path, len))) 396 | .into_result() 397 | .map_err(|e| e.into()) 398 | } 399 | 400 | #[derive(Clone)] 401 | pub enum ResolvedItem { 402 | Ref(Rc), 403 | Proc(Rc), 404 | Const(Rc), 405 | Mem(Rc), 406 | Var(Rc), 407 | Struct(Rc), 408 | Module(Rc), 409 | } 410 | 411 | impl Debug for ResolvedItem { 412 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 413 | match self { 414 | Self::Ref(arg0) => f.debug_tuple("Ref").field(arg0).finish(), 415 | Self::Proc(arg0) => arg0.fmt(f), 416 | Self::Const(arg0) => arg0.fmt(f), 417 | Self::Mem(arg0) => arg0.fmt(f), 418 | Self::Var(arg0) => arg0.fmt(f), 419 | Self::Struct(arg0) => arg0.fmt(f), 420 | Self::Module(arg0) => arg0.fmt(f), 421 | } 422 | } 423 | } 424 | 425 | #[derive(Debug, Clone)] 426 | pub struct ResolvedFile { 427 | pub path: ItemPathBuf, 428 | pub ast: FnvHashMap>, 429 | } 430 | 431 | impl ResolvedFile { 432 | pub fn find(&self, path: &ItemPath) -> Option> { 433 | let mut segments = path.iter(); 434 | let segment = segments.next(); 435 | let item = segment.and_then(|s| self.ast.get(s)); 436 | if let Some(path) = path.drop_first() { 437 | match item { 438 | Some(i) => match &**i { 439 | ResolvedItem::Ref(_) => return None, 440 | ResolvedItem::Proc(_) => return None, 441 | ResolvedItem::Const(_) => return None, 442 | ResolvedItem::Mem(_) => return None, 443 | ResolvedItem::Var(_) => return None, 444 | ResolvedItem::Struct(_) => return None, 445 | ResolvedItem::Module(f) => return f.find(path), 446 | }, 447 | None => return None, 448 | } 449 | } 450 | item.cloned() 451 | } 452 | } 453 | 454 | #[derive(Debug, Clone)] 455 | pub struct ResolvedStruct { 456 | pub name: Spanned, 457 | pub generics: Vec>, 458 | pub fields: FnvHashMap>, 459 | } 460 | 461 | pub fn resolve_includes(root: File) -> Result> { 462 | let path = path!(); 463 | resolve_includes_from(path, root) 464 | } 465 | 466 | fn resolve_includes_from( 467 | path: ItemPathBuf, 468 | root: File, 469 | ) -> Result> { 470 | let mut ast: FnvHashMap> = Default::default(); 471 | let mut errors = Vec::new(); 472 | for item in root.0 { 473 | let src_file = item.span.file; 474 | let name = item.inner.name(); 475 | let resolved_item = match item.inner { 476 | TopLevel::Proc(p) => ResolvedItem::Proc(Rc::new(p)), 477 | TopLevel::Const(c) => ResolvedItem::Const(Rc::new(c)), 478 | TopLevel::Var(v) => ResolvedItem::Var(Rc::new(v)), 479 | TopLevel::Struct(s) => match make_struct(s) { 480 | Ok(s) => ResolvedItem::Struct(Rc::new(s)), 481 | Err(mut es) => { 482 | errors.append(&mut es); 483 | continue; 484 | } 485 | }, 486 | TopLevel::Use(Use { 487 | use_: _, 488 | name: _, 489 | from: _, 490 | path: 491 | Spanned { 492 | span: _, 493 | inner: from, 494 | }, 495 | }) => { 496 | todo!() 497 | } 498 | TopLevel::Module(ModuleDef { module: _, name: _ }) => { 499 | let mut module_file = (*src_file).to_owned(); 500 | module_file.set_extension(""); 501 | module_file.push(&*name); 502 | module_file.set_extension("rh"); 503 | 504 | let module_src = std::fs::read_to_string(dbg! { &module_file }).unwrap(); 505 | let tokens = lex(&module_src, Intern::new(module_file)); 506 | 507 | let mut module_path = path.clone(); 508 | module_path.push(&*name); 509 | 510 | let module_ast = parse(tokens)?; 511 | let resolved = resolve_includes_from(module_path, module_ast)?; 512 | 513 | ResolvedItem::Module(Rc::new(resolved)) 514 | } 515 | }; 516 | 517 | if let Some(redefined) = ast.get(&name) { 518 | errors.push(Error::Redefinition(Redefinition { 519 | redefining_item: item.span, 520 | redefined_item: redefined.span, 521 | })) 522 | } else { 523 | ast.insert( 524 | name, 525 | Spanned { 526 | span: item.span, 527 | inner: resolved_item, 528 | }, 529 | ); 530 | } 531 | } 532 | 533 | if errors.is_empty() { 534 | Ok(ResolvedFile { path, ast }) 535 | } else { 536 | Err(ParserError(errors)) 537 | } 538 | } 539 | 540 | fn make_struct(s: Struct) -> Result>> { 541 | let mut errors = Vec::default(); 542 | let mut fields: FnvHashMap> = Default::default(); 543 | for field in s.body { 544 | let Word(name) = &*field.name; 545 | 546 | if let Some(redefined) = fields.get(name) { 547 | errors.push(Error::Redefinition(Redefinition { 548 | redefining_item: field.span, 549 | redefined_item: redefined.span, 550 | })) 551 | } else { 552 | fields.insert(name.clone(), field); 553 | } 554 | } 555 | let mut generics = Vec::new(); 556 | if let Some(Spanned { 557 | span: _, 558 | inner: 559 | Generics { 560 | left_bracket: _, 561 | tys, 562 | right_bracket: _, 563 | }, 564 | }) = s.generics 565 | { 566 | for ty in tys { 567 | generics.push(ty.map(|Word(ty)| ty)) 568 | } 569 | } 570 | if errors.is_empty() { 571 | Ok(ResolvedStruct { 572 | name: s.name, 573 | generics, 574 | fields, 575 | }) 576 | } else { 577 | Err(errors) 578 | } 579 | } 580 | -------------------------------------------------------------------------------- /rotth-parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(box_patterns)] 2 | #![feature(iter_intersperse)] 3 | #![feature(path_file_prefix)] 4 | 5 | pub mod ast; 6 | pub mod hir; 7 | pub mod types; 8 | 9 | use chumsky::error::Rich; 10 | use rotth_lexer::Token; 11 | use spanner::Span; 12 | use thiserror::Error; 13 | 14 | #[derive(Debug, Error)] 15 | #[error("{0:?}")] 16 | pub struct ParserError<'i>(pub Vec>); 17 | 18 | impl<'i> From>> for ParserError<'i> { 19 | fn from(es: Vec>) -> Self { 20 | Self(es.into_iter().map(Error::from).collect()) 21 | } 22 | } 23 | #[derive(Debug, Error)] 24 | pub enum Error<'i> { 25 | #[error("{0:?}")] 26 | Parser(Rich<'i, Token, Span>), 27 | #[error("{0:?}")] 28 | Redefinition(#[from] Redefinition), 29 | #[error("UnresolvedInclude {0:?}")] 30 | UnresolvedInclude(Span), 31 | } 32 | 33 | impl<'i> From> for Error<'i> { 34 | fn from(e: Rich<'i, Token, Span>) -> Self { 35 | Self::Parser(e) 36 | } 37 | } 38 | 39 | #[derive(Debug, Error)] 40 | #[error("RedefinitionError")] 41 | pub struct Redefinition { 42 | pub redefining_item: Span, 43 | pub redefined_item: Span, 44 | } 45 | -------------------------------------------------------------------------------- /rotth-parser/src/types.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::GenericParams; 2 | use fnv::FnvHashMap; 3 | use itempath::{path, ItemPath, ItemPathBuf}; 4 | use smol_str::SmolStr; 5 | use spanner::Spanned; 6 | use std::fmt::Write; 7 | 8 | #[derive(Clone, Eq, PartialEq)] 9 | pub enum Type { 10 | Ptr(Box), 11 | Primitive(Primitive), 12 | Custom(Custom), 13 | } 14 | 15 | impl std::fmt::Debug for Type { 16 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 17 | self.type_name().fmt(f) 18 | } 19 | } 20 | 21 | impl Type { 22 | pub fn type_name(&self) -> ItemPathBuf { 23 | match self { 24 | Type::Ptr(box ty) => { 25 | let mut name = ty.type_name(); 26 | if let Some(s) = name.segment_mut(0) { 27 | *s = format!("&>{s}").into(); 28 | } 29 | name 30 | } 31 | Type::Primitive(p) => match p { 32 | Primitive::Void => path!(void), 33 | Primitive::Bool => path!(bool), 34 | Primitive::Char => path!(char), 35 | Primitive::U64 => path!(u64), 36 | Primitive::U32 => path!(u32), 37 | Primitive::U16 => path!(u16), 38 | Primitive::U8 => path!(u8), 39 | Primitive::I64 => path!(i64), 40 | Primitive::I32 => path!(i32), 41 | Primitive::I16 => path!(i16), 42 | Primitive::I8 => path!(i8), 43 | }, 44 | Type::Custom(Custom { name, params }) => { 45 | let mut base = name.clone(); 46 | if let Some(params) = params { 47 | let mut paramstr = String::from("["); 48 | for (i, param) in params.tys.iter().enumerate() { 49 | if i == 0 { 50 | write!(paramstr, "{:?}", param.type_name()).unwrap(); 51 | } else { 52 | write!(paramstr, " {:?}", param.type_name()).unwrap(); 53 | } 54 | } 55 | write!(paramstr, "]").unwrap(); 56 | base.push(paramstr); 57 | } 58 | base 59 | } 60 | } 61 | } 62 | } 63 | 64 | #[derive(Clone, Eq, PartialEq)] 65 | pub struct Custom { 66 | pub name: ItemPathBuf, 67 | pub params: Option>, 68 | } 69 | 70 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 71 | pub enum Primitive { 72 | Void, 73 | 74 | Bool, 75 | Char, 76 | 77 | U64, 78 | U32, 79 | U16, 80 | U8, 81 | 82 | I64, 83 | I32, 84 | I16, 85 | I8, 86 | } 87 | 88 | impl Primitive { 89 | pub fn size(&self) -> usize { 90 | match self { 91 | Primitive::Void => 0, 92 | Primitive::Bool => 1, 93 | Primitive::Char => 1, 94 | Primitive::U64 => 8, 95 | Primitive::U32 => 4, 96 | Primitive::U16 => 2, 97 | Primitive::U8 => 1, 98 | Primitive::I64 => 8, 99 | Primitive::I32 => 4, 100 | Primitive::I16 => 2, 101 | Primitive::I8 => 1, 102 | } 103 | } 104 | } 105 | 106 | pub struct StructBuilder<'i> { 107 | index: &'i mut StructIndex, 108 | generics: Vec>, 109 | fields: FnvHashMap>, 110 | name: ItemPathBuf, 111 | } 112 | 113 | impl<'i> StructBuilder<'i> { 114 | pub fn field(&mut self, name: SmolStr, ty: Spanned) -> &mut Self { 115 | self.fields.insert(name, ty); 116 | self 117 | } 118 | 119 | pub fn finish(self) { 120 | let struct_ = Struct { 121 | name: self.name, 122 | generics: self.generics, 123 | fields: self.fields, 124 | }; 125 | self.index.structs.push(struct_); 126 | } 127 | } 128 | 129 | #[derive(Default, Debug, Clone)] 130 | pub struct StructIndex { 131 | structs: Vec, 132 | } 133 | 134 | impl StructIndex { 135 | pub fn new_struct( 136 | &'_ mut self, 137 | name: ItemPathBuf, 138 | generics: Vec>, 139 | ) -> StructBuilder<'_> { 140 | StructBuilder { 141 | index: self, 142 | fields: Default::default(), 143 | generics, 144 | name, 145 | } 146 | } 147 | 148 | pub fn get(&self, name: &ItemPath) -> Option<&Struct> { 149 | self.structs.iter().find(|s| s.name == name) 150 | } 151 | } 152 | 153 | impl IntoIterator for StructIndex { 154 | type Item = Struct; 155 | 156 | type IntoIter = std::vec::IntoIter; 157 | 158 | fn into_iter(self) -> Self::IntoIter { 159 | self.structs.into_iter() 160 | } 161 | } 162 | 163 | impl<'s> IntoIterator for &'s StructIndex { 164 | type Item = &'s Struct; 165 | 166 | type IntoIter = std::slice::Iter<'s, Struct>; 167 | 168 | fn into_iter(self) -> Self::IntoIter { 169 | self.structs.iter() 170 | } 171 | } 172 | 173 | #[derive(Debug, PartialEq, Eq, Clone)] 174 | pub struct Struct { 175 | pub name: ItemPathBuf, 176 | pub generics: Vec>, 177 | pub fields: FnvHashMap>, 178 | } 179 | -------------------------------------------------------------------------------- /rotth-src/core.rh: -------------------------------------------------------------------------------- 1 | proc [T] ptr+ &>T u64 : &>T do 2 | swap cast u64 + cast &>T 3 | end 4 | -------------------------------------------------------------------------------- /rotth-src/examples/args.rh: -------------------------------------------------------------------------------- 1 | include cstrlen puts from "../std.rh" 2 | 3 | proc main: u64 do 4 | argv cast &>u64 5 | @u64 cast &>char 6 | dup cstrlen swap puts 7 | 0 8 | end 9 | -------------------------------------------------------------------------------- /rotth-src/examples/bind.rh: -------------------------------------------------------------------------------- 1 | include puts from "../std.rh" 2 | 3 | proc main: u64 do 4 | "hello" 5 | bind len: u64 str: ptr do 6 | 1 while dup len <= do 7 | dup str puts "\n" puts 8 | 1 + 9 | end drop 10 | end 11 | 0 12 | end 13 | -------------------------------------------------------------------------------- /rotth-src/examples/bind_nested.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 0 1 2 3 | bind a b c do 4 | b b 5 | bind d e do 6 | a drop 7 | end 8 | c 9 | end drop 0 10 | end 11 | -------------------------------------------------------------------------------- /rotth-src/examples/comparsions.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 'a' 'a' = print 3 | 1 1 = print 4 | "hello" bind _ ptr: ptr do 5 | ptr ptr = print 6 | end 7 | 0 8 | end -------------------------------------------------------------------------------- /rotth-src/examples/cond.rh: -------------------------------------------------------------------------------- 1 | include getch puts from "../std.rh" 2 | 3 | const QUIT: char do 'q' end 4 | 5 | proc main: u64 do 6 | getch cond 7 | 'a' do 8 | "hello\n" 9 | else 10 | QUIT do 11 | "bye\n" 12 | else 13 | 'c' do 14 | "get out\n" 15 | else _ do 16 | "default case\n" 17 | end puts 18 | 0 19 | end 20 | -------------------------------------------------------------------------------- /rotth-src/examples/cond_loop.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 0 while dup 10 < do 3 | dup print 4 | 1 + 5 | end drop 6 | 0 7 | end -------------------------------------------------------------------------------- /rotth-src/examples/const.rh: -------------------------------------------------------------------------------- 1 | const foo: u64 do 1 end 2 | ; const bar: u64 do foo 1 + end 3 | 4 | proc main: u64 do foo print 0 end -------------------------------------------------------------------------------- /rotth-src/examples/count.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 10 count 3 | end 4 | 5 | proc count u64 : u64 do 6 | dup 0 = if 7 | else 8 | dup print 9 | 1 - count 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /rotth-src/examples/divmod.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 5 2 div print 3 | 0 4 | end 5 | 6 | proc div u64 u64 : u64 do 7 | divmod drop 8 | end -------------------------------------------------------------------------------- /rotth-src/examples/early_return.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 0 while dup 10 < do 3 | dup print 4 | dup 5 = if 5 | drop 6 | 0 return 7 | end 8 | 1 + 9 | end drop 10 | 0 11 | end -------------------------------------------------------------------------------- /rotth-src/examples/echo.rh: -------------------------------------------------------------------------------- 1 | include exit cstrlen puts core::ptr+ from "../std.rh" 2 | proc main: u64 do 3 | argc 2 < if 1 exit 4 | else 5 | argv 8 ptr+ 6 | argc 1 - while dup 0 > do 7 | over cast &>u64 @u64 cast &>char 8 | dup cstrlen swap puts " " puts 9 | 1 - swap 8 ptr+ swap 10 | end drop drop 11 | "\n" puts 12 | end 13 | 0 14 | end 15 | -------------------------------------------------------------------------------- /rotth-src/examples/exit.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 69 3 | end -------------------------------------------------------------------------------- /rotth-src/examples/fib.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 25 fib print 3 | 0 4 | end 5 | 6 | proc fib u64 : u64 do 7 | dup 3 <= if 8 | drop 1 9 | else 10 | dup 1 - swap 2 - swap fib swap fib + 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /rotth-src/examples/fib_bind.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 25 fib print 3 | 0 4 | end 5 | 6 | proc fib u64 : u64 do 7 | dup 3 <= if 8 | drop 1 9 | else 10 | bind n: u64 do 11 | n 1 - fib n 2 - fib + 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /rotth-src/examples/fib_loop.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 1 while dup 10 < do 3 | dup fib print 4 | 1 + 5 | end drop 6 | 0 7 | end 8 | 9 | proc fib u64 : u64 do 10 | dup 3 <= if 11 | drop 1 12 | else 13 | dup 1 - swap 2 - swap fib swap fib + 14 | end 15 | end -------------------------------------------------------------------------------- /rotth-src/examples/foobar.rh: -------------------------------------------------------------------------------- 1 | include mod puts putu or not from "../std.rh" 2 | 3 | proc main: u64 do 4 | 0 while dup 15 <= do 5 | bind i: u64 do 6 | i 3 mod 7 | 0 = dup if "Foo" puts end 8 | i 5 mod 9 | 0 = dup if "Bar" puts end 10 | or not if i putu end 11 | "\n" puts 12 | i 1 + 13 | end 14 | end drop 15 | 0 16 | end 17 | -------------------------------------------------------------------------------- /rotth-src/examples/generic.rh: -------------------------------------------------------------------------------- 1 | proc [T] incptr &>T : &>T do 2 | cast u64 1 + cast &>T 3 | end 4 | 5 | proc main: u64 do 6 | var foo: u64 7 | foo cast u64 print 8 | foo incptr cast u64 print 9 | 0 10 | end 11 | -------------------------------------------------------------------------------- /rotth-src/examples/generic_struct.rh: -------------------------------------------------------------------------------- 1 | struct [T] Vec do 2 | len: u64 3 | cap: u64 4 | buf: &>T 5 | end 6 | 7 | mem FOO_BUF do 8 32 * end 8 | 9 | proc main: u64 do 10 | var FOO: Vec 11 | 32 FOO -> cap !u64 12 | FOO -> buf 13 | bind buf: &>&>u64 do 14 | FOO_BUF cast u64 15 | buf cast &>u64 !u64 16 | end 17 | 0 18 | end 19 | -------------------------------------------------------------------------------- /rotth-src/examples/if.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 1 2 > if 1 print else 2 print end 3 | 0 4 | end -------------------------------------------------------------------------------- /rotth-src/examples/mem.rh: -------------------------------------------------------------------------------- 1 | include "../std.rh" 2 | 3 | const ALPHABET: u64 do 4 | 'z' cast u64 'a' cast u64 - 1 + 5 | end 6 | 7 | mem foo do ALPHABET end 8 | 9 | proc main: u64 do 10 | 'a' 11 | 0 while dup ALPHABET < do 12 | bind c: char i: u64 do 13 | c cast u64 i + cast u8 14 | foo i std::core::ptr+ 15 | !u8 16 | c i 17 | end 18 | 1 + 19 | end 20 | drop drop 21 | ALPHABET foo cast &>char std::puts 22 | 0 23 | end 24 | -------------------------------------------------------------------------------- /rotth-src/examples/module_structure/main.rh: -------------------------------------------------------------------------------- 1 | module foo 2 | ; use bar from foo 3 | 4 | proc main: i64 do 1 end 5 | -------------------------------------------------------------------------------- /rotth-src/examples/module_structure/main/foo.rh: -------------------------------------------------------------------------------- 1 | proc bar: i64 do 0 end 2 | -------------------------------------------------------------------------------- /rotth-src/examples/parametrised_generic.rh: -------------------------------------------------------------------------------- 1 | struct [T] Vec do 2 | len: u64 3 | cap: u64 4 | buf: &>T 5 | end 6 | 7 | var FOO: Vec[u64] 8 | var COMPLEX: Vec[Vec[char]] 9 | 10 | proc main: u64 do 11 | var hmm: Vec[Vec] 12 | var BAR: Vec 13 | FOO ->buf 14 | BAR ->buf 15 | COMPLEX ->buf 16 | hmm 17 | bind 18 | f: &>&>u64 19 | b: &>&>u8 20 | c: &>&>Vec[char] 21 | h 22 | do 23 | h ->buf @ &>Vec[u64] ->buf @ &>u64 @u64 drop 24 | end 25 | 0 26 | end 27 | -------------------------------------------------------------------------------- /rotth-src/examples/pointer_to.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | foo drop 0 3 | end 4 | 5 | proc foo: &>&>&>&>&>char do &?& end 6 | -------------------------------------------------------------------------------- /rotth-src/examples/print_string.rh: -------------------------------------------------------------------------------- 1 | include puts from "../std.rh" 2 | 3 | proc main: u64 do 4 | "Hello, World!\n" puts 5 | 0 6 | end 7 | -------------------------------------------------------------------------------- /rotth-src/examples/recurse.rh: -------------------------------------------------------------------------------- 1 | proc foo u64 do 2 | dup 10 = if drop return end 3 | dup print 1 + foo 4 | end 5 | 6 | proc main: u64 do 7 | 0 foo 8 | 0 9 | end 10 | -------------------------------------------------------------------------------- /rotth-src/examples/redefinition.rh: -------------------------------------------------------------------------------- 1 | include puts from "../std.rh" 2 | 3 | proc main: u64 do 4 | end 5 | 6 | proc puts do 7 | puts 8 | end 9 | -------------------------------------------------------------------------------- /rotth-src/examples/simple_bind.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 1 bind i: u64 do 3 | i i print print 4 | end 5 | 0 6 | end -------------------------------------------------------------------------------- /rotth-src/examples/simple_cond.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 'q' cond 3 | 'a' do 4 | 1 5 | else 6 | 'q' do 7 | 2 8 | else 9 | 'c' do 10 | 3 11 | else _ do 12 | 4 13 | end print 14 | 0 15 | end -------------------------------------------------------------------------------- /rotth-src/examples/simple_if.rh: -------------------------------------------------------------------------------- 1 | proc main: i64 do 2 | 'q' 'a' = if 0 else 1 end 3 | end 4 | -------------------------------------------------------------------------------- /rotth-src/examples/simple_loop.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 0 while dup 10 < do 3 | 1 + 4 | end print 5 | 0 6 | end -------------------------------------------------------------------------------- /rotth-src/examples/simple_string.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | "abc" print print 0 3 | end -------------------------------------------------------------------------------- /rotth-src/examples/simplegeneric.rh: -------------------------------------------------------------------------------- 1 | ; include puts putc putu from "../std.rh" 2 | 3 | proc [T] inc T : T do 4 | cast u64 1 + cast T 5 | end 6 | 7 | proc main: u64 do 8 | ; 1 inc putu 9 | ; "\n" puts 10 | ; 'a' inc putc 11 | 12 | 1 inc print 13 | 0 14 | end 15 | -------------------------------------------------------------------------------- /rotth-src/examples/simpleprint.rh: -------------------------------------------------------------------------------- 1 | proc main: u64 do 2 | 1 2 3 4 5 6 7 8 9 10 3 | print print print print print 4 | print print print print print 5 | 0 6 | end -------------------------------------------------------------------------------- /rotth-src/examples/struct.rh: -------------------------------------------------------------------------------- 1 | include puts putu from "../std.rh" 2 | 3 | struct String do 4 | len: u64 5 | buf: &>char 6 | end 7 | 8 | proc main: u64 do 9 | returns-string 10 | bind str: &>String do 11 | str -> len @u64 dup putu "\n" puts 12 | str -> buf cast &>u64 @u64 cast &>char 13 | puts 14 | end 15 | 0 16 | end 17 | 18 | proc returns-string: &>String do 19 | var str: String 20 | "foobar" 21 | bind n: u64 s: &>char do 22 | n str -> len !u64 23 | s cast u64 str -> buf cast &>u64 !u64 24 | end 25 | str 26 | end 27 | -------------------------------------------------------------------------------- /rotth-src/examples/utoa.rh: -------------------------------------------------------------------------------- 1 | include putu puts from "../std.rh" 2 | 3 | proc main: u64 do 4 | 69 putu "\n" puts 5 | 420 putu "\n" puts 6 | 0 7 | end 8 | -------------------------------------------------------------------------------- /rotth-src/examples/write_u8.rh: -------------------------------------------------------------------------------- 1 | include puts from "../std.rh" 2 | 3 | proc main: u64 do 4 | "hello\n" 5 | dup cast &>u8 'f' cast u8 swap !u8 puts 6 | 0 7 | end 8 | -------------------------------------------------------------------------------- /rotth-src/main.rh: -------------------------------------------------------------------------------- 1 | include examples::simple_bind 2 | -------------------------------------------------------------------------------- /rotth-src/rotth.rh: -------------------------------------------------------------------------------- 1 | include puts putu core::ptr+ streq absdiff from "./std.rh" 2 | 3 | ; Token is: 4 | struct Token do 5 | loc: u64 ; Location - offset into source, 8 bytes 6 | kind: u64 ; Kind - token kind, 8 bytes 7 | end 8 | const sizeof(Token): u64 do 16 end 9 | mem Tokens do sizeof(Token) 1024 * end 10 | var Tok-count: u64 11 | 12 | proc push-token u64 &>char do 13 | Tokens Tok-count @u64 sizeof(Token) * ptr+ cast &>u64 14 | bind len: u64 start: &>char tokens: &>u64 do 15 | 16 | end 17 | end 18 | 19 | proc main: u64 do 20 | "proc main : u64 do 21 | 10 6 * 9 + 22 | end" 23 | bind n: u64 s: &>char do 24 | n s while over 0 != do 25 | scan bind restn: u64 rests: &>char tokn: u64 toks: &>char do 26 | s tokn toks process-token 27 | restn rests 28 | end 29 | end drop drop 30 | end 31 | 0 32 | end 33 | 34 | proc process-token &>char u64 &>char do 35 | var tok: Token 36 | bind buf: &>char tn: u64 ts: &>char do 37 | buf cast u64 ts cast u64 absdiff tok -> loc !u64 38 | tn ts type-of-token tok -> kind !u64 39 | tn ts puts "\n" puts 40 | tok put-token 41 | end 42 | end 43 | 44 | proc put-token &>Token do 45 | bind tok: &>Token do 46 | " loc: " puts tok -> loc @u64 putu "\n" puts 47 | " kind: " puts tok -> kind @u64 putu "\n" puts 48 | end 49 | end 50 | 51 | ; "include" 52 | ; "return" 53 | ; "cond" 54 | ; "if" 55 | ; "else" 56 | ; "proc" 57 | ; "while" 58 | ; "do" 59 | ; "bind" 60 | ; "const" 61 | ; "mem" 62 | ; "cast" 63 | ; "end" 64 | 65 | const TOK_WORD: u64 do 0 end 66 | const TOK_KEYWORD: u64 do 1 end 67 | const TOK_SEPARATOR: u64 do 2 end 68 | proc type-of-token u64 &>char : u64 do 69 | bind tn: u64 ts: &>char do 70 | tn ts "include" streq if TOK_KEYWORD 71 | else tn ts "return" streq if TOK_KEYWORD 72 | else tn ts "cond" streq if TOK_KEYWORD 73 | else tn ts "if" streq if TOK_KEYWORD 74 | else tn ts "else" streq if TOK_KEYWORD 75 | else tn ts "proc" streq if TOK_KEYWORD 76 | else tn ts "while" streq if TOK_KEYWORD 77 | else tn ts "do" streq if TOK_KEYWORD 78 | else tn ts "bind" streq if TOK_KEYWORD 79 | else tn ts "const" streq if TOK_KEYWORD 80 | else tn ts "mem" streq if TOK_KEYWORD 81 | else tn ts "cast" streq if TOK_KEYWORD 82 | else tn ts "end" streq if TOK_KEYWORD 83 | else tn ts ":" streq if TOK_SEPARATOR 84 | else TOK_WORD 85 | end end end end end end end end end end end end end end 86 | end 87 | end 88 | 89 | proc scan u64 &>char : u64 &>char u64 &>char do 90 | bind n: u64 s: &>char do 91 | n s scan-start 92 | bind start: u64 do 93 | n start - s start ptr+ cast &>char 94 | bind n1: u64 s1: &>char do 95 | n1 s1 scan-end 96 | bind finish: u64 do 97 | n start finish + - s1 finish ptr+ cast &>char 98 | finish s1 99 | end 100 | end 101 | end 102 | end 103 | end 104 | 105 | proc scan-start u64 &>char : u64 do 106 | swap 107 | 0 while over over != do 108 | bind s: &>char n: u64 i: u64 do 109 | s i ptr+ cast &>u8 @u8 cast u64 110 | 32 > if i return end 111 | s n i 1 + 112 | end 113 | end bind _ _ i: u64 do i end 114 | end 115 | 116 | proc scan-end u64 &>char : u64 do 117 | swap 118 | 0 while over over != do 119 | bind s: &>char n: u64 i: u64 do 120 | s i ptr+ cast &>u8 @u8 cast u64 121 | 32 <= if i return end 122 | s n i 1 + 123 | end 124 | end bind _ _ i: u64 do i end 125 | end 126 | -------------------------------------------------------------------------------- /rotth-src/std.rh: -------------------------------------------------------------------------------- 1 | include SYS_write SYS_read SYS_exit from "./syscalls.rh" 2 | include "./core.rh" 3 | 4 | const STDIN: u64 do 0 end 5 | const STDOUT: u64 do 1 end 6 | const STDERR: u64 do 2 end 7 | 8 | proc utoa &>u8 u64 : u64 &>char do 9 | 0 10 | bind buf: &>u8 u: u64 n: u64 do 11 | u 0 = if 12 | '0' cast u8 buf !u8 13 | 2 buf cast &>char return 14 | end 15 | n 16 | u while dup 0 != do 17 | bind n: u64 u: u64 do 18 | u 10 mod '0' cast u64 + 19 | cast u8 20 | buf n core::ptr+ cast &>u8 !u8 21 | n 1 + u 10 div 22 | end 23 | end drop 24 | bind n: u64 do 25 | n buf rev-slice 26 | n buf cast &>char 27 | end 28 | end 29 | end 30 | 31 | proc rev-slice u64 &>u8 do 32 | bind n: u64 buf: &>u8 do 33 | 0 34 | n 1 - 35 | while over over < do 36 | bind start: u64 finish: u64 do 37 | buf start core::ptr+ cast &>u8 @u8 cast u8 38 | buf finish core::ptr+ cast &>u8 @u8 cast u8 39 | 40 | buf start core::ptr+ cast &>u8 !u8 41 | buf finish core::ptr+ cast &>u8 !u8 42 | 43 | start 1 + 44 | finish 1 - 45 | end 46 | end drop drop 47 | end 48 | end 49 | 50 | mem PUTU_BUF do 32 end 51 | proc ZERO_PUTU_BUF do 52 | 0 while dup 32 8 div < do 53 | dup PUTU_BUF cast &>u64 swap core::ptr+ 0 swap !u64 54 | 1 + 55 | end drop 56 | end 57 | 58 | proc putb bool do 59 | if "true" else "false" end puts 60 | end 61 | 62 | proc putc char do 63 | cast u8 PUTU_BUF !u8 64 | 1 PUTU_BUF cast &>char puts 65 | ZERO_PUTU_BUF 66 | end 67 | 68 | proc putu u64 do 69 | PUTU_BUF swap utoa puts 70 | ZERO_PUTU_BUF 71 | end 72 | 73 | proc eputu u64 do 74 | PUTU_BUF swap utoa eputs 75 | ZERO_PUTU_BUF 76 | end 77 | 78 | proc puts u64 &>char do 79 | STDOUT SYS_write syscall3 drop 80 | end 81 | 82 | proc eputs u64 &>char do 83 | STDERR SYS_write syscall3 drop 84 | end 85 | 86 | proc cstrlen &>char : u64 do 87 | cast &>u8 88 | dup 89 | while dup @u8 0 cast u8 != do 90 | 1 core::ptr+ 91 | end cast u64 swap cast u64 absdiff 92 | end 93 | 94 | proc streq u64 &>char u64 &>char : bool do 95 | bind an: u64 as: &>char bn: u64 bs: &>char do 96 | an bn != if false return end 97 | 0 while dup an != do 98 | dup dup as swap core::ptr+ swap bs swap core::ptr+ cast &>u8 @u8 swap cast &>u8 @u8 != if drop false return end 99 | 1 + 100 | end drop 101 | end 102 | true 103 | end 104 | 105 | proc absdiff u64 u64 : u64 do 106 | over over > if - 107 | else 108 | swap - 109 | end 110 | end 111 | 112 | mem GETCH_BUF do 1 end 113 | 114 | proc getch: char do 115 | 1 GETCH_BUF STDIN SYS_read syscall3 drop 116 | GETCH_BUF @u8 cast char 117 | end 118 | 119 | proc exit u64 do 120 | SYS_exit syscall1 drop 121 | end 122 | 123 | proc div u64 u64 : u64 do 124 | divmod drop 125 | end 126 | 127 | proc mod u64 u64 : u64 do 128 | divmod swap drop 129 | end 130 | 131 | proc and bool bool : bool do 132 | cast u64 swap cast u64 + 2 = 133 | end 134 | 135 | proc not bool : bool do 136 | if false else true end 137 | end 138 | 139 | proc or bool bool : bool do 140 | cast u64 swap cast u64 + 0 > 141 | end 142 | -------------------------------------------------------------------------------- /rotth-src/syscalls.rh: -------------------------------------------------------------------------------- 1 | const SYS_read: u64 do 0 end 2 | const SYS_write: u64 do 1 end 3 | const SYS_open: u64 do 2 end 4 | const SYS_close: u64 do 3 end 5 | const SYS_stat: u64 do 4 end 6 | const SYS_fstat: u64 do 5 end 7 | const SYS_lstat: u64 do 6 end 8 | const SYS_poll: u64 do 7 end 9 | const SYS_lseek: u64 do 8 end 10 | const SYS_mmap: u64 do 9 end 11 | const SYS_mprotect: u64 do 10 end 12 | const SYS_munmap: u64 do 11 end 13 | const SYS_brk: u64 do 12 end 14 | const SYS_rt_sigaction: u64 do 13 end 15 | const SYS_rt_sigprocmask: u64 do 14 end 16 | const SYS_rt_sigreturn: u64 do 15 end 17 | const SYS_ioctl: u64 do 16 end 18 | const SYS_pread64: u64 do 17 end 19 | const SYS_pwrite64: u64 do 18 end 20 | const SYS_readv: u64 do 19 end 21 | const SYS_writev: u64 do 20 end 22 | const SYS_access: u64 do 21 end 23 | const SYS_pipe: u64 do 22 end 24 | const SYS_select: u64 do 23 end 25 | const SYS_sched_yield: u64 do 24 end 26 | const SYS_mremap: u64 do 25 end 27 | const SYS_msync: u64 do 26 end 28 | const SYS_mincore: u64 do 27 end 29 | const SYS_madvise: u64 do 28 end 30 | const SYS_shmget: u64 do 29 end 31 | const SYS_shmat: u64 do 30 end 32 | const SYS_shmctl: u64 do 31 end 33 | const SYS_dup: u64 do 32 end 34 | const SYS_dup2: u64 do 33 end 35 | const SYS_pause: u64 do 34 end 36 | const SYS_nanosleep: u64 do 35 end 37 | const SYS_getitimer: u64 do 36 end 38 | const SYS_alarm: u64 do 37 end 39 | const SYS_setitimer: u64 do 38 end 40 | const SYS_getpid: u64 do 39 end 41 | const SYS_sendfile: u64 do 40 end 42 | const SYS_socket: u64 do 41 end 43 | const SYS_connect: u64 do 42 end 44 | const SYS_accept: u64 do 43 end 45 | const SYS_sendto: u64 do 44 end 46 | const SYS_recvfrom: u64 do 45 end 47 | const SYS_sendmsg: u64 do 46 end 48 | const SYS_recvmsg: u64 do 47 end 49 | const SYS_shutdown: u64 do 48 end 50 | const SYS_bind: u64 do 49 end 51 | const SYS_listen: u64 do 50 end 52 | const SYS_getsockname: u64 do 51 end 53 | const SYS_getpeername: u64 do 52 end 54 | const SYS_socketpair: u64 do 53 end 55 | const SYS_setsockopt: u64 do 54 end 56 | const SYS_getsockopt: u64 do 55 end 57 | const SYS_clone: u64 do 56 end 58 | const SYS_fork: u64 do 57 end 59 | const SYS_vfork: u64 do 58 end 60 | const SYS_execve: u64 do 59 end 61 | const SYS_exit: u64 do 60 end 62 | const SYS_wait4: u64 do 61 end 63 | const SYS_kill: u64 do 62 end 64 | const SYS_uname: u64 do 63 end 65 | const SYS_semget: u64 do 64 end 66 | const SYS_semop: u64 do 65 end 67 | const SYS_semctl: u64 do 66 end 68 | const SYS_shmdt: u64 do 67 end 69 | const SYS_msgget: u64 do 68 end 70 | const SYS_msgsnd: u64 do 69 end 71 | const SYS_msgrcv: u64 do 70 end 72 | const SYS_msgctl: u64 do 71 end 73 | const SYS_fcntl: u64 do 72 end 74 | const SYS_flock: u64 do 73 end 75 | const SYS_fsync: u64 do 74 end 76 | const SYS_fdatasync: u64 do 75 end 77 | const SYS_truncate: u64 do 76 end 78 | const SYS_ftruncate: u64 do 77 end 79 | const SYS_getdents: u64 do 78 end 80 | const SYS_getcwd: u64 do 79 end 81 | const SYS_chdir: u64 do 80 end 82 | const SYS_fchdir: u64 do 81 end 83 | const SYS_rename: u64 do 82 end 84 | const SYS_mkdir: u64 do 83 end 85 | const SYS_rmdir: u64 do 84 end 86 | const SYS_creat: u64 do 85 end 87 | const SYS_link: u64 do 86 end 88 | const SYS_unlink: u64 do 87 end 89 | const SYS_symlink: u64 do 88 end 90 | const SYS_readlink: u64 do 89 end 91 | const SYS_chmod: u64 do 90 end 92 | const SYS_fchmod: u64 do 91 end 93 | const SYS_chown: u64 do 92 end 94 | const SYS_fchown: u64 do 93 end 95 | const SYS_lchown: u64 do 94 end 96 | const SYS_umask: u64 do 95 end 97 | const SYS_gettimeofday: u64 do 96 end 98 | const SYS_getrlimit: u64 do 97 end 99 | const SYS_getrusage: u64 do 98 end 100 | const SYS_sysinfo: u64 do 99 end 101 | const SYS_times: u64 do 100 end 102 | const SYS_ptrace: u64 do 101 end 103 | const SYS_getuid: u64 do 102 end 104 | const SYS_syslog: u64 do 103 end 105 | const SYS_getgid: u64 do 104 end 106 | const SYS_setuid: u64 do 105 end 107 | const SYS_setgid: u64 do 106 end 108 | const SYS_geteuid: u64 do 107 end 109 | const SYS_getegid: u64 do 108 end 110 | const SYS_setpgid: u64 do 109 end 111 | const SYS_getppid: u64 do 110 end 112 | const SYS_getpgrp: u64 do 111 end 113 | const SYS_setsid: u64 do 112 end 114 | const SYS_setreuid: u64 do 113 end 115 | const SYS_setregid: u64 do 114 end 116 | const SYS_getgroups: u64 do 115 end 117 | const SYS_setgroups: u64 do 116 end 118 | const SYS_setresuid: u64 do 117 end 119 | const SYS_getresuid: u64 do 118 end 120 | const SYS_setresgid: u64 do 119 end 121 | const SYS_getresgid: u64 do 120 end 122 | const SYS_getpgid: u64 do 121 end 123 | const SYS_setfsuid: u64 do 122 end 124 | const SYS_setfsgid: u64 do 123 end 125 | const SYS_getsid: u64 do 124 end 126 | const SYS_capget: u64 do 125 end 127 | const SYS_capset: u64 do 126 end 128 | const SYS_rt_sigpending: u64 do 127 end 129 | const SYS_rt_sigtimedwait: u64 do 128 end 130 | const SYS_rt_sigqueueinfo: u64 do 129 end 131 | const SYS_rt_sigsuspend: u64 do 130 end 132 | const SYS_sigaltstack: u64 do 131 end 133 | const SYS_utime: u64 do 132 end 134 | const SYS_mknod: u64 do 133 end 135 | const SYS_uselib: u64 do 134 end 136 | const SYS_personality: u64 do 135 end 137 | const SYS_ustat: u64 do 136 end 138 | const SYS_statfs: u64 do 137 end 139 | const SYS_fstatfs: u64 do 138 end 140 | const SYS_sysfs: u64 do 139 end 141 | const SYS_getpriority: u64 do 140 end 142 | const SYS_setpriority: u64 do 141 end 143 | const SYS_sched_setparam: u64 do 142 end 144 | const SYS_sched_getparam: u64 do 143 end 145 | const SYS_sched_setscheduler: u64 do 144 end 146 | const SYS_sched_getscheduler: u64 do 145 end 147 | const SYS_sched_get_priority_max: u64 do 146 end 148 | const SYS_sched_get_priority_min: u64 do 147 end 149 | const SYS_sched_rr_get_interval: u64 do 148 end 150 | const SYS_mlock: u64 do 149 end 151 | const SYS_munlock: u64 do 150 end 152 | const SYS_mlockall: u64 do 151 end 153 | const SYS_munlockall: u64 do 152 end 154 | const SYS_vhangup: u64 do 153 end 155 | const SYS_modify_ldt: u64 do 154 end 156 | const SYS_pivot_root: u64 do 155 end 157 | const SYS__sysctl: u64 do 156 end 158 | const SYS_prctl: u64 do 157 end 159 | const SYS_arch_prctl: u64 do 158 end 160 | const SYS_adjtimex: u64 do 159 end 161 | const SYS_setrlimit: u64 do 160 end 162 | const SYS_chroot: u64 do 161 end 163 | const SYS_sync: u64 do 162 end 164 | const SYS_acct: u64 do 163 end 165 | const SYS_settimeofday: u64 do 164 end 166 | const SYS_mount: u64 do 165 end 167 | const SYS_umount2: u64 do 166 end 168 | const SYS_swapon: u64 do 167 end 169 | const SYS_swapoff: u64 do 168 end 170 | const SYS_reboot: u64 do 169 end 171 | const SYS_sethostname: u64 do 170 end 172 | const SYS_setdomainname: u64 do 171 end 173 | const SYS_iopl: u64 do 172 end 174 | const SYS_ioperm: u64 do 173 end 175 | const SYS_create_module: u64 do 174 end 176 | const SYS_init_module: u64 do 175 end 177 | const SYS_delete_module: u64 do 176 end 178 | const SYS_get_kernel_syms: u64 do 177 end 179 | const SYS_query_module: u64 do 178 end 180 | const SYS_quotactl: u64 do 179 end 181 | const SYS_nfsservctl: u64 do 180 end 182 | const SYS_getpmsg: u64 do 181 end 183 | const SYS_putpmsg: u64 do 182 end 184 | const SYS_afs_syscall: u64 do 183 end 185 | const SYS_tuxcall: u64 do 184 end 186 | const SYS_security: u64 do 185 end 187 | const SYS_gettid: u64 do 186 end 188 | const SYS_readahead: u64 do 187 end 189 | const SYS_setxattr: u64 do 188 end 190 | const SYS_lsetxattr: u64 do 189 end 191 | const SYS_fsetxattr: u64 do 190 end 192 | const SYS_getxattr: u64 do 191 end 193 | const SYS_lgetxattr: u64 do 192 end 194 | const SYS_fgetxattr: u64 do 193 end 195 | const SYS_listxattr: u64 do 194 end 196 | const SYS_llistxattr: u64 do 195 end 197 | const SYS_flistxattr: u64 do 196 end 198 | const SYS_removexattr: u64 do 197 end 199 | const SYS_lremovexattr: u64 do 198 end 200 | const SYS_fremovexattr: u64 do 199 end 201 | const SYS_tkill: u64 do 200 end 202 | const SYS_time: u64 do 201 end 203 | const SYS_futex: u64 do 202 end 204 | const SYS_sched_setaffinity: u64 do 203 end 205 | const SYS_sched_getaffinity: u64 do 204 end 206 | const SYS_set_thread_area: u64 do 205 end 207 | const SYS_io_setup: u64 do 206 end 208 | const SYS_io_destroy: u64 do 207 end 209 | const SYS_io_getevents: u64 do 208 end 210 | const SYS_io_submit: u64 do 209 end 211 | const SYS_io_cancel: u64 do 210 end 212 | const SYS_get_thread_area: u64 do 211 end 213 | const SYS_lookup_dcookie: u64 do 212 end 214 | const SYS_epoll_create: u64 do 213 end 215 | const SYS_epoll_ctl_old: u64 do 214 end 216 | const SYS_epoll_wait_old: u64 do 215 end 217 | const SYS_remap_file_pages: u64 do 216 end 218 | const SYS_getdents64: u64 do 217 end 219 | const SYS_set_tid_address: u64 do 218 end 220 | const SYS_restart_syscall: u64 do 219 end 221 | const SYS_semtimedop: u64 do 220 end 222 | const SYS_fadvise64: u64 do 221 end 223 | const SYS_timer_create: u64 do 222 end 224 | const SYS_timer_settime: u64 do 223 end 225 | const SYS_timer_gettime: u64 do 224 end 226 | const SYS_timer_getoverrun: u64 do 225 end 227 | const SYS_timer_delete: u64 do 226 end 228 | const SYS_clock_settime: u64 do 227 end 229 | const SYS_clock_gettime: u64 do 228 end 230 | const SYS_clock_getres: u64 do 229 end 231 | const SYS_clock_nanosleep: u64 do 230 end 232 | const SYS_exit_group: u64 do 231 end 233 | const SYS_epoll_wait: u64 do 232 end 234 | const SYS_epoll_ctl: u64 do 233 end 235 | const SYS_tgkill: u64 do 234 end 236 | const SYS_utimes: u64 do 235 end 237 | const SYS_vserver: u64 do 236 end 238 | const SYS_mbind: u64 do 237 end 239 | const SYS_set_mempolicy: u64 do 238 end 240 | const SYS_get_mempolicy: u64 do 239 end 241 | const SYS_mq_open: u64 do 240 end 242 | const SYS_mq_unlink: u64 do 241 end 243 | const SYS_mq_timedsend: u64 do 242 end 244 | const SYS_mq_timedreceive: u64 do 243 end 245 | const SYS_mq_notify: u64 do 244 end 246 | const SYS_mq_getsetattr: u64 do 245 end 247 | const SYS_kexec_load: u64 do 246 end 248 | const SYS_waitid: u64 do 247 end 249 | const SYS_add_key: u64 do 248 end 250 | const SYS_request_key: u64 do 249 end 251 | const SYS_keyctl: u64 do 250 end 252 | const SYS_ioprio_set: u64 do 251 end 253 | const SYS_ioprio_get: u64 do 252 end 254 | const SYS_inotify_init: u64 do 253 end 255 | const SYS_inotify_add_watch: u64 do 254 end 256 | const SYS_inotify_rm_watch: u64 do 255 end 257 | const SYS_migrate_pages: u64 do 256 end 258 | const SYS_openat: u64 do 257 end 259 | const SYS_mkdirat: u64 do 258 end 260 | const SYS_mknodat: u64 do 259 end 261 | const SYS_fchownat: u64 do 260 end 262 | const SYS_futimesat: u64 do 261 end 263 | const SYS_newfstatat: u64 do 262 end 264 | const SYS_unlinkat: u64 do 263 end 265 | const SYS_renameat: u64 do 264 end 266 | const SYS_linkat: u64 do 265 end 267 | const SYS_symlinkat: u64 do 266 end 268 | const SYS_readlinkat: u64 do 267 end 269 | const SYS_fchmodat: u64 do 268 end 270 | const SYS_faccessat: u64 do 269 end 271 | const SYS_pselect6: u64 do 270 end 272 | const SYS_ppoll: u64 do 271 end 273 | const SYS_unshare: u64 do 272 end 274 | const SYS_set_robust_list: u64 do 273 end 275 | const SYS_get_robust_list: u64 do 274 end 276 | const SYS_splice: u64 do 275 end 277 | const SYS_tee: u64 do 276 end 278 | const SYS_sync_file_range: u64 do 277 end 279 | const SYS_vmsplice: u64 do 278 end 280 | const SYS_move_pages: u64 do 279 end 281 | const SYS_utimensat: u64 do 280 end 282 | const SYS_epoll_pwait: u64 do 281 end 283 | const SYS_signalfd: u64 do 282 end 284 | const SYS_timerfd_create: u64 do 283 end 285 | const SYS_eventfd: u64 do 284 end 286 | const SYS_fallocate: u64 do 285 end 287 | const SYS_timerfd_settime: u64 do 286 end 288 | const SYS_timerfd_gettime: u64 do 287 end 289 | const SYS_accept4: u64 do 288 end 290 | const SYS_signalfd4: u64 do 289 end 291 | const SYS_eventfd2: u64 do 290 end 292 | const SYS_epoll_create1: u64 do 291 end 293 | const SYS_dup3: u64 do 292 end 294 | const SYS_pipe2: u64 do 293 end 295 | const SYS_inotify_init1: u64 do 294 end 296 | const SYS_preadv: u64 do 295 end 297 | const SYS_pwritev: u64 do 296 end 298 | const SYS_rt_tgsigqueueinfo: u64 do 297 end 299 | const SYS_perf_event_open: u64 do 298 end 300 | const SYS_recvmmsg: u64 do 299 end 301 | const SYS_fanotify_init: u64 do 300 end 302 | const SYS_fanotify_mark: u64 do 301 end 303 | const SYS_prlimit64: u64 do 302 end 304 | const SYS_name_to_handle_at: u64 do 303 end 305 | const SYS_open_by_handle_at: u64 do 304 end 306 | const SYS_clock_adjtime: u64 do 305 end 307 | const SYS_syncfs: u64 do 306 end 308 | const SYS_sendmmsg: u64 do 307 end 309 | const SYS_setns: u64 do 308 end 310 | const SYS_getcpu: u64 do 309 end 311 | const SYS_process_vm_readv: u64 do 310 end 312 | const SYS_process_vm_writev: u64 do 311 end 313 | const SYS_kcmp: u64 do 312 end 314 | const SYS_finit_module: u64 do 313 end -------------------------------------------------------------------------------- /rotth-vscode/.gitignore: -------------------------------------------------------------------------------- 1 | *.vsix 2 | vsc-extension-quickstart.md -------------------------------------------------------------------------------- /rotth-vscode/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /rotth-vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | .gitignore 4 | vsc-extension-quickstart.md 5 | -------------------------------------------------------------------------------- /rotth-vscode/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to the "rotth" extension will be documented in this file. 4 | 5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 6 | 7 | ## [Unreleased] 8 | 9 | - Initial release -------------------------------------------------------------------------------- /rotth-vscode/README.md: -------------------------------------------------------------------------------- 1 | # Rotth README 2 | 3 | Language support for Rotth 4 | 5 | -------------------------------------------------------------------------------- /rotth-vscode/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": ";" 4 | }, 5 | "autoClosingPairs": [ 6 | { 7 | "open": "\"", 8 | "close": "\"", 9 | "notIn": ["string"] 10 | }, 11 | { 12 | "open": "'", 13 | "close": "'", 14 | "notIn": ["string"] 15 | } 16 | ], 17 | "surroundingPairs": [ 18 | ["\"", "\""], 19 | ["'", "'"] 20 | ], 21 | "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\\"\\,\\.\\<\\>\\/\\?\\s]+)", 22 | "indentationRules": { 23 | "increaseIndentPattern": "^.*(while|do|if|else|cond|otherwise)$", 24 | "decreaseIndentPattern": "^\\s*(else|end|otherwise)$" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rotth-vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rotth", 3 | "displayName": "Rotth", 4 | "description": "Language support for Rotth", 5 | "version": "0.0.1", 6 | "engines": { 7 | "vscode": "^1.62.0" 8 | }, 9 | "categories": [ 10 | "Programming Languages" 11 | ], 12 | "contributes": { 13 | "languages": [ 14 | { 15 | "id": "rotth", 16 | "aliases": [ 17 | "Rotth", 18 | "rotth" 19 | ], 20 | "extensions": [ 21 | "rh" 22 | ], 23 | "configuration": "./language-configuration.json" 24 | } 25 | ], 26 | "grammars": [ 27 | { 28 | "language": "rotth", 29 | "scopeName": "source.rotth", 30 | "path": "./syntaxes/rotth.tmLanguage.json" 31 | } 32 | ] 33 | } 34 | } -------------------------------------------------------------------------------- /rotth-vscode/syntaxes/rotth.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "rotth", 4 | "patterns": [ 5 | { 6 | "include": "#comments" 7 | }, 8 | { 9 | "include": "#keywords" 10 | }, 11 | { 12 | "include": "#strings" 13 | }, 14 | { 15 | "include": "#chars" 16 | }, 17 | { 18 | "include": "#words" 19 | }, 20 | { 21 | "include": "#numbers" 22 | } 23 | ], 24 | "repository": { 25 | "keywords": { 26 | "patterns": [ 27 | { 28 | "name": "keyword.control.rotth", 29 | "match": "\\b(if|else|proc|while|do|end|const|bind|include|return|cond|mem|cast|otherwise)\\b" 30 | } 31 | ] 32 | }, 33 | "comments": { 34 | "match": ";.*$", 35 | "name": "comment.rotth" 36 | }, 37 | "strings": { 38 | "name": "string.quoted.double.rotth", 39 | "begin": "\"", 40 | "end": "\"", 41 | "patterns": [ 42 | { 43 | "name": "constant.character.escape.rotth", 44 | "match": "\\\\." 45 | } 46 | ] 47 | }, 48 | "chars": { 49 | "name": "string.quoted.single.char.rotth", 50 | "begin": "'", 51 | "end": "'", 52 | "patterns": [ 53 | { 54 | "name": "constant.character.escape.rotth", 55 | "match": "\\\\." 56 | } 57 | ] 58 | }, 59 | "words": { 60 | "name": "entity.name.rotth", 61 | "begin": "[a-zA-Z+=-_!@#$%^&*()?\\\\/\\[\\]{}<>]", 62 | "end": "[\\s:]", 63 | "patterns": [ 64 | { 65 | "name": "entity.name.rotth", 66 | "match": "\\\\." 67 | } 68 | ] 69 | }, 70 | "numbers": { 71 | "name": "constant.numeric.rotth", 72 | "begin": "\\d", 73 | "end": "\\b" 74 | } 75 | }, 76 | "scopeName": "source.rotth" 77 | } -------------------------------------------------------------------------------- /rotth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rotth" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | clap.workspace = true 10 | simplearena = { path = "../simplearena" } 11 | indoc = "2.0" 12 | fnv = "1.0" 13 | rotth-lexer = { path = "../rotth-lexer" } 14 | rotth-parser = { path = "../rotth-parser" } 15 | rotth-analysis = { path = "../rotth-analysis" } 16 | spanner = { path = "../spanner" } 17 | itempath = { path = "../itempath" } 18 | chumsky.workspace = true 19 | ariadne.workspace = true 20 | smol_str.workspace = true 21 | internment.workspace = true 22 | thiserror.workspace = true 23 | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = [ 24 | "llvm15-0-force-dynamic", 25 | ] } 26 | 27 | target-lexicon = "0.12" 28 | cranelift = "0.106" 29 | cranelift-jit = "0.106" 30 | cranelift-module = "0.106" 31 | cranelift-preopt = "0.92" 32 | capstone = "0.12" 33 | -------------------------------------------------------------------------------- /rotth/src/emit.rs: -------------------------------------------------------------------------------- 1 | // pub mod asm; 2 | // pub mod cranelift; 3 | // pub mod llvm; 4 | -------------------------------------------------------------------------------- /rotth/src/emit/cranelift.rs: -------------------------------------------------------------------------------- 1 | use capstone::arch::BuildsCapstoneSyntax; 2 | use codegen::isa; 3 | use cranelift::codegen::ir::StackSlot; 4 | use cranelift::prelude::StackSlotData; 5 | use cranelift::prelude::{codegen::isa::*, Signature}; 6 | use cranelift::{ 7 | codegen::ir, 8 | frontend::Variable, 9 | prelude::{ 10 | codegen::settings::{self, Configurable}, 11 | types, AbiParam, 12 | }, 13 | }; 14 | use cranelift::{ 15 | codegen::ir::{FuncRef, InstBuilder}, 16 | frontend::FunctionBuilder, 17 | prelude::{IntCC, JumpTableData}, 18 | }; 19 | use cranelift::{ 20 | codegen::{self}, 21 | frontend::FunctionBuilderContext, 22 | }; 23 | use cranelift_jit::{JITBuilder, JITModule}; 24 | use cranelift_module::{FuncId, Linkage, Module}; 25 | use rotth_parser::ast::Literal; 26 | use std::cell::{Ref, RefCell, RefMut}; 27 | use std::collections::BTreeMap; 28 | use std::ops::Deref; 29 | use std::ptr::null_mut; 30 | use std::rc::Rc; 31 | use std::sync::atomic::AtomicU64; 32 | use target_lexicon::Triple; 33 | 34 | use crate::lir::Op; 35 | 36 | fn get_isa() -> Box { 37 | let mut flags_builder = codegen::settings::builder(); 38 | flags_builder.set("opt_level", "speed").unwrap(); 39 | flags_builder.set("is_pic", "false").unwrap(); 40 | codegen::isa::lookup(Triple::host()) 41 | .unwrap() 42 | .finish(settings::Flags::new(flags_builder)) 43 | .unwrap() 44 | } 45 | 46 | const TRIPLE: Triple = Triple::host(); 47 | 48 | pub fn calling_convention() -> isa::CallConv { 49 | isa::CallConv::triple_default(&TRIPLE) 50 | } 51 | 52 | pub struct BasicBlock { 53 | pub low: usize, 54 | pub high: usize, 55 | pub max: usize, 56 | 57 | pub block: ir::Block, 58 | 59 | pub stack_state: Box<[ir::Value]>, 60 | flags: u8, 61 | } 62 | 63 | impl BasicBlock { 64 | const STACK_KNOWN: u8 = 0x01; 65 | const LOCAL_KNOWN: u8 = 0x02; 66 | const SELF_REGEN: u8 = 0x04; 67 | const GENERATED: u8 = 0x08; 68 | const COLOR: u8 = 0x10; 69 | const IN_CODE_ORDER: u8 = 0x20; 70 | } 71 | 72 | impl BasicBlock { 73 | pub fn set_stack_known(&mut self) { 74 | self.flags |= Self::STACK_KNOWN; 75 | } 76 | 77 | pub fn clear_stack_known(&mut self) { 78 | self.flags &= !Self::STACK_KNOWN; 79 | } 80 | 81 | pub fn is_stack_known(&self) -> bool { 82 | (self.flags & Self::STACK_KNOWN) != 0 83 | } 84 | 85 | pub fn set_local_known(&mut self) { 86 | self.flags |= Self::LOCAL_KNOWN; 87 | } 88 | 89 | pub fn clear_local_known(&mut self) { 90 | self.flags &= !Self::LOCAL_KNOWN; 91 | } 92 | 93 | pub fn is_local_known(&self) -> bool { 94 | (self.flags & Self::LOCAL_KNOWN) != 0 95 | } 96 | 97 | pub fn set_self_regen(&mut self) { 98 | self.flags |= Self::SELF_REGEN; 99 | } 100 | 101 | pub fn clear_self_regen(&mut self) { 102 | self.flags &= !Self::SELF_REGEN; 103 | } 104 | 105 | pub fn is_self_regen(&self) -> bool { 106 | (self.flags & Self::SELF_REGEN) != 0 107 | } 108 | 109 | pub fn set_generated(&mut self) { 110 | self.flags |= Self::GENERATED; 111 | } 112 | 113 | pub fn clear_generated(&mut self) { 114 | self.flags &= !Self::GENERATED; 115 | } 116 | 117 | pub fn is_generated(&self) -> bool { 118 | (self.flags & Self::GENERATED) != 0 119 | } 120 | 121 | pub fn set_black(&mut self) { 122 | self.flags |= Self::COLOR; 123 | } 124 | 125 | pub fn set_red(&mut self) { 126 | self.flags &= !Self::COLOR; 127 | } 128 | 129 | pub fn is_red(&self) -> bool { 130 | (self.flags & Self::COLOR) == 0 131 | } 132 | pub fn is_black(&self) -> bool { 133 | (self.flags & Self::COLOR) == 0 134 | } 135 | 136 | pub fn set_in_code_order(&mut self) { 137 | self.flags |= Self::IN_CODE_ORDER; 138 | } 139 | 140 | pub fn clear_in_code_order(&mut self) { 141 | self.flags &= !Self::IN_CODE_ORDER; 142 | } 143 | 144 | pub fn is_in_code_order(&self) -> bool { 145 | (self.flags & Self::IN_CODE_ORDER) != 0 146 | } 147 | 148 | pub fn new(loc: usize, block: ir::Block) -> Self { 149 | Self { 150 | block, 151 | low: loc, 152 | high: loc, 153 | max: usize::MAX, 154 | 155 | stack_state: vec![].into_boxed_slice(), 156 | flags: 0, 157 | } 158 | } 159 | } 160 | 161 | pub struct Stack2SSA<'a> { 162 | builder: FunctionBuilder<'a>, 163 | locals: StackSlot, 164 | bindings: Vec, 165 | operand_stack: Vec, 166 | code: &'a Vec, 167 | instr_index: usize, 168 | ungenerated: Vec, 169 | current_block: BasicBlock, 170 | blocks: BTreeMap, 171 | end_of_basic_block: bool, 172 | fallthrough: bool, 173 | runoff: usize, 174 | } 175 | 176 | impl<'a> Stack2SSA<'a> { 177 | fn generate(&mut self) { 178 | self.generate_from(0); 179 | 180 | while let Some(block) = self.ungenerated.pop() { 181 | println!( 182 | "generate {:?} (stack {})", 183 | block.block, 184 | block.stack_state.len() 185 | ); 186 | self.operand_stack.clear(); 187 | let params = self.builder.block_params(block.block); 188 | self.operand_stack.extend_from_slice(params); 189 | self.current_block = block; 190 | 191 | self.generate_from(self.current_block.low); 192 | } 193 | println!( 194 | "initial IR after lowering: \n{}", 195 | self.builder.func.display() 196 | ); 197 | } 198 | 199 | fn get_or_create_block(&mut self, offset: u32) -> ir::Block { 200 | self.end_of_basic_block = true; 201 | *self.blocks.entry(offset).or_insert_with(|| { 202 | let block = self.builder.create_block(); 203 | for _ in 0..self.operand_stack.len() { 204 | self.builder.append_block_param(block, types::I32); 205 | } 206 | let mut bb = BasicBlock::new(offset as _, block); 207 | 208 | bb.stack_state = self.operand_stack.clone().into_boxed_slice(); 209 | self.ungenerated.push(bb); 210 | block 211 | }) 212 | } 213 | 214 | #[allow(unused_variables, unused_assignments)] 215 | fn generate_from(&mut self, from: usize) { 216 | self.builder.switch_to_block(self.current_block.block); 217 | let mut index = from; 218 | self.end_of_basic_block = false; 219 | self.fallthrough = false; 220 | loop { 221 | self.current_block.high = from; 222 | self.instr_index = index; 223 | let code = &self.code[index]; 224 | 225 | index += 1; 226 | let mut s = None; 227 | match code { 228 | Op::Push(value) => match value { 229 | Literal::Bool(value) => { 230 | let x = self.builder.ins().iconst(types::I64, (*value) as i64); 231 | self.operand_stack.push(x); 232 | } 233 | Literal::Num(value) => { 234 | let x = self.builder.ins().iconst(types::I64, (*value) as i64); 235 | self.operand_stack.push(x); 236 | } 237 | Literal::String(_) => todo!(), 238 | Literal::Char(value) => { 239 | let x = self.builder.ins().iconst(types::I64, (*value) as i64); 240 | self.operand_stack.push(x); 241 | } 242 | }, 243 | Op::Add => { 244 | let y = self.operand_stack.pop().unwrap(); 245 | let x = self.operand_stack.pop().unwrap(); 246 | let z = self.builder.ins().iadd(x, y); 247 | self.operand_stack.push(z); 248 | } 249 | Op::Return => { 250 | let v = self.operand_stack.pop().unwrap(); 251 | self.end_of_basic_block = true; 252 | s = Some(self.builder.ins().return_(&[v])); 253 | } 254 | Op::PushStr(_) => todo!(), 255 | Op::PushMem(_) => todo!(), 256 | Op::Drop => { 257 | self.operand_stack.pop().unwrap(); 258 | } 259 | Op::Dup => { 260 | let x = self.operand_stack.pop().unwrap(); 261 | self.operand_stack.push(x); 262 | self.operand_stack.push(x); 263 | } 264 | Op::Swap => { 265 | let x = self.operand_stack.pop().unwrap(); 266 | let y = self.operand_stack.pop().unwrap(); 267 | self.operand_stack.push(y); 268 | self.operand_stack.push(x); 269 | } 270 | Op::Over => { 271 | let x = self.operand_stack.pop().unwrap(); 272 | let y = self.operand_stack.pop().unwrap(); 273 | self.operand_stack.push(y); 274 | self.operand_stack.push(x); 275 | self.operand_stack.push(y); 276 | } 277 | Op::Bind => { 278 | let x = self.operand_stack.pop().unwrap(); 279 | self.bindings.push(x) 280 | } 281 | Op::UseBinding(id) => { 282 | let x = self.bindings[*id]; 283 | self.operand_stack.push(x) 284 | } 285 | Op::Unbind => { 286 | self.bindings.pop(); 287 | } 288 | Op::ReadU64 => todo!(), 289 | Op::ReadU8 => todo!(), 290 | Op::WriteU64 => todo!(), 291 | Op::WriteU8 => todo!(), 292 | Op::ReserveLocals(_) => todo!(), 293 | Op::FreeLocals(_) => todo!(), 294 | Op::PushLvar(_) => todo!(), 295 | Op::Dump => todo!(), 296 | Op::Print => todo!(), 297 | Op::Syscall0 => todo!(), 298 | Op::Syscall1 => todo!(), 299 | Op::Syscall2 => todo!(), 300 | Op::Syscall3 => todo!(), 301 | Op::Syscall4 => todo!(), 302 | Op::Syscall5 => todo!(), 303 | Op::Syscall6 => todo!(), 304 | Op::Argc => todo!(), 305 | Op::Argv => todo!(), 306 | Op::Sub => todo!(), 307 | Op::Divmod => todo!(), 308 | Op::Mul => todo!(), 309 | Op::Eq => todo!(), 310 | Op::Ne => todo!(), 311 | Op::Lt => todo!(), 312 | Op::Le => todo!(), 313 | Op::Gt => todo!(), 314 | Op::Ge => todo!(), 315 | Op::Label(_) => todo!(), 316 | Op::Jump(_) => todo!(), 317 | Op::JumpF(_) => todo!(), 318 | Op::JumpT(_) => todo!(), 319 | Op::Call(_) => todo!(), 320 | Op::Exit => todo!(), 321 | }; 322 | 323 | dbg! {self.end_of_basic_block}; 324 | if !self.end_of_basic_block && index == self.runoff { 325 | self.end_of_basic_block = true; 326 | self.fallthrough = true; 327 | } 328 | if self.end_of_basic_block { 329 | if self.fallthrough { 330 | let block = self.get_or_create_block(index as _); 331 | self.builder.ins().jump(block, &self.operand_stack); 332 | } 333 | return; 334 | } 335 | } 336 | } 337 | } 338 | 339 | pub struct JIT { 340 | builder: FunctionBuilderContext, 341 | ctx: codegen::Context, 342 | module: JITModule, 343 | } 344 | static ID: AtomicU64 = AtomicU64::new(0); 345 | 346 | fn get_jit_name() -> String { 347 | format!( 348 | "jit-{}", 349 | ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed) 350 | ) 351 | } 352 | 353 | impl JIT { 354 | pub fn new() -> Self { 355 | let mut builder = 356 | JITBuilder::with_isa(get_isa(), cranelift_module::default_libcall_names()); 357 | 358 | builder.hotswap(false); 359 | let module = JITModule::new(builder); 360 | 361 | Self { 362 | ctx: module.make_context(), 363 | module, 364 | builder: FunctionBuilderContext::new(), 365 | } 366 | } 367 | 368 | pub fn compile(&mut self, code: &Vec, argc: usize, locals: usize) -> *mut u8 { 369 | self.module.clear_context(&mut self.ctx); 370 | for _ in 0..argc { 371 | self.ctx 372 | .func 373 | .signature 374 | .params 375 | .push(AbiParam::new(types::I64)); 376 | } 377 | 378 | self.ctx 379 | .func 380 | .signature 381 | .returns 382 | .push(AbiParam::new(types::I64)); 383 | 384 | let mut builder = FunctionBuilder::new(&mut self.ctx.func, &mut self.builder); 385 | let entry_block = builder.create_block(); 386 | builder.append_block_params_for_function_params(entry_block); 387 | builder.switch_to_block(entry_block); 388 | 389 | let operand_stack = builder.block_params(entry_block).to_vec(); 390 | let mut bmap = BTreeMap::new(); 391 | bmap.insert(0, entry_block); 392 | let current_block = BasicBlock::new(0, entry_block); 393 | let locals = builder.create_stack_slot(StackSlotData::new( 394 | cranelift::prelude::StackSlotKind::ExplicitSlot, 395 | locals as u32 * 4, 396 | )); 397 | let mut compiler = Stack2SSA { 398 | builder, 399 | operand_stack, 400 | blocks: bmap, 401 | end_of_basic_block: false, 402 | fallthrough: false, 403 | code, 404 | current_block, 405 | locals, 406 | instr_index: 0, 407 | runoff: usize::MAX, 408 | ungenerated: vec![], 409 | bindings: vec![], 410 | }; 411 | 412 | compiler.generate(); 413 | compiler.builder.seal_all_blocks(); 414 | cranelift_preopt::optimize(&mut self.ctx, &*get_isa()).unwrap(); 415 | println!("IR: \n{}", self.ctx.func.display()); 416 | let name = get_jit_name(); 417 | let id = self 418 | .module 419 | .declare_function( 420 | &name, 421 | cranelift_module::Linkage::Export, 422 | &self.ctx.func.signature, 423 | ) 424 | .unwrap(); 425 | 426 | let info = self.ctx.compile(&*get_isa()).unwrap(); 427 | let mut code_ = vec![0; info.total_size as usize]; 428 | unsafe { 429 | self.ctx.emit_to_memory(&mut code_[0]); 430 | } 431 | self.module.define_function(id, &mut self.ctx).unwrap(); 432 | self.module.clear_context(&mut self.ctx); 433 | self.module.finalize_definitions(); 434 | let code = self.module.get_finalized_function(id); 435 | 436 | println!("Disassembly: ",); 437 | let cs = disasm(); 438 | let insns = cs.disasm_all(&code_, code as _); 439 | for ins in insns.unwrap().iter() { 440 | println!("{}", ins); 441 | } 442 | 443 | code as *mut u8 444 | } 445 | } 446 | 447 | impl Default for JIT { 448 | fn default() -> Self { 449 | Self::new() 450 | } 451 | } 452 | fn disasm() -> capstone::Capstone { 453 | use capstone::arch::BuildsCapstone; 454 | let cs = capstone::prelude::Capstone::new(); 455 | #[cfg(target_arch = "aarch64")] 456 | { 457 | let mut cs = cs 458 | .arm64() 459 | .mode(capstone::prelude::arch::arm64::ArchMode::Arm) 460 | .detail(true) 461 | .build() 462 | .unwrap(); 463 | cs.set_skipdata(true).unwrap(); 464 | cs 465 | } 466 | #[cfg(target_arch = "x86_64")] 467 | { 468 | cs.x86() 469 | .mode(capstone::prelude::arch::x86::ArchMode::Mode64) 470 | .syntax(capstone::prelude::arch::x86::ArchSyntax::Att) 471 | .detail(true) 472 | .build() 473 | .unwrap() 474 | } 475 | } 476 | 477 | #[test] 478 | fn test() { 479 | let mut jit = JIT::new(); 480 | 481 | let code = vec![ 482 | Op::Push(Literal::Num(1)), 483 | Op::Bind, 484 | Op::UseBinding(0), 485 | Op::Return, 486 | ]; 487 | 488 | let code = jit.compile(&code, 0, 0); 489 | let func = unsafe { std::mem::transmute::<_, extern "C" fn() -> u64>(code) }; 490 | 491 | println!("{}", func()); 492 | } 493 | -------------------------------------------------------------------------------- /rotth/src/emit/llvm.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use crate::lir::{Block, CompiledProc, Mangled, Op, SpannedOp}; 4 | use fnv::FnvHashMap; 5 | use inkwell::{ 6 | basic_block::BasicBlock, 7 | builder::Builder, 8 | context::Context, 9 | module::Module, 10 | passes::PassManager, 11 | values::{FunctionValue, InstructionOpcode, IntValue, PointerValue}, 12 | AddressSpace, 13 | }; 14 | use rotth_parser::ast::Literal; 15 | use smol_str::SmolStr; 16 | 17 | struct ProcBlock<'ctx> { 18 | low: usize, 19 | high: usize, 20 | block: BasicBlock<'ctx>, 21 | stack_state: Box<[IntValue<'ctx>]>, 22 | } 23 | 24 | impl<'ctx> ProcBlock<'ctx> { 25 | fn new(loc: usize, block: BasicBlock<'ctx>) -> Self { 26 | Self { 27 | block, 28 | low: loc, 29 | high: loc, 30 | 31 | stack_state: vec![].into_boxed_slice(), 32 | } 33 | } 34 | } 35 | 36 | pub struct Compiler<'a, 'ctx> { 37 | pub ctx: &'ctx Context, 38 | pub builder: &'a Builder<'ctx>, 39 | pub fpm: &'a PassManager>, 40 | pub module: &'a Module<'ctx>, 41 | pub proc: CompiledProc, 42 | 43 | bindings: Vec>, 44 | operand_stack: Vec>, 45 | ungenerated: Vec>, 46 | current_block: Option>, 47 | blocks: BTreeMap>, 48 | end_of_block: bool, 49 | 50 | proc_value: Option>, 51 | labels: FnvHashMap, 52 | ret_slot: Option>, 53 | 54 | vid: usize, 55 | bid: usize, 56 | } 57 | 58 | impl<'a, 'ctx> Compiler<'a, 'ctx> { 59 | pub fn compile( 60 | context: &'ctx Context, 61 | builder: &'a Builder<'ctx>, 62 | pass_manager: &'a PassManager>, 63 | module: &'a Module<'ctx>, 64 | mut proc: CompiledProc, 65 | ) -> Result, &'static str> { 66 | let mut labels: FnvHashMap<_, _> = Default::default(); 67 | proc.code = proc 68 | .code 69 | .into_iter() 70 | .enumerate() 71 | .map(|(i, spanned)| { 72 | let SpannedOp { op, span: _ } = &spanned; 73 | if let Op::Label(l) = op { 74 | labels.insert(l.clone(), i); 75 | } 76 | spanned 77 | }) 78 | .collect(); 79 | 80 | let mut this = Self { 81 | ctx: context, 82 | builder, 83 | fpm: pass_manager, 84 | module, 85 | proc, 86 | 87 | bindings: Vec::new(), 88 | operand_stack: Vec::new(), 89 | ungenerated: Default::default(), 90 | current_block: Default::default(), 91 | blocks: Default::default(), 92 | end_of_block: false, 93 | 94 | proc_value: None, 95 | labels, 96 | ret_slot: None, 97 | 98 | vid: 0, 99 | bid: 0, 100 | }; 101 | this.compile_fn() 102 | } 103 | 104 | fn vid(&mut self) -> SmolStr { 105 | let vid = self.vid; 106 | self.vid += 1; 107 | SmolStr::new_inline(&vid.to_string()) 108 | } 109 | fn bid(&mut self) -> SmolStr { 110 | let bid = self.bid; 111 | self.bid += 1; 112 | SmolStr::new_inline(&bid.to_string()) 113 | } 114 | 115 | fn ret_slot(&self) -> PointerValue<'ctx> { 116 | self.ret_slot.unwrap() 117 | } 118 | 119 | fn compile_fn(&mut self) -> Result, &'static str> { 120 | let proc_name = &*self.proc.name; 121 | 122 | let params: Vec<_> = (0..self.proc.ins.len() as u32) 123 | .map(|_| self.ctx.i64_type().into()) 124 | .collect(); 125 | 126 | let rets = self.proc.outs.len() as u32; 127 | let ret_ty = self 128 | .ctx 129 | .i64_type() 130 | .array_type(rets) 131 | .ptr_type(AddressSpace::Generic); 132 | 133 | let proc_type = ret_ty.fn_type(¶ms, false); 134 | let proc = self.module.add_function(proc_name, proc_type, None); 135 | 136 | for param in proc.get_param_iter() { 137 | self.operand_stack.push(param.into_int_value()) 138 | } 139 | 140 | let entry = self.ctx.append_basic_block(proc, "entry"); 141 | self.builder.position_at_end(entry); 142 | let ret_slot = self.builder.build_array_alloca( 143 | self.ctx.i64_type(), 144 | self.ctx.i64_type().const_int(rets as _, false), 145 | "outs", 146 | ); 147 | self.ret_slot = Some(ret_slot); 148 | 149 | self.blocks.insert(0, entry); 150 | let block = ProcBlock::new(0, entry); 151 | self.current_block = Some(block); 152 | 153 | self.proc_value = Some(proc); 154 | 155 | self.compile_from(0)?; 156 | 157 | while let Some(block) = self.ungenerated.pop() { 158 | self.operand_stack.clear(); 159 | self.operand_stack.extend_from_slice(&*block.stack_state); 160 | self.current_block = Some(block); 161 | 162 | self.compile_from(self.block().low)?; 163 | } 164 | 165 | Ok(proc) 166 | } 167 | 168 | fn block(&self) -> &ProcBlock<'ctx> { 169 | self.current_block.as_ref().unwrap() 170 | } 171 | fn block_mut(&mut self) -> &mut ProcBlock<'ctx> { 172 | self.current_block.as_mut().unwrap() 173 | } 174 | fn func(&self) -> FunctionValue { 175 | self.proc_value.unwrap() 176 | } 177 | fn code(&self) -> &[SpannedOp] { 178 | &self.proc.code 179 | } 180 | 181 | fn get_or_create_block( 182 | &mut self, 183 | offset: usize, 184 | name: Option<&'static str>, 185 | ) -> BasicBlock<'ctx> { 186 | self.end_of_block = true; 187 | #[allow(clippy::map_entry)] 188 | if self.blocks.contains_key(&offset) { 189 | *self.blocks.get(&offset).unwrap() 190 | } else { 191 | let bid = self.bid(); 192 | let bid = if let Some(name) = name { name } else { &*bid }; 193 | let block = self.ctx.append_basic_block(self.func(), bid); 194 | self.blocks.insert(offset, block); 195 | 196 | let mut pb = ProcBlock::new(offset as _, block); 197 | pb.stack_state = self.operand_stack.clone().into_boxed_slice(); 198 | self.ungenerated.push(pb); 199 | block 200 | } 201 | } 202 | 203 | fn compile_from(&mut self, mut index: usize) -> Result<(), &'static str> { 204 | self.builder.position_at_end(self.block().block); 205 | self.end_of_block = false; 206 | self.block_mut().high = index; 207 | loop { 208 | let SpannedOp { op, span: _ } = &self.code()[index]; 209 | index += 1; 210 | 211 | match op { 212 | Op::Push(lit) => { 213 | let v = match lit { 214 | Literal::Bool(_) => todo!(), 215 | Literal::Num(u) => self.ctx.i64_type().const_int(*u, false), 216 | Literal::String(_) => todo!(), 217 | Literal::Char(_) => todo!(), 218 | }; 219 | self.operand_stack.push(v); 220 | } 221 | Op::PushStr(_) => todo!(), 222 | Op::PushMem(_) => todo!(), 223 | Op::Drop => { 224 | self.operand_stack.pop(); 225 | } 226 | Op::Dup => { 227 | let v = self.operand_stack.pop().unwrap(); 228 | self.operand_stack.push(v); 229 | self.operand_stack.push(v); 230 | } 231 | Op::Swap => { 232 | let a = self.operand_stack.pop().unwrap(); 233 | let b = self.operand_stack.pop().unwrap(); 234 | self.operand_stack.push(a); 235 | self.operand_stack.push(b); 236 | } 237 | Op::Over => { 238 | let a = self.operand_stack.pop().unwrap(); 239 | let b = self.operand_stack.pop().unwrap(); 240 | self.operand_stack.push(b); 241 | self.operand_stack.push(a); 242 | self.operand_stack.push(b); 243 | } 244 | Op::Bind => { 245 | let v = self.operand_stack.pop().unwrap(); 246 | self.bindings.push(v); 247 | } 248 | Op::UseBinding(i) => { 249 | let v = self.bindings[*i]; 250 | self.operand_stack.push(v) 251 | } 252 | Op::Unbind => { 253 | self.bindings.pop(); 254 | } 255 | Op::ReadU64 => todo!(), 256 | Op::ReadU8 => todo!(), 257 | Op::WriteU64 => todo!(), 258 | Op::WriteU8 => todo!(), 259 | Op::ReserveLocals(_) => todo!(), 260 | Op::FreeLocals(_) => todo!(), 261 | Op::PushLvar(_) => todo!(), 262 | Op::Dump => todo!(), 263 | Op::Print => todo!(), 264 | Op::Syscall0 => todo!(), 265 | Op::Syscall1 => todo!(), 266 | Op::Syscall2 => todo!(), 267 | Op::Syscall3 => todo!(), 268 | Op::Syscall4 => todo!(), 269 | Op::Syscall5 => todo!(), 270 | Op::Syscall6 => todo!(), 271 | Op::Argc => todo!(), 272 | Op::Argv => todo!(), 273 | Op::Add => { 274 | let a = self.operand_stack.pop().unwrap(); 275 | let b = self.operand_stack.pop().unwrap(); 276 | let r = self.builder.build_int_add(a, b, &*self.vid()); 277 | self.operand_stack.push(r) 278 | } 279 | Op::Sub => { 280 | let a = self.operand_stack.pop().unwrap(); 281 | let b = self.operand_stack.pop().unwrap(); 282 | let r = self.builder.build_int_sub(a, b, &*self.vid()); 283 | self.operand_stack.push(r) 284 | } 285 | Op::Divmod => todo!(), 286 | Op::Mul => { 287 | let a = self.operand_stack.pop().unwrap(); 288 | let b = self.operand_stack.pop().unwrap(); 289 | let r = self.builder.build_int_mul(a, b, &*self.vid()); 290 | self.operand_stack.push(r) 291 | } 292 | Op::Eq => todo!(), 293 | Op::Ne => todo!(), 294 | Op::Lt => todo!(), 295 | Op::Le => todo!(), 296 | Op::Gt => todo!(), 297 | Op::Ge => todo!(), 298 | Op::Label(_) => {} 299 | Op::Jump(target) => { 300 | let target = self.labels[target]; 301 | let target = self.get_or_create_block(target, None); 302 | self.builder.build_unconditional_branch(target); 303 | } 304 | Op::JumpF(target) => { 305 | let target = self.labels[target]; 306 | 307 | let x = self.operand_stack.pop().unwrap(); 308 | let x = self 309 | .builder 310 | .build_cast(InstructionOpcode::Trunc, x, self.ctx.bool_type(), "cond") 311 | .into_int_value(); 312 | let target = self.get_or_create_block(target, None); 313 | let else_block = self.get_or_create_block(index, None); 314 | 315 | self.builder.build_conditional_branch(x, else_block, target); 316 | } 317 | Op::JumpT(target) => { 318 | let target = self.labels[target]; 319 | 320 | let x = self.operand_stack.pop().unwrap(); 321 | let x = self 322 | .builder 323 | .build_cast(InstructionOpcode::Trunc, x, self.ctx.bool_type(), "cond") 324 | .into_int_value(); 325 | let target = self.get_or_create_block(target, Some("True")); 326 | let else_block = self.get_or_create_block(index, Some("Else")); 327 | 328 | self.builder.build_conditional_branch(x, target, else_block); 329 | } 330 | Op::Call(_) => todo!(), 331 | Op::Return => { 332 | let ret = self.ret_slot(); 333 | for (i, _) in self.proc.outs.iter().enumerate() { 334 | let value = self.operand_stack.pop().unwrap(); 335 | let slot = unsafe { 336 | self.builder.build_gep( 337 | ret, 338 | &[self.ctx.i64_type().const_int(i as _, false)], 339 | "build_ret", 340 | ) 341 | }; 342 | self.builder.build_store(slot, value); 343 | } 344 | 345 | let ret = self 346 | .builder 347 | .build_bitcast( 348 | ret, 349 | self.ctx 350 | .i64_type() 351 | .array_type(2) 352 | .ptr_type(AddressSpace::Generic), 353 | "cast_ret", 354 | ) 355 | .into_pointer_value(); 356 | 357 | self.builder.build_return(Some(&ret)); 358 | self.end_of_block = true; 359 | } 360 | Op::Exit => todo!(), 361 | } 362 | 363 | if self.end_of_block { 364 | return Ok(()); 365 | } 366 | } 367 | } 368 | } 369 | 370 | #[test] 371 | fn test() { 372 | use crate::ops; 373 | use rotth_analysis::inference::ReifiedType; 374 | use rotth_parser::{ast::Literal, types::Primitive}; 375 | let u64 = ReifiedType::Primitive(Primitive::U64); 376 | let proc = CompiledProc { 377 | ins: vec![u64.clone(), u64.clone()], 378 | outs: vec![u64.clone(), u64], 379 | name: "test".into(), 380 | blocks: vec![Block { 381 | code: vec![ops![ 382 | JumpT(Mangled("t1".to_string())), 383 | Push(Literal::Num(60)), 384 | Push(Literal::Num(9)), 385 | Add, 386 | Jump(Mangled("p1".to_string())), 387 | Label(Mangled("t1".to_string())), 388 | Push(Literal::Num(400)), 389 | Push(Literal::Num(20)), 390 | Add, 391 | Label(Mangled("p1".to_string())), 392 | Swap, 393 | JumpT(Mangled("t2".to_string())), 394 | Push(Literal::Num(60)), 395 | Push(Literal::Num(9)), 396 | Add, 397 | Jump(Mangled("p2".to_string())), 398 | Label(Mangled("t2".to_string())), 399 | Push(Literal::Num(400)), 400 | Push(Literal::Num(20)), 401 | Add, 402 | Label(Mangled("p2".to_string())), 403 | Return, 404 | ]], 405 | }], 406 | }; 407 | let context = Context::create(); 408 | let module = context.create_module("temp"); 409 | let builder = context.create_builder(); 410 | let fpm = PassManager::create(&module); 411 | fpm.initialize(); 412 | 413 | let res = Compiler::compile(&context, &builder, &fpm, &module, proc).unwrap(); 414 | res.print_to_stderr(); 415 | assert!(res.verify(true)); 416 | } 417 | -------------------------------------------------------------------------------- /rotth/src/eval.rs: -------------------------------------------------------------------------------- 1 | use crate::lir2::{cfg::Block, Op, Value}; 2 | 3 | pub fn eval(blocks: &[Block], debug: bool) -> Option { 4 | let mut block = &blocks[0]; 5 | 6 | let mut bind_stack = Vec::new(); 7 | let mut stack = Vec::new(); 8 | let mut i = 0; 9 | 10 | while let Some(op) = block.ops.get(i) { 11 | if debug { 12 | println!("{}:\t{:?}", i, op); 13 | } 14 | match op { 15 | Op::PushMem(_i) => { 16 | todo!("Support memories in eval") 17 | } 18 | Op::Push(v) => stack.push(v.clone()), 19 | Op::Drop => { 20 | stack.pop(); 21 | } 22 | Op::Dup => { 23 | let v = stack.last().cloned().unwrap(); 24 | stack.push(v); 25 | } 26 | Op::Swap => { 27 | let (a, b) = (stack.pop().unwrap(), stack.pop().unwrap()); 28 | stack.push(a); 29 | stack.push(b); 30 | } 31 | Op::Over => { 32 | let v = stack[stack.len() - 2].clone(); 33 | stack.push(v); 34 | } 35 | 36 | Op::Bind => bind_stack.push(stack.pop().unwrap()), 37 | Op::UseBinding(offset) => { 38 | stack.push(bind_stack[(bind_stack.len() - 1) - offset].clone()) 39 | } 40 | Op::Unbind => { 41 | bind_stack.pop(); 42 | } 43 | 44 | Op::Read(_) | Op::Write(_) => { 45 | panic!("Pointer operations are not supported in const eval") 46 | } 47 | 48 | Op::Syscall(_) => todo!("Syscalls not supported in eval"), 49 | 50 | Op::Add => { 51 | let v = match (stack.pop().unwrap(), stack.pop().unwrap()) { 52 | (Value::Int(a), Value::Int(b)) => Value::Int(a + b), 53 | (Value::UInt(a), Value::UInt(b)) => Value::UInt(a + b), 54 | _ => unreachable!(), 55 | }; 56 | stack.push(v); 57 | } 58 | Op::Sub => { 59 | let v = match (stack.pop().unwrap(), stack.pop().unwrap()) { 60 | (Value::Int(a), Value::Int(b)) => Value::Int(a - b), 61 | (Value::UInt(a), Value::UInt(b)) => Value::UInt(a - b), 62 | _ => unreachable!(), 63 | }; 64 | stack.push(v); 65 | } 66 | Op::Divmod => { 67 | let (d, m) = match (stack.pop().unwrap(), stack.pop().unwrap()) { 68 | (Value::Int(a), Value::Int(b)) => (Value::Int(a / b), Value::Int(a % b)), 69 | (Value::UInt(a), Value::UInt(b)) => (Value::UInt(a / b), Value::UInt(a % b)), 70 | _ => unreachable!(), 71 | }; 72 | stack.push(d); 73 | stack.push(m); 74 | } 75 | Op::Mul => { 76 | let v = match (stack.pop().unwrap(), stack.pop().unwrap()) { 77 | (Value::Int(a), Value::Int(b)) => Value::Int(a * b), 78 | (Value::UInt(a), Value::UInt(b)) => Value::UInt(a * b), 79 | _ => unreachable!(), 80 | }; 81 | stack.push(v); 82 | } 83 | 84 | Op::Eq => { 85 | let (a, b) = (stack.pop().unwrap(), stack.pop().unwrap()); 86 | stack.push(Value::Bool(a == b)); 87 | } 88 | Op::Ne => { 89 | let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 90 | stack.push(Value::Bool(a != b)); 91 | } 92 | Op::Lt => { 93 | let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 94 | stack.push(Value::Bool(a < b)); 95 | } 96 | Op::Le => { 97 | let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 98 | stack.push(Value::Bool(a <= b)); 99 | } 100 | Op::Gt => { 101 | let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 102 | stack.push(Value::Bool(a > b)); 103 | } 104 | Op::Ge => { 105 | let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 106 | stack.push(Value::Bool(a >= b)); 107 | } 108 | 109 | Op::Jump(t) => block = &blocks[t.0], 110 | Op::Branch(t, l) => { 111 | if stack.pop() == Some(Value::Bool(true)) { 112 | block = &blocks[t.0]; 113 | } else { 114 | block = &blocks[l.0]; 115 | } 116 | i = 0; 117 | continue; 118 | } 119 | Op::Call(_) => todo!("Call"), 120 | Op::Return => return stack.pop(), 121 | Op::PushLvar(_) => todo!(), 122 | Op::ReserveLocals(_) => todo!(), 123 | Op::FreeLocals(_) => todo!(), 124 | } 125 | i += 1; 126 | } 127 | None 128 | } 129 | -------------------------------------------------------------------------------- /rotth/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(assert_matches)] 2 | #![feature(iter_intersperse)] 3 | #![feature(box_patterns)] 4 | #![feature(string_remove_matches)] 5 | #![feature(type_alias_impl_trait)] 6 | #![feature(array_windows)] 7 | 8 | pub mod emit; 9 | pub mod eval; 10 | // pub mod lir; 11 | pub mod lir2; 12 | 13 | #[macro_export] 14 | macro_rules! ops { 15 | [$($op:ident $(($($arg:expr),+))?),* $(,)?] => { 16 | vec![$( 17 | SpannedOp { 18 | op: Op::$op $(($($arg),+))?, 19 | span: None, 20 | }, 21 | )*] 22 | } 23 | } 24 | 25 | use rotth_analysis::ctir::ConcreteError; 26 | use rotth_analysis::Error as TypecheckError; 27 | use rotth_parser::ParserError; 28 | use thiserror::Error; 29 | 30 | #[derive(Debug, Error)] 31 | pub enum Error<'i> { 32 | #[error("IO error {0}")] 33 | IO(#[from] std::io::Error), 34 | #[error("Lexer error")] 35 | Lexer, 36 | #[error("Parser error {0:?}")] 37 | Parser(ParserError<'i>), 38 | #[error("Typecheck error {0:?}")] 39 | Typecheck(TypecheckError), 40 | #[error("Concretisation error {0:?}")] 41 | Concrete(ConcreteError), 42 | } 43 | 44 | impl<'i> From> for Error<'i> { 45 | fn from(e: ParserError<'i>) -> Self { 46 | Self::Parser(e) 47 | } 48 | } 49 | impl<'i> From for Error<'i> { 50 | fn from(e: TypecheckError) -> Self { 51 | Self::Typecheck(e) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /rotth/src/lir2.rs: -------------------------------------------------------------------------------- 1 | use crate::eval::eval; 2 | 3 | use self::cfg::{Block, BlockId, ProcBuilder}; 4 | use fnv::FnvHashMap; 5 | use itempath::{ItemPath, ItemPathBuf}; 6 | use rotth_analysis::{ 7 | ctir::{CConst, CMem, CProc, ConcreteNode, ConcreteProgram, Intrinsic}, 8 | inference::ReifiedType, 9 | tir::{Bind, Cond, CondBranch, FieldAccess, If, TypedIr, While}, 10 | }; 11 | use rotth_parser::{ast::Literal, hir::Binding}; 12 | use smol_str::SmolStr; 13 | use spanner::Spanned; 14 | 15 | pub mod cfg; 16 | 17 | pub struct CompiledProc { 18 | pub ins: Vec, 19 | pub outs: Vec, 20 | pub blocks: Vec, 21 | } 22 | 23 | impl std::fmt::Debug for CompiledProc { 24 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 25 | for (i, block) in self.blocks.iter().enumerate() { 26 | writeln!(f, "{i}:")?; 27 | block.fmt(f)?; 28 | } 29 | Ok(()) 30 | } 31 | } 32 | 33 | #[derive(Clone)] 34 | pub enum ComConst { 35 | Compiled(Vec), 36 | NotCompiled(CConst), 37 | } 38 | 39 | #[derive(Clone)] 40 | pub enum ComMem { 41 | Compiled(usize), 42 | NotCompiled(CMem), 43 | } 44 | 45 | #[derive(Clone)] 46 | pub enum ComVar { 47 | Compiled(usize), 48 | NotCompiled(ReifiedType), 49 | } 50 | 51 | #[derive(Default)] 52 | pub struct Compiler { 53 | strings: Vec, 54 | mems: FnvHashMap, 55 | consts: FnvHashMap, 56 | mangle_table: FnvHashMap, 57 | unmangle_table: FnvHashMap, 58 | proc_id: usize, 59 | } 60 | 61 | impl Compiler { 62 | pub fn compile( 63 | program: ConcreteProgram, 64 | ) -> ( 65 | FnvHashMap, 66 | Vec, 67 | FnvHashMap, 68 | ) { 69 | let mut this = Self::default(); 70 | 71 | for (name, const_) in program.consts { 72 | let mangled = this.mangle(&name); 73 | this.mangle_table.insert(name.clone(), mangled.clone()); 74 | this.unmangle_table.insert(mangled.clone(), name.clone()); 75 | this.consts.insert(name, ComConst::NotCompiled(const_)); 76 | } 77 | 78 | for (name, mem) in program.mems { 79 | let mangled = this.mangle(&name); 80 | this.mangle_table.insert(name.clone(), mangled.clone()); 81 | this.unmangle_table.insert(mangled.clone(), name.clone()); 82 | this.mems.insert(name, ComMem::NotCompiled(mem)); 83 | } 84 | 85 | for (name, var) in program.vars { 86 | let mangled = this.mangle(&name); 87 | this.mangle_table.insert(name.clone(), mangled.clone()); 88 | this.unmangle_table.insert(mangled.clone(), name.clone()); 89 | this.mems.insert(name, ComMem::Compiled(var.ty.size())); 90 | } 91 | 92 | let procs = program 93 | .procs 94 | .into_iter() 95 | .map(|(name, proc)| { 96 | this.inc_proc_id(); 97 | let mangled = this.mangle(&name); 98 | this.mangle_table.insert(name.clone(), mangled.clone()); 99 | this.unmangle_table.insert(mangled.clone(), name); 100 | let proc = this.compile_proc(proc); 101 | (mangled, proc) 102 | }) 103 | .collect(); 104 | 105 | let mems = this 106 | .mems 107 | .into_iter() 108 | .map(|(n, m)| match m { 109 | ComMem::Compiled(c) => (this.mangle_table.get(&n).unwrap().clone(), c), 110 | _ => todo!(), 111 | }) 112 | .collect(); 113 | 114 | (procs, this.strings, mems) 115 | } 116 | 117 | fn compile_proc( 118 | &mut self, 119 | CProc { 120 | generics: _, 121 | vars: _, 122 | ins, 123 | outs, 124 | body, 125 | }: CProc, 126 | ) -> CompiledProc { 127 | let mut proc = ProcBuilder::new(); 128 | self.compile_body(&mut proc, body); 129 | proc.jump(proc.exit); 130 | proc.switch_block(proc.exit); 131 | proc.return_(); 132 | 133 | proc.dbg_graph("./proc.dot").unwrap(); 134 | 135 | let ProcBuilder { 136 | blocks, 137 | current_block: _, 138 | entry: _, 139 | exit: _, 140 | bindings: _, 141 | locals: _, 142 | } = proc; 143 | CompiledProc { ins, outs, blocks } 144 | } 145 | 146 | fn compile_if(&mut self, proc: &mut ProcBuilder, If { truth, lie }: If) { 147 | let truth_blk = proc.new_block(); 148 | let after = proc.new_block(); 149 | if let Some(lie) = lie { 150 | let lie_blk = proc.new_block(); 151 | proc.branch(truth_blk, lie_blk); 152 | 153 | proc.switch_block(lie_blk); 154 | self.compile_body(proc, lie); 155 | proc.switch_block(lie_blk); 156 | 157 | proc.jump(after); 158 | } else { 159 | proc.branch(truth_blk, after); 160 | } 161 | proc.switch_block(truth_blk); 162 | self.compile_body(proc, truth); 163 | proc.switch_block(truth_blk); 164 | proc.jump(after); 165 | proc.switch_block(after); 166 | } 167 | 168 | fn compile_cond( 169 | &mut self, 170 | proc: &mut ProcBuilder, 171 | Cond { branches }: Cond, 172 | ) { 173 | let phi_b = proc.new_block(); 174 | let n_branches = branches.len() - 1; 175 | let mut this_pat_b = proc.new_block(); 176 | proc.jump(this_pat_b); 177 | for (i, CondBranch { pattern, body }) in branches.into_iter().enumerate() { 178 | proc.switch_block(this_pat_b); 179 | match pattern.node { 180 | TypedIr::ConstUse(_) => todo!(), 181 | TypedIr::Literal(l) => match l { 182 | Literal::Bool(b) => proc.push(Value::Bool(b)), 183 | Literal::Int(i) => proc.push(Value::Int(i)), 184 | Literal::UInt(u) => proc.push(Value::UInt(u)), 185 | Literal::String(s) => proc.push(Value::String(s)), 186 | Literal::Char(c) => proc.push(Value::Char(c)), 187 | }, 188 | TypedIr::IgnorePattern => proc.dup(), 189 | _ => unreachable!(), 190 | } 191 | let body_b = proc.new_block(); 192 | proc.eq(); 193 | if i < n_branches { 194 | this_pat_b = proc.new_block(); 195 | proc.branch(body_b, this_pat_b); 196 | } else { 197 | proc.jump(body_b) 198 | } 199 | proc.switch_block(body_b); 200 | self.compile_body(proc, body); 201 | proc.jump(phi_b); 202 | } 203 | proc.switch_block(phi_b); 204 | } 205 | 206 | fn compile_body(&mut self, proc: &mut ProcBuilder, body: Vec) { 207 | for node in body { 208 | let _span = node.span; 209 | match node.node { 210 | TypedIr::MemUse(name) => { 211 | self.compile_mem(&name); 212 | let mangled = self.mangle_table.get(&name).unwrap().clone(); 213 | proc.push_mem(mangled); 214 | } 215 | TypedIr::GVarUse(name) => { 216 | let mangled = self.mangle_table.get(&name).unwrap().clone(); 217 | proc.push_mem(mangled); 218 | } 219 | TypedIr::LVarUse(lvar) => { 220 | let lvar = proc.locals.get(&lvar).unwrap(); 221 | proc.push_lvar(*lvar); 222 | } 223 | TypedIr::BindingUse(name) => { 224 | let offset = proc 225 | .bindings 226 | .iter() 227 | .flatten() 228 | .rev() 229 | .position(|b| b == &name) 230 | .unwrap(); 231 | proc.use_binding(offset); 232 | } 233 | TypedIr::ConstUse(_) => todo!(), 234 | TypedIr::Call(name) => { 235 | let mangled = self.mangle_table.get(&name).unwrap(); 236 | proc.call(mangled.clone()) 237 | } 238 | TypedIr::Intrinsic(i) => match i { 239 | Intrinsic::Drop => proc.drop(), 240 | Intrinsic::Dup => proc.dup(), 241 | Intrinsic::Swap => proc.swap(), 242 | Intrinsic::Over => proc.over(), 243 | Intrinsic::Cast(_) => (), // this is a noop 244 | Intrinsic::Read(ty) => proc.read(ty.size()), 245 | Intrinsic::Write(ty) => proc.write(ty.size()), 246 | Intrinsic::Add => proc.add(), 247 | Intrinsic::Sub => proc.sub(), 248 | Intrinsic::Divmod => proc.divmod(), 249 | Intrinsic::Mul => proc.mul(), 250 | Intrinsic::Eq => proc.eq(), 251 | Intrinsic::Ne => proc.ne(), 252 | Intrinsic::Lt => proc.lt(), 253 | Intrinsic::Le => proc.le(), 254 | Intrinsic::Gt => proc.gt(), 255 | Intrinsic::Ge => proc.ge(), 256 | i => todo!("{i:?}"), 257 | }, 258 | TypedIr::Bind(Bind { bindings, body }) => { 259 | proc.bindings.push(Vec::new()); 260 | for Spanned { 261 | span: _, 262 | inner: binding, 263 | } in &bindings 264 | { 265 | match binding { 266 | Binding::Ignore => (), 267 | Binding::Bind { name, ty: _ } => { 268 | proc.bind(); 269 | proc.bindings.last_mut().unwrap().push(name.clone()); 270 | } 271 | } 272 | } 273 | self.compile_body(proc, body); 274 | for _ in proc.bindings.pop().unwrap() { 275 | proc.unbind(); 276 | } 277 | } 278 | TypedIr::While(While { cond, body }) => { 279 | let cond_b = proc.new_block(); 280 | let body_b = proc.new_block(); 281 | let after = proc.new_block(); 282 | proc.switch_block(cond_b); 283 | self.compile_body(proc, cond); 284 | proc.branch(body_b, after); 285 | proc.switch_block(body_b); 286 | self.compile_body(proc, body); 287 | proc.jump(cond_b); 288 | proc.switch_block(after) 289 | } 290 | TypedIr::If(if_) => self.compile_if(proc, if_), 291 | TypedIr::Cond(cond) => self.compile_cond(proc, cond), 292 | TypedIr::Literal(lit) => { 293 | let v = match lit { 294 | Literal::Bool(b) => Value::Bool(b), 295 | Literal::Int(i) => Value::Int(i), 296 | Literal::UInt(u) => Value::UInt(u), 297 | Literal::String(s) => Value::String(s), 298 | Literal::Char(c) => Value::Char(c), 299 | }; 300 | proc.push(v); 301 | } 302 | TypedIr::IgnorePattern => unreachable!(), 303 | TypedIr::Return => { 304 | let num_bindings = proc.bindings.iter().flatten().count(); 305 | for _ in 0..num_bindings { 306 | proc.unbind() 307 | } 308 | proc.return_() 309 | } 310 | TypedIr::FieldAccess(FieldAccess { 311 | ty: ReifiedType::Custom(ty), 312 | field, 313 | }) => { 314 | let size = ty.fields.get(&field).unwrap().ty.size(); 315 | proc.read(size); 316 | } 317 | TypedIr::FieldAccess(_) => { 318 | unreachable!() 319 | } 320 | } 321 | } 322 | } 323 | 324 | fn mangle(&self, name: &ItemPath) -> Mangled { 325 | let joiner = "__".into(); 326 | let name = name 327 | .iter() 328 | .intersperse(&joiner) 329 | .map(|s| { 330 | s.replace( 331 | |c: char| !c.is_alphanumeric() && c != '_', 332 | &format!("{}", self.proc_id), 333 | ) 334 | }) 335 | .collect(); 336 | Mangled(name) 337 | } 338 | 339 | fn inc_proc_id(&mut self) { 340 | self.proc_id += 1; 341 | } 342 | 343 | fn compile_mem(&mut self, name: &ItemPath) { 344 | let CMem { body } = match self.mems.get(name) { 345 | Some(ComMem::Compiled(_)) => return, 346 | Some(ComMem::NotCompiled(mem)) => mem.clone(), 347 | None => unreachable!(), 348 | }; 349 | 350 | let mut proc = ProcBuilder::new(); 351 | 352 | self.compile_body(&mut proc, body); 353 | proc.return_(); 354 | 355 | let size = match eval(&proc.blocks, true) { 356 | Some(Value::UInt(size)) => size as usize, 357 | Some(Value::Int(size)) => size as usize, 358 | Some(_) => unreachable!(), 359 | None => todo!(), 360 | }; 361 | self.mems.insert(name.to_owned(), ComMem::Compiled(size)); 362 | } 363 | } 364 | 365 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 366 | pub enum Value { 367 | Bool(bool), 368 | Int(i64), 369 | UInt(u64), 370 | String(SmolStr), 371 | Char(char), 372 | } 373 | 374 | #[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 375 | pub struct Mangled(pub SmolStr); 376 | 377 | impl std::fmt::Display for Mangled { 378 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 379 | self.0.fmt(f) 380 | } 381 | } 382 | 383 | #[derive(Debug)] 384 | pub enum Op { 385 | Push(Value), 386 | PushMem(Mangled), 387 | Drop, 388 | Dup, 389 | Swap, 390 | Over, 391 | 392 | Bind, 393 | UseBinding(usize), 394 | Unbind, 395 | 396 | Read(usize), 397 | Write(usize), 398 | 399 | ReserveLocals(usize), 400 | FreeLocals(usize), 401 | PushLvar(usize), 402 | 403 | Add, 404 | Sub, 405 | Divmod, 406 | Mul, 407 | 408 | Eq, 409 | Ne, 410 | Lt, 411 | Le, 412 | Gt, 413 | Ge, 414 | 415 | Jump(BlockId), 416 | Branch(BlockId, BlockId), 417 | Call(Mangled), 418 | Return, 419 | 420 | Syscall(u8), 421 | } 422 | -------------------------------------------------------------------------------- /rotth/src/lir2/cfg.rs: -------------------------------------------------------------------------------- 1 | use fnv::{FnvHashMap, FnvHashSet}; 2 | use itempath::ItemPathBuf; 3 | 4 | use super::{Mangled, Op, Value}; 5 | 6 | #[derive(Default)] 7 | pub struct Block { 8 | pub ops: Vec, 9 | pub parents: FnvHashSet, 10 | pub children: FnvHashSet, 11 | } 12 | 13 | impl std::fmt::Debug for Block { 14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | for (i, op) in self.ops.iter().enumerate() { 16 | writeln!(f, "\t{i}:\t{op:?}")?; 17 | } 18 | Ok(()) 19 | } 20 | } 21 | 22 | pub struct ProcBuilder { 23 | pub blocks: Vec, 24 | pub current_block: BlockId, 25 | pub entry: BlockId, 26 | pub exit: BlockId, 27 | pub bindings: Vec>, 28 | pub locals: FnvHashMap, 29 | } 30 | 31 | impl ProcBuilder { 32 | pub fn new() -> Self { 33 | let blocks = vec![Block::default(), Block::default()]; 34 | Self { 35 | blocks, 36 | current_block: BlockId(0), 37 | entry: BlockId(0), 38 | exit: BlockId(1), 39 | bindings: Default::default(), 40 | locals: Default::default(), 41 | } 42 | } 43 | 44 | pub fn new_block(&mut self) -> BlockId { 45 | let id = BlockId(self.blocks.len()); 46 | self.blocks.push(Block::default()); 47 | id 48 | } 49 | 50 | fn push_child(&mut self, blk: BlockId, child: BlockId) { 51 | self.blocks[child.0].parents.insert(blk); 52 | self.blocks[blk.0].children.insert(child); 53 | } 54 | 55 | pub fn switch_block(&mut self, blk: BlockId) { 56 | self.current_block = blk 57 | } 58 | 59 | pub fn dbg_graph(&self, path: impl AsRef) -> std::io::Result<()> { 60 | use std::io::Write; 61 | let mut f = std::fs::File::create(path)?; 62 | writeln!(f, "strict digraph {{")?; 63 | 64 | for (c, blk) in self.blocks.iter().enumerate() { 65 | for &child in &blk.children { 66 | writeln!(f, "\t{c} -> {}", child.0)?; 67 | } 68 | } 69 | 70 | writeln!(f, "}}")?; 71 | Ok(()) 72 | } 73 | 74 | pub fn push(&mut self, const_: Value) { 75 | self.blocks[self.current_block.0].ops.push(Op::Push(const_)) 76 | } 77 | pub fn push_mem(&mut self, sym: Mangled) { 78 | self.blocks[self.current_block.0].ops.push(Op::PushMem(sym)) 79 | } 80 | #[allow(clippy::should_implement_trait)] 81 | pub fn drop(&mut self) { 82 | self.blocks[self.current_block.0].ops.push(Op::Drop) 83 | } 84 | pub fn dup(&mut self) { 85 | self.blocks[self.current_block.0].ops.push(Op::Dup) 86 | } 87 | pub fn swap(&mut self) { 88 | self.blocks[self.current_block.0].ops.push(Op::Swap) 89 | } 90 | pub fn over(&mut self) { 91 | self.blocks[self.current_block.0].ops.push(Op::Over) 92 | } 93 | pub fn bind(&mut self) { 94 | self.blocks[self.current_block.0].ops.push(Op::Bind) 95 | } 96 | pub fn use_binding(&mut self, binding: usize) { 97 | self.blocks[self.current_block.0] 98 | .ops 99 | .push(Op::UseBinding(binding)) 100 | } 101 | pub fn unbind(&mut self) { 102 | self.blocks[self.current_block.0].ops.push(Op::Unbind) 103 | } 104 | pub fn read(&mut self, size: usize) { 105 | self.blocks[self.current_block.0].ops.push(Op::Read(size)) 106 | } 107 | pub fn write(&mut self, size: usize) { 108 | self.blocks[self.current_block.0].ops.push(Op::Write(size)) 109 | } 110 | pub fn push_lvar(&mut self, lvar: usize) { 111 | self.blocks[self.current_block.0] 112 | .ops 113 | .push(Op::PushLvar(lvar)) 114 | } 115 | pub fn add(&mut self) { 116 | self.blocks[self.current_block.0].ops.push(Op::Add) 117 | } 118 | pub fn sub(&mut self) { 119 | self.blocks[self.current_block.0].ops.push(Op::Sub) 120 | } 121 | pub fn divmod(&mut self) { 122 | self.blocks[self.current_block.0].ops.push(Op::Divmod) 123 | } 124 | pub fn mul(&mut self) { 125 | self.blocks[self.current_block.0].ops.push(Op::Mul) 126 | } 127 | pub fn eq(&mut self) { 128 | self.blocks[self.current_block.0].ops.push(Op::Eq) 129 | } 130 | pub fn ne(&mut self) { 131 | self.blocks[self.current_block.0].ops.push(Op::Ne) 132 | } 133 | pub fn lt(&mut self) { 134 | self.blocks[self.current_block.0].ops.push(Op::Lt) 135 | } 136 | pub fn le(&mut self) { 137 | self.blocks[self.current_block.0].ops.push(Op::Le) 138 | } 139 | pub fn gt(&mut self) { 140 | self.blocks[self.current_block.0].ops.push(Op::Gt) 141 | } 142 | pub fn ge(&mut self) { 143 | self.blocks[self.current_block.0].ops.push(Op::Ge) 144 | } 145 | pub fn jump(&mut self, block: BlockId) { 146 | self.blocks[self.current_block.0].ops.push(Op::Jump(block)); 147 | self.push_child(self.current_block, block); 148 | } 149 | pub fn branch(&mut self, truth: BlockId, lie: BlockId) { 150 | self.blocks[self.current_block.0] 151 | .ops 152 | .push(Op::Branch(truth, lie)); 153 | self.push_child(self.current_block, truth); 154 | self.push_child(self.current_block, lie); 155 | } 156 | pub fn call(&mut self, proc: Mangled) { 157 | self.blocks[self.current_block.0].ops.push(Op::Call(proc)) 158 | } 159 | pub fn return_(&mut self) { 160 | self.blocks[self.current_block.0].ops.push(Op::Return); 161 | self.push_child(self.current_block, self.exit); 162 | } 163 | } 164 | 165 | impl Default for ProcBuilder { 166 | fn default() -> Self { 167 | Self::new() 168 | } 169 | } 170 | 171 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 172 | pub struct BlockId(pub usize); 173 | -------------------------------------------------------------------------------- /rotth/src/main.rs: -------------------------------------------------------------------------------- 1 | use ariadne::{Color, FileCache, Fmt, Label, Report, ReportKind, Span}; 2 | use chumsky::error::RichReason; 3 | use clap::{Parser, ValueEnum}; 4 | use internment::Intern; 5 | use rotth::{lir2, Error}; 6 | use rotth_analysis::{ctir, tir}; 7 | use rotth_parser::hir; 8 | use std::{path::PathBuf, time::Instant}; 9 | 10 | #[derive(Parser)] 11 | struct Args { 12 | #[clap(short = 'k', long)] 13 | dump_tokens: bool, 14 | #[clap(short = 'a', long)] 15 | dump_ast: bool, 16 | #[clap(short = 'i', long)] 17 | dump_hir: bool, 18 | #[clap(short = 'r', long)] 19 | dump_tir: bool, 20 | #[clap(short = 'c', long)] 21 | dump_ctir: bool, 22 | #[clap(short = 'l', long)] 23 | dump_lir: bool, 24 | #[clap(short = 't', long)] 25 | time: bool, 26 | #[clap(short = 'b', long, value_enum, default_value = "asm")] 27 | backend: Backend, 28 | source: PathBuf, 29 | } 30 | 31 | #[derive(Clone, ValueEnum)] 32 | enum Backend { 33 | Asm, 34 | Llvm, 35 | Cranelift, 36 | } 37 | 38 | fn main() -> std::result::Result<(), ()> { 39 | match compiler() { 40 | Ok(_) => Ok(()), 41 | Err(e) => { 42 | report_errors(e); 43 | Err(()) 44 | } 45 | } 46 | } 47 | 48 | fn report_errors(e: Error) { 49 | let mut sources = FileCache::default(); 50 | match e { 51 | Error::IO(e) => eprintln!("{e}"), 52 | Error::Lexer => {} 53 | Error::Parser(rotth_parser::ParserError(es)) => { 54 | for e in es { 55 | match e { 56 | rotth_parser::Error::Parser(e) => { 57 | let report = 58 | Report::build(ReportKind::Error, e.span().source(), e.span().start); 59 | 60 | let report = match e.reason() { 61 | RichReason::ExpectedFound { .. } => report 62 | .with_message(format!( 63 | "{}, expected {}", 64 | if e.found().is_some() { 65 | "Unexpected token in input" 66 | } else { 67 | "Unexpected end of input" 68 | }, 69 | if e.expected().len() == 0 { 70 | "something else".to_string() 71 | } else { 72 | e.expected() 73 | .map(|expected| expected.to_string()) 74 | .collect::>() 75 | .join(", ") 76 | } 77 | )) 78 | .with_label( 79 | Label::new(*e.span()) 80 | .with_message(format!( 81 | "Unexpected token {}", 82 | e.found() 83 | .map(ToString::to_string) 84 | .unwrap_or_else(|| "end of file".to_string()) 85 | .fg(Color::Red) 86 | )) 87 | .with_color(Color::Red), 88 | ), 89 | RichReason::Custom(msg) => report.with_message(msg).with_label( 90 | Label::new(*e.span()) 91 | .with_message(format!("{}", msg.fg(Color::Red))) 92 | .with_color(Color::Red), 93 | ), 94 | RichReason::Many(_es) => todo!(), 95 | }; 96 | report.finish().print(&mut sources).unwrap(); 97 | } 98 | rotth_parser::Error::Redefinition(e) => { 99 | let report = Report::build( 100 | ReportKind::Error, 101 | e.redefined_item.source(), 102 | e.redefined_item.start, 103 | ) 104 | .with_message("Duplicate word definitions") 105 | .with_label( 106 | Label::new(e.redefined_item) 107 | .with_message("Word originally defined here...") 108 | .with_color(Color::Green), 109 | ) 110 | .with_label( 111 | Label::new(e.redefining_item) 112 | .with_message("redefined here") 113 | .with_color(Color::Yellow), 114 | ); 115 | report.finish().print(&mut sources).unwrap(); 116 | } 117 | rotth_parser::Error::UnresolvedInclude(_) => todo!(), 118 | } 119 | } 120 | } 121 | Error::Typecheck(e) => { 122 | let span = if let Some(span) = e.span { 123 | span 124 | } else { 125 | dbg!(e); 126 | return; 127 | }; 128 | let report = 129 | Report::build(ReportKind::Error, span.source(), span.start).with_message(e.message); 130 | 131 | let report = match e.kind { 132 | rotth_analysis::ErrorKind::TypeMismatch { expected, actual } => report.with_label( 133 | Label::new(span).with_message( 134 | format!( 135 | "Unexpected types: {} where {} expected", 136 | format!("{:?}", actual).fg(Color::Green), 137 | format!("{:?}", expected).fg(Color::Yellow) 138 | ) 139 | .fg(Color::Red), 140 | ), 141 | ), 142 | rotth_analysis::ErrorKind::NotEnoughData => report.with_label( 143 | Label::new(span).with_message("Not enough data on the stack".fg(Color::Red)), 144 | ), 145 | rotth_analysis::ErrorKind::UnsupportedOperation => report.with_label( 146 | Label::new(span).with_message("Not enough data on the stack".fg(Color::Red)), 147 | ), 148 | rotth_analysis::ErrorKind::Undefined => { 149 | report.with_label(Label::new(span).with_message("Unknown word".fg(Color::Red))) 150 | } 151 | rotth_analysis::ErrorKind::InvalidMain => report.with_label( 152 | Label::new(span).with_message( 153 | format!("Invalid type signature for `{}`", "main".fg(Color::Yellow)) 154 | .fg(Color::Red), 155 | ), 156 | ), 157 | rotth_analysis::ErrorKind::InvalidWhile => { 158 | report.with_label(Label::new(span).with_message( 159 | "While body must not alter types on the stack".fg(Color::Red), 160 | )) 161 | } 162 | rotth_analysis::ErrorKind::CompStop => { 163 | report.with_label(Label::new(span).with_message("Compilation stopped here")) 164 | } 165 | rotth_analysis::ErrorKind::Unexpected => { 166 | report.with_label(Label::new(span).with_message("Unexpected word")) 167 | } 168 | rotth_analysis::ErrorKind::CallInConst => { 169 | report.with_label(Label::new(span).with_message("Procedure call here")) 170 | } 171 | rotth_analysis::ErrorKind::UnificationError(msg) => { 172 | report.with_label(Label::new(span).with_message(format!( 173 | "{}: {}", 174 | "Unification error".fg(Color::Red), 175 | msg 176 | ))) 177 | } 178 | rotth_analysis::ErrorKind::Concrete(c) => { 179 | match c { 180 | rotth_analysis::ctir::ConcreteError::IncorrectMainOutputs => report 181 | .with_label(Label::new(span).with_message(format!( 182 | "{}: `main` proc must have a single u64 output", 183 | "IncorrectMainOutputs".fg(Color::Red) 184 | ))), 185 | rotth_analysis::ctir::ConcreteError::MainWithInputs => { 186 | report.with_label(Label::new(span).with_message(format!( 187 | "{}: `main` proc can't take inputs", 188 | "MainWithInputs".fg(Color::Red) 189 | ))) 190 | } 191 | rotth_analysis::ctir::ConcreteError::GenericMain => { 192 | report.with_label(Label::new(span).with_message(format!( 193 | "{}: `main` proc can't be generic", 194 | "GenericMain".fg(Color::Red) 195 | ))) 196 | } 197 | rotth_analysis::ctir::ConcreteError::NoEntry => { 198 | report.with_label(Label::new(span).with_message(format!( 199 | "{}: Entry point not found in this file", 200 | "NoEntry".fg(Color::Red) 201 | ))) 202 | } 203 | rotth_analysis::ctir::ConcreteError::IncompleteProcedure(p) => report 204 | .with_label(Label::new(span).with_message(format!( 205 | "{}: encountered incomplete item {:?}", 206 | "IncompleteProcedure".fg(Color::Red), 207 | p 208 | ))), 209 | rotth_analysis::ctir::ConcreteError::IncompleteConst(p) => report 210 | .with_label(Label::new(span).with_message(format!( 211 | "{}: encountered incomplete item {:?}", 212 | "IncompleteConst".fg(Color::Red), 213 | p 214 | ))), 215 | rotth_analysis::ctir::ConcreteError::IncompleteMem(p) => { 216 | report.with_label(Label::new(span).with_message(format!( 217 | "{}: encountered incomplete item {:?}", 218 | "IncompleteMem".fg(Color::Red), 219 | p 220 | ))) 221 | } 222 | rotth_analysis::ctir::ConcreteError::IncompleteVar(p) => { 223 | report.with_label(Label::new(span).with_message(format!( 224 | "{}: encountered incomplete item {:?}", 225 | "IncompleteVar".fg(Color::Red), 226 | p 227 | ))) 228 | } 229 | } 230 | } 231 | }; 232 | 233 | report.finish().print(&mut sources).unwrap(); 234 | } 235 | Error::Concrete(e) => todo!("{e:?}"), 236 | } 237 | } 238 | 239 | fn compiler<'i>() -> Result<(), Error<'i>> { 240 | let args = Args::parse(); 241 | 242 | let start = Instant::now(); 243 | 244 | let source = args.source.canonicalize()?; 245 | 246 | let src_text = Box::leak(std::fs::read_to_string(&source)?.into_boxed_str()); 247 | 248 | let tokens = rotth_lexer::lex(src_text, Intern::new(source)); 249 | 250 | let tokenized = Instant::now(); 251 | if args.time { 252 | println!("Tokenized in:\t{:?}", tokenized - start) 253 | } 254 | 255 | if args.dump_tokens { 256 | println!("Tokens:"); 257 | println!("{:?}", tokens.iter().map(|(a, _)| a).collect::>()); 258 | } 259 | 260 | let ast = rotth_parser::ast::parse(tokens)?; 261 | let ast = rotth_parser::ast::resolve_includes(ast)?; 262 | 263 | let parsed = Instant::now(); 264 | if args.time { 265 | println!("Parsed in:\t{:?}", parsed - tokenized) 266 | } 267 | 268 | if args.dump_ast { 269 | println!("AST:"); 270 | println!("{ast:#?}"); 271 | } 272 | 273 | let (hir, structs) = hir::Walker::walk_ast(ast); 274 | 275 | let lowered = Instant::now(); 276 | if args.time { 277 | println!("Lowered in:\t{:?}", lowered - parsed) 278 | } 279 | 280 | if args.dump_hir { 281 | println!("HIR:"); 282 | println!("{hir:#?}"); 283 | } 284 | 285 | let tir = tir::Walker::walk(hir, structs)?; 286 | 287 | let typechecked = Instant::now(); 288 | if args.time { 289 | println!("Typechecked in:\t{:?}", typechecked - lowered) 290 | } 291 | 292 | if args.dump_tir { 293 | println!("TIR:"); 294 | println!("{tir:#?}"); 295 | } 296 | 297 | let ctir = ctir::Walker::walk(tir)?; 298 | 299 | let concretized = Instant::now(); 300 | if args.time { 301 | println!("Concretized in:\t{:?}", concretized - typechecked) 302 | } 303 | 304 | if args.dump_ctir { 305 | println!("CTIR:"); 306 | println!("{ctir:#?}"); 307 | } 308 | 309 | let (lir, strings, mems) = lir2::Compiler::compile(ctir); 310 | 311 | let transpiled = Instant::now(); 312 | if args.time { 313 | println!("Transpiled in:\t{:?}", transpiled - typechecked); 314 | } 315 | 316 | if args.dump_lir { 317 | println!("LIR:"); 318 | for (name, proc) in lir.iter() { 319 | println!("{name}:"); 320 | println!("{proc:?}"); 321 | } 322 | println!(); 323 | } 324 | 325 | // match args.backend { 326 | // Backend::Asm => { 327 | // emit::asm::compile( 328 | // lir, 329 | // strings, 330 | // mems, 331 | // BufWriter::new( 332 | // OpenOptions::new() 333 | // .create(true) 334 | // .write(true) 335 | // .truncate(true) 336 | // .open(source.with_extension("asm"))?, 337 | // ), 338 | // )?; 339 | // } 340 | // Backend::Llvm => todo!(), 341 | // Backend::Cranelift => todo!(), 342 | // } 343 | 344 | let compiled = Instant::now(); 345 | if args.time { 346 | println!("Compiled in:\t{:?}", compiled - transpiled); 347 | println!("Total:\t{:?}", compiled - start); 348 | } 349 | 350 | Ok(()) 351 | } 352 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /simplearena/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simplearena" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /simplearena/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Debug, marker::PhantomData}; 2 | 3 | #[derive(Clone, Copy, PartialEq)] 4 | struct AllocId(u32); 5 | impl AllocId { 6 | fn as_usize(&self) -> usize { 7 | self.0 as usize 8 | } 9 | } 10 | 11 | impl From for AllocId { 12 | fn from(u: usize) -> Self { 13 | Self(u as u32) 14 | } 15 | } 16 | 17 | #[derive(Clone, Copy)] 18 | pub struct RefFmt<'h, 'r, T, const ID: u32>(&'r AllocId, &'h Heap) 19 | where 20 | T: Debug; 21 | 22 | impl<'h, 'r, T, const ID: u32> Debug for RefFmt<'h, 'r, T, ID> 23 | where 24 | T: Debug, 25 | { 26 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 27 | write!(f, "{:?}", self.1.get(self.0)) 28 | } 29 | } 30 | 31 | #[derive(Clone, Copy, PartialEq)] 32 | pub struct Ref(AllocId, PhantomData>) 33 | where 34 | T: 'static; 35 | 36 | impl Debug for Ref 37 | where 38 | T: 'static, 39 | { 40 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 41 | f.debug_tuple("Ref").field(&self.0 .0).finish() 42 | } 43 | } 44 | 45 | impl<'h, T, const ID: u32> Ref { 46 | pub fn as_usize(&self) -> usize { 47 | self.0.as_usize() 48 | } 49 | 50 | fn new(alloc_id: AllocId) -> Self { 51 | Self(alloc_id, Default::default()) 52 | } 53 | 54 | pub fn deref(&self, heap: &'h Heap) -> Option<&'h T> { 55 | heap.get(&self.0) 56 | } 57 | pub fn deref_mut(&mut self, heap: &'h mut Heap) -> Option<&'h mut T> { 58 | heap.get_mut(&mut self.0) 59 | } 60 | } 61 | impl<'h, T, const ID: u32> Ref 62 | where 63 | T: Debug, 64 | { 65 | pub fn ref_fmt<'r>(&'r self, heap: &'h Heap) -> RefFmt<'h, 'r, T, ID> { 66 | RefFmt(&self.0, heap) 67 | } 68 | } 69 | 70 | pub enum Container { 71 | Free, 72 | Value(T), 73 | } 74 | 75 | impl Debug for Container { 76 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 77 | match self { 78 | Self::Free => write!(f, "[]"), 79 | Self::Value(value) => write!(f, "[{value:?}]"), 80 | } 81 | } 82 | } 83 | 84 | impl Container { 85 | pub fn as_value(&self) -> Option<&T> { 86 | if let Self::Value(v) = self { 87 | Some(v) 88 | } else { 89 | None 90 | } 91 | } 92 | pub fn as_value_mut(&mut self) -> Option<&mut T> { 93 | if let Self::Value(v) = self { 94 | Some(v) 95 | } else { 96 | None 97 | } 98 | } 99 | } 100 | 101 | pub struct Heap { 102 | values: Vec>, 103 | free: Vec, 104 | } 105 | 106 | impl std::fmt::Debug for Heap { 107 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 108 | f.debug_struct("Heap") 109 | .field("inner", &self.values.iter().enumerate().collect::>()) 110 | .finish() 111 | } 112 | } 113 | 114 | impl Heap { 115 | pub fn new() -> Self { 116 | Self { 117 | values: Default::default(), 118 | free: Default::default(), 119 | } 120 | } 121 | 122 | pub fn alloc(&mut self, value: T) -> Ref { 123 | if let Some(slot) = self.free.pop() { 124 | self.values[slot] = Container::Value(value); 125 | Ref::new(slot.into()) 126 | } else { 127 | self.values.push(Container::Value(value)); 128 | Ref::new((self.values.len() - 1).into()) 129 | } 130 | } 131 | 132 | fn get(&self, rf: &AllocId) -> Option<&T> { 133 | let cont = self.values.get(rf.as_usize())?; 134 | cont.as_value() 135 | } 136 | 137 | fn get_mut(&mut self, vref: &mut AllocId) -> Option<&mut T> { 138 | let cont = self.values.get_mut(vref.as_usize())?; 139 | cont.as_value_mut() 140 | } 141 | } 142 | 143 | impl Default for Heap { 144 | fn default() -> Self { 145 | Self::new() 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /spanner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spanner" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | chumsky.workspace = true 10 | ariadne.workspace = true 11 | internment.workspace = true 12 | -------------------------------------------------------------------------------- /spanner/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::Debug, 3 | ops::{Deref, DerefMut, Range}, 4 | path::{Path, PathBuf}, 5 | }; 6 | 7 | use internment::Intern; 8 | 9 | #[derive(Copy, Clone, Hash, PartialEq, Eq)] 10 | pub struct Spanned { 11 | pub span: Span, 12 | pub inner: T, 13 | } 14 | 15 | impl Debug for Spanned { 16 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 17 | if f.alternate() { 18 | write!(f, "{:#?}@{:#?}", self.inner, self.span) 19 | } else { 20 | write!(f, "{:?}@{:?}", self.inner, self.span) 21 | } 22 | } 23 | } 24 | 25 | impl Spanned { 26 | pub fn map_ref(&self, f: F) -> Spanned 27 | where 28 | F: FnOnce(&T) -> B, 29 | { 30 | Spanned { 31 | span: self.span, 32 | inner: f(&self.inner), 33 | } 34 | } 35 | pub fn map(self, f: F) -> Spanned 36 | where 37 | F: FnOnce(T) -> B, 38 | { 39 | Spanned { 40 | span: self.span, 41 | inner: f(self.inner), 42 | } 43 | } 44 | } 45 | 46 | impl Deref for Spanned { 47 | type Target = T; 48 | 49 | fn deref(&self) -> &Self::Target { 50 | &self.inner 51 | } 52 | } 53 | 54 | impl DerefMut for Spanned { 55 | fn deref_mut(&mut self) -> &mut Self::Target { 56 | &mut self.inner 57 | } 58 | } 59 | 60 | #[derive(Copy, Clone, Hash, PartialEq, Eq)] 61 | pub struct Span { 62 | pub file: Intern, 63 | pub start: usize, 64 | pub end: usize, 65 | } 66 | 67 | impl Span { 68 | pub fn new(file: Intern, start: usize, end: usize) -> Self { 69 | Self { file, start, end } 70 | } 71 | pub fn point(file: Intern, point: usize) -> Self { 72 | Self { 73 | file, 74 | start: point, 75 | end: point + 1, 76 | } 77 | } 78 | pub fn length(&self) -> usize { 79 | self.end - self.start 80 | } 81 | 82 | pub fn merge(self, other: Self) -> Self { 83 | assert!(self.file == other.file); 84 | assert!(self.start < other.start); 85 | Self { 86 | file: self.file, 87 | start: self.start, 88 | end: other.end, 89 | } 90 | } 91 | } 92 | 93 | impl Debug for Span { 94 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 95 | if f.alternate() { 96 | write!(f, "{:?}[{}..{}]", &self.file, &self.start, &self.end) 97 | } else { 98 | write!( 99 | f, 100 | "{:?}[{}..{}]", 101 | self.file.file_name().unwrap(), 102 | &self.start, 103 | &self.end 104 | ) 105 | } 106 | } 107 | } 108 | 109 | impl ariadne::Span for Span { 110 | type SourceId = Path; 111 | 112 | fn source(&self) -> &Self::SourceId { 113 | &self.file 114 | } 115 | 116 | fn start(&self) -> usize { 117 | self.start 118 | } 119 | 120 | fn end(&self) -> usize { 121 | self.end 122 | } 123 | } 124 | 125 | impl chumsky::span::Span for Span { 126 | type Context = Intern; 127 | 128 | type Offset = usize; 129 | 130 | fn new(file: Self::Context, range: Range) -> Self { 131 | Self::new(file, range.start, range.end) 132 | } 133 | 134 | fn context(&self) -> Self::Context { 135 | self.file 136 | } 137 | 138 | fn start(&self) -> Self::Offset { 139 | self.start 140 | } 141 | 142 | fn end(&self) -> Self::Offset { 143 | self.end 144 | } 145 | } 146 | --------------------------------------------------------------------------------