support token extraction in expressions

This commit is contained in:
Dave Holoway
2020-06-17 15:37:30 +01:00
parent 3c505b05be
commit 13cdd1e0bc
21 changed files with 325 additions and 48 deletions

View File

@@ -16,6 +16,7 @@ const { TokenList } = require("./TokenList");
const { AnyMethod, AnyType, AnyValue } = require("./anys"); const { AnyMethod, AnyType, AnyValue } = require("./anys");
const { Label, Local, MethodDeclarations, ResolvedIdent } = require("./body-types"); const { Label, Local, MethodDeclarations, ResolvedIdent } = require("./body-types");
const { resolveImports, resolveSingleImport } = require('../java/import-resolver'); const { resolveImports, resolveSingleImport } = require('../java/import-resolver');
const { getTypeInheritanceList } = require('./expression-resolver');
const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression"); const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression");
const { ArrayValueExpression } = require("./expressiontypes/ArrayValueExpression"); const { ArrayValueExpression } = require("./expressiontypes/ArrayValueExpression");
@@ -30,6 +31,7 @@ const { MethodCallExpression } = require("./expressiontypes/MethodCallExpression
const { NewArray, NewObject } = require("./expressiontypes/NewExpression"); const { NewArray, NewObject } = require("./expressiontypes/NewExpression");
const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression"); const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression");
const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression"); const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression");
const { Variable } = require("./expressiontypes/Variable");
const { BooleanLiteral } = require('./expressiontypes/literals/Boolean'); const { BooleanLiteral } = require('./expressiontypes/literals/Boolean');
const { CharacterLiteral } = require('./expressiontypes/literals/Character'); const { CharacterLiteral } = require('./expressiontypes/literals/Character');
@@ -843,15 +845,15 @@ function enumValueList(type, tokens, imports, typemap) {
* @param {Map<string,CEIType>} typemap * @param {Map<string,CEIType>} typemap
*/ */
function statementBlock(tokens, mdecls, method, imports, typemap) { function statementBlock(tokens, mdecls, method, imports, typemap) {
const b = new Block(); const block = new Block(tokens.current);
tokens.expectValue('{'); tokens.expectValue('{');
mdecls.pushScope(); mdecls.pushScope();
while (!tokens.isValue('}')) { while (!tokens.isValue('}')) {
const s = statement(tokens, mdecls, method, imports, typemap); const s = statement(tokens, mdecls, method, imports, typemap);
b.statements.push(s); block.statements.push(s);
} }
mdecls.popScope(); mdecls.popScope();
return b; return block;
} }
/** /**
@@ -1538,15 +1540,16 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
// the result of a bracketed expression is always a value, never a variable // the result of a bracketed expression is always a value, never a variable
// - this prevents things like: (a) = 5; // - this prevents things like: (a) = 5;
return new ResolvedIdent(`(${matches.source})`, [new BracketedExpression(matches)]); return new ResolvedIdent(`(${matches.source})`, [new BracketedExpression(matches)]);
case tokens.isValue('{') && 'symbol': case tokens.current.value === '{' && 'symbol':
// array initer // array initer
let elements = []; let elements = [], open = tokens.current;
tokens.expectValue('{');
if (!tokens.isValue('}')) { if (!tokens.isValue('}')) {
elements = expressionList(tokens, mdecls, scope, imports, typemap, { isArrayLiteral:true }); elements = expressionList(tokens, mdecls, scope, imports, typemap, { isArrayLiteral:true });
tokens.expectValue('}'); tokens.expectValue('}');
} }
const ident = `{${elements.map(e => e.source).join(',')}}`; const ident = `{${elements.map(e => e.source).join(',')}}`;
return new ResolvedIdent(ident, [new ArrayValueExpression(elements)]); return new ResolvedIdent(ident, [new ArrayValueExpression(elements, open)]);
default: default:
addproblem(tokens, ParseProblem.Error(tokens.current, 'Expression expected')); addproblem(tokens, ParseProblem.Error(tokens.current, 'Expression expected'));
return new ResolvedIdent(''); return new ResolvedIdent('');
@@ -1563,9 +1566,10 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
* @param {Map<string,CEIType>} typemap * @param {Map<string,CEIType>} typemap
*/ */
function newTerm(tokens, mdecls, scope, imports, typemap) { function newTerm(tokens, mdecls, scope, imports, typemap) {
const new_token = tokens.current;
tokens.expectValue('new'); tokens.expectValue('new');
const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]}); const ctr_type = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]});
let match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]); let match = new ResolvedIdent(`new ${ctr_type.resolved.simpleTypeName}`, [], [], [ctr_type.resolved]);
let ctr_args = [], type_body = null; let ctr_args = [], type_body = null;
switch(tokens.current.value) { switch(tokens.current.value) {
case '[': case '[':
@@ -1575,7 +1579,7 @@ function newTerm(tokens, mdecls, scope, imports, typemap) {
// array init // array init
rootTerm(tokens, mdecls, scope, imports, typemap); rootTerm(tokens, mdecls, scope, imports, typemap);
} }
return new ResolvedIdent(match.source, [new NewArray(ctr_type, match)]); return new ResolvedIdent(match.source, [new NewArray(new_token, ctr_type, match)]);
case '(': case '(':
tokens.inc(); tokens.inc();
if (!tokens.isValue(')')) { if (!tokens.isValue(')')) {
@@ -1592,7 +1596,7 @@ function newTerm(tokens, mdecls, scope, imports, typemap) {
addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected')); addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected'));
break; break;
} }
return new ResolvedIdent(match.source, [new NewObject(ctr_type, ctr_args, type_body)]); return new ResolvedIdent(match.source, [new NewObject(new_token, ctr_type, ctr_args, type_body)]);
} }
/** /**
@@ -1630,28 +1634,6 @@ function arrayElementOrConstructor(tokens, open_array, instance, index) {
return new ResolvedIdent(ident, [new ArrayIndexExpression(instance, index)]); return new ResolvedIdent(ident, [new ArrayIndexExpression(instance, index)]);
} }
/**
* @param {CEIType} type
*/
function getTypeInheritanceList(type) {
const types = {
/** @type {JavaType[]} */
list: [type],
/** @type {Set<JavaType>} */
done: new Set(),
};
for (let type; type = types.list.shift(); ) {
if (types.done.has(type)) {
continue;
}
types.done.add(type);
if (type instanceof CEIType)
types.list.push(...type.supers);
}
return Array.from(types.done);
}
/** /**
* @param {ResolvedIdent} matches * @param {ResolvedIdent} matches
* @param {TokenList} tokens * @param {TokenList} tokens
@@ -1807,7 +1789,7 @@ function arrayTypeExpression(matches) {
*/ */
function resolveIdentifier(tokens, mdecls, scope, imports, typemap) { function resolveIdentifier(tokens, mdecls, scope, imports, typemap) {
const ident = tokens.current.value; const ident = tokens.current.value;
const matches = findIdentifier(ident, mdecls, scope, imports, typemap); const matches = findIdentifier(tokens.current, mdecls, scope, imports, typemap);
checkIdentifierFound(tokens, ident, matches); checkIdentifierFound(tokens, ident, matches);
return matches; return matches;
} }
@@ -1827,20 +1809,22 @@ function checkIdentifierFound(tokens, ident, matches) {
} }
/** /**
* @param {string} ident * @param {Token} token
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {Scope} scope * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap * @param {Map<string,CEIType>} typemap
*/ */
function findIdentifier(ident, mdecls, scope, imports, typemap) { function findIdentifier(token, mdecls, scope, imports, typemap) {
const ident = token.value;
const matches = new ResolvedIdent(ident); const matches = new ResolvedIdent(ident);
matches.tokens = [token];
// is it a local or parameter - note that locals must be ordered innermost-scope-first // is it a local or parameter - note that locals must be ordered innermost-scope-first
const local = mdecls.locals.find(local => local.name === ident); const local = mdecls.locals.find(local => local.name === ident);
let param = scope && !(scope instanceof SourceType) && scope.parameters.find(p => p.name === ident); let param = scope && !(scope instanceof SourceType) && scope.parameters.find(p => p.name === ident);
if (local || param) { if (local || param) {
matches.variables = [local || param]; matches.variables = [new Variable(token, local || param)];
} else if (scope) { } else if (scope) {
// is it a field, method or enum value in the current type (or any of the outer types or superclasses) // is it a field, method or enum value in the current type (or any of the outer types or superclasses)
const scoped_type = scope instanceof SourceType ? scope : scope.owner; const scoped_type = scope instanceof SourceType ? scope : scope.owner;
@@ -1857,12 +1841,12 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) {
if (!matches.variables[0]) { if (!matches.variables[0]) {
const field = type.fields.find(f => f.name === ident); const field = type.fields.find(f => f.name === ident);
if (field) { if (field) {
matches.variables = [field]; matches.variables = [new Variable(token, field)];
return; return;
} }
const enumValue = (type instanceof SourceType) && type.enumValues.find(e => e.ident.value === ident); const enumValue = (type instanceof SourceType) && type.enumValues.find(e => e.ident.value === ident);
if (enumValue) { if (enumValue) {
matches.variables = [enumValue]; matches.variables = [new Variable(token, enumValue)];
return; return;
} }
} }
@@ -1883,7 +1867,7 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) {
imp.members.forEach(member => { imp.members.forEach(member => {
if (member.name === ident) { if (member.name === ident) {
if (member instanceof Field) { if (member instanceof Field) {
matches.variables.push(member); matches.variables.push(new Variable(token, member));
} else if (member instanceof Method) { } else if (member instanceof Method) {
matches.methods.push(member); matches.methods.push(member);
} }

View File

@@ -7,7 +7,7 @@ const { Token } = require('./tokenizer');
class ResolvedIdent { class ResolvedIdent {
/** /**
* @param {string} ident * @param {string} ident
* @param {(Local|Parameter|Field|Expression)[]} variables * @param {Expression[]} variables
* @param {Method[]} methods * @param {Method[]} methods
* @param {JavaType[]} types * @param {JavaType[]} types
* @param {string} package_name * @param {string} package_name

View File

@@ -0,0 +1,175 @@
/**
* @typedef {import('./tokenizer').Token} Token
*/
const ParseProblem = require('./parsetypes/parse-problem');
const { TypeVariable, JavaType, PrimitiveType, NullType, ArrayType, CEIType, WildcardType, TypeVariableType, InferredTypeArgument } = require('java-mti');
const { AnyType } = require('./anys');
const { Local } = require('./body-types');
const { Expression } = require('./expressiontypes/Expression');
const { Variable } = require('./expressiontypes/Variable');
/**
* @param {import('./body-types').ResolvedIdent} e
* @param {JavaType} assign_type
* @param {Map<string,CEIType>} typemap
* @param {ParseProblem[]} problems
*/
function checkAssignment(e, assign_type, typemap, problems) {
const value = e.variables[0];
if (value instanceof Variable) {
checkTypeAssignable(assign_type, value.type, () => value.name_token, problems);
return;
}
if (value instanceof Expression) {
const expression_result_type = null;//value.resolveType(typemap);
checkTypeAssignable(assign_type, expression_result_type, value.tokens, problems);
return;
}
}
/**
*
* @param {JavaType} variable_type
* @param {JavaType} value_type
* @param {() => Token|Token[]} tokens
*/
function checkTypeAssignable(variable_type, value_type, tokens, problems) {
if (!isTypeAssignable(variable_type, value_type)) {
const t = tokens();
problems.push(ParseProblem.Error(t, `Incompatible types: Expression of type '${value_type.fullyDottedTypeName}' cannot be assigned to a variable of type '${variable_type.fullyDottedTypeName}'`));
}
}
/**
* Set of regexes to map source primitives to their destination types.
* eg, long (J) is type-assignable to long, float and double (and their boxed counterparts)
* Note that void (V) is never type-assignable to anything
*/
const valid_primitive_types = {
// conversions from a primitive to a value
from: {
B: /^[BSIJFD]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Float|Double);$/,
S: /^[SIJFD]$|^Ljava\/lang\/(Short|Integer|Long|Float|Double);$/,
I: /^[IJFD]$|^Ljava\/lang\/(Integer|Long|Float|Double);$/,
J: /^[JFD]$|^Ljava\/lang\/(Long|Float|Double);$/,
F: /^[FD]$|^Ljava\/lang\/(Float|Double);$/,
D: /^D$|^Ljava\/lang\/(Double);$/,
C: /^[CIJFD]$|^Ljava\/lang\/(Character|Integer|Long|Float|Double);$/,
Z: /^Z$|^Ljava\/lang\/(Boolean);$/,
V: /$^/, // V.test() always returns false
},
// conversions to a primitive from a value
to: {
B: /^[B]$|^Ljava\/lang\/(Byte);$/,
S: /^[BS]$|^Ljava\/lang\/(Byte|Short);$/,
I: /^[BSIC]$|^Ljava\/lang\/(Byte|Short|Integer|Character);$/,
J: /^[BSIJC]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character);$/,
F: /^[BSIJCF]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character|Float);$/,
D: /^[BSIJCFD]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character|Float|Double);$/,
C: /^C$|^Ljava\/lang\/(Character);$/,
Z: /^Z$|^Ljava\/lang\/(Boolean);$/,
V: /$^/, // V.test() always returns false
}
}
/**
* Returns true if a value of value_type is assignable to a variable of dest_type
* @param {JavaType} dest_type
* @param {JavaType} value_type
*/
function isTypeAssignable(dest_type, value_type) {
let is_assignable = false;
if (dest_type.typeSignature === value_type.typeSignature) {
// exact signature match
is_assignable = true;
} else if (dest_type instanceof AnyType || value_type instanceof AnyType) {
// everything is assignable to or from AnyType
is_assignable = true;
} else if (dest_type.rawTypeSignature === 'Ljava/lang/Object;') {
// everything is assignable to Object
is_assignable = true;
} else if (value_type instanceof PrimitiveType) {
// primitive values can only be assigned to wider primitives or their class equivilents
is_assignable = valid_primitive_types.from[value_type.typeSignature].test(dest_type.typeSignature);
} else if (dest_type instanceof PrimitiveType) {
// primitive variables can only be assigned from narrower primitives or their class equivilents
is_assignable = valid_primitive_types.to[dest_type.typeSignature].test(value_type.typeSignature);
} else if (value_type instanceof NullType) {
// null is assignable to any non-primitive
is_assignable = !(dest_type instanceof PrimitiveType);
} else if (value_type instanceof ArrayType) {
// arrays are assignable to other arrays with the same dimensionality and type-assignable bases
is_assignable = dest_type instanceof ArrayType
&& dest_type.arrdims === value_type.arrdims
&& isTypeAssignable(dest_type.base, value_type.base);
} else if (value_type instanceof CEIType && dest_type instanceof CEIType) {
// class/interfaces types are assignable to any class/interface types in their inheritence tree
const valid_types = getTypeInheritanceList(value_type);
is_assignable = valid_types.includes(dest_type);
if (!is_assignable) {
// generic types are also assignable to their raw counterparts
const valid_raw_types = valid_types.map(t => t.getRawType());
is_assignable = valid_raw_types.includes(dest_type);
if (!is_assignable) {
// generic types are also assignable to compatible wildcard type bounds
const raw_type = valid_raw_types.find(rt => rt.rawTypeSignature === dest_type.rawTypeSignature);
if (raw_type instanceof CEIType && raw_type.typeVariables.length === value_type.typeVariables.length) {
is_assignable = dest_type.typeVariables.every((dest_tv, idx) => isTypeArgumentCompatible(dest_tv, value_type.typeVariables[idx].type));
}
}
}
}
return is_assignable;
}
/**
* @param {TypeVariable} dest_typevar
* @param {JavaType} value_typevar_type
*/
function isTypeArgumentCompatible(dest_typevar, value_typevar_type) {
if (dest_typevar.type instanceof WildcardType) {
if (!dest_typevar.type.bound) {
// unbounded wildcard types are compatible with everything
return true;
}
if (dest_typevar.type.bound.type === value_typevar_type) {
return true;
}
switch (dest_typevar.type.bound.kind) {
case 'extends':
return isTypeAssignable(dest_typevar.type.bound.type, value_typevar_type);
case 'super':;
return isTypeAssignable(value_typevar_type, dest_typevar.type.bound.type);
}
return false;
}
if (value_typevar_type instanceof TypeVariableType) {
// inferred type arguments of the form `x = List<>` are compatible with every destination type variable
return value_typevar_type.typeVariable instanceof InferredTypeArgument;
}
return dest_typevar.type === value_typevar_type;
}
/**
* @param {CEIType} type
*/
function getTypeInheritanceList(type) {
const types = {
/** @type {JavaType[]} */
list: [type],
/** @type {Set<JavaType>} */
done: new Set(),
};
for (let type; type = types.list.shift(); ) {
if (types.done.has(type)) {
continue;
}
types.done.add(type);
if (type instanceof CEIType)
types.list.push(...type.supers);
}
return Array.from(types.done);
}
exports.checkAssignment = checkAssignment;
exports.getTypeInheritanceList = getTypeInheritanceList;

View File

@@ -13,6 +13,10 @@ class ArrayIndexExpression extends Expression {
this.instance = instance; this.instance = instance;
this.index = index; this.index = index;
} }
tokens() {
return [...this.instance.tokens, ...this.index.tokens];
}
} }
exports.ArrayIndexExpression = ArrayIndexExpression; exports.ArrayIndexExpression = ArrayIndexExpression;

View File

@@ -1,15 +1,22 @@
/** /**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent * @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../tokenizer').Token} Token
*/ */
const { Expression } = require("./Expression"); const { Expression } = require("./Expression");
class ArrayValueExpression extends Expression { class ArrayValueExpression extends Expression {
/** /**
* @param {ResolvedIdent[]} elements * @param {ResolvedIdent[]} elements
* @param {Token} open
*/ */
constructor(elements) { constructor(elements, open) {
super(); super();
this.elements = elements; this.elements = elements;
this.open = open;
}
tokens() {
return this.open;
} }
} }

View File

@@ -16,6 +16,10 @@ class BinaryOpExpression extends Expression {
this.op = op; this.op = op;
this.rhs = rhs; this.rhs = rhs;
} }
tokens() {
return [...this.lhs.tokens, this.op, ...this.rhs.tokens];
}
} }
exports.BinaryOpExpression = BinaryOpExpression; exports.BinaryOpExpression = BinaryOpExpression;

View File

@@ -11,6 +11,10 @@ class BracketedExpression extends Expression {
super(); super();
this.expression = expression; this.expression = expression;
} }
tokens() {
return this.expression.tokens;
}
} }
exports.BracketedExpression = BracketedExpression; exports.BracketedExpression = BracketedExpression;

