package openmods.calc.parsing;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.PeekingIterator;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import openmods.calc.BinaryOperator;
import openmods.calc.Environment;
import openmods.calc.ExecutionErrorException;
import openmods.calc.ExprType;
import openmods.calc.Frame;
import openmods.calc.FrameFactory;
import openmods.calc.ICallable;
import openmods.calc.ICompilerMapFactory;
import openmods.calc.IExecutable;
import openmods.calc.IGettable;
import openmods.calc.OperatorDictionary;
import openmods.calc.SymbolMap;
import openmods.utils.OptionalInt;
import openmods.utils.Stack;
import openmods.utils.StackValidationException;
public class CommonSimpleSymbolFactory<E> {
public static final String SYMBOL_LET = "let";
public static final String SYMBOL_FAIL = "fail";
public static final String SYMBOL_CONSTANT = "const";
private class FailStateTransition extends SingleStateTransition.ForSymbol<E> {
@Override
public IExprNode<E> createRootNode(List<IExprNode<E>> children) {
Preconditions.checkState(children.size() <= 1, "'fail' expects at most single argument, got %s", children.size());
if (children.isEmpty()) {
return new SingleExecutableNode<E>(new IExecutable<E>() {
@Override
public void execute(Frame<E> frame) {
throw new ExecutionErrorException();
}
});
} else {
return children.get(0);
}
}
@Override
public IExprNode<E> parseSymbol(ICompilerState<E> state, PeekingIterator<Token> input) {
final Token arg = input.next();
final String failCause = arg.value;
return new SingleExecutableNode<E>(new IExecutable<E>() {
@Override
public void execute(Frame<E> frame) {
throw new ExecutionErrorException(failCause);
}
});
}
}
public class ExtendedCompilerMapFactory extends BasicCompilerMapFactory<E> {
@Override
protected DefaultExprNodeFactory<E> createExprNodeFactory(IValueParser<E> valueParser) {
return SquareBracketContainerNode.install(new MappedExprNodeFactory<E>(valueParser));
}
@Override
protected void configureCompilerStateCommon(MappedCompilerState<E> compilerState, Environment<E> environment) {
super.configureCompilerStateCommon(compilerState, environment);
compilerState.addStateTransition(SYMBOL_LET, createParserTransition(compilerState));
compilerState.addStateTransition(SYMBOL_FAIL, new FailStateTransition());
compilerState.addStateTransition(SYMBOL_CONSTANT, new ConstantSymbolStateTransition<E>(compilerState, environment, SYMBOL_CONSTANT));
}
}
private static class KeyValueSeparator<E> extends BinaryOperator.Direct<E> {
private KeyValueSeparator(String id, int precendence) {
super(id, precendence);
}
@Override
public E execute(E left, E right) {
throw new UnsupportedOperationException(); // not supposed to be used directly;
}
}
private final Set<BinaryOperator<E>> keyValueSeparators;
private final String keyValueSeparatorsIds;
public CommonSimpleSymbolFactory(int keyValueSeparatorPriority, String... keyValueSeparatorIds) {
final ImmutableSet.Builder<BinaryOperator<E>> separators = ImmutableSet.builder();
for (String opId : keyValueSeparatorIds)
separators.add(new KeyValueSeparator<E>(opId, keyValueSeparatorPriority));
this.keyValueSeparators = separators.build();
this.keyValueSeparatorsIds = Joiner.on(',').join(keyValueSeparatorIds);
}
public Collection<BinaryOperator<E>> getKeyValueSeparators() {
return keyValueSeparators;
}
private interface ISymbolBinder<E> {
public void bind(SymbolMap<E> localSymbols, Frame<E> enclosingFrame);
}
private static <E> ISymbolBinder<E> createLetLazyConstant(final String name, IExprNode<E> valueNode) {
final IExecutable<E> exprExecutable = ExprUtils.flattenNode(valueNode);
return new ISymbolBinder<E>() {
@Override
public void bind(SymbolMap<E> localSymbols, final Frame<E> enclosingFrame) {
class LazyConstant implements IGettable<E> {
private boolean isEvaluated;
private E value;
@Override
public E get() {
if (!isEvaluated) {
final Frame<E> executionFrame = FrameFactory.newLocalFrame(enclosingFrame);
exprExecutable.execute(executionFrame);
value = executionFrame.stack().popAndExpectEmptyStack();
}
return value;
}
}
localSymbols.put(name, new LazyConstant());
}
};
}
private static <E> ISymbolBinder<E> createLetFunction(final String name, SymbolCallNode<E> symbolCallNode, IExprNode<E> valueNode) {
List<String> args = Lists.newArrayList();
for (IExprNode<E> arg : symbolCallNode.getChildren()) {
Preconditions.checkState(arg instanceof SymbolGetNode, "Expected symbol, got %s", arg);
args.add(((SymbolGetNode<E>)arg).symbol());
}
final List<String> reversedArgs = Lists.reverse(args);
final IExecutable<E> exprExecutable = ExprUtils.flattenNode(valueNode);
return new ISymbolBinder<E>() {
@Override
public void bind(SymbolMap<E> localSymbols, final Frame<E> enclosingFrame) {
class LetFunction implements ICallable<E> {
@Override
public void call(Frame<E> callSiteFrame, OptionalInt argumentsCount, OptionalInt returnsCount) {
final int expectedArgCount = reversedArgs.size();
if (!argumentsCount.compareIfPresent(expectedArgCount)) throw new StackValidationException("Expected %s argument(s) but got %s", expectedArgCount, argumentsCount.get());
final Frame<E> executionFrame = FrameFactory.newLocalFrameWithSubstack(enclosingFrame, expectedArgCount);
final Stack<E> argStack = executionFrame.stack();
for (String arg : reversedArgs)
executionFrame.symbols().put(arg, argStack.pop());
exprExecutable.execute(executionFrame);
final int actualReturns = argStack.size();
if (!returnsCount.compareIfPresent(actualReturns)) throw new StackValidationException("Has %s result(s) but expected %s", actualReturns, returnsCount.get());
}
}
localSymbols.put(name, new LetFunction());
}
};
}
private class LetExecutable implements IExecutable<E> {
private final List<ISymbolBinder<E>> variables;
private final IExecutable<E> expr;
public LetExecutable(List<ISymbolBinder<E>> variables, IExecutable<E> expr) {
this.variables = variables;
this.expr = expr;
}
@Override
public void execute(Frame<E> frame) {
final Frame<E> letFrame = FrameFactory.newLocalFrameWithSubstack(frame, 0);
for (ISymbolBinder<E> e : variables)
e.bind(letFrame.symbols(), frame);
expr.execute(letFrame);
}
}
private class LetNode implements IExprNode<E> {
private final IExprNode<E> argsNode;
private final IExprNode<E> codeNode;
public LetNode(IExprNode<E> argsNode, IExprNode<E> codeNode) {
this.argsNode = argsNode;
this.codeNode = codeNode;
}
@Override
public void flatten(List<IExecutable<E>> output) {
// expecting [a:b:,c:1+2]. If correctly formed, arg name (symbol) will be transformed into symbol atom
Preconditions.checkState(argsNode instanceof SquareBracketContainerNode, "Malformed 'let' expressions: expected brackets, got %s", argsNode);
final SquareBracketContainerNode<E> bracketNode = (SquareBracketContainerNode<E>)argsNode;
final ImmutableList<ISymbolBinder<E>> vars = collectVars(bracketNode);
final IExecutable<E> code = ExprUtils.flattenNode(codeNode);
output.add(new LetExecutable(vars, code));
}
private ImmutableList<ISymbolBinder<E>> collectVars(final SquareBracketContainerNode<E> bracketNode) {
final ImmutableList.Builder<ISymbolBinder<E>> varsBuilder = ImmutableList.builder();
for (IExprNode<E> argNode : bracketNode.getChildren())
flattenArgNode(varsBuilder, argNode);
return varsBuilder.build();
}
private void flattenArgNode(ImmutableList.Builder<ISymbolBinder<E>> output, IExprNode<E> argNode) {
Preconditions.checkState(argNode instanceof BinaryOpNode, "Expected expression in from <name>:<expr>, got %s", argNode);
final BinaryOpNode<E> opNode = (BinaryOpNode<E>)argNode;
Preconditions.checkState(keyValueSeparators.contains(opNode.operator), "Expected operators %s as separator, got %s", keyValueSeparatorsIds, opNode.operator.id);
final IExprNode<E> nameNode = opNode.left;
final IExprNode<E> valueExprNode = opNode.right;
if (nameNode instanceof SymbolGetNode) {
final SymbolGetNode<E> symbolGetNode = (SymbolGetNode<E>)nameNode;
output.add(createLetLazyConstant(symbolGetNode.symbol(), valueExprNode));
} else if (nameNode instanceof SymbolCallNode) {
final SymbolCallNode<E> symbolCallNode = (SymbolCallNode<E>)nameNode;
output.add(createLetFunction(symbolCallNode.symbol(), symbolCallNode, valueExprNode));
} else {
throw new IllegalStateException("Expected symbol, got " + nameNode);
}
}
@Override
public Iterable<IExprNode<E>> getChildren() {
return ImmutableList.of(argsNode, codeNode);
}
}
public ISymbolCallStateTransition<E> createParserTransition(final ICompilerState<E> currentState) {
return new ISymbolCallStateTransition<E>() {
@Override
public ICompilerState<E> getState() {
return currentState;
}
@Override
public IExprNode<E> createRootNode(List<IExprNode<E>> children) {
Preconditions.checkState(children.size() == 2, "Expected two args for 'let' expression");
return new LetNode(children.get(0), children.get(1));
}
};
}
public ICompilerMapFactory<E, ExprType> createCompilerFactory() {
return new ExtendedCompilerMapFactory();
}
public void registerSeparators(OperatorDictionary<E> operators) {
for (BinaryOperator<E> separator : keyValueSeparators)
operators.registerBinaryOperator(separator);
}
}