Remove Runtime Reflection In Parser. (#267)

This commit is contained in:
Josef 2019-10-31 14:50:27 +01:00 committed by GitHub
parent 3961738c15
commit 59bcabeb21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 153 additions and 174 deletions

View File

@ -1,14 +0,0 @@
package org.enso.data
object ADT {
import reflect.runtime.universe.TypeTag
def constructors[T](implicit ttag: TypeTag[T]) = {
val subs = ttag.tpe.typeSymbol.asClass.knownDirectSubclasses
subs.map { symbol =>
val module = reflect.runtime.currentMirror.staticModule(symbol.fullName)
val clazz = reflect.runtime.currentMirror.reflectModule(module)
clazz.instance.asInstanceOf[T]
}
}
}

View File

@ -1,7 +1,7 @@
package org.enso.syntax.text.ast
import org.enso.data.ADT
import org.enso.data.List1
import org.enso.flexer.ADT
import org.enso.syntax.text.ast.Repr.R
import scalatags.Text.all._
import scalatags.Text.TypedTag
@ -194,7 +194,7 @@ object Doc {
s"""var code = document.getElementById("$uniqueIDCode");
|var btn = document.getElementById("$uniqueIDBtn").firstChild;
|btn.data = btn.data == "Show" ? "Hide" : "Show";
|code.style.display = code.style.display ==
|code.style.display = code.style.display ==
|"inline-block" ? "none" : "inline-block";""".stripMargin
.replaceAll("\n", "")
val btn = HTML.button(btnAction)(htmlIdBtn)("Show")
@ -319,11 +319,10 @@ object Doc {
Seq(HTML.div(htmlCls)(elem.html))
}
}
def getObjectName: String = {
getClass.getEnclosingClass.toString.split('$').last
}
}
def getObjectName: String =
getClass.toString.split('$').last
}
}

View File

