/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This file is part of SableCC. *
* See the file "LICENSE" for copyright information and the *
* terms and conditions for copying, distribution and *
* modification of SableCC. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package org.sablecc.sablecc;
import org.sablecc.sablecc.analysis.*;
import org.sablecc.sablecc.node.*;
import java.util.LinkedList;
import java.util.Map;
/*
* ResolveAstIds
*
* This class computes basic semantic verifications for AST alternatives
* section. The same thing is done by ResolveIds class for Productions
* section. It makes sure that there is no conflictual names and it also
* constructs few symbol tables necessary in the rest of the code.
*/
/**
* Last Modification date : 18-10-2004
* correct AST alternative element error bug (error2())
* Now only tokens and AST section's productions can be used in
* AST alternatives
* <p/>
* 15-01-2004
* Remove comment method error1(...)
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class ResolveAstIds extends DepthFirstAdapter {
//Map of AST productions. The AST production node can be obtained
//by giving the name of this production
// Example :: PAstProd is the name of the declared the following productions
// ast_prod = id equal [alts]:ast_alt* semicolon;
public final Map ast_prods = new TypedTreeMap(
StringComparator.instance,
StringCast.instance,
NodeCast.instance);
//Same thing that above for AST alternatives.
public final Map ast_alts = new TypedTreeMap(
StringComparator.instance,
StringCast.instance,
NodeCast.instance);
//Same thing that above for AST alternatives elements.
public final Map ast_elems = new TypedTreeMap(
StringComparator.instance,
StringCast.instance,
NodeCast.instance);
//Map of all names of AST productions.
//They are essentially used to generate AST node classes.
public final Map ast_names = new TypedHashMap(
NodeCast.instance,
StringCast.instance);
public final Map ast_elemTypes = new TypedHashMap(
NodeCast.instance,
StringCast.instance);
public ResolveIds astIds;
private String firstAstProduction;
private String currentProd;
private String currentAlt;
private int lastLine;
private int lastPos;
public ResolveAstIds(ResolveIds ids) {
astIds = ids;
}
public String getFirstAstProduction() {
return firstAstProduction;
}
@Override
public void inAAst(AAst node) {
LinkedList listProds = node.getProds();
if (listProds.size() > 0) {
AAstProd firstAstProd = (AAstProd) listProds.getFirst();
firstAstProduction = "P" + ResolveIds.name(firstAstProd.getId().getText());
}
}
@Override
public void inAAstProd(AAstProd node) {
currentProd = ResolveIds.name(node.getId().getText());
String name = "P" + currentProd;
if (ast_prods.put(name, node) != null) {
error(node.getId(), name);
}
ast_names.put(node, name);
}
@Override
public void inAAstAlt(final AAstAlt alt) {
if (alt.getAltName() != null) {
currentAlt =
"A" +
ResolveIds.name(alt.getAltName().getText()) +
currentProd;
if (ast_alts.put(currentAlt, alt) != null) {
error(alt.getAltName(), currentAlt);
}
ast_names.put(alt, currentAlt);
} else {
currentAlt = "A" + currentProd;
if (ast_alts.put(currentAlt, alt) != null) {
error(currentAlt);
}
ast_names.put(alt, currentAlt);
}
}
//Only Abstract Syntax Tree section is concerned by the visitor here.
@Override
public void caseAProductions(AProductions node) {
}
@Override
public void caseAElem(final AElem elem) {
String name;
String elem_name;
TId tid;
if (elem.getElemName() != null) {
tid = elem.getElemName();
} else {
tid = elem.getId();
}
elem_name = tid.getText();
name = currentAlt + "." + ResolveIds.name(elem_name);
if (ast_elems.put(name, elem) != null) {
error(tid, name);
}
if (elem_name.equals("class")) {
error5(tid);
}
ast_names.put(elem, ResolveIds.name(elem_name));
}
@Override
public void outAAstProd(AAstProd prod) {
prod.apply(new DepthFirstAdapter() {
@Override
public void caseAElem(AElem node) {
String name = ResolveIds.name(node.getId().getText());
if (node.getSpecifier() != null) {
if (node.getSpecifier() instanceof ATokenSpecifier) {
ast_elemTypes.put(node, "T" + name);
} else {
ast_elemTypes.put(node, "P" + name);
}
} else {
Object token = astIds.tokens.get("T" + name);
if (token != null) {
ast_elemTypes.put(node, "T" + name);
} else {
ast_elemTypes.put(node, "P" + name);
}
}
}
}
);
}
@Override
public void outAAst(AAst prod) {
prod.apply(new DepthFirstAdapter() {
@Override
public void caseAElem(AElem node) {
String name = ResolveIds.name(node.getId().getText());
if (node.getSpecifier() != null) {
if (node.getSpecifier() instanceof ATokenSpecifier) {
if (astIds.tokens.get("T" + name) == null) {
error2(node.getId(), "T" + name);
}
if (astIds.ignTokens.get("T" + name) != null) {
error3(node.getId(), "T" + name);
}
ast_elemTypes.put(node, "T" + name);
} else {
if (ast_prods.get("P" + name) == null) {
error2(node.getId(), "P" + name);
}
ast_elemTypes.put(node, "P" + name);
}
} else {
Object token = astIds.tokens.get("T" + name);
Object ignToken = astIds.ignTokens.get("T" + name);
//Object production = astIds.prods.get("P" + name);
Object ast_production = ast_prods.get("P" + name);
//if()
if ((token == null) && (ast_production == null)) {
error2(node.getId(), "P" + name + " and T" + name);
}
//if the alternative element is a token
if (token != null) {
//and also appears to be a valid production, there is an ambiguity
if (ast_production != null) {
error4(node.getId(), "P" + name + " and T" + name);
}
//it should not be an ignored token
if (ignToken != null) {
error3(node.getId(), "T" + name);
}
ast_elemTypes.put(node, "T" + name);
}
//the alternative element is a production and everything is fine
else {
ast_elemTypes.put(node, "P" + name);
}
}
}
}
);
}
public void defaultcase(Node node) {
if (node instanceof Token) {
Token t = (Token) node;
lastLine = t.getLine();
lastPos = t.getPos() + t.getText().length();
}
}
private static void error(Token token, String name) {
throw new RuntimeException(
"[" + token.getLine() + "," + token.getPos() + "] " +
"Redefinition of " + name + ".");
}
private void error(String name) {
throw new RuntimeException(
"[" + lastLine + "," + lastPos + "] " +
"Redefinition of " + name + ".");
}
private static void error2(Token token, String name) {
throw new RuntimeException(
"[" + token.getLine() + "," + token.getPos() + "] " +
name + " undefined. IfStmt it is a production, It should be defined in AST section");
}
private static void error3(Token token, String name) {
throw new RuntimeException(
"[" + token.getLine() + "," + token.getPos() + "] " +
name + " is ignored.");
}
private static void error4(Token token, String name) {
throw new RuntimeException(
"[" + token.getLine() + "," + token.getPos() + "] " +
"ambiguous " + name + ".");
}
private static void error5(Token token) {
throw new RuntimeException(
"[" + token.getLine() + "," + token.getPos() + "] " +
"class is an invalid element name.");
}
@Override
public String toString() {
StringBuffer s = new StringBuffer();
String nl = System.getProperty("line.separator");
s.append("Productions:");
s.append(nl);
s.append(ast_prods);
s.append(nl);
s.append("Alternatives:");
s.append(nl);
s.append(ast_alts);
s.append(nl);
s.append("Elements:");
s.append(nl);
s.append(ast_elems);
s.append(nl);
return s.toString();
}
}