From 70299a3eb281d3083829a0f97c809e2f4af59fe2 Mon Sep 17 00:00:00 2001 From: bjculkin Date: Mon, 20 Mar 2017 19:18:27 -0400 Subject: Work more on parser This does more work on the parser, including moving from the old LineReader/Scanner combo to BlockReader. It includes more syntax checking, but still doesn't actually do anything. With some dummy methods in RGrammarBuilder, should be able to see if the basic file parser is working correctly. Where blocks and their syntax still need to implemented, and actual pragma implementations need to be provided. --- .../java/bjc/rgens/newparser/GrammarException.java | 39 ++++ .../java/bjc/rgens/newparser/RGrammarBuilder.java | 19 ++ .../java/bjc/rgens/newparser/RGrammarParser.java | 256 ++++++++++++--------- 3 files changed, 206 insertions(+), 108 deletions(-) create mode 100644 RGens/src/main/java/bjc/rgens/newparser/GrammarException.java (limited to 'RGens/src/main/java/bjc/rgens/newparser') diff --git a/RGens/src/main/java/bjc/rgens/newparser/GrammarException.java b/RGens/src/main/java/bjc/rgens/newparser/GrammarException.java new file mode 100644 index 0000000..c9f2723 --- /dev/null +++ b/RGens/src/main/java/bjc/rgens/newparser/GrammarException.java @@ -0,0 +1,39 @@ +package bjc.rgens.newparser; + +/** + * The exception thrown when something goes wrong while parsing a + * grammar. + * + * @author student + * + */ +public class GrammarException extends RuntimeException { + /* + * Serialization ID. + */ + private static final long serialVersionUID = -7287427479316953668L; + + /** + * Create a new grammar exception with the specified message. + * + * @param msg + * The message for this exception. + */ + public GrammarException(String msg) { + super(msg); + } + + /** + * Create a new grammar exception with the specified message and + * cause. + * + * @param msg + * The message for this exception. + * + * @param cause + * The cause of this exception. + */ + public GrammarException(String msg, Exception cause) { + super(msg, cause); + } +} \ No newline at end of file diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java index 073e0ee..fdf2433 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammarBuilder.java @@ -29,4 +29,23 @@ public class RGrammarBuilder { */ return null; } + + public void addCasePart(String csepart) { + // TODO Auto-generated method stub + } + + public void endRule() { + // TODO Auto-generated method stub + + } + + public void finishCase() { + // TODO Auto-generated method stub + + } + + public void startWhereBlock(String string) { + // TODO Auto-generated method stub + + } } diff --git a/RGens/src/main/java/bjc/rgens/newparser/RGrammarParser.java b/RGens/src/main/java/bjc/rgens/newparser/RGrammarParser.java index cf85cb5..9e55736 100644 --- a/RGens/src/main/java/bjc/rgens/newparser/RGrammarParser.java +++ b/RGens/src/main/java/bjc/rgens/newparser/RGrammarParser.java @@ -1,6 +1,8 @@ package bjc.rgens.newparser; import bjc.utils.funcutils.TriConsumer; +import bjc.utils.parserutils.BlockReader; +import bjc.utils.parserutils.BlockReader.Block; import java.io.InputStream; import java.io.InputStreamReader; @@ -17,43 +19,16 @@ import java.util.Scanner; * */ public class RGrammarParser { - /** - * The exception thrown when something goes wrong while parsing a - * grammar. - * - * @author student - * + /* + * Templates for level-dependent delimiters. */ - public static class GrammarException extends Exception { - /* - * Serialization ID. - */ - private static final long serialVersionUID = -7287427479316953668L; - - /** - * Create a new grammar exception with the specified message. - * - * @param msg - * The message for this exception. - */ - public GrammarException(String msg) { - super(msg); - } + private static final String TMPL_PRAGMA_BLOCK_DELIM = "\\n\\t{%d}(?!\\t)"; + private static final String TMPL_RULEDECL_BLOCK_DELIM = "\\n\\t\\t{%d}"; - /** - * Create a new grammar exception with the specified message and - * cause. - * - * @param msg - * The message for this exception. - * - * @param cause - * The cause of this exception. - */ - public GrammarException(String msg, Exception cause) { - super(msg, cause); - } - } + /* + * Templates for non-level-dependent delimiters. + */ + private static final String TOPLEVEL_BLOCK_DELIM = "\\n\\.?\\n"; /* * Pragma impls. @@ -79,25 +54,25 @@ public class RGrammarParser { * Thrown if the grammar has a syntax error. */ public RGrammar readGrammar(InputStream is) throws GrammarException { - LineNumberReader lnReader = new LineNumberReader(new InputStreamReader(is)); - - int blockNo = 0; - try(Scanner scn = new Scanner(lnReader)) { - scn.useDelimiter("\\n\\.?\\n"); + try(BlockReader reader = new BlockReader(TOPLEVEL_BLOCK_DELIM, new InputStreamReader(is))) { + if(!reader.hasNextBlock()) { + throw new GrammarException("At least one top-level block must be present"); + } - RGrammarBuilder build = new RGrammarBuilder(); + try { + RGrammarBuilder build = new RGrammarBuilder(); - while(scn.hasNext()) { - String block = scn.next(); - blockNo += 1; + reader.forEachBlock((block) -> { + handleBlock(build, block.contents, 0); + }); - handleBlock(build, block, 0); + return build.toRGrammar(); + } catch(GrammarException gex) { + throw new GrammarException(String.format("Error in block (%s)", reader.getBlock()), + gex); } - - return build.toRGrammar(); - } catch(GrammarException gex) { - throw new GrammarException(String.format("Error in block %d at line %d of stream", blockNo, - lnReader.getLineNumber()), gex); + } catch(Exception ex) { + throw new GrammarException(String.format("Unknown error handling block"), ex); } } @@ -110,14 +85,23 @@ public class RGrammarParser { * Handles an arbitrary block. */ private void handleBlock(RGrammarBuilder build, String block, int level) throws GrammarException { - if(block.startsWith("pragma")) { + int typeSep = block.indexOf(' '); + + if(typeSep == -1) { + throw new GrammarException( + "A block must start with a type, followed by a space, then the rest of the block"); + } + + String blockType = block.substring(0, typeSep); + + if(blockType.equalsIgnoreCase("pragma")) { handlePragmaBlock(block, build, level); - } else if(block.startsWith("[")) { + } else if(blockType.startsWith("[")) { handleRuleBlock(block, build, level); - } else if(block.startsWith("where")) { + } else if(blockType.equalsIgnoreCase("where")) { handleWhereBlock(block, build, level); } else { - throw new GrammarException(String.format("Unknown block: %s", block)); + throw new GrammarException(String.format("Unknown block type: '%s'", blockType)); } } @@ -125,88 +109,144 @@ public class RGrammarParser { * Handle reading a block of pragmas. */ private void handlePragmaBlock(String block, RGrammarBuilder build, int level) throws GrammarException { - LineNumberReader lnReader = new LineNumberReader(new StringReader(block)); - - int pragmaNo = 0; - try(Scanner deblocker = new Scanner(lnReader)) { - deblocker.useDelimiter(String.format("\\n\\t{%d}(?!\\t)", level)); - - while(deblocker.hasNext()) { - String pragma = deblocker.next(); - pragmaNo += 1; - - if(!pragma.startsWith("pragma")) { - throw new GrammarException(String.format("Illegal line: %s", pragma)); - } else { - handlePragma(pragma.substring(7), build, level); - } + try(BlockReader pragmaReader = new BlockReader(String.format(TMPL_PRAGMA_BLOCK_DELIM, level), + new StringReader(block))) { + try { + pragmaReader.forEachBlock((pragma) -> { + String pragmaContents = pragma.contents; + + int pragmaSep = pragmaContents.indexOf(' '); + + if(pragmaSep == -1) { + throw new GrammarException( + "A pragma invocation must consist of the word pragma," + + " followed by a space, then the body of the pragma"); + } + + String pragmaLeader = pragmaContents.substring(0, pragmaSep); + String pragmaBody = pragmaContents.substring(pragmaSep); + + if(!pragmaLeader.equalsIgnoreCase("pragma")) { + throw new GrammarException( + String.format("Illegal line leader in pragma block: '%s'", + pragmaLeader)); + } else { + handlePragma(pragmaBody, build, level); + } + }); + } catch(GrammarException gex) { + Block pragma = pragmaReader.getBlock(); + + throw new GrammarException(String.format("Error in pragma: (%s)", pragma), gex); } - } catch(GrammarException gex) { - throw new GrammarException(String.format("Error in pragma %d at line %d", pragmaNo, - lnReader.getLineNumber()), gex); + } catch(Exception ex) { + throw new GrammarException("Unknown error handling pragma block", ex); } } + /* + * Handle an individual pragma in a block. + */ private void handlePragma(String pragma, RGrammarBuilder build, int level) throws GrammarException { - String pragmaName = pragma.substring(0, pragma.indexOf(' ')); + int bodySep = pragma.indexOf(' '); + + if(bodySep == -1) bodySep = pragma.length(); + + String pragmaName = pragma.substring(0, bodySep); + String pragmaBody = pragma.substring(bodySep); if(pragmas.containsKey(pragmaName)) { - pragmas.get(pragmaName).accept(pragma.substring(pragma.indexOf(' ')), build, level); + pragmas.get(pragmaName).accept(pragmaBody, build, level); } else { - throw new GrammarException(String.format("Unknown pragma %s", pragmaName)); + throw new GrammarException(String.format("Unknown pragma named '%s'", pragmaName)); } } /* - * Handle a block of a rule and multiple cases. + * Handle a block of a rule declaration and one or more cases. */ - private void handleRuleBlock(String block, RGrammarBuilder build, int level) throws GrammarException { - LineNumberReader lnReader = new LineNumberReader(new StringReader(block)); - - int caseNo = 0; - try(Scanner scn = new Scanner(lnReader)) { - scn.useDelimiter(String.format("\\n\\t\\t{%d}", level)); - - String decl = scn.next(); - - String ruleName = decl.substring(0, decl.indexOf(' ')); - - if(ruleName.equals("")) { - throw new GrammarException("The empty string is not a valid rule name"); + private void handleRuleBlock(String ruleBlock, RGrammarBuilder build, int level) throws GrammarException { + try(BlockReader ruleReader = new BlockReader(String.format(TMPL_RULEDECL_BLOCK_DELIM, level), + new StringReader(ruleBlock))) { + try { + if(ruleReader.hasNextBlock()) { + /* + * Rule with a declaration followed by + * multiple cases. + */ + ruleReader.nextBlock(); + Block declBlock = ruleReader.getBlock(); + + String declContents = declBlock.contents; + handleRuleDecl(build, declContents); + + ruleReader.forEachBlock((block) -> { + handleRuleCase(block.contents, build); + }); + } else { + /* + * Rule with a declaration followed by a + * single case. + */ + handleRuleDecl(build, ruleBlock); + } + } catch(GrammarException gex) { + throw new GrammarException( + String.format("Error in rule case (%s)", ruleReader.getBlock()), gex); } + } catch(Exception ex) { + throw new GrammarException("Unknown error handling rule block", ex); + } + } - build.setCurrentRule(ruleName); - - String initCase = decl.substring(decl.indexOf(' ')); - caseNo += 1; + /* + * Handle a rule declaration and its initial case. + */ + private void handleRuleDecl(RGrammarBuilder build, String declContents) { + int declSep = declContents.indexOf("\u2192"); - handleRuleCase(initCase, build); + if(declSep == -1) { + throw new GrammarException("A rule must be given at least one case in its declaration, and" + + "seperated from that case by \u2192"); + } - while(scn.hasNext()) { - String cse = scn.next(); - caseNo += 1; + String ruleName = declContents.substring(0, declSep).trim(); + String ruleBody = declContents.substring(declSep).trim(); - handleRuleCase(cse, build); - } - } catch(GrammarException gex) { - throw new GrammarException( - String.format("Error in case %d at line %d", caseNo, lnReader.getLineNumber()), - gex); + if(ruleName.equals("")) { + throw new GrammarException("The empty string is not a valid rule name"); } + + build.setCurrentRule(ruleName); + + handleRuleCase(ruleBody, build); } /* * Handle a single case of a rule. */ - private void handleRuleCase(String initCase, RGrammarBuilder build) { - // TODO Auto-generated method stub + private void handleRuleCase(String cse, RGrammarBuilder build) { + for(String csepart : cse.split(" ")) { + build.addCasePart(csepart); + } + build.finishCase(); } /* - * Handle a block of a rule with local rules. + * Handle a where block (a block with local rules). */ - private void handleWhereBlock(String block, RGrammarBuilder build, int level) { - // TODO Auto-generated method stub + private void handleWhereBlock(String block, RGrammarBuilder build, int level) throws GrammarException { + try(BlockReader whereReader = new BlockReader("", new StringReader(block))) { + try { + + } catch(GrammarException gex) { + throw new GrammarException( + String.format("Error in where block (%s)", whereReader.getBlock()), + gex); + } + } catch(Exception ex) { + throw new GrammarException("Unknown error in where block", ex); + } } } -- cgit v1.2.3