This commit is contained in:
2024-10-18 16:52:12 -04:00
parent b15a40c4d9
commit 14a4fb1ff9
22 changed files with 1672 additions and 77 deletions
+68
View File
@@ -0,0 +1,68 @@
use std::{iter::Peekable, str::Chars};
use super::super::ParserError;
use super::FilePos;
pub struct CharCursor<'a> {
chars: Peekable<Chars<'a>>,
next_pos: FilePos,
prev_pos: FilePos,
}
impl CharCursor<'_> {
pub fn next(&mut self) -> Option<char> {
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<char> {
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<char, ParserError> {
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(),
}
}
}
+80
View File
@@ -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(())
}
}
+19
View File
@@ -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<Self> {
Some(match str {
"fn" => Self::Fn,
"let" => Self::Let,
"if" => Self::If,
"return" => Self::Return,
_ => return None,
})
}
}
+90
View File
@@ -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<TokenInstance> {
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
}
}
+146
View File
@@ -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> {
Self::from_char(cursor.peek()?).map(|mut s| {
cursor.advance();
s.finish(cursor);
s
})
}
pub fn from_char(c: char) -> Option<Self> {
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())
}
}