package warthog.parsegen;
import java.io.*;
import java.util.*;
import warthog.cradle.Descent;
import warthog.cradle.JavaClass;
public class MetaParser extends Descent {
private Generator gen;
private Map<String, SemanticClass> types = new HashMap<String, SemanticClass>();
GeneratorData data;
private JavaClass javaClass;
public MetaParser(File path) throws IOException {
super(new FileReader(path));
javaClass = new JavaClass(path);
data = new GeneratorData();
gen = new Generator();
}
public void run() throws IOException {
parseHeader();
matchSeparator();
parseGrammar();
gen.build();
gen.writeAllCode(javaClass, data);
javaClass.writeFile();
}
private void parseHeader() throws IOException {
while (true) {
if (inWhite()||inEOL()) skipLine();
else if (inAlpha()) {
String optionName = parseWord("option");
skipSpace();
if ("package".equals(optionName)) {
javaClass.packageName = collectLine().toString();
matchEOL();
}
else if ("import".equals(optionName)) {
javaClass.preamble.append("import ");
javaClass.preamble.append(collectWholeLine());
}
else if ("abstract".equals(optionName)) {
javaClass.isAbstract = true;
matchEOL();
}
else if ("final".equals(optionName)) {
javaClass.isFinal = true;
matchEOL();
}
else if ("public".equals(optionName)) {
javaClass.isPublic = true;
matchEOL();
}
else if ("type".equals(optionName)) {
parseTypeDeclaration();
}
else if ("left".equals(optionName)) {
gen.assoc(Assoc.LEFT, parseTokens());
matchEOL();
}
else if ("right".equals(optionName)) {
gen.assoc(Assoc.RIGHT, parseTokens());
matchEOL();
}
else if ("non".equals(optionName)) {
gen.assoc(Assoc.NON, parseTokens());
matchEOL();
}
else if ("token".equals(optionName)) {
for (String s:parseTokens()) {
gen.sym(s);
}
matchEOL();
}
else {
error("option "+optionName+" not understood.");
}
}
else break;
}
}
private void parseTypeDeclaration() throws IOException {
Collection<String> tokens = parseTokens();
match(':');
skipSpace();
String type=collectLine().toString();
for (String t:tokens) setType(t,type);
matchEOL();
}
private Collection<String> parseTokens() throws IOException {
ArrayList<String> tokens = new ArrayList<String>();
while (inAlpha()) {
tokens.add(parseWord("token"));
skipSpace();
}
return tokens;
}
private void setType(String symName, String javaType) {
if (!types.containsKey(javaType)) types.put(javaType, new SemanticClass(javaType));
Sym s = gen.sym(symName);
// if (s.semanticClass != null) foo ;
s.semanticClass = types.get(javaType);
}
private void matchSeparator() throws IOException {
match('%');
match('%');
matchEOL();
}
private void parseGrammar() throws IOException {
while (!atEOF()) {
if (is('%')) {
matchSeparator();
parsePostamble();
return;
} else if (inAlpha()) {
parseNonterminal();
} else if (inEOL()) {
matchEOL();
}
else error("Confused");
}
}
String head;
private void parseNonterminal() throws IOException {
head = parseWord("nonterminal");
skipSpace();
if (maybe(':')) {
skipSpace();
String type=collectLine().toString();
setType(head, type);
}
matchEOL();
while (maybe('\t')) {
parseRule(head);
}
}
private void parseRule(String head) throws IOException {
Collection<String> rhs = parseTokens();
if (rhs.isEmpty() && inEOL()) {
// If the line is completely whitespace, it doesn't count as a rule.
matchEOL();
return;
}
Rule rule = gen.rule(head, rhs);
rule.line = lineNr;
if (maybe(':')) {
rule.note = collectWholeLine().toString();
} else matchEOL();
}
private void parsePostamble() throws IOException {
while(!atEOF()) {
javaClass.code.append((char)look); read();
}
}
}