├── project ├── build.properties ├── assembly.sbt ├── gnupg │ └── signkey.asc.enc └── plugins.sbt ├── src ├── test │ └── scala │ │ └── de │ │ └── thm │ │ └── mni │ │ └── ii │ │ └── phpparser │ │ ├── FailureTest.scala │ │ ├── ExpressionTest.scala │ │ ├── StatementsTest.scala │ │ └── ClassTest.scala └── main │ └── scala │ └── de │ └── thm │ └── mni │ └── ii │ └── phpparser │ ├── parser │ ├── literals │ │ ├── Lexical.scala │ │ ├── KeywordConversions.scala │ │ ├── Keywords.scala │ │ └── Literals.scala │ ├── Basic.scala │ ├── expressions │ │ ├── VariableParser.scala │ │ ├── ExpressionParser.scala │ │ └── OperatorParser.scala │ └── statements │ │ ├── ControlFlowParser.scala │ │ ├── StatementParser.scala │ │ └── DeclarationParser.scala │ ├── PHPParser.scala │ └── ast │ ├── Basic.scala │ ├── Expressions.scala │ └── Statements.scala ├── .travis.yml ├── LICENSE ├── README.md └── .gitignore /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.1.0 -------------------------------------------------------------------------------- /project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5") -------------------------------------------------------------------------------- /project/gnupg/signkey.asc.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thm-mni-ii/PHP-Parser/HEAD/project/gnupg/signkey.asc.enc -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += Classpaths.sbtPluginReleases 2 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") 3 | addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.2") 4 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0") 5 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.1") -------------------------------------------------------------------------------- /src/test/scala/de/thm/mni/ii/phpparser/FailureTest.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | 5 | /** 6 | * Test PHPParsers ability to detect malformed php code. 7 | * @author Andrej Sajenko 8 | */ 9 | class FailureTest extends FlatSpec with Matchers { 10 | "PHPParser" must "fail parsing malformed php code" in { 11 | PHPParser.parse(" c != '\n' && c != '\r' && c != '?') | !("?>" | Newline) ~ AnyChar) 10 | val LineComment = P(("//" | "#") ~ LineCommentText.rep) 11 | 12 | val MultilineText = P(CharsWhile(c => c != '*') | (!"*/" ~ AnyChar)) 13 | val MultiLineComment = P("/*" ~ MultilineText.rep ~ ("*/" | End)) 14 | 15 | val Comment = P(LineComment | MultiLineComment) 16 | 17 | val Whitespace = P(NoTrace((WsChars | Newline | Comment).rep)) 18 | 19 | val Ws = P(&(NoTrace(WsChars | Newline | Comment).rep(1))) 20 | } 21 | 22 | object WsAPI extends fastparse.WhitespaceApi.Wrapper(Lexical.Whitespace) 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | 3 | scala: 4 | - 2.12.2 5 | 6 | services: 7 | - docker 8 | 9 | script: 10 | - sbt compile 11 | - sbt clean coverage test coverageReport && sbt coverageAggregate 12 | 13 | # see also https://docs.travis-ci.com/user/languages/java/#Caching 14 | before_cache: 15 | # Cleanup the cached directories to avoid unnecessary cache updates 16 | - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete 17 | - find $HOME/.sbt -name "*.lock" -print -delete 18 | cache: 19 | directories: 20 | - $HOME/.ivy2/cache 21 | - $HOME/.sbt 22 | 23 | before_install: 24 | - openssl aes-256-cbc -d -K $ENC_KEY -iv $ENC_IV -in project/gnupg/signkey.asc.enc -out project/gnupg/signkey.asc 25 | 26 | after_success: 27 | - sbt coveralls 28 | 29 | deploy: 30 | provider: script 31 | skip_cleanup: true 32 | script: sbt publishSigned 33 | on: 34 | tags: true 35 | branch: master -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Institute for Information Sciences 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/PHPParser.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser 2 | 3 | import fastparse.all._ 4 | import fastparse.core.Frame 5 | import de.thm.mni.ii.phpparser.ast.Basic.Script 6 | 7 | object PHPParser { 8 | 9 | def parse(toParse: String) = { 10 | val result = de.thm.mni.ii.phpparser.parser.Basic.Script.parse(toParse) 11 | result match { 12 | case Parsed.Success(value, currentIndex) => { 13 | PHPParser.Success(value) 14 | } 15 | case Parsed.Failure(parser, index, extra) => { 16 | val msg = 17 | s""" 18 | |Parsing failed on ${extra.input.repr.prettyIndex(extra.input, index)} 19 | | expected: ${extra.traced.expected} 20 | | got: "...${extra.input.slice(index, index + 20)}" 21 | """.stripMargin 22 | val framePrint = extra.traced.fullStack.reverse.map { case Frame(index, p) => s" while trying to parse '${p}' at ${extra.input.repr.prettyIndex(extra.input, index)}\n" }.mkString 23 | val fullMsg = 24 | s""" 25 | |${msg} 26 | | 27 | |Parsing failed on "...${extra.input.slice(index, index + 20)}" 28 | | while trying to parse '${extra.traced.expected}' at ${extra.input.repr.prettyIndex(extra.input, index)} 29 | |${framePrint} 30 | """ 31 | .stripMargin 32 | PHPParser.Failure(msg, fullMsg, result.asInstanceOf[Parsed.Failure]) 33 | } 34 | } 35 | } 36 | 37 | sealed abstract class Result {} 38 | case class Success(script: Script) extends PHPParser.Result 39 | case class Failure(msg: String, fullMsg: String, failure: Parsed.Failure) extends PHPParser.Result 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/Basic.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser 2 | 3 | import fastparse.noApi._ 4 | import de.thm.mni.ii.phpparser.parser.literals.WsAPI._ 5 | import de.thm.mni.ii.phpparser.parser.literals.Lexical.Ws 6 | import de.thm.mni.ii.phpparser.parser.literals.Keywords.{NAMESPACE, PHP} 7 | import de.thm.mni.ii.phpparser.parser.literals.Literals.Name 8 | import de.thm.mni.ii.phpparser.ast.{Basic => BAst} 9 | import de.thm.mni.ii.phpparser.parser.statements.StatementParser.AvailableStatement 10 | import de.thm.mni.ii.phpparser.ast.Basic.Script 11 | 12 | object Basic { 13 | 14 | val NormalStartTag = P("") 17 | val StartTag = P(NormalStartTag | EchoStartTag) 18 | 19 | val WsOrSemicolon = P(Ws | ";" | "?>") 20 | val WsExp = P(Ws | "(") 21 | val Semicolon = P(";" | "?>") 22 | 23 | val Script : P[Script] = P(Text ~~ NormalStartTag.? ~ AvailableStatement.rep ~ End) 24 | .map(t => BAst.Script(t._1, t._3)) 25 | 26 | val Text : P[BAst.Text] = P((!StartTag ~~ AnyChar.!).repX).map(t => BAst.Text(t.mkString)) 27 | 28 | val SemicolonFactory : P[Option[BAst.Text]] = 29 | P((";".!.map(_ => None) ~ !EchoStartTag) | ("?>" ~~ Text ~~ ((NormalStartTag ~~ !EchoStartTag) | EchoStartTag | End)).map(t => Some(t._1))) 30 | .opaque("\";\" | \"?>\"") 31 | 32 | val QualifiedName : P[BAst.QualifiedName] = P((NAMESPACE ~ "\\" ~ (Name ~ "\\").rep ~ Name).map { 33 | case (path, name) => BAst.QualifiedName(BAst.NamespaceType.LOCAL, path, name) 34 | } | ("\\".!.? ~ (Name ~ "\\").rep ~ Name).map { 35 | case (Some(_), path, name) => BAst.QualifiedName(BAst.NamespaceType.GLOBAL, path, name) 36 | case (None, path, name) => BAst.QualifiedName(BAst.NamespaceType.RELATIVE, path, name) 37 | }).opaque("QualifiedName") 38 | 39 | val NamespaceName: P[Seq[BAst.Name]] = P(Name.rep(min = 0, sep = "\\")) 40 | } 41 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/literals/KeywordConversions.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.literals 2 | 3 | import de.thm.mni.ii.phpparser.ast.{Expressions => EAst, Statements => SAst} 4 | import fastparse.all._ 5 | import de.thm.mni.ii.phpparser.parser.literals.Keywords._ 6 | 7 | /** 8 | * Created by tobias on 27.05.17. 9 | */ 10 | object KeywordConversions { 11 | 12 | // NamespaceUseTypes 13 | val FunctionUseType = P(FUNCTION).map(_ => SAst.NamespaceUseType.FUNCTION) 14 | val ConstUseType = P(CONST).map(_ => SAst.NamespaceUseType.CONST) 15 | 16 | // Modifier 17 | val AbstractMod = P(ABSTRACT).map(_ => SAst.AbstractMod) 18 | val FinalMod = P(FINAL).map(_ => SAst.FinalMod) 19 | val PublicMod = P(PUBLIC).map(_ => SAst.PublicMod) 20 | val PrivateMod = P(PRIVATE).map(_ => SAst.PrivateMod) 21 | val ProtectedMod = P(PROTECTED).map(_ => SAst.ProtectedMod) 22 | val StaticMod = P(STATIC).map(_ => SAst.StaticMod) 23 | val NoMod = P(VAR).map(_ => SAst.NoMod) 24 | 25 | // CastTypes 26 | val ArrayCastType = P(ARRAY).map(_ => EAst.CastType.ARRAY) 27 | val BinaryCastType = P(BINARY).map(_ => EAst.CastType.BINARY) 28 | val BooleanCastType = P(BOOLEAN).map(_ => EAst.CastType.BOOLEAN) 29 | val BoolCastType = P(BOOL).map(_ => EAst.CastType.BOOL) 30 | val DoubleCastType = P(DOUBLE).map(_ => EAst.CastType.DOUBLE) 31 | val IntegerCastType = P(INTEGER).map(_ => EAst.CastType.INTEGER) 32 | val IntCastType = P(INT).map(_ => EAst.CastType.INT) 33 | val FloatCastType = P(FLOAT).map(_ => EAst.CastType.FLOAT) 34 | val ObjectCastType = P(OBJECT).map(_ => EAst.CastType.OBJECT) 35 | val RealCastType = P(REAL).map(_ => EAst.CastType.REAL) 36 | val StringCastType = P(STRING).map(_ => EAst.CastType.STRING) 37 | val UnsetCastType = P(UNSET).map(_ => EAst.CastType.UNSET) 38 | 39 | // DeclareDeclarative 40 | val TicksDeclarative = P(TICKS).map(_ => SAst.DeclareDeclarative.TICKS ) 41 | val EncodingDeclarative = P(ENCODING).map(_ => SAst.DeclareDeclarative.ENCODING) 42 | val StrictTypesDeclarative = P(STRICT_TYPES).map(_ => SAst.DeclareDeclarative.STRICT_TYPES) 43 | 44 | // TypeDecl 45 | val ArrayType = P(ARRAY).map(_ => SAst.ArrayType) 46 | val CallableType = P(CALLABLE).map(_ => SAst.CallableType) 47 | val IterableType = P(ITERABLE).map(_ => SAst.IterableType) 48 | val BoolType = P(BOOL).map(_ => SAst.BoolType) 49 | val FloatType = P(FLOAT).map(_ => SAst.FloatType) 50 | val IntType = P(INT).map(_ => SAst.IntType) 51 | val StringType = P(STRING).map(_ => SAst.StringType) 52 | val VoidType = P(VOID).map(_ => SAst.VoidType) 53 | 54 | // ScopeTypes 55 | val SelfScope = P(SELF).map(_ => EAst.ScopeType.SELF) 56 | val ParentScope = P(PARENT).map(_ => EAst.ScopeType.PARENT) 57 | val StaticScope = P(STATIC).map(_ => EAst.ScopeType.STATIC) 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/ast/Basic.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.ast 2 | 3 | import de.thm.mni.ii.phpparser.ast.Expressions.{Expression, SimpleNameVar, Variable} 4 | import de.thm.mni.ii.phpparser.ast.Statements.Statement 5 | 6 | object Basic { 7 | 8 | case class Script(leadingText: Text, s: Seq[Statement]) 9 | 10 | case class Text(text: String) 11 | 12 | trait EndTagElement { 13 | val text: Option[Text] 14 | } 15 | 16 | case class Name(name: String) 17 | 18 | case class QualifiedName(nType: NamespaceType.Value, namespace: Seq[Name], name: Name) 19 | case object NamespaceType extends Enumeration { 20 | val RELATIVE, LOCAL, GLOBAL = Value 21 | } 22 | 23 | sealed abstract class Literal extends Expression 24 | 25 | sealed abstract class StringLiteral extends Literal 26 | 27 | sealed abstract class StringElement 28 | case class UnicodeStringElement(value: Either[Seq[Char], Variable]) extends StringElement 29 | case class OctalStringElement(digits: Seq[Char]) extends StringElement 30 | case class HexStringElement(digits: Seq[Char]) extends StringElement 31 | case class ExpressionStringElement(exp: Expression) extends StringElement 32 | case class VarStringElement(variable: SimpleNameVar, access: Option[StringVarAccess]) extends StringElement 33 | 34 | sealed abstract class StringVarAccess 35 | case class PropertyStringVarAcc(name: Name) extends StringVarAccess 36 | case class NameOffsetStringVarAcc(name: Name) extends StringVarAccess 37 | case class VarOffsetStringVarAcc(variable: SimpleNameVar) extends StringVarAccess 38 | case class IntOffsetStringVarAcc(integerLiteral: IntegerLiteral) extends StringVarAccess 39 | 40 | case class SQStringLiteral(prefix: Option[String], sequence: String) extends StringLiteral 41 | 42 | case class DQStringLiteral(prefix: Option[String], sequence: Seq[StringElement]) extends StringLiteral 43 | case class DQStringElement(s: String) extends StringElement 44 | 45 | case class HeredocStringLiteral(prefix: Option[String], hdIdentifier: Name, sequence: Seq[StringElement]) extends StringLiteral 46 | case class HDStringElement(s: String) extends StringElement 47 | case object HDNewLine extends StringElement 48 | 49 | case class NowdocStringLiteral(prefix: Option[String], ndIdentifier: Name, sequence: Seq[StringElement]) extends StringLiteral 50 | case class NDStringElement(s: String) extends StringElement 51 | case object NDNewLine extends StringElement 52 | 53 | 54 | sealed abstract class IntegerLiteral extends Literal 55 | case class DecimalLiteral(value: String) extends IntegerLiteral 56 | case class OctalLiteral(value: String) extends IntegerLiteral 57 | case class HexadecimalLiteral(value: String) extends IntegerLiteral 58 | case class BinaryLiteral(value: String) extends IntegerLiteral 59 | 60 | case class FloatingLiteral(digits: String, fracDigits: String, exponent: Option[(Boolean, String)]) extends Literal 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP-Parser [![Build Status](https://travis-ci.org/thm-mni-ii/PHP-Parser.svg?branch=master)](https://travis-ci.org/thm-mni-ii/PHP-Parser) [![Coverage Status](https://coveralls.io/repos/github/thm-mni-ii/PHP-Parser/badge.svg?branch=master)](https://coveralls.io/github/thm-mni-ii/PHP-Parser?branch=master) 2 | This project contains a PHP-Parser based on the current [php language specification](https://github.com/php/php-langspec). It supports PHP version 7. 3 | 4 | The parser is written in Scala, but can be used in **Java** as well as in **Scala** projects. 5 | Based on [FastParse](https://github.com/lihaoyi/fastparse) it transforms a valid PHP-Script into an abstract syntax tree. 6 | 7 | ## Getting Started 8 | 9 | Import the artifacts from Maven Central. 10 | 11 | ```xml 12 | 13 | de.thm.mni.ii 14 | phpparser 15 | 1.0.0 16 | 17 | ``` 18 | 19 | ## Usage 20 | 21 | These simple examples present the basic usage of the parser. 22 | 23 | #### Java 24 | ```java 25 | import de.thm.mni.ii.phpparser.PHPParser; 26 | import de.thm.mni.ii.phpparser.ast.Basic; 27 | 28 | public class Main { 29 | public static void main(String[] args) { 30 | PHPParser.Result res = (PHPParser.Result) PHPParser.parse(" println(s.script) 49 | case f: PHPParser.Failure => println(f.fullMsg) 50 | } 51 | } 52 | ``` 53 | 54 | The top-level __PHPParser__ parses a whole PHP-Script. The result is an instance of __PHPParser.Success__ or __PHPParser.Failure__. 55 | 56 | __PHPParser.Success__ contains the abstract syntax tree. __PHPParser.Failure__ contains additional information about the error. If you need further error information, take a look at the _failure_-member. FastParse provides additional methods to present the origin of the parse-error. 57 | 58 | ## Project Structure 59 | 60 | ``` 61 | . 62 | ├── ... 63 | ├── src/main/scala/de/thm/mni/ii/phpparser 64 | │   ├── PHPParser.scala # top-level parser 65 | │   ├── ast/ # case classes of abstract syntax tree 66 | │   └── parser/ # specific parsers for all different elements 67 | └── ... 68 | ``` 69 | 70 | ## Contribute 71 | 72 | Feel free to send Pull-Requests to improve this parser. If you find a bug, please report it as an issue on github. 73 | 74 | ## License 75 | 76 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details 77 | 78 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/expressions/VariableParser.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.expressions 2 | 3 | import fastparse.noApi._ 4 | import de.thm.mni.ii.phpparser.parser.literals.WsAPI._ 5 | 6 | import de.thm.mni.ii.phpparser.ast.{Expressions => EAst} 7 | 8 | import de.thm.mni.ii.phpparser.parser.literals.Keywords._ 9 | import de.thm.mni.ii.phpparser.parser.literals.KeywordConversions._ 10 | import de.thm.mni.ii.phpparser.parser.literals.Literals._ 11 | 12 | import de.thm.mni.ii.phpparser.parser.Basic._ 13 | import de.thm.mni.ii.phpparser.parser.expressions.ExpressionParser.{Expression, ArgumentExpressionList} 14 | 15 | 16 | /** 17 | * Created by tobias on 02.06.17. 18 | */ 19 | object VariableParser { 20 | 21 | val VariableName: P[EAst.SimpleNameVar] = P( 22 | "$" ~ NameWithKeyword).map(EAst.SimpleNameVar) 23 | 24 | val SimpleVariable: P[EAst.SimpleVar] = P( 25 | ("$" ~ SimpleVariable).map(EAst.SimpleAccessVar) 26 | | ("$" ~ "{" ~/ Expression ~ "}").map(EAst.SimpleExpVar) 27 | | VariableName) 28 | 29 | val ArrayElement: P[EAst.ArrayElement] = P( 30 | ("&" ~ NoCut(Expression)).map(EAst.ArrayElement(None, _, true)) 31 | | (Expression ~ ("=>" ~ "&".!.? ~ Expression).?).map(t => 32 | if (t._2.isDefined) EAst.ArrayElement(Some(t._1), t._2.get._2, t._2.get._1.isDefined) 33 | else EAst.ArrayElement(None, t._1, false)) 34 | ) 35 | 36 | val ArrayCreationVar = P( 37 | ARRAY ~ "(" ~/ !"," ~ ArrayElement.rep(sep = ("," ~ !")").~/) ~ ",".? ~ ")" 38 | | "[" ~/ !"," ~ ArrayElement.rep(sep = ("," ~ !"]").~/) ~ ",".? ~ "]" 39 | ).map(EAst.ArrayCreationVar) 40 | 41 | val StringLiteralVar = P(StringLiteral).map(EAst.StringLiteralVar) 42 | val EnclosedExp = P("(" ~/ Expression ~ ")").map(EAst.EnclosedExp) 43 | val ScopeAccVar = P((SelfScope | ParentScope | StaticScope) ~~ !NonDigit).map(EAst.ScopeAccessVar) 44 | val QualifiedNameVar = P(QualifiedName).map(EAst.QualifiedNameVar) 45 | 46 | val Variable: P[EAst.Variable] = { 47 | val SingleVariable: P[EAst.Variable] = P( 48 | SimpleVariable | ArrayCreationVar | StringLiteralVar 49 | | ScopeAccVar | QualifiedNameVar | EnclosedExp) 50 | 51 | val MemberName: P[EAst.MemberName] = P( 52 | NameWithKeyword.map(EAst.NameMember) 53 | | SimpleVariable.map(EAst.SimpleVarMember) 54 | | ("{" ~/ Expression ~ "}").map(EAst.ExpMember)) 55 | 56 | P(SingleVariable ~ ( 57 | ("::" ~ MemberName ~ "(" ~/ ArgumentExpressionList ~ ")").map(t => (x: EAst.Variable) => EAst.MemberCallStaticAcc(x, t._1, t._2)) 58 | | ("::" ~ SimpleVariable).map(t => (x: EAst.Variable) => EAst.SimpleVarStaticAcc(x, t)) 59 | | ("(" ~/ ArgumentExpressionList ~ ")").map(t => (x: EAst.Variable) => EAst.CallAccessor(x, t)) 60 | | ("[" ~/ Expression.? ~ "]").map(t => (x: EAst.Variable) => EAst.ArrayAcc(x, t)) 61 | | ("{" ~/ Expression ~ "}").map(t => (x: EAst.Variable) => EAst.BlockAcc(x, t)) 62 | | ("->" ~/ MemberName ~ ("(" ~/ ArgumentExpressionList ~ ")").?).map(t => 63 | if (t._2.isDefined) (x: EAst.Variable) => EAst.MemberCallPropertyAcc(x, t._1, t._2.get) 64 | else (x: EAst.Variable) => EAst.MemberPropertyAcc(x, t._1)) 65 | ).rep 66 | ).map(t => t._2.foldLeft(t._1)((a, b) => b(a))) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/expressions/ExpressionParser.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.expressions 2 | 3 | import fastparse.noApi._ 4 | import de.thm.mni.ii.phpparser.parser.literals.WsAPI._ 5 | import de.thm.mni.ii.phpparser.parser.literals.Lexical.Ws 6 | 7 | import de.thm.mni.ii.phpparser.ast.{Expressions => EAst} 8 | 9 | import de.thm.mni.ii.phpparser.parser.literals.Keywords._ 10 | import de.thm.mni.ii.phpparser.parser.literals.Literals._ 11 | 12 | import de.thm.mni.ii.phpparser.parser.Basic.WsExp 13 | import de.thm.mni.ii.phpparser.parser.statements.StatementParser.{CompoundStmnt, AnonymousFuncHeader} 14 | import de.thm.mni.ii.phpparser.parser.expressions.OperatorParser.{LogicalOrExpr2, CondExp} 15 | import de.thm.mni.ii.phpparser.parser.expressions.VariableParser.{ArrayElement, Variable} 16 | 17 | 18 | object ExpressionParser { 19 | 20 | val Expression : P[EAst.Expression] = P(YieldExp | RequireOnceExp | RequireExp | IncludeOnceExp | IncludeExp | LogicalOrExpr2) 21 | 22 | val RequireExp = P(REQUIRE ~~ &(WsExp) ~/ Expression).map(EAst.RequireExp) 23 | val RequireOnceExp = P(REQUIRE_ONCE ~~ &(WsExp) ~/ Expression).map(EAst.RequireOnceExp) 24 | val IncludeExp = P(INCLUDE ~~ &(WsExp) ~/ Expression).map(EAst.IncludeExp) 25 | val IncludeOnceExp = P(INCLUDE_ONCE ~~ &(WsExp) ~/ Expression).map(EAst.IncludeOnceExp) 26 | 27 | val YieldExp : P[EAst.Expression] = P( 28 | (YIELD ~~ Ws ~ FROM ~~ &(WsExp) ~/ Expression).map(EAst.YieldFromExp) 29 | | (YIELD ~~ &(WsExp) ~/ ArrayElement).map(EAst.YieldExp)) 30 | 31 | val CloneExp = P(CLONE ~~ &(WsExp) ~/ Expression).map(EAst.CloneExp) 32 | 33 | val PrimaryExpWithoutVariable : P[EAst.Expression] = 34 | P(Literal | Intrinsic | AnonymousFuncExp) 35 | 36 | val ListIntrinsic = P( 37 | LIST ~ "(" ~/ ( 38 | (",".rep ~ NoCut(Expression).rep(sep=",".rep(1)) ~ ",".rep).map(Left(_)) 39 | | ((Expression ~ "=>" ~ Expression).rep(sep=",") ~ ",".?).map(Right(_))) 40 | ~ ")").map(EAst.ListIntrinsic) 41 | val EchoIntrinsic = P(ECHO ~~ &(WsExp) ~/ Expression.rep(min=1, sep=",")).map(EAst.EchoIntrinsic) 42 | val UnsetIntrinsic = P(UNSET ~ "(" ~ Variable.rep(1, sep=",") ~ ")").map(EAst.UnsetIntrinsic) 43 | 44 | val EmptyIntrinsic = P(EMPTY ~ "(" ~/ Expression ~ ")").map(EAst.EmptyIntrinsic) 45 | val EvalIntrinsic = P(EVAL ~ "(" ~/ Expression ~ ")").map(EAst.EvalIntrinsic) 46 | val ExitIntrinsic = P((EXIT|DIE) ~ ("(" ~/ Expression.? ~ ")").?).map(t => EAst.ExitIntrinsic(t.getOrElse(None))) 47 | val IssetIntrinsic = P(ISSET ~ "(" ~/ Variable.rep(1, sep=",") ~ ")").map(EAst.IssetIntrinsic) 48 | val PrintIntrinsic = P(PRINT ~~ &(WsExp) ~/ Expression).map(EAst.PrintIntrinsic) 49 | 50 | val IntrinsicConstruct : P[EAst.Intrinsic] = P(EchoIntrinsic | UnsetIntrinsic | ListIntrinsic) 51 | val IntrinsicOperator : P[EAst.Intrinsic] = P(EmptyIntrinsic | EvalIntrinsic | ExitIntrinsic | IssetIntrinsic | PrintIntrinsic) 52 | val Intrinsic : P[EAst.Intrinsic] = P(IntrinsicOperator | IntrinsicConstruct) 53 | 54 | val AnonymousFuncExp = P(STATIC.!.?.map(_.isDefined) ~ AnonymousFuncHeader ~ CompoundStmnt) 55 | .map(t => EAst.AnonymousFunctionCreationExp(t._1, t._2._1, t._2._2, t._3)) 56 | 57 | val SingleExpression : P[EAst.Expression] = P(YieldExp | RequireOnceExp | RequireExp | IncludeOnceExp | IncludeExp) 58 | 59 | val ArgumentExpressionList : P[Seq[EAst.ArgumentExpression]] = { 60 | val ArgumentExp = P( 61 | "...".!.? ~ (CondExp | SingleExpression)).map(t => EAst.ArgumentExpression(t._1.isDefined, t._2)) 62 | 63 | P(ArgumentExp.rep(sep=",")) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/scala/de/thm/mni/ii/phpparser/ExpressionTest.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | import de.thm.mni.ii.phpparser.ast.Basic._ 5 | import de.thm.mni.ii.phpparser.ast.Expressions._ 6 | import de.thm.mni.ii.phpparser.ast.Statements.{CompoundStmnt, ExpressionStmnt, FuncHeader, ReturnStmnt} 7 | 8 | import scala.collection.mutable.ArrayBuffer 9 | 10 | /** 11 | *Test PHPParsers ability to parse php expressions. 12 | * @author Andrej Sajenko 13 | */ 14 | class ExpressionTest extends FlatSpec with Matchers { 15 | "PHPParser" must "parse 'echo' expression" in { 16 | PHPParser.parse(" } 55 | } 56 | 57 | it must "parse a simple formular [&& || == > < >= <=]" in { 58 | PHPParser.parse( 59 | """ 60 | | 3 && 2 < 3 || 1 >= 2 && 1 <= 2 || 9 == 3; 62 | """.stripMargin) should matchPattern { case 63 | PHPParser.Success(Script(_,ArrayBuffer( 64 | ExpressionStmnt(SimpleAssignmentExp(false,SimpleNameVar(Name("var1")), 65 | LogicalOrExp( 66 | LogicalOrExp( 67 | LogicalAndExp( 68 | RelationalExp(">", 69 | DecimalLiteral("5"), 70 | DecimalLiteral("3")), 71 | RelationalExp("<", 72 | DecimalLiteral("2"), 73 | DecimalLiteral("3") 74 | ) 75 | ), 76 | LogicalAndExp( 77 | RelationalExp(">=", 78 | DecimalLiteral("1"), 79 | DecimalLiteral("2") 80 | ), 81 | RelationalExp("<=", 82 | DecimalLiteral("1"), 83 | DecimalLiteral("2")) 84 | ) 85 | ), 86 | EqualityExp("==", 87 | DecimalLiteral("9"), 88 | DecimalLiteral("3") 89 | ) 90 | ) 91 | )) 92 | ))) 93 | => } 94 | } 95 | 96 | it must "parse function expression" in { 97 | PHPParser.parse( 98 | """ 99 | | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/literals/Keywords.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.literals 2 | 3 | import fastparse.all._ 4 | import fastparse.all.{IgnoreCase => I} 5 | 6 | /** 7 | * Created by tobias on 26.05.17. 8 | */ 9 | object Keywords { 10 | 11 | val AllKeywords = Seq( 12 | "abstract", "and", "array", "as", "break", "callable", "case", "catch", 13 | "class", "clone", "const", "continue", "declare", "default", "die", "do", "echo", "else", 14 | "elseif", "empty", "enddeclare", "endfor", "endforeach", "endif", "endswitch", "endwhile", 15 | "eval", "exit", "extends", "final", "finally", "for", "foreach", "function", "global", "goto", 16 | "if", "implements", "include", "include_once", "instanceof", "insteadof", "interface", "isset", 17 | "list", "namespace", "new", "or", "print", "private", "protected", "public", "require", 18 | "require_once", "return", "static", "switch", "throw", "trait", "try", "unset", "use", 19 | "var", "while", "xor", "yield") 20 | 21 | val ABSTRACT = P(I("abstract")) 22 | val AND = P(I("and")) 23 | val ARRAY = P(I("array")) 24 | val AS = P(I("as")) 25 | val BINARY = P(I("binary")) 26 | val BOOL = P(I("bool")) 27 | val BOOLEAN = P(I("boolean")) 28 | val BREAK = P(I("break")) 29 | val CALLABLE = P(I("callable")) 30 | val CASE = P(I("case")) 31 | val CATCH = P(I("catch")) 32 | val CLASS = P(I("class")) 33 | val CLONE = P(I("clone")) 34 | val CONST = P(I("const")) 35 | val CONTINUE = P(I("continue")) 36 | val DECLARE = P(I("declare")) 37 | val DEFAULT = P(I("default")) 38 | val DIE = P(I("die")) 39 | val DO = P(I("do")) 40 | val DOUBLE = P(I("double")) 41 | val ECHO = P(I("echo")) 42 | val ELSE = P(I("else")) 43 | val ELSEIF = P(I("elseif")) 44 | val EMPTY = P(I("empty")) 45 | val ENCODING = P(I("encoding")) 46 | val ENDDECLARE = P(I("enddeclare")) 47 | val ENDFOR = P(I("endfor")) 48 | val ENDFOREACH = P(I("endforeach")) 49 | val ENDIF = P(I("endif")) 50 | val ENDSWITCH = P(I("endswitch")) 51 | val ENDWHILE = P(I("endwhile")) 52 | val EVAL = P(I("eval")) 53 | val EXIT = P(I("exit")) 54 | val EXTENDS = P(I("extends")) 55 | val FINAL = P(I("final")) 56 | val FINALLY = P(I("finally")) 57 | val FLOAT = P(I("float")) 58 | val FOR = P(I("for")) 59 | val FOREACH = P(I("foreach")) 60 | val FROM = P(I("from")) 61 | val FUNCTION = P(I("function")) 62 | val GLOBAL = P(I("global")) 63 | val GOTO = P(I("goto")) 64 | val IF = P(I("if")) 65 | val IMPLEMENTS = P(I("implements")) 66 | val INCLUDE = P(I("include")) 67 | val INCLUDE_ONCE = P(I("include_once")) 68 | val INSTANCEOF = P(I("instanceof")) 69 | val INSTEADOF = P(I("insteadof")) 70 | val INT = P(I("int")) 71 | val INTEGER = P(I("integer")) 72 | val INTERFACE = P(I("interface")) 73 | val ISSET = P(I("isset")) 74 | val ITERABLE = P(I("iterable")) 75 | val LIST = P(I("list")) 76 | val NAMESPACE = P(I("namespace")) 77 | val NEW = P(I("new")) 78 | val OBJECT = P(I("object")) 79 | val OR = P(I("or")) 80 | val PARENT = P(I("parent")) 81 | val PHP = P(I("php")) 82 | val PRINT = P(I("print")) 83 | val PRIVATE = P(I("private")) 84 | val PROTECTED = P(I("protected")) 85 | val PUBLIC = P(I("public")) 86 | val REAL = P(I("real")) 87 | val RETURN = P(I("return")) 88 | val REQUIRE = P(I("require")) 89 | val REQUIRE_ONCE = P(I("require_once")) 90 | val SELF = P(I("self")) 91 | val STATIC = P(I("static")) 92 | val STRICT_TYPES = P(I("strict_types")) 93 | val STRING = P(I("string")) 94 | val SWITCH = P(I("switch")) 95 | val THROW = P(I("throw")) 96 | val TICKS = P(I("ticks")) 97 | val TRAIT = P(I("trait")) 98 | val TRY = P(I("try")) 99 | val UNSET = P(I("unset")) 100 | val USE = P(I("use")) 101 | val VAR = P(I("var")) 102 | val VOID = P(I("void")) 103 | val WHILE = P(I("while")) 104 | val XOR = P(I("xor")) 105 | val YIELD = P(I("yield")) 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/test/scala/de/thm/mni/ii/phpparser/StatementsTest.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | import de.thm.mni.ii.phpparser.ast.Basic._ 5 | import de.thm.mni.ii.phpparser.ast.Expressions._ 6 | import de.thm.mni.ii.phpparser.ast.Statements 7 | import de.thm.mni.ii.phpparser.ast.Statements._ 8 | 9 | import scala.collection.mutable.ArrayBuffer 10 | 11 | /** 12 | * Test PHPParsers ability to parse Statements. 13 | * @author Andrej Sajenko 14 | */ 15 | class StatementsTest extends FlatSpec with Matchers { 16 | 17 | "PHPParser" must "parse 'if' statement" in { 18 | PHPParser.parse(" } 35 | } 36 | 37 | it must "parse ifelse statement" in { 38 | PHPParser.parse(" 54 | } 55 | } 56 | 57 | it must "parse if else statement" in { 58 | PHPParser.parse(" 73 | } 74 | } 75 | 76 | it must "parse empty statement" in { 77 | PHPParser.parse(" 80 | } 81 | } 82 | 83 | it must "parse end tag" in { 84 | PHPParser.parse("") should matchPattern { case PHPParser.Success( 85 | Script(Text(""), ArrayBuffer(Statements.EndTagStmnt(stmnt, text))) 86 | ) => 87 | } 88 | } 89 | 90 | it must "parse do while loop" in { 91 | PHPParser.parse(" 0);") should matchPattern { case PHPParser.Success( 92 | Script(Text(""),ArrayBuffer(DoStmnt(RelationalExp(">", 93 | SimpleNameVar(Name(i)),OctalLiteral("")),CompoundStmnt(ArrayBuffer())))) 94 | ) => 95 | } 96 | } 97 | 98 | it must "parse continue in loop" in { 99 | PHPParser.parse( 100 | """ 101 | | 109 | } 110 | } 111 | 112 | it must "pase include / require statements" in { 113 | PHPParser.parse( 114 | """ 115 | | 126 | } 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/sbt,java,macos,linux,scala,windows,eclipse,intellij 3 | 4 | ### Eclipse ### 5 | 6 | .metadata 7 | bin/ 8 | tmp/ 9 | *.tmp 10 | *.bak 11 | *.swp 12 | *~.nib 13 | local.properties 14 | .settings/ 15 | .loadpath 16 | .recommenders 17 | 18 | # External tool builders 19 | .externalToolBuilders/ 20 | 21 | # Locally stored "Eclipse launch configurations" 22 | *.launch 23 | 24 | # PyDev specific (Python IDE for Eclipse) 25 | *.pydevproject 26 | 27 | # CDT-specific (C/C++ Development Tooling) 28 | .cproject 29 | 30 | # Java annotation processor (APT) 31 | .factorypath 32 | 33 | # PDT-specific (PHP Development Tools) 34 | .buildpath 35 | 36 | # sbteclipse plugin 37 | .target 38 | 39 | # Tern plugin 40 | .tern-project 41 | 42 | # TeXlipse plugin 43 | .texlipse 44 | 45 | # STS (Spring Tool Suite) 46 | .springBeans 47 | 48 | # Code Recommenders 49 | .recommenders/ 50 | 51 | # Scala IDE specific (Scala & Java development for Eclipse) 52 | .cache-main 53 | .scala_dependencies 54 | .worksheet 55 | 56 | ### Eclipse Patch ### 57 | # Eclipse Core 58 | .project 59 | 60 | # JDT-specific (Eclipse Java Development Tools) 61 | .classpath 62 | 63 | ### Intellij ### 64 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 65 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 66 | 67 | # User-specific stuff: 68 | .idea/**/workspace.xml 69 | .idea/**/tasks.xml 70 | .idea/dictionaries 71 | 72 | # Sensitive or high-churn files: 73 | .idea/**/dataSources/ 74 | .idea/**/dataSources.ids 75 | .idea/**/dataSources.xml 76 | .idea/**/dataSources.local.xml 77 | .idea/**/sqlDataSources.xml 78 | .idea/**/dynamic.xml 79 | .idea/**/uiDesigner.xml 80 | 81 | # Gradle: 82 | .idea/**/gradle.xml 83 | .idea/**/libraries 84 | 85 | # CMake 86 | cmake-build-debug/ 87 | 88 | # Mongo Explorer plugin: 89 | .idea/**/mongoSettings.xml 90 | 91 | ## File-based project format: 92 | *.iws 93 | 94 | ## Plugin-specific files: 95 | 96 | # IntelliJ 97 | /out/ 98 | 99 | # mpeltonen/sbt-idea plugin 100 | .idea_modules/ 101 | 102 | # JIRA plugin 103 | atlassian-ide-plugin.xml 104 | 105 | # Cursive Clojure plugin 106 | .idea/replstate.xml 107 | 108 | # Crashlytics plugin (for Android Studio and IntelliJ) 109 | com_crashlytics_export_strings.xml 110 | crashlytics.properties 111 | crashlytics-build.properties 112 | fabric.properties 113 | 114 | ### Intellij Patch ### 115 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 116 | 117 | # *.iml 118 | # modules.xml 119 | # .idea/misc.xml 120 | # *.ipr 121 | 122 | # Sonarlint plugin 123 | .idea/sonarlint 124 | 125 | ### Java ### 126 | # Compiled class file 127 | *.class 128 | 129 | # Log file 130 | *.log 131 | 132 | # BlueJ files 133 | *.ctxt 134 | 135 | # Mobile Tools for Java (J2ME) 136 | .mtj.tmp/ 137 | 138 | # Package Files # 139 | *.jar 140 | *.war 141 | *.ear 142 | *.zip 143 | *.tar.gz 144 | *.rar 145 | 146 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 147 | hs_err_pid* 148 | 149 | ### Linux ### 150 | *~ 151 | 152 | # temporary files which can be created if a process still has a handle open of a deleted file 153 | .fuse_hidden* 154 | 155 | # KDE directory preferences 156 | .directory 157 | 158 | # Linux trash folder which might appear on any partition or disk 159 | .Trash-* 160 | 161 | # .nfs files are created when an open file is removed but is still being accessed 162 | .nfs* 163 | 164 | ### macOS ### 165 | *.DS_Store 166 | .AppleDouble 167 | .LSOverride 168 | 169 | # Icon must end with two \r 170 | Icon 171 | 172 | # Thumbnails 173 | ._* 174 | 175 | # Files that might appear in the root of a volume 176 | .DocumentRevisions-V100 177 | .fseventsd 178 | .Spotlight-V100 179 | .TemporaryItems 180 | .Trashes 181 | .VolumeIcon.icns 182 | .com.apple.timemachine.donotpresent 183 | 184 | # Directories potentially created on remote AFP share 185 | .AppleDB 186 | .AppleDesktop 187 | Network Trash Folder 188 | Temporary Items 189 | .apdisk 190 | 191 | ### SBT ### 192 | # Simple Build Tool 193 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 194 | 195 | dist/* 196 | target/ 197 | lib_managed/ 198 | src_managed/ 199 | project/boot/ 200 | project/plugins/project/ 201 | .history 202 | .cache 203 | .lib/ 204 | 205 | ### Scala ### 206 | 207 | ### Windows ### 208 | # Windows thumbnail cache files 209 | Thumbs.db 210 | ehthumbs.db 211 | ehthumbs_vista.db 212 | 213 | # Folder config file 214 | Desktop.ini 215 | 216 | # Recycle Bin used on file shares 217 | $RECYCLE.BIN/ 218 | 219 | # Windows Installer files 220 | *.cab 221 | *.msi 222 | *.msm 223 | *.msp 224 | 225 | # Windows shortcuts 226 | *.lnk 227 | 228 | # End of https://www.gitignore.io/api/sbt,java,macos,linux,scala,windows,eclipse,intellij 229 | 230 | ### Ignore all IntelliJ-files ### 231 | .idea/ -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/statements/ControlFlowParser.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.statements 2 | 3 | import fastparse.noApi._ 4 | import de.thm.mni.ii.phpparser.parser.literals.WsAPI._ 5 | import de.thm.mni.ii.phpparser.parser.literals.Lexical.Ws 6 | 7 | import de.thm.mni.ii.phpparser.ast.{Basic => BAst, Expressions => EAst, Statements => SAst} 8 | 9 | import de.thm.mni.ii.phpparser.parser.literals.Keywords._ 10 | 11 | import de.thm.mni.ii.phpparser.parser.Basic.{SemicolonFactory, WsExp, Semicolon} 12 | import de.thm.mni.ii.phpparser.parser.literals.Literals.{IntegerLiteral, Name} 13 | import de.thm.mni.ii.phpparser.parser.expressions.ExpressionParser.Expression 14 | import de.thm.mni.ii.phpparser.parser.statements.StatementParser.{Statement, Statements, wrap} 15 | 16 | /** 17 | * This object contains all statements, which manipulate the sequential control flow. 18 | */ 19 | object ControlFlowParser { 20 | 21 | // selection statements 22 | 23 | val IfStmnt = { 24 | val IfBody: P[(Seq[de.thm.mni.ii.phpparser.ast.Statements.Statement], Seq[(EAst.Expression, Seq[SAst.Statement])], Option[Seq[SAst.Statement]], Option[BAst.Text])] = P(( 25 | ":" ~/ Statements 26 | ~ (ELSEIF ~ "(" ~/ Expression ~ ")" ~/ ":" ~/ Statements).rep 27 | ~ (ELSE ~ ":" ~/ Statements).? ~ ENDIF ~/ SemicolonFactory) | 28 | (Statement 29 | ~/ (ELSEIF ~/ "(" ~ Expression ~ ")" ~/ Statement).rep 30 | ~ (ELSE ~~ &(WsExp | Semicolon | "{") ~/ Statement).? 31 | ).map(t => (Seq(t._1), t._2.map(e => (e._1, Seq(e._2))), t._3.map(Seq(_)), None)) 32 | ) 33 | 34 | P(IF ~ "(" ~/ Expression ~ ")" ~/ IfBody).map { 35 | case (exp, (stmnts, elseifs, elseStmnts, text)) => wrap(SAst.IfStmnt(exp, stmnts, elseifs, elseStmnts), text) 36 | } 37 | } 38 | 39 | val SwitchStmnt = { 40 | val CaseBlock = P(CASE ~~ &(WsExp) ~/ Expression ~ ((":" ~/ Statements) | Statement.rep(1))) 41 | .map(t => SAst.CaseBlock(t._1, t._2)) 42 | 43 | val DefaultBlock = P(DEFAULT ~ (":" ~/ Statements | Statement.rep(1))) 44 | .map(SAst.DefaultBlock) 45 | 46 | val SwitchBody: P[(Seq[SAst.SwitchBlock], Option[BAst.Text])] = P( 47 | (":" ~/ (CaseBlock | DefaultBlock).rep ~ ENDSWITCH ~ SemicolonFactory) 48 | | ("{" ~/ (CaseBlock | DefaultBlock).rep ~ "}").map(t => (t, None))) 49 | 50 | P(SWITCH ~ "(" ~/ Expression ~ ")" ~/ SwitchBody).map { 51 | case (exp, (blocks, text)) => wrap(SAst.SwitchStmnt(exp, blocks), text) 52 | } 53 | } 54 | 55 | val SelectionStmnt: P[SAst.Statement] = P(IfStmnt | SwitchStmnt) 56 | 57 | 58 | // iteration statements 59 | 60 | val WhileStmnt = { 61 | val WhileBody: P[(Seq[SAst.Statement], Option[BAst.Text])] = P( 62 | (":" ~/ Statements ~ ENDWHILE ~ SemicolonFactory) 63 | | Statement.map(t => (Seq(t), None)) 64 | ) 65 | 66 | P(WHILE ~ "(" ~/ Expression ~ ")" ~/ WhileBody).map { 67 | case (exp, (stmnt, text)) => wrap(SAst.WhileStmnt(exp, stmnt), text) 68 | } 69 | } 70 | 71 | val DoStmnt = 72 | P(DO ~~ &(WsExp | Semicolon | "{") ~/ Statement ~/ WHILE ~/ "(" ~/ Expression ~ ")" ~/ SemicolonFactory).map { 73 | case (stmnt, exp, text) => wrap(SAst.DoStmnt(exp, stmnt), text) 74 | } 75 | 76 | val ForStmnt = { 77 | val ForBody: P[(Seq[SAst.Statement], Option[BAst.Text])] = P( 78 | (":" ~/ Statements ~ ENDFOR ~ SemicolonFactory) 79 | | Statement.map(t => (Seq(t), None)) 80 | ) 81 | 82 | val ForExpressionList = P(Expression.rep(sep = ",".~/) ~ SemicolonFactory) 83 | .map(t => SAst.ForExpressionList(t._1, t._2)) 84 | 85 | P(FOR ~ "(" ~/ ForExpressionList ~/ ForExpressionList ~/ Expression.rep(sep = ",".~/) ~ ")" ~/ ForBody).map { 86 | case (init, control, exps, (body, text)) => wrap(SAst.ForStmnt(init, control, SAst.ForExpressionList(exps, None), body), text) 87 | } 88 | } 89 | 90 | val ForeachStmnt = { 91 | val ForeachBody: P[(Seq[SAst.Statement], Option[BAst.Text])] = P( 92 | (":" ~/ Statements ~ ENDFOREACH ~ SemicolonFactory) 93 | | Statement.map(t => (Seq(t), None)) 94 | ) 95 | 96 | P(FOREACH ~ "(" ~/ Expression ~~ &(Ws) ~/ AS ~~ WsExp ~/ (( 97 | "&" ~ NoCut(Expression)).map(t => (None, true, t)) | 98 | (Expression ~ ("=>" ~ "&".!.? ~ Expression).?) 99 | .map(t => if (t._2.isDefined) (Some(t._1), t._2.get._1.isDefined, t._2.get._2) else (None, false, t._1)) 100 | ) ~ ")" ~/ ForeachBody 101 | ).map { 102 | case (exp, (key, valueDesVar, value), (body, text)) => wrap(SAst.ForeachStmnt(exp, key, valueDesVar, value, body), text) 103 | } 104 | } 105 | 106 | val IterationStmnt: P[SAst.Statement] = P(WhileStmnt | DoStmnt | ForeachStmnt | ForStmnt) 107 | 108 | 109 | // jump statements 110 | 111 | val JumpStmnt: P[SAst.Statement] = P( 112 | (GOTO ~~ Ws ~/ Name ~ SemicolonFactory).map(t => wrap(SAst.GotoStmnt(t._1), t._2)) 113 | | (CONTINUE ~~ &(Ws | Semicolon) ~/ IntegerLiteral.? ~ SemicolonFactory).map(t => wrap(SAst.ContinueStmnt(t._1), t._2)) 114 | | (BREAK ~~ &(Ws | Semicolon) ~/ IntegerLiteral.? ~ SemicolonFactory).map(t => wrap(SAst.BreakStmnt(t._1), t._2)) 115 | | (RETURN ~~ &(WsExp | Semicolon) ~/ Expression.? ~ SemicolonFactory).map(t => wrap(SAst.ReturnStmnt(t._1), t._2)) 116 | | (THROW ~~ &(WsExp) ~/ Expression ~ SemicolonFactory).map(t => wrap(SAst.ThrowStmnt(t._1), t._2))) 117 | } 118 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/statements/StatementParser.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.statements 2 | 3 | import fastparse.noApi._ 4 | import de.thm.mni.ii.phpparser.parser.literals.WsAPI._ 5 | import de.thm.mni.ii.phpparser.parser.literals.Lexical.Ws 6 | 7 | import de.thm.mni.ii.phpparser.ast.{Basic => BAst, Expressions => EAst, Statements => SAst} 8 | 9 | import de.thm.mni.ii.phpparser.parser.literals.KeywordConversions._ 10 | import de.thm.mni.ii.phpparser.parser.literals.Keywords._ 11 | import de.thm.mni.ii.phpparser.parser.literals.Literals._ 12 | 13 | import de.thm.mni.ii.phpparser.parser.Basic.{EchoStartTag, QualifiedName, SemicolonFactory} 14 | import de.thm.mni.ii.phpparser.parser.expressions.ExpressionParser.Expression 15 | import de.thm.mni.ii.phpparser.parser.statements.ControlFlowParser._ 16 | import de.thm.mni.ii.phpparser.parser.statements.DeclarationParser._ 17 | 18 | /** 19 | * This object contains all basic statements 20 | */ 21 | object StatementParser { 22 | 23 | val Statement: P[SAst.Statement] = P(CompoundStmnt | NamedLabelStmnt | SelectionStmnt | IterationStmnt | JumpStmnt | TryStmnt 24 | | DeclareStmnt | ConstDeclStmnt | FunctionDefStmnt | ClassDeclStmnt | InterfaceDeclStmnt | TraitDeclStmnt 25 | | NamespaceDefStmnt | NamespaceUseDeclStmnt | GlobalDeclStmnt | FunctionStaticDeclStmnt | EmptyStmnt | ExpStmnt) 26 | 27 | val Statements: P[Seq[SAst.Statement]] = P(Statement.? ~ AvailableStatement.rep).map(t => t._1 match { 28 | case Some(stmnt) => stmnt +: t._2 29 | case None => t._2 30 | }) 31 | 32 | val AvailableStatement: P[SAst.Statement] = P(EchoTagStmnt | Statement) 33 | 34 | val EchoTagStmnt = P(EchoStartTag ~ Expression.rep(min = 1, sep = ",") ~ SemicolonFactory).map { 35 | case (_, expressions, text) => wrap(SAst.EchoTagStmnt(expressions), text) 36 | } 37 | 38 | private[statements] def wrap(stmnt: SAst.Statement, text: Option[BAst.Text]): SAst.Statement = text match { 39 | case Some(_) => SAst.EndTagStmnt(stmnt, text) 40 | case None => stmnt 41 | } 42 | 43 | val EmptyStmnt = P(SemicolonFactory).map(wrap(SAst.EmptyStmnt(), _)) 44 | val CompoundStmnt = P("{" ~/ Statements ~ "}").map(SAst.CompoundStmnt) 45 | val NamedLabelStmnt = P(Name ~ ":" ~ Statement).map { 46 | case (name, stmnt) => SAst.NamedLabelStmnt(name, stmnt) 47 | } 48 | val ExpStmnt = P(Expression ~ SemicolonFactory).map { 49 | case (exp, text) => wrap(SAst.ExpressionStmnt(exp), text) 50 | } 51 | 52 | val TryStmnt = { 53 | val CatchClause = P(CATCH ~ "(" ~/ QualifiedName ~~ Ws ~ VariableName ~ ")" ~/ CompoundStmnt).map { 54 | case (qName, varName, stmnt) => SAst.CatchClause(qName, varName, stmnt) 55 | } 56 | 57 | P(TRY ~ &("{") ~/ CompoundStmnt ~/ CatchClause.rep() ~ (FINALLY ~ &("{") ~/ CompoundStmnt).?).map { 58 | case (stmnt, clauses, finallyStmnt) => SAst.TryStmnt(stmnt, clauses, finallyStmnt) 59 | } 60 | } 61 | 62 | val DeclareStmnt = { 63 | val DeclareDeclarative: P[SAst.DeclareDeclarative.Value] = 64 | P(TicksDeclarative | EncodingDeclarative | StrictTypesDeclarative) 65 | 66 | val DeclareBody: P[(Seq[SAst.Statement], Option[BAst.Text])] = 67 | P(":" ~/ Statements ~ ENDDECLARE ~ SemicolonFactory | 68 | Statement.map(stmnt => (Seq(stmnt), None))) 69 | 70 | P(DECLARE ~ "(" ~/ DeclareDeclarative ~ "=" ~ Literal ~ ")" ~/ DeclareBody).map { 71 | case (decl, literal, (stmnt, text)) => wrap(SAst.DeclareStmnt(decl, literal, stmnt), text) 72 | } 73 | } 74 | 75 | 76 | val TypeDecl: P[SAst.TypeDecl] = P(ArrayType | CallableType | IterableType | 77 | BoolType | FloatType | IntType | StringType | QualifiedName.map(SAst.QualifiedType)) 78 | val PossibleFunctionType: P[SAst.PossibleTypes] = P(VoidType | TypeDecl) 79 | 80 | private val ParamType: P[(Option[SAst.TypeDecl], Boolean)] = P(TypeDecl.? ~ "&".!.?.map(_.isDefined)) 81 | private val ParameterDecl: P[SAst.ParameterDecl] = { 82 | val ParameterDeclFactory: P[(Option[SAst.TypeDecl], Boolean) => SAst.ParameterDecl] = P( 83 | ("..." ~ VariableName).map(name => (a: Option[SAst.TypeDecl], b: Boolean) => SAst.VariadicParam(a, b, name)) 84 | | (VariableName ~ ("=" ~ Expression).?).map(t => (a: Option[SAst.TypeDecl], b: Boolean) => SAst.SimpleParam(a, b, t._1, t._2))) 85 | 86 | P(ParamType ~ ParameterDeclFactory).map { 87 | case (typeDecl, isRef, toParamDecl) => toParamDecl(typeDecl, isRef) 88 | } 89 | } 90 | 91 | private[statements] val FuncHeader: P[SAst.FuncHeader] = 92 | P(FUNCTION ~~ &(Ws) ~ "&".!.? ~ Name ~/ "(" 93 | ~/ ParameterDecl.rep(sep = ",".~/) ~ ")" ~/ (":" ~/ PossibleFunctionType).? 94 | ).map { 95 | case (returnRef, name, params, returnValue) => SAst.FuncHeader(returnRef.isDefined, Some(name), params, returnValue) 96 | } 97 | 98 | private[parser] val AnonymousFuncHeader: P[(SAst.FuncHeader, Seq[(Boolean, EAst.SimpleNameVar)])] = 99 | P(FUNCTION ~~ &(Ws | "(") ~ "&".!.? 100 | ~ "(" ~/ ParameterDecl.rep(sep = ",".~/) ~ ")" 101 | ~/ (USE ~ "(" ~ ("&".!.?.map(_.isDefined) ~ VariableName).rep(1, sep = ",") ~ ")").? 102 | ~ (":" ~/ PossibleFunctionType).? 103 | ).map { 104 | case (returnRef, params, uses, returnValue) => (SAst.FuncHeader(returnRef.isDefined, None, params, returnValue), uses.getOrElse(Seq())) 105 | } 106 | 107 | val FunctionDefStmnt = P(FuncHeader ~ &("{") ~/ CompoundStmnt).map { 108 | case (header, body) => SAst.FuncDef(header, body) 109 | } 110 | 111 | val NamespaceDefStmnt: P[SAst.Statement] = P( 112 | (NAMESPACE ~~ &(Ws) ~ QualifiedName ~ SemicolonFactory).map { 113 | case (name, text) => wrap(SAst.NamespaceDef(Some(name), None), text) 114 | } | (NAMESPACE ~~ &(Ws) ~/ QualifiedName.? ~ CompoundStmnt).map{ 115 | case (name, stmnt) => SAst.NamespaceDef(name, Some(stmnt)) 116 | }) 117 | } 118 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/expressions/OperatorParser.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.expressions 2 | 3 | import fastparse.noApi._ 4 | import de.thm.mni.ii.phpparser.parser.literals.WsAPI._ 5 | import de.thm.mni.ii.phpparser.parser.literals.Lexical.Ws 6 | 7 | import de.thm.mni.ii.phpparser.ast.{Expressions => EAst} 8 | import de.thm.mni.ii.phpparser.ast.Statements.ClassDecl 9 | 10 | import de.thm.mni.ii.phpparser.parser.literals.Keywords._ 11 | import de.thm.mni.ii.phpparser.parser.literals.KeywordConversions._ 12 | import de.thm.mni.ii.phpparser.parser.literals.Literals._ 13 | 14 | import de.thm.mni.ii.phpparser.parser.Basic._ 15 | import de.thm.mni.ii.phpparser.parser.statements.DeclarationParser.ClassDeclBody 16 | import de.thm.mni.ii.phpparser.parser.expressions.VariableParser.Variable 17 | import de.thm.mni.ii.phpparser.parser.expressions.ExpressionParser.{Expression, ArgumentExpressionList, ListIntrinsic, PrimaryExpWithoutVariable, CloneExp, SingleExpression} 18 | 19 | /** 20 | * Created by tobias on 02.06.17. 21 | */ 22 | object OperatorParser { 23 | 24 | val LogicalOrExpr2 = P(LogicalXOrExp.rep(sep=OR.~/, min=1)).map(_.reduceLeft(EAst.LogicalOrExp2)) 25 | val LogicalXOrExp = P(LogicalAndExp2.rep(sep=XOR.~/, min=1)).map(_.reduceLeft(EAst.LogicalXOrExp)) 26 | val LogicalAndExp2 = P(CondExp.rep(sep=AND.~/, min=1)).map(_.reduceLeft(EAst.LogicalAndExp2)) 27 | 28 | val CondExp : P[EAst.Expression] = { 29 | val ConditionalExpFactory : P[EAst.Expression => EAst.Expression] = P( 30 | ("??" ~/ Expression).map(e => (x: EAst.Expression) => EAst.CoalesceExp(x,e)) 31 | | ("?" ~~ !">" ~/ Expression.? ~ ":" ~/ CondExp).map(e => (x) => EAst.TernaryExp(x,e._1, e._2))) 32 | 33 | P((LogicalOrExp ~ ConditionalExpFactory.?).map(t => if(t._2.isDefined) t._2.get(t._1) else t._1)) 34 | } 35 | 36 | val LogicalOrExp = P(LogicalAndExp.rep(sep="||".~/, min=1)).map(_.reduceLeft(EAst.LogicalOrExp)) 37 | val LogicalAndExp = P(BitwiseOrExp.rep(sep="&&".~/, min=1)).map(_.reduceLeft(EAst.LogicalAndExp)) 38 | val BitwiseOrExp = P(BitwiseXOrExp.rep(sep=("|" ~~ !"|").~/, min=1)).map(_.reduceLeft(EAst.BitwiseOrExp)) 39 | val BitwiseXOrExp = P(BitwiseAndExp.rep(sep="^".~/, min=1)).map(_.reduceLeft(EAst.BitwiseXOrExp)) 40 | val BitwiseAndExp = P(EqualityExp.rep(sep=("&" ~~ !"&").~/, min=1)).map(_.reduceLeft(EAst.BitwiseAndExp)) 41 | 42 | val EqualityExp: P[EAst.Expression] = P(RelationalExp ~ (EqualityOp ~/ RelationalExp).rep) 43 | .map(t => t._2.foldLeft(t._1)((exp, op) => EAst.EqualityExp(op._1, exp, op._2))) 44 | val RelationalExp: P[EAst.Expression] = P(ShiftExp ~ (RelationalOp ~ ShiftExp).rep) 45 | .map(t => t._2.foldLeft(t._1)((exp, op) => EAst.RelationalExp(op._1, exp, op._2))) 46 | 47 | val ShiftExp: P[EAst.Expression] = { 48 | val ShiftFactory : P[EAst.Expression => EAst.Expression] = P( 49 | ("<<" ~~ !"=" ~~ !"<" ~/ AdditiveExp).map(e => (x: EAst.Expression) => EAst.LShiftExp(x, e)) 50 | | (">>" ~~ !"=" ~/ AdditiveExp).map(e => (x: EAst.Expression) => EAst.RShiftExp(x, e))) 51 | 52 | P(AdditiveExp ~ ShiftFactory.rep) 53 | .map(t => t._2.foldLeft(t._1)((exp, op) => op(exp))) 54 | } 55 | 56 | val AdditiveExp: P[EAst.Expression] = { 57 | val AdditiveFactory : P[EAst.Expression => EAst.Expression] = P( 58 | ("+" ~/ MultExp).map(e => (x: EAst.Expression) => EAst.AddExp(x, e)) 59 | | ("-" ~/ MultExp).map(e => (x: EAst.Expression) => EAst.SubExp(x, e)) 60 | | ("." ~/ MultExp).map(e => (x) => EAst.SubExp(x, e))) 61 | 62 | P(MultExp ~ AdditiveFactory.rep) 63 | .map(t => t._2.foldLeft(t._1)((exp, op) => op(exp))) 64 | } 65 | 66 | val MultExp: P[EAst.Expression] = { 67 | val MultFactory : P[EAst.Expression => EAst.Expression] = P( 68 | ("*" ~/ ExponentiationExp).map(e => (x: EAst.Expression) => EAst.MulExp(x, e)) 69 | | ("/" ~/ ExponentiationExp).map(e => (x: EAst.Expression) => EAst.DivExp(x, e)) 70 | | ("%" ~/ ExponentiationExp).map(e => (x) => EAst.ModExp(x, e))) 71 | 72 | P(ExponentiationExp ~ MultFactory.rep) 73 | .map(t => t._2.foldLeft(t._1)((exp, op) => op(exp))) 74 | } 75 | 76 | val ExponentiationExp: P[EAst.Expression] = P(InstanceOfExp ~ ("**" ~/ Expression).?) 77 | .map(t => if(t._2.isDefined) EAst.ExponentiationExp(t._1, t._2.get) else t._1) 78 | 79 | val InstanceOfExp : P[EAst.Expression] = P(UnaryExp ~~ 80 | (Ws ~ INSTANCEOF ~~ &(WsExp) ~/ (QualifiedName.map(Right(_)) | Expression.map(Left(_)))).? 81 | ).map(t => if(t._2.isDefined) EAst.InstanceOfExp(t._1, t._2.get) else t._1) 82 | 83 | val PrefixIncrementExp = P("++" ~/ Variable).map(EAst.PrefixIncrementExp) 84 | val PrefixDecrementExp = P("--" ~/ Variable).map(EAst.PrefixDecrementExp) 85 | val UnaryOpExp = P(UnaryOp ~ (SingleExpression | UnaryExp)).map(t => EAst.UnaryOpExp(t._1,t._2)) 86 | val ErrorControlExp = P("@" ~/ Expression).map(EAst.ErrorControlExp) 87 | val ShellCommandExp = P("`" ~~ DqCommandCharSequence ~~ "`").map(EAst.ShellCommandExp) 88 | 89 | val CastExp = { 90 | val CastType : P[EAst.CastType.Value] = P(ArrayCastType | BinaryCastType | BooleanCastType | BoolCastType | 91 | DoubleCastType | IntegerCastType | IntCastType | FloatCastType | ObjectCastType | 92 | RealCastType | StringCastType | UnsetCastType) 93 | 94 | P("(" ~ CastType ~ ")" ~/ Expression).map(t => EAst.CastExp(t._1, t._2)) 95 | } 96 | 97 | val UnaryExp : P[EAst.Expression] = P( 98 | PrefixIncrementExp | PrefixDecrementExp | UnaryOpExp 99 | | ErrorControlExp | ShellCommandExp | CastExp | PostfixExp) 100 | 101 | val ListAssignment = P(NoCut(ListIntrinsic) ~ "=" ~/ (CondExp | SingleExpression)) 102 | .map(t => EAst.ListAssignmentExp(t._1, t._2)) 103 | 104 | val ObjectCreationExp : P[EAst.Expression] = P( 105 | NEW ~~ &(WsExp) ~/ (( 106 | CLASS ~~ &("(" | "{" | Ws) ~/ ("(" ~/ ArgumentExpressionList ~ ")").? ~ ClassDeclBody) 107 | .map(t => EAst.AnonymousClassCreationExp(ClassDecl(None, None, t._2._1, t._2._2, t._2._3), t._1)) 108 | | (Expression ~ ("(" ~/ ArgumentExpressionList ~ ")").?) 109 | .map(t => EAst.InstanceCreationExp(t._1, t._2))) 110 | ) 111 | 112 | val PostfixExp : P[EAst.Expression] = { 113 | val PostfixOperatorFactory : P[EAst.Variable => EAst.Expression] = P( 114 | "++".!.map(_ => (x: EAst.Variable) => EAst.PostfixIncrementExp(x)) 115 | | "--".!.map(_ => (x: EAst.Variable) => EAst.PostfixDecrementExp(x)) 116 | | "::" ~ NameWithKeyword.map(n => (x: EAst.Variable) => EAst.ClassConstAcc(x, n)) 117 | | ("=" ~ "&".!.? ~ (CondExp | SingleExpression)).map(e => (x: EAst.Variable) => EAst.SimpleAssignmentExp(e._1.isDefined, x, e._2)) 118 | | (AssignmentOp ~~ "=" ~/ (CondExp | SingleExpression)).map(e => (x: EAst.Variable) => EAst.CompoundAssignmentExp(e._1, x, e._2))) 119 | 120 | P(ListAssignment | PrimaryExpWithoutVariable | CloneExp | ObjectCreationExp 121 | | (Variable ~/ PostfixOperatorFactory.?).map(t => if(t._2.isDefined) t._2.get(t._1) else t._1)) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/literals/Literals.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.literals 2 | 3 | import de.thm.mni.ii.phpparser.ast.{Basic => BAst} 4 | import de.thm.mni.ii.phpparser.ast.Expressions.SimpleNameVar 5 | 6 | import fastparse.all._ 7 | 8 | import de.thm.mni.ii.phpparser.parser.literals.Keywords.AllKeywords 9 | import de.thm.mni.ii.phpparser.parser.literals.Lexical.{Whitespace, WsChars, Newline} 10 | import de.thm.mni.ii.phpparser.parser.expressions.ExpressionParser.{Expression} 11 | import de.thm.mni.ii.phpparser.parser.expressions.VariableParser.Variable 12 | 13 | /** 14 | * Created by tobias on 27.05.17. 15 | */ 16 | object Literals { 17 | 18 | val NonDigitSeq = ('a' to 'z') ++ ('A' to 'Z') ++ ('\u0080' to '\u00ff') :+ '_' 19 | val NonDigit = P(CharIn(NonDigitSeq).!) 20 | 21 | val NameWithKeyword : P[BAst.Name] = P(NonDigit ~ (NonDigit | Digit).rep).map(t => BAst.Name(t._1 + t._2.mkString)) 22 | val Name : P[BAst.Name] = P(!Keyword ~ NameWithKeyword) 23 | 24 | val Keyword = P(StringInIgnoreCase(AllKeywords:_*) ~ !(NonDigit | Digit)) 25 | 26 | val VariableName : P[SimpleNameVar] = P("$" ~ NameWithKeyword).map(SimpleNameVar) 27 | 28 | val StringLiteral : P[BAst.StringLiteral] = P(SqStringLiteral | DqStringLiteral | HdStringLiteral | NdStringLiteral) 29 | 30 | val Digit = P(CharIn("0123456789").!) 31 | val NonZeroDigit = P(CharIn("123456789").!) 32 | val OctalDigit = P(CharIn("01234567").!) 33 | val HexadecimalDigit = P(CharIn("0123456789ABCDEFabcdef").!) 34 | val BinaryDigit = P(CharIn("01").!) 35 | 36 | val DecimalLiteral = P(NonZeroDigit ~ Digit.rep).map(t => BAst.DecimalLiteral(t._1 + t._2.mkString)) 37 | val OctalLiteral = P("0" ~ OctalDigit.rep).map(t => BAst.OctalLiteral(t.mkString)) 38 | val HexadecimalLiteral = P(("0x" | "0X") ~ HexadecimalDigit.rep).map(t => BAst.HexadecimalLiteral(t.mkString)) 39 | val BinaryLiteral = P(("0b" | "0B") ~ BinaryDigit.rep).map(t => BAst.BinaryLiteral(t.mkString)) 40 | 41 | val IntegerLiteral : P[BAst.IntegerLiteral] = P(DecimalLiteral | BinaryLiteral | HexadecimalLiteral | OctalLiteral) 42 | 43 | val ExponentPart : P[(Boolean, String)] = P(("e" | "E") ~ ("+".!.map(_ => true) | "-".!.map(_ => false)).? ~ Digit.rep(1)).map(t => (if(t._1.isDefined) t._1.get else true, t._2.mkString)) 44 | val FloatingLiteral : P[BAst.FloatingLiteral] = P(("." ~ Digit.rep(1) ~ ExponentPart.?).map(t => BAst.FloatingLiteral("", t._1.mkString, t._2)) | 45 | (Digit.rep(1) ~ (ExponentPart.map(e => (Some(e), "")) | ("." ~ Digit.rep ~ ExponentPart.?).map(t => (t._2, t._1.mkString)))).map(t => BAst.FloatingLiteral(t._1.mkString, t._2._2, t._2._1))) 46 | 47 | val AssignmentOp = P(StringIn("**", "*", "/", "+", "-", ".", "<<", ">>", "&", "^", "|", "%").!) 48 | val EqualityOp = P(StringIn("===", "==", "!==", "!=", "<>").!) 49 | val RelationalOp = P(StringIn("<=>", "<=", ">=", "<", ">").!) 50 | val UnaryOp = P(CharIn("+-!~").!) 51 | 52 | 53 | val OctalStringElement = P("\\" ~ OctalDigit.rep(min = 1, max = 3)).map(t => BAst.OctalStringElement(t.map(_(0)))) 54 | val HexStringElement = P("\\" ~ IgnoreCase("x") ~ (HexadecimalDigit.rep(min = 1, max = 2) | "{" ~ HexadecimalDigit.rep ~"}")).map(t => BAst.HexStringElement(t.map(_(0)))) 55 | val UnicodeStringElement = P("\\u{" ~ ( 56 | HexadecimalDigit.rep(min = 1).map(t => Left(t.map(_(0)))) | 57 | Variable.map(Right(_)) ~ Whitespace 58 | ) ~ "}").map(BAst.UnicodeStringElement) 59 | val VarStringElement = P(VariableName ~ (( 60 | "->" ~ Name).map(BAst.PropertyStringVarAcc) | ("[" ~ ( 61 | Name.map(BAst.NameOffsetStringVarAcc) | 62 | VariableName.map(BAst.VarOffsetStringVarAcc) | 63 | IntegerLiteral.map(BAst.IntOffsetStringVarAcc) 64 | ) ~ "]")).?).map(t => BAst.VarStringElement(t._1, t._2)) 65 | val ExpressionStringElement = P("${" ~ Expression ~ "}").map(BAst.ExpressionStringElement) 66 | 67 | 68 | val SqEscapeSequence = P("\\".! ~ AnyChar.!).map(t => t._1 + t._2) 69 | val SqUnescapedSequence = P(CharsWhile(!"\\'".contains(_)).!) 70 | val SqCharSequence = P((SqEscapeSequence | SqUnescapedSequence).rep).map(_.mkString) 71 | val SqStringLiteral = P(CharIn("bB").!.? ~ "'" ~/ SqCharSequence ~ "'").map(t => BAst.SQStringLiteral(t._1, t._2)) 72 | 73 | val DqNormalEscapeSequence = P((("\\".! ~ !(CharIn("xX01234567") | "u{")) | &("$" ~ !NonDigit)) ~ AnyChar.!).map(t => t._1 + t._2) 74 | val DqUnescapedSequence = P(CharsWhile(!"\\\"$".contains(_)).!) 75 | val DqStringElement = P((DqNormalEscapeSequence | DqUnescapedSequence).rep(1)).map(t => BAst.DQStringElement(t.mkString)) 76 | val DqCharSequence = P((DqStringElement | OctalStringElement | HexStringElement | UnicodeStringElement | VarStringElement | ExpressionStringElement).rep) 77 | val DqStringLiteral = P(CharIn("bB").!.? ~ "\"" ~/ DqCharSequence ~ "\"").map(t => BAst.DQStringLiteral(t._1, t._2)) 78 | 79 | val DqCommandUnescapedSequence = P(CharsWhile(!"\\`$".contains(_)).!) 80 | val DqCommandStringElement = P((DqNormalEscapeSequence | DqCommandUnescapedSequence).rep(1)).map(t => BAst.DQStringElement(t.mkString)) 81 | val DqCommandCharSequence = P((DqCommandStringElement | OctalStringElement | HexStringElement | UnicodeStringElement | VarStringElement | ExpressionStringElement).rep) 82 | 83 | val HdUnescapedSequence = P(CharsWhile(!"\\\n\r$".contains(_)).!) 84 | val HdStringElement = P((DqNormalEscapeSequence | HdUnescapedSequence).rep(1)).map(t => BAst.HDStringElement(t.mkString)) 85 | val HdCharSequence = P((HdStringElement | OctalStringElement | HexStringElement | UnicodeStringElement | VarStringElement | ExpressionStringElement).rep) 86 | def HdRest(identifier: BAst.Name) : P[(BAst.Name, Seq[BAst.StringElement])] = P(HdCharSequence ~ 87 | (Newline ~ !(identifier.name ~ ";".? ~ Newline) ~ HdCharSequence).rep ~ 88 | Newline ~ identifier.name ~ &(";".? ~ Newline) 89 | ).map(t => (identifier, t._2.foldLeft(t._1)(_ ++ Seq(BAst.HDNewLine) ++ _))) 90 | val HdStringLiteral = P(CharIn("bB").!.? ~ Whitespace ~ "<<<" ~ WsChars.rep ~ 91 | ((("\"" ~/ Name ~ "\"") | Name) ~/ WsChars.rep ~ Newline) 92 | .flatMap(HdRest)) 93 | .map(t => BAst.HeredocStringLiteral(t._1, t._2._1, t._2._2)) 94 | 95 | val NdNormalEscapeSequence = P("\\".! ~ AnyChar.!).map(t => t._1 + t._2) 96 | val NdUnescapedSequence = P(CharsWhile(!"\\\n\r".contains(_)).!) 97 | val NdStringElement = P((NdNormalEscapeSequence | NdUnescapedSequence).rep(1)).map(t => BAst.NDStringElement(t.mkString)) 98 | def NdRest(identifier: BAst.Name) : P[(BAst.Name, Seq[BAst.StringElement])] = P(NdStringElement ~ 99 | (Newline ~ !(identifier.name ~ ";".? ~ Newline) ~ NdStringElement).rep ~ 100 | Newline ~ identifier.name ~ &(";".? ~ Newline) 101 | ).map(t => (identifier, t._2.foldLeft(Seq[BAst.StringElement](t._1))(_ ++ Seq(BAst.NDNewLine, _)))) 102 | val NdStringLiteral = P(CharIn("bB").!.? ~ Whitespace ~ "<<<" ~ WsChars.rep ~ 103 | ("\'" ~/ Name ~ "\'" ~ WsChars.rep ~ Newline) 104 | .flatMap(NdRest)) 105 | .map(t => BAst.HeredocStringLiteral(t._1, t._2._1, t._2._2)) 106 | 107 | val Literal : P[BAst.Literal] = P(FloatingLiteral | IntegerLiteral | StringLiteral) 108 | } 109 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/ast/Expressions.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.ast 2 | 3 | import de.thm.mni.ii.phpparser.ast.Basic._ 4 | import de.thm.mni.ii.phpparser.ast.Statements.{ClassDecl, CompoundStmnt, FuncHeader} 5 | 6 | object Expressions { 7 | 8 | abstract class Expression 9 | 10 | case class IncludeExp(exp: Expression) extends Expression 11 | case class IncludeOnceExp(exp: Expression) extends Expression 12 | case class RequireExp(exp: Expression) extends Expression 13 | case class RequireOnceExp(exp: Expression) extends Expression 14 | case class YieldExp(a: ArrayElement) extends Expression 15 | case class YieldFromExp(exp: Expression) extends Expression 16 | case class LogicalOrExp2(exp1: Expression, exp2: Expression) extends Expression 17 | case class LogicalXOrExp(exp1: Expression, exp2: Expression) extends Expression 18 | case class LogicalAndExp2(exp1: Expression, exp2: Expression) extends Expression 19 | case class AssignmentExp(e: Boolean) extends Expression 20 | case class TernaryExp(cond: Expression, first: Option[Expression], second: Expression) extends Expression 21 | case class SimpleAssignmentExp(byRef: Boolean, variable: Variable, exp: Expression) extends Expression 22 | case class ListAssignmentExp(list: ListIntrinsic, exp: Expression) extends Expression 23 | case class CoalesceExp(exp1: Expression, exp2: Expression) extends Expression 24 | case class CompoundAssignmentExp(op: String, variable: Variable, exp: Expression) extends Expression 25 | case class LogicalOrExp(exp1: Expression, exp2: Expression) extends Expression 26 | case class LogicalAndExp(exp1: Expression, exp2: Expression) extends Expression 27 | case class BitwiseOrExp(exp1: Expression, exp2: Expression) extends Expression 28 | case class BitwiseXOrExp(exp1: Expression, exp2: Expression) extends Expression 29 | case class BitwiseAndExp(exp1: Expression, exp2: Expression) extends Expression 30 | case class EqualityExp(op: String, exp1: Expression, exp: Expression) extends Expression 31 | case class RelationalExp(op: String, exp1: Expression, exp2: Expression) extends Expression 32 | case class LShiftExp(exp1: Expression, exp2: Expression) extends Expression 33 | case class RShiftExp(exp1: Expression, exp2: Expression) extends Expression 34 | case class AddExp(exp1: Expression, exp2: Expression) extends Expression 35 | case class SubExp(exp1: Expression, exp2: Expression) extends Expression 36 | case class ConcatExp(exp1: Expression, exp2: Expression) extends Expression 37 | case class MulExp(exp1: Expression, exp2: Expression) extends Expression 38 | case class DivExp(exp1: Expression, exp2: Expression) extends Expression 39 | case class ModExp(exp1: Expression, exp2: Expression) extends Expression 40 | case class ExponentiationExp(exp1: Expression, exp2: Expression) extends Expression 41 | case class InstanceOfExp(exp: Expression, of: Either[Expression, QualifiedName]) extends Expression 42 | case class PrefixIncrementExp(va: Variable) extends Expression 43 | case class PrefixDecrementExp(va: Variable) extends Expression 44 | case class UnaryOpExp(op: String, exp: Expression) extends Expression 45 | case class ErrorControlExp(exp: Expression) extends Expression 46 | case class AnonymousFunctionCreationExp(isStatic: Boolean, header: FuncHeader, useClause: Seq[(Boolean, SimpleNameVar)], body: CompoundStmnt) extends Expression 47 | case class ShellCommandExp(sequence: Seq[StringElement]) extends Expression 48 | case class CastExp(cast: CastType.Value, exp: Expression) extends Expression 49 | 50 | case object CastType extends Enumeration { 51 | val ARRAY, BINARY, BOOL, BOOLEAN, DOUBLE, INT, INTEGER, FLOAT, OBJECT, REAL, STRING, UNSET = Value 52 | } 53 | 54 | sealed abstract class Intrinsic extends Expression 55 | case class EchoIntrinsic(exps: Seq[Expression]) extends Intrinsic 56 | case class UnsetIntrinsic(vars: Seq[Variable]) extends Intrinsic 57 | case class EmptyIntrinsic(exp: Expression) extends Intrinsic 58 | case class EvalIntrinsic(exp: Expression) extends Intrinsic 59 | case class ExitIntrinsic(exp: Option[Expression]) extends Intrinsic 60 | case class IssetIntrinsic(vars: Seq[Variable]) extends Intrinsic 61 | case class PrintIntrinsic(exp: Expression) extends Intrinsic 62 | case class ListIntrinsic(expList: Either[Seq[Expression], Seq[(Expression, Expression)]]) extends Intrinsic 63 | 64 | case class InstanceCreationExp(designator: Expression, arguments: Option[Seq[ArgumentExpression]]) extends Expression 65 | case class AnonymousClassCreationExp(decl: ClassDecl, arguments: Option[Seq[ArgumentExpression]]) extends Expression 66 | 67 | case class PostfixIncrementExp(va: Variable) extends Expression 68 | case class PostfixDecrementExp(va: Variable) extends Expression 69 | 70 | case class CloneExp(exp: Expression) extends Expression 71 | 72 | case class ClassConstAccessExp() 73 | 74 | sealed abstract class Variable extends Expression 75 | 76 | sealed abstract class SimpleVar extends Variable 77 | case class SimpleNameVar(name: Name) extends SimpleVar 78 | case class SimpleAccessVar(acc: SimpleVar) extends SimpleVar 79 | case class SimpleExpVar(exp: Expression) extends SimpleVar 80 | case class ScopeAccessVar(scope: ScopeType.Value) extends Variable 81 | case object ScopeType extends Enumeration { 82 | val SELF, PARENT, STATIC = Value 83 | } 84 | 85 | case class QualifiedNameVar(name: QualifiedName) extends Variable 86 | case class EnclosedExp(exp: Expression) extends Variable 87 | case class StringLiteralVar(literal: StringLiteral) extends Variable 88 | 89 | sealed abstract class MemberName 90 | 91 | case class NameMember(name: Name) extends MemberName 92 | case class SimpleVarMember(s: SimpleVar) extends MemberName 93 | case class ExpMember(exp: Expression) extends MemberName 94 | 95 | sealed abstract class Accessor(from: Variable) extends Variable 96 | 97 | sealed abstract class StaticAccessor(from: Variable) extends Accessor(from) 98 | case class MemberCallStaticAcc(from: Variable, member: MemberName, args: Seq[ArgumentExpression]) extends StaticAccessor(from) 99 | case class SimpleVarStaticAcc(from: Variable, s: SimpleVar) extends StaticAccessor(from) 100 | 101 | sealed abstract class PropertyAccessor(from: Variable) extends Accessor(from) 102 | case class MemberPropertyAcc(from: Variable, member: MemberName) extends PropertyAccessor(from: Variable) 103 | case class MemberCallPropertyAcc(from: Variable, member: MemberName, args: Seq[ArgumentExpression]) extends PropertyAccessor(from: Variable) 104 | 105 | case class CallAccessor(from: Variable, args: Seq[ArgumentExpression]) extends Accessor(from) 106 | case class ArrayAcc(from: Variable, exp: Option[Expression]) extends Accessor(from) 107 | case class BlockAcc(from: Variable, exp: Expression) extends Accessor(from) 108 | 109 | case class ArrayCreationVar(elems: Seq[ArrayElement]) extends Variable 110 | case class ArrayElement(key: Option[Expression], value: Expression, designateVar: Boolean) 111 | case class ArgumentExpression(isVariadic: Boolean, exp: Expression) 112 | 113 | case class ClassConstAcc(from: Variable, name: Name) extends Accessor(from) 114 | } 115 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/ast/Statements.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.ast 2 | 3 | import de.thm.mni.ii.phpparser.ast.Basic._ 4 | import de.thm.mni.ii.phpparser.ast.Expressions.{Expression, SimpleNameVar, SimpleVar} 5 | 6 | object Statements { 7 | 8 | sealed abstract class Statement 9 | 10 | case class EmptyStmnt() extends Statement 11 | case class EndTagStmnt(stmnt: Statement, override val text: Option[Text]) extends Statement with EndTagElement 12 | case class EchoTagStmnt(exps : Seq[Expression]) extends Statement 13 | 14 | case class CompoundStmnt(stmnts: Seq[Statement]) extends Statement 15 | case class NamedLabelStmnt(name: Name, stmt: Statement) extends Statement 16 | 17 | case class ExpressionStmnt(exp: Expression) extends Statement 18 | 19 | sealed abstract class SelectionStmnt extends Statement 20 | 21 | case class IfStmnt(exp: Expression, stmnts: Seq[Statement], elseifs: Seq[(Expression, Seq[Statement])], elseStmnts: Option[Seq[Statement]]) extends SelectionStmnt 22 | case class SwitchStmnt(exp: Expression, cases: Seq[SwitchBlock]) extends SelectionStmnt 23 | sealed abstract class SwitchBlock 24 | case class CaseBlock(exp: Expression, stmnts: Seq[Statement]) extends SwitchBlock 25 | case class DefaultBlock(stmnts: Seq[Statement]) extends SwitchBlock 26 | 27 | sealed abstract class IterationStmnt extends Statement 28 | 29 | case class WhileStmnt(cond: Expression, stmnts: Seq[Statement]) extends IterationStmnt 30 | case class DoStmnt(cond: Expression, stmnt: Statement) extends IterationStmnt 31 | case class ForExpressionList(exps: Seq[Expression], override val text: Option[Text]) extends EndTagElement 32 | case class ForStmnt(init: ForExpressionList, control: ForExpressionList, end: ForExpressionList, stmnts: Seq[Statement]) extends IterationStmnt 33 | case class ForeachStmnt(collection: Expression, key: Option[Expression], valueDesVar: Boolean, value: Expression, stmnts: Seq[Statement]) extends IterationStmnt 34 | 35 | sealed abstract class JumpStmnt extends Statement 36 | 37 | case class GotoStmnt(name: Name) extends JumpStmnt 38 | case class ContinueStmnt(lvl: Option[IntegerLiteral]) extends JumpStmnt 39 | case class BreakStmnt(lvl: Option[IntegerLiteral]) extends JumpStmnt 40 | case class ReturnStmnt(exp: Option[Expression]) extends JumpStmnt 41 | case class ThrowStmnt(exp: Expression) extends JumpStmnt 42 | 43 | case class TryStmnt(stmnt: CompoundStmnt, catches: Seq[CatchClause], fin: Option[CompoundStmnt]) extends Statement 44 | case class CatchClause(qualifiedName: QualifiedName, name: SimpleNameVar, stmnt: CompoundStmnt) 45 | 46 | case class DeclareStmnt(decl: DeclareDeclarative.Value, declLiteral: Literal, stmnts: Seq[Statement]) extends Statement 47 | case object DeclareDeclarative extends Enumeration { 48 | val TICKS, ENCODING, STRICT_TYPES = Value 49 | } 50 | 51 | sealed abstract class Declaration extends Statement 52 | 53 | case class ConstDecl(elems: Seq[ConstElement]) extends Declaration 54 | case class ConstElement(name: Name, exp: Expression) 55 | case class ClassDecl(mod: Option[ClassModifier], name: Option[Name], extend: Option[QualifiedName], impl: Option[Seq[QualifiedName]], body: Seq[MemberDecl]) extends Declaration 56 | 57 | trait MemberDecl 58 | 59 | case class EndTagMemberDecl(memberDecl: MemberDecl, override val text: Option[Text]) extends MemberDecl with EndTagElement 60 | case class EmptyMemberDecl() extends MemberDecl 61 | case class ClassConstDecl(mod: Option[VisibilityModifier], elems: Seq[ConstElement]) extends MemberDecl 62 | case class PropertyDecl(mod: PropertyModifier, elems: Seq[PropertyElement]) extends MemberDecl 63 | case class PropertyElement(name: SimpleNameVar, initValue: Option[Expression]) 64 | case class MethodDecl(mods: Seq[MethodModifier], header: FuncHeader, body: Option[CompoundStmnt]) extends MemberDecl 65 | case class TraitUseClause(traits: Seq[QualifiedName], useSpec: Seq[TraitUseSpec]) extends MemberDecl 66 | 67 | sealed abstract class TraitUseSpec 68 | case class SelectInsteadofClause(from: Name, to: Name) extends TraitUseSpec 69 | case class TraitAliasClause(from: Name, asMod: Option[VisibilityModifier], as: Option[Name]) extends TraitUseSpec 70 | 71 | sealed abstract class Modifier 72 | 73 | trait ClassModifier extends MethodModifier 74 | trait VisibilityModifier extends MethodModifier 75 | trait PropertyModifier 76 | trait StaticModifier extends MethodModifier 77 | trait MethodModifier 78 | 79 | case object NoMod extends Modifier with PropertyModifier 80 | case class CombinedMod(staticMod: Option[StaticModifier], visMod: Option[VisibilityModifier]) extends Modifier with PropertyModifier 81 | case object StaticMod extends Modifier with StaticModifier 82 | case object AbstractMod extends Modifier with ClassModifier 83 | case object FinalMod extends Modifier with ClassModifier 84 | case object PublicMod extends Modifier with VisibilityModifier 85 | case object ProtectedMod extends Modifier with VisibilityModifier 86 | case object PrivateMod extends Modifier with VisibilityModifier 87 | 88 | case class InterfaceDecl(name: Name, extend: Option[Seq[QualifiedName]], body: Seq[MemberDecl]) extends Declaration 89 | 90 | case class TraitDecl(name: Name, body: Seq[MemberDecl]) extends Declaration 91 | 92 | case class NamespaceUseDecl(useType: Option[NamespaceUseType.Value], namespaceName: Option[Seq[Name]], clauses: Seq[NamespaceUseClause]) extends Declaration 93 | case class NamespaceUseClause(useType: Option[NamespaceUseType.Value], from: Either[QualifiedName, Seq[Name]], as: Option[Name]) 94 | case object NamespaceUseType extends Enumeration { 95 | val FUNCTION, CONST = Value 96 | } 97 | 98 | case class GlobalDecl(vars: Seq[SimpleVar]) extends Declaration 99 | 100 | case class FuncStaticDecl(vars: Seq[StaticVarElement]) extends Declaration 101 | case class StaticVarElement(name: SimpleNameVar, initValue: Option[Expression]) 102 | 103 | sealed abstract class Definition extends Statement 104 | 105 | case class FuncDef(header: FuncHeader, body: CompoundStmnt) extends Definition 106 | case class FuncHeader(returnRef: Boolean, name: Option[Name], params: Seq[ParameterDecl], ret: Option[PossibleTypes]) 107 | sealed abstract class ParameterDecl 108 | case class SimpleParam(t: Option[TypeDecl], desVar: Boolean, name: SimpleNameVar, defValue: Option[Expression]) extends ParameterDecl 109 | case class VariadicParam(t: Option[TypeDecl], desVar: Boolean, name: SimpleNameVar) extends ParameterDecl 110 | sealed abstract class PossibleTypes 111 | case object VoidType extends PossibleTypes 112 | sealed abstract class TypeDecl extends PossibleTypes 113 | case object ArrayType extends TypeDecl 114 | case object CallableType extends TypeDecl 115 | case object IterableType extends TypeDecl 116 | sealed abstract class ScalarTypeDecl extends TypeDecl 117 | case object BoolType extends ScalarTypeDecl 118 | case object FloatType extends ScalarTypeDecl 119 | case object IntType extends ScalarTypeDecl 120 | case object StringType extends ScalarTypeDecl 121 | case class QualifiedType(name: QualifiedName) extends TypeDecl() 122 | 123 | case class NamespaceDef(name: Option[QualifiedName], stmnt: Option[CompoundStmnt]) extends Definition 124 | } 125 | -------------------------------------------------------------------------------- /src/main/scala/de/thm/mni/ii/phpparser/parser/statements/DeclarationParser.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser.parser.statements 2 | 3 | import fastparse.noApi._ 4 | import de.thm.mni.ii.phpparser.parser.literals.WsAPI._ 5 | import de.thm.mni.ii.phpparser.parser.literals.Lexical.Ws 6 | 7 | import de.thm.mni.ii.phpparser.ast.{Basic => BAst, Statements => SAst} 8 | import de.thm.mni.ii.phpparser.parser.literals.KeywordConversions._ 9 | import de.thm.mni.ii.phpparser.parser.literals.Keywords._ 10 | import de.thm.mni.ii.phpparser.parser.literals.Literals.{Name, VariableName} 11 | 12 | import de.thm.mni.ii.phpparser.parser.Basic.{NamespaceName, QualifiedName, SemicolonFactory} 13 | import de.thm.mni.ii.phpparser.parser.expressions.ExpressionParser.Expression 14 | import de.thm.mni.ii.phpparser.parser.expressions.VariableParser.SimpleVariable 15 | import de.thm.mni.ii.phpparser.parser.statements.StatementParser.{CompoundStmnt, FuncHeader, wrap} 16 | 17 | 18 | /** 19 | * This object contains all declaration statements 20 | */ 21 | object DeclarationParser { 22 | 23 | // declaration statements 24 | 25 | private val ConstElem = P(Name ~ "=" ~ Expression).map { 26 | case (name, exp) => SAst.ConstElement(name, exp) 27 | } 28 | 29 | val ConstDeclStmnt = P(CONST ~~ &(Ws) ~/ ConstElem.rep ~ SemicolonFactory).map { 30 | case (constElems, text) => wrap(SAst.ConstDecl(constElems), text) 31 | } 32 | 33 | val GlobalDeclStmnt = P(GLOBAL ~~ &(Ws) ~/ SimpleVariable.rep(sep = ",".~/) ~ SemicolonFactory).map { 34 | case (simpleVar, text) => wrap(SAst.GlobalDecl(simpleVar), text) 35 | } 36 | 37 | 38 | val FunctionStaticDeclStmnt = { 39 | val StaticVarElement = P(VariableName ~ ("=" ~/ Expression).?).map { 40 | case (varName, exp) => SAst.StaticVarElement(varName, exp) 41 | } 42 | 43 | P(STATIC ~~ &(Ws) ~/ StaticVarElement.rep(sep = ",".~/) ~ SemicolonFactory).map { 44 | case (varElements, text) => wrap(SAst.FuncStaticDecl(varElements), text) 45 | } 46 | } 47 | 48 | val NamespaceUseDeclStmnt = { 49 | val NamespaceUseType: P[SAst.NamespaceUseType.Value] = P(FunctionUseType | ConstUseType) 50 | 51 | val NameSpaceUseClause1 = P(NamespaceName ~ (AS ~ Name).?).map { 52 | case (namespaceName, asName) => SAst.NamespaceUseClause(None, Right(namespaceName), asName) 53 | } 54 | val NameSpaceUseClause2 = P(QualifiedName ~ (AS ~ Name).?).map { 55 | case (qName, asName) => SAst.NamespaceUseClause(None, Left(qName), asName) 56 | } 57 | val NameSpaceUseClause3 = P(NamespaceUseType.? ~ NamespaceName ~ (AS ~ Name).?).map { 58 | case (useType, name, asName) => SAst.NamespaceUseClause(useType, Right(name), asName) 59 | } 60 | 61 | P(USE ~~ &(Ws) ~/ ( 62 | (NamespaceUseType ~~ &(Ws) ~ "\\".? ~ NamespaceName ~ "\\" ~ "{" ~ NameSpaceUseClause1.rep(min = 1, sep = ",") ~ "}").map(t => SAst.NamespaceUseDecl(Some(t._1), Some(t._2), t._3)) 63 | | ((NamespaceUseType ~~ &(Ws)).? ~ NameSpaceUseClause2.rep(min = 1, sep = ",") ~ SemicolonFactory).map(t => wrap(SAst.NamespaceUseDecl(t._1, None, t._2), t._3)) 64 | | ("\\".? ~ NamespaceName ~ "\\" ~ "{" ~ NameSpaceUseClause3.rep(min = 1, sep = ",") ~ "}").map(t => SAst.NamespaceUseDecl(None, Some(t._1), t._2))) 65 | ) 66 | } 67 | 68 | 69 | // available modifiers 70 | 71 | private val VisibilityMod: P[SAst.VisibilityModifier] = P(PublicMod | PrivateMod | ProtectedMod) 72 | private val ClassMod: P[SAst.ClassModifier] = P(AbstractMod | FinalMod) 73 | private val MethodMod: P[SAst.MethodModifier] = P(VisibilityMod | StaticMod | ClassMod) 74 | private val PropertyMod: P[SAst.PropertyModifier] = P(NoMod 75 | | (VisibilityMod ~ StaticMod.?).map(t => SAst.CombinedMod(t._2, Some(t._1))) 76 | | (StaticMod ~ VisibilityMod.?).map(t => SAst.CombinedMod(Some(t._1), t._2))) 77 | 78 | // member declarations 79 | 80 | private def wrapMemberDecl(mDecl: SAst.MemberDecl, text: Option[BAst.Text]): SAst.MemberDecl = text match { 81 | case Some(_) => SAst.EndTagMemberDecl(mDecl, text) 82 | case None => mDecl 83 | } 84 | 85 | val EmptyMemberDecl = P(SemicolonFactory).map(wrapMemberDecl(SAst.EmptyMemberDecl(),_)) 86 | 87 | val ClassConstDecl = P((VisibilityMod ~~ &(Ws)).? ~ CONST ~~ &(Ws) ~/ ConstElem.rep ~ SemicolonFactory).map { 88 | case (mod, constElems, text) => wrapMemberDecl(SAst.ClassConstDecl(mod, constElems), text) 89 | } 90 | 91 | val PropertyDecl = { 92 | val propertyElem: P[SAst.PropertyElement] = P(VariableName ~ ("=" ~ Expression).?).map{ 93 | case (varName, exp) => SAst.PropertyElement(varName, exp) 94 | } 95 | 96 | P(PropertyMod ~~ &(Ws) ~ propertyElem.rep(sep=",") ~ SemicolonFactory).map { 97 | case (mod, pElems, text) => wrapMemberDecl(SAst.PropertyDecl(mod, pElems), text) 98 | } 99 | } 100 | 101 | val MethodDecl = { 102 | val BodyOrEnd: P[(Option[SAst.CompoundStmnt], Option[BAst.Text])] = 103 | P(SemicolonFactory.map((None, _)) | CompoundStmnt.map(stmnt => (Some(stmnt), None))) 104 | 105 | P((MethodMod ~~ &(Ws)).rep ~ FuncHeader ~ BodyOrEnd).map { 106 | case (mod, header, (body, text)) => wrapMemberDecl(SAst.MethodDecl(mod, header, body), text) 107 | } 108 | } 109 | 110 | val TraitUseClause = { 111 | val TraitUseSpec: P[SAst.TraitUseSpec] = 112 | P((Name ~~ &(Ws) ~ INSTEADOF ~~ &(Ws) ~ Name).map(t => SAst.SelectInsteadofClause(t._1, t._2)) 113 | | (Name ~~ &(Ws) ~ AS ~~ &(Ws) ~ (VisibilityMod ~~ &(Ws)).? ~ Name).map(t => SAst.TraitAliasClause(t._1, t._2, Some(t._3))) 114 | | (Name ~~ &(Ws) ~ AS ~~ &(Ws) ~ VisibilityMod ~~ &(Ws) ~ Name.?).map(t => SAst.TraitAliasClause(t._1, Some(t._2), t._3))) 115 | 116 | val TraitUseSpecs: P[(Seq[SAst.TraitUseSpec], Option[BAst.Text])] = P(SemicolonFactory.map((Seq(), _)) | 117 | ("{" ~ TraitUseSpec.rep(sep = ",") ~ "}").map((_, None))) 118 | 119 | P(USE ~~ &(Ws) ~/ QualifiedName.rep(sep = ",") ~/ TraitUseSpecs).map { 120 | case (qName, (specs, text)) => wrapMemberDecl(SAst.TraitUseClause(qName, specs), text) 121 | } 122 | } 123 | 124 | 125 | // class declarations 126 | 127 | private[parser] val ClassDeclBody: P[(Option[BAst.QualifiedName], Option[Seq[BAst.QualifiedName]], Seq[SAst.MemberDecl])] = { 128 | val ClassMemberDecl: P[SAst.MemberDecl] = P(EmptyMemberDecl | ClassConstDecl | PropertyDecl | MethodDecl | TraitUseClause) 129 | 130 | P((&(Ws) ~ EXTENDS ~~ &(Ws) ~ QualifiedName).? 131 | ~~ (&(Ws) ~ IMPLEMENTS ~~ &(Ws) ~ QualifiedName.rep(sep = ",".~/)).? 132 | ~ "{" ~/ ClassMemberDecl.rep ~ "}") 133 | } 134 | 135 | val ClassDeclStmnt = P(ClassMod.? ~ CLASS ~~ &(Ws) ~/ Name ~~ ClassDeclBody).map { 136 | case (mod, name, (extendsName, interfaces, members)) => SAst.ClassDecl(mod, Some(name), extendsName, interfaces, members) 137 | } 138 | 139 | 140 | // interface declarations 141 | 142 | val InterfaceDeclStmnt = { 143 | val InterfaceMemberDecl: P[SAst.MemberDecl] = P(EmptyMemberDecl | ClassConstDecl | MethodDecl) 144 | 145 | P(INTERFACE ~~ &(Ws) ~/ Name ~~ (&(Ws) 146 | ~ EXTENDS ~~ &(Ws) ~ QualifiedName.rep(sep = ",".~/)).? 147 | ~ "{" ~/ InterfaceMemberDecl.rep ~ "}" 148 | ).map { 149 | case (name, extendsNames, members) => SAst.InterfaceDecl(name, extendsNames, members) 150 | } 151 | } 152 | 153 | 154 | // trait declarations 155 | 156 | val TraitDeclStmnt = { 157 | val TraitMemberDecl: P[SAst.MemberDecl] = P(EmptyMemberDecl | PropertyDecl | MethodDecl | TraitUseClause) 158 | 159 | P(TRAIT ~~ &(Ws) ~/ Name 160 | ~ "{" ~/ TraitMemberDecl.rep ~ "}" 161 | ).map{ 162 | case (name, members) => SAst.TraitDecl(name, members) 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/test/scala/de/thm/mni/ii/phpparser/ClassTest.scala: -------------------------------------------------------------------------------- 1 | package de.thm.mni.ii.phpparser 2 | 3 | import org.scalatest.{FlatSpec, Matchers} 4 | import de.thm.mni.ii.phpparser.ast.Basic._ 5 | import de.thm.mni.ii.phpparser.ast.Expressions._ 6 | import de.thm.mni.ii.phpparser.ast.Statements._ 7 | 8 | import scala.collection.mutable.ArrayBuffer 9 | 10 | /** 11 | * Test PHPParsers ability to parse php classes and interfaces. 12 | * @author Andrej Sajenko 13 | */ 14 | class ClassTest extends FlatSpec with Matchers { 15 | 16 | "PHPParser" must "parse empty class" in { 17 | PHPParser.parse(" } 59 | } 60 | 61 | it must "parse class with methods" in { 62 | PHPParser.parse( 63 | """ 64 | | } 84 | } 85 | 86 | it must "parse class methods with parameters" in { 87 | PHPParser.parse( 88 | """ 89 | | } 105 | } 106 | 107 | it must "parse class methods with types" in { 108 | PHPParser.parse( 109 | """ 110 | | } 122 | } 123 | 124 | it must "parse interface with methods" in { 125 | PHPParser.parse( 126 | """ 127 | | } 141 | } 142 | 143 | it must "parse abstract class with methods" in { 144 | PHPParser.parse( 145 | """ 146 | |bar1(); 153 | | } 154 | |} 155 | """.stripMargin) should matchPattern { case 156 | PHPParser.Success(Script(_,ArrayBuffer( 157 | ClassDecl(Some(AbstractMod),Some(Name("Foo")),None,None,ArrayBuffer( 158 | MethodDecl(ArrayBuffer(AbstractMod, ProtectedMod),FuncHeader(false,Some(Name("bar1")),ArrayBuffer(),None),None), 159 | MethodDecl(ArrayBuffer(AbstractMod, ProtectedMod),FuncHeader(false,Some(Name("bar2")),ArrayBuffer( 160 | SimpleParam(None,false,SimpleNameVar(Name(prefix)),None)),None),None), 161 | MethodDecl(ArrayBuffer(PublicMod),FuncHeader(false,Some(Name("funP")),ArrayBuffer(),None),Some( 162 | CompoundStmnt(ArrayBuffer(ExpressionStmnt(PrintIntrinsic(MemberCallPropertyAcc(SimpleNameVar(Name("this")), 163 | NameMember(Name("bar1")),ArrayBuffer()))))))))) 164 | ))) 165 | => } 166 | } 167 | 168 | 169 | } 170 | --------------------------------------------------------------------------------