diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala index b6e9efb780b..bae872053a6 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala @@ -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") diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala index 68020905e1f..4e59c1f01ea 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala @@ -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. diff --git a/lib/syntax/definition/src/main/scala/org/enso/syntax/text/AST.scala b/lib/syntax/definition/src/main/scala/org/enso/syntax/text/AST.scala index ca89006e186..8dec0acde43 100644 --- a/lib/syntax/definition/src/main/scala/org/enso/syntax/text/AST.scala +++ b/lib/syntax/definition/src/main/scala/org/enso/syntax/text/AST.scala @@ -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] = diff --git a/lib/syntax/specialization/js/src/main/scala/org/enso/syntax/text/Main.scala b/lib/syntax/specialization/js/src/main/scala/org/enso/syntax/text/Main.scala index d156958a392..c0ca0411b13 100644 --- a/lib/syntax/specialization/js/src/main/scala/org/enso/syntax/text/Main.scala +++ b/lib/syntax/specialization/js/src/main/scala/org/enso/syntax/text/Main.scala @@ -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 diff --git a/lib/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/Parser.scala b/lib/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/Parser.scala index 6f40fc07143..eaa039e6b99 100644 --- a/lib/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/Parser.scala +++ b/lib/syntax/specialization/shared/src/main/scala/org/enso/syntax/text/Parser.scala @@ -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 diff --git a/lib/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/ParserTest.scala b/lib/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/ParserTest.scala index 1b9cd4ed68e..d777e9cc3fe 100644 --- a/lib/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/ParserTest.scala +++ b/lib/syntax/specialization/shared/src/test/scala/org/enso/syntax/text/ParserTest.scala @@ -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) {