mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 06:52:34 +03:00
Assign a UUID to Each AST Node (#738)
This commit is contained in:
parent
a42495a68e
commit
81bde28589
@ -471,17 +471,21 @@ object AstToIr {
|
||||
val leftArg = translateCallArgument(left)
|
||||
val rightArg = translateCallArgument(right)
|
||||
|
||||
if (leftArg.name.isDefined) {
|
||||
IR.Error.Syntax(left, IR.Error.Syntax.NamedArgInOperator)
|
||||
} else if (rightArg.name.isDefined) {
|
||||
IR.Error.Syntax(right, IR.Error.Syntax.NamedArgInOperator)
|
||||
} else {
|
||||
Application.Operator.Binary(
|
||||
leftArg,
|
||||
Name.Literal(fn.name, getIdentifiedLocation(fn)),
|
||||
rightArg,
|
||||
getIdentifiedLocation(callable)
|
||||
)
|
||||
fn match {
|
||||
case AST.Ident.Opr.any(fn) =>
|
||||
if (leftArg.name.isDefined) {
|
||||
IR.Error.Syntax(left, IR.Error.Syntax.NamedArgInOperator)
|
||||
} else if (rightArg.name.isDefined) {
|
||||
IR.Error.Syntax(right, IR.Error.Syntax.NamedArgInOperator)
|
||||
} else {
|
||||
Application.Operator.Binary(
|
||||
leftArg,
|
||||
Name.Literal(fn.name, getIdentifiedLocation(fn)),
|
||||
rightArg,
|
||||
getIdentifiedLocation(callable)
|
||||
)
|
||||
}
|
||||
case _ => IR.Error.Syntax(left, IR.Error.Syntax.InvalidOperatorName)
|
||||
}
|
||||
case AST.App.Prefix(_, _) =>
|
||||
throw new UnhandledEntity(callable, "translateCallable")
|
||||
|
@ -2908,6 +2908,10 @@ object IR {
|
||||
case object NamedArgInOperator extends Reason {
|
||||
override def explanation: String = "Named argument in operator section."
|
||||
}
|
||||
|
||||
case object InvalidOperatorName extends Reason {
|
||||
override def explanation: String = "Invalid operator name."
|
||||
}
|
||||
}
|
||||
|
||||
/** A representation of an invalid piece of IR.
|
||||
|
@ -173,6 +173,10 @@ object Shape extends ShapeImplicit {
|
||||
import HasSpan.implicits._
|
||||
import AST.StreamOf
|
||||
|
||||
|
||||
/// Utils ///
|
||||
val newline = R + '\n'
|
||||
|
||||
/////////////////
|
||||
//// Invalid ////
|
||||
/////////////////
|
||||
@ -281,7 +285,7 @@ object Shape extends ShapeImplicit {
|
||||
final case class Infix[T](
|
||||
larg: T,
|
||||
loff: Int,
|
||||
opr: AST.Opr,
|
||||
opr: T,
|
||||
roff: Int,
|
||||
rarg: T
|
||||
) extends App[T]
|
||||
@ -544,10 +548,10 @@ object Shape extends ShapeImplicit {
|
||||
|
||||
object TextBlock extends IntermediateTrait[TextBlock] {
|
||||
def lineRepr[T: Repr](off: Int, l: TextBlockLine[SegmentFmt[T]]): Builder =
|
||||
R + l.empty_lines.map(Block.newline + _) + Block.newline + off + l.text
|
||||
R + l.empty_lines.map(newline + _) + newline + off + l.text
|
||||
def lineSpan[T: HasSpan](off: Int, l: TextBlockLine[SegmentFmt[T]]): Int = {
|
||||
val emptyLinesSpan = l.empty_lines.map(Block.newline.span + _).sum
|
||||
emptyLinesSpan + Block.newline.span + off + l.text.span()
|
||||
val emptyLinesSpan = l.empty_lines.map(newline.span + _).sum
|
||||
emptyLinesSpan + newline.span + off + l.text.span()
|
||||
}
|
||||
|
||||
implicit def ftor: Functor[TextBlock] = semi.functor
|
||||
@ -724,8 +728,10 @@ object Shape extends ShapeImplicit {
|
||||
implicit def repr[T: Repr]: Repr[Infix[T]] =
|
||||
t => R + t.larg + t.loff + t.opr + t.roff + t.rarg
|
||||
implicit def ozip[T: HasSpan]: OffsetZip[Infix, T] = t => {
|
||||
val rargIndex = Index(t.larg.span + t.loff + t.opr.span + t.roff)
|
||||
t.copy(larg = (Index.Start, t.larg), rarg = (rargIndex, t.rarg))
|
||||
val larg = Index.Start -> t.larg
|
||||
val opr = Index(t.larg.span + t.loff) -> t.opr
|
||||
val rarg = Index(t.larg.span + t.loff + t.opr.span + t.roff) -> t.rarg
|
||||
t.copy(larg = larg, opr = opr, rarg = rarg)
|
||||
}
|
||||
implicit def span[T: HasSpan]: HasSpan[Infix[T]] =
|
||||
t => t.larg.span + t.loff + t.opr.span + t.roff + t.rarg.span
|
||||
@ -805,8 +811,6 @@ object Shape extends ShapeImplicit {
|
||||
headSpan + emptyLinesSpan + firstLineSpan + linesSpan
|
||||
}
|
||||
|
||||
/// Utils ///
|
||||
val newline = R + '\n'
|
||||
|
||||
/// Block type ///
|
||||
sealed trait Type
|
||||
@ -833,11 +837,20 @@ object Shape extends ShapeImplicit {
|
||||
object Module {
|
||||
implicit def ftor: Functor[Module] = semi.functor
|
||||
implicit def fold: Foldable[Module] = semi.foldable
|
||||
implicit def ozip[T]: OffsetZip[Module, T] = _.map(Index.Start -> _)
|
||||
implicit def ozip[T: HasSpan]: OffsetZip[Module, T] = t => {
|
||||
var index = 0
|
||||
val lines = t.lines.map { line =>
|
||||
val elem = line.elem.map((Index(index), _))
|
||||
index += line.span + newline.span
|
||||
line.copy(elem = elem)
|
||||
}
|
||||
t.copy(lines = lines)
|
||||
}
|
||||
|
||||
implicit def repr[T: Repr]: Repr[Module[T]] =
|
||||
t => R + t.lines.head + t.lines.tail.map(Block.newline + _)
|
||||
t => R + t.lines.head + t.lines.tail.map(newline + _)
|
||||
implicit def span[T: HasSpan]: HasSpan[Module[T]] =
|
||||
t => t.lines.span() + (t.lines.size - 1) * Block.newline.span
|
||||
t => t.lines.span() + (t.lines.size - 1) * newline.span
|
||||
}
|
||||
|
||||
object Macro extends IntermediateTrait[Macro] {
|
||||
@ -935,9 +948,8 @@ object Shape extends ShapeImplicit {
|
||||
implicit def ftor[T]: Functor[Documented] = semi.functor
|
||||
implicit def fold[T]: Foldable[Documented] = semi.foldable
|
||||
implicit def repr[T: Repr]: Repr[Documented[T]] = t => {
|
||||
val symbolRepr = R + symbol + symbol
|
||||
val betweenDocAstRepr = R + Block.newline +
|
||||
Block.newline.build * t.emptyLinesBetween
|
||||
val symbolRepr = R + symbol + symbol
|
||||
val betweenDocAstRepr = R + newline + newline.build * t.emptyLinesBetween
|
||||
R + symbolRepr + t.doc + betweenDocAstRepr + t.ast
|
||||
}
|
||||
implicit def offsetZip[T]: OffsetZip[Documented, T] =
|
||||
|
@ -11,10 +11,10 @@ object Parse {
|
||||
@JSExportTopLevel("parse")
|
||||
def parse(program: String, idsJson: String): String = {
|
||||
try {
|
||||
val ids = Parser.idMapFromJson(idsJson).left.map { error =>
|
||||
val idmap = Parser.idMapFromJson(idsJson).left.map { error =>
|
||||
throw new ParserError("Could not deserialize idmap.", error)
|
||||
}.merge
|
||||
new Parser().run(new Reader(program), ids).toJson().noSpacesSortKeys
|
||||
new Parser().run(new Reader(program), idmap).toJson().noSpacesSortKeys
|
||||
} catch {
|
||||
// FIXME We wrap the error message in JavaScriptException, so that javascript
|
||||
// can display it. This is no longer needed in scalajs 1.0
|
||||
|
@ -2,7 +2,7 @@ package org.enso.syntax.text
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
import cats.{Foldable, Functor}
|
||||
import cats.Foldable
|
||||
import org.enso.data.List1
|
||||
import org.enso.data.Span
|
||||
import org.enso.flexer
|
||||
@ -12,7 +12,6 @@ import org.enso.syntax.text.AST.Block.OptLine
|
||||
import org.enso.syntax.text.AST.Macro.Match.SegmentOps
|
||||
import org.enso.syntax.text.AST.App
|
||||
import org.enso.syntax.text.ast.meta.Builtin
|
||||
import org.enso.syntax.text.ast.Repr
|
||||
import org.enso.syntax.text.prec.Macro
|
||||
import org.enso.syntax.text.spec.ParserDef
|
||||
import cats.implicits._
|
||||
@ -230,14 +229,13 @@ class Parser {
|
||||
def run(input: Reader, idMap: IDMap): AST.Module = {
|
||||
val tokenStream = engine.run(input)
|
||||
val spanned = tokenStream.map(attachModuleLocations)
|
||||
val resolved = spanned.map(Macro.run) match {
|
||||
val resolved = spanned.map(Macro.run) match {
|
||||
case flexer.Parser.Result(_, flexer.Parser.Result.Success(mod)) =>
|
||||
val mod2 = annotateModule(idMap, mod)
|
||||
resolveMacros(mod2).asInstanceOf[AST.Module]
|
||||
case _ => throw ParsingFailed
|
||||
}
|
||||
val withExpressionIds = fillExpressionIds(resolved)
|
||||
withExpressionIds
|
||||
resolved
|
||||
}
|
||||
|
||||
/**
|
||||
@ -335,10 +333,7 @@ class Parser {
|
||||
ids = ids.tail
|
||||
ast.setID(id)
|
||||
case _ =>
|
||||
ast match {
|
||||
case AST.Macro.Match.any(_) => ast.withNewID()
|
||||
case _ => ast
|
||||
}
|
||||
ast.withNewID()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -364,60 +359,6 @@ class Parser {
|
||||
case _ => ast.map(resolveMacros)
|
||||
}
|
||||
|
||||
/** All [[AST]] elements that are top-level expressions (i.e. can be nodes in
|
||||
* IDE and are potentially worth of visualizing.
|
||||
*/
|
||||
def fillExpressionIds(module: AST.Module): AST.Module = {
|
||||
sealed trait Context
|
||||
|
||||
/** A module block or nested block (e.g. method body). */
|
||||
final case object Block extends Context
|
||||
|
||||
/** Any non-block context */
|
||||
final case object Other extends Context
|
||||
|
||||
/** Goes over AST and for Assignments (either infix or section right) that
|
||||
* are within [[AST.Block]] or [[AST.Module]] node, assigns IDs to the
|
||||
* right hand side. Any other expression in the block gets ID on its root
|
||||
* AST. All other nodes are returned as-is.
|
||||
*/
|
||||
implicit class Processor[T[S] <: Shape[S]](ast: AST.ASTOf[T])(
|
||||
implicit
|
||||
functor: Functor[T],
|
||||
fold: Foldable[T],
|
||||
repr: Repr[T[AST]],
|
||||
ozip: OffsetZip[T, AST]
|
||||
) {
|
||||
def process(context: Context): AST.ASTOf[T] = {
|
||||
def assignmentInBlock(opr: AST.Opr): Boolean =
|
||||
context == Block && opr.name == "="
|
||||
|
||||
ast match {
|
||||
case AST.Module.any(block) => block.map(_.process(Block))
|
||||
case AST.Block.any(block) => block.map(_.process(Block))
|
||||
case AST.App.Infix.any(infix) if assignmentInBlock(infix.opr) =>
|
||||
val newShape = infix.shape.copy(
|
||||
larg = infix.larg.process(Other),
|
||||
opr = infix.opr.process(Other),
|
||||
rarg = infix.rarg.process(Other).withNewIDIfMissing()
|
||||
)
|
||||
infix.copy(shape = newShape)
|
||||
case AST.App.Section.Right.any(right)
|
||||
if assignmentInBlock(right.opr) =>
|
||||
val newShape = right.shape.copy(
|
||||
arg = right.arg.process(Other),
|
||||
opr = right.opr.process(Other)
|
||||
)
|
||||
right.copy(shape = newShape)
|
||||
case otherAst if context == Block =>
|
||||
otherAst.process(Other).withNewIDIfMissing()
|
||||
case _ => ast.map(_.process(Other))
|
||||
}
|
||||
}.asInstanceOf[AST.ASTOf[T]] // Note: [Type safety]
|
||||
}
|
||||
module.process(Other)
|
||||
}
|
||||
|
||||
/* Note: [Type safety]
|
||||
* ~~~~~~~~~~~~~~~~~~~
|
||||
* This function promises to return AST with the same shape as it
|
||||
|
@ -48,8 +48,12 @@ class ParserTest extends AnyFlatSpec with Matchers {
|
||||
|
||||
def assertIdentity(input: String): Assertion = {
|
||||
val module = Parser().run(input)
|
||||
val idmap1 = module.idMap
|
||||
val idmap2 = Parser().run(new Reader(input), idmap1).idMap
|
||||
println(module.zipWithOffset)
|
||||
assertSpan(input, module)
|
||||
assert(module.show() == new Reader(input).toString())
|
||||
assert(idmap1 == idmap2)
|
||||
}
|
||||
|
||||
implicit class TestString(input: String) {
|
||||
|
Loading…
Reference in New Issue
Block a user