Skip to content

Commit

Permalink
✨ Add typing system
Browse files Browse the repository at this point in the history
semver: minor
  • Loading branch information
Somfic committed Oct 30, 2024
1 parent bf9bee3 commit 228f751
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 39 deletions.
16 changes: 16 additions & 0 deletions src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ impl<'de> Lexer<'de> {
self.peeked.as_ref()
}

pub fn peek_expect(
&mut self,
expected: TokenKind,
) -> Option<&Result<Token<'de>, miette::Error>> {
match self.peek() {
Some(Ok(token::Token { kind, .. })) => {
if *kind == expected {
self.peeked.as_ref()
} else {
None
}
}
_ => None,
}
}

fn parse_compound_operator(
&mut self,
single: TokenKind,
Expand Down
45 changes: 39 additions & 6 deletions src/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@ pub enum Symbol<'de> {
pub enum Statement<'de> {
Block(Vec<Statement<'de>>),
Expression(Expression<'de>),
Assignment(Cow<'de, str>, Expression<'de>),
Assignment {
name: Cow<'de, str>,
value: Expression<'de>,
},
Struct {
name: Cow<'de, str>,
fields: Vec<Cow<'de, str>>,
fields: Vec<StructMemberDeclaration<'de>>,
},
Enum {
name: Cow<'de, str>,
variants: Vec<Cow<'de, str>>,
variants: Vec<EnumMemberDeclaration<'de>>,
},
Function {
name: Cow<'de, str>,
parameters: Vec<Cow<'de, str>>,
header: FunctionHeader<'de>,
body: Expression<'de>,
explicit_return_type: Option<Type<'de>>,
},
Trait {
name: Cow<'de, str>,
functions: Vec<(Cow<'de, str>, Vec<Cow<'de, str>>)>,
functions: Vec<FunctionHeader<'de>>,
},
}

Expand Down Expand Up @@ -58,6 +61,30 @@ pub enum Expression<'de> {
},
}

#[derive(Debug)]
pub struct FunctionHeader<'de> {
pub name: Cow<'de, str>,
pub parameters: Vec<ParameterDeclaration<'de>>,
}

#[derive(Debug)]
pub struct ParameterDeclaration<'de> {
pub name: Cow<'de, str>,
pub explicit_type: Type<'de>,
}

#[derive(Debug)]
pub struct StructMemberDeclaration<'de> {
pub name: Cow<'de, str>,
pub explicit_type: Type<'de>,
}

#[derive(Debug)]
pub struct EnumMemberDeclaration<'de> {
pub name: Cow<'de, str>,
pub value_type: Option<Type<'de>>,
}

#[derive(Debug)]
pub enum Primitive<'de> {
Integer(i64),
Expand Down Expand Up @@ -91,3 +118,9 @@ pub enum UnaryOperator {
Negate,
Negative,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type<'de> {
Symbol(Cow<'de, str>),
Array(Box<Type<'de>>),
}
50 changes: 28 additions & 22 deletions src/parser/lookup.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
ast::{Expression, Primitive, Statement},
expression, statement, Parser,
ast::{Expression, Primitive, Statement, Type},
expression, statement, typing, Parser,
};
use crate::lexer::TokenKind;
use miette::Result;
Expand All @@ -21,9 +21,8 @@ pub enum BindingPower {
Primary = 10,
}

// pub type TypeHandler<'de> = fn(&mut Parser<'de>) -> Result<(Type, usize), Error<'de>>;
// pub type LeftTypeHandler<'de> =
// fn(&'de Parser<'de>, usize, Type, &BindingPower) -> Result<(Type, usize), Error<'de>>;
pub type TypeHandler<'de> = fn(&mut Parser<'de>) -> Result<Type<'de>>;
pub type LeftTypeHandler<'de> = fn(&mut Parser<'de>, Type, BindingPower) -> Result<Type<'de>>;
pub type StatementHandler<'de> = fn(&mut Parser<'de>) -> Result<Statement<'de>>;
pub type ExpressionHandler<'de> = fn(&mut Parser<'de>) -> Result<Expression<'de>>;
pub type LeftExpressionHandler<'de> =
Expand All @@ -33,8 +32,8 @@ pub struct Lookup<'de> {
pub statement_lookup: HashMap<TokenKind, StatementHandler<'de>>,
pub expression_lookup: HashMap<TokenKind, ExpressionHandler<'de>>,
pub left_expression_lookup: HashMap<TokenKind, LeftExpressionHandler<'de>>,
// pub type_lookup: HashMap<TokenKind, TypeHandler<'de>>,
// pub left_type_lookup: HashMap<TokenKind, LeftTypeHandler<'de>>,
pub type_lookup: HashMap<TokenKind, TypeHandler<'de>>,
pub left_type_lookup: HashMap<TokenKind, LeftTypeHandler<'de>>,
pub binding_power_lookup: HashMap<TokenKind, BindingPower>,
}

