/** * Copyright (c) 2011 Cloudsmith Inc. and other contributors, as listed below. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Cloudsmith * */ package org.cloudsmith.geppetto.forge.util; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.cloudsmith.geppetto.common.os.StreamUtil; import org.jrubyparser.CompatVersion; import org.jrubyparser.Parser; import org.jrubyparser.ast.CallNode; import org.jrubyparser.ast.NewlineNode; import org.jrubyparser.ast.Node; import org.jrubyparser.ast.NodeType; import org.jrubyparser.ast.RootNode; import org.jrubyparser.ast.StrNode; import org.jrubyparser.ast.SymbolNode; import org.jrubyparser.lexer.SyntaxException; import org.jrubyparser.parser.ParserConfiguration; public abstract class RubyParserUtils { /** * Find a list of nodes that correspond to a specific path of node types extending * from a root node. The type of the root node is not included in the path. * * @param root * The root node * @param path * The node type path * @return The list of nodes, possibly empty but never <code>null</null> */ public static List<Node> findNodes(Node root, NodeType[] path) { if(root == null || path == null) return Collections.emptyList(); ArrayList<Node> result = new ArrayList<Node>(); findNodes(root, path, 0, result); return result; } private static void findNodes(Node root, NodeType[] path, int pathIndex, List<Node> result) { if(pathIndex >= path.length) { result.add(root); return; } NodeType searchedType = path[pathIndex++]; for(Node child : root.childNodes()) { while(child instanceof NewlineNode) child = ((NewlineNode) child).getNextNode(); if(child == null) continue; if(child.getNodeType() == searchedType) findNodes(child, path, pathIndex, result); } } public static RootNode parse(String id, Reader reader) throws SyntaxException { Parser parser = new Parser(); return (RootNode) parser.parse(id, reader, new ParserConfiguration(0, CompatVersion.RUBY1_9)); } /** * Parse a File containing Ruby syntax and return the root node of the AST. * * @param file * @return * @throws IOException */ public static RootNode parseFile(File file) throws IOException, SyntaxException { String fileStr = file.getAbsolutePath(); Reader reader = new BufferedReader(new FileReader(file)); try { return parse(fileStr, reader); } finally { StreamUtil.close(reader); } } public static RootNode parseString(String id, String content) throws SyntaxException { return parse(id, new StringReader(content)); } public static String stringValue(Node node) throws IOException { switch(node.getNodeType()) { case NEWLINENODE: return "\n"; case COMMENTNODE: return ""; case SYMBOLNODE: return ((SymbolNode) node).getName(); case STRNODE: return ((StrNode) node).getValue(); case FALSENODE: return "false"; case TRUENODE: return "true"; case CALLNODE: { // We can handle simple string concatenation CallNode argCall = (CallNode) node; if("+".equals(argCall.getName())) { StringBuilder bld = new StringBuilder(); bld.append(stringValue(argCall.getReceiver())); for(Node arg : argCall.getArgs().childNodes()) bld.append(stringValue(arg)); return bld.toString(); } throw new IOException("Unable to evaluate call node " + argCall.getName() + " into a string"); } default: throw new IOException("Unable to evaluate node of type " + node.getNodeType() + " into a string"); } } }