|
// Copyright 2013 Stefan Matthias Aust. Licensed under http://opensource.org/licenses/MIT. |
|
library java2dart; |
|
|
|
import 'dart:io'; |
|
import 'dart:convert'; |
|
|
|
class Scanner { |
|
String currentToken; |
|
|
|
Iterator<Match> _matches; |
|
|
|
Scanner(String source) { |
|
_matches = new RegExp( |
|
'\\s+|//.*\$|/\\*[\\s\\S]*?\\*/|' // whitespace & comments |
|
'(\\d+(?:\\.\\d*|\\.\\d+)(?:[eE][-+]\\d+)|' // numbers |
|
'[\\w\$_]+|' // names & keywords |
|
'"(?:\\\\.|[^"])*?"|\'(?:\\\\.|[^\'])+?\'|' // strings & characters |
|
'&&|\\|\\||\\+\\+|--|' // operators |
|
'[+\\-*/%&^|]=?|<<=?|>>>?=?|[=<>!]=?|~|' // operators |
|
'[.]{3}|[.,;()[\\]{}?:])|(.)', // syntax |
|
multiLine: true).allMatches(source).iterator; |
|
advance(); |
|
} |
|
|
|
/// Advances [currentToken] to the next token. |
|
String advance() { |
|
String token = currentToken; |
|
while (_matches.moveNext()) { |
|
Match m = _matches.current; |
|
if (m[1] != null) { |
|
currentToken = m[1]; |
|
return token; |
|
} |
|
if (m[2] != null) { |
|
error("unknown token ${m[2]} at ${m.start}"); |
|
} |
|
} |
|
currentToken = ""; |
|
return token; |
|
} |
|
|
|
/// Returns true if the current token matches [token] and false otherwise. |
|
/// Advances to the next token if the current token was matched. |
|
bool at(String token) { |
|
if (currentToken == token) { |
|
advance(); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/// Advances [currentToken] if it matches [token] and raises an error otherwise. |
|
void expect(String token) { |
|
if (!at(token)) { |
|
error("expected $token but found $currentToken"); |
|
} |
|
} |
|
|
|
/// Returns true if [currentToken] is a name. |
|
bool get isName => currentToken.startsWith(new RegExp("[\\w\$_]")); |
|
|
|
/// Returns true if [currentToken] is a number. |
|
bool get isNumber => currentToken.startsWith(new RegExp("\\.?[\\d]")); |
|
|
|
/// Returns true if [currentToken] is a string. |
|
bool get isString => currentToken[0] == '"' || currentToken[0] == "'"; |
|
|
|
void error(String message) { |
|
throw new Exception(message); |
|
} |
|
} |
|
|
|
class Parser extends Scanner { |
|
Parser(String source) : super(source); |
|
|
|
// ["package" qualifiedName ";"] { importStatement } { typeDeclaration } |
|
parseCompilationUnit() { |
|
String package; |
|
if (at("package")) { |
|
package = parseQualifiedName(); |
|
expect(";"); |
|
} |
|
var imports = []; |
|
while (at("import")) { |
|
imports.add(parseImportStatement()); |
|
} |
|
var declarations = []; |
|
while (!at("")) { |
|
declarations.add(parseTypeDeclaration()); |
|
} |
|
return ["unit", package, imports, declarations]; |
|
} |
|
|
|
// "import" name {"." name} ["." "*"] ";" |
|
// no static |
|
parseImportStatement() { |
|
var names = [parseName()]; |
|
while (at(".")) { |
|
names.add(at("*") ? "*" : parseName()); |
|
} |
|
expect(";"); |
|
return ["import", names.join(".")]; |
|
} |
|
|
|
// classDeclaration | interfaceDeclaration |
|
// no enum |
|
parseTypeDeclaration() { |
|
var modifiers = parseModifiers(); |
|
if (at("class")) return parseClassDeclaration(modifiers); |
|
if (at("interface")) return parseInterfaceDeclaration(modifiers); |
|
error("expected class or interface but found $currentToken."); |
|
} |
|
|
|
// no protected, abstract, native, synchronized, transient, volatile, strictfp |
|
parseModifiers() { |
|
var modifiers = []; |
|
while (true) { |
|
if (at("public")) { |
|
modifiers.add("public"); |
|
} else if (at("private")) { |
|
modifiers.add("private"); |
|
} else if (at("static")) { |
|
modifiers.add("static"); |
|
} else if (at("final")) { |
|
modifiers.add("final"); |
|
} else break; |
|
} |
|
return ["modifiers", modifiers]; |
|
} |
|
|
|
// [modifiers] [typeParameter] "class" name ["extends" type] ["implements" type {"," type] classBody |
|
parseClassDeclaration(modifiers) { |
|
var className = parseName(); |
|
var typeParameters = at("<") ? parseTypeParameters() : null; |
|
var superclassType = at("extends") ? parseType() : null; |
|
var interfaceTypes = []; |
|
if (at("implements")) { |
|
interfaceTypes.add(parseType()); |
|
while (at(",")) { |
|
interfaceTypes.add(parseType()); |
|
} |
|
} |
|
return ["class", modifiers, className, typeParameters, superclassType, interfaceTypes, parseClassOrInterfaceBody()]; |
|
} |
|
|
|
// [modifiers] [typeParameter] "interface" name ["extends" type {"," type}] interfaceBody |
|
parseInterfaceDeclaration(modifiers) { |
|
var interfaceName = parseName(); |
|
var typeParameters = at("<") ? parseTypeParameters() : null; |
|
var interfaceTypes = []; |
|
if (at("extends")) { |
|
interfaceTypes.add(parseType()); |
|
while (at(",")) { |
|
interfaceTypes.add(parseType()); |
|
} |
|
} |
|
return ["interface", modifiers, interfaceName, typeParameters, interfaceTypes, parseClassOrInterfaceBody()]; |
|
} |
|
|
|
// "{" {memberDeclaration} "}" |
|
// should distinguish class & interface (has no static, no method bodies) |
|
parseClassOrInterfaceBody() { |
|
expect("{"); |
|
var declarations = []; |
|
while (!at("}")) { |
|
declarations.add(parseMemberDeclaration()); |
|
//print("- ${declarations.last}"); |
|
} |
|
return declarations; |
|
} |
|
|
|
parseMemberDeclaration() { |
|
if (at("<")) { |
|
parseTypeParameters(); |
|
} |
|
var modifiers = parseModifiers(); |
|
if (modifiers[1].length == 1 && modifiers[1][0] == "static" && at("{")) { |
|
return ["static_initializer", parseStatementBlock()]; |
|
} |
|
if (at("class")) return parseClassDeclaration(modifiers); |
|
if (at("interface")) return parseInterfaceDeclaration(modifiers); |
|
String name = parseName(); // could be a type or constructor |
|
if (at("(")) { // it's a constructor declaration |
|
var parameters = parseParameterList(); |
|
expect("{"); |
|
return ["constructor", modifiers, name, parameters, parseStatementBlock()]; |
|
} |
|
var type = parseArrayType(parseTypeWith(name)); |
|
name = parseName(); |
|
if (at("(")) { // it's a method declaration |
|
var parameters = parseParameterList(); |
|
type = parseArrayType(type); |
|
var statements = []; |
|
if (!at(";")) { |
|
expect("{"); |
|
statements = parseStatementBlock(); |
|
} |
|
return ["method", modifiers, type, name, parameters, statements]; |
|
} else { // must be variable declaration |
|
var declarations = parseTypeDeclarations(modifiers, type, name); |
|
expect(";"); |
|
return declarations; |
|
} |
|
} |
|
|
|
parseParameterList() { |
|
var parameters = []; |
|
while (!at(")")) { |
|
var t = parseType(); |
|
t = parseArrayType(t); |
|
bool rest = at("..."); |
|
var n = parseName(); |
|
t = parseArrayType(t); |
|
parameters.add([rest ? "rest_parameter" : "parameter", t, n]); |
|
if (at(")")) break; |
|
expect(","); |
|
} |
|
return parameters; |
|
} |
|
|
|
parseParameter() { |
|
var type = parseType(); |
|
type = parseArrayType(type); |
|
bool rest = at("..."); |
|
var name = parseName(); |
|
type = parseArrayType(type); |
|
return [rest ? "rest_parameter" : "parameter", type, name]; |
|
} |
|
|
|
parseArrayInitializer() { |
|
var elements = []; |
|
while (!at("}")) { |
|
if (at("{")) { |
|
elements.add(parseArrayInitializer()); |
|
} else { |
|
elements.add(parseExpression()); |
|
} |
|
if (at("}")) break; |
|
expect(","); |
|
} |
|
return ["array", elements]; |
|
} |
|
|
|
// types ----------------------------------------------------------------------------------------------------- |
|
|
|
parseType() { |
|
return parseTypeWith(advance()); |
|
} |
|
|
|
static const primitiveTypes = const["boolean", "byte", "char", "short", "int", "float", "long", "double", "void"]; |
|
|
|
parseTypeWith(String name) { |
|
var type; |
|
if (primitiveTypes.contains(name)) { |
|
type = ["primitive_type", name]; |
|
} else { |
|
var names = [name]; |
|
while (at(".")) { |
|
names.add(parseName()); |
|
} |
|
type = ["reference_type", names.join(".")]; |
|
if (at("<")) { |
|
var names = [at("?") ? "?" : parseName()]; |
|
while (at(",")) { |
|
names.add([at("?") ? "?" : parseName()]); |
|
} |
|
expect(">"); |
|
type = ["generic_type", type, names]; |
|
} |
|
} |
|
return type; |
|
} |
|
|
|
parseArrayType(type) { |
|
while (at("[]")) { |
|
type = ["array_type", type]; |
|
} |
|
while (at("[")) { |
|
expect("]"); |
|
type = ["array_type", type]; |
|
} |
|
return type; |
|
} |
|
|
|
parseTypeParameters() { |
|
var parameters = [parseTypeParameter()]; |
|
while (at(",")) { |
|
parameters.add(parseTypeParameter()); |
|
} |
|
expect(">"); |
|
return ["type_parameters", parameters]; |
|
} |
|
|
|
String parseTypeParameter() { |
|
return at("?") ? "?" : parseName(); |
|
} |
|
|
|
parseTypeDeclarations(modifiers, type, name) { |
|
var type2 = parseArrayType(type); |
|
var init = parseTypeInitializer(); |
|
var declarations = [["variable_declaration", modifiers, type2, name, init]]; |
|
while (at(",")) { |
|
name = parseName(); |
|
type2 = parseArrayType(type); |
|
init = parseTypeInitializer(); |
|
declarations.add(["variable_declaration", modifiers, type2, name, init]); |
|
} |
|
return ["variable_declarations", declarations]; |
|
} |
|
|
|
parseTypeInitializer() { |
|
if (at("=")) { |
|
if (at("{")) { |
|
return parseArrayInitializer(); |
|
} else { |
|
return parseExpression(); |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
// statements ----------------------------------------------------------------------------------------------------- |
|
|
|
parseStatementBlock() { |
|
var statements = []; |
|
while (!at("}")) { |
|
statements.add(parseStatement()); |
|
//print("= ${statements.last}"); |
|
} |
|
return ["block", statements]; |
|
} |
|
|
|
parseStatement() { |
|
if (at("{")) return parseStatementBlock(); |
|
if (at("if")) return parseIfStatement(); |
|
if (at("do")) return parseDoWhileStatement(); |
|
if (at("while")) return parseWhileStatement(); |
|
if (at("for")) return parseForStatement(); |
|
if (at("try")) return parseTryStatement(); |
|
if (at("switch")) return parseSwitchStatement(); |
|
if (at("synchronized")) { |
|
var expression = parseParenthesizedExpression(); |
|
return ["synchronized", expression, parseStatement()]; |
|
} |
|
if (at("return")) { |
|
var expression = null; |
|
if (!at(";")) { |
|
expression = parseExpression(); |
|
expect(";"); |
|
} |
|
return ["return", expression]; |
|
} |
|
if (at("throw")) { |
|
var expression = parseExpression(); |
|
expect(";"); |
|
return ["throw", expression]; |
|
} |
|
if (at("assert")) { |
|
var e = parseExpression(); |
|
var m = at(":") ? parseExpression() : null; |
|
expect(";"); |
|
return ["assert", e, m]; |
|
} |
|
if (at("break")) { |
|
var label; |
|
if (!at(";")) { |
|
label = parseName(); |
|
expect(";"); |
|
} |
|
return ["break", label]; |
|
} |
|
if (at("continue")) { |
|
var label; |
|
if (!at(";")) { |
|
label = parseName(); |
|
expect(";"); |
|
} |
|
return ["continue", label]; |
|
} |
|
if (at(";")) return ["pass"]; |
|
var expression = parseExpression(); |
|
if (expression[0] == "variable" && at(":")) { |
|
return ["label", expression[1]]; |
|
} |
|
expect(";"); |
|
if (expression[0] == "variable_declarations") { |
|
return expression; |
|
} |
|
return ["void", expression]; |
|
} |
|
|
|
parseIfStatement() { |
|
var c = parseParenthesizedExpression(); |
|
var t = parseStatement(); |
|
var e = at("else") ? parseStatement() : null; |
|
return ["if", c, t, e]; |
|
} |
|
|
|
parseDoWhileStatement() { |
|
var s = parseStatement(); |
|
expect("while"); |
|
var e = parseParenthesizedExpression(); |
|
expect(";"); |
|
return ["dowhile", e, s]; |
|
} |
|
|
|
parseWhileStatement() { |
|
var e = parseParenthesizedExpression(); |
|
var s = parseStatement(); |
|
return ["whiledo", e, s]; |
|
} |
|
|
|
parseForStatement() { |
|
var a = [], b, c = []; |
|
expect("("); |
|
if (!at(";")) { |
|
var e = parseExpression(); |
|
if (at(":")) { |
|
// e must be variable_declarations with a single variable_declaration |
|
assert(e[0] == "variable_declarations" && e[1].length == 1); |
|
b = parseExpression(); |
|
expect(")"); |
|
var s = parseStatement(); |
|
return ["foreach", e[1][0], b, s]; |
|
} |
|
a.add(e); |
|
while (at(",")) { |
|
a.add(parseExpression()); |
|
} |
|
expect(";"); |
|
} |
|
if (!at(";")) { |
|
b = parseExpression(); |
|
expect(";"); |
|
} |
|
if (!at(")")) { |
|
c.add(parseExpression()); |
|
while (at(",")) { |
|
c.add(parseExpression()); |
|
} |
|
expect(")"); |
|
} |
|
var s = parseStatement(); |
|
return ["for", a, b, c, s]; |
|
} |
|
|
|
parseTryStatement() { |
|
var s = parseStatement(); |
|
var c = []; |
|
while (at("catch")) { |
|
expect("("); |
|
var p = parseParameter(); |
|
expect(")"); |
|
c.add(["catch", p, parseStatement()]); |
|
} |
|
var f = at("finally") ? parseStatement() : null; |
|
return ["try", s, c, f]; |
|
} |
|
|
|
parseSwitchStatement() { |
|
var expression = parseParenthesizedExpression(); |
|
expect("{"); |
|
var statements = []; |
|
while (!at("}")) { |
|
if (at("case")) { |
|
var e = parseExpression(); |
|
expect(":"); |
|
statements.add(["case", e]); |
|
} else if (at("default")) { |
|
expect(":"); |
|
statements.add(["default"]); |
|
} else { |
|
statements.add(parseStatement()); |
|
} |
|
} |
|
return ["switch", expression, statements]; |
|
} |
|
|
|
parseParenthesizedExpression() { |
|
expect("("); |
|
var expression = parseExpression(); |
|
expect(")"); |
|
return expression; |
|
} |
|
|
|
// expressions ----------------------------------------------------------------------------------------------------- |
|
|
|
static const assignmentOperators = const["=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", ">>>="]; |
|
|
|
parseExpression() { |
|
var e = parseConditionalExpression(); |
|
if (assignmentOperators.contains(currentToken)) { |
|
var operator = advance(); |
|
return [operator, e, parseExpression()]; |
|
} |
|
return e; |
|
} |
|
|
|
parseConditionalExpression() { |
|
var e = parseOrExpression(); |
|
if (at("?")) { |
|
var t = parseConditionalExpression(); |
|
expect(":"); |
|
return ["?:", e, t, parseConditionalExpression()]; |
|
} |
|
return e; |
|
} |
|
|
|
parseOrExpression() { |
|
var e = parseAndExpression(); |
|
while (at("||")) { |
|
e = ["||", e, parseAndExpression()]; |
|
} |
|
return e; |
|
} |
|
|
|
parseAndExpression() { |
|
var e = parseBitIorExpression(); |
|
while (at("&&")) { |
|
e = ["&&", e, parseBitIorExpression()]; |
|
} |
|
return e; |
|
} |
|
|
|
parseBitIorExpression() { |
|
var e = parseBitXorExpression(); |
|
while (at("|")) { |
|
e = ["|", e, parseBitXorExpression()]; |
|
} |
|
return e; |
|
} |
|
|
|
parseBitXorExpression() { |
|
var e = parseBitAndExpression(); |
|
while (at("^")) { |
|
e = ["^", e, parseBitAndExpression()]; |
|
} |
|
return e; |
|
} |
|
|
|
parseBitAndExpression() { |
|
var e = parseEqualityExpression(); |
|
while (at("&")) { |
|
e = ["&", e, parseEqualityExpression()]; |
|
} |
|
return e; |
|
} |
|
|
|
parseEqualityExpression() { |
|
var e = parseRelationalExpression(); |
|
while (true) { |
|
if (at("==")) { |
|
e = ["==", e, parseRelationalExpression()]; |
|
} else if (at("!=")) { |
|
e = ["!=", e, parseRelationalExpression()]; |
|
} else return e; |
|
} |
|
} |
|
|
|
parseRelationalExpression() { |
|
var e = parseBitShiftExpression(); |
|
while (true) { |
|
if (at("<")) { |
|
// hack: could be a variable declaration |
|
var e2 = parseBitShiftExpression(); |
|
if (e[0] == "variable" && e2[0] == "variable" && at(">")) { |
|
var type = parseArrayType(["generic_type", ["type", e[1]], [e2[1]]]); |
|
var name = parseName(); |
|
return parseTypeDeclarations(null, type, name); |
|
} |
|
e = ["<", e, e2]; |
|
} else if (at("<=")) { |
|
e = ["<=", e, parseBitShiftExpression()]; |
|
} else if (at(">")) { |
|
e = [">", e, parseBitShiftExpression()]; |
|
} else if (at(">=")) { |
|
e = [">=", e, parseBitShiftExpression()]; |
|
} else if (at("instanceof")) { |
|
e = ["instanceof", e, parseBitShiftExpression()]; |
|
} else return e; |
|
} |
|
} |
|
|
|
parseBitShiftExpression() { |
|
var e = parseAdditionExpression(); |
|
while (true) { |
|
if (at("<<")) { |
|
e = ["<<", e, parseAdditionExpression()]; |
|
} else if (at(">>")) { |
|
e = [">>", e, parseAdditionExpression()]; |
|
} else if (at(">>>")) { |
|
e = [">>>", e, parseAdditionExpression()]; |
|
} else return e; |
|
} |
|
} |
|
|
|
parseAdditionExpression() { |
|
var e = parseMultiplicationExpression(); |
|
while (true) { |
|
if (at("+")) { |
|
e = ["+", e, parseMultiplicationExpression()]; |
|
} else if (at("-")) { |
|
e = ["-", e, parseMultiplicationExpression()]; |
|
} else return e; |
|
} |
|
} |
|
|
|
parseMultiplicationExpression() { |
|
var e = parsePrefixExpression(); |
|
while (true) { |
|
if (at("*")) { |
|
e = ["*", e, parsePrefixExpression()]; |
|
} else if (at("/")) { |
|
e = ["/", e, parsePrefixExpression()]; |
|
} else if (at("%")) { |
|
e = ["%", e, parsePrefixExpression()]; |
|
} else return e; |
|
} |
|
} |
|
|
|
parsePrefixExpression() { |
|
if (at("++")) { |
|
return ["++p", parsePrefixExpression()]; |
|
} |
|
if (at("--")) { |
|
return ["--p", parsePrefixExpression()]; |
|
} |
|
if (at("+")) { |
|
return ["+p", parsePrefixExpression()]; |
|
} |
|
if (at("-")) { |
|
return ["-p", parsePrefixExpression()]; |
|
} |
|
if (at("!")) { |
|
return ["!", parsePrefixExpression()]; |
|
} |
|
if (at("~")) { |
|
return ["~", parsePrefixExpression()]; |
|
} |
|
return parsePostfixExpression(); |
|
} |
|
|
|
parsePostfixExpression() { |
|
var e = parsePrimary(); |
|
if (at("++")) { |
|
return ["p++", e]; |
|
} |
|
if (at("--")) { |
|
return ["p--", e]; |
|
} |
|
return e; |
|
} |
|
|
|
parsePrimary() { |
|
var e = parseLiteral(); |
|
while (true) { |
|
if (at("(")) { // simple call |
|
assert(e[0] == "variable"); |
|
e = ["call", null, e[1], parseArguments()]; |
|
} else if (at("[")) { |
|
// hack: could be a variable declaration |
|
if (e[0] == "variable" && at("]")) { |
|
var type = ["array_type", ["type", e[1]]]; |
|
return parseTypeDeclarations(null, parseArrayType(type), parseName()); |
|
} |
|
var index = parseExpression(); |
|
expect("]"); |
|
e = ["[]", e, index]; |
|
} else if (at(".")) { // method call or field access |
|
var name = parseName(); |
|
if (at("(")) { // method call |
|
return ["call", e, name, parseArguments()]; |
|
} |
|
e = [".", e, name]; |
|
} else return e; |
|
} |
|
} |
|
|
|
parseArguments() { |
|
var arguments = []; |
|
while (!at(")")) { |
|
arguments.add(parseExpression()); |
|
if (at(")")) break; |
|
expect(","); |
|
} |
|
return arguments; |
|
} |
|
|
|
static const literalFirst = const["++", "+", "--", "-", "!", "~", "("]; |
|
|
|
parseLiteral() { |
|
if (at("null")) return ["literal", null]; |
|
if (at("true")) return ["literal", true]; |
|
if (at("false")) return ["literal", false]; |
|
if (at("this")) return ["this"]; |
|
if (at("super")) return ["super"]; |
|
if (at("new")) { |
|
var type = parseType(); |
|
if (at("[")) { |
|
while (at("]")) { |
|
type = ["array_type", type]; |
|
expect("["); |
|
} |
|
var expression = parseExpression(); |
|
expect("]"); |
|
return ["new_array", type, expression]; |
|
} |
|
if (at("(")) { |
|
return ["new_instance", type, parseArguments()]; |
|
} |
|
error("unexpected $currentToken after new $type"); |
|
} |
|
if (at("(")) { |
|
var e = parseExpression(); |
|
expect(")"); |
|
// need to distinguish expression in parentheses from a cast |
|
if (e[0] == "variable") { |
|
if (isName || isNumber || isString || literalFirst.contains(currentToken)) { |
|
return ["cast", e[1], parsePrefixExpression()]; |
|
} |
|
} |
|
return e; |
|
} |
|
if (isString) { |
|
return ["literal", advance()]; |
|
} |
|
if (isNumber) { |
|
return ["literal", num.parse(advance())]; |
|
} |
|
// need to distinguish type declarations and variables |
|
// a sequence of two names should be a type declaration |
|
// ("foo[] bar" or "foo<x> bar" is not detected) |
|
if (isName) { |
|
var name = advance(); |
|
if (isName) { |
|
var type = parseArrayType(parseTypeWith(name)); |
|
return parseTypeDeclarations(null, type, parseName()); |
|
} |
|
return ["variable", name]; |
|
} |
|
error("unexpected $currentToken in expression"); |
|
} |
|
|
|
// utilities ------------------------------------------------------------------------------------------------------- |
|
|
|
List<String> parseNameList() { |
|
var names = [parseName()]; |
|
while (at(",")) { |
|
names.add(parseName()); |
|
} |
|
return names; |
|
} |
|
|
|
String parseQualifiedName() { |
|
var names = [parseName()]; |
|
while (at(".")) { |
|
names.add(parseName()); |
|
} |
|
return names.join("."); |
|
} |
|
|
|
String parseName() { |
|
if (!isName) { |
|
error("expected NAME but found $currentToken"); |
|
} |
|
return advance(); |
|
} |
|
} |
|
|
|
/// Translates Java nodes into Dart (printed to [stdout]). |
|
class Translator { |
|
Map _funcs; |
|
int _indent; |
|
|
|
Translator() { |
|
_indent = 0; |
|
_funcs = { |
|
"unit": (node) { |
|
if (node[1] != null) { |
|
emit("library ${node[1]};"); |
|
if (node[2].isNotEmpty) { |
|
newline(); |
|
} |
|
} |
|
node[2].forEach(translate); |
|
node[3].forEach(translate); |
|
}, |
|
"import": (node) { |
|
//Dart doesn't need Java imports |
|
//emit("import '${node[1]}';"); |
|
}, |
|
"class": (node) { |
|
newline(); |
|
emit("class ${node[2]} ${node[4] != null ? "extends ${translate(node[4])}" : ""}"); |
|
emit("{"); |
|
indent(); |
|
node[6].where((n) => n[0] != "class").forEach(translate); |
|
dedent(); |
|
emit("}"); |
|
// Dart doesn't support nested classes |
|
node[6].where((n) => n[0] == "class").forEach(translate); |
|
}, |
|
"variable_declarations": (node) { |
|
node[1].forEach((n) => emit(translate(n) + ";")); |
|
}, |
|
"variable_declaration": (node) { |
|
var init = node[4] != null ? " = ${translate(node[4])}" : ""; |
|
var mods = node[1] != null ? translate(node[1]) : ""; |
|
return "$mods${translate(node[2])} ${name(node[3])}$init"; |
|
}, |
|
"modifiers": (node) { |
|
var mods = ""; |
|
if (node[1].contains("static")) mods += "static "; |
|
if (node[1].contains("final")) mods += "final "; |
|
return mods; |
|
}, |
|
"method": (node) { |
|
newline(); |
|
var mods = node[1] != null ? translate(node[1]) : ""; |
|
emit("$mods${translate(node[2])} ${node[3]}(${node[4].map(translate).join(", ")})"); |
|
emit("{"); |
|
indent(); |
|
translate(node[5]); |
|
dedent(); |
|
emit("}"); |
|
}, |
|
"parameter": (node) { |
|
return "${translate(node[1])} ${name(node[2])}"; |
|
}, |
|
"rest_parameter": (node) { |
|
return "List<${translate(node[1])}> ${name(node[2])}"; |
|
}, |
|
"block": (node) { |
|
node[1].forEach(translate); |
|
}, |
|
"type": (node) { // TODO should be primitive_type or generic_type |
|
var s = dartType(node[1]); |
|
return s != null ? s : node[1]; |
|
}, |
|
"primitive_type": (node) { |
|
return dartType(node[1]); |
|
}, |
|
"reference_type": (node) { |
|
return node[1]; |
|
}, |
|
"array_type": (node) { |
|
return "List<${translate(node[1])}>"; |
|
}, |
|
"generic_type": (node) { |
|
var type = translate(node[1]); |
|
return node[2] != null && node[2][0] != "?" ? "$type<${node[2].join(", ")}>" : type; |
|
}, |
|
"void": (node) { |
|
emit("${strip(translate(node[1]))};"); |
|
}, |
|
"call": (node) { |
|
var receiver = node[1] != null ? translate(node[1]) : ""; |
|
if (node[1] != null && node[2] == "length" && node[3].length == 0) { |
|
return "$receiver.length"; |
|
} |
|
if (node[1] != null && node[2] == "charAt" && node[3].length == 1) { |
|
return "$receiver[${translate(node[3][0])}]"; |
|
} |
|
return "${receiver != "" ? receiver + "." : ""}${node[2]}(${node[3].map(translate).map(strip).join(", ")})"; |
|
}, |
|
"literal": (node) { |
|
if (node[1] is String) { |
|
node[1] = node[1].replaceAll("\$", "\\\$"); |
|
if (node[1][0] == "'") { // it's a char not a string |
|
return "${node[1]}.codeUnitAt(0)"; |
|
} |
|
} |
|
return "${node[1]}"; |
|
}, |
|
"variable": (node) { |
|
return name(node[1]); |
|
}, |
|
"new_array": (node) { |
|
return "new List<${translate(node[1])}>(${translate(node[2])})"; |
|
}, |
|
"new_instance": (node) { |
|
return "new ${translate(node[1])}(${node[2].map(translate).join(",")})"; |
|
}, |
|
"array": (node) { |
|
return "[${node[1].map(translate).join(", ")}]"; |
|
}, |
|
"type_parameters": (node) { |
|
return node[1].join(", "); |
|
}, |
|
// TODO use precedence to omit unneeded parentheses |
|
"+": (node) { return "(${translate(node[1])} + ${translate(node[2])})"; }, |
|
"-": (node) { return "(${translate(node[1])} - ${translate(node[2])})"; }, |
|
"*": (node) { return "(${translate(node[1])} * ${translate(node[2])})"; }, |
|
"/": (node) { return "(${translate(node[1])} ~/ ${translate(node[2])})"; }, // int only |
|
"%": (node) { return "(${translate(node[1])} % ${translate(node[2])})"; }, |
|
"&": (node) { return "(${translate(node[1])} & ${translate(node[2])})"; }, |
|
"|": (node) { return "(${translate(node[1])} | ${translate(node[2])})"; }, |
|
"^": (node) { return "(${translate(node[1])} ^ ${translate(node[2])})"; }, |
|
"<<": (node) { return "(${translate(node[1])} << ${translate(node[2])})"; }, |
|
">>": (node) { return "(${translate(node[1])} >> ${translate(node[2])})"; }, |
|
">>>": (node) { return "(${translate(node[1])} >> ${translate(node[2])})"; }, |
|
"&&": (node) { return "(${translate(node[1])} && ${translate(node[2])})"; }, |
|
"||": (node) { return "(${translate(node[1])} || ${translate(node[2])})"; }, |
|
"<": (node) { return "(${translate(node[1])} < ${translate(node[2])})"; }, |
|
"<=": (node) { return "(${translate(node[1])} <= ${translate(node[2])})"; }, |
|
">": (node) { return "(${translate(node[1])} > ${translate(node[2])})"; }, |
|
">=": (node) { return "(${translate(node[1])} >= ${translate(node[2])})"; }, |
|
"==": (node) { return "(${translate(node[1])} == ${translate(node[2])})"; }, |
|
"!=": (node) { return "(${translate(node[1])} != ${translate(node[2])})"; }, |
|
"=": (node) { return "(${translate(node[1])} = ${translate(node[2])})"; }, |
|
"+=": (node) { return "(${translate(node[1])} += ${translate(node[2])})"; }, |
|
"-=": (node) { return "(${translate(node[1])} -= ${translate(node[2])})"; }, |
|
"*=": (node) { return "(${translate(node[1])} *= ${translate(node[2])})"; }, |
|
"/=": (node) { return "(${translate(node[1])} ~/= ${translate(node[2])})"; }, // int only |
|
"[]": (node) { return "${translate(node[1])}[${strip(translate(node[2]))}]"; }, |
|
"p++": (node) { return "${translate(node[1])}++"; }, |
|
"p--": (node) { return "${translate(node[1])}--"; }, |
|
"++p": (node) { return "(++${translate(node[1])})"; }, |
|
"--p": (node) { return "(--${translate(node[1])})"; }, |
|
"!": (node) { return "(!${translate(node[1])})"; }, |
|
"~": (node) { return "(~${translate(node[1])})"; }, |
|
"-p": (node) { return "(-${translate(node[1])})"; }, |
|
"cast": (node) { |
|
//Dart doesn't support casts |
|
//return "(${translate(node[2])} as ${node[1]})"; |
|
return translate(node[2]); |
|
}, |
|
".": (node) { |
|
return translate(node[1]) + ".${node[2]}"; |
|
}, |
|
"?:": (node) { |
|
return "(${translate(node[1])} ? ${translate(node[2])} : ${translate(node[3])})"; |
|
}, |
|
"if": (node) { |
|
emit("if (${strip(translate(node[1]))}) {"); |
|
indent(); |
|
translate(node[2]); |
|
if (node[3] != null) { |
|
dedent(); |
|
emit("} else {"); |
|
indent(); |
|
translate(node[3]); |
|
} |
|
dedent(); |
|
emit("}"); |
|
}, |
|
"return": (node) { |
|
if (node[1] != null) { |
|
emit("return ${strip(translate(node[1]))};"); |
|
} else { |
|
emit("return;"); |
|
} |
|
}, |
|
"for": (node) { |
|
var i = []; |
|
node[1].forEach((n) { |
|
if (n[0] == 'variable_declarations') i.addAll(n[1]); |
|
else i.add(n); |
|
}); |
|
if (i.length > 1) { |
|
// Dart doesn't like multiple declarations in for so we move them |
|
i.forEach(translate); |
|
i = []; |
|
} |
|
emit("for (${i.map(translate).join(", ")}; " |
|
"${node[2] != null ? strip(translate(node[2])) : ""}; " |
|
"${node[3].map(translate).join(", ")}) {"); |
|
indent(); |
|
translate(node[4]); |
|
dedent(); |
|
emit("}"); |
|
}, |
|
"foreach": (node) { |
|
emit("for (${translate(node[1])} in ${translate(node[2])}) {"); |
|
indent(); |
|
translate(node[3]); |
|
dedent(); |
|
emit("}"); |
|
}, |
|
"dowhile": (node) { |
|
emit("do {"); |
|
indent(); |
|
translate(node[2]); |
|
dedent(); |
|
emit("} while (${strip(translate(node[1]))});"); |
|
}, |
|
"whiledo": (node) { |
|
emit("while (${strip(translate(node[1]))}) {"); |
|
indent(); |
|
translate(node[2]); |
|
dedent(); |
|
emit("}"); |
|
}, |
|
"try": (node) { |
|
emit("try {"); |
|
indent(); |
|
translate(node[1]); |
|
dedent(); |
|
node[2].forEach(translate); |
|
if (node[3] != null) { |
|
emit("} finally {"); |
|
indent(); |
|
translate(node[3]); |
|
dedent(); |
|
} |
|
emit("}"); |
|
}, |
|
"catch": (node) { |
|
emit("} on ${translate(node[1][1])} catch (${name(node[1][2])}) {"); |
|
indent(); |
|
translate(node[2]); |
|
dedent(); |
|
}, |
|
"assert": (node) { |
|
emit("assert(${strip(translate(node[1]))});"); |
|
}, |
|
"break": (node) { |
|
if (node[1] != null) { |
|
emit("break ${node[1]};"); |
|
} |
|
emit("break;"); |
|
}, |
|
"continue": (node) { |
|
if (node[1] != null) { |
|
emit("continue ${node[1]};"); |
|
} |
|
emit("continue;"); |
|
}, |
|
"switch": (node) { |
|
emit("switch (${translate(node[1])}) {"); |
|
indent(); |
|
indent(); |
|
node[2].forEach(translate); |
|
dedent(); |
|
dedent(); |
|
emit("}"); |
|
}, |
|
"case": (node) { |
|
dedent(); |
|
emit("case ${translate(node[1])}:"); |
|
indent(); |
|
}, |
|
"default": (node) { |
|
dedent(); |
|
emit("default:"); |
|
indent(); |
|
}, |
|
"label": (node) { |
|
emit("${node[1]}:"); |
|
}, |
|
"throw": (node) { |
|
emit("throw ${translate(node[1])};"); |
|
}, |
|
}; |
|
} |
|
|
|
String dartType(String type) { |
|
// map Java type to Dart type |
|
return const{ |
|
"boolean": "bool", |
|
"byte": "int", |
|
"char": "int", |
|
"short": "int", |
|
"int": "int", |
|
"float": "double", |
|
"long": "int", |
|
"double": "double", |
|
"void" : "void" |
|
}[type]; |
|
} |
|
|
|
String name(String name) { |
|
// do not use Dart reserved words |
|
if (const["is"].contains(name)) name += "_"; |
|
return name; |
|
} |
|
|
|
String strip(String s) { |
|
// ignore parentheses on top level |
|
if (s.startsWith("(") && s.endsWith(")")) { |
|
return s.substring(1, s.length - 1); |
|
} |
|
return s; |
|
} |
|
|
|
void newline() { |
|
stdout.write("\n"); |
|
} |
|
|
|
void emit(String line) { |
|
for (int i = 0; i < _indent; i++) { |
|
stdout.write(' '); |
|
} |
|
stdout.writeln(line); |
|
} |
|
|
|
void indent() { _indent++; } |
|
|
|
void dedent() { _indent--; } |
|
|
|
translate(node) { |
|
return _funcs[node[0]](node); |
|
} |
|
} |
|
|
|
void main(List<String> args) { |
|
if (args.length > 0) { |
|
var source = new File("/Users/sma/Desktop/Atlantis.java").readAsStringSync(); |
|
var node = new Parser(source).parseCompilationUnit(); |
|
new File("/tmp/json").writeAsStringSync(JSON.encode(node)); |
|
} else { |
|
var node = JSON.decode(new File("/tmp/json").readAsStringSync()); |
|
new Translator().translate(node); |
|
} |
|
} |