+++ /dev/null
-/*--------------------------------------------------------------------------\r
- * Copyright 2009 Taro L. Saito\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- *--------------------------------------------------------------------------*/\r
-//--------------------------------------\r
-// XerialJ\r
-//\r
-// SilkNodeParser.java\r
-// Since: May 29, 2009 11:47:21 AM\r
-//\r
-// $URL: http://www.xerial.org/svn/project/XerialJ/trunk/xerial-core/src/main/java/org/xerial/silk/impl/SilkNodeParser.java $\r
-// $Author: leo $\r
-//--------------------------------------\r
-package org.xerial.silk.impl;\r
-\r
-import static org.xerial.silk.impl.SilkLineLexer.*;\r
-\r
-import java.util.List;\r
-\r
-import org.antlr.runtime.CommonTokenStream;\r
-import org.antlr.runtime.Token;\r
-import org.xerial.core.XerialErrorCode;\r
-import org.xerial.core.XerialException;\r
-import org.xerial.util.CollectionUtil;\r
-import org.xerial.util.Functor;\r
-import org.xerial.util.StringUtil;\r
-\r
-/**\r
- * Recursive descent parser for {@link SilkNode} and {@link SilkFunction}\r
- * \r
- * @author leo\r
- * \r
- */\r
-public class SilkNodeParser {\r
- private CommonTokenStream tokenStream;\r
-\r
- public SilkNodeParser(CommonTokenStream tokenStream) {\r
- this.tokenStream = tokenStream;\r
- }\r
-\r
- public SilkElement parse() throws XerialException {\r
- switch (tokenStream.LA(1)) {\r
- case NodeIndent:\r
- if (tokenStream.LA(2) == At)\r
- return parseFunction();\r
- else\r
- return parseSilkNode();\r
- case FunctionIndent:\r
- SilkFunction func = parseFunction();\r
- return func;\r
- default:\r
- throw unexpectedToken(tokenStream.LT(1), NodeIndent, FunctionIndent);\r
- }\r
- }\r
-\r
- private Token getToken(int index) {\r
- Token t = tokenStream.LT(index);\r
- return t;\r
- }\r
-\r
- private void consume() {\r
- tokenStream.consume();\r
- }\r
-\r
- public SilkNode parseSilkNode() throws XerialException {\r
- return parseSilkNode(new SilkNode());\r
- }\r
-\r
- private SilkNode parseSilkNode(SilkNode node) throws XerialException {\r
- if (!nextTokenIs(NodeIndent))\r
- throw new XerialException(XerialErrorCode.PARSE_ERROR, "expected a node indent, but "\r
- + toString(tokenStream.LT(1)));\r
-\r
- Token indent = getToken(1);\r
- node.setNodeIndent(indent.getText());\r
- consume();\r
-\r
- switch (tokenStream.LA(1)) {\r
- case LParen:\r
- consume();\r
- parseAttributeList(node);\r
- testAndConsume(RParen);\r
- break;\r
- default: {\r
- parseNodeItem(node);\r
- break;\r
- }\r
- }\r
- return node;\r
- }\r
-\r
- private SilkNode parseNodeItem() throws XerialException {\r
- SilkNode newNode = new SilkNode();\r
- return parseNodeItem(newNode);\r
- }\r
-\r
- private SilkNode parseNodeItem(SilkNode contextNode) throws XerialException {\r
- // node name\r
- String nodeName = parseNodeName();\r
- contextNode.setName(nodeName);\r
-\r
- parseDataType(contextNode);\r
-\r
- parseNodeItemAttr(contextNode);\r
-\r
- parsePlural(contextNode);\r
-\r
- parseNodeValueOpt(contextNode);\r
-\r
- return contextNode;\r
- }\r
-\r
- private void parseNodeValueOpt(SilkNode node) throws XerialException {\r
- if (!nextTokenIs(Colon))\r
- return;\r
-\r
- consume();\r
-\r
- parseNodeValue(node);\r
- }\r
-\r
- private void parseNodeValue(SilkNode node) throws XerialException {\r
- int nextToken = tokenStream.LA(1);\r
-\r
- switch (nextToken) {\r
- case At:\r
- SilkFunction func = parseFunctionInternal(new SilkFunction());\r
- node.setFunction(func);\r
- break;\r
- case PlainOneLine:\r
- case String: {\r
- Token t = getToken(1);\r
- consume();\r
- node.setValue(t.getText());\r
- break;\r
- }\r
- case JSON: {\r
- Token t = getToken(1);\r
- consume();\r
- node.setJSON(t.getText());\r
- break;\r
- }\r
- default:\r
- throw unexpectedToken(tokenStream.LT(1), At, PlainOneLine, String, JSON);\r
- }\r
-\r
- }\r
-\r
- private String parseNodeValue() throws XerialException {\r
- int nextToken = tokenStream.LA(1);\r
-\r
- switch (nextToken) {\r
- case At:\r
- // TODO\r
- throw new XerialException(XerialErrorCode.PARSE_ERROR,\r
- "nested function is not yet supported");\r
- case PlainOneLine:\r
- case String:\r
- case JSON: {\r
- Token t = getToken(1);\r
- consume();\r
- return t.getText();\r
- }\r
- default:\r
- throw unexpectedToken(tokenStream.LT(1), At, PlainOneLine, String, JSON);\r
- }\r
-\r
- }\r
-\r
- private void parsePlural(SilkNode node) {\r
- int nextTokenType = tokenStream.LA(1);\r
- switch (nextTokenType) {\r
- case Star:\r
- node.setOccurrence(SilkNodeOccurrence.ZERO_OR_MORE);\r
- consume();\r
- break;\r
- case Plus:\r
- node.setOccurrence(SilkNodeOccurrence.ONE_OR_MORE);\r
- consume();\r
- break;\r
- case Question:\r
- node.setOccurrence(SilkNodeOccurrence.ZERO_OR_ONE);\r
- consume();\r
- break;\r
- case EqEq:\r
- node.setOccurrence(SilkNodeOccurrence.MULTILINE_SEQUENCE);\r
- consume();\r
- break;\r
- case Seq:\r
- if (tokenStream.LA(2) == Seq) {\r
- node.setOccurrence(SilkNodeOccurrence.SEQUENCE_WITH_NEWLINE);\r
- consume();\r
- break;\r
- }\r
- else {\r
- node.setOccurrence(SilkNodeOccurrence.SEQUENCE);\r
- consume();\r
- break;\r
- }\r
- case TabSeq:\r
- node.setOccurrence(SilkNodeOccurrence.TABBED_SEQUENCE);\r
- consume();\r
- break;\r
- default:\r
- // do nothing\r
- break;\r
- }\r
-\r
- }\r
-\r
- private void parseNodeItemAttr(SilkNode node) throws XerialException {\r
- if (!nextTokenIs(LParen))\r
- return;\r
-\r
- consume();\r
-\r
- parseAttributeList(node);\r
-\r
- testAndConsume(RParen);\r
-\r
- }\r
-\r
- private void parseAttributeList(SilkNode contextNode) throws XerialException {\r
- SilkNode attributeNode = parseNodeItem();\r
- contextNode.addSilkNode(attributeNode);\r
-\r
- while (nextTokenIs(Comma)) {\r
- consume();\r
- attributeNode = parseNodeItem();\r
- contextNode.addSilkNode(attributeNode);\r
- }\r
-\r
- }\r
-\r
- /**\r
- * test match with the specified token type, and return the matched token.\r
- * If the input token doesn't match, throw an exception\r
- * \r
- * @param tokenType\r
- * @return matched token\r
- * @throws XerialException\r
- */\r
- private Token testAndConsume(int tokenType) throws XerialException {\r
- Token t = getToken(1);\r
- if (t.getType() == tokenType) {\r
- consume();\r
- return t;\r
- }\r
- else {\r
- throw unexpectedToken(t, tokenType);\r
- }\r
- }\r
-\r
- /**\r
- * @param tokenType\r
- * @return true when the next token has the specified token type, otherwise\r
- * false\r
- */\r
- private boolean nextTokenIs(int tokenType) {\r
- return tokenStream.LA(1) == tokenType;\r
- }\r
-\r
- private void parseDataType(SilkNode node) throws XerialException {\r
- if (!nextTokenIs(LBracket))\r
- return;\r
-\r
- consume();\r
-\r
- // dataTypeName\r
- Token dtToken = getToken(1);\r
-\r
- String nodeName = testAndConsume(PlainOneLine).getText();\r
- node.setDataType(nodeName);\r
-\r
- testAndConsume(RBracket);\r
-\r
- }\r
-\r
- private String parseNodeName() throws XerialException {\r
- Token t = getToken(1);\r
- switch (tokenStream.LA(1)) {\r
- case PlainOneLine:\r
- case String:\r
- consume();\r
- return t.getText().trim();\r
- default:\r
- throw unexpectedToken(t, PlainOneLine, String);\r
- }\r
- }\r
-\r
- private SilkFunction parseFunction() throws XerialException {\r
- SilkFunction func = new SilkFunction();\r
- switch (tokenStream.LA(1)) {\r
- case NodeIndent: {\r
- Token t = getToken(1);\r
- func.setNodeIndent(t.getText());\r
- consume();\r
- parseFunctionInternal(func);\r
- return func;\r
- }\r
- case FunctionIndent: {\r
- Token t = getToken(1); // function indent\r
- func.setNodeIndent(t.getText());\r
- consume();\r
-\r
- Token funcName = testAndConsume(PlainOneLine);\r
- func.setName(funcName.getText().trim());\r
-\r
- parseFunctionArgs(func);\r
-\r
- return func;\r
- }\r
- default:\r
-\r
- throw unexpectedToken(tokenStream.LT(1), NodeIndent, FunctionIndent);\r
- }\r
-\r
- }\r
-\r
- private void parseFunctionArgs(SilkFunction func) throws XerialException {\r
- testAndConsume(LParen);\r
- if (!nextTokenIs(RParen)) {\r
- parseFunctionArg(func);\r
- while (nextTokenIs(Comma)) {\r
- consume();\r
- parseFunctionArg(func);\r
- }\r
- }\r
-\r
- testAndConsume(RParen);\r
-\r
- }\r
-\r
- private void parseFunctionArg(SilkFunction func) throws XerialException {\r
- if (nextTokenIs(String) || nextTokenIs(PlainOneLine)) {\r
- if (tokenStream.LA(2) == Colon) {\r
- Token key = getToken(1);\r
- consume();\r
- consume();\r
- String value = parseNodeValue();\r
- func.addKeyAndValue(key.getText().trim(), value.trim());\r
- return;\r
- }\r
- }\r
-\r
- String value = parseNodeValue();\r
- func.addArgument(value);\r
- }\r
-\r
- private SilkFunction parseFunctionInternal(SilkFunction func) throws XerialException {\r
- testAndConsume(At);\r
- Token funcName = testAndConsume(PlainOneLine);\r
- func.setName(funcName.getText().trim());\r
-\r
- parseFunctionArgs(func);\r
-\r
- return func;\r
- }\r
-\r
- private XerialException unexpectedToken(Token t) throws XerialException {\r
- return new XerialException(XerialErrorCode.PARSE_ERROR, java.lang.String.format(\r
- "unexpected token %s", SilkLineParser.tokenNames[t.getType()], toString(t)));\r
-\r
- }\r
-\r
- private XerialException unexpectedToken(Token t, Integer... expectedTokenTypes) {\r
- List<String> expectedTokenNames = CollectionUtil.collect(expectedTokenTypes,\r
- new Functor<Integer, String>() {\r
- public String apply(Integer input) {\r
- return SilkLineParser.tokenNames[input.intValue()];\r
- }\r
- });\r
-\r
- return new XerialException(XerialErrorCode.PARSE_ERROR, java.lang.String.format(\r
- "expected %s, but %s", StringUtil.join(expectedTokenNames, " or "), toString(t)));\r
- }\r
-\r
- private String toString(Token t) {\r
-\r
- int type = t.getType();\r
- return java.lang.String.format("[%s at char %d: %s]", type >= 0\r
- && type < SilkLineParser.tokenNames.length ? SilkLineParser.tokenNames[type]\r
- : "unknown token type: " + type, t.getCharPositionInLine(), t.getText());\r
- }\r
-}\r