├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.scala-ide.sdt.core.prefs └── sf.eclipse.javacc.prefs ├── LICENSE ├── Makefile ├── README ├── cmd └── scala-bison ├── examples ├── Calc.lex ├── Calc.scala ├── Calc.y ├── CalcParserBase.scala ├── CalcScanner.scala ├── Java.y ├── Makefile └── README └── src └── edu └── uwm └── cs ├── scalabison ├── .gitignore ├── AcceptAction.scala ├── AcceptNTAction.scala ├── Action.scala ├── AnnounceAction.scala ├── ArtificialNonterminal.scala ├── Bison.y ├── BisonGrammar.scala ├── BisonParserBase.scala ├── BisonScanner.scala ├── BisonTable.scala ├── BlockCode.scala ├── CharLitTerminal.scala ├── Code.scala ├── ErrorAction.scala ├── ErrorNonterminal.scala ├── First.scala ├── FirstSet.scala ├── Follow.scala ├── Generator.scala ├── Grammar.scala ├── GrammarSpecificationError.scala ├── Item.scala ├── LeftCornerFollow.scala ├── LeftCornerState.scala ├── LeftCornerTable.scala ├── LeftPrecedence.scala ├── LiteralCode.scala ├── NoCode.scala ├── NonAssocPrecedence.scala ├── Nonterminal.scala ├── Options.scala ├── Precedence.scala ├── ReduceAction.scala ├── RightPrecedence.scala ├── Rule.scala ├── ShiftAction.scala ├── State.scala ├── Symbol.scala ├── Table.scala ├── Terminal.scala ├── VariableCode.scala └── Version.scala └── util ├── CharUtil.scala ├── Cycle.scala ├── Dominator.scala └── Reach.scala /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/**/* 2 | scala-bison.jar 3 | lib/* 4 | .cache 5 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | scala-bison 4 | JavaCC Nature 5 | 6 | 7 | 8 | 9 | sf.eclipse.javacc.javaccbuilder 10 | 11 | 12 | 13 | 14 | org.scala-ide.sdt.core.scalabuilder 15 | 16 | 17 | 18 | 19 | 20 | org.scala-ide.sdt.core.scalanature 21 | org.eclipse.jdt.core.javanature 22 | sf.eclipse.javacc.javaccnature 23 | 24 | 25 | -------------------------------------------------------------------------------- /.settings/org.scala-ide.sdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Thu Jan 05 17:00:09 CST 2012 2 | Xcheck-null=false 3 | Xcheckinit=false 4 | Xdisable-assertions=false 5 | Xelide-below=-2147483648 6 | Xexperimental=false 7 | Xfatal-warnings=false 8 | Xfuture=false 9 | Xlog-implicits=false 10 | Xmigration=false 11 | Xno-uescape=false 12 | Xpluginsdir=misc/scala-devel/plugins 13 | Ybuild-manager-debug=false 14 | Yno-generic-signatures=false 15 | Yno-imports=false 16 | Ypresentation-debug=false 17 | Ypresentation-delay=0 18 | Ypresentation-verbose=false 19 | Yrecursion=0 20 | Yself-in-annots=false 21 | Ystruct-dispatch=poly-cache 22 | Ywarn-dead-code=false 23 | buildmanager=sbt 24 | compileorder=Mixed 25 | deprecation=false 26 | eclipse.preferences.version=1 27 | explaintypes=false 28 | formatter.alignParameters=false 29 | formatter.alignSingleLineCaseStatements=false 30 | formatter.alignSingleLineCaseStatements.maxArrowIndent=40 31 | formatter.compactControlReadability=false 32 | formatter.compactStringConcatenation=false 33 | formatter.doubleIndentClassDeclaration=false 34 | formatter.formatXml=true 35 | formatter.indentLocalDefs=false 36 | formatter.indentPackageBlocks=true 37 | formatter.indentSpaces=2 38 | formatter.indentWithTabs=false 39 | formatter.multilineScaladocCommentsStartOnFirstLine=false 40 | formatter.placeScaladocAsterisksBeneathSecondAsterisk=false 41 | formatter.preserveDanglingCloseParenthesis=false 42 | formatter.preserveSpaceBeforeArguments=false 43 | formatter.rewriteArrowSymbols=false 44 | formatter.spaceBeforeColon=false 45 | formatter.spaceInsideBrackets=false 46 | formatter.spaceInsideParentheses=false 47 | formatter.spacesWithinPatternBinders=true 48 | g=vars 49 | no-specialization=false 50 | nowarn=false 51 | optimise=false 52 | organizeimports.expandcollapse=expand 53 | organizeimports.groups=java$scala$org$com 54 | organizeimports.wildcards=scalaz$scalaz.Scalaz 55 | scala.compiler.useProjectSettings=false 56 | syntaxColouring.bracket.bold=false 57 | syntaxColouring.bracket.colour=0,0,0 58 | syntaxColouring.bracket.italic=false 59 | syntaxColouring.bracket.strikethrough=false 60 | syntaxColouring.bracket.underline=false 61 | syntaxColouring.default.bold=false 62 | syntaxColouring.default.colour=0,0,0 63 | syntaxColouring.default.italic=false 64 | syntaxColouring.default.strikethrough=false 65 | syntaxColouring.default.underline=false 66 | syntaxColouring.keyword.bold=true 67 | syntaxColouring.keyword.colour=127,0,85 68 | syntaxColouring.keyword.italic=false 69 | syntaxColouring.keyword.strikethrough=false 70 | syntaxColouring.keyword.underline=false 71 | syntaxColouring.operator.bold=false 72 | syntaxColouring.operator.colour=0,0,0 73 | syntaxColouring.operator.italic=false 74 | syntaxColouring.operator.strikethrough=false 75 | syntaxColouring.operator.underline=false 76 | syntaxColouring.scaladoc.bold=false 77 | syntaxColouring.scaladoc.colour=63,95,191 78 | syntaxColouring.scaladoc.italic=false 79 | syntaxColouring.scaladoc.strikethrough=false 80 | syntaxColouring.scaladoc.underline=false 81 | syntaxColouring.singleLineComment.bold=false 82 | syntaxColouring.singleLineComment.colour=63,127,95 83 | syntaxColouring.singleLineComment.italic=false 84 | syntaxColouring.singleLineComment.strikethrough=false 85 | syntaxColouring.singleLineComment.underline=false 86 | syntaxColouring.string.bold=false 87 | syntaxColouring.string.colour=42,0,255 88 | syntaxColouring.string.italic=false 89 | syntaxColouring.string.strikethrough=false 90 | syntaxColouring.string.underline=false 91 | syntaxColouring.xml.attributeName.bold=false 92 | syntaxColouring.xml.attributeName.colour=127,0,127 93 | syntaxColouring.xml.attributeName.italic=false 94 | syntaxColouring.xml.attributeName.strikethrough=false 95 | syntaxColouring.xml.attributeName.underline=false 96 | syntaxColouring.xml.attributeValue.bold=false 97 | syntaxColouring.xml.attributeValue.colour=42,0,255 98 | syntaxColouring.xml.attributeValue.italic=true 99 | syntaxColouring.xml.attributeValue.strikethrough=false 100 | syntaxColouring.xml.attributeValue.underline=false 101 | syntaxColouring.xml.comment.bold=false 102 | syntaxColouring.xml.comment.colour=63,85,191 103 | syntaxColouring.xml.comment.italic=false 104 | syntaxColouring.xml.comment.strikethrough=false 105 | syntaxColouring.xml.comment.underline=false 106 | syntaxColouring.xml.equals.bold=false 107 | syntaxColouring.xml.equals.colour=0,0,0 108 | syntaxColouring.xml.equals.italic=false 109 | syntaxColouring.xml.equals.strikethrough=false 110 | syntaxColouring.xml.equals.underline=false 111 | syntaxColouring.xml.processingInstruction.bold=false 112 | syntaxColouring.xml.processingInstruction.colour=0,128,128 113 | syntaxColouring.xml.processingInstruction.italic=false 114 | syntaxColouring.xml.processingInstruction.strikethrough=false 115 | syntaxColouring.xml.processingInstruction.underline=false 116 | syntaxColouring.xml.tagDelimiter.bold=false 117 | syntaxColouring.xml.tagDelimiter.colour=0,128,128 118 | syntaxColouring.xml.tagDelimiter.italic=false 119 | syntaxColouring.xml.tagDelimiter.strikethrough=false 120 | syntaxColouring.xml.tagDelimiter.underline=false 121 | syntaxColouring.xml.tagName.bold=false 122 | syntaxColouring.xml.tagName.colour=63,127,127 123 | syntaxColouring.xml.tagName.italic=false 124 | syntaxColouring.xml.tagName.strikethrough=false 125 | syntaxColouring.xml.tagName.underline=false 126 | target=jvm-1.5 127 | unchecked=false 128 | verbose=false 129 | -------------------------------------------------------------------------------- /.settings/sf.eclipse.javacc.prefs: -------------------------------------------------------------------------------- 1 | #Thu Jan 05 14:43:47 CST 2012 2 | CLEAR_CONSOLE=true 3 | JAVACC_OPTIONS= 4 | JJDOC_OPTIONS= 5 | JJTREE_OPTIONS= 6 | JJ_NATURE=true 7 | JTB_OPTIONS=-ia -jd -tk 8 | RUNTIME_JJJAR=/Applications/eclipse/plugins/sf.eclipse.javacc_1.5.24/javacc.jar 9 | RUNTIME_JTBJAR=/Applications/eclipse/plugins/sf.eclipse.javacc_1.5.24/jtb-1.4.4.jar 10 | SUPPRESS_WARNINGS=false 11 | eclipse.preferences.version=1 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | scala-bison.jar : 2 | @if [ -f bin/edu/uwm/cs/scalabison/BisonParser.class ]; \ 3 | then \ 4 | echo jar cf scala-bison.jar -C bin edu; \ 5 | jar cf scala-bison.jar -C bin edu; \ 6 | elif [ -f src/edu/uwm/cs/scalabison/BisonParser.scala ]; \ 7 | then \ 8 | make compile && make scala-bison.jar; \ 9 | else \ 10 | echo "Need to fetch scala-bison.jar from the distribution site."; \ 11 | fi 12 | 13 | .PHONY: compile 14 | compile: 15 | mkdir -p bin 16 | (cd src; scalac -d ../bin edu/uwm/cs/util/*.scala edu/uwm/cs/scalabison/*.scala) 17 | rm -f scala-bison.jar 18 | 19 | PDIR = src/edu/uwm/cs/scalabison/ 20 | 21 | .PHONY: boot 22 | boot : scala-bison.jar 23 | bison -v ${PDIR}/Bison.y 24 | rm Bison.tab.c 25 | scala -cp scala-bison.jar -howtorun:object edu.uwm.cs.scalabison.RunGenerator ${PDIR}/Bison.y 26 | rm Bison.output 27 | cp BisonParser.scala BisonTokens.scala ${PDIR}/. 28 | @echo "Now refresh/rebuild the project" 29 | 30 | .PHONY: boot-trace 31 | boot-trace: scala-bison.jar 32 | bison -v ${PDIR}/Bison.y 33 | rm Bison.tab.c 34 | scala -cp scala-bison.jar -howtorun:object edu.uwm.cs.scalabison.RunGenerator -t -T ${PDIR}/Bison.y 35 | rm Bison.output 36 | cp BisonParser.scala BisonTokens.scala ${PDIR}/. 37 | @echo "Now refresh/rebuild the project" 38 | 39 | .PHONY: clean 40 | clean : 41 | rm -fr Bison* scala-bison.jar bin/edu 42 | 43 | .PHONY: all-jar 44 | all-jar: jar2.9 jar2.10 jar2.11 45 | 46 | .PHONY: jar2.* 47 | 48 | jar2.% : clean 49 | mkdir -p bin 50 | (cd src; scalac-2.$* -d ../bin edu/uwm/cs/util/*.scala edu/uwm/cs/scalabison/*.scala) 51 | jar cf lib/scala-bison-2.$*.jar -C bin edu 52 | 53 | .PHONY: %.lcoutput 54 | %.lcoutput : %.y 55 | bison -n -v $*.y 56 | scala edu.uwm.cs.cool.meta.parser.RunGenerator -v $*.y 57 | 58 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ScalaBison -- Generate LAXLC(1) parsers in Scala from bison input and output files. 2 | 3 | Release Notes 4 | Version 1.1, September 2018 5 | 6 | Please see LICENSE for software licensing. 7 | 8 | 9 | (1) Downloading 10 | 11 | You need the script scala-bison and library scala-bison-$VERSION.jar 12 | from the release area. The former (but not the latter) is also available 13 | in the source tree in the cmd/ directory. 14 | $VERSION is the Scala version (2.9, 2.10 or 2.11) 15 | 16 | You must also have bison installed. ScalaBison uses it. 17 | 18 | (2) Installation 19 | 20 | There is no automatic installation. Minimally you need: 21 | SCALABISONDIR/X/scala-bison 22 | SCALABISONDIR/lib/scala-bison-$VERSION.jar 23 | where SCALABISONDIR is a path such as "/usr/local", and "X" is "cmd" or "bin" or the like. 24 | 25 | The scala-bison script assumes "bison" is in the default path. 26 | 27 | (3) Using ScalaBison 28 | 29 | In lieu of documentation: here's the basic way to use ScalaBison 30 | 31 | (a) Create a bison-style xxx.y file. 32 | Unlike bison, ScalaBison requires every grammar production 33 | to end with a semicolon. (Semicolons are optional in bison). 34 | 35 | (b) run scala-bison xxx.y to create two files: XXXParser.scala and XXXTokens.scala 36 | 37 | (c) Move these files into the correct Scala package and then compile with the Scala compiler. 38 | (ScalaBison generated parsers do not need or use the scala-bison.jar.) 39 | 40 | Interested in a Scanner generator for Scala too? 41 | See https://github.com/moy/JFlex/tree/1.5+scala 42 | 43 | ----------------------------------------------------------------------------------------------- 44 | 45 | (4) Compiling ScalaBison from source 46 | 47 | ScalaBison is bootstrapped through itself. Thus you need 48 | scalabison.jar before you can build ScalaBison, and thus create 49 | the JAR file. 50 | 51 | You can use one of the released scala-bison-$VERSION files, but 52 | will need to put it in the root directory of the source and name it "scala-bison.jar" 53 | 54 | Then using "make" 55 | make boot 56 | make compile 57 | make 58 | This will create your own scala-bison.jar file (deleting the old one 59 | in the process). Then you should repeat the bootstrapping process: 60 | make boot 61 | make compile 62 | make 63 | Now you have a scala-bison generated and compiled by itself. 64 | 65 | 66 | -------------------------------------------------------------------------------- /cmd/scala-bison: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | SCALA_BISON_HOME=`dirname $0`/.. 3 | VERSION=`scala -version 2>&1 | sed 's/.*version \([0-9]*\.[0-9]*\).*/\1/'` 4 | JAR=$SCALA_BISON_HOME/lib/scala-bison-$VERSION.jar 5 | if [ ! -f $JAR ] 6 | then 7 | echo "JAR file $JAR not found." 8 | echo "You may need to fetch it from the distribution directory." 9 | fi 10 | # bison MUST be version 1.875 or later 11 | bison -v $1.y 12 | rm $1.tab.c 13 | scala -cp $JAR -howtorun:object edu.uwm.cs.scalabison.RunGenerator -v $1.y 14 | rm $1.output 15 | -------------------------------------------------------------------------------- /examples/Calc.lex: -------------------------------------------------------------------------------- 1 | %% 2 | 3 | %class CalcScanner 4 | %type CalcTokens.YYToken 5 | %implements Iterator[CalcTokens.YYToken] 6 | 7 | %line 8 | 9 | %{ 10 | // These features are added to the Scanner class 11 | var lookahead : CalcTokens.YYToken = null; 12 | 13 | override def hasNext() : Boolean = { 14 | if (lookahead == null) lookahead = yylex(); 15 | lookahead match { 16 | case x:CalcTokens.YYEOF => false; 17 | case x:CalcTokens.YYToken => true; 18 | } 19 | }; 20 | 21 | override def next() : CalcTokens.YYToken = { 22 | if (lookahead == null) lookahead = yylex(); 23 | var result : CalcTokens.YYToken = lookahead; 24 | lookahead = null; 25 | result 26 | }; 27 | 28 | def getLineNumber() : Int = yyline+1; 29 | 30 | %} 31 | 32 | NEWLINE = [\n] 33 | WHITESPACE = [ \t\r] 34 | SINGLE = [+\-*/\^()] 35 | NUM = [0-9]+ 36 | ERROR = [^\n \t\r+\-*/\^()0-9] 37 | 38 | %% 39 | 40 | {NEWLINE} { return CalcTokens.YYCHAR('\n'); } 41 | {WHITESPACE}+ {} 42 | {SINGLE} { return CalcTokens.YYCHAR(yytext.charAt(0)); } 43 | {NUM} { return CalcTokens.NUM(Integer.parseInt(yytext)); } 44 | 45 | {ERROR}+ { println("Ignored characters: " + yytext); } 46 | 47 | <> { return CalcTokens.YYEOF(); } 48 | -------------------------------------------------------------------------------- /examples/Calc.scala: -------------------------------------------------------------------------------- 1 | object Calc extends App { 2 | { 3 | val cs = new CalcScanner(System.in); 4 | val cp = new CalcParser(); 5 | cp.yyreset(cs); 6 | if (!cp.yyparse()) { 7 | println("Fatal error. Stopped."); 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /examples/Calc.y: -------------------------------------------------------------------------------- 1 | /* 2 | * A Parser for bison's "calc" example language. 3 | */ 4 | 5 | %token EOF 6 | %token NUM 7 | 8 | %type exp 9 | 10 | %left '-' '+' 11 | %left '*' '/' 12 | %left UMINUS 13 | %right '^' 14 | 15 | %% 16 | program : 17 | | program action '\n' 18 | ; 19 | 20 | action : { } 21 | | exp { println($1); } 22 | ; 23 | 24 | exp: NUM { $$ = $1; } 25 | | exp '+' exp { $$ = $1 + $3; } 26 | | exp '-' exp { $$ = $1 - $3; } 27 | | exp '*' exp { $$ = $1 * $3; } 28 | | exp '/' exp { $$ = $1 / $3; } 29 | | '-' exp %prec UMINUS { $$ = -$2; } 30 | | exp '^' exp { $$ = math.pow ($1, $3); } 31 | | '(' exp ')' { $$ = $2; } 32 | ; 33 | %% 34 | 35 | def yyerror(s:String) = println(s); 36 | -------------------------------------------------------------------------------- /examples/CalcParserBase.scala: -------------------------------------------------------------------------------- 1 | class CalcParserBase { } -------------------------------------------------------------------------------- /examples/CalcScanner.scala: -------------------------------------------------------------------------------- 1 | /* The following code was generated by JFlex 1.5.0-SNAPSHOT (+scala) on 7/24/14 3:35 PM */ 2 | 3 | /** 4 | * This class is a scanner generated by 5 | * JFlex 1.5.0-SNAPSHOT (+scala) 6 | * on 7/24/14 3:35 PM from the specification file 7 | * Calc.lex 8 | */ 9 | class CalcScanner(private var zzReader : java.io.Reader) extends Iterator[CalcTokens.YYToken] { 10 | def this(in : java.io.InputStream) = this(new java.io.InputStreamReader(in)); 11 | 12 | /** This character denotes the end of file */ 13 | val YYEOF : Int = -1; 14 | 15 | /** initial size of the lookahead buffer */ 16 | private val ZZ_BUFFERSIZE:Int = 16384; 17 | 18 | /** lexical states */ 19 | val YYINITIAL:Int = 0; 20 | 21 | /** 22 | * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l 23 | * ZZ_LEXSTATE[l+1] is the state in the DFA for the lexical state l 24 | * at the beginning of a line 25 | * l is of the form l = 2*k, k a non negative integer 26 | */ 27 | private val ZZ_LEXSTATE:Array[Int] = Array( 28 | 0, 0 29 | ); 30 | 31 | /** 32 | * Translates characters to character classes 33 | */ 34 | private val ZZ_CMAP_PACKED:String = 35 | "\11\0\1\2\1\1\2\0\1\2\22\0\1\2\7\0\4\3\1\0"+ 36 | "\1\3\1\0\1\3\12\4\44\0\1\3\uffa1\0"; 37 | 38 | /** 39 | * Translates characters to character classes 40 | */ 41 | private val ZZ_CMAP:Array[Char] = zzUnpackCMap(ZZ_CMAP_PACKED); 42 | 43 | /** 44 | * Translates DFA states to action switch labels. 45 | */ 46 | 47 | private val ZZ_ACTION_PACKED_0:String = 48 | "\1\0\1\1\1\2\1\3\1\4\1\5"; 49 | 50 | private def zzUnpackAction() : Array[Int] = { 51 | val result:Array[Int] = new Array[Int](6); 52 | var offset:Int = 0; 53 | offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); 54 | return result; 55 | } 56 | 57 | private def zzUnpackAction(packed:String,offset:Int,result:Array[Int]) : Int = { 58 | var i:Int = 0; /* index in packed string */ 59 | var j:Int = offset; /* index in unpacked array */ 60 | val l:Int = packed.length(); 61 | while (i < l) { 62 | var count:Int = packed.charAt(i); i+= 1; 63 | var value:Int = packed.charAt(i); i+= 1; 64 | do { result(j) = value; j+=1; count -= 1; } while (count > 0); 65 | } 66 | return j; 67 | } 68 | private val ZZ_ACTION:Array[Int] = zzUnpackAction(); 69 | 70 | 71 | /** 72 | * Translates a state to a row index in the transition table 73 | */ 74 | 75 | private val ZZ_ROWMAP_PACKED_0:String = 76 | "\0\0\0\5\0\12\0\17\0\12\0\24"; 77 | 78 | private def zzUnpackRowMap() : Array[Int] = { 79 | val result:Array[Int] = new Array[Int](6); 80 | var offset:Int = 0; 81 | offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); 82 | return result; 83 | } 84 | 85 | private def zzUnpackRowMap(packed:String,offset:Int,result:Array[Int]) : Int = { 86 | var i:Int = 0; /* index in packed string */ 87 | var j:Int = offset; /* index in unpacked array */ 88 | val l:Int = packed.length(); 89 | while (i < l) { 90 | val high:Int = packed.charAt(i) << 16; i+= 1; 91 | result(j) = high | packed.charAt(i); i+= 1; j += 1; 92 | } 93 | return j; 94 | } 95 | private val ZZ_ROWMAP:Array[Int] = zzUnpackRowMap(); 96 | 97 | /** 98 | * The transition table of the DFA 99 | */ 100 | 101 | private val ZZ_TRANS_PACKED_0:String = 102 | "\1\2\1\3\1\4\1\5\1\6\1\2\13\0\1\4"+ 103 | "\6\0\1\6"; 104 | 105 | private def zzUnpackTrans() : Array[Int] = { 106 | val result:Array[Int] = new Array[Int](25); 107 | var offset:Int = 0; 108 | offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); 109 | return result; 110 | } 111 | 112 | private def zzUnpackTrans(packed:String,offset:Int,result:Array[Int]) : Int = { 113 | var i:Int = 0; /* index in packed string */ 114 | var j:Int = offset; /* index in unpacked array */ 115 | val l:Int = packed.length(); 116 | while (i < l) { 117 | var count:Int = packed.charAt(i); i+= 1; 118 | var value:Int = packed.charAt(i); i+= 1; 119 | value-= 1; 120 | do { result(j) = value; j+=1; count -= 1; } while (count > 0); 121 | } 122 | return j; 123 | } 124 | private val ZZ_TRANS:Array[Int] = zzUnpackTrans(); 125 | 126 | 127 | /* error codes */ 128 | private val ZZ_UNKNOWN_ERROR : Int = 0; 129 | private val ZZ_NO_MATCH : Int = 1; 130 | private val ZZ_PUSHBACK_2BIG : Int = 2; 131 | 132 | /* error messages for the codes above */ 133 | private val ZZ_ERROR_MSG: Array[String] = Array( 134 | "Unkown internal scanner error", 135 | "Error: could not match input", 136 | "Error: pushback value was too large" 137 | ); 138 | 139 | /** 140 | * ZZ_ATTRIBUTE[aState] contains the attributes of state aState 141 | */ 142 | 143 | private val ZZ_ATTRIBUTE_PACKED_0:String = 144 | "\1\0\1\1\1\11\1\1\1\11\1\1"; 145 | 146 | private def zzUnpackAttribute() : Array[Int] = { 147 | val result:Array[Int] = new Array[Int](6); 148 | var offset:Int = 0; 149 | offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); 150 | return result; 151 | } 152 | 153 | private def zzUnpackAttribute(packed:String,offset:Int,result:Array[Int]) : Int = { 154 | var i:Int = 0; /* index in packed string */ 155 | var j:Int = offset; /* index in unpacked array */ 156 | val l:Int = packed.length(); 157 | while (i < l) { 158 | var count:Int = packed.charAt(i); i+= 1; 159 | var value:Int = packed.charAt(i); i+= 1; 160 | do { result(j) = value; j+=1; count -= 1; } while (count > 0); 161 | } 162 | return j; 163 | } 164 | private val ZZ_ATTRIBUTE:Array[Int] = zzUnpackAttribute(); 165 | 166 | 167 | /** the current state of the DFA */ 168 | private var zzState : Int = 0; 169 | 170 | /** the current lexical state */ 171 | private var zzLexicalState : Int = YYINITIAL; 172 | 173 | /** this buffer contains the current text to be matched and is 174 | the source of the yytext() string */ 175 | private var zzBuffer : Array[Char] = new Array(ZZ_BUFFERSIZE); 176 | 177 | /** the textposition at the last accepting state */ 178 | private var zzMarkedPos : Int = 0; 179 | 180 | /** the current text position in the buffer */ 181 | private var zzCurrentPos : Int = 0; 182 | 183 | /** startRead marks the beginning of the yytext() string in the buffer */ 184 | private var zzStartRead : Int = 0; 185 | 186 | /** endRead marks the last character in the buffer, that has been read 187 | from input */ 188 | private var zzEndRead : Int = 0; 189 | 190 | /** number of newlines encountered up to the start of the matched text */ 191 | private var yyline : Int = 0; 192 | 193 | /** the number of characters up to the start of the matched text */ 194 | private var yychar : Int = 0; 195 | 196 | /** 197 | * the number of characters from the last newline up to the start of the 198 | * matched text 199 | */ 200 | private var yycolumn : Int = 0; 201 | 202 | /** 203 | * zzAtBOL == true <=> the scanner is currently at the beginning of a line 204 | */ 205 | private var zzAtBOL : Boolean = true; 206 | 207 | /** zzAtEOF == true <=> the scanner is at the EOF */ 208 | private var zzAtEOF : Boolean = false; 209 | 210 | /* user code: */ 211 | // These features are added to the Scanner class 212 | var lookahead : CalcTokens.YYToken = null; 213 | 214 | override def hasNext() : Boolean = { 215 | if (lookahead == null) lookahead = yylex(); 216 | lookahead match { 217 | case x:CalcTokens.YYEOF => false; 218 | case x:CalcTokens.YYToken => true; 219 | } 220 | }; 221 | 222 | override def next() : CalcTokens.YYToken = { 223 | if (lookahead == null) lookahead = yylex(); 224 | var result : CalcTokens.YYToken = lookahead; 225 | lookahead = null; 226 | result 227 | }; 228 | 229 | def getLineNumber() : Int = yyline+1; 230 | 231 | 232 | 233 | /** 234 | * Creates a new scanner 235 | * There is also a java.io.InputStream version of this constructor. 236 | * 237 | * @param in the java.io.Reader to read input from. 238 | */ 239 | 240 | /** 241 | * Unpacks the compressed character translation table. 242 | * 243 | * @param packed the packed character translation table 244 | * @return the unpacked character translation table 245 | */ 246 | private def zzUnpackCMap(packed:String) : Array[Char] = { 247 | val map:Array[Char] = new Array[Char](0x10000); 248 | var i:Int = 0; /* index in packed string */ 249 | var j:Int = 0; /* index in unpacked array */ 250 | while (i < 34) { 251 | var count:Int = packed.charAt(i); i+= 1; 252 | var value:Char = packed.charAt(i); i+= 1; 253 | do { map(j) = value; j+=1; count-=1; } while (count > 0); 254 | } 255 | return map; 256 | } 257 | 258 | 259 | /** 260 | * Refills the input buffer. 261 | * 262 | * @return false, iff there was new input. 263 | */ 264 | private def zzRefill() : Boolean = { 265 | 266 | /* first: make room (if you can) */ 267 | if (zzStartRead > 0) { 268 | System.arraycopy(zzBuffer, zzStartRead, 269 | zzBuffer, 0, 270 | zzEndRead-zzStartRead); 271 | 272 | /* translate stored positions */ 273 | zzEndRead-= zzStartRead; 274 | zzCurrentPos-= zzStartRead; 275 | zzMarkedPos-= zzStartRead; 276 | zzStartRead = 0; 277 | } 278 | 279 | /* is the buffer big enough? */ 280 | if (zzCurrentPos >= zzBuffer.length) { 281 | /* if not: blow it up */ 282 | val newBuffer : Array[Char] = new Array(zzCurrentPos*2); 283 | System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); 284 | zzBuffer = newBuffer; 285 | } 286 | 287 | /* finally: fill the buffer with new input */ 288 | val numRead : Int = zzReader.read(zzBuffer, zzEndRead, 289 | zzBuffer.length-zzEndRead); 290 | 291 | if (numRead > 0) { 292 | zzEndRead+= numRead; 293 | return false; 294 | } 295 | // unlikely but not impossible: read 0 characters, but not at end of stream 296 | if (numRead == 0) { 297 | val c : Int = zzReader.read(); 298 | if (c == -1) { 299 | return true; 300 | } else { 301 | zzBuffer(zzEndRead) = c toChar; 302 | zzEndRead += 1 303 | return false; 304 | } 305 | } 306 | 307 | // numRead < 0 308 | return true; 309 | } 310 | 311 | 312 | /** 313 | * Closes the input stream. 314 | */ 315 | def yyclose() : Unit = { 316 | zzAtEOF = true; /* indicate end of file */ 317 | zzEndRead = zzStartRead; /* invalidate buffer */ 318 | 319 | if (zzReader != null) 320 | zzReader.close(); 321 | } 322 | 323 | 324 | /** 325 | * Resets the scanner to read from a new input stream. 326 | * Does not close the old reader. 327 | * 328 | * All internal variables are reset, the old input stream 329 | * cannot be reused (internal buffer is discarded and lost). 330 | * Lexical state is set to ZZ_INITIAL. 331 | * 332 | * @param reader the new input stream 333 | */ 334 | def yyreset(reader : java.io.Reader) : Unit = { 335 | zzReader = reader; 336 | zzAtBOL = true; 337 | zzAtEOF = false; 338 | zzEndRead = 0; zzStartRead = 0; 339 | zzCurrentPos = 0; zzMarkedPos = 0; 340 | yyline = 0; yychar = 0; yycolumn = 0; 341 | zzLexicalState = YYINITIAL; 342 | } 343 | 344 | 345 | /** 346 | * Returns the current lexical state. 347 | */ 348 | def yystate() : Int = { 349 | zzLexicalState; 350 | } 351 | 352 | 353 | /** 354 | * Enters a new lexical state 355 | * 356 | * @param newState the new lexical state 357 | */ 358 | def yybegin(newState : Int) : Unit = { 359 | zzLexicalState = newState; 360 | } 361 | 362 | 363 | /** 364 | * Returns the text matched by the current regular expression. 365 | */ 366 | def yytext() : String = { 367 | new String( zzBuffer, zzStartRead, zzMarkedPos-zzStartRead ); 368 | } 369 | 370 | 371 | /** 372 | * Returns the character at position pos from the 373 | * matched text. 374 | * 375 | * It is equivalent to yytext().charAt(pos), but faster 376 | * 377 | * @param pos the position of the character to fetch. 378 | * A value from 0 to yylength()-1. 379 | * 380 | * @return the character at position pos 381 | */ 382 | def yycharat(pos : Int) : Char = { 383 | zzBuffer(zzStartRead+pos); 384 | } 385 | 386 | 387 | /** 388 | * Returns the length of the matched text region. 389 | */ 390 | def yylength() : Int = { 391 | zzMarkedPos-zzStartRead; 392 | } 393 | 394 | 395 | /** 396 | * Reports an error that occured while scanning. 397 | * 398 | * In a wellformed scanner (no or only correct usage of 399 | * yypushback(int) and a match-all fallback rule) this method 400 | * will only be called with things that "Can't Possibly Happen". 401 | * If this method is called, something is seriously wrong 402 | * (e.g. a JFlex bug producing a faulty scanner etc.). 403 | * 404 | * Usual syntax/scanner level error handling should be done 405 | * in error fallback rules. 406 | * 407 | * @param errorCode the code of the errormessage to display 408 | */ 409 | private def zzScanError(errorCode:Int) : Unit = { 410 | var message : String = null; 411 | try { 412 | message = ZZ_ERROR_MSG(errorCode); 413 | } catch { 414 | case e:ArrayIndexOutOfBoundsException => 415 | message = ZZ_ERROR_MSG(ZZ_UNKNOWN_ERROR); 416 | } 417 | 418 | throw new Error(message); 419 | } 420 | 421 | 422 | /** 423 | * Pushes the specified amount of characters back into the input stream. 424 | * 425 | * They will be read again by then next call of the scanning method 426 | * 427 | * @param number the number of characters to be read again. 428 | * This number must not be greater than yylength()! 429 | */ 430 | def yypushback(number:Int) : Unit = { 431 | if ( number > yylength() ) 432 | zzScanError(ZZ_PUSHBACK_2BIG); 433 | 434 | zzMarkedPos -= number; 435 | } 436 | 437 | /** Nested class to simulate multi-level break */ 438 | private case class ZZbreak(name:String) extends Throwable; 439 | 440 | /** 441 | * Resumes scanning until the next regular expression is matched, 442 | * the end of input is encountered or an I/O-Error occurs. 443 | * 444 | * @return the next token 445 | * @exception java.io.IOException if any I/O-Error occurs 446 | */ 447 | def yylex() : CalcTokens.YYToken = 448 | { 449 | var zzInput : Int = 0; 450 | var zzAction : Int = 0; 451 | 452 | // cached fields: 453 | var zzCurrentPosL : Int = 0; 454 | var zzMarkedPosL : Int = 0; 455 | var zzEndReadL : Int = zzEndRead; 456 | var zzBufferL : Array[Char] = zzBuffer; 457 | var zzCMapL : Array[Char] = ZZ_CMAP; 458 | 459 | val zzTransL:Array[Int] = ZZ_TRANS; 460 | val zzRowMapL:Array[Int] = ZZ_ROWMAP; 461 | val zzAttrL:Array[Int] = ZZ_ATTRIBUTE; 462 | 463 | while (true) { 464 | zzMarkedPosL = zzMarkedPos; 465 | 466 | var zzR:Boolean = false; 467 | zzCurrentPosL = zzStartRead 468 | while (zzCurrentPosL < zzMarkedPosL) { 469 | (zzBufferL(zzCurrentPosL)) match { 470 | case '\u000B' 471 | |'\u000C' 472 | |'\u0085' 473 | |'\u2028' 474 | |'\u2029' => { 475 | yyline+=1; 476 | zzR = false; 477 | } 478 | case '\r' => { 479 | yyline+=1; 480 | zzR = true; 481 | } 482 | case '\n' => { 483 | if (zzR) 484 | zzR = false; 485 | else { 486 | yyline+=1; 487 | } 488 | } 489 | case _ => { 490 | zzR = false; 491 | } 492 | } 493 | zzCurrentPosL += 1; 494 | } 495 | 496 | if (zzR) { 497 | // peek one character ahead if it is \n (if we have counted one line too much) 498 | var zzPeek:Boolean = false; 499 | if (zzMarkedPosL < zzEndReadL) 500 | zzPeek = zzBufferL(zzMarkedPosL) == '\n'; 501 | else if (zzAtEOF) 502 | zzPeek = false; 503 | else { 504 | val eof:Boolean = zzRefill(); 505 | zzEndReadL = zzEndRead; 506 | zzMarkedPosL = zzMarkedPos; 507 | zzBufferL = zzBuffer; 508 | if (eof) 509 | zzPeek = false; 510 | else 511 | zzPeek = zzBufferL(zzMarkedPosL) == '\n'; 512 | } 513 | if (zzPeek) yyline-= 1; 514 | } 515 | zzAction = -1; 516 | 517 | { val p : Int = zzMarkedPosL; 518 | zzCurrentPosL = p; zzCurrentPos = p; zzStartRead = p; 519 | } 520 | 521 | zzState = ZZ_LEXSTATE(zzLexicalState); 522 | 523 | // set up zzAction for empty match case: 524 | var zzAttributes:Int = zzAttrL(zzState); 525 | if ( (zzAttributes & 1) == 1 ) { 526 | zzAction = zzState; 527 | } 528 | 529 | 530 | /* zzForAction: */ try { 531 | while (true) { 532 | 533 | if (zzCurrentPosL < zzEndReadL) { 534 | zzInput = zzBufferL(zzCurrentPosL); 535 | zzCurrentPosL += 1; 536 | } else if (zzAtEOF) { 537 | zzInput = YYEOF; 538 | throw ZZbreak("zzForAction"); 539 | } 540 | else { 541 | // store back cached positions 542 | zzCurrentPos = zzCurrentPosL; 543 | zzMarkedPos = zzMarkedPosL; 544 | val eof:Boolean = zzRefill(); 545 | // get translated positions and possibly new buffer 546 | zzCurrentPosL = zzCurrentPos; 547 | zzMarkedPosL = zzMarkedPos; 548 | zzBufferL = zzBuffer; 549 | zzEndReadL = zzEndRead; 550 | if (eof) { 551 | zzInput = YYEOF; 552 | throw ZZbreak("zzForAction"); 553 | } 554 | else { 555 | zzInput = zzBufferL(zzCurrentPosL); 556 | zzCurrentPosL += 1; 557 | } 558 | } 559 | val zzNext:Int = zzTransL(zzRowMapL(zzState) + zzCMapL(zzInput)); 560 | if (zzNext == -1) throw ZZbreak("zzForAction"); 561 | zzState = zzNext; 562 | 563 | zzAttributes = zzAttrL(zzState); 564 | if ( (zzAttributes & 1) == 1 ) { 565 | zzAction = zzState; 566 | zzMarkedPosL = zzCurrentPosL; 567 | if ( (zzAttributes & 8) == 8 ) throw ZZbreak("zzForAction"); 568 | } 569 | 570 | } 571 | } catch { case ZZbreak("zzForAction") => () } 572 | 573 | // store back cached position 574 | zzMarkedPos = zzMarkedPosL; 575 | 576 | (if ( zzAction < 0 ) zzAction; else ZZ_ACTION(zzAction)) match { 577 | case 3 => { 578 | { 579 | } 580 | } 581 | case 6 => {} 582 | case 1 => { 583 | { println("Ignored characters: " + yytext); 584 | } 585 | } 586 | case 7 => {} 587 | case 5 => { 588 | { return CalcTokens.NUM(Integer.parseInt(yytext)); 589 | } 590 | } 591 | case 8 => {} 592 | case 2 => { 593 | { return CalcTokens.YYCHAR('\n'); 594 | } 595 | } 596 | case 9 => {} 597 | case 4 => { 598 | { return CalcTokens.YYCHAR(yytext.charAt(0)); 599 | } 600 | } 601 | case 10 => {} 602 | case _ => 603 | if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { 604 | zzAtEOF = true; 605 | { 606 | return CalcTokens.YYEOF(); 607 | } 608 | } else { 609 | zzScanError(ZZ_NO_MATCH); 610 | } 611 | } 612 | } 613 | throw new RuntimeException("NOT REACHED"); 614 | } 615 | 616 | 617 | } 618 | -------------------------------------------------------------------------------- /examples/Java.y: -------------------------------------------------------------------------------- 1 | %token Identifier 2 | 3 | %token IntegerLiteral 4 | %token FloatingPointLiteral 5 | %token BooleanLiteral 6 | %token CharacterLiteral 7 | %token StringLiteral 8 | %token NullLiteral 9 | 10 | %token BOOLEAN 11 | %token BYTE SHORT INT LONG CHAR 12 | %token FLOAT DOUBLE 13 | 14 | %token PACKAGE IMPORT CLASS INTERFACE 15 | %token PUBLIC PROTECTED PRIVATE 16 | %token STATIC 17 | %token ABSTRACT FINAL NATIVE SYNCHRONIZED TRANSIENT VOLATILE 18 | %token EXTENDS IMPLEMENTS 19 | %token VOID 20 | %token THROWS 21 | 22 | %token THIS SUPER 23 | %token IF ELSE SWITCH CASE DEFAULT WHILE DO FOR 24 | %token BREAK CONTINUE RETURN THROW 25 | %token TRY CATCH FINALLY 26 | %token NEW NULL INSTANCEOF 27 | 28 | %token PLUSPLUS MINUSMINUS 29 | %token LTLT GTGT GTGTGT LTEQ GTEQ EQEQ BANGEQ 30 | %token ANDAND OROR 31 | %token TIMESEQ DIVEQ MODEQ PLUSEQ MINUSEQ 32 | %token LTLTEQ GTGTEQ GTGTGTEQ ANDEQ XOREQ OREQ 33 | %% 34 | 35 | /* 19.2 Productions from S2.3: The Syntactic Grammar */ 36 | 37 | Goal: 38 | 39 | CompilationUnit 40 | ; 41 | 42 | /* 19.3 Productions from S3: Lexical Structure */ 43 | 44 | Literal: 45 | 46 | IntegerLiteral| 47 | 48 | FloatingPointLiteral| 49 | 50 | BooleanLiteral| 51 | 52 | CharacterLiteral| 53 | 54 | StringLiteral| 55 | 56 | NullLiteral; 57 | 58 | /* 19.4 Productions from S4: Types, Values, and Variables */ 59 | 60 | Type: 61 | 62 | PrimitiveType| 63 | 64 | ReferenceType; 65 | 66 | PrimitiveType: 67 | 68 | NumericType| 69 | 70 | BOOLEAN; 71 | 72 | NumericType: 73 | 74 | IntegralType| 75 | 76 | FloatingPointType ; 77 | 78 | IntegralType: 79 | 80 | BYTE | SHORT | INT | LONG | CHAR; 81 | 82 | FloatingPointType: 83 | 84 | FLOAT | DOUBLE; 85 | 86 | ReferenceType: 87 | 88 | ClassOrInterfaceType| 89 | 90 | ArrayType; 91 | 92 | ClassOrInterfaceType: 93 | 94 | Name; 95 | 96 | ClassType: 97 | 98 | ClassOrInterfaceType; 99 | 100 | InterfaceType: 101 | 102 | ClassOrInterfaceType; 103 | 104 | ArrayType: 105 | 106 | PrimitiveType '[' ']'| 107 | 108 | Name '[' ']'| 109 | 110 | ArrayType '[' ']'; 111 | 112 | /* 19.5 Productions from S6: Names */ 113 | 114 | Name: 115 | 116 | SimpleName| 117 | 118 | QualifiedName; 119 | 120 | SimpleName: 121 | 122 | Identifier; 123 | 124 | QualifiedName: 125 | 126 | Name '.' Identifier; 127 | 128 | /* 19.6 Productions from S7: Packages */ 129 | 130 | CompilationUnit: 131 | 132 | PackageDeclaration ImportDeclarations TypeDeclarations| 133 | PackageDeclaration ImportDeclarations| 134 | PackageDeclaration TypeDeclarations| 135 | PackageDeclaration| 136 | ImportDeclarations TypeDeclarations| 137 | ImportDeclarations| 138 | TypeDeclarations| 139 | ; 140 | 141 | ImportDeclarations: 142 | 143 | ImportDeclaration| 144 | 145 | ImportDeclarations ImportDeclaration; 146 | 147 | TypeDeclarations: 148 | 149 | TypeDeclaration| 150 | 151 | TypeDeclarations TypeDeclaration; 152 | 153 | PackageDeclaration: 154 | 155 | PACKAGE Name ';' ; 156 | 157 | ImportDeclaration: 158 | 159 | SingleTypeImportDeclaration| 160 | 161 | TypeImportOnDemandDeclaration; 162 | 163 | SingleTypeImportDeclaration: 164 | 165 | IMPORT Name ';'; 166 | 167 | TypeImportOnDemandDeclaration: 168 | 169 | IMPORT Name '.' '*' ';'; 170 | 171 | TypeDeclaration: 172 | 173 | ClassDeclaration| 174 | 175 | InterfaceDeclaration| 176 | 177 | ';'; 178 | 179 | /* 19.7 Productions Used Only in the LALR(1) Grammar */ 180 | 181 | Modifiers: 182 | 183 | Modifier| 184 | 185 | Modifiers Modifier; 186 | 187 | Modifier: 188 | 189 | PUBLIC| PROTECTED| PRIVATE| 190 | 191 | STATIC| 192 | 193 | ABSTRACT| FINAL| NATIVE| SYNCHRONIZED| TRANSIENT| VOLATILE; 194 | 195 | /* 19.8 Productions from S8: Classes */ 196 | 197 | /* 19.8.1 Productions from S8.1: Class Declaration */ 198 | 199 | ClassDeclaration: 200 | 201 | Modifiers CLASS Identifier Super Interfaces ClassBody| 202 | Modifiers CLASS Identifier Super ClassBody| 203 | Modifiers CLASS Identifier Interfaces ClassBody| 204 | Modifiers CLASS Identifier ClassBody| 205 | CLASS Identifier Super Interfaces ClassBody| 206 | CLASS Identifier Super ClassBody| 207 | CLASS Identifier Interfaces ClassBody| 208 | CLASS Identifier ClassBody; 209 | 210 | Super: 211 | 212 | EXTENDS ClassType; 213 | 214 | Interfaces: 215 | 216 | IMPLEMENTS InterfaceTypeList; 217 | 218 | InterfaceTypeList: 219 | 220 | InterfaceType| 221 | 222 | InterfaceTypeList ',' InterfaceType; 223 | 224 | ClassBody: 225 | 226 | '{' ClassBodyDeclarations '}'| 227 | 228 | '{' '}'; 229 | 230 | ClassBodyDeclarations: 231 | 232 | ClassBodyDeclaration| 233 | 234 | ClassBodyDeclarations ClassBodyDeclaration; 235 | 236 | ClassBodyDeclaration: 237 | 238 | ClassMemberDeclaration| 239 | 240 | StaticInitializer| 241 | 242 | ConstructorDeclaration; 243 | 244 | ClassMemberDeclaration: 245 | 246 | FieldDeclaration| 247 | 248 | MethodDeclaration; 249 | 250 | /* 19.8.2 Productions from S8.3: Field Declarations */ 251 | 252 | FieldDeclaration: 253 | 254 | Modifiers Type VariableDeclarators ';'| 255 | 256 | Type VariableDeclarators ';'; 257 | 258 | VariableDeclarators: 259 | 260 | VariableDeclarator| 261 | 262 | VariableDeclarators ',' VariableDeclarator; 263 | 264 | VariableDeclarator: 265 | 266 | VariableDeclaratorId| 267 | 268 | VariableDeclaratorId '=' VariableInitializer; 269 | 270 | VariableDeclaratorId: 271 | 272 | Identifier| 273 | 274 | VariableDeclaratorId '[' ']'; 275 | 276 | VariableInitializer: 277 | 278 | Expression| 279 | 280 | ArrayInitializer; 281 | 282 | /* 19.8.3 Productions from S8.4: Method Declarations */ 283 | 284 | MethodDeclaration: 285 | 286 | MethodHeader MethodBody; 287 | 288 | MethodHeader: 289 | 290 | Modifiers Type MethodDeclarator Throws | 291 | Modifiers Type MethodDeclarator | 292 | Type MethodDeclarator Throws | 293 | Type MethodDeclarator | 294 | 295 | Modifiers VOID MethodDeclarator Throws| 296 | Modifiers VOID MethodDeclarator | 297 | VOID MethodDeclarator Throws| 298 | VOID MethodDeclarator ; 299 | 300 | MethodDeclarator: 301 | 302 | Identifier '(' FormalParameterList ')' | 303 | Identifier '(' ')' | 304 | 305 | MethodDeclarator '[' ']'; 306 | 307 | FormalParameterList: 308 | 309 | FormalParameter| 310 | 311 | FormalParameterList ',' FormalParameter; 312 | 313 | FormalParameter: 314 | 315 | Type VariableDeclaratorId; 316 | 317 | Throws: 318 | 319 | THROWS ClassTypeList; 320 | 321 | ClassTypeList: 322 | 323 | ClassType| 324 | 325 | ClassTypeList ',' ClassType; 326 | 327 | MethodBody: 328 | 329 | Block| 330 | 331 | ';'; 332 | 333 | /* 19.8.4 Productions from S8.5: Static Initializers */ 334 | 335 | StaticInitializer: 336 | 337 | STATIC Block; 338 | 339 | /* 19.8.5 Productions from S8.6: Constructor Declarations */ 340 | 341 | ConstructorDeclaration: 342 | 343 | Modifiers ConstructorDeclarator Throws ConstructorBody| 344 | Modifiers ConstructorDeclarator ConstructorBody| 345 | ConstructorDeclarator Throws ConstructorBody| 346 | ConstructorDeclarator ConstructorBody; 347 | 348 | ConstructorDeclarator: 349 | 350 | SimpleName '(' FormalParameterList ')'| 351 | SimpleName '(' ')'; 352 | 353 | ConstructorBody: 354 | 355 | '{' ExplicitConstructorInvocation BlockStatements '}'| 356 | '{' ExplicitConstructorInvocation '}'| 357 | '{' BlockStatements '}'| 358 | '{' '}'; 359 | 360 | ExplicitConstructorInvocation: 361 | 362 | THIS '(' ArgumentList ')' ';'| 363 | THIS '(' ')' ';'| 364 | 365 | SUPER '(' ArgumentList ')' ';'| 366 | SUPER '(' ')' ';'; 367 | 368 | 369 | /* 19.9 Productions from S9: Interfaces */ 370 | 371 | /* 19.9.1 Productions from S9.1: Interface Declarations */ 372 | 373 | InterfaceDeclaration: 374 | 375 | Modifiers INTERFACE Identifier ExtendsInterfaces InterfaceBody| 376 | Modifiers INTERFACE Identifier InterfaceBody| 377 | INTERFACE Identifier ExtendsInterfaces InterfaceBody| 378 | INTERFACE Identifier InterfaceBody; 379 | 380 | ExtendsInterfaces: 381 | 382 | EXTENDS InterfaceType| 383 | 384 | ExtendsInterfaces ',' InterfaceType; 385 | 386 | InterfaceBody: 387 | 388 | '{' InterfaceMemberDeclarations '}'| 389 | '{' '}'; 390 | 391 | InterfaceMemberDeclarations: 392 | 393 | InterfaceMemberDeclaration| 394 | 395 | InterfaceMemberDeclarations InterfaceMemberDeclaration; 396 | 397 | InterfaceMemberDeclaration: 398 | 399 | ConstantDeclaration| 400 | 401 | AbstractMethodDeclaration; 402 | 403 | ConstantDeclaration: 404 | 405 | FieldDeclaration; 406 | 407 | AbstractMethodDeclaration: 408 | 409 | MethodHeader ';'; 410 | 411 | /* 19.10 Productions from S10: Arrays */ 412 | 413 | ArrayInitializer: 414 | 415 | '{' VariableInitializers ',' '}'| 416 | 417 | '{' VariableInitializers '}'| 418 | 419 | '{' ',' '}'| 420 | 421 | '{' '}'; 422 | 423 | VariableInitializers: 424 | 425 | VariableInitializer| 426 | 427 | VariableInitializers ',' VariableInitializer; 428 | 429 | /* 19.11 Productions from S14: Blocks and Statements */ 430 | 431 | Block: 432 | 433 | '{' BlockStatements '}'| 434 | '{' '}'; 435 | 436 | BlockStatements: 437 | 438 | BlockStatement| 439 | 440 | BlockStatements BlockStatement; 441 | 442 | BlockStatement: 443 | 444 | LocalVariableDeclarationStatement| 445 | 446 | Statement; 447 | 448 | LocalVariableDeclarationStatement: 449 | 450 | LocalVariableDeclaration ';'; 451 | 452 | LocalVariableDeclaration: 453 | 454 | Type VariableDeclarators; 455 | 456 | Statement: 457 | 458 | StatementWithoutTrailingSubstatement| 459 | 460 | LabeledStatement| 461 | 462 | IfThenStatement| 463 | 464 | IfThenElseStatement| 465 | 466 | WhileStatement| 467 | 468 | ForStatement; 469 | 470 | StatementNoShortIf: 471 | 472 | StatementWithoutTrailingSubstatement| 473 | 474 | LabeledStatementNoShortIf| 475 | 476 | IfThenElseStatementNoShortIf| 477 | 478 | WhileStatementNoShortIf| 479 | 480 | ForStatementNoShortIf; 481 | 482 | StatementWithoutTrailingSubstatement: 483 | 484 | Block| 485 | 486 | EmptyStatement| 487 | 488 | ExpressionStatement| 489 | 490 | SwitchStatement| 491 | 492 | DoStatement| 493 | 494 | BreakStatement| 495 | 496 | ContinueStatement| 497 | 498 | ReturnStatement| 499 | 500 | SynchronizedStatement| 501 | 502 | ThrowStatement| 503 | 504 | TryStatement; 505 | 506 | EmptyStatement: 507 | 508 | ';'; 509 | 510 | LabeledStatement: 511 | 512 | Identifier ':' Statement; 513 | 514 | LabeledStatementNoShortIf: 515 | 516 | Identifier ':' StatementNoShortIf; 517 | 518 | ExpressionStatement: 519 | 520 | StatementExpression ';'; 521 | 522 | StatementExpression: 523 | 524 | Assignment| 525 | 526 | PreIncrementExpression| 527 | 528 | PreDecrementExpression| 529 | 530 | PostIncrementExpression| 531 | 532 | PostDecrementExpression| 533 | 534 | MethodInvocation| 535 | 536 | ClassInstanceCreationExpression; 537 | 538 | IfThenStatement: 539 | 540 | IF '(' Expression ')' Statement; 541 | 542 | IfThenElseStatement: 543 | 544 | IF '(' Expression ')' StatementNoShortIf ELSE Statement; 545 | 546 | IfThenElseStatementNoShortIf: 547 | 548 | IF '(' Expression ')' StatementNoShortIf ELSE StatementNoShortIf; 549 | 550 | SwitchStatement: 551 | 552 | SWITCH '(' Expression ')' SwitchBlock; 553 | 554 | SwitchBlock: 555 | 556 | '{' SwitchBlockStatementGroups SwitchLabels '}'| 557 | '{' SwitchLabels '}'| 558 | '{' SwitchBlockStatementGroups '}'| 559 | '{' '}'; 560 | 561 | 562 | SwitchBlockStatementGroups: 563 | 564 | SwitchBlockStatementGroup| 565 | 566 | SwitchBlockStatementGroups SwitchBlockStatementGroup; 567 | 568 | SwitchBlockStatementGroup: 569 | 570 | SwitchLabels BlockStatements; 571 | 572 | SwitchLabels: 573 | 574 | SwitchLabel| 575 | 576 | SwitchLabels SwitchLabel; 577 | 578 | SwitchLabel: 579 | 580 | CASE ConstantExpression ':'| 581 | 582 | DEFAULT ':'; 583 | 584 | WhileStatement: 585 | 586 | WHILE '(' Expression ')' Statement; 587 | 588 | WhileStatementNoShortIf: 589 | 590 | WHILE '(' Expression ')' StatementNoShortIf; 591 | 592 | DoStatement: 593 | 594 | DO Statement WHILE '(' Expression ')' ';'; 595 | 596 | ForStatement: 597 | 598 | FOR '(' ForInit ';' Expression ';' ForUpdate ')' 599 | Statement| 600 | FOR '(' ForInit ';' Expression ';' ')' 601 | Statement| 602 | FOR '(' ForInit ';' ';' ForUpdate ')' 603 | Statement| 604 | FOR '(' ForInit ';' ';' ')' 605 | Statement| 606 | FOR '(' ';' Expression ';' ForUpdate ')' 607 | Statement| 608 | FOR '(' ';' Expression ';' ')' 609 | Statement| 610 | FOR '(' ';' ';' ForUpdate ')' 611 | Statement| 612 | FOR '(' ';' ';' ')' 613 | Statement; 614 | 615 | ForStatementNoShortIf: 616 | 617 | FOR '(' ForInit ';' Expression ';' ForUpdate ')' 618 | StatementNoShortIf| 619 | FOR '(' ForInit ';' Expression ';' ')' 620 | StatementNoShortIf| 621 | FOR '(' ForInit ';' ';' ForUpdate ')' 622 | StatementNoShortIf| 623 | FOR '(' ForInit ';' ';' ')' 624 | StatementNoShortIf| 625 | FOR '(' ';' Expression ';' ForUpdate ')' 626 | StatementNoShortIf| 627 | FOR '(' ';' Expression ';' ')' 628 | StatementNoShortIf| 629 | FOR '(' ';' ';' ForUpdate ')' 630 | StatementNoShortIf| 631 | FOR '(' ';' ';' ')' 632 | StatementNoShortIf; 633 | 634 | ForInit: 635 | 636 | StatementExpressionList| 637 | 638 | LocalVariableDeclaration; 639 | 640 | ForUpdate: 641 | 642 | StatementExpressionList; 643 | 644 | StatementExpressionList: 645 | 646 | StatementExpression| 647 | 648 | StatementExpressionList ',' StatementExpression; 649 | 650 | BreakStatement: 651 | 652 | BREAK Identifier ';'| 653 | BREAK ';'; 654 | 655 | ContinueStatement: 656 | 657 | CONTINUE Identifier ';'| 658 | CONTINUE ';'; 659 | 660 | ReturnStatement: 661 | 662 | RETURN Expression ';'| 663 | RETURN ';'; 664 | 665 | ThrowStatement: 666 | 667 | THROW Expression ';'; 668 | 669 | SynchronizedStatement: 670 | 671 | SYNCHRONIZED '(' Expression ')' Block; 672 | 673 | TryStatement: 674 | 675 | TRY Block Catches| 676 | 677 | TRY Block Catches Finally| 678 | TRY Block Finally; 679 | 680 | Catches: 681 | 682 | CatchClause| 683 | 684 | Catches CatchClause; 685 | 686 | CatchClause: 687 | 688 | CATCH '(' FormalParameter ')' Block; 689 | 690 | Finally: 691 | 692 | FINALLY Block; 693 | 694 | /* 19.12 Productions from S15: Expressions */ 695 | 696 | Primary: 697 | 698 | PrimaryNoNewArray| 699 | 700 | ArrayCreationExpression; 701 | 702 | PrimaryNoNewArray: 703 | 704 | Literal| 705 | 706 | THIS| 707 | 708 | '(' Expression ')'| 709 | 710 | ClassInstanceCreationExpression| 711 | 712 | FieldAccess| 713 | 714 | MethodInvocation| 715 | 716 | ArrayAccess; 717 | 718 | ClassInstanceCreationExpression: 719 | 720 | NEW ClassType '(' ArgumentList ')'| 721 | NEW ClassType '(' ')'; 722 | 723 | ArgumentList: 724 | 725 | Expression| 726 | 727 | ArgumentList ',' Expression; 728 | 729 | ArrayCreationExpression: 730 | 731 | NEW PrimitiveType DimExprs Dims| 732 | NEW PrimitiveType DimExprs | 733 | 734 | NEW ClassOrInterfaceType DimExprs Dims| 735 | NEW ClassOrInterfaceType DimExprs ; 736 | 737 | DimExprs: 738 | 739 | DimExpr| 740 | 741 | DimExprs DimExpr; 742 | 743 | DimExpr: 744 | 745 | '[' Expression ']'; 746 | 747 | Dims: 748 | 749 | '[' ']'| 750 | 751 | Dims '[' ']'; 752 | 753 | FieldAccess: 754 | 755 | Primary '.' Identifier| 756 | 757 | SUPER '.' Identifier; 758 | 759 | MethodInvocation: 760 | 761 | Name '(' ArgumentList ')'| 762 | Name '(' ')'| 763 | 764 | Primary '.' Identifier '(' ArgumentList ')'| 765 | Primary '.' Identifier '(' ')'| 766 | 767 | SUPER '.' Identifier '(' ArgumentList ')'| 768 | SUPER '.' Identifier '(' ')'; 769 | 770 | ArrayAccess: 771 | 772 | Name '[' Expression ']'| 773 | 774 | PrimaryNoNewArray '[' Expression ']'; 775 | 776 | PostfixExpression: 777 | 778 | Primary| 779 | 780 | Name| 781 | 782 | PostIncrementExpression| 783 | 784 | PostDecrementExpression; 785 | 786 | PostIncrementExpression: 787 | 788 | PostfixExpression PLUSPLUS; 789 | 790 | PostDecrementExpression: 791 | 792 | PostfixExpression MINUSMINUS; 793 | 794 | UnaryExpression: 795 | 796 | PreIncrementExpression| 797 | 798 | PreDecrementExpression| 799 | 800 | '+' UnaryExpression| 801 | 802 | '-' UnaryExpression| 803 | 804 | UnaryExpressionNotPlusMinus; 805 | 806 | PreIncrementExpression: 807 | 808 | PLUSPLUS UnaryExpression; 809 | 810 | PreDecrementExpression: 811 | 812 | MINUSMINUS UnaryExpression; 813 | 814 | UnaryExpressionNotPlusMinus: 815 | 816 | PostfixExpression| 817 | 818 | '~' UnaryExpression| 819 | 820 | '!' UnaryExpression| 821 | 822 | CastExpression; 823 | 824 | CastExpression: 825 | 826 | '(' PrimitiveType Dims ')' UnaryExpression| 827 | '(' PrimitiveType ')' UnaryExpression| 828 | 829 | '(' Expression ')' UnaryExpressionNotPlusMinus| 830 | 831 | '(' Name Dims ')' UnaryExpressionNotPlusMinus; 832 | 833 | MultiplicativeExpression: 834 | 835 | UnaryExpression| 836 | 837 | MultiplicativeExpression '*' UnaryExpression| 838 | 839 | MultiplicativeExpression '/' UnaryExpression| 840 | 841 | MultiplicativeExpression '%' UnaryExpression; 842 | 843 | AdditiveExpression: 844 | 845 | MultiplicativeExpression| 846 | 847 | AdditiveExpression '+' MultiplicativeExpression| 848 | 849 | AdditiveExpression '-' MultiplicativeExpression; 850 | 851 | ShiftExpression: 852 | 853 | AdditiveExpression| 854 | 855 | ShiftExpression LTLT AdditiveExpression| 856 | 857 | ShiftExpression GTGT AdditiveExpression| 858 | 859 | ShiftExpression GTGTGT AdditiveExpression; 860 | 861 | RelationalExpression: 862 | 863 | ShiftExpression| 864 | 865 | RelationalExpression '<' ShiftExpression| 866 | 867 | RelationalExpression '>' ShiftExpression| 868 | 869 | RelationalExpression LTEQ ShiftExpression| 870 | 871 | RelationalExpression GTEQ ShiftExpression| 872 | 873 | RelationalExpression INSTANCEOF ReferenceType; 874 | 875 | EqualityExpression: 876 | 877 | RelationalExpression| 878 | 879 | EqualityExpression EQEQ RelationalExpression| 880 | 881 | EqualityExpression BANGEQ RelationalExpression; 882 | 883 | AndExpression: 884 | 885 | EqualityExpression| 886 | 887 | AndExpression '&' EqualityExpression; 888 | 889 | ExclusiveOrExpression: 890 | 891 | AndExpression| 892 | 893 | ExclusiveOrExpression '^' AndExpression; 894 | 895 | InclusiveOrExpression: 896 | 897 | ExclusiveOrExpression| 898 | 899 | InclusiveOrExpression '|' ExclusiveOrExpression; 900 | 901 | ConditionalAndExpression: 902 | 903 | InclusiveOrExpression| 904 | 905 | ConditionalAndExpression ANDAND InclusiveOrExpression; 906 | 907 | ConditionalOrExpression: 908 | 909 | ConditionalAndExpression| 910 | 911 | ConditionalOrExpression OROR ConditionalAndExpression; 912 | 913 | ConditionalExpression: 914 | 915 | ConditionalOrExpression| 916 | 917 | ConditionalOrExpression '?' Expression ':' ConditionalExpression; 918 | 919 | AssignmentExpression: 920 | 921 | ConditionalExpression| 922 | 923 | Assignment; 924 | 925 | Assignment: 926 | 927 | LeftHandSide AssignmentOperator AssignmentExpression; 928 | 929 | LeftHandSide: 930 | 931 | Name| 932 | 933 | FieldAccess| 934 | 935 | ArrayAccess; 936 | 937 | AssignmentOperator: 938 | 939 | '='| TIMESEQ | DIVEQ | MODEQ | PLUSEQ | MINUSEQ | 940 | LTLTEQ | GTGTEQ | GTGTGTEQ | ANDEQ | XOREQ | OREQ ; 941 | 942 | Expression: 943 | 944 | AssignmentExpression; 945 | 946 | ConstantExpression: 947 | 948 | Expression; 949 | 950 | %% 951 | 952 | def yyerror(s:String) = println(s); 953 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: Calc.class JavaParser.scala 4 | 5 | Calc.class : Calc.scala CalcScanner.scala CalcParser.scala CalcTokens.scala CalcParserbase.scala 6 | scalac Calc.scala CalcScanner.scala CalcParser.scala CalcTokens.scala CalcParserBase.scala 7 | 8 | CalcScanner.scala : Calc.lex 9 | jflex --scala Calc.lex 10 | 11 | CalcParser.scala CalcTokens.scala : Calc.y 12 | ../cmd/scala-bison Calc 13 | 14 | JavaParser.scala : Java.y 15 | ../cmd/scala-bison Java 16 | 17 | clean : 18 | rm -f *.class *Parser.scala *Tokens.scala *~ *.output *.lcoutput *.tab.c 19 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | Example Uses of ScalaBison 2 | 3 | This directory has two examples using ScalaBison. 4 | The main source includes also a parser for Bison grammars. 5 | 6 | The "Calc" grammar takes an example from the bison manual together with 7 | a scanner implemented using the "--scala" option of JFlex. 8 | The "Calc" object can be used to execute the parser and scanner on standard input. 9 | 10 | The Java grammar is directly from the Java Language Specification. 11 | It is not set up for execution although it will compile. 12 | (There is no scanner). 13 | 14 | The Makefile in this directory allows one to build the various parsers. 15 | It also (because it uses the script ../cmd/scala-bison) creates .lcoutput files 16 | that show the "left corner" parsing tables used. -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/.gitignore: -------------------------------------------------------------------------------- 1 | BisonParser.scala 2 | BisonTokens.scala 3 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/AcceptAction.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class AcceptAction() extends Action { 20 | override def toString() = "accept"; 21 | } 22 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/AcceptNTAction.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class AcceptNTAction(nt : Nonterminal) extends Action { 20 | override def toString() = "accept " + nt.name; 21 | } 22 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Action.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | trait Action {} 20 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/AnnounceAction.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class AnnounceAction(val rule : Rule) extends Action { 20 | override def toString = "announce rule " + rule 21 | } 22 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/ArtificialNonterminal.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class ArtificialNonterminal(n : String, ty : String) extends Nonterminal(n,ty) { 20 | } 21 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Bison.y: -------------------------------------------------------------------------------- 1 | %{ 2 | package edu.uwm.cs.scalabison; 3 | 4 | import scala.io.Source; 5 | 6 | %} 7 | 8 | %token CHARLIT 9 | %token ID 10 | %token BEGIN PROLOGUE_BEGIN PROLOGUE_END 11 | %token LEFT RIGHT NONASSOC TOKEN TYPE START PREC 12 | %token VAR 13 | %token TYPELIT CODE END 14 | %token STRINGLIT 15 | %token UNION 16 | %type symbol anon 17 | %type rhs 18 | %type block piece 19 | %type <(Symbol,Code)> action 20 | %type rev_pieces 21 | %type precedence 22 | %type grammar 23 | 24 | %% 25 | 26 | grammar : declarations BEGIN rules END 27 | { result.addExtra($4); $$ = result; } 28 | ; 29 | 30 | declarations : /* EMPTY */ 31 | | declarations { symbol_type = ""; prec = null; } declaration 32 | ; 33 | 34 | declaration 35 | : PROLOGUE_BEGIN rev_pieces PROLOGUE_END 36 | { result.addPrologue($2 .reverse); } 37 | | TOKEN token_names 38 | | precedence { prec = $1; } token_names 39 | | TYPE TYPELIT { symbol_type = $2; } nonterminal_names 40 | | START ID { result.setStart(result.getNT($2)); } 41 | ; 42 | 43 | token_names 44 | : token_names token_name 45 | | token_name 46 | ; 47 | 48 | token_name 49 | : ID { val t : Terminal = result.add(new Terminal($1,symbol_type)); 50 | if (prec != null) t.setPrecedence(prec); } 51 | | CHARLIT 52 | { val t : Terminal = result.add(new CharLitTerminal($1)); 53 | if (prec != null) t.setPrecedence(prec); } 54 | | TYPELIT 55 | { symbol_type = $1; } 56 | ; 57 | 58 | nonterminal_names 59 | : nonterminal_names nonterminal_name 60 | | nonterminal_name 61 | ; 62 | 63 | nonterminal_name 64 | : ID { result.add(new Nonterminal($1,symbol_type)); } 65 | ; 66 | 67 | precedence 68 | : LEFT { precLevel += 1; $$ = new LeftPrecedence(precLevel); } 69 | | RIGHT { precLevel += 1; $$ = new RightPrecedence(precLevel); } 70 | | NONASSOC { precLevel += 1; $$ = new NonAssocPrecedence(precLevel); } 71 | ; 72 | 73 | rules : /* EMPTY */ 74 | | rules rule 75 | ; 76 | 77 | rule : ID ':' rhs action 78 | { currNT = result.getNT($1); 79 | result.addRule(new Rule(genRuleNum(),currNT,$3,getPrec($3,$4._1),$4._2)); } 80 | morerules ';' 81 | ; 82 | 83 | morerules 84 | : /* EMPTY */ 85 | | morerules '|' rhs action 86 | { result.addRule(new Rule(genRuleNum(),currNT,$3,getPrec($3,$4._1),$4._2)); } 87 | ; 88 | 89 | rhs : /* EMPTY */ { $$ = Nil; } 90 | | rhs symbol { $$ = $1 ++ ($2 :: Nil); } 91 | | rhs anon symbol { $$ = $1 ++ ($2 :: $3 :: Nil); } 92 | ; 93 | 94 | anon : block 95 | { uniq += 1; 96 | val nt : Nonterminal = result.add(new ArtificialNonterminal("@" + uniq,"unit")); 97 | result.addRule(new Rule(genRuleNum(),nt,Nil,null,$1)); 98 | $$ = nt; } 99 | ; 100 | 101 | symbol : ID { $$ = result.get($1); } 102 | | CHARLIT 103 | { $$ = result.getCLT($1); } 104 | ; 105 | 106 | action : block { $$ = (null,$1); } 107 | | PREC symbol block 108 | { $$ = ($2,$3); } 109 | | PREC symbol 110 | { $$ = ($2,NoCode()); } 111 | | /* EMPTY */ 112 | { $$ = (null,NoCode()); } 113 | ; 114 | 115 | block : '{' rev_pieces '}' 116 | { $$ = BlockCode($2.reverse); } 117 | ; 118 | 119 | rev_pieces 120 | : /* EMPTY */ 121 | { $$ = Nil; } 122 | | rev_pieces piece 123 | { $$ = $2 :: $1; } 124 | ; 125 | 126 | piece : block { $$ = $1; } 127 | | CODE { $$ = LiteralCode($1); } 128 | | VAR { $$ = VariableCode($1); } 129 | ; 130 | %% 131 | 132 | def yyerror(s : String) = { 133 | errorsOccured = true; 134 | println(filename + ":" + scanner.getLineNumber + ":" + s); 135 | } 136 | 137 | var scanner : BisonScanner = null; 138 | var filename : String = ""; 139 | var errorsOccured : Boolean = false; 140 | var result : BisonGrammar = new BisonGrammar(); 141 | var precLevel : Int = 0; 142 | var prec : Precedence = null; 143 | var symbol_type : String = ""; 144 | var uniq : Int = 0; 145 | var currNT : Nonterminal = null; 146 | var rulenum : Int = 0; 147 | 148 | def genRuleNum() : Int = { 149 | rulenum += 1; 150 | rulenum 151 | } 152 | 153 | def getPrec(rhs:List[Symbol], sym : Symbol) : Precedence = { 154 | if (sym != null) return sym.precedence; 155 | for (sym <- rhs.reverse) { 156 | if (sym.precedence != null) return sym.precedence; 157 | } 158 | null 159 | } 160 | 161 | def reset(fn : String, sc : BisonScanner) = { 162 | filename = fn; 163 | scanner = sc; 164 | yyreset(sc); 165 | result = new BisonGrammar(); 166 | precLevel = 0; 167 | prec = null; 168 | uniq = 0; 169 | currNT = null; 170 | rulenum = 0; 171 | errorsOccured = false; 172 | } 173 | 174 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/BisonGrammar.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.mutable.Map; 20 | import scala.collection.mutable.ArrayBuffer; 21 | import scala.collection.mutable.HashMap; 22 | 23 | import edu.uwm.cs.util.CharUtil; 24 | 25 | class BisonGrammar() extends Grammar { 26 | val special : Nonterminal = add(ArtificialNonterminal("$accept","")); 27 | val error : Nonterminal = add(ErrorNonterminal()); 28 | 29 | var prologue : String = "/* Generated by Scala-Bison version "+Version.version+" */\n"; 30 | var extra : String = ""; 31 | 32 | def addExtra(x : String) = { extra = x; } 33 | 34 | def addPrologue(x : List[Code]) = { 35 | // Can't get this to work: 36 | // prologue = (prologue /: x)(+) 37 | for (c <- x) { 38 | prologue += (c.toString); 39 | } 40 | } 41 | 42 | def setStart(sym : Nonterminal) = { 43 | _start = sym; 44 | rules += new Rule(0,special,start :: end :: Nil,null,NoCode()); 45 | } 46 | 47 | def addRule(new_rule : Rule) = { 48 | if (_start == null) setStart(new_rule.lhs); 49 | rules += new_rule; 50 | } 51 | 52 | override def add[T <: Symbol](name : T) : T = super.add(name) 53 | 54 | def getNT(name : String) : Nonterminal = { 55 | get(name) match { 56 | case x:Nonterminal => x 57 | case _ => throw new GrammarSpecificationError("Not a nonterminal: " + name); 58 | } 59 | } 60 | 61 | def getCLT(ch : Char) : Terminal = { 62 | val name : String = CharUtil.lit(ch); 63 | find(name) match { 64 | case Some(t:Terminal) => t 65 | case None => add(new CharLitTerminal(ch)); 66 | case Some(s:Symbol) => throw new GrammarSpecificationError("Not a terminal: " + name); 67 | } 68 | } 69 | 70 | override def find(name : String) : Option[Symbol] = { 71 | val key = if (name.startsWith("$@")) name.substring(1) else name; 72 | super.find(key); 73 | } 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/BisonParserBase.scala: -------------------------------------------------------------------------------- 1 | package edu.uwm.cs.scalabison 2 | 3 | class BisonParserBase { 4 | 5 | } -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/BisonScanner.scala: -------------------------------------------------------------------------------- 1 | /* Scanner for Scala-Bison files 2 | * John Boyland 3 | * This file may be used, copied and/or modified for any purpose. 4 | * Eventually it might be nice to have this file generated. 5 | */ 6 | package edu.uwm.cs.scalabison; 7 | 8 | import scala.collection.Set; 9 | import scala.io.Source; 10 | 11 | class BisonScanner(input : Iterator[Char]) 12 | extends Iterator[BisonTokens.YYToken] 13 | { 14 | private val NOCURRENT : Int = -1; 15 | private var current : Int = NOCURRENT; 16 | private var linenumber : Int = 1; 17 | 18 | def getLineNumber : Int = linenumber; 19 | 20 | private def headInput : Char = { 21 | if (current == NOCURRENT) { 22 | if (!input.hasNext) { 23 | throw new GrammarSpecificationError("unexpected EOF"); 24 | } 25 | current = (input.next).toInt 26 | } 27 | current.toChar 28 | } 29 | private def nextInput : Unit = { 30 | if (current == '\n') linenumber += 1; 31 | (current = NOCURRENT); 32 | } 33 | private def pushInput(chx : Char) : Unit = { 34 | var ch : Char = chx 35 | if (ch == '\n') ch = ' '; 36 | if (current == NOCURRENT) { 37 | current = ch; 38 | } else { 39 | throw new GrammarSpecificationError("meta error in pushInput"); 40 | } 41 | } 42 | private def restInput : String = { 43 | val sb:StringBuilder = new StringBuilder; 44 | if (current != NOCURRENT) sb += (current.toChar); 45 | current = NOCURRENT; 46 | while (input.hasNext) { 47 | sb += (input.next) 48 | } 49 | sb.toString 50 | } 51 | 52 | val whitespace : Set[Char] = 53 | Set.empty + ' ' + '\t' + '\r' + '\n' + '\f' + '\b'; 54 | 55 | private def skipWhitespace : Unit = { 56 | if (whitespace contains headInput) { 57 | nextInput 58 | skipWhitespace 59 | } 60 | } 61 | 62 | private def readComment() : String = { 63 | val sb : StringBuilder = new StringBuilder("/"); 64 | if (headInput == '/') { 65 | do { 66 | sb += headInput 67 | nextInput 68 | } while (headInput != '\n'); 69 | } else if (headInput == '*') { 70 | do { 71 | do { 72 | sb += headInput; 73 | //print("+" + headInput); 74 | nextInput; 75 | } while (headInput != '*'); 76 | do { 77 | sb += headInput; 78 | //print("-" + headInput); 79 | nextInput; 80 | } while (headInput == '*'); 81 | } while (headInput != '/'); 82 | sb += headInput; 83 | nextInput 84 | } else { 85 | throw new GrammarSpecificationError("Meta error in readComment"); 86 | } 87 | sb.toString 88 | } 89 | 90 | def isKeyChar(ch : Char) : Boolean = { 91 | ch == '-' || ch == '_' || ch >= 'a' && ch <= 'z' || 92 | ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' 93 | } 94 | 95 | object ID { 96 | def unapply(ch : Char) : Boolean = isKeyChar(ch); 97 | } 98 | 99 | def readID() : String = { 100 | val sb : StringBuilder = new StringBuilder(); 101 | do { 102 | sb += headInput; 103 | nextInput 104 | } while (isKeyChar(headInput)); 105 | sb.toString 106 | } 107 | 108 | def readQuoted(quote : Char) : String = { 109 | val sb : StringBuilder = new StringBuilder(); 110 | sb += quote; 111 | nextInput 112 | while (headInput != quote) { 113 | if (headInput == '\\') { 114 | sb += headInput; 115 | nextInput 116 | } 117 | sb += headInput; 118 | nextInput 119 | } 120 | sb += headInput; 121 | nextInput; 122 | 123 | return sb.toString 124 | } 125 | 126 | def readSingleQuoted : String = { 127 | val sb : StringBuilder = new StringBuilder(); 128 | sb += '\''; 129 | nextInput 130 | if (headInput == '\\') { 131 | sb += headInput; 132 | nextInput 133 | } 134 | sb += headInput; 135 | nextInput 136 | if (headInput == '\'') { 137 | sb += headInput; 138 | nextInput; 139 | } 140 | return sb.toString 141 | } 142 | 143 | def convert(quoted : String) : String = { 144 | // println("Converting " + quoted); 145 | val sb : StringBuilder = new StringBuilder(); 146 | val n : Int = quoted.length() - 1; 147 | var i : Int = 1; 148 | while (i < n) { 149 | val ch : Char = quoted.charAt(i); 150 | sb += { 151 | if (ch == '\\') { 152 | i += 1 153 | quoted.charAt(i) match { 154 | case 'b' => '\b' 155 | case 't' => '\t' 156 | case 'f' => '\f' 157 | case 'n' => '\n' 158 | case 'r' => '\r' 159 | case '0' => '\u0000' 160 | case c => c 161 | } 162 | } else { 163 | ch 164 | } 165 | }; 166 | i += 1 167 | } 168 | // println("Result is '" + sb + "'"); 169 | sb.toString 170 | } 171 | 172 | abstract class State { 173 | def next : BisonTokens.YYToken; 174 | def hasNext : Boolean; 175 | } 176 | 177 | private var state : State = new InitialState; 178 | private var savedState : State = null; 179 | 180 | /*override*/ def hasNext : Boolean = state != null && state.hasNext; 181 | /*override*/ def next : BisonTokens.YYToken = state.next; 182 | 183 | class InitialState extends State { 184 | override def hasNext : Boolean = { 185 | skipWhitespace 186 | if (headInput == '/') { 187 | nextInput 188 | if (headInput == '/' || headInput == '*') { 189 | readComment 190 | hasNext 191 | } else { 192 | pushInput('/'); 193 | true 194 | } 195 | } else true 196 | } 197 | 198 | override def next : BisonTokens.YYToken = { 199 | headInput match { 200 | case '%' => { 201 | nextInput 202 | headInput match { 203 | case '%' => { 204 | nextInput 205 | state = new RuleState; 206 | BisonTokens.BEGIN() 207 | } 208 | case ID() => { 209 | readID match { 210 | case "left" => BisonTokens.LEFT() 211 | case "right" => BisonTokens.RIGHT() 212 | case "nonassoc" => BisonTokens.NONASSOC() 213 | case "token" => BisonTokens.TOKEN() 214 | case "type" => BisonTokens.TYPE() 215 | case "start" => BisonTokens.START() 216 | case "union" => BisonTokens.UNION() 217 | case s:String => 218 | throw new GrammarSpecificationError("Unknown key %" + s) 219 | } 220 | } 221 | case '{' => { 222 | nextInput 223 | savedState = this 224 | state = new CodeState; 225 | BisonTokens.PROLOGUE_BEGIN() 226 | } 227 | case ch:Char => { 228 | throw new GrammarSpecificationError("Unknown directive %" + ch) 229 | } 230 | } 231 | } 232 | 233 | case ID() => BisonTokens.ID(readID()) 234 | case '{' => { 235 | nextInput 236 | savedState = this 237 | state = new CodeState; 238 | BisonTokens.YYCHAR('{') 239 | } 240 | case '<' => { 241 | val sb : StringBuilder = new StringBuilder; 242 | nextInput 243 | while (headInput != '>' && headInput != '\n') { 244 | sb += headInput 245 | nextInput 246 | } 247 | if (headInput != '>') { 248 | throw new GrammarSpecificationError(" BisonTokens.CHARLIT(convert(readQuoted('\'')).charAt(0)) 254 | case '"' => BisonTokens.STRINGLIT(convert(readQuoted('"'))) 255 | case ch:Char => nextInput; BisonTokens.YYCHAR(ch) 256 | } 257 | } 258 | } 259 | 260 | class CodeState extends State { 261 | var level : Int = 1; 262 | override def hasNext : Boolean = true; 263 | override def next : BisonTokens.YYToken = { 264 | headInput match { 265 | case '{' => { 266 | nextInput 267 | level += 1; 268 | BisonTokens.YYCHAR('{') 269 | } 270 | case '}' => { 271 | nextInput 272 | level -= 1 273 | if (level == 0) state = savedState; 274 | BisonTokens.YYCHAR('}') 275 | } 276 | case '/' => { 277 | nextInput; 278 | if (headInput == '*' || headInput == '/') { 279 | BisonTokens.CODE(readComment()) 280 | } else { 281 | BisonTokens.CODE("/") 282 | } 283 | } 284 | case '$' => { 285 | nextInput; 286 | if (headInput == '$') { 287 | nextInput 288 | BisonTokens.VAR(-1) 289 | } else if (headInput >= '0' && headInput <= '9') { 290 | var result : Int = 0 291 | do { 292 | result *= 10; 293 | result += (headInput - '0'); 294 | nextInput 295 | } while (headInput >= '0' && headInput <= '9'); 296 | BisonTokens.VAR(result) 297 | } else { 298 | throw new GrammarSpecificationError("$ must be followed by int"); 299 | } 300 | } 301 | case '%' => { 302 | nextInput 303 | if (headInput == '}') { 304 | nextInput 305 | state = savedState; 306 | BisonTokens.PROLOGUE_END() 307 | } else { 308 | BisonTokens.CODE("%"); 309 | } 310 | } 311 | case '\'' => BisonTokens.CODE(readSingleQuoted) 312 | case '"' => BisonTokens.CODE(readQuoted('"')) 313 | case ch:Char => { 314 | var sb : StringBuilder = new StringBuilder 315 | do { 316 | sb += headInput 317 | nextInput 318 | } while (headInput != '$' && 319 | headInput != '}' && 320 | headInput != '{' && 321 | headInput != '%' && 322 | headInput != '/' && 323 | headInput != '\'' && headInput != '"') 324 | return BisonTokens.CODE(sb.toString) 325 | } 326 | } 327 | } 328 | } 329 | 330 | class RuleState extends InitialState { 331 | override def next : BisonTokens.YYToken = { 332 | headInput match { 333 | case ID() => BisonTokens.ID(readID()) 334 | case '{' => super.next 335 | case '\'' => BisonTokens.CHARLIT(convert(readQuoted('\'')).charAt(0)) 336 | case '"' => BisonTokens.STRINGLIT(convert(readQuoted('"'))) 337 | case '%' => { 338 | nextInput 339 | headInput match { 340 | case '%' => { 341 | nextInput 342 | state = null 343 | BisonTokens.END(restInput) 344 | } 345 | case ID() => { 346 | readID() match { 347 | case "prec" => BisonTokens.PREC() 348 | case s:String => 349 | throw new GrammarSpecificationError("Illegal keyword %"+s) 350 | } 351 | } 352 | case ch:Char => { 353 | throw new GrammarSpecificationError("Illegal directive %"+ch) 354 | } 355 | } 356 | } 357 | case ch:Char => nextInput; BisonTokens.YYCHAR(ch); 358 | } 359 | } 360 | } 361 | } 362 | 363 | object BisonScanner { 364 | def main(args : Array[String]) = { 365 | for (s <- args) { 366 | val scanner : BisonScanner = new BisonScanner(Source.fromFile(s)) 367 | while (scanner.hasNext) { 368 | println(scanner.next) 369 | } 370 | } 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/BisonTable.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.io.Source 20 | import scala.collection.mutable.Set 21 | import scala.collection.mutable.HashSet; 22 | 23 | 24 | /** 25 | * LALR(1) Parse tables in Bison format 26 | */ 27 | class BisonTable(val bison : BisonGrammar) extends Table(bison) { 28 | 29 | def fromFile(filename : String) = { 30 | if (Options.meta_debug) { 31 | println("Reading bison output " + filename); 32 | } 33 | val s : Source = Source.fromFile(filename); 34 | getStates(s); 35 | s.reset(); 36 | getActions(Source.fromFile(filename)) 37 | } 38 | 39 | private def skipToStates(it : Iterator[String]) : Int = { 40 | for (s <- it) { 41 | if (s.startsWith("state ") || s.startsWith("State ")) { 42 | val rest = s.substring(6,s.length) 43 | if (!rest.contains(':')) { 44 | val stateNum = Integer.parseInt(rest) 45 | return stateNum; 46 | } 47 | } 48 | } 49 | -1 50 | } 51 | 52 | private def getStates(s : Source) = { 53 | val it : Iterator[String] = s.getLines; 54 | while (skipToStates(it) >= 0) { 55 | it.next; 56 | states += getState(it) 57 | } 58 | } 59 | 60 | private def getState(it : Iterator[String]) : State = { 61 | val is : Set[Item] = new HashSet[Item](); 62 | /*if (expectTwoEmpty) { 63 | val first = it.next; 64 | if (!first.equals("\n")) { 65 | println("Unexpected bison output file format: expected blank line: " + first); 66 | System.exit(-1); 67 | } 68 | }*/ 69 | for (s <- it) { 70 | if (s.equals("") || s.equals("\n")) { 71 | return new State(states.length,is); 72 | } else { 73 | is += Item.fromLine(grammar,s); 74 | } 75 | } 76 | throw new GrammarSpecificationError("unexpected EOF") 77 | } 78 | 79 | private def getActions(s : Source) = { 80 | val it : Iterator[String] = s.getLines; 81 | var i : Int = 0; 82 | while (skipToStates(it) >= 0) { 83 | // println("Getting actions for state " + i); 84 | it.next; 85 | val state : State = states(i); 86 | val check : State = getState(it); 87 | if (!state.equals(check)) { 88 | throw new GrammarSpecificationError("State mismatch: " + state + " != " + check); 89 | } 90 | getAction(state,it) 91 | // println(state); 92 | i += 1; 93 | } 94 | } 95 | 96 | private def getAction(state : State, it : Iterator[String]) : Unit = { 97 | var empty : Boolean = false; 98 | for (s <- it) { 99 | if (s.equals("\n") || s.equals("")) { 100 | if (empty) return (); 101 | empty = true; 102 | } else { 103 | empty = false; 104 | val pieces : Array[String] = s.substring(4,s.length).split(" +"); 105 | if (pieces(1).charAt(0) != '[' ) { 106 | pieces(1) match { 107 | case "shift," => { 108 | val next : Int = Integer.parseInt(pieces(6)); 109 | grammar.find(pieces(0)) match { 110 | case Some(nt:ArtificialNonterminal) => 111 | state.putGoto(nt,states(next)) 112 | case Some(t:Terminal) => 113 | state.putAction(t,ShiftAction(states(next))); 114 | case s => throw new GrammarSpecificationError("Unknown terminal " 115 | +pieces(0)); 116 | } 117 | } 118 | case "reduce" => { 119 | val num : Int = Integer.parseInt(pieces(4)); 120 | val rule : Rule = grammar.rules(num); 121 | val action : Action = ReduceAction(rule); 122 | if (pieces(0).equals("$default")) { 123 | state.default = action 124 | } else { 125 | grammar.find(pieces(0)) match { 126 | case Some(t:Terminal) => state.putAction(t,action) 127 | case _ => throw new GrammarSpecificationError("Unknown terminal" 128 | +pieces(0)); 129 | } 130 | } 131 | } 132 | case "go" => { 133 | val next : Int = Integer.parseInt(pieces(4)); 134 | grammar.find(pieces(0)) match { 135 | case Some(nt:Nonterminal) => state.putGoto(nt,states(next)) 136 | case _ => throw new GrammarSpecificationError("Unknown nonterm " 137 | +pieces(0)); 138 | } 139 | } 140 | case "accept" => { 141 | state.default = AcceptAction(); 142 | } 143 | case "error" => { 144 | grammar.find(pieces(0)) match { 145 | case Some(t:Terminal) => 146 | state.putAction(t,ErrorAction(pieces(2))); 147 | case _ => throw new GrammarSpecificationError("Unknown terminal " 148 | +pieces(0)); 149 | } 150 | } 151 | case _ => { 152 | for (s <- pieces) { 153 | println("Piece: " + s); 154 | } 155 | throw new GrammarSpecificationError("Unknown action " + pieces(1) + " in " + pieces); 156 | } 157 | } 158 | } 159 | } 160 | } 161 | } 162 | } 163 | 164 | object BisonTable { 165 | def main(args : Array[String]) = { 166 | for (s <- args) { 167 | val scanner : BisonScanner = new BisonScanner(Source.fromFile(s+".y")) 168 | val parser : BisonParser = new BisonParser(); 169 | parser.reset(s+".y",scanner); 170 | if (parser.yyparse()) { 171 | println(parser.result); 172 | val table : BisonTable = new BisonTable(parser.result); 173 | table.fromFile(s+".output"); 174 | println(table); 175 | table.setRecognitionPoints(); 176 | } 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/BlockCode.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class BlockCode(cl : List[Code]) extends Code { 20 | override def toString : String = { 21 | "{" + (cl foldRight "}")((c:Code,s:String) => (c.toString) + s); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/CharLitTerminal.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | import edu.uwm.cs.util.CharUtil; 19 | 20 | case class CharLitTerminal(c : Char) extends Terminal(CharUtil.lit(c),"") { 21 | } -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Code.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | class Code { } 20 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/ErrorAction.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class ErrorAction(val reason : String) extends Action { 20 | override def toString = "error " + reason 21 | } 22 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/ErrorNonterminal.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | /* 20 | case class ErrorNonterminal() extends ArtificialNonterminal("error","") { 21 | }*/ 22 | 23 | object ErrorNonterminal { 24 | def apply() = ArtificialNonterminal("error",""); 25 | def unapply(x : Symbol) : Boolean = x match { 26 | case ArtificialNonterminal("error","") => true; 27 | case _ => false 28 | }; 29 | } -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/First.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.Set; 20 | import scala.collection.mutable.{HashMap}; 21 | import scala.collection.immutable.ListSet; 22 | 23 | class First(grammar : Grammar) extends Function[Any,FirstSet] { 24 | 25 | private val ntmap : HashMap[Nonterminal,FirstSet] = new HashMap; 26 | 27 | // initialize and run to a fixed point 28 | { 29 | for (sym <- grammar.all_symbols) { 30 | sym match { 31 | case nt:Nonterminal => ntmap.put(nt,FirstSet.empty); 32 | case _ => () 33 | } 34 | } 35 | var done : Boolean = false; 36 | while (!done) { 37 | done = true; 38 | for (sym <- grammar.all_symbols) { 39 | sym match { 40 | case nt:Nonterminal => { 41 | var fs : FirstSet = FirstSet.empty; 42 | for (rule <- nt.rules) { 43 | fs = fs ++ first(rule) 44 | } 45 | if (!(fs.equals(ntmap(nt)))) { 46 | ntmap.put(nt,fs); 47 | // println("getting bigger: FIRST(" + nt.name + ") = " + fs); 48 | done = false; 49 | } 50 | } 51 | case _ => () 52 | } 53 | } 54 | } 55 | /* 56 | for ((nt,fs) <- ntmap) { 57 | println("FIRST(" + nt.name + ") = " + fs); 58 | }*/ 59 | } 60 | 61 | def first(sym : Symbol) : FirstSet = { 62 | sym match { 63 | case nt : Nonterminal => ntmap(nt); 64 | case t : Terminal => new FirstSet(ListSet.empty + t,false) 65 | } 66 | } 67 | 68 | def first(rule : Rule) : FirstSet = first(new Item(rule,0)); 69 | 70 | def first(item : Item) : FirstSet = first(item.rule.rhs.drop(item.index)); 71 | 72 | def first(l : List[Any]) : FirstSet = { 73 | l match { 74 | case Nil => FirstSet.epsilon 75 | case h::t => apply(h) dot first(t) 76 | } 77 | } 78 | 79 | override def apply(x : Any) : FirstSet = { 80 | x match { 81 | case x:Item => first(x) 82 | case x:Rule => first(x) 83 | case x:List[Any] => first(x) 84 | case x:Symbol => first(x) 85 | case _ => FirstSet.empty 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/FirstSet.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.immutable.Set; 20 | import scala.collection.immutable.ListSet; 21 | 22 | class FirstSet(val set : Set[Terminal], val incEpsilon : Boolean) { 23 | def dot(other : => FirstSet) : FirstSet = { 24 | if (incEpsilon) new FirstSet(set ++ other.set, other.incEpsilon); 25 | else this; 26 | } 27 | def ++(other : FirstSet) : FirstSet = { 28 | if (incEpsilon) new FirstSet(set ++ other.set, true); 29 | else new FirstSet(set ++ other.set, other.incEpsilon); 30 | } 31 | override def equals(x : Any) = { 32 | x match { 33 | case o:FirstSet => set.equals(o.set) && incEpsilon == o.incEpsilon 34 | case _ => false 35 | } 36 | } 37 | override def hashCode() : Int = set.hashCode() + incEpsilon.hashCode(); 38 | 39 | override def toString : String = { 40 | if (incEpsilon) set.toString + " + e" 41 | else set.toString 42 | } 43 | } 44 | 45 | object FirstSet { 46 | val empty : FirstSet = new FirstSet(ListSet.empty,false); 47 | val epsilon : FirstSet = new FirstSet(ListSet.empty,true); 48 | } 49 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Follow.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.Set; 20 | import scala.collection.mutable.{HashMap}; 21 | import scala.collection.immutable.ListSet; 22 | 23 | 24 | class Follow(grammar : Grammar, first : First) 25 | extends Function[Nonterminal,Set[Terminal]] 26 | { 27 | private val ntfol : HashMap[Nonterminal,scala.collection.immutable.Set[Terminal]] = new HashMap; 28 | 29 | { // initialize the follow set 30 | for (sym <- grammar.all_symbols) { 31 | sym match { 32 | case nt:Nonterminal => 33 | ntfol.put(nt,scala.collection.immutable.Set.empty); 34 | case _ => () 35 | } 36 | } 37 | } 38 | 39 | private var done : Boolean = false; 40 | 41 | { 42 | // standard "code until done" loop: 43 | while (!done) { 44 | done = true; 45 | for (rule <- grammar.rules) { 46 | setFollow(rule.rhs,ntfol(rule.lhs)); 47 | } 48 | } 49 | } 50 | 51 | private def setFollow(l : List[Symbol], fol : scala.collection.immutable.Set[Terminal]) : Unit = { 52 | l match { 53 | case (nt:Nonterminal)::t => { 54 | val oldFol : scala.collection.immutable.Set[Terminal] = ntfol(nt); 55 | val newFol : Set[Terminal] = follow(t,fol); 56 | if (!(newFol subsetOf oldFol)) { 57 | ntfol.put(nt,oldFol ++ newFol); 58 | done = false; 59 | } 60 | } 61 | case _ => () 62 | } 63 | l match { 64 | case Nil => (); 65 | case _::t => setFollow(t,fol); 66 | } 67 | } 68 | 69 | def apply(item : Item) : Set[Terminal] = { 70 | follow(item.rule.rhs.drop(item.index),ntfol(item.rule.lhs)) 71 | } 72 | 73 | private def follow(t : List[Symbol], fol : scala.collection.immutable.Set[Terminal]) : Set[Terminal] = { 74 | (first(t) dot new FirstSet(fol,false)).set 75 | } 76 | 77 | def apply(nt : Nonterminal) : Set[Terminal] = ntfol(nt); 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Generator.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.io.Source 20 | import java.io._ 21 | import scala.collection.Set 22 | import scala.collection.immutable.ListSet 23 | import scala.collection.mutable._ 24 | import edu.uwm.cs.util.CharUtil 25 | 26 | /** Generate recursive-ascent-descent parser for the given tables. 27 | * This technique is inspired by Nigel Horspool's paper 28 | * on the same topic with a number of sloppy shortcuts. 29 | * Any errors are strictly my own (John Boyland). 30 | */ 31 | class Generator(prefix : String, table : BisonTable) 32 | { 33 | val lctable : LeftCornerTable = new LeftCornerTable(table); 34 | 35 | def writeFiles() = { 36 | if (Options.verbose > 0) writeOutput(); 37 | writeTokens(); 38 | writeParser(); 39 | } 40 | 41 | def writeOutput() : Unit = { 42 | var fw : Writer = new FileWriter(prefix + ".lcoutput") 43 | val pw : PrintWriter = new PrintWriter(fw); 44 | pw.println("Recognition points"); 45 | pw.println(); 46 | pw.println(table.grammar.toString(true)); 47 | pw.println(); 48 | pw.println(lctable.toString(true)); // was false 49 | pw.close(); 50 | } 51 | 52 | def writeTokens() : Unit = { 53 | var fw : Writer = new FileWriter(prefix + "Tokens.scala") 54 | val pw : PrintWriter = new PrintWriter(fw); 55 | 56 | pw.println(table.bison.prologue); 57 | pw.println("object " + prefix + "Tokens {"); 58 | pw.println(" class YYSymbol;") 59 | pw.println(); 60 | writeTerminals(pw); 61 | pw.println("}"); 62 | pw.close(); 63 | } 64 | 65 | def writeParser() : Unit = { 66 | var fw : Writer = new FileWriter(prefix + "Parser.scala") 67 | val pw : PrintWriter = new PrintWriter(fw); 68 | 69 | pw.println(table.bison.prologue); 70 | pw.println("/** Generated LALR(1) recursive-ascent-descent parser */"); 71 | pw.println("class " + prefix + "Parser extends " + prefix + "ParserBase {"); 72 | writeNonterminals(pw); 73 | pw.println(); 74 | /* 75 | pw.println(" private class YYGoto;"); 76 | pw.println(" private case class YYBase(yy : YYNonterminal) extends YYGoto"); 77 | pw.println(" private case class YYNested(yy : YYGoto) extends YYGoto"); 78 | */ 79 | pw.println(" private var yynt : YYNonterminal = null;"); 80 | pw.println(); 81 | pw.println(" case class YYError(s:String) extends Exception(s);"); 82 | pw.println(); 83 | pw.println(" // boilerplate"); 84 | pw.println(); 85 | pw.println(" var yydebug : Boolean = false;"); 86 | pw.println(" private var yyinput : Iterator["+prefix+"Tokens.YYToken] = null;"); 87 | pw.println(" private var yycur : "+prefix+"Tokens.YYToken = null;"); 88 | pw.println(); 89 | pw.println(" private def yynext() = {"); 90 | pw.println(" yycur = {"); 91 | pw.println(" if (yyinput.hasNext) {"); 92 | pw.println(" yyinput.next"); 93 | pw.println(" } else {"); 94 | pw.println(" "+prefix+"Tokens.YYEOF();"); 95 | pw.println(" }"); 96 | pw.println(" }"); 97 | if (Options.debug) 98 | pw.println(" if (yydebug) println(\"Current token now is \" + yycur);"); 99 | pw.println(" }"); 100 | pw.println(""); 101 | pw.println(" private def yypanic(test : ("+prefix+"Tokens.YYToken) => Boolean) = {"); 102 | pw.println(" while (!test(yycur)) {"); 103 | if (Options.debug) 104 | pw.println(" if (yydebug) println(\"Discarding current token\");"); 105 | pw.println(" yynext;"); 106 | pw.println(" if (yycur == "+prefix+"Tokens.YYEOF()) throw new YYError(\"Giving up\")"); 107 | pw.println(" }"); 108 | pw.println(" }"); 109 | pw.println(""); 110 | pw.println(" def yyreset(input : Iterator["+prefix+"Tokens.YYToken]) = {"); 111 | pw.println(" yyinput = input;"); 112 | pw.println(" yynt = null;"); // No YYGoto 113 | pw.println(" yynext"); 114 | pw.println(" }"); 115 | pw.println(""); 116 | pw.println(" def yyparse() : Boolean = {"); 117 | pw.println(" try {"); 118 | pw.println(" parse_"+table.grammar.start.name+"()"); 119 | pw.println(" parse_YYEOF()"); 120 | pw.println(" true"); 121 | pw.println(" } catch {"); 122 | pw.println(" case YYError(s) => yyerror(s); false"); 123 | pw.println(" }"); 124 | pw.println(" }"); 125 | pw.println(""); 126 | /* 127 | pw.println(" private def yynest(nx:Int,g: YYNonterminal) : YYGoto = {"); 128 | //pw.println(" try {"); 129 | pw.println(" var n : Int = nx;"); 130 | pw.println(" var yygoto : YYGoto = YYBase(g)"); 131 | pw.println(" while (n > 0) {"); 132 | pw.println(" yygoto = YYNested(yygoto)"); 133 | pw.println(" n -= 1"); 134 | pw.println(" }"); 135 | pw.println(" yygoto"); 136 | //pw.println(" } catch {"); 137 | //pw.println(" case YYError(s) => YYBase(YYNTerror(s))"); 138 | //pw.println(" }"); 139 | pw.println(" }"); 140 | pw.println(""); 141 | */ 142 | pw.println(" def parse_YYCHAR(yy:Char) : Unit = {"); 143 | pw.println(" yycur match {"); 144 | pw.println(" case " + prefix +"Tokens.YYCHAR(`yy`) => yynext; ()"); 145 | pw.println(" case _ => throw new YYError(\"Expected '\"+yy+\"'\");"); 146 | pw.println(" }"); 147 | pw.println(" }"); 148 | pw.println(""); 149 | pw.println(" // generated parser"); 150 | pw.println(); 151 | for (sym <- table.grammar.all_symbols) { 152 | sym match { 153 | case _:CharLitTerminal => () 154 | case t:Terminal => { 155 | pw.println(" def parse_" + t.name + "() : " + t.getType() + " = {") 156 | pw.println(" yycur match {"); 157 | pw.print(" case " + prefix + "Tokens." + t.name); 158 | if (t.typed) { 159 | pw.println("(yy) => yynext; yy"); 160 | } else { 161 | pw.println("() => yynext; ()"); 162 | } 163 | pw.println(" case _ => throw new YYError(\"Expected '" + t.name + "'\");"); 164 | pw.println(" }"); 165 | pw.println(" }\n"); 166 | } 167 | case _ => () 168 | } 169 | } 170 | 171 | for ((nt,state) <- lctable.NTstate) { 172 | pw.print(" def parse_" + code(nt) + "()") 173 | pw.print(" : " + nt.getType) 174 | pw.println(" = {"); 175 | if (Options.debug) 176 | pw.println(" if (yydebug) println(\"Parsing "+nt.name+"\");"); 177 | /* YYGoto: 178 | pw.println(" yystate" + state.number + "() match {"); 179 | pw.println(" case YYBase(YYNT" + (code(nt)) + "(yy)) => yy"); 180 | pw.println(" case YYBase(YYNTerror(s)) => throw new YYError(s)"); 181 | if (Options.debug) 182 | pw.println(" case _ => throw new YYError(\"internal parser error\")"); 183 | pw.println(" }"); 184 | */ 185 | if (Options.debug) { 186 | pw.println(" if (yystate" + state.number + "() != 0)"); 187 | pw.println(" throw new YYError(\"internal parse error\");"); 188 | } else { 189 | pw.println(" yystate" + state.number + "();"); 190 | } 191 | pw.println(" yynt match {"); 192 | if (nt.typed) { 193 | pw.println(" case YYNT" + (code(nt)) + "(yy) => yy"); 194 | } else { 195 | pw.println(" case YYNT" + (code(nt)) + "() => ()"); 196 | } 197 | pw.println(" case YYNTerror(s) => throw new YYError(s)"); 198 | if (Options.debug) 199 | pw.println(" case _ => throw new YYError(\"internal parser error\")"); 200 | pw.println(" }"); 201 | /* end removal of YYGoto */ 202 | pw.println(" }\n"); 203 | } 204 | 205 | for (rule <- table.grammar.rules) { 206 | writeRule(pw,rule); 207 | } 208 | 209 | for ((_,state) <- lctable.NTstate) { 210 | writeState(pw,state); 211 | } 212 | pw.println(table.bison.extra); 213 | 214 | if (Options.gen_lalr_table) { 215 | writeLALRTable(pw); 216 | } 217 | pw.println("}"); 218 | pw.close(); 219 | } 220 | 221 | private def writeTerminals(pw : PrintWriter) = { 222 | pw.println(" class YYToken extends YYSymbol;\n"); 223 | pw.println(" case class YYCHAR(yy: Char) extends YYToken {"); 224 | pw.println(" override def toString() : String = \"'\" + yy + \"'\";"); 225 | pw.println(" }"); 226 | for (symbol <- table.grammar.all_symbols) { 227 | symbol match { 228 | case _:CharLitTerminal => () 229 | case t:Terminal => { 230 | pw.print(" case class " + t.name + "("); 231 | if (t.typed) { 232 | pw.print("yy: " + t.getType()) 233 | } 234 | pw.println(") extends YYToken;"); 235 | } 236 | case _ => () 237 | } 238 | } 239 | } 240 | 241 | private def writeNonterminals(pw : PrintWriter) = { 242 | pw.println(" class YYNonterminal extends " + prefix + "Tokens.YYSymbol;\n"); 243 | pw.println(" case class YYNTerror(yy : String) extends YYNonterminal;"); 244 | for (symbol <- table.grammar.all_symbols) { 245 | symbol match { 246 | case _:ArtificialNonterminal => () 247 | case nt:Nonterminal => { 248 | pw.print(" case class YYNT" + code(nt) + "("); 249 | if (nt.typed) { 250 | pw.print("yy: " + nt.getType()) 251 | } 252 | pw.println(") extends YYNonterminal;"); 253 | } 254 | case _ => () 255 | } 256 | } 257 | if (Options.gen_lalr_table) { 258 | pw.println(" case class YYNT(num : Int) extends YYNonterminal;"); 259 | } 260 | } 261 | 262 | private val usedAfterRecognition : Map[Symbol,Item] = new HashMap(); 263 | 264 | private def writeRule(pw: PrintWriter, rule : Rule) : Unit = { 265 | if (rule.lhs.isInstanceOf[ArtificialNonterminal]) return; 266 | // write the rescurive descent part after the recognition point 267 | pw.println(); 268 | pw.println(" /** Recursive descent parser after recognition point"); 269 | pw.println(" * " + new Item(rule,rule.recognitionPoint)) 270 | pw.println(" */"); 271 | pw.print(" private def yyrule" + rule.number + "("); 272 | var noargs : Boolean = true; 273 | var i : Int = 0; 274 | var recognized : Boolean = false; 275 | for (symbol <- rule.rhs) { 276 | if (i == rule.recognitionPoint) { 277 | finishRuleHeader(pw,rule); 278 | recognized = true; 279 | } 280 | i += 1; 281 | if (recognized) { 282 | pw.print(" "); 283 | symbol match { 284 | case CharLitTerminal(ch) => { 285 | pw.println("parse_YYCHAR("+ toLit(ch) +");"); 286 | } 287 | case nt:ArtificialNonterminal => { 288 | if (nt.name.charAt(0) == '@') { 289 | pw.println(nt.rules(0).action); 290 | } else { 291 | sys.error("Unknown artificial nonterminal " + nt); 292 | } 293 | } 294 | case _ => { 295 | usedAfterRecognition.put(symbol,new Item(rule,i-1)); 296 | if (symbol.typed) { 297 | pw.print("val yyarg" + i + " : " + symbol.getType() + " = "); 298 | } 299 | pw.println("parse_" + code(symbol) + "();"); 300 | } 301 | } 302 | } else { 303 | // not yet recognized 304 | if (symbol.typed) { 305 | if (noargs) { 306 | noargs = false; 307 | } else { 308 | pw.print(", "); 309 | } 310 | pw.print("yyarg" + i + " : " + symbol.getType()); 311 | } 312 | } 313 | } 314 | if (!recognized) finishRuleHeader(pw,rule); 315 | rule.action match { 316 | case NoCode() => { 317 | if (rule.lhs.typed) { 318 | pw.println(" yyresult = yyarg1;"); 319 | } 320 | } 321 | case _ => { 322 | pw.println(" " + rule.action); 323 | } 324 | } 325 | if (rule.lhs.typed) { 326 | pw.println(" yyresult"); 327 | } 328 | pw.println(" }"); 329 | } 330 | 331 | private def finishRuleHeader(pw : PrintWriter, rule : Rule) = { 332 | pw.print(") "); 333 | if (rule.lhs.typed) { 334 | pw.println(": " + rule.lhs.getType() + " = {"); 335 | pw.println(" var yyresult : " + rule.lhs.getType() + 336 | " = " + defaultInitial(rule.lhs.getType()) + ";"); 337 | } else { 338 | pw.println(": Unit = {"); 339 | } 340 | if (Options.debug) 341 | pw.println(" if (yydebug) println(\"Announcing rule "+rule.number+"\");"); 342 | } 343 | 344 | private def defaultInitial(t : String) : String = { 345 | t match { 346 | case "Boolean" => "false"; 347 | case "Int" => "0"; 348 | case "Double" => "0"; 349 | case "Float" => "0"; 350 | case "Char" => "0"; 351 | case "Byte" => "0"; 352 | case "Short" => "0"; 353 | case _ => "null"; 354 | } 355 | } 356 | 357 | private def toLit(ch : Char) : String = CharUtil.lit(ch); 358 | 359 | private def code(s : Symbol) : String = s.name; 360 | 361 | private val stateWritten : HashSet[Int] = new HashSet; 362 | 363 | private def writeState(pw : PrintWriter, state : LeftCornerState) : Unit = { 364 | if (stateWritten contains (state.number)) return; 365 | stateWritten += state.number; 366 | // println("Writing state " + state.number); 367 | 368 | // we cache everything into a string builder first, 369 | // to avoid intermixing states 370 | val sb : StringBuilder = new StringBuilder(); 371 | 372 | // the "longest" item 373 | val longest : Item = state.longestItem; 374 | var nextIndex : Int = 1; 375 | if (longest != null) nextIndex = longest.index+1; 376 | val nextarg : String = "yyarg" + nextIndex; 377 | 378 | sb.append("\n private def yystate" + state.number + "("); 379 | var noargs : Boolean = true; 380 | var i : Int = 0; 381 | if (longest != null) { 382 | for (symbol <- (longest.rule.rhs.slice(0,longest.index))) { 383 | i += 1; 384 | if (symbol.typed) { 385 | if (noargs) { 386 | noargs = false; 387 | } else { 388 | sb.append(", "); 389 | } 390 | sb.append("yyarg" + i + ": " + symbol.getType()); 391 | } 392 | } 393 | } else if (state.nonterminal != null && !state.atStart) { 394 | // for fake item: 395 | if (state.nonterminal.typed) { 396 | sb.append("yyarg1: " + state.nonterminal.getType()) 397 | } 398 | } 399 | /*YYGoto 400 | sb.append(") : YYGoto = {\n") 401 | */ 402 | sb.append(") : Int = {\n") // No YYGoto 403 | if (Options.debug) 404 | sb append " if (yydebug) println(\"Entering state "+ state.number+"\")\n"; 405 | /* YYGoto 406 | sb append " var yygoto : YYGoto = null;\n"; 407 | */ 408 | sb append " var yygoto : Int = 0;\n"; // No YYGoto 409 | 410 | if (state.gotos contains (ErrorNonterminal())) { 411 | sb append " try {\n"; 412 | } 413 | 414 | sb append " yycur match {\n"; 415 | for ((t,action) <- state.actions) { 416 | sb append " case "; 417 | sb append prefix 418 | sb append "Tokens." 419 | t match { 420 | case CharLitTerminal(ch) => { 421 | sb append "YYCHAR(" + toLit(ch) + ")"; 422 | } 423 | case _ => { 424 | sb append (t.name); 425 | sb append '('; 426 | if (t.typed) { 427 | action match { 428 | case ShiftAction(_) => sb append nextarg 429 | case _ => sb append "_" 430 | } 431 | } 432 | sb append ')' 433 | } 434 | } 435 | sb append " => "; 436 | action match { 437 | case AnnounceAction(r) => { 438 | appendRuleCall(sb,longest,r); 439 | } 440 | case AcceptNTAction(nt) => { 441 | appendAcceptNT(sb,nt); 442 | } 443 | case ShiftAction(s) => { 444 | sb append "yynext; "; 445 | writeState(pw,s.asInstanceOf[LeftCornerState]); 446 | appendGoto(sb,longest,s); 447 | } 448 | case ErrorAction(s) => { 449 | /* YYGoto 450 | sb append "yygoto = YYBase(YYNTerror(\""; 451 | sb append s 452 | sb append "\"));\n" 453 | */ 454 | sb append "yynt = YYNTerror(\""; 455 | sb append s 456 | sb append "\");\n" 457 | } 458 | } 459 | } 460 | sb append " case _ => " 461 | state.default match { 462 | case null => 463 | /* YYGoto 464 | sb append "yygoto = YYBase(YYNTerror(\"syntax error\"));\n"; 465 | */ 466 | sb append "yynt = YYNTerror(\"syntax error\");\n"; 467 | case AnnounceAction(r) => 468 | appendRuleCall(sb,longest,r) 469 | case AcceptNTAction(nt) => 470 | appendAcceptNT(sb,nt); 471 | case AcceptAction() => 472 | appendRuleCall(sb,longest,table.grammar.rules(0)); 473 | } 474 | sb append " }\n"; 475 | 476 | if (state.gotos contains ErrorNonterminal()) { 477 | sb append " } catch {\n"; 478 | /* YYGoto 479 | sb append " case YYError(s) => yygoto = YYBase(YYNTerror(s));\n"; 480 | */ 481 | sb append " case YYError(s) => yynt = YYNTerror(s);\n"; 482 | sb append " }\n"; 483 | } 484 | 485 | /* YYGoto: 486 | sb append " while (true) {\n"; 487 | sb append " yygoto match {\n"; 488 | sb append " case YYNested(g) => return g;\n"; 489 | */ 490 | sb append " while (yygoto == 0) {\n"; 491 | if (state.gotos contains (ErrorNonterminal())) { 492 | sb append " try {\n"; 493 | } 494 | 495 | sb append " yynt match {\n"; 496 | 497 | for ((nt,next) <- state.gotos) { 498 | writeState(pw,next.asInstanceOf[LeftCornerState]); 499 | if (nt == ErrorNonterminal()) { 500 | /* YYGoto 501 | sb append " case YYBase(YYNTerror(s)) => \n" 502 | */ 503 | sb append " case YYNTerror(s) => \n" 504 | sb append " if (yycur == "+prefix+"Tokens.YYEOF()) return 0;" 505 | sb append " yyerror(s)\n" 506 | if (Options.debug) 507 | sb append " if (yydebug) println(\"Recovering in state "+ state.number+"\")\n"; 508 | var panic : scala.collection.immutable.Set[Terminal] = 509 | scala.collection.immutable.Set.empty; 510 | for (item <- next.core) { 511 | panic ++= lctable.follow(item); 512 | } 513 | sb append " yypanic({ t:" 514 | sb append prefix 515 | sb append "Tokens.YYToken => t match {\n"; 516 | for (t <- panic) { 517 | sb append " case "; 518 | sb append prefix 519 | sb append "Tokens."; 520 | t match { 521 | case CharLitTerminal(ch) => { 522 | sb append "YYCHAR(" 523 | sb append toLit(ch) 524 | sb append ")" 525 | } 526 | case _ => { 527 | sb append (t.name) 528 | sb append "(" 529 | if (t.typed) sb append "_" 530 | sb append ")" 531 | } 532 | } 533 | sb append " => true\n" 534 | } 535 | sb append " case _ => false\n"; 536 | sb append " }})\n"; 537 | sb append " "; 538 | } else { 539 | /* YYGoto: 540 | sb append (" case YYBase(YYNT" + code(nt) + "("); 541 | sb append nextarg; 542 | sb append ")) => "; 543 | */ 544 | sb append (" case YYNT" + code(nt) + "("); 545 | if (nt.typed) { 546 | sb append nextarg; 547 | } 548 | sb append ") => "; 549 | } 550 | appendGoto(sb,longest,next); 551 | } 552 | // The following won't happen any more 553 | if (state.nonterminal != null && 554 | !(state.gotos isDefinedAt(state.nonterminal))) { 555 | /* YYGoto: 556 | sb append " case x@YYBase(_:YYNT" + code(state.nonterminal) + 557 | ") => return x;\n"; 558 | */ 559 | sb append " case _:YYNT" + code(state.nonterminal) + 560 | " => return 0;\n"; 561 | } 562 | if (!(state.gotos isDefinedAt ErrorNonterminal())) { 563 | /* YYYGoto: 564 | sb append " case e@YYBase(_:YYNTerror) => return e\n"; 565 | */ 566 | sb append " case _:YYNTerror => return 0;\n"; 567 | } 568 | 569 | if (Options.debug) { 570 | /* YYGoto: 571 | sb append " case _ => return YYBase(YYNTerror(\"internal parser error\"));\n"; 572 | */ 573 | sb append " case _ => yynt = YYNTerror(\"internal parser error\"); return 0;\n"; 574 | } 575 | sb append " }\n"; 576 | if (state.gotos contains ErrorNonterminal()) { 577 | sb append " } catch {\n"; 578 | /* YYGoto 579 | sb append " case YYError(s) => yygoto = YYBase(YYNTerror(s));\n"; 580 | */ 581 | sb append " case YYError(s) => yynt = YYNTerror(s);\n"; 582 | sb append " }\n"; 583 | } 584 | sb append " }\n"; 585 | sb append " yygoto-1\n"; // YYGoto omits "-1" 586 | sb append " }\n"; 587 | pw.append(sb.toString); 588 | } 589 | 590 | private def appendAcceptNT(sb : StringBuilder, nt : Nonterminal) : Unit = { 591 | /* YYGoto 592 | sb append "yygoto = YYNested(YYNested(YYBase(YYNT"+code(nt)+"("; 593 | if (nt.typed) sb.append("yyarg1"); 594 | else sb.append("()"); 595 | sb append "))));\n"; 596 | */ 597 | sb append "yynt = YYNT"+code(nt)+"("; 598 | if (nt.typed) { 599 | sb.append("yyarg1"); 600 | } 601 | sb append "); yygoto = 2;\n" 602 | } 603 | 604 | private def appendRuleCall(sb : StringBuilder, 605 | longest : Item, 606 | rule : Rule) = { 607 | // YYGOTO: This routine must be rewritten to use yynest. 608 | val rp = rule.getRecognitionPoint; 609 | var li : Int = 0; 610 | if (longest != null) li = longest.index; 611 | if (rule.lhs.typed) { 612 | sb append "yynt = " // No YYGoto 613 | sb append "YYNT" 614 | sb append code(rule.lhs) 615 | sb append "(yyrule"; 616 | sb append rule.number; 617 | sb append "("; 618 | appendActuals(sb,li-rp,rule.rhs.slice(0,rp)) 619 | sb append ")); "; 620 | } else { 621 | sb append "yyrule"; 622 | sb append rule.number; 623 | sb append "("; 624 | appendActuals(sb,li-rp,rule.rhs.slice(0,rp)) 625 | sb append "); yynt = YYNT"; 626 | sb append code(rule.lhs); 627 | sb append "(); "; 628 | } 629 | sb append "yygoto = " 630 | sb append rp; 631 | sb append "\n"; 632 | } 633 | 634 | private def appendGoto(sb : StringBuilder, longest : Item, target : State) = { 635 | val nextlongest = target.longestItem; 636 | var li : Int = 0; 637 | if (longest != null) li = longest.index; 638 | sb append "yygoto = yystate"; 639 | sb append target.number; 640 | sb append "("; 641 | if (nextlongest != null) { 642 | appendActuals(sb,li-nextlongest.index+1, 643 | nextlongest.rule.rhs.slice(0,nextlongest.index)); 644 | } else if (target.asInstanceOf[LeftCornerState].nonterminal.typed) { 645 | sb append "yyarg1"; 646 | } 647 | sb append ");\n"; 648 | } 649 | 650 | private def appendActuals(sb : StringBuilder, 651 | offset : Int, 652 | syms :scala.collection.Seq[Symbol]) : Boolean = { 653 | var nonempty : Boolean = false; 654 | var i : Int = offset; 655 | for (sym <- syms) { 656 | i += 1; 657 | if (sym.typed) { 658 | if (nonempty) { 659 | sb append ','; 660 | } else { 661 | nonempty = true 662 | } 663 | sb append "yyarg"; 664 | sb append i; 665 | } 666 | } 667 | nonempty; 668 | } 669 | 670 | private def writeLALRTable(pw : PrintWriter) = { 671 | pw.print(""" abstract class YYAction; 672 | case class YYAshift(state : Int) extends YYAction; 673 | case class YYAreduce(rule : Int, nt:YYNonterminal, size : Int) extends YYAction; 674 | case class YYAerror(reason : String) extends YYAction; 675 | case class YYAaccept() extends YYAction; 676 | 677 | private var table : Array[Function[CoolTokens.YYSymbol,YYAction]] = Array("""); 678 | var started : Boolean = false; 679 | for (state <- table.states) { 680 | if (started) pw.print(","); else started = true; 681 | pw.println("\n (x => x match {"); 682 | for ((t,action) <- state.actions) { 683 | pw.print(" case " + prefix + "Tokens."); 684 | t match { 685 | case CharLitTerminal(ch) => pw.print("YYCHAR(" + toLit(ch) + ")"); 686 | case _ => pw.print(t.name + (if (t.typed) "(_)" else "()")); 687 | } 688 | pw.print(" => "); 689 | writeAction(pw,action); 690 | pw.println(); 691 | } 692 | for ((nt,st) <- state.gotos) { 693 | val nts = 694 | if (nt.name.startsWith("@")) "(" + nt.name.substring(1) + ")" 695 | else if (nt.typed) nt.name + "(_)" 696 | else if (nt.name == "error") nt.name + "(_)"; 697 | else nt.name + "()"; 698 | pw.println(" case YYNT" + nts + " => YYAshift(" + st.number + ")"); 699 | } 700 | pw.print(" case _ => "); 701 | var defAction : Action = state.default; 702 | if (defAction == null) defAction = ErrorAction("parse error"); 703 | writeAction(pw,defAction); 704 | pw.print("})"); 705 | } 706 | pw.println(");\n"); 707 | pw.println(" val MAX_STATE = table.length-1;\n"); 708 | pw.println(" def getAction(state : Int, s : " + prefix + "Tokens.YYSymbol) : YYAction = table(state)(s);"); 709 | } 710 | 711 | private def writeAction(pw: PrintWriter, action: Action): Unit = { 712 | action match { 713 | case AcceptAction() => pw.print("YYAaccept()"); 714 | case ErrorAction(reason) => pw.print("YYAerror(\"" + reason + "\")"); 715 | case ShiftAction(target) => { 716 | val st = target.number; 717 | pw.print("YYAshift(" + st + ")"); 718 | } 719 | case ReduceAction(rule) => { 720 | val nt = rule.lhs; 721 | val nts = 722 | if (nt.name.startsWith("@")) "(" + nt.name.substring(1) + ")" 723 | else if (nt.getType() == "Boolean") nt.name + "(false)" 724 | else if (nt.getType() == "Int") nt.name + "(0)" 725 | else if (nt.typed) nt.name + "(null)" 726 | else nt.name + "()"; 727 | pw.print("YYAreduce(" + rule.number + ", YYNT" + nts + ", " + rule.rhs.length + ")"); 728 | } 729 | } 730 | } 731 | } 732 | 733 | object RunGenerator { 734 | def main(args : Array[String]) : Unit = { 735 | for (s <- args) { 736 | if (s == "-v" || s == "--verbose") { 737 | Options.verbose += 1; 738 | } else if (s == "-V" || s == "--version") { 739 | println("ScalaBison version " + Version.version); 740 | return; 741 | } else if (s == "-h" || s == "--help") { 742 | println("ScalaBison version " + Version.version); 743 | println("Generates LAXLC(1) parsers in Scala from bison input and output files."); 744 | println(); 745 | println("Usage: scala-bison [OPTION]... GRAMMARFILE...\n"); 746 | println("Options: "); 747 | println(" -v \n --verbose"); 748 | println("\tGenerate XXX.lcoutput file"); 749 | println(" -V \n --version"); 750 | println("\tPrint version and exit"); 751 | println(" -t \n --debug"); 752 | println("\tGenerate debugging code that can be turned on using yydebug."); 753 | println(" -h \n --help"); 754 | println("\tPrint this message, and exit."); 755 | } else if (s == "-t" || s == "--debug") { 756 | Options.debug = true; 757 | } else if (s == "-T" || s == "--meta-debug") { 758 | Options.meta_debug = true; 759 | } else if (s == "-d" || s == "--lalr-table") { 760 | Options.gen_lalr_table = true; 761 | } else if (s.startsWith("-")) { 762 | println("Unknown option " + s + "; use --help to get legal options"); 763 | System.exit(-1); 764 | } else { 765 | try { 766 | val scanner : BisonScanner = new BisonScanner(Source.fromFile(s)) 767 | val parser : BisonParser = new BisonParser(); 768 | val sep : Int = s.lastIndexOf(File.separatorChar); 769 | val filename = if (sep < 0) s; else s.substring(sep+1); 770 | val dot : Int = filename.lastIndexOf('.'); 771 | val prefix : String = 772 | if (dot < 0) filename; else filename.substring(0,dot); 773 | if (Options.meta_debug) { 774 | println("Parsing " + filename); 775 | } 776 | parser.reset(s,scanner); 777 | if (Options.meta_debug) { 778 | parser.yydebug = true; 779 | } 780 | if (parser.yyparse()) { 781 | // println(parser.result); 782 | val table : BisonTable = new BisonTable(parser.result); 783 | table.fromFile(prefix+".output"); 784 | // println(table); 785 | if (table.states.isEmpty) { 786 | println("Error: didn't find any states in the bison outfile file.\nPerhaps this is a bison version SCalaBison doesn't understand."); 787 | System.exit(-1); 788 | } 789 | val gen : Generator = new Generator(prefix,table); 790 | gen.writeFiles(); 791 | } else { 792 | println("Could not parse "+filename); 793 | System.exit(-1); 794 | } 795 | } catch { 796 | case e:java.io.IOException => { 797 | println("Problem with input/output files: " + e); 798 | System.exit(-1); 799 | } 800 | } 801 | } 802 | } 803 | } 804 | } 805 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Grammar.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.mutable.Map; 20 | import scala.collection.mutable.ArrayBuffer; 21 | import scala.collection.mutable.HashMap; 22 | 23 | class Grammar { 24 | protected val symbolmap : Map[String,Symbol] = new HashMap; 25 | protected val symbols : ArrayBuffer[Symbol] = new ArrayBuffer; 26 | 27 | protected var _start : Nonterminal = null; 28 | def start : Nonterminal = _start; 29 | 30 | val end : Terminal = new Terminal("YYEOF",""); 31 | 32 | def all_symbols : Iterable[Symbol] = symbols; 33 | 34 | def find(name : String) : Option[Symbol] = symbolmap.get(name); 35 | 36 | protected def add[T <: Symbol](symbol : T) : T = { 37 | if (symbolmap.contains(symbol.name)) { 38 | return symbolmap.apply(symbol.name).asInstanceOf[T]; 39 | } 40 | symbols += symbol; 41 | symbolmap.put(symbol.name,symbol); symbol 42 | } 43 | 44 | def get(name : String) : Symbol = { 45 | find(name) match { 46 | case Some(s) => s; 47 | case None => add(new Nonterminal(name,"")); 48 | } 49 | } 50 | 51 | // add(start); 52 | // add(epsilon); 53 | add(end); 54 | symbolmap.put("$end",end); // another name 55 | 56 | val rules : ArrayBuffer[Rule] = new ArrayBuffer[Rule](); 57 | 58 | override def toString : String = toString(false); 59 | 60 | def toString(showRecognition : Boolean) : String = { 61 | val sb : StringBuilder = new StringBuilder("Grammar\n"); 62 | var lastNT : Nonterminal = null; 63 | var i : Int = 0; 64 | for (rule <- rules) { 65 | val nt : Nonterminal = rule.lhs; 66 | if (nt != lastNT) sb append '\n'; 67 | if (i < 10000) sb append ' '; 68 | if (i < 1000) sb append ' '; 69 | if (i < 100) sb append ' '; 70 | if (i < 10) sb append ' '; 71 | sb append i; 72 | i += 1; 73 | sb append ' '; 74 | if (nt == lastNT) { 75 | for (_ <- (0 until nt.name.length)) { 76 | sb append ' '; 77 | } 78 | sb append '|'; 79 | } else { 80 | sb append (nt.name); 81 | sb append ':'; 82 | lastNT = nt; 83 | } 84 | if (rule.rhs == Nil) { 85 | if (showRecognition) sb append " ."; 86 | sb append " /* empty */"; 87 | } else { 88 | var rp : Int = rule.recognitionPoint; 89 | for (sym <- rule.rhs) { 90 | if (showRecognition && rp == 0) { sb append " ."; } 91 | rp -= 1 92 | sb append ' '; 93 | sb append (sym.name); 94 | } 95 | if (showRecognition && rp == 0) { sb append " ."; } 96 | } 97 | sb append '\n'; 98 | } 99 | sb.toString 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/GrammarSpecificationError.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | class GrammarSpecificationError(s : String) extends Exception(s) { } 20 | 21 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Item.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.immutable.Set; 20 | 21 | class Item(val rule : Rule, val index : Int) { 22 | 23 | def symbol : Symbol = { 24 | if (index == rule.length) null; 25 | else rule.rhs(index); 26 | } 27 | 28 | def next() : Item = new Item(rule,index+1); 29 | 30 | /** Ready for recognition */ 31 | def ready : Boolean = index == rule.getRecognitionPoint(); 32 | 33 | override def equals(o : Any) : Boolean = { 34 | o match { 35 | case i:Item => rule == i.rule && index == i.index; 36 | case _ => false 37 | } 38 | } 39 | 40 | override def hashCode() : Int = rule.hashCode() + index; 41 | 42 | override def toString : String = { 43 | val sb : StringBuilder = new StringBuilder( rule.lhs.name ); 44 | sb append ":"; 45 | var i : Int = 0; 46 | for (symbol <- rule.rhs) { 47 | if (index == i) { 48 | sb append " ."; 49 | } 50 | sb append ' ' 51 | sb append symbol.name 52 | i += 1 53 | } 54 | if (index == i) { 55 | sb append " ."; 56 | } 57 | sb.toString 58 | } 59 | 60 | } 61 | 62 | object Item { 63 | def fromLine(grammar : Grammar, line : String) : Item = { 64 | val rulenum : Int = Integer.parseInt(line.substring(0,5).trim); 65 | val rule : Rule = grammar.rules(rulenum); 66 | val itemtext : String = line.substring(6).trim; 67 | val parts : Array[String] = itemtext.split(" "); 68 | if (!parts(0).equals("|") && !parts(0).equals(rule.lhs.name + ":")) { 69 | throw new GrammarSpecificationError("Expected rule " + rulenum + 70 | " to have lhs = " + parts(0) + 71 | " not " + rule.lhs.name); 72 | } 73 | for (i <- 1 until (parts.length)) { 74 | if (parts(i).equals(".") || parts(i).equals("•")) { 75 | val item : Item = new Item(rule,i-1); 76 | /* 77 | if (!itemtext.equals(item.toString())) { 78 | throw new GrammarSpecificationError("sanity check failed: " + 79 | item + " != " + itemtext); 80 | } 81 | */ 82 | return item 83 | } 84 | } 85 | throw new GrammarSpecificationError("Couldn't find . in " + itemtext); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/LeftCornerFollow.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.Set; 20 | import scala.collection.mutable.{HashMap}; 21 | import scala.collection.immutable.ListSet; 22 | 23 | import scala.io.Source; 24 | 25 | /** 26 | * A restriction of the Follow Set that 27 | * applies to left corner parsing: 28 | * this is the set of tokens that can follow a NT 29 | * during town-down parsing. 30 | * 31 | * This code is copied almost verbatim from Follow.scala. 32 | */ 33 | class LeftCornerFollow(val grammar : Grammar, 34 | val first : First, val follow : Follow) 35 | { 36 | private val ntfol : HashMap[Nonterminal,scala.collection.immutable.Set[Terminal]] = new HashMap; 37 | 38 | { // initialize the follow set 39 | for (sym <- grammar.all_symbols) { 40 | sym match { 41 | case nt:Nonterminal => 42 | ntfol.put(nt,scala.collection.immutable.Set.empty); 43 | case _ => () 44 | } 45 | } 46 | } 47 | 48 | { 49 | for (rule <- grammar.rules) { 50 | val rp : Int = rule.getRecognitionPoint(); 51 | setFollow(rule.rhs.drop(rp),follow(rule.lhs)); 52 | } 53 | 54 | /*for ((nt,fs) <- ntfol) { 55 | println("FFollow(" + nt + ") = " + fs); 56 | }*/ 57 | } 58 | 59 | private def setFollow(l : List[Symbol], fol : Set[Terminal]) : Unit = { 60 | /*println("Calling setFollow(" + l + "," + fol + ")");*/ 61 | l match { 62 | case (nt:Nonterminal)::t => { 63 | val oldFol : scala.collection.immutable.Set[Terminal] = ntfol(nt); 64 | val newFol : Set[Terminal] = computeFollow(t,fol); 65 | if (!(newFol subsetOf oldFol)) { 66 | ntfol.put(nt,oldFol ++ newFol); 67 | } 68 | } 69 | case _ => () 70 | } 71 | l match { 72 | case Nil => (); 73 | case _::t => setFollow(t,fol); 74 | } 75 | } 76 | 77 | def apply(item : Item) : Set[Terminal] = { 78 | computeFollow(item.rule.rhs.drop(item.index),follow(item.rule.lhs)) 79 | } 80 | 81 | private def computeFollow(t : List[Symbol], fol : Set[Terminal]) : Set[Terminal] = { 82 | /*println("Calling computeFollow(" + t + "," + fol + ")");*/ 83 | (first(t) dot new FirstSet(scala.collection.immutable.Set.empty[Terminal]++fol,false)).set 84 | } 85 | 86 | def apply(nt : Nonterminal) : Set[Terminal] = ntfol(nt); 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/LeftCornerState.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.Set; 20 | import scala.collection.immutable.ListSet; 21 | import scala.collection.mutable.HashMap; 22 | import scala.collection.mutable.ArrayBuffer; 23 | import scala.collection.mutable.HashSet; 24 | 25 | import scala.io.Source; 26 | 27 | /** 28 | * A state used for left corner parsing. We build the states 29 | * from existing LALR(1) states, and these are passed to the constructor. 30 | * This new state will have a subset of the items and a modified 31 | * subset of the actions -- some of the shift actions and all of 32 | * the ReduceActions are converted into AnnounceActions. 33 | *