View File

@@ -13,6 +13,10 @@ class CastExpression extends Expression {
this.castType = castType; this.castType = castType;
this.expression = expression; this.expression = expression;
} }
tokens() {
return [...this.castType.tokens, ...this.expression.tokens];
}
} }
exports.CastExpression = CastExpression; exports.CastExpression = CastExpression;

View File

@@ -14,5 +14,9 @@ class ClassMemberExpression extends Expression {
this.instance = instance; this.instance = instance;
this.classToken = class_token; this.classToken = class_token;
} }
tokens() {
return this.classToken;
}
} }
exports.ClassMemberExpression = ClassMemberExpression; exports.ClassMemberExpression = ClassMemberExpression;

View File

@@ -1,5 +1,12 @@
/**
* @typedef {import('../tokenizer').Token} Token
*/
class Expression { class Expression {
/** @returns {Token|Token[]} */
tokens() {
throw new Error('Expression.tokens');
}
} }
exports.Expression = Expression; exports.Expression = Expression;

View File

@@ -16,6 +16,10 @@ class IncDecExpression extends Expression {
this.operator = operator; this.operator = operator;
this.which = which; this.which = which;
} }
tokens() {
return this.operator;
}
} }
exports.IncDecExpression = IncDecExpression; exports.IncDecExpression = IncDecExpression;

