work
This commit is contained in:
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user