Expand Down Expand Up @@ -80,22 +79,28 @@ impl<'de> Lookup<'de> {
self
}

// pub(crate) fn add_type_handler(&mut self, token: TokenType, handler: TypeHandler<'de>) {
// if self.type_lookup.contains_key(&token) {
// panic!("Token already has a type handler");
// }
pub(crate) fn add_type_handler(mut self, token: TokenKind, handler: TypeHandler<'de>) -> Self {
if self.type_lookup.contains_key(&token) {
panic!("Token already has a type handler");
}

// self.type_lookup.insert(token, handler);
// }
self.type_lookup.insert(token, handler);
self
}

// #[allow(dead_code)]
// pub(crate) fn add_left_type_handler(&mut self, token: TokenType, handler: LeftTypeHandler<'de>) {
// if self.left_type_lookup.contains_key(&token) {
// panic!("Token already has a left type handler");
// }
#[allow(dead_code)]
pub(crate) fn add_left_type_handler(
mut self,
token: TokenKind,
handler: LeftTypeHandler<'de>,
) -> Self {
if self.left_type_lookup.contains_key(&token) {
panic!("Token already has a left type handler");
}

// self.left_type_lookup.insert(token, handler);
// }
self.left_type_lookup.insert(token, handler);
self
}
}

impl Default for Lookup<'_> {
Expand All @@ -105,8 +110,8 @@ impl Default for Lookup<'_> {
expression_lookup: HashMap::new(),
left_expression_lookup: HashMap::new(),
binding_power_lookup: HashMap::new(),
// type_lookup: HashMap::new(),
// left_type_lookup: HashMap::new(),
type_lookup: HashMap::new(),
left_type_lookup: HashMap::new(),
}
.add_expression_handler(TokenKind::Integer, expression::primitive::integer)
.add_expression_handler(TokenKind::Decimal, expression::primitive::decimal)
Expand Down Expand Up @@ -190,6 +195,7 @@ impl Default for Lookup<'_> {
.add_statement_handler(TokenKind::Enum, statement::enum_)
.add_statement_handler(TokenKind::Function, statement::function_)
.add_statement_handler(TokenKind::Trait, statement::trait_)
.add_type_handler(TokenKind::Identifier, typing::identifier)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod ast;
pub mod expression;
pub mod lookup;
pub mod statement;
pub mod typing;

pub struct Parser<'de> {
lexer: Lexer<'de>,
Expand Down
71 changes: 60 additions & 11 deletions src/parser/statement.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
use crate::lexer::{TokenKind, TokenValue};

use super::{ast::Statement, expression, lookup::BindingPower, Parser};
use crate::lexer::{self, TokenKind, TokenValue};

use super::{
ast::{
EnumMemberDeclaration, FunctionHeader, ParameterDeclaration, Statement,
StructMemberDeclaration,
},
expression,
lookup::BindingPower,
typing, Parser,
};
use miette::{Context, Result};

pub fn parse<'de>(parser: &mut Parser<'de>, optional_semicolon: bool) -> Result<Statement<'de>> {
Expand Down Expand Up @@ -62,7 +70,10 @@ pub fn let_<'de>(parser: &mut Parser<'de>) -> Result<Statement<'de>> {
.lexer
.expect(TokenKind::Semicolon, "expected a semicolon")?;

Ok(Statement::Assignment(identifier, expression))
Ok(Statement::Assignment {
name: identifier,
value: expression,
})
}

pub fn struct_<'de>(parser: &mut Parser<'de>) -> Result<Statement<'de>> {
Expand Down Expand Up @@ -103,7 +114,14 @@ pub fn struct_<'de>(parser: &mut Parser<'de>) -> Result<Statement<'de>> {
_ => unreachable!(),
};

