diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java
index 01fe732d3..ef0db33ff 100644
--- a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java
+++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java
@@ -640,8 +640,10 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
try {
IContainer container = (IContainer)workspaceRoot.findMember(
depJavaProject.getOutputLocation());
- collector.addPath(container.getLocation());
- binCollector.addContainer(container);
+ if (container != null) {
+ collector.addPath(container.getLocation());
+ binCollector.addContainer(container);
+ }
} catch (MalformedURLException e) {
TeaVMEclipsePlugin.logError(e);
}
diff --git a/teavm-samples/pom.xml b/teavm-samples/pom.xml
index a1964207a..6722bef47 100644
--- a/teavm-samples/pom.xml
+++ b/teavm-samples/pom.xml
@@ -36,5 +36,6 @@
teavm-samples-video
teavm-samples-async
teavm-samples-kotlin
+ teavm-samples-scala
\ No newline at end of file
diff --git a/teavm-samples/teavm-samples-scala/.gitignore b/teavm-samples/teavm-samples-scala/.gitignore
new file mode 100644
index 000000000..8bd3a0588
--- /dev/null
+++ b/teavm-samples/teavm-samples-scala/.gitignore
@@ -0,0 +1,4 @@
+/target/
+/.settings/
+/.classpath
+/.project
diff --git a/teavm-samples/teavm-samples-scala/pom.xml b/teavm-samples/teavm-samples-scala/pom.xml
new file mode 100644
index 000000000..e1c2c61a0
--- /dev/null
+++ b/teavm-samples/teavm-samples-scala/pom.xml
@@ -0,0 +1,108 @@
+
+ 4.0.0
+
+ org.teavm
+ teavm-samples
+ 0.4.0-SNAPSHOT
+
+ war
+
+ teavm-samples-scala
+
+ TeaVM Scala example
+ A sample application written in Scala.
+
+
+ 1.8
+ 0.4.0-SNAPSHOT
+ 2.11.7
+ UTF-8
+
+
+
+
+ org.teavm
+ teavm-classlib
+ ${teavm.version}
+ provided
+
+
+ org.teavm
+ teavm-jso
+ ${teavm.version}
+
+
+ org.scala-lang
+ scala-library
+ ${scala.version}
+
+
+
+
+ src/main/scala
+ src/test/scala
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+ 3.2.1
+
+
+
+ compile
+ testCompile
+
+
+
+
+ ${scala.version}
+
+
+
+ org.teavm
+ teavm-maven-plugin
+ ${teavm.version}
+
+
+ web-client
+ prepare-package
+
+ build-javascript
+
+
+ ${project.build.directory}/generated/js/teavm
+ org.teavm.samples.scala.Client
+ SEPARATE
+ false
+ true
+ true
+ true
+
+
+
+
+
+ maven-war-plugin
+ 2.4
+
+
+
+ ${project.build.directory}/generated/js
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/teavm-samples/teavm-samples-scala/src/main/scala/org/teavm/samples/scala/Client.scala b/teavm-samples/teavm-samples-scala/src/main/scala/org/teavm/samples/scala/Client.scala
new file mode 100644
index 000000000..dcac00e16
--- /dev/null
+++ b/teavm-samples/teavm-samples-scala/src/main/scala/org/teavm/samples/scala/Client.scala
@@ -0,0 +1,86 @@
+package org.teavm.samples.scala
+
+import org.teavm.jso.browser.Window
+import org.teavm.jso.dom.html._
+import org.teavm.jso.dom.events._
+
+object Client extends Grammar {
+ def main(args: Array[String]) {
+ var document = HTMLDocument.current
+ var exprElem = document.getElementById("expr").asInstanceOf[HTMLInputElement]
+ var calcElem = document.getElementById("calculate")
+ var resultList = document.getElementById("result-list");
+ calcElem.addEventListener("click", new EventListener[MouseEvent]() {
+ def handleEvent(e : MouseEvent) {
+ additive.parse(exprElem.getValue().toSeq) match {
+ case (None, _) => Window.alert("Error parsing expression");
+ case (Some(x), Nil) => {
+ val resultElem = document.createElement("div")
+ var exprSpan = document.createElement("span");
+ exprSpan.setAttribute("class", "plan")
+ exprSpan.appendChild(document.createTextNode(print(x) + " = "))
+ var resultSpan = document.createElement("span")
+ resultSpan.setAttribute("class", "result")
+ resultSpan.appendChild(document.createTextNode(eval(x).toString()))
+ resultElem.appendChild(exprSpan)
+ resultElem.appendChild(resultSpan)
+ resultList.insertBefore(resultElem, resultList.getFirstChild)
+ }
+ case (_, err) => Window.alert("Error parsing expression: " + err);
+ }
+ }
+ })
+ }
+
+ def eval(expr : Expr) : BigInt = expr match {
+ case Add(a, b) => eval(a) + eval(b)
+ case Subtract(a, b) => eval(a) - eval(b)
+ case Multiply(a, b) => eval(a) * eval(b)
+ case Divide(a, b) => eval(a) * eval(b)
+ case Negate(n) => -eval(n)
+ case Number(v) => v
+ }
+
+ def print(expr : Expr) : String = expr match {
+ case Add(a, b) => "(" + print(a) + " + " + print(b) + ")"
+ case Subtract(a, b) => "(" + print(a) + " - " + print(b) + ")"
+ case Multiply(a, b) => "(" + print(a) + " * " + print(b) + ")"
+ case Divide(a, b) => "(" + print(a) + " / " + print(b) + ")"
+ case Negate(n) => "-" + print(n)
+ case Number(v) => v.toString()
+ }
+
+ def additive = multiplicative ~ ((keyword("+") | keyword("-")) ~ multiplicative).* >> {
+ case (h, t) => t.foldLeft(h) {
+ case (left, ("+", right)) => Add(left, right)
+ case (left, ("-", right)) => Subtract(left, right)
+ }
+ }
+ def multiplicative = primitive ~ ((keyword("*") | keyword("/")) ~ primitive).* >> {
+ case (h, t) => t.foldLeft(h) {
+ case (left, ("*", right)) => Multiply(left, right)
+ case (left, ("/", right)) => Divide(left, right)
+ }
+ }
+ def unary : Rule[Expr] = keyword("-").? ~ primitive >> {
+ case (Some(_), v) => Negate(v)
+ case (None, v) => v
+ }
+ def primitive : Rule[Expr] = Rule.firstOf(number >> Number, group)
+ def group : Rule[Expr] = keyword("(") ~ additive ~ keyword(")") >> { case ((_, result), _) => result }
+
+ def number : Rule[Int] = range('1', '9') ~ range('0', '9').* ~ ws >> {
+ case ((h, t), _) => t.foldLeft(h - '0')((n, c) => n * 10 + (c - '0'))
+ }
+
+ def keyword(str : String) = s(str) ~ ws >> { case (s, _) => s }
+ def ws = s(" ").* >> { case _ => Unit }
+}
+
+sealed abstract class Expr
+case class Number(value : Int) extends Expr
+case class Add(left : Expr, right : Expr) extends Expr
+case class Subtract(left : Expr, right : Expr) extends Expr
+case class Multiply(left : Expr, right : Expr) extends Expr
+case class Divide(left : Expr, right : Expr) extends Expr
+case class Negate(argument : Expr) extends Expr
diff --git a/teavm-samples/teavm-samples-scala/src/main/scala/org/teavm/samples/scala/Grammar.scala b/teavm-samples/teavm-samples-scala/src/main/scala/org/teavm/samples/scala/Grammar.scala
new file mode 100644
index 000000000..732e60d8a
--- /dev/null
+++ b/teavm-samples/teavm-samples-scala/src/main/scala/org/teavm/samples/scala/Grammar.scala
@@ -0,0 +1,112 @@
+package org.teavm.samples.scala
+
+trait Grammar {
+ def rule[T](f : Seq[Char] => (Option[T], Seq[Char])) : Rule[T] = Rule.rule(f)
+ def s(str : String) : Rule[String] = Rule.expect(str)
+ def range(first : Char, last : Char) : Rule[Char] = Rule.range(first, last)
+}
+
+trait Rule[T] {
+ def parse(chars : Seq[Char]) : (Option[T], Seq[Char])
+ def ~[S](other : Rule[S]) : Rule[(T, S)] = Rule.concat(this, other)
+ def ~(other : => String) : Rule[(T, String)] = Rule.concat(this, Rule.expect(other))
+ def |(other : => Rule[T]) : Rule[T] = Rule.firstOf(this, other)
+ def *() : Rule[List[T]] = Rule.unlimited(this)
+ def >>[S](f : T => S) : Rule[S] = Rule.andThen(this, f)
+ def ?() : Rule[Option[T]] = Rule.optional(this)
+
+ def debug(str : String) = Rule.rule { x =>
+ var (result, rem) = parse(x)
+ println(str + ":" + result + ":" + rem)
+ (result, rem)
+ }
+}
+
+object Rule {
+ def rule[T](f : Seq[Char] => (Option[T], Seq[Char])) : Rule[T] = {
+ new Rule[T]() {
+ def parse(chars : Seq[Char]) : (Option[T], Seq[Char]) = f(chars)
+ }
+ }
+ def concat[S, T](left : Rule[S], right : => Rule[T]) : Rule[(S, T)] = {
+ lazy val right2 = right
+ rule(chars => {
+ left.parse(chars) match {
+ case (Some(leftResult), rem) => right2.parse(rem) match {
+ case (Some(rightResult), rem2) => (Some((leftResult, rightResult)), rem2)
+ case (None, rem2) => (None, rem)
+ }
+ case (None, rem) => (None, rem)
+ }
+ })
+ }
+ def unlimited[T](inner : Rule[T]) : Rule[List[T]] = {
+ def iter(chars : Seq[Char]) : (List[T], Seq[Char]) = {
+ inner.parse(chars) match {
+ case (Some(result), rem) => {
+ val (tail, rem2) = iter(rem)
+ (result :: tail, rem2)
+ }
+ case (None, rem) => (Nil, rem)
+ }
+ }
+ rule(chars => {
+ val (result, rem) = iter(chars)
+ (Some(result.reverse), rem)
+ })
+ }
+ def firstOf[T](first : Rule[T], second : => Rule[T]) : Rule[T] = {
+ lazy val second2 = second
+ rule(chars => {
+ first.parse(chars) match {
+ case (Some(result), rem) => (Some(result), rem)
+ case (None, _) => second2.parse(chars) match {
+ case (Some(result), rem) => (Some(result), rem)
+ case (None, _) => (None, chars)
+ }
+ }
+ })
+ }
+ def optional[T](inner : Rule[T]) : Rule[Option[T]] = {
+ rule {
+ chars => inner.parse(chars) match {
+ case (Some(result), rem) => (Some(Some(result)), rem)
+ case (None, rem) => (Some(None), chars)
+ }
+ }
+ }
+ implicit def expect(str : String) : Rule[String] = {
+ def iter(chars : Seq[Char], index : Int) : (Boolean, Seq[Char]) = {
+ if (index == str.length())
+ (true, chars)
+ else chars match {
+ case Seq() => (false, Nil);
+ case c +: tail if c == str.charAt(index) => iter(tail, index + 1)
+ case _ => (false, chars)
+ }
+ }
+ rule(chars => {
+ val (matched, rem) = iter(chars, 0)
+ (matched match {
+ case true => Some(str)
+ case false => None
+ }, rem)
+ })
+ }
+ def andThen[T, S](inner : Rule[T], f : T => S) : Rule[S] = {
+ rule(chars => {
+ inner.parse(chars) match {
+ case (Some(result), rem) => (Some(f(result)), rem)
+ case (None, rem) => (None, rem)
+ }
+ })
+ }
+ def range(first : Char, last : Char) : Rule[Char] = {
+ rule(chars => {
+ chars match {
+ case c +: rem if c >= first && c <= last => (Some(c), rem)
+ case _ => (None, chars)
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/teavm-samples/teavm-samples-scala/src/main/webapp/WEB-INF/web.xml b/teavm-samples/teavm-samples-scala/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 000000000..bfc410b12
--- /dev/null
+++ b/teavm-samples/teavm-samples-scala/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,21 @@
+
+
+
+
\ No newline at end of file
diff --git a/teavm-samples/teavm-samples-scala/src/main/webapp/calculator.css b/teavm-samples/teavm-samples-scala/src/main/webapp/calculator.css
new file mode 100644
index 000000000..ad81f5df0
--- /dev/null
+++ b/teavm-samples/teavm-samples-scala/src/main/webapp/calculator.css
@@ -0,0 +1,65 @@
+html, body {
+ margin: 0;
+ padding: 0
+}
+.top {
+ position: static;
+ padding-top: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 80px;
+ border-bottom-color: rgb(120,120,120);
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ background-color: rgb(230,230,230);
+}
+.expr {
+ width: 100%;
+ display: block
+}
+.calc-panel {
+ margin-right: 80px;
+}
+.calculate {
+ float: right;
+ width: 70px;
+}
+.result-list {
+ position: static;
+ top: 81;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ overflow: auto
+}
+.result-list > div {
+ padding-top: 4px;
+ padding-bottom: 4px;
+ border-bottom-color: rgb(170,170,170);
+ border-bottom-width: 1px;
+ border-bottom-style: dotted;
+}
+.result-list > div > span.result {
+ font-weight: bold;
+}
+.result-list > div:nth-child(1) {
+ color: rgb(0,0,0);
+}
+.result-list > div:nth-child(2) {
+ color: rgb(30,30,30);
+}
+.result-list > div:nth-child(3) {
+ color: rgb(60,60,60);
+}
+.result-list > div:nth-child(4) {
+ color: rgb(90,90,90);
+}
+.result-list > div:nth-child(5) {
+ color: rgb(120,120,120);
+}
+.result-list > div:nth-child(n+6) {
+ color: rgb(150,150,150);
+}
\ No newline at end of file
diff --git a/teavm-samples/teavm-samples-scala/src/main/webapp/index.html b/teavm-samples/teavm-samples-scala/src/main/webapp/index.html
new file mode 100644
index 000000000..c18b19349
--- /dev/null
+++ b/teavm-samples/teavm-samples-scala/src/main/webapp/index.html
@@ -0,0 +1,21 @@
+
+
+
+ Hello Scala
+
+
+
+
+
+
+
+
This calculator supports the following operators: + - * /.
+ Also grouping parentheses supported. Example: (4 + 5) * 6
+
+
+
+
+
+
+
+
\ No newline at end of file