mirror of
https://github.com/danog/parser.git
synced 2024-11-26 20:04:57 +01:00
parser/lexer: implement all assignment ops
This commit is contained in:
parent
5a0fe5beb2
commit
27d5705f85
@ -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
|
||||||
|
@ -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 => "**",
|
||||||
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user