parse method calls directly

This commit is contained in:
collin 2022-06-09 13:01:59 -07:00
parent df08cd1e26
commit a32418133c
5 changed files with 107 additions and 73 deletions

View File

@ -21,8 +21,10 @@ use super::*;
/// Precedence is defined in the parser.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum BinaryOperation {
/// Addition, i.e. `+`.
/// Addition, i.e. `+`, `.add()`.
Add,
/// Wrapped addition, i.e. `.add_wrapped()`.
AddWrapped,
/// Subtraction, i.e. `-`.
Sub,
/// Multiplication, i.e. `*`.
@ -61,41 +63,20 @@ pub enum BinaryOperationClass {
impl AsRef<str> for BinaryOperation {
fn as_ref(&self) -> &'static str {
match self {
BinaryOperation::Add => "+",
BinaryOperation::Sub => "-",
BinaryOperation::Mul => "*",
BinaryOperation::Div => "/",
BinaryOperation::Pow => "**",
BinaryOperation::Or => "||",
BinaryOperation::And => "&&",
BinaryOperation::Eq => "==",
BinaryOperation::Ne => "!=",
BinaryOperation::Ge => ">=",
BinaryOperation::Gt => ">",
BinaryOperation::Le => "<=",
BinaryOperation::Lt => "<",
}
}
}
impl BinaryOperation {
/// The class ("category") that the binary operation belongs to.
/// For example, the `+` operator is numeric and `==` results in a boolean value.
pub fn class(&self) -> BinaryOperationClass {
match self {
BinaryOperation::Add
| BinaryOperation::Sub
| BinaryOperation::Mul
| BinaryOperation::Div
| BinaryOperation::Pow => BinaryOperationClass::Numeric,
BinaryOperation::Or
| BinaryOperation::And
| BinaryOperation::Eq
| BinaryOperation::Ne
| BinaryOperation::Ge
| BinaryOperation::Gt
| BinaryOperation::Le
| BinaryOperation::Lt => BinaryOperationClass::Boolean,
BinaryOperation::Add => "add",
BinaryOperation::AddWrapped => "add_wrapped",
BinaryOperation::Sub => "sub",
BinaryOperation::Mul => "mul",
BinaryOperation::Div => "div",
BinaryOperation::Pow => "pow",
BinaryOperation::Or => "or",
BinaryOperation::And => "and",
BinaryOperation::Eq => "eq",
BinaryOperation::Ne => "ne",
BinaryOperation::Ge => "ge",
BinaryOperation::Gt => "gt",
BinaryOperation::Le => "le",
BinaryOperation::Lt => "lt",
}
}
}
@ -116,7 +97,7 @@ pub struct BinaryExpression {
impl fmt::Display for BinaryExpression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} {}", self.left, self.op.as_ref(), self.right)
write!(f, "{}.{}({})", self.left, self.op.as_ref(), self.right)
}
}

View File

@ -218,6 +218,48 @@ impl ParserContext<'_> {
Ok(inner)
}
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 {
self.bump();
// Check if the method exists.
let index = method.as_u32();
if index <= 2 {
// Binary operators.
let operator = match index {
1 => BinaryOperation::Add,
2 => BinaryOperation::AddWrapped,
_ => unimplemented!("throw error for invalid method call")
};
// Parse left parenthesis `(`.
self.expect(&Token::LeftParen)?;
// Parse operand.
let operand = self.parse_expression()?;
// Parse close parenthesis `)`.
let right_span = self.expect(&Token::RightParen)?;
return Ok(Expression::Binary(BinaryExpression {
span: receiver.span() + right_span,
op: operator,
left: Box::new(receiver),
right: Box::new(operand),
}))
} else {
// handle core module operators `commit`, `hash` etc.
unimplemented!("throw error for invalid method call")
}
} else {
let curr = &self.token;
Err(ParserError::unexpected_str(&curr.token, "int or ident", curr.span).into())
}
}
/// Returns an [`Expression`] AST node if the next tokens represent an
/// array access, circuit member access, function call, or static function call expression.
///
@ -229,21 +271,7 @@ impl ParserContext<'_> {
let mut expr = self.parse_primary_expression()?;
loop {
if self.eat(&Token::Dot) {
// Handle method call expression.
if let Some(method) = self.eat_identifier() {
if self.check(&Token::LeftParen) {
let (arguments, _, span) = self.parse_paren_comma_list(|p| p.parse_expression().map(Some))?;
expr = Expression::Method(MethodCallExpression {
span: expr.span() + span,
receiver: Box::new(expr),
method,
arguments,
});
continue;
}
}
let curr = &self.token;
return Err(ParserError::unexpected_str(&curr.token, "int or ident", curr.span).into());
expr = self.parse_method_call_expression(expr)?
}
if !self.check(&Token::LeftParen) {
break;

View File

@ -311,6 +311,13 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
Some(Type::Boolean)
}
BinaryOperation::AddWrapped => {
self.visitor.assert_int_type(expected, input.span);
let t1 = self.visit_expression(&input.left, expected);
let t2 = self.visit_expression(&input.right, expected);
return_incorrect_type(t1, t2, expected)
}
};
}

View File

@ -138,6 +138,11 @@ impl<'a> TypeChecker<'a> {
}
}
/// Emits an error to the handler if the given type is not an integer.
pub(crate) fn assert_int_type(&self, type_: &Option<Type>, span: Span) {
self.assert_one_of_types(type_, &INT_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

@ -100,30 +100,51 @@ macro_rules! symbols {
}
symbols! {
// unary operators
// binary operators
add,
add_w,
// arity three operators
// types
address,
AlwaysConst,
array,
assert,
bool,
Class: "class",
context,
constants,
CoreFunction,
console,
Const: "const",
Constant,
Else: "else",
error,
False: "false",
constants,
field,
For: "for",
function,
group,
i8,
i16,
i32,
i64,
i128,
scalar,
string,
u8,
u16,
u32,
u64,
u128,
// values
False: "false",
True: "true",
// general keywords
AlwaysConst,
assert,
Class: "class",
context,
CoreFunction,
console,
Else: "else",
error,
For: "for",
function,
If: "if",
In: "in",
input,
@ -134,19 +155,11 @@ symbols! {
prelude,
Public,
Return: "return",
scalar,
Star: "*",
std,
string,
Struct: "struct",
test,
True: "true",
Type: "type",
u8,
u16,
u32,
u64,
u128,
CONTAINER_PSEUDO_CIRCUIT: "$InputContainer",
REGISTERS_PSEUDO_CIRCUIT: "$InputRegister",
@ -154,11 +167,11 @@ symbols! {
STATE_PSEUDO_CIRCUIT: "$InputState",
STATE_LEAF_PSEUDO_CIRCUIT: "$InputStateLeaf",
// input file
registers,
record,
state,
state_leaf,
public,
private,
}