34 | * Since we are building our states from existing LALR(1) states, 35 | * we distinguish three kinds of states:

    36 | *
  1. A state with the implicit item S -> |- . NT (empty core) 37 | *
  2. A state with the implicit item S -> |- NT . 38 | *
  3. A state with no implicit items. 39 | */ 40 | class LeftCornerState(n : Int, 41 | val nonterminal : Nonterminal, 42 | val atStart : Boolean, 43 | val state : State, 44 | c : Set[Item]) extends State(n,c) 45 | { 46 | def this(n : Int, nt : Nonterminal, st : State) = this(n,nt,true,st,Set.empty); 47 | def this(n : Int, st : State, c : Set[Item]) = this(n,null,false,st,c); 48 | 49 | override def hashCode() : Int = { 50 | if (nonterminal == null) c.hashCode(); 51 | else nonterminal.hashCode() + atStart.hashCode() + c.hashCode(); 52 | } 53 | override def equals(a : Any) : Boolean = { 54 | a match { 55 | case o : LeftCornerState => 56 | (nonterminal == o.nonterminal && 57 | atStart == o.atStart && 58 | core.equals(o.core)) 59 | case _ => false 60 | } 61 | } 62 | 63 | override def appendHeader(sb : StringBuilder) : Unit = { 64 | sb append "state "; 65 | sb append number 66 | sb append " ( based on state " 67 | sb append state.number 68 | sb append " )\n\n" 69 | if (nonterminal != null) { 70 | sb append " _: \u22a2 "; 71 | if (atStart) { sb append ". "; } 72 | sb append nonterminal.name 73 | if (!atStart) { sb append " ."; } 74 | sb append "\n"; 75 | } 76 | } 77 | 78 | override protected def complete() : Set[Item] = { 79 | /* 80 | println("Completing LC state " + number + ":"); 81 | if (nonterminal != null) { 82 | println(" _: |- . " + nonterminal.name); 83 | } 84 | for (item <- core) { 85 | println(" " + item); 86 | } 87 | println(); 88 | */ 89 | var completion : HashSet[Item] = new HashSet; 90 | completion ++= core; 91 | var addedNT : ListSet[Nonterminal] = ListSet.empty; 92 | if (nonterminal != null && atStart) { 93 | // force the fake rule: 94 | addedNT += nonterminal; 95 | for (rule <- nonterminal.rules) { 96 | val nitem : Item = new Item(rule,0); 97 | // println("Adding implicit item " + nitem); 98 | completion += nitem; 99 | } 100 | } 101 | var added : List[Item] = Nil ++ completion; 102 | while (added.length > 0) { 103 | val temp : List[Item] = added; 104 | added = Nil 105 | for (item <- temp) { 106 | if (!item.ready) { 107 | // not yet reached recognition point 108 | item.symbol match { 109 | case nt:Nonterminal => 110 | if (!(addedNT contains nt)) { 111 | addedNT += nt; 112 | for (rule <- nt.rules) { 113 | val nitem : Item = new Item(rule,0); 114 | // println("Adding item " + nitem); 115 | completion += nitem; 116 | added ::= nitem; 117 | } 118 | } 119 | case _ => () 120 | } 121 | } 122 | } 123 | } 124 | completion 125 | } 126 | 127 | private var inited : Boolean = false; 128 | 129 | def init(first : First, lcfollow : LeftCornerFollow, 130 | states : HashSet[LeftCornerState]) : Unit = { 131 | if (inited) return; 132 | inited = true; 133 | val ignored = completion; 134 | //println("Initializing " + this.toString(true)); 135 | 136 | val gotoItems = new HashMap[Symbol,ListSet[Item]](); 137 | val recogActions : HashMap[Terminal,Rule] = new HashMap[Terminal,Rule]; 138 | 139 | // force the artificial state transition 140 | if (nonterminal != null && atStart) { 141 | gotoItems.put(nonterminal,ListSet.empty); 142 | } 143 | for (item <- completion) { 144 | if (item.ready) { 145 | for (t <- first(item).set) { 146 | recogActions.put(t,item.rule); 147 | } 148 | } else { 149 | gotoItems.get(item.symbol) match { 150 | case None => gotoItems.put(item.symbol,ListSet.empty + item.next()); 151 | case Some(s) => gotoItems.put(item.symbol,s + item.next()); 152 | } 153 | } 154 | } 155 | 156 | for ((token,action) <- state.actions) { 157 | action match { 158 | case ReduceAction(r) => { 159 | if (completion contains new Item(r,r.length)) { 160 | if (r.recognitionPoint != r.length) { 161 | throw new GrammarSpecificationError("assertion error: " + r); 162 | } 163 | putAction(token,AnnounceAction(r)); 164 | } else { 165 | val rule : Rule = getAnnouncedRule(r.lhs); 166 | if (rule != null) 167 | putAction(token,AnnounceAction(rule)); 168 | } 169 | } 170 | case ShiftAction(state) => { 171 | // only if this shift is relevant, do we use it 172 | if (gotoItems isDefinedAt token) { 173 | val newState = makeState(first,lcfollow,states,state,gotoItems(token)); 174 | putAction(token,new ShiftAction(newState)); 175 | } else { 176 | val rule : Rule = getAnnouncedRule(token); 177 | 178 | // println("On token " + token + ", action is " + action + ", rule = " + rule); 179 | if (rule != null) { 180 | putAction(token,AnnounceAction(rule)); 181 | } else if (nonterminal != null && !atStart) { 182 | putAction(token,AcceptNTAction(nonterminal)); 183 | } 184 | } 185 | } 186 | case ErrorAction(s) => { 187 | putAction(token,action) 188 | } 189 | } 190 | } 191 | state.default match { 192 | case null => { } 193 | case ReduceAction(r) => { 194 | if (completion contains new Item(r,r.length)) { 195 | if (r.recognitionPoint != r.length) { 196 | throw new GrammarSpecificationError("assertion error: " + r); 197 | } 198 | default = AnnounceAction(r); 199 | } else { 200 | val rule : Rule = getAnnouncedRule(r.lhs); 201 | if (rule != null) 202 | default = AnnounceAction(rule); 203 | } 204 | } 205 | } 206 | 207 | if (nonterminal != null && atStart) { 208 | var special : Action = null; 209 | for (rule <- nonterminal.rules) { 210 | if (first(rule).incEpsilon) { 211 | if (special == null) 212 | special = AnnounceAction(rule); 213 | else 214 | println("Warning: announce conflict in state " + number); 215 | } 216 | } 217 | if (special != null) { 218 | //println("Generated announce action " + special); 219 | val sp = special; 220 | for (token <- lcfollow(nonterminal)) { 221 | actions.get(token) match { 222 | case Some(`sp`) => 223 | // everything is fine 224 | case Some(_) => 225 | println("Warning: announce conflict in state " + number + " on " 226 | + token) 227 | case None => 228 | putAction(token,special) 229 | } 230 | } 231 | } 232 | } 233 | 234 | if (nonterminal != null && !atStart) { 235 | val special = AcceptNTAction(nonterminal) 236 | for (token <- lcfollow(nonterminal)) { 237 | actions.get(token) match { 238 | case Some(AcceptNTAction(`nonterminal`)) => 239 | // everything is fine 240 | case Some(x) => 241 | println("Warning: accept conflict in state " + number + " on " 242 | + token + ", conflicts with " + x) 243 | case None => 244 | putAction(token,special) 245 | } 246 | } 247 | } 248 | 249 | for ((nt,state) <- state.gotos) { 250 | if (gotoItems isDefinedAt nt) { 251 | var next : LeftCornerState = null; 252 | if (nonterminal == nt && atStart) { 253 | next = cacheState(first,lcfollow,states, 254 | new LeftCornerState(states.size, 255 | nonterminal,false,state, 256 | gotoItems(nt))); 257 | } else { 258 | next = makeState(first,lcfollow,states,state,gotoItems(nt)); 259 | } 260 | putGoto(nt,next); 261 | } 262 | } 263 | } 264 | 265 | private var announcedRule : HashMap[Symbol,Rule] = new HashMap[Symbol,Rule]; 266 | // private var debuglevel : Int = 0; 267 | 268 | protected def getAnnouncedRule(sym : Symbol) : Rule = { 269 | /*if (debuglevel == 0) { 270 | print("st. " + debuglevel); 271 | } 272 | print("\t"); 273 | for (i <- 0 upto debuglevel) { 274 | print(" "); 275 | } 276 | print("announced(" + sym + ") = ");*/ 277 | announcedRule.get(sym) match { 278 | case Some(r) => return r; 279 | case None => announcedRule.put(sym,null); 280 | } 281 | for (item <- completion) { 282 | if (item.symbol == sym) { 283 | if (item.ready) { 284 | //println("inferred ready rule for " + sym + " is " + item.rule); 285 | announcedRule.put(sym,item.rule); 286 | return item.rule; 287 | } 288 | } 289 | } 290 | //println("no ready rules"); 291 | var rule : Rule = null; 292 | for (item <- state.completion) { 293 | if (item.index == 0 && item.symbol == sym) { 294 | //println("Found extra rule that can handle " + sym + ": " + item); 295 | val xrule : Rule = getAnnouncedRule(item.rule.lhs); 296 | if (rule == null) 297 | rule = xrule; 298 | else if (xrule != null && xrule != rule) 299 | throw new AssertionError("different: " + rule + " != " + xrule + 300 | " for " + sym + " in " + this.toString(true)); 301 | } 302 | } 303 | announcedRule.put(sym,rule); 304 | rule 305 | } 306 | 307 | protected def makeState(first : First, lcfollow : LeftCornerFollow, 308 | states : HashSet[LeftCornerState], 309 | state : State, 310 | newcore : Set[Item]) 311 | : LeftCornerState = { 312 | cacheState(first,lcfollow, 313 | states,new LeftCornerState(states.size,state,newcore)); 314 | } 315 | 316 | protected def cacheState(first : First, lcfollow : LeftCornerFollow, 317 | states : HashSet[LeftCornerState], 318 | newState : LeftCornerState) : LeftCornerState = { 319 | states.find(_ == newState) match { 320 | case Some(s) => s; 321 | case None => { 322 | states += newState; 323 | newState.init(first,lcfollow,states); 324 | newState 325 | } 326 | } 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/LeftCornerTable.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.mutable.HashSet; 20 | 21 | import scala.collection.Set; 22 | import scala.collection.mutable.{HashMap,ArrayBuffer}; 23 | import scala.io.Source; 24 | 25 | /** 26 | * LeftCorner parser tables built from LALR(1) Parse tables 27 | */ 28 | class LeftCornerTable(val table : Table) { 29 | protected val _states : HashSet[LeftCornerState] = new HashSet; 30 | def states : Set[LeftCornerState] = _states; 31 | private var _startState : LeftCornerState = null; 32 | def startState : LeftCornerState = _startState; 33 | 34 | val NTstate : HashMap[Nonterminal,LeftCornerState] = new HashMap; 35 | 36 | def grammar : Grammar = table.grammar; 37 | 38 | override def toString : String = toString(true); 39 | 40 | def toString(showAll : Boolean) : String = { 41 | val sb : StringBuilder = new StringBuilder(); 42 | val a : Array[LeftCornerState] = new Array(states.size); 43 | for (state <- states) { 44 | a(state.number) = state; 45 | } 46 | for (state <- a) { 47 | sb append (state.toString(showAll)) 48 | } 49 | sb.toString 50 | } 51 | 52 | val first : First = new First(grammar); 53 | val follow : Follow = new Follow(grammar,first); 54 | 55 | { 56 | table.setRecognitionPoints(); 57 | } 58 | 59 | val lcfollow : LeftCornerFollow = new LeftCornerFollow(grammar,first,follow); 60 | // compute the states: 61 | { 62 | // get the start state 63 | _startState = new LeftCornerState(0,grammar.start,table.states(0)); 64 | _states += startState; 65 | startState.init(first,lcfollow,_states); 66 | NTstate.put(grammar.start,startState); 67 | // for all rules, make sure we have NT states: 68 | for (rule <- grammar.rules) { 69 | var n : Int = rule.recognitionPoint; // NB: n is changed in loop 70 | for (symbol <- rule.rhs.drop(n)) { 71 | symbol match { 72 | case _:ArtificialNonterminal => () 73 | case nt:Nonterminal => { 74 | if (!(NTstate isDefinedAt nt)) { 75 | NTstate.put(nt,makeNTstate(nt,new Item(rule,n))); 76 | } 77 | } 78 | case _ => () 79 | } 80 | n += 1; 81 | } 82 | } 83 | for (symbol <- grammar.all_symbols) { 84 | symbol match { 85 | case _:ArtificialNonterminal => () 86 | case nt:Nonterminal => { 87 | if (Options.verbose > 1 && !(NTstate isDefinedAt nt)) { 88 | println("Warning: Nonterminal " + nt.name + 89 | " never used in unambiguous context."); 90 | } 91 | } 92 | case _ => () 93 | } 94 | } 95 | } 96 | 97 | protected def makeNTstate(nt : Nonterminal, item : Item) : LeftCornerState = { 98 | // println("Building NT state for " + nt.name); 99 | for (state <- table.states) { 100 | if (state.completion contains item) { 101 | // println("Found state " + state.number + " to include " + item); 102 | val newState : LeftCornerState = 103 | new LeftCornerState(_states.size,nt,state); 104 | _states += (newState); 105 | newState.init(first,lcfollow,_states); 106 | return newState; 107 | } 108 | } 109 | throw new GrammarSpecificationError("No state found for item " + item); 110 | } 111 | } 112 | 113 | object LeftCornerTable { 114 | def main(args : Array[String]) = { 115 | for (s <- args) { 116 | val scanner : BisonScanner = new BisonScanner(Source.fromFile(s+".y")) 117 | val parser : BisonParser = new BisonParser(); 118 | parser.yydebug = true; 119 | parser.reset(s+".y",scanner); 120 | if (parser.yyparse()) { 121 | println(parser.result); 122 | val table : BisonTable = new BisonTable(parser.result); 123 | table.fromFile(s+".output"); 124 | val table2 : LeftCornerTable = new LeftCornerTable(table); 125 | println(table2); 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/LeftPrecedence.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | class LeftPrecedence(l : Int) extends Precedence(l) {} 20 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/LiteralCode.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class LiteralCode(s : String) extends Code { 20 | override def toString : String = s; 21 | } 22 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/NoCode.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class NoCode() extends Code { 20 | override def toString : String = "" 21 | } 22 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/NonAssocPrecedence.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | class NonAssocPrecedence(l : Int) extends Precedence(l) {} 20 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Nonterminal.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.Set; 20 | import scala.collection.immutable.ListSet; 21 | import scala.collection.mutable.{ArrayBuffer,HashSet}; 22 | 23 | class Nonterminal(s : String, t : String) extends Symbol(s,t) { 24 | val rules : ArrayBuffer[Rule] = new ArrayBuffer(); 25 | 26 | def addRule(r : Rule) : Unit = rules += r; 27 | } 28 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Options.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | object Options { 20 | var verbose : Int = 0; 21 | var debug : Boolean = false; 22 | var meta_debug : Boolean = false; 23 | var gen_lalr_table : Boolean = false; 24 | } 25 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Precedence.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | abstract class Precedence(val level : Int) {} 20 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/ReduceAction.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class ReduceAction(val rule : Rule) extends Action { 20 | override def toString = "reduce using rule " + rule 21 | } 22 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/RightPrecedence.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | class RightPrecedence(l : Int) extends Precedence(l) {} 20 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Rule.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.Set; 20 | 21 | class Rule(val number : Int, val lhs : Nonterminal, val rhs : List[Symbol], 22 | val precedence : Precedence, val action : Code) 23 | { 24 | { 25 | lhs.addRule(this); 26 | } 27 | 28 | def this(number : Int, lhs:Nonterminal, rhs:List[Symbol], 29 | precedence : Precedence, action:Code, 30 | ignored : Boolean) = this(number,lhs,rhs.reverse,precedence,action); 31 | 32 | def length : Int = rhs.length; 33 | 34 | var recognitionPoint : Int = rhs.length; 35 | 36 | def getRecognitionPoint() = recognitionPoint; 37 | 38 | def setRecognitionPoint(nonfree : Set[Item]) : Unit = { 39 | recognitionPoint = 0; // optimistic 40 | for (i <- 0 until length) { 41 | if (precedence != null || // precedence forces using LALR parsing 42 | (nonfree contains new Item(this,i)) || 43 | (rhs(i) == ErrorNonterminal())) { 44 | recognitionPoint = i+1; // oops! must be later than we hoped! 45 | } 46 | } 47 | // println("Recognition Point is " + new Item(this,recognitionPoint)); 48 | } 49 | 50 | override def toString = { 51 | val sb : StringBuffer = new StringBuffer(lhs.name); 52 | sb.append(':'); 53 | if (rhs.isEmpty) sb.append(" /* empty */"); 54 | else { 55 | for (sym <- rhs) { 56 | sb.append(' '); 57 | sb.append(sym.name); 58 | } 59 | } 60 | sb.toString 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/ShiftAction.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class ShiftAction(target : State) extends Action { 20 | override def toString = "shift, and go to " + (target.number) 21 | } 22 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/State.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.mutable.Map 20 | import scala.collection.Set 21 | import scala.collection.immutable.ListSet 22 | import scala.collection.mutable.HashMap 23 | import scala.collection.mutable.HashSet 24 | import scala.collection.mutable.ArrayBuffer 25 | import edu.uwm.cs.util.Dominator 26 | import edu.uwm.cs.util.Reach 27 | 28 | 29 | class State(val number : Int, val core : Set[Item]) { 30 | // Scala 2.7 dosn't permit super.x any more for some unspecified reason, 31 | // so I have to fake the getter as a def. 32 | private val _actions : Map[Terminal,Action] = new HashMap(); 33 | def actions : scala.collection.Map[Terminal,Action] = _actions; 34 | def putAction(t : Terminal, a : Action) = _actions.put(t,a); 35 | 36 | private val _gotos : Map[Nonterminal,State] = new HashMap(); 37 | def gotos : scala.collection.Map[Nonterminal,State] = _gotos; 38 | def putGoto(n : Nonterminal, s : State) = _gotos.put(n,s); 39 | 40 | private var _default : Action = null; 41 | def default : Action = _default; 42 | def default_=(a : Action) : Unit = { 43 | _default = a; 44 | } 45 | 46 | override def equals(a : Any) : Boolean = { 47 | a match { 48 | case o:State => core.equals(o.core); 49 | case _ => false 50 | } 51 | } 52 | 53 | override def hashCode() : Int = core.hashCode(); 54 | 55 | private var cachedCompletion : Set[Item] = null; 56 | def completion : Set[Item] = { 57 | if (cachedCompletion == null) { 58 | cachedCompletion = complete(); 59 | } 60 | cachedCompletion 61 | } 62 | 63 | protected def complete() : Set[Item] = { 64 | /* 65 | println("Completing state " + number + ":"); 66 | for (item <- core) { 67 | println(" " + item); 68 | } 69 | println(); 70 | */ 71 | var completion : HashSet[Item] = new HashSet; 72 | completion ++= core; 73 | var addedNT : ListSet[Nonterminal] = ListSet.empty; 74 | var added : List[Item] = Nil ++ completion; 75 | while (added.length > 0) { 76 | val temp : List[Item] = added; 77 | added = Nil 78 | for (item <- temp) { 79 | item.symbol match { 80 | case nt:Nonterminal => 81 | if (!(addedNT contains nt)) { 82 | addedNT += nt; 83 | for (rule <- nt.rules) { 84 | val nitem : Item = new Item(rule,0); 85 | // println("Adding item " + nitem); 86 | completion += nitem; 87 | added ::= nitem; 88 | } 89 | } 90 | case _ => () 91 | } 92 | } 93 | } 94 | completion 95 | } 96 | 97 | def addNonFree(nonfree : scala.collection.mutable.Set[Item]) : Unit = { 98 | // create a graph 99 | // 1. first the nodes 100 | val actionset : HashSet[Action] = new HashSet(); 101 | val nodes : ArrayBuffer[Any] = new ArrayBuffer(); 102 | val index : Map[Any,Int] = new HashMap(); 103 | nodes += (null); 104 | nodes += (null); 105 | for (item <- completion) { 106 | val n : Int = nodes.size; 107 | nodes += (item); 108 | index.put(item,n); 109 | } 110 | val firstAction : Int = nodes.size; 111 | for ((_,action) <- actions) { 112 | actionset += action; 113 | } 114 | if (default != null) actionset += default; 115 | for (action <- actionset) { 116 | val n : Int = nodes.size; 117 | nodes += (action); 118 | index.put(action,n); 119 | } 120 | // 2. then the successor relation 121 | val succ : PartialFunction[Int,scala.collection.Set[Int]] = { 122 | i:Int => i match { 123 | case 1 => { 124 | var s : ListSet[Int] = ListSet.empty; 125 | for (item <- core) { 126 | s = s + index(item) 127 | } 128 | s 129 | } 130 | case _ => 131 | nodes(i) match { 132 | case _:Action => ListSet.empty 133 | case item:Item => { 134 | item.symbol match { 135 | case t:Terminal => { 136 | actions.get(t) match { 137 | case Some(a:ShiftAction) => ListSet.empty + index(a); 138 | case Some(a:ErrorAction) => ListSet.empty + index(a); 139 | case _ => ListSet.empty 140 | } 141 | } 142 | case nt:Nonterminal => { 143 | var s : ListSet[Int] = ListSet.empty; 144 | for (rule <- nt.rules) { 145 | s = s + index(new Item(rule,0)); 146 | } 147 | s 148 | } 149 | case null => { 150 | val red : ReduceAction = ReduceAction(item.rule); 151 | if (actionset contains red) 152 | ListSet.empty + index(red) 153 | else if (_default == AcceptAction()) 154 | ListSet.empty + index(AcceptAction()) 155 | else 156 | ListSet.empty 157 | } 158 | } 159 | } 160 | } 161 | } 162 | }; 163 | if (Options.meta_debug) { 164 | println("Graph of state (for computing free positions)"); 165 | for (i <- 1 until nodes.size) { 166 | print(" " + i + ": "); 167 | nodes(i) match { 168 | case null => println("[INIT]") 169 | case x => println(x); 170 | } 171 | print(" ->"); 172 | for (j <- succ(i)) { 173 | print(" " + j); 174 | } 175 | println(); 176 | } 177 | } 178 | // 3. now compute reachability and domination: 179 | val reach : Reach = new Reach(nodes.size-1,succ); 180 | val dominator : Dominator = new Dominator(nodes.size-1,succ,1); 181 | // 4. now for every item, check that it dosn't reach itself: 182 | for (item <- completion) { 183 | val i : Int = index(item); 184 | if (reach(i) contains i) { 185 | if (Options.verbose > 1) { 186 | println("Item is forbidden: " + item); 187 | } 188 | nonfree += item; 189 | } 190 | } 191 | // 5. check that every item dominates every action it can reach: 192 | for (item <- completion) { 193 | val i : Int = index(item); 194 | for (action <- actionset) { 195 | val a : Int = index(action); 196 | if ((reach(i) contains a) && !dominator.dominates(i,a)) { 197 | if (Options.verbose > 1) { 198 | println("Item is dependent: " + item); 199 | } 200 | nonfree += item; 201 | } 202 | } 203 | } 204 | } 205 | 206 | def longestItem : Item = { 207 | var longest : Item = null; 208 | for (item <- core) { 209 | if (longest == null || item.index > longest.index) { 210 | longest = item; 211 | } 212 | } 213 | longest 214 | } 215 | 216 | /** A rough-and-ready way to say the state doesn't need 217 | * to be implemented in a Left-corner parser. Even 218 | * if it is not past recognition, it may still be useless, 219 | * but it's harder to detect. 220 | */ 221 | def pastRecognition : Boolean = { 222 | for (item <- core) { 223 | if (item.index > item.rule.recognitionPoint) return true; 224 | } 225 | return false; 226 | } 227 | 228 | protected def appendHeader(sb : StringBuilder) : Unit = { 229 | sb append "state "; 230 | sb append number 231 | sb append '\n' 232 | sb append '\n' 233 | } 234 | 235 | override def toString : String = toString(true); 236 | 237 | def toString(showAll : Boolean) : String = { 238 | val sb : StringBuilder = new StringBuilder(); 239 | appendHeader(sb) 240 | for (item <- (showAll match { case true => completion; 241 | case false => core})) { 242 | sb append " " 243 | sb append item 244 | sb append '\n' 245 | } 246 | sb append '\n' 247 | for ((token,action) <- actions) { 248 | sb append " " 249 | sb append (token.name) 250 | sb append '\t' 251 | sb append action 252 | sb append '\n' 253 | } 254 | if (default != null) { 255 | sb append " $default\t" 256 | sb append default 257 | sb append '\n' 258 | } 259 | sb append '\n' 260 | for ((nt,state) <- gotos) { 261 | sb append " " 262 | sb append (nt.name) 263 | sb append "\tgo to state " 264 | sb append (state.number) 265 | sb append '\n' 266 | } 267 | if (gotos.size > 0) { 268 | sb append '\n' 269 | } 270 | sb append '\n' 271 | sb.toString 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Symbol.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.Set; 20 | 21 | abstract class Symbol(val name : String, val resultType : String) { 22 | def getType() : String = if (typed) resultType else "Unit"; 23 | def typed : Boolean = (!resultType.equals("")) 24 | var precedence : Precedence = null; 25 | def setPrecedence(p : Precedence) = { 26 | if (precedence == null) { 27 | precedence = p; 28 | } else { 29 | throw new GrammarSpecificationError("precedence for " + name + " already specified"); 30 | } 31 | } 32 | 33 | override def toString = name; 34 | } 35 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Table.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.mutable.ArrayBuffer; 20 | import scala.collection.Set; 21 | import scala.collection.mutable.HashSet; 22 | 23 | /** 24 | * LALR(1) Parse tables 25 | */ 26 | class Table(val grammar : Grammar) { 27 | val states : ArrayBuffer[State] = new ArrayBuffer; 28 | 29 | override def toString : String = toString(true); 30 | 31 | def toString(showAll : Boolean) : String = { 32 | val sb : StringBuffer = new StringBuffer(); 33 | for (state <- states) { 34 | sb append (state.toString(showAll)) 35 | } 36 | sb.toString 37 | } 38 | 39 | private var nonfree : HashSet[Item] = null; 40 | 41 | def getNonFree() : Set[Item] = { 42 | if (nonfree != null) return nonfree; 43 | nonfree = new HashSet; 44 | for (state <- states) { 45 | // println("Getting nonfree for " + state); 46 | state.addNonFree(nonfree); 47 | } 48 | nonfree 49 | } 50 | 51 | def setRecognitionPoints() : Unit = { 52 | val nonfree : Set[Item] = getNonFree(); 53 | for (rule <- grammar.rules) { 54 | rule.setRecognitionPoint(nonfree); 55 | } 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Terminal.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | import scala.collection.immutable.ListSet; 20 | import scala.collection.Set; 21 | 22 | class Terminal(s : String, t : String) extends Symbol(s,t) { 23 | } 24 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/VariableCode.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 University of Wisconsin, Milwaukee 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package edu.uwm.cs.scalabison; 18 | 19 | case class VariableCode(i : Int) extends Code { 20 | override def toString : String = { 21 | if (i == -1) "yyresult"; 22 | else "yyarg" + i; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/scalabison/Version.scala: -------------------------------------------------------------------------------- 1 | package edu.uwm.cs.scalabison; 2 | 3 | object Version { 4 | val version : String = "1.1"; 5 | } 6 | 7 | // History: 8 | // 0.76: More debugging information on errors 9 | // 0.77: Catch ALL exceptions, not just in shifts 10 | // 0.78: Don't add any default actions. 11 | // 0.79: use simple name of file, defaultInitial not Integer 12 | // 0.791: don't try recovery when at EOF 13 | // 0.792: Bug introduced in 0.78: need to handle followset too 14 | // 0.793: Fixed announce conflicts & shadowing for accept actions 15 | // 0.794: Fixed bug (identifiers could not include digits) 16 | // 0.8: Updated for Scala 2.9.1, re-bootstrapped 17 | // 0.81: No longer generates "Unit" values for untyped nonterminals (caused warnings) 18 | // 0.82: More support for '\x' terminals, primitive type nonterminals 19 | // 0.83: added support for bison 2.6.2, re-bootstrapped 20 | // 0.84: Updated for Scala 2.10 21 | // 0.99: Updated for Scala 2.11 and Release candidate 1 22 | // 1.0: Handle conflicts in grammars 23 | // 1.1: Added back "-d" option from 2010 (used for CS 854 at UWM). 24 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/util/CharUtil.scala: -------------------------------------------------------------------------------- 1 | package edu.uwm.cs.util 2 | 3 | object CharUtil { 4 | def lit(ch : Char) : String = ch match { 5 | case '\'' => "'\\''"; 6 | case '\n' => "'\\n'"; 7 | case '\t' => "'\\t'"; 8 | case '\f' => "'\\f'"; 9 | case '\r' => "'\\r'"; 10 | case '\b' => "'\\b'"; 11 | case 0 => "'\\0'"; 12 | case _ => { 13 | if (ch < 32 || ch > 126) String.format("'\\u%04x'",new java.lang.Integer(ch)) else "'" + ch + "'"; 14 | } 15 | } 16 | 17 | def main(args : Array[String]) = { println ("lit('\u00fc') = "+lit('\u00fc')); } 18 | } -------------------------------------------------------------------------------- /src/edu/uwm/cs/util/Cycle.scala: -------------------------------------------------------------------------------- 1 | /* Determining which nodes are in cycles in a graph. 2 | * John Boyland 3 | * This file may be used, copied and/or modified for any purpose. 4 | */ 5 | package edu.uwm.cs.util; 6 | 7 | import scala.collection.immutable.Set; 8 | import scala.collection.immutable.ListSet; 9 | import scala.collection.mutable.BitSet; 10 | 11 | /** 12 | * Cycle computation of graph represented by vertices 1..max 13 | * (Not 0 .. (max-1) !). The result returns whether the given node 14 | * is involved in a cycle. 15 | */ 16 | class Cycle(max : Int, val succ : PartialFunction[Int,Set[Int]]) 17 | extends PartialFunction[Int,Boolean] 18 | { 19 | private val reachable : Array[BitSet] = new Array(max+1); 20 | 21 | { 22 | for (i <- 1 to max) { 23 | reachable(i) = new BitSet(max+1); 24 | for (j <- succ(i)) { 25 | reachable(i) += j 26 | if (j < i) reachable(i) ++= reachable(j); 27 | } 28 | } 29 | var done : Boolean = false; 30 | while (!done) { 31 | done = true; 32 | for (i <- 1 to max) { 33 | val is : BitSet = reachable(i); 34 | for (j <- is.clone) { 35 | val js : BitSet = reachable(j); 36 | if (!(js subsetOf is)) { 37 | is ++= js; 38 | done = false; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | override def isDefinedAt(n : Int) = n >= 1 && n <= max; 46 | 47 | override def apply(n : Int) = reachable(n) contains n; 48 | } 49 | 50 | object TestCycle extends App { 51 | val e : Set[Int] = ListSet.empty; 52 | val c : Cycle = 53 | new Cycle(13, { i => i match { 54 | case 13 => e + 1 + 2 + 3 + 13 // The +13 is new to this test 55 | case 1 => e + 4 56 | case 2 => e + 4 + 1 + 5 57 | case 3 => e + 7+6 58 | case 4 => e + 12 59 | case 5 => e + 8 60 | case 6 => e + 9 61 | case 7 => e + 9+10 62 | case 8 => e + 5+11 63 | case 9 => e + 11 64 | case 10 => e + 9 65 | case 11 => e + 9 // formerly + 13 66 | case 12 => e + 8 67 | }}); 68 | 69 | def name(i:Int) : String = { 70 | if (i == 0) ""; 71 | else ((i - 1 + 'A').toChar).toString(); 72 | } 73 | 74 | { 75 | for (i <- 1 to 13) { 76 | if (c(i)) println(name(i) + " is cyclic"); 77 | } 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/util/Dominator.scala: -------------------------------------------------------------------------------- 1 | /* Dominator computation from Langauer and Tarjan (TOPLAS 1:1) 2 | * John Boyland 3 | * This file may be used, copied and/or modified for any purpose. 4 | */ 5 | package edu.uwm.cs.util; 6 | 7 | import scala.collection.Set; 8 | import scala.collection.immutable.ListSet; 9 | 10 | /** 11 | * Dominator computation of graph represented by vertices 1..max 12 | * (Not 0 .. (max-1) !). The result returns the immediate dominator of any 13 | * node or 0 for a node without any dominator. 14 | */ 15 | class Dominator(max : Int, val succ : PartialFunction[Int,Set[Int]], root : Int) 16 | extends PartialFunction[Int,Int] { 17 | private val parent : Array[Int] = new Array(max+1); 18 | private val ancestor : Array[Int] = new Array(max+1); 19 | private val child : Array[Int] = new Array(max+1); 20 | private val vertex : Array[Int] = new Array(max+1); 21 | private val label : Array[Int] = new Array(max+1); 22 | private val semi : Array[Int] = new Array(max+1); 23 | private val size : Array[Int] = new Array(max+1); 24 | private val dom : Array[Int] = new Array(max+1); 25 | 26 | private val pred : Array[ListSet[Int]] = new Array(max+1); 27 | private val bucket : Array[ListSet[Int]] = new Array(max+1); 28 | 29 | var n : Int = 0; // NB: In paper, n is also used for "max" 30 | 31 | private def dfs(v : Int) : Unit = { 32 | n += 1; 33 | semi(v) = n; 34 | label(v) = v; 35 | vertex(n) = v; 36 | ancestor(v) = 0; 37 | child(v) = 0; 38 | size(v) = 1; 39 | for (w <- succ(v)) { 40 | if (semi(w) == 0) { 41 | parent(w) = v; dfs(w) 42 | } 43 | pred(w) += v; 44 | } 45 | } 46 | 47 | private def compress(v : Int) : Unit = { 48 | if (ancestor(ancestor(v)) != 0) { 49 | compress(ancestor(v)); 50 | if (semi(label(ancestor(v))) < semi(label(v))) { 51 | label(v) = label(ancestor(v)); 52 | } 53 | ancestor(v) = ancestor(ancestor(v)); 54 | } 55 | } 56 | 57 | private def evalX(v : Int) : Int = { 58 | if (ancestor(v) == 0) { 59 | v; 60 | } else { 61 | compress(v); 62 | label(v); 63 | } 64 | } 65 | 66 | private def eval(v : Int) : Int = { 67 | if (ancestor(v) == 0) { 68 | label(v) 69 | } else { 70 | compress(v); 71 | if (semi(label(ancestor(v))) >= semi(label(v))) { 72 | label(v) 73 | } else { 74 | label(ancestor(v)); 75 | } 76 | } 77 | } 78 | 79 | private def linkX(v : Int, w : Int) = { 80 | ancestor(w) = v; 81 | } 82 | 83 | private def link(v : Int, w : Int) = { 84 | var s : Int = w; 85 | while (semi(label(w)) < semi(label(child(s)))) { 86 | if (size(s) + size(child(child(s))) >= 2 * size(child(s))) { 87 | ancestor(child(s)) = s; 88 | child(s) = child(child(s)); 89 | } else { 90 | size(child(s)) = size(s); 91 | ancestor(s) = child(s); 92 | s = ancestor(s); 93 | } 94 | } 95 | label(s) = label(w); 96 | size(v) = size(v) + size(w); 97 | if (size(v) < 2 * size(w)) { 98 | val tmp = child(v); 99 | child(v) = s; 100 | s = tmp; 101 | } 102 | while (s != 0) { 103 | ancestor(s) = v; 104 | s = child(s) 105 | } 106 | } 107 | 108 | // Step 1: 109 | { 110 | for (v <- 1 to max) { 111 | pred(v) = ListSet.empty; 112 | bucket(v) = ListSet.empty; 113 | semi(v) = 0; 114 | } 115 | n = 0; 116 | dfs(root); 117 | if (n != max) { 118 | throw new Exception("Assertion failed"); 119 | } 120 | size(0) = 0; 121 | label(0) = 0; 122 | semi(0) = 0; 123 | for (i <- n to 2 by -1) { // NB: vertex(1) = root always 124 | val w : Int = vertex(i); 125 | 126 | // step 2 127 | for (v <- pred(w)) { 128 | val u : Int = eval(v); 129 | if (semi(u) < semi(w)) { 130 | semi(w) = semi(u); 131 | } 132 | } 133 | bucket(vertex(semi(w))) += w; 134 | link(parent(w),w) 135 | 136 | // step 3 137 | val bucketcopy : Set[Int] = bucket(parent(w)); 138 | bucket(parent(w)) = ListSet.empty; 139 | for (v <- bucketcopy) { 140 | val u : Int = eval(v); 141 | dom(v) = { 142 | if (semi(u) < semi(v)) u else parent(w) 143 | } 144 | } 145 | } 146 | 147 | // step 4 148 | for (i <- 2 to n) { 149 | val w : Int = vertex(i); 150 | if (dom(w) != vertex(semi(w))) { 151 | dom(w) = dom(dom(w)) 152 | } 153 | } 154 | dom(root) = 0; 155 | } 156 | 157 | override def isDefinedAt(n : Int) = n >= 1 && n <= max; 158 | 159 | override def apply(n : Int) = dom(n); 160 | 161 | def dominates(i : Int, j : Int) : Boolean = { 162 | j != 0 && (i == j || dominates(i,apply(j))) 163 | } 164 | } 165 | 166 | object TestDominator extends App { 167 | val e : ListSet[Int] = ListSet.empty; 168 | val d : Dominator = 169 | new Dominator(13, { i => i match { 170 | case 13 => e + 1 + 2 + 3 171 | case 1 => e + 4 172 | case 2 => e + 4 + 1 + 5 173 | case 3 => e + 7+6 174 | case 4 => e + 12 175 | case 5 => e + 8 176 | case 6 => e + 9 177 | case 7 => e + 9+10 178 | case 8 => e + 5+11 179 | case 9 => e + 11 180 | case 10 => e + 9 181 | case 11 => e + 9+13 182 | case 12 => e + 8 183 | }}, 13); 184 | 185 | def name(i:Int) : String = { 186 | if (i == 0) ""; 187 | else ((i - 1 + 'A').toChar).toString(); 188 | } 189 | 190 | { 191 | for (i <- 1 to 13) { 192 | println(name(i) + " is dominated by " + name(d(i))); 193 | } 194 | } 195 | } 196 | 197 | -------------------------------------------------------------------------------- /src/edu/uwm/cs/util/Reach.scala: -------------------------------------------------------------------------------- 1 | /* Reachability computation of nodes in a graph. 2 | * John Boyland 3 | * This file may be used, copied and/or modified for any purpose. 4 | */ 5 | package edu.uwm.cs.util; 6 | 7 | import scala.collection.Set; 8 | import scala.collection.immutable.ListSet; 9 | import scala.collection.mutable.BitSet; 10 | 11 | /** 12 | * Reaching computation of graph represented by vertices 1..max 13 | * (Not 0 .. (max-1) !). The result returns all nodes reachable 14 | * from the given node. 15 | */ 16 | class Reach(max : Int, val succ : PartialFunction[Int,Set[Int]]) 17 | extends PartialFunction[Int,Set[Int]] 18 | { 19 | private val reachable : Array[BitSet] = new Array(max+1); 20 | 21 | { 22 | for (i <- 1 to max) { 23 | reachable(i) = new BitSet(max+1); 24 | for (j <- succ(i)) { 25 | reachable(i) += j 26 | if (j < i) reachable(i) ++= reachable(j); 27 | } 28 | } 29 | var done : Boolean = false; 30 | while (!done) { 31 | done = true; 32 | for (i <- 1 to max) { 33 | val is : BitSet = reachable(i); 34 | for (j <- is.clone) { 35 | val js : BitSet = reachable(j); 36 | if (!(js subsetOf is)) { 37 | is ++= js; 38 | done = false; 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | override def isDefinedAt(n : Int) = n >= 1 && n <= max; 46 | 47 | override def apply(n : Int) = reachable(n); 48 | } 49 | 50 | object TestReach extends App { 51 | val e : ListSet[Int] = ListSet.empty; 52 | val c : Reach = 53 | new Reach(13, { i => i match { 54 | case 13 => e + 1 + 2 + 3 + 13 // The +13 is new to this test 55 | case 1 => e + 4 56 | case 2 => e + 4 + 1 + 5 57 | case 3 => e + 7+6 58 | case 4 => e + 12 59 | case 5 => e + 8 60 | case 6 => e + 9 61 | case 7 => e + 9+10 62 | case 8 => e + 5+11 63 | case 9 => e + 11 64 | case 10 => e + 9 65 | case 11 => e + 9 // formerly + 13 66 | case 12 => e + 8 67 | }}); 68 | 69 | def name(i:Int) : String = { 70 | if (i == 0) ""; 71 | else ((i - 1 + 'A').toChar).toString(); 72 | } 73 | 74 | { 75 | for (i <- 1 to 13) { 76 | print(name(i) + " reaches"); 77 | for (j <- c(i)) { 78 | print(" "+name(j)); 79 | } 80 | println(); 81 | } 82 | } 83 | } 84 | 85 | --------------------------------------------------------------------------------