/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package cb.parser;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Stack;
import cb.petal.Design;
import cb.petal.List;
import cb.petal.Petal;
import cb.petal.PetalFile;
import cb.petal.PetalNode;
import cb.petal.PetalObject;
import cb.petal.StringLiteral;
import cb.petal.Value;
public class Parser {
private Lexer lexer;
private PetalNode current_parent = null;
private Stack parent_stack = new Stack(); // Stack<PetalNode>
private void saveParent(PetalNode new_parent) {
parent_stack.push(current_parent);
current_parent = new_parent;
}
private void restoreParent() {
current_parent = (PetalNode) parent_stack.pop();
}
private static ObjectFactory factory = ObjectFactory.getInstance();
public Parser(Reader r) {
lexer = new Lexer(r);
}
private java.util.List ignored_nodes = Collections.EMPTY_LIST;
/**
* If the parser finds such a node while building the petal tree, the node will be ignored and not added to the tree. E.g,
* setIgnoredNodes(Diagram.class) will abandon all diagrams of the model.
*/
public void setIgnoredNodes(java.lang.Class[] nodes) {
ignored_nodes = new ArrayList(Arrays.asList(nodes));
}
public java.lang.Class[] getIgnoredNodes() {
java.lang.Class[] nodes = new java.lang.Class[ignored_nodes.size()];
ignored_nodes.toArray(nodes);
return nodes;
}
private boolean ignored(PetalNode obj) {
for (Iterator i = ignored_nodes.iterator(); i.hasNext();) {
java.lang.Class clazz = (java.lang.Class) i.next();
if (clazz.isInstance(obj)) {
return true;
}
}
return false;
}
private Token match(int kind, String match) {
Token t = lexer.getToken();
if (t.kind != kind) {
throw new RuntimeException("Mismatch: Expected " + kind + " but got " + t.kind + " at line " + t.line);
}
if (match != null && !match.equals(t.image)) {
throw new RuntimeException("Mismatch: Expected " + match + " but got " + t.image + " at line " + t.line);
}
return t;
}
private Token match(int kind) {
return match(kind, null);
}
private ArrayList list = new ArrayList(); // Reused list to collect docs
/**
* (...)* wildcard
*
* @return images
*/
private ArrayList matchAll(int kind) {
list.clear();
Token t = lexer.getToken();
while (t.kind == kind) {
list.add(t.image);
t = lexer.getToken();
}
lexer.ungetToken(t);
return list;
}
/**
* [...] optional
*
* @return image
*/
private Token matchAny(int kind) {
Token t = lexer.getToken();
if (t.kind == kind) {
return t;
} else {
lexer.ungetToken(t);
return null;
}
}
public static PetalFile parse(String file_name) {
return parse(new File(file_name));
}
public static PetalFile parse(java.net.URL url) {
try {
return parse(url.openStream());
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
public static PetalFile parse(File file) {
try {
PetalFile tree = parse(new FileReader(file));
String name = file.getName();
int index = name.lastIndexOf('.');
if (index > 0) {
name = name.substring(0, index);
}
tree.setModelName(name);
return tree;
} catch (IOException e) {
throw new RuntimeException(e.toString());
}
}
public static PetalFile parse(Reader stream) {
return new Parser(stream).parse();
}
public static PetalFile parse(InputStream stream) {
return parse(new InputStreamReader(stream));
}
/**
* Top level construct are always petal and design objects
*/
public PetalFile parse() {
PetalObject petal, design;
PetalFile file = new PetalFile();
current_parent = file;
petal = parseObject();
design = parseObject();
file.setPetal((Petal) petal);
file.setDesign((Design) design);
return file;
}
/* Example: (object ClassView "Class" "Use Case View::Student" @76
* location (160, 176))
*/
public PetalObject parseObject() {
match(Lexer.LPAREN);
match(Lexer.IDENT, "object");
Token t1 = match(Lexer.IDENT);
ArrayList docs = matchAll(Lexer.STRING);
Token t3 = matchAny(Lexer.TAG);
PetalNode parent = ignored(current_parent) ? null : current_parent;
PetalObject obj = factory.createObject(parent, t1.image, docs, t3 == null ? null : t3.image);
saveParent(obj);
/* List of properties
*/
Token t4 = matchAny(Lexer.IDENT);
while (t4 != null) {
PetalNode prop = parseValue(false);
if (prop != null) {
obj.addProperty(t4.image, prop);
}
t4 = matchAny(Lexer.IDENT);
}
match(Lexer.RPAREN);
restoreParent();
if (!ignored(obj)) {
obj.init();
return obj;
} else {
return null;
}
}
public PetalNode parseValue(boolean rparen_ok) {
Token t = lexer.getToken();
switch (t.kind) {
case Lexer.STRING:
return factory.createString(t.image, false);
case Lexer.MULTI_STRING:
return factory.createString(t.image, true);
case Lexer.INTEGER:
return factory.createInteger(t.image);
case Lexer.FLOAT:
return factory.createFloat(t.image);
case Lexer.BOOLEAN:
return factory.createBoolean(t.image);
case Lexer.TAG:
return factory.createTag(t.image);
case Lexer.LPAREN:
Token t2 = lexer.getToken();
switch (t2.kind) {
case Lexer.IDENT:
lexer.ungetToken(t2);
lexer.ungetToken(t);
if (t2.image.equals("object")) {
return parseObject();
} else if (t2.image.equals("list")) {
return parseList();
} else if (t2.image.equals("value")) {
return parseValueObject();
} else {
throw new RuntimeException("Unexpected " + t2.image + " after (");
}
case Lexer.INTEGER:
match(Lexer.COMMA);
Token t3 = match(Lexer.INTEGER);
match(Lexer.RPAREN);
return factory.createLocation(t2.image, t3.image);
case Lexer.STRING:
Token t4 = match(Lexer.INTEGER);
match(Lexer.RPAREN);
return factory.createTuple(t2.image, t4.image);
default:
throw new RuntimeException("Unexpected " + t2.image + "after (");
}
default:
if (t.kind == Lexer.RPAREN && rparen_ok) {
return null;
} else {
throw new RuntimeException("Unexpected " + t.image);
}
}
}
/* Example: (list unit_reference_list (object Module_Diagram "Main"
* quid "35CB163B03CF"))
*
*/
public List parseList() {
match(Lexer.LPAREN);
match(Lexer.IDENT, "list");
Token t = matchAny(Lexer.IDENT);
List list = factory.createList(t == null ? null : t.image);
PetalNode obj;
while ((obj = parseValue(true)) != null) {
// null == RPAREN
list.add(obj);
}
return list;
}
public Value parseValueObject() {
match(Lexer.LPAREN);
match(Lexer.IDENT, "value");
Token t = match(Lexer.IDENT);
Token t2 = lexer.getToken();
StringLiteral str;
switch (t2.kind) {
case Lexer.STRING:
str = factory.createString(t2.image, false);
break;
case Lexer.MULTI_STRING:
str = factory.createString(t2.image, true);
break;
default:
throw new RuntimeException("Unexpected " + t2.image + " in (value ...)");
}
match(Lexer.RPAREN);
return factory.createValue(t.image, str);
}
public static void main(String[] args) throws Exception {
FileReader r = new FileReader(args[0]);
Parser parser = new Parser(r);
parser.parse();
}
}