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

View File

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

View File

@ -650,6 +650,13 @@ pub enum InfixOp {
LogicalOr,
LogicalXor,
Spaceship,
PowAssign,
ModAssign,
BitwiseAndAssign,
BitwiseOrAssign,
BitwiseXorAssign,
LeftShiftAssign,
RightShiftAssign,
}
impl From<TokenKind> for InfixOp {
@ -689,6 +696,13 @@ impl From<TokenKind> for InfixOp {
TokenKind::LogicalAnd => Self::LogicalAnd,
TokenKind::LogicalOr => Self::LogicalOr,
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!(),
}
}

View File

@ -2257,6 +2257,13 @@ fn is_infix(t: &TokenKind) -> bool {
matches!(
t,
TokenKind::Pow
| TokenKind::RightShiftEquals
| TokenKind::LeftShiftEquals
| TokenKind::CaretEquals
| TokenKind::AmpersandEquals
| TokenKind::PipeEquals
| TokenKind::PercentEquals
| TokenKind::PowEquals
| TokenKind::LogicalAnd
| TokenKind::LogicalOr
| 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]) {
let mut lexer = Lexer::new(None);
let tokens = lexer.tokenize(source).unwrap();

View File

@ -66,7 +66,8 @@ impl Precedence {
Coalesce => Self::NullCoalesce,
Question => Self::Ternary,
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,
LogicalAnd => Self::KeyAnd,
LogicalOr => Self::KeyOr,