diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 37baf34..db7f61f 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -1,11 +1,20 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, +}; -use crate::parser::{Body, Expr, Function, Ident, Literal, ParserError, Statement, Unresolved}; +use crate::parser::{ + Body, Expr, FileSpan, Function, Ident, Literal, Module, ParserError, ParserErrors, Statement, +}; #[derive(Debug)] pub enum IRInstruction { - Li(IRIdent, Literal), - Mv(IRIdent, IRIdent), + Li(Var, Literal), + Mv(Var, Var), + Not(Var, Var), + Noti(Var, Literal), + La(Var, IRIdent), + Call(FnIdent, Vec), } #[derive(Debug)] @@ -13,116 +22,257 @@ pub struct IRFunction { instructions: Vec, } -impl Function { - pub fn lower(&self, functions: &mut Vec, map: &mut Namespace) -> Result<(), ParserError> { - let Ok(name) = &self.name.inner else { - return Ok(()); - }; - if map.get(name).is_some() { - Err(ParserError { - msg: format!("Already something named '{:?}'", self.name), - spans: vec![self.name.span], - }) - } else { - map.add(name); - let mut instructions = Vec::new(); - self.body.as_ref().map(|b| b.lower(map, &mut instructions)); - functions.push(IRFunction { instructions }); - Ok(()) +impl Module { + pub fn lower(&self, map: &mut Namespace, errors: &mut ParserErrors) { + for f in &self.functions { + if let Some(f) = f.as_ref() { + f.lower(map, errors); + } } } } -impl Body { - pub fn lower(&self, map: &Namespace, instructions: &mut Vec) { - let mut map = map.clone(); +impl Function { + pub fn lower(&self, map: &mut Namespace, errors: &mut ParserErrors) { + let Some(name) = self.name.as_ref() else { + return; + }; + if map.get(name).is_some() { + errors.add(ParserError { + msg: format!("Already something named '{:?}'", self.name), + spans: vec![self.name.span], + }); + } else { + let f = map.reserve_fn(self.name.span); + let mut instructions = Vec::new(); + if let Some(b) = self.body.as_ref() { + b.lower(map, &mut instructions, errors) + } + map.write_fn(name, f, IRFunction { instructions }); + } + } +} + +impl Body { + pub fn lower( + &self, + map: &mut Namespace, + instructions: &mut Vec, + errors: &mut ParserErrors, + ) { + let mut map = map.push(); for statement in &self.statements { - let Ok(statement) = &statement.inner else { + let Some(statement) = statement.as_ref() else { continue; }; match statement { - Statement::Let(name, expr) => { - let Ok(name) = &name.inner else { + Statement::Let(name_node, expr) => { + let Some(name) = name_node.as_ref() else { continue; }; - let name = map.add(name); - let Ok(expr) = &expr.inner else { + let Some(expr) = expr.as_ref() else { continue; }; - if let Ok(Some(res)) = expr.lower(&map, instructions) { + let res = expr.lower(&mut map, instructions, errors); + let name = map.add_var(name, name_node.span); + if let Some(res) = res { instructions.push(match res { ExprResult::Lit(l) => IRInstruction::Li(name, l), - ExprResult::Ident(i) => IRInstruction::Mv(name, i), + ExprResult::Var(i) => IRInstruction::Mv(name, i), + ExprResult::Fn(f) => todo!(), }); } } Statement::Return(e) => todo!(), - Statement::Expr(expr) => todo!(), + Statement::Expr(expr) => { + expr.as_ref().map(|e| e.lower(&mut map, instructions, errors)); + } } } } } -impl Expr { +impl Expr { pub fn lower( &self, - map: &Namespace, + map: &mut Namespace, instructions: &mut Vec, - ) -> Result, String> { - Ok(match self { - Expr::Lit(l) => { - let Ok(l) = &l.inner else {return Ok(None)}; - Some(ExprResult::Lit(l.clone())) - }, + errors: &mut ParserErrors, + ) -> Option { + match self { + Expr::Lit(l) => Some(ExprResult::Lit(l.as_ref()?.clone())), Expr::Ident(i) => { - let Ok(i) = &i.inner else {return Ok(None)}; - let Some(id) = map.get(i) else { - return Err(format!("Identifier '{:?}' not found", i)); + let Some(id) = map.get(i.as_ref()?) else { + errors.add(ParserError::identifier_not_found(i)); + return None; }; - Some(ExprResult::Ident(id)) + match id.ty() { + IdentTypeMatch::Var(var) => Some(ExprResult::Var(var)), + IdentTypeMatch::Fn(f) => Some(ExprResult::Fn(f)), + } } Expr::BinaryOp(_, _, _) => todo!(), - Expr::UnaryOp(_, _) => todo!(), + Expr::UnaryOp(op, e) => { + let res = e.as_ref()?.lower(&mut map, instructions, errors)?; + let res = match op { + crate::parser::UnaryOperator::Not => { + let temp = map.reserve_var(e.span); + match res { + ExprResult::Lit(l) => instructions.push(IRInstruction::Noti(temp, l)), + ExprResult::Var(i) => instructions.push(IRInstruction::Not(temp, i)), + ExprResult::Fn(_) => { + errors.add(ParserError::from_span( + e.span, + "Cannot call not on a function".to_string(), + )); + return None; + } + } + temp + } + crate::parser::UnaryOperator::Ref => todo!(), + }; + Some(ExprResult::Var(res)) + } Expr::Block(_) => todo!(), - Expr::Call(_, _) => todo!(), - Expr::Group(_) => todo!(), - }) + Expr::Call(e, args) => { + let e = e.as_ref()?.lower(&mut map, instructions, errors); + let args = args.iter().map(|a| a.as_ref()?.lower(map, instructions, errors)).collect(); + if let Some(r) = e { + let fun = match r { + ExprResult::Lit(literal) => todo!(), + ExprResult::Var(var) => todo!(), + ExprResult::Fn(f) => { + instructions.push(IRInstruction::Call(f, args)); + }, + }; + } else { + todo!(); + } + }, + Expr::Group(e) => e.as_ref()?.lower(&mut map, instructions, errors), + } } } -enum ExprResult { +pub enum ExprResult { Lit(Literal), - Ident(IRIdent), + Var(Var), + Fn(FnIdent), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Namespace { - pub cur: usize, - pub map: HashMap, + pub fns: Vec>, + pub vars: usize, + pub stack: Vec>, } impl Namespace { pub fn new() -> Self { Self { - cur: 0, - map: HashMap::new(), + fns: Vec::new(), + vars: 0, + stack: vec![HashMap::new()], } } + pub fn push(&mut self) -> NamespaceGuard { + self.stack.push(HashMap::new()); + NamespaceGuard(self) + } pub fn get(&self, name: &Ident) -> Option { - self.map.get(name.val()).copied() + for map in self.stack.iter().rev() { + let res = map.get(name.val()); + if res.is_some() { + return res.copied(); + } + } + None } - pub fn reserve(&mut self) -> IRIdent { - let id = IRIdent ( self.cur ); - self.cur += 1; + pub fn reserve_var(&mut self, origin: FileSpan) -> Var { + let i = self.vars; + self.vars += 1; + Var(IRIdent { + origin, + ty: IdentType::Var, + i, + }) + } + pub fn reserve_fn(&mut self, origin: FileSpan) -> FnIdent { + let i = self.fns.len(); + self.fns.push(None); + FnIdent(IRIdent { + origin, + ty: IdentType::Fn, + i, + }) + } + pub fn write_fn(&mut self, name: &Ident, id: FnIdent, f: IRFunction) -> IRIdent { + self.insert(name, id.0); + self.fns[id.0.i] = Some(f); + id.0 + } + pub fn add_var(&mut self, name: &Ident, origin: FileSpan) -> Var { + let id = self.reserve_var(origin); + self.insert(name, id.0); id } - pub fn add(&mut self, name: &Ident) -> IRIdent { - let id = self.reserve(); - self.map.insert(name.val().to_string(), id); - id + fn insert(&mut self, name: &Ident, id: IRIdent) { + let last = self.stack.len() - 1; + self.stack[last].insert(name.val().to_string(), id); + } +} + +pub struct NamespaceGuard<'a>(&'a mut Namespace); + +impl Drop for NamespaceGuard<'_> { + fn drop(&mut self) { + self.0.stack.pop(); + } +} + +impl Deref for NamespaceGuard<'_> { + type Target = Namespace; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for NamespaceGuard<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } #[derive(Debug, Clone, Copy)] -pub struct IRIdent (usize); +pub struct IRIdent { + origin: FileSpan, + ty: IdentType, + i: usize, +} +#[derive(Debug, Clone, Copy)] +pub struct FnIdent(IRIdent); +#[derive(Debug, Clone, Copy)] +pub struct Var(IRIdent); + +#[derive(Debug, Clone, Copy)] +pub enum IdentType { + Var, + Fn, +} + +#[derive(Debug, Clone, Copy)] +pub enum IdentTypeMatch { + Var(Var), + Fn(FnIdent), +} + +impl IRIdent { + pub fn ty(self) -> IdentTypeMatch { + match self.ty { + IdentType::Var => IdentTypeMatch::Var(Var(self)), + IdentType::Fn => IdentTypeMatch::Fn(FnIdent(self)), + } + } +} diff --git a/src/main.rs b/src/main.rs index 01cfa51..6c15b86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,13 @@ mod ir; mod parser; fn main() { - parser::main(); + let arg = std::env::args_os().nth(1); + if let Some(path) = arg { + let file = std::fs::read_to_string(path).expect("failed to read file"); + println!("{file}"); + parser::parse_file(&file); + } else { + parser::run_stdin(); + } // compiler::main(); } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 85305a5..65b5018 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,16 +1,5 @@ mod v1; mod v2; +mod v3; -pub use v1::*; - -pub fn main() { - let arg = std::env::args_os().nth(1); - if let Some(path) = arg { - let file = std::fs::read_to_string(path).expect("failed to read file"); - println!("{file}"); - v1::parse_file(&file); - // v2::parse_file(&file); - } else { - v1::run_stdin(); - } -} +pub use v3::*; diff --git a/src/parser/v3/cursor.rs b/src/parser/v3/cursor.rs new file mode 100644 index 0000000..70bf0d9 --- /dev/null +++ b/src/parser/v3/cursor.rs @@ -0,0 +1,91 @@ +use super::error::ParserError; +use super::token::{CharCursor, Keyword, Symbol, Token, TokenInstance}; +use super::FilePos; + +pub struct TokenCursor<'a> { + cursor: CharCursor<'a>, + next: Option, + next_pos: FilePos, + prev_end: FilePos, +} + +impl<'a> TokenCursor<'a> { + pub fn next(&mut self) -> Option { + self.prev_end = self.cursor.prev_pos(); + self.next_pos = self.cursor.next_pos(); + std::mem::replace(&mut self.next, TokenInstance::parse(&mut self.cursor)) + } + pub fn expect_next(&mut self) -> Result { + self.peek().ok_or(ParserError::unexpected_end())?; + Ok(self.next().unwrap()) + } + pub fn expect_token(&mut self, t: Token) -> Result<(), ParserError> { + let next = self.expect_next()?; + if t == next.token { + Ok(()) + } else { + Err(ParserError::unexpected_token(&next, &format!("{t:?}"))) + } + } + pub fn expect_sym(&mut self, symbol: Symbol) -> Result<(), ParserError> { + self.expect_token(Token::Symbol(symbol)) + } + pub fn seek_sym(&mut self, symbol: Symbol) { + while self + .next() + .is_some_and(|n| n.token != Token::Symbol(symbol)) + {} + } + pub fn seek_syms(&mut self, syms: &[Symbol]) { + while self + .peek() + .is_some_and(|n| !syms.iter().any(|s| n.is_symbol(*s))) + { + self.next(); + } + } + pub fn seek(&mut self, f: impl Fn(&TokenInstance) -> bool) -> Option<&TokenInstance> { + loop { + if f(self.peek()?) { + return self.peek(); + } + self.next(); + } + } + pub fn expect_kw(&mut self, kw: Keyword) -> Result<(), ParserError> { + self.expect_token(Token::Keyword(kw)) + } + pub fn peek(&self) -> Option<&TokenInstance> { + self.next.as_ref() + } + pub fn expect_peek(&mut self) -> Result<&TokenInstance, ParserError> { + self.peek().ok_or(ParserError::unexpected_end()) + } + pub fn chars(&mut self) -> &mut CharCursor<'a> { + &mut self.cursor + } + pub fn prev_end(&self) -> FilePos { + self.prev_end + } + pub fn next_pos(&self) -> FilePos { + self.next_pos + } +} + +impl<'a> From<&'a str> for TokenCursor<'a> { + fn from(string: &'a str) -> Self { + Self::from(CharCursor::from(string)) + } +} + +impl<'a> From> for TokenCursor<'a> { + fn from(mut cursor: CharCursor<'a>) -> Self { + let cur = TokenInstance::parse(&mut cursor); + Self { + cursor, + next: cur, + next_pos: FilePos::start(), + prev_end: FilePos::start(), + } + } +} diff --git a/src/parser/v3/error.rs b/src/parser/v3/error.rs new file mode 100644 index 0000000..1057903 --- /dev/null +++ b/src/parser/v3/error.rs @@ -0,0 +1,74 @@ +use super::{ + token::{FileSpan, TokenInstance}, + FilePos, Ident, Node, +}; + +#[derive(Debug, Clone)] +pub struct ParserError { + pub msg: String, + pub spans: Vec, +} + +pub struct ParserErrors { + pub errs: Vec, +} + +impl ParserError { + pub fn from_instances(instances: &[&TokenInstance], msg: String) -> Self { + ParserError { + msg, + spans: instances.iter().map(|i| i.span).collect(), + } + } + pub fn from_msg(msg: String) -> Self { + Self { + msg, + spans: Vec::new(), + } + } + pub fn from_span(span: FileSpan, msg: String) -> Self { + Self { + msg, + spans: vec![span], + } + } + pub fn identifier_not_found(id: &Node) -> Self { + Self { + msg: format!("Identifier '{}' not found", id.as_ref().unwrap().val()), + spans: vec![id.span], + } + } + pub fn at(pos: FilePos, msg: String) -> Self { + Self { + msg, + spans: vec![FileSpan::at(pos)], + } + } + pub fn unexpected_end() -> Self { + Self::from_msg("unexpected end of input".to_string()) + } + pub fn unexpected_token(inst: &TokenInstance, expected: &str) -> Self { + let t = &inst.token; + ParserError::from_instances( + &[inst], + format!("unexpected token {t:?}; expected {expected}"), + ) + } + pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> { + let after = if self.spans.is_empty() { "" } else { ":" }; + writeln!(writer, "error: {}{}", self.msg, after)?; + for span in &self.spans { + span.write_for(writer, file)?; + } + Ok(()) + } +} + +impl ParserErrors { + pub fn new() -> Self { + Self { errs: Vec::new() } + } + pub fn add(&mut self, err: ParserError) { + self.errs.push(err); + } +} diff --git a/src/parser/v3/mod.rs b/src/parser/v3/mod.rs new file mode 100644 index 0000000..ad94c87 --- /dev/null +++ b/src/parser/v3/mod.rs @@ -0,0 +1,51 @@ +use std::io::{stdout, BufRead, BufReader}; + +mod cursor; +mod error; +mod node; +mod nodes; +mod parse; +mod token; + +pub use cursor::*; +pub use error::*; +pub use node::*; +pub use nodes::*; +pub use parse::*; +pub use token::*; + +use crate::ir::Namespace; + +pub fn parse_file(file: &str) { + let mut errors = ParserErrors::new(); + let res = Module::parse_node(&mut TokenCursor::from(file), &mut errors); + println!("{:?}", res.node); + let out = &mut stdout(); + if let Some(module) = res.node.as_ref() { + let mut namespace = Namespace::new(); + module.lower(&mut namespace, &mut errors); + println!("{:?}", namespace); + } + for err in errors.errs { + err.write_for(out, file).unwrap(); + } +} + +pub fn run_stdin() { + for line in BufReader::new(std::io::stdin()).lines() { + let mut errors = ParserErrors::new(); + let str = &line.expect("failed to read line"); + let mut cursor = TokenCursor::from(&str[..]); + if let Some(expr) = Statement::parse_node(&mut cursor, &mut errors).node.as_ref() { + if cursor.next().is_none() { + println!("{:?}", expr); + } else { + println!("uhhhh ehehe"); + } + } + let out = &mut stdout(); + for err in errors.errs { + err.write_for(out, str).unwrap(); + } + } +} diff --git a/src/parser/v3/node.rs b/src/parser/v3/node.rs new file mode 100644 index 0000000..39b6c88 --- /dev/null +++ b/src/parser/v3/node.rs @@ -0,0 +1,49 @@ +use std::{ + fmt::Debug, + ops::{Deref, DerefMut}, +}; + +use super::FileSpan; + +pub struct Node { + pub inner: Option, + pub span: FileSpan, +} + +impl Node { + pub fn new(inner: T, span: FileSpan) -> Self { + Self { + inner: Some(inner), + span, + } + } + pub fn bx(self) -> Node> { + Node { + inner: self.inner.map(|v| Box::new(v)), + span: self.span, + } + } +} + +impl Deref for Node { + type Target = Option; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Node { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl Debug for Node { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.inner { + Some(v) => v.fmt(f), + None => f.write_str("{error}"), + } + } +} + diff --git a/src/parser/v3/nodes/body.rs b/src/parser/v3/nodes/body.rs new file mode 100644 index 0000000..d9ca192 --- /dev/null +++ b/src/parser/v3/nodes/body.rs @@ -0,0 +1,73 @@ +use std::fmt::{Debug, Write}; + +use super::{ + token::Symbol, Node, NodeParsable, Parsable, ParseResult, ParserError, + ParserErrors, Statement, TokenCursor, +}; +use crate::util::Padder; + +pub struct Body { + pub statements: Vec>, +} + +impl Parsable for Body { + fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult { + let mut statements = Vec::new(); + cursor.expect_sym(Symbol::OpenCurly)?; + if cursor.expect_peek()?.is_symbol(Symbol::CloseCurly) { + cursor.next(); + return ParseResult::Ok(Self { statements }); + } + let mut expect_semi = false; + let mut recover = false; + loop { + let Some(next) = cursor.peek() else { + recover = true; + errors.add(ParserError::unexpected_end()); + break; + }; + if next.is_symbol(Symbol::CloseCurly) { + cursor.next(); + break; + } + if next.is_symbol(Symbol::Semicolon) { + cursor.next(); + expect_semi = false; + continue; + } else if expect_semi { + errors.add(ParserError { + msg: "expected ';'".to_string(), + spans: vec![cursor.next_pos().char_span()], + }); + } + let res = Statement::parse_node(cursor, errors); + statements.push(res.node); + expect_semi = true; + if res.recover { + cursor.seek_syms(&[Symbol::Semicolon, Symbol::CloseCurly]); + if cursor.peek().is_none() { + recover = true; + break; + } + } + } + ParseResult::from_recover(Self { statements }, recover) + } +} + +impl Debug for Body { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.statements.first().is_some() { + f.write_str("{\n ")?; + let mut padder = Padder::new(f); + for s in &self.statements { + // they don't expose wrap_buf :grief: + padder.write_str(&format!("{s:?}\n"))?; + } + f.write_char('}')?; + } else { + f.write_str("{}")?; + } + Ok(()) + } +} diff --git a/src/parser/v3/nodes/expr.rs b/src/parser/v3/nodes/expr.rs new file mode 100644 index 0000000..a304cf3 --- /dev/null +++ b/src/parser/v3/nodes/expr.rs @@ -0,0 +1,156 @@ +use std::fmt::{Debug, Write}; + +use super::{ + BinaryOperator, Body, Ident, Literal, Node, NodeParsable, Parsable, ParseResult, ParserError, + ParserErrors, Symbol, TokenCursor, UnaryOperator, +}; + +type BoxNode = Node>; + +pub enum Expr { + Lit(Node), + Ident(Node), + BinaryOp(BinaryOperator, BoxNode, BoxNode), + UnaryOp(UnaryOperator, BoxNode), + Block(Node), + Call(BoxNode, Vec>), + Group(BoxNode), +} + +impl Parsable for Expr { + fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult { + let start = cursor.next_pos(); + let next = cursor.expect_peek()?; + let mut e1 = if next.is_symbol(Symbol::OpenParen) { + cursor.next(); + if cursor.expect_peek()?.is_symbol(Symbol::CloseParen) { + cursor.next(); + return ParseResult::Ok(Expr::Lit(Node::new( + Literal::Unit, + cursor.next_pos().char_span(), + ))); + } + let res = Node::parse(cursor, errors); + if res.recover { + cursor.seek_sym(Symbol::CloseParen); + } + cursor.expect_sym(Symbol::CloseParen)?; + Self::Group(res.node.bx()) + } else if next.is_symbol(Symbol::OpenCurly) { + Self::Block(Body::parse_node(cursor, errors)?) + } else if let Some(op) = UnaryOperator::from_token(next) { + cursor.next(); + return Node::parse(cursor, errors).map(|n| { + let n = n.bx(); + if let Some(box Self::BinaryOp(op2, n1, n2)) = n.inner { + let span = start.to(n1.span.end); + Self::BinaryOp(op2, Node::new(Self::UnaryOp(op, n1), span).bx(), n2) + } else { + Self::UnaryOp(op, n) + } + }); + } else if let Some(val) = Node::maybe_parse(cursor, errors) { + Self::Lit(val) + } else { + let res = Node::parse(cursor, &mut ParserErrors::new()); + if res.node.is_some() { + Self::Ident(res.node) + } else { + let next = cursor.expect_peek()?; + return ParseResult::Err(ParserError::unexpected_token(next, "an expression")); + } + }; + let Some(mut next) = cursor.peek() else { + return ParseResult::Ok(e1); + }; + while next.is_symbol(Symbol::OpenParen) { + cursor.next(); + let mut args = Vec::new(); + loop { + let next = cursor.expect_peek()?; + if next.is_symbol(Symbol::CloseParen) { + break; + } + let res = Node::::parse(cursor, errors); + args.push(res.node); + if res.recover { + cursor.seek_syms(&[Symbol::CloseParen, Symbol::Comma]); + } + let next = cursor.expect_peek()?; + if !next.is_symbol(Symbol::Comma) { + break; + } + cursor.next(); + } + cursor.expect_sym(Symbol::CloseParen)?; + let end = cursor.prev_end(); + e1 = Self::Call(Node::new(Box::new(e1), start.to(end)), args); + let Some(next2) = cursor.peek() else { + return ParseResult::Ok(e1); + }; + next = next2 + } + let end = cursor.prev_end(); + let mut recover = false; + let res = if let Some(mut op) = BinaryOperator::from_token(&next.token) { + cursor.next(); + let mut n1 = Node::new(e1, start.to(end)).bx(); + let res = Node::parse(cursor, errors); + let mut n2 = res.node.bx(); + recover = res.recover; + if let Some(box Self::BinaryOp(op2, _, _)) = n2.as_ref() { + if op.presedence() > op2.presedence() { + let Some(box Self::BinaryOp(op2, n21, n22)) = n2.inner else { + unreachable!(); + }; + let end = n21.span.end; + n1 = Node::new(Self::BinaryOp(op, n1, n21), start.to(end)).bx(); + op = op2; + n2 = n22; + } + } + Self::BinaryOp(op, n1, n2) + } else { + e1 + }; + ParseResult::from_recover(res, recover) + } +} + +impl Debug for Expr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Expr::Lit(c) => c.fmt(f)?, + Expr::Ident(n) => n.fmt(f)?, + Expr::Block(b) => b.fmt(f)?, + Expr::BinaryOp(op, e1, e2) => { + write!(f, "({:?}", *e1)?; + if op.pad() { + write!(f, " {} ", op.str())?; + } else { + write!(f, "{}", op.str())?; + } + write!(f, "{:?})", *e2)?; + } + Expr::Call(n, args) => { + n.fmt(f)?; + f.write_char('(')?; + if let Some(a) = args.first() { + a.fmt(f)?; + } + for arg in args.iter().skip(1) { + f.write_str(", ")?; + arg.fmt(f)?; + } + f.write_char(')')?; + } + Expr::UnaryOp(op, e) => { + write!(f, "(")?; + write!(f, "{}", op.str())?; + write!(f, "{:?})", *e)?; + } + Expr::Group(inner) => inner.fmt(f)?, + } + Ok(()) + } +} diff --git a/src/parser/v3/nodes/func.rs b/src/parser/v3/nodes/func.rs new file mode 100644 index 0000000..23b5248 --- /dev/null +++ b/src/parser/v3/nodes/func.rs @@ -0,0 +1,28 @@ +use super::{Body, Ident, Keyword, Node, Parsable, ParseResult, ParserErrors, Symbol, TokenCursor}; +use std::fmt::Debug; + +pub struct Function { + pub name: Node, + pub body: Node, +} + +impl Parsable for Function { + fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult { + cursor.expect_kw(Keyword::Fn)?; + let name = Node::parse(cursor, errors)?; + cursor.expect_sym(Symbol::OpenParen)?; + cursor.expect_sym(Symbol::CloseParen)?; + Node::parse(cursor, errors).map(|body| Self { name, body }) + } +} + +impl Debug for Function { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("fn ")?; + self.name.fmt(f)?; + f.write_str("() ")?; + self.body.fmt(f)?; + Ok(()) + } +} + diff --git a/src/parser/v3/nodes/ident.rs b/src/parser/v3/nodes/ident.rs new file mode 100644 index 0000000..0c6f941 --- /dev/null +++ b/src/parser/v3/nodes/ident.rs @@ -0,0 +1,29 @@ +use std::fmt::Debug; +use super::{Parsable, ParseResult, ParserError, Token}; + +pub struct Ident(String); + +impl Ident { + pub fn val(&self) -> &String { + &self.0 + } +} + +impl Parsable for Ident { + fn parse(cursor: &mut super::TokenCursor, errors: &mut super::ParserErrors) -> ParseResult { + let next = cursor.expect_peek()?; + let Token::Ident(name) = &next.token else { + return ParseResult::Err(ParserError::unexpected_token(next, "an identifier")); + }; + let name = name.to_string(); + cursor.next(); + ParseResult::Ok(Self(name)) + } +} + +impl Debug for Ident { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + diff --git a/src/parser/v3/nodes/lit.rs b/src/parser/v3/nodes/lit.rs new file mode 100644 index 0000000..b80679c --- /dev/null +++ b/src/parser/v3/nodes/lit.rs @@ -0,0 +1,114 @@ +use super::{ + CharCursor, MaybeParsable, ParserError, ParserErrors, Symbol, Token, TokenCursor, +}; +use std::fmt::Debug; + +#[derive(Clone, PartialEq, Eq)] +pub enum Literal { + String(String), + Char(char), + Number(Number), + Unit, +} + +#[derive(Clone, PartialEq, Eq)] +pub struct Number { + pub whole: String, + pub decimal: Option, + pub ty: Option, +} + +impl MaybeParsable for Literal { + fn maybe_parse( + cursor: &mut TokenCursor, + _: &mut ParserErrors, + ) -> Result, ParserError> { + let inst = cursor.expect_peek()?; + let mut res = match &inst.token { + Token::Symbol(Symbol::SingleQuote) => { + let chars = cursor.chars(); + let c = chars.expect_next()?; + chars.expect('\'')?; + Self::Char(c) + } + Token::Symbol(Symbol::DoubleQuote) => Self::String(string_from(cursor.chars())?), + Token::Ident(text) => { + let first = text.chars().next().unwrap(); + if first.is_ascii_digit() { + Self::Number(Number { + whole: text.to_string(), + decimal: None, + ty: None, + }) + } else { + return Ok(None); + } + } + _ => return Ok(None), + }; + cursor.next(); + if let (Some(next), Self::Number(num)) = (cursor.peek(), &mut res) { + if next.token.is_symbol(Symbol::Dot) { + cursor.next(); + if let Some(next) = cursor.peek() { + if let Token::Ident(i) = &next.token { + if i.chars().next().unwrap().is_ascii_digit() { + num.decimal = Some(i.to_string()); + cursor.next(); + } + } + } + } + } + Ok(Some(res)) + } +} +pub fn string_from(cursor: &mut CharCursor) -> Result { + let mut str = String::new(); + loop { + let c = cursor.expect_next()?; + if c == '"' { + return Ok(str); + } + str.push(match c { + '\\' => { + let next = cursor.expect_next()?; + match next { + '"' => '"', + '\'' => '\'', + 't' => '\t', + 'n' => '\n', + '0' => '\0', + _ => { + todo!(); + } + } + } + _ => c, + }) + } +} + +impl Debug for Literal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::String(str) => str.fmt(f), + Self::Char(c) => c.fmt(f), + Self::Number(n) => n.fmt(f), + Self::Unit => f.write_str("()"), + } + } +} + +impl Debug for Number { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.whole)?; + if let Some(d) = &self.decimal { + write!(f, ".{}", d)?; + } + if let Some(ty) = &self.ty { + write!(f, "T{}", ty)?; + } + Ok(()) + } +} diff --git a/src/parser/v3/nodes/mod.rs b/src/parser/v3/nodes/mod.rs new file mode 100644 index 0000000..365b9a4 --- /dev/null +++ b/src/parser/v3/nodes/mod.rs @@ -0,0 +1,19 @@ +mod body; +mod expr; +mod func; +mod module; +mod op; +mod statement; +mod lit; +mod ident; + +pub use body::*; +pub use expr::*; +pub use func::*; +pub use module::*; +pub use op::*; +pub use statement::*; +pub use lit::*; +pub use ident::*; + +use super::*; diff --git a/src/parser/v3/nodes/module.rs b/src/parser/v3/nodes/module.rs new file mode 100644 index 0000000..ab3fd64 --- /dev/null +++ b/src/parser/v3/nodes/module.rs @@ -0,0 +1,35 @@ +use super::{ + Function, Keyword, Node, Parsable, ParseResult, ParserError, ParserErrors, TokenCursor, +}; +use std::fmt::Debug; + +pub struct Module { + pub functions: Vec>, +} + +impl Parsable for Module { + fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult { + let mut functions = Vec::new(); + loop { + let Some(next) = cursor.peek() else { + return ParseResult::Ok(Self { functions }); + }; + if next.is_keyword(Keyword::Fn) { + let res = Node::parse(cursor, errors); + functions.push(res.node); + if res.recover { + return ParseResult::Recover(Self { functions }); + } + } else { + errors.add(ParserError::unexpected_token(next, "fn")); + cursor.next(); + } + } + } +} + +impl Debug for Module { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.functions.fmt(f) + } +} diff --git a/src/parser/v3/nodes/op.rs b/src/parser/v3/nodes/op.rs new file mode 100644 index 0000000..e066530 --- /dev/null +++ b/src/parser/v3/nodes/op.rs @@ -0,0 +1,96 @@ +use super::{Symbol, Token}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum BinaryOperator { + Add, + Sub, + Mul, + Div, + LessThan, + GreaterThan, + Access, + Assign, +} + +impl BinaryOperator { + pub fn presedence(&self) -> u32 { + match self { + Self::Assign => 0, + Self::LessThan => 1, + Self::GreaterThan => 1, + Self::Add => 2, + Self::Sub => 3, + Self::Mul => 4, + Self::Div => 5, + Self::Access => 6, + } + } + pub fn str(&self) -> &str { + match self { + Self::Add => "+", + Self::Sub => "-", + Self::Mul => "*", + Self::Div => "/", + Self::LessThan => "<", + Self::GreaterThan => ">", + Self::Access => ".", + Self::Assign => "=", + } + } + pub fn from_token(token: &Token) -> Option { + let Token::Symbol(symbol) = token else { + return None; + }; + Some(match symbol { + Symbol::OpenAngle => Self::LessThan, + Symbol::CloseAngle => Self::GreaterThan, + Symbol::Plus => Self::Add, + Symbol::Minus => Self::Sub, + Symbol::Asterisk => Self::Mul, + Symbol::Slash => Self::Div, + Symbol::Dot => Self::Access, + Symbol::Equals => Self::Assign, + _ => { + return None; + } + }) + } + pub fn pad(&self) -> bool { + match self { + Self::Add => true, + Self::Sub => true, + Self::Mul => true, + Self::Div => true, + Self::LessThan => true, + Self::GreaterThan => true, + Self::Access => false, + Self::Assign => true, + } + } +} + +pub enum UnaryOperator { + Not, + Ref, +} + +impl UnaryOperator { + pub fn str(&self) -> &str { + match self { + Self::Not => "!", + Self::Ref => "&", + } + } + pub fn from_token(token: &Token) -> Option { + let Token::Symbol(symbol) = token else { + return None; + }; + Some(match symbol { + Symbol::Ampersand => Self::Ref, + Symbol::Bang => Self::Not, + _ => { + return None; + } + }) + } +} diff --git a/src/parser/v3/nodes/statement.rs b/src/parser/v3/nodes/statement.rs new file mode 100644 index 0000000..fa0827b --- /dev/null +++ b/src/parser/v3/nodes/statement.rs @@ -0,0 +1,53 @@ +use super::{ + Expr, Ident, Keyword, Node, Parsable, ParseResult, ParserErrors, Symbol, Token, TokenCursor, +}; +use std::fmt::{Debug, Write}; + +pub enum Statement { + Let(Node, Node), + Return(Node), + Expr(Node), +} + +impl Parsable for Statement { + fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult { + let next = cursor.expect_peek()?; + match next.token { + Token::Keyword(Keyword::Let) => { + cursor.next(); + let name = Node::parse(cursor, errors)?; + cursor.expect_sym(Symbol::Equals)?; + Node::parse(cursor, errors).map(|expr| Self::Let(name, expr)) + } + Token::Keyword(Keyword::Return) => { + cursor.next(); + Node::parse(cursor, errors).map(Self::Return) + } + _ => Node::parse(cursor, errors).map(Self::Expr), + } + } +} + +impl Debug for Statement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Statement::Let(n, e) => { + f.write_str("let ")?; + n.fmt(f); + f.write_str(" = ")?; + e.fmt(f)?; + f.write_char(';')?; + } + Statement::Return(e) => { + f.write_str("return ")?; + e.fmt(f)?; + f.write_char(';')?; + } + Statement::Expr(e) => { + e.fmt(f)?; + f.write_char(';')?; + } + } + Ok(()) + } +} diff --git a/src/parser/v3/parse.rs b/src/parser/v3/parse.rs new file mode 100644 index 0000000..e5a58ff --- /dev/null +++ b/src/parser/v3/parse.rs @@ -0,0 +1,178 @@ +use std::{ + convert::Infallible, + ops::{ControlFlow, FromResidual, Try}, +}; + +use super::{Node, ParserError, ParserErrors, TokenCursor}; + +pub enum ParseResult { + Ok(T), + Recover(T), + Err(ParserError), + SubErr, +} + +impl ParseResult { + pub fn from_recover(data: T, recover: bool) -> Self { + if recover { + Self::Recover(data) + } else { + Self::Ok(data) + } + } +} + +impl Try for ParseResult { + type Output = Result; + type Residual = Option; + fn from_output(output: Self::Output) -> Self { + match output { + Ok(v) => Self::Ok(v), + Err(v) => Self::Recover(v), + } + } + fn branch(self) -> ControlFlow { + match self { + ParseResult::Ok(v) => ControlFlow::Continue(Ok(v)), + ParseResult::Recover(v) => ControlFlow::Continue(Err(v)), + ParseResult::Err(e) => ControlFlow::Break(Some(e)), + ParseResult::SubErr => ControlFlow::Break(None), + } + } +} + +impl FromResidual for ParseResult { + fn from_residual(residual: ::Residual) -> Self { + match residual { + Some(err) => Self::Err(err), + None => Self::SubErr, + } + } +} + +impl FromResidual> for ParseResult { + fn from_residual(residual: Result) -> Self { + match residual { + Err(e) => Self::Err(e), + } + } +} + +impl FromResidual> for ParseResult { + fn from_residual(residual: ParseResult) -> Self { + match residual { + ParseResult::Err(e) => Self::Err(e), + ParseResult::SubErr => Self::SubErr, + _ => unreachable!(), + } + } +} + +pub struct NodeParseResult { + pub node: Node, + pub recover: bool, +} + +impl NodeParseResult { + pub fn map) -> U, U>(self, op: F) -> ParseResult { + let res = op(self.node); + if self.recover { + ParseResult::Recover(res) + } else { + ParseResult::Ok(res) + } + } +} + +impl Try for NodeParseResult { + type Output = Node; + type Residual = ParseResult; + + fn from_output(output: Self::Output) -> Self { + Self { + node: output, + recover: false, + } + } + + fn branch(self) -> ControlFlow { + if self.recover { + ControlFlow::Break(ParseResult::SubErr) + } else { + ControlFlow::Continue(self.node) + } + } +} + +impl FromResidual for NodeParseResult { + fn from_residual(_: ::Residual) -> Self { + // I hope this is unreachable ??? + unreachable!() + } +} + +pub trait Parsable: Sized { + fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> ParseResult; +} + +pub trait MaybeParsable: Sized { + fn maybe_parse( + cursor: &mut TokenCursor, + errors: &mut ParserErrors, + ) -> Result, ParserError>; +} + +impl Node { + pub fn parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult { + let start = cursor.next_pos(); + let (inner, recover) = match T::parse(cursor, errors) { + ParseResult::Ok(v) => (Some(v), false), + ParseResult::Recover(v) => (Some(v), true), + ParseResult::Err(e) => { + errors.add(e); + (None, true) + } + ParseResult::SubErr => (None, true), + }; + let end = cursor.prev_end(); + NodeParseResult { + node: Self { + inner, + span: start.to(end), + }, + recover, + } + } +} + +impl Node { + pub fn maybe_parse(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> Option { + let start = cursor.next_pos(); + let inner = match T::maybe_parse(cursor, errors) { + Ok(v) => Some(v?), + Err(e) => { + errors.add(e); + None + } + }; + let end = cursor.prev_end(); + Some(Self { + inner, + span: start.to(end), + }) + } +} + +pub trait NodeParsable { + fn parse_node(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult + where + Self: Sized; +} +impl NodeParsable for T { + fn parse_node(cursor: &mut TokenCursor, errors: &mut ParserErrors) -> NodeParseResult + where + Self: Sized, + { + Node::::parse(cursor, errors) + } +} diff --git a/src/parser/v3/token/cursor.rs b/src/parser/v3/token/cursor.rs new file mode 100644 index 0000000..02796f6 --- /dev/null +++ b/src/parser/v3/token/cursor.rs @@ -0,0 +1,68 @@ +use std::{iter::Peekable, str::Chars}; + +use super::super::ParserError; +use super::FilePos; + +pub struct CharCursor<'a> { + chars: Peekable>, + next_pos: FilePos, + prev_pos: FilePos, +} + +impl CharCursor<'_> { + pub fn next(&mut self) -> Option { + let res = self.peek()?; + self.advance(); + Some(res) + } + pub fn expect(&mut self, c: char) -> Result<(), ParserError> { + let next = self.expect_next()?; + if next == c { + Ok(()) + } else { + Err(ParserError::at( + self.prev_pos, + format!("unexpected char '{next}'; expected '{c}'"), + )) + } + } + pub fn skip_whitespace(&mut self) { + while self.peek().is_some_and(|c| c.is_whitespace()) { + self.advance(); + } + } + pub fn peek(&mut self) -> Option { + self.chars.peek().copied() + } + pub fn advance(&mut self) { + let Some(next) = self.chars.next() else { + return; + }; + self.prev_pos = self.next_pos; + if next == '\n' { + self.next_pos.col = 0; + self.next_pos.line += 1; + } else { + self.next_pos.col += 1; + } + } + pub fn expect_next(&mut self) -> Result { + self.next().ok_or(ParserError::unexpected_end()) + } + pub fn next_pos(&self) -> FilePos { + self.next_pos + } + pub fn prev_pos(&self) -> FilePos { + self.prev_pos + } +} + +impl<'a> From<&'a str> for CharCursor<'a> { + fn from(value: &'a str) -> Self { + Self { + chars: value.chars().peekable(), + next_pos: FilePos::start(), + prev_pos: FilePos::start(), + } + } +} diff --git a/src/parser/v3/token/file.rs b/src/parser/v3/token/file.rs new file mode 100644 index 0000000..6402f4a --- /dev/null +++ b/src/parser/v3/token/file.rs @@ -0,0 +1,80 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct FilePos { + pub line: usize, + pub col: usize, +} + +#[derive(Debug, Clone, Copy)] +pub struct FileSpan { + pub start: FilePos, + pub end: FilePos, +} + +impl FilePos { + pub fn start() -> Self { + Self { line: 0, col: 0 } + } +} + +impl FilePos { + pub fn to(self, end: FilePos) -> FileSpan { + FileSpan { start: self, end } + } + pub fn char_span(self) -> FileSpan { + FileSpan::at(self) + } +} + +const BEFORE: usize = 1; +const AFTER: usize = 0; + +impl FileSpan { + pub fn at(pos: FilePos) -> Self { + Self { + start: pos, + end: pos, + } + } + pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> { + let start = self.start.line.saturating_sub(BEFORE); + let num_before = self.start.line - start; + let mut lines = file.lines().skip(start); + let width = format!("{}", self.end.line + AFTER).len(); + let same_line = self.start.line == self.end.line; + for i in 0..num_before { + writeln!(writer, "{:>width$} | {}", start + i, lines.next().unwrap())?; + } + let line = lines.next().unwrap(); + writeln!(writer, "{:>width$} | {}", self.start.line, line)?; + let len = if same_line { + self.end.col - self.start.col + 1 + } else { + line.len() - self.start.col + }; + writeln!( + writer, + "{} | {}", + " ".repeat(width), + " ".repeat(self.start.col) + &"^".repeat(len) + )?; + if !same_line { + for _ in 0..self.end.line - self.start.line - 1 { + lines.next(); + } + let line = lines.next().unwrap(); + writeln!(writer, "{:>width$} | {}", self.end.line, line)?; + writeln!( + writer, + "{} | {}", + " ".repeat(width), + "^".repeat(self.end.col + 1) + )?; + } + // for i in 0..AFTER { + // if let Some(next) = lines.next() { + // writeln!(writer, "{:>width$} | {}", self.end.line + i + 1, next)?; + // } + // } + Ok(()) + } +} diff --git a/src/parser/v3/token/keyword.rs b/src/parser/v3/token/keyword.rs new file mode 100644 index 0000000..f22782c --- /dev/null +++ b/src/parser/v3/token/keyword.rs @@ -0,0 +1,19 @@ +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Keyword { + Fn, + Let, + If, + Return, +} + +impl Keyword { + pub fn from_string(str: &str) -> Option { + Some(match str { + "fn" => Self::Fn, + "let" => Self::Let, + "if" => Self::If, + "return" => Self::Return, + _ => return None, + }) + } +} diff --git a/src/parser/v3/token/mod.rs b/src/parser/v3/token/mod.rs new file mode 100644 index 0000000..59e25f6 --- /dev/null +++ b/src/parser/v3/token/mod.rs @@ -0,0 +1,90 @@ +mod cursor; +mod file; +mod keyword; +mod symbol; + +use std::ops::Deref; + +pub use cursor::*; +pub use file::*; +pub use keyword::*; +pub use symbol::*; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Token { + Symbol(Symbol), + Ident(String), + Keyword(Keyword), +} + +#[derive(Debug, Clone)] +pub struct TokenInstance { + pub token: Token, + pub span: FileSpan, +} + +impl TokenInstance { + pub fn parse(cursor: &mut CharCursor) -> Option { + cursor.skip_whitespace(); + cursor.peek()?; + let start = cursor.next_pos(); + if let Some(s) = Symbol::parse(cursor) { + if s == Symbol::DoubleSlash { + while cursor.next() != Some('\n') {} + return Self::parse(cursor); + } + let end = cursor.prev_pos(); + return Some(Self { + token: Token::Symbol(s), + span: FileSpan { start, end }, + }); + } + let mut word = String::new(); + while let Some(c) = cursor.peek() { + if c.is_whitespace() || Symbol::from_char(c).is_some() { + break; + } + word.push(c); + cursor.advance(); + } + let end = cursor.prev_pos(); + let token = if let Some(keyword) = Keyword::from_string(&word) { + Token::Keyword(keyword) + } else { + Token::Ident(word) + }; + Some(Self { + token, + span: FileSpan { start, end }, + }) + } +} + +impl Token { + pub fn is_symbol(&self, symbol: Symbol) -> bool { + match self { + Token::Symbol(s) => *s == symbol, + _ => false, + } + } + pub fn is_symbol_and(&self, f: impl Fn(Symbol) -> bool) -> bool { + match self { + Token::Symbol(s) => f(*s), + _ => false, + } + } + pub fn is_keyword(&self, kw: Keyword) -> bool { + match self { + Token::Keyword(k) => *k == kw, + _ => false, + } + } +} + +impl Deref for TokenInstance { + type Target = Token; + + fn deref(&self) -> &Self::Target { + &self.token + } +} diff --git a/src/parser/v3/token/symbol.rs b/src/parser/v3/token/symbol.rs new file mode 100644 index 0000000..2ed689a --- /dev/null +++ b/src/parser/v3/token/symbol.rs @@ -0,0 +1,146 @@ +use std::fmt::Debug; + +use super::CharCursor; + +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum Symbol { + Semicolon, + Colon, + DoubleColon, + Equals, + DoubleEquals, + Arrow, + DoubleArrow, + Plus, + Minus, + Asterisk, + Slash, + DoubleSlash, + Dot, + OpenParen, + CloseParen, + OpenCurly, + CloseCurly, + OpenSquare, + CloseSquare, + OpenAngle, + CloseAngle, + SingleQuote, + DoubleQuote, + Bang, + Ampersand, + DoubleAmpersand, + Pipe, + DoublePipe, + Comma, +} + +impl Symbol { + pub fn parse(cursor: &mut CharCursor) -> Option { + Self::from_char(cursor.peek()?).map(|mut s| { + cursor.advance(); + s.finish(cursor); + s + }) + } + pub fn from_char(c: char) -> Option { + Some(match c { + '(' => Self::OpenParen, + ')' => Self::CloseParen, + '[' => Self::OpenSquare, + ']' => Self::CloseSquare, + '{' => Self::OpenCurly, + '}' => Self::CloseCurly, + '<' => Self::OpenAngle, + '>' => Self::CloseAngle, + ';' => Self::Semicolon, + ':' => Self::Colon, + '+' => Self::Plus, + '-' => Self::Minus, + '*' => Self::Asterisk, + '/' => Self::Slash, + '=' => Self::Equals, + '.' => Self::Dot, + '\'' => Self::SingleQuote, + '"' => Self::DoubleQuote, + '!' => Self::Bang, + '&' => Self::Ampersand, + '|' => Self::Pipe, + ',' => Self::Comma, + _ => return None, + }) + } + pub fn finish(&mut self, cursor: &mut CharCursor) { + let Some(next) = cursor.peek() else { + return; + }; + *self = match self { + Self::Colon => match next { + ':' => Self::DoubleColon, + _ => return, + }, + Self::Minus => match next { + '>' => Self::Arrow, + _ => return, + }, + Self::Equals => match next { + '=' => Self::DoubleEquals, + '>' => Self::DoubleArrow, + _ => return, + }, + Self::Slash => match next { + '/' => Self::DoubleSlash, + _ => return, + }, + Self::Ampersand => match next { + '&' => Self::DoubleAmpersand, + _ => return, + }, + Self::Pipe => match next { + '&' => Self::DoublePipe, + _ => return, + }, + _ => return, + }; + cursor.advance(); + } + pub fn str(&self) -> &str { + match self { + Self::Semicolon => ";", + Self::Colon => ":", + Self::DoubleColon => "::", + Self::Equals => "=", + Self::DoubleEquals => "==", + Self::Arrow => "->", + Self::DoubleArrow => "=>", + Self::Plus => "+", + Self::Minus => "-", + Self::Asterisk => "*", + Self::Slash => "/", + Self::DoubleSlash => "//", + Self::Dot => ".", + Self::OpenParen => "(", + Self::CloseParen => ")", + Self::OpenCurly => "{", + Self::CloseCurly => "}", + Self::OpenSquare => "[", + Self::CloseSquare => "]", + Self::OpenAngle => "<", + Self::CloseAngle => ">", + Self::SingleQuote => "'", + Self::DoubleQuote => "\"", + Self::Bang => "!", + Self::Comma => ",", + Self::Ampersand => "&", + Self::DoubleAmpersand => "&&", + Self::Pipe => "|", + Self::DoublePipe => "||", + } + } +} + +impl Debug for Symbol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "'{}'", self.str()) + } +}