View File

@@ -1,19 +1,26 @@
/** /**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent * @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../statementtypes/Block').Block} Block
*/ */
const { Expression } = require("./Expression"); const { Expression } = require("./Expression");
const { Block } = require('../statementtypes/Block');
class LambdaExpression extends Expression { class LambdaExpression extends Expression {
/** /**
* *
* @param {*[]} params * @param {*[]} params
* @param {Expression|Block} body * @param {ResolvedIdent|Block} body
*/ */
constructor(params, body) { constructor(params, body) {
super(); super();
this.params = params; this.params = params;
this.body = body; this.body = body;
} }
tokens() {
if (this.body instanceof Block) {
return this.body.open;
}
return this.body.tokens;
}
} }
exports.LambdaExpression = LambdaExpression; exports.LambdaExpression = LambdaExpression;

View File

@@ -15,6 +15,10 @@ class MemberExpression extends Expression {
// member will be null for incomplete expressions // member will be null for incomplete expressions
this.member = member; this.member = member;
} }
tokens() {
return this.member;
}
} }
exports.MemberExpression = MemberExpression; exports.MemberExpression = MemberExpression;

View File

@@ -13,6 +13,10 @@ class MethodCallExpression extends Expression {
this.instance = instance; this.instance = instance;
this.args = args; this.args = args;
} }
tokens() {
return this.instance.tokens;
}
} }
exports.MethodCallExpression = MethodCallExpression; exports.MethodCallExpression = MethodCallExpression;

