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