Rotth is a stack based concatenative language highly inspired by Porth
0

Configure Feed

Select the types of activity you want to include in your feed.

Work on basic block transpiler

Ivan Chinenov (Mar 23, 2024, 10:53 PM +0300) 08aba354 3e0938e6

+218 -185
-8
Cargo.lock
··· 1310 1310 ] 1311 1311 1312 1312 [[package]] 1313 - name = "slate" 1314 - version = "0.1.0" 1315 - dependencies = [ 1316 - "chumsky", 1317 - "logos", 1318 - ] 1319 - 1320 - [[package]] 1321 1313 name = "slice-group-by" 1322 1314 version = "0.3.1" 1323 1315 source = "registry+https://github.com/rust-lang/crates.io-index"
-1
Cargo.toml
··· 8 8 "rotth-lsp", 9 9 "spanner", 10 10 "simplearena", 11 - "slate", 12 11 ] 13 12 14 13 [workspace.dependencies]
+1 -1
rotth-analysis/src/tir.rs
··· 562 562 if let Err(msg) = self.engine.unify_stacks(heap, outs, ins) { 563 563 return error( 564 564 proc.span, 565 - ErrorKind::UnificationError(msg), 565 + ErrorKind::UnificationError(dbg! {msg}), 566 566 "Expected proc outputs do not match actual outputs", 567 567 ); 568 568 }
+2 -1
rotth-parser/src/ast.rs
··· 385 385 386 386 pub fn parse(tokens: Vec<(Token, Span)>) -> Result<File, ParserError<'static>> { 387 387 let len = tokens.len(); 388 + let path = tokens.first().unwrap().1.file; 388 389 parsers::file() 389 - .parse(Stream::from_iter(tokens).spanned(Span::point(std::path::Path::new(""), len))) 390 + .parse(Stream::from_iter(tokens).spanned(Span::point(path, len))) 390 391 .into_result() 391 392 .map_err(|e| e.into()) 392 393 }
+6
rotth-src/examples/simple_mem.rh
··· 1 + mem foo do "nonsense" end 2 + 3 + proc main: u64 do 4 + 1 foo cast &>i64 !i64 5 + 1 cast u64 6 + end
+58 -84
rotth/src/eval.rs
··· 1 - use crate::lir::{Mangled, Op}; 2 - use rotth_parser::ast::Literal; 3 - use somok::{Either, Somok}; 4 - use std::collections::HashMap; 1 + use crate::lir2::{cfg::Block, Op, Value}; 5 2 6 - pub fn eval( 7 - ops: Vec<Op>, 8 - _strings: &[String], 9 - debug: bool, 10 - ) -> Result<Either<u64, Vec<u64>>, Mangled> { 11 - let labels = ops 12 - .iter() 13 - .enumerate() 14 - .filter_map(|(i, op)| { 15 - if let Op::Label(l) = op { 16 - (l.clone(), i).some() 17 - } else { 18 - None 19 - } 20 - }) 21 - .collect::<HashMap<Mangled, usize>>(); 3 + pub fn eval(blocks: &[Block], debug: bool) -> Option<Value> { 4 + let mut block = &blocks[0]; 22 5 23 - let mut call_stack = Vec::new(); 6 + let mut bind_stack = Vec::new(); 24 7 let mut stack = Vec::new(); 25 8 let mut i = 0; 26 9 27 - while let Some(op) = ops.get(i) { 10 + while let Some(op) = block.ops.get(i) { 28 11 if debug { 29 12 println!("{}:\t{:?}", i, op); 30 13 } ··· 32 15 Op::PushMem(_i) => { 33 16 todo!("Support memories in eval") 34 17 } 35 - Op::PushStr(_i) => { 36 - todo!("Support strings in eval"); 37 - // let len = strings[*i].len() as u64; 38 - // stack.push(len); 39 - // stack.push(strings[*i].as_ptr() as u64); 40 - } 41 - Op::Push(c) => match c { 42 - Literal::Bool(b) => stack.push(*b as u64), 43 - Literal::Num(u) => stack.push(*u), 44 - Literal::Char(c) => stack.push(*c as u64), 45 - Literal::String(_) => todo!(), 46 - }, 18 + Op::Push(v) => stack.push(v.clone()), 47 19 Op::Drop => { 48 20 stack.pop(); 49 21 } 50 22 Op::Dup => { 51 - let v = stack.last().copied().unwrap(); 23 + let v = stack.last().cloned().unwrap(); 52 24 stack.push(v); 53 25 } 54 26 Op::Swap => { ··· 57 29 stack.push(b); 58 30 } 59 31 Op::Over => { 60 - let v = stack[stack.len() - 2]; 32 + let v = stack[stack.len() - 2].clone(); 61 33 stack.push(v); 62 34 } 63 35 64 - Op::Bind => call_stack.push(stack.pop().unwrap()), 65 - Op::UseBinding(offset) => stack.push(call_stack[(call_stack.len() - 1) - offset]), 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 + } 66 40 Op::Unbind => { 67 - call_stack.pop(); 41 + bind_stack.pop(); 68 42 } 69 43 70 - Op::ReadU64 | Op::ReadU8 | Op::WriteU64 | Op::WriteU8 => { 44 + Op::Read(_) | Op::Write(_) => { 71 45 panic!("Pointer operations are not supported in const eval") 72 46 } 73 47 74 - Op::Dump => println!("{:?}", stack), 75 - Op::Print => println!("{:?}", stack.pop().unwrap()), 76 - Op::Syscall0 77 - | Op::Syscall1 78 - | Op::Syscall2 79 - | Op::Syscall3 80 - | Op::Syscall4 81 - | Op::Syscall5 82 - | Op::Syscall6 83 - | Op::Argc 84 - | Op::Argv => todo!("Syscalls not supported in eval"), 48 + Op::Syscall(_) => todo!("Syscalls not supported in eval"), 85 49 86 50 Op::Add => { 87 - let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 88 - stack.push(a + b); 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); 89 57 } 90 58 Op::Sub => { 91 - let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 92 - stack.push(a - b); 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); 93 65 } 94 66 Op::Divmod => { 95 - let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 96 - stack.push(a / b); 97 - stack.push(a % b); 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); 98 74 } 99 75 Op::Mul => { 100 - let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 101 - stack.push(a * b); 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); 102 82 } 103 83 104 84 Op::Eq => { 105 - let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 106 - stack.push((a == b) as u64); 85 + let (a, b) = (stack.pop().unwrap(), stack.pop().unwrap()); 86 + stack.push(Value::Bool(a == b)); 107 87 } 108 88 Op::Ne => { 109 89 let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 110 - stack.push((a != b) as u64); 90 + stack.push(Value::Bool(a != b)); 111 91 } 112 92 Op::Lt => { 113 93 let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 114 - stack.push((a < b) as u64); 94 + stack.push(Value::Bool(a < b)); 115 95 } 116 96 Op::Le => { 117 97 let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 118 - stack.push((a <= b) as u64); 98 + stack.push(Value::Bool(a <= b)); 119 99 } 120 100 Op::Gt => { 121 101 let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 122 - stack.push((a > b) as u64); 102 + stack.push(Value::Bool(a > b)); 123 103 } 124 104 Op::Ge => { 125 105 let (b, a) = (stack.pop().unwrap(), stack.pop().unwrap()); 126 - stack.push((a >= b) as u64); 106 + stack.push(Value::Bool(a >= b)); 127 107 } 128 108 129 - Op::Label(_) => (), 130 - Op::Jump(l) => i = labels[l], 131 - Op::JumpF(l) => { 132 - if stack.pop() == Some(0) { 133 - i = labels[l] 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]; 134 115 } 116 + i = 0; 117 + continue; 135 118 } 136 - Op::JumpT(l) => { 137 - if stack.pop() == Some(1) { 138 - i = labels[l] 139 - } 140 - } 141 - Op::Call(l) => { 142 - call_stack.push(i as u64); 143 - i = labels.get(l).copied().ok_or_else(|| l.clone())? 144 - } 145 - Op::Return => i = call_stack.pop().unwrap() as usize, 146 - Op::Exit => return stack.pop().unwrap().left().okay(), 119 + Op::Call(_) => todo!("Call"), 120 + Op::Return => return stack.pop(), 147 121 Op::PushLvar(_) => todo!(), 148 122 Op::ReserveLocals(_) => todo!(), 149 123 Op::FreeLocals(_) => todo!(), 150 124 } 151 125 i += 1; 152 126 } 153 - stack.right().okay() 127 + None 154 128 }
+1 -1
rotth/src/lib.rs
··· 6 6 #![feature(array_windows)] 7 7 8 8 pub mod emit; 9 - // pub mod eval; 9 + pub mod eval; 10 10 // pub mod lir; 11 11 pub mod lir2; 12 12
+145 -17
rotth/src/lir2.rs
··· 1 + use crate::eval::eval; 2 + 1 3 use self::cfg::{Block, BlockId, ProcBuilder}; 2 4 use fnv::FnvHashMap; 3 5 use rotth_analysis::{ 4 6 ctir::{CConst, CMem, CProc, ConcreteNode, ConcreteProgram, Intrinsic}, 5 7 inference::ReifiedType, 6 - tir::{If, TypedIr}, 8 + tir::{Bind, Cond, CondBranch, FieldAccess, If, TypedIr, While}, 7 9 }; 8 - use rotth_parser::ast::{ItemPath, ItemPathBuf, Literal}; 10 + use rotth_parser::{ 11 + ast::{ItemPath, ItemPathBuf, Literal}, 12 + hir::Binding, 13 + }; 9 14 use smol_str::SmolStr; 15 + use spanner::Spanned; 10 16 11 - mod cfg; 17 + pub mod cfg; 12 18 13 19 pub struct CompiledProc { 14 20 pub ins: Vec<ReifiedType>, ··· 113 119 fn compile_proc( 114 120 &mut self, 115 121 CProc { 116 - generics, 117 - vars, 122 + generics: _, 123 + vars: _, 118 124 ins, 119 125 outs, 120 126 body, ··· 122 128 ) -> CompiledProc { 123 129 let mut proc = ProcBuilder::new(); 124 130 self.compile_body(&mut proc, body); 131 + proc.jump(proc.exit); 132 + 133 + proc.dbg_graph("./proc.dot").unwrap(); 125 134 126 135 let ProcBuilder { 127 136 blocks, 128 - current_block, 137 + current_block: _, 129 138 entry: _, 130 139 exit: _, 131 - bindings, 132 - locals, 140 + bindings: _, 141 + locals: _, 133 142 } = proc; 134 143 CompiledProc { ins, outs, blocks } 135 144 } ··· 156 165 proc.switch_block(after); 157 166 } 158 167 168 + fn compile_cond( 169 + &mut self, 170 + proc: &mut ProcBuilder, 171 + Cond { branches }: Cond<ReifiedType, Intrinsic>, 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 + 159 206 fn compile_body(&mut self, proc: &mut ProcBuilder, body: Vec<ConcreteNode>) { 160 207 for node in body { 161 208 let _span = node.span; 162 209 match node.node { 163 - TypedIr::MemUse(_) => proc.push_mem(), 164 - TypedIr::GVarUse(_) => todo!(), 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 + } 165 219 TypedIr::LVarUse(lvar) => { 166 220 let lvar = proc.locals.get(&lvar).unwrap(); 167 221 proc.push_lvar(*lvar); 168 222 } 169 - TypedIr::BindingUse(_) => todo!(), 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 + } 170 233 TypedIr::ConstUse(_) => todo!(), 171 - TypedIr::Call(_) => todo!(), 234 + TypedIr::Call(name) => { 235 + let mangled = self.mangle_table.get(&name).unwrap(); 236 + proc.call(mangled.clone()) 237 + } 172 238 TypedIr::Intrinsic(i) => match i { 173 239 Intrinsic::Drop => proc.drop(), 174 240 Intrinsic::Dup => proc.dup(), ··· 189 255 Intrinsic::Ge => proc.ge(), 190 256 i => todo!("{i:?}"), 191 257 }, 192 - TypedIr::Bind(_) => todo!(), 193 - TypedIr::While(_) => todo!(), 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 + } 194 290 TypedIr::If(if_) => self.compile_if(proc, if_), 195 - TypedIr::Cond(_) => todo!(), 291 + TypedIr::Cond(cond) => self.compile_cond(proc, cond), 196 292 TypedIr::Literal(lit) => { 197 293 let v = match lit { 198 294 Literal::Bool(b) => Value::Bool(b), ··· 211 307 } 212 308 proc.return_() 213 309 } 214 - TypedIr::FieldAccess(_) => todo!(), 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 + } 215 320 } 216 321 } 217 322 } ··· 234 339 fn inc_proc_id(&mut self) { 235 340 self.proc_id += 1; 236 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 + } 237 363 } 238 364 239 - #[derive(Debug)] 365 + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 240 366 pub enum Value { 241 367 Bool(bool), 242 368 Int(i64), ··· 290 416 Branch(BlockId, BlockId), 291 417 Call(Mangled), 292 418 Return, 419 + 420 + Syscall(u8), 293 421 }
+5 -3
rotth/src/lir2/cfg.rs
··· 63 63 64 64 for (c, blk) in self.blocks.iter().enumerate() { 65 65 for &child in &blk.children { 66 - writeln!(f, "\t{c} -> {child:?}")?; 66 + writeln!(f, "\t{c} -> {}", child.0)?; 67 67 } 68 68 } 69 69 ··· 74 74 pub fn push(&mut self, const_: Value) { 75 75 self.blocks[self.current_block.0].ops.push(Op::Push(const_)) 76 76 } 77 - pub fn push_mem(&mut self) {} 77 + pub fn push_mem(&mut self, sym: Mangled) { 78 + self.blocks[self.current_block.0].ops.push(Op::PushMem(sym)) 79 + } 78 80 pub fn drop(&mut self) { 79 81 self.blocks[self.current_block.0].ops.push(Op::Drop) 80 82 } 81 83 pub fn dup(&mut self) { 82 - self.blocks[self.current_block.0].ops.push(Op::Drop) 84 + self.blocks[self.current_block.0].ops.push(Op::Dup) 83 85 } 84 86 pub fn swap(&mut self) { 85 87 self.blocks[self.current_block.0].ops.push(Op::Swap)
-8
slate/Cargo.toml
··· 1 - [package] 2 - name = "slate" 3 - version = "0.1.0" 4 - edition = "2021" 5 - 6 - [dependencies] 7 - chumsky.workspace = true 8 - logos.workspace = true
-14
slate/src/lib.rs
··· 1 - pub fn add(left: usize, right: usize) -> usize { 2 - left + right 3 - } 4 - 5 - #[cfg(test)] 6 - mod tests { 7 - use super::*; 8 - 9 - #[test] 10 - fn it_works() { 11 - let result = add(2, 2); 12 - assert_eq!(result, 4); 13 - } 14 - }
-47
slate/src/parsers.rs
··· 1 - use chumsky::{input::Stream, prelude::*}; 2 - use internment::Intern; 3 - use logos::Lexer; 4 - 5 - use crate::{ 6 - ast::{Expr, Ident, Item, Module, Package, Proc}, 7 - lexer::Token, 8 - }; 9 - 10 - pub fn parser<'s>() -> impl Parser<'s, Stream<Lexer<'s, Token>>, Package> { 11 - item().repeated().collect::<Vec<_>>().map(|items| Package { 12 - root_module: Module { 13 - name: Ident(Intern::new("".into())), 14 - items: items.into_iter().map(|i| (i.name(), i)).collect(), 15 - }, 16 - dependencies: Default::default(), 17 - }) 18 - } 19 - 20 - fn item<'s>() -> impl Parser<'s, Stream<Lexer<'s, Token>>, Item> { 21 - choice((proc().map(Item::Proc),)) 22 - } 23 - 24 - fn proc<'s>() -> impl Parser<'s, Stream<Lexer<'s, Token>>, Proc> { 25 - just(Ok(Token::Proc)) 26 - .then(ident()) 27 - .then(body()) 28 - .then(just(Ok(Token::End))) 29 - .map(|(((_proc, name), _body), _end)| Proc { name }) 30 - } 31 - 32 - fn body<'s>() -> impl Parser<'s, Stream<Lexer<'s, Token>>, Vec<Expr>> { 33 - recursive(|body| { 34 - let bind = just(Ok(Token::Bind)) 35 - .ignore_then(ident().repeated().collect::<Vec<_>>()) 36 - .then(body) 37 - .map(|a| Expr::Bind); 38 - 39 - bind.or(ident().map(|a| Expr::Word)) 40 - }) 41 - } 42 - 43 - fn ident<'s>() -> impl Parser<'s, Stream<Lexer<'s, Token>>, Ident> + Clone { 44 - select! { 45 - Ok(Token::Ident(i)) => Ident(i) 46 - } 47 - }