View File

@@ -1,34 +1,47 @@
/** /**
* @typedef {import('../tokenizer').Token} Token * @typedef {import('../tokenizer').Token} Token
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent * @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../source-types').SourceTypeIdent} SourceTypeIdent
* @typedef {import('java-mti').JavaType} JavaType * @typedef {import('java-mti').JavaType} JavaType
*/ */
const { Expression } = require("./Expression"); const { Expression } = require("./Expression");
class NewArray extends Expression { class NewArray extends Expression {
/** /**
* @param {JavaType} element_type * @param {Token} new_token
* @param {SourceTypeIdent} element_type
* @param {ResolvedIdent} dimensions * @param {ResolvedIdent} dimensions
*/ */
constructor(element_type, dimensions) { constructor(new_token, element_type, dimensions) {
super(); super();
this.new_token = new_token;
this.element_type = element_type; this.element_type = element_type;
this.dimensions = dimensions; this.dimensions = dimensions;
} }
tokens() {
return [this.new_token, ...this.element_type.tokens, ...this.dimensions.tokens];
}
} }
class NewObject extends Expression { class NewObject extends Expression {
/** /**
* @param {JavaType} object_type * @param {Token} new_token
* @param {SourceTypeIdent} object_type
* @param {ResolvedIdent[]} ctr_args * @param {ResolvedIdent[]} ctr_args
* @param {Token[]} type_body * @param {Token[]} type_body
*/ */
constructor(object_type, ctr_args, type_body) { constructor(new_token, object_type, ctr_args, type_body) {
super(); super();
this.element_type = object_type; this.new_token = new_token;
this.object_type = object_type;
this.ctr_args = ctr_args; this.ctr_args = ctr_args;
this.type_body = type_body; this.type_body = type_body;
} }
tokens() {
return [this.new_token, ...this.object_type.tokens];
}
} }
exports.NewArray = NewArray; exports.NewArray = NewArray;

