mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Add hacks for scala classlib. Add DOM method for convenience. Improve
Scala example
This commit is contained in:
parent
219fab22ef
commit
a4e41fc6be
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("<init>")) {
|
||||
Program program = method.getProgram();
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
List<Instruction> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Object, Object> {
|
||||
/**
|
||||
* 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<Object, Object> selected = new THashtable<>();
|
||||
selectProperties(selected);
|
||||
return selected.keys();
|
||||
}
|
||||
|
||||
private void selectProperties(THashtable<Object, Object> 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<TMap.Entry<Object, Object>> iter = entrySet().iterator(); iter.hasNext();) {
|
||||
TMap.Entry<Object, Object> 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");
|
||||
}
|
||||
}
|
|
@ -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<HTMLElement> consumer) {
|
||||
HTMLElement result = createElement(tagName);
|
||||
consumer.accept(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
HTMLElement getElementById(String elementId);
|
||||
|
||||
|
|
|
@ -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<HTMLElement> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,4 +103,10 @@ public interface Node extends JSObject {
|
|||
|
||||
@JSProperty
|
||||
Document getOwnerDocument();
|
||||
|
||||
default void delete() {
|
||||
if (getParentNode() != null) {
|
||||
getParentNode().removeChild(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user