Skip to content

Commit

Permalink
Disallow whitespace in multi-token operators
Browse files Browse the repository at this point in the history
  • Loading branch information
sbillig committed Jan 27, 2024
1 parent db835f3 commit 0b75030
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 50 deletions.
24 changes: 12 additions & 12 deletions crates/parser2/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,49 +281,49 @@ impl super::Parse for FieldExprScope {
define_scope! { pub(super) LShiftScope, LShift, Inheritance }
impl super::Parse for LShiftScope {
fn parse<S: TokenStream>(&mut self, parser: &mut Parser<S>) {
parser.bump_or_recover(SyntaxKind::Lt, "expected `<<`", None);
parser.bump_or_recover(SyntaxKind::Lt, "expected `<<`", None);
parser.bump_expected(SyntaxKind::Lt);
parser.bump_expected(SyntaxKind::Lt);
}
}

define_scope! { pub(super) RShiftScope, RShift, Inheritance }
impl super::Parse for RShiftScope {
fn parse<S: TokenStream>(&mut self, parser: &mut Parser<S>) {
parser.bump_or_recover(SyntaxKind::Gt, "expected `>>`", None);
parser.bump_or_recover(SyntaxKind::Gt, "expected `>>`", None);
parser.bump_expected(SyntaxKind::Gt);
parser.bump_expected(SyntaxKind::Gt);
}
}

define_scope! { pub(super) LtEqScope, LtEq, Inheritance }
impl super::Parse for LtEqScope {
fn parse<S: TokenStream>(&mut self, parser: &mut Parser<S>) {
parser.bump_or_recover(SyntaxKind::Lt, "expected `<=`", None);
parser.bump_or_recover(SyntaxKind::Eq, "expected `<=`", None);
parser.bump_expected(SyntaxKind::Lt);
parser.bump_expected(SyntaxKind::Eq);
}
}

define_scope! { pub(super) GtEqScope, GtEq, Inheritance }
impl super::Parse for GtEqScope {
fn parse<S: TokenStream>(&mut self, parser: &mut Parser<S>) {
parser.bump_or_recover(SyntaxKind::Gt, "expected `>=`", None);
parser.bump_or_recover(SyntaxKind::Eq, "expected `>=`", None);
parser.bump_expected(SyntaxKind::Gt);
parser.bump_expected(SyntaxKind::Eq);
}
}

pub(crate) fn is_lshift<S: TokenStream>(parser: &mut Parser<S>) -> bool {
parser.dry_run(|parser| parser.parse(LShiftScope::default(), None).0)
parser.peek_two() == (Some(SyntaxKind::Lt), Some(SyntaxKind::Lt))
}

pub(crate) fn is_rshift<S: TokenStream>(parser: &mut Parser<S>) -> bool {
parser.dry_run(|parser| parser.parse(RShiftScope::default(), None).0)
parser.peek_two() == (Some(SyntaxKind::Gt), Some(SyntaxKind::Gt))
}

fn is_lt_eq<S: TokenStream>(parser: &mut Parser<S>) -> bool {
parser.dry_run(|parser| parser.parse(LtEqScope::default(), None).0)
parser.peek_two() == (Some(SyntaxKind::Lt), Some(SyntaxKind::Eq))
}

fn is_gt_eq<S: TokenStream>(parser: &mut Parser<S>) -> bool {
parser.dry_run(|parser| parser.parse(GtEqScope::default(), None).0)
parser.peek_two() == (Some(SyntaxKind::Gt), Some(SyntaxKind::Eq))
}

fn bump_bin_op<S: TokenStream>(parser: &mut Parser<S>) {
Expand Down
27 changes: 27 additions & 0 deletions crates/parser2/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,33 @@ impl<S: TokenStream> Parser<S> {
}
}

/// Skip trivias (and newlines), then peek the next three tokens.
pub fn peek_three(&mut self) -> (Option<SyntaxKind>, Option<SyntaxKind>, Option<SyntaxKind>) {
self.stream.set_bt_point();

while let Some(next) = self.stream.peek().map(|tok| tok.syntax_kind()) {
if !(next.is_trivia() || next == SyntaxKind::Newline) {
break;
}
self.stream.next();
}

let tokens = (
self.stream.next().map(|t| t.syntax_kind()),
self.stream.next().map(|t| t.syntax_kind()),
self.stream.next().map(|t| t.syntax_kind()),
);

self.stream.backtrack();
tokens
}

/// Skip trivias, then peek the next two tokens.
pub fn peek_two(&mut self) -> (Option<SyntaxKind>, Option<SyntaxKind>) {
let (a, b, _) = self.peek_three();
(a, b)
}

/// Add the `msg` to the error list, at `current_pos`.
fn error(&mut self, msg: &str) -> ErrorScope {
self.is_err = true;
Expand Down
55 changes: 17 additions & 38 deletions crates/parser2/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,29 +129,13 @@ impl super::Parse for AugAssignStmtScope {
fn parse<S: TokenStream>(&mut self, parser: &mut Parser<S>) {
parser.set_newline_as_trivia(false);

parser.with_recovery_tokens(
|parser| {
parser.bump_or_recover(
SyntaxKind::Ident,
"expeced identifier for the assignment",
None,
)
},
&[SyntaxKind::Eq],
parser.bump_or_recover(
SyntaxKind::Ident,
"expeced identifier for the assignment",
None,
);

parser.with_next_expected_tokens(
|parser| {
if !bump_aug_assign_op(parser) {
parser.error_and_recover("expected augmented assignment operator", None);
}
},
&[SyntaxKind::Eq],
);

if !parser.bump_if(SyntaxKind::Eq) {
parser.error_and_recover("expected `=`", None);
return;
if !bump_aug_assign_op(parser) {
parser.error_and_recover("expected augmented assignment operator", None);
}

parse_expr(parser);
Expand Down Expand Up @@ -182,26 +166,21 @@ impl super::Parse for ExprStmtScope {

fn bump_aug_assign_op<S: TokenStream>(parser: &mut Parser<S>) -> bool {
use SyntaxKind::*;
match parser.current_kind() {
Some(Pipe | Hat | Amp | Plus | Minus | Star | Slash | Percent | Star2) => {
match parser.peek_three() {
(Some(Pipe | Hat | Amp | Plus | Minus | Star | Slash | Percent | Star2), Some(Eq), _) => {
parser.bump();
parser.bump();
true
}
Some(Lt) => {
if expr::is_lshift(parser) {
parser.parse(expr::LShiftScope::default(), None);
true
} else {
false
}
(Some(Lt), Some(Lt), Some(Eq)) => {
parser.parse(expr::LShiftScope::default(), None);
parser.bump_expected(SyntaxKind::Eq);
true
}
Some(Gt) => {
if expr::is_rshift(parser) {
parser.parse(expr::RShiftScope::default(), None);
true
} else {
false
}
(Some(Gt), Some(Gt), Some(Eq)) => {
parser.parse(expr::RShiftScope::default(), None);
parser.bump_expected(SyntaxKind::Eq);
true
}
_ => false,
}
Expand Down
11 changes: 11 additions & 0 deletions crates/uitest/fixtures/parser/operators.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fn f() {
x += 1
x + = 1
x -= 1
x - = 1
x * = 1
x << 1
x < < 1
x <= 1
x < = 1
}
66 changes: 66 additions & 0 deletions crates/uitest/fixtures/parser/operators.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
source: crates/uitest/tests/parser.rs
expression: diags
input_file: crates/uitest/fixtures/parser/operators.fe
---
error[1-0001]: expected expression
┌─ operators.fe:3:8
3x + = 1
^ expected expression

error[1-0001]: unexpected syntax
┌─ operators.fe:3:9
3x + = 1
^^^ unexpected syntax

error[1-0001]: expected expression
┌─ operators.fe:5:8
5x - = 1
^ expected expression

error[1-0001]: unexpected syntax
┌─ operators.fe:5:12
5x - = 1
^^^ unexpected syntax

error[1-0001]: expected expression
┌─ operators.fe:6:8
6x * = 1
^ expected expression

error[1-0001]: unexpected syntax
┌─ operators.fe:6:9
6x * = 1
^^^ unexpected syntax

error[1-0001]: expected expression
┌─ operators.fe:8:8
8x < < 1
^ expected expression

error[1-0001]: unexpected syntax
┌─ operators.fe:8:9
8x < < 1
^^^ unexpected syntax

error[1-0001]: expected expression
┌─ operators.fe:10:8
10x < = 1
^ expected expression

error[1-0001]: unexpected syntax
┌─ operators.fe:10:9
10x < = 1
^^^ unexpected syntax


0 comments on commit 0b75030

Please sign in to comment.