@ -1,6 +1,6 @@
package org.enso.syntax.text.ast.text
import org.enso.data.ADT
import org.enso.flexer.ADT
sealed trait Escape {
val repr: String

View File

@ -7,8 +7,6 @@ import org.enso.data.List1
import org.enso.syntax.text.ast.Doc._
import org.enso.syntax.text.ast.Doc
import scala.reflect.runtime.universe.reify
case class DocParserDef() extends Parser[Doc] {
//////////////////////////////////////////////////////////////////////////////
@ -103,7 +101,7 @@ case class DocParserDef() extends Parser[Doc] {
}
}
ROOT || normalText || reify { text.onPushing(currentMatch) }
ROOT || normalText || text.onPushing(currentMatch)
//////////////////////////////////////////////////////////////////////////////
//// Tags ////////////////////////////////////////////////////////////////////
@ -205,10 +203,10 @@ case class DocParserDef() extends Parser[Doc] {
val notNewLine: Pattern = not(newline).many1
val CODE: State = state.define("Code")
ROOT || code.inlinePattern || reify { code.onPushingInline(currentMatch) }
CODE || newline || reify { state.end(); state.begin(NEWLINE) }
CODE || notNewLine || reify { code.onPushingMultiline(currentMatch) }
CODE || eof || reify { state.end(); documentation.onEOF() }
ROOT || code.inlinePattern || code.onPushingInline(currentMatch)
CODE || newline || { state.end(); state.begin(NEWLINE) }
CODE || notNewLine || code.onPushingMultiline(currentMatch)
CODE || eof || { state.end(); documentation.onEOF() }
//////////////////////////////////////////////////////////////////////////////
//// Formatter ///////////////////////////////////////////////////////////////
@ -287,15 +285,14 @@ case class DocParserDef() extends Parser[Doc] {
val strikeoutTrigger: Char = Elem.Formatter.Strikeout.marker
}
ROOT || formatter.boldTrigger || reify {
formatter.onPushing(Elem.Formatter.Bold)
}
ROOT || formatter.italicTrigger || reify {
formatter.onPushing(Elem.Formatter.Italic)
}
ROOT || formatter.strikeoutTrigger || reify {
formatter.onPushing(Elem.Formatter.Strikeout)
}
ROOT || formatter.boldTrigger || formatter
.onPushing(Elem.Formatter.Bold)
ROOT || formatter.italicTrigger || formatter
.onPushing(Elem.Formatter.Italic)
ROOT || formatter.strikeoutTrigger || formatter
.onPushing(Elem.Formatter.Strikeout)
//////////////////////////////////////////////////////////////////////////////
//// Header //////////////////////////////////////////////////////////////////
@ -401,10 +398,10 @@ case class DocParserDef() extends Parser[Doc] {
).many1 >> eof
}
ROOT || link.imagePattern || reify { link.onCreatingImage() }
ROOT || link.urlPattern || reify { link.onCreatingURL() }
ROOT || link.invalidPatternNewline || reify { link.onInvalidLinkNewline() }
ROOT || link.invalidPatternEOF || reify { link.onInvalidLinkEOF() }
ROOT || link.imagePattern || link.onCreatingImage()
ROOT || link.urlPattern || link.onCreatingURL()
ROOT || link.invalidPatternNewline || link.onInvalidLinkNewline()
ROOT || link.invalidPatternEOF || link.onInvalidLinkEOF()
//////////////////////////////////////////////////////////////////////////////
//// Indent Management & New line ////////////////////////////////////////////
@ -524,9 +521,9 @@ case class DocParserDef() extends Parser[Doc] {
val NEWLINE: State = state.define("Newline")
ROOT || newline || reify { state.begin(NEWLINE) }
NEWLINE || indent.EOFPattern || reify { indent.onEOFPattern() }
NEWLINE || indent.indentPattern || reify { indent.onIndentPattern() }
ROOT || newline || state.begin(NEWLINE)
NEWLINE || indent.EOFPattern || indent.onEOFPattern()
NEWLINE || indent.indentPattern || indent.onIndentPattern()
//////////////////////////////////////////////////////////////////////////////
//// Lists ///////////////////////////////////////////////////////////////////
@ -597,8 +594,8 @@ case class DocParserDef() extends Parser[Doc] {
: Pattern = indent.indentPattern >> unorderedListTrigger >> notNewLine
}
NEWLINE || list.orderedPattern || reify { list.onOrdered() }
NEWLINE || list.unorderedPattern || reify { list.onUnordered() }
NEWLINE || list.orderedPattern || list.onOrdered()
NEWLINE || list.unorderedPattern || list.onUnordered()
//////////////////////////////////////////////////////////////////////////////
//// Section /////////////////////////////////////////////////////////////////
@ -728,19 +725,19 @@ case class DocParserDef() extends Parser[Doc] {
: Pattern = indent.indentPattern >> exampleTrigger >> indent.indentPattern
}
NEWLINE || indent.emptyLine || reify { section.onNewRaw() }
NEWLINE || indent.emptyLine >> indent.emptyLine || reify {
section.onNewRawWithHeader()
}
ROOT || section.importantPattern || reify {
section.onNewMarked(Section.Marked.Important)
}
ROOT || section.infoPattern || reify {
section.onNewMarked(Section.Marked.Info)
}
ROOT || section.examplePattern || reify {
section.onNewMarked(Section.Marked.Example)
}
NEWLINE || indent.emptyLine || section.onNewRaw()
NEWLINE || indent.emptyLine >> indent.emptyLine || section
.onNewRawWithHeader()
ROOT || section.importantPattern || section
.onNewMarked(Section.Marked.Important)
ROOT || section.infoPattern || section
.onNewMarked(Section.Marked.Info)
ROOT || section.examplePattern || section
.onNewMarked(Section.Marked.Example)
//////////////////////////////////////////////////////////////////////////////
//// Documentation ///////////////////////////////////////////////////////////
@ -804,5 +801,5 @@ case class DocParserDef() extends Parser[Doc] {
}
}
ROOT || eof || reify { documentation.onEOF() }
ROOT || eof || documentation.onEOF()
}

View File

