diff --git a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java index 6df6aeaf1..cceaf4452 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -50,5 +50,6 @@ public class JCLPlugin implements TeaVMPlugin { host.add(new MethodReference(LambdaMetafactory.class, "metafactory", MethodHandles.Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class, CallSite.class), new LambdaMetafactorySubstitutor()); + host.add(new ScalaHacks()); } } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ScalaHacks.java b/classlib/src/main/java/org/teavm/classlib/impl/ScalaHacks.java new file mode 100644 index 000000000..cf3f4e481 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/ScalaHacks.java @@ -0,0 +1,81 @@ +package org.teavm.classlib.impl; + +import java.util.List; +import java.util.Properties; +import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.BasicBlock; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.FieldHolder; +import org.teavm.model.Instruction; +import org.teavm.model.MethodHolder; +import org.teavm.model.Program; +import org.teavm.model.emit.ProgramEmitter; +import org.teavm.model.instructions.ConstructInstruction; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.PutFieldInstruction; + +/** + * + * @author Alexey Andreev + */ +public class ScalaHacks implements ClassHolderTransformer { + private static final String ATTR_NAME_CLASS = "java.util.jar.Attributes$Name"; + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { + switch (cls.getName()) { + case "scala.util.PropertiesTrait$class": + transformPropertiesTrait(cls, innerSource); + break; + case "scala.util.Properties$": + transformProperties(cls); + break; + } + } + + private void transformPropertiesTrait(ClassHolder cls, ClassReaderSource innerSource) { + for (MethodHolder method : cls.getMethods()) { + if (method.getName().equals("scalaProps")) { + ProgramEmitter pe = ProgramEmitter.create(method, innerSource); + pe.construct(Properties.class).returnValue(); + } + } + } + + private void transformProperties(ClassHolder cls) { + for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) { + if (field.getName().equals("ScalaCompilerVersion")) { + cls.removeField(field); + } + } + for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { + if (method.getName().equals("ScalaCompilerVersion")) { + cls.removeMethod(method); + } else if (method.getName().equals("")) { + Program program = method.getProgram(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + List instructions = block.getInstructions(); + for (int j = 0; j < instructions.size(); ++j) { + Instruction insn = instructions.get(j); + if (insn instanceof InvokeInstruction) { + if (((InvokeInstruction) insn).getMethod().getClassName().equals(ATTR_NAME_CLASS)) { + instructions.remove(j--); + } + } else if (insn instanceof PutFieldInstruction) { + if (((PutFieldInstruction) insn).getField().getFieldName().equals("ScalaCompilerVersion")) { + instructions.remove(j--); + } + } else if (insn instanceof ConstructInstruction) { + ConstructInstruction cons = (ConstructInstruction) insn; + if (cons.getType().equals(ATTR_NAME_CLASS)) { + cons.setType("java.lang.Object"); + } + } + } + } + } + } + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TProperties.java b/classlib/src/main/java/org/teavm/classlib/java/util/TProperties.java new file mode 100644 index 000000000..e8ae779d1 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TProperties.java @@ -0,0 +1,383 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.util; + +import org.teavm.classlib.java.io.TBufferedInputStream; +import org.teavm.classlib.java.io.TIOException; +import org.teavm.classlib.java.io.TInputStream; +import org.teavm.classlib.java.io.TOutputStream; +import org.teavm.classlib.java.io.TOutputStreamWriter; +import org.teavm.classlib.java.io.TPrintStream; +import org.teavm.classlib.java.io.TWriter; +import org.teavm.classlib.java.lang.TString; + +/** + * + * @author Alexey Andreev + */ +public class TProperties extends THashtable { + /** + * The default values for keys not found in this {@code Properties} + * instance. + */ + protected TProperties defaults; + + private static final int NONE = 0, SLASH = 1, UNICODE = 2, CONTINUE = 3, KEY_DONE = 4, IGNORE = 5; + + public TProperties() { + super(); + } + + public TProperties(TProperties properties) { + defaults = properties; + } + + private void dumpString(StringBuilder buffer, String string, boolean isKey) { + int index = 0, length = string.length(); + if (!isKey && index < length && string.charAt(index) == ' ') { + buffer.append("\\ "); //$NON-NLS-1$ + index++; + } + + for (; index < length; index++) { + char ch = string.charAt(index); + switch (ch) { + case '\t': + buffer.append("\\t"); + break; + case '\n': + buffer.append("\\n"); + break; + case '\f': + buffer.append("\\f"); + break; + case '\r': + buffer.append("\\r"); + break; + default: + if ("\\#!=:".indexOf(ch) >= 0 || (isKey && ch == ' ')) { + buffer.append('\\'); + } + if (ch >= ' ' && ch <= '~') { + buffer.append(ch); + } else { + buffer.append(toHexaDecimal(ch)); + } + } + } + } + + private char[] toHexaDecimal(final int ch) { + char[] hexChars = { '\\', 'u', '0', '0', '0', '0' }; + int hexChar, index = hexChars.length, copyOfCh = ch; + do { + hexChar = copyOfCh & 15; + if (hexChar > 9) { + hexChar = hexChar - 10 + 'A'; + } else { + hexChar += '0'; + } + hexChars[--index] = (char) hexChar; + } while ((copyOfCh >>>= 4) != 0); + return hexChars; + } + + public String getProperty(String name) { + Object result = super.get(name); + String property = result instanceof String ? (String) result : null; + if (property == null && defaults != null) { + property = defaults.getProperty(name); + } + return property; + } + + public String getProperty(String name, String defaultValue) { + Object result = super.get(name); + String property = result instanceof String ? (String) result : null; + if (property == null && defaults != null) { + property = defaults.getProperty(name); + } + if (property == null) { + return defaultValue; + } + return property; + } + + public void list(TPrintStream out) { + if (out == null) { + throw new NullPointerException(); + } + StringBuilder buffer = new StringBuilder(80); + TEnumeration keys = propertyNames(); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + buffer.append(key); + buffer.append('='); + String property = (String) super.get(key); + TProperties def = defaults; + while (property == null) { + property = (String) def.get(key); + def = def.defaults; + } + if (property.length() > 40) { + buffer.append(property.substring(0, 37)); + buffer.append("..."); //$NON-NLS-1$ + } else { + buffer.append(property); + } + out.println(TString.wrap(buffer.toString())); + buffer.setLength(0); + } + } + + @SuppressWarnings("fallthrough") + public synchronized void load(TInputStream in) throws TIOException { + if (in == null) { + throw new NullPointerException(); + } + int mode = NONE, unicode = 0, count = 0; + char nextChar, buf[] = new char[40]; + int offset = 0, keyLength = -1, intVal; + boolean firstChar = true; + TBufferedInputStream bis = new TBufferedInputStream(in); + + while (true) { + intVal = bis.read(); + if (intVal == -1) { + // if mode is UNICODE but has less than 4 hex digits, should + // throw an IllegalArgumentException + if (mode == UNICODE && count < 4) { + throw new IllegalArgumentException("Invalid Unicode sequence: expected format \\uxxxx"); + } + // if mode is SLASH and no data is read, should append '\u0000' + // to buf + if (mode == SLASH) { + buf[offset++] = '\u0000'; + } + break; + } + nextChar = (char) (intVal & 0xff); + + if (offset == buf.length) { + char[] newBuf = new char[buf.length * 2]; + System.arraycopy(buf, 0, newBuf, 0, offset); + buf = newBuf; + } + if (mode == UNICODE) { + int digit = Character.digit(nextChar, 16); + if (digit >= 0) { + unicode = (unicode << 4) + digit; + if (++count < 4) { + continue; + } + } else if (count <= 4) { + throw new IllegalArgumentException("Invalid Unicode sequence: illegal character"); + } + mode = NONE; + buf[offset++] = (char) unicode; + if (nextChar != '\n') { + continue; + } + } + if (mode == SLASH) { + mode = NONE; + switch (nextChar) { + case '\r': + mode = CONTINUE; // Look for a following \n + continue; + case '\n': + mode = IGNORE; // Ignore whitespace on the next line + continue; + case 'b': + nextChar = '\b'; + break; + case 'f': + nextChar = '\f'; + break; + case 'n': + nextChar = '\n'; + break; + case 'r': + nextChar = '\r'; + break; + case 't': + nextChar = '\t'; + break; + case 'u': + mode = UNICODE; + unicode = count = 0; + continue; + } + } else { + switch (nextChar) { + case '#': + case '!': + if (firstChar) { + while (true) { + intVal = bis.read(); + if (intVal == -1) { + break; + } + // & 0xff not required + nextChar = (char) intVal; + if (nextChar == '\r' || nextChar == '\n') { + break; + } + } + continue; + } + break; + case '\n': + if (mode == CONTINUE) { // Part of a \r\n sequence + mode = IGNORE; // Ignore whitespace on the next line + continue; + } + // fall into the next case + case '\r': + mode = NONE; + firstChar = true; + if (offset > 0 || (offset == 0 && keyLength == 0)) { + if (keyLength == -1) { + keyLength = offset; + } + String temp = new String(buf, 0, offset); + put(temp.substring(0, keyLength), temp + .substring(keyLength)); + } + keyLength = -1; + offset = 0; + continue; + case '\\': + if (mode == KEY_DONE) { + keyLength = offset; + } + mode = SLASH; + continue; + case ':': + case '=': + if (keyLength == -1) { // if parsing the key + mode = NONE; + keyLength = offset; + continue; + } + break; + } + if (nextChar < 256 && Character.isWhitespace(nextChar)) { + if (mode == CONTINUE) { + mode = IGNORE; + } + // if key length == 0 or value length == 0 + if (offset == 0 || offset == keyLength || mode == IGNORE) { + continue; + } + if (keyLength == -1) { // if parsing the key + mode = KEY_DONE; + continue; + } + } + if (mode == IGNORE || mode == CONTINUE) { + mode = NONE; + } + } + firstChar = false; + if (mode == KEY_DONE) { + keyLength = offset; + mode = NONE; + } + buf[offset++] = nextChar; + } + if (keyLength == -1 && offset > 0) { + keyLength = offset; + } + if (keyLength >= 0) { + String temp = new String(buf, 0, offset); + put(temp.substring(0, keyLength), temp.substring(keyLength)); + } + } + + public TEnumeration propertyNames() { + THashtable selected = new THashtable<>(); + selectProperties(selected); + return selected.keys(); + } + + private void selectProperties(THashtable selected) { + if(defaults != null) { + defaults.selectProperties(selected); + } + selected.putAll(this); + } + + @Deprecated + public void save(TOutputStream out, String comment) { + try { + store(out, comment); + } catch (TIOException e) { + } + } + + public Object setProperty(String name, String value) { + return put(name, value); + } + + public synchronized void store(TOutputStream out, String comments) throws TIOException { + TOutputStreamWriter writer = new TOutputStreamWriter(out, "ISO8859_1"); + if (comments != null) { + writeComments(writer, comments); + } + writer.write('#'); + writer.write(new TDate().toString()); + writer.write("\n"); + + StringBuilder buffer = new StringBuilder(200); + for (TIterator> iter = entrySet().iterator(); iter.hasNext();) { + TMap.Entry entry = iter.next(); + String key = (String) entry.getKey(); + dumpString(buffer, key, true); + buffer.append('='); + dumpString(buffer, (String) entry.getValue(), false); + buffer.append("\n"); + writer.write(buffer.toString()); + buffer.setLength(0); + } + writer.flush(); + } + + private void writeComments(TWriter writer, String comments) throws TIOException { + writer.write('#'); + char[] chars = comments.toCharArray(); + for (int index = 0; index < chars.length; index++) { + if (chars[index] == '\r' || chars[index] == '\n') { + int indexPlusOne = index + 1; + if (chars[index] == '\r' && indexPlusOne < chars.length + && chars[indexPlusOne] == '\n') { + // "\r\n" + continue; + } + writer.write("\n"); + if (indexPlusOne < chars.length + && (chars[indexPlusOne] == '#' || chars[indexPlusOne] == '!')) { + // return char with either '#' or '!' afterward + continue; + } + writer.write('#'); + } else { + writer.write(chars[index]); + } + } + writer.write("\n"); + } +} diff --git a/jso/apis/src/main/java/org/teavm/jso/dom/html/HTMLDocument.java b/jso/apis/src/main/java/org/teavm/jso/dom/html/HTMLDocument.java index b14474148..335e5775c 100644 --- a/jso/apis/src/main/java/org/teavm/jso/dom/html/HTMLDocument.java +++ b/jso/apis/src/main/java/org/teavm/jso/dom/html/HTMLDocument.java @@ -15,6 +15,7 @@ */ package org.teavm.jso.dom.html; +import java.util.function.Consumer; import org.teavm.jso.JSProperty; import org.teavm.jso.browser.Window; import org.teavm.jso.dom.events.EventTarget; @@ -32,6 +33,12 @@ public interface HTMLDocument extends Document, EventTarget { @Override HTMLElement createElement(String tagName); + default HTMLElement createElement(String tagName, Consumer consumer) { + HTMLElement result = createElement(tagName); + consumer.accept(result); + return result; + } + @Override HTMLElement getElementById(String elementId); diff --git a/jso/apis/src/main/java/org/teavm/jso/dom/html/HTMLElement.java b/jso/apis/src/main/java/org/teavm/jso/dom/html/HTMLElement.java index 8bdac4d85..812ceae93 100644 --- a/jso/apis/src/main/java/org/teavm/jso/dom/html/HTMLElement.java +++ b/jso/apis/src/main/java/org/teavm/jso/dom/html/HTMLElement.java @@ -15,6 +15,7 @@ */ package org.teavm.jso.dom.html; +import java.util.function.Consumer; import org.teavm.jso.JSProperty; import org.teavm.jso.dom.css.ElementCSSInlineStyle; import org.teavm.jso.dom.events.EventTarget; @@ -110,4 +111,34 @@ public interface HTMLElement extends Element, ElementCSSInlineStyle, EventTarget void setInnerHTML(String content); TextRectangle getBoundingClientRect(); + + default HTMLElement withAttr(String name, String value) { + setAttribute(name, value); + return this; + } + + default HTMLElement withChild(String tagName) { + HTMLElement result = getOwnerDocument().createElement(tagName); + appendChild(result); + return result; + } + + default HTMLElement withChild(String tagName, Consumer consumer) { + HTMLElement result = getOwnerDocument().createElement(tagName); + appendChild(result); + consumer.accept(result); + return result; + } + + default HTMLElement clear() { + while (getLastChild() != null) { + removeChild(getLastChild()); + } + return this; + } + + default HTMLElement withText(String content) { + clear().appendChild(getOwnerDocument().createTextNode(content)); + return this; + } } diff --git a/jso/apis/src/main/java/org/teavm/jso/dom/xml/Node.java b/jso/apis/src/main/java/org/teavm/jso/dom/xml/Node.java index e872bef00..7caf95add 100644 --- a/jso/apis/src/main/java/org/teavm/jso/dom/xml/Node.java +++ b/jso/apis/src/main/java/org/teavm/jso/dom/xml/Node.java @@ -103,4 +103,10 @@ public interface Node extends JSObject { @JSProperty Document getOwnerDocument(); + + default void delete() { + if (getParentNode() != null) { + getParentNode().removeChild(this); + } + } } diff --git a/samples/scala/src/main/scala/org/teavm/samples/scala/Calculator.scala b/samples/scala/src/main/scala/org/teavm/samples/scala/Calculator.scala new file mode 100644 index 000000000..9606b60c8 --- /dev/null +++ b/samples/scala/src/main/scala/org/teavm/samples/scala/Calculator.scala @@ -0,0 +1,59 @@ +package org.teavm.samples.scala + +import org.teavm.samples.scala.Grammar._ + +object Calculator { + 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 parse(str : Seq[Char]) = additive.parse(str) + + 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 = unary ~ ((keyword("*") | keyword("/")) ~ unary).* >> { + 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 \ No newline at end of file diff --git a/samples/scala/src/main/scala/org/teavm/samples/scala/Client.scala b/samples/scala/src/main/scala/org/teavm/samples/scala/Client.scala index dcac00e16..3c7bd63a1 100644 --- a/samples/scala/src/main/scala/org/teavm/samples/scala/Client.scala +++ b/samples/scala/src/main/scala/org/teavm/samples/scala/Client.scala @@ -1,86 +1,30 @@ package org.teavm.samples.scala +import org.teavm.samples.scala.DOM._ +import org.teavm.samples.scala.Calculator.parse +import org.teavm.samples.scala.Calculator.print +import org.teavm.samples.scala.Calculator.eval import org.teavm.jso.browser.Window import org.teavm.jso.dom.html._ import org.teavm.jso.dom.events._ -object Client extends Grammar { +object Client { 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); + var doc = HTMLDocument.current + var exprElem = doc.getElementById("expr").asInstanceOf[HTMLInputElement] + var calcElem = doc.getElementById("calculate") + var resultList = doc.getElementById("result-list"); + calcElem.addEventListener("click", (e : MouseEvent) => { + parse(exprElem.getValue().toSeq) match { + case (None, _) => Window.alert("Error parsing expression"); + case (Some(x), Nil) => { + resultList.insertBefore(doc.createElement("div", (elem : HTMLElement) => { + elem.withChild("span").withAttr("class", "plan").withText(print(x) + " = ") + elem.withChild("span").withAttr("class", "result").withText(eval(x).toString) + }), 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/samples/scala/src/main/scala/org/teavm/samples/scala/DOM.scala b/samples/scala/src/main/scala/org/teavm/samples/scala/DOM.scala new file mode 100644 index 000000000..6a5224735 --- /dev/null +++ b/samples/scala/src/main/scala/org/teavm/samples/scala/DOM.scala @@ -0,0 +1,18 @@ +package org.teavm.samples.scala + +import org.teavm.jso.dom.events._ +import java.util.function.Consumer + +object DOM { + implicit def toEventListener[T <: Event](f : T => Any) : EventListener[T] = { + new EventListener[T]() { + def handleEvent(e : T) = f(e) + } + } + + implicit def toConsumer[T](f : T => Any) : Consumer[T] = { + new Consumer[T]() { + def accept(e : T) = f(e) + } + } +} \ No newline at end of file diff --git a/samples/scala/src/main/scala/org/teavm/samples/scala/Grammar.scala b/samples/scala/src/main/scala/org/teavm/samples/scala/Grammar.scala index 732e60d8a..4f926950c 100644 --- a/samples/scala/src/main/scala/org/teavm/samples/scala/Grammar.scala +++ b/samples/scala/src/main/scala/org/teavm/samples/scala/Grammar.scala @@ -1,6 +1,6 @@ package org.teavm.samples.scala -trait Grammar { +object 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) @@ -23,21 +23,16 @@ trait Rule[T] { } 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 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) + rule(chars => left.parse(chars) match { + case (Some(leftResult), rem) => right.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]] = { @@ -56,24 +51,19 @@ object Rule { }) } def firstOf[T](first : Rule[T], second : => Rule[T]) : Rule[T] = { - lazy val second2 = second - rule(chars => { - first.parse(chars) match { + rule(chars => first.parse(chars) match { + case (Some(result), rem) => (Some(result), rem) + case (None, _) => second.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) - } + 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) - } - } + 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]) = { @@ -94,19 +84,15 @@ object Rule { }) } 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) - } + 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) - } + rule(chars => chars match { + case c +: rem if c >= first && c <= last => (Some(c), rem) + case _ => (None, chars) }) } } \ No newline at end of file