package org.develnext.jphp.core.syntax;
import org.develnext.jphp.core.syntax.generators.*;
import org.develnext.jphp.core.syntax.generators.manually.BodyGenerator;
import org.develnext.jphp.core.syntax.generators.manually.SimpleExprGenerator;
import org.develnext.jphp.core.tokenizer.Tokenizer;
import org.develnext.jphp.core.tokenizer.token.CommentToken;
import org.develnext.jphp.core.tokenizer.token.Token;
import org.develnext.jphp.core.tokenizer.token.expr.ValueExprToken;
import org.develnext.jphp.core.tokenizer.token.expr.value.ClosureStmtToken;
import org.develnext.jphp.core.tokenizer.token.expr.value.FulledNameToken;
import org.develnext.jphp.core.tokenizer.token.expr.value.NameToken;
import org.develnext.jphp.core.tokenizer.token.stmt.*;
import php.runtime.env.Context;
import php.runtime.env.Environment;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import static org.develnext.jphp.core.tokenizer.token.stmt.NamespaceUseStmtToken.UseType.CONSTANT;
public class SyntaxAnalyzer {
private Tokenizer tokenizer;
private List<Token> tokens;
private List<Token> tree;
private List<Generator> generators;
private final Map<Class<? extends Generator>, Generator> map = new HashMap<Class<? extends Generator>, Generator>();
private NamespaceStmtToken namespace = NamespaceStmtToken.getDefault();
private ClassStmtToken clazz;
private FunctionStmtToken function;
private Stack<FunctionStmtToken> closureStack;
private Stack<Scope> scopeStack;
private Stack<Scope> rootScopeStack = new Stack<Scope>();
//private Stack<Set<VariableExprToken>> localStack;
//private Stack<Set<VariableExprToken>> rootLocalStack = new Stack<Set<VariableExprToken>>();
private Map<String, ClassStmtToken> classes;
private List<FunctionStmtToken> functions;
private List<ConstStmtToken> constants;
private List<ClosureStmtToken> closures;
private Environment environment;
private int generatorSize = 0;
public SyntaxAnalyzer(Environment environment, Tokenizer tokenizer){
this(environment, tokenizer, null);
}
public void reset(Environment environment, Tokenizer tokenizer){
removeScope();
//removeLocalScope();
generatorSize = 0;
tokenizer.reset();
this.environment = environment;
this.tokenizer = tokenizer;
this.function = null;
classes.clear();
functions.clear();
constants.clear();
closures.clear();
closureStack.clear();
tokens.clear();
tree.clear();
//localStack.clear();
scopeStack.clear();
addScope(true);
//addLocalScope(true);
process();
}
public SyntaxAnalyzer(Environment environment, Tokenizer tokenizer, FunctionStmtToken function) {
if (tokenizer != null)
tokenizer.reset();
this.environment = environment;
this.tokenizer = tokenizer;
this.function = function;
classes = new LinkedHashMap<String, ClassStmtToken>();
functions = new ArrayList<FunctionStmtToken>();
constants = new ArrayList<ConstStmtToken>();
closures = new ArrayList<ClosureStmtToken>();
closureStack = new Stack<FunctionStmtToken>();
tokens = new LinkedList<Token>();
tree = new ArrayList<Token>();
//localStack = new Stack<Set<VariableExprToken>>();
scopeStack = new Stack<Scope>();
generators = new ArrayList<Generator>(50);
generators.add(new NamespaceGenerator(this));
generators.add(new UseGenerator(this));
generators.add(new ClassGenerator(this));
generators.add(new ConstGenerator(this));
generators.add(new FunctionGenerator(this));
generators.add(new TryCatchGenerator(this));
generators.add(new ThrowGenerator(this));
generators.add(new NameGenerator(this));
// non-automatic
generators.add(new SimpleExprGenerator(this));
generators.add(new BodyGenerator(this));
generators.add(new ExprGenerator(this));
for (Generator generator : generators)
map.put(generator.getClass(), generator);
addScope(true);
//addLocalScope(true);
if (tokenizer != null)
process();
}
public Environment getEnvironment() {
return environment;
}
public void registerClass(ClassStmtToken clazz){
classes.put(clazz.getFulledName().toLowerCase(), clazz);
for (MethodStmtToken method : clazz.getMethods()) {
if (method.isGenerator()) {
method.setGeneratorId(generatorSize++);
}
}
}
public void registerFunction(FunctionStmtToken function){
if (function.getId() <= functions.size()){
function.setId(functions.size());
if (function.isGenerator()) {
function.setGeneratorId(generatorSize++);
}
functions.add(function);
}
}
public void registerConstant(ConstStmtToken constant){
constants.add(constant);
}
public void registerClosure(ClosureStmtToken closure){
closure.setId(closures.size());
if (closure.getFunction().isGenerator()) {
closure.getFunction().setGeneratorId(generatorSize++);
}
closures.add(closure);
}
public Collection<ClassStmtToken> getClasses() {
return classes.values();
}
public Collection<FunctionStmtToken> getFunctions() {
return functions;
}
public Collection<ConstStmtToken> getConstants(){
return constants;
}
public Collection<ClosureStmtToken> getClosures(){
return closures;
}
public ClassStmtToken findClass(String name){
return classes.get(name.toLowerCase());
}
protected void process(){
tokenizer.reset();
tree.clear();
Token token;
while ((token = tokenizer.nextToken()) != null){
if (token instanceof CommentToken){
if (((CommentToken) token).getKind() != CommentToken.Kind.DOCTYPE)
continue;
}
tokens.add(token);
}
/*if (tokenizer.hasDirective("mode")){
Directive mode = tokenizer.getDirective("mode");
try {
this.langMode = LangMode.valueOf(mode.value.toUpperCase());
} catch (IllegalArgumentException e){
environment.warning(
mode.trace, "Invalid value '%s' for directive 'mode'", mode.value
);
}
}*/
ListIterator<Token> iterator = tokens.listIterator();
tree = process(iterator);
}
protected void registerToken(Token token){
if (token instanceof ClassStmtToken)
registerClass((ClassStmtToken)token);
else if (token instanceof FunctionStmtToken)
registerFunction((FunctionStmtToken)token);
else if (token instanceof ConstStmtToken)
registerConstant((ConstStmtToken)token);
}
public List<Token> process(ListIterator<Token> iterator){
List<Token> result = new ArrayList<Token>();
while (iterator.hasNext()){
Token gen = processNext(iterator);
if (gen instanceof NamespaceStmtToken) {
List<Token> tree = ((NamespaceStmtToken) gen).getTree();
((NamespaceStmtToken) gen).setTree(null);
result.add(gen);
registerToken(gen);
result.addAll(tree);
if (!((NamespaceStmtToken) gen).isTokenRegistered()) {
for (Token el : tree) {
registerToken(el);
}
}
} else {
result.add(gen);
registerToken(gen);
}
}
return result;
}
public Token processNext(ListIterator<Token> iterator){
Token current = iterator.next();
Token gen = generateToken(current, iterator);
return (gen == null ? current : gen);
}
public Scope addScope(boolean isRoot) {
Scope scope = new Scope(scopeStack.empty() ? null : scopeStack.peek());
scopeStack.push(scope);
if (isRoot)
rootScopeStack.push(scope);
return scope;
}
public Scope addScope(){
return addScope(false);
}
public Scope removeScope() {
Scope scope = getScope();
if (rootScopeStack.peek() == scope)
rootScopeStack.pop();
else
rootScopeStack.peek().appendScope(scope);
return scopeStack.pop();
}
public Scope getScope() {
return scopeStack.peek();
}
/*
public Set<VariableExprToken> addLocalScope(){
return addLocalScope(false);
}
public Set<VariableExprToken> addLocalScope(boolean isRoot){
Set<VariableExprToken> local = new HashSet<VariableExprToken>();
localStack.push(local);
if (isRoot)
rootLocalStack.push(local);
return local;
}
public Set<VariableExprToken> removeLocalScope(){
Set<VariableExprToken> scope = getLocalScope();
if (rootLocalStack.peek() == scope){
rootLocalStack.pop();
} else
rootLocalStack.peek().addAll(getLocalScope());
return localStack.pop();
}
public Set<VariableExprToken> getLocalScope(){
return localStack.peek();
} */
@SuppressWarnings("unchecked")
public <T extends Generator> T generator(Class<T> clazz){
Generator generator = map.get(clazz);
if (generator == null)
throw new AssertionError("Generator '"+clazz.getName()+"' not found");
if (generator.isSingleton())
return (T) generator;
Constructor<T> constructor = null;
try {
constructor = clazz.getConstructor(SyntaxAnalyzer.class);
return constructor.newInstance(this);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public Token generateToken(Token current, ListIterator<Token> iterator){
Token gen = null;
for(Generator generator : generators){
if (!generator.isAutomatic())
continue;
gen = generator.getToken(current, iterator);
if (gen != null)
break;
}
return gen;
}
public List<Token> getTree(){
return tree;
}
public Context getContext(){
return tokenizer.getContext();
}
public NamespaceStmtToken getNamespace() {
return namespace;
}
public void setNamespace(NamespaceStmtToken namespace) {
this.namespace = namespace;
}
public ClassStmtToken getClazz() {
return clazz;
}
public void setClazz(ClassStmtToken clazz) {
this.clazz = clazz;
}
public FunctionStmtToken getFunction(boolean absolute) {
if (!absolute){
FunctionStmtToken func = peekClosure();
if (func != null)
return func;
}
return function;
}
public FunctionStmtToken getFunction() {
return getFunction(false);
}
public void setFunction(FunctionStmtToken function) {
this.function = function;
}
public void pushClosure(FunctionStmtToken closure) {
closureStack.push(closure);
}
public FunctionStmtToken peekClosure() {
if (closureStack.empty())
return null;
else
return closureStack.peek();
}
public FunctionStmtToken popClosure(){
return closureStack.pop();
}
public ValueExprToken getRealName(ValueExprToken value, NamespaceUseStmtToken.UseType useType){
if (value == null)
return null;
if (value instanceof NameToken)
return getRealName((NameToken)value, useType);
else
return value;
}
public FulledNameToken getRealName(NameToken what, NamespaceUseStmtToken.UseType useType) {
return getRealName(what, namespace, useType);
}
public FulledNameToken getRealName(NameToken what) {
return getRealName(what, NamespaceUseStmtToken.UseType.CLASS);
}
public static FulledNameToken getRealName(NameToken what, NamespaceStmtToken namespace) {
return getRealName(what, namespace, NamespaceUseStmtToken.UseType.CLASS);
}
public static FulledNameToken getRealName(NameToken what, NamespaceStmtToken namespace, NamespaceUseStmtToken.UseType useType) {
if (what instanceof FulledNameToken) {
FulledNameToken fulledNameToken = (FulledNameToken) what;
if (fulledNameToken.isProcessed(useType)) {
return fulledNameToken;
} else {
if (fulledNameToken.isAnyProcessed()) {
what = (fulledNameToken).getLastName();
}
}
}
String name = what.getName();
// check name in uses
if (namespace != null) {
for (NamespaceUseStmtToken use : namespace.getUses()) {
if (use.getUseType() != useType) {
continue;
}
if (use.getAs() == null) {
String string = use.getName().getLastName().getName();
if ((useType == CONSTANT && name.equals(string)) || (useType != CONSTANT && name.equalsIgnoreCase(string))) {
FulledNameToken t = new FulledNameToken(use.getName());
t.setProcessed(useType);
return t;
}
} else {
String string = use.getAs().getName();
if ((useType == CONSTANT && name.equals(string)) || (useType != CONSTANT && name.equalsIgnoreCase(string))) {
FulledNameToken t = new FulledNameToken(use.getName());
t.setProcessed(useType);
return t;
}
}
}
}
List<NameToken> names = namespace == null || namespace.getName() == null
? new ArrayList<NameToken>()
: new ArrayList<NameToken>(namespace.getName().getNames());
if (what instanceof FulledNameToken)
names.addAll(((FulledNameToken) what).getNames());
else
names.add(what);
FulledNameToken t = new FulledNameToken(what.getMeta(), names);
t.setProcessed(useType);
return t;
}
}