package org.elasticsearch.plan.a;
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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.
*/
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import static org.elasticsearch.plan.a.Caster.*;
import static org.elasticsearch.plan.a.Default.*;
import static org.elasticsearch.plan.a.Definition.*;
import static org.elasticsearch.plan.a.PlanAParser.*;
class Adapter {
static class Variable {
final String name;
final Type type;
final int slot;
private Variable(final String name, final Type type, final int slot) {
this.name = name;
this.type = type;
this.slot = slot;
}
}
static class StatementMetadata {
final ParserRuleContext source;
boolean allExit;
boolean allReturn;
boolean anyReturn;
boolean allBreak;
boolean anyBreak;
boolean allContinue;
boolean anyContinue;
private StatementMetadata(final ParserRuleContext source) {
this.source = source;
allExit = false;
allReturn = false;
anyReturn = false;
allBreak = false;
anyBreak = false;
allContinue = false;
anyContinue = false;
}
}
static class ExpressionMetadata {
final ParserRuleContext source;
boolean statement;
Object preConst;
Object postConst;
boolean isNull;
Type to;
Type from;
Promotion promotion;
boolean explicit;
Cast cast;
private ExpressionMetadata(final ParserRuleContext source) {
this.source = source;
statement = false;
preConst = null;
postConst = null;
isNull = false;
to = null;
from = null;
promotion = null;
explicit = false;
cast = null;
}
}
static class Branch {
final ParserRuleContext source;
Label begin;
Label end;
Label tru;
Label fals;
private Branch(final ParserRuleContext source) {
this.source = source;
begin = null;
end = null;
tru = null;
fals = null;
}
}
static String error(final ParserRuleContext ctx) {
return "Error [" + ctx.getStart().getLine() + ":" + ctx.getStart().getCharPositionInLine() + "]: ";
}
final Definition definition;
final Standard standard;
final Caster caster;
final String source;
final ParserRuleContext root;
private final Deque<Integer> scopes;
private final Deque<Variable> variables;
private final Map<ParserRuleContext, StatementMetadata> statementMetadata;
private final Map<ParserRuleContext, ExpressionMetadata> expressionMetadata;
private final Map<ParserRuleContext, External> externals;
private final Map<ParserRuleContext, Branch> branches;
private final Deque<Branch> jumps;
private final Set<ParserRuleContext> strings;
Adapter(final Definition definition, final Standard standard, final Caster caster,
final String source, final ParserRuleContext root) {
this.definition = definition;
this.standard = standard;
this.caster = caster;
this.source = source;
this.root = root;
scopes = new ArrayDeque<>();
variables = new ArrayDeque<>();
statementMetadata = new HashMap<>();
expressionMetadata = new HashMap<>();
externals = new HashMap<>();
branches = new HashMap<>();
jumps = new ArrayDeque<>();
strings = new HashSet<>();
}
void incrementScope() {
scopes.push(0);
}
void decrementScope() {
int remove = scopes.pop();
while (remove > 0) {
variables.pop();
--remove;
}
}
Variable getVariable(final String name) {
final Iterator<Variable> itr = variables.iterator();
while (itr.hasNext()) {
final Variable variable = itr.next();
if (variable.name.equals(name)) {
return variable;
}
}
return null;
}
Variable addVariable(final ParserRuleContext source, final String name, final Type type) {
if (getVariable(name) != null) {
if (source == null) {
throw new IllegalArgumentException("Argument name [" + name + "] already defined within the scope.");
} else {
throw new IllegalArgumentException(
error(source) + "Variable name [" + name + "] already defined within the scope.");
}
}
final Variable previous = variables.peekFirst();
int slot = 0;
if (previous != null) {
slot += previous.slot + previous.type.metadata.size;
}
final Variable variable = new Variable(name, type, slot);
variables.push(variable);
final int update = scopes.pop() + 1;
scopes.push(update);
return variable;
}
StatementMetadata createStatementMetadata(final ParserRuleContext source) {
final StatementMetadata sourcesmd = new StatementMetadata(source);
statementMetadata.put(source, sourcesmd);
return sourcesmd;
}
StatementMetadata getStatementMetadata(final ParserRuleContext source) {
final StatementMetadata sourcesmd = statementMetadata.get(source);
if (sourcesmd == null) {
throw new IllegalStateException(error(source) + "Statement metadata does not exist at" +
" the parse node with text [" + source.getText() + "].");
}
return sourcesmd;
}
ExpressionContext getExpressionContext(ExpressionContext source) {
if (source instanceof PrecedenceContext) {
final ParserRuleContext parent = source.getParent();
int index = 0;
for (final ParseTree child : parent.children) {
if (child == source) {
break;
}
++index;
}
while (source instanceof PrecedenceContext) {
source = ((PrecedenceContext)source).expression();
}
parent.children.set(index, source);
}
return source;
}
ExpressionMetadata createExpressionMetadata(ParserRuleContext source) {
final ExpressionMetadata sourceemd = new ExpressionMetadata(source);
expressionMetadata.put(source, sourceemd);
return sourceemd;
}
ExpressionMetadata getExpressionMetadata(final ParserRuleContext source) {
final ExpressionMetadata sourceemd = expressionMetadata.get(source);
if (sourceemd == null) {
throw new IllegalStateException(error(source) + "Expression metadata does not exist at" +
" the parse node with text [" + source.getText() + "].");
}
return sourceemd;
}
void putExternal(final ParserRuleContext source, final External external) {
externals.put(source, external);
}
External getExternal(final ParserRuleContext source) {
final External external = externals.get(source);
if (external == null) {
throw new IllegalStateException(error(source) + "External data does not exist at" +
" the parse node with text [" + source.getText() + "].");
}
return external;
}
Branch markBranch(final ParserRuleContext source, final ParserRuleContext... nodes) {
final Branch branch = new Branch(source);
for (final ParserRuleContext node : nodes) {
branches.put(node, branch);
}
return branch;
}
Branch getBranch(final ParserRuleContext source) {
return branches.get(source);
}
void checkWriteBranch(final MethodVisitor visitor, final ParserRuleContext source) {
final Branch branch = getBranch(source);
if (branch != null) {
if (branch.tru != null) {
visitor.visitJumpInsn(Opcodes.IFNE, branch.tru);
} else if (branch.fals != null) {
visitor.visitJumpInsn(Opcodes.IFEQ, branch.fals);
}
}
}
void pushJump(final Branch branch) {
jumps.push(branch);
}
Branch peekJump() {
return jumps.peek();
}
void popJump() {
jumps.pop();
}
void markStrings(final ParserRuleContext node) {
strings.add(node);
}
void unmarkStrings(final ParserRuleContext node) {
strings.remove(node);
}
boolean getStrings(final ParserRuleContext node) {
return strings.contains(node);
}
}