parser/lexer: implement all assignment ops

This commit is contained in:
Ryan Chandler 2022-09-16 16:16:14 +01:00
parent 5a0fe5beb2
commit 27d5705f85
No known key found for this signature in database
GPG Key ID: F113BCADDB3B0CCA
5 changed files with 232 additions and 1 deletions

View File

@ -149,6 +149,10 @@ impl Lexer {
self.skip(2); self.skip(2);
TokenKind::BooleanAnd TokenKind::BooleanAnd
} }
[b'&', b'=', ..] => {
self.skip(2);
TokenKind::AmpersandEquals
}
[b'&', ..] => { [b'&', ..] => {
self.next(); self.next();
TokenKind::Ampersand TokenKind::Ampersand
@ -363,6 +367,10 @@ impl Lexer {
self.next(); self.next();
TokenKind::Slash TokenKind::Slash
} }
[b'*', b'*', b'=', ..] => {
self.skip(3);
TokenKind::PowEquals
}
[b'*', b'*', ..] => { [b'*', b'*', ..] => {
self.skip(2); self.skip(2);
TokenKind::Pow TokenKind::Pow
@ -379,10 +387,18 @@ impl Lexer {
self.skip(2); self.skip(2);
TokenKind::Pipe TokenKind::Pipe
} }
[b'|', b'=', ..] => {
self.skip(2);
TokenKind::PipeEquals
}
[b'|', ..] => { [b'|', ..] => {
self.next(); self.next();
TokenKind::Pipe TokenKind::Pipe
} }
[b'^', b'=', ..] => {
self.skip(2);
TokenKind::CaretEquals
}
[b'^', ..] => { [b'^', ..] => {
self.next(); self.next();
TokenKind::Caret TokenKind::Caret
@ -458,6 +474,10 @@ impl Lexer {
self.next(); self.next();
TokenKind::Plus TokenKind::Plus
} }
[b'%', b'=', ..] => {
self.skip(2);
TokenKind::PercentEquals
}
[b'%', ..] => { [b'%', ..] => {
self.next(); self.next();
TokenKind::Percent TokenKind::Percent
@ -484,6 +504,11 @@ impl Lexer {
todo!("heredocs & nowdocs"); todo!("heredocs & nowdocs");
} }
[b'<', b'<', b'=', ..] => {
self.skip(3);
TokenKind::LeftShiftEquals
}
[b'<', b'<', ..] => { [b'<', b'<', ..] => {
self.skip(2); self.skip(2);
TokenKind::LeftShift TokenKind::LeftShift
@ -504,6 +529,10 @@ impl Lexer {
self.next(); self.next();
TokenKind::LessThan TokenKind::LessThan
} }
[b'>', b'>', b'=', ..] => {
self.skip(3);
TokenKind::RightShiftEquals
}
[b'>', b'>', ..] => { [b'>', b'>', ..] => {
self.skip(2); self.skip(2);
TokenKind::RightShift TokenKind::RightShift

View File

@ -18,6 +18,7 @@ pub enum TokenKind {
Global, Global,
Abstract, Abstract,
Ampersand, Ampersand,
AmpersandEquals,
And, And,
AndEqual, AndEqual,
Array, Array,
@ -40,6 +41,7 @@ pub enum TokenKind {
Break, Break,
Callable, Callable,
Caret, Caret,
CaretEquals,
Case, Case,
Catch, Catch,
Class, Class,
@ -117,7 +119,9 @@ pub enum TokenKind {
LeftBracket, LeftBracket,
LeftParen, LeftParen,
LeftShift, LeftShift,
LeftShiftEquals,
RightShift, RightShift,
RightShiftEquals,
LessThan, LessThan,
LessThanEquals, LessThanEquals,
Match, Match,
@ -130,7 +134,9 @@ pub enum TokenKind {
UnsetCast, UnsetCast,
OpenTag(OpenTagKind), OpenTag(OpenTagKind),
Percent, Percent,
PercentEquals,
Pipe, Pipe,
PipeEquals,
Plus, Plus,
PlusEquals, PlusEquals,
Pow, Pow,
@ -215,6 +221,7 @@ impl Display for TokenKind {
Self::Break => "break", Self::Break => "break",
Self::Callable => "callable", Self::Callable => "callable",
Self::Caret => "^", Self::Caret => "^",
Self::CaretEquals => "^=",
Self::Case => "case", Self::Case => "case",
Self::Catch => "catch", Self::Catch => "catch",
Self::Class => "class", Self::Class => "class",
@ -297,6 +304,9 @@ impl Display for TokenKind {
Self::LeftBracket => "[", Self::LeftBracket => "[",
Self::LeftParen => "(", Self::LeftParen => "(",
Self::LeftShift => "<<", Self::LeftShift => "<<",
Self::LeftShiftEquals => "<<=",
Self::RightShift => ">>",
Self::RightShiftEquals => ">>=",
Self::LessThan => "<", Self::LessThan => "<",
Self::LessThanEquals => "<=", Self::LessThanEquals => "<=",
Self::Match => "match", Self::Match => "match",
@ -310,7 +320,9 @@ impl Display for TokenKind {
OpenTagKind::Full => "<?php", OpenTagKind::Full => "<?php",
}, },
Self::Percent => "%", Self::Percent => "%",
Self::PercentEquals => "%=",
Self::Pipe => "|", Self::Pipe => "|",
Self::PipeEquals => "|=",
Self::Plus => "+", Self::Plus => "+",
Self::PlusEquals => "+=", Self::PlusEquals => "+=",
Self::Pow => "**", Self::Pow => "**",

View File

@ -650,6 +650,13 @@ pub enum InfixOp {
LogicalOr, LogicalOr,
LogicalXor, LogicalXor,
Spaceship, Spaceship,
PowAssign,
ModAssign,
BitwiseAndAssign,
BitwiseOrAssign,
BitwiseXorAssign,
LeftShiftAssign,
RightShiftAssign,
} }
impl From<TokenKind> for InfixOp { impl From<TokenKind> for InfixOp {
@ -689,6 +696,13 @@ impl From<TokenKind> for InfixOp {
TokenKind::LogicalAnd => Self::LogicalAnd, TokenKind::LogicalAnd => Self::LogicalAnd,
TokenKind::LogicalOr => Self::LogicalOr, TokenKind::LogicalOr => Self::LogicalOr,
TokenKind::LogicalXor => Self::LogicalXor, TokenKind::LogicalXor => Self::LogicalXor,
TokenKind::PowEquals => Self::PowAssign,
TokenKind::PercentEquals => Self::ModAssign,
TokenKind::AmpersandEquals => Self::BitwiseAndAssign,
TokenKind::PipeEquals => Self::BitwiseOrAssign,
TokenKind::CaretEquals => Self::BitwiseXorAssign,
TokenKind::LeftShiftEquals => Self::LeftShiftAssign,
TokenKind::RightShiftEquals => Self::RightShiftAssign,
_ => unreachable!(), _ => unreachable!(),
} }
} }

View File

@ -2257,6 +2257,13 @@ fn is_infix(t: &TokenKind) -> bool {
matches!( matches!(
t, t,
TokenKind::Pow TokenKind::Pow
| TokenKind::RightShiftEquals
| TokenKind::LeftShiftEquals
| TokenKind::CaretEquals
| TokenKind::AmpersandEquals
| TokenKind::PipeEquals
| TokenKind::PercentEquals
| TokenKind::PowEquals
| TokenKind::LogicalAnd | TokenKind::LogicalAnd
| TokenKind::LogicalOr | TokenKind::LogicalOr
| TokenKind::LogicalXor | TokenKind::LogicalXor
@ -4499,6 +4506,174 @@ mod tests {
); );
} }
#[test]
fn assign() {
assert_ast(
"<?php $a = 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::Assign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn add_assign() {
assert_ast(
"<?php $a += 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::AddAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn sub_assign() {
assert_ast(
"<?php $a -= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::SubAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn mul_assign() {
assert_ast(
"<?php $a *= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::MulAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn pow_assign() {
assert_ast(
"<?php $a **= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::PowAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn div_assign() {
assert_ast(
"<?php $a /= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::DivAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn concat_assign() {
assert_ast(
"<?php $a .= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::ConcatAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn mod_assign() {
assert_ast(
"<?php $a %= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::ModAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn bit_and_assign() {
assert_ast(
"<?php $a &= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::BitwiseAndAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn bit_or_assign() {
assert_ast(
"<?php $a |= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::BitwiseOrAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn bit_xor_assign() {
assert_ast(
"<?php $a ^= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::BitwiseXorAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn left_shift_assign() {
assert_ast(
"<?php $a <<= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::LeftShiftAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn right_shift_assign() {
assert_ast(
"<?php $a >>= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::RightShiftAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
#[test]
fn null_coalese_assign() {
assert_ast(
"<?php $a ??= 1;",
&[expr!(Expression::Infix {
lhs: Box::new(Expression::Variable { name: "a".into() }),
op: InfixOp::CoalesceAssign,
rhs: Box::new(Expression::Int { i: 1 }),
})],
);
}
fn assert_ast(source: &str, expected: &[Statement]) { fn assert_ast(source: &str, expected: &[Statement]) {
let mut lexer = Lexer::new(None); let mut lexer = Lexer::new(None);
let tokens = lexer.tokenize(source).unwrap(); let tokens = lexer.tokenize(source).unwrap();

View File

@ -66,7 +66,8 @@ impl Precedence {
Coalesce => Self::NullCoalesce, Coalesce => Self::NullCoalesce,
Question => Self::Ternary, Question => Self::Ternary,
Equals | PlusEquals | MinusEquals | AsteriskEqual | PowEquals | SlashEquals Equals | PlusEquals | MinusEquals | AsteriskEqual | PowEquals | SlashEquals
| DotEquals | AndEqual | CoalesceEqual => Self::Assignment, | DotEquals | AndEqual | CoalesceEqual | PercentEquals | AmpersandEquals
| PipeEquals | CaretEquals | LeftShiftEquals | RightShiftEquals => Self::Assignment,
Yield => Self::Yield, Yield => Self::Yield,
LogicalAnd => Self::KeyAnd, LogicalAnd => Self::KeyAnd,
LogicalOr => Self::KeyOr, LogicalOr => Self::KeyOr,