Utils.java
package com.mackenziehigh.autumn.lang.compiler.parser;
import autumn.lang.compiler.TreeBuilder;
import autumn.lang.compiler.ast.commons.IConstruct;
import autumn.lang.compiler.ast.commons.IExpression;
import autumn.lang.compiler.ast.literals.BigDecimalLiteral;
import autumn.lang.compiler.ast.literals.BigIntegerLiteral;
import autumn.lang.compiler.ast.literals.ByteLiteral;
import autumn.lang.compiler.ast.literals.CharLiteral;
import autumn.lang.compiler.ast.literals.DoubleLiteral;
import autumn.lang.compiler.ast.literals.FloatLiteral;
import autumn.lang.compiler.ast.literals.IntLiteral;
import autumn.lang.compiler.ast.literals.LongLiteral;
import autumn.lang.compiler.ast.literals.ShortLiteral;
import autumn.lang.compiler.ast.nodes.Name;
import autumn.util.F;
import com.google.common.collect.Lists;
import com.mackenziehigh.autumn.resources.Finished;
import com.mackenziehigh.snowflake.ITreeNode;
import com.mackenziehigh.snowflake.LinesAndColumns;
import com.mackenziehigh.snowflake.NewlineStyles;
import com.mackenziehigh.snowflake.TreeNode;
import java.io.File;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
/**
* This class is used by the parser to help facilitate the building of an abstract-syntax-tree.
* This class helps connect the parser to the tree-builder.
* Specifically, this class provides a location to store state and place utility methods.
*
* <p>
* <b>Warning: </b>Only one parser can be utilizing this class at a time.
* </p>
*
* @author Mackenzie High
*/
@Finished("2014/07/12")
public final class Utils
{
/**
* It is OK for this to be public.
*/
public static URL source_file = null;
/**
* This builder is used to construct an AST.
*/
private static final TreeBuilder builder = new TreeBuilder();
/**
* This method retrieves the builder that is building the AST.
*
* @return the aforedescribed builder.
*/
public static TreeBuilder builder()
{
return builder;
}
/**
* Whenever a string-literal is encountered, this field will be assigned its content.
*/
private static String last_string;
/**
* Whenever a string-literal is encountered, this field will be set.
*/
private static boolean verbatim;
public static void storeStringValue(final ITreeNode node,
final boolean verbatim)
{
Utils.verbatim = verbatim;
String literal = TreeNode.find(node, "STRING_LITERAL").text().trim();
if (literal.startsWith("'''"))
{
last_string = literal.substring(3, literal.length() - 3);
}
else // literal.startsWith("\"")
{
last_string = literal.substring(1, literal.length() - 1);
}
if (verbatim)
{
return;
}
// TODO: escape sequences
}
public static boolean getVerbatim()
{
return verbatim;
}
public static String getString()
{
return last_string;
}
public static File getFile()
{
return new File(last_string);
}
public static void createChainedMethodCall()
{
if (builder.size() == 1)
{
return;
}
/**
* The primary stack looks something like this (from top to bottom):
*
* . argument[N]
* . argument[2]
* . argument[1]
* . name
* . argument[N]
* . argument[2]
* . argument[1]
* . name
* . argument[N]
* . argument[2]
* . argument[1]
* . name
* . owner
*
* Get the primary stack off of the stack of stacks.
*/
final Stack<IConstruct> stack = builder.copyStack();
/**
* Remove the primary stack from the stack of stack.
*/
builder.clear();
builder.popStack();
/**
* Reverse the order of the stack (i.e. the former primary stack).
* So now, the stack looks something like this (from top to bottom):
*
* . owner
* . name
* . argument[1]
* . argument[2]
* . argument[N]
* . name
* . argument[1]
* . argument[2]
* . argument[N]
* . name
* . argument[1]
* . argument[2]
* . argument[N]
*/
Collections.reverse(stack);
/**
* Push a new primary stack onto the stack of stacks.
*/
builder.pushStack();
/**
* Transfer the owner onto the primary stack.
*/
assert stack.peek() instanceof IExpression;
builder.push(stack.pop());
/**
* For each chained method call:
*/
while (stack.isEmpty() == false)
{
/**
* Transfer the name onto the primary stack.
*/
assert stack.peek() instanceof Name;
builder.push(stack.pop());
/**
* Transfer each argument onto the primary stack.
*/
while (stack.isEmpty() == false && stack.peek() instanceof Name == false)
{
assert stack.peek() instanceof IExpression;
builder.push(stack.pop());
}
/**
* Create a call-method-expression given the single chained method call
* that is currently in pieces on the primary stack.
*/
builder.createExpressionCallMethod();
}
assert stack.isEmpty();
assert builder.size() == 1;
}
public static void createComponentTypeSpecifier(final ITreeNode node)
{
final int dimensions = TreeNode.findAll(node, "dimension").size();
builder.createComponentTypeSpecifier(dimensions == 0 ? null : dimensions);
}
public static void createComponentName(final ITreeNode node)
{
final String identifier = TreeNode.find(node, "ID").text().trim();
Name result = new Name();
result = result.setName(identifier);
builder.push(result);
}
public static void createComponentVariable(final ITreeNode node)
{
final String identifier = node.childAt(0).text().trim();
builder.createComponentVariable(identifier);
}
public static void createComponentLabel(final ITreeNode node)
{
final String identifier = node.childAt(0).text().trim();
builder.createComponentLabel(identifier);
}
public static IntLiteral extractIntLiteral(final ITreeNode node)
{
final ITreeNode integer = TreeNode.find(node.iterableBFS(), "int_value");
final IntLiteral constant = extractIntValue(integer);
return constant;
}
public static boolean extractBooleanValue(final ITreeNode node)
{
final String value = node.childAt(0).text().trim();
return Boolean.valueOf(value);
}
public static CharLiteral extractCharValue(final ITreeNode node)
{
return new CharLiteral(removeWS(node));
}
public static ByteLiteral extractByteValue(final ITreeNode node)
{
return new ByteLiteral(removeWS(node));
}
public static ShortLiteral extractShortValue(final ITreeNode node)
{
return new ShortLiteral(removeWS(node));
}
public static IntLiteral extractIntValue(final ITreeNode node)
{
return new IntLiteral(removeWS(node));
}
public static LongLiteral extractLongValue(final ITreeNode node)
{
return new LongLiteral(removeWS(node));
}
public static FloatLiteral extractFloatValue(final ITreeNode node)
{
return new FloatLiteral(removeWS(node));
}
public static DoubleLiteral extractDoubleValue(final ITreeNode node)
{
return new DoubleLiteral(removeWS(node));
}
public static BigIntegerLiteral extractBigIntegerValue(final ITreeNode node)
{
return new BigIntegerLiteral(removeWS(node));
}
public static BigDecimalLiteral extractBigDecimalValue(final ITreeNode node)
{
return new BigDecimalLiteral(removeWS(node));
}
public static List<String> extractAnnotationValues(final ITreeNode node)
{
final List<ITreeNode> nodes = TreeNode.findAll(node, "annotation_value");
if (nodes.isEmpty())
{
return null;
}
final List<String> values = Lists.newLinkedList();
for (ITreeNode x : nodes)
{
values.add(extractAnnotationValue(x));
}
return values;
}
private static String extractAnnotationValue(final ITreeNode node)
{
final ITreeNode annotation_value = TreeNode.find(node, "annotation_value");
if (annotation_value == null)
{
return null;
}
final String text = annotation_value.text().trim();
final String modified = text.startsWith("\"") || text.startsWith("'")
? text.substring(1, text.length() - 1)
: text;
final String escaped = F.escape(modified);
return escaped;
}
public static String extractCommentLine(final ITreeNode node)
{
return TreeNode.find(node, "api_comment_text").text().trim();
}
public static String removeWS(final ITreeNode node)
{
// This method removes all nodes created by the WS rule.
// Then, the leaf nodes are used to form a string, which is then returned.
final StringBuilder result = new StringBuilder();
for (ITreeNode kid : node.children())
{
if (kid.rule().equals("WS"))
{
// Do Nothing
}
else if (kid.childCount() == 0)
{
result.append(kid.text());
}
else
{
result.append(removeWS(kid));
}
}
return result.toString();
}
public static void setSourceLocation(final ITreeNode node)
{
final char[] code = node.input();
final int position = node.start();
final NewlineStyles newline = NewlineStyles.fromSystem();
final LinesAndColumns finder = new LinesAndColumns(code, newline);
final int line = finder.lineNumbers().length == 0 ? 1 : finder.lineNumbers()[position];
final int column = finder.columnNumbers().length == 0 ? 1 : finder.columnNumbers()[position];
builder.setSourceLocation(source_file, line, column, null);
}
}