AST: Init

This commit is contained in:
bad 2022-03-24 16:33:10 +01:00
parent 8b967efda7
commit c14cd20d8c
6 changed files with 392 additions and 7 deletions

214
src/ast/astnode.rs Normal file
View file

@ -0,0 +1,214 @@
use std::fmt::{Debug, Display};
use crate::lexer::token;
pub enum ASTNode {
BinaryExpr(BinaryExpr),
GroupingExpr(GroupingExpr),
Literal(Literal),
UnaryExpr(UnaryExpr),
}
impl From<BinaryExpr> for ASTNode {
fn from(b: BinaryExpr) -> Self {
ASTNode::BinaryExpr(b)
}
}
impl From<GroupingExpr> for ASTNode {
fn from(g: GroupingExpr) -> Self {
Self::GroupingExpr(g)
}
}
impl From<Literal> for ASTNode {
fn from(l: Literal) -> Self {
Self::Literal(l)
}
}
impl From<UnaryExpr> for ASTNode {
fn from(u: UnaryExpr) -> Self {
Self::UnaryExpr(u)
}
}
impl Debug for ASTNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BinaryExpr(b) => b.fmt(f),
Self::GroupingExpr(g) => g.fmt(f),
Self::Literal(l) => l.fmt(f),
Self::UnaryExpr(u) => u.fmt(f),
}
}
}
pub enum UnaryOperator {
Minus,
Bang,
}
impl TryFrom<token::TokenType> for UnaryOperator {
type Error = EnumConvertError;
fn try_from(value: token::TokenType) -> Result<Self, Self::Error> {
Ok(match value {
token::TokenType::Bang => Self::Bang,
token::TokenType::Minus => Self::Minus,
_ => return Err(EnumConvertError),
})
}
}
impl Display for UnaryOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match *self {
UnaryOperator::Minus => "-",
UnaryOperator::Bang => "!",
}
)
}
}
pub enum Operator {
Bang,
BangEqual,
Equal,
EqualEqual,
Greater,
GreaterEqual,
Less,
LessEqual,
}
#[derive(Debug)]
pub struct EnumConvertError;
impl std::fmt::Display for EnumConvertError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Couldn't convert between enums")
}
}
impl std::error::Error for EnumConvertError {}
impl TryFrom<token::TokenType> for Operator {
type Error = EnumConvertError;
fn try_from(value: token::TokenType) -> Result<Self, Self::Error> {
Ok(match value {
token::TokenType::Bang => Self::Bang,
token::TokenType::BangEqual => Self::BangEqual,
token::TokenType::Equal => Self::Equal,
token::TokenType::EqualEqual => Self::EqualEqual,
token::TokenType::Greater => Self::Greater,
token::TokenType::GreaterEqual => Self::GreaterEqual,
token::TokenType::Less => Self::Less,
token::TokenType::LessEqual => Self::LessEqual,
_ => return Err(EnumConvertError),
})
}
}
impl Display for Operator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match *self {
Operator::Bang => "!",
Operator::Less => "<",
Operator::Equal => "=",
Operator::Greater => ">",
Operator::BangEqual => "!=",
Operator::LessEqual => "<=",
Operator::EqualEqual => "==",
Operator::GreaterEqual => ">=",
}
)
}
}
#[derive(Debug)]
pub enum Literal {
String(String),
Int(i64),
Float(f64),
Bool(bool),
}
pub struct BinaryExpr {
left: Box<ASTNode>,
operator: Operator,
right: Box<ASTNode>,
}
impl BinaryExpr {
pub fn new(left: Box<ASTNode>, operator: Operator, right: Box<ASTNode>) -> Self {
Self {
left,
operator,
right,
}
}
}
pub struct GroupingExpr(Box<ASTNode>);
impl GroupingExpr {
pub(crate) fn new(expr: Box<ASTNode>) -> Self {
Self(expr)
}
}
pub struct UnaryExpr {
operator: UnaryOperator,
right: Box<ASTNode>,
}
impl UnaryExpr {
pub fn new(operator: UnaryOperator, right: Box<ASTNode>) -> Self {
Self { operator, right }
}
}
impl Debug for BinaryExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({} {:?} {:?})", self.operator, self.left, self.right)
}
}
impl Debug for GroupingExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({:?})", self.0)
}
}
impl Debug for UnaryExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({} {:?})", self.operator, self.right)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ast_printer() {
let ast = ASTNode::BinaryExpr(BinaryExpr {
left: Box::new(ASTNode::UnaryExpr(UnaryExpr {
operator: UnaryOperator::Bang,
right: Box::new(ASTNode::Literal(Literal::Int(1))),
})),
operator: Operator::EqualEqual,
right: Box::new(ASTNode::Literal(Literal::Int(0))),
});
let formated = format!("{:?}", ast);
assert_eq!("(== (! Int(1)) Int(0))", formated);
}
}

