146 lines
3.0 KiB
Rust
146 lines
3.0 KiB
Rust
#![feature(box_syntax)]
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
enum Token<'a> {
|
|
IntegerLiteral(&'a str),
|
|
Plus,
|
|
Semicolon,
|
|
}
|
|
|
|
impl Token<'_> {
|
|
pub fn len(&self) -> usize {
|
|
return match self {
|
|
Token::IntegerLiteral(s) => s.len(),
|
|
Token::Plus | Token::Semicolon => 1,
|
|
};
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for Token<'_> {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
return match self {
|
|
Token::IntegerLiteral(i) => write!(f, "{}", i),
|
|
Token::Plus => write!(f, "+"),
|
|
_ => unreachable!(),
|
|
};
|
|
}
|
|
}
|
|
|
|
struct Source<'a> {
|
|
source: &'a str,
|
|
cursor: usize,
|
|
last: Option<Token<'a>>,
|
|
}
|
|
|
|
impl<'a> Source<'a> {
|
|
pub fn new(source: &'a str) -> Self {
|
|
return Self {
|
|
source,
|
|
cursor: 0,
|
|
last: None,
|
|
};
|
|
}
|
|
|
|
fn skip_whitespace(&mut self) {
|
|
let mut chars = self.source[self.cursor..].chars();
|
|
|
|
while let Some(c) = chars.next() {
|
|
if c.is_whitespace() {
|
|
self.cursor += c.len_utf8();
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_next(&mut self) -> Option<Token<'a>> {
|
|
self.skip_whitespace();
|
|
let mut chars = self.source[self.cursor..].chars();
|
|
|
|
let token = match chars.next()? {
|
|
'+' => Token::Plus,
|
|
';' => Token::Semicolon,
|
|
c if c.is_ascii_digit() => {
|
|
let start = self.cursor;
|
|
let mut length = c.len_utf8();
|
|
|
|
loop {
|
|
match chars.next()? {
|
|
c if c.is_ascii_digit() => length += c.len_utf8(),
|
|
_ => break,
|
|
};
|
|
};
|
|
|
|
Token::IntegerLiteral(&self.source[start..start + length])
|
|
},
|
|
c => todo!("invalid character `{:?}`", c)
|
|
};
|
|
|
|
return Some(token);
|
|
}
|
|
|
|
pub fn next(&mut self) -> Option<Token<'a>> {
|
|
let token = match self.last {
|
|
Some(t) => t,
|
|
None => self.get_next()?,
|
|
};
|
|
|
|
self.last = None;
|
|
self.cursor += token.len();
|
|
return Some(token);
|
|
}
|
|
|
|
pub fn peek(&mut self) -> Option<Token<'a>> {
|
|
self.last = Some(self.get_next()?);
|
|
return self.last;
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum Statement<'a> {
|
|
Expression(Expression<'a>),
|
|
}
|
|
|
|
// statement = expression ';' .
|
|
fn parse_statement<'a>(source: &mut Source<'a>) -> Option<Statement<'a>> {
|
|
let expression = match source.peek()? {
|
|
Token::IntegerLiteral(_) => parse_expression(source)?,
|
|
_ => return None,
|
|
};
|
|
|
|
return match source.next()? {
|
|
Token::Semicolon => Some(Statement::Expression(expression)),
|
|
_ => None,
|
|
};
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum Expression<'a> {
|
|
Literal(&'a str),
|
|
Binary(Token<'a>, Box<Expression<'a>>, Box<Expression<'a>>),
|
|
}
|
|
|
|
// expression = literal | expression '+' expression .
|
|
fn parse_expression<'a>(source: &mut Source<'a>) -> Option<Expression<'a>> {
|
|
let lhs = match source.next()? {
|
|
Token::IntegerLiteral(i) => Expression::Literal(i),
|
|
_ => return None,
|
|
};
|
|
|
|
let operator = match source.peek()? {
|
|
token @ Token::Plus => token,
|
|
Token::Semicolon => return Some(lhs),
|
|
_ => return None,
|
|
};
|
|
source.next();
|
|
|
|
let rhs = parse_expression(source)?;
|
|
return Some(Expression::Binary(operator, box lhs, box rhs));
|
|
}
|
|
|
|
fn main() {
|
|
let inline_source = "3 + 5 + 7;";
|
|
let mut source = Source::new(inline_source);
|
|
eprintln!("{:?}", parse_statement(&mut source));
|
|
}
|