# flexar An extremely flexible lexer/parser (get it?) for writing your own programming language # Examples The following examples are implementations of each of the aspects of this library; all working together to create a simple math interpreter with support for varibles. ### Imports ```rust use std::collections::HashMap; use flexar::prelude::*; ``` ## Compiler Errors ```rust flexar::compiler_error! { [[Define] Errors] (E001) "unexpected character": "character `", "` is unexpected"; (E002) "string not closed": "expected `\"` to close string"; (E003) "expected number": "expected number, found `", "`."; (E004) "expected an expr": "expected expr, found `", "`."; (E005) "expected `+` or `-` in binary operation": "expected `+` or `-`, found `", "`."; (E006) "unexpected token": "unexpected token `", "`."; (E007) "unclosed parentheses": "expected `)` to close parentheses"; (E008) "expected identifier in `let` statement": "expected ident, found `", "`."; (E009) "expected `=` in `let` statement": "expected `=`, found `", "`."; (E010) "expected one of `;`, `+`, `-`, `/` or `*`.": "expected `;` or operation, found `", "`."; (RT001) "non-existant varible": "varible `", "` doesn't exist"; } ``` ## Lexer ```rust flexar::lexer! { [[Token] lext, current, 'cycle] else flexar::compiler_error!((E001, lext.position()) current).throw(); token_types { LParen => "("; RParen => ")"; Int(val: u32) => val; Float(val: f32) => val; Plus => "+"; Minus => "-"; Mul => "*"; Div => "/"; Let => "let"; EQ => "="; Semi => ";"; Ident(val: String) => val; } Plus: +; LParen: '('; RParen: ')'; Minus: '-'; Mul: *; EQ: =; Semi: ;; [" \n\t"] >> ({ lext.advance(); lext = lext.spawn(); continue 'cycle; }); / child { advance: current; ck (current, /) { rsome current { { if current == '\n' { lext = child; continue 'cycle } }; }; }; advance:(); done Div(); }; ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"] child { set ident { String::new() }; rsome (current, 'ident) { set matched false; ck (current, ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-0123456789"]) { mut matched true; { ident.push(current) }; }; { if !matched { break 'ident } }; }; if (ident == "let") { done Let(); }; done Ident(ident); }; ["0123456789"] child { set number { String::new() }; set dot false; rsome (current, 'number) { set matched false; ck (current, ["0123456789"]) { mut matched true; { number.push(current) }; }; ck (current, '.') { if (dot) { done Float(number.parse().unwrap()); }; mut matched true; mut dot true; { number.push(current) }; }; {if !matched {break 'number}}; }; if (dot) { done Float(number.parse().unwrap()); }; done Int(number.parse().unwrap()); }; } ``` ## Parser and nodes ```rust #[derive(Debug)] pub enum Stmt { Expr(Node), Let(String, Node), } #[derive(Debug)] pub enum Expr { Plus(Node, Box>), Minus(Node, Box>), Factor(Node), } #[derive(Debug)] pub enum Factor { Mul(Node, Box>), Div(Node, Box>), Number(Node), } #[derive(Debug)] pub enum Number { Get(String), Neg(Box>), Expr(Box>), Int(u32), Float(f32), } #[derive(Debug)] pub enum ProgramFile { Single(Node), Program(Box<[Node]>), } flexar::parser! { [[Number] parxt: Token] parse { (Ident(x)) => (Get(x.clone())); (Plus), [number: Number::parse] => [number]; (Minus), [number: Number::parse] => (Neg(Box::new(number))); (Int(x)) => (Int(*x)); (Float(x)) => (Float(*x)); (LParen) => { [expr: Expr::parse] => { (RParen) => (Expr(Box::new(expr))); } (else Err(E007)) }; } else Err(E003:parxt.current_token()); } flexar::parser! { [[Factor] parxt: Token] parse { [left: Number::parse] => { (Mul), [right: Factor::parse] => (Mul(left, Box::new(right))); (Div), [right: Factor::parse] => (Div(left, Box::new(right))); } (else Ok(Factor::Number(left))) } else Other(Number Number::parse(parxt)); } flexar::parser! { [[Expr] parxt: Token] parse { [left: Factor::parse] => { (Plus), [right: Expr::parse] => (Plus(left, Box::new(right))); (Minus), [right: Expr::parse] => (Minus(left, Box::new(right))); } (else Ok(Expr::Factor(left))) } else Err(E004:parxt.current_token()); } flexar::parser! { [[Stmt] parxt: Token] parse { [expr: Expr::parse] => (Expr(expr)); (Let) => { (Ident(ident)) => { (EQ), [expr: Expr::parse] => (Let(ident.clone(), expr)); } (else Err(E009:parxt.current_token())) } (else Err(E008:parxt.current_token())) } else Err(E006:parxt.current_token()); } flexar::parser! { [[ProgramFile] parxt: Token] single { [stmt: Stmt::parse] => { (Semi) => (Single(stmt)); } (else Err(E010:parxt.current_token())) } else Err(E006:parxt.current_token()); } impl ProgramFile { pub fn parse(tokens: &[token_node::Token]) -> Option { if tokens.len() == 0 { return None } let mut parxt = Parxt::new(tokens); let mut stmts = Vec::new(); while parxt.current().is_some() { match Self::single(&mut parxt) { Ok(Node { node: Self::Single(x), .. }) => stmts.push(x), Err((_, x)) => x.throw(), _ => panic!("not possible"), } } Some(Self::Program(stmts.into_boxed_slice())) } } ``` ## Interpreter ```rust pub struct VisitCtx(HashMap, Position); pub trait Visit { fn visit(&self, visit_ctx: &mut VisitCtx) -> f32; } impl ProgramFile { pub fn visit(&self) { if let Self::Program(stmts) = self { let mut visit_ctx = VisitCtx(HashMap::new(), stmts[0].position.clone()); stmts.iter() .for_each(|x| {x.visit(&mut visit_ctx);}); } } } impl Visit for Node { fn visit(&self, visit_ctx: &mut VisitCtx) -> f32 { visit_ctx.1 = self.position.clone(); self.node.visit(visit_ctx) } } impl Visit for Number { fn visit(&self, visit_ctx: &mut VisitCtx) -> f32 { use Number as N; match self { N::Int(x) => *x as f32, N::Neg(x) => -x.visit(visit_ctx), N::Float(x) => *x, N::Get(x) => *visit_ctx.0.get(x).unwrap_or_else(|| compiler_error!((RT001, visit_ctx.1.clone()) x).throw() ), N::Expr(x) => x.visit(visit_ctx) } } } impl Visit for Factor { fn visit(&self, visit_ctx: &mut VisitCtx) -> f32 { use Factor as F; match self { F::Mul(a, b) => a.visit(visit_ctx) * b.visit(visit_ctx), F::Div(a, b) => a.visit(visit_ctx) / b.visit(visit_ctx), F::Number(x) => x.visit(visit_ctx), } } } impl Visit for Expr { fn visit(&self, visit_ctx: &mut VisitCtx) -> f32 { use Expr as E; match self { E::Factor(x) => x.visit(visit_ctx), E::Plus(a, b) => a.visit(visit_ctx) + b.visit(visit_ctx), E::Minus(a, b) => a.visit(visit_ctx) - b.visit(visit_ctx), } } } impl Visit for Stmt { fn visit(&self, visit_ctx: &mut VisitCtx) -> f32 { use Stmt as S; match self { S::Expr(x) => println!("{}", x.visit(visit_ctx)), S::Let(key, x) => { let value = x.visit(visit_ctx); visit_ctx.0.insert(key.clone(), value); }, } 0f32 // means nothing } } ``` ## Example main function to run the language ```rust fn main() { let contents = fs::read_to_string("example.fx").unwrap(); // Lexer let first_time = Instant::now(); let tokens = Token::tokenize(Lext::new("example.fx".into(), &contents)); print_time("Tokenising completed in", first_time); println!("{:?}", tokens.iter().map(|x| &x.token_type).collect::>()); // Parser let time = Instant::now(); let node = ProgramFile::parse(&tokens); print_time("Parsing completed in", time); let node = match node { Some(x) => x, None => return, }; // Interpreter let time = Instant::now(); println!("\n\x1b[34m=== Program output ===\x1b[0m"); node.visit(); println!("\x1b[34m=== Program output ===\x1b[0m\n"); print_time("Interpreting completed in", time); print_time("Full program finished in", time); } fn print_time(str: &str, time: Instant) { println!("\x1b[32m{str}: \x1b[33m{}s\x1b[0m", time.elapsed().as_secs_f64()); } ```