2
src/ast/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod astnode;
pub mod parser;

164
src/ast/parser.rs Normal file
View file

@ -0,0 +1,164 @@
use super::astnode;
use super::astnode::{ASTNode, BinaryExpr};
use crate::lexer::{token, token::TokenType};
use std::iter;
use std::result::Result as StdResult;
type Result<T> = StdResult<T, ASTParsingError>;
#[derive(Debug)]
pub enum ASTParsingError {
UnmatchedBrace,
}
impl std::fmt::Display for ASTParsingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!()
}
}
impl std::error::Error for ASTParsingError {}
pub struct Parser<T: Iterator<Item = token::Token>> {
token_iter: iter::Peekable<T>,
}
impl<T: Iterator<Item = token::Token>> Parser<T> {
pub fn new(iter: T) -> Parser<T> {
Parser {
token_iter: iter.peekable(),
}
}
pub fn scan_expressions(&mut self) -> StdResult<Vec<ASTNode>, Vec<ASTParsingError>> {
let mut tokens = Vec::new();
let mut errors = Vec::new();
while self.token_iter.peek().is_some() {
match self.expression() {
Ok(token) => {
if errors.is_empty() {
tokens.push(token)
}
}
Err(e) => errors.push(e),
}
}
if errors.is_empty() {
Ok(tokens)
} else {
Err(errors)
}
}
fn expression(&mut self) -> Result<ASTNode> {
self.equality()
}
fn equality(&mut self) -> Result<ASTNode> {
let mut node = self.comparison()?;
while let Some(o) = self
.token_iter
.next_if(|t| matches!(t.token_type, TokenType::EqualEqual | TokenType::BangEqual))
{
node = BinaryExpr::new(
Box::new(node),
o.token_type.try_into().unwrap(),
Box::new(self.comparison()?),
)
.into();
}
Ok(node)
}
fn comparison(&mut self) -> Result<ASTNode> {
let mut node = self.term()?;
while let Some(o) = self.token_iter.next_if(|t| {
matches!(
t.token_type,
TokenType::Greater
| TokenType::GreaterEqual
| TokenType::Less | TokenType::LessEqual
)
}) {
node = BinaryExpr::new(
Box::new(node),
o.token_type.try_into().unwrap(),
Box::new(self.comparison()?),
)
.into();
}
Ok(node)
}
fn term(&mut self) -> Result<ASTNode> {
let mut node = self.factor()?;
while let Some(o) = self
.token_iter
.next_if(|t| matches!(t.token_type, TokenType::Minus | TokenType::Plus))
{
node = BinaryExpr::new(
Box::new(node),
o.token_type.try_into().unwrap(),
Box::new(self.comparison()?),
)
.into()
}
Ok(node)
}
fn factor(&mut self) -> Result<ASTNode> {
let mut node = self.unary()?;
while let Some(o) = self
.token_iter
.next_if(|t| matches!(t.token_type, TokenType::Star | TokenType::Slash))
{
node = BinaryExpr::new(
Box::new(node),
o.token_type.try_into().unwrap(),
Box::new(self.comparison()?),
)
.into();
}
Ok(node)
}
fn unary(&mut self) -> Result<ASTNode> {
if let Some(op) = self
.token_iter
.next_if(|t| matches!(t.token_type, TokenType::Bang | TokenType::Minus))
{
let right = Box::new(self.unary()?);
Ok(ASTNode::UnaryExpr(astnode::UnaryExpr::new(
op.token_type.try_into().unwrap(),
right,
)))
} else {
self.primary()
}
}
fn primary(&mut self) -> Result<ASTNode> {
let node = match self.token_iter.next().map(|it| it.token_type) {
Some(TokenType::False) => ASTNode::Literal(astnode::Literal::Bool(false)),
Some(TokenType::True) => ASTNode::Literal(astnode::Literal::Bool(false)),
Some(TokenType::Int(i)) => ASTNode::Literal(astnode::Literal::Int(i)),
Some(TokenType::String(i)) => ASTNode::Literal(astnode::Literal::String(i)),
Some(TokenType::Float(f)) => ASTNode::Literal(astnode::Literal::Float(f)),
Some(TokenType::LeftParen) => {
let expr = self.expression()?;
let group = astnode::GroupingExpr::new(Box::new(expr));
match self
.token_iter
.next_if(|v| matches!(v.token_type, TokenType::RightParen))
{
Some(_) => return Ok(group.into()),
None => return Err(todo!()),
}
}
Some(a) => panic!("{:#?}", a),
None => todo!(),
};
Ok(node)
}
}

