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,
|
host.add(new MethodReference(LambdaMetafactory.class, "metafactory", MethodHandles.Lookup.class,
|
||||||
String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class,
|
String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class,
|
||||||
CallSite.class), new LambdaMetafactorySubstitutor());
|
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;
|
package org.teavm.jso.dom.html;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
import org.teavm.jso.JSProperty;
|
import org.teavm.jso.JSProperty;
|
||||||
import org.teavm.jso.browser.Window;
|
import org.teavm.jso.browser.Window;
|
||||||
import org.teavm.jso.dom.events.EventTarget;
|
import org.teavm.jso.dom.events.EventTarget;
|
||||||
|
@ -32,6 +33,12 @@ public interface HTMLDocument extends Document, EventTarget {
|
||||||
@Override
|
@Override
|
||||||
HTMLElement createElement(String tagName);
|
HTMLElement createElement(String tagName);
|
||||||
|
|
||||||
|
default HTMLElement createElement(String tagName, Consumer<HTMLElement> consumer) {
|
||||||
|
HTMLElement result = createElement(tagName);
|
||||||
|
consumer.accept(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
HTMLElement getElementById(String elementId);
|
HTMLElement getElementById(String elementId);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.jso.dom.html;
|
package org.teavm.jso.dom.html;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
import org.teavm.jso.JSProperty;
|
import org.teavm.jso.JSProperty;
|
||||||
import org.teavm.jso.dom.css.ElementCSSInlineStyle;
|
import org.teavm.jso.dom.css.ElementCSSInlineStyle;
|
||||||
import org.teavm.jso.dom.events.EventTarget;
|
import org.teavm.jso.dom.events.EventTarget;
|
||||||
|
@ -110,4 +111,34 @@ public interface HTMLElement extends Element, ElementCSSInlineStyle, EventTarget
|
||||||
void setInnerHTML(String content);
|
void setInnerHTML(String content);
|
||||||
|
|
||||||
TextRectangle getBoundingClientRect();
|
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
|
@JSProperty
|
||||||
Document getOwnerDocument();
|
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
|
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.browser.Window
|
||||||
import org.teavm.jso.dom.html._
|
import org.teavm.jso.dom.html._
|
||||||
import org.teavm.jso.dom.events._
|
import org.teavm.jso.dom.events._
|
||||||
|
|
||||||
object Client extends Grammar {
|
object Client {
|
||||||
def main(args: Array[String]) {
|
def main(args: Array[String]) {
|
||||||
var document = HTMLDocument.current
|
var doc = HTMLDocument.current
|
||||||
var exprElem = document.getElementById("expr").asInstanceOf[HTMLInputElement]
|
var exprElem = doc.getElementById("expr").asInstanceOf[HTMLInputElement]
|
||||||
var calcElem = document.getElementById("calculate")
|
var calcElem = doc.getElementById("calculate")
|
||||||
var resultList = document.getElementById("result-list");
|
var resultList = doc.getElementById("result-list");
|
||||||
calcElem.addEventListener("click", new EventListener[MouseEvent]() {
|
calcElem.addEventListener("click", (e : MouseEvent) => {
|
||||||
def handleEvent(e : MouseEvent) {
|
parse(exprElem.getValue().toSeq) match {
|
||||||
additive.parse(exprElem.getValue().toSeq) match {
|
case (None, _) => Window.alert("Error parsing expression");
|
||||||
case (None, _) => Window.alert("Error parsing expression");
|
case (Some(x), Nil) => {
|
||||||
case (Some(x), Nil) => {
|
resultList.insertBefore(doc.createElement("div", (elem : HTMLElement) => {
|
||||||
val resultElem = document.createElement("div")
|
elem.withChild("span").withAttr("class", "plan").withText(print(x) + " = ")
|
||||||
var exprSpan = document.createElement("span");
|
elem.withChild("span").withAttr("class", "result").withText(eval(x).toString)
|
||||||
exprSpan.setAttribute("class", "plan")
|
}), resultList.getFirstChild)
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
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
|
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 rule[T](f : Seq[Char] => (Option[T], Seq[Char])) : Rule[T] = Rule.rule(f)
|
||||||
def s(str : String) : Rule[String] = Rule.expect(str)
|
def s(str : String) : Rule[String] = Rule.expect(str)
|
||||||
def range(first : Char, last : Char) : Rule[Char] = Rule.range(first, last)
|
def range(first : Char, last : Char) : Rule[Char] = Rule.range(first, last)
|
||||||
|
@ -23,21 +23,16 @@ trait Rule[T] {
|
||||||
}
|
}
|
||||||
|
|
||||||
object Rule {
|
object Rule {
|
||||||
def rule[T](f : Seq[Char] => (Option[T], Seq[Char])) : Rule[T] = {
|
def rule[T](f : Seq[Char] => (Option[T], Seq[Char])) : Rule[T] = new Rule[T]() {
|
||||||
new Rule[T]() {
|
def parse(chars : Seq[Char]) : (Option[T], Seq[Char]) = f(chars)
|
||||||
def parse(chars : Seq[Char]) : (Option[T], Seq[Char]) = f(chars)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
def concat[S, T](left : Rule[S], right : => Rule[T]) : Rule[(S, T)] = {
|
def concat[S, T](left : Rule[S], right : => Rule[T]) : Rule[(S, T)] = {
|
||||||
lazy val right2 = right
|
rule(chars => left.parse(chars) match {
|
||||||
rule(chars => {
|
case (Some(leftResult), rem) => right.parse(rem) match {
|
||||||
left.parse(chars) match {
|
case (Some(rightResult), rem2) => (Some((leftResult, rightResult)), rem2)
|
||||||
case (Some(leftResult), rem) => right2.parse(rem) match {
|
case (None, rem2) => (None, rem)
|
||||||
case (Some(rightResult), rem2) => (Some((leftResult, rightResult)), rem2)
|
|
||||||
case (None, rem2) => (None, rem)
|
|
||||||
}
|
|
||||||
case (None, rem) => (None, rem)
|
|
||||||
}
|
}
|
||||||
|
case (None, rem) => (None, rem)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
def unlimited[T](inner : Rule[T]) : Rule[List[T]] = {
|
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] = {
|
def firstOf[T](first : Rule[T], second : => Rule[T]) : Rule[T] = {
|
||||||
lazy val second2 = second
|
rule(chars => first.parse(chars) match {
|
||||||
rule(chars => {
|
case (Some(result), rem) => (Some(result), rem)
|
||||||
first.parse(chars) match {
|
case (None, _) => second.parse(chars) match {
|
||||||
case (Some(result), rem) => (Some(result), rem)
|
case (Some(result), rem) => (Some(result), rem)
|
||||||
case (None, _) => second2.parse(chars) match {
|
case (None, _) => (None, chars)
|
||||||
case (Some(result), rem) => (Some(result), rem)
|
|
||||||
case (None, _) => (None, chars)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
def optional[T](inner : Rule[T]) : Rule[Option[T]] = {
|
def optional[T](inner : Rule[T]) : Rule[Option[T]] = {
|
||||||
rule {
|
rule(chars => inner.parse(chars) match {
|
||||||
chars => inner.parse(chars) match {
|
case (Some(result), rem) => (Some(Some(result)), rem)
|
||||||
case (Some(result), rem) => (Some(Some(result)), rem)
|
case (None, rem) => (Some(None), chars)
|
||||||
case (None, rem) => (Some(None), chars)
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
implicit def expect(str : String) : Rule[String] = {
|
implicit def expect(str : String) : Rule[String] = {
|
||||||
def iter(chars : Seq[Char], index : Int) : (Boolean, Seq[Char]) = {
|
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] = {
|
def andThen[T, S](inner : Rule[T], f : T => S) : Rule[S] = {
|
||||||
rule(chars => {
|
rule(chars => inner.parse(chars) match {
|
||||||
inner.parse(chars) match {
|
case (Some(result), rem) => (Some(f(result)), rem)
|
||||||
case (Some(result), rem) => (Some(f(result)), rem)
|
case (None, rem) => (None, rem)
|
||||||
case (None, rem) => (None, rem)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
def range(first : Char, last : Char) : Rule[Char] = {
|
def range(first : Char, last : Char) : Rule[Char] = {
|
||||||
rule(chars => {
|
rule(chars => chars match {
|
||||||
chars match {
|
case c +: rem if c >= first && c <= last => (Some(c), rem)
|
||||||
case c +: rem if c >= first && c <= last => (Some(c), rem)
|
case _ => (None, chars)
|
||||||
case _ => (None, chars)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user