Merge pull request #100 from ryangjchandler/feature/all-unary-ops

This commit is contained in:
Ryan Chandler 2022-09-16 15:01:34 +01:00 committed by GitHub
commit 50cc9aa5d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 1 deletions

View File

@ -520,6 +520,10 @@ impl Lexer {
self.next();
TokenKind::Colon
}
&[b'~', ..] => {
self.next();
TokenKind::BitwiseNot
}
&[b, ..] => unimplemented!(
"<scripting> char: {}, line: {}, col: {}",
b as char,
@ -766,6 +770,7 @@ impl Lexer {
fn identifier_to_keyword(ident: &[u8]) -> Option<TokenKind> {
Some(match ident {
b"print" => TokenKind::Print,
b"__halt_compiler" | b"__HALT_COMPILER" => TokenKind::HaltCompiler,
b"readonly" => TokenKind::Readonly,
b"global" => TokenKind::Global,

View File

@ -11,6 +11,7 @@ pub enum OpenTagKind {
#[derive(Debug, PartialEq, Clone)]
pub enum TokenKind {
Print,
Dollar,
HaltCompiler,
Readonly,
@ -161,6 +162,7 @@ pub enum TokenKind {
Variable(ByteString),
Yield,
While,
BitwiseNot,
}
#[derive(Debug, Clone, PartialEq)]
@ -181,6 +183,8 @@ impl Default for Token {
impl Display for TokenKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Print => "print",
Self::BitwiseNot => "~",
Self::Dollar => "$",
Self::HaltCompiler => "__halt_compiler",
Self::Readonly => "readonly",

View File

@ -560,7 +560,22 @@ pub enum Expression {
value: Box<Expression>,
},
Negate {
value: Box<Expression>,
value: Box<Self>,
},
UnaryPlus {
value: Box<Self>,
},
BitwiseNot {
value: Box<Self>,
},
PreDecrement {
value: Box<Self>,
},
PreIncrement {
value: Box<Self>,
},
Print {
value: Box<Self>,
},
Cast {
kind: CastKind,

View File

@ -2178,7 +2178,12 @@ fn is_prefix(op: &TokenKind) -> bool {
matches!(
op,
TokenKind::Bang
| TokenKind::Print
| TokenKind::BitwiseNot
| TokenKind::Decrement
| TokenKind::Increment
| TokenKind::Minus
| TokenKind::Plus
| TokenKind::StringCast
| TokenKind::BinaryCast
| TokenKind::ObjectCast
@ -2197,12 +2202,27 @@ fn is_prefix(op: &TokenKind) -> bool {
fn prefix(op: &TokenKind, rhs: Expression) -> Expression {
match op {
TokenKind::Print => Expression::Print {
value: Box::new(rhs),
},
TokenKind::Bang => Expression::BooleanNot {
value: Box::new(rhs),
},
TokenKind::Minus => Expression::Negate {
value: Box::new(rhs),
},
TokenKind::Plus => Expression::UnaryPlus {
value: Box::new(rhs),
},
TokenKind::BitwiseNot => Expression::BitwiseNot {
value: Box::new(rhs),
},
TokenKind::Decrement => Expression::PreDecrement {
value: Box::new(rhs),
},
TokenKind::Increment => Expression::PreIncrement {
value: Box::new(rhs),
},
TokenKind::StringCast
| TokenKind::BinaryCast
| TokenKind::ObjectCast
@ -4286,6 +4306,56 @@ mod tests {
);
}
#[test]
fn unary_plus() {
assert_ast(
"<?php +1;",
&[expr!(Expression::UnaryPlus {
value: Box::new(Expression::Int { i: 1 })
})],
);
}
#[test]
fn bitwise_not() {
assert_ast(
"<?php ~2;",
&[expr!(Expression::BitwiseNot {
value: Box::new(Expression::Int { i: 2 })
})],
);
}
#[test]
fn pre_decrement() {
assert_ast(
"<?php --$a;",
&[expr!(Expression::PreDecrement {
value: Box::new(Expression::Variable { name: "a".into() })
})],
);
}
#[test]
fn pre_increment() {
assert_ast(
"<?php ++$a;",
&[expr!(Expression::PreIncrement {
value: Box::new(Expression::Variable { name: "a".into() })
})],
);
}
#[test]
fn print_expression() {
assert_ast(
"<?php print $foo;",
&[expr!(Expression::Print {
value: Box::new(Expression::Variable { name: "foo".into() })
})],
);
}
fn assert_ast(source: &str, expected: &[Statement]) {
let mut lexer = Lexer::new(None);
let tokens = lexer.tokenize(source).unwrap();