View file

@ -10,7 +10,7 @@ use self::token::Token;
#[derive(Debug)] #[derive(Debug)]
pub struct Lexer<'a> { pub struct Lexer<'a> {
source: &'a str, _source: &'a str,
source_iter: iter::Peekable<Chars<'a>>, source_iter: iter::Peekable<Chars<'a>>,
line: usize, line: usize,
} }
@ -18,7 +18,7 @@ pub struct Lexer<'a> {
impl<'a> Lexer<'a> { impl<'a> Lexer<'a> {
pub fn new(code: &'a str) -> Lexer<'a> { pub fn new(code: &'a str) -> Lexer<'a> {
return Lexer { return Lexer {
source: code, _source: code,
source_iter: code.chars().peekable(), source_iter: code.chars().peekable(),
line: 0, line: 0,
}; };
@ -39,7 +39,6 @@ impl<'a> Lexer<'a> {
None => (), None => (),
} }
} }
tokens.push(self.get_token(token::TokenType::Eof));
if errors.is_empty() { if errors.is_empty() {
Ok(tokens) Ok(tokens)
@ -81,6 +80,7 @@ impl<'a> Lexer<'a> {
'+' => self.get_token(token::TokenType::Plus), '+' => self.get_token(token::TokenType::Plus),
';' => self.get_token(token::TokenType::Semicolon), ';' => self.get_token(token::TokenType::Semicolon),
'*' => self.get_token(token::TokenType::Star), '*' => self.get_token(token::TokenType::Star),
'/' => self.get_token(token::TokenType::Slash),
'!' => self.get_token_if_next_eq_or( '!' => self.get_token_if_next_eq_or(
'=', '=',
token::TokenType::BangEqual, token::TokenType::BangEqual,
@ -94,7 +94,7 @@ impl<'a> Lexer<'a> {
'<' => self.get_token_if_next_eq_or( '<' => self.get_token_if_next_eq_or(
'=', '=',
token::TokenType::LessEqual, token::TokenType::LessEqual,
token::TokenType::Equal, token::TokenType::Less,
), ),
'>' => self.get_token_if_next_eq_or( '>' => self.get_token_if_next_eq_or(
'=', '=',

View file

@ -45,5 +45,4 @@ pub enum TokenType {
True, True,
Let, Let,
While, While,
Eof,
} }

View file

@ -1,3 +1,4 @@
mod ast;
mod lexer; mod lexer;
use lexer::Lexer; use lexer::Lexer;
@ -27,7 +28,12 @@ fn run_repl() {
fn run(code: &str) -> Result<(), Vec<lexer::LexingError>> { fn run(code: &str) -> Result<(), Vec<lexer::LexingError>> {
let mut lexer = Lexer::new(code); let mut lexer = Lexer::new(code);
println!("{:?}", lexer.scan_tokens()?); let tokens = lexer.scan_tokens()?;
println!("{:?}", tokens);
let mut parser = crate::ast::parser::Parser::new(tokens.into_iter());
let expressions = parser.scan_expressions().unwrap();
println!("{:?}", expressions);
Ok(()) Ok(())
} }