fields.push(field);
parser.lexer.expect(TokenKind::Tilde, "expected a tilde")?;

let explicit_type = typing::parse(parser, BindingPower::None)?;

fields.push(StructMemberDeclaration {
name: field,
explicit_type,
});
}

parser
Expand Down Expand Up @@ -154,7 +172,10 @@ pub fn enum_<'de>(parser: &mut Parser<'de>) -> Result<Statement<'de>> {
_ => unreachable!(),
};

variants.push(variant);
variants.push(EnumMemberDeclaration {
name: variant,
value_type: None,
});
}

parser
Expand Down Expand Up @@ -207,19 +228,37 @@ pub fn function_<'de>(parser: &mut Parser<'de>) -> Result<Statement<'de>> {
_ => unreachable!(),
};

parameters.push(parameter);
parser.lexer.expect(TokenKind::Tilde, "expected a tilde")?;

let explicit_type = typing::parse(parser, BindingPower::None)?;

parameters.push(ParameterDeclaration {
name: parameter,
explicit_type,
});
}

parser
.lexer
.expect(TokenKind::ParenClose, "expected a close parenthesis")?;

let explicit_return_type = match parser.lexer.peek_expect(TokenKind::Tilde) {
None => None,
Some(_) => {
parser.lexer.next();
Some(typing::parse(parser, BindingPower::None)?)
}
};

let body = expression::parse(parser, BindingPower::None)?;

Ok(Statement::Function {
name: identifier,
parameters,
header: FunctionHeader {
name: identifier,
parameters,
},
body,
explicit_return_type,
})
}

Expand Down Expand Up @@ -291,14 +330,24 @@ pub fn trait_<'de>(parser: &mut Parser<'de>) -> Result<Statement<'de>> {
_ => unreachable!(),
};

parameters.push(parameter);
parser.lexer.expect(TokenKind::Tilde, "expected a tilde")?;

let explicit_type = typing::parse(parser, BindingPower::None)?;

parameters.push(ParameterDeclaration {
name: parameter,
explicit_type,
});
}

parser
.lexer
.expect(TokenKind::ParenClose, "expected a close parenthesis")?;

functions.push((function, parameters));
functions.push(FunctionHeader {
name: function,
parameters,
})
}

parser
Expand Down
75 changes: 75 additions & 0 deletions src/parser/typing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::lexer::{TokenKind, TokenValue};

use super::{ast::Type, lookup::BindingPower, Parser};
use miette::Result;

pub fn parse<'de>(parser: &mut Parser<'de>, binding_power: BindingPower) -> Result<Type<'de>> {
let token = match parser.lexer.peek().as_ref() {
Some(Ok(token)) => token,
Some(Err(err)) => return Err(miette::miette!(err.to_string())), // FIXME: better error handling
None => {
return Err(miette::miette! {
help = "expected a type",
"expected a type"
})
}
};

let handler = parser
.lookup
.type_lookup
.get(&token.kind)
.ok_or(miette::miette! {
labels = vec![token.label("expected a type")],
help = format!("{} is not a type", token.kind),
"expected a type, found {}", token.kind
})?;
let mut lhs = handler(parser)?;

let mut next_token = parser.lexer.peek();

while let Some(token) = next_token {
let token = match token {
Ok(token) => token,
Err(err) => return Err(miette::miette!(err.to_string())), // FIXME: better error handling
};

let token_binding_power = {
let binding_power_lookup = parser.lookup.binding_power_lookup.clone();
binding_power_lookup
.get(&token.kind)
.unwrap_or(&BindingPower::None)
.clone()
};

if binding_power > token_binding_power {
break;
}

let handler = match parser.lookup.left_type_lookup.get(&token.kind) {
Some(handler) => handler,
None => break,
};

parser.lexer.next();

lhs = handler(parser, lhs, token_binding_power)?;

next_token = parser.lexer.peek();
}

Ok(lhs)
}

pub fn identifier<'de>(parser: &mut Parser<'de>) -> Result<Type<'de>> {
let token = parser
.lexer
.expect(TokenKind::Identifier, "expected an identifier")?;

let identifier = match token.value {
TokenValue::Identifier(identifier) => identifier,
_ => unreachable!(),
};

Ok(Type::Symbol(identifier))
}

0 comments on commit 228f751

Please sign in to comment.