|
@@ -10,15 +10,36 @@ import scala.meta._
|
|
|
class Parser extends AbstractParser {
|
|
class Parser extends AbstractParser {
|
|
|
private var currentFile: String = _
|
|
private var currentFile: String = _
|
|
|
|
|
|
|
|
- var tokens: TokenList = _
|
|
|
|
|
|
|
+ private var tokens: TokenList = _
|
|
|
|
|
|
|
|
private val traverser: Traverser = new Traverser {
|
|
private val traverser: Traverser = new Traverser {
|
|
|
|
|
|
|
|
|
|
+ private val Operators : Array[String] = Array(
|
|
|
|
|
+ "+", "-", "*", "/", "%", "**",
|
|
|
|
|
+ "==", "!=", ">", "<", ">=", "<=",
|
|
|
|
|
+ "&&", "||", "!",
|
|
|
|
|
+ "=", "+=", "-=", "*=", "/=", "%=", "**=", "<<=", ">>=", ">>>=", "&=", "^=", "|=",
|
|
|
|
|
+ "&", "|", "^", "<<", ">>", "~", ">>>",
|
|
|
|
|
+ "++", "::", ":::",
|
|
|
|
|
+ "<:", ">:", "#"
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * TraverserRecord; Identifier kept short for readability
|
|
|
|
|
+ * @param before opt. token type to insert at the beginning of the tree
|
|
|
|
|
+ * @param after opt. token type to insert at the end of the tree
|
|
|
|
|
+ * @param traverse custom method of traversing the tree
|
|
|
|
|
+ */
|
|
|
case class TR(before: Option[ScalaTokenConstants.Value] = None,
|
|
case class TR(before: Option[ScalaTokenConstants.Value] = None,
|
|
|
after: Option[ScalaTokenConstants.Value] = None,
|
|
after: Option[ScalaTokenConstants.Value] = None,
|
|
|
traverse: Tree => Unit = _.children.foreach(traverser.apply)
|
|
traverse: Tree => Unit = _.children.foreach(traverser.apply)
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Adds a token at the beginning of the tree and traverses it if a tree is given.
|
|
|
|
|
+ * @param tree opt. tree
|
|
|
|
|
+ * @param token token type to insert at the beginning of the tree
|
|
|
|
|
+ */
|
|
|
private def maybeAddAndApply(tree: Option[Tree], token: ScalaTokenConstants.Value): Unit = tree match {
|
|
private def maybeAddAndApply(tree: Option[Tree], token: ScalaTokenConstants.Value): Unit = tree match {
|
|
|
case Some(exp) =>
|
|
case Some(exp) =>
|
|
|
add(token, exp, fromEnd = false)
|
|
add(token, exp, fromEnd = false)
|
|
@@ -29,31 +50,35 @@ class Parser extends AbstractParser {
|
|
|
private def processCases(cases: List[Case]): Unit = cases.foreach {
|
|
private def processCases(cases: List[Case]): Unit = cases.foreach {
|
|
|
case c@Case(pattern, condition, body) =>
|
|
case c@Case(pattern, condition, body) =>
|
|
|
add(ScalaTokenConstants.CaseStatement, c, fromEnd = false)
|
|
add(ScalaTokenConstants.CaseStatement, c, fromEnd = false)
|
|
|
- apply(pattern)
|
|
|
|
|
- apply(condition)
|
|
|
|
|
- enclose(body, TR(Some(CaseBegin), Some(CaseEnd)))
|
|
|
|
|
|
|
+ applyRecursively(Seq(pattern, condition))
|
|
|
|
|
+
|
|
|
|
|
+ encloseAndApply(body, TR(Some(CaseBegin), Some(CaseEnd)))
|
|
|
case _ =>
|
|
case _ =>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- def isNotArithmetic(op: Term.Name): Boolean = {
|
|
|
|
|
- op.value.matches("""[_\w]+""")
|
|
|
|
|
|
|
+ private def isStandardOperator(op: String): Boolean = {
|
|
|
|
|
+ Operators.contains(op)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private def getMethodIdentifier(fun: Term): String = {
|
|
|
|
|
+ fun.toString().split("\\.").last
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private def doMatch(tree: Tree): TR = {
|
|
private def doMatch(tree: Tree): TR = {
|
|
|
tree match {
|
|
tree match {
|
|
|
case Term.Do(body, condition) => TR(Some(DoWhile), Some(DoWhileEnd), traverse = _ => {
|
|
case Term.Do(body, condition) => TR(Some(DoWhile), Some(DoWhileEnd), traverse = _ => {
|
|
|
- enclose(body, TR(Some(DoBodyBegin), Some(DoBodyEnd)))
|
|
|
|
|
|
|
+ encloseAndApply(body, TR(Some(DoBodyBegin), Some(DoBodyEnd)))
|
|
|
apply(condition)
|
|
apply(condition)
|
|
|
})
|
|
})
|
|
|
- case Term.Assign(_) => TR(Some(Assign), None)
|
|
|
|
|
|
|
+ case Term.Assign(_) => TR(Some(Assign))
|
|
|
case Term.While(condition, body) => TR(Some(While), traverse = _ => {
|
|
case Term.While(condition, body) => TR(Some(While), traverse = _ => {
|
|
|
apply(condition)
|
|
apply(condition)
|
|
|
- enclose(body, TR(Some(WhileBodyBegin), Some(WhileBodyEnd)))
|
|
|
|
|
|
|
+ encloseAndApply(body, TR(Some(WhileBodyBegin), Some(WhileBodyEnd)))
|
|
|
})
|
|
})
|
|
|
case Term.For(enumerators, body) =>
|
|
case Term.For(enumerators, body) =>
|
|
|
TR(Some(For), traverse = _ => {
|
|
TR(Some(For), traverse = _ => {
|
|
|
apply(enumerators)
|
|
apply(enumerators)
|
|
|
- enclose(body, TR(Some(ForBodyBegin), Some(ForBodyEnd)))
|
|
|
|
|
|
|
+ encloseAndApply(body, TR(Some(ForBodyBegin), Some(ForBodyEnd)))
|
|
|
})
|
|
})
|
|
|
case Term.Try(expr, catchExpr, finallyExpr) => TR(Some(TryBegin),
|
|
case Term.Try(expr, catchExpr, finallyExpr) => TR(Some(TryBegin),
|
|
|
traverse = _ => {
|
|
traverse = _ => {
|
|
@@ -73,14 +98,15 @@ class Parser extends AbstractParser {
|
|
|
case Term.TryWithHandler(expr, catchp, finallyp) => TR(Some(TryBegin),
|
|
case Term.TryWithHandler(expr, catchp, finallyp) => TR(Some(TryBegin),
|
|
|
traverse = _ => {
|
|
traverse = _ => {
|
|
|
apply(expr)
|
|
apply(expr)
|
|
|
- enclose(catchp, TR(Some(CatchBegin), Some(CatchEnd)))
|
|
|
|
|
|
|
+ encloseAndApply(catchp, TR(Some(CatchBegin), Some(CatchEnd)))
|
|
|
|
|
|
|
|
maybeAddAndApply(finallyp, ScalaTokenConstants.Finally)
|
|
maybeAddAndApply(finallyp, ScalaTokenConstants.Finally)
|
|
|
})
|
|
})
|
|
|
- case Term.Apply(fun, args) =>
|
|
|
|
|
|
|
+ case Term.Apply(fun, args) if !isStandardOperator(getMethodIdentifier(fun)) =>
|
|
|
// `f()` can also be written as `f`, so we simply don't see `f()` as a function call
|
|
// `f()` can also be written as `f`, so we simply don't see `f()` as a function call
|
|
|
// But Java style function calls with no args should still be recognized
|
|
// But Java style function calls with no args should still be recognized
|
|
|
TR(traverse = _ => {
|
|
TR(traverse = _ => {
|
|
|
|
|
+
|
|
|
add(Apply, fun, fromEnd = false)
|
|
add(Apply, fun, fromEnd = false)
|
|
|
apply(fun)
|
|
apply(fun)
|
|
|
for (arg <- args) {
|
|
for (arg <- args) {
|
|
@@ -107,13 +133,13 @@ class Parser extends AbstractParser {
|
|
|
case Term.ForYield(enumerators, body) => TR(traverse = _ => {
|
|
case Term.ForYield(enumerators, body) => TR(traverse = _ => {
|
|
|
apply(enumerators)
|
|
apply(enumerators)
|
|
|
add(ForBodyBegin, body, fromEnd = false)
|
|
add(ForBodyBegin, body, fromEnd = false)
|
|
|
- enclose(body, TR(Some(Yield), Some(ForBodyEnd)))
|
|
|
|
|
|
|
+ encloseAndApply(body, TR(Some(Yield), Some(ForBodyEnd)))
|
|
|
})
|
|
})
|
|
|
case Term.If(conditionExpr, thenExpression, elseExpression) => TR(traverse = _ => {
|
|
case Term.If(conditionExpr, thenExpression, elseExpression) => TR(traverse = _ => {
|
|
|
add(If, tree, fromEnd = false)
|
|
add(If, tree, fromEnd = false)
|
|
|
apply(conditionExpr)
|
|
apply(conditionExpr)
|
|
|
|
|
|
|
|
- enclose(thenExpression, TR(Some(IfBegin), Some(IfEnd)))
|
|
|
|
|
|
|
+ encloseAndApply(thenExpression, TR(Some(IfBegin), Some(IfEnd)))
|
|
|
|
|
|
|
|
elseExpression match {
|
|
elseExpression match {
|
|
|
case Lit.Unit() => apply(elseExpression)
|
|
case Lit.Unit() => apply(elseExpression)
|
|
@@ -121,7 +147,7 @@ class Parser extends AbstractParser {
|
|
|
val elseStart = tree.pos.text.indexOf("else", thenExpression.pos.end - tree.pos.start)
|
|
val elseStart = tree.pos.text.indexOf("else", thenExpression.pos.end - tree.pos.start)
|
|
|
val elsePosition = Position.Range(tree.pos.input, tree.pos.start + elseStart, tree.pos.start + elseStart + 4)
|
|
val elsePosition = Position.Range(tree.pos.input, tree.pos.start + elseStart, tree.pos.start + elseStart + 4)
|
|
|
add(Else, elsePosition.startLine + 1, elsePosition.startColumn + 1, elsePosition.text.length)
|
|
add(Else, elsePosition.startLine + 1, elsePosition.startColumn + 1, elsePosition.text.length)
|
|
|
- enclose(elseExpression, TR(Some(ElseBegin), Some(ElseEnd)))
|
|
|
|
|
|
|
+ encloseAndApply(elseExpression, TR(Some(ElseBegin), Some(ElseEnd)))
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -132,17 +158,14 @@ class Parser extends AbstractParser {
|
|
|
TR(traverse = _ => {
|
|
TR(traverse = _ => {
|
|
|
applyRecursively(mods)
|
|
applyRecursively(mods)
|
|
|
add(MethodDef, name, fromEnd = false)
|
|
add(MethodDef, name, fromEnd = false)
|
|
|
- for (tParam <- typeParams) add(TypeParameter, tParam, fromEnd = false)
|
|
|
|
|
- for (params <- paramss)
|
|
|
|
|
- for (param <- params) add(Parameter, param, fromEnd = false)
|
|
|
|
|
|
|
+ assignRecursively(typeParams, TypeParameter)
|
|
|
|
|
+ assignRecursively(paramss, Parameter)
|
|
|
|
|
|
|
|
- enclose(body, TR(Some(MethodBegin), Some(MethodEnd)))
|
|
|
|
|
|
|
+ encloseAndApply(body, TR(Some(MethodBegin), Some(MethodEnd)))
|
|
|
})
|
|
})
|
|
|
case Defn.Macro(mods, name, tparams, paramss, declType, body) => TR(Some(Macro), traverse = _ => {
|
|
case Defn.Macro(mods, name, tparams, paramss, declType, body) => TR(Some(Macro), traverse = _ => {
|
|
|
- applyRecursively(mods)
|
|
|
|
|
- applyRecursively(tparams)
|
|
|
|
|
- applyRecursively(paramss)
|
|
|
|
|
- enclose(body, TR(Some(MacroBegin), Some(MacroEnd)))
|
|
|
|
|
|
|
+ applyRecursively(Seq(mods, tparams, paramss))
|
|
|
|
|
+ encloseAndApply(body, TR(Some(MacroBegin), Some(MacroEnd)))
|
|
|
})
|
|
})
|
|
|
case Defn.Class(_) =>
|
|
case Defn.Class(_) =>
|
|
|
TR(Some(ClassBegin), Some(ClassEnd))
|
|
TR(Some(ClassBegin), Some(ClassEnd))
|
|
@@ -184,45 +207,39 @@ class Parser extends AbstractParser {
|
|
|
|
|
|
|
|
case Ctor.Secondary(_) =>
|
|
case Ctor.Secondary(_) =>
|
|
|
TR(Some(ConstructorBegin), Some(ConstructorEnd))
|
|
TR(Some(ConstructorBegin), Some(ConstructorEnd))
|
|
|
|
|
+
|
|
|
case Init(tpe, name, argss) if argss.nonEmpty => TR(traverse = _ => {
|
|
case Init(tpe, name, argss) if argss.nonEmpty => TR(traverse = _ => {
|
|
|
- for (args <- argss) {
|
|
|
|
|
- for (arg <- args) {
|
|
|
|
|
- add(Argument, arg, fromEnd = false)
|
|
|
|
|
- apply(arg)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ assignRecursively(argss, Argument, doApply = true)
|
|
|
})
|
|
})
|
|
|
case Enumerator.Guard(_) => TR(Some(Guard))
|
|
case Enumerator.Guard(_) => TR(Some(Guard))
|
|
|
|
|
|
|
|
case Term.Param(_) => TR(traverse = _ => add(Parameter, tree, fromEnd = false))
|
|
case Term.Param(_) => TR(traverse = _ => add(Parameter, tree, fromEnd = false))
|
|
|
case Term.NewAnonymous(_) => TR(Some(ClassBegin), Some(ClassEnd))
|
|
case Term.NewAnonymous(_) => TR(Some(ClassBegin), Some(ClassEnd))
|
|
|
case Term.ApplyInfix(_, op, _, _) if op.value.contains("=") && !Array("==", "!=").contains(op.value) => TR(Some(Assign))
|
|
case Term.ApplyInfix(_, op, _, _) if op.value.contains("=") && !Array("==", "!=").contains(op.value) => TR(Some(Assign))
|
|
|
- case Term.ApplyInfix(fun, op, typeArgs, args) if isNotArithmetic(op) => TR(traverse = _ => {
|
|
|
|
|
|
|
+ case Term.ApplyInfix(fun, op, typeArgs, args) if !isStandardOperator(op.value) => TR(traverse = _ => {
|
|
|
add(Apply, tree, fromEnd = false)
|
|
add(Apply, tree, fromEnd = false)
|
|
|
apply(fun)
|
|
apply(fun)
|
|
|
- for (typeArg <- typeArgs) {
|
|
|
|
|
- add(TypeArgument, typeArg, fromEnd = false)
|
|
|
|
|
- apply(typeArg)
|
|
|
|
|
- }
|
|
|
|
|
- for (arg <- args) {
|
|
|
|
|
- add(Argument, arg, fromEnd = false)
|
|
|
|
|
- apply(arg)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ assignRecursively(typeArgs, TypeArgument, doApply = true)
|
|
|
|
|
+ assignRecursively(args, Argument, doApply = true)
|
|
|
})
|
|
})
|
|
|
case Term.Select(refObj, member) =>
|
|
case Term.Select(refObj, member) =>
|
|
|
TR(traverse = _ => {
|
|
TR(traverse = _ => {
|
|
|
apply(refObj)
|
|
apply(refObj)
|
|
|
- add(ScalaTokenConstants.Member, member, fromEnd = false)
|
|
|
|
|
|
|
+ if (!isStandardOperator(member.value)) add(ScalaTokenConstants.Member, member, fromEnd = false)
|
|
|
apply(member)
|
|
apply(member)
|
|
|
})
|
|
})
|
|
|
case Term.ApplyType(_, typeArgs) => TR(traverse = _ => {
|
|
case Term.ApplyType(_, typeArgs) => TR(traverse = _ => {
|
|
|
add(Apply, tree, fromEnd = false)
|
|
add(Apply, tree, fromEnd = false)
|
|
|
- for (typeArg <- typeArgs) add(TypeArgument, typeArg, fromEnd = false)
|
|
|
|
|
|
|
+ assignRecursively(typeArgs, TypeArgument)
|
|
|
})
|
|
})
|
|
|
case Term.New(_) => TR(Some(NewObject))
|
|
case Term.New(_) => TR(Some(NewObject))
|
|
|
case Self(_) => TR(Some(SelfType))
|
|
case Self(_) => TR(Some(SelfType))
|
|
|
- case block@Term.Block(_) if block.parent.nonEmpty && block.parent.get.isInstanceOf[Term.Apply] =>
|
|
|
|
|
- TR(Some(BlockStart), Some(BlockEnd))
|
|
|
|
|
|
|
+ case block@Term.Block(_) => block.parent match {
|
|
|
|
|
+ // inner block
|
|
|
|
|
+ case Some(Term.Apply(_)) => TR(Some(BlockStart), Some(BlockEnd))
|
|
|
|
|
+ // block in an expression context, e.g. for, if, while
|
|
|
|
|
+ case _ => TR()
|
|
|
|
|
+ }
|
|
|
case Enumerator.Generator(_) => TR(Some(EnumGenerator))
|
|
case Enumerator.Generator(_) => TR(Some(EnumGenerator))
|
|
|
case meta.Type.Param(_) => TR(Some(TypeParameter))
|
|
case meta.Type.Param(_) => TR(Some(TypeParameter))
|
|
|
|
|
|
|
@@ -230,10 +247,16 @@ class Parser extends AbstractParser {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- def applyRecursively[T](els: List[T]): Unit = els.foreach({
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Traverses a sequence of elements, which may in turn be lists as well.
|
|
|
|
|
+ * @param els a sequence of values, Trees or Lists
|
|
|
|
|
+ * @tparam T the type of elements
|
|
|
|
|
+ */
|
|
|
|
|
+ def applyRecursively[T](els: Seq[T]): Unit = els.foreach {
|
|
|
case tree: Tree => apply(tree)
|
|
case tree: Tree => apply(tree)
|
|
|
case treeList: List[_] => applyRecursively(treeList)
|
|
case treeList: List[_] => applyRecursively(treeList)
|
|
|
- })
|
|
|
|
|
|
|
+ case _ =>
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
override def apply(tree: Tree): Unit = {
|
|
override def apply(tree: Tree): Unit = {
|
|
|
val record: TR = doMatch(tree)
|
|
val record: TR = doMatch(tree)
|
|
@@ -251,7 +274,28 @@ class Parser extends AbstractParser {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- def enclose(tree: Tree, record: TR): Unit = {
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Assigns the same token type to each element of a list of e.g. parameters.
|
|
|
|
|
+ * @param els list of equal elements
|
|
|
|
|
+ * @param tokenType type of token to assign to each of the elements
|
|
|
|
|
+ * @param doApply if true, the substructure of each element is traversed
|
|
|
|
|
+ * @tparam T list may contain more lists or leaves
|
|
|
|
|
+ */
|
|
|
|
|
+ def assignRecursively[T](els: List[T], tokenType: ScalaTokenConstants.Value, doApply: Boolean = false): Unit = els.foreach {
|
|
|
|
|
+ case treeList: List[_] => assignRecursively(treeList, tokenType)
|
|
|
|
|
+ case el: Tree =>
|
|
|
|
|
+ add(tokenType, el, fromEnd = false)
|
|
|
|
|
+ if (doApply) apply(el)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Adds a token to the beginning of the tree, traverses the tree, adds a token at the end of the tree.
|
|
|
|
|
+ * The token types are given by record.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tree Tree to enclose and traverse
|
|
|
|
|
+ * @param record contains token types
|
|
|
|
|
+ */
|
|
|
|
|
+ def encloseAndApply(tree: Tree, record: TR): Unit = {
|
|
|
record.before match {
|
|
record.before match {
|
|
|
case Some(value) => add(value, tree, fromEnd = false)
|
|
case Some(value) => add(value, tree, fromEnd = false)
|
|
|
case None =>
|
|
case None =>
|
|
@@ -283,7 +327,7 @@ class Parser extends AbstractParser {
|
|
|
tokens
|
|
tokens
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- def parseFile(dir: File, file: String): Boolean = {
|
|
|
|
|
|
|
+ private def parseFile(dir: File, file: String): Boolean = {
|
|
|
currentFile = file
|
|
currentFile = file
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -304,11 +348,24 @@ class Parser extends AbstractParser {
|
|
|
true
|
|
true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Adds a token to the token list.
|
|
|
|
|
+ * @param tType the type of the token
|
|
|
|
|
+ * @param line line of the occurrence in the file
|
|
|
|
|
+ * @param column column of the occurrence in the file
|
|
|
|
|
+ * @param length length of the occurrence in the file
|
|
|
|
|
+ */
|
|
|
private def add(tType: ScalaTokenConstants.Value, line: Int, column: Int, length: Int): Unit = {
|
|
private def add(tType: ScalaTokenConstants.Value, line: Int, column: Int, length: Int): Unit = {
|
|
|
tokens.addToken(new ScalaToken(tType.id, currentFile, line, column, length))
|
|
tokens.addToken(new ScalaToken(tType.id, currentFile, line, column, length))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Adds a token to the token list.
|
|
|
|
|
+ * @param tType the type of the token
|
|
|
|
|
+ * @param node the tree that marks the occurrence
|
|
|
|
|
+ * @param fromEnd if true, the token is added at the end of the tree (length 0).
|
|
|
|
|
+ */
|
|
|
private def add(tType: ScalaTokenConstants.Value, node: Tree, fromEnd: Boolean): Unit = {
|
|
private def add(tType: ScalaTokenConstants.Value, node: Tree, fromEnd: Boolean): Unit = {
|
|
|
if (node.pos.text.nonEmpty) {
|
|
if (node.pos.text.nonEmpty) {
|
|
|
// SELF type tokens with no text content mess up the sequence
|
|
// SELF type tokens with no text content mess up the sequence
|