1470 lines
48 KiB
Rust
1470 lines
48 KiB
Rust
use crate::ast::assign_statement::AssignStatement;
|
|
use crate::ast::binary_expression::{BinaryExpression, BinaryOperation};
|
|
use crate::ast::call::Call;
|
|
use crate::ast::class::Class;
|
|
use crate::ast::compilation_unit::CompilationUnit;
|
|
use crate::ast::constructor::Constructor;
|
|
use crate::ast::double_literal::DoubleLiteral;
|
|
use crate::ast::expression::Expression;
|
|
use crate::ast::expression_statement::ExpressionStatement;
|
|
use crate::ast::extern_function::ExternFunction;
|
|
use crate::ast::field::Field;
|
|
use crate::ast::function::Function;
|
|
use crate::ast::generic_parameter::GenericParameter;
|
|
use crate::ast::identifier::Identifier;
|
|
use crate::ast::integer_literal::IntegerLiteral;
|
|
use crate::ast::let_statement::LetStatement;
|
|
use crate::ast::negative_expression::NegativeExpression;
|
|
use crate::ast::parameter::Parameter;
|
|
use crate::ast::statement::Statement;
|
|
use crate::ast::string_literal::StringLiteral;
|
|
use crate::ast::type_use::TypeUse;
|
|
use crate::diagnostic::Diagnostic;
|
|
use crate::lexer::Lexer;
|
|
use crate::source_range::SourceRange;
|
|
use crate::token::{Token, TokenKind};
|
|
use std::str::FromStr;
|
|
|
|
pub fn parse_compilation_unit(input: &str) -> Result<CompilationUnit, Vec<Diagnostic>> {
|
|
let mut parser = Parser::new(input);
|
|
parser.compilation_unit()
|
|
}
|
|
|
|
pub fn parse_expression(input: &str) -> Result<Expression, Vec<Diagnostic>> {
|
|
let mut parser = Parser::new(input);
|
|
parser.advance(); // get started
|
|
parser.expression()
|
|
}
|
|
|
|
macro_rules! matches_expression_first {
|
|
( $token_kind : expr ) => {
|
|
matches!(
|
|
$token_kind,
|
|
TokenKind::IntegerLiteral
|
|
| TokenKind::DoubleLiteral
|
|
| TokenKind::LongLiteral
|
|
| TokenKind::String
|
|
| TokenKind::Minus
|
|
| TokenKind::SelfKw
|
|
| TokenKind::Identifier
|
|
)
|
|
};
|
|
}
|
|
|
|
macro_rules! matches_statement_first {
|
|
( $token_kind : expr ) => {
|
|
matches!($token_kind, TokenKind::Let) || matches_expression_first!($token_kind)
|
|
};
|
|
}
|
|
|
|
macro_rules! matches_type_use_first {
|
|
( $token_kind: expr ) => {
|
|
matches!($token_kind, TokenKind::LeftSquare | TokenKind::Identifier)
|
|
};
|
|
}
|
|
|
|
struct Parser<'a> {
|
|
input: &'a str,
|
|
lexer: Lexer<'a>,
|
|
current: Option<Token>,
|
|
lookahead: Option<Token>,
|
|
}
|
|
|
|
impl<'a> Parser<'a> {
|
|
fn new(input: &'a str) -> Self {
|
|
Self {
|
|
input,
|
|
lexer: Lexer::new(input),
|
|
current: None,
|
|
lookahead: None,
|
|
}
|
|
}
|
|
|
|
fn advance_until(&mut self, token_kinds: &[TokenKind]) {
|
|
while self.current.is_some() {
|
|
self.advance();
|
|
match &self.current {
|
|
None => {
|
|
// reached eoi
|
|
}
|
|
Some(current) => {
|
|
if token_kinds.contains(¤t.kind()) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn advance(&mut self) {
|
|
if self.lookahead.is_some() {
|
|
// we've advanced at least once
|
|
self.current = self.lookahead.take();
|
|
self.lookahead = match self.lexer.next() {
|
|
None => None,
|
|
Some(result) => match result {
|
|
Ok(token) => Some(token),
|
|
Err(lexer_error) => {
|
|
panic!("{:?}", lexer_error);
|
|
}
|
|
},
|
|
}
|
|
} else if self.lookahead.is_none() && self.current.is_some() {
|
|
// we're on the last token
|
|
self.current = None;
|
|
} else {
|
|
// we've not yet advanced, so fetch both
|
|
// current
|
|
match self.lexer.next() {
|
|
None => {}
|
|
Some(result) => match result {
|
|
Ok(token) => {
|
|
self.current = Some(token);
|
|
}
|
|
Err(lexer_error) => {
|
|
panic!("{:?}", lexer_error);
|
|
}
|
|
},
|
|
}
|
|
// lookahead
|
|
match self.lexer.next() {
|
|
None => {}
|
|
Some(result) => match result {
|
|
Ok(token) => {
|
|
self.lookahead = Some(token);
|
|
}
|
|
Err(lexer_error) => {
|
|
panic!("{:?}", lexer_error);
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn expect_advance(&mut self, token_kind: TokenKind) -> Result<Token, Vec<Diagnostic>> {
|
|
match self.current.take() {
|
|
None => Err(vec![
|
|
Diagnostic::new(
|
|
&format!("Expected {:?} but found end-of-input.", token_kind),
|
|
self.input.len(),
|
|
self.input.len(),
|
|
)
|
|
.with_reporter(file!(), line!()),
|
|
]),
|
|
Some(token) => {
|
|
if token.kind() == token_kind {
|
|
self.advance();
|
|
Ok(token)
|
|
} else {
|
|
self.advance_until(&[token_kind]);
|
|
Err(vec![
|
|
Diagnostic::new(
|
|
&format!("Expected {:?} but found {:?}", token_kind, token.kind()),
|
|
token.start(),
|
|
token.end(),
|
|
)
|
|
.with_reporter(file!(), line!()),
|
|
])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn expect_position_advance(
|
|
&mut self,
|
|
token_kind: TokenKind,
|
|
start_position: usize,
|
|
) -> Result<Token, Vec<Diagnostic>> {
|
|
let matched = self.expect_advance(token_kind)?;
|
|
if matched.start() == start_position {
|
|
Ok(matched)
|
|
} else {
|
|
Err(vec![
|
|
Diagnostic::new(
|
|
&format!("Expected {:?} but found {:?}", token_kind, matched.kind()),
|
|
matched.start(),
|
|
matched.end(),
|
|
)
|
|
.with_reporter(file!(), line!()),
|
|
])
|
|
}
|
|
}
|
|
|
|
fn peek_current(&self, token_kind: TokenKind) -> bool {
|
|
match &self.current {
|
|
None => panic!("Unexpected end of input."),
|
|
Some(token) => token.kind() == token_kind,
|
|
}
|
|
}
|
|
|
|
fn get_current(&self) -> &Token {
|
|
match &self.current {
|
|
None => {
|
|
panic!("Unexpected end of input");
|
|
}
|
|
Some(token) => token,
|
|
}
|
|
}
|
|
|
|
fn peek_lookahead(&self, token_kind: TokenKind) -> bool {
|
|
match &self.lookahead {
|
|
None => panic!("Unexpected end of input."),
|
|
Some(token) => token.kind() == token_kind,
|
|
}
|
|
}
|
|
|
|
fn sample_input(&self, start: usize, end: usize) -> &'a str {
|
|
&self.input[start..end]
|
|
}
|
|
|
|
fn token_text(&self, token: &Token) -> &'a str {
|
|
self.sample_input(token.start(), token.end())
|
|
}
|
|
|
|
pub fn compilation_unit(&mut self) -> Result<CompilationUnit, Vec<Diagnostic>> {
|
|
let mut functions: Vec<Function> = vec![];
|
|
let mut extern_functions: Vec<ExternFunction> = vec![];
|
|
let mut classes: Vec<Class> = vec![];
|
|
|
|
let mut diagnostics = vec![];
|
|
|
|
self.advance(); // get started
|
|
|
|
while self.current.is_some() {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::Fn | TokenKind::Extern | TokenKind::Class => {
|
|
match self.module_level_declaration(
|
|
&mut functions,
|
|
&mut extern_functions,
|
|
&mut classes,
|
|
) {
|
|
Ok(_) => {}
|
|
Err(mut declaration_diagnostics) => {
|
|
diagnostics.append(&mut declaration_diagnostics)
|
|
}
|
|
}
|
|
}
|
|
_ => {
|
|
diagnostics.push(Diagnostic::new(
|
|
&format!(
|
|
"Expected any of {:?}; found {:?}",
|
|
[TokenKind::Fn, TokenKind::Extern, TokenKind::Class],
|
|
current.kind()
|
|
),
|
|
current.start(),
|
|
current.end(),
|
|
));
|
|
self.advance_until(&[TokenKind::Fn, TokenKind::Extern]);
|
|
}
|
|
}
|
|
}
|
|
if diagnostics.is_empty() {
|
|
Ok(CompilationUnit::new(functions, extern_functions, classes))
|
|
} else {
|
|
Err(diagnostics)
|
|
}
|
|
}
|
|
|
|
fn module_level_declaration(
|
|
&mut self,
|
|
functions: &mut Vec<Function>,
|
|
extern_functions: &mut Vec<ExternFunction>,
|
|
classes: &mut Vec<Class>,
|
|
) -> Result<(), Vec<Diagnostic>> {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::Fn => {
|
|
let function_result = self.function();
|
|
match function_result {
|
|
Ok(function) => {
|
|
functions.push(function);
|
|
Ok(())
|
|
}
|
|
Err(function_diagnostics) => Err(function_diagnostics),
|
|
}
|
|
}
|
|
TokenKind::Extern => {
|
|
let extern_function_result = self.extern_function();
|
|
match extern_function_result {
|
|
Ok(extern_function) => {
|
|
extern_functions.push(extern_function);
|
|
Ok(())
|
|
}
|
|
Err(extern_function_diagnostics) => Err(extern_function_diagnostics),
|
|
}
|
|
}
|
|
TokenKind::Class => match self.class() {
|
|
Ok(class) => {
|
|
classes.push(class);
|
|
Ok(())
|
|
}
|
|
Err(class_diagnostics) => Err(class_diagnostics),
|
|
},
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn function(&mut self) -> Result<Function, Vec<Diagnostic>> {
|
|
let is_public = if self.current.is_some() && self.peek_current(TokenKind::Public) {
|
|
self.advance(); // pub
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
self.expect_advance(TokenKind::Fn)?;
|
|
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
|
|
|
self.expect_advance(TokenKind::LeftParentheses)?;
|
|
let parameters = self.parameter_list()?;
|
|
self.expect_advance(TokenKind::RightParentheses)?;
|
|
|
|
let mut diagnostics = vec![];
|
|
|
|
let return_type = if self.current.is_some() && self.peek_current(TokenKind::RightArrow) {
|
|
match self.return_type() {
|
|
Ok(type_use) => Some(type_use),
|
|
Err(mut return_type_diagnostics) => {
|
|
diagnostics.append(&mut return_type_diagnostics);
|
|
None
|
|
}
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let mut statements = vec![];
|
|
while self.current.is_some() && !self.peek_current(TokenKind::End) {
|
|
let statement_result = self.statement();
|
|
match statement_result {
|
|
Ok(statement) => {
|
|
statements.push(statement);
|
|
}
|
|
Err(mut statement_diagnostics) => {
|
|
diagnostics.append(&mut statement_diagnostics);
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we're missing "end", append it to the other statement diagnostics
|
|
let end_result = self.expect_advance(TokenKind::End);
|
|
match end_result {
|
|
Err(mut end_diagnostics) => {
|
|
diagnostics.append(&mut end_diagnostics);
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
if diagnostics.is_empty() {
|
|
Ok(Function::new(
|
|
self.token_text(&identifier_token),
|
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
|
is_public,
|
|
parameters,
|
|
return_type,
|
|
statements,
|
|
))
|
|
} else {
|
|
Err(diagnostics)
|
|
}
|
|
}
|
|
|
|
fn extern_function(&mut self) -> Result<ExternFunction, Vec<Diagnostic>> {
|
|
self.expect_advance(TokenKind::Extern)?;
|
|
self.expect_advance(TokenKind::Fn)?;
|
|
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
|
self.expect_advance(TokenKind::LeftParentheses)?;
|
|
|
|
let mut diagnostics = vec![];
|
|
|
|
let mut maybe_parameters: Option<Vec<Parameter>> = None;
|
|
let params_result = self.parameter_list();
|
|
match params_result {
|
|
Ok(parameters) => {
|
|
maybe_parameters = Some(parameters);
|
|
}
|
|
Err(mut parameter_list_diagnostics) => {
|
|
diagnostics.append(&mut parameter_list_diagnostics);
|
|
}
|
|
}
|
|
|
|
let right_parentheses_result = self.expect_advance(TokenKind::RightParentheses);
|
|
match right_parentheses_result {
|
|
Err(mut right_parentheses_diagnostics) => {
|
|
diagnostics.append(&mut right_parentheses_diagnostics);
|
|
}
|
|
Ok(_) => {}
|
|
}
|
|
|
|
let return_type = self.return_type()?;
|
|
|
|
if diagnostics.is_empty() {
|
|
Ok(ExternFunction::new(
|
|
self.token_text(&identifier_token),
|
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
|
maybe_parameters.unwrap(),
|
|
return_type,
|
|
))
|
|
} else {
|
|
Err(diagnostics)
|
|
}
|
|
}
|
|
|
|
fn class(&mut self) -> Result<Class, Vec<Diagnostic>> {
|
|
self.expect_advance(TokenKind::Class)?;
|
|
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
|
let mut generic_parameters: Vec<GenericParameter> = vec![];
|
|
let mut fields = vec![];
|
|
let mut functions = vec![];
|
|
let mut maybe_constructor: Option<Constructor> = None;
|
|
|
|
let mut diagnostics = vec![];
|
|
|
|
while self.current.is_some() && !self.peek_current(TokenKind::End) {
|
|
match self.get_current().kind() {
|
|
TokenKind::Public => match self.public_class_member(
|
|
&mut fields,
|
|
&mut functions,
|
|
&mut maybe_constructor,
|
|
) {
|
|
Ok(_) => {}
|
|
Err(mut member_diagnostics) => diagnostics.append(&mut member_diagnostics),
|
|
},
|
|
TokenKind::Mut | TokenKind::Identifier => match self.field() {
|
|
Ok(field) => fields.push(field),
|
|
Err(mut field_diagnostics) => diagnostics.append(&mut field_diagnostics),
|
|
},
|
|
TokenKind::Fn => match self.function() {
|
|
Ok(function) => functions.push(function),
|
|
Err(mut function_diagnostics) => diagnostics.append(&mut function_diagnostics),
|
|
},
|
|
TokenKind::Ctor => match self.constructor() {
|
|
Ok(constructor) => {
|
|
maybe_constructor = Some(constructor);
|
|
}
|
|
Err(mut ctor_diagnostics) => diagnostics.append(&mut ctor_diagnostics),
|
|
},
|
|
TokenKind::Lt => match self.generic_parameters() {
|
|
Ok(parsed_params) => {
|
|
generic_parameters = parsed_params;
|
|
}
|
|
Err(mut generic_parameters_diagnostics) => {
|
|
diagnostics.append(&mut generic_parameters_diagnostics);
|
|
}
|
|
},
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
self.expect_advance(TokenKind::End)?;
|
|
|
|
if diagnostics.is_empty() {
|
|
Ok(Class::new(
|
|
self.token_text(&identifier_token),
|
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
|
generic_parameters,
|
|
maybe_constructor,
|
|
fields,
|
|
functions,
|
|
))
|
|
} else {
|
|
Err(diagnostics)
|
|
}
|
|
}
|
|
|
|
fn parameter_list(&mut self) -> Result<Vec<Parameter>, Vec<Diagnostic>> {
|
|
let mut parameters = vec![];
|
|
let mut diagnostics = vec![];
|
|
while self.current.is_some() && self.peek_current(TokenKind::Identifier) {
|
|
let parameter_result = self.parameter();
|
|
match parameter_result {
|
|
Ok(parameter) => {
|
|
parameters.push(parameter);
|
|
}
|
|
Err(mut parameter_diagnostics) => {
|
|
diagnostics.append(&mut parameter_diagnostics);
|
|
}
|
|
}
|
|
if self.current.is_some() && self.peek_current(TokenKind::Comma) {
|
|
self.advance();
|
|
}
|
|
}
|
|
if diagnostics.is_empty() {
|
|
Ok(parameters)
|
|
} else {
|
|
Err(diagnostics)
|
|
}
|
|
}
|
|
|
|
fn parameter(&mut self) -> Result<Parameter, Vec<Diagnostic>> {
|
|
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
|
self.expect_advance(TokenKind::Colon)?;
|
|
let type_use = self.type_use()?;
|
|
Ok(Parameter::new(
|
|
self.token_text(&identifier_token),
|
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
|
type_use,
|
|
))
|
|
}
|
|
|
|
fn return_type(&mut self) -> Result<TypeUse, Vec<Diagnostic>> {
|
|
self.expect_advance(TokenKind::RightArrow)?;
|
|
self.type_use()
|
|
}
|
|
|
|
fn type_use(&mut self) -> Result<TypeUse, Vec<Diagnostic>> {
|
|
if self.current.is_some() {
|
|
let current = self.get_current();
|
|
return match current.kind() {
|
|
TokenKind::LeftSquare => {
|
|
self.advance(); // [
|
|
let inner_type_use = self.type_use()?;
|
|
self.expect_advance(TokenKind::RightSquare)?;
|
|
todo!()
|
|
}
|
|
TokenKind::Identifier => {
|
|
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
|
|
let generic_arguments =
|
|
if self.current.is_some() && self.peek_current(TokenKind::Lt) {
|
|
self.advance(); // <
|
|
let generic_arguments = self.generic_arguments_list()?;
|
|
self.expect_advance(TokenKind::Gt)?; // >
|
|
generic_arguments
|
|
} else {
|
|
vec![]
|
|
};
|
|
Ok(TypeUse::new(
|
|
self.token_text(&identifier_token),
|
|
SourceRange::new(identifier_token.start(), identifier_token.end()),
|
|
generic_arguments,
|
|
))
|
|
}
|
|
_ => Err(vec![Diagnostic::new(
|
|
&format!(
|
|
"Expected LeftSquare or Identifier; found: {:?}",
|
|
current.kind()
|
|
),
|
|
current.start(),
|
|
current.end(),
|
|
)]),
|
|
};
|
|
}
|
|
Err(vec![Diagnostic::new(
|
|
"Expected LeftSquare or Identifier; found end of input.",
|
|
self.input.len(),
|
|
self.input.len(),
|
|
)])
|
|
}
|
|
|
|
fn generic_arguments_list(&mut self) -> Result<Vec<TypeUse>, Vec<Diagnostic>> {
|
|
let mut generic_arguments: Vec<TypeUse> = vec![];
|
|
while self.current.is_some() && matches_type_use_first!(self.get_current().kind()) {
|
|
generic_arguments.push(self.type_use()?);
|
|
if self.current.is_some() && self.peek_current(TokenKind::Comma) {
|
|
self.advance(); // comma
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
Ok(generic_arguments)
|
|
}
|
|
|
|
fn generic_parameters(&mut self) -> Result<Vec<GenericParameter>, Vec<Diagnostic>> {
|
|
self.expect_advance(TokenKind::Lt)?;
|
|
let mut parameters: Vec<GenericParameter> = vec![];
|
|
while self.current.is_some() && self.peek_current(TokenKind::Identifier) {
|
|
parameters.push(self.generic_parameter()?);
|
|
if self.current.is_some() && self.peek_current(TokenKind::Plus) {
|
|
self.advance(); // +
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
self.expect_advance(TokenKind::Gt)?;
|
|
Ok(parameters)
|
|
}
|
|
|
|
fn generic_parameter(&mut self) -> Result<GenericParameter, Vec<Diagnostic>> {
|
|
let identifier = self.expect_advance(TokenKind::Identifier)?;
|
|
let mut extends_list: Vec<TypeUse> = vec![];
|
|
if self.current.is_some() && self.peek_current(TokenKind::Colon) {
|
|
self.advance(); // :
|
|
while self.current.is_some() && matches_type_use_first!(self.get_current().kind()) {
|
|
extends_list.push(self.type_use()?);
|
|
if self.current.is_some() && self.peek_current(TokenKind::Comma) {
|
|
self.advance(); // ,
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Ok(GenericParameter::new(
|
|
self.token_text(&identifier),
|
|
SourceRange::new(identifier.start(), identifier.end()),
|
|
extends_list,
|
|
))
|
|
}
|
|
|
|
fn public_class_member(
|
|
&mut self,
|
|
fields: &mut Vec<Field>,
|
|
functions: &mut Vec<Function>,
|
|
maybe_ctor: &mut Option<Constructor>,
|
|
) -> Result<(), Vec<Diagnostic>> {
|
|
if self.lookahead.is_some() {
|
|
match self.lookahead.as_ref().unwrap().kind() {
|
|
TokenKind::Mut | TokenKind::Identifier => {
|
|
fields.push(self.field()?);
|
|
}
|
|
TokenKind::Fn => functions.push(self.function()?),
|
|
TokenKind::Ctor => {
|
|
maybe_ctor.replace(self.constructor()?);
|
|
}
|
|
_ => {
|
|
let lookahead = self.lookahead.as_ref().unwrap();
|
|
return Err(vec![Diagnostic::new(
|
|
&format!(
|
|
"Expected any of {:?}; found {:?}",
|
|
[TokenKind::Mut, TokenKind::Identifier, TokenKind::Fn],
|
|
lookahead.kind()
|
|
),
|
|
lookahead.start(),
|
|
lookahead.end(),
|
|
)]);
|
|
}
|
|
}
|
|
Ok(())
|
|
} else {
|
|
let current = self.current.as_ref().unwrap();
|
|
Err(vec![Diagnostic::new(
|
|
&format!(
|
|
"Expected any of {:?}; found end-of-input.",
|
|
[TokenKind::Mut, TokenKind::Identifier, TokenKind::Fn]
|
|
),
|
|
current.end(),
|
|
current.end(),
|
|
)])
|
|
}
|
|
}
|
|
|
|
fn constructor(&mut self) -> Result<Constructor, Vec<Diagnostic>> {
|
|
let is_public = if self.current.is_some() && self.peek_current(TokenKind::Public) {
|
|
self.advance();
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let ctor_keyword = self.expect_advance(TokenKind::Ctor)?;
|
|
self.expect_advance(TokenKind::LeftParentheses)?;
|
|
|
|
let parameters = if self.current.is_some() && self.peek_current(TokenKind::Identifier) {
|
|
self.parameter_list()?
|
|
} else {
|
|
vec![]
|
|
};
|
|
|
|
self.expect_advance(TokenKind::RightParentheses)?;
|
|
|
|
// statements
|
|
let mut statements: Vec<Statement> = vec![];
|
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
|
|
|
while self.current.is_some()
|
|
&& matches_statement_first!(self.current.as_ref().unwrap().kind())
|
|
{
|
|
match self.statement() {
|
|
Ok(statement) => {
|
|
statements.push(statement);
|
|
}
|
|
Err(mut statement_diagnostics) => {
|
|
diagnostics.append(&mut statement_diagnostics);
|
|
}
|
|
}
|
|
}
|
|
|
|
self.expect_advance(TokenKind::End)?;
|
|
|
|
if diagnostics.is_empty() {
|
|
Ok(Constructor::new(
|
|
is_public,
|
|
SourceRange::new(ctor_keyword.start(), ctor_keyword.end()),
|
|
parameters,
|
|
statements,
|
|
))
|
|
} else {
|
|
Err(diagnostics)
|
|
}
|
|
}
|
|
|
|
fn field(&mut self) -> Result<Field, Vec<Diagnostic>> {
|
|
let is_public = if self.current.is_some() && self.peek_current(TokenKind::Public) {
|
|
self.advance();
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let is_mut = if self.current.is_some() && self.peek_current(TokenKind::Mut) {
|
|
self.advance();
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let identifier = self.expect_advance(TokenKind::Identifier)?;
|
|
|
|
let declared_type = if self.current.is_some() && self.peek_current(TokenKind::Colon) {
|
|
self.advance(); // colon
|
|
Some(self.type_use()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let initializer = if self.current.is_some() && self.peek_current(TokenKind::Equals) {
|
|
self.advance(); // equals
|
|
Some(self.expression()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Ok(Field::new(
|
|
self.token_text(&identifier),
|
|
SourceRange::new(identifier.start(), identifier.end()),
|
|
is_public,
|
|
is_mut,
|
|
declared_type,
|
|
initializer,
|
|
))
|
|
}
|
|
|
|
fn statement(&mut self) -> Result<Statement, Vec<Diagnostic>> {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::Let => Ok(Statement::Let(self.let_statement()?)),
|
|
_ => self.expression_statement_or_assign_statement(),
|
|
}
|
|
}
|
|
|
|
fn let_statement(&mut self) -> Result<LetStatement, Vec<Diagnostic>> {
|
|
self.expect_advance(TokenKind::Let)?;
|
|
|
|
let is_mut = if self.current.is_some() && self.peek_current(TokenKind::Mut) {
|
|
self.advance();
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let identifier = self.expect_advance(TokenKind::Identifier)?;
|
|
self.expect_advance(TokenKind::Equals)?;
|
|
let expression = self.expression()?;
|
|
Ok(LetStatement::new(
|
|
self.token_text(&identifier),
|
|
SourceRange::new(identifier.start(), identifier.end()),
|
|
is_mut,
|
|
expression,
|
|
))
|
|
}
|
|
|
|
fn expression_statement_or_assign_statement(&mut self) -> Result<Statement, Vec<Diagnostic>> {
|
|
let base = self.expression()?;
|
|
if self.current.is_some() && self.peek_current(TokenKind::Equals) {
|
|
Ok(Statement::Assign(self.assign_rhs(base)?))
|
|
} else {
|
|
Ok(Statement::Expression(ExpressionStatement::new(base)))
|
|
}
|
|
}
|
|
|
|
fn assign_rhs(&mut self, destination: Expression) -> Result<AssignStatement, Vec<Diagnostic>> {
|
|
self.expect_advance(TokenKind::Equals)?;
|
|
let value = self.expression()?;
|
|
Ok(AssignStatement::new(destination, value))
|
|
}
|
|
|
|
fn expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
self.bitwise_or_expression()
|
|
}
|
|
|
|
fn bitwise_or_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
let mut result = self.bitwise_xor_expression()?;
|
|
while self.current.is_some() && self.peek_current(TokenKind::Bar) {
|
|
self.advance(); // |
|
|
let rhs = self.bitwise_xor_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::BitwiseOr,
|
|
source_range,
|
|
));
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn bitwise_xor_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
let mut result = self.bitwise_and_expression()?;
|
|
while self.current.is_some() && self.peek_current(TokenKind::Caret) {
|
|
self.advance(); // ^
|
|
let rhs = self.bitwise_and_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::BitwiseXor,
|
|
source_range,
|
|
));
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn bitwise_and_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
let mut result = self.shift_expression()?;
|
|
while self.current.is_some() && self.peek_current(TokenKind::Ampersand) {
|
|
self.advance(); // &
|
|
let rhs = self.shift_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::BitwiseAnd,
|
|
source_range,
|
|
));
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn shift_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
let mut result = self.additive_expression()?;
|
|
while self.current.is_some() {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::Lt => {
|
|
let second_lt_start = current.start() + 1;
|
|
self.advance(); // first <
|
|
self.expect_position_advance(TokenKind::Lt, second_lt_start)?; // second <
|
|
let rhs = self.additive_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::LeftShift,
|
|
source_range,
|
|
));
|
|
}
|
|
TokenKind::Gt => {
|
|
let second_gt_start = current.start() + 1;
|
|
self.advance(); // first >
|
|
self.expect_position_advance(TokenKind::Gt, second_gt_start)?; // second gt
|
|
let rhs = self.additive_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::RightShift,
|
|
source_range,
|
|
));
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn additive_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
let mut result = self.multiplicative_expression()?;
|
|
while self.current.is_some() {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::Plus => {
|
|
self.advance(); // plus
|
|
let rhs = self.multiplicative_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::Add,
|
|
source_range,
|
|
));
|
|
}
|
|
TokenKind::Minus => {
|
|
self.advance(); // minus
|
|
let rhs = self.multiplicative_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::Subtract,
|
|
source_range,
|
|
));
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn multiplicative_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
let mut result = self.prefix_expression()?;
|
|
while self.current.is_some() {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::Star => {
|
|
self.advance(); // multiply
|
|
let rhs = self.prefix_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::Multiply,
|
|
source_range,
|
|
));
|
|
}
|
|
TokenKind::Slash => {
|
|
self.advance(); // slash
|
|
let rhs = self.prefix_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::Divide,
|
|
source_range,
|
|
))
|
|
}
|
|
TokenKind::Modulo => {
|
|
self.advance(); // modulo
|
|
let rhs = self.prefix_expression()?;
|
|
let source_range =
|
|
SourceRange::new(result.source_range().start(), rhs.source_range().end());
|
|
result = Expression::Binary(BinaryExpression::new(
|
|
result,
|
|
rhs,
|
|
BinaryOperation::Modulo,
|
|
source_range,
|
|
));
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn prefix_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
// first, collect all consecutive operators
|
|
let mut operator_tokens = vec![];
|
|
while self.current.is_some() {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::Minus => {
|
|
operator_tokens.push(current.clone()); // unfortunately necessary
|
|
self.advance();
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
|
|
// now go in reverse and build up expressions
|
|
// the parser is currently just after the prefix operators, so we need a suffix expression
|
|
// as a base
|
|
let mut result = self.suffix_expression()?;
|
|
while let Some(operator_token) = operator_tokens.pop() {
|
|
let source_range =
|
|
SourceRange::new(operator_token.start(), result.source_range().end());
|
|
match operator_token.kind() {
|
|
TokenKind::Minus => {
|
|
result = Expression::Negative(NegativeExpression::new(result, source_range));
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn suffix_expression(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
let mut result = self.expression_base()?;
|
|
while self.current.is_some() {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::LeftParentheses => {
|
|
result = Expression::Call(self.call(result)?);
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn expression_base(&mut self) -> Result<Expression, Vec<Diagnostic>> {
|
|
let current = self.get_current();
|
|
match current.kind() {
|
|
TokenKind::IntegerLiteral => {
|
|
let raw = self.token_text(¤t);
|
|
let source_range = SourceRange::new(current.start(), current.end());
|
|
self.advance();
|
|
Ok(Expression::Integer(IntegerLiteral::new(
|
|
i32::from_str(raw).unwrap(),
|
|
source_range,
|
|
)))
|
|
}
|
|
TokenKind::DoubleLiteral => {
|
|
let raw = self.token_text(¤t);
|
|
let source_range = SourceRange::new(current.start(), current.end());
|
|
self.advance();
|
|
Ok(Expression::Double(DoubleLiteral::new(
|
|
f64::from_str(raw).unwrap(),
|
|
source_range,
|
|
)))
|
|
}
|
|
TokenKind::String => {
|
|
let with_quotes = self.token_text(¤t);
|
|
let source_range = SourceRange::new(current.start(), current.end());
|
|
self.advance();
|
|
Ok(Expression::String(StringLiteral::new(
|
|
&with_quotes[1..with_quotes.len() - 1],
|
|
source_range,
|
|
)))
|
|
}
|
|
TokenKind::Identifier => {
|
|
let declared_name = self.token_text(¤t);
|
|
let source_range = SourceRange::new(current.start(), current.end());
|
|
self.advance();
|
|
Ok(Expression::Identifier(Identifier::new(
|
|
declared_name,
|
|
source_range,
|
|
)))
|
|
}
|
|
_ => unreachable!("Unreachable token type found: {:?}", current.kind()),
|
|
}
|
|
}
|
|
|
|
fn call(&mut self, callee: Expression) -> Result<Call, Vec<Diagnostic>> {
|
|
self.expect_advance(TokenKind::LeftParentheses)?;
|
|
let mut arguments = vec![];
|
|
if let Some(current) = &self.current {
|
|
if matches_expression_first!(current.kind()) {
|
|
arguments.append(&mut self.expression_list()?);
|
|
}
|
|
}
|
|
let right_parentheses_token = self.expect_advance(TokenKind::RightParentheses)?;
|
|
let source_range =
|
|
SourceRange::new(callee.source_range().start(), right_parentheses_token.end());
|
|
Ok(Call::new(callee, arguments, source_range))
|
|
}
|
|
|
|
fn expression_list(&mut self) -> Result<Vec<Expression>, Vec<Diagnostic>> {
|
|
let mut expressions = vec![];
|
|
expressions.push(self.expression()?);
|
|
while self.current.is_some() && self.peek_current(TokenKind::Comma) {
|
|
self.advance(); // comma
|
|
expressions.push(self.expression()?);
|
|
}
|
|
Ok(expressions)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod smoke_tests {
|
|
use super::*;
|
|
|
|
fn smoke_test(input: &str) {
|
|
let parse_result = parse_compilation_unit(input);
|
|
match parse_result {
|
|
Ok(_) => {}
|
|
Err(diagnostics) => {
|
|
eprintln!("{:#?}", diagnostics);
|
|
panic!("There were diagnostics during parsing");
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn forty_two() {
|
|
smoke_test("fn main() 42 end");
|
|
}
|
|
|
|
#[test]
|
|
fn chained_calls() {
|
|
smoke_test("fn main() getCl()() end");
|
|
}
|
|
|
|
#[test]
|
|
fn extern_fn_with_param() {
|
|
smoke_test("extern fn println(message: Any) -> Void");
|
|
}
|
|
|
|
#[test]
|
|
fn fn_with_param() {
|
|
smoke_test("fn foo(bar: Int) end");
|
|
}
|
|
|
|
#[test]
|
|
fn fn_with_params() {
|
|
smoke_test("fn foo(bar: Int, baz: Int) end");
|
|
}
|
|
|
|
#[test]
|
|
fn return_type() {
|
|
smoke_test("fn foo() -> Int end")
|
|
}
|
|
|
|
#[test]
|
|
fn extern_return_type() {
|
|
smoke_test("extern fn foo() -> Int");
|
|
}
|
|
|
|
#[test]
|
|
fn add_two_numbers() {
|
|
smoke_test("fn main() 1 + 2 end");
|
|
}
|
|
|
|
#[test]
|
|
fn negative_return() {
|
|
smoke_test("fn main() -> Int -1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn negative_left_add() {
|
|
smoke_test("fn main() -> Int -1 + 1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn negative_right_add() {
|
|
smoke_test("fn main() -> Int 1 + -1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn two_negatives() {
|
|
smoke_test("fn main() -> Int -1 + -1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn minus_positive_number() {
|
|
smoke_test("fn main() -> Int 1 - 1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn minus_negative_number() {
|
|
smoke_test("fn main() -> Int 1 - -1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn empty_class() {
|
|
smoke_test("class Foo end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_pub_member() {
|
|
smoke_test("class Foo pub bar end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_mut_member() {
|
|
smoke_test("class Foo mut bar end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_nothing_member() {
|
|
smoke_test("class Foo bar end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_member_type_use() {
|
|
smoke_test("class Foo bar: Int end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_member_init() {
|
|
smoke_test("class Foo bar = 42 end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_member_type_use_and_init() {
|
|
smoke_test("class Foo bar: Int = 42 end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_member_all() {
|
|
smoke_test("class Foo pub mut bar: Bar = Baz() end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_pub_fn() {
|
|
smoke_test(
|
|
"
|
|
class Greeter
|
|
pub fn greet() end
|
|
end
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn simple_assign() {
|
|
smoke_test(
|
|
"
|
|
fn main()
|
|
let mut x = 4
|
|
x = 42
|
|
end
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn simple_multiply() {
|
|
smoke_test("fn main() 1 * 2 end");
|
|
}
|
|
|
|
#[test]
|
|
fn simple_divide() {
|
|
smoke_test("fn main() 1 / 2 end");
|
|
}
|
|
|
|
#[test]
|
|
fn simple_modulo() {
|
|
smoke_test("fn main() 1 % 2 end");
|
|
}
|
|
|
|
#[test]
|
|
fn simple_left_shift() {
|
|
smoke_test("fn main() 2 << 1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn simple_right_shift() {
|
|
smoke_test("fn main() 4 >> 1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn simple_bitwise_and() {
|
|
smoke_test("fn main() 2 & 1 end");
|
|
}
|
|
|
|
#[test]
|
|
fn simple_bitwise_xor() {
|
|
smoke_test("fn main() 1 ^ 2 end");
|
|
}
|
|
|
|
#[test]
|
|
fn simple_bitwise_or() {
|
|
smoke_test("fn main() 1 | 2 end");
|
|
}
|
|
|
|
#[test]
|
|
fn ops_left_to_right() {
|
|
smoke_test(
|
|
"
|
|
fn main()
|
|
1 | 2 ^ 3 & 4 << 5 >> 7 + 8 - 9 * 10 / 11 % 12
|
|
end
|
|
",
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn array_generic_arg() {
|
|
smoke_test("fn main(args: Array<String>) end");
|
|
}
|
|
|
|
#[test]
|
|
fn nested_generic_args() {
|
|
smoke_test("fn main(foo: Array<Bar<Foo>>) end");
|
|
}
|
|
|
|
#[test]
|
|
fn class_with_generic_param() {
|
|
smoke_test("class Foo<T> end");
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod concrete_tests {
|
|
use super::*;
|
|
|
|
fn report_diagnostics(diagnostics: &[Diagnostic]) -> ! {
|
|
for diagnostic in diagnostics {
|
|
eprintln!("{:?}", diagnostic);
|
|
}
|
|
panic!();
|
|
}
|
|
|
|
fn assert_compilation_unit(input: &str) -> CompilationUnit {
|
|
let parse_result = parse_compilation_unit(input);
|
|
match parse_result {
|
|
Ok(compilation_unit) => compilation_unit,
|
|
Err(diagnostics) => {
|
|
report_diagnostics(&diagnostics);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn assert_expression(input: &str) -> Expression {
|
|
let parse_result = parse_expression(input);
|
|
match parse_result {
|
|
Ok(expression) => expression,
|
|
Err(diagnostics) => {
|
|
report_diagnostics(&diagnostics);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn assert_function_in<'a>(
|
|
compilation_unit: &'a CompilationUnit,
|
|
function_name: &str,
|
|
) -> &'a Function {
|
|
compilation_unit
|
|
.functions()
|
|
.iter()
|
|
.find(|f| f.declared_name() == function_name)
|
|
.unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn parses_extern_fn() {
|
|
let compilation_unit = assert_compilation_unit("extern fn println() -> Void");
|
|
let extern_functions = compilation_unit.extern_functions();
|
|
assert_eq!(extern_functions.len(), 1);
|
|
let extern_function = &extern_functions[0];
|
|
assert_eq!(extern_function.declared_name(), "println");
|
|
}
|
|
|
|
#[test]
|
|
fn hello_world() {
|
|
let compilation_unit = assert_compilation_unit("fn main() println(\"Hello, World!\") end");
|
|
let function = assert_function_in(&compilation_unit, "main");
|
|
let statements = function.statements();
|
|
assert_eq!(statements.len(), 1);
|
|
if let Statement::Expression(expression_statement) = statements[0] {
|
|
if let Expression::Call(call) = expression_statement.expression() {
|
|
let callee = call.callee();
|
|
match callee {
|
|
Expression::Identifier(identifier) => {
|
|
assert_eq!(identifier.name(), "println");
|
|
}
|
|
_ => panic!("Expected identifier"),
|
|
}
|
|
let arguments = call.arguments();
|
|
assert_eq!(arguments.len(), 1);
|
|
let first_argument = arguments[0];
|
|
match first_argument {
|
|
Expression::String(s) => {
|
|
assert_eq!(s.content(), "Hello, World!");
|
|
}
|
|
_ => panic!("Expected string"),
|
|
}
|
|
} else {
|
|
panic!("Expected call");
|
|
}
|
|
} else {
|
|
panic!("Expected expression");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn negative_expression() {
|
|
let expression = assert_expression("-1");
|
|
match expression {
|
|
Expression::Negative(negative_expression) => match negative_expression.operand() {
|
|
Expression::Integer(integer_literal) => {
|
|
assert_eq!(integer_literal.value(), 1);
|
|
}
|
|
_ => panic!("Expected integer literal"),
|
|
},
|
|
_ => panic!("Expected negative expression"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn add_negative() {
|
|
let expression = assert_expression("1 + -1");
|
|
match expression {
|
|
Expression::Binary(binary_expression) => {
|
|
assert!(matches!(binary_expression.op(), BinaryOperation::Add));
|
|
match binary_expression.lhs() {
|
|
Expression::Integer(integer_literal) => {
|
|
assert_eq!(integer_literal.value(), 1);
|
|
}
|
|
_ => panic!("Expected integer literal"),
|
|
}
|
|
match binary_expression.rhs() {
|
|
Expression::Negative(negative_expression) => {
|
|
match negative_expression.operand() {
|
|
Expression::Integer(integer_literal) => {
|
|
assert_eq!(integer_literal.value(), 1);
|
|
}
|
|
_ => panic!("Expected integer literal"),
|
|
}
|
|
}
|
|
_ => panic!("Expected negative expression"),
|
|
}
|
|
}
|
|
_ => panic!("Expected additive expression"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn simple_subtract() {
|
|
let expression = assert_expression("1 - 1");
|
|
match expression {
|
|
Expression::Binary(binary_expression) => {
|
|
assert!(matches!(binary_expression.op(), BinaryOperation::Subtract));
|
|
match binary_expression.lhs() {
|
|
Expression::Integer(integer_literal) => {
|
|
assert_eq!(integer_literal.value(), 1);
|
|
}
|
|
_ => panic!("Expected integer literal"),
|
|
}
|
|
match binary_expression.rhs() {
|
|
Expression::Integer(integer_literal) => {
|
|
assert_eq!(integer_literal.value(), 1);
|
|
}
|
|
_ => panic!("Expected integer literal"),
|
|
}
|
|
}
|
|
_ => panic!("Expected subtract expression"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod parse_failure_tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn lone_end() {
|
|
let parse_result = parse_compilation_unit("end");
|
|
match parse_result {
|
|
Err(diagnostics) => {
|
|
assert_eq!(diagnostics.len(), 1);
|
|
for diagnostic in &diagnostics {
|
|
println!("{:?}", diagnostic)
|
|
}
|
|
}
|
|
Ok(_) => panic!(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn two_ends() {
|
|
let parse_result = parse_compilation_unit("end end");
|
|
match parse_result {
|
|
Err(diagnostics) => {
|
|
// Should only have an error on the first end, since we advance until we find a
|
|
// token we can recover from (fn or extern)
|
|
assert_eq!(diagnostics.len(), 1);
|
|
}
|
|
Ok(_) => panic!(),
|
|
}
|
|
}
|
|
}
|