@ -9,7 +9,6 @@ import org.enso.flexer.automata.Pattern._
import org.enso.syntax.text.AST
import scala.annotation.tailrec
import scala.reflect.runtime.universe.reify
case class ParserDef() extends flexer.Parser[AST.Module] {
import ParserDef2._
@ -169,11 +168,11 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
val SFX_CHECK = state.define("Identifier Suffix Check")
}
ROOT || ident._var || reify { ident.on(AST.Var(_)) }
ROOT || ident.cons || reify { ident.on(AST.Cons(_)) }
ROOT || "_" || reify { ident.on(AST.Blank()) }
ident.SFX_CHECK || ident.errSfx || reify { ident.onErrSfx() }
ident.SFX_CHECK || always || reify { ident.onNoErrSfx() }
ROOT || ident._var || ident.on(AST.Var(_))
ROOT || ident.cons || ident.on(AST.Cons(_))
ROOT || "_" || ident.on(AST.Blank())
ident.SFX_CHECK || ident.errSfx || ident.onErrSfx()
ident.SFX_CHECK || always || ident.onNoErrSfx()
//////////////////
//// Operator ////
@ -223,12 +222,12 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
MOD_CHECK.parent = SFX_CHECK
}
ROOT || opr.body || reify { opr.on(AST.Opr(_)) }
ROOT || opr.opsNoMod || reify { opr.onNoMod(AST.Opr(_)) }
ROOT || opr.opsGrp || reify { opr.onGrp(AST.Opr(_)) }
opr.MOD_CHECK || "=" || reify { opr.onMod() }
opr.SFX_CHECK || opr.errSfx || reify { ident.onErrSfx() }
opr.SFX_CHECK || always || reify { ident.onNoErrSfx() }
ROOT || opr.body || opr.on(AST.Opr(_))
ROOT || opr.opsNoMod || opr.onNoMod(AST.Opr(_))
ROOT || opr.opsGrp || opr.onGrp(AST.Opr(_))
opr.MOD_CHECK || "=" || opr.onMod()
opr.SFX_CHECK || opr.errSfx || ident.onErrSfx()
opr.SFX_CHECK || always || ident.onNoErrSfx()
////////////////
//// NUMBER ////
@ -278,10 +277,10 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
val PHASE2: State = state.define("Number Phase 2")
}
ROOT || num.decimal || reify { num.onDecimal() }
num.PHASE2 || "_" >> alphaNum.many1 || reify { num.onExplicitBase() }
num.PHASE2 || "_" || reify { num.onDanglingBase() }
num.PHASE2 || always || reify { num.onNoExplicitBase() }
ROOT || num.decimal || num.onDecimal()
num.PHASE2 || "_" >> alphaNum.many1 || num.onExplicitBase()
num.PHASE2 || "_" || num.onDanglingBase()
num.PHASE2 || always || num.onNoExplicitBase()
//////////////
//// Text ////
@ -459,49 +458,45 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
INTERPOLATE.parent = ROOT
}
ROOT || '`' || reify { text.onInterpolateEnd() }
text.FMT || '`' || reify { text.onInterpolateBegin() }
ROOT || "'" || reify { text.onBegin(text.FMT, Quote.Single) }
ROOT || "'''" || reify { text.onBegin(text.FMT, Quote.Triple) }
text.FMT || "'" || reify { text.onQuote(Quote.Single) }
text.FMT || "'''" || reify { text.onQuote(Quote.Triple) }
text.FMT || text.fmtSeg || reify { text.submitPlainSegment() }
text.FMT || eof || reify { text.onEOF() }
text.FMT || '\n' || reify { state.begin(text.NEWLINE) }
ROOT || '`' || text.onInterpolateEnd()
text.FMT || '`' || text.onInterpolateBegin()
ROOT || "'" || text.onBegin(text.FMT, Quote.Single)
ROOT || "'''" || text.onBegin(text.FMT, Quote.Triple)
text.FMT || "'" || text.onQuote(Quote.Single)
text.FMT || "'''" || text.onQuote(Quote.Triple)
text.FMT || text.fmtSeg || text.submitPlainSegment()
text.FMT || eof || text.onEOF()
text.FMT || '\n' || state.begin(text.NEWLINE)
ROOT || "\"" || reify { text.onBegin(text.RAW, Quote.Single) }
ROOT || "\"\"\"" || reify { text.onBegin(text.RAW, Quote.Triple) }
text.RAW || "\"" || reify { text.onQuote(Quote.Single) }
text.RAW || "$$$$$" || reify {}
text.RAW || "\"\"\"" || reify { text.onQuote(Quote.Triple) }
text.RAW || text.rawSeg || reify { text.submitPlainSegment() }
text.RAW || eof || reify { text.onEOF() }
text.RAW || '\n' || reify { state.begin(text.NEWLINE) }
ROOT || "\"" || text.onBegin(text.RAW, Quote.Single)
ROOT || "\"\"\"" || text.onBegin(text.RAW, Quote.Triple)
text.RAW || "\"" || text.onQuote(Quote.Single)
text.RAW || "$$$$$" || {}
text.RAW || "\"\"\"" || text.onQuote(Quote.Triple)
text.RAW || text.rawSeg || text.submitPlainSegment()
text.RAW || eof || text.onEOF()
text.RAW || '\n' || state.begin(text.NEWLINE)
text.NEWLINE || space.opt || reify { text.onNewLine() }
text.NEWLINE || space.opt || text.onNewLine()
AST.Text.Segment.Escape.Character.codes.foreach { code =>
import scala.reflect.runtime.universe._
val name = TermName(code.toString)
val char = q"text.Segment.Escape.Character.$name"
text.FMT || s"\\$code" || q"text.onEscape($char)"
val char = s"text.Segment.Escape.Character.$code"
text.FMT || s"\\$code" run s"text.onEscape($char)"
}
AST.Text.Segment.Escape.Control.codes.foreach { code =>
import scala.reflect.runtime.universe._
val name = TermName(code.toString)
val ctrl = q"text.Segment.Escape.Control.$name"
text.FMT || s"\\$code" || q"text.onEscape($ctrl)"
val ctrl = s"text.Segment.Escape.Control.$code"
text.FMT || s"\\$code" run s"text.onEscape($ctrl)"
}
text.FMT || text.escape_u16 || reify { text.onEscapeU16() }
text.FMT || text.escape_u32 || reify { text.onEscapeU32() }
text.FMT || text.escape_int || reify { text.onEscapeInt() }
text.FMT || "\\\\" || reify { text.onEscapeSlash() }
text.FMT || "\\'" || reify { text.onEscapeQuote() }
text.FMT || "\\\"" || reify { text.onEscapeRawQuote() }
text.FMT || ("\\" >> text.fmtChar) || reify { text.onInvalidEscape() }
text.FMT || "\\" || reify { text.submitPlainSegment() }
text.FMT || text.escape_u16 || text.onEscapeU16()
text.FMT || text.escape_u32 || text.onEscapeU32()
text.FMT || text.escape_int || text.onEscapeInt()
text.FMT || "\\\\" || text.onEscapeSlash()
text.FMT || "\\'" || text.onEscapeQuote()
text.FMT || "\\\"" || text.onEscapeRawQuote()
text.FMT || ("\\" >> text.fmtChar) || text.onInvalidEscape()
text.FMT || "\\" || text.submitPlainSegment()
//////////////
/// Blocks ///
@ -661,13 +656,13 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
val FIRSTCHAR = state.define("First Char")
}
ROOT || newline || reify { block.onEndLine() }
block.NEWLINE || space.opt >> newline || reify { block.onEmptyLine() }
block.NEWLINE || space.opt >> eof || reify { block.onEOFLine() }
block.NEWLINE || space.opt || reify { block.onNewLine() }
block.MODULE || space.opt >> newline || reify { block.onEmptyLine() }
block.MODULE || space.opt || reify { block.onModuleBegin() }
block.FIRSTCHAR || always || reify { state.end() }
ROOT || newline || block.onEndLine()
block.NEWLINE || space.opt >> newline || block.onEmptyLine()
block.NEWLINE || space.opt >> eof || block.onEOFLine()
block.NEWLINE || space.opt || block.onNewLine()
block.MODULE || space.opt >> newline || block.onEmptyLine()
block.MODULE || space.opt || block.onModuleBegin()
block.FIRSTCHAR || always || state.end()
////////////////
/// Defaults ///
@ -685,9 +680,9 @@ case class ParserDef() extends flexer.Parser[AST.Module] {
block.submitModule()
}
ROOT || space || reify { off.on() }
ROOT || eof || reify { onEOF() }
ROOT || any || reify { onUnrecognized() }
ROOT || space || off.on()
ROOT || eof || onEOF()
ROOT || any || onUnrecognized()
}
object ParserDef2 {

View File

@ -7,8 +7,6 @@ import org.enso.syntax.text.spec.DocParserDef
import scalatags.Text.TypedTag
import scalatags.Text.{all => HTML}
import HTML._
import java.io.File
import java.io.PrintWriter
import flexer.Parser.{Result => res}
import org.enso.data.List1
import org.enso.syntax.text.AST.Block.{LineOf => Line}
@ -54,6 +52,7 @@ class DocParser {
//// HTML Rendering of Documentation /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// TODO remove this functionality from parser
/**
* Used to create HTML files from Doc with or without title after Doc Parser
* Runner finished it's job
@ -66,7 +65,7 @@ class DocParser {
val htmlCode = renderHTML(documented.ast, documented.doc, cssFileName)
val astLines = documented.ast.show().split("\n")
val fileName = astLines.head.replaceAll("/", "")
saveHTMLToFile(path, fileName, htmlCode)
htmlCode
}
/**
@ -119,22 +118,6 @@ object DocParser {
def runMatched(input: String): Doc = new DocParser().runMatched(input)
def run(input: String): Result[Doc] = new DocParser().run(input)
/**
* Saves HTML code to file
*
* @param path - path to file
* @param name - file name
* @param code - HTML code generated with Doc Parser
*/
def saveHTMLToFile(
path: String,
name: String,
code: TypedTag[String]
): Unit = {
val writer = new PrintWriter(new File(path + name + ".html"))
writer.write(code.toString)
writer.close()
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,17 @@
package org.enso.flexer
import scala.reflect.macros.blackbox.Context
object ADT {
def constructors[T]: Set[T] = macro constructorsImpl[T]
def constructorsImpl[T: c.WeakTypeTag](c: Context): c.Expr[Set[T]] = {
import c.universe._
val subs = weakTypeTag[T].tpe.typeSymbol.asClass.knownDirectSubclasses.map {
symbol =>
q"${c.mirror.staticModule(symbol.fullName)}"
}
c.Expr[Set[T]](q"Set(..$subs)")
}
}

View File

@ -5,7 +5,7 @@ import org.enso.flexer.automata.State
import scala.collection.immutable.Range
import scala.collection.mutable
import scala.reflect.runtime.universe._
import scala.reflect.macros.blackbox.Context
/** Creates update functions for given DFA ~ nextState : state -> state.
* Each state has a pattern match on current utf code point.
@ -13,7 +13,8 @@ import scala.reflect.runtime.universe._
* The rest of UTF characters is dispatched by tree of if-else,
* with O(log(N)) lookup.
*/
case class Spec(dfa: DFA) {
case class Spec[C <: Context](c: C, dfa: DFA) {
import c.universe._
import Spec._
val stateHasOverlappingRules = mutable.Map(0 -> false)

View File

@ -4,7 +4,7 @@ import org.enso.flexer.automata.NFA
import org.enso.flexer.automata.Pattern
import org.enso.flexer.state.Rule
import scala.reflect.runtime.universe.Tree
import scala.reflect.macros.blackbox.Context
class State(val label: String, val ix: Int, val finish: () => Unit) {
var parent: Option[State] = None
@ -22,9 +22,9 @@ class State(val label: String, val ix: Int, val finish: () => Unit) {
def ||(expr: Pattern): Rule.Builder =
rule(expr)
def rules(): List[Rule] = {
def rules: List[Rule] = {
val myRules = revRules.reverse
parent.map(myRules ++ _.rules()).getOrElse(myRules)
parent.map(myRules ++ _.rules).getOrElse(myRules)
}
private def ruleName(ruleIx: Int): String =
@ -33,7 +33,7 @@ class State(val label: String, val ix: Int, val finish: () => Unit) {
private def buildAutomata(): NFA = {
val nfa = new NFA
val start = nfa.addState()
val endpoints = rules().zipWithIndex.map {
val endpoints = rules.zipWithIndex.map {
case (rule, ix) => buildRuleAutomata(nfa, start, ix, rule)
}
val end = nfa.addState()
@ -82,14 +82,16 @@ class State(val label: String, val ix: Int, val finish: () => Unit) {
}
}
def generate(): Tree = {
import scala.reflect.runtime.universe._
def generate[C <: Context](c: C): c.Tree = {
import c.universe._
val nfa = buildAutomata()
val dfa = nfa.toDFA()
val state = Spec(dfa).generate(ix)
val state = Spec[c.type](c, dfa).generate(ix)
val rs = rules.zipWithIndex.map {
case (rule, ruleIx) =>
q"def ${TermName(ruleName(ruleIx))}() = ${rule.tree}"
val tree = c.parse(rule.tree)
q"def ${TermName(ruleName(ruleIx))}() = $tree"
}
q"..$state; ..$rs"
}

View File

@ -4,11 +4,28 @@ import org.enso.flexer.Parser
import org.enso.lint.Unused
import scala.reflect.macros.blackbox.Context
import scala.reflect.runtime.universe
// FIXME: Needs to be refactored. Contains deprecated API usage
object Macro {
def print(c: Context, msg: String) = c.echo(c.enclosingPosition, msg)
def runRule(c: Context)(program: c.Tree): c.Tree = {
import c.universe._
val tree = new Transformer {
override def transform(tree: Tree): Tree = tree match {
case Select(This(TypeName(_)), name) =>
super.transform(Ident(name))
case node => super.transform(node)
}
}.transform(program)
c.macroApplication match {
case Apply(Select(lhs, _), _) => q"$lhs.run(${showCode(tree)})"
case x => throw new Error("Unsupported shape")
}
}
def compileImpl[T: c.WeakTypeTag, P: c.WeakTypeTag](
c: Context
)(p: c.Expr[P])(ev: c.Expr[P <:< Parser[T]]): c.Expr[() => P] = {
@ -17,10 +34,7 @@ object Macro {
val tree = p.tree
val expr = q"$tree"
val parser = c.eval(c.Expr[Parser[T]](c.untypecheck(expr.duplicate)))
val groups = c.internal
.createImporter(universe)
.importTree(universe.Block(parser.state.registry.map(_.generate()): _*))
val groups = q"..${parser.state.registry.map(_.generate(c))}"
val (superClassName, tree2) = tree match {
case Apply(Select(tree2 @ Select(_, name), _), _) => (name, tree2)
case _ =>
@ -32,21 +46,10 @@ object Macro {
)
}
val groupsRebind = new Transformer {
override def transform(tree: Tree): Tree = tree match {
case Select(Ident(base), name) =>
val base2 = if (base == superClassName) q"this" else Ident(base)
super.transform(Select(base2, name))
case node => super.transform(node)
}
}
val reboundGroups = groupsRebind.transform(groups)
val addGroupDefs = new Transformer {
override def transform(tree: Tree): Tree = tree match {
case Template(parents, self, body) =>
val exprs = q"..$reboundGroups;None".asInstanceOf[Block].stats
val exprs = q"..$groups;None".asInstanceOf[Block].stats
Template(parents, self, body ++ exprs)
case node => super.transform(node)
}

View File

@ -1,16 +1,12 @@
package org.enso.flexer.state
import org.enso.flexer.automata.Pattern
import org.enso.flexer.spec.Macro
import scala.reflect.runtime.universe.Expr
import scala.reflect.runtime.universe.Tree
final case class Rule(pattern: Pattern, tree: Tree)
final case class Rule(pattern: Pattern, tree: String)
object Rule {
final case class Builder(pattern: Pattern, finalizer: Rule => Unit) {
def run(expr: Expr[_]): Unit = run(expr.tree)
def run(tree: Tree): Unit = finalizer(Rule(pattern, tree))
def ||(expr: Expr[_]) = run(expr)
def ||(expr: Tree) = run(expr)
def run(program: String): Unit = finalizer(Rule(pattern, program))
def ||(program: => Unit): Unit = macro Macro.runRule
}
}