impl shl shr tokens and type checking

This commit is contained in:
collin 2022-06-10 16:33:30 -07:00
parent 4e3ed8d4dc
commit 32f0c96b6f
6 changed files with 88 additions and 43 deletions

View File

@ -101,7 +101,7 @@ impl ParserContext<'_> {
}
/// Returns an [`Expression`] AST node if the next tokens represent
/// a binary or expression.
/// a binary OR expression.
///
/// Otherwise, tries to parse the next token using [`parse_conjunctive_expression`].
fn parse_disjunctive_expression(&mut self) -> Result<Expression> {
@ -109,11 +109,19 @@ impl ParserContext<'_> {
}
/// Returns an [`Expression`] AST node if the next tokens represent a
/// binary and expression.
/// binary AND expression.
///
/// Otherwise, tries to parse the next token using [`parse_equality_expression`].
fn parse_conjunctive_expression(&mut self) -> Result<Expression> {
self.parse_bin_expr(&[Token::And], Self::parse_equality_expression)
self.parse_bin_expr(&[Token::And], Self::parse_shift_expression)
}
/// Returns an [`Expression`] AST node if the next tokens represent a
/// shift left or a shift right expression.
///
/// Otherwise, tries to parse the next token using [`parse_equality_expression`].
fn parse_shift_expression(&mut self) -> Result<Expression> {
self.parse_bin_expr(&[Token::Shl, Token::Shr], Self::parse_equality_expression)
}
/// Eats one of binary operators matching any in `tokens`.
@ -132,6 +140,8 @@ impl ParserContext<'_> {
Token::Or => BinaryOperation::Or,
Token::And => BinaryOperation::And,
Token::Exp => BinaryOperation::Pow,
Token::Shl => BinaryOperation::Shl,
Token::Shr => BinaryOperation::Shr,
_ => unreachable!("`eat_bin_op` shouldn't produce this"),
})
}
@ -218,6 +228,8 @@ impl ParserContext<'_> {
Ok(inner)
}
/// Returns an [`Expression`] AST node if the next tokens represent a
/// method call expression.
fn parse_method_call_expression(&mut self, receiver: Expression) -> Result<Expression> {
// Get the name of the method.
if let Token::Ident(method) = self.token.token {

View File

@ -192,6 +192,24 @@ impl Token {
(1, els)
})
};
// Consumes a character followed by `on_1`, `on_2` or none. Outputs case_1, case_2, or els.
let three_cases = |
input: &mut Peekable<_>,
on_1,
case_1,
on_2,
case_2,
els
| {
input.next();
Ok(if input.next_if_eq(&on_1).is_some() {
(2, case_1)
} else if input.next_if_eq(&on_2).is_some() {
(2, case_2)
} else {
(1, els)
})
};
// Consumes `on` again and produces `token` if found.
let twice = |input: &mut Peekable<_>, on, token| {
input.next();
@ -292,8 +310,14 @@ impl Token {
}
':' => return single(&mut input, Token::Colon),
';' => return single(&mut input, Token::Semicolon),
'<' => return followed_by(&mut input, '=', Token::LtEq, Token::Lt),
'>' => return followed_by(&mut input, '=', Token::GtEq, Token::Gt),
'<' => return three_cases(&mut input,
'=', Token::LtEq,
'<', Token::Shl,
Token::Lt),
'>' => return three_cases(&mut input,
'=', Token::GtEq,
'>', Token::Shr,
Token::Gt),
'=' => return followed_by(&mut input, '=', Token::Eq, Token::Assign),
'[' => return single(&mut input, Token::LeftSquare),
']' => return single(&mut input, Token::RightSquare),

View File

@ -63,6 +63,8 @@ pub enum Token {
Colon,
Question,
Arrow,
Shl,
Shr,
Underscore,
// Syntactic Grammar
@ -221,6 +223,8 @@ impl fmt::Display for Token {
Colon => write!(f, ":"),
Question => write!(f, "?"),
Arrow => write!(f, "->"),
Shl => write!(f, "<<"),
Shr => write!(f, ">>"),
Underscore => write!(f, "_"),
Address => write!(f, "address"),

View File

@ -258,45 +258,38 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
return_incorrect_type(t1, t2, expected)
}
BinaryOperation::Pow => {
self.visitor.assert_field_int_type(expected, input.span());
let t1 = self.visit_expression(&input.left, &None);
let t2 = self.visit_expression(&input.right, &None);
let t2 = self.visit_expression(&input.left, &None);
match (t1.as_ref(), t2.as_ref()) {
// Type A must be an int.
// Type B must be a unsigned int.
(Some(Type::IntegerType(_)), Some(Type::IntegerType(itype))) if !itype.is_signed() => {
self.visitor.assert_type(t1.unwrap(), expected, input.left.span());
}
// Type A was an int.
// But Type B was not a unsigned int.
(Some(Type::IntegerType(_)), Some(t)) => {
self.visitor.handler.emit_err(
TypeCheckerError::incorrect_pow_exponent_type("unsigned int", t, input.right.span())
.into(),
);
}
// Type A must be a field.
// Type B must be an int.
(Some(Type::Field), Some(Type::IntegerType(_))) => {
(Some(Type::Field), Some(other)) => {
self.visitor.assert_type(Type::Field, expected, input.left.span());
self.visitor.assert_type(*other, &Some(Type::Field), input.left.span());
Some(Type::Field)
}
// Type A was a field.
// But Type B was not an int.
(Some(Type::Field), Some(t)) => {
self.visitor.handler.emit_err(
TypeCheckerError::incorrect_pow_exponent_type("int", t, input.right.span()).into(),
);
(Some(other), Some(Type::Field)) => {
self.visitor.assert_type(*other, &Some(Type::Field), input.left.span());
self.visitor.assert_type(Type::Field, expected, input.right.span());
Some(Type::Field)
}
// The base is some type thats not an int or field.
(Some(t), _) if !matches!(t, Type::IntegerType(_) | Type::Field) => {
self.visitor
.handler
.emit_err(TypeCheckerError::incorrect_pow_base_type(t, input.left.span()).into());
(Some(t1), Some(t2)) => {
// Allow integer exponentiation.
self.visitor.assert_type(*t1, expected, input.left.span());
self.visitor.assert_type(*t2, expected, input.right.span());
return_incorrect_type(Some(*t1), Some(*t2), expected)
}
_ => {}
(Some(type_), None) => {
self.visitor.assert_type(*type_, expected, input.left.span());
None
}
(None, Some(type_)) => {
self.visitor.assert_type(*type_, expected, input.right.span());
None
}
(None, None) => None,
}
t1
}
BinaryOperation::Eq | BinaryOperation::Neq => {
let t1 = self.visit_expression(&input.left, &None);
@ -320,8 +313,7 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
BinaryOperation::AddWrapped
| BinaryOperation::SubWrapped
| BinaryOperation::DivWrapped
| BinaryOperation::MulWrapped
| BinaryOperation::PowWrapped => {
| BinaryOperation::MulWrapped => {
self.visitor.assert_int_type(expected, input.span);
let t1 = self.visit_expression(&input.left, expected);
let t2 = self.visit_expression(&input.right, expected);
@ -331,11 +323,13 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
BinaryOperation::Shl
| BinaryOperation::ShlWrapped
| BinaryOperation::Shr
| BinaryOperation::ShrWrapped => {
// todo @collinc97: add magnitude check for second operand (u8, u16, u32).
| BinaryOperation::ShrWrapped
| BinaryOperation::PowWrapped => {
self.visitor.assert_int_type(expected, input.span);
let t1 = self.visit_expression(&input.left, expected);
let t2 = self.visit_expression(&input.right, expected);
let t2 = self.visit_expression(&input.left, &None);
self.visitor.assert_magnitude_type(&t2, input.right.span());
return_incorrect_type(t1, t2, expected)
}

View File

@ -45,6 +45,12 @@ const INT_TYPES: [Type; 10] = [
Type::IntegerType(IntegerType::U128),
];
const MAGNITUDE_TYPES: [Type; 3] = [
Type::IntegerType(IntegerType::U8),
Type::IntegerType(IntegerType::U16),
Type::IntegerType(IntegerType::U32),
];
const fn create_type_superset<const S: usize, const A: usize, const O: usize>(
subset: [Type; S],
additional: [Type; A],
@ -148,6 +154,11 @@ impl<'a> TypeChecker<'a> {
self.assert_one_of_types(type_, &INT_TYPES, span)
}
/// Emits an error to the handler if the given type is not a magnitude (u8, u16, u32).
pub(crate) fn assert_magnitude_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &MAGNITUDE_TYPES, span)
}
/// Emits an error to the handler if the given type is not a field or integer.
pub(crate) fn assert_field_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &FIELD_INT_TYPES, span)

View File

@ -1,3 +1,3 @@
function main(a: u8, b: u8) -> u8 {
return a.add_wrapped(b);
function main(a: u8) -> u8 {
return a >> 1u32;
}