package mumbler.truffle.parser;
import static mumbler.truffle.parser.MumblerReadException.throwReaderException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.StreamSupport;
import org.antlr.v4.runtime.misc.Pair;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.source.SourceSection;
import mumbler.truffle.MumblerContext;
import mumbler.truffle.MumblerException;
import mumbler.truffle.node.MumblerNode;
import mumbler.truffle.node.call.InvokeNode;
import mumbler.truffle.node.call.TCOInvokeNode;
import mumbler.truffle.node.literal.BigIntegerNode;
import mumbler.truffle.node.literal.BooleanNode;
import mumbler.truffle.node.literal.LiteralListNode;
import mumbler.truffle.node.literal.LiteralSymbolNode;
import mumbler.truffle.node.literal.LongNode;
import mumbler.truffle.node.literal.StringNode;
import mumbler.truffle.node.read.ClosureSymbolNodeGen;
import mumbler.truffle.node.read.GlobalSymbolNodeGen;
import mumbler.truffle.node.read.LocalSymbolNodeGen;
import mumbler.truffle.node.read.SymbolNode;
import mumbler.truffle.node.special.DefineNode;
import mumbler.truffle.node.special.DefineNodeGen;
import mumbler.truffle.node.special.IfNode;
import mumbler.truffle.node.special.LambdaNode;
import mumbler.truffle.node.special.LambdaNodeGen;
import mumbler.truffle.node.special.LoopNode;
import mumbler.truffle.node.special.QuoteNode;
import mumbler.truffle.node.special.QuoteNode.QuoteKind;
import mumbler.truffle.node.special.QuoteNodeGen;
import mumbler.truffle.syntax.BigIntegerSyntax;
import mumbler.truffle.syntax.BooleanSyntax;
import mumbler.truffle.syntax.ListSyntax;
import mumbler.truffle.syntax.LongSyntax;
import mumbler.truffle.syntax.StringSyntax;
import mumbler.truffle.syntax.SymbolSyntax;
import mumbler.truffle.type.MumblerFunction;
import mumbler.truffle.type.MumblerList;
import mumbler.truffle.type.MumblerSymbol;
public class Converter {
private MumblerContext context;
private Analyzer analyzer;
private final boolean isTailCallOptimizationEnabled;
public Converter(boolean tailCallOptimizationEnabled) {
this.isTailCallOptimizationEnabled = tailCallOptimizationEnabled;
}
public MumblerNode[] convertSexp(MumblerContext context, ListSyntax sexp) {
this.context = context;
Namespace fileNamespace = new Namespace(Namespace.TOP_NS,
this.context.getGlobalNamespace());
this.analyzer = new Analyzer(fileNamespace);
this.analyzer.walk(sexp);
return StreamSupport.stream(sexp.getValue().spliterator(), false)
.map(obj -> this.convert(obj, fileNamespace))
.toArray(size -> new MumblerNode[size]);
}
public MumblerNode convert(Syntax<?> syntax, Namespace ns) {
if (syntax instanceof LongSyntax) {
return convert((LongSyntax) syntax);
} else if (syntax instanceof BigIntegerSyntax) {
return convert((BigIntegerSyntax) syntax);
} else if (syntax instanceof BooleanSyntax) {
return convert((BooleanSyntax) syntax);
} else if (syntax instanceof StringSyntax) {
return convert((StringSyntax) syntax);
} else if (syntax instanceof SymbolSyntax) {
return convert((SymbolSyntax) syntax, ns);
} else if (syntax instanceof ListSyntax) {
return convert((ListSyntax) syntax, ns);
} else {
throw new MumblerException("Unknown type: " + syntax.getClass());
}
}
public static LongNode convert(LongSyntax number) {
return new LongNode(number);
}
public static BigIntegerNode convert(BigIntegerSyntax number) {
return new BigIntegerNode(number);
}
public static BooleanNode convert(BooleanSyntax bool) {
return new BooleanNode(bool);
}
public static StringNode convert(StringSyntax str) {
return new StringNode(str);
}
public SymbolNode convert(SymbolSyntax syntax, Namespace ns) {
SymbolNode node;
MumblerSymbol sym = syntax.getValue();
Pair<Integer, FrameSlot> pair = ns.getIdentifier(sym.name);
if (pair.a == Namespace.LEVEL_UNDEFINED) {
throwReaderException(sym.name + " undefined", syntax, ns);
return null;
} else if (pair.a == 0) {
node = LocalSymbolNodeGen.create(pair.b);
} else if (pair.a == Namespace.LEVEL_GLOBAL) {
node = GlobalSymbolNodeGen.create(pair.b, this.context.getGlobalFrame());
} else {
node = ClosureSymbolNodeGen.create(pair.b, pair.a);
}
node.setSourceSection(syntax.getSourceSection());
return node;
}
public MumblerNode convert(ListSyntax syntax, Namespace ns) {
MumblerList<? extends Syntax<? extends Object>> list = syntax.getValue();
if (list == MumblerList.EMPTY || list.size() == 0) {
return new LiteralListNode(MumblerList.EMPTY);
}
Syntax<? extends Object> car = list.car();
if (car instanceof SymbolSyntax) {
MumblerSymbol sym = ((SymbolSyntax) car).getValue();
switch (sym.name) {
case "define":
return convertDefine(syntax, ns);
case "lambda":
return convertLambda(syntax, ns);
case "if":
return convertIf(syntax, ns);
case "quote":
return convertQuote(syntax, ns);
case "loop":
return convertLoop(syntax, ns);
}
}
return convertInvoke(list, syntax.getSourceSection(), ns);
}
private InvokeNode convertInvoke(MumblerList<? extends Syntax<? extends Object>> list,
SourceSection sourceSection, Namespace ns) {
MumblerNode functionNode = convert(list.car(), ns);
MumblerNode[] arguments = StreamSupport
.stream(list.cdr().spliterator(), false)
.map(syn-> convert(syn, ns))
.toArray(size -> new MumblerNode[size]);
if (isTailCallOptimizationEnabled) {
return new TCOInvokeNode(functionNode, arguments, sourceSection);
} else {
return new InvokeNode(functionNode, arguments, sourceSection);
}
}
private DefineNode convertDefine(ListSyntax syntax, Namespace ns) {
MumblerList<? extends Syntax<? extends Object>> list = syntax.getValue();
SymbolSyntax symSyntax = (SymbolSyntax) list.cdr().car();
FrameSlot nameSlot = ns.getIdentifier(symSyntax.getValue().name).b;
MumblerNode valueNode = convert(list.cdr().cdr().car(), ns);
DefineNode node = DefineNodeGen.create(valueNode, nameSlot);
node.setSourceSection(syntax.getSourceSection());
if (valueNode instanceof LambdaNode) {
// TODO : not good enough. if there's an error in the lambda,
// the name won't be used. Have to pass name
LambdaNode lambda = (LambdaNode) valueNode;
lambda.setName(nameSlot.toString());
}
return node;
}
@SuppressWarnings("unchecked")
private LambdaNode convertLambda(ListSyntax syntax, Namespace ns) {
MumblerList<? extends Syntax<? extends Object>> list = syntax.getValue();
Namespace lambdaNs = this.analyzer.getNamespace(syntax);
List<FrameSlot> formalParameters = new ArrayList<>();
ListSyntax argsSyntax = (ListSyntax) list.cdr().car();
for (SymbolSyntax arg : (MumblerList<SymbolSyntax>) argsSyntax.getValue()) {
formalParameters.add(convert(arg, lambdaNs).getSlot());
}
List<MumblerNode> bodyNodes = new ArrayList<>();
for (Syntax<? extends Object> body : list.cdr().cdr()) {
bodyNodes.add(convert(body, lambdaNs));
}
bodyNodes.get(bodyNodes.size() - 1).setIsTail();
MumblerFunction function = MumblerFunction.create(
formalParameters.toArray(new FrameSlot[] {}),
bodyNodes.toArray(new MumblerNode[] {}),
lambdaNs.getFrameDescriptor());
LambdaNode node = LambdaNodeGen.create(function);
node.setSourceSection(syntax.getSourceSection());
return node;
}
private IfNode convertIf(ListSyntax syntax, Namespace ns) {
MumblerList<? extends Syntax<? extends Object>> list = syntax.getValue();
return new IfNode(convert(list.cdr().car(), ns),
convert(list.cdr().cdr().car(), ns),
convert(list.cdr().cdr().cdr().car(), ns),
syntax.getSourceSection());
}
private static QuoteNode convertQuote(ListSyntax syntax,
Namespace ns) {
MumblerList<? extends Syntax<? extends Object>> list = syntax.getValue();
Syntax<? extends Object> value = list.cdr().car();
MumblerNode node;
QuoteKind kind;
if (value instanceof LongSyntax) {
kind = QuoteKind.LONG;
node = convert((LongSyntax) value);
} else if (value instanceof BooleanSyntax) {
kind = QuoteKind.BOOLEAN;
node = convert((BooleanSyntax) value);
} else if (value instanceof StringSyntax) {
kind = QuoteKind.STRING;
node = new StringNode((StringSyntax) value);
} else if (value instanceof SymbolSyntax) {
kind = QuoteKind.SYMBOL;
node = new LiteralSymbolNode((SymbolSyntax) value);
} else if (value instanceof ListSyntax) {
kind = QuoteKind.LIST;
node = new LiteralListNode(((MumblerList<?>) value.strip()));
} else {
throw new MumblerException("Unknown quote type: " +
value.getClass());
}
return QuoteNodeGen.create(node, kind);
}
private LoopNode convertLoop(ListSyntax syntax, Namespace ns) {
return new LoopNode(convertInvoke(
syntax.getValue().cdr(), syntax.getSourceSection(), ns));
}
}