package org.cloudsmith.geppetto.ruby.jrubyparser; import java.util.List; import java.util.Map; import org.jrubyparser.ast.ArrayNode; import org.jrubyparser.ast.CallNode; import org.jrubyparser.ast.Colon2Node; import org.jrubyparser.ast.ConstNode; import org.jrubyparser.ast.HashNode; import org.jrubyparser.ast.ListNode; import org.jrubyparser.ast.Node; import org.jrubyparser.ast.StrNode; import org.jrubyparser.ast.SymbolNode; import com.google.common.collect.Lists; import com.google.common.collect.Maps; /** * Evaluates (a limited set of) Ruby constant expressions. TODO: Colon3Node * (i.e. name relative to global root) not handled - as FQN are returned as list * of String there is currently no marker if it is relative or absolute. * */ public class ConstEvaluator extends AbstractJRubyVisitor { private List<String> addAll(List<String> a, List<String> b) { a.addAll(b); return a; } public Object eval(Node node) { if(node == null) return null; // Can't visit ListNode, because they are not supposed to be "evaluated" (duh! impl sucks). // Must compare against exact class since everything derived implements "accept". if(node.getClass() == ListNode.class) return this.visitListNode((ListNode) node); return node.accept(this); } private List<String> splice(Object a, Object b) { return addAll(stringList(a), stringList(b)); } @SuppressWarnings("unchecked") public List<String> stringList(Object x) { if(x instanceof List) return (List<String>) x; // have faith if(x instanceof String) return Lists.newArrayList((String) x); if(x == null) return Lists.newArrayList(); // empty list throw new IllegalArgumentException("Not a string or lists of strings"); } @Override public Object visitArrayNode(ArrayNode iVisited) { List<Object> result = Lists.newArrayList(); for(Node n : iVisited.childNodes()) result.add(eval(n)); return result; } @Override public Object visitCallNode(CallNode iVisited) { if("intern".equals(iVisited.getName())) return eval(iVisited.getReceiver()); return null; } @Override public Object visitColon2Node(Colon2Node iVisited) { return splice(eval(iVisited.getLeftNode()), iVisited.getName()); } @Override public Object visitConstNode(ConstNode iVisited) { return iVisited.getName(); } @Override public Object visitHashNode(HashNode iVisited) { Map<Object, Object> result = Maps.newHashMap(); List<Node> children = iVisited.childNodes(); children = children.get(0).childNodes(); for(int i = 0; i < children.size(); i++) { Object key = eval(children.get(i++)); Object value = eval(children.get(i)); result.put(key, value); } return result; } public Object visitListNode(ListNode iVisited) { List<Object> result = Lists.newArrayList(); for(Node n : iVisited.childNodes()) result.add(eval(n)); return result; } @Override public Object visitStrNode(StrNode iVisited) { return iVisited.getValue(); } @Override public Object visitSymbolNode(SymbolNode iVisited) { return iVisited.getName(); } }