package org.develnext.jphp.core.syntax.generators;
import org.develnext.jphp.core.tokenizer.token.CommentToken;
import org.develnext.jphp.core.tokenizer.token.expr.operator.ArgumentUnpackExprToken;
import org.develnext.jphp.core.tokenizer.token.expr.value.*;
import php.runtime.common.HintType;
import org.develnext.jphp.core.syntax.SyntaxAnalyzer;
import org.develnext.jphp.core.syntax.generators.manually.BodyGenerator;
import org.develnext.jphp.core.syntax.generators.manually.SimpleExprGenerator;
import org.develnext.jphp.core.tokenizer.token.SemicolonToken;
import org.develnext.jphp.core.tokenizer.token.Token;
import org.develnext.jphp.core.tokenizer.token.expr.BraceExprToken;
import org.develnext.jphp.core.tokenizer.token.expr.CommaToken;
import org.develnext.jphp.core.tokenizer.token.expr.operator.AmpersandRefToken;
import org.develnext.jphp.core.tokenizer.token.expr.operator.AssignExprToken;
import org.develnext.jphp.core.tokenizer.token.stmt.*;
import java.util.*;
public class FunctionGenerator extends Generator<FunctionStmtToken> {
public FunctionGenerator(SyntaxAnalyzer analyzer) {
super(analyzer);
}
protected final static Set<String> scalarTypeHints = new HashSet<String>(){{
add("array");
add("callable");
}};
protected final static Set<String> jphp_scalarTypeHints = new HashSet<String>(){{
add("scalar");
add("number");
add("string");
add("int");
add("integer");
add("double");
add("float");
add("bool");
add("boolean");
}};
@SuppressWarnings("unchecked")
protected ArgumentStmtToken processArgument(ListIterator<Token> iterator){
boolean isReference = false;
boolean isVariadic = false;
VariableExprToken variable = null;
ExprStmtToken value = null;
Token next = nextToken(iterator);
if (next instanceof CommaToken || isClosedBrace(next, BraceExprToken.Kind.SIMPLE))
return null;
NameToken hintTypeClass = null;
HintType hintType = null;
if (next instanceof SelfExprToken) {
if (analyzer.getClazz() == null) {
unexpectedToken(next);
}
next = analyzer.getClazz().getName();
}
if (next instanceof NameToken){
String word = ((NameToken) next).getName().toLowerCase();
if (scalarTypeHints.contains(word))
hintType = HintType.of(word);
else {
hintType = jphp_scalarTypeHints.contains(word) ? null : HintType.of(word);
if (hintType == null)
hintTypeClass = analyzer.getRealName((NameToken)next);
}
next = nextToken(iterator);
}
if (next instanceof AmpersandRefToken){
isReference = true;
next = nextToken(iterator);
}
if (next instanceof ArgumentUnpackExprToken) {
isVariadic = true;
next = nextToken(iterator);
}
if (next instanceof VariableExprToken){
variable = (VariableExprToken)next;
} else
unexpectedToken(next);
next = nextToken(iterator);
if (next instanceof AssignExprToken) {
if (isVariadic) {
unexpectedToken(next);
}
value = analyzer.generator(SimpleExprGenerator.class).getToken(
nextToken(iterator), iterator, true, BraceExprToken.Kind.SIMPLE
);
} else {
if (next instanceof CommaToken || isClosedBrace(next, BraceExprToken.Kind.SIMPLE)){
if (next instanceof BraceExprToken) {
iterator.previous();
} else {
if (isVariadic) {
unexpectedToken(next);
}
}
} else
unexpectedToken(next);
}
ArgumentStmtToken argument = new ArgumentStmtToken(variable.getMeta());
argument.setName(variable);
argument.setHintType(hintType);
argument.setHintTypeClass(hintTypeClass);
argument.setReference(isReference);
argument.setVariadic(isVariadic);
argument.setValue(value);
if (argument.isReference() && argument.getValue() != null)
analyzer.getFunction().variable(argument.getName()).setUsed(true);
return argument;
}
protected void processArguments(FunctionStmtToken result, ListIterator<Token> iterator){
checkUnexpectedEnd(iterator);
List<ArgumentStmtToken> arguments = new ArrayList<ArgumentStmtToken>();
while (iterator.hasNext()){
ArgumentStmtToken argument = processArgument(iterator);
if (argument == null)
break;
arguments.add(argument);
}
result.setArguments(arguments);
}
protected void processUses(FunctionStmtToken result, ListIterator<Token> iterator){
Token next = nextToken(iterator);
if (next instanceof NamespaceUseStmtToken){
next = nextToken(iterator);
if (!isOpenedBrace(next, BraceExprToken.Kind.SIMPLE))
unexpectedToken(next, "(");
List<ArgumentStmtToken> arguments = new ArrayList<ArgumentStmtToken>();
while (iterator.hasNext()) {
ArgumentStmtToken argument = processArgument(iterator);
if (argument == null)
break;
if (argument.getValue() != null)
unexpectedToken(argument.getValue().getSingle());
arguments.add(argument);
FunctionStmtToken parent = analyzer.getFunction(true);
if (parent != null) {
parent.variable(argument.getName()).setUsed(true);
if (argument.isReference()){
parent.variable(argument.getName())
.setPassed(true)
.setUnstable(true);
}
parent = analyzer.peekClosure();
if (parent != null){
parent.variable(argument.getName()).setUnstable(true);
}
}
}
result.setUses(arguments);
} else {
result.setUses(new ArrayList<ArgumentStmtToken>());
iterator.previous();
}
}
protected void processBody(FunctionStmtToken result, ListIterator<Token> iterator){
Token next = nextToken(iterator);
if (isOpenedBrace(next, BraceExprToken.Kind.BLOCK)){
BodyStmtToken body = analyzer.generator(BodyGenerator.class).getToken(next, iterator);
result.setBody(body);
} else if (next instanceof SemicolonToken) {
result.setInterfacable(true);
result.setBody(null);
} else
unexpectedToken(next);
}
@SuppressWarnings("unchecked")
public FunctionStmtToken getToken(Token current, ListIterator<Token> iterator, boolean closureAllowed) {
if (current instanceof FunctionStmtToken) {
CommentToken commentToken = null;
iterator.previous();
if (iterator.hasPrevious()) {
int cnt = 0;
while (iterator.hasPrevious()) {
cnt++;
Token previous = iterator.previous();
if (previous.isNamedToken()) continue;
if (previous instanceof CommentToken && ((CommentToken) previous).getKind() == CommentToken.Kind.DOCTYPE) {
commentToken = ((CommentToken) previous);
}
break;
}
for (int i = 0; i < cnt; i++) {
iterator.next();
}
}
iterator.next();
FunctionStmtToken result = (FunctionStmtToken)current;
result.setStatic(analyzer.getFunction() == null);
Class<? extends Token>[] excludes = new Class[] {EchoStmtToken.class, ImportExprToken.class};
if (analyzer.getClazz() != null) {
excludes = new Class[0];
}
Token next = nextTokenSensitive(iterator, excludes);
if (next instanceof AmpersandRefToken){
result.setReturnReference(true);
next = nextTokenSensitive(iterator, excludes);
}
if (next instanceof NameToken){
/*if (analyzer.getFunction() != null)
unexpectedToken(current);*/
analyzer.addScope(true);
FunctionStmtToken oldFunction = analyzer.getFunction();
analyzer.setFunction(result);
BraceExprToken brace = nextAndExpected(iterator, BraceExprToken.class);
if (!brace.isSimpleOpened())
unexpectedToken(brace, "(");
result.setNamespace(analyzer.getNamespace());
result.setName((NameToken) next);
result.setDocComment(commentToken);
processArguments(result, iterator);
processBody(result, iterator);
result.setTypeInfo(analyzer.getScope().getTypeInfo());
result.setLabels(analyzer.getScope().getLabels());
result.setLocal(analyzer.removeScope().getVariables());
Token previous = iterator.previous();
result.getMeta().setEndLine(previous.getMeta().getStartLine());
result.getMeta().setEndPosition(previous.getMeta().getStartPosition());
iterator.next();
analyzer.setFunction(oldFunction);
return result;
} else if (next instanceof BraceExprToken){
// xClosure
if (((BraceExprToken) next).isSimpleOpened()){
if (closureAllowed){
analyzer.pushClosure(result);
analyzer.addScope(true);
processArguments(result, iterator);
processUses(result, iterator);
processBody(result, iterator);
//boolean thisExists = result.isThisExists();
result.setTypeInfo(analyzer.getScope().getTypeInfo());
result.setLabels(analyzer.getScope().getLabels());
result.setStaticExists(analyzer.getScope().isStaticExists());
result.setLocal(analyzer.removeScope().getVariables());
//result.setThisExists(thisExists);
analyzer.popClosure();
FunctionStmtToken prevClosure = analyzer.peekClosure();
if (prevClosure != null){
if (result.isThisExists()) {
analyzer.getScope().addVariable(FunctionStmtToken.thisVariable);
//prevClosure.variable(FunctionStmtToken.thisVariable).setUsed(true);
//prevClosure.setThisExists(true);
}
}
List<VariableExprToken> uses = new ArrayList<VariableExprToken>();
for(ArgumentStmtToken argument : result.getUses()){
if (argument.isReference()){
if (analyzer.getFunction() != null){
analyzer.getFunction().variable(argument.getName()).setReference(true);
}
}
if (analyzer.getFunction() != null) {
analyzer.getFunction().variable(argument.getName()).setUsed(true);
}
uses.add(argument.getName());
}
analyzer.getScope().addVariables(uses);
Token previous = iterator.previous();
result.getMeta().setEndLine(previous.getMeta().getStartLine());
result.getMeta().setEndPosition(previous.getMeta().getStartPosition());
iterator.next();
return result;
}
iterator.previous();
return null;
}
}
unexpectedToken(next);
}
return null;
}
@Override
public FunctionStmtToken getToken(Token current, ListIterator<Token> iterator) {
return getToken(current, iterator, false);
}
}