Compiler experimentation.
0
fork

Configure Feed

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

Implement Do Blocks, Parser, Interpreter

+209 -15
+54
Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 + name = "peg" 7 + version = "0.8.5" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477" 10 + dependencies = [ 11 + "peg-macros", 12 + "peg-runtime", 13 + ] 14 + 15 + [[package]] 16 + name = "peg-macros" 17 + version = "0.8.5" 18 + source = "registry+https://github.com/rust-lang/crates.io-index" 19 + checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71" 20 + dependencies = [ 21 + "peg-runtime", 22 + "proc-macro2", 23 + "quote", 24 + ] 25 + 26 + [[package]] 27 + name = "peg-runtime" 28 + version = "0.8.5" 29 + source = "registry+https://github.com/rust-lang/crates.io-index" 30 + checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca" 31 + 32 + [[package]] 33 + name = "proc-macro2" 34 + version = "1.0.106" 35 + source = "registry+https://github.com/rust-lang/crates.io-index" 36 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 37 + dependencies = [ 38 + "unicode-ident", 39 + ] 40 + 41 + [[package]] 42 + name = "quote" 43 + version = "1.0.44" 44 + source = "registry+https://github.com/rust-lang/crates.io-index" 45 + checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" 46 + dependencies = [ 47 + "proc-macro2", 48 + ] 49 + 50 + [[package]] 51 + name = "unicode-ident" 52 + version = "1.0.24" 53 + source = "registry+https://github.com/rust-lang/crates.io-index" 54 + checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 55 + 56 + [[package]] 6 57 name = "zauber" 7 58 version = "0.1.0" 59 + dependencies = [ 60 + "peg", 61 + ]
+1
Cargo.toml
··· 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 + peg = "0.8.5"
+58
src/interpret.rs
··· 1 + use std::collections::HashMap; 2 + 3 + use crate::parse::{Ast, Expr}; 4 + 5 + struct Context<'s> { 6 + scopes: Vec<HashMap<&'s str, u64>>, 7 + } 8 + 9 + impl<'s> Context<'s> { 10 + pub fn new() -> Self { 11 + Self { scopes: Vec::new() } 12 + } 13 + 14 + pub fn get(&self, name: &'s str) -> Option<u64> { 15 + self.scopes 16 + .iter() 17 + .rev() 18 + .find_map(|scope| scope.get(name).copied()) 19 + } 20 + 21 + pub fn put(&mut self, name: &'s str, value: u64) { 22 + self.scopes.last_mut().unwrap().insert(name, value); 23 + } 24 + 25 + pub fn enter(&mut self) { 26 + self.scopes.push(HashMap::new()); 27 + } 28 + 29 + pub fn exit(&mut self) { 30 + self.scopes.pop(); 31 + } 32 + } 33 + 34 + /// Walk an AST, interpreting it, to produce a value. 35 + /// 36 + /// This is very efficient, but great for testing. 37 + pub fn interpret<'s>(ast: Ast<'s>) -> u64 { 38 + let mut ctx = Context::new(); 39 + eval(&mut ctx, ast.main_expr) 40 + } 41 + 42 + fn eval<'s>(ctx: &mut Context<'s>, expr: Expr<'s>) -> u64 { 43 + use Expr::*; 44 + match expr { 45 + Num(x) => x, 46 + Name(n) => ctx.get(n).unwrap_or_else(|| panic!("undefined name: {n}")), 47 + Do(do_expr) => { 48 + ctx.enter(); 49 + for assignment in do_expr.assignments { 50 + let value = eval(ctx, assignment.value); 51 + ctx.put(assignment.name, value); 52 + } 53 + let out = eval(ctx, *do_expr.value); 54 + ctx.exit(); 55 + out 56 + } 57 + } 58 + }
+23 -3
src/lex.rs
··· 4 4 pub enum Token<'a> { 5 5 Ident(&'a str), 6 6 Num(u64), 7 + Do, 7 8 Colon, 8 9 Equals, 10 + SemiColon, 11 + RBrace, 12 + LBrace, 9 13 } 10 14 11 15 struct Lexer<'a> { ··· 21 25 } 22 26 } 23 27 24 - fn ident(&mut self, i: usize) -> Token<'a> { 28 + fn ident(&mut self, i: usize) -> &'a str { 25 29 let start = i; 26 30 let mut end = start; 27 31 while let Some((j, x)) = self.iter.peek() { ··· 31 35 } 32 36 self.iter.next(); 33 37 } 34 - Token::Ident(&self.code[start..end]) 38 + &self.code[start..end] 35 39 } 36 40 37 41 fn num(&mut self, i: usize) -> Token<'a> { ··· 67 71 self.iter.next(); 68 72 break Some(Equals); 69 73 } 74 + Some((_, ';')) => { 75 + self.iter.next(); 76 + break Some(SemiColon); 77 + } 78 + Some((_, '}')) => { 79 + self.iter.next(); 80 + break Some(RBrace); 81 + } 82 + Some((_, '{')) => { 83 + self.iter.next(); 84 + break Some(LBrace); 85 + } 70 86 Some((i, a)) if a.is_alphabetic() => { 71 87 self.iter.next(); 72 - break Some(self.ident(i)); 88 + let out = match self.ident(i) { 89 + "do" => Do, 90 + other => Ident(other), 91 + }; 92 + break Some(out); 73 93 } 74 94 Some((i, a)) if a.is_ascii_digit() => { 75 95 self.iter.next();
+5 -12
src/lib.rs
··· 1 - use crate::lex::Token; 1 + mod interpret; 2 2 mod lex; 3 + mod parse; 3 4 4 5 /// Interpret a program, returning the result. 5 6 pub fn interpret(input: &str) -> u64 { 6 - use Token::*; 7 - let mut tokens = lex::lex(input); 8 - assert_eq!(Some(Ident("main")), tokens.next()); 9 - assert_eq!(Some(Colon), tokens.next()); 10 - assert_eq!(Some(Ident("I32")), tokens.next()); 11 - assert_eq!(Some(Ident("main")), tokens.next()); 12 - assert_eq!(Some(Equals), tokens.next()); 13 - let Some(Num(out)) = tokens.next() else { 14 - panic!("expected main = <NUMBER>"); 15 - }; 16 - out 7 + let tokens = lex::lex(input).collect::<Vec<_>>(); 8 + let ast = parse::parse(&tokens); 9 + interpret::interpret(ast) 17 10 }
+54
src/parse.rs
··· 1 + use crate::lex::Token; 2 + 3 + #[derive(Debug)] 4 + pub struct Assignment<'s> { 5 + pub name: &'s str, 6 + pub value: Expr<'s>, 7 + } 8 + 9 + #[derive(Debug)] 10 + pub struct DoExpr<'s> { 11 + pub assignments: Vec<Assignment<'s>>, 12 + pub value: Box<Expr<'s>>, 13 + } 14 + 15 + #[derive(Debug)] 16 + pub enum Expr<'s> { 17 + Do(DoExpr<'s>), 18 + Name(&'s str), 19 + Num(u64), 20 + } 21 + 22 + #[derive(Debug)] 23 + pub struct Ast<'s> { 24 + pub main_expr: Expr<'s>, 25 + } 26 + 27 + pub fn parse<'s>(tokens: &[Token<'s>]) -> Ast<'s> { 28 + ast_parser::ast(tokens).expect("parsing failed, sorry :(") 29 + } 30 + 31 + peg::parser! { 32 + grammar ast_parser<'s>() for [Token<'s>] { 33 + use Token::*; 34 + 35 + pub rule ast() -> Ast<'s> = 36 + [Ident("main")] [Colon] [Ident("I32")] 37 + [Ident("main")] [Equals] e:expr() { 38 + Ast { main_expr: e } 39 + } 40 + 41 + rule expr() -> Expr<'s> 42 + = e:do() { Expr::Do(e) } 43 + / [Ident(i)] { Expr::Name(i) } 44 + / [Num(n)] { Expr::Num(n) } 45 + 46 + rule do() -> DoExpr<'s> = [Do] [LBrace] assignments:assignment()* value:expr() [RBrace] { 47 + DoExpr { assignments, value: Box::new(value) } 48 + } 49 + 50 + rule assignment() -> Assignment<'s> = [Ident(name)] [Equals] value:expr() [SemiColon] { 51 + Assignment { name, value } 52 + } 53 + } 54 + }
+13
tests/examples/t_002.zbr
··· 1 + main : I32 2 + main = do { 3 + a = do { 4 + b = 2; 5 + b 6 + }; 7 + c = a; 8 + do { 9 + do { 10 + c 11 + } 12 + } 13 + }
+1
tests/main.rs
··· 13 13 } 14 14 15 15 expect!(t_001, 1); 16 + expect!(t_002, 2);