summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--book/src/main/scalatex/book/Intro.scalatex11
-rw-r--r--book/src/main/scalatex/book/handson/CanvasApp.scalatex2
-rw-r--r--book/src/main/scalatex/book/handson/GettingStarted.scalatex2
-rw-r--r--build.sbt14
-rw-r--r--scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala415
-rw-r--r--scalaParser/src/main/scala/scalaParser/syntax/Basic.scala (renamed from scalatexApi/src/main/scala/scalaparser/syntax/Basic.scala)2
-rw-r--r--scalaParser/src/main/scala/scalaParser/syntax/Identifiers.scala (renamed from scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala)2
-rw-r--r--scalaParser/src/main/scala/scalaParser/syntax/Literals.scala (renamed from scalatexApi/src/main/scala/scalaparser/syntax/Literals.scala)2
-rw-r--r--scalaParser/src/test/scala/scalaParser/SyntaxTest.scala (renamed from scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala)98
-rw-r--r--scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala412
-rw-r--r--scalatexApi/src/main/scala/scalatex/stages/Parser.scala10
-rw-r--r--scalatexApi/src/test/scala/scalatex/ParserTests.scala2
-rw-r--r--test.txt3
14 files changed, 509 insertions, 468 deletions
diff --git a/.gitignore b/.gitignore
index 0e25b59..4a67d33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,4 @@ target/
output/
.DS_STORE
*.iml
-.idea/
+.idea
diff --git a/book/src/main/scalatex/book/Intro.scalatex b/book/src/main/scalatex/book/Intro.scalatex
index 87c8895..5438cfb 100644
--- a/book/src/main/scalatex/book/Intro.scalatex
+++ b/book/src/main/scalatex/book/Intro.scalatex
@@ -35,7 +35,7 @@
@li
Typo-safety due to its compiler which catches many silly errors before the code is run
@li
- In-editor support for autocomplete, error-highlghting, refactors, and intelligent navigation
+ In-editor support for autocomplete, error-highlighting, refactors, and intelligent navigation
@li
Moderate sized compiled executables, in the 100-400kb range
@li
@@ -59,7 +59,7 @@
However, Javascript is not an easy language to work in at scale: when your code-base extends to thousands, tens or hundreds of thousands of lines of code. The un-typed nature of the language, which is fine for small applications, becomes an issue when you are mainly working with code that you did not write.
@p
- In a large code-base, finding out what methods or properties a variable has is often a long chase through dozens of files to see how it ended up being passed to the current function. Refactorings, which are OK when you can just test the code see if it works, become dangerous when your code base is large enough that "just test all the code" would take hours. Language-warts which are slightly annoying in small programs become a minefield in large ones: it's only a matter of time before you hit one, often in code you did-not/cannot test, resulting in breakages in production.
+ In a large code-base, finding out what methods or properties a variable has is often a long chase through dozens of files to see how it ended up being passed to the current function. Refactorings, which are OK when you can just test the code to see if it works, become dangerous when your code base is large enough that "just test all the code" would take hours. Language-warts which are slightly annoying in small programs become a minefield in large ones: it's only a matter of time before you hit one, often in code you did-not/cannot test, resulting in breakages in production.
@p
Apart from the inherent danger of the language, Javascript has another major problem: the language has left many things unspecified, yet at the same time provides the ability to emulate these things in a variety of ways. This means that rather than having a single way of e.g. defining a class and instantiating an object, there is a decade-long debate between a dozen different and equally-bad, hand-crafted alternatives. Large code-bases use third-party libraries, and most are guaranteed (purely due to how stastistics work) to do these basic things differently from your own code, making understanding these disparate code-bases (e.g. when something goes wrong) very difficult.
@@ -95,7 +95,7 @@
Desktop application security is non-existent. Install one rogue application and it can take over your computer, steal your credit card number, use your email for sending spam, and all sorts of other nasty things. Removing these for-good sometimes involves re-installing your entire operating system. Hence people are much more wary about only installing desktop software from people they "trust".
@p
- In many ways, mobile App platforms like Android and iOS have closed the gap between "native" and "web" applications. Installing a new App may take 30 seconds, you can often deep-link to certain pages within an App, and Apps a much tighter security model than desktop software does. Nevertheless, 30 seconds is still much longer than the 0.5 seconds it takes to open a web page, deep-linking in apps is not very prevalent, and the security model still often leaves space for rogue Apps to misbehave and steal data.
+ In many ways, mobile App platforms like Android and iOS have closed the gap between "native" and "web" applications. Installing a new App may take 30 seconds, you can often deep-link to certain pages within an App, and Apps have a much tighter security model than desktop software does. Nevertheless, 30 seconds is still much longer than the 0.5 seconds it takes to open a web page, deep-linking in apps is not very prevalent, and the security model still often leaves space for rogue Apps to misbehave and steal data.
@hr
@p
@@ -151,7 +151,7 @@
Not having to resort to awkward Ajax-calls or pre-computation to avoid duplicating logic between the client and server
@p
- Shared code doesn't just mean sharing pre-made libraries between the client and server. You can easily @sect.ref("Cross Publishing Libraries", "publish your own libraries") that can be used on both Scala-JVM and Scala.js. This means that as a library author, you can at once target two completely different platforms, and (with some work) take advantage of the intricacies of each platform to optimize your library for each one. Take Scalatags as an example: as the first client-server Scala.js-ScalaJVM shared libraries, it enjoys a roughly event split of downloads from people using it on both platforms:
+ Shared code doesn't just mean sharing pre-made libraries between the client and server. You can easily @sect.ref("Cross Publishing Libraries", "publish your own libraries") that can be used on both Scala-JVM and Scala.js. This means that as a library author, you can at once target two completely different platforms, and (with some work) take advantage of the intricacies of each platform to optimize your library for each one. Take Scalatags as an example: as the first client-server Scala.js-ScalaJVM shared libraries, it enjoys a roughly even split of downloads from people using it on both platforms:
@img(src:="images/Scalatags Downloads.png", width:="100%")
@@ -180,6 +180,3 @@
@p
Scala.js provides all these things, and much more. If you're interested enough to want to make use of Scala.js, read on!
-
-
-
diff --git a/book/src/main/scalatex/book/handson/CanvasApp.scalatex b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
index e68adb7..230ae67 100644
--- a/book/src/main/scalatex/book/handson/CanvasApp.scalatex
+++ b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
@@ -160,7 +160,7 @@
@p
We've by now written a good chunk of Scala.js code, and perhaps debugged some mysterious errors, and tried some new things. One thing you've probably noticed is the efficiency of the process: you make a change in your editor, the browser reloads itself, and life goes on. There is a compile cycle, but after a few runs the compiler warms up and the compilation cycle drops to less than a second.
@p
- Apart from the compilation/reload speed, you've probably noticed the benefit of tooling around Scala.js. Unlike Javascript editors, your existin Scala IDEs like @lnk.misc.IntelliJ or @lnk.misc.Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlghting, jump-to-definition, and a myriad other modern conveniences that are missing when working in dynamically-typed languages are present when working in Scala.js. This makes the code much less mysterious: you're no longer trying to guess what methods a value has, or what a method returns: it's all laid out in front of you in plain sight.
+ Apart from the compilation/reload speed, you've probably noticed the benefit of tooling around Scala.js. Unlike Javascript editors, your existin Scala IDEs like @lnk.misc.IntelliJ or @lnk.misc.Eclipse can give very useful help when you're working with Scala.js. Autocomplete, error-highlighting, jump-to-definition, and a myriad other modern conveniences that are missing when working in dynamically-typed languages are present when working in Scala.js. This makes the code much less mysterious: you're no longer trying to guess what methods a value has, or what a method returns: it's all laid out in front of you in plain sight.
@sect{Full Scala}
@p
diff --git a/book/src/main/scalatex/book/handson/GettingStarted.scalatex b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
index 7a24f1c..5d9bf7b 100644
--- a/book/src/main/scalatex/book/handson/GettingStarted.scalatex
+++ b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
@@ -4,7 +4,7 @@
@ul
@li
- @lnk("sbt", "http://www.scala-sbt.org/"): SBT is the most common build-tool in the Scala community, and is what we will use for building our Scala.js application. Their home page has a link to download and install it.
+ @lnk("sbt", "http://www.scala-sbt.org/"): SBT is the most common build-tool in the Scala community, and is what we will use for building our Scala.js application. Their home page has a link to download and install it. (If you are already using Typesafe Activator, that is effectively sbt.)
@li
An editor for Scala: @lnk("IntelliJ Scala", "http://blog.jetbrains.com/scala/") and @lnk("Eclipse ScalaIDE", "http://scala-ide.org/") are the most popular choices and work on all platforms, though there are others.
@li
diff --git a/build.sbt b/build.sbt
index 895c2b9..8df7b50 100644
--- a/build.sbt
+++ b/build.sbt
@@ -2,9 +2,19 @@
import scala.scalajs.sbtplugin.ScalaJSPlugin._
import ScalaJSKeys._
-
+lazy val scalaParser = project.in(file("scalaParser")).settings(
+ scalaVersion := "2.11.4",
+ libraryDependencies ++= Seq(
+ "com.lihaoyi" %% "utest" % "0.2.4",
+ "com.lihaoyi" %% "acyclic" % "0.1.2" % "provided",
+ "org.parboiled" %% "parboiled" % "2.0.1"
+ ),
+ addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2"),
+ testFrameworks += new TestFramework("utest.runner.JvmFramework")
+)
lazy val scalatexApi = project.in(file("scalatexApi"))
- .settings(
+ .dependsOn(scalaParser)
+ .settings(
scalaVersion := "2.11.4",
libraryDependencies ++= Seq(
"com.lihaoyi" %% "utest" % "0.2.4",
diff --git a/scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala b/scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala
new file mode 100644
index 0000000..b164ec4
--- /dev/null
+++ b/scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala
@@ -0,0 +1,415 @@
+package scalaParser
+import acyclic.file
+import language.implicitConversions
+import syntax._
+import org.parboiled2._
+
+/**
+ * Parser for Scala syntax.
+ *
+ * The `G` parameter that gets passed in to each rule stands for
+ * "Greedy", and determines whether or not that rule is to consume
+ * newlines after the last terminal in that rule. We need to pass it
+ * everywhere so it can go all the way to the last terminal deep
+ * inside the parse tree, which can then decide whether or not to
+ * consume whitespace.
+ *
+ * The vast majority of terminals will consume newlines; only rules
+ * which occur in {} blocks won't have their terminals consume newlines,
+ * and only the *last* terminal in the rule will be affected.
+ * That's why the parser does terminals-consume-newlines-by-default,
+ * and leaves it up to the dev to thread the `G` variable where-ever
+ * we want the opposite behavior.
+ */
+class ScalaSyntax(val input: ParserInput) extends Parser with Basic with Identifiers with Literals {
+ // Aliases for common things. These things are used in almost every parser
+ // in the file, so it makes sense to keep them short.
+ type B = Boolean
+ val t = true
+ type R0 = Rule0
+ /**
+ * Parses all whitespace, excluding newlines. This is only
+ * really useful in e.g. {} blocks, where we want to avoid
+ * capturing newlines so semicolon-inference would work
+ */
+ def WS = rule { zeroOrMore(Basic.WhitespaceChar | Literals.Comment) }
+
+ /**
+ * Parses whitespace, including newlines.
+ * This is the default for most things
+ */
+ def WL = rule{ zeroOrMore(Basic.WhitespaceChar | Literals.Comment | Basic.Newline) }
+
+
+
+ /**
+ * By default, all strings and characters greedily
+ * capture all whitespace immediately after the token.
+ */
+ implicit private[this] def wspStr(s: String): R0 = rule { WL ~ str(s) }
+ implicit private[this] def wspChar(s: Char): R0 = rule { WL ~ ch(s) }
+
+ /**
+ * Most keywords don't just require the correct characters to match,
+ * they have to ensure that subsequent characters *don't* match in
+ * order for it to be a keyword. This enforces that rule for key-words
+ * (W) and key-operators (O) which have different non-match criteria.
+ */
+ object K {
+ def W(s: String) = rule {
+ WL ~ Key.W(s)
+ }
+
+ def O(s: String) = rule {
+ WL ~ Key.O(s)
+ }
+ }
+
+
+ def pos = cursor -> cursorChar
+
+ /**
+ * helper printing function
+ */
+ def pr(s: String) = rule { run(println(s"LOGGING $cursor: $s")) }
+
+ def Id = rule { WL ~ Identifiers.Id }
+ def VarId = rule { WL ~ Identifiers.VarId }
+ def Literal = rule { WL ~ Literals.Literal }
+ def Semi = rule { WS ~ Basic.Semi }
+ def Semis = rule { oneOrMore(Semi) }
+ def Newline = rule { WL ~ Basic.Newline }
+
+ def QualId = rule { WL ~ oneOrMore(Id).separatedBy('.') }
+ def Ids = rule { oneOrMore(Id) separatedBy ',' }
+
+ def Path: R0 = rule {
+ zeroOrMore(Id ~ '.') ~ K.W("this") ~ zeroOrMore(Id).separatedBy('.') |
+ StableId
+ }
+ def StableId: R0 = rule {
+ zeroOrMore(Id ~ '.') ~ (K.W("this") | K.W("super") ~ optional(ClassQualifier)) ~ '.' ~ oneOrMore(Id).separatedBy('.') |
+ Id ~ zeroOrMore(WL ~ '.' ~ WL ~ Id)
+ }
+
+ def ClassQualifier = rule { '[' ~ Id ~ ']' }
+
+ def Type: R0 = rule {
+ FunctionArgTypes ~ K.O("=>") ~ Type | InfixType ~ optional(WL ~ ExistentialClause)
+ }
+ def FunctionArgTypes = rule {
+ InfixType | '(' ~ optional(oneOrMore(ParamType) separatedBy ',') ~ ')'
+ }
+
+ def ExistentialClause = rule { "forSome" ~ '{' ~ oneOrMore(ExistentialDcl).separatedBy(Semi) }
+ def ExistentialDcl = rule { K.W("type") ~ TypeDcl | K.W("val") ~ ValDcl }
+
+ def InfixType = rule {
+ CompoundType ~ zeroOrMore(WL ~ Id ~ optional(Newline) ~ CompoundType)
+ }
+ def CompoundType = rule {
+ oneOrMore(AnnotType).separatedBy(WL ~ K.W("with")) ~ optional(Refinement)
+ }
+ def AnnotType = rule {
+ SimpleType ~ zeroOrMore(WL ~ Annotation)
+ }
+ def SimpleType: R0 = rule {
+ BasicType ~
+ optional(WL ~ '#' ~ Id) ~
+ optional(WL ~ TypeArgs)
+ }
+ def BasicType: R0 = rule {
+ '(' ~ Types ~ ')' |
+ Path ~ '.' ~ K.W("type") |
+ StableId
+ }
+ def TypeArgs = rule { '[' ~ Types ~ "]" }
+ def Types = rule { oneOrMore(Type).separatedBy(',') }
+ def Refinement = rule {
+ optional(Newline) ~ '{' ~ oneOrMore(RefineStat).separatedBy(Semi) ~ "}"
+ }
+ def RefineStat = rule { "type" ~ TypeDef | Dcl | MATCH }
+ def TypePat = rule { CompoundType }
+ def Ascription = rule {
+ ":" ~ ("_" ~ "*" | InfixType | oneOrMore(Annotation))
+ }
+
+ def ParamType = rule { K.O("=>") ~ Type | Type ~ "*" | Type }
+
+ def Expr: R0 = rule {
+ (Bindings | optional(K.W("implicit")) ~ Id | "_") ~ K.O("=>") ~ Expr |
+ Expr1
+ }
+ def Expr1: R0 = rule {
+ IfCFlow |
+ WhileCFlow |
+ TryCFlow |
+ DoWhileCFlow |
+ ForCFlow |
+ K.W("throw") ~ Expr |
+ K.W("return") ~ optional(Expr) |
+ SimpleExpr ~ K.O("=") ~ Expr |
+ PostfixExpr ~ optional("match" ~ '{' ~ CaseClauses ~ "}" | Ascription)
+
+ }
+ def IfCFlow = rule { "if" ~ '(' ~ Expr ~ ')' ~ zeroOrMore(Newline) ~ Expr ~ optional(optional(Semi) ~ K.W("else") ~ Expr) }
+ def WhileCFlow = rule { "while" ~ '(' ~ Expr ~ ')' ~ zeroOrMore(Newline) ~ Expr }
+ def TryCFlow = rule {
+ K.W("try") ~ Expr ~
+ optional(WL ~ K.W("catch") ~ Expr) ~
+ optional(WL ~ K.W("finally") ~ Expr)
+ }
+
+ def DoWhileCFlow = rule { K.W("do") ~ Expr ~ optional(Semi) ~ "while" ~ '(' ~ Expr ~ ")" }
+ def ForCFlow = rule {
+ "for" ~
+ ('(' ~ Enumerators ~ ')' | '{' ~ Enumerators ~ '}') ~
+ zeroOrMore(Newline) ~
+ optional(K.W("yield")) ~
+ Expr }
+ def NotNewline: R0 = rule{ &( WS ~ noneOf("\n") )}
+ def PostfixExpr: R0 = rule { InfixExpr ~ optional(NotNewline ~ Id ~ optional(Newline)) }
+ def InfixExpr: R0 = rule {
+ PrefixExpr ~
+ zeroOrMore(
+ NotNewline ~
+ Id ~
+ optional(Newline) ~
+ PrefixExpr
+ )
+ }
+ def PrefixExpr = rule { optional(WL ~ anyOf("-+~!")) ~ SimpleExpr }
+
+ def SimpleExpr: R0 = rule {
+ SimpleExpr1 ~
+ zeroOrMore(WL ~ ('.' ~ Id | TypeArgs | ArgumentExprs)) ~
+ optional(WL ~ "_")
+ }
+
+ def SimpleExpr1 = rule{
+ K.W("new") ~ (ClassTemplate | TemplateBody) |
+ BlockExpr |
+ Literal ~ drop[String] |
+ Path |
+ K.W("_") |
+ '(' ~ optional(Exprs) ~ ")"
+ }
+
+
+
+ def Exprs: R0 = rule { oneOrMore(Expr).separatedBy(',') }
+ def ArgumentExprs: R0 = rule {
+ '(' ~ optional(Exprs ~ optional(K.O(":") ~ K.W("_") ~ '*')) ~ ")" |
+ optional(Newline) ~ BlockExpr
+ }
+
+ def BlockExpr: R0 = rule { '{' ~ (CaseClauses | Block) ~ "}" }
+ def BlockEnd: R0 = rule{ optional(Semis) ~ &("}" | "case") }
+ def Block: R0 = rule {
+ optional(Semis) ~
+ (
+ ResultExpr ~ BlockEnd |
+ BlockStats ~ Semis ~ ResultExpr ~ BlockEnd |
+ BlockStats ~ BlockEnd |
+ MATCH ~ BlockEnd
+ )
+ }
+ def BlockStats: R0 = rule{
+ oneOrMore(BlockStat).separatedBy(Semis)
+ }
+ def BlockStat: R0 = rule {
+ Import |
+ zeroOrMore(Annotation) ~ (optional(K.W("implicit") | K.W("lazy")) ~ Def | zeroOrMore(LocalModifier) ~ TmplDef) |
+ Expr1
+ }
+ def ResultExpr: R0 = rule {
+ (Bindings | optional(K.W("implicit")) ~ Id | "_") ~ K.W("=>") ~ Block | Expr1
+ }
+ def Enumerators: R0 = rule { Generator ~ zeroOrMore(Semi ~ Enumerator) ~ WL }
+ def Enumerator: R0 = rule { Generator | Guard | Pattern1 ~ K.O("=") ~ Expr }
+ def Generator: R0 = rule { Pattern1 ~ K.O("<-") ~ Expr ~ optional(WL ~ Guard) }
+ def CaseClauses: R0 = rule { oneOrMore(CaseClause) }
+ def CaseClause: R0 = rule { K.W("case") ~ Pattern ~ optional(Guard) ~ K.O("=>") ~ Block }
+ def Guard: R0 = rule { K.W("if") ~ PostfixExpr }
+ def Pattern: R0 = rule {
+ oneOrMore(Pattern1).separatedBy('|')
+ }
+ def Pattern1: R0 = rule {
+ K.W("_") ~ K.O(":") ~ TypePat | VarId ~ K.O(":") ~ TypePat | Pattern2
+ }
+ def Pattern2: R0 = rule {
+ VarId ~ "@" ~ Pattern3 | Pattern3 | VarId
+ }
+ def Pattern3: R0 = rule {
+ SimplePattern ~ zeroOrMore(Id ~ SimplePattern)
+ }
+ def SimplePattern: R0 = rule {
+ K.W("_") |
+ Literal ~ drop[String] |
+ '(' ~ optional(Patterns) ~ ')' |
+ (
+ StableId ~
+ optional(
+ '(' ~
+ (optional(Patterns ~ ',') ~ optional(VarId ~ '@') ~ K.W("_") ~ '*' | optional(Patterns)) ~
+ ')'
+ )
+ ) |
+ VarId
+ }
+ def Patterns: R0 = rule { K.W("_") ~ '*' | oneOrMore(Pattern).separatedBy(',') }
+
+ def TypeParamClause: R0 = rule { '[' ~ oneOrMore(VariantTypeParam).separatedBy(',') ~ ']' }
+ def FunTypeParamClause: R0 = rule { '[' ~ oneOrMore(TypeParam).separatedBy(',') ~ ']' }
+ def VariantTypeParam: R0 = rule { zeroOrMore(Annotation) ~ optional(anyOf("+-")) ~ TypeParam }
+ def TypeParam: R0 = rule {
+ (Id | K.W("_")) ~
+ optional(TypeParamClause) ~
+ optional(K.O(">:") ~ Type) ~
+ optional(K.O("<:") ~ Type) ~
+ zeroOrMore(K.O("<%") ~ Type) ~
+ zeroOrMore(K.O(":") ~ Type)
+ }
+ def ParamClauses: R0 = rule { zeroOrMore(ParamClause) ~ optional(optional(Newline) ~ '(' ~ K.W("implicit") ~ Params ~ ')') }
+ def ParamClause: R0 = rule { optional(Newline) ~ '(' ~ optional(Params) ~ ')' }
+ def Params: R0 = rule { zeroOrMore(Param).separatedBy(',') }
+ def Param: R0 = rule { zeroOrMore(Annotation) ~ Id ~ optional(K.O(":") ~ ParamType) ~ optional(K.O("=") ~ Expr) }
+ def ClassParamClauses: R0 = rule { zeroOrMore(ClassParamClause) ~ optional(optional(Newline) ~ '(' ~ K.W("implicit") ~ ClassParam ~ ")") }
+ def ClassParamClause: R0 = rule { optional(Newline) ~ '(' ~ optional(ClassParams) ~ ")" }
+ def ClassParams: R0 = rule { oneOrMore(ClassParam).separatedBy(',') }
+ def ClassParam: R0 = rule { zeroOrMore(Annotation) ~ optional(zeroOrMore(Modifier) ~ (K.W("val") | K.W("var"))) ~ Id ~ K.O(":") ~ ParamType ~ optional(K.O("=") ~ Expr) }
+
+ def Bindings: R0 = rule { '(' ~ zeroOrMore(Binding).separatedBy(',') ~ ')' }
+ def Binding: R0 = rule { (Id | K.W("_")) ~ optional(K.O(":") ~ Type) }
+
+ def Modifier: R0 = rule { LocalModifier | AccessModifier | K.W("override") }
+ def LocalModifier: R0 = rule { K.W("abstract") | K.W("final") | K.W("sealed") | K.W("implicit") | K.W("lazy") }
+ def AccessModifier: R0 = rule { (K.W("private") | K.W("protected")) ~ optional(AccessQualifier) }
+ def AccessQualifier: R0 = rule { '[' ~ (K.W("this") | Id) ~ ']' }
+
+ def Annotation: R0 = rule { '@' ~ SimpleType ~ zeroOrMore(WL ~ ArgumentExprs) }
+ def ConstrAnnotation: R0 = rule { '@' ~ SimpleType ~ ArgumentExprs }
+
+ def TemplateBody: R0 = rule {
+ '{' ~
+ optional(SelfType) ~
+ zeroOrMore(TemplateStat).separatedBy(Semis) ~
+ '}'
+ }
+ def TemplateStat: R0 = rule {
+ Import |
+ zeroOrMore(Annotation ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ (Def | Dcl) |
+ Expr
+ }
+
+ def SelfType: R0 = rule { K.W("this") ~ K.O(":") ~ Type ~ K.O("=>") | Id ~ optional(K.O(":") ~ Type) ~ K.O("=>") }
+
+ def Import: R0 = rule { K.W("import") ~ oneOrMore(ImportExpr).separatedBy(',') }
+
+ def ImportExpr: R0 = rule {
+ StableId ~ optional('.' ~ ("_" | ImportSelectors))
+ }
+ def ImportSelectors: R0 = rule { '{' ~ zeroOrMore(ImportSelector ~ ',') ~ (ImportSelector | K.W("_")) ~ "}" }
+ def ImportSelector: R0 = rule { Id ~ optional(K.O("=>") ~ (Id | K.W("_"))) }
+
+ def Dcl: R0 = rule {
+ K.W("val") ~ ValDcl |
+ K.W("var") ~ VarDcl |
+ K.W("def") ~ FunDcl |
+ K.W("type") ~ zeroOrMore(Newline) ~ TypeDcl
+ }
+ def ValDcl: R0 = rule { Ids ~ K.O(":") ~ Type }
+ def VarDcl: R0 = rule { Ids ~ K.O(":") ~ Type }
+ def FunDcl: R0 = rule { FunSig ~ optional(WL ~ K.O(":") ~ Type) }
+ def FunSig: R0 = rule { Id ~ optional(FunTypeParamClause) ~ ParamClauses }
+ def TypeDcl: R0 = rule {
+ Id ~
+ optional(WL ~ TypeParamClause) ~
+ optional(WL ~ K.O(">:") ~ Type) ~
+ optional(WL ~ K.O("<:") ~ Type)
+ }
+
+ def PatVarDef: R0 = rule { K.W("val") ~ PatDef | K.W("var") ~ VarDef }
+ def Def: R0 = rule { K.W("def") ~ FunDef | K.W("type") ~ zeroOrMore(Newline) ~ TypeDef | PatVarDef | TmplDef }
+ def PatDef: R0 = rule { oneOrMore(Pattern2).separatedBy(',') ~ optional(K.O(":") ~ Type) ~ K.O("=") ~ Expr }
+ def VarDef: R0 = rule { Ids ~ K.O(":") ~ Type ~ K.O("=") ~ K.W("_") | PatDef }
+ def FunDef: R0 = rule {
+ K.W("this") ~ ParamClause ~ ParamClauses ~ (K.O("=") ~ ConstrExpr | optional(Newline) ~ ConstrBlock) |
+ FunSig ~
+ (
+ optional(K.O(":") ~ Type) ~ K.O("=") ~ optional(K.W("macro")) ~ Expr |
+ optional(Newline) ~ '{' ~ Block ~ "}"
+ )
+ }
+ def TypeDef: R0 = rule { Id ~ optional(TypeParamClause) ~ K.O("=") ~ Type }
+
+ def TmplDef: R0 = rule {
+ K.W("trait") ~ TraitDef |
+ optional(K.W("case")) ~ (K.W("class") ~ ClassDef |
+ K.W("object") ~ ObjectDef)
+ }
+ def ClassDef: R0 = rule {
+ Id ~
+ optional(TypeParamClause) ~
+ zeroOrMore(ConstrAnnotation) ~
+ optional(AccessModifier) ~
+ ClassParamClauses ~
+ ClassTemplateOpt
+ }
+ def TraitDef: R0 = rule { Id ~ optional(TypeParamClause) ~ TraitTemplateOpt }
+ def ObjectDef: R0 = rule { Id ~ ClassTemplateOpt }
+ def ClassTemplateOpt: R0 = rule {
+ WL ~ K.W("extends") ~ ClassTemplate |
+ optional(WL ~ optional(K.W("extends")) ~ TemplateBody)
+ }
+ def TraitTemplateOpt: R0 = rule { K.W("extends") ~ TraitTemplate | optional(optional(K.W("extends")) ~ TemplateBody) }
+ def ClassTemplate: R0 = rule {
+ optional(EarlyDefs) ~
+ ClassParents ~
+ optional(WL ~ TemplateBody)
+ }
+
+ def TraitTemplate: R0 = rule {
+ optional(EarlyDefs) ~ TraitParents ~ optional(TemplateBody)
+ }
+ def ClassParents: R0 = rule {
+ Constr ~ zeroOrMore(WL ~ K.W("with") ~ AnnotType)
+ }
+ def TraitParents: R0 = rule {
+ AnnotType ~ zeroOrMore(WL ~ K.W("with") ~ AnnotType)
+ }
+ def Constr: R0 = rule {
+ AnnotType ~ zeroOrMore(WL ~ ArgumentExprs)
+ }
+ def EarlyDefs: R0 = rule {
+ '{' ~ optional(oneOrMore(EarlyDef).separatedBy(Semis)) ~ '}' ~ K.W("with")
+ }
+ def EarlyDef: R0 = rule {
+ zeroOrMore(Annotation ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ PatVarDef
+ }
+ def ConstrExpr: R0 = rule { ConstrBlock | SelfInvocation }
+ def ConstrBlock: R0 = rule { '{' ~ SelfInvocation ~ zeroOrMore(Semis ~ BlockStat) ~ '}' }
+ def SelfInvocation: R0 = rule { K.W("this") ~ oneOrMore(ArgumentExprs) }
+
+ def TopStatSeq: R0 = rule { oneOrMore(TopStat).separatedBy(Semis) }
+ def TopStat: R0 = rule {
+ Packaging |
+ PackageObject |
+ Import |
+ zeroOrMore(Annotation ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ TmplDef
+ }
+ def Packaging: R0 = rule { K.W("package") ~ QualId ~ '{' ~ TopStatSeq ~ '}' }
+ def PackageObject: R0 = rule { K.W("package") ~ K.W("object") ~ ObjectDef }
+ def TopPackageSeq: R0 = rule{
+ oneOrMore(K.W("package") ~ QualId).separatedBy(Semis)
+ }
+ def CompilationUnit: Rule1[String] = rule {
+ capture(
+ pr("CompulationUnit 0") ~
+ optional(Semis) ~
+ pr("CompulationUnit 1") ~
+ (TopPackageSeq ~ Semis ~ TopStatSeq | TopPackageSeq | TopStatSeq) ~
+ WL
+ )
+ }
+}
diff --git a/scalatexApi/src/main/scala/scalaparser/syntax/Basic.scala b/scalaParser/src/main/scala/scalaParser/syntax/Basic.scala
index 157b0bb..8d3232a 100644
--- a/scalatexApi/src/main/scala/scalaparser/syntax/Basic.scala
+++ b/scalaParser/src/main/scala/scalaParser/syntax/Basic.scala
@@ -1,4 +1,4 @@
-package scalaparser
+package scalaParser
package syntax
import acyclic.file
import org.parboiled2._
diff --git a/scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala b/scalaParser/src/main/scala/scalaParser/syntax/Identifiers.scala
index d10c7ea..4bc972f 100644
--- a/scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala
+++ b/scalaParser/src/main/scala/scalaParser/syntax/Identifiers.scala
@@ -1,4 +1,4 @@
-package scalaparser
+package scalaParser
package syntax
import acyclic.file
import org.parboiled2._
diff --git a/scalatexApi/src/main/scala/scalaparser/syntax/Literals.scala b/scalaParser/src/main/scala/scalaParser/syntax/Literals.scala
index 3222254..b8342e2 100644
--- a/scalatexApi/src/main/scala/scalaparser/syntax/Literals.scala
+++ b/scalaParser/src/main/scala/scalaParser/syntax/Literals.scala
@@ -1,4 +1,4 @@
-package scalaparser
+package scalaParser
package syntax
import acyclic.file
import org.parboiled2._
diff --git a/scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala b/scalaParser/src/test/scala/scalaParser/SyntaxTest.scala
index 9559154..d33ead0 100644
--- a/scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala
+++ b/scalaParser/src/test/scala/scalaParser/SyntaxTest.scala
@@ -1,4 +1,4 @@
-package scalaparser
+package scalaParser
import org.parboiled2.ParseError
import utest._
@@ -9,11 +9,12 @@ import scala.util.{Failure, Success}
object SyntaxTest extends TestSuite{
def check[T](input: String) = {
+ println("Checking...")
new ScalaSyntax(input).CompilationUnit.run() match{
case Failure(f: ParseError) =>
println(f.position)
println(f.formatExpectedAsString)
-// println(f.formatTraces)
+ println(f.formatTraces)
throw new Exception(f.position + "\t" + f.formatTraces)
case Success(parsed) =>
assert(parsed == input)
@@ -27,14 +28,16 @@ object SyntaxTest extends TestSuite{
)
* - check(
- """
- |package torimatomeru
+ """package torimatomeru
|
- |import org.parboiled2.ParseError
- |import utest._
- |import utest.framework.Test
+ |package lols
+ """.stripMargin
+ )
+ * - check(
+ """package torimatomeru
+ |import a
+ |import b
""".stripMargin
-
)
* - check(
"""
@@ -70,16 +73,9 @@ object SyntaxTest extends TestSuite{
* - check(
"""
|object SyntaxTest extends TestSuite{
- | def check[T](input: String) = {
- | new ScalaSyntax(input).CompilationUnit.run() match{
- | case Failure(f: ParseError) =>
- | println(f.position)
- | println(f.formatExpectedAsString)
- | println(f.formatTraces)
- | throw new Exception(f.position + "\t" + f.formatTraces)
- | case Success(parsed) =>
- | assert(parsed == input)
- | }
+ | {
+ | println
+ | throw 1
| }
|}
""".stripMargin
@@ -220,6 +216,15 @@ object SyntaxTest extends TestSuite{
""".stripMargin
)
* - check(
+ """
+ |object L{
+ | a b c
+ | d = 1
+ |}
+ """.stripMargin
+ )
+
+ * - check(
"""/* __ *\
|** ________ ___ / / ___ __ ____ Scala.js CLI **
|** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2014, LAMP/EPFL **
@@ -360,17 +365,40 @@ object SyntaxTest extends TestSuite{
|}
""".stripMargin
)
+ * - check(
+ """
+ |trait Basic {
+ | b match {
+ | case C => true; case _ => false
+ | }
+ |}
+ """.stripMargin
+ )
+ * - check(
+ """trait Basic {
+ | !a.b
+ |}
+ """.stripMargin
+ )
+ * - check(
+ """
+ |class Parser {
+ | {() => }
+ |}
+ |
+ """.stripMargin
+ )
}
def checkFile(path: String) = check(io.Source.fromFile(path).mkString)
'file{
+ * - checkFile("test.txt")
+ * - checkFile("scalaParser/src/main/scala/scalaParser/syntax/Basic.scala")
+ * - checkFile("scalaParser/src/main/scala/scalaParser/syntax/Identifiers.scala")
+ * - checkFile("scalaParser/src/main/scala/scalaParser/syntax/Literals.scala")
+ * - checkFile("scalaParser/src/main/scala/scalaParser/ScalaSyntax.scala")
- * - checkFile("scalatexApi/src/main/scala/scalaparser/syntax/Basic.scala")
- * - checkFile("scalatexApi/src/main/scala/scalaparser/syntax/Identifiers.scala")
- * - checkFile("scalatexApi/src/main/scala/scalaparser/syntax/Literals.scala")
- * - checkFile("scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala")
-
- * - checkFile("scalatexApi/src/test/scala/scalaparser/SyntaxTest.scala")
+ * - checkFile("scalaParser/src/test/scala/scalaParser/SyntaxTest.scala")
* - checkFile("scalatexApi/src/main/scala/scalatex/stages/Compiler.scala")
@@ -386,16 +414,16 @@ object SyntaxTest extends TestSuite{
* - checkFile("scalatexPlugin/src/main/scala/scalatex/ScalaTexPlugin.scala")
}
- 'omg{
- val root = new java.io.File("../scala-js/")
- def listFiles(s: java.io.File): Iterator[String] = {
- val (dirs, files) = s.listFiles().toIterator.partition(_.isDirectory)
- files.map(_.getPath) ++ dirs.flatMap(listFiles)
- }
- for(f <- listFiles(root).filter(_.endsWith(".scala"))){
- println("CHECKING " + f)
- checkFile(f)
- }
- }
+// 'omg{
+// val root = new java.io.File("../scala-js/")
+// def listFiles(s: java.io.File): Iterator[String] = {
+// val (dirs, files) = s.listFiles().toIterator.partition(_.isDirectory)
+// files.map(_.getPath) ++ dirs.flatMap(listFiles)
+// }
+// for(f <- listFiles(root).filter(_.endsWith(".scala"))){
+// println("CHECKING " + f)
+// checkFile(f)
+// }
+// }
}
}
diff --git a/scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala b/scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala
deleted file mode 100644
index 83e0b90..0000000
--- a/scalatexApi/src/main/scala/scalaparser/ScalaSyntax.scala
+++ /dev/null
@@ -1,412 +0,0 @@
-package scalaparser
-import acyclic.file
-import language.implicitConversions
-import syntax._
-import org.parboiled2._
-
-/**
- * Parser for Scala syntax.
- *
- * The `G` parameter that gets passed in to each rule stands for
- * "Greedy", and determines whether or not that rule is to consume
- * newlines after the last terminal in that rule. We need to pass it
- * everywhere so it can go all the way to the last terminal deep
- * inside the parse tree, which can then decide whether or not to
- * consume whitespace.
- *
- * The vast majority of terminals will consume newlines; only rules
- * which occur in {} blocks won't have their terminals consume newlines,
- * and only the *last* terminal in the rule will be affected.
- * That's why the parser does terminals-consume-newlines-by-default,
- * and leaves it up to the dev to thread the `G` variable where-ever
- * we want the opposite behavior.
- */
-class ScalaSyntax(val input: ParserInput) extends Parser with Basic with Identifiers with Literals {
- // Aliases for common things. These things are used in almost every parser
- // in the file, so it makes sense to keep them short.
- type B = Boolean
- val t = true
- type R0 = Rule0
- /**
- * Parses all whitespace, excluding newlines. This is only
- * really useful in e.g. {} blocks, where we want to avoid
- * capturing newlines so semicolon-inference would work
- */
- def WS = rule { zeroOrMore(Basic.WhitespaceChar | Literals.Comment) }
-
- /**
- * Parses whitespace, including newlines.
- * This is the default for most things
- */
- def WL = rule{ zeroOrMore(Basic.WhitespaceChar | Literals.Comment | Basic.Newline) }
-
-
- /**
- * Whitespace which captures or doesn't-capture
- * newlines depending on the G that gets passed in
- */
- def W(G: B = t) =
- if (G) WL
- else WS
-
- /**
- * By default, all strings and characters greedily
- * capture all whitespace immediately after the token.
- */
- implicit private[this] def wspStr(s: String): R0 = rule { str(s) ~ WL }
- implicit private[this] def wspChar(s: Char): R0 = rule { ch(s) ~ WL }
-
- /**
- * Most keywords don't just require the correct characters to match,
- * they have to ensure that subsequent characters *don't* match in
- * order for it to be a keyword. This enforces that rule for key-words
- * (W) and key-operators (O) which have different non-match criteria.
- */
- object K {
- def W(s: String) = rule {
- Key.W(s) ~ WL
- }
-
- def O(s: String) = rule {
- Key.O(s) ~ WL
- }
- }
-
- /**
- * Occasionally, you want to decide whether or not to
- * capture newlines based on the context, so use this
- * and pass in G manually.
- */
- def StrW(s: String, G: B): R0 = rule { str(s) ~ W(G) }
-
- def pos = cursor -> cursorChar
-
- /**
- * helper printing function
- */
- def pr(s: String) = rule { run(println(s"LOGGING $cursor: $s")) }
-
- def Id(G: B = t) = rule { Identifiers.Id ~ W(G) }
- def VarId(G: B = t) = rule { Identifiers.VarId ~ W(G) }
- def Literal(G: B = t) = rule { Literals.Literal ~ W(G) }
- def Semi = rule { Basic.Semi ~ WL }
- def Newline = rule { Basic.Newline ~ WL }
-
- def QualId(G: B = t) = rule { oneOrMore(Id(false)) separatedBy '.' ~ W(G) }
- def Ids = rule { oneOrMore(Id()) separatedBy ',' }
-
- def Path(G: B = t): R0 = rule {
- zeroOrMore(Id(G) ~ '.') ~ K.W("this") ~ zeroOrMore(Id(G)).separatedBy('.') |
- StableId(G)
- }
- def StableId(G: B = t): R0 = rule {
- zeroOrMore(Id() ~ '.') ~ (K.W("this") | K.W("super") ~ optional(ClassQualifier)) ~ '.' ~ oneOrMore(Id(G)).separatedBy('.') |
- Id(false) ~ zeroOrMore(WL ~ '.' ~ WL ~ Id(false)) ~ W(G)
- }
-
- def ClassQualifier = rule { '[' ~ Id() ~ ']' }
-
- def Type(G: B = t): R0 = rule {
- FunctionArgTypes ~ K.O("=>") ~ Type(G) | InfixType(false) ~ optional(WL ~ ExistentialClause) ~ W(G)
- }
- def FunctionArgTypes = rule {
- InfixType() | '(' ~ optional(oneOrMore(ParamType) separatedBy ',') ~ ')'
- }
-
- def ExistentialClause = rule { "forSome" ~ '{' ~ oneOrMore(ExistentialDcl(false)).separatedBy(Semi) }
- def ExistentialDcl(G: B = t) = rule { K.W("type") ~ TypeDcl(G) | K.W("val") ~ ValDcl(G) }
-
- def InfixType(G: B = t) = rule {
- CompoundType(false) ~ zeroOrMore(WL ~ Id() ~ optional(Newline) ~ CompoundType(false)) ~ W(G)
- }
- def CompoundType(G: B = t) = rule {
- oneOrMore(AnnotType(false)).separatedBy(WL ~ K.W("with")) ~ optional(Refinement(false)) ~ W(G)
- }
- def AnnotType(G: B = t) = rule {
- SimpleType(false) ~ zeroOrMore(WL ~ Annotation(false)) ~ W(G)
- }
- def SimpleType(G: B = t): R0 = rule {
- BasicType(false) ~
- optional(WL ~ '#' ~ Id(false)) ~
- optional(WL ~ TypeArgs(false)) ~
- W(G)
- }
- def BasicType(G: B = t): R0 = rule {
- '(' ~ Types ~ ')' |
- Path() ~ '.' ~ K.W("type") |
- StableId(G)
- }
- def TypeArgs(G: B = t) = rule { '[' ~ Types ~ StrW("]", G) }
- def Types = rule { oneOrMore(Type()).separatedBy(',') }
- def Refinement(G: B = t) = rule {
- optional(Newline) ~ '{' ~ oneOrMore(RefineStat).separatedBy(Semi) ~ StrW("}", G)
- }
- def RefineStat = rule { "type" ~ TypeDef(false) | Dcl(false) | MATCH }
- def TypePat = rule { CompoundType() }
- def Ascription(G: B = t) = rule {
- ":" ~ ("_" ~ StrW("*", G) | InfixType(G) | oneOrMore(Annotation(G)))
- }
-
- def ParamType = rule { K.O("=>") ~ Type() | Type() ~ "*" | Type() }
-
- def Expr(G: B = t): R0 = rule { (Bindings | optional(K.W("implicit")) ~ Id() | "_") ~ K.O("=>") ~ Expr(G) | Expr1(G) }
- def Expr1(G: B = t): R0 = rule {
- IfCFlow(G) |
- WhileCFlow(G) |
- TryCFlow(G) |
- DoWhileCFlow(G) |
- ForCFlow(G) |
- K.W("throw") ~ Expr(G) |
- K.W("return") ~ optional(Expr(G)) |
- SimpleExpr() ~ K.O("=") ~ Expr(G) |
- PostfixExpr(false) ~ optional("match" ~ '{' ~ CaseClauses ~ StrW("}", false) | Ascription(false)) ~ W(G)
- }
- def IfCFlow(G: B = t) = rule { "if" ~ '(' ~ Expr() ~ ')' ~ zeroOrMore(Newline) ~ Expr(G) ~ optional(optional(Semi) ~ K.W("else") ~ Expr(G)) }
- def WhileCFlow(G: B = t) = rule { "while" ~ '(' ~ Expr() ~ ')' ~ zeroOrMore(Newline) ~ Expr(G) }
- def TryCFlow(G: B = t) = rule {
- K.W("try") ~ Expr(false) ~
- optional(WL ~ K.W("catch") ~ Expr(false)) ~
- optional(WL ~ K.W("finally") ~ Expr(false)) ~
- W(G)
- }
-
- def DoWhileCFlow(G: B = t) = rule { K.W("do") ~ Expr() ~ optional(Semi) ~ "while" ~ '(' ~ Expr() ~ StrW(")", G) }
- def ForCFlow(G: B = t) = rule {
- "for" ~
- ('(' ~ Enumerators ~ ')' | '{' ~ Enumerators ~ '}') ~
- zeroOrMore(Newline) ~
- optional(K.W("yield")) ~
- Expr(G) }
- def PostfixExpr(G: B = t): R0 = rule { InfixExpr(G) ~ optional(Id() ~ optional(Newline)) }
- def InfixExpr(G: B = t): R0 = rule { PrefixExpr(G) ~ zeroOrMore(Id() ~ optional(Newline) ~ PrefixExpr(G)) }
- def PrefixExpr(G: B = t) = rule { optional(anyOf("-+~!")) ~ SimpleExpr(G) }
-
- def SimpleExpr(G: B = t): R0 = rule {
- SimpleExpr1(false) ~
- zeroOrMore(WL ~ ('.' ~ Id(false) | TypeArgs(false) | ArgumentExprs(false))) ~
- optional(WL ~ StrW("_", false)) ~
- W(G)
- }
-
- def SimpleExpr1(G: B = t) = rule{
- K.W("new") ~ (ClassTemplate(G) | TemplateBody(G)) |
- BlockExpr(G) |
- Literal(G) ~ drop[String] |
- Path(G) |
- K.W("_") |
- '(' ~ optional(Exprs) ~ StrW(")", G)
- }
-
-
-
- def Exprs: R0 = rule { oneOrMore(Expr()).separatedBy(',') }
- def ArgumentExprs(G: B = t): R0 = rule {
- '(' ~ optional(Exprs ~ optional(K.O(":") ~ K.W("_") ~ '*')) ~ StrW(")", G) |
- optional(Newline) ~ BlockExpr(G)
- }
-
- def BlockExpr(G: B = t): R0 = rule { '{' ~ (CaseClauses | Block) ~ StrW("}", G) }
- def Block: R0 = rule {
- zeroOrMore(BlockStat ~ Semi) ~ optional(ResultExpr())
- }
-
- def BlockStat: R0 = rule {
- Semi |
- Import(false) |
- zeroOrMore(Annotation(false)) ~ (optional(K.W("implicit") | K.W("lazy")) ~ Def(false) | zeroOrMore(LocalModifier) ~ TmplDef(false)) |
- Expr1(false)
- }
- def ResultExpr(G: B = t): R0 = rule { (Bindings | optional(K.W("implicit")) ~ Id() | "_") ~ K.W("=>") ~ Block | Expr1(t) }
- def Enumerators: R0 = rule { Generator(false) ~ zeroOrMore(Semi ~ Enumerator(false)) ~ WL }
- def Enumerator(G: B = t): R0 = rule { Generator(G) | Guard(G) | Pattern1 ~ K.O("=") ~ Expr(G) }
- def Generator(G: B = t): R0 = rule { Pattern1 ~ K.O("<-") ~ Expr(false) ~ optional(WL ~ Guard(false)) ~ W(G) }
- def CaseClauses: R0 = rule { oneOrMore(CaseClause) }
- def CaseClause: R0 = rule { K.W("case") ~ Pattern ~ optional(Guard(true)) ~ K.O("=>") ~ Block }
- def Guard(G: B = t): R0 = rule { K.W("if") ~ PostfixExpr(G) }
- def Pattern: R0 = rule {
- oneOrMore(Pattern1).separatedBy('|')
- }
- def Pattern1: R0 = rule {
- K.W("_") ~ K.O(":") ~ TypePat | VarId() ~ K.O(":") ~ TypePat | Pattern2
- }
- def Pattern2: R0 = rule {
- VarId() ~ "@" ~ Pattern3 | Pattern3 | VarId()
- }
- def Pattern3: R0 = rule {
- SimplePattern ~ zeroOrMore(Id() ~ SimplePattern)
- }
- def SimplePattern: R0 = rule {
- K.W("_") |
- Literal() ~ drop[String] |
- '(' ~ optional(Patterns) ~ ')' |
- (
- StableId() ~
- optional(
- '(' ~
- (optional(Patterns ~ ',') ~ optional(VarId() ~ '@') ~ K.W("_") ~ '*' | optional(Patterns)) ~
- ')'
- )
- ) |
- VarId()
- }
- def Patterns: R0 = rule { K.W("_") ~ '*' | oneOrMore(Pattern).separatedBy(',') }
-
- def TypeParamClause: R0 = rule { '[' ~ oneOrMore(VariantTypeParam).separatedBy(',') ~ ']' }
- def FunTypeParamClause: R0 = rule { '[' ~ oneOrMore(TypeParam).separatedBy(',') ~ ']' }
- def VariantTypeParam: R0 = rule { zeroOrMore(Annotation()) ~ optional(anyOf("+-")) ~ TypeParam }
- def TypeParam: R0 = rule {
- (Id() | K.W("_")) ~
- optional(TypeParamClause) ~
- optional(K.O(">:") ~ Type()) ~
- optional(K.O("<:") ~ Type()) ~
- zeroOrMore(K.O("<%") ~ Type()) ~
- zeroOrMore(K.O(":") ~ Type())
- }
- def ParamClauses: R0 = rule { zeroOrMore(ParamClause) ~ optional(optional(Newline) ~ '(' ~ K.W("implicit") ~ Params ~ ')') }
- def ParamClause: R0 = rule { optional(Newline) ~ '(' ~ optional(Params) ~ ')' }
- def Params: R0 = rule { zeroOrMore(Param).separatedBy(',') }
- def Param: R0 = rule { zeroOrMore(Annotation()) ~ Id() ~ optional(K.O(":") ~ ParamType) ~ optional(K.O("=") ~ Expr()) }
- def ClassParamClauses(G: B = t): R0 = rule { zeroOrMore(ClassParamClause(G)) ~ optional(optional(Newline) ~ '(' ~ K.W("implicit") ~ ClassParam ~ StrW(")", G)) }
- def ClassParamClause(G: B = t): R0 = rule { optional(Newline) ~ '(' ~ optional(ClassParams) ~ StrW(")", G) }
- def ClassParams: R0 = rule { oneOrMore(ClassParam).separatedBy(',') }
- def ClassParam: R0 = rule { zeroOrMore(Annotation()) ~ optional(zeroOrMore(Modifier) ~ (K.W("val") | K.W("var"))) ~ Id() ~ K.O(":") ~ ParamType ~ optional(K.O("=") ~ Expr()) }
-
- def Bindings: R0 = rule { '(' ~ oneOrMore(Binding).separatedBy(',') ~ ')' }
- def Binding: R0 = rule { (Id() | K.W("_")) ~ optional(K.O(":") ~ Type()) }
-
- def Modifier: R0 = rule { LocalModifier | AccessModifier | K.W("override") }
- def LocalModifier: R0 = rule { K.W("abstract") | K.W("final") | K.W("sealed") | K.W("implicit") | K.W("lazy") }
- def AccessModifier: R0 = rule { (K.W("private") | K.W("protected")) ~ optional(AccessQualifier) }
- def AccessQualifier: R0 = rule { '[' ~ (K.W("this") | Id()) ~ ']' }
-
- def Annotation(G: B = t): R0 = rule { '@' ~ SimpleType(false) ~ zeroOrMore(WL ~ ArgumentExprs(false)) ~ W(G) }
- def ConstrAnnotation: R0 = rule { '@' ~ SimpleType() ~ ArgumentExprs() }
-
- def TemplateBody(G: B = t): R0 = rule {
- WL ~
- '{' ~
- optional(SelfType) ~
- TemplateStat ~
- zeroOrMore(Semi ~ TemplateStat) ~
- WL ~
- StrW("}", G)
- }
- def TemplateStat: R0 = rule {
- Import(false) |
- zeroOrMore(Annotation() ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ (Def(false) | Dcl(false)) |
- Expr(false) |
- MATCH
- }
-
- def SelfType: R0 = rule { K.W("this") ~ K.O(":") ~ Type() ~ K.O("=>") | Id() ~ optional(K.O(":") ~ Type()) ~ K.O("=>") }
-
- def Import(G: B = t): R0 = rule { K.W("import") ~ oneOrMore(ImportExpr(G)).separatedBy(',') }
-
- def ImportExpr(G: B = t): R0 = rule { StableId(G) ~ optional('.' ~ (StrW("_", G) | ImportSelectors(G))) }
- def ImportSelectors(G: B = t): R0 = rule { '{' ~ zeroOrMore(ImportSelector ~ ',') ~ (ImportSelector | K.W("_")) ~ StrW("}", G) }
- def ImportSelector: R0 = rule { Id() ~ optional(K.O("=>") ~ (Id() | K.W("_"))) }
-
- def Dcl(G: B = t): R0 = rule {
- K.W("val") ~ ValDcl(G) |
- K.W("var") ~ VarDcl(G) |
- K.W("def") ~ FunDcl(G) |
- K.W("type") ~ zeroOrMore(Newline) ~ TypeDcl(G)
- }
- def ValDcl(G: B = t): R0 = rule { Ids ~ K.O(":") ~ Type(G) }
- def VarDcl(G: B = t): R0 = rule { Ids ~ K.O(":") ~ Type(G) }
- def FunDcl(G: B = t): R0 = rule { FunSig(false) ~ optional(WL ~ K.O(":") ~ Type(G)) }
- def FunSig(G: B = t): R0 = rule { Id() ~ optional(FunTypeParamClause) ~ ParamClauses }
- def TypeDcl(G: B = t): R0 = rule {
- Id(false) ~
- optional(WL ~ TypeParamClause) ~
- optional(WL ~ K.O(">:") ~ Type(false)) ~
- optional(WL ~ K.O("<:") ~ Type(false)) ~
- W(G)
- }
-
- def PatVarDef(G: B = t): R0 = rule { K.W("val") ~ PatDef(G) | K.W("var") ~ VarDef(G) }
- def Def(G: B = t): R0 = rule { K.W("def") ~ FunDef(G) | K.W("type") ~ zeroOrMore(Newline) ~ TypeDef(G) | PatVarDef(G) | TmplDef(G) }
- def PatDef(G: B = t): R0 = rule { oneOrMore(Pattern2).separatedBy(',') ~ optional(K.O(":") ~ Type()) ~ K.O("=") ~ Expr(G) }
- def VarDef(G: B = t): R0 = rule { Ids ~ K.O(":") ~ Type() ~ K.O("=") ~ K.W("_") | PatDef(G) }
- def FunDef(G: B = t): R0 = rule {
- K.W("this") ~ ParamClause ~ ParamClauses ~ (K.O("=") ~ ConstrExpr | optional(Newline) ~ ConstrBlock) |
- FunSig() ~
- (
- optional(K.O(":") ~ Type()) ~ K.O("=") ~ optional(K.W("macro")) ~ Expr(G) |
- optional(Newline) ~ '{' ~ Block ~ StrW("}", G)
- )
- }
- def TypeDef(G: B = t): R0 = rule { Id() ~ optional(TypeParamClause) ~ K.O("=") ~ Type(G) }
-
- def TmplDef(G: B = t): R0 = rule {
- K.W("trait") ~ TraitDef(G) |
- optional(K.W("case")) ~ (K.W("class") ~ ClassDef(G) |
- K.W("object") ~ ObjectDef(G))
- }
- def ClassDef(G: B = t): R0 = rule {
- Id() ~
- optional(TypeParamClause) ~
- zeroOrMore(ConstrAnnotation) ~
- optional(AccessModifier) ~
- ClassParamClauses(false) ~
- ClassTemplateOpt(false) ~
- W(G)
-
- }
- def TraitDef(G: B = t): R0 = rule { Id() ~ optional(TypeParamClause) ~ TraitTemplateOpt(G) }
- def ObjectDef(G: B = t): R0 = rule { Id() ~ ClassTemplateOpt(G) }
- def ClassTemplateOpt(G: B = t): R0 = rule {
- WL ~ K.W("extends") ~ ClassTemplate(G) |
- optional(WL ~ optional(K.W("extends")) ~ TemplateBody(G))
- }
- def TraitTemplateOpt(G: B = t): R0 = rule { K.W("extends") ~ TraitTemplate(G) | optional(optional(K.W("extends")) ~ TemplateBody(G)) }
- def ClassTemplate(G: B = t): R0 = rule {
- optional(EarlyDefs) ~
- ClassParents(false) ~
- optional(WL ~ TemplateBody(false)) ~
- W(G)
- }
-
- def TraitTemplate(G: B = t): R0 = rule {
- optional(EarlyDefs) ~ TraitParents(false) ~ optional(TemplateBody(false)) ~ W(G)
- }
- def ClassParents(G: B = t): R0 = rule {
- Constr(false) ~ zeroOrMore(WL ~ K.W("with") ~ AnnotType(G)) ~ W(G)
- }
- def TraitParents(G: B = t): R0 = rule {
- AnnotType(false) ~ zeroOrMore(WL ~ K.W("with") ~ AnnotType(false)) ~ W(G)
- }
- def Constr(G: B = t): R0 = rule {
- AnnotType(false) ~ zeroOrMore(WL ~ ArgumentExprs(false)) ~
- W(G)
- }
- def EarlyDefs: R0 = rule {
- '{' ~ optional(oneOrMore(EarlyDef).separatedBy(Semi)) ~ '}' ~ K.W("with")
- }
- def EarlyDef: R0 = rule {
- zeroOrMore(Annotation() ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ PatVarDef(false)
- }
- def ConstrExpr: R0 = rule { ConstrBlock | SelfInvocation }
- def ConstrBlock: R0 = rule { '{' ~ SelfInvocation ~ zeroOrMore(Semi ~ BlockStat) ~ '}' }
- def SelfInvocation: R0 = rule { K.W("this") ~ oneOrMore(ArgumentExprs()) }
-
- def TopStatSeq: R0 = rule { zeroOrMore(TopStat).separatedBy(Semi) }
- def TopStat: R0 = rule {
- Packaging |
- PackageObject(false) |
- Import(false) |
- zeroOrMore(Annotation(false) ~ optional(Newline)) ~ zeroOrMore(Modifier) ~ TmplDef(false) |
- MATCH
- }
- def Packaging: R0 = rule { K.W("package") ~ QualId() ~ '{' ~ TopStatSeq ~ '}' }
- def PackageObject(G: B = t): R0 = rule { K.W("package") ~ K.W("object") ~ ObjectDef(G) }
- def CompilationUnit: Rule1[String] = rule {
- capture(
- WL ~
- zeroOrMore(Semi) ~
- zeroOrMore(K.W("package") ~ QualId(false)).separatedBy(Semi) ~
- TopStatSeq ~
- EOI
- )
- }
-}
diff --git a/scalatexApi/src/main/scala/scalatex/stages/Parser.scala b/scalatexApi/src/main/scala/scalatex/stages/Parser.scala
index 403cb72..0b87d97 100644
--- a/scalatexApi/src/main/scala/scalatex/stages/Parser.scala
+++ b/scalatexApi/src/main/scala/scalatex/stages/Parser.scala
@@ -2,7 +2,7 @@ package scalatex
package stages
import acyclic.file
import org.parboiled2._
-import scalaparser.ScalaSyntax
+import scalaParser.ScalaSyntax
/**
* Parses the input text into a roughly-structured AST. This AST
@@ -15,7 +15,7 @@ object Parser extends ((String, Int) => Ast.Block){
new Parser(input, offset).Body.run().get
}
}
-class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends ScalaSyntax(input) {
+class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends scalaParser.ScalaSyntax(input) {
def offsetCursor = offset + cursor
val txt = input.sliceString(0, input.length)
val indentTable = txt.split('\n').map{ s =>
@@ -41,7 +41,7 @@ class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends Scala
"@" ~ capture(Identifiers.Id | BlockExpr2 | ('(' ~ optional(Exprs) ~ ')'))
}
def Header = rule {
- "@" ~ capture(Def(false) | Import(false))
+ "@" ~ capture(Def | Import)
}
def HeaderBlock: Rule1[Ast.Header] = rule{
@@ -65,7 +65,7 @@ class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends Scala
test(cursorNextIndent() > indent) ~
runSubParser(new Parser(_, cursorNextIndent(), cursor).Body)
}
- def IfHead = rule{ "@" ~ capture("if" ~ "(" ~ Expr() ~ ")") }
+ def IfHead = rule{ "@" ~ capture("if" ~ "(" ~ Expr ~ ")") }
def IfElse1 = rule{
push(offsetCursor) ~ IfHead ~ BraceBlock ~ optional("else" ~ (BraceBlock | IndentBlock))
}
@@ -104,7 +104,7 @@ class Parser(input: ParserInput, indent: Int = 0, offset: Int = 0) extends Scala
def TypeArgs2 = rule { '[' ~ Ws ~ Types ~ ']' }
def ArgumentExprs2 = rule {
'(' ~ Ws ~
- (optional(Exprs ~ ',' ~ Ws) ~ PostfixExpr() ~ ':' ~ Ws ~ '_' ~ Ws ~ '*' ~ Ws | optional(Exprs) ) ~
+ (optional(Exprs ~ ',' ~ Ws) ~ PostfixExpr ~ ':' ~ Ws ~ '_' ~ Ws ~ '*' ~ Ws | optional(Exprs) ) ~
')'
}
def BlockExpr2: Rule0 = rule { '{' ~ Ws ~ (CaseClauses | Block) ~ Ws ~ '}' }
diff --git a/scalatexApi/src/test/scala/scalatex/ParserTests.scala b/scalatexApi/src/test/scala/scalatex/ParserTests.scala
index a4b11b6..9a4ee63 100644
--- a/scalatexApi/src/test/scala/scalatex/ParserTests.scala
+++ b/scalatexApi/src/test/scala/scalatex/ParserTests.scala
@@ -2,7 +2,7 @@ package scalatex
import org.parboiled2._
-import scalaparser.ScalaSyntax
+import scalaParser.ScalaSyntax
import scalatex.stages.{Trim, Parser, Ast}
import scalatex.stages.Ast.Block.{IfElse, For, Text}
diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..a36a573
--- /dev/null
+++ b/test.txt
@@ -0,0 +1,3 @@
+class Parser {
+ {() => }
+}