View File

@@ -15,6 +15,10 @@ class TernaryOpExpression extends Expression {
this.truthExpression = truthExpression; this.truthExpression = truthExpression;
this.falseExpression = falseExpression; this.falseExpression = falseExpression;
} }
tokens() {
return [...this.test.tokens, ...this.truthExpression.tokens, ...this.falseExpression.tokens];
}
} }
exports.TernaryOpExpression = TernaryOpExpression; exports.TernaryOpExpression = TernaryOpExpression;

View File

@@ -14,6 +14,10 @@ class ThisMemberExpression extends Expression {
this.instance = instance; this.instance = instance;
this.thisToken = this_token; this.thisToken = this_token;
} }
tokens() {
return this.thisToken;
}
} }
exports.ThisMemberExpression = ThisMemberExpression; exports.ThisMemberExpression = ThisMemberExpression;

View File

@@ -0,0 +1,28 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').Local} Local
* @typedef {import('../tokenizer').Token} Token
* @typedef {import('java-mti').Field} Field
* @typedef {import('java-mti').Parameter} Parameter
* @typedef {import('../source-types').SourceEnumValue} SourceEnumValue
*/
const { Expression } = require("./Expression");
class Variable extends Expression {
/**
* @param {Token} name_token
* @param {Local|Parameter|Field|SourceEnumValue} variable
*/
constructor(name_token, variable) {
super();
this.name_token = name_token;
this.variable = variable;
this.type = this.variable.type;
}
tokens() {
return this.name_token;
}
}
exports.Variable = Variable;

View File

@@ -11,6 +11,10 @@ class LiteralValue extends Expression {
super(); super();
this.token = token; this.token = token;
} }
tokens() {
return this.token;
}
} }
exports.LiteralValue = LiteralValue; exports.LiteralValue = LiteralValue;

View File

@@ -498,3 +498,4 @@ exports.SourceAnnotation = SourceAnnotation;
exports.SourceUnit = SourceUnit; exports.SourceUnit = SourceUnit;
exports.SourcePackage = SourcePackage; exports.SourcePackage = SourcePackage;
exports.SourceImport = SourceImport; exports.SourceImport = SourceImport;
exports.SourceEnumValue = SourceEnumValue;

View File

@@ -1,8 +1,19 @@
/**
* @typedef {import('../tokenizer').Token} Token
*/
const { Statement } = require("./Statement"); const { Statement } = require("./Statement");
class Block extends Statement { class Block extends Statement {
/** @type {Statement[]} */ /** @type {Statement[]} */
statements = []; statements = [];
/**
* @param {Token} open
*/
constructor(open) {
super();
this.open = open;
}
} }
exports.Block = Block; exports.Block = Block;