Skip to content

Commit

Permalink
implement parsing for cast
Browse files Browse the repository at this point in the history
  • Loading branch information
andogq committed Aug 31, 2024
1 parent f8d4890 commit b0b6ee5
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 17 deletions.
85 changes: 85 additions & 0 deletions src/hir/expression/cast.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::stage::parse::{parse_ty, ParseError};

use super::*;

ast_node! {
Expand All @@ -9,6 +11,33 @@ ast_node! {
}
}

impl<M: AstMetadata> Parsable for Cast<M> {
fn register(parser: &mut Parser) {
assert!(parser.register_infix(Token::As, |_, _, lexer, left| {
let as_span = match lexer.next_spanned().unwrap() {
(Token::As, span) => span,
(token, _) => {
return Err(ParseError::ExpectedToken {
expected: Box::new(Token::As),
found: Box::new(token),
reason: "expected to find cast expression".to_string(),
});
}
};

// Parse out type from right hand side
let (target_ty, target_ty_span) = parse_ty(lexer)?;

Ok(Expression::Cast(Cast {
value: Box::new(left),
target_ty,
span: as_span.start..target_ty_span.end,
ty_info: None,
}))
}));
}
}

impl SolveType for Cast<UntypedAstMetadata> {
type State = Scope;

Expand Down Expand Up @@ -39,3 +68,59 @@ impl SolveType for Cast<UntypedAstMetadata> {
})
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::stage::parse::Lexer;
use crate::stage::parse::Precedence;
use rstest::*;

#[fixture]
fn parser() -> Parser {
let mut parser = Parser::new();

Cast::<UntypedAstMetadata>::register(&mut parser);

// Helper parsers
Integer::<UntypedAstMetadata>::register(&mut parser);

parser
}

#[rstest]
#[case::single_cast("1 as int", |e| matches!(e, Expression::Integer(_)))]
#[case::double_cast("1 as int as uint", |e| matches!(e, Expression::Cast(_)))]
fn success(
parser: Parser,
#[case] source: &str,
#[case] test: fn(Expression<UntypedAstMetadata>) -> bool,
) {
let cast = parser
.parse(
&mut Compiler::default(),
&mut Lexer::from(source),
Precedence::Lowest,
)
.unwrap();

let Expression::Cast(cast) = dbg!(cast) else {
panic!("expected to parse cast");
};

assert!(test(*cast.value));
}

#[rstest]
#[case::missing_type("1 as")]
#[case::repeated_as("1 as as")]
fn fail(parser: Parser, #[case] source: &str) {
let result = parser.parse(
&mut Compiler::default(),
&mut Lexer::from(source),
Precedence::Lowest,
);

assert!(result.is_err());
}
}
2 changes: 2 additions & 0 deletions src/stage/parse/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub enum Precedence {
Equality,
Sum,
Multiply,
Cast,
Call,
}

Expand All @@ -47,6 +48,7 @@ impl Precedence {
| Token::MinusAssign
| Token::DivAssign
| Token::MulAssign => Precedence::Assign,
Token::As => Precedence::Cast,
_ => Precedence::Lowest,
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/stage/parse/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub fn parse_function(
}

// Extract the type
let ty = match parse_ty(tokens) {
let (ty, _) = match parse_ty(tokens) {
Ok(ty) => ty,
Err(e) => {
return Some(Err(e));
Expand Down Expand Up @@ -127,7 +127,7 @@ pub fn parse_function(
}

// return type
let return_ty = parse_ty(tokens)?;
let (return_ty, _) = parse_ty(tokens)?;

// Parse out the body
let body = parse_block(compiler, tokens)?;
Expand Down
1 change: 1 addition & 0 deletions src/stage/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use self::block::*;
pub use self::expression::*;
use self::function::*;
use self::statement::*;
pub use self::ty::parse_ty;

use crate::repr::ast::untyped::*;

Expand Down
35 changes: 20 additions & 15 deletions src/stage/parse/ty.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
use crate::repr::ty::Ty;

use super::{Lexer, ParseError, Token};
use super::{Lexer, ParseError, Span, Token};

pub fn parse_ty(lexer: &mut Lexer) -> Result<Ty, ParseError> {
Ok(match lexer.next_token().unwrap() {
Token::Int => Ty::Int,
Token::Uint => Ty::Uint,
Token::Bool => Ty::Boolean,
token => {
return Err(ParseError::ExpectedToken {
// WARN: This should be any of the primitive type tokens
expected: Box::new(Token::Int),
found: Box::new(token),
reason: "expected valid type".to_string(),
});
}
})
pub fn parse_ty(lexer: &mut Lexer) -> Result<(Ty, Span), ParseError> {
let (token, span) = lexer.next_spanned().ok_or(ParseError::UnexpectedEOF)?;

Ok((
match token {
Token::Int => Ty::Int,
Token::Uint => Ty::Uint,
Token::Bool => Ty::Boolean,
token => {
return Err(ParseError::ExpectedToken {
// WARN: This should be any of the primitive type tokens
expected: Box::new(Token::Int),
found: Box::new(token),
reason: "expected valid type".to_string(),
});
}
},
span,
))
}

0 comments on commit b0b6ee5

Please sign in to comment.