pax_global_header00006660000000000000000000000064142126311210014503gustar00rootroot0000000000000052 comment=8c0bd6b6c684b0ea2f07492dd16588b62ed9344f parboiled-1.4.1/000077500000000000000000000000001421263112100134475ustar00rootroot00000000000000parboiled-1.4.1/.github/000077500000000000000000000000001421263112100150075ustar00rootroot00000000000000parboiled-1.4.1/.github/CODEOWNERS000066400000000000000000000000131421263112100163740ustar00rootroot00000000000000* @xuwei-k parboiled-1.4.1/.github/dependabot.yml000066400000000000000000000001661421263112100176420ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" parboiled-1.4.1/.github/workflows/000077500000000000000000000000001421263112100170445ustar00rootroot00000000000000parboiled-1.4.1/.github/workflows/ci.yml000066400000000000000000000006471421263112100201710ustar00rootroot00000000000000name: CI on: pull_request: push: schedule: - cron: '0 4 * * 2' jobs: test: runs-on: ubuntu-latest strategy: matrix: include: - java: 11 - java: 17 steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v2 with: java-version: ${{ matrix.java }} distribution: temurin - uses: coursier/cache-action@v6 - run: sbt -v "+ test" parboiled-1.4.1/.gitignore000066400000000000000000000007421421263112100154420ustar00rootroot00000000000000# Only put project-/build-tool-specific things here! # Everything specific to your particular dev machine # (like OS- or IDE-related artifacts) should go into # a personal, global .gitignore file! # (See https://help.github.com/articles/ignoring-files/ for details) .bsp lib_managed target project/boot project/build/target project/plugins/lib_managed project/plugins/src_managed project/plugins/target project/plugins/project/build.properties buildinfo.properties proguard-sbt.txtparboiled-1.4.1/CHANGELOG000066400000000000000000000562561421263112100146770ustar00rootroot00000000000000Version 1.4.1 (2022-03-11) -------------------------- - Improved support for Java 16+ (thanks to @kenwenzel for the patch!) - Dropped support for Java 8 Version 1.4.0 (2022-01-24) -------------------------- - Support Java 17 - Upgraded to ASM 9.2 - Dropped support for Scala 2.11 Version 1.3.1 (2019-06-24) -------------------------- - Upgraded to ASM 7.1 - Fixed class loader in parser generation - Added cross-build for Scala 2.13.0 - Dropped support for Scala 2.10 Version 1.3.0 (2019-01-23) -------------------------- - Upgraded to ASM 7.0 - Added Scala 2.13.0-M5 build Version 1.2.0 (2018-09-14) -------------------------- - Added support for `~~>` and friends in ReductionRules - Added support for PushRules to chain after ReductionRules - Upgraded to ASM 6.2.1 - Added Scala 2.13.0-M4 build - Dropped support for Java 1.5 Version 1.1.8 (2017-01-11) -------------------------- - Cross build for Scala 2.10, 2.11 and 2.12 - Upgrade to ASM 5.2 - Fixed "Utils.findConstructor doesn't match boolean parameter" (#94) - Added method for clearing class cache in AsmUtil Version 1.1.7 (2015-02-09) -------------------------- - Upgrade to Scala 2.11.4, cross build for Scala 2.9.2, 2.9.3, 2.10 and 2.11 - Upgrade to ASM 5.0.3, closed #76 - Make parboiled-java relocatable, closed #80 Version 1.1.6 (2013-08-22) -------------------------- - Core: - Improved error msg for unexpected end of input - Fixed error reporting for rules underneath testNot rules - Scala - Added support for ReductionRule1 inside oneOrMore/zeroOrMore while returning a subtype - Upgraded to Scala 2.10.2 Version 1.1.5 (2013-05-02) -------------------------- - General: Added missing OSGi Bundle metadata - Core: Fixed MemoMismatcher producing erroneous parsing results for subsequent parsing runs with the same parser instance - Java: Fixed problem AsmUtils - Scala: Upgraded to Scala 2.9.3 and 2.10.1 Version 1.1.4 (2012-11-27) -------------------------- Core: - Added parser automatic parser termination after matching 100K EOI, closed #52 - Extended IndentDedentBuffer with option to not swallow empty lines, closed #56 Java: - Upgraded to ASM 4.1 Scala: - Upgraded to Scala 2.10.0-RC3 Version 1.1.3 (2012-10-16) -------------------------- Core & Java: - Removed superfluous dependency on Scala library Version 1.1.2 (2012-10-15) -------------------------- Scala: - Fix cross-building path generation to include the full scala-version for unstable versions (milestones, RC) - Upgrade to Scala 2.10-RC1 Version 1.1.1 (2012-09-21) -------------------------- General: - Fixed incorrect scope for scalatest dependency - Upgraded to SBT 0.12.0 - Changed deployment target to sonatype (i.e. maven central) Scala: - Enabled proper scala-version cross-building - Upgraded to Scala 2.10-M7 Version 1.1 (2012-07-28) ------------------------ Java: - Added `NTimes` rule construction helper - Upgraded to ASM 4.0, closes #41 - Improved ABC example Scala: - Upgraded to Scala 2.10-M6 Version 1.0.2 (2011-09-30) -------------------------- Core: - Fixed #32 (ReportingParseRunner assumes only one parse error) Java: - Added 'NoneOf' rule constructor - Updated Java parser example - Fixed #22 (problem with parse tree creation for skipNode() matchers) - Fixed #24 (IllegalStateException in certain cases involving parser inheritance) - Fixed #30 (problem in action class naming resulting in NoSuchFieldError in certain cases) - Fixed #31 (incorrect handling of 'switch' statements in rule method extension) Scala: - Upgraded to Scala 2.9.1 - Fixed #29 (nTimes(1, "a") will throw ClassCastException) Version 1.0.1 (2011-07-05) -------------------------- Scala: - Upgraded to Scala 2.9.0-1 Version 1.0.0 (2011-05-13) -------------------------- Scala: - Upgraded to Scala 2.9.0.final Version 0.11.2 (2011-05-13) --------------------------- - Changed build system from Apache Buildr to SBT 0.7.7 (Scala Build Tool) Core: - Added convenience methods to Context and BaseActions returning the current location in the InputBuffer as a Position - Fixed another potential problem with ActionMatcher subcontext initialization - Fixed MissingFormatArgumentException in ParseRuntimeException when args array is empty Java: - Added convenience 'currentIndex()' method to BaseActions Scala: - Added '~:?' operator - Fixed 'withContext' not usable on '~%' operator problem - Turned all remaining references to Context[_] to Context[Any] (this is the last version built against Scala 2.8.1) Version 0.11.1 (2011-04-06) --------------------------- Core: - Fixed bug causing parser actions at the first position in sequences to sometimes inadvertently advance the match cursor, leading to hard-to-find input mismatches - Fix bug in TracingParseRunner.withLog - Added support for timeouts to RecoveringParseRunner Version 0.11.0 (2011-03-21) --------------------------- Core: - Major overhaul of RecoveryParseRunner, fixed numerous bugs, improved performance - Changed RecoveringParseRunner to not create additional ParseTreeNodes underneath error nodes - Added Context.inErrorRecovery method - Added "strict" flag to IndentDedentBuffer for enabling/disabling exception throwing on illegal indentations - Fixed bug in IndentDedentBuffer leaving blank lines on indented comments before EOI - Smaller fixes, cleanups and improvements Java: - Switched internal class name resolving from using the current Threads ContextClassLoader to using the respective class' own ClassLoader in an attempt to increase compatibility with a broader range of application environments Scala: - Simplified ParsingResult to be more scala idiomatic, removed "result", renamed "resultOption" to "result", removed "hasErrors" method - Renamed POPx rules to DROP, DROP2 and DROP3 - Removed Parser.withParseTreeBuilding method, changed Parser.buildParseTree from var to a def, which needs to be overridden in order to activate parse tree building - Removed empty parens on a number of rule marker methods - Added overloads with Rule2 parameter to optional, zeroOrMore, oneOrMore and nTimes Version 0.10.1 (2011-02-11) --------------------------- Core: - Added Context.getMatchLength() method and helpers - Added support for IndexRanges - Completely overhauled ParseRunner implementations architecture in preparation for coming extensions - Deprecated static run methods of main ParseRunners - Improved logging architecture for DebuggingValueStack and TracingParseRunner - Moved class InputBuffer.Position into package org.parboiled.support - Fixed small memory leak in DefaultValueStack - Completely rewrote IndentDedentBuffer implementation (the previous one had serious limitations) - Many smaller fixes, cleanups and improvements Java: - Added StringBuilderVar helper for parser actions - Fixed bug causing StringIndexOutOfBoundsException during parser extension for parsers without package prefix - Fixed NPE in AsmUtils caused by actions involving methods defined in super-interfaces - Upgraded to ASM 3.3.1 Scala: - Fixed bug in Rule0 causing ClassCastException in certain cases involving ReductionRule2s and PopRule2s - Moved Input.collectContents from scala module into core Version 0.10.0 (2011-01-13) --------------------------- - Switched build system from Ant to Apache Buildr - Completed mavenization, parboiled artifacts are now also available from the http://scala-tools.org/repo-releases/ maven repository - Split source into several modules creating distinct artifacts - Changed to 3 part version numbering in preparation of the coming semantic versioning compatibility (http://semver.org) - Fixed VerifyError with certain Var<...> constructs - Fixed IllegalStateException during error recovery involving AnyMatchers - Fixed bug in creation of expected string error message for AnyOf matchers - Fixed bug in DebuggingValueStack (thx to Matthieu Baechler) - Fixed GH issue #10 (ParseTreeUtils.findNodeByLabel() methods are non static) - Fixed bug in ErrorUtils - Fixed Java parser example to support non-ascii chars in identifiers - Added pushFromContext rule creator to Scala facade - Improved Scala Input wrapper - Removed internal dependency on jetbrains annotations - Removed internal dependency on google collections - Upgraded to ASM 3.3 - Upgraded to Scala 2.8.1 - Many smaller cleanup and improvements Version 0.9.9.0 (2010-10-04) ---------------------------- - BREAKING CHANGES: - Moved all named MatcherVisitor implementations from org.parboiled.support to new package org.parboiled.matchervisitors - Moved InputBuffer implementations from org.parboiled.support to to new package org.parboiled.buffers - Moved special character definitions from org.parboiled.Characters class into dedicated class org.parboiled.Characters - Decapitalized not-extended rule construction helper methods in BaseParser (e.g. ToRule -> toRule) for better compliance with the style guide - New MatcherPath implementation, not more "lastMatch" property in InvalidInputError - Significantly extended Scala facade: - Cleaner structure, faster rule building, faster rules (e.g. enabled fast-string-matching for FirstOf operator) - More flexible action functions (more action operators, 'withContext' actions) - Added missing variance annotations to rule types and aliases - Added org.parboiled.scala.testing.ParboiledTest trait for simplified parser testing - More and better scala wrappers (ParseRunners, ParsingResult, parsing Input) - Added TracingParseRunner as another important debugging tool - Added IndentDedentInputBuffer supporting line indentation based grammars - Added Context.getFirstMatchChar and helpers in Java and Scala - Added NothingMatcher as counterpart to the EmptyMatcher, along with respective rule constants - Added missing 'getStarterChar' functionality to CustomMatcher base class - Improved filtering of Node and Matcher tree printing with simple predicate logic - Fixed incorrect parse error reporting in some cases - Fixed in Scala facade: Some standard rule builders don't group their sub rules correctly - Fixed bug in RecoveringParseRunner causing NPE if very first char was not matched - Fixed bug in RecoveryParseRunner incorrectly executing pre-error sub rules of resync sequences - Fixed bug preventing the creation of 'action-only' rules - Fixed bug in FirstOfStringsMatcher preventing the matching of alternatives that are prefixes of other alternatives - Fixed matcher labelling problem in Scala facade - Many smaller cleanups and improvements Version 0.9.8.2 (2010-08-13) ---------------------------- - BREAKING CHANGES: - Renamed BaseParser.FirstOf(string) overload to BaseParser.AnyOf - Renamed CharSetMatcher to AnyOfMatcher - Fixed bug in MemoMismatchesMatcher causing erroneous rule mismatches at input location 0 Version 0.9.8.1 (2010-08-12) ---------------------------- - BREAKING CHANGE: - Renamed BaseParser.CharSet methods to BaseParser.FirstOf overloads - Fixed bug in RecoveringParseRunner producing inconsistenst value stack states during rule resynchronization - Added BaseParser.matchOrDefault() helper - Added ability to set ClassLoader for auxiliary classes (for parser extension) via the threads ContextClassLoader - Added a few more convenience overloads to Parser.zeroOrMore(...) and Parser.oneOrMore(...) in Scala facade - Added MemoMismatches rule option to Scala facade - Improved Javadoc and Scaladoc documentation as well as Scala examples Version 0.9.8.0 (2010-08-10) ---------------------------- - BREAKING CHANGES: - Added a parser "Value Stack", retired parser value handling via parse tree decoration - Removed all action expression helpers for the old parse-tree-based value handling - Changed default parsing behavior from parse-tree-building to parse-tree-less, added @BuildParseTree annotation - Changed Eoi(), Any() and Empty() to public static final fields on the BaseParser - Renamed BaseParser.CharIgnoreCase and BaseParser.StringIgnoreCase to BaseParser.IgnoreCase - Fixed bug provoking an exception at the 65536th input character - Fixed bug in Test-/TestNotMatchers - Fixed some problems in Java Parser example - Added Scala facade for efficient parser building without byte code manipulation, featuring DSL elements for value stack operations with complete static type checking - Added a complete JsonParser as an example for using the Scala facade - Added a matcher specialization for fast string matching also in FirstOf-Rules (~10% speedup in JavaParser example) - Added overloads to standard rule builders OneOrMore(...), Optional(...), Test(...), TestNot(...) and ZeroOrMore(...) directly taking several sub rules as a sequence - Added @MemoMismatches and respective implementation (which is a first step towards "packratting" support) - Added ProfilingParseRunner and JavaParserProfiler example - Adapted build process to build two independent libraries, one for Java, one for Scala - Changed source directory structure to "Maven-style" Version 0.9.7.3 (2010-07-09) ---------------------------- - Fixed several problems in parse error recovery - Added fast string matching (significant speed increase for languages relying on string matching) - Added additional error recovery strategy: Single character replacement - Added support for custom InputBuffer implementations - Removed superfluous CAPTURE constructs along with all related code Version 0.9.7.2 (2010-06-14) ---------------------------- - BREAKING CHANGE: Tracking of locations in input text is now purely done via integer indices (was: instances of class org.parboiled.common.InputLocation) for a much reduced memory footprint and a significant performance increase, especially for large input files and rather simple grammars (most of the functionality of org.parboiled.common.InputLocation has been moved to org.parboiled.common.InputBuffer) - Added smaller extensions in a few support classes Version 0.9.7.1 (2010-06-08) ---------------------------- - BREAKING CHANGE: Rule methods w/ parameters are now also automatically labelled, @Label now requires parameter! - Fixed action variable initializers not working as expected in certain scenarios - Fixed several bugs related to overriding of Rule methods in multi-level parser class hierarchies - Fixed "Constructor with descriptor '()V' not found" exception in certain action expressions - Fixed displaying of empty "expected" part in parse error messages caused by TestNot(...) failures - Fixed small problems with building against Java 6 - Added missing field and method private access verification for non-action instructions - Added Context.getPrevStartLocation() and Context.getPrevEndLocation() and respective helpers - Added convenience overload ErrorUtils.printParseErrors(ParsingResult) - Improved error reporting of parse errors caused by TestNot(...) failures - Improved parsing performance of Reporting- and RecoveringParseRunner by 10+ % at the expense of one more parsing run on invalid input - Improved calculator examples, added new CalculatorParser6 example demonstrating parse-tree-less parsing and action variables - Changed java example to exercise parboileds parse-tree-less instead of the parse-tree-based parsing performance Version 0.9.7.0 (2010-04-28) ---------------------------- - Fixed exception during parser creation when parser contains action/capture expressions with constructor calls - Fixed NoSuchFieldError occuring in some scenarios with similar actions in derived parser classes - Fixed "Couldn't get field" exception during parser extension occuring in parsers accessing parent fields - Fixed bug in parser extension causing java.lang.VerifyError in some scenarios - Fixed Context.inPredicate() not working underneath TestNotMatchers - Fixed illegal context issue in action expressions recursing into themselves - Fixed problem in MutableTreeNodeImpl preventing the addition of new child nodes - Fixed unintended @SuppressNode on String/StringIgnoreCase rules (now:@SuppressSubnodes) - Added support for action variables (org.parboiled.support.Var) - Added prevValue(), prevText() and prevChar() functionality for parse tree less AST node building - Added support for parser statistics (class org.parboiled.ParserStatistics) - Added @SkipActionsInPredicates and @DontSkipActionsInPredicates - Added BaseParser.newInstance() Version 0.9.6.0 (2010-04-14) ---------------------------- - Fixed serious performance problem in construction of parse tree nodes with many subnodes - Fixed ClassCastException on caching of rule creating methods with primitive array parameters - Fixed smaller problems in Java parser example - MAJOR BREAKING CHANGES: - Renamed all methods returning Rule instances to begin with an uppercase character - Renamed all helper method defined in BaseActions class from all uppercase to "regular" Java method names - Renamed @Leaf to @SuppressSubnodes - Significantly improved parser extension logic for speed and memory requirements - Added support for action expressions in rule methods with parameters - Added support for accessing local variables and methods parameters from within action expressions - Added new overloads taking char[] for CharSet, String and StringIgnoreCase rule creators - Added BaseParser.FromCharArray(...) - Added support for explicit action expressions via BaseParser.ACTION(...) - Added UP2, UP3, UP4, UP5, UP6 and DOWN2, DOWN3, DOWN4, DOWN5, DOWN6 context switching helpers - Added @DontLabel annotation preventing the automatic labelling of rules created by parameterless rule methods - Added @SuppressNode and @SkipNode annotations and logic - Added CustomMatcher base class for custom matcher implementations - Added Context.lookAhead(int) - Added support for Captures (special closure-like constructs simplifying certain rule creation scenarios) - Improved Calculator examples - Smaller bug fixes and other improvements Version 0.9.5.1 (2010-03-16) ---------------------------- - Fixed classLoader issue triggering an IOException(Class not found) exception in some environments - Added org.parboiled.examples.pegtranslator.PegTranslator example, thanks to Radu Vlasov for the contribution - Replaced BaseActions.NO_ERROR() with the inversed BaseActions.HAS_ERROR() - Small Javadoc improvements and other cosmetic changes Version 0.9.5.0 (2010-03-02) ---------------------------- - Major update of basic matching architecture (some breaking changes) - Removed BaseParser.parse(...), introduced ParseRunner interface with 4 standard implementations - Implemented a completely new and much improved parse error reporting and recovery logic - Removed all, now obsolete, rule enforcement constructs like enforcedSequence(...) - Replaced a number of utility classes (like ImmutableList) with implementations from Google Collections - Added Proguard-based build step to perform unused class/member removal for more flexible use of external libraries - Smaller performance optimization (ca. 10% faster parsing of error free input over version 0.9.2.0) - Corrected and improved API documentation - Many smaller bug fixes, refactorings and other improvements Version 0.9.2.0 (2010-02-02) ---------------------------- - Added @Leaf annotation for marking rules as "creating parse tree leaf nodes" - Added @Label annotation - Significantly improved parsing speed (with some @Leaf usage: speedup factor of ca. 3) - Changed Action.run() to Action.run(Context), removed AbstractAction - Changed ParseError.context to ParseError.matcherPath - Removed Parboiled.RecoverFromErrors flag logic - Removed Failed-Rule-Memoization option due to limited effectiveness - Improved Java parser example, added performance test - Smaller bug fixes and architectural improvements Version 0.9.1.1 (2010-01-29) ---------------------------- - Fixed @Cached problem with overloaded rule creation methods - Added BaseParser.charSet(...) and a few other standard rule definition overloads Version 0.9.1.0 (2010-01-28) ---------------------------- - Added Failed-Rule-Memoization option - Added @Cached annotation logic for automatic caching of rule creating methods with parameters - Added parser extension caching for not recreating extension class upon repeated Parboiled.createParser(...) - Added ENFORCED() action expression helper - Improved character matchers for increased parsing speed - Renamed @DontExtend to @KeepAsIs - Removed BaseParser.fromUserObject(...), overwrite toRule(...) for the same effect - Smaller bug fixes and architectural improvements Version 0.9.0.2 (2010-01-26) ---------------------------- Major rewrite of action and parser extension infrastructure, now based on bytecode analysis, action instructions separation and automatic action class generation/parser class rewriting. Many, many thanks to Ken Wenzel for the original idea as well as a sample implementation! - Removed ActionResult interface, actions can now be arbitrary boolean expressions - Removed now obsolete EQUALS, AND, OR, NOT as well as all CONVERT_XXX helpers - Removed dependency on cglib, parboiled JAR now has no non-SDK dependencies - Simplified running parboiled examples (now run via ANT) Version 0.8.5.0 (2010-01-18) ---------------------------- - Fixed IllegalStateException on trying to label a rule from within a recursion - Fixed rare bug occurring in certain scenarios involving recursive test/testNot rules - Improved reporting of parse errors, also renamed ParserConstructionException to GrammarException and ParsingException to ActionException, introduced ParserRuntimeException - Enabled writing access to ParseError list returned by Context.getParseErrors() - Inlined MatcherContext.runMatcher(boolean) for reduced stack footprint and cleaner stack traces Version 0.8.4.0 (2010-01-07) ---------------------------- - Fixed incorrect ordering of action calls when several calls are present in one rule - Fixed test/testNot rules throwing ParserConstructionException at EOI - Added action helpers: EQUALS, AND, OR, NOT, NEXT_CHAR, NODE_BY_LABEL and NODES_BY_LABEL - Added Time and RPN example (thanks to Reinier Zwitserloot from http://projectlombok.org for the RPN code) - Added Action interface enabling stand-alone action objects within rule creation expressions - Added SplitParserTest, testing splitting of one grammar into several parser objects - Simplified ActionParameter infrastructure, now allows casting of action parameters - Changed access of BaseParser.toRule(s) from private to protected to allow for easy conversion of custom objects Version 0.8.3.0 (2009-12-15) ---------------------------- - Improved parsing speed by factor 4 after first basic CPU profiling - Added @SkipInPredicates annotation for action methods - Added option for convenient rule creation from user objects (suggested by Manuel Brotz) Version 0.8.2.0 (2009-12-11) ---------------------------- - Added option for manual rule label change - Added error filter to only report the first parse error at a specific input location - Added string matching optimization for one-letter strings - Added some more parser structure validation checks - Improved customizability of parse tree printouts - Improved naming of firstOf-rules in parse errors and parse tree printing - Updated SparqlParser example Version 0.8.1.0 (2009-12-07) ---------------------------- - Fixed definition of special characters to be fully compatible with the unicode standard - Fixed IllegalStateException during parse error recovery of testNot() rules - Added support for case independent matching of characters and strings - Added SPARQL parser example (incl. test), thanks to Ken Wenzel from http://www.iwu.fraunhofer.de Version 0.8.0.0 (2009-11-12) ---------------------------- first public releaseparboiled-1.4.1/LICENSE000066400000000000000000000261351421263112100144630ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. parboiled-1.4.1/README.markdown000066400000000000000000000007751421263112100161610ustar00rootroot00000000000000# Please see * for download access to the artifacts * for all documentation * for the parboiled-core API javadoc * for the parboiled-java API javadoc * for the parboiled-scala API scaladoc * for the Patch Policy parboiled-1.4.1/build.sbt000066400000000000000000000121701421263112100152610ustar00rootroot00000000000000import Dependencies._ val basicSettings = Seq( shellPrompt := { s => Project.extract(s).currentProject.id + " > " }, version := "1.4.1", scalaVersion := "2.13.8", homepage := Some(new URL("http://parboiled.org")), organization := "org.parboiled", organizationHomepage := Some(new URL("http://parboiled.org")), description := "Elegant parsing in Java and Scala - lightweight, easy-to-use, powerful", startYear := Some(2009), licenses := Seq("Apache 2" -> new URL("http://www.apache.org/licenses/LICENSE-2.0.txt")), resolvers ++= resolutionRepos, javacOptions ++= Seq( "-deprecation", "-target", "11", "-source", "11", "-encoding", "utf8", "-Xlint:unchecked" ), scalacOptions ++= Seq( "-feature", "-language:implicitConversions", "-unchecked", "-deprecation", "-encoding", "utf8" ), libraryDependencies ++= Dependencies.test(testNG), libraryDependencies ++= Dependencies.test(scalatest(scalaVersion.value): _*), // scaladoc settings (doc / scalacOptions) ++= Seq("-doc-title", name.value, "-doc-version", version.value), // publishing crossScalaVersions := Seq("2.12.15", "2.13.8"), scalaBinaryVersion := { if (CrossVersion.isScalaApiCompatible(scalaVersion.value)) CrossVersion.binaryScalaVersion(scalaVersion.value) else scalaVersion.value }, publishMavenStyle := true, Test / publishArtifact := false, pomIncludeRepository := { _ => false }, publishTo := { val nexus = "https://oss.sonatype.org/" if (version.value.trim.endsWith("SNAPSHOT")) Some("snapshots" at nexus + "content/repositories/snapshots") else Some("releases" at nexus + "service/local/staging/deploy/maven2") }, pomExtra := git@github.com:sirthias/parboiled.git scm:git:git@github.com:sirthias/parboiled.git sirthias Mathias Doenitz ) val javaTestModulesSettings = if (List("11", "17").exists(System.getProperty("java.version", "").startsWith)) Seq( Test / fork := true, Test / javaOptions += "--add-opens=java.base/java.lang=ALL-UNNAMED" ) else Seq.empty val noPublishing = Seq( publishArtifact := false, publishTo := Some(Resolver.file("Unused transient repository", file("target/unusedrepo"))), publishConfiguration := publishConfiguration.value.withOverwrite(true) ) def javaDoc = Seq( (Compile / doc) := { val cp = (Compile / doc / fullClasspath).value val docTarget = (Compile / doc / target).value val compileSrc = (Compile / javaSource).value val s = streams.value def replace(x: Any) = x.toString.replace("parboiled-java", "parboiled-core") def docLink = name.value match { case "parboiled-java" => " -linkoffline http://www.decodified.com/parboiled/api/core " + replace(docTarget) case _ => "" } val cmd = "javadoc" + " -sourcepath " + compileSrc + " -classpath " + cp.map(_.data).mkString(":") + " -d " + docTarget + docLink + " -encoding utf8" + " -public" + " -windowtitle " + name.value + "_" + version.value + " -subpackages" + " org.parboiled" s.log.info(cmd) sys.process.Process(cmd) ! s.log docTarget } ) lazy val root = Project("root", file(".")) .aggregate(parboiledCore, parboiledJava, parboiledScala, examplesJava, examplesScala) .settings(basicSettings: _*) .settings(noPublishing: _*) lazy val parboiledCore = Project("parboiled-core", file("parboiled-core")) .settings(basicSettings: _*) .settings(javaDoc: _*) .settings( crossPaths := false, autoScalaLibrary := false ) lazy val parboiledJava = Project("parboiled-java", file("parboiled-java")) .dependsOn(parboiledCore % "compile->compile;test->test") .settings(basicSettings: _*) .settings(javaDoc: _*) .settings(javaTestModulesSettings: _*) .settings( libraryDependencies ++= Dependencies.compile(asm, asmTree, asmAnalysis, asmUtil), Test / javacOptions += "-g", // needed for bytecode rewriting crossPaths := false, autoScalaLibrary := false, // java 17 patch module path by letting parboiled reflective access to java base package Compile / packageBin / packageOptions += Package.ManifestAttributes("Add-Opens" -> "java.base/java.lang") ) lazy val parboiledScala = Project("parboiled-scala", file("parboiled-scala")) .dependsOn(parboiledCore) .settings(basicSettings: _*) lazy val examplesJava = Project("examples-java", file("examples-java")) .dependsOn(parboiledJava % "compile->compile;test->test") .settings(basicSettings: _*) .settings(noPublishing: _*) .settings(javaTestModulesSettings: _*) .settings(javacOptions += "-g") // needed for bytecode rewriting lazy val examplesScala = Project("examples-scala", file("examples-scala")) .dependsOn(parboiledScala % "compile->compile;test->test") .settings(basicSettings: _*) .settings(noPublishing: _*) parboiled-1.4.1/examples-java/000077500000000000000000000000001421263112100162045ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/000077500000000000000000000000001421263112100167735ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/000077500000000000000000000000001421263112100177175ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/000077500000000000000000000000001421263112100206405ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/000077500000000000000000000000001421263112100214275ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/000077500000000000000000000000001421263112100233705ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/000077500000000000000000000000001421263112100252065ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/abc/000077500000000000000000000000001421263112100257335ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/abc/AbcParser.java000066400000000000000000000025241421263112100304430ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.abc; import org.parboiled.BaseParser; import org.parboiled.Rule; import org.parboiled.annotations.BuildParseTree; /** * A parser for the classic non-context free language example { a^n b^n c^n : n >= 1 } * S <- &(A c) a+ B !(a|b|c) * A <- a A? b * B <- b B? c */ @SuppressWarnings({"InfiniteRecursion"}) @BuildParseTree public class AbcParser extends BaseParser { public Rule S() { return Sequence( Test(A(), 'c'), OneOrMore('a'), B(), TestNot(AnyOf("abc")) ); } public Rule A() { return Sequence('a', Optional(A()), 'b'); } public Rule B() { return Sequence('b', Optional(B()), 'c'); } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/abc/Main.java000066400000000000000000000032431421263112100274640ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.abc; import org.parboiled.Parboiled; import org.parboiled.common.StringUtils; import org.parboiled.errors.ErrorUtils; import org.parboiled.parserunners.RecoveringParseRunner; import static org.parboiled.support.ParseTreeUtils.printNodeTree; import org.parboiled.parserunners.ReportingParseRunner; import org.parboiled.support.ParsingResult; import java.util.Scanner; public class Main { public static void main(String[] args) { AbcParser parser = Parboiled.createParser(AbcParser.class); while (true) { System.out.print("Enter an a^n b^n c^n expression (single RETURN to exit)!\n"); String input = new Scanner(System.in).nextLine(); if (StringUtils.isEmpty(input)) break; ParsingResult result = new ReportingParseRunner(parser.S()).run(input); if (!result.parseErrors.isEmpty()) System.out.println(ErrorUtils.printParseError(result.parseErrors.get(0))); else System.out.println(printNodeTree(result) + '\n'); } } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/calculators/000077500000000000000000000000001421263112100275225ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/calculators/CalculatorParser.java000066400000000000000000000055011421263112100336340ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.BaseParser; import org.parboiled.Parboiled; import org.parboiled.parserunners.RecoveringParseRunner; import org.parboiled.Rule; import org.parboiled.common.StringUtils; import org.parboiled.support.ParsingResult; import org.parboiled.support.ToStringFormatter; import org.parboiled.trees.GraphNode; import java.util.Scanner; import static org.parboiled.errors.ErrorUtils.printParseErrors; import static org.parboiled.support.ParseTreeUtils.printNodeTree; import static org.parboiled.trees.GraphUtils.printTree; /** * Base class of all calculator parsers in the org.parboiled.examples.calculators package. * Simply adds the public static main entry point. * * @param the type of the main value object created by the parser */ public abstract class CalculatorParser extends BaseParser { public abstract Rule InputLine(); @SuppressWarnings({"unchecked"}) public static > void main(Class

parserClass) { CalculatorParser parser = Parboiled.createParser(parserClass); while (true) { System.out.print("Enter a calculators expression (single RETURN to exit)!\n"); String input = new Scanner(System.in).nextLine(); if (StringUtils.isEmpty(input)) break; ParsingResult result = new RecoveringParseRunner(parser.InputLine()).run(input); if (result.hasErrors()) { System.out.println("\nParse Errors:\n" + printParseErrors(result)); } Object value = result.parseTreeRoot.getValue(); if (value != null) { String str = value.toString(); int ix = str.indexOf('|'); if (ix >= 0) str = str.substring(ix + 2); // extract value part of AST node toString() System.out.println(input + " = " + str + '\n'); } if (value instanceof GraphNode) { System.out.println("\nAbstract Syntax Tree:\n" + printTree((GraphNode) value, new ToStringFormatter(null)) + '\n'); } else { System.out.println("\nParse Tree:\n" + printNodeTree(result) + '\n'); } } } } CalculatorParser0.java000066400000000000000000000030361421263112100336360ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Rule; import org.parboiled.annotations.BuildParseTree; /** * A basic calculator parser without any actions. */ @BuildParseTree public class CalculatorParser0 extends CalculatorParser { @Override public Rule InputLine() { return Sequence(Expression(), EOI); } Rule Expression() { return Sequence(Term(), ZeroOrMore(AnyOf("+-"), Term())); } Rule Term() { return Sequence(Factor(), ZeroOrMore(AnyOf("*/"), Factor())); } Rule Factor() { return FirstOf(Number(), Parens()); } Rule Parens() { return Sequence('(', Expression(), ')'); } Rule Number() { return OneOrMore(Digit()); } Rule Digit() { return CharRange('0', '9'); } //**************** MAIN **************** public static void main(String[] args) { main(CalculatorParser0.class); } } CalculatorParser1.java000066400000000000000000000074301421263112100336410ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Rule; import org.parboiled.annotations.BuildParseTree; import org.parboiled.annotations.SuppressSubnodes; /** * A calculator parser building calculation results directly in the parsers value stack. * All calculations are implemented directly in action expressions. */ @BuildParseTree public class CalculatorParser1 extends CalculatorParser { @Override public Rule InputLine() { return Sequence(Expression(), EOI); } public Rule Expression() { return Sequence( Term(), // a successful match of a Term pushes one Integer value onto the value stack ZeroOrMore( FirstOf( // the action that is run after the '+' and the Term have been matched consumes the // two top value stack elements and replaces them with the calculation result Sequence('+', Term(), push(pop() + pop())), // same for the '-' operator, however, here the order of the "pop"s matters, we need to // retrieve the second to last value first, which is what the pop(1) call does Sequence('-', Term(), push(pop(1) - pop())) ) ) ); } public Rule Term() { return Sequence( Factor(), // a successful match of a Factor pushes one Integer value onto the value stack ZeroOrMore( FirstOf( // the action that is run after the '*' and the Factor have been matched consumes the // two top value stack elements and replaces them with the calculation result Sequence('*', Factor(), push(pop() * pop())), // same for the '/' operator, however, here the order of the "pop"s matters, we need to // retrieve the second to last value first, which is what the pop(1) call does Sequence('/', Factor(), push(pop(1) / pop())) ) ) ); } public Rule Factor() { return FirstOf(Number(), Parens()); // a factor "produces" exactly one Integer value on the value stack } public Rule Parens() { return Sequence('(', Expression(), ')'); } public Rule Number() { return Sequence( Digits(), // parse the input text matched by the preceding "Digits" rule, // convert it into an Integer and push it onto the value stack // the action uses a default string in case it is run during error recovery (resynchronization) push(Integer.parseInt(matchOrDefault("0"))) ); } @SuppressSubnodes public Rule Digits() { return OneOrMore(Digit()); } public Rule Digit() { return CharRange('0', '9'); } //**************** MAIN **************** public static void main(String[] args) { main(CalculatorParser1.class); } } CalculatorParser2.java000066400000000000000000000125361421263112100336450ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Rule; import org.parboiled.annotations.BuildParseTree; import org.parboiled.annotations.SuppressSubnodes; import org.parboiled.examples.calculators.CalculatorParser2.CalcNode; import org.parboiled.support.Var; import org.parboiled.trees.ImmutableBinaryTreeNode; /** * A calculator parser building an AST representing the expression structure before performing the actual calculation. * The parser value stack is used to build the AST nodes of type CalcNode. */ @BuildParseTree public class CalculatorParser2 extends CalculatorParser { @Override public Rule InputLine() { return Sequence(Expression(), EOI); } public Rule Expression() { Var op = new Var(); // we use an action variable to hold the operator character return Sequence( Term(), ZeroOrMore( AnyOf("+-"), op.set(matchedChar()), // set the action variable to the matched operator char Term(), // create an AST node for the operation that was just matched // we consume the two top stack elements and replace them with a new AST node // we use an alternative technique to the one shown in CalculatorParser1 to reverse // the order of the two top value stack elements swap() && push(new CalcNode(op.get(), pop(), pop())) ) ); } public Rule Term() { Var op = new Var(); // we use an action variable to hold the operator character return Sequence( Factor(), ZeroOrMore( AnyOf("*/"), op.set(matchedChar()), // set the action variable to the matched operator char Factor(), // create an AST node for the operation that was just matched // we consume the two top stack elements and replace them with a new AST node // we use an alternative technique to the one shown in CalculatorParser1 to reverse // the order of the two top value stack elements swap() && push(new CalcNode(op.get(), pop(), pop())) ) ); } public Rule Factor() { return FirstOf(Number(), Parens()); } public Rule Parens() { return Sequence('(', Expression(), ')'); } public Rule Number() { return Sequence( Digits(), // parse the input text matched by the preceding "Digits" rule, // convert it into an Integer and push a new AST node for it onto the value stack // the action uses a default string in case it is run during error recovery (resynchronization) push(new CalcNode(Integer.parseInt(matchOrDefault("0")))) ); } @SuppressSubnodes public Rule Digits() { return OneOrMore(Digit()); } public Rule Digit() { return CharRange('0', '9'); } //**************************************************************** /** * The AST node for the calculators. The type of the node is carried as a Character that can either contain * an operator char or be null. In the latter case the AST node is a leaf directly containing a value. */ public static class CalcNode extends ImmutableBinaryTreeNode { private int value; private Character operator; public CalcNode(int value) { super(null, null); this.value = value; } public CalcNode(Character operator, CalcNode left, CalcNode right) { super(left, right); this.operator = operator; } public int getValue() { if (operator == null) return value; switch (operator) { case '+': return left().getValue() + right().getValue(); case '-': return left().getValue() - right().getValue(); case '*': return left().getValue() * right().getValue(); case '/': return left().getValue() / right().getValue(); default: throw new IllegalStateException(); } } @Override public String toString() { return (operator == null ? "Value " + value : "Operator '" + operator + '\'') + " | " + getValue(); } } //**************** MAIN **************** public static void main(String[] args) { main(CalculatorParser2.class); } } CalculatorParser3.java000066400000000000000000000143511421263112100336430ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Rule; import org.parboiled.annotations.BuildParseTree; import org.parboiled.examples.calculators.CalculatorParser3.CalcNode; import org.parboiled.support.Var; import org.parboiled.trees.ImmutableBinaryTreeNode; /** * A calculator parser building an AST representing the expression structure before performing the actual calculation. * The value field of the parse tree nodes is used for AST nodes. * As opposed to the CalculatorParser2 this parser also supports floating point operations, negative numbers, a "power" * and a "SQRT" operation as well as optional whitespace between the various expressions components. */ @BuildParseTree public class CalculatorParser3 extends CalculatorParser { @Override public Rule InputLine() { return Sequence(Expression(), EOI); } Rule Expression() { Var op = new Var(); return Sequence( Term(), ZeroOrMore( // we use a FirstOf(String, String) instead of a AnyOf(String) so we can use the // fromStringLiteral transformation (see below), which automatically consumes trailing whitespace FirstOf("+ ", "- "), op.set(matchedChar()), Term(), // same as in CalculatorParser2 push(new CalcNode(op.get(), pop(1), pop())) ) ); } Rule Term() { Var op = new Var(); return Sequence( Factor(), ZeroOrMore( FirstOf("* ", "/ "), op.set(matchedChar()), Factor(), push(new CalcNode(op.get(), pop(1), pop())) ) ); } Rule Factor() { return Sequence( Atom(), ZeroOrMore( "^ ", Atom(), push(new CalcNode('^', pop(1), pop())) ) ); } Rule Atom() { return FirstOf(Number(), SquareRoot(), Parens()); } Rule SquareRoot() { return Sequence( "SQRT ", Parens(), // create a new AST node with a special operator 'R' and only one child push(new CalcNode('R', pop(), null)) ); } Rule Parens() { return Sequence("( ", Expression(), ") "); } Rule Number() { return Sequence( // we use another Sequence in the "Number" Sequence so we can easily access the input text matched // by the three enclosed rules with "match()" or "matchOrDefault()" Sequence( Optional('-'), OneOrMore(Digit()), Optional('.', OneOrMore(Digit())) ), // the matchOrDefault() call returns the matched input text of the immediately preceding rule // or a default string (in this case if it is run during error recovery (resynchronization)) push(new CalcNode(Double.parseDouble(matchOrDefault("0")))), WhiteSpace() ); } Rule Digit() { return CharRange('0', '9'); } Rule WhiteSpace() { return ZeroOrMore(AnyOf(" \t\f")); } // we redefine the rule creation for string literals to automatically match trailing whitespace if the string // literal ends with a space character, this way we don't have to insert extra whitespace() rules after each // character or string literal @Override protected Rule fromStringLiteral(String string) { return string.endsWith(" ") ? Sequence(String(string.substring(0, string.length() - 1)), WhiteSpace()) : String(string); } //**************************************************************** /** * The AST node for the calculators. The type of the node is carried as a Character that can either contain * an operator char or be null. In the latter case the AST node is a leaf directly containing a value. */ public static class CalcNode extends ImmutableBinaryTreeNode { private double value; private Character operator; public CalcNode(double value) { super(null, null); this.value = value; } public CalcNode(Character operator, CalcNode left, CalcNode right) { super(left, right); this.operator = operator; } public double getValue() { if (operator == null) return value; switch (operator) { case '+': return left().getValue() + right().getValue(); case '-': return left().getValue() - right().getValue(); case '*': return left().getValue() * right().getValue(); case '/': return left().getValue() / right().getValue(); case '^': return Math.pow(left().getValue(), right().getValue()); case 'R': return Math.sqrt(left().getValue()); default: throw new IllegalStateException(); } } @Override public String toString() { return (operator == null ? "Value " + value : "Operator '" + operator + '\'') + " | " + getValue(); } } //**************** MAIN **************** public static void main(String[] args) { main(CalculatorParser3.class); } } CalculatorParser4.java000066400000000000000000000066651421263112100336550ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Rule; import org.parboiled.annotations.BuildParseTree; import org.parboiled.examples.calculators.CalculatorParser3.CalcNode; import org.parboiled.support.Var; /** * A calculator parser defining the same language as the CalculatorParser3 but using a rule building helper methods * to Factor out common constructs. */ @BuildParseTree public class CalculatorParser4 extends CalculatorParser { @Override public Rule InputLine() { return Sequence(Expression(), EOI); } public Rule Expression() { return OperatorRule(Term(), FirstOf("+ ", "- ")); } public Rule Term() { return OperatorRule(Factor(), FirstOf("* ", "/ ")); } public Rule Factor() { // by using toRule("^ ") instead of Ch('^') we make use of the fromCharLiteral(...) transformation below return OperatorRule(Atom(), toRule("^ ")); } public Rule OperatorRule(Rule subRule, Rule operatorRule) { Var op = new Var(); return Sequence( subRule, ZeroOrMore( operatorRule, op.set(matchedChar()), subRule, push(new CalcNode(op.get(), pop(1), pop())) ) ); } public Rule Atom() { return FirstOf(Number(), SquareRoot(), Parens()); } public Rule SquareRoot() { return Sequence("SQRT", Parens(), push(new CalcNode('R', pop(), null))); } public Rule Parens() { return Sequence("( ", Expression(), ") "); } public Rule Number() { return Sequence( Sequence( Optional(Ch('-')), OneOrMore(Digit()), Optional(Ch('.'), OneOrMore(Digit())) ), // the action uses a default string in case it is run during error recovery (resynchronization) push(new CalcNode(Double.parseDouble(matchOrDefault("0")))), WhiteSpace() ); } public Rule Digit() { return CharRange('0', '9'); } public Rule WhiteSpace() { return ZeroOrMore(AnyOf(" \t\f")); } // we redefine the rule creation for string literals to automatically match trailing whitespace if the string // literal ends with a space character, this way we don't have to insert extra whitespace() rules after each // character or string literal @Override protected Rule fromStringLiteral(String string) { return string.endsWith(" ") ? Sequence(String(string.substring(0, string.length() - 1)), WhiteSpace()) : String(string); } //**************** MAIN **************** public static void main(String[] args) { main(CalculatorParser4.class); } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/indenting/000077500000000000000000000000001421263112100271655ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/indenting/IndentNode.java000066400000000000000000000011111421263112100320510ustar00rootroot00000000000000package org.parboiled.examples.indenting; import java.util.ArrayList; import java.util.List; public class IndentNode { private final String name; private final List children = new ArrayList(); public IndentNode(String name) { this.name = name; } public String getName() { return name; } public boolean addChild(IndentNode child) { children.add(child); return true; } List getChildren() { return children; } @Override public String toString() { return "IndentNode [name=" + name + ", children=" + children + "]"; } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/indenting/Main.java000066400000000000000000000021061421263112100307130ustar00rootroot00000000000000package org.parboiled.examples.indenting; import static org.parboiled.support.ParseTreeUtils.printNodeTree; import org.parboiled.Parboiled; import org.parboiled.buffers.IndentDedentInputBuffer; import org.parboiled.errors.ErrorUtils; import org.parboiled.parserunners.ReportingParseRunner; import org.parboiled.support.ParsingResult; public class Main { public static void main(String[] args) { SimpleIndent parser = Parboiled.createParser(SimpleIndent.class); String input = "NodeA \n\tNodeB\n\tNodeC \n\t\tNodeD \nNodeE"; ParsingResult result = new ReportingParseRunner(parser.Parent()) .run(new IndentDedentInputBuffer(input.toCharArray(), 2, ";", true, true)); if (!result.parseErrors.isEmpty()) { System.out.println(ErrorUtils.printParseError(result.parseErrors .get(0))); } else { System.out.println("NodeTree: " + printNodeTree(result) + '\n'); Object value = result.parseTreeRoot.getValue(); System.out.println(value.toString()); } } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/indenting/SimpleIndent.java000066400000000000000000000027201421263112100324240ustar00rootroot00000000000000package org.parboiled.examples.indenting; import org.parboiled.BaseParser; import org.parboiled.Rule; import org.parboiled.annotations.BuildParseTree; @BuildParseTree public class SimpleIndent extends BaseParser { Rule Parent() { return Sequence(push(new IndentNode("root")), OneOrMore(Data()), EOI); } Rule Data() { return Sequence(Identifier(), push(new IndentNode(match())), peek(1) .addChild(peek()), Optional(Sequence(Spacing(), ChildNodeList())), drop()); } Rule ChildNodeList() { return Sequence(INDENT, Spacing(), OneOrMore(Data(), Spacing()), DEDENT); } Rule Identifier() { return Sequence(PN_CHARS_U(), ZeroOrMore(PN_CHARS_DIGIT_U())); } public Rule PN_CHARS_DIGIT_U() { return FirstOf(PN_CHARS_U(), DIGIT()); } public Rule PN_CHARS_U() { return FirstOf(PN_CHARS_BASE(), '_'); } public Rule PN_CHARS_BASE() { return FirstOf( CharRange('A', 'Z'), CharRange('a', 'z'), CharRange('\u00C0', '\u00D6'), CharRange('\u00D8', '\u00F6'), CharRange('\u00F8', '\u02FF'), CharRange('\u0370', '\u037D'), CharRange('\u037F', '\u1FFF'), CharRange('\u200C', '\u200D'), CharRange('\u2070', '\u218F'), CharRange('\u2C00', '\u2FEF'), CharRange('\u3001', '\uD7FF'), CharRange('\uF900', '\uFDCF'), CharRange('\uFDF0', '\uFFFD') ); } public Rule DIGIT() { return CharRange('0', '9'); } Rule Spacing() { return ZeroOrMore(AnyOf(" \t\r\n\f").label("Whitespace")); } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/java/000077500000000000000000000000001421263112100261275ustar00rootroot00000000000000AbstractJavaCharacterMatcher.java000066400000000000000000000030041421263112100343760ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/java/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; import org.parboiled.MatcherContext; import org.parboiled.matchers.CustomMatcher; public abstract class AbstractJavaCharacterMatcher extends CustomMatcher { protected AbstractJavaCharacterMatcher(String label) { super(label); } @Override public final boolean isSingleCharMatcher() { return true; } @Override public final boolean canMatchEmpty() { return false; } @Override public boolean isStarterChar(char c) { return acceptChar(c); } @Override public final char getStarterChar() { return 'a'; } public final boolean match(MatcherContext context) { if (!acceptChar(context.getCurrentChar())) { return false; } context.advanceIndex(1); context.createNode(); return true; } protected abstract boolean acceptChar(char c); } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/java/JavaLetterMatcher.java000066400000000000000000000015771421263112100323510ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; public class JavaLetterMatcher extends AbstractJavaCharacterMatcher { public JavaLetterMatcher() { super("Letter"); } @Override protected boolean acceptChar(char c) { return Character.isJavaIdentifierStart(c); } } JavaLetterOrDigitMatcher.java000066400000000000000000000016231421263112100335440ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/java/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; public class JavaLetterOrDigitMatcher extends AbstractJavaCharacterMatcher { public JavaLetterOrDigitMatcher() { super("LetterOrDigit"); } @Override protected boolean acceptChar(char c) { return Character.isJavaIdentifierPart(c); } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/java/JavaParser.java000066400000000000000000001117141421263112100310350ustar00rootroot00000000000000//=========================================================================== // // Parsing Expression Grammar for Java 1.6 as a parboiled parser. // Based on Chapters 3 and 18 of Java Language Specification, Third Edition (JLS) // at http://java.sun.com/docs/books/jls/third_edition/html/j3TOC.html. // //--------------------------------------------------------------------------- // // Copyright (C) 2010 by Mathias Doenitz // Based on the Mouse 1.3 grammar for Java 1.6, which is // Copyright (C) 2006, 2009, 2010, 2011 by Roman R Redziejowski (www.romanredz.se). // // The author gives unlimited permission to copy and distribute // this file, with or without modifications, as long as this notice // is preserved, and any changes are properly documented. // // This file is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // //--------------------------------------------------------------------------- // // Change log // 2006-12-06 Posted on Internet. // 2009-04-04 Modified to conform to Mouse syntax: // Underscore removed from names // \f in Space replaced by Unicode for FormFeed. // 2009-07-10 Unused rule THREADSAFE removed. // 2009-07-10 Copying and distribution conditions relaxed by the author. // 2010-01-28 Transcribed to parboiled // 2010-02-01 Fixed problem in rule "FormalParameterDecls" // 2010-03-29 Fixed problem in "annotation" // 2010-03-31 Fixed problem in unicode escapes, String literals and line comments // (Thanks to Reinier Zwitserloot for the finds) // 2010-07-26 Fixed problem in LocalVariableDeclarationStatement (accept annotations), // HexFloat (HexSignificant) and AnnotationTypeDeclaration (bug in the JLS!) // 2010-10-07 Added full support of Unicode Identifiers as set forth in the JLS // (Thanks for Ville Peurala for the patch) // 2011-07-23 Transcribed all missing fixes from Romans Mouse grammar (http://www.romanredz.se/papers/Java.1.6.peg) // //=========================================================================== package org.parboiled.examples.java; import org.parboiled.BaseParser; import org.parboiled.Rule; import org.parboiled.annotations.*; @SuppressWarnings({"InfiniteRecursion"}) @BuildParseTree public class JavaParser extends BaseParser { //------------------------------------------------------------------------- // Compilation Unit //------------------------------------------------------------------------- public Rule CompilationUnit() { return Sequence( Spacing(), Optional(PackageDeclaration()), ZeroOrMore(ImportDeclaration()), ZeroOrMore(TypeDeclaration()), EOI ); } Rule PackageDeclaration() { return Sequence(ZeroOrMore(Annotation()), Sequence(PACKAGE, QualifiedIdentifier(), SEMI)); } Rule ImportDeclaration() { return Sequence( IMPORT, Optional(STATIC), QualifiedIdentifier(), Optional(DOT, STAR), SEMI ); } Rule TypeDeclaration() { return FirstOf( Sequence( ZeroOrMore(Modifier()), FirstOf( ClassDeclaration(), EnumDeclaration(), InterfaceDeclaration(), AnnotationTypeDeclaration() ) ), SEMI ); } //------------------------------------------------------------------------- // Class Declaration //------------------------------------------------------------------------- Rule ClassDeclaration() { return Sequence( CLASS, Identifier(), Optional(TypeParameters()), Optional(EXTENDS, ClassType()), Optional(IMPLEMENTS, ClassTypeList()), ClassBody() ); } Rule ClassBody() { return Sequence(LWING, ZeroOrMore(ClassBodyDeclaration()), RWING); } Rule ClassBodyDeclaration() { return FirstOf( SEMI, Sequence(Optional(STATIC), Block()), Sequence(ZeroOrMore(Modifier()), MemberDecl()) ); } Rule MemberDecl() { return FirstOf( Sequence(TypeParameters(), GenericMethodOrConstructorRest()), Sequence(Type(), Identifier(), MethodDeclaratorRest()), Sequence(Type(), VariableDeclarators(), SEMI), Sequence(VOID, Identifier(), VoidMethodDeclaratorRest()), Sequence(Identifier(), ConstructorDeclaratorRest()), InterfaceDeclaration(), ClassDeclaration(), EnumDeclaration(), AnnotationTypeDeclaration() ); } Rule GenericMethodOrConstructorRest() { return FirstOf( Sequence(FirstOf(Type(), VOID), Identifier(), MethodDeclaratorRest()), Sequence(Identifier(), ConstructorDeclaratorRest()) ); } Rule MethodDeclaratorRest() { return Sequence( FormalParameters(), ZeroOrMore(Dim()), Optional(THROWS, ClassTypeList()), FirstOf(MethodBody(), SEMI) ); } Rule VoidMethodDeclaratorRest() { return Sequence( FormalParameters(), Optional(THROWS, ClassTypeList()), FirstOf(MethodBody(), SEMI) ); } Rule ConstructorDeclaratorRest() { return Sequence(FormalParameters(), Optional(THROWS, ClassTypeList()), MethodBody()); } Rule MethodBody() { return Block(); } //------------------------------------------------------------------------- // Interface Declaration //------------------------------------------------------------------------- Rule InterfaceDeclaration() { return Sequence( INTERFACE, Identifier(), Optional(TypeParameters()), Optional(EXTENDS, ClassTypeList()), InterfaceBody() ); } Rule InterfaceBody() { return Sequence(LWING, ZeroOrMore(InterfaceBodyDeclaration()), RWING); } Rule InterfaceBodyDeclaration() { return FirstOf( Sequence(ZeroOrMore(Modifier()), InterfaceMemberDecl()), SEMI ); } Rule InterfaceMemberDecl() { return FirstOf( InterfaceMethodOrFieldDecl(), InterfaceGenericMethodDecl(), Sequence(VOID, Identifier(), VoidInterfaceMethodDeclaratorsRest()), InterfaceDeclaration(), AnnotationTypeDeclaration(), ClassDeclaration(), EnumDeclaration() ); } Rule InterfaceMethodOrFieldDecl() { return Sequence(Sequence(Type(), Identifier()), InterfaceMethodOrFieldRest()); } Rule InterfaceMethodOrFieldRest() { return FirstOf( Sequence(ConstantDeclaratorsRest(), SEMI), InterfaceMethodDeclaratorRest() ); } Rule InterfaceMethodDeclaratorRest() { return Sequence( FormalParameters(), ZeroOrMore(Dim()), Optional(THROWS, ClassTypeList()), SEMI ); } Rule InterfaceGenericMethodDecl() { return Sequence(TypeParameters(), FirstOf(Type(), VOID), Identifier(), InterfaceMethodDeclaratorRest()); } Rule VoidInterfaceMethodDeclaratorsRest() { return Sequence(FormalParameters(), Optional(THROWS, ClassTypeList()), SEMI); } Rule ConstantDeclaratorsRest() { return Sequence(ConstantDeclaratorRest(), ZeroOrMore(COMMA, ConstantDeclarator())); } Rule ConstantDeclarator() { return Sequence(Identifier(), ConstantDeclaratorRest()); } Rule ConstantDeclaratorRest() { return Sequence(ZeroOrMore(Dim()), EQU, VariableInitializer()); } //------------------------------------------------------------------------- // Enum Declaration //------------------------------------------------------------------------- Rule EnumDeclaration() { return Sequence( ENUM, Identifier(), Optional(IMPLEMENTS, ClassTypeList()), EnumBody() ); } Rule EnumBody() { return Sequence( LWING, Optional(EnumConstants()), Optional(COMMA), Optional(EnumBodyDeclarations()), RWING ); } Rule EnumConstants() { return Sequence(EnumConstant(), ZeroOrMore(COMMA, EnumConstant())); } Rule EnumConstant() { return Sequence( ZeroOrMore(Annotation()), Identifier(), Optional(Arguments()), Optional(ClassBody()) ); } Rule EnumBodyDeclarations() { return Sequence(SEMI, ZeroOrMore(ClassBodyDeclaration())); } //------------------------------------------------------------------------- // Variable Declarations //------------------------------------------------------------------------- Rule LocalVariableDeclarationStatement() { return Sequence(ZeroOrMore(FirstOf(FINAL, Annotation())), Type(), VariableDeclarators(), SEMI); } Rule VariableDeclarators() { return Sequence(VariableDeclarator(), ZeroOrMore(COMMA, VariableDeclarator())); } Rule VariableDeclarator() { return Sequence(Identifier(), ZeroOrMore(Dim()), Optional(EQU, VariableInitializer())); } //------------------------------------------------------------------------- // Formal Parameters //------------------------------------------------------------------------- Rule FormalParameters() { return Sequence(LPAR, Optional(FormalParameterDecls()), RPAR); } Rule FormalParameter() { return Sequence(ZeroOrMore(FirstOf(FINAL, Annotation())), Type(), VariableDeclaratorId()); } Rule FormalParameterDecls() { return Sequence(ZeroOrMore(FirstOf(FINAL, Annotation())), Type(), FormalParameterDeclsRest()); } Rule FormalParameterDeclsRest() { return FirstOf( Sequence(VariableDeclaratorId(), Optional(COMMA, FormalParameterDecls())), Sequence(ELLIPSIS, VariableDeclaratorId()) ); } Rule VariableDeclaratorId() { return Sequence(Identifier(), ZeroOrMore(Dim())); } //------------------------------------------------------------------------- // Statements //------------------------------------------------------------------------- Rule Block() { return Sequence(LWING, BlockStatements(), RWING); } Rule BlockStatements() { return ZeroOrMore(BlockStatement()); } Rule BlockStatement() { return FirstOf( LocalVariableDeclarationStatement(), Sequence(ZeroOrMore(Modifier()), FirstOf(ClassDeclaration(), EnumDeclaration())), Statement() ); } Rule Statement() { return FirstOf( Block(), Sequence(ASSERT, Expression(), Optional(COLON, Expression()), SEMI), Sequence(IF, ParExpression(), Statement(), Optional(ELSE, Statement())), Sequence(FOR, LPAR, Optional(ForInit()), SEMI, Optional(Expression()), SEMI, Optional(ForUpdate()), RPAR, Statement()), Sequence(FOR, LPAR, FormalParameter(), COLON, Expression(), RPAR, Statement()), Sequence(WHILE, ParExpression(), Statement()), Sequence(DO, Statement(), WHILE, ParExpression(), SEMI), Sequence(TRY, Block(), FirstOf(Sequence(OneOrMore(Catch_()), Optional(Finally_())), Finally_())), Sequence(SWITCH, ParExpression(), LWING, SwitchBlockStatementGroups(), RWING), Sequence(SYNCHRONIZED, ParExpression(), Block()), Sequence(RETURN, Optional(Expression()), SEMI), Sequence(THROW, Expression(), SEMI), Sequence(BREAK, Optional(Identifier()), SEMI), Sequence(CONTINUE, Optional(Identifier()), SEMI), Sequence(Sequence(Identifier(), COLON), Statement()), Sequence(StatementExpression(), SEMI), SEMI ); } Rule Catch_() { return Sequence(CATCH, LPAR, FormalParameter(), RPAR, Block()); } Rule Finally_() { return Sequence(FINALLY, Block()); } Rule SwitchBlockStatementGroups() { return ZeroOrMore(SwitchBlockStatementGroup()); } Rule SwitchBlockStatementGroup() { return Sequence(SwitchLabel(), BlockStatements()); } Rule SwitchLabel() { return FirstOf( Sequence(CASE, ConstantExpression(), COLON), Sequence(CASE, EnumConstantName(), COLON), Sequence(DEFAULT, COLON) ); } Rule ForInit() { return FirstOf( Sequence(ZeroOrMore(FirstOf(FINAL, Annotation())), Type(), VariableDeclarators()), Sequence(StatementExpression(), ZeroOrMore(COMMA, StatementExpression())) ); } Rule ForUpdate() { return Sequence(StatementExpression(), ZeroOrMore(COMMA, StatementExpression())); } Rule EnumConstantName() { return Identifier(); } //------------------------------------------------------------------------- // Expressions //------------------------------------------------------------------------- // The following is more generous than the definition in section 14.8, // which allows only specific forms of Expression. Rule StatementExpression() { return Expression(); } Rule ConstantExpression() { return Expression(); } // The following definition is part of the modification in JLS Chapter 18 // to minimize look ahead. In JLS Chapter 15.27, Expression is defined // as AssignmentExpression, which is effectively defined as // (LeftHandSide AssignmentOperator)* ConditionalExpression. // The following is obtained by allowing ANY ConditionalExpression // as LeftHandSide, which results in accepting statements like 5 = a. Rule Expression() { return Sequence( ConditionalExpression(), ZeroOrMore(AssignmentOperator(), ConditionalExpression()) ); } Rule AssignmentOperator() { return FirstOf(EQU, PLUSEQU, MINUSEQU, STAREQU, DIVEQU, ANDEQU, OREQU, HATEQU, MODEQU, SLEQU, SREQU, BSREQU); } Rule ConditionalExpression() { return Sequence( ConditionalOrExpression(), ZeroOrMore(QUERY, Expression(), COLON, ConditionalOrExpression()) ); } Rule ConditionalOrExpression() { return Sequence( ConditionalAndExpression(), ZeroOrMore(OROR, ConditionalAndExpression()) ); } Rule ConditionalAndExpression() { return Sequence( InclusiveOrExpression(), ZeroOrMore(ANDAND, InclusiveOrExpression()) ); } Rule InclusiveOrExpression() { return Sequence( ExclusiveOrExpression(), ZeroOrMore(OR, ExclusiveOrExpression()) ); } Rule ExclusiveOrExpression() { return Sequence( AndExpression(), ZeroOrMore(HAT, AndExpression()) ); } Rule AndExpression() { return Sequence( EqualityExpression(), ZeroOrMore(AND, EqualityExpression()) ); } Rule EqualityExpression() { return Sequence( RelationalExpression(), ZeroOrMore(FirstOf(EQUAL, NOTEQUAL), RelationalExpression()) ); } Rule RelationalExpression() { return Sequence( ShiftExpression(), ZeroOrMore( FirstOf( Sequence(FirstOf(LE, GE, LT, GT), ShiftExpression()), Sequence(INSTANCEOF, ReferenceType()) ) ) ); } Rule ShiftExpression() { return Sequence( AdditiveExpression(), ZeroOrMore(FirstOf(SL, SR, BSR), AdditiveExpression()) ); } Rule AdditiveExpression() { return Sequence( MultiplicativeExpression(), ZeroOrMore(FirstOf(PLUS, MINUS), MultiplicativeExpression()) ); } Rule MultiplicativeExpression() { return Sequence( UnaryExpression(), ZeroOrMore(FirstOf(STAR, DIV, MOD), UnaryExpression()) ); } Rule UnaryExpression() { return FirstOf( Sequence(PrefixOp(), UnaryExpression()), Sequence(LPAR, Type(), RPAR, UnaryExpression()), Sequence(Primary(), ZeroOrMore(Selector()), ZeroOrMore(PostFixOp())) ); } Rule Primary() { return FirstOf( ParExpression(), Sequence( NonWildcardTypeArguments(), FirstOf(ExplicitGenericInvocationSuffix(), Sequence(THIS, Arguments())) ), Sequence(THIS, Optional(Arguments())), Sequence(SUPER, SuperSuffix()), Literal(), Sequence(NEW, Creator()), Sequence(QualifiedIdentifier(), Optional(IdentifierSuffix())), Sequence(BasicType(), ZeroOrMore(Dim()), DOT, CLASS), Sequence(VOID, DOT, CLASS) ); } Rule IdentifierSuffix() { return FirstOf( Sequence(LBRK, FirstOf( Sequence(RBRK, ZeroOrMore(Dim()), DOT, CLASS), Sequence(Expression(), RBRK) ) ), Arguments(), Sequence( DOT, FirstOf( CLASS, ExplicitGenericInvocation(), THIS, Sequence(SUPER, Arguments()), Sequence(NEW, Optional(NonWildcardTypeArguments()), InnerCreator()) ) ) ); } Rule ExplicitGenericInvocation() { return Sequence(NonWildcardTypeArguments(), ExplicitGenericInvocationSuffix()); } Rule NonWildcardTypeArguments() { return Sequence(LPOINT, ReferenceType(), ZeroOrMore(COMMA, ReferenceType()), RPOINT); } Rule ExplicitGenericInvocationSuffix() { return FirstOf( Sequence(SUPER, SuperSuffix()), Sequence(Identifier(), Arguments()) ); } Rule PrefixOp() { return FirstOf(INC, DEC, BANG, TILDA, PLUS, MINUS); } Rule PostFixOp() { return FirstOf(INC, DEC); } Rule Selector() { return FirstOf( Sequence(DOT, Identifier(), Optional(Arguments())), Sequence(DOT, ExplicitGenericInvocation()), Sequence(DOT, THIS), Sequence(DOT, SUPER, SuperSuffix()), Sequence(DOT, NEW, Optional(NonWildcardTypeArguments()), InnerCreator()), DimExpr() ); } Rule SuperSuffix() { return FirstOf(Arguments(), Sequence(DOT, Identifier(), Optional(Arguments()))); } @MemoMismatches Rule BasicType() { return Sequence( FirstOf("byte", "short", "char", "int", "long", "float", "double", "boolean"), TestNot(LetterOrDigit()), Spacing() ); } Rule Arguments() { return Sequence( LPAR, Optional(Expression(), ZeroOrMore(COMMA, Expression())), RPAR ); } Rule Creator() { return FirstOf( Sequence(Optional(NonWildcardTypeArguments()), CreatedName(), ClassCreatorRest()), Sequence(Optional(NonWildcardTypeArguments()), FirstOf(ClassType(), BasicType()), ArrayCreatorRest()) ); } Rule CreatedName() { return Sequence( Identifier(), Optional(NonWildcardTypeArguments()), ZeroOrMore(DOT, Identifier(), Optional(NonWildcardTypeArguments())) ); } Rule InnerCreator() { return Sequence(Identifier(), ClassCreatorRest()); } // The following is more generous than JLS 15.10. According to that definition, // BasicType must be followed by at least one DimExpr or by ArrayInitializer. Rule ArrayCreatorRest() { return Sequence( LBRK, FirstOf( Sequence(RBRK, ZeroOrMore(Dim()), ArrayInitializer()), Sequence(Expression(), RBRK, ZeroOrMore(DimExpr()), ZeroOrMore(Dim())) ) ); } Rule ClassCreatorRest() { return Sequence(Arguments(), Optional(ClassBody())); } Rule ArrayInitializer() { return Sequence( LWING, Optional( VariableInitializer(), ZeroOrMore(COMMA, VariableInitializer()) ), Optional(COMMA), RWING ); } Rule VariableInitializer() { return FirstOf(ArrayInitializer(), Expression()); } Rule ParExpression() { return Sequence(LPAR, Expression(), RPAR); } Rule QualifiedIdentifier() { return Sequence(Identifier(), ZeroOrMore(DOT, Identifier())); } Rule Dim() { return Sequence(LBRK, RBRK); } Rule DimExpr() { return Sequence(LBRK, Expression(), RBRK); } //------------------------------------------------------------------------- // Types and Modifiers //------------------------------------------------------------------------- Rule Type() { return Sequence(FirstOf(BasicType(), ClassType()), ZeroOrMore(Dim())); } Rule ReferenceType() { return FirstOf( Sequence(BasicType(), OneOrMore(Dim())), Sequence(ClassType(), ZeroOrMore(Dim())) ); } Rule ClassType() { return Sequence( Identifier(), Optional(TypeArguments()), ZeroOrMore(DOT, Identifier(), Optional(TypeArguments())) ); } Rule ClassTypeList() { return Sequence(ClassType(), ZeroOrMore(COMMA, ClassType())); } Rule TypeArguments() { return Sequence(LPOINT, TypeArgument(), ZeroOrMore(COMMA, TypeArgument()), RPOINT); } Rule TypeArgument() { return FirstOf( ReferenceType(), Sequence(QUERY, Optional(FirstOf(EXTENDS, SUPER), ReferenceType())) ); } Rule TypeParameters() { return Sequence(LPOINT, TypeParameter(), ZeroOrMore(COMMA, TypeParameter()), RPOINT); } Rule TypeParameter() { return Sequence(Identifier(), Optional(EXTENDS, Bound())); } Rule Bound() { return Sequence(ClassType(), ZeroOrMore(AND, ClassType())); } // the following common definition of Modifier is part of the modification // in JLS Chapter 18 to minimize look ahead. The main body of JLS has // different lists of modifiers for different language elements. Rule Modifier() { return FirstOf( Annotation(), Sequence( FirstOf("public", "protected", "private", "static", "abstract", "final", "native", "synchronized", "transient", "volatile", "strictfp"), TestNot(LetterOrDigit()), Spacing() ) ); } //------------------------------------------------------------------------- // Annotations //------------------------------------------------------------------------- Rule AnnotationTypeDeclaration() { return Sequence(AT, INTERFACE, Identifier(), AnnotationTypeBody()); } Rule AnnotationTypeBody() { return Sequence(LWING, ZeroOrMore(AnnotationTypeElementDeclaration()), RWING); } Rule AnnotationTypeElementDeclaration() { return FirstOf( Sequence(ZeroOrMore(Modifier()), AnnotationTypeElementRest()), SEMI ); } Rule AnnotationTypeElementRest() { return FirstOf( Sequence(Type(), AnnotationMethodOrConstantRest(), SEMI), ClassDeclaration(), EnumDeclaration(), InterfaceDeclaration(), AnnotationTypeDeclaration() ); } Rule AnnotationMethodOrConstantRest() { return FirstOf(AnnotationMethodRest(), AnnotationConstantRest()); } Rule AnnotationMethodRest() { return Sequence(Identifier(), LPAR, RPAR, Optional(DefaultValue())); } Rule AnnotationConstantRest() { return VariableDeclarators(); } Rule DefaultValue() { return Sequence(DEFAULT, ElementValue()); } @MemoMismatches Rule Annotation() { return Sequence(AT, QualifiedIdentifier(), Optional(AnnotationRest())); } Rule AnnotationRest() { return FirstOf(NormalAnnotationRest(), SingleElementAnnotationRest()); } Rule NormalAnnotationRest() { return Sequence(LPAR, Optional(ElementValuePairs()), RPAR); } Rule ElementValuePairs() { return Sequence(ElementValuePair(), ZeroOrMore(COMMA, ElementValuePair())); } Rule ElementValuePair() { return Sequence(Identifier(), EQU, ElementValue()); } Rule ElementValue() { return FirstOf(ConditionalExpression(), Annotation(), ElementValueArrayInitializer()); } Rule ElementValueArrayInitializer() { return Sequence(LWING, Optional(ElementValues()), Optional(COMMA), RWING); } Rule ElementValues() { return Sequence(ElementValue(), ZeroOrMore(COMMA, ElementValue())); } Rule SingleElementAnnotationRest() { return Sequence(LPAR, ElementValue(), RPAR); } //------------------------------------------------------------------------- // JLS 3.6-7 Spacing //------------------------------------------------------------------------- @SuppressNode Rule Spacing() { return ZeroOrMore(FirstOf( // whitespace OneOrMore(AnyOf(" \t\r\n\f").label("Whitespace")), // traditional comment Sequence("/*", ZeroOrMore(TestNot("*/"), ANY), "*/"), // end of line comment Sequence( "//", ZeroOrMore(TestNot(AnyOf("\r\n")), ANY), FirstOf("\r\n", '\r', '\n', EOI) ) )); } //------------------------------------------------------------------------- // JLS 3.8 Identifiers //------------------------------------------------------------------------- @SuppressSubnodes @MemoMismatches Rule Identifier() { return Sequence(TestNot(Keyword()), Letter(), ZeroOrMore(LetterOrDigit()), Spacing()); } // JLS defines letters and digits as Unicode characters recognized // as such by special Java procedures. Rule Letter() { // switch to this "reduced" character space version for a ~10% parser performance speedup //return FirstOf(CharRange('a', 'z'), CharRange('A', 'Z'), '_', '$'); return FirstOf(Sequence('\\', UnicodeEscape()), new JavaLetterMatcher()); } @MemoMismatches Rule LetterOrDigit() { // switch to this "reduced" character space version for a ~10% parser performance speedup //return FirstOf(CharRange('a', 'z'), CharRange('A', 'Z'), CharRange('0', '9'), '_', '$'); return FirstOf(Sequence('\\', UnicodeEscape()), new JavaLetterOrDigitMatcher()); } //------------------------------------------------------------------------- // JLS 3.9 Keywords //------------------------------------------------------------------------- @MemoMismatches Rule Keyword() { return Sequence( FirstOf("assert", "break", "case", "catch", "class", "const", "continue", "default", "do", "else", "enum", "extends", "finally", "final", "for", "goto", "if", "implements", "import", "interface", "instanceof", "new", "package", "return", "static", "super", "switch", "synchronized", "this", "throws", "throw", "try", "void", "while"), TestNot(LetterOrDigit()) ); } public final Rule ASSERT = Keyword("assert"); public final Rule BREAK = Keyword("break"); public final Rule CASE = Keyword("case"); public final Rule CATCH = Keyword("catch"); public final Rule CLASS = Keyword("class"); public final Rule CONTINUE = Keyword("continue"); public final Rule DEFAULT = Keyword("default"); public final Rule DO = Keyword("do"); public final Rule ELSE = Keyword("else"); public final Rule ENUM = Keyword("enum"); public final Rule EXTENDS = Keyword("extends"); public final Rule FINALLY = Keyword("finally"); public final Rule FINAL = Keyword("final"); public final Rule FOR = Keyword("for"); public final Rule IF = Keyword("if"); public final Rule IMPLEMENTS = Keyword("implements"); public final Rule IMPORT = Keyword("import"); public final Rule INTERFACE = Keyword("interface"); public final Rule INSTANCEOF = Keyword("instanceof"); public final Rule NEW = Keyword("new"); public final Rule PACKAGE = Keyword("package"); public final Rule RETURN = Keyword("return"); public final Rule STATIC = Keyword("static"); public final Rule SUPER = Keyword("super"); public final Rule SWITCH = Keyword("switch"); public final Rule SYNCHRONIZED = Keyword("synchronized"); public final Rule THIS = Keyword("this"); public final Rule THROWS = Keyword("throws"); public final Rule THROW = Keyword("throw"); public final Rule TRY = Keyword("try"); public final Rule VOID = Keyword("void"); public final Rule WHILE = Keyword("while"); @SuppressNode @DontLabel Rule Keyword(String keyword) { return Terminal(keyword, LetterOrDigit()); } //------------------------------------------------------------------------- // JLS 3.10 Literals //------------------------------------------------------------------------- Rule Literal() { return Sequence( FirstOf( FloatLiteral(), IntegerLiteral(), CharLiteral(), StringLiteral(), Sequence("true", TestNot(LetterOrDigit())), Sequence("false", TestNot(LetterOrDigit())), Sequence("null", TestNot(LetterOrDigit())) ), Spacing() ); } @SuppressSubnodes Rule IntegerLiteral() { return Sequence(FirstOf(HexNumeral(), OctalNumeral(), DecimalNumeral()), Optional(AnyOf("lL"))); } @SuppressSubnodes Rule DecimalNumeral() { return FirstOf('0', Sequence(CharRange('1', '9'), ZeroOrMore(Digit()))); } @SuppressSubnodes @MemoMismatches Rule HexNumeral() { return Sequence('0', IgnoreCase('x'), OneOrMore(HexDigit())); } Rule HexDigit() { return FirstOf(CharRange('a', 'f'), CharRange('A', 'F'), CharRange('0', '9')); } @SuppressSubnodes Rule OctalNumeral() { return Sequence('0', OneOrMore(CharRange('0', '7'))); } Rule FloatLiteral() { return FirstOf(HexFloat(), DecimalFloat()); } @SuppressSubnodes Rule DecimalFloat() { return FirstOf( Sequence(OneOrMore(Digit()), '.', ZeroOrMore(Digit()), Optional(Exponent()), Optional(AnyOf("fFdD"))), Sequence('.', OneOrMore(Digit()), Optional(Exponent()), Optional(AnyOf("fFdD"))), Sequence(OneOrMore(Digit()), Exponent(), Optional(AnyOf("fFdD"))), Sequence(OneOrMore(Digit()), Optional(Exponent()), AnyOf("fFdD")) ); } Rule Exponent() { return Sequence(AnyOf("eE"), Optional(AnyOf("+-")), OneOrMore(Digit())); } Rule Digit() { return CharRange('0', '9'); } @SuppressSubnodes Rule HexFloat() { return Sequence(HexSignificant(), BinaryExponent(), Optional(AnyOf("fFdD"))); } Rule HexSignificant() { return FirstOf( Sequence(FirstOf("0x", "0X"), ZeroOrMore(HexDigit()), '.', OneOrMore(HexDigit())), Sequence(HexNumeral(), Optional('.')) ); } Rule BinaryExponent() { return Sequence(AnyOf("pP"), Optional(AnyOf("+-")), OneOrMore(Digit())); } Rule CharLiteral() { return Sequence( '\'', FirstOf(Escape(), Sequence(TestNot(AnyOf("'\\")), ANY)).suppressSubnodes(), '\'' ); } Rule StringLiteral() { return Sequence( '"', ZeroOrMore( FirstOf( Escape(), Sequence(TestNot(AnyOf("\r\n\"\\")), ANY) ) ).suppressSubnodes(), '"' ); } Rule Escape() { return Sequence('\\', FirstOf(AnyOf("btnfr\"\'\\"), OctalEscape(), UnicodeEscape())); } Rule OctalEscape() { return FirstOf( Sequence(CharRange('0', '3'), CharRange('0', '7'), CharRange('0', '7')), Sequence(CharRange('0', '7'), CharRange('0', '7')), CharRange('0', '7') ); } Rule UnicodeEscape() { return Sequence(OneOrMore('u'), HexDigit(), HexDigit(), HexDigit(), HexDigit()); } //------------------------------------------------------------------------- // JLS 3.11-12 Separators, Operators //------------------------------------------------------------------------- final Rule AT = Terminal("@"); final Rule AND = Terminal("&", AnyOf("=&")); final Rule ANDAND = Terminal("&&"); final Rule ANDEQU = Terminal("&="); final Rule BANG = Terminal("!", Ch('=')); final Rule BSR = Terminal(">>>", Ch('=')); final Rule BSREQU = Terminal(">>>="); final Rule COLON = Terminal(":"); final Rule COMMA = Terminal(","); final Rule DEC = Terminal("--"); final Rule DIV = Terminal("/", Ch('=')); final Rule DIVEQU = Terminal("/="); final Rule DOT = Terminal("."); final Rule ELLIPSIS = Terminal("..."); final Rule EQU = Terminal("=", Ch('=')); final Rule EQUAL = Terminal("=="); final Rule GE = Terminal(">="); final Rule GT = Terminal(">", AnyOf("=>")); final Rule HAT = Terminal("^", Ch('=')); final Rule HATEQU = Terminal("^="); final Rule INC = Terminal("++"); final Rule LBRK = Terminal("["); final Rule LE = Terminal("<="); final Rule LPAR = Terminal("("); final Rule LPOINT = Terminal("<"); final Rule LT = Terminal("<", AnyOf("=<")); final Rule LWING = Terminal("{"); final Rule MINUS = Terminal("-", AnyOf("=-")); final Rule MINUSEQU = Terminal("-="); final Rule MOD = Terminal("%", Ch('=')); final Rule MODEQU = Terminal("%="); final Rule NOTEQUAL = Terminal("!="); final Rule OR = Terminal("|", AnyOf("=|")); final Rule OREQU = Terminal("|="); final Rule OROR = Terminal("||"); final Rule PLUS = Terminal("+", AnyOf("=+")); final Rule PLUSEQU = Terminal("+="); final Rule QUERY = Terminal("?"); final Rule RBRK = Terminal("]"); final Rule RPAR = Terminal(")"); final Rule RPOINT = Terminal(">"); final Rule RWING = Terminal("}"); final Rule SEMI = Terminal(";"); final Rule SL = Terminal("<<", Ch('=')); final Rule SLEQU = Terminal("<<="); final Rule SR = Terminal(">>", AnyOf("=>")); final Rule SREQU = Terminal(">>="); final Rule STAR = Terminal("*", Ch('=')); final Rule STAREQU = Terminal("*="); final Rule TILDA = Terminal("~"); //------------------------------------------------------------------------- // helper methods //------------------------------------------------------------------------- @Override protected Rule fromCharLiteral(char c) { // turn of creation of parse tree nodes for single characters return super.fromCharLiteral(c).suppressNode(); } @SuppressNode @DontLabel Rule Terminal(String string) { return Sequence(string, Spacing()).label('\'' + string + '\''); } @SuppressNode @DontLabel Rule Terminal(String string, Rule mustNotFollow) { return Sequence(string, TestNot(mustNotFollow), Spacing()).label('\'' + string + '\''); } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/java/JavaParserProfiler.java000066400000000000000000000026341421263112100325400ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; import org.parboiled.parserunners.ProfilingParseRunner; import org.parboiled.Rule; import org.parboiled.support.ParsingResult; public class JavaParserProfiler extends Main { private ProfilingParseRunner parseRunner; public static void main(String[] args) { new JavaParserProfiler().run(args); } @Override protected void run(String[] args) { super.run(args); ProfilingParseRunner.Report report = parseRunner.getReport(); System.out.println(); System.out.println(report.print()); } @Override protected ParsingResult run(Rule rootRule, String sourceText) { if (parseRunner == null) { parseRunner = new ProfilingParseRunner(rootRule); } return parseRunner.run(sourceText); } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/java/Main.java000066400000000000000000000141651421263112100276650ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; import static org.parboiled.common.Preconditions.*; import org.parboiled.Parboiled; import org.parboiled.parserunners.ReportingParseRunner; import org.parboiled.Rule; import org.parboiled.support.ParsingResult; import java.io.*; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import static org.parboiled.errors.ErrorUtils.printParseErrors; public class Main { public static void main(String[] args) { new Main().run(args); } @SuppressWarnings({"ConstantConditions"}) protected void run(String[] args) { System.out.println("parboiled Java parser, performance test"); System.out.println("---------------------------------------"); System.out.print("Creating parser... :"); long start = System.currentTimeMillis(); Parboiled.createParser(JavaParser.class); time(start); System.out.print("Creating 100 more parser instances... :"); JavaParser parser = null; start = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { parser = Parboiled.createParser(JavaParser.class); } time(start); System.out.print("Creating 100 more parser instances using BaseParser.newInstance() ... :"); start = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { parser = parser.newInstance(); } time(start); start = System.currentTimeMillis(); File baseDir = args.length == 1 ? new File(args[0]) : null; if (baseDir == null || !baseDir.exists()) baseDir = new File("."); System.out.printf("Retrieving file list from '%s'", baseDir); List sources = recursiveGetAllJavaSources(baseDir, new ArrayList()); time(start); System.out.printf("Parsing all %s given java sources", sources.size()); Rule rootRule = parser.CompilationUnit().suppressNode(); // we want to see the parse-tree-less performance start = System.currentTimeMillis(); long lines = 0, characters = 0; for (File sourceFile : sources) { long dontCountStart = System.currentTimeMillis(); String sourceText = readAllText(sourceFile); start += System.currentTimeMillis() - dontCountStart; // do not count the time for reading the text file ParsingResult result = null; try { result = run(rootRule, sourceText); } catch (Exception e) { System.out.printf("\nException while parsing file '%s':\n%s", sourceFile, e); System.exit(1); } if (!result.matched) { System.out.printf("\nParse error(s) in file '%s':\n%s", sourceFile, printParseErrors(result)); System.exit(1); } else { System.out.print('.'); } lines += result.inputBuffer.getLineCount(); characters += sourceText.length(); } long time = time(start); System.out.println("Parsing performance:"); System.out.printf(" %6d Files -> %6.2f Files/sec\n", sources.size(), sources.size() * 1000.0 / time); System.out.printf(" %6d Lines -> %6d Lines/sec\n", lines, lines * 1000 / time); System.out.printf(" %6d Chars -> %6d Chars/sec\n", characters, characters * 1000 / time); } protected ParsingResult run(Rule rootRule, String sourceText) { return new ReportingParseRunner(rootRule).run(sourceText); } private static long time(long start) { long end = System.currentTimeMillis(); System.out.printf(" %s ms\n", end - start); return end - start; } private static final FileFilter fileFilter = new FileFilter() { public boolean accept(File file) { return file.isDirectory() || file.getName().endsWith(".java"); } }; private static List recursiveGetAllJavaSources(File file, ArrayList list) { if (file.isDirectory()) { for (File f : file.listFiles(fileFilter)) { recursiveGetAllJavaSources(f, list); } } else { list.add(file); } return list; } public static String readAllText(File file) { checkArgNotNull(file, "file"); return readAllText(file, Charset.forName("UTF8")); } public static String readAllText(File file, Charset charset) { checkArgNotNull(file, "file"); checkArgNotNull(charset, "charset"); try { return readAllText(new FileInputStream(file), charset); } catch (FileNotFoundException e) { return null; } } public static String readAllText(InputStream stream, Charset charset) { checkArgNotNull(charset, "charset"); if (stream == null) return null; BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset)); StringWriter writer = new StringWriter(); copyAll(reader, writer); return writer.toString(); } public static void copyAll(Reader reader, Writer writer) { checkArgNotNull(reader, "reader"); checkArgNotNull(writer, "writer"); try { char[] data = new char[4096]; // copy in chunks of 4K int count; while ((count = reader.read(data)) >= 0) writer.write(data, 0, count); reader.close(); writer.close(); } catch (IOException e) { throw new RuntimeException(e); } } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/sparql/000077500000000000000000000000001421263112100265105ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/sparql/SparqlParser.java000066400000000000000000000527521421263112100320050ustar00rootroot00000000000000/* * Copyright (c) 2009 Ken Wenzel * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.examples.sparql; import org.parboiled.BaseParser; import org.parboiled.Rule; /** * SPARQL Parser * * @author Ken Wenzel, adapted by Mathias Doenitz */ @SuppressWarnings({"InfiniteRecursion"}) public class SparqlParser extends BaseParser { // public Rule Query() { return Sequence(WS(), Prologue(), FirstOf(SelectQuery(), ConstructQuery(), DescribeQuery(), AskQuery()), EOI); } public Rule Prologue() { return Sequence(Optional(BaseDecl()), ZeroOrMore(PrefixDecl())); } public Rule BaseDecl() { return Sequence(BASE(), IRI_REF()); } public Rule PrefixDecl() { return Sequence(PREFIX(), PNAME_NS(), IRI_REF()); } public Rule SelectQuery() { return Sequence(SELECT(), Optional(FirstOf(DISTINCT(), REDUCED())), FirstOf(OneOrMore(Var()), ASTERISK()), ZeroOrMore(DatasetClause()), WhereClause(), SolutionModifier()); } public Rule ConstructQuery() { return Sequence(CONSTRUCT(), ConstructTemplate(), ZeroOrMore(DatasetClause()), WhereClause(), SolutionModifier()); } public Rule DescribeQuery() { return Sequence(DESCRIBE(), FirstOf(OneOrMore(VarOrIRIref()), ASTERISK()), ZeroOrMore(DatasetClause()), Optional(WhereClause()), SolutionModifier()); } public Rule AskQuery() { return Sequence(ASK(), ZeroOrMore(DatasetClause()), WhereClause()); } public Rule DatasetClause() { return Sequence(FROM(), FirstOf(DefaultGraphClause(), NamedGraphClause())); } public Rule DefaultGraphClause() { return SourceSelector(); } public Rule NamedGraphClause() { return Sequence(NAMED(), SourceSelector()); } public Rule SourceSelector() { return IriRef(); } public Rule WhereClause() { return Sequence(Optional(WHERE()), GroupGraphPattern()); } public Rule SolutionModifier() { return Sequence(Optional(OrderClause()), Optional(LimitOffsetClauses())); } public Rule LimitOffsetClauses() { return FirstOf(Sequence(LimitClause(), Optional(OffsetClause())), Sequence(OffsetClause(), Optional(LimitClause()))); } public Rule OrderClause() { return Sequence(ORDER(), BY(), OneOrMore(OrderCondition())); } public Rule OrderCondition() { return FirstOf( Sequence(FirstOf(ASC(), DESC()), BrackettedExpression()), FirstOf(Constraint(), Var())); } public Rule LimitClause() { return Sequence(LIMIT(), INTEGER()); } public Rule OffsetClause() { return Sequence(OFFSET(), INTEGER()); } public Rule GroupGraphPattern() { return Sequence(OPEN_CURLY_BRACE(), Optional(TriplesBlock()), ZeroOrMore(Sequence( FirstOf(GraphPatternNotTriples(), Filter()), Optional(DOT()), Optional(TriplesBlock()))), CLOSE_CURLY_BRACE()); } public Rule TriplesBlock() { return Sequence(TriplesSameSubject(), Optional(Sequence(DOT(), Optional(TriplesBlock())))); } public Rule GraphPatternNotTriples() { return FirstOf(OptionalGraphPattern(), GroupOrUnionGraphPattern(), GraphGraphPattern()); } public Rule OptionalGraphPattern() { return Sequence(OPTIONAL(), GroupGraphPattern()); } public Rule GraphGraphPattern() { return Sequence(GRAPH(), VarOrIRIref(), GroupGraphPattern()); } public Rule GroupOrUnionGraphPattern() { return Sequence(GroupGraphPattern(), ZeroOrMore(Sequence(UNION(), GroupGraphPattern()))); } public Rule Filter() { return Sequence(FILTER(), Constraint()); } public Rule Constraint() { return FirstOf(BrackettedExpression(), BuiltInCall(), FunctionCall()); } public Rule FunctionCall() { return Sequence(IriRef(), ArgList()); } public Rule ArgList() { return FirstOf(Sequence(OPEN_BRACE(), CLOSE_BRACE()), Sequence( OPEN_BRACE(), Expression(), ZeroOrMore(Sequence(COMMA(), Expression())), CLOSE_BRACE())); } public Rule ConstructTemplate() { return Sequence(OPEN_CURLY_BRACE(), Optional(ConstructTriples()), CLOSE_CURLY_BRACE()); } public Rule ConstructTriples() { return Sequence(TriplesSameSubject(), Optional(Sequence(DOT(), Optional(ConstructTriples())))); } public Rule TriplesSameSubject() { return FirstOf(Sequence(VarOrTerm(), PropertyListNotEmpty()), Sequence( TriplesNode(), PropertyList())); } public Rule PropertyListNotEmpty() { return Sequence(Verb(), ObjectList(), ZeroOrMore(Sequence(SEMICOLON(), Optional(Sequence(Verb(), ObjectList()))))); } public Rule PropertyList() { return Optional(PropertyListNotEmpty()); } public Rule ObjectList() { return Sequence(Object_(), ZeroOrMore(Sequence(COMMA(), Object_()))); } public Rule Object_() { return GraphNode(); } public Rule Verb() { return FirstOf(VarOrIRIref(), A()); } public Rule TriplesNode() { return FirstOf(Collection(), BlankNodePropertyList()); } public Rule BlankNodePropertyList() { return Sequence(OPEN_SQUARE_BRACE(), PropertyListNotEmpty(), CLOSE_SQUARE_BRACE()); } public Rule Collection() { return Sequence(OPEN_BRACE(), OneOrMore(GraphNode()), CLOSE_BRACE()); } public Rule GraphNode() { return FirstOf(VarOrTerm(), TriplesNode()); } public Rule VarOrTerm() { return FirstOf(Var(), GraphTerm()); } public Rule VarOrIRIref() { return FirstOf(Var(), IriRef()); } public Rule Var() { return FirstOf(VAR1(), VAR2()); } public Rule GraphTerm() { return FirstOf(IriRef(), RdfLiteral(), NumericLiteral(), BooleanLiteral(), BlankNode(), Sequence(OPEN_BRACE(), CLOSE_BRACE())); } public Rule Expression() { return ConditionalOrExpression(); } public Rule ConditionalOrExpression() { return Sequence(ConditionalAndExpression(), ZeroOrMore(Sequence(OR(), ConditionalAndExpression()))); } public Rule ConditionalAndExpression() { return Sequence(ValueLogical(), ZeroOrMore(Sequence(AND(), ValueLogical()))); } public Rule ValueLogical() { return RelationalExpression(); } public Rule RelationalExpression() { return Sequence(NumericExpression(), Optional(FirstOf(// Sequence(EQUAL(), NumericExpression()), // Sequence(NOT_EQUAL(), NumericExpression()), // Sequence(LESS(), NumericExpression()), // Sequence(GREATER(), NumericExpression()), // Sequence(LESS_EQUAL(), NumericExpression()), // Sequence(GREATER_EQUAL(), NumericExpression()) // ) // )); } public Rule NumericExpression() { return AdditiveExpression(); } public Rule AdditiveExpression() { return Sequence(MultiplicativeExpression(), // ZeroOrMore(FirstOf( Sequence(PLUS(), MultiplicativeExpression()), // Sequence(MINUS(), MultiplicativeExpression()), // NumericLiteralPositive(), NumericLiteralNegative()) // )); } public Rule MultiplicativeExpression() { return Sequence(UnaryExpression(), ZeroOrMore(FirstOf(Sequence( ASTERISK(), UnaryExpression()), Sequence(DIVIDE(), UnaryExpression())))); } public Rule UnaryExpression() { return FirstOf(Sequence(NOT(), PrimaryExpression()), Sequence(PLUS(), PrimaryExpression()), Sequence(MINUS(), PrimaryExpression()), PrimaryExpression()); } public Rule PrimaryExpression() { return FirstOf(BrackettedExpression(), BuiltInCall(), IriRefOrFunction(), RdfLiteral(), NumericLiteral(), BooleanLiteral(), Var()); } public Rule BrackettedExpression() { return Sequence(OPEN_BRACE(), Expression(), CLOSE_BRACE()); } public Rule BuiltInCall() { return FirstOf( Sequence(STR(), OPEN_BRACE(), Expression(), CLOSE_BRACE()), Sequence(LANG(), OPEN_BRACE(), Expression(), CLOSE_BRACE()), Sequence(LANGMATCHES(), OPEN_BRACE(), Expression(), COMMA(), Expression(), CLOSE_BRACE()), Sequence(DATATYPE(), OPEN_BRACE(), Expression(), CLOSE_BRACE()), Sequence(BOUND(), OPEN_BRACE(), Var(), CLOSE_BRACE()), Sequence(SAMETERM(), OPEN_BRACE(), Expression(), COMMA(), Expression(), CLOSE_BRACE()), Sequence(ISIRI(), OPEN_BRACE(), Expression(), CLOSE_BRACE()), Sequence(ISURI(), OPEN_BRACE(), Expression(), CLOSE_BRACE()), Sequence(ISBLANK(), OPEN_BRACE(), Expression(), CLOSE_BRACE()), Sequence(ISLITERAL(), OPEN_BRACE(), Expression(), CLOSE_BRACE()), RegexExpression()); } public Rule RegexExpression() { return Sequence(REGEX(), OPEN_BRACE(), Expression(), COMMA(), Expression(), Optional(Sequence(COMMA(), Expression())), CLOSE_BRACE()); } public Rule IriRefOrFunction() { return Sequence(IriRef(), Optional(ArgList())); } public Rule RdfLiteral() { return Sequence(String(), Optional(FirstOf(LANGTAG(), Sequence( REFERENCE(), IriRef())))); } public Rule NumericLiteral() { return FirstOf(NumericLiteralUnsigned(), NumericLiteralPositive(), NumericLiteralNegative()); } public Rule NumericLiteralUnsigned() { return FirstOf(DOUBLE(), DECIMAL(), INTEGER()); } public Rule NumericLiteralPositive() { return FirstOf(DOUBLE_POSITIVE(), DECIMAL_POSITIVE(), INTEGER_POSITIVE()); } public Rule NumericLiteralNegative() { return FirstOf(DOUBLE_NEGATIVE(), DECIMAL_NEGATIVE(), INTEGER_NEGATIVE()); } public Rule BooleanLiteral() { return FirstOf(TRUE(), FALSE()); } public Rule String() { return FirstOf(STRING_LITERAL_LONG1(), STRING_LITERAL1(), STRING_LITERAL_LONG2(), STRING_LITERAL2()); } public Rule IriRef() { return FirstOf(IRI_REF(), PrefixedName()); } public Rule PrefixedName() { return FirstOf(PNAME_LN(), PNAME_NS()); } public Rule BlankNode() { return FirstOf(BLANK_NODE_LABEL(), Sequence(OPEN_SQUARE_BRACE(), CLOSE_SQUARE_BRACE())); } // // public Rule WS() { return ZeroOrMore(FirstOf(COMMENT(), WS_NO_COMMENT())); } public Rule WS_NO_COMMENT() { return FirstOf(Ch(' '), Ch('\t'), Ch('\f'), EOL()); } public Rule PNAME_NS() { return Sequence(Optional(PN_PREFIX()), ChWS(':')); } public Rule PNAME_LN() { return Sequence(PNAME_NS(), PN_LOCAL()); } public Rule BASE() { return StringIgnoreCaseWS("BASE"); } public Rule PREFIX() { return StringIgnoreCaseWS("PREFIX"); } public Rule SELECT() { return StringIgnoreCaseWS("SELECT"); } public Rule DISTINCT() { return StringIgnoreCaseWS("DISTINCT"); } public Rule REDUCED() { return StringIgnoreCaseWS("REDUCED"); } public Rule CONSTRUCT() { return StringIgnoreCaseWS("CONSTRUCT"); } public Rule DESCRIBE() { return StringIgnoreCaseWS("DESCRIBE"); } public Rule ASK() { return StringIgnoreCaseWS("ASK"); } public Rule FROM() { return StringIgnoreCaseWS("FROM"); } public Rule NAMED() { return StringIgnoreCaseWS("NAMED"); } public Rule WHERE() { return StringIgnoreCaseWS("WHERE"); } public Rule ORDER() { return StringIgnoreCaseWS("ORDER"); } public Rule BY() { return StringIgnoreCaseWS("BY"); } public Rule ASC() { return StringIgnoreCaseWS("ASC"); } public Rule DESC() { return StringIgnoreCaseWS("DESC"); } public Rule LIMIT() { return StringIgnoreCaseWS("LIMIT"); } public Rule OFFSET() { return StringIgnoreCaseWS("OFFSET"); } public Rule OPTIONAL() { return StringIgnoreCaseWS("OPTIONAL"); } public Rule GRAPH() { return StringIgnoreCaseWS("GRAPH"); } public Rule UNION() { return StringIgnoreCaseWS("UNION"); } public Rule FILTER() { return StringIgnoreCaseWS("FILTER"); } public Rule A() { return ChWS('a'); } public Rule STR() { return StringIgnoreCaseWS("STR"); } public Rule LANG() { return StringIgnoreCaseWS("LANG"); } public Rule LANGMATCHES() { return StringIgnoreCaseWS("LANGMATCHES"); } public Rule DATATYPE() { return StringIgnoreCaseWS("DATATYPE"); } public Rule BOUND() { return StringIgnoreCaseWS("BOUND"); } public Rule SAMETERM() { return StringIgnoreCaseWS("SAMETERM"); } public Rule ISIRI() { return StringIgnoreCaseWS("ISIRI"); } public Rule ISURI() { return StringIgnoreCaseWS("ISURI"); } public Rule ISBLANK() { return StringIgnoreCaseWS("ISBLANK"); } public Rule ISLITERAL() { return StringIgnoreCaseWS("ISLITERAL"); } public Rule REGEX() { return StringIgnoreCaseWS("REGEX"); } public Rule TRUE() { return StringIgnoreCaseWS("TRUE"); } public Rule FALSE() { return StringIgnoreCaseWS("FALSE"); } public Rule IRI_REF() { return Sequence(LESS_NO_COMMENT(), // ZeroOrMore(Sequence(TestNot(FirstOf(LESS_NO_COMMENT(), GREATER(), '"', OPEN_CURLY_BRACE(), CLOSE_CURLY_BRACE(), '|', '^', '\\', '`', CharRange('\u0000', '\u0020'))), ANY)), // GREATER()); } public Rule BLANK_NODE_LABEL() { return Sequence("_:", PN_LOCAL(), WS()); } public Rule VAR1() { return Sequence('?', VARNAME(), WS()); } public Rule VAR2() { return Sequence('$', VARNAME(), WS()); } public Rule LANGTAG() { return Sequence('@', OneOrMore(PN_CHARS_BASE()), ZeroOrMore(Sequence( MINUS(), OneOrMore(Sequence(PN_CHARS_BASE(), DIGIT())))), WS()); } public Rule INTEGER() { return Sequence(OneOrMore(DIGIT()), WS()); } public Rule DECIMAL() { return Sequence(FirstOf( // Sequence(OneOrMore(DIGIT()), DOT(), ZeroOrMore(DIGIT())), // Sequence(DOT(), OneOrMore(DIGIT())) // ), WS()); } public Rule DOUBLE() { return Sequence(FirstOf(// Sequence(OneOrMore(DIGIT()), DOT(), ZeroOrMore(DIGIT()), EXPONENT()), // Sequence(DOT(), OneOrMore(DIGIT()), EXPONENT()), // Sequence(OneOrMore(DIGIT()), EXPONENT())), WS()); } public Rule INTEGER_POSITIVE() { return Sequence(PLUS(), INTEGER()); } public Rule DECIMAL_POSITIVE() { return Sequence(PLUS(), DECIMAL()); } public Rule DOUBLE_POSITIVE() { return Sequence(PLUS(), DOUBLE()); } public Rule INTEGER_NEGATIVE() { return Sequence(MINUS(), INTEGER()); } public Rule DECIMAL_NEGATIVE() { return Sequence(MINUS(), DECIMAL()); } public Rule DOUBLE_NEGATIVE() { return Sequence(MINUS(), DOUBLE()); } public Rule EXPONENT() { return Sequence(IgnoreCase('e'), Optional(FirstOf(PLUS(), MINUS())), OneOrMore(DIGIT())); } public Rule STRING_LITERAL1() { return Sequence("'", ZeroOrMore(FirstOf(Sequence(TestNot(FirstOf("'", '\\', '\n', '\r')), ANY), ECHAR())), "'", WS()); } public Rule STRING_LITERAL2() { return Sequence('"', ZeroOrMore(FirstOf(Sequence(TestNot(AnyOf("\"\\\n\r")), ANY), ECHAR())), '"', WS()); } public Rule STRING_LITERAL_LONG1() { return Sequence("'''", ZeroOrMore(Sequence( Optional(FirstOf("''", "'")), FirstOf(Sequence(TestNot(FirstOf( "'", "\\")), ANY), ECHAR()))), "'''", WS()); } public Rule STRING_LITERAL_LONG2() { return Sequence("\"\"\"", ZeroOrMore(Sequence(Optional(FirstOf("\"\"", "\"")), FirstOf(Sequence(TestNot(FirstOf("\"", "\\")), ANY), ECHAR()))), "\"\"\"", WS()); } public Rule ECHAR() { return Sequence('\\', AnyOf("tbnrf\\\"\'")); } public Rule PN_CHARS_U() { return FirstOf(PN_CHARS_BASE(), '_'); } public Rule VARNAME() { return Sequence(FirstOf(PN_CHARS_U(), DIGIT()), ZeroOrMore(FirstOf( PN_CHARS_U(), DIGIT(), '\u00B7', CharRange('\u0300', '\u036F'), CharRange('\u203F', '\u2040'))), WS()); } public Rule PN_CHARS() { return FirstOf(MINUS(), DIGIT(), PN_CHARS_U(), '\u00B7', CharRange('\u0300', '\u036F'), CharRange('\u203F', '\u2040')); } public Rule PN_PREFIX() { return Sequence(PN_CHARS_BASE(), Optional(ZeroOrMore(FirstOf(PN_CHARS(), Sequence(DOT(), PN_CHARS()))))); } public Rule PN_LOCAL() { return Sequence(FirstOf(PN_CHARS_U(), DIGIT()), Optional(ZeroOrMore(FirstOf(PN_CHARS(), Sequence(DOT(), PN_CHARS())))), WS()); } public Rule PN_CHARS_BASE() { return FirstOf( // CharRange('A', 'Z'),// CharRange('a', 'z'), // CharRange('\u00C0', '\u00D6'), // CharRange('\u00D8', '\u00F6'), // CharRange('\u00F8', '\u02FF'), // CharRange('\u0370', '\u037D'), // CharRange('\u037F', '\u1FFF'), // CharRange('\u200C', '\u200D'), // CharRange('\u2070', '\u218F'), // CharRange('\u2C00', '\u2FEF'), // CharRange('\u3001', '\uD7FF'), // CharRange('\uF900', '\uFDCF'), // CharRange('\uFDF0', '\uFFFD') // ); } public Rule DIGIT() { return CharRange('0', '9'); } public Rule COMMENT() { return Sequence('#', ZeroOrMore(Sequence(TestNot(EOL()), ANY)), EOL()); } public Rule EOL() { return AnyOf("\n\r"); } public Rule REFERENCE() { return StringWS("^^"); } public Rule LESS_EQUAL() { return StringWS("<="); } public Rule GREATER_EQUAL() { return StringWS(">="); } public Rule NOT_EQUAL() { return StringWS("!="); } public Rule AND() { return StringWS("&&"); } public Rule OR() { return StringWS("||"); } public Rule OPEN_BRACE() { return ChWS('('); } public Rule CLOSE_BRACE() { return ChWS(')'); } public Rule OPEN_CURLY_BRACE() { return ChWS('{'); } public Rule CLOSE_CURLY_BRACE() { return ChWS('}'); } public Rule OPEN_SQUARE_BRACE() { return ChWS('['); } public Rule CLOSE_SQUARE_BRACE() { return ChWS(']'); } public Rule SEMICOLON() { return ChWS(';'); } public Rule DOT() { return ChWS('.'); } public Rule PLUS() { return ChWS('+'); } public Rule MINUS() { return ChWS('-'); } public Rule ASTERISK() { return ChWS('*'); } public Rule COMMA() { return ChWS(','); } public Rule NOT() { return ChWS('!'); } public Rule DIVIDE() { return ChWS('/'); } public Rule EQUAL() { return ChWS('='); } public Rule LESS_NO_COMMENT() { return Sequence(Ch('<'), ZeroOrMore(WS_NO_COMMENT())); } public Rule LESS() { return ChWS('<'); } public Rule GREATER() { return ChWS('>'); } // public Rule ChWS(char c) { return Sequence(Ch(c), WS()); } public Rule StringWS(String s) { return Sequence(String(s), WS()); } public Rule StringIgnoreCaseWS(String string) { return Sequence(IgnoreCase(string), WS()); } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/time/000077500000000000000000000000001421263112100261445ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/time/Main.java000066400000000000000000000032541421263112100276770ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.time; import org.parboiled.Parboiled; import org.parboiled.parserunners.RecoveringParseRunner; import org.parboiled.common.StringUtils; import static org.parboiled.support.ParseTreeUtils.printNodeTree; import org.parboiled.support.ParsingResult; import java.util.Scanner; public class Main { public static void main(String[] args) { TimeParser parser = Parboiled.createParser(TimeParser.class); while (true) { System.out.print("Enter a time expression (hh:mm(:ss)?, hh(mm(ss)?)? or h(mm)?, single RETURN to exit)!\n"); String input = new Scanner(System.in).nextLine(); if (StringUtils.isEmpty(input)) break; ParsingResult result = new RecoveringParseRunner(parser.Time()).run(input); System.out.println(input + " = " + result.parseTreeRoot.getValue() + '\n'); System.out.println(printNodeTree(result) + '\n'); if (!result.matched) { System.out.println(StringUtils.join(result.parseErrors, "---\n")); } } } } parboiled-1.4.1/examples-java/src/main/java/org/parboiled/examples/time/TimeParser.java000066400000000000000000000057271421263112100310750ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.time; import org.parboiled.BaseParser; import org.parboiled.Rule; import org.parboiled.annotations.BuildParseTree; /** * Parser for very relaxed time literals. Demonstrates usage of the value stack with default values for unmatched rules. */ @BuildParseTree public class TimeParser extends BaseParser { public Rule Time() { return FirstOf(Time_HH_MM_SS(), Time_HHMMSS(), Time_HMM()); } // h(h)?:mm(:ss)? Rule Time_HH_MM_SS() { return Sequence( OneOrTwoDigits(), ':', TwoDigits(), FirstOf(Sequence(':', TwoDigits()), push(0)), EOI, swap3() && push(convertToTime(popAsInt(), popAsInt(), popAsInt())) ); } // hh(mm(ss)?)? Rule Time_HHMMSS() { return Sequence( TwoDigits(), FirstOf( Sequence( TwoDigits(), FirstOf(TwoDigits(), push(0)) ), pushAll(0, 0) ), EOI, swap3() && push(convertToTime(popAsInt(), popAsInt(), popAsInt())) ); } // h(mm)? Rule Time_HMM() { return Sequence( OneDigit(), FirstOf(TwoDigits(), push(0)), EOI, swap() && push(convertToTime(popAsInt(), popAsInt())) ); } Rule OneOrTwoDigits() { return FirstOf(TwoDigits(), OneDigit()); } Rule OneDigit() { return Sequence(Digit(), push(Integer.parseInt(matchOrDefault("0")))); } Rule TwoDigits() { return Sequence(Sequence(Digit(), Digit()), push(Integer.parseInt(matchOrDefault("0")))); } Rule Digit() { return CharRange('0', '9'); } // ************************* ACTIONS ***************************** protected Integer popAsInt() { return (Integer) pop(); } protected String convertToTime(Integer hours, Integer minutes) { return convertToTime(hours, minutes, 0); } protected String convertToTime(Integer hours, Integer minutes, Integer seconds) { return String.format("%s h, %s min, %s s", hours != null ? hours : 0, minutes != null ? minutes : 0, seconds != null ? seconds : 0); } } parboiled-1.4.1/examples-java/src/test/000077500000000000000000000000001421263112100177525ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/000077500000000000000000000000001421263112100206735ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/ExamplesJavaTestWrapper.java000066400000000000000000000023401421263112100263160ustar00rootroot00000000000000import org.parboiled.common.FileUtils; import org.parboiled.common.ImmutableList; import org.scalatestplus.testng.TestNGWrapperSuite; import java.io.File; import java.io.IOException; @SuppressWarnings("unchecked") public class ExamplesJavaTestWrapper extends TestNGWrapperSuite { public ExamplesJavaTestWrapper() throws IOException { super(new org.parboiled.JavaCollectionWrapper(ImmutableList.of(getSuiteFileName())).toList()); } public static String getSuiteFileName() throws IOException { File temp = File.createTempFile("parboiled_testng_suite", ".xml"); temp.deleteOnExit(); String xml = "" + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; FileUtils.writeAllText(xml, temp); return temp.getCanonicalPath(); } } parboiled-1.4.1/examples-java/src/test/java/org/000077500000000000000000000000001421263112100214625ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/000077500000000000000000000000001421263112100234235ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/000077500000000000000000000000001421263112100252415ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/TestNgParboiledTest.java000066400000000000000000000017471421263112100320030ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples; import org.parboiled.test.ParboiledTest; import org.testng.Assert; public class TestNgParboiledTest extends ParboiledTest { @Override protected void fail(String message) { Assert.fail(message); } @Override protected void assertEquals(Object actual, Object expected) { Assert.assertEquals(actual, expected); } } parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/abc/000077500000000000000000000000001421263112100257665ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/abc/AbcTest.java000066400000000000000000000066421421263112100301660ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.abc; import org.parboiled.Parboiled; import org.parboiled.examples.TestNgParboiledTest; import org.testng.annotations.Test; public class AbcTest extends TestNgParboiledTest { @Test public void test() { AbcParser parser = Parboiled.createParser(AbcParser.class); test(parser.S(), "aabbcc") .hasNoErrors() .hasParseTree("" + "[S] 'aabbcc'\n" + " [OneOrMore] 'aa'\n" + " ['a'] 'a'\n" + " ['a'] 'a'\n" + " [B] 'bbcc'\n" + " ['b'] 'b'\n" + " [Optional] 'bc'\n" + " [B] 'bc'\n" + " ['b'] 'b'\n" + " [Optional]\n" + " ['c'] 'c'\n" + " ['c'] 'c'\n"); } @Test public void testFail1() { AbcParser parser = Parboiled.createParser(AbcParser.class); testWithRecovery(parser.S(), "aabbbcc") .hasErrors("" + "Invalid input 'b', expected 'c' (line 1, pos 5):\n" + "aabbbcc\n" + " ^\n") .hasParseTree("" + "[S]E 'aabbcc'\n" + " [OneOrMore] 'aa'\n" + " ['a'] 'a'\n" + " ['a'] 'a'\n" + " [B]E 'bbcc'\n" + " ['b'] 'b'\n" + " [Optional]E 'bc'\n" + " [B]E 'bc'\n" + " ['b'] 'b'\n" + " [Optional]\n" + " ['c'] 'c'\n" + " ['c'] 'c'\n" ); } @Test public void testFail2() { AbcParser parser = Parboiled.createParser(AbcParser.class); testWithRecovery(parser.S(), "aabcc") .hasErrors("" + "Invalid input 'c', expected 'b' (line 1, pos 4):\n" + "aabcc\n" + " ^\n") .hasParseTree("" + "[S]E 'aabbcc'\n" + " [OneOrMore] 'aa'\n" + " ['a'] 'a'\n" + " ['a'] 'a'\n" + " [B]E 'bbcc'\n" + " ['b'] 'b'\n" + " [Optional]E 'bc'\n" + " [B]E 'bc'\n" + " ['b']E 'b'\n" + " [Optional]\n" + " ['c'] 'c'\n" + " ['c'] 'c'\n" ); } } parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/calculators/000077500000000000000000000000001421263112100275555ustar00rootroot00000000000000CalculatorRecoveryTest.java000066400000000000000000000040421421263112100350110ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Parboiled; import org.parboiled.common.FileUtils; import org.parboiled.examples.TestNgParboiledTest; import org.testng.annotations.Test; public class CalculatorRecoveryTest extends TestNgParboiledTest { @Test public void testCalculatorErrorRecovery() { CalculatorParser parser = Parboiled.createParser(CalculatorParser1.class); String[] tests = FileUtils.readAllTextFromResource("CalculatorErrorRecoveryTest.test").split("###\r?\n"); if (!runSingleTest(parser, tests)) { // no single, important test found, so run all tests for (String test : tests) { runTest(parser, test); } } } // if there is a test with its input starting with '>>>' only run that one private boolean runSingleTest(CalculatorParser parser, String[] tests) { for (String test : tests) { if (test.startsWith(">>>")) { runTest(parser, test.substring(3)); return true; } } return false; } private void runTest(CalculatorParser parser, String test) { String[] s = test.split("===\r?\n"); if (!s[0].startsWith("//")) { testWithRecovery(parser.InputLine(), s[0].replaceAll("\r?\n", "")) .hasErrors(s[1]) .hasParseTree(s.length > 2 ? s[2] : ""); } } } parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/calculators/CalculatorsTest.java000066400000000000000000000133051421263112100335360ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Parboiled; import org.parboiled.common.Predicates; import org.parboiled.examples.TestNgParboiledTest; import org.parboiled.matchers.Matcher; import org.parboiled.support.Filters; import org.parboiled.support.ToStringFormatter; import org.testng.annotations.Test; import static org.parboiled.trees.GraphUtils.printTree; public class CalculatorsTest extends TestNgParboiledTest { @Test public void testCalculator0() { CalculatorParser parser = Parboiled.createParser(CalculatorParser0.class); test(parser.InputLine(), "1+5") .hasNoErrors() .hasParseTree("" + "[InputLine] '1+5'\n" + " [Expression] '1+5'\n" + " [Term] '1'\n" + " [Factor] '1'\n" + " [Number] '1'\n" + " [Digit] '1'\n" + " [ZeroOrMore]\n" + " [ZeroOrMore] '+5'\n" + " [Sequence] '+5'\n" + " [[+-]] '+'\n" + " [Term] '5'\n" + " [Factor] '5'\n" + " [Number] '5'\n" + " [Digit] '5'\n" + " [ZeroOrMore]\n" + " [EOI]\n"); } @Test public void testCalculator1() { CalculatorParser parser = Parboiled.createParser(CalculatorParser1.class); runBasicCalculationTests(parser, ""); } @Test public void testCalculator2() { CalculatorParser parser = Parboiled.createParser(CalculatorParser2.class); assertEquals( printTree( (Matcher) parser.InputLine(), new ToStringFormatter(), Predicates.alwaysTrue(), Filters.preventLoops() ), "" + "InputLine\n" + " Expression\n" + " Term\n" + " Factor\n" + " Number\n" + " Digits\n" + " Digit\n" + " Number_Action1\n" + " Parens\n" + " '('\n" + " Expression\n" + " ')'\n" + " ZeroOrMore\n" + " Sequence\n" + " [*/]\n" + " Term_Action1\n" + " Factor\n" + " Term_Action2\n" + " ZeroOrMore\n" + " Sequence\n" + " [+-]\n" + " Expression_Action1\n" + " Term\n" + " Expression_Action2\n" + " EOI\n"); runBasicCalculationTests(parser, ""); } @Test public void testCalculator3() { CalculatorParser parser = Parboiled.createParser(CalculatorParser3.class); runBasicCalculationTests(parser, ".0"); runExtendedCalculationTests(parser); } @Test public void testCalculator4() { CalculatorParser parser = Parboiled.createParser(CalculatorParser4.class); runBasicCalculationTests(parser, ".0"); runExtendedCalculationTests(parser); } private void runBasicCalculationTests(CalculatorParser parser, String suffix) { test(parser, "1+2", "3" + suffix); test(parser, "1+2-3+4", "4" + suffix); test(parser, "1-2-3", "-4" + suffix); test(parser, "1-(2-3)", "2" + suffix); test(parser, "1*2+3", "5" + suffix); test(parser, "1+2*3", "7" + suffix); test(parser, "1*2*3", "6" + suffix); test(parser, "3*4/6", "2" + suffix); test(parser, "24/6/2", "2" + suffix); test(parser, "1-2*3-4", "-9" + suffix); test(parser, "1-2*3-4*5-6", "-31" + suffix); test(parser, "1-24/6/2-(5+7)", "-13" + suffix); test(parser, "((1+2)*3-(4-5))/5", "2" + suffix); } private void runExtendedCalculationTests(CalculatorParser parser) { test(parser, "1 + 2 +3", "6.0"); test(parser, "1-2.0- -3.5", "2.5"); test(parser, "13+SQRT( 2*50 )^2 ", "113.0"); test(parser, "-0.10 * 10^(3+1)", "-1000.0"); test(parser, "1-2*3^ 4 /5+6", "-25.4"); } private void test(CalculatorParser parser, String input, String value) { String str = test(parser.InputLine(), input).hasNoErrors().result.parseTreeRoot.getValue().toString(); int ix = str.indexOf('|'); if (ix >= 0) str = str.substring(ix + 2); assertEquals(str, value); } } ProfilingParseRunnerTest.java000066400000000000000000000156171421263112100353310ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Parboiled; import org.parboiled.examples.TestNgParboiledTest; import org.parboiled.parserunners.ProfilingParseRunner; import org.parboiled.test.ParboiledTest; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; public class ProfilingParseRunnerTest extends TestNgParboiledTest { //@Test public void testProfiling() { CalculatorParser1 parser = Parboiled.createParser(CalculatorParser1.class); ProfilingParseRunner runner = new ProfilingParseRunner(parser.InputLine()); assertFalse(runner.run("1+2*(3-4)").hasErrors()); /*assertEquals(runner.getReport().printBasics(), "" + "Runs : 1\n" + "Active rules : 28\n" + "Total net rule time : 0.002 s\n" + "Total rule invocations : 89\n" + "Total rule matches : 50\n" + "Total rule mismatches : 39\n" + "Total match share : 56.18 %\n" + "Rule re-invocations : 0\n" + "Rule re-matches : 0\n" + "Rule re-mismatches : 0\n" + "Rule re-invocation share : 0.00 %\n");*/ assertEquals(runner.getReport().sortByInvocations().printTopRules(20, ProfilingParseRunner.Report.namedRules), "" + "Rule | Net-Time | Invocations | Matches | Mismatches | Time/Invoc. | Match % | Re-Invocs | Re-Matches | Re-Mismatch | Re-Invoc % \n" + "-------------------------------|-----------|-----------------|-----------------|-----------------|-----------------|---------|-----------------|-----------------|-----------------|-------------------\n" + "Digit: CharRange | 0 ms | 9 / 0 | 4 / 0 | 5 / 0 | 889 ns | 44.44% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "'*': Char | 0 ms | 5 / 0 | 1 / 0 | 4 / 0 | 1,000 ns | 20.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "Number: Sequence | 2 ms | 5 / 18 | 4 / 16 | 1 / 2 | 414,000 ns | 80.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / 0.00%\n" + "Factor: FirstOf | 2 ms | 5 / 55 | 5 / 55 | 0 / 0 | 458,800 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / 0.00%\n" + "Digits: OneOrMore | 1 ms | 5 / 9 | 4 / 8 | 1 / 1 | 175,200 ns | 80.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / 0.00%\n" + "'+': Char | 0 ms | 4 / 0 | 1 / 0 | 3 / 0 | 1,500 ns | 25.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "Number_Action1: Action | 1 ms | 4 / 0 | 4 / 0 | 0 / 0 | 286,250 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "'/': Char | 0 ms | 4 / 0 | 0 / 0 | 4 / 0 | 250 ns | 0.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "Term: Sequence | 2 ms | 4 / 74 | 4 / 74 | 0 / 0 | 611,750 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / 0.00%\n" + "'-': Char | 0 ms | 3 / 0 | 1 / 0 | 2 / 0 | 1,000 ns | 33.33% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "Expression: Sequence | 3 ms | 2 / 86 | 2 / 86 | 0 / 0 | 1,286,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / 0.00%\n" + "Expression_Action1: Action | 0 ms | 1 / 0 | 1 / 0 | 0 / 0 | 12,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "'(': Char | 0 ms | 1 / 0 | 1 / 0 | 0 / 0 | 4,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "')': Char | 0 ms | 1 / 0 | 1 / 0 | 0 / 0 | 4,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "InputLine: Sequence | 2 ms | 1 / 88 | 1 / 88 | 0 / 0 | 2,435,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / 0.00%\n" + "Expression_Action2: Action | 0 ms | 1 / 0 | 1 / 0 | 0 / 0 | 25,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "Term_Action1: Action | 0 ms | 1 / 0 | 1 / 0 | 0 / 0 | 13,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "EOI: Char | 0 ms | 1 / 0 | 1 / 0 | 0 / 0 | 4,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / NaN%\n" + "Parens: Sequence | 0 ms | 1 / 41 | 1 / 41 | 0 / 0 | 188,000 ns | 100.00% | 0 / 0 | 0 / 0 | 0 / 0 | 0.00% / 0.00%\n" + "Term_Action2: Action | 0 ms | 0 / 0 | 0 / 0 | 0 / 0 | NaN ns | NaN% | 0 / 0 | 0 / 0 | 0 / 0 | NaN% / NaN%\n"); } } ReportingParseRunnerTest.java000066400000000000000000000062411421263112100353420ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Parboiled; import org.parboiled.Rule; import org.parboiled.examples.TestNgParboiledTest; import org.parboiled.support.ParsingResult; import org.parboiled.test.ParboiledTest; import org.parboiled.parserunners.ReportingParseRunner; import org.testng.annotations.Test; import static org.parboiled.errors.ErrorUtils.printParseErrors; import static org.testng.Assert.assertEquals; public class ReportingParseRunnerTest extends TestNgParboiledTest { private CalculatorParser1 parser = Parboiled.createParser(CalculatorParser1.class); @Test public void testSimpleReporting() { test("X1+2", "Invalid input 'X', expected InputLine (line 1, pos 1):\nX1+2\n^\n"); test("1X+2", "Invalid input 'X', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 2):\n1X+2\n ^\n"); test("1+X2", "Invalid input 'X', expected Term (line 1, pos 3):\n1+X2\n ^\n"); test("1+2X", "Invalid input 'X', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 4):\n1+2X\n ^\n"); test("1+2X*(3-4)-5", "Invalid input 'X', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 4):\n1+2X*(3-4)-5\n ^\n"); test("1+2*X(3-4)-5", "Invalid input 'X', expected Factor (line 1, pos 5):\n1+2*X(3-4)-5\n ^\n"); test("1+2*(X3-4)-5", "Invalid input 'X', expected Expression (line 1, pos 6):\n1+2*(X3-4)-5\n ^\n"); test("1+2*(3X-4)-5", "Invalid input 'X', expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 7):\n1+2*(3X-4)-5\n ^\n"); test("1+2*(3-X4)-5", "Invalid input 'X', expected Term (line 1, pos 8):\n1+2*(3-X4)-5\n ^\n"); test("1+2*(3-4X)-5", "Invalid input 'X', expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 9):\n1+2*(3-4X)-5\n ^\n"); test("1+2*(3-4)X-5", "Invalid input 'X', expected '*', '/', '+', '-' or EOI (line 1, pos 10):\n1+2*(3-4)X-5\n ^\n"); test("1+2*(3-4)-X5", "Invalid input 'X', expected Term (line 1, pos 11):\n1+2*(3-4)-X5\n ^\n"); test("1+2*(3-4)-5X", "Invalid input 'X', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 12):\n1+2*(3-4)-5X\n ^\n"); test("1+2*(3-4-5", "Unexpected end of input, expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 11):\n1+2*(3-4-5\n ^\n"); test("1+2*3-4)-5", "Invalid input ')', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 8):\n1+2*3-4)-5\n ^\n"); } private void test(String input, String expectedErrorMessage) { test(parser.InputLine(), input).hasErrors(expectedErrorMessage); } } TracingParseRunnerTest.java000066400000000000000000000126341421263112100347630ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators; import org.parboiled.Parboiled; import org.parboiled.common.StringBuilderSink; import org.parboiled.support.ParsingResult; import org.testng.annotations.Test; import org.parboiled.parserunners.TracingParseRunner; import static org.parboiled.common.Predicates.*; import static org.parboiled.errors.ErrorUtils.printParseErrors; import static org.parboiled.support.Filters.rules; import static org.parboiled.support.Filters.rulesBelow; import static org.testng.Assert.assertEquals; public class TracingParseRunnerTest { @Test public void testTracingParseRunner() { CalculatorParser1 parser = Parboiled.createParser(CalculatorParser1.class); StringBuilderSink log = new StringBuilderSink(); TracingParseRunner runner = new TracingParseRunner(parser.InputLine()) .withFilter(and(rules(parser.Number(), parser.Parens()), not(rulesBelow(parser.Digits())))) .withLog(log); ParsingResult result = runner.run("2*(4+5"); assertEquals(printParseErrors(result), "" + "Unexpected end of input, expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 7):\n" + "2*(4+5\n" + " ^\n"); assertEquals(log.toString(), "Starting new parsing run\n" + "InputLine/Expression/Term/Factor/Number/Digits, matched, cursor at 1:2 after \"2\"\n" + "..(4)../Number/Number_Action1, matched, cursor at 1:2 after \"2\"\n" + "..(4)../Number, matched, cursor at 1:2 after \"2\"\n" + "..(2)../Term/ZeroOrMore/FirstOf/Sequence/Factor/Number/Digits, failed, cursor at 1:3 after \"2*\"\n" + "..(7)../Number, failed, cursor at 1:3 after \"2*\"\n" + "..(6)../Factor/Parens/'(', matched, cursor at 1:4 after \"2*(\"\n" + "..(7)../Parens/Expression/Term/Factor/Number/Digits, matched, cursor at 1:5 after \"2*(4\"\n" + "..(11)../Number/Number_Action1, matched, cursor at 1:5 after \"2*(4\"\n" + "..(11)../Number, matched, cursor at 1:5 after \"2*(4\"\n" + "..(10)../Factor, matched, cursor at 1:5 after \"2*(4\"\n" + "..(9)../Term/ZeroOrMore/FirstOf/Sequence/'*', failed, cursor at 1:5 after \"2*(4\"\n" + "..(12)../Sequence, failed, cursor at 1:5 after \"2*(4\"\n" + "..(11)../FirstOf/Sequence/'/', failed, cursor at 1:5 after \"2*(4\"\n" + "..(12)../Sequence, failed, cursor at 1:5 after \"2*(4\"\n" + "..(11)../FirstOf, failed, cursor at 1:5 after \"2*(4\"\n" + "..(10)../ZeroOrMore, matched, cursor at 1:5 after \"2*(4\"\n" + "..(9)../Term, matched, cursor at 1:5 after \"2*(4\"\n" + "..(8)../Expression/ZeroOrMore/FirstOf/Sequence/'+', matched, cursor at 1:6 after \"2*(4+\"\n" + "..(11)../Sequence/Term/Factor/Number/Digits, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(14)../Number/Number_Action1, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(14)../Number, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(13)../Factor, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(12)../Term/ZeroOrMore/FirstOf/Sequence/'*', failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(15)../Sequence, failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(14)../FirstOf/Sequence/'/', failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(15)../Sequence, failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(14)../FirstOf, failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(13)../ZeroOrMore, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(12)../Term, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(11)../Sequence/Expression_Action1, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(11)../Sequence, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(10)../FirstOf, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(9)../ZeroOrMore/FirstOf/Sequence/'+', failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(11)../Sequence, failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(10)../FirstOf/Sequence/'-', failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(11)../Sequence, failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(10)../FirstOf, failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(9)../ZeroOrMore, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(8)../Expression, matched, cursor at 1:7 after \"2*(4+5\"\n" + "..(7)../Parens/')', failed, cursor at 1:7 after \"2*(4+5\"\n" + "..(7)../Parens, failed, cursor at 1:7 after \"2*(4+5\"\n"); } } parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/java/000077500000000000000000000000001421263112100261625ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/java/JavaRecoveryTest.java000066400000000000000000000042031421263112100322640ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; import org.parboiled.Node; import org.parboiled.Parboiled; import org.parboiled.common.FileUtils; import org.parboiled.common.Predicates; import org.parboiled.examples.TestNgParboiledTest; import org.parboiled.support.Filters; import org.testng.annotations.Test; public class JavaRecoveryTest extends TestNgParboiledTest { @Test public void testJavaErrorRecovery() { JavaParser parser = Parboiled.createParser(JavaParser.class); String[] tests = FileUtils.readAllTextFromResource("JavaErrorRecoveryTest.test") .split("###\r?\n"); if (!runSingleTest(parser, tests)) { // no single, important test found, so run all tests for (String test : tests) { runTest(parser, test); } } } // if there is a test with its input starting with '>>>' only run that one private boolean runSingleTest(JavaParser parser, String[] tests) { for (String test : tests) { if (test.startsWith(">>>")) { runTest(parser, test.substring(3)); return true; } } return false; } private void runTest(JavaParser parser, String test) { String[] s = test.split("===\r?\n"); if (!s[0].startsWith("//")) { testWithRecovery(parser.CompilationUnit(), s[0]) .hasErrors(s[1]) .hasParseTree(Filters.SKIP_EMPTY_OPTS_AND_ZOMS, Predicates.>alwaysTrue(), s[2]); } } } parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/java/JavaTest.java000066400000000000000000000076251421263112100305600ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; import org.parboiled.Node; import org.parboiled.Parboiled; import org.parboiled.ParserStatistics; import org.parboiled.Rule; import org.parboiled.common.*; import org.parboiled.parserunners.RecoveringParseRunner; import org.parboiled.support.Filters; import org.parboiled.support.ParsingResult; import org.parboiled.support.ToStringFormatter; import org.testng.annotations.Test; import static org.parboiled.support.ParseTreeUtils.printNodeTree; import static org.parboiled.trees.GraphUtils.printTree; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; public class JavaTest { // the following line is actually being uncommented programmatically before running the test, // the reason being that the IntelliJ IDEA parser does not like "funny" identifies like these itself //private char \u0041_identifierWithNonAsciiCharacters_åäöÅÄÖ_\u0030_$ = '\u0061'; @SuppressWarnings("unused") private char[] octalEscapes = new char[] {'\1', '\12', '\123'}; @Test public void simpleJavaTest() { String testSource = FileUtils.readAllTextFromResource("SimpleJavaTest.test"); JavaParser parser = Parboiled.createParser(JavaParser.class); Rule compilationUnit = parser.CompilationUnit(); assertEquals(ParserStatistics.generateFor(compilationUnit).toString(), "" + "Parser statistics for rule 'CompilationUnit':\n" + " Total rules : 682\n" + " Actions : 0\n" + " Any : 1\n" + " CharIgnoreCase: 1\n" + " Char : 83\n" + " Custom : 2\n" + " CharRange : 7\n" + " AnyOf : 16\n" + " Empty : 0\n" + " FirstOf : 65\n" + " FirstOfStrings: 4\n" + " Nothing : 0\n" + " OneOrMore : 7\n" + " Optional : 40\n" + " Sequence : 310\n" + " String : 82\n" + " Test : 0\n" + " TestNot : 13\n" + " ZeroOrMore : 51\n" + "\n" + " Action Classes : 0\n" + " ProxyMatchers : 14\n" + " VarFramingMatchers: 0\n" + "MemoMismatchesMatchers: 7\n"); ParsingResult parsingResult = new RecoveringParseRunner(compilationUnit).run(testSource); if (parsingResult.hasErrors()) { fail("\n--- ParseErrors ---\n" + StringUtils.join(parsingResult.parseErrors, "---\n") + "\n--- ParseTree ---\n" + printNodeTree(parsingResult, Filters.SKIP_EMPTY_OPTS_AND_ZOMS, Predicates.>alwaysTrue()) ); } assertEquals( printTree(parsingResult.parseTreeRoot, new ToStringFormatter>(), Filters.SKIP_EMPTY_OPTS_AND_ZOMS, Predicates.>alwaysTrue()), FileUtils.readAllTextFromResource("SimpleJavaTestParseTree.test") ); } } parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/java/JavaTest2.java000066400000000000000000000151101421263112100306260ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; import org.parboiled.Node; import org.parboiled.Parboiled; import org.parboiled.ParserStatistics; import org.parboiled.Rule; import org.parboiled.common.FileUtils; import org.parboiled.common.Formatter; import org.parboiled.common.Predicates; import org.parboiled.common.StringUtils; import org.parboiled.errors.ErrorUtils; import org.parboiled.parserunners.RecoveringParseRunner; import org.parboiled.parserunners.ReportingParseRunner; import org.parboiled.support.Filters; import org.parboiled.support.ParsingResult; import org.parboiled.support.ToStringFormatter; import org.testng.annotations.Test; import static org.parboiled.support.ParseTreeUtils.printNodeTree; import static org.parboiled.trees.GraphUtils.printTree; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; public class JavaTest2 { @Test public void javaTest2() { JavaParser parser = Parboiled.createParser(JavaParser.class); Rule compilationUnit = parser.CompilationUnit(); ParsingResult parsingResult = new ReportingParseRunner(compilationUnit).run( "class test\n" + "{\n" + " double d = 0xff;\n" + "}" ); if (parsingResult.hasErrors()) { fail(ErrorUtils.printParseErrors(parsingResult)); } assertEquals( printTree(parsingResult.parseTreeRoot, new ToStringFormatter>()), "[CompilationUnit]\n" + " [Optional]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [TypeDeclaration]\n" + " [Sequence]\n" + " [ZeroOrMore]\n" + " [FirstOf]\n" + " [ClassDeclaration]\n" + " [Identifier]\n" + " [Optional]\n" + " [Optional]\n" + " [Optional]\n" + " [ClassBody]\n" + " [ZeroOrMore]\n" + " [ClassBodyDeclaration]\n" + " [Sequence]\n" + " [ZeroOrMore]\n" + " [MemberDecl]\n" + " [Sequence]\n" + " [Type]\n" + " [FirstOf]\n" + " [BasicType]\n" + " [FirstOf]\n" + " [ZeroOrMore]\n" + " [VariableDeclarators]\n" + " [VariableDeclarator]\n" + " [Identifier]\n" + " [ZeroOrMore]\n" + " [Optional]\n" + " [Sequence]\n" + " [VariableInitializer]\n" + " [Expression]\n" + " [ConditionalExpression]\n" + " [ConditionalOrExpression]\n" + " [ConditionalAndExpression]\n" + " [InclusiveOrExpression]\n" + " [ExclusiveOrExpression]\n" + " [AndExpression]\n" + " [EqualityExpression]\n" + " [RelationalExpression]\n" + " [ShiftExpression]\n" + " [AdditiveExpression]\n" + " [MultiplicativeExpression]\n" + " [UnaryExpression]\n" + " [Sequence]\n" + " [Primary]\n" + " [Literal]\n" + " [FirstOf]\n" + " [IntegerLiteral]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [ZeroOrMore]\n" + " [EOI]\n" ); } } ReportingParseRunnerTest.java000066400000000000000000000056561421263112100337600ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/java/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; import org.parboiled.Parboiled; import org.parboiled.Rule; import org.parboiled.parserunners.ReportingParseRunner; import org.parboiled.support.ParsingResult; import org.testng.annotations.Test; import static org.parboiled.errors.ErrorUtils.printParseErrors; import static org.testng.Assert.assertEquals; public class ReportingParseRunnerTest { @Test public void testJavaError1() { String sourceWithErrors = "package org.parboiled.examples;\n" + "public class JavaTestSource {\n" + " @SuppressWarnings({\"UnnecessaryLocalVariable\", \"UnusedDeclaration\"})\n" + " public String method(int param) {\n" + " String name = toString(;\n" + " return name;\n" + " }\n" + "}"; JavaParser parser = Parboiled.createParser(JavaParser.class); Rule rule = parser.CompilationUnit(); ParsingResult result = new ReportingParseRunner(rule).run(sourceWithErrors); assertEquals(result.parseErrors.size(), 1); assertEquals(printParseErrors(result), "" + "Invalid input ';', expected Spacing, Expression or ')' (line 5, pos 32):\n" + " String name = toString(;\n" + " ^\n"); } @Test public void testJavaError2() { String sourceWithErrors = "package org.parboiled.examples;\n" + "public class JavaTestSource {\n" + " @SuppressWarnings({\"UnnecessaryLocalVariable\", \"UnusedDeclaration\"})\n" + " public String method(int param) {\n" + " String name toString();\n" + " return name;\n" + " }\n" + "}"; JavaParser parser = Parboiled.createParser(JavaParser.class); Rule rule = parser.CompilationUnit(); ParsingResult result = new ReportingParseRunner(rule).run(sourceWithErrors); assertEquals(result.parseErrors.size(), 1); assertEquals(printParseErrors(result), "" + "Invalid input 't', expected Whitespace, \"/*\", \"//\", Dim, '=', ',' or ';' (line 5, pos 22):\n" + " String name toString();\n" + " ^\n"); } } parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/sparql/000077500000000000000000000000001421263112100265435ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/sparql/SparqlTest.java000066400000000000000000000076471421263112100315260ustar00rootroot00000000000000/* * Copyright (c) 2009 Ken Wenzel * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.examples.sparql; import org.parboiled.Parboiled; import org.parboiled.common.FileUtils; import org.parboiled.parserunners.RecoveringParseRunner; import org.parboiled.support.ParsingResult; import org.testng.Assert; import org.testng.annotations.Test; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.regex.Matcher; import java.util.regex.Pattern; public class SparqlTest { enum Result { OK, FAIL } class TextInfo { Result result; String text; public TextInfo(Result result, String text) { super(); this.result = result; this.text = text; } } SparqlParser parser = Parboiled.createParser(SparqlParser.class); @Test public void test() throws Exception { int failures = 0; for (TextInfo textInfo : getTextInfos()) { ParsingResult result = new RecoveringParseRunner(parser.Query()).run(textInfo.text); boolean passed = result.hasErrors() == (textInfo.result == Result.FAIL); if (!passed) { failures++; //System.err.println(textInfo.text + " --> " + textInfo.result + "\n\n"); } } Assert.assertEquals(failures, 12); // currently 12 tests require semantic validation and therefore fail } protected List getTextInfos() throws Exception { List textInfos = new ArrayList(); BufferedReader in = new BufferedReader(new InputStreamReader( getClass().getClassLoader().getResourceAsStream("SparqlTest.test"), Charset.forName("UTF8"))); while (in.ready()) { if (in.read() == '<' && in.ready() && in.read() == '<') { in.readLine(); textInfos.add(parseText(in)); } } return textInfos; } private TextInfo parseText(BufferedReader in) throws Exception { Pattern unicodes = Pattern.compile("\\\\u([0-9a-fA-F]{4})"); StringBuffer text = new StringBuffer(); while (in.ready()) { String line = in.readLine(); if (line.startsWith(">>")) { return new TextInfo(line.toLowerCase().contains("ok") ? Result.OK : Result.FAIL, text.toString()); } else { Matcher matcher = unicodes.matcher(line); while (matcher.find()) { matcher.appendReplacement(text, Character.toString((char) Integer.parseInt(matcher.group(1), 16))); } matcher.appendTail(text); text.append('\n'); } } throw new NoSuchElementException("Expected \">>\""); } } parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/time/000077500000000000000000000000001421263112100261775ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/java/org/parboiled/examples/time/TimeTest.java000066400000000000000000000040311421263112100305760ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.time; import org.parboiled.Parboiled; import org.parboiled.parserunners.RecoveringParseRunner; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; public class TimeTest { @Test public void test() { TimeParser parser = Parboiled.createParser(TimeParser.class); assertEquals(new RecoveringParseRunner(parser.Time()).run("12:00").parseTreeRoot.getValue(), "12 h, 0 min, 0 s"); assertEquals(new RecoveringParseRunner(parser.Time()).run("9:01").parseTreeRoot.getValue(), "9 h, 1 min, 0 s"); assertEquals(new RecoveringParseRunner(parser.Time()).run("02:34:56").parseTreeRoot.getValue(), "2 h, 34 min, 56 s"); assertEquals(new RecoveringParseRunner(parser.Time()).run("1").parseTreeRoot.getValue(), "1 h, 0 min, 0 s"); assertEquals(new RecoveringParseRunner(parser.Time()).run("12").parseTreeRoot.getValue(), "12 h, 0 min, 0 s"); assertEquals(new RecoveringParseRunner(parser.Time()).run("123").parseTreeRoot.getValue(), "1 h, 23 min, 0 s"); assertEquals(new RecoveringParseRunner(parser.Time()).run("1234").parseTreeRoot.getValue(), "12 h, 34 min, 0 s"); assertTrue(new RecoveringParseRunner(parser.Time()).run("12345").hasErrors()); assertEquals(new RecoveringParseRunner(parser.Time()).run("123456").parseTreeRoot.getValue(), "12 h, 34 min, 56 s"); } } parboiled-1.4.1/examples-java/src/test/resources/000077500000000000000000000000001421263112100217645ustar00rootroot00000000000000parboiled-1.4.1/examples-java/src/test/resources/CalculatorErrorRecoveryTest.test000066400000000000000000000775331421263112100303660ustar00rootroot00000000000000xyz === Invalid input 'x...', expected InputLine (line 1, pos 1): xyz ^^^ === [InputLine]E ### X1+2 === Invalid input 'X', expected InputLine (line 1, pos 1): X1+2 ^ === [InputLine, {3}]E '1+2' [Expression, {3}]E '1+2' [Term, {1}]E '1' [Factor, {1}]E '1' [Number, {1}]E '1' [Digits]E '1' [ZeroOrMore, {1}] [ZeroOrMore, {3}] '+2' [FirstOf, {3}] '+2' [Sequence, {3}] '+2' ['+', {1}] '+' [Term, {2}] '2' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {2}] [EOI, {3}] ### xyz1+2 === Invalid input 'x...', expected InputLine (line 1, pos 1): xyz1+2 ^^^^^^ === [InputLine]E ### x1+*2 === Invalid input 'x', expected InputLine (line 1, pos 1): x1+*2 ^ --- Invalid input '*', expected Term (line 1, pos 4): x1+*2 ^ === [InputLine, {3}]E '1+2' [Expression, {3}]E '1+2' [Term, {1}]E '1' [Factor, {1}]E '1' [Number, {1}]E '1' [Digits]E '1' [ZeroOrMore, {1}] [ZeroOrMore, {3}]E '+2' [FirstOf, {3}]E '+2' [Sequence, {3}]E '+2' ['+', {1}] '+' [Term, {2}]E '2' [Factor, {2}]E '2' [Number, {2}]E '2' [Digits, {1}]E '2' [ZeroOrMore, {2}] [EOI, {3}] ### 1X+2 === Invalid input 'X', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 2): 1X+2 ^ === [InputLine, {3}]E '1+2' [Expression, {3}]E '1+2' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {3}]E '+2' [FirstOf, {3}]E '+2' [Sequence, {3}]E '+2' ['+', {1}] '+' [Term, {2}] '2' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {2}] [EOI, {3}] ### 1+X2 === Invalid input 'X', expected Term (line 1, pos 3): 1+X2 ^ === [InputLine, {3}]E '1+2' [Expression, {3}]E '1+2' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {3}]E '+2' [FirstOf, {3}]E '+2' [Sequence, {3}]E '+2' ['+', {1}] '+' [Term, {2}]E '2' [Factor, {2}]E '2' [Number, {2}]E '2' [Digits, {1}]E '2' [ZeroOrMore, {2}] [EOI, {3}] ### 1+2X === Invalid input 'X', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 4): 1+2X ^ === [InputLine, {3}]E '1+2' [Expression, {3}] '1+2' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {3}] '+2' [FirstOf, {3}] '+2' [Sequence, {3}] '+2' ['+', {1}] '+' [Term, {2}] '2' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {2}] [EOI, {3}] ### 1+2X*(3-4)-5 === Invalid input 'X', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 4): 1+2X*(3-4)-5 ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}]E '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}]E '+2*(3-4)-5' [FirstOf, {-1}]E '+2*(3-4)' [Sequence, {-1}]E '+2*(3-4)' ['+', {1}] '+' [Term, {-2}]E '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}]E '*(3-4)' [FirstOf, {-2}]E '*(3-4)' [Sequence, {-2}]E '*(3-4)' ['*', {2}] '*' [Factor, {-1}] '(3-4)' [Parens, {-1}] '(3-4)' ['(', {2}] '(' [Expression, {-1}] '3-4' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}] '-4' [FirstOf, {-1}] '-4' [Sequence, {-1}] '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}] '-5' [Sequence, {-6}] '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*X(3-4)-5 === Invalid input 'X', expected Factor (line 1, pos 5): 1+2*X(3-4)-5 ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}]E '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}]E '+2*(3-4)-5' [FirstOf, {-1}]E '+2*(3-4)' [Sequence, {-1}]E '+2*(3-4)' ['+', {1}] '+' [Term, {-2}]E '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}]E '*(3-4)' [FirstOf, {-2}]E '*(3-4)' [Sequence, {-2}]E '*(3-4)' ['*', {2}] '*' [Factor, {-1}]E '(3-4)' [Parens, {-1}]E '(3-4)' ['(', {2}] '(' [Expression, {-1}] '3-4' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}] '-4' [FirstOf, {-1}] '-4' [Sequence, {-1}] '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}] '-5' [Sequence, {-6}] '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*(X3-4)-5 === Invalid input 'X', expected Expression (line 1, pos 6): 1+2*(X3-4)-5 ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}]E '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}]E '+2*(3-4)-5' [FirstOf, {-1}]E '+2*(3-4)' [Sequence, {-1}]E '+2*(3-4)' ['+', {1}] '+' [Term, {-2}]E '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}]E '*(3-4)' [FirstOf, {-2}]E '*(3-4)' [Sequence, {-2}]E '*(3-4)' ['*', {2}] '*' [Factor, {-1}]E '(3-4)' [Parens, {-1}]E '(3-4)' ['(', {2}] '(' [Expression, {-1}]E '3-4' [Term, {3}]E '3' [Factor, {3}]E '3' [Number, {3}]E '3' [Digits, {2}]E '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}] '-4' [FirstOf, {-1}] '-4' [Sequence, {-1}] '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}] '-5' [Sequence, {-6}] '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*(3X-4)-5 === Invalid input 'X', expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 7): 1+2*(3X-4)-5 ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}]E '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}]E '+2*(3-4)-5' [FirstOf, {-1}]E '+2*(3-4)' [Sequence, {-1}]E '+2*(3-4)' ['+', {1}] '+' [Term, {-2}]E '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}]E '*(3-4)' [FirstOf, {-2}]E '*(3-4)' [Sequence, {-2}]E '*(3-4)' ['*', {2}] '*' [Factor, {-1}]E '(3-4)' [Parens, {-1}]E '(3-4)' ['(', {2}] '(' [Expression, {-1}]E '3-4' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}]E '-4' [FirstOf, {-1}]E '-4' [Sequence, {-1}]E '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}] '-5' [Sequence, {-6}] '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*(3-X4)-5 === Invalid input 'X', expected Term (line 1, pos 8): 1+2*(3-X4)-5 ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}]E '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}]E '+2*(3-4)-5' [FirstOf, {-1}]E '+2*(3-4)' [Sequence, {-1}]E '+2*(3-4)' ['+', {1}] '+' [Term, {-2}]E '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}]E '*(3-4)' [FirstOf, {-2}]E '*(3-4)' [Sequence, {-2}]E '*(3-4)' ['*', {2}] '*' [Factor, {-1}]E '(3-4)' [Parens, {-1}]E '(3-4)' ['(', {2}] '(' [Expression, {-1}]E '3-4' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}]E '-4' [FirstOf, {-1}]E '-4' [Sequence, {-1}]E '-4' ['-', {3}] '-' [Term, {4}]E '4' [Factor, {4}]E '4' [Number, {4}]E '4' [Digits, {3}]E '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}] '-5' [Sequence, {-6}] '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*(3-4X)-5 === Invalid input 'X', expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 9): 1+2*(3-4X)-5 ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}]E '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}]E '+2*(3-4)-5' [FirstOf, {-1}]E '+2*(3-4)' [Sequence, {-1}]E '+2*(3-4)' ['+', {1}] '+' [Term, {-2}]E '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}]E '*(3-4)' [FirstOf, {-2}]E '*(3-4)' [Sequence, {-2}]E '*(3-4)' ['*', {2}] '*' [Factor, {-1}]E '(3-4)' [Parens, {-1}]E '(3-4)' ['(', {2}] '(' [Expression, {-1}] '3-4' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}] '-4' [FirstOf, {-1}] '-4' [Sequence, {-1}] '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}] '-5' [Sequence, {-6}] '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*(3-4)X-5 === Invalid input 'X', expected '*', '/', '+', '-' or EOI (line 1, pos 10): 1+2*(3-4)X-5 ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}]E '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}]E '+2*(3-4)-5' [FirstOf, {-1}] '+2*(3-4)' [Sequence, {-1}] '+2*(3-4)' ['+', {1}] '+' [Term, {-2}] '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}] '*(3-4)' [FirstOf, {-2}] '*(3-4)' [Sequence, {-2}] '*(3-4)' ['*', {2}] '*' [Factor, {-1}] '(3-4)' [Parens, {-1}] '(3-4)' ['(', {2}] '(' [Expression, {-1}] '3-4' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}] '-4' [FirstOf, {-1}] '-4' [Sequence, {-1}] '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}]E '-5' [Sequence, {-6}]E '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*(3-4)-X5 === Invalid input 'X', expected Term (line 1, pos 11): 1+2*(3-4)-X5 ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}]E '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}]E '+2*(3-4)-5' [FirstOf, {-1}] '+2*(3-4)' [Sequence, {-1}] '+2*(3-4)' ['+', {1}] '+' [Term, {-2}] '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}] '*(3-4)' [FirstOf, {-2}] '*(3-4)' [Sequence, {-2}] '*(3-4)' ['*', {2}] '*' [Factor, {-1}] '(3-4)' [Parens, {-1}] '(3-4)' ['(', {2}] '(' [Expression, {-1}] '3-4' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}] '-4' [FirstOf, {-1}] '-4' [Sequence, {-1}] '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}]E '-5' [Sequence, {-6}]E '-5' ['-', {-1}] '-' [Term, {5}]E '5' [Factor, {5}]E '5' [Number, {5}]E '5' [Digits, {-1}]E '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*(3-4)-5X === Invalid input 'X', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 12): 1+2*(3-4)-5X ^ === [InputLine, {-6}]E '1+2*(3-4)-5' [Expression, {-6}] '1+2*(3-4)-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-6}] '+2*(3-4)-5' [FirstOf, {-1}] '+2*(3-4)' [Sequence, {-1}] '+2*(3-4)' ['+', {1}] '+' [Term, {-2}] '2*(3-4)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-2}] '*(3-4)' [FirstOf, {-2}] '*(3-4)' [Sequence, {-2}] '*(3-4)' ['*', {2}] '*' [Factor, {-1}] '(3-4)' [Parens, {-1}] '(3-4)' ['(', {2}] '(' [Expression, {-1}] '3-4' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-1}] '-4' [FirstOf, {-1}] '-4' [Sequence, {-1}] '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [')', {-1}] ')' [FirstOf, {-6}] '-5' [Sequence, {-6}] '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [EOI, {-6}] ### 1+2*(3-4-5 === Unexpected end of input, expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 11): 1+2*(3-4-5 ^ === [InputLine, {-11}]E '1+2*(3-4-5)' [Expression, {-11}]E '1+2*(3-4-5)' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-11}]E '+2*(3-4-5)' [FirstOf, {-11}]E '+2*(3-4-5)' [Sequence, {-11}]E '+2*(3-4-5)' ['+', {1}] '+' [Term, {-12}]E '2*(3-4-5)' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {-12}]E '*(3-4-5)' [FirstOf, {-12}]E '*(3-4-5)' [Sequence, {-12}]E '*(3-4-5)' ['*', {2}] '*' [Factor, {-6}]E '(3-4-5)' [Parens, {-6}]E '(3-4-5)' ['(', {2}] '(' [Expression, {-6}] '3-4-5' [Term, {3}] '3' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [ZeroOrMore, {3}] [ZeroOrMore, {-6}] '-4-5' [FirstOf, {-1}] '-4' [Sequence, {-1}] '-4' ['-', {3}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {3}] '4' [ZeroOrMore, {4}] [FirstOf, {-6}] '-5' [Sequence, {-6}] '-5' ['-', {-1}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {-1}] '5' [ZeroOrMore, {5}] [')', {-6}]E ')' [EOI, {-11}] ### 1+2*3-4)-5 === Invalid input ')', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 8): 1+2*3-4)-5 ^ === [InputLine, {-2}]E '1+2*3-4-5' [Expression, {-2}]E '1+2*3-4-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-2}]E '+2*3-4-5' [FirstOf, {7}] '+2*3' [Sequence, {7}] '+2*3' ['+', {1}] '+' [Term, {6}] '2*3' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {6}] '*3' [FirstOf, {6}] '*3' [Sequence, {6}] '*3' ['*', {2}] '*' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [FirstOf, {3}] '-4' [Sequence, {3}] '-4' ['-', {7}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {7}] '4' [ZeroOrMore, {4}] [FirstOf, {-2}]E '-5' [Sequence, {-2}]E '-5' ['-', {3}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {3}] '5' [ZeroOrMore, {5}] [EOI, {-2}] ### 1+*6 === Invalid input '*', expected Term (line 1, pos 3): 1+*6 ^ === [InputLine, {7}]E '1+6' [Expression, {7}]E '1+6' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {7}]E '+6' [FirstOf, {7}]E '+6' [Sequence, {7}]E '+6' ['+', {1}] '+' [Term, {6}]E '6' [Factor, {6}]E '6' [Number, {6}]E '6' [Digits, {1}]E '6' [ZeroOrMore, {6}] [EOI, {7}] ### 1+++2 === Invalid input '+', expected Term (line 1, pos 3): 1+++2 ^ === [InputLine, {3}]E '1+0+2' [Expression, {3}]E '1+0+2' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {3}]E '+0+2' [FirstOf, {1}]E '+0' [Sequence, {1}]E '+0' ['+', {1}] '+' [Term, {0}]E '0' [Factor, {0}]E '0' [Number, {0}]E '0' [Digits, {1}]E '0' [ZeroOrMore, {0}] [FirstOf, {3}] '+2' [Sequence, {3}] '+2' ['+', {1}] '+' [Term, {2}] '2' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {2}] [EOI, {3}] ### 1+2x*3-4)-5 === Invalid input 'x', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 4): 1+2x*3-4)-5 ^ --- Invalid input ')', expected Digit, '*', '/', '+', '-' or EOI (line 1, pos 9): 1+2x*3-4)-5 ^ === [InputLine, {-2}]E '1+2*3-4-5' [Expression, {-2}]E '1+2*3-4-5' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {-2}]E '+2*3-4-5' [FirstOf, {7}]E '+2*3' [Sequence, {7}]E '+2*3' ['+', {1}] '+' [Term, {6}]E '2*3' [Factor, {2}] '2' [Number, {2}] '2' [Digits, {1}] '2' [ZeroOrMore, {6}]E '*3' [FirstOf, {6}]E '*3' [Sequence, {6}]E '*3' ['*', {2}] '*' [Factor, {3}] '3' [Number, {3}] '3' [Digits, {2}] '3' [FirstOf, {3}] '-4' [Sequence, {3}] '-4' ['-', {7}] '-' [Term, {4}] '4' [Factor, {4}] '4' [Number, {4}] '4' [Digits, {7}] '4' [ZeroOrMore, {4}] [FirstOf, {-2}]E '-5' [Sequence, {-2}]E '-5' ['-', {3}] '-' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {3}] '5' [ZeroOrMore, {5}] [EOI, {-2}] ### ((1+x2) === Invalid input 'x', expected Term (line 1, pos 5): ((1+x2) ^ --- Unexpected end of input, expected '*', '/', '+', '-' or ')' (line 1, pos 8): ((1+x2) ^ === [InputLine, {3}]E '((1+2))' [Expression, {3}]E '((1+2))' [Term, {3}]E '((1+2))' [Factor, {3}]E '((1+2))' [Parens, {3}]E '((1+2))' ['('] '(' [Expression, {3}]E '(1+2)' [Term, {3}]E '(1+2)' [Factor, {3}]E '(1+2)' [Parens, {3}]E '(1+2)' ['('] '(' [Expression, {3}]E '1+2' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits] '1' [ZeroOrMore, {1}] [ZeroOrMore, {3}]E '+2' [FirstOf, {3}]E '+2' [Sequence, {3}]E '+2' ['+', {1}] '+' [Term, {2}]E '2' [Factor, {2}]E '2' [Number, {2}]E '2' [Digits, {1}]E '2' [ZeroOrMore, {2}] [')', {3}] ')' [ZeroOrMore, {3}] [ZeroOrMore, {3}] [')', {3}]E ')' [ZeroOrMore, {3}] [ZeroOrMore, {3}] [EOI, {3}] ### (2+*(5-1x)y+1 === Invalid input '*', expected Term (line 1, pos 4): (2+*(5-1x)y+1 ^ --- Invalid input 'x', expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 9): (2+*(5-1x)y+1 ^ --- Invalid input 'y', expected '*', '/', '+', '-' or ')' (line 1, pos 11): (2+*(5-1x)y+1 ^ === [InputLine, {7}]E '(2+(5-1))+1' [Expression, {7}]E '(2+(5-1))+1' [Term, {6}]E '(2+(5-1))' [Factor, {6}]E '(2+(5-1))' [Parens, {6}]E '(2+(5-1))' ['('] '(' [Expression, {6}]E '2+(5-1)' [Term, {2}] '2' [Factor, {2}] '2' [Number, {2}] '2' [Digits] '2' [ZeroOrMore, {2}] [ZeroOrMore, {6}]E '+(5-1)' [FirstOf, {6}]E '+(5-1)' [Sequence, {6}]E '+(5-1)' ['+', {2}] '+' [Term, {4}]E '(5-1)' [Factor, {4}]E '(5-1)' [Parens, {4}]E '(5-1)' ['(', {2}] '(' [Expression, {4}] '5-1' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {2}] '5' [ZeroOrMore, {5}] [ZeroOrMore, {4}] '-1' [FirstOf, {4}] '-1' [Sequence, {4}] '-1' ['-', {5}] '-' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits, {5}] '1' [ZeroOrMore, {1}] [')', {4}] ')' [ZeroOrMore, {4}] [')', {6}]E ')' [ZeroOrMore, {6}] [ZeroOrMore, {7}] '+1' [FirstOf, {7}] '+1' [Sequence, {7}] '+1' ['+', {6}] '+' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits, {6}] '1' [ZeroOrMore, {1}] [EOI, {7}] ### (2*ab4-7 === Invalid input 'a...', expected Factor (line 1, pos 4): (2*ab4-7 ^^^ --- Unexpected end of input, expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 9): (2*ab4-7 ^ === [InputLine, {-7}]E '(2*-7)' [Expression, {-7}]E '(2*-7)' [Term, {-7}]E '(2*-7)' [Factor, {-7}]E '(2*-7)' [Parens, {-7}]E '(2*-7)' ['('] '(' [Expression, {-7}]E '2*-7' [Term, {0}]E '2*' [Factor, {2}] '2' [Number, {2}] '2' [Digits] '2' [ZeroOrMore, {0}]E '*' [FirstOf, {0}]E '*' [Sequence, {2}]E '*' ['*', {2}] '*' [ZeroOrMore, {-7}] '-7' [FirstOf, {-7}] '-7' [Sequence, {-7}] '-7' ['-', {0}] '-' [Term, {7}] '7' [Factor, {7}] '7' [Number, {7}] '7' [Digits, {0}] '7' [ZeroOrMore, {7}] [')', {-7}]E ')' [ZeroOrMore, {-7}] [ZeroOrMore, {-7}] [EOI, {-7}] ### >>>(2++*(5-1xy)y+1 === Invalid input '+', expected Term (line 1, pos 4): (2++*(5-1xy)y+1 ^ --- Invalid input 'x...', expected Digit, '*', '/', '+', '-' or ')' (line 1, pos 10): (2++*(5-1xy)y+1 ^^ --- Invalid input 'y', expected '*', '/', '+', '-' or EOI (line 1, pos 13): (2++*(5-1xy)y+1 ^ === [InputLine, {3}]E '(2+0*(5-1)+1' [Expression, {3}]E '(2+0*(5-1)+1' [Term, {2}]E '(2+0*(5-1)' [Factor, {2}]E '(2+0*(5-1)' [Parens, {2}]E '(2+0*(5-1)' ['('] '(' [Expression, {2}]E '2+0*(5-1' [Term, {2}] '2' [Factor, {2}] '2' [Number, {2}] '2' [Digits] '2' [ZeroOrMore, {2}] [ZeroOrMore, {2}]E '+0*(5-1' [FirstOf, {2}]E '+0*(5-1' [Sequence, {2}]E '+0*(5-1' ['+', {2}] '+' [Term, {0}]E '0*(5-1' [Factor, {0}]E '0' [Number, {0}]E '0' [Digits, {2}]E '0' [ZeroOrMore, {0}]E '*(5-1' [FirstOf, {0}]E '*(5-1' [Sequence, {0}]E '*(5-1' ['*', {0}] '*' [Factor, {4}]E '(5-1' [Parens, {0}]E '(5-1' ['(', {0}] '(' [Expression, {4}] '5-1' [Term, {5}] '5' [Factor, {5}] '5' [Number, {5}] '5' [Digits, {0}] '5' [ZeroOrMore, {5}] [ZeroOrMore, {4}] '-1' [FirstOf, {4}] '-1' [Sequence, {4}] '-1' ['-', {5}] '-' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits, {5}] '1' [ZeroOrMore, {1}] [')', {2}] ')' [ZeroOrMore, {2}] [ZeroOrMore, {3}]E '+1' [FirstOf, {3}]E '+1' [Sequence, {3}]E '+1' ['+', {2}] '+' [Term, {1}] '1' [Factor, {1}] '1' [Number, {1}] '1' [Digits, {2}] '1' [ZeroOrMore, {1}] [EOI, {3}] parboiled-1.4.1/examples-java/src/test/resources/JavaErrorRecoveryTest.test000066400000000000000000000563451421263112100271540ustar00rootroot00000000000000package org.parboiled.examples; public class JavaTestSource { public String method(int param { return name; } } === Invalid input '{', expected Whitespace, "/*", "//", Dim, ',' or ')' (line 3, pos 36): public String method(int param { ^ === [CompilationUnit]E 'package org.parboiled.examples;\npublic class JavaTestSource {\n public String method(int param ){\n return name;\n }\n}\n' [Optional] 'package org.parboiled.examples;\n' [PackageDeclaration] 'package org.parboiled.examples;\n' [Sequence] 'package org.parboiled.examples;\n' [QualifiedIdentifier] 'org.parboiled.examples' [Identifier] 'org' [ZeroOrMore] '.parboiled.examples' [Sequence] '.parboiled' [Identifier] 'parboiled' [Sequence] '.examples' [Identifier] 'examples' [ZeroOrMore]E 'public class JavaTestSource {\n public String method(int param ){\n return name;\n }\n}\n' [TypeDeclaration]E 'public class JavaTestSource {\n public String method(int param ){\n return name;\n }\n}\n' [Sequence]E 'public class JavaTestSource {\n public String method(int param ){\n return name;\n }\n}\n' [ZeroOrMore] 'public ' [Modifier] 'public ' [Sequence] 'public ' [FirstOf] 'public' ["public"] 'public' [FirstOf]E 'class JavaTestSource {\n public String method(int param ){\n return name;\n }\n}\n' [ClassDeclaration]E 'class JavaTestSource {\n public String method(int param ){\n return name;\n }\n}\n' [Identifier] 'JavaTestSource ' [ClassBody]E '{\n public String method(int param ){\n return name;\n }\n}\n' [ZeroOrMore]E 'public String method(int param ){\n return name;\n }\n' [ClassBodyDeclaration]E 'public String method(int param ){\n return name;\n }\n' [Sequence]E 'public String method(int param ){\n return name;\n }\n' [ZeroOrMore] 'public ' [Modifier] 'public ' [Sequence] 'public ' [FirstOf] 'public' ["public"] 'public' [MemberDecl]E 'String method(int param ){\n return name;\n }\n' [Sequence]E 'String method(int param ){\n return name;\n }\n' [Type] 'String ' [FirstOf] 'String ' [ClassType] 'String ' [Identifier] 'String ' [Identifier] 'method' [MethodDeclaratorRest]E '(int param ){\n return name;\n }\n' [FormalParameters]E '(int param )' [Optional] 'int param ' [FormalParameterDecls] 'int param ' [Type] 'int ' [FirstOf] 'int ' [BasicType] 'int ' [FirstOf] 'int' ["int"] 'int' [FormalParameterDeclsRest] 'param ' [Sequence] 'param ' [VariableDeclaratorId] 'param ' [Identifier] 'param ' [FirstOf] '{\n return name;\n }\n' [MethodBody] '{\n return name;\n }\n' [BlockStatements] 'return name;\n ' [BlockStatement] 'return name;\n ' [Statement] 'return name;\n ' [Sequence] 'return name;\n ' [Optional] 'name' [Expression] 'name' [ConditionalExpression] 'name' [ConditionalOrExpression] 'name' [ConditionalAndExpression] 'name' [InclusiveOrExpression] 'name' [ExclusiveOrExpression] 'name' [AndExpression] 'name' [EqualityExpression] 'name' [RelationalExpression] 'name' [ShiftExpression] 'name' [AdditiveExpression] 'name' [MultiplicativeExpression] 'name' [UnaryExpression] 'name' [Sequence] 'name' [Primary] 'name' [Sequence] 'name' [QualifiedIdentifier] 'name' [Identifier] 'name' [EOI] ### package org.parboiled.examples; public class JavaTestSource { public String method(int param) { String name = toString(; return name; } } === Invalid input ';', expected Spacing, Expression or ')' (line 4, pos 32): String name = toString(; ^ === [CompilationUnit]E 'package org.parboiled.examples;\npublic class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name;\n }\n}\n' [Optional] 'package org.parboiled.examples;\n' [PackageDeclaration] 'package org.parboiled.examples;\n' [Sequence] 'package org.parboiled.examples;\n' [QualifiedIdentifier] 'org.parboiled.examples' [Identifier] 'org' [ZeroOrMore] '.parboiled.examples' [Sequence] '.parboiled' [Identifier] 'parboiled' [Sequence] '.examples' [Identifier] 'examples' [ZeroOrMore]E 'public class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name;\n }\n}\n' [TypeDeclaration]E 'public class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name;\n }\n}\n' [Sequence]E 'public class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name;\n }\n}\n' [ZeroOrMore] 'public ' [Modifier] 'public ' [Sequence] 'public ' [FirstOf] 'public' ["public"] 'public' [FirstOf]E 'class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name;\n }\n}\n' [ClassDeclaration]E 'class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name;\n }\n}\n' [Identifier] 'JavaTestSource ' [ClassBody]E '{\n public String method(int param) {\n String name = toString();\n return name;\n }\n}\n' [ZeroOrMore]E 'public String method(int param) {\n String name = toString();\n return name;\n }\n' [ClassBodyDeclaration]E 'public String method(int param) {\n String name = toString();\n return name;\n }\n' [Sequence]E 'public String method(int param) {\n String name = toString();\n return name;\n }\n' [ZeroOrMore] 'public ' [Modifier] 'public ' [Sequence] 'public ' [FirstOf] 'public' ["public"] 'public' [MemberDecl]E 'String method(int param) {\n String name = toString();\n return name;\n }\n' [Sequence]E 'String method(int param) {\n String name = toString();\n return name;\n }\n' [Type] 'String ' [FirstOf] 'String ' [ClassType] 'String ' [Identifier] 'String ' [Identifier] 'method' [MethodDeclaratorRest]E '(int param) {\n String name = toString();\n return name;\n }\n' [FormalParameters] '(int param) ' [Optional] 'int param' [FormalParameterDecls] 'int param' [Type] 'int ' [FirstOf] 'int ' [BasicType] 'int ' [FirstOf] 'int' ["int"] 'int' [FormalParameterDeclsRest] 'param' [Sequence] 'param' [VariableDeclaratorId] 'param' [Identifier] 'param' [FirstOf]E '{\n String name = toString();\n return name;\n }\n' [MethodBody]E '{\n String name = toString();\n return name;\n }\n' [BlockStatements]E 'String name = toString();\n return name;\n ' [BlockStatement]E 'String name = toString();\n ' [LocalVariableDeclarationStatement]E 'String name = toString();\n ' [Type] 'String ' [FirstOf] 'String ' [ClassType] 'String ' [Identifier] 'String ' [VariableDeclarators]E 'name = toString()' [VariableDeclarator]E 'name = toString()' [Identifier] 'name ' [Optional]E '= toString()' [Sequence]E '= toString()' [VariableInitializer]E 'toString()' [Expression]E 'toString()' [ConditionalExpression]E 'toString()' [ConditionalOrExpression]E 'toString()' [ConditionalAndExpression]E 'toString()' [InclusiveOrExpression]E 'toString()' [ExclusiveOrExpression]E 'toString()' [AndExpression]E 'toString()' [EqualityExpression]E 'toString()' [RelationalExpression]E 'toString()' [ShiftExpression]E 'toString()' [AdditiveExpression]E 'toString()' [MultiplicativeExpression]E 'toString()' [UnaryExpression]E 'toString()' [Sequence]E 'toString()' [Primary]E 'toString()' [Sequence]E 'toString()' [QualifiedIdentifier] 'toString' [Identifier] 'toString' [Optional]E '()' [IdentifierSuffix]E '()' [Arguments]E '()' [BlockStatement] 'return name;\n ' [Statement] 'return name;\n ' [Sequence] 'return name;\n ' [Optional] 'name' [Expression] 'name' [ConditionalExpression] 'name' [ConditionalOrExpression] 'name' [ConditionalAndExpression] 'name' [InclusiveOrExpression] 'name' [ExclusiveOrExpression] 'name' [AndExpression] 'name' [EqualityExpression] 'name' [RelationalExpression] 'name' [ShiftExpression] 'name' [AdditiveExpression] 'name' [MultiplicativeExpression] 'name' [UnaryExpression] 'name' [Sequence] 'name' [Primary] 'name' [Sequence] 'name' [QualifiedIdentifier] 'name' [Identifier] 'name' [EOI] ### package org.parboiled.examples; public class JavaTestSource { public String method(int param) { String name = toString(); return name } } === Invalid input '}', expected Whitespace, "/*", "//", '.', IdentifierSuffix, Selector, PostFixOp, '*', '/', '%', '+', '-', '<<', '>>', '>>>', '<=', '>=', '<', '>', 'instanceof', '==', '!=', '&', '^', '|', '&&', '||', '?', AssignmentOperator or ';' (line 6, pos 5): } ^ === [CompilationUnit]E 'package org.parboiled.examples;\npublic class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name\n ;}\n}\n' [Optional] 'package org.parboiled.examples;\n' [PackageDeclaration] 'package org.parboiled.examples;\n' [Sequence] 'package org.parboiled.examples;\n' [QualifiedIdentifier] 'org.parboiled.examples' [Identifier] 'org' [ZeroOrMore] '.parboiled.examples' [Sequence] '.parboiled' [Identifier] 'parboiled' [Sequence] '.examples' [Identifier] 'examples' [ZeroOrMore]E 'public class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name\n ;}\n}\n' [TypeDeclaration]E 'public class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name\n ;}\n}\n' [Sequence]E 'public class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name\n ;}\n}\n' [ZeroOrMore] 'public ' [Modifier] 'public ' [Sequence] 'public ' [FirstOf] 'public' ["public"] 'public' [FirstOf]E 'class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name\n ;}\n}\n' [ClassDeclaration]E 'class JavaTestSource {\n public String method(int param) {\n String name = toString();\n return name\n ;}\n}\n' [Identifier] 'JavaTestSource ' [ClassBody]E '{\n public String method(int param) {\n String name = toString();\n return name\n ;}\n}\n' [ZeroOrMore]E 'public String method(int param) {\n String name = toString();\n return name\n ;}\n' [ClassBodyDeclaration]E 'public String method(int param) {\n String name = toString();\n return name\n ;}\n' [Sequence]E 'public String method(int param) {\n String name = toString();\n return name\n ;}\n' [ZeroOrMore] 'public ' [Modifier] 'public ' [Sequence] 'public ' [FirstOf] 'public' ["public"] 'public' [MemberDecl]E 'String method(int param) {\n String name = toString();\n return name\n ;}\n' [Sequence]E 'String method(int param) {\n String name = toString();\n return name\n ;}\n' [Type] 'String ' [FirstOf] 'String ' [ClassType] 'String ' [Identifier] 'String ' [Identifier] 'method' [MethodDeclaratorRest]E '(int param) {\n String name = toString();\n return name\n ;}\n' [FormalParameters] '(int param) ' [Optional] 'int param' [FormalParameterDecls] 'int param' [Type] 'int ' [FirstOf] 'int ' [BasicType] 'int ' [FirstOf] 'int' ["int"] 'int' [FormalParameterDeclsRest] 'param' [Sequence] 'param' [VariableDeclaratorId] 'param' [Identifier] 'param' [FirstOf]E '{\n String name = toString();\n return name\n ;}\n' [MethodBody]E '{\n String name = toString();\n return name\n ;}\n' [BlockStatements]E 'String name = toString();\n return name\n ;' [BlockStatement] 'String name = toString();\n ' [LocalVariableDeclarationStatement] 'String name = toString();\n ' [Type] 'String ' [FirstOf] 'String ' [ClassType] 'String ' [Identifier] 'String ' [VariableDeclarators] 'name = toString()' [VariableDeclarator] 'name = toString()' [Identifier] 'name ' [Optional] '= toString()' [Sequence] '= toString()' [VariableInitializer] 'toString()' [Expression] 'toString()' [ConditionalExpression] 'toString()' [ConditionalOrExpression] 'toString()' [ConditionalAndExpression] 'toString()' [InclusiveOrExpression] 'toString()' [ExclusiveOrExpression] 'toString()' [AndExpression] 'toString()' [EqualityExpression] 'toString()' [RelationalExpression] 'toString()' [ShiftExpression] 'toString()' [AdditiveExpression] 'toString()' [MultiplicativeExpression] 'toString()' [UnaryExpression] 'toString()' [Sequence] 'toString()' [Primary] 'toString()' [Sequence] 'toString()' [QualifiedIdentifier] 'toString' [Identifier] 'toString' [Optional] '()' [IdentifierSuffix] '()' [Arguments] '()' [BlockStatement]E 'return name\n ;' [Statement]E 'return name\n ;' [Sequence]E 'return name\n ;' [Optional] 'name\n ' [Expression] 'name\n ' [ConditionalExpression] 'name\n ' [ConditionalOrExpression] 'name\n ' [ConditionalAndExpression] 'name\n ' [InclusiveOrExpression] 'name\n ' [ExclusiveOrExpression] 'name\n ' [AndExpression] 'name\n ' [EqualityExpression] 'name\n ' [RelationalExpression] 'name\n ' [ShiftExpression] 'name\n ' [AdditiveExpression] 'name\n ' [MultiplicativeExpression] 'name\n ' [UnaryExpression] 'name\n ' [Sequence] 'name\n ' [Primary] 'name\n ' [Sequence] 'name\n ' [QualifiedIdentifier] 'name\n ' [Identifier] 'name\n ' [EOI] parboiled-1.4.1/examples-java/src/test/resources/SimpleJavaTest.test000066400000000000000000000045211421263112100255620ustar00rootroot00000000000000/* * Copyright (C) 2009-2010 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.java; import org.parboiled.Node; import org.parboiled.Parboiled; import org.parboiled.ParserStatistics; import static org.parboiled.support.ParseTreeUtils.printNodeTree; public class SimpleJavaTest { private char \u0041_identifierWithNonAsciiCharacters_åäöÅÄÖ_\u0030_$ = '\u0061'; @SuppressWarnings("unused") private char[] octalEscapes = new char[] {'\1', '\12', '\123'}; @Test public void simpleJavaTest() { String testSource = FileUtils.readAllTextFromResource("SimpleJavaTest.java"); JavaParser parser = Parboiled.createParser(JavaParser.class); Rule compilationUnit = parser.CompilationUnit(); assertEquals(ParserStatistics.generateFor(compilationUnit).toString(), "" + "Parser statistics for rule 'CompilationUnit':\n" + "MemoMismatchesMatchers: 7\n"); ParsingResult parsingResult = RecoveringParseRunner.run(compilationUnit, testSource); if (parsingResult.hasErrors()) { fail("\n--- ParseErrors ---\n" + StringUtils.join(parsingResult.parseErrors, "---\n") + "\n--- ParseTree ---\n" + printNodeTree(parsingResult, Filters.SKIP_EMPTY_OPTS_AND_ZOMS, Predicates.>alwaysTrue()) ); } assertEquals( printTree(parsingResult.parseTreeRoot, new Formatter>() { public String format(Node node) { return node.toString(); } }, Filters.SKIP_EMPTY_OPTS_AND_ZOMS, Predicates.>alwaysTrue()), FileUtils.readAllTextFromResource("SimpleJavaTestParseTree.test") ); } } parboiled-1.4.1/examples-java/src/test/resources/SimpleJavaTestParseTree.test000066400000000000000000004016071421263112100274030ustar00rootroot00000000000000[CompilationUnit] [Optional] [PackageDeclaration] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Sequence] [Identifier] [Sequence] [Identifier] [ZeroOrMore] [ImportDeclaration] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Sequence] [Identifier] [ImportDeclaration] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Sequence] [Identifier] [ImportDeclaration] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Sequence] [Identifier] [ImportDeclaration] [Optional] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Sequence] [Identifier] [Sequence] [Identifier] [Sequence] [Identifier] [ZeroOrMore] [TypeDeclaration] [Sequence] [ZeroOrMore] [Modifier] [Sequence] [FirstOf] [FirstOf] [ClassDeclaration] [Identifier] [ClassBody] [ZeroOrMore] [ClassBodyDeclaration] [Sequence] [ZeroOrMore] [Modifier] [Sequence] [FirstOf] [MemberDecl] [Sequence] [Type] [FirstOf] [BasicType] [FirstOf] [VariableDeclarators] [VariableDeclarator] [Identifier] [Optional] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [CharLiteral] [FirstOf] [ClassBodyDeclaration] [Sequence] [ZeroOrMore] [Modifier] [Annotation] [QualifiedIdentifier] [Identifier] [Optional] [AnnotationRest] [SingleElementAnnotationRest] [ElementValue] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [Modifier] [Sequence] [FirstOf] [MemberDecl] [Sequence] [Type] [FirstOf] [BasicType] [FirstOf] [ZeroOrMore] [Dim] [VariableDeclarators] [VariableDeclarator] [Identifier] [Optional] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [Creator] [Sequence] [FirstOf] [ClassType] [Identifier] [ArrayCreatorRest] [FirstOf] [Sequence] [ArrayInitializer] [Optional] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [CharLiteral] [FirstOf] [ZeroOrMore] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [CharLiteral] [FirstOf] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [CharLiteral] [FirstOf] [ClassBodyDeclaration] [Sequence] [ZeroOrMore] [Modifier] [Annotation] [QualifiedIdentifier] [Identifier] [Modifier] [Sequence] [FirstOf] [MemberDecl] [Sequence] [Identifier] [VoidMethodDeclaratorRest] [FormalParameters] [FirstOf] [MethodBody] [BlockStatements] [BlockStatement] [LocalVariableDeclarationStatement] [Type] [FirstOf] [ClassType] [Identifier] [VariableDeclarators] [VariableDeclarator] [Identifier] [Optional] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [BlockStatement] [LocalVariableDeclarationStatement] [Type] [FirstOf] [ClassType] [Identifier] [VariableDeclarators] [VariableDeclarator] [Identifier] [Optional] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [Optional] [IdentifierSuffix] [Sequence] [FirstOf] [BlockStatement] [LocalVariableDeclarationStatement] [Type] [FirstOf] [ClassType] [Identifier] [VariableDeclarators] [VariableDeclarator] [Identifier] [Optional] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [BlockStatement] [Statement] [Sequence] [StatementExpression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Selector] [Sequence] [Identifier] [Optional] [Arguments] [ZeroOrMore] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [Sequence] [FirstOf] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [Sequence] [FirstOf] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [BlockStatement] [LocalVariableDeclarationStatement] [Type] [FirstOf] [ClassType] [Identifier] [Optional] [TypeArguments] [TypeArgument] [ReferenceType] [Sequence] [ClassType] [Identifier] [VariableDeclarators] [VariableDeclarator] [Identifier] [Optional] [Sequence] [VariableInitializer] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [BlockStatement] [Statement] [Sequence] [ParExpression] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Statement] [Block] [BlockStatements] [BlockStatement] [Statement] [Sequence] [StatementExpression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [ZeroOrMore] [Sequence] [FirstOf] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [ZeroOrMore] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [Sequence] [FirstOf] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [Sequence] [FirstOf] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [Optional] [IdentifierSuffix] [Sequence] [FirstOf] [ExplicitGenericInvocation] [NonWildcardTypeArguments] [ReferenceType] [Sequence] [ClassType] [Identifier] [Optional] [TypeArguments] [TypeArgument] [ReferenceType] [Sequence] [ClassType] [Identifier] [ExplicitGenericInvocationSuffix] [Sequence] [Identifier] [Arguments] [BlockStatement] [Statement] [Sequence] [StatementExpression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [ZeroOrMore] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [Creator] [Sequence] [CreatedName] [Identifier] [Optional] [NonWildcardTypeArguments] [ReferenceType] [Sequence] [ClassType] [Identifier] [Optional] [TypeArguments] [TypeArgument] [ReferenceType] [Sequence] [ClassType] [Identifier] [ClassCreatorRest] [Arguments] [Optional] [ClassBody] [ZeroOrMore] [ClassBodyDeclaration] [Sequence] [ZeroOrMore] [Modifier] [Sequence] [FirstOf] [MemberDecl] [Sequence] [Type] [FirstOf] [ClassType] [Identifier] [Identifier] [MethodDeclaratorRest] [FormalParameters] [Optional] [FormalParameterDecls] [Type] [FirstOf] [ClassType] [Identifier] [Optional] [TypeArguments] [TypeArgument] [ReferenceType] [Sequence] [ClassType] [Identifier] [FormalParameterDeclsRest] [Sequence] [VariableDeclaratorId] [Identifier] [FirstOf] [MethodBody] [BlockStatements] [BlockStatement] [Statement] [Sequence] [Optional] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [Optional] [IdentifierSuffix] [Sequence] [FirstOf] [ExplicitGenericInvocation] [NonWildcardTypeArguments] [ReferenceType] [Sequence] [ClassType] [Identifier] [Optional] [TypeArguments] [TypeArgument] [ReferenceType] [Sequence] [ClassType] [Identifier] [ExplicitGenericInvocationSuffix] [Sequence] [Identifier] [Arguments] [ZeroOrMore] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Sequence] [QualifiedIdentifier] [Identifier] [ZeroOrMore] [Sequence] [Identifier] [Optional] [IdentifierSuffix] [Arguments] [Optional] [Sequence] [Expression] [ConditionalExpression] [ConditionalOrExpression] [ConditionalAndExpression] [InclusiveOrExpression] [ExclusiveOrExpression] [AndExpression] [EqualityExpression] [RelationalExpression] [ShiftExpression] [AdditiveExpression] [MultiplicativeExpression] [UnaryExpression] [Sequence] [Primary] [Literal] [FirstOf] [StringLiteral] [ZeroOrMore] [EOI] parboiled-1.4.1/examples-java/src/test/resources/SparqlTest.test000066400000000000000000001766741421263112100250140ustar00rootroot00000000000000/* * Copyright 2007-2009 The sparkle-g Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Juergen Pfundt (Juergen.Pfundt) * @author Simone Tripodi (simone.tripodi) * @version $Id$ */ gunit Sparql; @header { package com.sparkleg; } query: << SELECT * WHERE { } >> OK << SELECT * {} >> OK << # No trailing dot PREFIX : SELECT * WHERE { ?x ?y ?z } >> OK << # With trailing dot SELECT * WHERE { ?x ?y ?z . } >> OK << # Two triples : no trailing dot SELECT * WHERE { ?x ?y ?z . ?a ?b ?c } >> OK << # Two triples : with trailing dot SELECT * WHERE { ?x ?y ?z . ?a ?b ?c . } >> OK << PREFIX : SELECT * WHERE { [:p :q ] } >> OK << PREFIX : SELECT * WHERE { [] :p :q } >> OK << PREFIX : SELECT * WHERE { [ ?x ?y ] :p [ ?pa ?b ] } >> OK << PREFIX : SELECT * WHERE { [ :p :q ; ] } >> OK << PREFIX : SELECT * WHERE { _:a :p1 :q1 . _:a :p2 :q2 . } >> OK << SELECT * WHERE { ?s ?p ?o . FILTER (?o) } >> OK << SELECT * WHERE { ?s ?p ?o . FILTER REGEX(?o, "foo") } >> OK << SELECT * WHERE { ?s ?p ?o . FILTER REGEX(?o, "foo", "i") } >> OK << PREFIX xsd: SELECT * WHERE { ?s ?p ?o . FILTER xsd:integer(?o) } >> OK << PREFIX : PREFIX xsd: SELECT * WHERE { ?s ?p ?o . FILTER :myFunc(?s,?o) } >> OK << PREFIX : SELECT * WHERE { ( [ ?x ?y ] ) :p ( [ ?pa ?b ] 57 ) } >> OK << PREFIX : SELECT * WHERE { ( [] [] ) } >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY ?o LIMIT 5 >> OK << # LIMIT and OFFSET can be in either order PREFIX : SELECT * { ?s ?p ?o } ORDER BY ?o LIMIT 5 OFFSET 3 >> OK << # LIMIT and OFFSET can be in either order PREFIX : SELECT * { ?s ?p ?o } ORDER BY ?o OFFSET 3 LIMIT 5 >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY ?o OFFSET 3 >> OK << PREFIX : SELECT * WHERE { ( ?x ) :p ?z } >> OK << PREFIX : SELECT * WHERE { ?x :p ( ?z ) } >> OK << SELECT * WHERE { ( ?z ) } >> OK << SELECT * WHERE { ( ( ?z ) ) } >> OK << SELECT * WHERE { ( ( ) ) } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p "x" } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p 'x' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p "x\"y'z" } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p 'x"y\'z' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p "x\"" } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p 'x\'' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p 123 } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p 123. . } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p """Long "" Literal """ } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p '''Long '' """ Literal''' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p """Long""\"Literal""" } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p '''Long''\'Literal''' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p """Long\"""Literal""" } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p '''Long\'''Literal''' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p '''Long '' Literal''' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p '''Long ' Literal''' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p '''Long''\\Literal with '\\ single quotes ''' } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p """Long "" Literal""" } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p """Long " Literal""" } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x :p """Long""\\Literal with "\\ single quotes""" } >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY ?o >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY (?o+5) >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY ASC(?o) >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY DESC(?o) >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY DESC(:func(?s, ?o)) >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY DESC(?o+57) :func2(?o) ASC(?s) >> OK << PREFIX : SELECT * { ?s ?p ?o } ORDER BY str(?o) >> OK << PREFIX : SELECT * { } >> OK << # No DOT after optional PREFIX : SELECT * { ?a :b :c OPTIONAL{:x :y :z} :x ?y ?z } >> OK << # No DOT between non-triples patterns PREFIX : SELECT * { ?a :b :c OPTIONAL{:x :y :z} { :x1 :y1 :z1 } UNION { :x2 :y2 :z2 } } >> OK << # No DOT between non-triples patterns PREFIX : SELECT * { OPTIONAL{:x :y :z} ?a :b :c { :x1 :y1 :z1 } UNION { :x2 :y2 :z2 } } >> OK << PREFIX : SELECT * { ?x :p ?z } >> OK << PREFIX : SELECT * WHERE { :x :p :z . } >> OK << PREFIX : SELECT * WHERE { :_1 :p.rdf :z.z . } >> OK << PREFIX : PREFIX a: SELECT * WHERE { : a: :a . : : : . } >> OK << PREFIX : <> SELECT * WHERE { : : : . } >> OK << PREFIX : <#> SELECT * WHERE { : : : . } >> OK << BASE PREFIX : <#> SELECT * WHERE { : : : . } >> OK << BASE PREFIX : <#> PREFIX x.y: SELECT * WHERE { :a.b x.y: : . } >> OK << # Operator PREFIX : SELECT * { OPTIONAL { } } >> OK << # Operator PREFIX : SELECT * { OPTIONAL { :a :b :c } } >> OK << # Triple, no DOT, operator PREFIX : SELECT * { :p :q :r OPTIONAL { :a :b :c } } >> OK << # Triple, DOT, operator PREFIX : SELECT * { :p :q :r . OPTIONAL { :a :b :c } } >> OK << # Triple, DOT, operator, DOT PREFIX : SELECT * { :p :q :r . OPTIONAL { :a :b :c } . } >> OK << # Operator, no DOT PREFIX : SELECT * { OPTIONAL { :a :b :c } } >> OK << # Operator, DOT PREFIX : SELECT * { OPTIONAL { :a :b :c } . } >> OK << # Operator, triple PREFIX : SELECT * { OPTIONAL { :a :b :c } ?x ?y ?z } >> OK << # Operator, DOT triple PREFIX : SELECT * { OPTIONAL { :a :b :c } . ?x ?y ?z } >> OK << # Triple, semi, operator PREFIX : SELECT * { :p :q :r ; OPTIONAL { :a :b :c } } >> OK << # Triple, semi, DOT, operator PREFIX : SELECT * { :p :q :r ; . OPTIONAL { :a :b :c } } >> OK << # Two elements in the group PREFIX : SELECT * { :p :q :r . OPTIONAL { :a :b :c } :p :q :r . OPTIONAL { :a :b :c } } >> OK << # Two elements in the group PREFIX : SELECT * { :p :q :r OPTIONAL { :a :b :c } :p :q :r OPTIONAL { :a :b :c } } >> OK << PREFIX : SELECT * { { ?s ?p ?o } UNION { ?a ?b ?c } } >> OK << PREFIX : SELECT * { { ?s ?p ?o } UNION { ?a ?b ?c } UNION { ?r ?s ?t } } >> OK << PREFIX : SELECT * WHERE { [] :p [] } >> OK << PREFIX : # Tab SELECT * WHERE { [ ] :p [ ] } >> OK << PREFIX : SELECT * WHERE { [ :p :q ] } >> OK << PREFIX : SELECT ?x FROM WHERE {} >> OK << PREFIX : SELECT ?x FROM NAMED WHERE {} >> OK << PREFIX : SELECT ?x FROM NAMED :graph1 FROM NAMED :graph2 WHERE {} >> OK << PREFIX : SELECT ?x FROM :g1 FROM :g2 FROM NAMED :graph1 FROM NAMED :graph2 WHERE {} >> OK << SELECT * WHERE {

"\t" } >> OK << SELECT * WHERE {

"x\t" } >> OK << SELECT * WHERE {

"\tx" } >> OK << PREFIX : SELECT * WHERE { <\u0078> :\u0070 ?xx\u0078 } >> OK << PREFIX : SELECT * # Comments can contain \ u # <\u0078> :\u0070 ?xx\u0078 WHERE { <\u0078> :\u0070 ?xx\u0078 } >> OK << ASK {} >> OK << CONSTRUCT { ?s . ?s ?o } WHERE {?s ?p ?o} >> OK << CONSTRUCT { ?s . ?s ?o .} WHERE {?s ?p ?o} >> OK << PREFIX rdf: CONSTRUCT { [] rdf:subject ?s ; rdf:predicate ?p ; rdf:object ?o } WHERE {?s ?p ?o} >> OK << PREFIX rdf: CONSTRUCT { [] rdf:subject ?s ; rdf:predicate ?p ; rdf:object ?o . } WHERE {?s ?p ?o} >> OK << CONSTRUCT {} WHERE {} >> OK << DESCRIBE >> OK << DESCRIBE ?u WHERE { ?u . } >> OK << SELECT * WHERE { } >> OK << SELECT * { } >> OK << PREFIX q: SELECT * WHERE { FILTER (q:name()) } >> OK << PREFIX q: SELECT * WHERE { FILTER (q:name( )) } >> OK << PREFIX q: SELECT * WHERE { FILTER (q:name( )) } >> OK << PREFIX q: SELECT * WHERE { FILTER (q:name(1 )) . FILTER (q:name(1,2)) . FILTER (q:name(1 ,2))} >> OK << SELECT * WHERE { } >> OK << SELECT * WHERE { _:x } >> OK << SELECT * WHERE { 1 } >> OK << SELECT * WHERE { +11 } >> OK << SELECT * WHERE { -1 } >> OK << SELECT * WHERE { 1.0 } >> OK << SELECT * WHERE { +1.0 } >> OK << SELECT * WHERE { -1.0 } >> OK << SELECT * WHERE { 1.0e0 } >> OK << SELECT * WHERE { +1.0e+1 } >> OK << SELECT * WHERE { -1.0e-1 } >> OK << # Legal, if unusual, IRIs SELECT * WHERE { } >> OK << # Legal, if unusual, IRIs BASE SELECT * WHERE { <#x> } >> OK << # Legal, if unusual, IRIs BASE SELECT * WHERE { <¶m=value> } >> OK << PREFIX : SELECT * WHERE { GRAPH ?g { } } >> OK << PREFIX : SELECT * WHERE { GRAPH :a { } } >> OK << PREFIX : SELECT * WHERE { GRAPH ?g { :x :b ?a } } >> OK << PREFIX : SELECT * WHERE { :x :p :z GRAPH ?g { :x :b ?a } } >> OK << PREFIX : SELECT * WHERE { :x :p :z GRAPH ?g { :x :b ?a . GRAPH ?g2 { :x :p ?x } } } >> OK << # use keyword FILTER as a namespace prefix PREFIX FILTER: SELECT * WHERE { ?x FILTER:foo ?z FILTER (?z) } >> OK << # use keyword FILTER as a local name PREFIX : SELECT * WHERE { ?x :FILTER ?z FILTER (?z) } >> OK << # use keyword UNION as a namespace prefix PREFIX UNION: SELECT * WHERE { ?x UNION:foo ?z } >> OK << PREFIX : SELECT * WHERE { () :p 1 } >> OK << PREFIX : SELECT * WHERE { ( ) :p 1 } >> OK << PREFIX : SELECT * WHERE { ( ) :p 1 } >> OK << PREFIX : SELECT * WHERE { ( 1 2 ) :p 1 } >> OK << PREFIX : SELECT * WHERE { ( 1 2 ) } >> OK << # $Id$ # BNode label used across a FILTER. PREFIX : ASK { _:who :homepage ?homepage FILTER REGEX(?homepage, "^http://example.org/") _:who :schoolHomepage ?schoolPage } >> OK << # $Id$ # BNode label used across a GRAPH. PREFIX : ASK { _:who :homepage ?homepage GRAPH ?g { ?someone :made ?homepage } _:who :schoolHomepage ?schoolPage } >> FAIL << # $Id$ # BNode label used across an OPTIONAL. # This isn't necessarily a *syntax* test, but references to bnode labels # may not span basic graph patterns. PREFIX foaf: ASK { _:who foaf:homepage ?homepage OPTIONAL { ?someone foaf:made ?homepage } _:who foaf:schoolHomepage ?schoolPage } >> FAIL << # $Id$ # BNode label used across a UNION. # This isn't necessarily a *syntax* test, but references to bnode labels # may not span basic graph patterns. PREFIX foaf: ASK { _:who foaf:homepage ?homepage { ?someone foaf:made ?homepage } UNION { ?homepage foaf:maker ?someone } _:who foaf:schoolHomepage ?schoolPage } >> FAIL << # NegativeSyntax/bnode-dot.rq SELECT * WHERE {[] . } >> FAIL << # NegativeSyntax/bnodes-missing-pvalues.rq PREFIX : SELECT * WHERE { [,] :p [;] . } >> FAIL << # NegativeSyntax/bnodes-missing-pvalues-02.rq SELECT * WHERE {() . [,] . [,;] } >> FAIL << # NegativeSyntax/empty-optional.rq SELECT * { OPTIONAL FILTER (?x) } >> FAIL << # NegativeSyntax/empty-optional-02.rq SELECT * { OPTIONAL GRAPH ?v OPTIONAL FILTER (?x) } >> FAIL << # NegativeSyntax/filter-missing-parens.rq SELECT * { ?s ?p ?o FILTER ?x } >> FAIL << # NegativeSyntax/lone-list.rq SELECT * WHERE { () } >> FAIL << # NegativeSyntax/lone-node.rq SELECT * WHERE {} >> FAIL << # Dot after triple SELECT * WHERE { ?s ?p ?o . } >> OK << # No dot after triple SELECT * WHERE { ?s ?p ?o } >> OK << SELECT * WHERE { ?s ?p ?o . ?s ?p ?o . } >> OK << # No dot SELECT * WHERE { ?s ?p ?o . ?s ?p ?o } >> OK << # DOT after non-triples SELECT * WHERE { FILTER (?o>5) . } >> OK << # DOT after non-triples SELECT * WHERE { FILTER (?o>5) . ?s ?p ?o } >> OK << # Trailing ; PREFIX : SELECT * WHERE { :s :p :o ; FILTER(?x) } >> OK << # Broken ; PREFIX : SELECT * WHERE { :s :p :o ; . } >> OK << # More a test that bad syntax tests work! PREFIX ex: SELECT * >> FAIL << # Missing DOT, 2 triples PREFIX : SELECT * { :s1 :p1 :o1 :s2 :p2 :o2 . } >> FAIL << # Missing DOT between triples PREFIX : SELECT * { :s1 :p1 :o1 :s2 :p2 :o2 . } >> FAIL << # Missing DOT after ; between triples PREFIX : SELECT * { :s1 :p1 :o1 ; :s2 :p2 :o2 . } >> FAIL << # DOT, no triples SELECT * WHERE { . } >> FAIL << # DOT, no triples SELECT * WHERE { . . } >> FAIL << # DOT, then triples SELECT * WHERE { . ?s ?p ?o } >> FAIL << # Multiple DOTs SELECT * WHERE { ?s ?p ?o . . } >> FAIL << # Multiple DOTs SELECT * WHERE { ?s ?p ?o .. } >> FAIL << # Multiple DOTs SELECT * WHERE { ?s ?p ?o . . ?s1 ?p1 ?o1 } >> FAIL << # Multiple DOTs SELECT * WHERE { ?s ?p ?o .. ?s1 ?p1 ?o1 } >> FAIL << # Multiple DOTs SELECT * WHERE { ?s ?p ?o . . ?s1 ?p1 ?o1 } >> FAIL << # Multiple DOTs SELECT * WHERE { ?s ?p ?o . ?s1 ?p1 ?o1 .. } >> FAIL << # DOT, no triples SELECT * WHERE { . FILTER(?x) } >> FAIL << # Broken ; SELECT * WHERE { ; FILTER(?x) } >> FAIL << # Broken ; PREFIX : SELECT * WHERE { :s ; :p :o } >> FAIL << # Broken ; PREFIX : SELECT * WHERE { :s :p ; } >> FAIL << # Broken ; PREFIX : SELECT * WHERE { :s :p ; FILTER(?x) } >> FAIL << # Broken ; PREFIX : SELECT * WHERE { :s :p :o . ; } >> FAIL << # Broken , PREFIX : SELECT * WHERE { :s , :p :o } >> FAIL << # Broken , PREFIX : SELECT * WHERE { :s :p , :o } >> FAIL << # Broken , PREFIX : SELECT * WHERE { :s :p , } >> FAIL << # Broken , can't trail PREFIX : SELECT * WHERE { :s :p :o , } >> FAIL << # Broken , (should be ;) PREFIX : SELECT * WHERE { :s :p1 :o1 , :p2 :o2} >> FAIL << CONSTRUCT >> FAIL << # Tokenizing matters. # "longest token rule" means this isn't a "<" and "&&" PREFIX : SELECT * WHERE { FILTER (?x?y) } >> FAIL << PREFIX : SELECT * WHERE { :x [] :q } >> FAIL << PREFIX : SELECT * WHERE { :x _:a :q } >> FAIL << # Syntactic blank node in a filter. SELECT * WHERE { _:x FILTER(_:x) } >> FAIL << # Syntactic blank node in a filter. SELECT * WHERE { _:x FILTER(_:x < 3) } >> FAIL << PREFIX : SELECT * WHERE { GRAPH [] { } } >> FAIL << PREFIX : SELECT * WHERE { _:a ?p ?v . _:a ?q 1 } >> OK << PREFIX : SELECT * WHERE { { _:a ?p ?v . _:a ?q _:a } UNION { _:b ?q _:c } } >> OK << PREFIX : SELECT * WHERE { _:a ?p ?v . FILTER(true) . [] ?q _:a } >> OK << PREFIX : SELECT * WHERE { _:a ?p ?v . _:a ?q 1 } >> FAIL << PREFIX : SELECT * WHERE { { _:a ?p ?v . } _:a ?q 1 } >> FAIL << PREFIX : SELECT * WHERE { { _:a ?p ?v . } UNION { _:a ?q 1 } } >> FAIL << PREFIX : SELECT * WHERE { { _:a ?p ?v . } _:a ?q 1 } >> FAIL << PREFIX : SELECT * WHERE { _:a ?p ?v . OPTIONAL {_:a ?q 1 } } >> FAIL << # bad: re-used BNode label after GRAPH # $Id$ PREFIX : SELECT * WHERE { _:a ?p ?v . GRAPH ?g { ?s ?p ?v } _:a ?q 1 } >> FAIL << # bad: re-used BNode label after OPTIONAL # $Id$ PREFIX : SELECT * WHERE { _:a ?p ?v . OPTIONAL { ?s ?p ?v } _:a ?q 1 } >> FAIL << # bad: re-used BNode label after UNION # $Id$ # This isn't necessarily a *syntax* test, but references to bnode labels # may not span basic graph patterns. PREFIX : SELECT * WHERE { _:a ?p ?v1 { ?s ?o } UNION { ?s ?o } _:a ?p ?v2 } >> FAIL << PREFIX dob: PREFIX time: PREFIX dc: SELECT ?desc WHERE { dob:1D a time:ProperInterval; dc:description ?desc. } >> OK << SELECT REDUCED * WHERE { ?x ?y ?z } >> OK << SELECT REDUCED ?x ?y WHERE { ?x ?y ?z } >> OK << PREFIX : SELECT ?v { :x :p ?v . FILTER(?v = 1) } >> OK << PREFIX : SELECT ?v { :x :p ?v . { FILTER(?v = 1) } } >> OK << PREFIX : SELECT ?v { ?s :p ?v . FILTER (?v = 2) } >> OK << PREFIX : SELECT ?v { FILTER (?v = 2) ?s :p ?v . } >> OK << PREFIX : SELECT ?v ?w { FILTER (?v = 2) FILTER (?w = 3) ?s :p ?v . ?s :q ?w . } >> OK << PREFIX : SELECT * { :x :p ?v . { :x :q ?w OPTIONAL { :x :p ?v2 FILTER(?v = 1) } } } >> OK << PREFIX : SELECT ?a ?y ?d ?z { ?a :p ?c OPTIONAL { ?a :r ?d }. ?a ?p 1 { ?p a ?y } UNION { ?a ?z ?p } } >> OK << PREFIX : SELECT ?x ?y ?z { GRAPH ?g { ?x ?p 1 } { ?x :p ?y } UNION { ?p a ?z } } >> OK << PREFIX : SELECT * { ?X :name "paul" {?Y :name "george" . OPTIONAL { ?X :email ?Z } } } >> OK << PREFIX : SELECT * { :x1 :p ?v . OPTIONAL { :x3 :q ?w . OPTIONAL { :x2 :p ?v } } } >> OK << PREFIX : ## The nested optional example, rewritten to a form that is the same ## for the SPARQL algebra and the declarative semantics. SELECT * { :x1 :p ?v . OPTIONAL { :x3 :q ?w } OPTIONAL { :x3 :q ?w . :x2 :p ?v } } >> OK << PREFIX : SELECT * { ?x :p ?v . OPTIONAL { ?y :q ?w . FILTER(?v=2) } } >> OK << PREFIX : SELECT * { ?x :p ?v . OPTIONAL { ?y :q ?w . FILTER(?v=2) FILTER(?w=3) } } >> OK << PREFIX : SELECT * { :x :p ?v . { :x :q ?w # ?v is not in scope so ?v2 never set OPTIONAL { :x :p ?v2 FILTER(?v = 1) } } } >> OK << PREFIX : ASK { :x :p 1 } >> OK << PREFIX : ASK { :x :p 99 } >> OK << PREFIX : ASK { :x :p ?x } >> OK << PREFIX : ASK { :x :p ?x . FILTER(?x = 99) } >> OK << BASE PREFIX : <> SELECT * WHERE { :x ?p ?v } >> OK << BASE PREFIX : <#> SELECT * WHERE { :x ?p ?v } >> OK << PREFIX ns: PREFIX x: SELECT * WHERE { x:x ns:p ?v } >> OK << BASE SELECT * WHERE {

?v } >> OK << BASE SELECT * WHERE { <#x> <#p> ?v } >> OK << PREFIX : PREFIX foaf: SELECT ?x WHERE { ?x foaf:name "John Smith" ; a foaf:Womble . } >> OK << PREFIX : SELECT ?p { :x ?p () . } >> OK << PREFIX : SELECT ?p { :x ?p (1) . } >> OK << PREFIX : SELECT ?p ?v { :x ?p (?v) . } >> OK << PREFIX : SELECT ?p ?v ?w { :x ?p (?v ?w) . } >> OK << PREFIX ex: SELECT ?p { ex: ?p 1 . } >> OK << PREFIX : SELECT ?x { ?x ?p '''x''' } >> OK << PREFIX : SELECT ?x { ?x ?p """x""" } >> OK << # This query uses UNIX line end conventions. # It is in CVS in binary. PREFIX : SELECT ?x { ?x ?p '''x y''' } >> OK << # This query uses UNIX line end conventions. # It is in CVS in binary. PREFIX : PREFIX xsd: SELECT ?x { ?x ?p """x y"""^^:someType } >> OK << PREFIX : PREFIX xsd: SELECT ?s WHERE { ?s :p1 1, 2 . } >> OK << PREFIX : PREFIX xsd: SELECT * { :x ?p true . } >> OK << PREFIX : PREFIX xsd: SELECT * { :x ?p false } >> OK << PREFIX : PREFIX xsd: SELECT * { :x a ?C . } >> OK << PREFIX : PREFIX xsd: SELECT * { :x ?p 123.0 } >> OK << PREFIX : PREFIX xsd: SELECT * { :x ?p 123.0. } >> OK << PREFIX : PREFIX xsd: # DOT is part of the decimal. SELECT * { :x ?p 456. } >> OK << PREFIX : PREFIX xsd: # DOT is part of the decimal. SELECT * { :x ?p 456. . } >> OK << PREFIX : PREFIX xsd: # DOT is part of the decimal. SELECT * { :x ?p +5 } >> OK << PREFIX : PREFIX xsd: # DOT is part of the decimal. SELECT * { :x ?p -18 } >> OK << PREFIX : PREFIX xsd: SELECT * { :x ?p $v } >> OK << PREFIX : PREFIX xsd: SELECT * { :x ?p $v . :x ?p ?v } >> OK << PREFIX rdf: PREFIX foaf: SELECT ?x ?y WHERE { ?x foaf:knows ?y . } >> OK << PREFIX xsd: PREFIX : SELECT ?a WHERE { ?a :p ?v . FILTER (?v) . } >> OK << PREFIX xsd: PREFIX : SELECT ?a WHERE { ?a :p ?v . FILTER ( ! ?v ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?a WHERE { ?a :p ?v . FILTER ("true"^^xsd:boolean && ?v) . } >> OK << PREFIX xsd: PREFIX : SELECT ?a WHERE { ?a :p ?v . FILTER ("false"^^xsd:boolean || ?v) . } >> OK << PREFIX xsd: PREFIX : SELECT ?a WHERE { ?a :p ?v . OPTIONAL { ?a :q ?w } . FILTER (?w) . } >> OK << PREFIX xsd: PREFIX : SELECT ?a ?w WHERE { ?a :p ?v . OPTIONAL { ?a :q ?w } . FILTER ( ! ?w ) . } >> OK << prefix : select ?x where { ?x :p "foo" . FILTER (true) . } >> OK << PREFIX : SELECT ?a ?c WHERE { ?a :b ?c . OPTIONAL { ?c :d ?e } . FILTER (! bound(?e)) } >> OK << PREFIX : PREFIX rdf: PREFIX xsd: SELECT ?s WHERE { ?s :p ?v . FILTER(datatype(xsd:boolean(?v)) = xsd:boolean) . } >> OK << PREFIX : PREFIX rdf: PREFIX xsd: SELECT ?s WHERE { ?s :p ?v . FILTER(datatype(xsd:dateTime(?v)) = xsd:dateTime) . } >> OK << PREFIX : PREFIX rdf: PREFIX xsd: SELECT ?s WHERE { ?s :p ?v . FILTER(datatype(xsd:double(?v)) = xsd:double) . } >> OK << PREFIX : PREFIX rdf: PREFIX xsd: SELECT ?s WHERE { ?s :p ?v . FILTER(datatype(xsd:decimal(?v)) = xsd:decimal) . } >> OK << PREFIX : PREFIX rdf: PREFIX xsd: SELECT ?s WHERE { ?s :p ?v . FILTER(datatype(xsd:float(?v)) = xsd:float) . } >> OK << PREFIX : PREFIX rdf: PREFIX xsd: SELECT ?s WHERE { ?s :p ?v . FILTER(datatype(xsd:integer(?v)) = xsd:integer) . } >> OK << PREFIX : PREFIX rdf: PREFIX xsd: SELECT ?s WHERE { ?s :p ?v . FILTER(datatype(xsd:string(?v)) = xsd:string) . } >> OK << PREFIX rdf: PREFIX foaf: CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . } >> OK << PREFIX rdf: PREFIX foaf: CONSTRUCT { ?s foaf:name ?o . } WHERE { ?s foaf:name ?o . } >> OK << PREFIX rdf: PREFIX foaf: CONSTRUCT { [ rdf:subject ?s ; rdf:predicate ?p ; rdf:object ?o ] . } WHERE { ?s ?p ?o . } >> OK << PREFIX rdf: PREFIX foaf: CONSTRUCT { _:a rdf:subject ?s ; rdf:predicate ?p ; rdf:object ?o . } WHERE { ?s ?p ?o . } >> OK << PREFIX : CONSTRUCT { ?x :p2 ?v } WHERE { ?x :p ?o . OPTIONAL {?o :q ?v } } >> OK << PREFIX : SELECT * FROM { ?s ?p ?o } >> OK << PREFIX : SELECT * FROM NAMED { ?s ?p ?o } >> OK << PREFIX : SELECT * FROM NAMED { GRAPH ?g { ?s ?p ?o } } >> OK << PREFIX : SELECT * FROM { GRAPH ?g { ?s ?p ?o } } >> OK << PREFIX : SELECT * FROM FROM NAMED { ?s ?p ?o } >> OK << PREFIX : SELECT * FROM FROM NAMED { GRAPH ?g { ?s ?p ?o } } >> OK << PREFIX : SELECT * FROM FROM NAMED { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } } >> OK << PREFIX : SELECT * FROM FROM NAMED { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * FROM FROM NAMED { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * FROM FROM NAMED { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * FROM FROM NAMED { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * FROM FROM NAMED { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * FROM FROM NAMED FROM NAMED FROM NAMED FROM NAMED { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } } >> OK << PREFIX : SELECT * FROM FROM FROM FROM FROM NAMED FROM NAMED FROM NAMED FROM NAMED { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } } >> OK << PREFIX : SELECT * FROM FROM FROM FROM FROM NAMED FROM NAMED FROM NAMED FROM NAMED { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } } >> OK << PREFIX : PREFIX xsd: SELECT DISTINCT ?v { ?x ?p ?v . } >> OK << PREFIX : PREFIX xsd: SELECT DISTINCT ?v { ?x ?p ?v . } >> OK << PREFIX : PREFIX xsd: SELECT DISTINCT ?v { ?x ?p ?v . } >> OK << PREFIX : PREFIX xsd: SELECT DISTINCT ?v { :x1 ?p ?o OPTIONAL { ?o :q ?v } } >> OK << PREFIX : PREFIX xsd: SELECT DISTINCT ?v { ?x ?p ?v . } >> OK << PREFIX : PREFIX xsd: SELECT DISTINCT * WHERE { { ?s :p ?o } UNION { ?s :q ?o } } >> OK << SELECT ?v { ?x ?p ?v . } >> OK << SELECT ?v { ?x ?p ?v . } >> OK << SELECT ?v { ?x ?p ?v . } >> OK << PREFIX : PREFIX xsd: SELECT ?v { :x1 ?p ?o OPTIONAL { ?o :q ?v } } >> OK << SELECT ?v { ?x ?p ?v . } >> OK << PREFIX xsd: PREFIX : SELECT ?x ?v WHERE { ?x :p ?v . FILTER ( datatype(?v) = xsd:double ) . } >> OK << # Which literals have a datatype and which are errors. PREFIX : PREFIX xsd: SELECT ?x { ?x :p ?v . FILTER( datatype(?v) != ) } >> OK << # Whichliterals have xsd:string as a datatype PREFIX : PREFIX xsd: SELECT ?x { ?x :p ?v . FILTER( datatype(?v) = xsd:string ) } >> OK << PREFIX xsd: PREFIX : SELECT ?x ?v WHERE { ?x :p ?v . FILTER isBlank(?v) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x ?v WHERE { ?x :p ?v . FILTER isIRI(?v) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p ?v . FILTER isLiteral(?v) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x ?v WHERE { ?x :p ?v . FILTER isURI(?v) . } >> OK << # Test which things have a lang tag of some form. PREFIX : PREFIX xsd: SELECT ?x { ?x :p ?v . FILTER ( lang(?v) != '@NotALangTag@' ) } >> OK << PREFIX : PREFIX xsd: SELECT ?x { ?x :p ?v . FILTER ( lang(?v) = '' ) } >> OK << PREFIX : PREFIX xsd: SELECT ?x { ?x :p "string"@EN } >> OK << PREFIX : SELECT * { :x ?p ?v . FILTER langMatches(lang(?v), "en-GB") . } >> OK << PREFIX : SELECT * { :x ?p ?v . FILTER langMatches(lang(?v), "en") . } >> OK << PREFIX : SELECT * { :x ?p ?v . FILTER langMatches(lang(?v), "*") . } >> OK << PREFIX : SELECT * { :x ?p ?v . FILTER (! langMatches(lang(?v), "*")) . } >> OK << # q-langMatches-de-de.rq # $Id$ PREFIX : SELECT * { :x ?p ?v . FILTER langMatches(lang(?v), "de-de") . } >> OK << PREFIX xsd: PREFIX : SELECT ?x ?v WHERE { ?x :p ?v . FILTER ( str(?v) = "1" ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x ?v WHERE { ?x :p ?v . FILTER ( str(?v) = "01" ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x ?v WHERE { ?x :p ?v . FILTER ( str(?v) = "zzz" ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x ?v WHERE { ?x :p ?v . FILTER ( str(?v) = "" ) . } >> OK << # Test: 'xyz'@en = 'xyz'@EN # $Id$ PREFIX : SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( ?v1 = ?v2 ) } >> OK << # Test: 'xyz'@en != 'xyz'@EN # $Id$ PREFIX : PREFIX xsd: SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( ?v1 != ?v2 ) } >> OK << # Test: sameTerm and eq # $Id$ PREFIX : SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( sameTerm(?v1, ?v2) && ?v1 = ?v2 ) } >> OK << # Test: !sameTerm and eq # $Id$ PREFIX : SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( !sameTerm(?v1, ?v2) && ?v1 = ?v2 ) } >> OK << # Test: sameTerm # $Id$ PREFIX : SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER sameTerm(?v1, ?v2) } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p ?v . FILTER ( ?v = 1 ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p ?v . FILTER ( ?v = 1.0e0 ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?v1 ?v2 WHERE { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( ?v1 = ?v2 ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?v1 ?v2 WHERE { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( ?v1 = ?v2 ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p ?v . FILTER ( ?v = "1" ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p ?v . FILTER ( ?v = "zzz" ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p ?v . FILTER ( ?v = :z ) . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p 1 . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p 1.0e0 . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p "1" } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p "zzz" . } >> OK << PREFIX xsd: PREFIX : SELECT ?x WHERE { ?x :p ?v . FILTER ( ?v = :z ) . } >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o . FILTER(?o >= 3) . } >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o . FILTER(?o <= 2) . } >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o . ?s2 :p ?o2 . FILTER(?o - ?o2 = 3) . } >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o . ?s2 :p ?o2 . FILTER(?o * ?o2 = 4) . } >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o . ?s2 :p ?o2 . FILTER(?o + ?o2 = 3) . } >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o . FILTER(-?o = -2) . } >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o . FILTER(?o = +3) . } >> OK << PREFIX : SELECT * { ?s ?p ?o } >> OK << PREFIX : SELECT * { ?s ?p ?o } >> OK << PREFIX : SELECT * { GRAPH ?g { ?s ?p ?o } } >> OK << PREFIX : SELECT * { GRAPH ?g { ?s ?p ?o } } >> OK << PREFIX : SELECT * { ?s ?p ?o } >> OK << PREFIX : SELECT * { GRAPH ?g { ?s ?p ?o } } >> OK << PREFIX : SELECT * { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } } >> OK << PREFIX : SELECT * { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * { ?s ?p ?o GRAPH ?g { ?s ?q ?v } } >> OK << PREFIX : SELECT * { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } } >> OK << # $Id$ # test kanji QNames PREFIX foaf: PREFIX 食: SELECT ?name ?food WHERE { [ foaf:name ?name ; 食:食べる ?food ] . } >> OK << # $Id$ # test wide spaces PREFIX foaf: PREFIX 食: SELECT ?name WHERE { [ foaf:name ?name ; 食:食べる 食:海老 ] . } >> OK << # Figure out what happens with normalization form C. PREFIX foaf: PREFIX HR: SELECT ?name WHERE { [ foaf:name ?name; HR:resum� ?resume ] . } >> OK << # Example 1 from # http://lists.w3.org/Archives/Public/public-rdf-dawg/2005JulSep/0096 # $Id$ PREFIX : PREFIX p1: SELECT ?S WHERE { ?S :p p1:xyz } >> OK << # Example 2 from # http://lists.w3.org/Archives/Public/public-rdf-dawg/2005JulSep/0096 # $Id$ PREFIX : PREFIX p2: SELECT ?S WHERE { ?S :p p2:abc } >> OK << PREFIX : PREFIX xsd: SELECT * { ?x :r ?v . FILTER ( ?v = "2006-08-23"^^xsd:date ) } >> OK << PREFIX : PREFIX xsd: SELECT * { ?x :r ?v . FILTER ( ?v != "2006-08-23"^^xsd:date ) } >> OK << PREFIX : PREFIX xsd: SELECT * { ?x :r ?v . FILTER ( ?v > "2006-08-22"^^xsd:date ) } >> OK << PREFIX : PREFIX xsd: SELECT ?x ?date { ?x :s ?date . FILTER ( datatype(?date) = xsd:date ) } >> OK << PREFIX : PREFIX xsd: SELECT ?x ?v1 ?v2 { ?x :p [ :v1 ?v1 ; :v2 ?v2 ] . FILTER ( ?v1 < ?v2 || ?v1 > ?v2 ) } >> OK << PREFIX : PREFIX xsd: SELECT ?x ?v1 ?v2 { ?x :p [ :v1 ?v1 ; :v2 ?v2 ] . FILTER ( ?v1 < ?v2 || ?v1 = ?v2 || ?v1 > ?v2 ) } >> OK << # SPARQL is defined over simple entailment so # only syntactic matches show. # (Some systems may match because they do # value-based matching in the graph (D-entailment)) # Does not strictly match "1"^xsd:integer PREFIX : PREFIX t: PREFIX xsd: SELECT * { ?x :p "001"^^xsd:integer } >> OK << # Test matching in a graph pattern # Unknown type PREFIX : PREFIX t: SELECT * { ?x :p "a"^^t:type1 } >> OK << # SPARQL FILTER test by value. # A processor knows about XSD integer # so 1 and 01 pass the filter PREFIX : PREFIX t: PREFIX rdf: PREFIX rdfs: PREFIX xsd: SELECT * { ?x :p ?v FILTER ( ?v = 1 ) } >> OK << # SPARQL FILTER test by value. # A processor knows about XSD integer # so 1 and 01 are excluded by the filter PREFIX : PREFIX t: PREFIX rdf: PREFIX rdfs: PREFIX xsd: SELECT * { ?x :p ?v FILTER ( ?v != 1 ) } >> OK << # SPARQL FILTER test by value. # Only one valus is known to be "a"^^t:type1 # (others maybe but the processor does not positively know this) PREFIX : PREFIX t: SELECT * { ?x :p ?v FILTER ( ?v = "a"^^t:type1 ) } >> OK << # SPARQL FILTER test by value for known types. # Nothing is known to be not the same value as "a"^^t:type1 # "b"^^t:type1 might be a different lexical form for the same value # "a"^^t:type2 might have overlapping value spaces for this lexicial form. PREFIX : PREFIX t: SELECT * { ?x :p ?v FILTER ( ?v != "a"^^t:type1 ) } >> OK << PREFIX : PREFIX xsd: SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( ?v1 = ?v2 ) } >> OK << PREFIX : PREFIX xsd: SELECT * { ?x1 :p ?v1 . ?x2 :p ?v2 . FILTER ( ?v1 != ?v2 ) } >> OK << PREFIX : PREFIX xsd: SELECT * { ?x :p ?v1 . ?y :q ?v2 . FILTER ( ?v1 = ?v2 ) } >> OK << PREFIX : PREFIX xsd: SELECT * { ?x :p ?v1 . ?y :q ?v2 . FILTER ( ?v1 != ?v2 ) } >> OK << PREFIX : PREFIX xsd: SELECT * { ?x :p ?v1 . ?y :q ?v2 . FILTER ( ?v1 != ?v2 || ?v1 = ?v2 ) } >> OK << PREFIX : PREFIX xsd: SELECT ?x ?v1 ?y ?v2 { ?x :p ?v1 . ?y :p ?v2 . OPTIONAL { ?y :p ?v3 . FILTER( ?v1 != ?v3 || ?v1 = ?v3 )} FILTER (!bound(?v3)) } >> OK << PREFIX dc: PREFIX x: SELECT ?title ?price WHERE { ?book dc:title ?title . OPTIONAL { ?book x:price ?price . FILTER (?price < 15) . } . } >> OK << PREFIX dc: PREFIX x: SELECT ?title ?price WHERE { ?book dc:title ?title . OPTIONAL { ?book x:price ?price } . FILTER (?price < 15) . } >> OK << PREFIX dc: PREFIX x: SELECT ?title ?price WHERE { ?book dc:title ?title . OPTIONAL { ?book x:price ?price } . FILTER ( ( ! bound(?price) ) || ( ?price < 15 ) ) . } >> OK << PREFIX dc: PREFIX x: SELECT ?title ?price WHERE { ?book dc:title ?title . OPTIONAL { ?book x:price ?price . FILTER (?price < 15 && ?title = "TITLE 2") . } . } >> OK << PREFIX dc: PREFIX x: SELECT ?title ?price WHERE { ?book dc:title ?title . OPTIONAL { { ?book x:price ?price . FILTER (?title = "TITLE 2") . } } . } >> OK << PREFIX dc: PREFIX x: SELECT ?title ?price WHERE { ?book dc:title ?title . OPTIONAL { { ?book x:price ?price . FILTER (?title = "TITLE 2") . } } . } >> OK << PREFIX foaf: SELECT ?mbox ?name { ?x foaf:mbox ?mbox . OPTIONAL { ?x foaf:name ?name } . } >> OK << PREFIX foaf: SELECT ?mbox ?name ?nick { ?x foaf:mbox ?mbox . OPTIONAL { ?x foaf:name ?name } . OPTIONAL { ?x foaf:nick ?nick } . } >> OK << PREFIX foaf: SELECT ?person ?nick ?page ?img ?name ?firstN { ?person foaf:nick ?nick OPTIONAL { ?person foaf:isPrimaryTopicOf ?page } OPTIONAL { ?person foaf:name ?name { ?person foaf:depiction ?img } UNION { ?person foaf:firstName ?firstN } } FILTER ( bound(?page) || bound(?img) || bound(?firstN) ) } >> OK << PREFIX foaf: PREFIX ex: SELECT ?id ?ssn WHERE { ?person a foaf:Person; foaf:name ?name . GRAPH ?x { [] foaf:name ?name; foaf:nick ?nick } OPTIONAL { { ?person ex:empId ?id } UNION { ?person ex:ssn ?ssn } } } >> OK << PREFIX foaf: PREFIX ex: SELECT ?name ?nick ?plan ?dept WHERE { ?person a foaf:Person; foaf:name ?name . GRAPH ?x { [] foaf:name ?name; foaf:nick ?nick } OPTIONAL { ?person ex:healthplan ?plan OPTIONAL { ?person ex:department ?dept } } } >> OK << PREFIX foaf: PREFIX ex: SELECT ?name ?plan ?dept ?img WHERE { ?person foaf:name ?name { ?person ex:healthplan ?plan } UNION { ?person ex:department ?dept } OPTIONAL { ?person a foaf:Person GRAPH ?g { [] foaf:name ?name; foaf:depiction ?img } } } >> OK << PREFIX foaf: SELECT ?mbox ?name { { ?x foaf:mbox ?mbox } UNION { ?x foaf:mbox ?mbox . ?x foaf:name ?name } } >> OK << PREFIX : PREFIX xsd: SELECT REDUCED * WHERE { { ?s :p ?o } UNION { ?s :q ?o } } >> OK << PREFIX : PREFIX xsd: SELECT REDUCED ?v { ?x ?p ?v . } >> OK << PREFIX rdf: PREFIX ex: SELECT ?val WHERE { ex:foo rdf:value ?val . FILTER regex(?val, "GHI") } >> OK << PREFIX ex: PREFIX rdf: SELECT ?val WHERE { ex:foo rdf:value ?val . FILTER regex(?val, "DeFghI", "i") } >> OK << PREFIX rdf: PREFIX ex: SELECT ?val WHERE { ex:foo rdf:value ?val . FILTER regex(?val, "example\\.com") } >> OK << PREFIX rdf: PREFIX ex: SELECT ?val WHERE { ex:foo rdf:value ?val . FILTER regex(str(?val), "example\\.com") } >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v LIMIT 1 >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v LIMIT 100 >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v LIMIT 0 >> OK << PREFIX : SELECT DISTINCT ?v WHERE { [] :num ?v } ORDER BY ?v LIMIT 100 >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v OFFSET 1 >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v OFFSET 0 >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v OFFSET 100 >> OK << PREFIX : SELECT DISTINCT ?v WHERE { [] :num ?v } ORDER BY ?v OFFSET 2 >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v LIMIT 1 OFFSET 1 >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v OFFSET 1 LIMIT 2 >> OK << PREFIX : SELECT ?v WHERE { [] ?p ?v } ORDER BY ?v OFFSET 100 LIMIT 1 >> OK << PREFIX : SELECT ?v WHERE { [] :num ?v } ORDER BY ?v OFFSET 2 LIMIT 5 >> OK << PREFIX : SELECT DISTINCT ?v WHERE { [] :num ?v } ORDER BY ?v OFFSET 2 LIMIT 5 >> OK << PREFIX foaf: SELECT ?name WHERE { ?x foaf:name ?name } ORDER BY ?name >> OK << PREFIX foaf: SELECT ?name WHERE { ?x foaf:name ?name } ORDER BY DESC(?name) >> OK << PREFIX foaf: SELECT ?name WHERE { ?x foaf:name ?name } ORDER BY DESC(?name) >> OK << PREFIX foaf: SELECT ?name ?mbox WHERE { ?x foaf:name ?name . OPTIONAL { ?x foaf:mbox ?mbox } } ORDER BY ASC(?mbox) >> OK << PREFIX foaf: PREFIX ex: SELECT ?name ?emp WHERE { ?x foaf:name ?name ; ex:empId ?emp } ORDER BY ASC(?emp) >> OK << PREFIX foaf: PREFIX ex: SELECT ?name ?emp WHERE { ?x foaf:name ?name ; ex:empId ?emp } ORDER BY ?name DESC(?emp) >> OK << PREFIX ex: SELECT ?address WHERE { ?x ex:address ?address } ORDER BY ASC(?address) >> OK << PREFIX foaf: PREFIX ex: SELECT ?name ?emp WHERE { ?x foaf:name ?name ; ex:empId ?emp } ORDER BY ASC(?emp) >> OK << PREFIX foaf: PREFIX ex: SELECT ?name ?emp WHERE { ?x foaf:name ?name ; ex:empId ?emp } ORDER BY ASC(?emp) >> OK << PREFIX foaf: SELECT ?name WHERE { ?x foaf:name ?name } ORDER BY ?name >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o . } ORDER BY str(?o) >> OK << PREFIX : PREFIX xsd: SELECT ?s WHERE { ?s :p ?o . } ORDER BY xsd:integer(?o) >> OK << PREFIX : SELECT ?s WHERE { ?s :p ?o1 ; :q ?o2 . } ORDER BY (?o1 + ?o2) >> OK << PREFIX : SELECT * WHERE { :x ?p ?q . } >> OK << PREFIX : SELECT * WHERE { ?x :p ?q . } >> OK << SELECT * WHERE { ?a ?a ?b . } >> OK << PREFIX rdf: PREFIX foaf: SELECT ?name WHERE { ?x rdf:type foaf:Person . ?x foaf:name ?name . } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:double1 rdf:value ?l . t:double1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:double ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:double1 rdf:value ?l . t:float1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:double ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:double1 rdf:value ?l . t:decimal1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:double ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:float1 rdf:value ?l . t:float1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:float ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:float1 rdf:value ?l . t:decimal1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:float ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:decimal1 rdf:value ?l . t:decimal1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:decimal ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:integer1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:nonPositiveIntegerN1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:negativeIntegerN1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:long1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:int1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:short1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:byte1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:nonNegativeInteger1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:unsignedLong1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:unsignedInt1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:unsignedShort1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:unsignedByte1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:positiveInteger1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:integer ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:short1 rdf:value ?l . t:double1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:double ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:short1 rdf:value ?l . t:float1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:float ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:short1 rdf:value ?l . t:decimal1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:decimal ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:short1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:short ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:byte1 rdf:value ?l . t:short1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:short ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:short1 rdf:value ?l . t:long1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:decimal ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:short1 rdf:value ?l . t:int1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:float ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:short1 rdf:value ?l . t:byte1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:double ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:double1 rdf:value ?l . t:float1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:float ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:double1 rdf:value ?l . t:decimal1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:decimal ) } >> OK << # Positive test: product of type promotion within the xsd:decimal type tree. # $Id$ PREFIX t: PREFIX rdf: PREFIX xsd: ASK WHERE { t:float1 rdf:value ?l . t:decimal1 rdf:value ?r . FILTER ( datatype(?l + ?r) = xsd:decimal ) } >> OK parboiled-1.4.1/examples-scala/000077500000000000000000000000001421263112100163465ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/000077500000000000000000000000001421263112100171355ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/000077500000000000000000000000001421263112100200615ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/scala/000077500000000000000000000000001421263112100211445ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/scala/org/000077500000000000000000000000001421263112100217335ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/000077500000000000000000000000001421263112100236745ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/examples/000077500000000000000000000000001421263112100255125ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/examples/calculators/000077500000000000000000000000001421263112100300265ustar00rootroot00000000000000SimpleCalculator0.scala000066400000000000000000000025401421263112100343000ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators import org.parboiled.scala._ /** * A recognizer for a very simple calculator language supporting the 4 basic calculation types on integers. * This grammar does not contain any actions, it only serves for determinining whether a given input conforms to * the language. The SimpleCalculator1 adds the actual calculation actions. */ class SimpleCalculator0 extends Parser { def InputLine = rule { Expression ~ EOI } def Expression: Rule0 = rule { Term ~ zeroOrMore(anyOf("+-") ~ Term) } def Term = rule { Factor ~ zeroOrMore(anyOf("*/") ~ Factor) } def Factor = rule { Digits | Parens } def Parens = rule { "(" ~ Expression ~ ")" } def Digits = rule { oneOrMore(Digit) } def Digit = rule { "0" - "9" } } SimpleCalculator1.scala000066400000000000000000000037401421263112100343040ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators import org.parboiled.scala._ import org.parboiled.errors.{ErrorUtils, ParsingException} /** * A parser for a simple calculator language supporting the 4 basic calculation types on integers. * The actual calculations are performed by inline parser actions using the parsers value stack as temporary storage. */ class SimpleCalculator1 extends Parser { def InputLine = rule { Expression ~ EOI } def Expression: Rule1[Int] = rule { Term ~ zeroOrMore( "+" ~ Term ~~> ((a:Int, b) => a + b) | "-" ~ Term ~~> ((a:Int, b) => a - b) ) } def Term = rule { Factor ~ zeroOrMore( "*" ~ Factor ~~> ((a:Int, b) => a * b) | "/" ~ Factor ~~> ((a:Int, b) => a / b) ) } def Factor = rule { Number | Parens } def Parens = rule { "(" ~ Expression ~ ")" } def Number = rule { Digits ~> (_.toInt) } def Digits = rule { oneOrMore(Digit) } def Digit = rule { "0" - "9" } /** * The main parsing method. Uses a ReportingParseRunner (which only reports the first error) for simplicity. */ def calculate(expression: String): Int = { val parsingResult = ReportingParseRunner(InputLine).run(expression) parsingResult.result match { case Some(i) => i case None => throw new ParsingException("Invalid calculation expression:\n" + ErrorUtils.printParseErrors(parsingResult)) } } } parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/examples/json/000077500000000000000000000000001421263112100264635ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/examples/json/JsonParser0.scala000066400000000000000000000045701421263112100316440ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.json import org.parboiled.scala._ import java.lang.String /** * A complete JSON recognizer without any parser actions. When run with the RecoveringParseRunner this parser could be * used to report all syntax errors in a piece of JSON source. */ class JsonParser0 extends Parser { def Json = rule { JsonObject | JsonArray } def JsonObject: Rule0 = rule { WhiteSpace ~ "{ " ~ zeroOrMore(Pair, separator = ", ") ~ "} " } def Pair = rule { JsonString ~ ": " ~ Value } def Value: Rule0 = rule { JsonString | JsonNumber | JsonObject | JsonArray | "true " | "false " | "null " } def JsonString = rule { "\"" ~ zeroOrMore(Character) ~ "\" " } def JsonNumber = rule { Integer ~ optional(Frac ~ optional(Exp)) ~ WhiteSpace } def JsonArray = rule { "[ " ~ zeroOrMore(Value, separator = ", ") ~ "] " } def Character = rule { EscapedChar | NormalChar } def EscapedChar = rule { "\\" ~ (anyOf("\"\\/bfnrt") | Unicode) } def NormalChar = rule { !anyOf("\"\\") ~ ANY } def Unicode = rule { "u" ~ HexDigit ~ HexDigit ~ HexDigit ~ HexDigit } def Integer = rule { optional("-") ~ (("1" - "9") ~ Digits | Digit) } def Digits = rule { oneOrMore(Digit) } def Digit = rule { "0" - "9" } def HexDigit = rule { "0" - "9" | "a" - "f" | "A" - "F" } def Frac = rule { "." ~ Digits } def Exp = rule { ignoreCase("e") ~ optional(anyOf("+-")) ~ Digits } def WhiteSpace = rule { zeroOrMore(anyOf(" \n\r\t\f")) } /** * We redefine the default string-to-rule conversion to also match trailing whitespace if the string ends with * a blank, this keeps the rules free from most whitespace matching clutter */ override implicit def toRule(string: String) = if (string.endsWith(" ")) str(string.trim) ~ WhiteSpace else str(string) } parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/examples/json/JsonParser1.scala000066400000000000000000000100371421263112100316400ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.json import org.parboiled.scala._ import java.lang.String import org.parboiled.errors.{ErrorUtils, ParsingException} /** *

A complete JSON parser producing an AST representation of the parsed JSON source.

*

The syntactical grammar is identical to the JsonParser0 example, however this parser adds the parser * actions required to build an AST during the parsing run.

*

You might also want to look at the functionally equivalent JsonParser2 class in this example. It makes * most of the type information explicit that is left out in JsonParser1 and can make the parsers workings * easier to understand for first-time users

*/ class JsonParser1 extends Parser { /** * These case classes form the nodes of the AST. */ sealed abstract class AstNode case class ObjectNode(members: List[MemberNode]) extends AstNode case class MemberNode(key: String, value: AstNode) extends AstNode case class ArrayNode(elements: List[AstNode]) extends AstNode case class StringNode(text: String) extends AstNode case class NumberNode(value: BigDecimal) extends AstNode case object True extends AstNode case object False extends AstNode case object Null extends AstNode // the root rule def Json = rule { WhiteSpace ~ (JsonObject | JsonArray) ~ EOI } def JsonObject: Rule1[ObjectNode] = rule { "{ " ~ zeroOrMore(Pair, separator = ", ") ~ "} " ~~> ObjectNode } def Pair = rule { JsonString ~~> (_.text) ~ ": " ~ Value ~~> MemberNode } def Value: Rule1[AstNode] = rule { JsonString | JsonNumber | JsonObject | JsonArray | JsonTrue | JsonFalse | JsonNull } def JsonString = rule { "\"" ~ zeroOrMore(Character) ~> StringNode ~ "\" " } def JsonNumber = rule { group(Integer ~ optional(Frac ~ optional(Exp))) ~> (s => NumberNode(BigDecimal(s))) ~ WhiteSpace } def JsonArray = rule { "[ " ~ zeroOrMore(Value, separator = ", ") ~ "] " ~~> ArrayNode } def Character = rule { EscapedChar | NormalChar } def EscapedChar = rule { "\\" ~ (anyOf("\"\\/bfnrt") | Unicode) } def NormalChar = rule { !anyOf("\"\\") ~ ANY } def Unicode = rule { "u" ~ HexDigit ~ HexDigit ~ HexDigit ~ HexDigit } def Integer = rule { optional("-") ~ (("1" - "9") ~ Digits | Digit) } def Digits = rule { oneOrMore(Digit) } def Digit = rule { "0" - "9" } def HexDigit = rule { "0" - "9" | "a" - "f" | "A" - "F" } def Frac = rule { "." ~ Digits } def Exp = rule { ignoreCase("e") ~ optional(anyOf("+-")) ~ Digits } def JsonTrue = rule { "true " ~ push(True) } def JsonFalse = rule { "false " ~ push(False) } def JsonNull = rule { "null " ~ push(Null) } def WhiteSpace: Rule0 = rule { zeroOrMore(anyOf(" \n\r\t\f")) } /** * We redefine the default string-to-rule conversion to also match trailing whitespace if the string ends with * a blank, this keeps the rules free from most whitespace matching clutter */ override implicit def toRule(string: String) = if (string.endsWith(" ")) str(string.trim) ~ WhiteSpace else str(string) /** * The main parsing method. Uses a ReportingParseRunner (which only reports the first error) for simplicity. */ def parseJson(json: String): AstNode = { val parsingResult = ReportingParseRunner(Json).run(json) parsingResult.result match { case Some(astRoot) => astRoot case None => throw new ParsingException("Invalid JSON source:\n" + ErrorUtils.printParseErrors(parsingResult)) } } } parboiled-1.4.1/examples-scala/src/main/scala/org/parboiled/examples/json/JsonParser2.scala000066400000000000000000000104011421263112100316340ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.json import org.parboiled.scala._ import java.lang.String import org.parboiled.errors.{ErrorUtils, ParsingException} /** *

A complete JSON parser producing an AST representation of the parsed JSON source.

*

The syntactical grammar is identical to the {@link JsonParser0} example, however this parser adds the parser * actions required to build an AST during the parsing run.

*

As opposed to the functionally equivalent JsonParser1 class this class contains type specifications that could * be left out due to Scalas type inference but might make the parsers workings easier to understand for first-time * users.

*/ class JsonParser2 extends Parser { /** * These case classes form the nodes of the AST. */ sealed abstract class AstNode case class ObjectNode(members: List[MemberNode]) extends AstNode case class MemberNode(key: String, value: AstNode) extends AstNode case class ArrayNode(elements: List[AstNode]) extends AstNode case class StringNode(text: String) extends AstNode case class NumberNode(value: BigDecimal) extends AstNode case object True extends AstNode case object False extends AstNode case object Null extends AstNode // the root rule def Json: Rule1[AstNode] = rule { WhiteSpace ~ (JsonObject | JsonArray) ~ EOI } def JsonObject: Rule1[ObjectNode] = rule { "{ " ~ zeroOrMore(Pair, separator = ", ") ~ "} " ~~> ObjectNode } def Pair: Rule1[MemberNode] = rule { JsonString ~~> (_.text) ~ ": " ~ Value ~~> MemberNode } def Value: Rule1[AstNode] = rule { JsonString | JsonNumber | JsonObject | JsonArray | JsonTrue | JsonFalse | JsonNull } def JsonString: Rule1[StringNode] = rule { "\"" ~ zeroOrMore(Character) ~> StringNode ~ "\" " } def JsonNumber: Rule1[NumberNode] = rule { group(Integer ~ optional(Frac ~ optional(Exp))) ~> ((matched) => NumberNode(BigDecimal(matched))) ~ WhiteSpace } def JsonArray: Rule1[ArrayNode] = rule { "[ " ~ zeroOrMore(Value, separator = ", ") ~ "] " ~~> ArrayNode } def Character: Rule0 = rule { EscapedChar | NormalChar } def EscapedChar: Rule0 = rule { "\\" ~ (anyOf("\"\\/bfnrt") | Unicode) } def NormalChar: Rule0 = rule { !anyOf("\"\\") ~ ANY } def Unicode: Rule0 = rule { "u" ~ HexDigit ~ HexDigit ~ HexDigit ~ HexDigit } def Integer: Rule0 = rule { optional("-") ~ (("1" - "9") ~ Digits | Digit) } def Digits: Rule0 = rule { oneOrMore(Digit) } def Digit: Rule0 = rule { "0" - "9" } def HexDigit: Rule0 = rule { "0" - "9" | "a" - "f" | "A" - "F" } def Frac: Rule0 = rule { "." ~ Digits } def Exp: Rule0 = rule { ignoreCase("e") ~ optional(anyOf("+-")) ~ Digits } def JsonTrue: Rule1[AstNode] = rule { "true " ~ push(True) } def JsonFalse: Rule1[AstNode] = rule { "false " ~ push(False) } def JsonNull: Rule1[AstNode] = rule { "null " ~ push(Null) } def WhiteSpace: Rule0 = rule { zeroOrMore(anyOf(" \n\r\t\f")) } /** * We redefine the default string-to-rule conversion to also match trailing whitespace if the string ends with * a blank, this keeps the rules free from most whitespace matching clutter */ override implicit def toRule(string: String) = if (string.endsWith(" ")) str(string.trim) ~ WhiteSpace else str(string) /** * The main parsing method. Uses a ReportingParseRunner (which only reports the first error) for simplicity. */ def parseJson(json: String): AstNode = { val parsingResult = ReportingParseRunner(Json).run(json) parsingResult.result match { case Some(astRoot) => astRoot case None => throw new ParsingException("Invalid JSON source:\n" + ErrorUtils.printParseErrors(parsingResult)) } } } parboiled-1.4.1/examples-scala/src/test/000077500000000000000000000000001421263112100201145ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/test/scala/000077500000000000000000000000001421263112100211775ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/test/scala/org/000077500000000000000000000000001421263112100217665ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/test/scala/org/parboiled/000077500000000000000000000000001421263112100237275ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/test/scala/org/parboiled/examples/000077500000000000000000000000001421263112100255455ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/test/scala/org/parboiled/examples/calculators/000077500000000000000000000000001421263112100300615ustar00rootroot00000000000000SimpleCalculatorTest.scala000066400000000000000000000057151421263112100351220ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/test/scala/org/parboiled/examples/calculators/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators import org.testng.annotations.Test import org.scalatestplus.testng.TestNGSuiteLike import org.testng.Assert.assertEquals import org.parboiled.matchers.Matcher import org.parboiled.trees.GraphUtils import org.parboiled.scala.testing.ParboiledTest import org.parboiled.support.{Filters, ToStringFormatter} import org.parboiled.common.Predicates import org.parboiled.scala.parserunners.ReportingParseRunner class SimpleCalculatorTest extends ParboiledTest with TestNGSuiteLike { // def fail(message: String): Nothing = super.fail(message)(null) val parser = new SimpleCalculator1() { override val buildParseTree = true } type Result = Int @Test def testSimpleCalculatorMatcherBuilding(): Unit = { assertEquals(GraphUtils.printTree(parser.InputLine.matcher, new ToStringFormatter[Matcher](), Predicates.alwaysTrue(), Filters.preventLoops), """|InputLine | Expression | Term | Factor | Number | Digits | Digit | NumberAction1 | Parens | '(' | Expression | ')' | ZeroOrMore | FirstOf | Sequence | '*' | Factor | TermAction1 | Sequence | '/' | Factor | TermAction2 | ZeroOrMore | FirstOf | Sequence | '+' | Term | ExpressionAction1 | Sequence | '-' | Term | ExpressionAction2 | EOI |""".stripMargin) } @Test def testCalculations(): Unit = { def test(input: String, expected: Int): Unit = { parse(ReportingParseRunner(parser.InputLine), input) { assertEquals(result.get, expected) } } test("1+2", 3) test("1+2", 3) test("1+2-3+4", 4) test("1-2-3", -4) test("1-(2-3)", 2) test("1*2+3", 5) test("1+2*3", 7) test("1*2*3", 6) test("3*4/6", 2) test("24/6/2", 2) test("1-2*3-4", -9) test("1-2*3-4*5-6", -31) test("1-24/6/2-(5+7)", -13) test("((1+2)*3-(4-5))/5", 2) } } parboiled-1.4.1/examples-scala/src/test/scala/org/parboiled/examples/calculators/TracingTest.scala000066400000000000000000000044631421263112100333240ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.calculators import org.testng.annotations.Test import org.scalatestplus.testng.TestNGSuiteLike import org.testng.Assert.assertEquals import org.parboiled.scala.parserunners._ import org.parboiled.scala.testing.ParboiledTest import org.parboiled.common.StringBuilderSink class TracingTest extends ParboiledTest with TestNGSuiteLike { // def fail(message: String): Nothing = super.fail(message)(null) val parser = new SimpleCalculator1 type Result = Int @Test def testTraceParse(): Unit = { val log = new StringBuilderSink parse(TracingParseRunner(parser.InputLine).filter(Rules.only(parser.Factor, parser.Term)).log(log), "1+2") { assertEquals(log.toString, """Starting new parsing run InputLine/Expression/Term/Factor, matched, cursor at 1:2 after "1" ..(2)../Term, matched, cursor at 1:2 after "1" ..(1)../Expression/ZeroOrMore/FirstOf/Sequence/Term/Factor, matched, cursor at 1:4 after "1+2" ..(5)../Term, matched, cursor at 1:4 after "1+2" """) } } @Test def testTraceParse2(): Unit = { val log = new StringBuilderSink parse(TracingParseRunner(parser.InputLine).filter(Rules.below(parser.Factor) && !Rules.below(parser.Digits)).log(log), "1+2") { assertEquals(log.toString, """Starting new parsing run InputLine/Expression/Term/Factor/Number/Digits, matched, cursor at 1:2 after "1" ..(4)../Number/NumberAction1, matched, cursor at 1:2 after "1" ..(4)../Number, matched, cursor at 1:2 after "1" ..(1)../Expression/ZeroOrMore/FirstOf/Sequence/Term/Factor/Number/Digits, matched, cursor at 1:4 after "1+2" ..(7)../Number/NumberAction1, matched, cursor at 1:4 after "1+2" ..(7)../Number, matched, cursor at 1:4 after "1+2" """) } } } parboiled-1.4.1/examples-scala/src/test/scala/org/parboiled/examples/json/000077500000000000000000000000001421263112100265165ustar00rootroot00000000000000parboiled-1.4.1/examples-scala/src/test/scala/org/parboiled/examples/json/JsonParserTest.scala000066400000000000000000000064761421263112100324660ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.examples.json import org.testng.annotations.Test import org.scalatestplus.testng.TestNGSuiteLike import org.testng.Assert.assertEquals import org.parboiled.scala.testing.ParboiledTest import org.parboiled.scala.parserunners.ReportingParseRunner class JsonParserTest extends ParboiledTest with TestNGSuiteLike { // def fail(message: String): Nothing = super.fail(message)(null) val parser = new JsonParser1() type Result = parser.AstNode @Test def testJsonParser(): Unit = { val json = """|{ | "simpleKey" : "some value", | "key with spaces": null, | "zero": 0, | "number": -1.2323424E-5, | "Boolean yes":true, | "Boolean no": false, | "Unic\u00f8de" : "Long string with newline\nescape", | "key with \"quotes\"" : "string", | "sub object" : { | "sub key": 26.5, | "a": "b", | "array": [1, 2, { "yes":1, "no":0 }, ["a", "b", null], false] | } |}""".stripMargin val rootNode = parser.parseJson(json) assertEquals(printAst(rootNode), """|{ | "simpleKey" : "some value" | "key with spaces" : null | "zero" : 0 | "number" : -0.000012323424 | "Boolean yes" : true | "Boolean no" : false | "Unicøde" : "Long string with newline\nescape" | "key with \"quotes\"" : "string" | "sub object" : { | "sub key" : 26.5 | "a" : "b" | "array" : [1, 2, { | "yes" : 1 | "no" : 0 | }, ["a", "b", null], false] | } |}""".stripMargin) } @Test def testJsonParserError(): Unit = { failParse(ReportingParseRunner(parser.Json), "XYZ") { assertEquals(errors, """|Invalid input 'X', expected Json (line 1, pos 1): |XYZ |^ |""".stripMargin ) } } def printAst(node: JsonParser1#AstNode, indent: String = ""): String = node match { case n: JsonParser1#ObjectNode => "{\n" + (for (sub <- n.members) yield printAst(sub, indent + " ")).mkString + indent + "}" case n: JsonParser1#MemberNode => indent + '"' + n.key + "\" : " + printAst(n.value, indent) + "\n" case n: JsonParser1#ArrayNode => "[" + (for (sub <- n.elements) yield printAst(sub, indent + " ")).mkString(", ") + "]" case n: JsonParser1#StringNode => "\"" + n.text + '"' case n: JsonParser1#NumberNode => n.value.toString case parser.True => "true" case parser.False => "false" case parser.Null => "null" } } parboiled-1.4.1/parboiled-core/000077500000000000000000000000001421263112100163365ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/000077500000000000000000000000001421263112100171255ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/000077500000000000000000000000001421263112100200515ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/000077500000000000000000000000001421263112100207725ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/000077500000000000000000000000001421263112100215615ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/000077500000000000000000000000001421263112100235225ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/Action.java000066400000000000000000000034231421263112100256040ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; /** * Instances of classes implementing this interface can be used directly in a rule definition to define a parser action. * If the class also implements the {@link org.parboiled.ContextAware} interface it will be used to inform the object of the * current parsing {@link org.parboiled.Context} immediately before the invocation of the {@link #run} method. * Additionally, if the class implementing this interface is an inner class (anonymous or not) and its outer class(es) * implement(s) {@link org.parboiled.ContextAware} its outer class(es) will also be informed object of the current parsing {@link org.parboiled.Context} * immediately before the invocation of the actions {@link #run} method. * This allows simple anonymous action class implementations directly in the parser rule definitions, even when * they access context-sensitive methods defined in the BaseActions or BaseParser classes. */ public interface Action { /** * Runs the parser action. * * @param context the current parsing context * @return true if the parsing process is to proceed, false if the current rule is to fail */ boolean run(Context context); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/Context.java000066400000000000000000000173241421263112100260200ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; import org.parboiled.buffers.InputBuffer; import org.parboiled.errors.ParseError; import org.parboiled.matchers.Matcher; import org.parboiled.support.IndexRange; import org.parboiled.support.MatcherPath; import org.parboiled.support.Position; import org.parboiled.support.ValueStack; import java.util.List; /** * A Context object is available to parser actions methods during their runtime and provides various support functionalities. */ public interface Context { /** * Returns the parent context, i.e. the context for the currently running parent matcher. * * @return the parent context */ Context getParent(); /** * Returns the InputBuffer the parser is currently running against * * @return the InputBuffer */ InputBuffer getInputBuffer(); /** * Returns the Matcher of this context or null, if this context is not valid anymore. * * @return the matcher */ Matcher getMatcher(); /** * Returns the index into the underlying input buffer where the matcher of this context started its match. * * @return the start index */ int getStartIndex(); /** * Returns the current index in the input buffer. * * @return the current index */ int getCurrentIndex(); /** * Returns the character at the current index.. * * @return the current character */ char getCurrentChar(); /** * Returns the list of parse errors for the entire parsing run. * * @return the list of parse errors */ List getParseErrors(); /** * Returns the {@link MatcherPath} to the currently running matcher. * * @return the path */ MatcherPath getPath(); /** * Returns the current matcher level, with 0 being the root level, 1 being one level below the root and so on. * * @return the current matcher level */ int getLevel(); /** *

Returns true if fast string matching is enabled for this parsing run.

*

Fast string matching "short-circuits" the default practice of treating string rules as simple Sequence of * character rules. When fast string matching is enabled strings are matched at once, without relying on inner * CharacterMatchers. Even though this can lead to significant increases of parsing performance it does not play * well with error reporting and recovery, which relies on character level matches. * Therefore the {@link org.parboiled.parserunners.ReportingParseRunner} and {@link org.parboiled.parserunners.RecoveringParseRunner} implementations only enable fast * string matching during their basic first parsing run and disable it once the input has proven to contain errors. *

* * @return true if fast string matching is enabled during the current parsing run */ boolean fastStringMatching(); /** * Returns the parse tree subnodes already created in the current context scope. * Note that the returned list is immutable. * * @return the parse tree subnodes already created in the current context scope */ List> getSubNodes(); /** * Determines if the current rule is running somewhere underneath a Test/TestNot rule. * * @return true if the current context has a parent which corresponds to a Test/TestNot rule */ boolean inPredicate(); /** * Determines if the action calling this method is run during the resynchronization phase of an error recovery. * * @return true if the action calling this method is run during the resynchronization phase of an error recovery */ boolean inErrorRecovery(); /** * Determines if the current context is for or below a rule marked @SuppressNode or below one * marked @SuppressSubnodes. * * @return true or false */ boolean isNodeSuppressed(); /** * Determines if this context or any sub node recorded a parse error. * * @return true if this context or any sub node recorded a parse error */ boolean hasError(); /** *

Returns the input text matched by the rule immediately preceding the action expression that is currently * being evaluated. This call can only be used in actions that are part of a Sequence rule and are not at first * position in this Sequence.

* * @return the input text matched by the immediately preceding subcontext */ String getMatch(); /** *

Returns the first character of the input text matched by the rule immediately preceding the action * expression that is currently being evaluated. This call can only be used in actions that are part of a Sequence * rule and are not at first position in this Sequence.

*

If the immediately preceding rule did not match anything this method throws a GrammarException. If you need * to able to handle that case use the getMatch() method.

* * @return the input text matched by the immediately preceding subcontext */ char getFirstMatchChar(); /** *

Returns the start index of the rule immediately preceding the action expression that is currently * being evaluated. This call can only be used in actions that are part of a Sequence rule and are not at first * position in this Sequence.

* * @return the start index of the context immediately preceding current action */ int getMatchStartIndex(); /** *

Returns the end index of the rule immediately preceding the action expression that is currently * being evaluated. This call can only be used in actions that are part of a Sequence rule and are not at first * position in this Sequence.

* * @return the end index of the context immediately preceding current action, i.e. the index of the character * immediately following the last matched character */ int getMatchEndIndex(); /** *

Returns the number of characters matched by the rule immediately preceding the action expression that is * currently being evaluated. This call can only be used in actions that are part of a Sequence rule and are not * at first position in this Sequence.

* * @return the number of characters matched */ int getMatchLength(); /** *

Returns the current position in the underlying {@link org.parboiled.buffers.InputBuffer} as a * {@link Position} instance.

* * @return the current position in the underlying inputbuffer */ Position getPosition(); /** * Creates a new {@link IndexRange} instance covering the input text matched by the rule immediately preceding the * action expression that is currently being evaluated. This call can only be used in actions that are part of a * Sequence rule and are not at first position in this Sequence. * * @return a new IndexRange instance */ IndexRange getMatchRange(); /** * Returns the value stack instance used during this parsing run. * * @return the value stack */ ValueStack getValueStack(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/ContextAware.java000066400000000000000000000022571421263112100267770ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; /** * Interface that can be implemented by classes containing action methods. * If the class containing action methods implements this interface parboiled will use it to inform the * instance of the current context, immediately before an action call. */ public interface ContextAware { /** * Called immediately before any parser action method invocation. Informs the object containing the * action about the context to be used for the coming action call. * * @param context the context */ void setContext(Context context); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/MatchHandler.java000066400000000000000000000020351421263112100267170ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; /** * A MatchHandler is responsible for actually running the match of a given {@link MatcherContext}. * Many times it wraps the actual call to the matcher with some custom logic, e.g. for error handling. */ public interface MatchHandler { /** * Runs the given MatcherContext. * * @param context the MatcherContext * @return true if matched */ boolean match(MatcherContext context); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/MatcherContext.java000066400000000000000000000337621421263112100273300ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.ImmutableLinkedList; import org.parboiled.common.StringUtils; import org.parboiled.errors.BasicParseError; import org.parboiled.errors.GrammarException; import org.parboiled.errors.ParseError; import org.parboiled.errors.ParserRuntimeException; import org.parboiled.matchers.*; import org.parboiled.parserunners.RecoveringParseRunner; import org.parboiled.support.*; import java.util.List; import java.util.Set; import java.util.HashSet; import static org.parboiled.errors.ErrorUtils.printParseError; import static org.parboiled.common.Preconditions.*; import static org.parboiled.matchers.MatcherUtils.unwrap; /** *

The Context implementation orchestrating most of the matching process.

*

The parsing process works as following: * After the rule tree (which is in fact a directed and potentially even cyclic graph of {@link Matcher} instances) * has been created a root MatcherContext is instantiated for the root rule (Matcher). * A subsequent call to {@link #runMatcher()} starts the parsing process.

*

The MatcherContext delegates to a given {@link MatchHandler} to call {@link Matcher#match(MatcherContext)}, * passing itself to the Matcher which executes its logic, potentially calling sub matchers. * For each sub matcher the matcher creates/initializes a subcontext with {@link Matcher#getSubContext(MatcherContext)} * and then calls {@link #runMatcher()} on it.

*

This basically creates a stack of MatcherContexts, each corresponding to their rule matchers. The MatcherContext * instances serve as companion objects to the matchers, providing them with support for building the * parse tree nodes, keeping track of input locations and error recovery.

*

At each point during the parsing process the matchers and action expressions have access to the current * MatcherContext and all "open" parent MatcherContexts through the {@link #getParent()} chain.

*

For performance reasons subcontext instances are reused instead of being recreated. If a MatcherContext instance * returns null on a {@link #getMatcher()} call it has been retired (is invalid) and is waiting to be reinitialized * with a new Matcher by its parent

*/ public class MatcherContext implements Context { private final InputBuffer inputBuffer; private final ValueStack valueStack; private final List parseErrors; private final MatchHandler matchHandler; private final MatcherContext parent; private final int level; private final boolean fastStringMatching; private final Set memoizedMismatches; private MatcherContext subContext; private int startIndex; private int currentIndex; private char currentChar; private Matcher matcher; private Node node; private ImmutableLinkedList> subNodes = ImmutableLinkedList.nil(); private MatcherPath path; private int intTag; private boolean hasError; private boolean nodeSuppressed; private boolean inErrorRecovery; /** * Initializes a new root MatcherContext. * * @param inputBuffer the InputBuffer for the parsing run * @param valueStack the ValueStack instance to use for the parsing run * @param parseErrors the parse error list to create ParseError objects in * @param matchHandler the MatcherHandler to use for the parsing run * @param matcher the root matcher * @param fastStringMatching

Fast string matching "short-circuits" the default practice of treating string rules * as simple Sequence of character rules. When fast string matching is enabled strings are * matched at once, without relying on inner CharacterMatchers. Even though this can lead * to significant increases of parsing performance it does not play well with error * reporting and recovery, which relies on character level matches. * Therefore the {@link org.parboiled.parserunners.ReportingParseRunner} and {@link org.parboiled.parserunners.RecoveringParseRunner} * implementations only enable fast string matching during their basic first parsing run * and disable it once the input has proven to contain errors.

*/ public MatcherContext(InputBuffer inputBuffer, ValueStack valueStack, List parseErrors, MatchHandler matchHandler, Matcher matcher, boolean fastStringMatching) { this(checkArgNotNull(inputBuffer, "inputBuffer"), checkArgNotNull(valueStack, "valueStack"), checkArgNotNull(parseErrors, "parseErrors"), checkArgNotNull(matchHandler, "matchHandler"), null, 0, fastStringMatching, new HashSet()); this.currentChar = inputBuffer.charAt(0); this.matcher = ProxyMatcher.unwrap(checkArgNotNull(matcher, "matcher")); this.nodeSuppressed = matcher.isNodeSuppressed(); } private MatcherContext(InputBuffer inputBuffer, ValueStack valueStack, List parseErrors, MatchHandler matchHandler, MatcherContext parent, int level, boolean fastStringMatching, Set memoizedMismatches) { this.inputBuffer = inputBuffer; this.valueStack = valueStack; this.parseErrors = parseErrors; this.matchHandler = matchHandler; this.parent = parent; this.level = level; this.fastStringMatching = fastStringMatching; this.memoizedMismatches = memoizedMismatches; } @Override public String toString() { return getPath().toString(); } //////////////////////////////// CONTEXT INTERFACE //////////////////////////////////// public MatcherContext getParent() { return parent; } public InputBuffer getInputBuffer() { return inputBuffer; } public int getStartIndex() { return startIndex; } public Matcher getMatcher() { return matcher; } public char getCurrentChar() { return currentChar; } public List getParseErrors() { return parseErrors; } public int getCurrentIndex() { return currentIndex; } public MatcherPath getPath() { if (path == null) { path = new MatcherPath(new MatcherPath.Element(matcher, startIndex, level), parent != null ? parent.getPath() : null); } return path; } public int getLevel() { return level; } public boolean fastStringMatching() { return fastStringMatching; } public ImmutableLinkedList> getSubNodes() { return matcher.isNodeSkipped() ? subNodes : getSubNodes(subNodes, ImmutableLinkedList.>nil()); } private static ImmutableLinkedList> getSubNodes(ImmutableLinkedList> remaining, ImmutableLinkedList> tail) { while (!remaining.isEmpty()) { Node head = remaining.head(); if (head.getMatcher().isNodeSkipped()) { tail = getSubNodes(((ImmutableLinkedList>)head.getChildren()), tail); } else { tail = tail.prepend(head); } remaining = remaining.tail(); } return tail; } public boolean inPredicate() { return matcher instanceof TestMatcher || matcher instanceof TestNotMatcher || parent != null && parent.inPredicate(); } public boolean inErrorRecovery() { return inErrorRecovery; } public boolean isNodeSuppressed() { return nodeSuppressed; } public boolean hasError() { return hasError; } public String getMatch() { checkActionContext(); MatcherContext prevContext = subContext; if (hasError) { Node prevNode = prevContext.node; return prevNode != null ? ParseTreeUtils.getNodeText(prevNode, inputBuffer) : ""; } return inputBuffer.extract(prevContext.startIndex, prevContext.currentIndex); } public char getFirstMatchChar() { checkActionContext(); int ix = subContext.startIndex; if (subContext.currentIndex <= ix) { throw new GrammarException("getFirstMatchChar called but previous rule did not match anything"); } return inputBuffer.charAt(ix); } public int getMatchStartIndex() { checkActionContext(); return subContext.startIndex; } public int getMatchEndIndex() { checkActionContext(); return subContext.currentIndex; } public int getMatchLength() { checkActionContext(); return subContext.currentIndex - subContext.getStartIndex(); } public Position getPosition() { return inputBuffer.getPosition(currentIndex); } public IndexRange getMatchRange() { checkActionContext(); return new IndexRange(subContext.startIndex, subContext.currentIndex); } private void checkActionContext() { // make sure all the constraints are met Checks.ensure(unwrap(matcher) instanceof SequenceMatcher && intTag > 0 && subContext.matcher instanceof ActionMatcher, "Illegal call to getMatch(), getMatchStartIndex(), getMatchEndIndex() or getMatchRange(), " + "only valid in Sequence rule actions that are not in first position"); } public ValueStack getValueStack() { return valueStack; } //////////////////////////////// PUBLIC //////////////////////////////////// public void setMatcher(Matcher matcher) { this.matcher = matcher; } public void setStartIndex(int startIndex) { checkArgument(startIndex >= 0); this.startIndex = startIndex; } public void setCurrentIndex(int currentIndex) { checkArgument(currentIndex >= 0); this.currentIndex = currentIndex; currentChar = inputBuffer.charAt(currentIndex); } public void setInErrorRecovery(boolean flag) { inErrorRecovery = flag; } public void advanceIndex(int delta) { currentIndex += delta; currentChar = inputBuffer.charAt(currentIndex); } public Node getNode() { return node; } public int getIntTag() { return intTag; } public void setIntTag(int intTag) { this.intTag = intTag; } public void markError() { if (!hasError) { hasError = true; if (parent != null) parent.markError(); } } public Boolean hasMismatched() { return memoizedMismatches.contains(MatcherPosition.at(matcher, currentIndex)); } public void memoizeMismatch() { memoizedMismatches.add(MatcherPosition.at(matcher, currentIndex)); } @SuppressWarnings({"ConstantConditions"}) public void createNode() { if (!nodeSuppressed) { node = new NodeImpl(matcher, getSubNodes(), startIndex, currentIndex, valueStack.isEmpty() ? null : valueStack.peek(), hasError); if (parent != null) { parent.subNodes = parent.subNodes.prepend(node); } } } public final MatcherContext getBasicSubContext() { if (subContext == null) { // init new level subContext = new MatcherContext(inputBuffer, valueStack, parseErrors, matchHandler, this, level + 1, fastStringMatching, memoizedMismatches); } else { subContext.path = null; // we always need to reset the MatcherPath, even for actions } return subContext; } public final MatcherContext getSubContext(Matcher matcher) { MatcherContext sc = getBasicSubContext(); sc.matcher = matcher; sc.startIndex = sc.currentIndex = currentIndex; sc.currentChar = currentChar; sc.node = null; sc.subNodes = ImmutableLinkedList.nil(); sc.nodeSuppressed = nodeSuppressed || this.matcher.areSubnodesSuppressed() || matcher.isNodeSuppressed(); sc.hasError = false; return sc; } public boolean runMatcher() { try { if (matchHandler.match(this)) { if (parent != null) { parent.currentIndex = currentIndex; parent.currentChar = currentChar; } matcher = null; // "retire" this context return true; } matcher = null; // "retire" this context until is "activated" again by a getSubContext(...) on the parent return false; } catch (ParserRuntimeException e) { throw e; // don't wrap, just bubble up } catch (RecoveringParseRunner.TimeoutException e) { throw e; // don't wrap, just bubble up } catch (Throwable e) { throw new ParserRuntimeException(e, printParseError(new BasicParseError(inputBuffer, currentIndex, StringUtils.escape(String.format("Error while parsing %s '%s' at input position", matcher instanceof ActionMatcher ? "action" : "rule", getPath())))) + '\n' + e); } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/Node.java000066400000000000000000000034741421263112100252620ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; import org.parboiled.matchers.Matcher; import org.parboiled.trees.TreeNode; /** * Represents a node in the parse tree created during a parsing run. */ public interface Node extends TreeNode> { /** * Returns the matcher that created this node. * * @return the matcher that created this node. */ Matcher getMatcher(); /** * Returns the label of this node which is equal to the name of the rule that created this node * * @return the label of this node */ String getLabel(); /** * Returns the start index of this nodes text in the underlying input buffer. * * @return the start index */ int getStartIndex(); /** * Returns the end index of this nodes text in the underlying input buffer, i.e. the index of the character * immediately following the last character matched by this node. * * @return the end index */ int getEndIndex(); /** * Returns the value object attached to this node. * * @return the value object */ V getValue(); /** * @return true if there were parse errors in the input range covered by this node */ boolean hasError(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/NodeImpl.java000066400000000000000000000044151421263112100261000ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; import static org.parboiled.common.Preconditions.*; import org.parboiled.common.ImmutableLinkedList; import org.parboiled.common.StringUtils; import org.parboiled.matchers.Matcher; import org.parboiled.trees.ImmutableTreeNode; import java.util.List; /** * An immutable implementation of the Node interface. */ class NodeImpl extends ImmutableTreeNode> implements Node { private final Matcher matcher; private final int startIndex; private final int endIndex; private final V value; private final boolean hasError; public NodeImpl(Matcher matcher, ImmutableLinkedList> children, int startIndex, int endIndex, V value, boolean hasError) { super(children); this.matcher = checkArgNotNull(matcher, "matcher"); this.startIndex = startIndex; this.endIndex = endIndex; this.value = value; this.hasError = hasError; } public Matcher getMatcher() { return matcher; } public String getLabel() { return matcher.getLabel(); } public int getStartIndex() { return startIndex; } public int getEndIndex() { return endIndex; } public V getValue() { return value; } public boolean hasError() { return hasError; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); sb.append(getLabel()); if (value != null) { sb.append(", {").append(value).append('}'); } sb.append(']'); if (hasError) sb.append('E'); return StringUtils.escape(sb.toString()); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/ParserStatistics.java000066400000000000000000000277231421263112100277070ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; import static org.parboiled.common.Preconditions.*; import org.parboiled.common.StringUtils; import org.parboiled.matchers.*; import org.parboiled.matchervisitors.MatcherVisitor; import java.util.*; public class ParserStatistics implements MatcherVisitor { private final Matcher root; private int totalRules; private final Set anyMatchers = new HashSet(); private final Set charIgnoreCaseMatchers = new HashSet(); private final Set charMatchers = new HashSet(); private final Set customMatchers = new HashSet(); private final Set charRangeMatchers = new HashSet(); private final Set anyOfMatchers = new HashSet(); private final Set emptyMatchers = new HashSet(); private final Set firstOfMatchers = new HashSet(); private final Set firstOfStringMatchers = new HashSet(); private final Set nothingMatchers = new HashSet(); private final Set oneOrMoreMatchers = new HashSet(); private final Set optionalMatchers = new HashSet(); private final Set sequenceMatchers = new HashSet(); private final Set stringMatchers = new HashSet(); private final Set testMatchers = new HashSet(); private final Set testNotMatchers = new HashSet(); private final Set zeroOrMoreMatchers = new HashSet(); private final Set actions = new HashSet(); private final Set> actionClasses = new HashSet>(); private final Set proxyMatchers = new HashSet(); private final Set varFramingMatchers = new HashSet(); private final Set memoMismatchesMatchers = new HashSet(); @SuppressWarnings({"unchecked"}) public static ParserStatistics generateFor(Rule rule) { checkArgNotNull(rule, "rule"); Matcher matcher = (Matcher) rule; return matcher.accept(new ParserStatistics(matcher)); } private ParserStatistics(Matcher root) { this.root = root; countSpecials(root); } public Rule getRootRule() { return root; } public int getTotalRules() { return totalRules; } public Set getAnyMatchers() { return anyMatchers; } public Set getCharIgnoreCaseMatchers() { return charIgnoreCaseMatchers; } public Set getCharMatchers() { return charMatchers; } public Set getCustomMatchers() { return customMatchers; } public Set getCharRangeMatchers() { return charRangeMatchers; } public Set getAnyOfMatchers() { return anyOfMatchers; } public Set getEmptyMatchers() { return emptyMatchers; } public Set getFirstOfMatchers() { return firstOfMatchers; } public Set getFirstOfStringMatchers() { return firstOfStringMatchers; } public Set getMemoMismatchesMatchers() { return memoMismatchesMatchers; } public Set getNothingMatchers() { return nothingMatchers; } public Set getOneOrMoreMatchers() { return oneOrMoreMatchers; } public Set getOptionalMatchers() { return optionalMatchers; } public Set getSequenceMatchers() { return sequenceMatchers; } public Set getStringMatchers() { return stringMatchers; } public Set getTestMatchers() { return testMatchers; } public Set getTestNotMatchers() { return testNotMatchers; } public Set getZeroOrMoreMatchers() { return zeroOrMoreMatchers; } public Set getActions() { return actions; } public Set> getActionClasses() { return actionClasses; } public Set getProxyMatchers() { return proxyMatchers; } public Set getVarFramingMatchers() { return varFramingMatchers; } // MatcherVisitor interface public ParserStatistics visit(ActionMatcher matcher) { if (!actions.contains(matcher.action)) { totalRules++; actions.add(matcher.action); actionClasses.add(matcher.action.getClass()); } return this; } public ParserStatistics visit(AnyMatcher matcher) { return visit(matcher, anyMatchers); } public ParserStatistics visit(CharIgnoreCaseMatcher matcher) { return visit(matcher, charIgnoreCaseMatchers); } public ParserStatistics visit(CharMatcher matcher) { return visit(matcher, charMatchers); } public ParserStatistics visit(CustomMatcher matcher) { return visit(matcher, customMatchers); } public ParserStatistics visit(CharRangeMatcher matcher) { return visit(matcher, charRangeMatchers); } public ParserStatistics visit(AnyOfMatcher matcher) { return visit(matcher, anyOfMatchers); } public ParserStatistics visit(EmptyMatcher matcher) { return visit(matcher, emptyMatchers); } public ParserStatistics visit(FirstOfMatcher matcher) { return matcher instanceof FirstOfStringsMatcher ? visit((FirstOfStringsMatcher)matcher, firstOfStringMatchers) : visit(matcher, firstOfMatchers); } public ParserStatistics visit(NothingMatcher matcher) { return visit(matcher, nothingMatchers); } public ParserStatistics visit(OneOrMoreMatcher matcher) { return visit(matcher, oneOrMoreMatchers); } public ParserStatistics visit(OptionalMatcher matcher) { return visit(matcher, optionalMatchers); } public ParserStatistics visit(SequenceMatcher matcher) { return matcher instanceof StringMatcher ? visit((StringMatcher)matcher, stringMatchers) : visit(matcher, sequenceMatchers); } public ParserStatistics visit(TestMatcher matcher) { return visit(matcher, testMatchers); } public ParserStatistics visit(TestNotMatcher matcher) { return visit(matcher, testNotMatchers); } public ParserStatistics visit(ZeroOrMoreMatcher matcher) { return visit(matcher, zeroOrMoreMatchers); } private ParserStatistics visit(M matcher, Set set) { if (!set.contains(matcher)) { totalRules++; set.add(matcher); for (Matcher child : matcher.getChildren()) { countSpecials(child); child.accept(this); } } return this; } private void countSpecials(Matcher matcher) { if (matcher instanceof ProxyMatcher) { proxyMatchers.add((ProxyMatcher) matcher); } else if (matcher instanceof VarFramingMatcher) { varFramingMatchers.add((VarFramingMatcher) matcher); } else if (matcher instanceof MemoMismatchesMatcher) { memoMismatchesMatchers.add((MemoMismatchesMatcher) matcher); } } @Override public String toString() { return new StringBuilder("Parser statistics for rule '").append(root).append("':\n") .append(" Total rules : ").append(totalRules).append('\n') .append(" Actions : ").append(actions.size()).append('\n') .append(" Any : ").append(anyMatchers.size()).append('\n') .append(" CharIgnoreCase: ").append(charIgnoreCaseMatchers.size()).append('\n') .append(" Char : ").append(charMatchers.size()).append('\n') .append(" Custom : ").append(customMatchers.size()).append('\n') .append(" CharRange : ").append(charRangeMatchers.size()).append('\n') .append(" AnyOf : ").append(anyOfMatchers.size()).append('\n') .append(" Empty : ").append(emptyMatchers.size()).append('\n') .append(" FirstOf : ").append(firstOfMatchers.size()).append('\n') .append(" FirstOfStrings: ").append(firstOfStringMatchers.size()).append('\n') .append(" Nothing : ").append(nothingMatchers.size()).append('\n') .append(" OneOrMore : ").append(oneOrMoreMatchers.size()).append('\n') .append(" Optional : ").append(optionalMatchers.size()).append('\n') .append(" Sequence : ").append(sequenceMatchers.size()).append('\n') .append(" String : ").append(stringMatchers.size()).append('\n') .append(" Test : ").append(testMatchers.size()).append('\n') .append(" TestNot : ").append(testNotMatchers.size()).append('\n') .append(" ZeroOrMore : ").append(zeroOrMoreMatchers.size()).append('\n') .append('\n') .append(" Action Classes : ").append(actionClasses.size()).append('\n') .append(" ProxyMatchers : ").append(proxyMatchers.size()).append('\n') .append(" VarFramingMatchers: ").append(varFramingMatchers.size()).append('\n') .append("MemoMismatchesMatchers: ").append(memoMismatchesMatchers.size()).append('\n') .toString(); } public String printActionClassInstances() { StringBuilder sb = new StringBuilder("Action classes and their instances for rule '") .append(root).append("':\n"); for (String line : printActionClassLines()) { sb.append(" ").append(line).append('\n'); } return sb.toString(); } private List printActionClassLines() { List lines = new ArrayList(); int anonymous = 0; for (Class actionClass : actionClasses) { String name = actionClass.getSimpleName(); if (StringUtils.isEmpty(name)) { anonymous++; } else { lines.add(name + " : " + StringUtils.join(printActionClassInstances(actionClass), ", ")); } } Collections.sort(lines); if (anonymous > 0) lines.add("and " + anonymous + " anonymous instance(s)"); return lines; } private List printActionClassInstances(Class actionClass) { List actionNames = new ArrayList(); for (Action action : actions) { if (action.getClass().equals(actionClass)) { actionNames.add(action.toString()); } } Collections.sort(actionNames); return actionNames; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/Rule.java000066400000000000000000000044621421263112100253020ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; /** * Describes the return values of parser rule production methods. */ public interface Rule { /** * Attaches a label to this Rule. * Corresponds to the @Label annotation. * * @param label the label * @return this Rule */ Rule label(String label); /** * Instructs parboiled to not create a parse tree node for this rule and all subrules, * which can significantly increase parsing performance. * Corresponds to the @SuppressNode annotation. * * @return this Rule */ Rule suppressNode(); /** * Instructs parboiled to not create parse tree nodes for the subrules of this rule, * which can significantly increase parsing performance. * Corresponds to the @SuppressSubnodes annotation. * * @return this Rule */ Rule suppressSubnodes(); /** * Instructs parboiled to not create a parse tree node for this rule. The parse tree nodes of all subrules are * directly attached to the parent of this rule (or more correctly: the first ancestor not having been marked * skipNode(). * Note that, even though a rule marked as skipNode() does not create a parse tree node of its own and is * therefore "invisible" in the parse tree, the rule still exists as a regular rule in the rule tree and is * accompanied by a "regular" rule {@link org.parboiled.Context} during rule matching. * Corresponds to the @SkipNode annotation. * * @return this Rule */ Rule skipNode(); /** * Enables memoization of rule mismatches for consecutive rule applications at the same input location. * * @return this rule */ Rule memoMismatches(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/SkippableAction.java000066400000000000000000000017611421263112100274420ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; /** * An action that can optionally be skipped when run underneath a predicate matcher. */ public interface SkippableAction extends Action { /** * Determines whether the execution of this action is to be skipped inside of predicate matchers. * * @return true if this action is not to be run inside predicates */ boolean skipInPredicates(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/buffers/000077500000000000000000000000001421263112100251565ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/buffers/DefaultInputBuffer.java000066400000000000000000000104201421263112100315540ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.buffers; import org.parboiled.common.IntArrayStack; import org.parboiled.errors.ParserRuntimeException; import org.parboiled.support.Chars; import org.parboiled.support.IndexRange; import org.parboiled.support.Position; import java.util.Arrays; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.common.Preconditions.checkArgument; /** * Immutable default implementation of an InputBuffer. */ public class DefaultInputBuffer implements InputBuffer { private final int length; private final char[] buffer; // the indices of the newline characters in the buffer // built lazily, since the newline information is normally only needed in the case of parse errors when // error messages need to be generated private int[] newlines; /** * Constructs a new DefaultInputBuffer wrapping the given char array. * CAUTION: For performance reasons the given char array is not defensively copied. * * @param buffer the chars */ public DefaultInputBuffer(char[] buffer) { checkArgNotNull(buffer, "buffer"); this.buffer = buffer; this.length = buffer.length; } public char charAt(int index) { return 0 <= index && index < length ? buffer[index] : index - length > 100000 ? throwParsingException() : Chars.EOI; } private char throwParsingException() { throw new ParserRuntimeException("Parser read more than 100K chars beyond EOI, " + "verify that your grammar does not consume EOI indefinitely!"); } public boolean test(int index, char[] characters) { int len = characters.length; if (index < 0 || index > length - len) { return false; } for (int i = 0; i < len; i++) { if (buffer[index + i] != characters[i]) return false; } return true; } public String extract(int start, int end) { if (start < 0) start = 0; if (end >= length) end = length; if (end <= start) return ""; return new String(buffer, start, end - start); } public String extract(IndexRange range) { return new String(buffer, range.start, Math.min(range.end, length) - range.start); } public Position getPosition(int index) { buildNewlines(); int line = getLine0(newlines, index); int column = index - (line > 0 ? newlines[line - 1] : -1); return new Position(line + 1, column); } public int getOriginalIndex(int index) { return index; } // returns the zero based input line number the character with the given index is found in private static int getLine0(int[] newlines, int index) { int j = Arrays.binarySearch(newlines, index); return j >= 0 ? j : -(j + 1); } public String extractLine(int lineNumber) { buildNewlines(); checkArgument(0 < lineNumber && lineNumber <= newlines.length + 1); int start = lineNumber > 1 ? newlines[lineNumber - 2] + 1 : 0; int end = lineNumber <= newlines.length ? newlines[lineNumber - 1] : length; if (charAt(end - 1) == '\r') end--; return extract(start, end); } public int getLineCount() { buildNewlines(); return newlines.length + 1; } private void buildNewlines() { if (newlines == null) { IntArrayStack newlines = new IntArrayStack(); for (int i = 0; i < length; i++) { if (buffer[i] == '\n') { newlines.push(i); } } this.newlines = new int[newlines.size()]; newlines.getElements(this.newlines, 0); } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/buffers/IndentDedentInputBuffer.java000066400000000000000000000245601421263112100325470ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.buffers; import org.parboiled.common.IntArrayStack; import org.parboiled.errors.IllegalIndentationException; import org.parboiled.support.Chars; import org.parboiled.support.IndexRange; import org.parboiled.support.Position; import static org.parboiled.common.Preconditions.checkArgument; /** * Special, immutable InputBuffer implementation for indentation based grammars. *

This InputBuffer collapses all space and tab characters at the beginning of a text line into either nothing (if * the line has the same indentation level as the previous line), a special {@link Chars#INDENT} character (if the line * has a greater indentation level than the previous line) or one or more {@link Chars#DEDENT} characters (if the line * has a lower indentation level than the previous line).

*

Blank lines (lines containing nothing but whitespace) are removed from the input and the buffer can, optionally, * remove line comments (i.e. comments that start with a predefined character sequence and go to the end of the line). *

*

This means that the highest index of this InputBuffer is probably smaller than that of the original input text * buffer, since all line indentations and blank lines have been collapsed. However, the implementation will make sure * that {@link #getPosition(int)}, {@link #extract(int, int)}, etc. will work as expected and always return the * "correct" result from the underlying, original input buffer.

*

If the input contains illegal indentation the buffer throws an {@link org.parboiled.errors.IllegalIndentationException} * during construction

*/ public class IndentDedentInputBuffer implements InputBuffer { private final DefaultInputBuffer origBuffer; private final DefaultInputBuffer convBuffer; private int[] indexMap; // maps convBuffer indices to origBuffer indices private final boolean strict; private final boolean skipEmptyLines; /** * Creates a new IndentDedentInputBuffer around the given char array. Note that for performance reasons the given * char array is not defensively copied. * * @param input the input text. * @param tabStop the number of characters in a tab stop. * @param lineCommentStart the string starting a line comment or null, if line comments are not defined * @param strict signals whether the buffer should throw an {@link IllegalIndentationException} on * "semi-dedents", if false the buffer silently accepts these * @throws org.parboiled.errors.IllegalIndentationException * if the input contains illegal indentations and the strict flag is set */ public IndentDedentInputBuffer(char[] input, int tabStop, String lineCommentStart, boolean strict) { this(input, tabStop, lineCommentStart, strict, true); } /** * Creates a new IndentDedentInputBuffer around the given char array. Note that for performance reasons the given * char array is not defensively copied. * * @param input the input text. * @param tabStop the number of characters in a tab stop. * @param lineCommentStart the string starting a line comment or null, if line comments are not defined * @param strict signals whether the buffer should throw an {@link IllegalIndentationException} on * "semi-dedents", if false the buffer silently accepts these * @param skipEmptyLines signals whether the buffer should swallow empty lines * @throws org.parboiled.errors.IllegalIndentationException * if the input contains illegal indentations and the strict flag is set */ public IndentDedentInputBuffer(char[] input, int tabStop, String lineCommentStart, boolean strict, boolean skipEmptyLines) { this.strict = strict; this.skipEmptyLines = skipEmptyLines; checkArgument(tabStop > 0, "tabStop must be > 0"); checkArgument(lineCommentStart == null || lineCommentStart.indexOf('\n') == -1, "lineCommentStart must not contain newlines"); origBuffer = new DefaultInputBuffer(input); BufferConverter converter = new BufferConverter(tabStop, lineCommentStart != null ? lineCommentStart.toCharArray() : null); convBuffer = new DefaultInputBuffer(converter.builder.getChars()); indexMap = converter.builder.getIndexMap(); } public char charAt(int index) { return convBuffer.charAt(index); } public boolean test(int index, char[] characters) { return convBuffer.test(index, characters); } public String extract(int start, int end) { return origBuffer.extract(map(start), map(end)); } public String extract(IndexRange range) { return origBuffer.extract(map(range.start), map(range.end)); } public Position getPosition(int index) { return origBuffer.getPosition(map(index)); } public int getOriginalIndex(int index) { return map(index); } public String extractLine(int lineNumber) { return origBuffer.extractLine(lineNumber); } public int getLineCount() {return origBuffer.getLineCount();} private int map(int convIndex) { if (convIndex < 0) return indexMap[0]; if (convIndex < indexMap.length) return indexMap[convIndex]; if (indexMap.length == 0) return 1; return indexMap[indexMap.length - 1] + 1; } private class BufferConverter { public final BufferBuilder builder = new BufferBuilder(); private final int tabStop; private final char[] lineCommentStart; private final IntArrayStack previousLevels = new IntArrayStack(); private int cursor = 0; private char currentChar; public BufferConverter(int tabStop, char[] lineCommentStart) { this.tabStop = tabStop; this.lineCommentStart = lineCommentStart; this.currentChar = origBuffer.charAt(0); build(); } private void build() { previousLevels.push(0); // consume inital indent int currentLevel = skipIndent(); // transform all other input while (currentChar != Chars.EOI) { int commentChars = skipLineComment(); if (currentChar != '\n' && currentChar != Chars.EOI) { builder.append(currentChar); advance(); continue; } // register newline builder.appendNewline(commentChars); advance(); // consume line indent int indent = skipIndent(); // generate INDENTS/DEDENTS if (indent > currentLevel) { previousLevels.push(currentLevel); currentLevel = indent; builder.append(Chars.INDENT); } else { while (indent < currentLevel && indent <= previousLevels.peek()) { currentLevel = previousLevels.pop(); builder.append(Chars.DEDENT); } if (strict && indent < currentLevel) { throw new IllegalIndentationException(origBuffer, origBuffer.getPosition(cursor)); } } } // make sure to close all remaining indentation scopes if (previousLevels.size() > 1) { builder.append('\n'); while (previousLevels.size() > 1) { previousLevels.pop(); builder.append(Chars.DEDENT); } } } private int skipIndent() { int indent = 0; loop: while (true) { switch (currentChar) { case ' ': indent++; advance(); continue; case '\t': indent = ((indent / tabStop) + 1) * tabStop; advance(); continue; case '\n': if (!skipEmptyLines) builder.appendNewline(0); indent = 0; advance(); continue; case Chars.EOI: indent = 0; break loop; default: if (skipLineComment() == 0) break loop; } } return indent; } private void advance() { currentChar = origBuffer.charAt(++cursor); } private int skipLineComment() { if (lineCommentStart != null && origBuffer.test(cursor, lineCommentStart)) { int start = cursor; while (currentChar != '\n' && currentChar != Chars.EOI) { advance(); } return cursor - start; } return 0; } private class BufferBuilder { private final StringBuilder sb = new StringBuilder(); private final IntArrayStack indexMap = new IntArrayStack(); private void append(char c) { indexMap.push(cursor); sb.append(c); } private void appendNewline(int commentChars) { indexMap.push(cursor - commentChars); sb.append('\n'); } public char[] getChars() { char[] buffer = new char[sb.length()]; sb.getChars(0, sb.length(), buffer, 0); return buffer; } public int[] getIndexMap() { return indexMap.toArray(); } } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/buffers/InputBuffer.java000066400000000000000000000067471421263112100302700ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.buffers; import org.parboiled.support.IndexRange; import org.parboiled.support.Position; /** * Abstraction of a simple char[] buffer holding the input text to be parsed. */ public interface InputBuffer { /** * Returns the character at the given index. If the index is invalid the method returns * {@link org.parboiled.support.Chars#EOI}. * * @param index the index * @return the character at the given index or Chars.EOI. */ char charAt(int index); /** * Determines whether the characters starting at the given index match the ones from the given array (in order). * * @param index the index into the input buffer where to start the comparison * @param characters the characters to test against the input buffer * @return true if matched */ boolean test(int index, char[] characters); /** * Constructs a new {@link String} from all character between the given indices. * Invalid indices are automatically adjusted to their respective boundary. * * @param start the start index (inclusively) * @param end the end index (exclusively) * @return a new String (non-interned) */ String extract(int start, int end); /** * Constructs a new {@link String} from all character covered by the given IndexRange. * * @param range the IndexRange * @return a new String (non-interned) */ String extract(IndexRange range); /** * Returns the line and column number of the character with the given index encapsulated in a * {@link org.parboiled.support.Position} * object. The very first character has the line number 1 and the column number 1. * * @param index the index of the character to get the line number of * @return the line number */ Position getPosition(int index); /** * Translates the given index from the scope of this InputBuffer to the scope of the original, underlying char * array. The {@link DefaultInputBuffer} implementation simply returns the given index, but other implementations * like the {@link IndentDedentInputBuffer} or the {@link MutableInputBuffer} need to "undo" all compressions and * index shiftings performed internally in order to return the underlying index. * * @param index the index relative to this InputBuffer * @return the index relative to the underlying string or char array */ int getOriginalIndex(int index); /** * Constructs a new {@link String} containing all characters with the given line number except for the trailing * newline. * * @param lineNumber the line number to get * @return the string */ String extractLine(int lineNumber); /** * Returns the number of lines in the input buffer. * * @return number of lines in the input buffer. */ int getLineCount(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/buffers/InputBufferUtils.java000066400000000000000000000033401421263112100312730ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.buffers; import static org.parboiled.support.Chars.*; public final class InputBufferUtils { private InputBufferUtils() {} /** * Collects the actual input text the input buffer provides into a String. * This is especially useful for IndentDedentInputBuffers created by "transformIndents". * @param buf the input buffer to collect from * @return a string containing the content of the given input buffer */ public static String collectContent(InputBuffer buf) { StringBuilder sb = new StringBuilder(); int ix = 0; loop: while (true) { char c = buf.charAt(ix++); switch (c) { case INDENT: sb.append('\u00bb'); // right pointed double angle quotation mark break; case DEDENT: sb.append('\u00ab'); // left pointed double angle quotation mark break; case EOI: break loop; default: sb.append(c); } } return sb.toString(); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/buffers/MutableInputBuffer.java000066400000000000000000000100261421263112100315630ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.buffers; import org.parboiled.support.IndexRange; import org.parboiled.support.Position; import java.util.Arrays; import static org.parboiled.common.Preconditions.*; /** * An InputBuffer wrapping another InputBuffer and providing for the ability to insert (and undo) characters at * certain index positions. Inserted chars do not appear in extracted text and have the same positions as the * original chars at their indices. * Note that this implementation is optimized for a rather small number of insertions and will perform badly with * a large number of insertions. */ public class MutableInputBuffer implements InputBuffer { private final InputBuffer buffer; private int[] inserts = new int[0]; private char[] chars = new char[0]; public MutableInputBuffer(InputBuffer buffer) { this.buffer = buffer; } public char charAt(int index) { int j = Arrays.binarySearch(inserts, index); if (j >= 0) return chars[j]; return buffer.charAt(index + (j + 1)); } public boolean test(int index, char[] characters) { throw new UnsupportedOperationException(); } public Position getPosition(int index) { return buffer.getPosition(map(index)); } public int getOriginalIndex(int index) { return buffer.getOriginalIndex(map(index)); } public String extractLine(int lineNumber) { return buffer.extractLine(lineNumber); } public String extract(int start, int end) { return buffer.extract(map(start), map(end)); } public String extract(IndexRange range) { return buffer.extract(map(range.start), map(range.end)); } public int getLineCount() { return buffer.getLineCount(); } private int map(int index) { int j = Arrays.binarySearch(inserts, index); if (j < 0) j = -(j + 1); return index - j; } public void insertChar(int index, char c) { int j = Arrays.binarySearch(inserts, index); if (j < 0) j = -(j + 1); char[] newChars = new char[chars.length + 1]; System.arraycopy(chars, 0, newChars, 0, j); newChars[j] = c; System.arraycopy(chars, j, newChars, j + 1, chars.length - j); chars = newChars; int[] newInserts = new int[inserts.length + 1]; System.arraycopy(inserts, 0, newInserts, 0, j); newInserts[j] = index; for (int i = j; i < inserts.length; i++) { newInserts[i + 1] = inserts[i] + 1; } inserts = newInserts; } public char undoCharInsertion(int index) { int j = Arrays.binarySearch(inserts, index); checkArgument(j >= 0, "Cannot undo a non-existing insertion"); char removedChar = chars[j]; char[] newChars = new char[chars.length - 1]; System.arraycopy(chars, 0, newChars, 0, j); System.arraycopy(chars, j + 1, newChars, j, newChars.length - j); chars = newChars; int[] newInserts = new int[inserts.length - 1]; System.arraycopy(inserts, 0, newInserts, 0, j); for (int i = j + 1; i < inserts.length; i++) { newInserts[i - 1] = inserts[i] - 1; } inserts = newInserts; return removedChar; } public void replaceInsertedChar(int index, char c) { int j = Arrays.binarySearch(inserts, index); checkArgument(j >= 0, "Can only replace chars that were previously inserted"); chars[j] = c; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/000077500000000000000000000000001421263112100250125ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/ArrayBuilder.java000066400000000000000000000043351421263112100302470ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import java.lang.reflect.Array; public class ArrayBuilder { private T[] array; public ArrayBuilder() { array = null; } public ArrayBuilder(T... elements) { array = elements; } public T[] get() { return array; } @SuppressWarnings({"unchecked"}) public ArrayBuilder add(T... elements) { if (elements == null) return this; if (array == null) { array = elements; return this; } T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + elements.length); System.arraycopy(array, 0, newArray, 0, array.length); System.arraycopy(elements, 0, newArray, array.length, elements.length); array = newArray; return this; } @SuppressWarnings({"unchecked"}) public ArrayBuilder addNonNulls(T... elements) { if (elements == null) return this; if (array == null) { array = elements; return this; } int nonNulls = 0; for (T element : elements) { if (element != null) nonNulls++; } if (nonNulls == 0) return this; T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + nonNulls); System.arraycopy(array, 0, newArray, 0, array.length); for (int i = 0, j = array.length; i < elements.length; i++) { T element = elements[i]; if (element != null) { newArray[j++] = element; } } array = newArray; return this; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Base64.java000066400000000000000000000660771421263112100267210ustar00rootroot00000000000000/** * A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance * with RFC 2045.

* On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster * on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes) * compared to sun.misc.Encoder()/Decoder().

* * On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and * about 50% faster for decoding large arrays. This implementation is about twice as fast on very small * arrays (< 30 bytes). If source/destination is a String this * version is about three times as fast due to the fact that the Commons Codec result has to be recoded * to a String from byte[], which is very expensive.

* * This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only * allocates the resulting array. This produces less garbage and it is possible to handle arrays twice * as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown * whether Sun's sun.misc.Encoder()/Decoder() produce temporary arrays but since performance * is quite low it probably does.

* * The encoder produces the same output as the Sun one except that the Sun's encoder appends * a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the * length and is probably a side effect. Both are in conformance with RFC 2045 though.
* Commons codec seem to always att a trailing line separator.

* * Note! * The encode/decode method pairs (types) come in three versions with the exact same algorithm and * thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different * format types. The methods not used can simply be commented out.

* * There is also a "fast" version of all decode methods that works the same way as the normal ones, but * har a few demands on the decoded input. Normally though, these fast verions should be used if the source if * the input is known and it hasn't bee tampered with.

* * If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com. * * Licence (BSD): * ============== * * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com) * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * Neither the name of the MiG InfoCom AB nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * @version 2.2 * @author Mikael Grev * Date: 2004-aug-02 * Time: 11:31:11 * * Adapted in 2009 by Mathias Doenitz. */ package org.parboiled.common; import java.util.Arrays; @SuppressWarnings({"UnnecessaryParentheses"}) public class Base64 { // -------- FIELDS ------------------------------------------------------------------------------------------------- private static Base64 RFC2045; private static Base64 CUSTOM; private final char[] CA; private final int[] IA; private final char fillChar; // -------- STATIC METHODS ----------------------------------------------------------------------------------------- public static Base64 custom() { if (CUSTOM == null) { CUSTOM = new Base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-_"); } return CUSTOM; } public static Base64 rfc2045() { if (RFC2045 == null) { RFC2045 = new Base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="); } return RFC2045; } // -------- CONSTRUCTORS ------------------------------------------------------------------------------------------- public Base64(String alphabet) { if (alphabet == null || alphabet.length() != 65) throw new IllegalArgumentException(); CA = alphabet.substring(0, 64).toCharArray(); IA = new int[256]; Arrays.fill(IA, -1); for (int i = 0, iS = CA.length; i < iS; i++) { IA[CA[i]] = i; } fillChar = alphabet.charAt(64); IA[fillChar] = 0; } // -------- OTHER METHODS ------------------------------------------------------------------------------------------ /** * Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with * and without line separators. * * @param sArr The source array. null or length 0 will return an empty array. * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters * (including '=') isn't divideable by 4. (I.e. definitely corrupted). */ public final byte[] decode(char[] sArr) { // Check special case int sLen = sArr != null ? sArr.length : 0; if (sLen == 0) { return new byte[0]; } // Count illegal characters (including '\r', '\n') to know what size the returned array will be, // so we don't have to reallocate & copy it later. int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) for ( int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. { if (IA[sArr[i]] < 0) { sepCnt++; } } // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. if ((sLen - sepCnt) % 4 != 0) { return null; } int pad = 0; for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;) { if (sArr[i] == fillChar) { pad++; } } int len = ((sLen - sepCnt) * 6 >> 3) - pad; byte[] dArr = new byte[len]; // Preallocate byte[] of exact length for (int s = 0, d = 0; d < len;) { // Assemble three bytes into an int from four "valid" characters. int i = 0; for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. int c = IA[sArr[s++]]; if (c >= 0) { i |= c << (18 - j * 6); } else { j--; } } // Add the bytes dArr[d++] = (byte) (i >> 16); if (d < len) { dArr[d++] = (byte) (i >> 8); if (d < len) { dArr[d++] = (byte) i; } } } return dArr; } /** * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with * and without line separators. * * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters * (including '=') isn't divideable by 4. (I.e. definitely corrupted). */ public final byte[] decode(byte[] sArr) { // Check special case int sLen = sArr.length; // Count illegal characters (including '\r', '\n') to know what size the returned array will be, // so we don't have to reallocate & copy it later. int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) for ( int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. { if (IA[sArr[i] & 0xff] < 0) { sepCnt++; } } // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. if ((sLen - sepCnt) % 4 != 0) { return null; } int pad = 0; for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;) { if (sArr[i] == fillChar) { pad++; } } int len = ((sLen - sepCnt) * 6 >> 3) - pad; byte[] dArr = new byte[len]; // Preallocate byte[] of exact length for (int s = 0, d = 0; d < len;) { // Assemble three bytes into an int from four "valid" characters. int i = 0; for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. int c = IA[sArr[s++] & 0xff]; if (c >= 0) { i |= c << (18 - j * 6); } else { j--; } } // Add the bytes dArr[d++] = (byte) (i >> 16); if (d < len) { dArr[d++] = (byte) (i >> 8); if (d < len) { dArr[d++] = (byte) i; } } } return dArr; } /** * Decodes a BASE64 encoded String. All illegal characters will be ignored and can handle both strings with * and without line separators.
* Note! It can be up to about 2x the speed to call decode(str.toCharArray()) instead. That * will create a temporary array though. This version will use str.charAt(i) to iterate the string. * * @param str The source string. null or length 0 will return an empty array. * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters * (including '=') isn't divideable by 4. (I.e. definitely corrupted). */ public final byte[] decode(String str) { // Check special case int sLen = str != null ? str.length() : 0; if (sLen == 0) { return new byte[0]; } // Count illegal characters (including '\r', '\n') to know what size the returned array will be, // so we don't have to reallocate & copy it later. int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) for ( int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. { if (IA[str.charAt(i)] < 0) { sepCnt++; } } // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. if ((sLen - sepCnt) % 4 != 0) { return null; } // Count '=' at end int pad = 0; for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;) { if (str.charAt(i) == fillChar) { pad++; } } int len = ((sLen - sepCnt) * 6 >> 3) - pad; byte[] dArr = new byte[len]; // Preallocate byte[] of exact length for (int s = 0, d = 0; d < len;) { // Assemble three bytes into an int from four "valid" characters. int i = 0; for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. int c = IA[str.charAt(s++)]; if (c >= 0) { i |= c << (18 - j * 6); } else { j--; } } // Add the bytes dArr[d++] = (byte) (i >> 16); if (d < len) { dArr[d++] = (byte) (i >> 8); if (d < len) { dArr[d++] = (byte) i; } } } return dArr; } /** * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as * fast as {@link #decode(char[])}. The preconditions are:
* + The array must have a line length of 76 chars OR no line separators at all (one line).
* + Line separator must be "\r\n", as specified in RFC 2045 * + The array must not contain illegal characters within the encoded string
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
* * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. * @return The decoded array of bytes. May be of length 0. */ public final byte[] decodeFast(char[] sArr) { // Check special case int sLen = sArr.length; if (sLen == 0) { return new byte[0]; } int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. // Trim illegal chars from start while (sIx < eIx && IA[sArr[sIx]] < 0) { sIx++; } // Trim illegal chars from end while (eIx > 0 && IA[sArr[eIx]] < 0) { eIx--; } // get the padding count (=) (0, 1 or 2) int pad = sArr[eIx] == fillChar ? (sArr[eIx - 1] == fillChar ? 2 : 1) : 0; // Count '=' at end. int cCnt = eIx - sIx + 1; // Content count including possible separators int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes byte[] dArr = new byte[len]; // Preallocate byte[] of exact length // Decode all but the last 0 - 2 bytes. int d = 0; for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { // Assemble three bytes into an int from four "valid" characters. int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; // Add the bytes dArr[d++] = (byte) (i >> 16); dArr[d++] = (byte) (i >> 8); dArr[d++] = (byte) i; // If line separator, jump over it. if (sepCnt > 0 && ++cc == 19) { sIx += 2; cc = 0; } } if (d < len) { // Decode last 1-3 bytes (incl '=') into 1-3 bytes int i = 0; for (int j = 0; sIx <= eIx - pad; j++) { i |= IA[sArr[sIx++]] << (18 - j * 6); } for (int r = 16; d < len; r -= 8) { dArr[d++] = (byte) (i >> r); } } return dArr; } /** * Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as * fast as {@link #decode(byte[])}. The preconditions are:
* + The array must have a line length of 76 chars OR no line separators at all (one line).
* + Line separator must be "\r\n", as specified in RFC 2045 * + The array must not contain illegal characters within the encoded string
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
* * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. * @return The decoded array of bytes. May be of length 0. */ public final byte[] decodeFast(byte[] sArr) { // Check special case int sLen = sArr.length; if (sLen == 0) { return new byte[0]; } int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. // Trim illegal chars from start while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) { sIx++; } // Trim illegal chars from end while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) { eIx--; } // get the padding count (=) (0, 1 or 2) int pad = sArr[eIx] == fillChar ? (sArr[eIx - 1] == fillChar ? 2 : 1) : 0; // Count '=' at end. int cCnt = eIx - sIx + 1; // Content count including possible separators int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes byte[] dArr = new byte[len]; // Preallocate byte[] of exact length // Decode all but the last 0 - 2 bytes. int d = 0; for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { // Assemble three bytes into an int from four "valid" characters. int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; // Add the bytes dArr[d++] = (byte) (i >> 16); dArr[d++] = (byte) (i >> 8); dArr[d++] = (byte) i; // If line separator, jump over it. if (sepCnt > 0 && ++cc == 19) { sIx += 2; cc = 0; } } if (d < len) { // Decode last 1-3 bytes (incl '=') into 1-3 bytes int i = 0; for (int j = 0; sIx <= eIx - pad; j++) { i |= IA[sArr[sIx++]] << (18 - j * 6); } for (int r = 16; d < len; r -= 8) { dArr[d++] = (byte) (i >> r); } } return dArr; } /** * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as * fast as {@link #decode(String)}. The preconditions are:
* + The array must have a line length of 76 chars OR no line separators at all (one line).
* + Line separator must be "\r\n", as specified in RFC 2045 * + The array must not contain illegal characters within the encoded string
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
* * @param s The source string. Length 0 will return an empty array. null will throw an exception. * @return The decoded array of bytes. May be of length 0. */ public final byte[] decodeFast(String s) { // Check special case int sLen = s.length(); if (sLen == 0) { return new byte[0]; } int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. // Trim illegal chars from start while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) { sIx++; } // Trim illegal chars from end while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) { eIx--; } // get the padding count (=) (0, 1 or 2) int pad = s.charAt(eIx) == fillChar ? (s.charAt(eIx - 1) == fillChar ? 2 : 1) : 0; // Count '=' at end. int cCnt = eIx - sIx + 1; // Content count including possible separators int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes byte[] dArr = new byte[len]; // Preallocate byte[] of exact length // Decode all but the last 0 - 2 bytes. int d = 0; for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) { // Assemble three bytes into an int from four "valid" characters. int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s .charAt(sIx++)]; // Add the bytes dArr[d++] = (byte) (i >> 16); dArr[d++] = (byte) (i >> 8); dArr[d++] = (byte) i; // If line separator, jump over it. if (sepCnt > 0 && ++cc == 19) { sIx += 2; cc = 0; } } if (d < len) { // Decode last 1-3 bytes (incl '=') into 1-3 bytes int i = 0; for (int j = 0; sIx <= eIx - pad; j++) { i |= IA[s.charAt(sIx++)] << (18 - j * 6); } for (int r = 16; d < len; r -= 8) { dArr[d++] = (byte) (i >> r); } } return dArr; } // **************************************************************************************** // * byte[] version // **************************************************************************************** /** * Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. * * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a * little faster. * @return A BASE64 encoded array. Never null. */ public final byte[] encodeToByte(byte[] sArr, boolean lineSep) { // Check special case int sLen = sArr != null ? sArr.length : 0; if (sLen == 0) { return new byte[0]; } int eLen = (sLen / 3) * 3; // Length of even 24-bits. int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array byte[] dArr = new byte[dLen]; // Encode even 24-bits for (int s = 0, d = 0, cc = 0; s < eLen;) { // Copy next three bytes into lower 24 bits of int, paying attension to sign. int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); // Encode the int into four chars dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; dArr[d++] = (byte) CA[i & 0x3f]; // Add optional line separator if (lineSep && ++cc == 19 && d < dLen - 2) { dArr[d++] = '\r'; dArr[d++] = '\n'; cc = 0; } } // Pad and encode last bits if source isn't an even 24 bits. int left = sLen - eLen; // 0 - 2. if (left > 0) { // Prepare the int int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); // Set last four chars dArr[dLen - 4] = (byte) CA[i >> 12]; dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) fillChar; dArr[dLen - 1] = (byte) fillChar; } return dArr; } // **************************************************************************************** // * String version // **************************************************************************************** /** * Encodes a raw byte array into a BASE64 String representation in accordance with RFC 2045. * * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a * little faster. * @return A BASE64 encoded array. Never null. */ public final String encodeToString(byte[] sArr, boolean lineSep) { // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. return new String(encodeToChar(sArr, lineSep)); } // **************************************************************************************** // * char[] version // **************************************************************************************** /** * Encodes a raw byte array into a BASE64 char[] representation i accordance with RFC 2045. * * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a * little faster. * @return A BASE64 encoded array. Never null. */ public final char[] encodeToChar(byte[] sArr, boolean lineSep) { // Check special case int sLen = sArr != null ? sArr.length : 0; if (sLen == 0) { return new char[0]; } int eLen = (sLen / 3) * 3; // Length of even 24-bits. int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array char[] dArr = new char[dLen]; // Encode even 24-bits for (int s = 0, d = 0, cc = 0; s < eLen;) { // Copy next three bytes into lower 24 bits of int, paying attension to sign. int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); // Encode the int into four chars dArr[d++] = CA[(i >>> 18) & 0x3f]; dArr[d++] = CA[(i >>> 12) & 0x3f]; dArr[d++] = CA[(i >>> 6) & 0x3f]; dArr[d++] = CA[i & 0x3f]; // Add optional line separator if (lineSep && ++cc == 19 && d < dLen - 2) { dArr[d++] = '\r'; dArr[d++] = '\n'; cc = 0; } } // Pad and encode last bits if source isn't even 24 bits. int left = sLen - eLen; // 0 - 2. if (left > 0) { // Prepare the int int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); // Set last four chars dArr[dLen - 4] = CA[i >> 12]; dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : fillChar; dArr[dLen - 1] = fillChar; } return dArr; } public char[] getAlphabet() { return CA; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/ConsoleSink.java000066400000000000000000000013771421263112100301140ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; public class ConsoleSink implements Sink{ public void receive(String value) { System.out.print(value); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Factory.java000066400000000000000000000012561421263112100272700ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; public interface Factory { T create(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/FileUtils.java000066400000000000000000000263471421263112100275710ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import static org.parboiled.common.Preconditions.*; import java.io.*; import java.nio.charset.Charset; public final class FileUtils { private FileUtils() {} public static String readAllTextFromResource(String resource) { checkArgNotNull(resource, "resource"); return readAllText(FileUtils.class.getClassLoader().getResourceAsStream(resource)); } public static String readAllTextFromResource(String resource, Charset charset) { checkArgNotNull(resource, "resource"); checkArgNotNull(charset, "charset"); return readAllText(FileUtils.class.getClassLoader().getResourceAsStream(resource), charset); } public static String readAllText(String filename) { checkArgNotNull(filename, "filename"); return readAllText(new File(filename)); } public static String readAllText(String filename, Charset charset) { checkArgNotNull(filename, "filename"); checkArgNotNull(charset, "charset"); return readAllText(new File(filename), charset); } public static String readAllText(File file) { checkArgNotNull(file, "file"); return readAllText(file, Charset.forName("UTF8")); } public static String readAllText(File file, Charset charset) { checkArgNotNull(file, "file"); checkArgNotNull(charset, "charset"); try { return readAllText(new FileInputStream(file), charset); } catch (FileNotFoundException e) { return null; } } public static String readAllText(InputStream stream) { return readAllText(stream, Charset.forName("UTF8")); } public static String readAllText(InputStream stream, Charset charset) { checkArgNotNull(charset, "charset"); if (stream == null) return null; BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset)); StringWriter writer = new StringWriter(); copyAll(reader, writer); return writer.toString(); } public static char[] readAllCharsFromResource(String resource) { checkArgNotNull(resource, "resource"); return readAllChars(FileUtils.class.getClassLoader().getResourceAsStream(resource)); } public static char[] readAllCharsFromResource(String resource, Charset charset) { checkArgNotNull(resource, "resource"); checkArgNotNull(charset, "charset"); return readAllChars(FileUtils.class.getClassLoader().getResourceAsStream(resource), charset); } public static char[] readAllChars(String filename) { checkArgNotNull(filename, "filename"); return readAllChars(new File(filename)); } public static char[] readAllChars(String filename, Charset charset) { checkArgNotNull(filename, "filename"); checkArgNotNull(charset, "charset"); return readAllChars(new File(filename), charset); } public static char[] readAllChars(File file) { checkArgNotNull(file, "file"); return readAllChars(file, Charset.forName("UTF8")); } public static char[] readAllChars(File file, Charset charset) { checkArgNotNull(file, "file"); checkArgNotNull(charset, "charset"); try { return readAllChars(new FileInputStream(file), charset); } catch (FileNotFoundException e) { return null; } } public static char[] readAllChars(InputStream stream) { return readAllChars(stream, Charset.forName("UTF8")); } public static char[] readAllChars(InputStream stream, Charset charset) { checkArgNotNull(charset, "charset"); if (stream == null) return null; BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset)); CharArrayWriter writer = new CharArrayWriter(); copyAll(reader, writer); return writer.toCharArray(); } public static byte[] readAllBytesFromResource(String resource) { checkArgNotNull(resource, "resource"); return readAllBytes(FileUtils.class.getClassLoader().getResourceAsStream(resource)); } public static byte[] readAllBytes(String filename) { checkArgNotNull(filename, "filename"); return readAllBytes(new File(filename)); } public static byte[] readAllBytes(File file) { checkArgNotNull(file, "file"); try { return readAllBytes(new FileInputStream(file)); } catch (FileNotFoundException e) { return null; } } public static byte[] readAllBytes(InputStream stream) { if (stream == null) return null; BufferedInputStream in = new BufferedInputStream(stream); ByteArrayOutputStream out = new ByteArrayOutputStream(); copyAll(in, out); return out.toByteArray(); } public static void writeAllText(String text, String filename) { checkArgNotNull(filename, "filename"); writeAllText(text, new File(filename)); } public static void writeAllText(String text, String filename, Charset charset) { checkArgNotNull(filename, "filename"); checkArgNotNull(charset, "charset"); writeAllText(text, new File(filename), charset); } public static void writeAllText(String text, File file) { checkArgNotNull(file, "file"); writeAllText(text, file, Charset.forName("UTF8")); } public static void writeAllText(String text, File file, Charset charset) { checkArgNotNull(file, "file"); checkArgNotNull(charset, "charset"); try { ensureParentDir(file); writeAllText(text, new FileOutputStream(file), charset); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } public static void writeAllText(String text, OutputStream stream) { checkArgNotNull(stream, "stream"); writeAllText(text, stream, Charset.forName("UTF8")); } public static void writeAllText(String text, OutputStream stream, Charset charset) { checkArgNotNull(stream, "stream"); checkArgNotNull(charset, "charset"); StringReader reader = new StringReader(text != null ? text : ""); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stream, charset)); copyAll(reader, writer); } public static void writeAllChars(char[] chars, String filename) { checkArgNotNull(filename, "filename"); writeAllChars(chars, new File(filename)); } public static void writeAllChars(char[] chars, String filename, Charset charset) { checkArgNotNull(filename, "filename"); checkArgNotNull(charset, "charset"); writeAllChars(chars, new File(filename), charset); } public static void writeAllChars(char[] chars, File file) { checkArgNotNull(file, "file"); writeAllChars(chars, file, Charset.forName("UTF8")); } public static void writeAllChars(char[] chars, File file, Charset charset) { checkArgNotNull(file, "file"); checkArgNotNull(charset, "charset"); try { ensureParentDir(file); writeAllChars(chars, new FileOutputStream(file), charset); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } public static void writeAllChars(char[] chars, OutputStream stream) { checkArgNotNull(stream, "stream"); writeAllChars(chars, stream, Charset.forName("UTF8")); } public static void writeAllChars(char[] chars, OutputStream stream, Charset charset) { checkArgNotNull(stream, "stream"); checkArgNotNull(charset, "charset"); CharArrayReader reader = new CharArrayReader(chars != null ? chars : new char[0]); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stream, charset)); copyAll(reader, writer); } public static void writeAllBytes(byte[] data, String filename) { checkArgNotNull(data, "data"); checkArgNotNull(filename, "filename"); writeAllBytes(data, new File(filename)); } public static void writeAllBytes(byte[] data, File file) { checkArgNotNull(data, "data"); checkArgNotNull(file, "file"); try { ensureParentDir(file); writeAllBytes(data, new FileOutputStream(file)); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } public static void writeAllBytes(byte[] data, OutputStream stream) { checkArgNotNull(data, "data"); checkArgNotNull(stream, "stream"); ByteArrayInputStream in = new ByteArrayInputStream(data); BufferedOutputStream out = new BufferedOutputStream(stream); copyAll(in, out); } public static void copyAll(Reader reader, Writer writer) { checkArgNotNull(reader, "reader"); checkArgNotNull(writer, "writer"); try { char[] data = new char[4096]; // copy in chunks of 4K int count; while ((count = reader.read(data)) >= 0) writer.write(data, 0, count); reader.close(); writer.close(); } catch (IOException e) { throw new RuntimeException(e); } } public static void copyAll(InputStream in, OutputStream out) { checkArgNotNull(in, "in"); checkArgNotNull(out, "out"); try { byte[] data = new byte[4096]; // copy in chunks of 4K int count; while ((count = in.read(data)) >= 0) { out.write(data, 0, count); } in.close(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } } public static void ensureParentDir(String filename) { ensureParentDir(new File(filename)); } public static void ensureParentDir(File file) { File parentDir = file.getParentFile(); if (parentDir != null && !parentDir.exists()) { try { forceMkdir(parentDir); } catch (IOException e) { throw new RuntimeException(String.format("Could not create directory %s", parentDir), e); } } } public static void forceMkdir(File directory) throws IOException { if (directory.exists()) { if (directory.isFile()) { throw new IOException( "File '" + directory + "' exists and is not a directory. Unable to create directory."); } } else { if (!directory.mkdirs()) { throw new IOException("Unable to create directory " + directory); } } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Formatter.java000066400000000000000000000017511421263112100276240ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; /** * The capability to transform an object of type T into a string representation. * * @param The type to transform. */ public interface Formatter { /** * Create a string representation for the given object. * * @param object the object to format * @return a string describing the object */ String format(T object); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/ImmutableLinkedList.java000066400000000000000000000121761421263112100315660ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import java.util.AbstractSequentialList; import java.util.ListIterator; import static org.parboiled.common.Preconditions.*; public class ImmutableLinkedList extends AbstractSequentialList { private static final ImmutableLinkedList NIL = new ImmutableLinkedList() { private final ListIterator iterator = new IllIterator(this); @Override public Object head() { throw new UnsupportedOperationException("head of empty list"); } @Override public ImmutableLinkedList tail() { throw new UnsupportedOperationException("tail of empty list"); } @Override public Object last() { throw new UnsupportedOperationException("last of empty list"); } @Override public ListIterator listIterator(int index) { return iterator; } }; @SuppressWarnings({"unchecked"}) public static ImmutableLinkedList nil() { return (ImmutableLinkedList) NIL; } private final T head; private final ImmutableLinkedList tail; // only used by NIL private ImmutableLinkedList() { head = null; tail = null; } public ImmutableLinkedList(T head, ImmutableLinkedList tail) { checkArgNotNull(tail, "tail"); this.head = head; this.tail = tail; } public T head() { return head; } public ImmutableLinkedList tail() { return tail; } public T last() { ImmutableLinkedList cursor = this; while (!cursor.tail.isEmpty()) { cursor = cursor.tail(); } return cursor.head(); } public ImmutableLinkedList prepend(T object) { return new ImmutableLinkedList(object, this); } public ImmutableLinkedList reverse() { if (tail == NIL) return this; ImmutableLinkedList reversed = nil(); ImmutableLinkedList next = this; while (next != NIL) { reversed = reversed.prepend(next.head); next = next.tail; } return reversed; } public static boolean equal(ImmutableLinkedList a, ImmutableLinkedList b) { checkArgNotNull(a, "a"); checkArgNotNull(b, "b"); return Utils.equal(a.head, b.head) && equal(a.tail, b.tail); } public static int hashCode(ImmutableLinkedList list) { checkArgNotNull(list, "list"); return list.isEmpty() ? 0 : 31 * list.head.hashCode() + hashCode(list.tail); } @Override public ListIterator listIterator(int index) { ListIterator iterator = new IllIterator(this); while (index-- > 0) { if (!iterator.hasNext()) throw new IndexOutOfBoundsException(); iterator.next(); } return iterator; } @Override public boolean isEmpty() { return this == NIL; } @Override public int size() { ImmutableLinkedList cursor = this; int size = 0; while (!cursor.isEmpty()) { size++; cursor = cursor.tail(); } return size; } private static class IllIterator implements ListIterator { private final ImmutableLinkedList start; private ImmutableLinkedList current; private int nextIndex = 0; private IllIterator(ImmutableLinkedList start) { this.start = start; this.current = start; } public boolean hasNext() { return current != NIL; } public T next() { ImmutableLinkedList next = current; current = current.tail; nextIndex++; return next.head; } public boolean hasPrevious() { return current != start; } public T previous() { ImmutableLinkedList previous = start; while (previous.tail != current) previous = previous.tail; nextIndex--; return previous.head; } public int nextIndex() { return nextIndex; } public int previousIndex() { return nextIndex - 1; } public void remove() { throw new UnsupportedOperationException(); } public void set(T t) { throw new UnsupportedOperationException(); } public void add(T t) { throw new UnsupportedOperationException(); } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/ImmutableList.java000066400000000000000000000143321421263112100304330ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import java.util.AbstractList; import java.util.List; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.common.Preconditions.checkElementIndex; import static org.parboiled.common.Preconditions.checkState; import static org.parboiled.common.Utils.arrayOf; /** * A simple, immutable List implementation wrapping an array. * * @param The type of the List elements. */ @SuppressWarnings( {"unchecked"}) public abstract class ImmutableList extends AbstractList { private final static ImmutableList EMPTY_LIST = new ImmutableList() { @Override public Object get(int index) { throw new IndexOutOfBoundsException("Empty list has no element with index " + index); } @Override public int size() { return 0; } @Override public ImmutableList append(Object element) { return of(element); } }; private static class SingleElementList extends ImmutableList { private final T element; public SingleElementList(T element) { this.element = element; } @Override public T get(int index) { checkElementIndex(index, 1); return element; } @Override public int size() { return 1; } @Override public ImmutableList append(T element) { return of(this.element, element); } } private static class TwoElementList extends ImmutableList { private final T element0; private final T element1; private TwoElementList(T element0, T element1) { this.element0 = element0; this.element1 = element1; } @Override public T get(int index) { checkElementIndex(index, 2); return index == 0 ? element0 : element1; } @Override public int size() { return 2; } @Override public ImmutableList append(T element) { return of(element0, element1, element); } } private static class RegularList extends ImmutableList { private final Object[] elements; private RegularList(Object[] elements) { this.elements = elements; } @Override public Object get(int index) { return elements[index]; } @Override public int size() { return elements.length; } @Override public ImmutableList append(Object element) { Object[] newElements = new Object[elements.length + 1]; System.arraycopy(elements, 0, newElements, 0, elements.length); newElements[elements.length] = element; return new RegularList(newElements); } } public abstract ImmutableList append(T element); public static ImmutableList copyOf(List other) { checkArgNotNull(other, "other"); return (ImmutableList) (other instanceof ImmutableList ? other : new RegularList(other.toArray())); } public static ImmutableList of() { return (ImmutableList) EMPTY_LIST; } public static ImmutableList of(T a) { return new SingleElementList(a); } public static ImmutableList of(T a, T b) { return new TwoElementList(a, b); } public static ImmutableList of(T a, T b, T c) { return (ImmutableList) new RegularList(new Object[] {a, b, c}); } public static ImmutableList of(T... elements) { checkArgNotNull(elements, "elements"); return (ImmutableList) new RegularList(elements.clone()); } public static ImmutableList of(T first, T[] more) { checkArgNotNull(more, "more"); return (ImmutableList) new RegularList(arrayOf(first, more.clone())); } public static ImmutableList of(T[] first, T last) { checkArgNotNull(first, "first"); return (ImmutableList) new RegularList(arrayOf(first.clone(), last)); } public static ImmutableList of(T first, ImmutableList more) { checkArgNotNull(more, "more"); if (more instanceof SingleElementList) { return of(first, (T) ((SingleElementList) more).element); } else if (more instanceof TwoElementList) { TwoElementList list = (TwoElementList) more; return (ImmutableList) new RegularList(new Object[] {first, list.element0, list.element1}); } else if (more instanceof RegularList) { RegularList list = (RegularList) more; return (ImmutableList) new RegularList(arrayOf(first, list.elements)); } else { checkState(more == EMPTY_LIST); return of(first); } } public static ImmutableList of(ImmutableList first, T last) { checkArgNotNull(first, "more"); if (first instanceof SingleElementList) { return of((T) ((SingleElementList) first).element, last); } else if (first instanceof TwoElementList) { TwoElementList list = (TwoElementList) first; return (ImmutableList) new RegularList(new Object[] {list.element0, list.element1, last}); } else if (first instanceof RegularList) { RegularList list = (RegularList) first; return (ImmutableList) new RegularList(arrayOf(list.elements, last)); } else { checkState(first == EMPTY_LIST); return of(last); } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/IntArrayStack.java000066400000000000000000000061031421263112100303740ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; public class IntArrayStack { public static class UnderflowException extends RuntimeException { public UnderflowException(String message) { super(message); } } private static final int INITIAL_CAPACITY = 16; private int[] array; private int top; public IntArrayStack() { array = new int[INITIAL_CAPACITY]; top = -1; } /** * Tests if the stack is empty. * * @return true if empty, false otherwise. */ public boolean isEmpty() { return top == -1; } /** * Returns the number of element currently on the stack. * * @return the number of element currently on the stack */ public int size() { return top + 1; } /** * Copies all elements currently on the stack into the given array. * * @param destArray the array * @param destStartIndex the index to start copying into */ public void getElements(int[] destArray, int destStartIndex) { System.arraycopy(array, 0, destArray, destStartIndex, size()); } /** * @return all elements in a new array. */ public int[] toArray() { int[] array = new int[size()]; getElements(array, 0); return array; } /** * Empties the stack. */ public void clear() { top = -1; } /** * Returns the item at the top of the stack without removing it. * * @return the most recently inserted item in the stack. * @throws UnderflowException if the stack is empty. */ public int peek() { if (isEmpty()) { throw new UnderflowException("IntArrayStack peek"); } return array[top]; } /** * Removes the most recently inserted item from the stack. * * @return the top stack item * @throws UnderflowException if the stack is empty. */ public int pop() { if (isEmpty()) { throw new UnderflowException("IntArrayStack pop"); } return array[top--]; } /** * Pushes a new item onto the stack. * * @param x the item to add. */ public void push(int x) { if (top == array.length - 1) { expandCapacity(); } array[++top] = x; } private void expandCapacity() { int[] newArray = new int[array.length * 2]; System.arraycopy(array, 0, newArray, 0, array.length); array = newArray; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Preconditions.java000066400000000000000000000445771421263112100305160ustar00rootroot00000000000000/* * Copyright (C) 2007 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import java.util.NoSuchElementException; /** * Simple static methods to be called at the start of your own methods to verify * correct arguments and state. This allows constructs such as *
 *     if (count <= 0) {
 *       throw new IllegalArgumentException("must be positive: " + count);
 *     }
 * 
* to be replaced with the more compact *
 *     checkArgument(count > 0, "must be positive: %s", count);
 * 
* Note that the sense of the expression is inverted; with {@code Preconditions} * you declare what you expect to be true, just as you do with an * * {@code assert} or a JUnit {@code assertTrue} call. * *

Warning: only the {@code "%s"} specifier is recognized as a * placeholder in these messages, not the full range of String.format(String, Object[]) specifiers. *

*

Take care not to confuse precondition checking with other similar types * of checks! Precondition exceptions -- including those provided here, but also * {@link IndexOutOfBoundsException}, {@link NoSuchElementException}, {@link * UnsupportedOperationException} and others -- are used to signal that the * calling method has made an error. This tells the caller that it should * not have invoked the method when it did, with the arguments it did, or * perhaps ever. Postcondition or other invariant failures should not throw * these types of exceptions.

* * @author Kevin Bourrillion */ public final class Preconditions { private Preconditions() {} /** * Ensures the truth of an expression involving one or more parameters to the * calling method. * * @param expression a boolean expression * @throws IllegalArgumentException if {@code expression} is false */ public static void checkArgument(boolean expression) { if (!expression) { throw new IllegalArgumentException(); } } /** * Ensures the truth of an expression involving one or more parameters to the * calling method. * * @param expression a boolean expression * @param errorMessage the exception message to use if the check fails; will * be converted to a string using {@link String#valueOf(Object)} * @throws IllegalArgumentException if {@code expression} is false */ public static void checkArgument(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalArgumentException(String.valueOf(errorMessage)); } } /** * Ensures the truth of an expression involving one or more parameters to the * calling method. * * @param expression a boolean expression * @param errorMessageTemplate a template for the exception message should the * check fail. The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. * @throws IllegalArgumentException if {@code expression} is false * @throws NullPointerException if the check fails and either {@code * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let * this happen) */ public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalArgumentException( format(errorMessageTemplate, errorMessageArgs)); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param expression a boolean expression * @throws IllegalStateException if {@code expression} is false */ public static void checkState(boolean expression) { if (!expression) { throw new IllegalStateException(); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param expression a boolean expression * @param errorMessage the exception message to use if the check fails; will * be converted to a string using {@link String#valueOf(Object)} * @throws IllegalStateException if {@code expression} is false */ public static void checkState(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalStateException(String.valueOf(errorMessage)); } } /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param expression a boolean expression * @param errorMessageTemplate a template for the exception message should the * check fail. The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. * @throws IllegalStateException if {@code expression} is false * @throws NullPointerException if the check fails and either {@code * errorMessageTemplate} or {@code errorMessageArgs} is null (don't let * this happen) */ public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (!expression) { throw new IllegalStateException( format(errorMessageTemplate, errorMessageArgs)); } } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. * * @param reference an object reference * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ public static T checkNotNull(T reference) { if (reference == null) { throw new NullPointerException(); } return reference; } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. * * @param reference an object reference * @param errorMessage the exception message to use if the check fails; will * be converted to a string using {@link String#valueOf(Object)} * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ public static T checkNotNull(T reference, Object errorMessage) { if (reference == null) { throw new NullPointerException(String.valueOf(errorMessage)); } return reference; } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. * * @param reference an object reference * @param parameterName the parameter name * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ public static T checkArgNotNull(T reference, String parameterName) { if (reference == null) { throw new NullPointerException(format("Argument '%s' must not be null", parameterName)); } return reference; } /** * Ensures that an object reference passed as a parameter to the calling * method is not null. * * @param reference an object reference * @param errorMessageTemplate a template for the exception message should the * check fail. The message is formed by replacing each {@code %s} * placeholder in the template with an argument. These are matched by * position - the first {@code %s} gets {@code errorMessageArgs[0]}, etc. * Unmatched arguments will be appended to the formatted message in square * braces. Unmatched placeholders will be left as-is. * @param errorMessageArgs the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. * @return the non-null reference that was validated * @throws NullPointerException if {@code reference} is null */ public static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { if (reference == null) { // If either of these parameters is null, the right thing happens anyway throw new NullPointerException( format(errorMessageTemplate, errorMessageArgs)); } return reference; } /** * Ensures that {@code index} specifies a valid element in an array, * list or string of size {@code size}. An element index may range from zero, * inclusive, to {@code size}, exclusive. * * @param index a user-supplied index identifying an element of an array, list * or string * @param size the size of that array, list or string * @return the value of {@code index} * @throws IndexOutOfBoundsException if {@code index} is negative or is not * less than {@code size} * @throws IllegalArgumentException if {@code size} is negative */ public static int checkElementIndex(int index, int size) { return checkElementIndex(index, size, "index"); } /** * Ensures that {@code index} specifies a valid element in an array, * list or string of size {@code size}. An element index may range from zero, * inclusive, to {@code size}, exclusive. * * @param index a user-supplied index identifying an element of an array, list * or string * @param size the size of that array, list or string * @param desc the text to use to describe this index in an error message * @return the value of {@code index} * @throws IndexOutOfBoundsException if {@code index} is negative or is not * less than {@code size} * @throws IllegalArgumentException if {@code size} is negative */ public static int checkElementIndex(int index, int size, String desc) { // Carefully optimized for execution by hotspot (explanatory comment above) if (index < 0 || index >= size) { throw new IndexOutOfBoundsException(badElementIndex(index, size, desc)); } return index; } private static String badElementIndex(int index, int size, String desc) { if (index < 0) { return format("%s (%s) must not be negative", desc, index); } else if (size < 0) { throw new IllegalArgumentException("negative size: " + size); } else { // index >= size return format("%s (%s) must be less than size (%s)", desc, index, size); } } /** * Ensures that {@code index} specifies a valid position in an array, * list or string of size {@code size}. A position index may range from zero * to {@code size}, inclusive. * * @param index a user-supplied index identifying a position in an array, list * or string * @param size the size of that array, list or string * @return the value of {@code index} * @throws IndexOutOfBoundsException if {@code index} is negative or is * greater than {@code size} * @throws IllegalArgumentException if {@code size} is negative */ public static int checkPositionIndex(int index, int size) { return checkPositionIndex(index, size, "index"); } /** * Ensures that {@code index} specifies a valid position in an array, * list or string of size {@code size}. A position index may range from zero * to {@code size}, inclusive. * * @param index a user-supplied index identifying a position in an array, list * or string * @param size the size of that array, list or string * @param desc the text to use to describe this index in an error message * @return the value of {@code index} * @throws IndexOutOfBoundsException if {@code index} is negative or is * greater than {@code size} * @throws IllegalArgumentException if {@code size} is negative */ public static int checkPositionIndex(int index, int size, String desc) { // Carefully optimized for execution by hotspot (explanatory comment above) if (index < 0 || index > size) { throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc)); } return index; } private static String badPositionIndex(int index, int size, String desc) { if (index < 0) { return format("%s (%s) must not be negative", desc, index); } else if (size < 0) { throw new IllegalArgumentException("negative size: " + size); } else { // index > size return format("%s (%s) must not be greater than size (%s)", desc, index, size); } } /** * Ensures that {@code start} and {@code end} specify a valid positions * in an array, list or string of size {@code size}, and are in order. A * position index may range from zero to {@code size}, inclusive. * * @param start a user-supplied index identifying a starting position in an * array, list or string * @param end a user-supplied index identifying a ending position in an array, * list or string * @param size the size of that array, list or string * @throws IndexOutOfBoundsException if either index is negative or is * greater than {@code size}, or if {@code end} is less than {@code start} * @throws IllegalArgumentException if {@code size} is negative */ public static void checkPositionIndexes(int start, int end, int size) { // Carefully optimized for execution by hotspot (explanatory comment above) if (start < 0 || end < start || end > size) { throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size)); } } private static String badPositionIndexes(int start, int end, int size) { if (start < 0 || start > size) { return badPositionIndex(start, size, "start index"); } if (end < 0 || end > size) { return badPositionIndex(end, size, "end index"); } // end < start return format("end index (%s) must not be less than start index (%s)", end, start); } /** * Substitutes each {@code %s} in {@code template} with an argument. These * are matched by position - the first {@code %s} gets {@code args[0]}, etc. * If there are more arguments than placeholders, the unmatched arguments will * be appended to the end of the formatted message in square braces. * * @param template a non-null string containing 0 or more {@code %s} * placeholders. * @param args the arguments to be substituted into the message * template. Arguments are converted to strings using * {@link String#valueOf(Object)}. Arguments can be null. * @return String */ static String format(String template, Object... args) { // start substituting the arguments into the '%s' placeholders StringBuilder builder = new StringBuilder( template.length() + 16 * args.length); int templateStart = 0; int i = 0; while (i < args.length) { int placeholderStart = template.indexOf("%s", templateStart); if (placeholderStart == -1) { break; } builder.append(template.substring(templateStart, placeholderStart)); builder.append(args[i++]); templateStart = placeholderStart + 2; } builder.append(template.substring(templateStart)); // if we run out of placeholders, append the extra args in square braces if (i < args.length) { builder.append(" ["); builder.append(args[i++]); while (i < args.length) { builder.append(", "); builder.append(args[i++]); } builder.append("]"); } return builder.toString(); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Predicate.java000066400000000000000000000012761421263112100275630ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; public interface Predicate { boolean apply(T input); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Predicates.java000066400000000000000000000307051421263112100277450ustar00rootroot00000000000000/* * Copyright (C) 2007 Google Inc., adapted in 2010 by Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import static org.parboiled.common.Preconditions.*; import java.util.Collection; public final class Predicates { private Predicates() {} /** * Returns a predicate that always evaluates to {@code true}. * * @return a predicate */ @SuppressWarnings("unchecked") public static Predicate alwaysTrue() { return (Predicate) AlwaysTruePredicate.INSTANCE; } /** * Returns a predicate that always evaluates to {@code false}. * * @return a predicate */ @SuppressWarnings("unchecked") public static Predicate alwaysFalse() { return (Predicate) AlwaysFalsePredicate.INSTANCE; } /** * Returns a predicate that evaluates to {@code true} if the object reference * being tested is null. * * @return a predicate */ @SuppressWarnings("unchecked") public static Predicate isNull() { return (Predicate) IsNullPredicate.INSTANCE; } /** * Returns a predicate that evaluates to {@code true} if the object reference * being tested is not null. * * @return a predicate */ @SuppressWarnings("unchecked") public static Predicate notNull() { return (Predicate) NotNullPredicate.INSTANCE; } /** * Returns a predicate that evaluates to {@code true} if the given predicate * evaluates to {@code false}. * * @param predicate the inner predicate * @return a predicate */ public static Predicate not(Predicate predicate) { return new NotPredicate(predicate); } /** * Returns a predicate that evaluates to {@code true} if each of its * components evaluates to {@code true}. The components are evaluated in * order, and evaluation will be "short-circuited" as soon as a false * predicate is found. It defensively copies the iterable passed in, so future * changes to it won't alter the behavior of this predicate. If {@code * components} is empty, the returned predicate will always evaluate to {@code * true}. * * @param components the components * @return a predicate */ public static Predicate and(Collection> components) { return new AndPredicate(components); } /** * Returns a predicate that evaluates to {@code true} if each of its * components evaluates to {@code true}. The components are evaluated in * order, and evaluation will be "short-circuited" as soon as a false * predicate is found. It defensively copies the array passed in, so future * changes to it won't alter the behavior of this predicate. If {@code * components} is empty, the returned predicate will always evaluate to {@code * true}. * * @param components the components * @return a predicate */ public static Predicate and(Predicate... components) { return new AndPredicate(ImmutableList.of(components)); } /** * Returns a predicate that evaluates to {@code true} if both of its * components evaluate to {@code true}. The components are evaluated in * order, and evaluation will be "short-circuited" as soon as a false * predicate is found. * * @param first the first * @param second the second * @return a predicate */ public static Predicate and(Predicate first, Predicate second) { checkArgNotNull(first, "first"); checkArgNotNull(second, "second"); return new AndPredicate(ImmutableList.>of(first, second)); } /** * Returns a predicate that evaluates to {@code true} if any one of its * components evaluates to {@code true}. The components are evaluated in * order, and evaluation will be "short-circuited" as soon as as soon as a * true predicate is found. It defensively copies the iterable passed in, so * future changes to it won't alter the behavior of this predicate. If {@code * components} is empty, the returned predicate will always evaluate to {@code * false}. * * @param components the components * @return a predicate */ public static Predicate or(Collection> components) { return new OrPredicate(components); } /** * Returns a predicate that evaluates to {@code true} if any one of its * components evaluates to {@code true}. The components are evaluated in * order, and evaluation will be "short-circuited" as soon as as soon as a * true predicate is found. It defensively copies the array passed in, so * future changes to it won't alter the behavior of this predicate. If {@code * components} is empty, the returned predicate will always evaluate to {@code * false}. * * @param components the components * @return a predicate */ public static Predicate or(Predicate... components) { return new OrPredicate(ImmutableList.of(components)); } /** * Returns a predicate that evaluates to {@code true} if either of its * components evaluates to {@code true}. The components are evaluated in * order, and evaluation will be "short-circuited" as soon as as soon as a * true predicate is found. * * @param first the first * @param second the second * @return a predicate */ public static Predicate or(Predicate first, Predicate second) { checkArgNotNull(first, "first"); checkArgNotNull(second, "second"); return new OrPredicate(ImmutableList.>of(first, second)); } /** * Returns a predicate that evaluates to {@code true} if the object being * tested {@code equals()} the given target or both are null. * * @param target the target * @return a predicate */ public static Predicate equalTo(T target) { return (target == null) ? Predicates.isNull() : new IsEqualToPredicate(target); } /** * Returns a predicate that evaluates to {@code true} if the object being * tested is an instance of the given class. If the object being tested * is {@code null} this predicate evaluates to {@code false}. * * @param clazz the clazz * @return a predicate */ public static Predicate instanceOf(Class clazz) { return new InstanceOfPredicate(clazz); } /** * Returns a predicate that evaluates to {@code true} if the object reference * being tested is a member of the given collection. It does not defensively * copy the collection passed in, so future changes to it will alter the * behavior of the predicate. * * This method can technically accept any Collection, but using a typed * collection helps prevent bugs. This approach doesn't block any potential * users since it is always possible to use {@code Predicates.in()}. * * @param target the collection that may contain the function input * @return a predicate */ public static Predicate in(Collection target) { return new InPredicate(target); } private static class AlwaysTruePredicate implements Predicate { private static final Predicate INSTANCE = new AlwaysTruePredicate(); public boolean apply(Object o) { return true; } @Override public String toString() { return "AlwaysTrue"; } } private static class AlwaysFalsePredicate implements Predicate { private static final Predicate INSTANCE = new AlwaysFalsePredicate(); public boolean apply(Object o) { return false; } @Override public String toString() { return "AlwaysFalse"; } } private static class NotPredicate implements Predicate { private final Predicate predicate; private NotPredicate(Predicate predicate) { checkArgNotNull(predicate, "predicate"); this.predicate = predicate; } public boolean apply(T t) { return !predicate.apply(t); } public String toString() { return "Not(" + predicate.toString() + ")"; } } private static class AndPredicate implements Predicate { private final Collection> components; private AndPredicate(Collection> components) { this.components = components; } public boolean apply(T t) { for (Predicate predicate : components) { if (!predicate.apply(t)) { return false; } } return true; } @Override public String toString() { return "And(" + StringUtils.join(components, ", ") + ")"; } } private static class OrPredicate implements Predicate { private final Collection> components; private OrPredicate(Collection> components) { this.components = components; } public boolean apply(T t) { for (Predicate predicate : components) { if (predicate.apply(t)) { return true; } } return false; } @Override public String toString() { return "Or(" + StringUtils.join(components, ", ") + ")"; } } private static class IsEqualToPredicate implements Predicate { private final T target; private IsEqualToPredicate(T target) { this.target = target; } public boolean apply(T t) { return target.equals(t); } @Override public String toString() { return "IsEqualTo(" + target + ")"; } } private static class InstanceOfPredicate implements Predicate { private final Class clazz; private InstanceOfPredicate(Class clazz) { checkArgNotNull(clazz, "clazz"); this.clazz = clazz; } public boolean apply(Object o) { return clazz.isInstance(o); } @Override public String toString() { return "IsInstanceOf(" + clazz.getName() + ")"; } } private static class IsNullPredicate implements Predicate { private static final Predicate INSTANCE = new IsNullPredicate(); public boolean apply(Object o) { return o == null; } @Override public String toString() { return "IsNull"; } } private static class NotNullPredicate implements Predicate { private static final Predicate INSTANCE = new NotNullPredicate(); public boolean apply(Object o) { return o != null; } @Override public String toString() { return "NotNull"; } } private static class InPredicate implements Predicate { private final Collection target; private InPredicate(Collection target) { checkArgNotNull(target, "target"); this.target = target; } public boolean apply(T t) { try { return target.contains(t); } catch (NullPointerException e) { return false; } catch (ClassCastException e) { return false; } } @Override public String toString() { return "In(" + target + ")"; } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Reference.java000066400000000000000000000050101421263112100275470ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; /** * A simple container holding a reference to another object. * * @param */ public class Reference { private T value; /** * Create a new Reference with a null value. */ public Reference() { } /** * Create a new Reference to the given value object. * * @param value the value object */ public Reference(T value) { this.value = value; } /** * Sets this references value field to null. * * @return true */ public boolean clear() { return set(null); } /** * Sets this references value object to the given instance. * * @param value the value * @return true */ public boolean set(T value) { this.value = value; return true; } /** * Retrieves this references value object. * * @return the target */ public T get() { return value; } /** * Retrieves this references value field and clears it. * Equivalent to getAndSet(null). * * @return the target */ public T getAndClear() { return getAndSet(null); } /** * Replaces this references value with the given one. * * @param value the new value * @return the previous value */ public T getAndSet(T value) { T t = this.value; this.value = value; return t; } /** * Replaces this references value with the given one. * * @param value the new value * @return the new value */ public T setAndGet(T value) { return this.value = value; } /** * @return true if this Reference holds a non-null value */ public boolean isSet() { return value != null; } /** * @return true if this Reference holds a null value */ public boolean isNotSet() { return value == null; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Sink.java000066400000000000000000000012641421263112100265640ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; public interface Sink { void receive(T value); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/StringBuilderSink.java000066400000000000000000000016351421263112100312640ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; public class StringBuilderSink implements Sink{ public final StringBuilder builder = new StringBuilder(); public void receive(String value) { builder.append(value); } @Override public String toString() { return builder.toString(); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/StringUtils.java000066400000000000000000000465351421263112100301610ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import org.parboiled.support.Characters; import org.parboiled.support.Chars; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; /** * General utility methods for string manipulation. */ public final class StringUtils { private StringUtils() {} /** * Replaces carriage returns, newlines, tabs, formfeeds and the special chars defined in {@link Characters} * with their respective escape sequences. * * @param string the string * @return the escaped string */ public static String escape(String string) { if (isEmpty(string)) return ""; StringBuilder sb = new StringBuilder(); char[] chars = string.toCharArray(); for (int i = 0; i < chars.length; i++) { if (i == chars.length - 1 || chars[i] != '\r' || chars[i + 1] != '\n') { sb.append(escape(chars[i])); } } return sb.toString(); } /** * Replaces carriage returns, newlines, tabs, formfeeds and the special chars defined in {@link Characters} * with their respective escape sequences. * * @param c the character to escape * @return the escaped string */ public static String escape(char c) { switch (c) { case '\r': return "\\r"; case '\n': return "\\n"; case '\t': return "\\t"; case '\f': return "\\f"; case Chars.DEL_ERROR: return "DEL_ERROR"; case Chars.INS_ERROR: return "INS_ERROR"; case Chars.RESYNC: return "RESYNC"; case Chars.RESYNC_START: return "RESYNC_START"; case Chars.RESYNC_END: return "RESYNC_END"; case Chars.RESYNC_EOI: return "RESYNC_EOI"; case Chars.INDENT: return "INDENT"; case Chars.DEDENT: return "DEDENT"; case Chars.EOI: return "EOI"; default: return String.valueOf(c); } } /** * Creates a string consisting of n times the given character. * * @param c the char * @param n the number of times to repeat * @return the string */ public static String repeat(char c, int n) { char[] array = new char[n]; Arrays.fill(array, c); return String.valueOf(array); } //*********************************************************************************************** //** THE FOLLOWING CODE IS A PARTIAL, VERBATIM COPY OF ** //** org.apache.commons.lang.StringUtils ** //** which is licensed under ASF 2.0 ** //*********************************************************************************************** /** *

Joins the elements of the provided Iterable into * a single String containing the provided elements.

*

No delimiter is added before or after the list. * A null separator is the same as an empty String ("").

* * @param iterable the Iterable of values to join together, may be null * @param separator the separator character to use, null treated as "" * @return the joined String, null if null iterator input */ public static String join(Iterable iterable, String separator) { return iterable == null ? null : join(iterable.iterator(), separator); } /** *

Joins the elements of the provided Iterator into * a single String containing the provided elements.

*

No delimiter is added before or after the list. * A null separator is the same as an empty String ("").

* * @param iterator the Iterator of values to join together, may be null * @param separator the separator character to use, null treated as "" * @return the joined String, null if null iterator input */ public static String join(Iterator iterator, String separator) { // handle null, zero and one elements before building a buffer if (iterator == null) return null; if (!iterator.hasNext()) return ""; Object first = iterator.next(); if (!iterator.hasNext()) return Utils.toString(first); // two or more elements StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small if (first != null) buf.append(first); while (iterator.hasNext()) { if (separator != null) buf.append(separator); Object obj = iterator.next(); if (obj != null) buf.append(obj); } return buf.toString(); } /** *

Joins the elements of the provided array into a single String * containing the provided list of elements.

*

No delimiter is added before or after the list. * A null separator is the same as an empty String (""). * Null objects or empty strings within the array are represented by * empty strings.

* *
     * StringUtils.join(null, *)                = null
     * StringUtils.join([], *)                  = ""
     * StringUtils.join([null], *)              = ""
     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
     * StringUtils.join(["a", "b", "c"], null)  = "abc"
     * StringUtils.join(["a", "b", "c"], "")    = "abc"
     * StringUtils.join([null, "", "a"], ',')   = ",,a"
     * 
* * @param array the array of values to join together, may be null * @param separator the separator character to use, null treated as "" * @return the joined String, null if null array input */ public static String join(Object[] array, String separator) { return array == null ? null : join(array, separator, 0, array.length); } /** *

Joins the elements of the provided array into a single String * containing the provided list of elements.

*

No delimiter is added before or after the list. * A null separator is the same as an empty String (""). * Null objects or empty strings within the array are represented by * empty strings.

*
     * StringUtils.join(null, *)                = null
     * StringUtils.join([], *)                  = ""
     * StringUtils.join([null], *)              = ""
     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
     * StringUtils.join(["a", "b", "c"], null)  = "abc"
     * StringUtils.join(["a", "b", "c"], "")    = "abc"
     * StringUtils.join([null, "", "a"], ',')   = ",,a"
     * 
* * @param array the array of values to join together, may be null * @param separator the separator character to use, null treated as "" * @param startIndex the first index to start joining from. It is * an error to pass in an end index past the end of the array * @param endIndex the index to stop joining from (exclusive). It is * an error to pass in an end index past the end of the array * @return the joined String, null if null array input */ public static String join(Object[] array, String separator, int startIndex, int endIndex) { if (array == null) return null; if (separator == null) separator = ""; // lastIndex - firstIndex > 0: Len = NofStrings *(len(firstString) + len(separator)) // (Assuming that all Strings are roughly equally long) int bufSize = (endIndex - startIndex); if (bufSize <= 0) return ""; bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length()) + separator.length()); StringBuilder buf = new StringBuilder(bufSize); for (int i = startIndex; i < endIndex; i++) { if (i > startIndex) buf.append(separator); if (array[i] != null) buf.append(array[i]); } return buf.toString(); } // Empty checks //----------------------------------------------------------------------- /** *

Checks if a String is empty ("") or null.

*
     * StringUtils.isEmpty(null)      = true
     * StringUtils.isEmpty("")        = true
     * StringUtils.isEmpty(" ")       = false
     * StringUtils.isEmpty("bob")     = false
     * StringUtils.isEmpty("  bob  ") = false
     * 
* * @param str the String to check, may be null * @return true if the String is empty or null */ public static boolean isEmpty(String str) { return str == null || str.length() == 0; } /** *

Checks if a String is not empty ("") and not null.

*
     * StringUtils.isNotEmpty(null)      = false
     * StringUtils.isNotEmpty("")        = false
     * StringUtils.isNotEmpty(" ")       = true
     * StringUtils.isNotEmpty("bob")     = true
     * StringUtils.isNotEmpty("  bob  ") = true
     * 
* * @param str the String to check, may be null * @return true if the String is not empty and not null */ public static boolean isNotEmpty(String str) { return !isEmpty(str); } /** * Gets a String's length or 0 if the String is null. * * @param str a String or null * @return String length or 0 if the String is null. */ public static int length(String str) { return str == null ? 0 : str.length(); } /** *

Compares two Strings, returning true if they are equal ignoring * the case.

*

nulls are handled without exceptions. Two null * references are considered equal. Comparison is case insensitive.

* *
     * StringUtils.equalsIgnoreCase(null, null)   = true
     * StringUtils.equalsIgnoreCase(null, "abc")  = false
     * StringUtils.equalsIgnoreCase("abc", null)  = false
     * StringUtils.equalsIgnoreCase("abc", "abc") = true
     * StringUtils.equalsIgnoreCase("abc", "ABC") = true
     * 
* * @param str1 the first String, may be null * @param str2 the second String, may be null * @return true if the Strings are equal, case insensitive, or * both null */ public static boolean equalsIgnoreCase(String str1, String str2) { return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2); } /** * Test whether a string starts with a given prefix, handling null values without exceptions. * * StringUtils.startsWith(null, null) = false * StringUtils.startsWith(null, "abc") = false * StringUtils.startsWith("abc", null) = true * StringUtils.startsWith("abc", "ab") = true * StringUtils.startsWith("abc", "abc") = true * * @param string the string * @param prefix the prefix * @return true if string starts with prefix */ public static boolean startsWith(String string, String prefix) { return string != null && (prefix == null || string.startsWith(prefix)); } /** *

Gets a substring from the specified String avoiding exceptions.

*

A negative start position can be used to start n * characters from the end of the String.

*

A null String will return null. * An empty ("") String will return "".

*
     * StringUtils.substring(null, *)   = null
     * StringUtils.substring("", *)     = ""
     * StringUtils.substring("abc", 0)  = "abc"
     * StringUtils.substring("abc", 2)  = "c"
     * StringUtils.substring("abc", 4)  = ""
     * StringUtils.substring("abc", -2) = "bc"
     * StringUtils.substring("abc", -4) = "abc"
     * 
* * @param str the String to get the substring from, may be null * @param start the position to start from, negative means * count back from the end of the String by this many characters * @return substring from start position, null if null String input */ public static String substring(String str, int start) { if (str == null) { return null; } // handle negatives, which means last n characters if (start < 0) { start = str.length() + start; // remember start is negative } if (start < 0) { start = 0; } if (start > str.length()) { return ""; } return str.substring(start); } /** *

Gets a substring from the specified String avoiding exceptions.

*

A negative start position can be used to start/end n * characters from the end of the String.

*

The returned substring starts with the character in the start * position and ends before the end position. All position counting is * zero-based -- i.e., to start at the beginning of the string use * start = 0. Negative start and end positions can be used to * specify offsets relative to the end of the String.

*

If start is not strictly to the left of end, "" * is returned.

*
     * StringUtils.substring(null, *, *)    = null
     * StringUtils.substring("", * ,  *)    = "";
     * StringUtils.substring("abc", 0, 2)   = "ab"
     * StringUtils.substring("abc", 2, 0)   = ""
     * StringUtils.substring("abc", 2, 4)   = "c"
     * StringUtils.substring("abc", 4, 6)   = ""
     * StringUtils.substring("abc", 2, 2)   = ""
     * StringUtils.substring("abc", -2, -1) = "b"
     * StringUtils.substring("abc", -4, 2)  = "ab"
     * 
* * @param str the String to get the substring from, may be null * @param start the position to start from, negative means * count back from the end of the String by this many characters * @param end the position to end at (exclusive), negative means * count back from the end of the String by this many characters * @return substring from start position to end positon, * null if null String input */ public static String substring(String str, int start, int end) { if (str == null) { return null; } // handle negatives if (end < 0) { end = str.length() + end; // remember end is negative } if (start < 0) { start = str.length() + start; // remember start is negative } // check length next if (end > str.length()) { end = str.length(); } // if start is greater than end, return "" if (start > end) { return ""; } if (start < 0) { start = 0; } if (end < 0) { end = 0; } return str.substring(start, end); } // Left/Right/Mid //----------------------------------------------------------------------- /** *

Gets the leftmost len characters of a String.

*

If len characters are not available, or the * String is null, the String will be returned without * an exception. An exception is thrown if len is negative.

*
     * StringUtils.left(null, *)    = null
     * StringUtils.left(*, -ve)     = ""
     * StringUtils.left("", *)      = ""
     * StringUtils.left("abc", 0)   = ""
     * StringUtils.left("abc", 2)   = "ab"
     * StringUtils.left("abc", 4)   = "abc"
     * 
* * @param str the String to get the leftmost characters from, may be null * @param len the length of the required String, must be zero or positive * @return the leftmost characters, null if null String input */ public static String left(String str, int len) { if (str == null) { return null; } if (len < 0) { return ""; } if (str.length() <= len) { return str; } return str.substring(0, len); } /** *

Gets the rightmost len characters of a String.

*

If len characters are not available, or the String * is null, the String will be returned without an * an exception. An exception is thrown if len is negative.

*
     * StringUtils.right(null, *)    = null
     * StringUtils.right(*, -ve)     = ""
     * StringUtils.right("", *)      = ""
     * StringUtils.right("abc", 0)   = ""
     * StringUtils.right("abc", 2)   = "bc"
     * StringUtils.right("abc", 4)   = "abc"
     * 
* * @param str the String to get the rightmost characters from, may be null * @param len the length of the required String, must be zero or positive * @return the rightmost characters, null if null String input */ public static String right(String str, int len) { if (str == null) { return null; } if (len < 0) { return ""; } if (str.length() <= len) { return str; } return str.substring(str.length() - len); } /** *

Gets len characters from the middle of a String.

*

If len characters are not available, the remainder * of the String will be returned without an exception. If the * String is null, null will be returned. * An exception is thrown if len is negative.

*
     * StringUtils.mid(null, *, *)    = null
     * StringUtils.mid(*, *, -ve)     = ""
     * StringUtils.mid("", 0, *)      = ""
     * StringUtils.mid("abc", 0, 2)   = "ab"
     * StringUtils.mid("abc", 0, 4)   = "abc"
     * StringUtils.mid("abc", 2, 4)   = "c"
     * StringUtils.mid("abc", 4, 2)   = ""
     * StringUtils.mid("abc", -2, 2)  = "ab"
     * 
* * @param str the String to get the characters from, may be null * @param pos the position to start from, negative treated as zero * @param len the length of the required String, must be zero or positive * @return the middle characters, null if null String input */ public static String mid(String str, int pos, int len) { if (str == null) { return null; } if (len < 0 || pos > str.length()) { return ""; } if (pos < 0) { pos = 0; } if (str.length() <= (pos + len)) { return str.substring(pos); } return str.substring(pos, pos + len); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Tuple2.java000066400000000000000000000026121421263112100270310ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; public final class Tuple2 { public final A a; public final B b; public Tuple2(A a, B b) { this.a = a; this.b = b; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Tuple2)) return false; Tuple2 tuple2 = (Tuple2) o; return (a != null ? a.equals(tuple2.a) : tuple2.a == null) && (b != null ? b.equals(tuple2.b) : tuple2.b == null); } @Override public int hashCode() { int result = a != null ? a.hashCode() : 0; result = 31 * result + (b != null ? b.hashCode() : 0); return result; } @Override public String toString() { return "Tuple2{a=" + a + ", b=" + b + '}'; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Tuple3.java000066400000000000000000000031171421263112100270330ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; public final class Tuple3 { public final A a; public final B b; public final C c; public Tuple3(A a, B b, C c) { this.a = a; this.b = b; this.c = c; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Tuple3)) return false; Tuple3 tuple2 = (Tuple3) o; return (a != null ? a.equals(tuple2.a) : tuple2.a == null) && (b != null ? b.equals(tuple2.b) : tuple2.b == null) && (c != null ? c.equals(tuple2.c) : tuple2.c == null); } @Override public int hashCode() { int result = a != null ? a.hashCode() : 0; result = 31 * result + (b != null ? b.hashCode() : 0); result = 31 * result + (c != null ? c.hashCode() : 0); return result; } @Override public String toString() { return "Tuple3{a=" + a + ", b=" + b + ", c=" + c + '}'; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/common/Utils.java000066400000000000000000000370011421263112100267560ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import static org.parboiled.common.Preconditions.*; import org.parboiled.errors.GrammarException; import java.lang.reflect.*; import java.util.*; /** * General utility methods. */ public final class Utils { public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0]; public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = new Integer[0]; public static final Long[] EMPTY_LONG_OBJECT_ARRAY = new Long[0]; public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = new Short[0]; public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = new Byte[0]; public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = new Float[0]; public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = new Double[0]; public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = new Boolean[0]; private Utils() {} public static Character[] toObjectArray(char[] array) { if (array == null) return null; if (array.length == 0) return EMPTY_CHARACTER_OBJECT_ARRAY; Character[] result = new Character[array.length]; for (int i = 0; i < array.length; i++) result[i] = array[i]; return result; } public static Integer[] toObjectArray(int[] array) { if (array == null) return null; if (array.length == 0) return EMPTY_INTEGER_OBJECT_ARRAY; Integer[] result = new Integer[array.length]; for (int i = 0; i < array.length; i++) result[i] = array[i]; return result; } public static Long[] toObjectArray(long[] array) { if (array == null) return null; if (array.length == 0) return EMPTY_LONG_OBJECT_ARRAY; Long[] result = new Long[array.length]; for (int i = 0; i < array.length; i++) result[i] = array[i]; return result; } public static Short[] toObjectArray(short[] array) { if (array == null) return null; if (array.length == 0) return EMPTY_SHORT_OBJECT_ARRAY; Short[] result = new Short[array.length]; for (int i = 0; i < array.length; i++) result[i] = array[i]; return result; } public static Byte[] toObjectArray(byte[] array) { if (array == null) return null; if (array.length == 0) return EMPTY_BYTE_OBJECT_ARRAY; Byte[] result = new Byte[array.length]; for (int i = 0; i < array.length; i++) result[i] = array[i]; return result; } public static Float[] toObjectArray(float[] array) { if (array == null) return null; if (array.length == 0) return EMPTY_FLOAT_OBJECT_ARRAY; Float[] result = new Float[array.length]; for (int i = 0; i < array.length; i++) result[i] = array[i]; return result; } public static Double[] toObjectArray(double[] array) { if (array == null) return null; if (array.length == 0) return EMPTY_DOUBLE_OBJECT_ARRAY; Double[] result = new Double[array.length]; for (int i = 0; i < array.length; i++) result[i] = array[i]; return result; } public static Boolean[] toObjectArray(boolean[] array) { if (array == null) return null; if (array.length == 0) return EMPTY_BOOLEAN_OBJECT_ARRAY; Boolean[] result = new Boolean[array.length]; for (int i = 0; i < array.length; i++) result[i] = array[i]; return result; } /** * Joins the given arguments into one array. * * @param firstElement the first element * @param moreElements more elements (optional) * @return a new array containing all arguments. */ @SuppressWarnings({"unchecked"}) public static T[] arrayOf(T firstElement, T... moreElements) { checkArgNotNull(moreElements, "moreElements"); Class elementType = moreElements.getClass().getComponentType(); T[] array = (T[]) Array.newInstance(elementType, moreElements.length + 1); array[0] = firstElement; System.arraycopy(moreElements, 0, array, 1, moreElements.length); return array; } /** * Joins the given arguments into one array. * * @param firstElement the first element * @param secondElement the second element * @param moreElements more elements (optional) * @return a new array containing all arguments. */ @SuppressWarnings({"unchecked"}) public static T[] arrayOf(T firstElement, T secondElement, T... moreElements) { checkArgNotNull(moreElements, "moreElements"); Class elementType = moreElements.getClass().getComponentType(); T[] array = (T[]) Array.newInstance(elementType, moreElements.length + 2); array[0] = firstElement; array[1] = secondElement; System.arraycopy(moreElements, 0, array, 2, moreElements.length); return array; } /** * Joins the given arguments into one array. * * @param firstElements the first elements * @param lastElement the element to append * @return a new array containing all arguments. */ @SuppressWarnings({"unchecked"}) public static T[] arrayOf(T[] firstElements, T lastElement) { checkArgNotNull(firstElements, "firstElements"); Class elementType = firstElements.getClass().getComponentType(); T[] array = (T[]) Array.newInstance(elementType, firstElements.length + 1); System.arraycopy(firstElements, 0, array, 0, firstElements.length); array[firstElements.length] = lastElement; return array; } /** * Null enabled toString(). * * @param obj the object * @return the empty string of obj is null, otherwise obj.toString() */ public static String toString(Object obj) { return obj == null ? "" : obj.toString(); } /** * Null enabled equals(). * * @param a the first object * @param b the second object * @return true if both are null or both are equal */ public static boolean equal(T a, T b) { return a != null ? a.equals(b) : b == null; } /** * Gets the actual type arguments that are used in a given implementation of a given generic base class or interface. * (Based on code copyright 2007 by Ian Robertson). * * @param base the generic base class or interface * @param implementation the type (potentially) implementing the given base class or interface * @return a list of the raw classes for the actual type arguments. */ public static List> getTypeArguments(Class base, Class implementation) { checkArgNotNull(base, "base"); checkArgNotNull(implementation, "implementation"); Map resolvedTypes = new HashMap(); // first we need to resolve all supertypes up to the required base class or interface // and find the right Type for it Type type; Queue toCheck = new LinkedList(); toCheck.add(implementation); while (true) { // if we have checked everything and not found the base class we return an empty list if (toCheck.isEmpty()) return ImmutableList.of(); type = toCheck.remove(); Class clazz; if (type instanceof Class) { // there is no useful information for us in raw types, so just keep going up the inheritance chain clazz = (Class) type; if (base.isInterface()) { // if we are actually looking for the type parameters to an interface we also need to // look at all the ones implemented by the given current one toCheck.addAll(Arrays.asList(clazz.getGenericInterfaces())); } } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; clazz = (Class) parameterizedType.getRawType(); // for instances of ParameterizedType we extract and remember all type arguments TypeVariable[] typeParameters = clazz.getTypeParameters(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (int i = 0; i < actualTypeArguments.length; i++) { resolvedTypes.put(typeParameters[i], actualTypeArguments[i]); } } else { return ImmutableList.of(); } // we can stop if we have reached the sought for base type if (base.equals(getClass(type))) break; toCheck.add(clazz.getGenericSuperclass()); } // finally, for each actual type argument provided to baseClass, // determine (if possible) the raw class for that type argument. Type[] actualTypeArguments; if (type instanceof Class) { actualTypeArguments = ((Class) type).getTypeParameters(); } else { actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); } List> typeArgumentsAsClasses = new ArrayList>(); // resolve types by chasing down type variables. for (Type baseType : actualTypeArguments) { while (resolvedTypes.containsKey(baseType)) { baseType = resolvedTypes.get(baseType); } typeArgumentsAsClasses.add(getClass(baseType)); } return typeArgumentsAsClasses; } /** * Get the underlying class for a type, or null if the type is a variable type. * (Copyright 2007 by Ian Robertson). * * @param type the type * @return the underlying class */ public static Class getClass(Type type) { if (type instanceof Class) { return (Class) type; } else if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType()); } else if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType) type).getGenericComponentType(); Class componentClass = getClass(componentType); if (componentClass != null) { return Array.newInstance(componentClass, 0).getClass(); } } return null; } /** * Determines if the primitive type is boxed as the boxed type * @param primitive the primitive type to check if boxed is the boxed type * @param boxed the possible boxed type of the primitive * @return true if boxed is the boxed type of primitive, false otherwise. */ public static boolean isBoxedType(Class primitive, Class boxed) { return (primitive.equals(boolean.class) && boxed.equals(Boolean.class)) || (primitive.equals(byte.class) && boxed.equals(Byte.class)) || (primitive.equals(char.class) && boxed.equals(Character.class)) || (primitive.equals(double.class) && boxed.equals(Double.class)) || (primitive.equals(float.class) && boxed.equals(Float.class)) || (primitive.equals(int.class) && boxed.equals(Integer.class)) || (primitive.equals(long.class) && boxed.equals(Long.class)) || (primitive.equals(short.class) && boxed.equals(Short.class)) || (primitive.equals(void.class) && boxed.equals(Void.class)); } /** * Finds the constructor of the given class that is compatible with the given arguments. * * @param type the class to find the constructor of * @param args the arguments * @return the constructor */ public static Constructor findConstructor(Class type, Object[] args) { outer: for (Constructor constructor : type.getConstructors()) { Class[] paramTypes = constructor.getParameterTypes(); if (paramTypes.length != args.length) continue; for (int i = 0; i < args.length; i++) { Object arg = args[i]; if (arg != null && !paramTypes[i].isAssignableFrom(arg.getClass()) && !isBoxedType(paramTypes[i], arg.getClass())) continue outer; if (arg == null && paramTypes[i].isPrimitive()) continue outer; } return constructor; } throw new GrammarException("No constructor found for %s and the given %s arguments", type, args.length); } /** * Formats the given long value into a human readable notation using the Kilo, Mega, Giga, etc. abbreviations. * * @param value the value to format * @return the string representation */ public static String humanize(long value) { if (value < 0) { return '-' + humanize(-value); } else if (value > 1000000000000000000L) { return Double.toString( (value + 500000000000000L) / 1000000000000000L * 1000000000000000L / 1000000000000000000.0) + 'E'; } else if (value > 100000000000000000L) { return Double.toString( (value + 50000000000000L) / 100000000000000L * 100000000000000L / 1000000000000000.0) + 'P'; } else if (value > 10000000000000000L) { return Double .toString((value + 5000000000000L) / 10000000000000L * 10000000000000L / 1000000000000000.0) + 'P'; } else if (value > 1000000000000000L) { return Double .toString((value + 500000000000L) / 1000000000000L * 1000000000000L / 1000000000000000.0) + 'P'; } else if (value > 100000000000000L) { return Double.toString((value + 50000000000L) / 100000000000L * 100000000000L / 1000000000000.0) + 'T'; } else if (value > 10000000000000L) { return Double.toString((value + 5000000000L) / 10000000000L * 10000000000L / 1000000000000.0) + 'T'; } else if (value > 1000000000000L) { return Double.toString((value + 500000000) / 1000000000 * 1000000000 / 1000000000000.0) + 'T'; } else if (value > 100000000000L) { return Double.toString((value + 50000000) / 100000000 * 100000000 / 1000000000.0) + 'G'; } else if (value > 10000000000L) { return Double.toString((value + 5000000) / 10000000 * 10000000 / 1000000000.0) + 'G'; } else if (value > 1000000000) { return Double.toString((value + 500000) / 1000000 * 1000000 / 1000000000.0) + 'G'; } else if (value > 100000000) { return Double.toString((value + 50000) / 100000 * 100000 / 1000000.0) + 'M'; } else if (value > 10000000) { return Double.toString((value + 5000) / 10000 * 10000 / 1000000.0) + 'M'; } else if (value > 1000000) { return Double.toString((value + 500) / 1000 * 1000 / 1000000.0) + 'M'; } else if (value > 100000) { return Double.toString((value + 50) / 100 * 100 / 1000.0) + 'K'; } else if (value > 10000) { return Double.toString((value + 5) / 10 * 10 / 1000.0) + 'K'; } else if (value > 1000) { return Double.toString(value / 1000.0) + 'K'; } else { return Long.toString(value) + ' '; } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/000077500000000000000000000000001421263112100250365ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/ActionError.java000066400000000000000000000033731421263112100301360ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; import org.parboiled.buffers.InputBuffer; import org.parboiled.support.MatcherPath; import static org.parboiled.common.Preconditions.checkArgNotNull; /** * A {@link ParseError} wrapping an ActionException. */ public class ActionError extends BasicParseError { private final MatcherPath errorPath; private final ActionException actionException; public ActionError(InputBuffer inputBuffer, int errorIndex, String errorMessage, MatcherPath errorPath, ActionException actionException) { super(checkArgNotNull(inputBuffer, "inputBuffer"), errorIndex, errorMessage); this.errorPath = checkArgNotNull(errorPath, "errorPath"); this.actionException = checkArgNotNull(actionException, "actionException"); } /** * Gets the path to the matcher that caused this error. * * @return the MatcherPath */ public MatcherPath getErrorPath() { return errorPath; } /** * Gets the wrapped ActionException. * * @return the wrapped ActionException */ public ActionException getActionException() { return actionException; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/ActionException.java000066400000000000000000000025201421263112100307740ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; /** * Exception that can be thrown by parser actions to signal that an error has occurred that is to be collected * in the ParseErrors for the parsing run. Throwing an ActionException does not stop the parsing process. */ public class ActionException extends RuntimeException { public ActionException() { } public ActionException(String message) { super(message); } public ActionException(String message, Throwable cause) { super(message, cause); } public ActionException(Throwable cause, String message, Object... messageArgs) { super(String.format(message, messageArgs), cause); } public ActionException(Throwable cause) { super(cause); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/BasicParseError.java000066400000000000000000000035761421263112100307420ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; import static org.parboiled.common.Preconditions.*; import org.parboiled.buffers.InputBuffer; /** * A basic {@link ParseError} implementation for a one-char parse error with an optional error message. */ public class BasicParseError implements ParseError { private final InputBuffer inputBuffer; private final int startIndex; private final String errorMessage; private int endIndex; private int indexDelta; public BasicParseError(InputBuffer inputBuffer, int errorIndex, String errorMessage) { this.inputBuffer = checkArgNotNull(inputBuffer, "inputBuffer"); this.startIndex = errorIndex; this.endIndex = errorIndex + 1; this.errorMessage = errorMessage; } public InputBuffer getInputBuffer() { return inputBuffer; } public int getStartIndex() { return startIndex + indexDelta; } public int getEndIndex() { return endIndex + indexDelta; } public void setEndIndex(int endIndex) { this.endIndex = endIndex - indexDelta; } public String getErrorMessage() { return errorMessage; } public int getIndexDelta() { return indexDelta; } public void shiftIndexDeltaBy(int delta) { this.indexDelta += delta; } } DefaultInvalidInputErrorFormatter.java000066400000000000000000000111121421263112100344270ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; import org.parboiled.common.Formatter; import org.parboiled.common.StringUtils; import org.parboiled.matchers.AnyOfMatcher; import org.parboiled.matchers.Matcher; import org.parboiled.support.Chars; import org.parboiled.support.MatcherPath; import java.util.ArrayList; import java.util.List; /** * A {@link Formatter} for {@link InvalidInputError}s that automatically creates the correct "expected" text * for the error. */ public class DefaultInvalidInputErrorFormatter implements Formatter { public String format(InvalidInputError error) { if (error == null) return ""; int len = error.getEndIndex() - error.getStartIndex(); StringBuilder sb = new StringBuilder(); if (len > 0) { char c = error.getInputBuffer().charAt(error.getStartIndex()); if (c == Chars.EOI) { sb.append("Unexpected end of input"); } else { sb.append("Invalid input '") .append(StringUtils.escape(String.valueOf(c))); if (len > 1) sb.append("..."); sb.append('\''); } } else { sb.append("Invalid input"); } String expectedString = getExpectedString(error); if (StringUtils.isNotEmpty(expectedString)) { sb.append(", expected ").append(expectedString); } return sb.toString(); } public String getExpectedString(InvalidInputError error) { // In non recovery-mode there is no complexity in the error and start indices since they are all stable. // However, in recovery-mode the RecoveringParseRunner inserts characters into the InputBuffer, which requires // for all indices taken before to be shifted. The RecoveringParseRunner does this by changing the indexDelta // of the parse runner. All users of the ParseError will then automatically see shifted start and end indices // matching the state of the underlying InputBuffer. However, since the failed MatcherPaths still carry the // "original" indices we need to unapply the IndexDelta in order to be able to compare with them. int pathStartIndex = error.getStartIndex() - error.getIndexDelta(); List labelList = new ArrayList(); for (MatcherPath path : error.getFailedMatchers()) { Matcher labelMatcher = ErrorUtils.findProperLabelMatcher(path, pathStartIndex); if (labelMatcher == null) continue; String[] labels = getLabels(labelMatcher); for (String label : labels) { if (label != null && !labelList.contains(label)) { labelList.add(label); } } } return join(labelList); } /** * Gets the labels corresponding to the given matcher, AnyOfMatchers are treated specially in that their * label is constructed as a list of their contents * * @param matcher the matcher * @return the labels */ public String[] getLabels(Matcher matcher) { if ((matcher instanceof AnyOfMatcher) && ((AnyOfMatcher)matcher).characters.toString().equals(matcher.getLabel())) { AnyOfMatcher cMatcher = (AnyOfMatcher) matcher; if (!cMatcher.characters.isSubtractive()) { String[] labels = new String[cMatcher.characters.getChars().length]; for (int i = 0; i < labels.length; i++) { labels[i] = '\'' + String.valueOf(cMatcher.characters.getChars()[i]) + '\''; } return labels; } } return new String[] {matcher.getLabel()}; } public String join(List labelList) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < labelList.size(); i++) { if (i > 0) sb.append(i < labelList.size() - 1 ? ", " : " or "); sb.append(labelList.get(i)); } return StringUtils.escape(sb.toString()); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/ErrorUtils.java000066400000000000000000000160111421263112100300120ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; import static org.parboiled.common.Preconditions.*; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.Formatter; import org.parboiled.common.StringUtils; import org.parboiled.matchers.Matcher; import org.parboiled.matchers.TestNotMatcher; import org.parboiled.support.MatcherPath; import org.parboiled.support.ParsingResult; import org.parboiled.support.Position; import java.util.List; /** * General utility methods regarding parse errors. */ public final class ErrorUtils { private ErrorUtils() {} /** * Finds the Matcher in the given failedMatcherPath whose label is best for presentation in "expected" strings * of parse error messages, given the provided lastMatchPath. * * @param path the path to the failed matcher * @param errorIndex the start index of the respective parse error * @return the matcher whose label is best for presentation in "expected" strings */ static Matcher findProperLabelMatcher(MatcherPath path, int errorIndex) { try { return findProperLabelMatcher0(path, errorIndex); } catch(RuntimeException e) { if (e == UnderneathTestNot) return null; else throw e; } } private static RuntimeException UnderneathTestNot = new RuntimeException() { @Override public synchronized Throwable fillInStackTrace() { return this; } }; private static Matcher findProperLabelMatcher0(MatcherPath path, int errorIndex) { checkArgNotNull(path, "path"); Matcher found = path.parent != null ? findProperLabelMatcher0(path.parent, errorIndex) : null; if (found != null) return found; Matcher m = path.element.matcher; if (m instanceof TestNotMatcher) throw UnderneathTestNot; if (path.element.startIndex == errorIndex && m.hasCustomLabel()) return m; return null; } /** * Pretty prints the parse errors of the given ParsingResult showing their location in the given input buffer. * * @param parsingResult the parsing result * @return the pretty print text */ public static String printParseErrors(ParsingResult parsingResult) { checkArgNotNull(parsingResult, "parsingResult"); return printParseErrors(parsingResult.parseErrors); } /** * Pretty prints the given parse errors showing their location in the given input buffer. * * @param errors the parse errors * @return the pretty print text */ public static String printParseErrors(List errors) { checkArgNotNull(errors, "errors"); StringBuilder sb = new StringBuilder(); for (ParseError error : errors) { if (sb.length() > 0) sb.append("---\n"); sb.append(printParseError(error)); } return sb.toString(); } /** * Pretty prints the given parse error showing its location in the given input buffer. * * @param error the parse error * @return the pretty print text */ public static String printParseError(ParseError error) { checkArgNotNull(error, "error"); return printParseError(error, new DefaultInvalidInputErrorFormatter()); } /** * Pretty prints the given parse error showing its location in the given input buffer. * * @param error the parse error * @param formatter the formatter for InvalidInputErrors * @return the pretty print text */ public static String printParseError(ParseError error, Formatter formatter) { checkArgNotNull(error, "error"); checkArgNotNull(formatter, "formatter"); String message = error.getErrorMessage() != null ? error.getErrorMessage() : error instanceof InvalidInputError ? formatter.format((InvalidInputError) error) : error.getClass().getSimpleName(); return printErrorMessage("%s (line %s, pos %s):", message, error.getStartIndex(), error.getEndIndex(), error.getInputBuffer()); } /** * Prints an error message showing a location in the given InputBuffer. * * @param format the format string, must include three placeholders for a string * (the error message) and two integers (the error line / column respectively) * @param errorMessage the error message * @param errorIndex the error location as an index into the inputBuffer * @param inputBuffer the underlying InputBuffer * @return the error message including the relevant line from the underlying input plus location indicator */ public static String printErrorMessage(String format, String errorMessage, int errorIndex, InputBuffer inputBuffer) { checkArgNotNull(inputBuffer, "inputBuffer"); return printErrorMessage(format, errorMessage, errorIndex, errorIndex + 1, inputBuffer); } /** * Prints an error message showing a location in the given InputBuffer. * * @param format the format string, must include three placeholders for a string * (the error message) and two integers (the error line / column respectively) * @param errorMessage the error message * @param startIndex the start location of the error as an index into the inputBuffer * @param endIndex the end location of the error as an index into the inputBuffer * @param inputBuffer the underlying InputBuffer * @return the error message including the relevant line from the underlying input plus location indicators */ public static String printErrorMessage(String format, String errorMessage, int startIndex, int endIndex, InputBuffer inputBuffer) { checkArgNotNull(inputBuffer, "inputBuffer"); checkArgument(startIndex <= endIndex); Position pos = inputBuffer.getPosition(startIndex); StringBuilder sb = new StringBuilder(String.format(format, errorMessage, pos.line, pos.column)); sb.append('\n'); String line = inputBuffer.extractLine(pos.line); sb.append(line); sb.append('\n'); int charCount = Math.max(Math.min(endIndex - startIndex, StringUtils.length(line) - pos.column + 2), 1); for (int i = 0; i < pos.column - 1; i++) sb.append(' '); for (int i = 0; i < charCount; i++) sb.append('^'); sb.append("\n"); return sb.toString(); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/GrammarException.java000066400000000000000000000025461421263112100311550ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; /** * Exception thrown by parboiled if a problem with the integrity of the grammar rules has been detected. */ public class GrammarException extends RuntimeException { public GrammarException() { } public GrammarException(String message) { super(message); } public GrammarException(String message, Throwable cause) { super(message, cause); } public GrammarException(String message, Object... messageArgs) { super(String.format(message, messageArgs)); } public GrammarException(Throwable cause, String message, Object... messageArgs) { super(String.format(message, messageArgs), cause); } public GrammarException(Throwable cause) { super(cause); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/IllegalIndentationException.java000066400000000000000000000026001421263112100333240ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.StringUtils; import org.parboiled.support.Position; /** * Exception thrown by the IndentDedentInputbuffer upon detection of an illegal indentation. */ public class IllegalIndentationException extends RuntimeException { public final InputBuffer buffer; public final Position position; public IllegalIndentationException(InputBuffer buffer, Position position) { this.buffer = buffer; this.position = position; } @Override public String getMessage() { return "Illegal indentation in line " + position.line + ":\n" + buffer.extractLine(position.line) + '\n' + StringUtils.repeat('^', position.column - 1) + '\n'; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/InvalidInputError.java000066400000000000000000000032651421263112100313270ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; import static org.parboiled.common.Preconditions.*; import org.parboiled.buffers.InputBuffer; import org.parboiled.support.MatcherPath; import java.util.List; /** * A {@link ParseError} describing one or more input characters that are illegal with regard to the underlying * language grammar. */ public class InvalidInputError extends BasicParseError { private final List failedMatchers; public InvalidInputError(InputBuffer inputBuffer, int startIndex, List failedMatchers, String errorMessage) { super(checkArgNotNull(inputBuffer, "inputBuffer"), startIndex, errorMessage); this.failedMatchers = checkArgNotNull(failedMatchers, "failedMatchers"); } /** * Gets the list of paths to the single character matchers that failed at the error location of this error. * * @return the list of paths to the single character matchers that failed at the error location of this error */ public List getFailedMatchers() { return failedMatchers; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/ParseError.java000066400000000000000000000030031421263112100277610ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; import org.parboiled.buffers.InputBuffer; /** * Common interface of all parboiled parse error implementations. */ public interface ParseError { /** * Gets the inputbuffer this error occurred in. * * @return the inputbuffer */ InputBuffer getInputBuffer(); /** * Gets the start index of the parse error in the underlying input buffer. * * @return the input index of the first character covered by this error */ int getStartIndex(); /** * Gets the end index of the parse error in the underlying input buffer. * * @return the end index of this error, i.e. the index of the character immediately following the last character * covered by this error */ int getEndIndex(); /** * An optional error message. * * @return an optional error message. */ String getErrorMessage(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/ParserRuntimeException.java000066400000000000000000000026641421263112100323700ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; /** * Exception that is thrown for any problem during the parsing run that cannot be overcome automatically. */ public class ParserRuntimeException extends RuntimeException { public ParserRuntimeException() { } public ParserRuntimeException(String message) { super(message); } public ParserRuntimeException(String message, Throwable cause) { super(message, cause); } public ParserRuntimeException(String message, Object... messageArgs) { super(String.format(message, messageArgs)); } public ParserRuntimeException(Throwable cause, String message, Object... messageArgs) { super(messageArgs.length > 0 ? String.format(message, messageArgs) : message, cause); } public ParserRuntimeException(Throwable cause) { super(cause); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/errors/ParsingException.java000066400000000000000000000026641421263112100311730ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.errors; /** * Exception type not directly used by parboiled but included as a convenience base class for custom exceptions * (or to be used directly if no custom exception types are required). */ public class ParsingException extends RuntimeException { public ParsingException() { } public ParsingException(String message) { super(message); } public ParsingException(String message, Throwable cause) { super(message, cause); } public ParsingException(String message, Object... messageArgs) { super(String.format(message, messageArgs)); } public ParsingException(Throwable cause, String message, Object... messageArgs) { super(String.format(message, messageArgs), cause); } public ParsingException(Throwable cause) { super(cause); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/000077500000000000000000000000001421263112100253305ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/AbstractMatcher.java000066400000000000000000000075731421263112100312560ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.common.ImmutableList; import org.parboiled.common.Utils; import org.parboiled.trees.ImmutableGraphNode; /** * Abstract base class of most regular {@link Matcher}s. */ public abstract class AbstractMatcher extends ImmutableGraphNode implements Matcher, Cloneable { private String label; private boolean nodeSuppressed; private boolean subnodesSuppressed; private boolean nodeSkipped; private Object tag; public AbstractMatcher(String label) { this(new Rule[0], label); } public AbstractMatcher(Rule subRule, String label) { this(new Rule[] {checkArgNotNull(subRule, "subRule")}, label); } public AbstractMatcher(Rule[] subRules, String label) { super(ImmutableList.of(toMatchers(checkArgNotNull(subRules, "subRules")))); this.label = label; } private static Matcher[] toMatchers(Rule[] subRules) { Matcher[] matchers = new Matcher[subRules.length]; for (int i = 0; i < subRules.length; i++) { matchers[i] = (Matcher) subRules[i]; } return matchers; } public boolean isNodeSuppressed() { return nodeSuppressed; } public boolean areSubnodesSuppressed() { return subnodesSuppressed; } public boolean isNodeSkipped() { return nodeSkipped; } public boolean areMismatchesMemoed() { return false; } public String getLabel() { return label; } public boolean hasCustomLabel() { // this is the default implementation for single character matchers // complex matchers override with a custom implementation return true; } @Override public String toString() { return getLabel(); } public AbstractMatcher label(String label) { if (Utils.equal(label, this.label)) return this; AbstractMatcher clone = createClone(); clone.label = label; return clone; } public Rule suppressNode() { if (nodeSuppressed) return this; AbstractMatcher clone = createClone(); clone.nodeSuppressed = true; return clone; } public Rule suppressSubnodes() { if (subnodesSuppressed) return this; AbstractMatcher clone = createClone(); clone.subnodesSuppressed = true; return clone; } public Rule skipNode() { if (nodeSkipped) return this; AbstractMatcher clone = createClone(); clone.nodeSkipped = true; return clone; } public Rule memoMismatches() { return new MemoMismatchesMatcher(this); } public Object getTag() { return tag; } public void setTag(Object tagObject) { tag = tagObject; } // default implementation is to simply delegate to the context public MatcherContext getSubContext(MatcherContext context) { return context.getSubContext(this); } // creates a shallow copy private AbstractMatcher createClone() { try { return (AbstractMatcher) clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException(); } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/ActionMatcher.java000066400000000000000000000120611421263112100307140ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.*; import org.parboiled.errors.ActionError; import org.parboiled.errors.ActionException; import org.parboiled.matchervisitors.MatcherVisitor; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; /** * A {@link org.parboiled.matchers.Matcher} that not actually matches input but runs a given parser {@link Action}. */ public class ActionMatcher extends AbstractMatcher { public final Action action; public final List contextAwares = new ArrayList(); public final boolean skipInPredicates; public ActionMatcher(Action action) { super(checkArgNotNull(action, "action").toString()); this.action = action; skipInPredicates = action instanceof SkippableAction && ((SkippableAction) action).skipInPredicates(); // check whether the action is a synthetic class generated by parboiled transformation // if so it will take care of context management itself and we can return immediately if (action.getClass().isSynthetic()) return; if (action instanceof ContextAware) { contextAwares.add((ContextAware) action); } // in order to make anonymous inner classes and other member classes work seamlessly // we collect the synthetic references to the outer parent classes and inform them of // the current parsing context if they implement ContextAware for (Field field : action.getClass().getDeclaredFields()) { if (field.isSynthetic() && ContextAware.class.isAssignableFrom(field.getType())) { field.setAccessible(true); try { ContextAware contextAware = (ContextAware) field.get(action); if (contextAware != null) contextAwares.add(contextAware); } catch (IllegalAccessException e) { // ignore } finally { field.setAccessible(false); } } } } @Override public MatcherContext getSubContext(MatcherContext context) { MatcherContext subContext = context.getBasicSubContext(); subContext.setMatcher(this); if (context.getCurrentIndex() > context.getStartIndex()) { // if we have already matched something we must be in a sequence at the second or later position // the subcontext contains match data that the action might want to access, so we use the existing // subcontext without reinitializing return subContext; } else { return context.getSubContext(this); } } @SuppressWarnings({"unchecked"}) public boolean match(MatcherContext context) { if (skipInPredicates && context.inPredicate()) return true; // actions need to run in the parent context MatcherContext parentContext = context.getParent(); if (!contextAwares.isEmpty()) { for (ContextAware contextAware : contextAwares) { contextAware.setContext(parentContext); } } try { Object valueStackSnapshot = context.getValueStack().takeSnapshot(); if (!action.run(parentContext)) { // failing actions are not allowed to change the ValueStack context.getValueStack().restoreSnapshot(valueStackSnapshot); return false; } // since we initialize the actions own context only partially in getSubContext(MatcherContext) // (in order to be able to still access the previous subcontexts fields in action expressions) // we need to make sure to not accidentally advance the current index of our parent with some old // index from a previous subcontext, so we explicitly set the marker here context.setCurrentIndex(parentContext.getCurrentIndex()); return true; } catch (ActionException e) { context.getParseErrors().add(new ActionError(context.getInputBuffer(), context.getCurrentIndex(), e.getMessage(), context.getPath(), e)); return false; } } @Override public Rule suppressNode() { return this; // actions are already "suppressNode" } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/AnyMatcher.java000066400000000000000000000031241421263112100302260ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.matchervisitors.MatcherVisitor; import org.parboiled.support.Chars; /** * A {@link org.parboiled.matchers.Matcher} matching any single character except EOI. */ public class AnyMatcher extends AbstractMatcher { public AnyMatcher() { super("ANY"); } public boolean match(MatcherContext context) { switch (context.getCurrentChar()) { case Chars.DEL_ERROR: case Chars.INS_ERROR: case Chars.RESYNC: case Chars.RESYNC_START: case Chars.RESYNC_END: case Chars.RESYNC_EOI: case Chars.EOI: return false; } context.advanceIndex(1); context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/AnyOfMatcher.java000066400000000000000000000031171421263112100305150ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.support.Characters; import org.parboiled.matchervisitors.MatcherVisitor; /** * A {@link Matcher} matching a single character out of a given {@link org.parboiled.support.Characters} set. */ public class AnyOfMatcher extends AbstractMatcher { public final Characters characters; public AnyOfMatcher(Characters characters) { super(checkArgNotNull(characters, "characters").toString()); checkArgument(!characters.equals(Characters.NONE)); this.characters = characters; } public boolean match(MatcherContext context) { if (!characters.contains(context.getCurrentChar())) return false; context.advanceIndex(1); context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/CharIgnoreCaseMatcher.java000066400000000000000000000032571421263112100323230ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import static org.parboiled.common.StringUtils.escape; import org.parboiled.MatcherContext; import org.parboiled.matchervisitors.MatcherVisitor; /** * A {@link Matcher} matching a single character case-independently. */ public class CharIgnoreCaseMatcher extends AbstractMatcher { public final char charLow; public final char charUp; public CharIgnoreCaseMatcher(char character) { super('\'' + escape(Character.toLowerCase(character)) + '/' + escape(Character.toUpperCase(character)) + '\''); this.charLow = Character.toLowerCase(character); this.charUp = Character.toUpperCase(character); } public boolean match(MatcherContext context) { char c = context.getCurrentChar(); if (c != charLow && c != charUp) return false; context.advanceIndex(1); context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/CharMatcher.java000066400000000000000000000037041421263112100303600ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import org.parboiled.MatcherContext; import org.parboiled.matchervisitors.MatcherVisitor; import org.parboiled.support.Chars; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.common.StringUtils.escape; /** * A {@link Matcher} matching a single given character. */ public class CharMatcher extends AbstractMatcher { public final char character; public CharMatcher(char character) { super(getLabel(character)); this.character = character; } private static String getLabel(char c) { switch (c) { case Chars.DEL_ERROR: case Chars.INS_ERROR: case Chars.RESYNC: case Chars.RESYNC_START: case Chars.RESYNC_END: case Chars.RESYNC_EOI: case Chars.INDENT: case Chars.DEDENT: case Chars.EOI: return escape(c); default: return '\'' + escape(c) + '\''; } } public boolean match(MatcherContext context) { if (context.getCurrentChar() != character) return false; context.advanceIndex(1); context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/CharRangeMatcher.java000066400000000000000000000031311421263112100313270ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import static org.parboiled.common.StringUtils.escape; import org.parboiled.MatcherContext; import org.parboiled.matchervisitors.MatcherVisitor; /** * A {@link Matcher} matching a single character out of a given range of characters. */ public class CharRangeMatcher extends AbstractMatcher { public final char cLow; public final char cHigh; public CharRangeMatcher(char cLow, char cHigh) { super(escape(cLow) + ".." + escape(cHigh)); checkArgument(cLow < cHigh); this.cLow = cLow; this.cHigh = cHigh; } public boolean match(MatcherContext context) { char c = context.getCurrentChar(); if (c < cLow || c > cHigh) return false; context.advanceIndex(1); context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/CustomDefaultLabelMatcher.java000066400000000000000000000027371421263112100332270ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import org.parboiled.Rule; abstract class CustomDefaultLabelMatcher> extends AbstractMatcher { private String defaultLabel; protected CustomDefaultLabelMatcher(Rule subRule, String defaultLabel) { super(subRule, null); this.defaultLabel = defaultLabel; } protected CustomDefaultLabelMatcher(Rule[] subRules, String defaultLabel) { super(subRules, null); this.defaultLabel = defaultLabel; } @Override public String getLabel() { return hasCustomLabel() ? super.getLabel() : defaultLabel; } @Override public boolean hasCustomLabel() { return super.getLabel() != null; } @SuppressWarnings( {"unchecked"}) public T defaultLabel(String defaultLabel) { this.defaultLabel = defaultLabel; return (T)this; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/CustomMatcher.java000066400000000000000000000053411421263112100307540ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; /** * Base class of custom matcher implementations. If you want to implement custom matchers they have to be derived * from this class. Instances of derived classes can be directly used in rule defining expressions. * Caution: For performance reasons the parsing engine delegates the task of taking and restoring value stack * snapshots to the matchers. If your custom matcher can run parser actions underneath it your custom matcher * implementation therefore has to take care of value stack managment itselves! * (See the implementation of the SequenceMatcher for hints on how to do this!) */ public abstract class CustomMatcher extends AbstractMatcher { protected CustomMatcher(String label) { super(label); } protected CustomMatcher(Rule subRule, String label) { super(checkArgNotNull(subRule, "subRule"), label); } protected CustomMatcher(Rule[] subRules, String label) { super(checkArgNotNull(subRules, "subRules"), label); } /** * Determines whether this matcher instance always matches exactly one character. * * @return true if this matcher always matches exactly one character */ public abstract boolean isSingleCharMatcher(); /** * Determines whether this matcher instance allows empty matches. * * @return true if this matcher instance allows empty matches */ public abstract boolean canMatchEmpty(); /** * Determines whether this matcher instance can start a match with the given char. * * @param c the char * @return true if this matcher instance can start a match with the given char. */ public abstract boolean isStarterChar(char c); /** * Returns one of possibly several chars that a match can start with. * * @return a starter char */ public abstract char getStarterChar(); public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/EmptyMatcher.java000066400000000000000000000022771421263112100306050ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.matchervisitors.MatcherVisitor; /** * A {@link Matcher} that always successfully matches nothing. */ public class EmptyMatcher extends AbstractMatcher { public EmptyMatcher() { super("EMPTY"); } public boolean match(MatcherContext context) { context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/FirstOfMatcher.java000066400000000000000000000033341421263112100310560ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; import java.util.List; /** * A {@link Matcher} trying all of its submatchers in sequence and succeeding when the first submatcher succeeds. */ public class FirstOfMatcher extends CustomDefaultLabelMatcher { public FirstOfMatcher(Rule[] subRules) { super(checkArgNotNull(subRules, "subRules"), "FirstOf"); } @SuppressWarnings( {"ForLoopReplaceableByForEach"}) public boolean match(MatcherContext context) { List children = getChildren(); int size = children.size(); for (int i = 0; i < size; i++) { Matcher matcher = children.get(i); if (matcher.getSubContext(context).runMatcher()) { context.createNode(); return true; } } return false; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/FirstOfStringsMatcher.java000066400000000000000000000132161421263112100324300ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.errors.GrammarException; import org.parboiled.buffers.InputBuffer; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; /** * A specialized FirstOfMatcher that handles FirstOf(string, string, ...) rules much faster that the regular * FirstOfMatcher. If fast string matching is enabled this matcher uses a prebuilt character tree to efficiently * determine whether the next input characters match the rule expression. */ public class FirstOfStringsMatcher extends FirstOfMatcher { // a node in the character tree static class Record { final char[] chars; // the sub characters of this node final Record[] subs; // the sub records corresponding to the respective character final boolean complete; // flag indicating that the path up to this record also constitutes a valid match private Record(char[] chars, Record[] subs, boolean complete) { this.chars = chars; this.subs = subs; this.complete = complete; } } private final Record root; // the root of the character tree public final char[][] strings; public FirstOfStringsMatcher(Rule[] subRules, char[][] strings) { super(checkArgNotNull(subRules, "subRules")); verify(strings); this.strings = strings; root = createRecord(0, strings); } @Override public boolean match(MatcherContext context) { if (!context.fastStringMatching()) { return super.match(context); } Record rec = root; int ix = context.getCurrentIndex(); InputBuffer buffer = context.getInputBuffer(); char c = context.getCurrentChar(); int endIx = -1; loop: while (true) { char[] chars = rec.chars; for (int i = 0; i < chars.length; i++) { if (c == chars[i]) { ix++; rec = rec.subs[i]; if (rec == null) { // success, we complected a tree path to a leave endIx = ix; break loop; } if (rec.complete) { // we completed a valid match path, but continue looking for a longer match endIx = ix; } c = buffer.charAt(ix); continue loop; } } // we checked all sub branches of the current node, none matched, so we are done break; } if (endIx == -1) return false; // we matched no complete path, so fail context.advanceIndex(endIx - context.getCurrentIndex()); context.createNode(); return true; } static Record createRecord(int pos, char[][] strings) { Map> map = new TreeMap>(); boolean complete = false; for (char[] s : strings) { if (s.length == pos) complete = true; if (s.length <= pos) continue; char c = s[pos]; Set charStrings = map.get(c); if (charStrings == null) { charStrings = new HashSet(); map.put(c, charStrings); } charStrings.add(s); } if (map.isEmpty()) return null; char[] chars = new char[map.size()]; Record[] subs = new Record[map.size()]; int i = 0; for (Map.Entry> entry : map.entrySet()) { chars[i] = entry.getKey(); subs[i++] = createRecord(pos + 1, entry.getValue().toArray(new char[entry.getValue().size()][])); } return new Record(chars, subs, complete); } // make sure that a string is no prefix of another string later in the array // this would cause the second string to never match without fast-string-matching, // but match in the fast implementation private static void verify(char[][] strings) { int length = strings.length; for (int i = 0; i < length; i++) { char[] a = strings[i]; inner: for (int j = i + 1; j < length; j++) { char[] b = strings[j]; if (b.length < a.length) continue; for (int k = 0; k < a.length; k++) { if (a[k] != b[k]) continue inner; } String sa = '"' + String.valueOf(a) + '"'; String sb = '"' + String.valueOf(b) + '"'; String msg = a.length == b.length ? sa + " is specified twice in a FirstOf(String...)" : sa + " is a prefix of " + sb + " in a FirstOf(String...) and comes before " + sb + ", which prevents " + sb + " from ever matching! You should reverse the order of the two alternatives."; throw new GrammarException(msg); } } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/Matcher.java000066400000000000000000000061611421263112100275620ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; import org.parboiled.trees.GraphNode; /** * A Matcher instance is responsible for "executing" a specific Rule instance, i.e. it implements the actual * rule type specific matching logic. * Since it extends the {@link GraphNode} interface it can have submatchers. */ public interface Matcher extends Rule, GraphNode { /** * @return the label of the matcher (which is identical to the label of the Rule this matcher matches) */ String getLabel(); /** * @return true if this matcher has been assigned a custom label */ boolean hasCustomLabel(); /** * @return true if this matcher has been marked with @SuppressNode */ boolean isNodeSuppressed(); /** * @return true if this matcher has been marked with @SuppressSubnodes */ boolean areSubnodesSuppressed(); /** * @return true if this matcher has been marked with @SkipNode */ boolean isNodeSkipped(); /** * @return true if this matcher has been marked with @MemoMismatches */ boolean areMismatchesMemoed(); /** * Creates a context for the matching of this matcher using the given parent context. * * @param context the parent context * @return the context this matcher is to be run in */ MatcherContext getSubContext(MatcherContext context); /** * Tries a match on the given MatcherContext. * * @param context the MatcherContext * @return true if the match was successful */ boolean match(MatcherContext context); /** * Associates an arbitrary object with this matcher. Used for example during profiling and packrat parsing. * The matcher implementations themselves completely ignore the contents of this property. It purely serves as a * performance optimization for ParseRunners and/or MatchHandlers and saves these from the need to use * Map<Matcher, XYZ> structures for associating internal objects with matchers. * * @param tagObject the tag object */ void setTag(Object tagObject); /** * Retrieves a previously set tag object. * * @return the tag object or null if none set */ Object getTag(); /** * Accepts the given matcher visitor. * * @param visitor the visitor * @return the value returned by the given visitor */ R accept(MatcherVisitor visitor); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/MatcherUtils.java000066400000000000000000000021071421263112100305770ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; public final class MatcherUtils { private MatcherUtils() {} public static Matcher unwrap(Matcher matcher) { if (matcher instanceof ProxyMatcher) return unwrap(ProxyMatcher.unwrap(matcher)); if (matcher instanceof VarFramingMatcher) return unwrap(VarFramingMatcher.unwrap(matcher)); if (matcher instanceof MemoMismatchesMatcher) return unwrap(MemoMismatchesMatcher.unwrap(matcher)); return matcher; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/MemoMismatchesMatcher.java000066400000000000000000000070741421263112100324220ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; import java.util.List; /** * Special wrapping matcher that performs memoization of the last mismatch of the wrapped sub rule. */ public class MemoMismatchesMatcher implements Matcher { private final Matcher inner; public MemoMismatchesMatcher(Rule inner) { this.inner = checkArgNotNull((Matcher) inner, "inner"); } @SuppressWarnings({"unchecked"}) public boolean match(MatcherContext context) { if (context.hasMismatched()) { return false; } if (inner.match(context)) { return true; } context.memoizeMismatch(); return false; } // GraphNode public List getChildren() { return inner.getChildren(); } // Rule public Rule label(String label) { return new MemoMismatchesMatcher(inner.label(label)); } public Rule suppressNode() { return new MemoMismatchesMatcher(inner.suppressNode()); } public Rule suppressSubnodes() { return new MemoMismatchesMatcher(inner.suppressSubnodes()); } public Rule skipNode() { return new MemoMismatchesMatcher(inner.skipNode()); } public Rule memoMismatches() { return this; // already done } // Matcher public String getLabel() {return inner.getLabel();} public boolean hasCustomLabel() {return inner.hasCustomLabel();} public boolean isNodeSuppressed() {return inner.isNodeSuppressed();} public boolean areSubnodesSuppressed() {return inner.areSubnodesSuppressed();} public boolean isNodeSkipped() {return inner.isNodeSkipped();} public boolean areMismatchesMemoed() { return true; } public void setTag(Object tagObject) { inner.setTag(tagObject); } public Object getTag() { return inner.getTag(); } public MatcherContext getSubContext(MatcherContext context) { MatcherContext subContext = inner.getSubContext(context); subContext.setMatcher(this); // we need to inject ourselves here otherwise we get cut out return subContext; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return inner.accept(visitor); } @Override public String toString() { return inner.toString(); } /** * Retrieves the innermost Matcher that is not a MemoMismatchesMatcher. * * @param matcher the matcher to unwrap * @return the given instance if it is not a MemoMismatchesMatcher, otherwise the innermost Matcher */ public static Matcher unwrap(Matcher matcher) { if (matcher instanceof MemoMismatchesMatcher) { MemoMismatchesMatcher memoMismatchesMatcher = (MemoMismatchesMatcher) matcher; return unwrap(memoMismatchesMatcher.inner); } return matcher; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/NothingMatcher.java000066400000000000000000000022621421263112100311070ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.matchervisitors.MatcherVisitor; /** * A {@link org.parboiled.matchers.Matcher} that never matches anything. */ public class NothingMatcher extends AbstractMatcher { public NothingMatcher() { super("NOTHING"); } public boolean match(MatcherContext context) { return false; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/OneOrMoreMatcher.java000066400000000000000000000041171421263112100313470ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.errors.GrammarException; import org.parboiled.matchervisitors.MatcherVisitor; /** * A {@link Matcher} that repeatedly tries its submatcher against the input. * Succeeds if its submatcher succeeds at least once. */ public class OneOrMoreMatcher extends CustomDefaultLabelMatcher { public final Matcher subMatcher; public OneOrMoreMatcher(Rule subRule) { super(checkArgNotNull(subRule, "subRule"), "OneOrMore"); this.subMatcher = getChildren().get(0); } public boolean match(MatcherContext context) { boolean matched = subMatcher.getSubContext(context).runMatcher(); if (!matched) return false; // collect all further matches as well int lastIndex = context.getCurrentIndex(); while (subMatcher.getSubContext(context).runMatcher()) { int currentIndex = context.getCurrentIndex(); if (currentIndex == lastIndex) { throw new GrammarException("The inner rule of OneOrMore rule '%s' must not allow empty matches", context.getPath()); } lastIndex = currentIndex; } context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/OptionalMatcher.java000066400000000000000000000027301421263112100312660ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; /** * A {@link Matcher} that tries its submatcher once against the input and always succeeds. */ public class OptionalMatcher extends CustomDefaultLabelMatcher { public final Matcher subMatcher; public OptionalMatcher(Rule subRule) { super(checkArgNotNull(subRule, "subRule"), "Optional"); this.subMatcher = getChildren().get(0); } public boolean match(MatcherContext context) { subMatcher.getSubContext(context).runMatcher(); context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/ProxyMatcher.java000066400000000000000000000174561421263112100306350ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; import java.util.List; /** * A {@link Matcher} that delegates all {@link Rule} and {@link Matcher} interface methods to another {@link Matcher}. * It can also hold a label and a leaf marker and lazily apply these to the underlying {@link Matcher} once it is available. */ public class ProxyMatcher implements Matcher, Cloneable { private Matcher target; private String label; private boolean nodeSuppressed; private boolean subnodesSuppressed; private boolean nodeSkipped; private boolean memoMismatches; private boolean dirty; public List getChildren() { if (dirty) apply(); return target.getChildren(); } public void setLabel(String label) { this.label = label; updateDirtyFlag(); } private void setNodeSuppressed(boolean nodeSuppressed) { this.nodeSuppressed = nodeSuppressed; updateDirtyFlag(); } private void setSubnodesSuppressed(boolean subnodesSuppressed) { this.subnodesSuppressed = subnodesSuppressed; updateDirtyFlag(); } private void setNodeSkipped(boolean nodeSkipped) { this.nodeSkipped = nodeSkipped; updateDirtyFlag(); } private void setMemoMismatches(boolean memoMismatches) { this.memoMismatches = memoMismatches; updateDirtyFlag(); } private void updateDirtyFlag() { dirty = label != null || nodeSuppressed || subnodesSuppressed || nodeSkipped || memoMismatches; } public boolean match(MatcherContext context) { if (dirty) apply(); return target.match(context); } public String getLabel() { if (dirty) apply(); return target.getLabel(); } public boolean hasCustomLabel() { if (dirty) apply(); return target.hasCustomLabel(); } public boolean isNodeSuppressed() { if (dirty) apply(); return target.isNodeSuppressed(); } public boolean areSubnodesSuppressed() { if (dirty) apply(); return target.areSubnodesSuppressed(); } public boolean isNodeSkipped() { if (dirty) apply(); return target.isNodeSkipped(); } public boolean areMismatchesMemoed() { if (dirty) apply(); return target.areMismatchesMemoed(); } public void setTag(Object tagObject) { if (dirty) apply(); target.setTag(tagObject); } public Object getTag() { if (dirty) apply(); return target.getTag(); } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); if (dirty) apply(); return target.accept(visitor); } @Override public String toString() { if (target == null) return super.toString(); if (dirty) apply(); return target.toString(); } private void apply() { if (label != null) label(label); if (nodeSuppressed) suppressNode(); if (subnodesSuppressed) suppressSubnodes(); if (nodeSkipped) skipNode(); } public Rule label(String label) { if (target == null) { // if we have no target yet we need to save the label and "apply" it later if (this.label == null) { setLabel(label); return this; } // this proxy matcher is already waiting for its label application opportunity, // so we need to create another proxy level ProxyMatcher anotherProxy = createClone(); anotherProxy.setLabel(label); anotherProxy.arm(this); return anotherProxy; } // we already have a target to which we can directly apply the label Rule inner = unwrap(target); target = (Matcher) inner.label(label); // since relabelling might change the instance we have to update it setLabel(null); return target; } public Rule suppressNode() { if (target == null) { // if we have no target yet we need to save the marker and "apply" it later setNodeSuppressed(true); return this; } // we already have a target to which we can directly apply the marker Rule inner = unwrap(target); target = (Matcher) inner.suppressNode(); // since this might change the instance we have to update it setNodeSuppressed(false); return target; } public Rule suppressSubnodes() { if (target == null) { // if we have no target yet we need to save the marker and "apply" it later setSubnodesSuppressed(true); return this; } // we already have a target to which we can directly apply the marker Rule inner = unwrap(target); target = (Matcher) inner.suppressSubnodes(); // since this might change the instance we have to update it setSubnodesSuppressed(false); return target; } public Rule skipNode() { if (target == null) { // if we have no target yet we need to save the marker and "apply" it later setNodeSkipped(true); return this; } // we already have a target to which we can directly apply the marker Rule inner = unwrap(target); target = (Matcher) inner.skipNode(); // since this might change the instance we have to update it setNodeSkipped(false); return target; } public Rule memoMismatches() { if (target == null) { // if we have no target yet we need to save the marker and "apply" it later setMemoMismatches(true); return this; } // we already have a target to which we can directly apply the marker Rule inner = unwrap(target); target = (Matcher) inner.memoMismatches(); // since this might change the instance we have to update it setMemoMismatches(false); return target; } /** * Supplies this ProxyMatcher with its underlying delegate. * * @param target the Matcher to delegate to */ public void arm(Matcher target) { this.target = checkArgNotNull(target, "target"); } /** * Retrieves the innermost Matcher that is not a ProxyMatcher. * * @param matcher the matcher to unwrap * @return the given instance if it is not a ProxyMatcher, otherwise the innermost non-proxy Matcher */ public static Matcher unwrap(Matcher matcher) { if (matcher instanceof ProxyMatcher) { ProxyMatcher proxyMatcher = (ProxyMatcher) matcher; if (proxyMatcher.dirty) proxyMatcher.apply(); return proxyMatcher.target; } return matcher; } public MatcherContext getSubContext(MatcherContext context) { if (dirty) apply(); return target.getSubContext(context); } // creates a shallow copy private ProxyMatcher createClone() { try { return (ProxyMatcher) clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException(); } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/SequenceMatcher.java000066400000000000000000000040571421263112100312550ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; import java.util.List; /** * A {@link Matcher} that executes all of its submatchers in sequence and only succeeds if all submatchers succeed. */ public class SequenceMatcher extends CustomDefaultLabelMatcher { public SequenceMatcher(Rule[] subRules) { super(checkArgNotNull(subRules, "subRules"), "Sequence"); } public boolean match(MatcherContext context) { Object valueStackSnapshot = context.getValueStack().takeSnapshot(); List children = getChildren(); int size = children.size(); for (int i = 0; i < size; i++) { Matcher matcher = children.get(i); // remember the current index in the context, so we can access it for building the current follower set context.setIntTag(i); if (!matcher.getSubContext(context).runMatcher()) { // rule failed, so invalidate all stack actions the rule might have done context.getValueStack().restoreSnapshot(valueStackSnapshot); return false; } } context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/StringMatcher.java000066400000000000000000000033561421263112100307540ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; /** * A {@link SequenceMatcher} specialization for sequences of CharMatchers. Performs fast string matching if the * current context has it enabled. */ public class StringMatcher extends SequenceMatcher { public final char[] characters; public StringMatcher(Rule[] charMatchers, char[] characters) { super(checkArgNotNull(charMatchers, "charMatchers")); this.characters = characters; } @Override public String getLabel() { return super.getLabel() != null ? super.getLabel() : '"' + String.valueOf(characters) + '"'; } @Override public boolean hasCustomLabel() { return true; } @Override public boolean match(MatcherContext context) { if (!context.fastStringMatching()) { return super.match(context); } if (!context.getInputBuffer().test(context.getCurrentIndex(), characters)) return false; context.advanceIndex(characters.length); context.createNode(); return true; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/TestMatcher.java000066400000000000000000000036101421263112100304160ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; /** * A special {@link Matcher} not actually matching any input but rather trying its submatcher against the current input * position. Succeeds if the submatcher would succeed. */ public class TestMatcher extends CustomDefaultLabelMatcher { public final Matcher subMatcher; public TestMatcher(Rule subRule) { super(checkArgNotNull(subRule, "subRule"), "Test"); this.subMatcher = getChildren().get(0); } public boolean match(MatcherContext context) { int lastIndex = context.getCurrentIndex(); Object valueStackSnapshot = context.getValueStack().takeSnapshot(); if (!subMatcher.getSubContext(context).runMatcher()) return false; // reset location, Test matchers never advance context.setCurrentIndex(lastIndex); // erase all value stack changes the the submatcher could have made context.getValueStack().restoreSnapshot(valueStackSnapshot); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/TestNotMatcher.java000066400000000000000000000036201421263112100311000ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; /** * A special {@link Matcher} not actually matching any input but rather trying its submatcher against the current input * position. Succeeds if the submatcher would fail. */ public class TestNotMatcher extends CustomDefaultLabelMatcher { public final Matcher subMatcher; public TestNotMatcher(Rule subRule) { super(checkArgNotNull(subRule, "subRule"), "TestNot"); this.subMatcher = getChildren().get(0); } public boolean match(MatcherContext context) { int lastIndex = context.getCurrentIndex(); Object valueStackSnapshot = context.getValueStack().takeSnapshot(); if (subMatcher.getSubContext(context).runMatcher()) return false; // reset location, Test matchers never advance context.setCurrentIndex(lastIndex); // erase all value stack changes the the submatcher could have made context.getValueStack().restoreSnapshot(valueStackSnapshot); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/VarFramingMatcher.java000066400000000000000000000074211421263112100315370ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.matchervisitors.MatcherVisitor; import org.parboiled.support.Var; import java.util.List; /** * Special wrapping matcher that manages the creation and destruction of execution frames for a number of action vars. */ public class VarFramingMatcher implements Matcher { private final Matcher inner; private final Var[] variables; public VarFramingMatcher(Rule inner, Var[] variables) { this.inner = checkArgNotNull((Matcher)inner, "inner"); this.variables = checkArgNotNull(variables, "variables"); } public boolean match(MatcherContext context) { for (Var var : variables) { var.enterFrame(); } boolean matched = inner.match(context); for (Var var : variables) { var.exitFrame(); } return matched; } // GraphNode public List getChildren() { return inner.getChildren(); } // Rule public Rule label(String label) { return new VarFramingMatcher(inner.label(label), variables); } public Rule suppressNode() { return new VarFramingMatcher(inner.suppressNode(), variables); } public Rule suppressSubnodes() { return new VarFramingMatcher(inner.suppressSubnodes(), variables); } public Rule skipNode() { return new VarFramingMatcher(inner.skipNode(), variables); } public Rule memoMismatches() { return new VarFramingMatcher(inner.memoMismatches(), variables); } // Matcher public String getLabel() {return inner.getLabel();} public boolean hasCustomLabel() {return inner.hasCustomLabel();} public boolean isNodeSuppressed() {return inner.isNodeSuppressed();} public boolean areSubnodesSuppressed() {return inner.areSubnodesSuppressed();} public boolean isNodeSkipped() {return inner.isNodeSkipped();} public boolean areMismatchesMemoed() { return inner.areMismatchesMemoed(); } public void setTag(Object tagObject) { inner.setTag(tagObject); } public Object getTag() { return inner.getTag(); } public MatcherContext getSubContext(MatcherContext context) { MatcherContext subContext = inner.getSubContext(context); subContext.setMatcher(this); // we need to inject ourselves here otherwise we get cut out return subContext; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return inner.accept(visitor); } @Override public String toString() { return inner.toString(); } /** * Retrieves the innermost Matcher that is not a VarFramingMatcher. * * @param matcher the matcher to unwrap * @return the given instance if it is not a VarFramingMatcher, otherwise the innermost Matcher */ public static Matcher unwrap(Matcher matcher) { if (matcher instanceof VarFramingMatcher) { VarFramingMatcher varFramingMatcher = (VarFramingMatcher) matcher; return unwrap(varFramingMatcher.inner); } return matcher; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchers/ZeroOrMoreMatcher.java000066400000000000000000000037101421263112100315430ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchers; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.errors.GrammarException; import org.parboiled.matchervisitors.MatcherVisitor; /** * A {@link Matcher} that repeatedly tries its submatcher against the input. Always succeeds. */ public class ZeroOrMoreMatcher extends CustomDefaultLabelMatcher { public final Matcher subMatcher; public ZeroOrMoreMatcher(Rule subRule) { super(checkArgNotNull(subRule, "subRule"), "ZeroOrMore"); this.subMatcher = getChildren().get(0); } public boolean match(MatcherContext context) { checkArgNotNull(context, "context"); int lastIndex = context.getCurrentIndex(); while (subMatcher.getSubContext(context).runMatcher()) { int currentLocation = context.getCurrentIndex(); if (currentLocation == lastIndex) { throw new GrammarException("The inner rule of ZeroOrMore rule '%s' must not allow empty matches", context.getPath()); } lastIndex = currentLocation; } context.createNode(); return true; } public R accept(MatcherVisitor visitor) { checkArgNotNull(visitor, "visitor"); return visitor.visit(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/000077500000000000000000000000001421263112100267505ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/CanMatchEmptyVisitor.java000066400000000000000000000053561421263112100337010ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchervisitors; import org.parboiled.matchers.ActionMatcher; import org.parboiled.matchers.*; import org.parboiled.support.Checks; /** * A {@link MatcherVisitor} determining whether a matcher can legally succeed with an empty match. */ public class CanMatchEmptyVisitor implements MatcherVisitor { public Boolean visit(ActionMatcher matcher) { return true; } public Boolean visit(AnyMatcher matcher) { return false; } public Boolean visit(CharIgnoreCaseMatcher matcher) { return false; } public Boolean visit(CharMatcher matcher) { return false; } public Boolean visit(CharRangeMatcher matcher) { return false; } public Boolean visit(AnyOfMatcher matcher) { return false; } public Boolean visit(CustomMatcher matcher) { return matcher.canMatchEmpty(); } public Boolean visit(EmptyMatcher matcher) { return true; } public Boolean visit(FirstOfMatcher matcher) { for (Matcher child : matcher.getChildren()) { if (child.accept(this)) return true; } return false; } public Boolean visit(NothingMatcher matcher) { return false; } public Boolean visit(OneOrMoreMatcher matcher) { Checks.ensure(!matcher.subMatcher.accept(this), "Rule '%s' must not allow empty matches as sub-rule of an OneOrMore-rule", matcher.subMatcher); return false; } public Boolean visit(OptionalMatcher matcher) { return true; } public Boolean visit(SequenceMatcher matcher) { for (Matcher child : matcher.getChildren()) { if (!child.accept(this)) return false; } return true; } public Boolean visit(TestMatcher matcher) { return true; } public Boolean visit(TestNotMatcher matcher) { return true; } public Boolean visit(ZeroOrMoreMatcher matcher) { Checks.ensure(!matcher.subMatcher.accept(this), "Rule '%s' must not allow empty matches as sub-rule of an ZeroOrMore-rule", matcher.subMatcher); return true; } } DefaultMatcherVisitor.java000066400000000000000000000052521421263112100340100ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchervisitors; import org.parboiled.matchers.ActionMatcher; import org.parboiled.matchers.*; /** * A basic {@link MatcherVisitor} implementation that delegates all visiting methods to one default value method. * * @param the return value of this visitor */ public class DefaultMatcherVisitor implements MatcherVisitor { public R visit(ActionMatcher matcher) { return defaultValue(matcher); } public R visit(AnyMatcher matcher) { return defaultValue(matcher); } public R visit(CharIgnoreCaseMatcher matcher) { return defaultValue(matcher); } public R visit(CharMatcher matcher) { return defaultValue(matcher); } public R visit(CharRangeMatcher matcher) { return defaultValue(matcher); } public R visit(AnyOfMatcher matcher) { return defaultValue(matcher); } public R visit(CustomMatcher matcher) { return defaultValue(matcher); } public R visit(EmptyMatcher matcher) { return defaultValue(matcher); } public R visit(FirstOfMatcher matcher) { return defaultValue(matcher); } public R visit(NothingMatcher matcher) { return defaultValue(matcher); } public R visit(OneOrMoreMatcher matcher) { return defaultValue(matcher); } public R visit(OptionalMatcher matcher) { return defaultValue(matcher); } public R visit(SequenceMatcher matcher) { return defaultValue(matcher); } public R visit(TestMatcher matcher) { return defaultValue(matcher); } public R visit(TestNotMatcher matcher) { return defaultValue(matcher); } public R visit(ZeroOrMoreMatcher matcher) { return defaultValue(matcher); } /** * Returns the default value for all visiting methods that have not been overridden. * * @param matcher the matcher * @return the return value (null by default) */ @SuppressWarnings({"UnusedDeclaration"}) public R defaultValue(AbstractMatcher matcher) { return null; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/DoWithMatcherVisitor.java000066400000000000000000000072751421263112100337100ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchervisitors; import org.parboiled.matchers.*; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.parboiled.common.Preconditions.checkArgNotNull; /** * A MatcherVisitor that executes a given {@link Action} against a whole matcher hierarchy in a depth-first manner. * Potential cycles are detected and not rerun. */ public class DoWithMatcherVisitor extends DefaultMatcherVisitor { public interface Action { void process(Matcher matcher); } private final Action action; private final Set visited = new HashSet(); public DoWithMatcherVisitor(Action action) { this.action = checkArgNotNull(action, "action"); } @Override public Void visit(FirstOfMatcher matcher) { if (!visited.contains(matcher)) { visited.add(matcher); List children = matcher.getChildren(); for (int i = 0, childrenSize = children.size(); i < childrenSize; i++) { Matcher sub = children.get(i); sub.accept(this); } action.process(matcher); } return null; } @Override public Void visit(SequenceMatcher matcher) { if (!visited.contains(matcher)) { visited.add(matcher); List children = matcher.getChildren(); for (int i = 0, childrenSize = children.size(); i < childrenSize; i++) { Matcher sub = children.get(i); sub.accept(this); } action.process(matcher); } return null; } @Override public Void visit(OneOrMoreMatcher matcher) { if (!visited.contains(matcher)) { visited.add(matcher); matcher.subMatcher.accept(this); action.process(matcher); } return null; } @Override public Void visit(OptionalMatcher matcher) { if (!visited.contains(matcher)) { visited.add(matcher); matcher.subMatcher.accept(this); action.process(matcher); } return null; } @Override public Void visit(TestMatcher matcher) { if (!visited.contains(matcher)) { visited.add(matcher); matcher.subMatcher.accept(this); action.process(matcher); } return null; } @Override public Void visit(TestNotMatcher matcher) { if (!visited.contains(matcher)) { visited.add(matcher); matcher.subMatcher.accept(this); action.process(matcher); } return null; } @Override public Void visit(ZeroOrMoreMatcher matcher) { if (!visited.contains(matcher)) { visited.add(matcher); matcher.subMatcher.accept(this); action.process(matcher); } return null; } @Override public Void defaultValue(AbstractMatcher matcher) { if (!visited.contains(matcher)) { visited.add(matcher); action.process(matcher); } return null; } } FollowMatchersVisitor.java000066400000000000000000000047021421263112100340500ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchervisitors; import org.parboiled.MatcherContext; import org.parboiled.matchers.*; import org.parboiled.support.Chars; import java.util.ArrayList; import java.util.List; /** * Collects the matchers that can legally follow the given matcher according to the grammar into a given * list. The visitor returns true if the collected matchers are all possible followers, and false if other matchers * higher up the rule stack can also follow. */ public class FollowMatchersVisitor extends DefaultMatcherVisitor { private final CanMatchEmptyVisitor canMatchEmptyVisitor = new CanMatchEmptyVisitor(); private final List followMatchers = new ArrayList(); private MatcherContext context; public List getFollowMatchers(MatcherContext currentContext) { followMatchers.clear(); context = currentContext.getParent(); while (context != null) { boolean complete = context.getMatcher().accept(this); if (complete) return followMatchers; context = context.getParent(); } return followMatchers; } @Override public Boolean visit(OneOrMoreMatcher matcher) { followMatchers.add(matcher.subMatcher); return false; } @Override public Boolean visit(SequenceMatcher matcher) { for (int i = context.getIntTag() + 1; i < matcher.getChildren().size(); i++) { Matcher child = matcher.getChildren().get(i); followMatchers.add(child); if (!child.accept(canMatchEmptyVisitor)) return true; } return false; } @Override public Boolean visit(ZeroOrMoreMatcher matcher) { followMatchers.add(matcher.subMatcher); return false; } @Override public Boolean defaultValue(AbstractMatcher matcher) { return false; } } GetStarterCharVisitor.java000066400000000000000000000040121421263112100337730ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchervisitors; import org.parboiled.matchers.*; import org.parboiled.support.Characters; import java.util.Random; /** * Returns the first character a given matcher can start a match with. * For all complex matchers, i.e. the ones not always matching just one character, the visitor returns null. */ public class GetStarterCharVisitor extends DefaultMatcherVisitor { @Override public Character visit(AnyMatcher matcher) { return 'X'; } @Override public Character visit(AnyOfMatcher matcher) { Characters characters = matcher.characters; if (!characters.isSubtractive()) { return characters.getChars()[0]; } // for substractive sets we try to randomly choose a fitting character Random random = new Random(); char c; do { c = (char) random.nextInt(Character.MAX_VALUE); } while (!Character.isDefined(c) || !characters.contains(c)); return c; } @Override public Character visit(CharIgnoreCaseMatcher matcher) { return matcher.charLow; } @Override public Character visit(CharMatcher matcher) { return matcher.character; } @Override public Character visit(CharRangeMatcher matcher) { return matcher.cLow; } @Override public Character visit(CustomMatcher matcher) { return matcher.getStarterChar(); } } IsSingleCharMatcherVisitor.java000066400000000000000000000042361421263112100347400ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchervisitors; import org.parboiled.matchers.ActionMatcher; import org.parboiled.matchers.*; /** * A {@link MatcherVisitor} determining whether a matcher is a basic single character matcher. */ public class IsSingleCharMatcherVisitor implements MatcherVisitor { public Boolean visit(ActionMatcher matcher) { return false; } public Boolean visit(AnyMatcher matcher) { return true; } public Boolean visit(CharIgnoreCaseMatcher matcher) { return true; } public Boolean visit(CharMatcher matcher) { return true; } public Boolean visit(CharRangeMatcher matcher) { return true; } public Boolean visit(AnyOfMatcher matcher) { return true; } public Boolean visit(CustomMatcher matcher) { return matcher.isSingleCharMatcher(); } public Boolean visit(EmptyMatcher matcher) { return false; } public Boolean visit(FirstOfMatcher matcher) { return false; } public Boolean visit(NothingMatcher matcher) { return false; } public Boolean visit(OneOrMoreMatcher matcher) { return false; } public Boolean visit(OptionalMatcher matcher) { return false; } public Boolean visit(SequenceMatcher matcher) { return false; } public Boolean visit(TestMatcher matcher) { return false; } public Boolean visit(TestNotMatcher matcher) { return false; } public Boolean visit(ZeroOrMoreMatcher matcher) { return false; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/IsStarterCharVisitor.java000066400000000000000000000057571421263112100337270ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchervisitors; import org.parboiled.matchers.ActionMatcher; import org.parboiled.matchers.*; import org.parboiled.support.Chars; /** * A {@link MatcherVisitor} determining whether a matcher can start a match with a given char. */ public class IsStarterCharVisitor implements MatcherVisitor { private final CanMatchEmptyVisitor canMatchEmptyVisitor = new CanMatchEmptyVisitor(); private final char starterChar; public IsStarterCharVisitor(char starterChar) { this.starterChar = starterChar; } public Boolean visit(ActionMatcher matcher) { return false; } public Boolean visit(AnyMatcher matcher) { return starterChar != Chars.EOI; } public Boolean visit(CharIgnoreCaseMatcher matcher) { return matcher.charLow == starterChar || matcher.charUp == starterChar; } public Boolean visit(CharMatcher matcher) { return matcher.character == starterChar; } public Boolean visit(CharRangeMatcher matcher) { return matcher.cLow <= starterChar && starterChar <= matcher.cHigh; } public Boolean visit(AnyOfMatcher matcher) { return matcher.characters.contains(starterChar); } public Boolean visit(CustomMatcher matcher) { return matcher.isStarterChar(starterChar); } public Boolean visit(EmptyMatcher matcher) { return false; } public Boolean visit(FirstOfMatcher matcher) { for (Matcher child : matcher.getChildren()) { if (child.accept(this)) return true; } return false; } public Boolean visit(NothingMatcher matcher) { return false; } public Boolean visit(OneOrMoreMatcher matcher) { return matcher.subMatcher.accept(this); } public Boolean visit(OptionalMatcher matcher) { return matcher.subMatcher.accept(this); } public Boolean visit(SequenceMatcher matcher) { for (Matcher child : matcher.getChildren()) { if (child.accept(this)) return true; if (!child.accept(canMatchEmptyVisitor)) break; } return false; } public Boolean visit(TestMatcher matcher) { return matcher.subMatcher.accept(this); } public Boolean visit(TestNotMatcher matcher) { return false; } public Boolean visit(ZeroOrMoreMatcher matcher) { return matcher.subMatcher.accept(this); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/matchervisitors/MatcherVisitor.java000066400000000000000000000030501421263112100325540ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.matchervisitors; import org.parboiled.matchers.*; /** * The interface to be implemented by all visitors of {@link org.parboiled.matchers.Matcher}s. * * @param the return value of this visitor * @see Visitor Pattern on Wikipedia */ public interface MatcherVisitor { R visit(ActionMatcher matcher); R visit(AnyMatcher matcher); R visit(CharIgnoreCaseMatcher matcher); R visit(CharMatcher matcher); R visit(CustomMatcher matcher); R visit(CharRangeMatcher matcher); R visit(AnyOfMatcher matcher); R visit(EmptyMatcher matcher); R visit(FirstOfMatcher matcher); R visit(NothingMatcher matcher); R visit(OneOrMoreMatcher matcher); R visit(OptionalMatcher matcher); R visit(SequenceMatcher matcher); R visit(TestMatcher matcher); R visit(TestNotMatcher matcher); R visit(ZeroOrMoreMatcher matcher); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/000077500000000000000000000000001421263112100262515ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/AbstractParseRunner.java000066400000000000000000000064121421263112100330470ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import org.parboiled.MatchHandler; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.buffers.DefaultInputBuffer; import org.parboiled.buffers.InputBuffer; import org.parboiled.errors.ParseError; import org.parboiled.matchers.Matcher; import org.parboiled.support.DefaultValueStack; import org.parboiled.support.ParsingResult; import org.parboiled.support.ValueStack; import java.util.ArrayList; import java.util.List; import static org.parboiled.common.Preconditions.checkArgNotNull; public abstract class AbstractParseRunner implements ParseRunner { private final Matcher rootMatcher; private List parseErrors; private ValueStack valueStack; private Object initialValueStackSnapshot; public AbstractParseRunner(Rule rule) { this.rootMatcher = checkArgNotNull((Matcher) rule, "rule"); } public Matcher getRootMatcher() { return rootMatcher; } public ParseRunner withParseErrors(List parseErrors) { this.parseErrors = parseErrors; return this; } public List getParseErrors() { if (parseErrors == null) { withParseErrors(new ArrayList()); } return parseErrors; } public ParseRunnerwithValueStack(ValueStack valueStack) { this.valueStack = checkArgNotNull(valueStack, "valueStack"); this.initialValueStackSnapshot = valueStack.takeSnapshot(); return this; } public ValueStack getValueStack() { if (valueStack == null) { withValueStack(new DefaultValueStack()); } return valueStack; } public ParsingResult run(String input) { checkArgNotNull(input, "input"); return run(input.toCharArray()); } public ParsingResult run(char[] input) { checkArgNotNull(input, "input"); return run(new DefaultInputBuffer(input)); } protected void resetValueStack() { getValueStack().restoreSnapshot(initialValueStackSnapshot); } protected MatcherContext createRootContext(InputBuffer inputBuffer, MatchHandler matchHandler, boolean fastStringMatching) { return new MatcherContext(inputBuffer, getValueStack(), getParseErrors(), matchHandler, rootMatcher, fastStringMatching); } protected ParsingResult createParsingResult(boolean matched, MatcherContext rootContext) { return new ParsingResult(matched, rootContext.getNode(), getValueStack(), getParseErrors(), rootContext.getInputBuffer()); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/BasicParseRunner.java000066400000000000000000000057741421263112100323370ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import static org.parboiled.common.Preconditions.*; import org.parboiled.MatchHandler; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.buffers.DefaultInputBuffer; import org.parboiled.buffers.InputBuffer; import org.parboiled.errors.ParseError; import org.parboiled.matchers.Matcher; import org.parboiled.support.*; import java.util.ArrayList; import java.util.List; /** * The most basic of all {@link ParseRunner} implementations. It runs a rule against a given input text and builds a * corresponding {@link ParsingResult} instance. However, it does not report any parse errors nor recover from them. * Instead it simply marks the ParsingResult as "unmatched" if the input is not valid with regard to the rule grammar. * It never causes the parser to perform more than one parsing run and is the fastest way to determine * whether a given input conforms to the rule grammar. */ public class BasicParseRunner extends AbstractParseRunner implements MatchHandler { /** * Create a new BasicParseRunner instance with the given rule and input text and returns the result of * its {@link #run(String)} method invocation. * * @param rule the parser rule to run * @param input the input text to run on * @return the ParsingResult for the parsing run * @deprecated As of 0.11.0 you should use the "regular" constructor and one of the "run" methods rather than * this static method. This method will be removed in one of the coming releases. */ @Deprecated public static ParsingResult run(Rule rule, String input) { checkArgNotNull(rule, "rule"); checkArgNotNull(input, "input"); return new BasicParseRunner(rule).run(input); } /** * Creates a new BasicParseRunner instance for the given rule. * * @param rule the parser rule */ public BasicParseRunner(Rule rule) { super(rule); } public ParsingResult run(InputBuffer inputBuffer) { checkArgNotNull(inputBuffer, "inputBuffer"); resetValueStack(); MatcherContext rootContext = createRootContext(inputBuffer, this, true); boolean matched = rootContext.runMatcher(); return createParsingResult(matched, rootContext); } public boolean match(MatcherContext context) { return context.getMatcher().match(context); } } ErrorLocatingParseRunner.java000066400000000000000000000065201421263112100337770ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import org.parboiled.MatchHandler; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.buffers.InputBuffer; import org.parboiled.errors.BasicParseError; import org.parboiled.errors.InvalidInputError; import org.parboiled.matchers.TestNotMatcher; import org.parboiled.support.ParsingResult; import static org.parboiled.common.Preconditions.checkArgNotNull; /** * A {@link ParseRunner} implementation that creates a simple {@link BasicParseError} for the first error found in the * input and adds it to the list of ParseErrors. * It never causes the parser to perform more than one parsing run and is rarely used directly. * Instead its functionality is relied upon by the {@link ReportingParseRunner} and {@link RecoveringParseRunner} classes. */ public class ErrorLocatingParseRunner extends AbstractParseRunner implements MatchHandler { private final MatchHandler inner; private int errorIndex; /** * Creates a new ErrorLocatingParseRunner instance for the given rule. * * @param rule the parser rule */ public ErrorLocatingParseRunner(Rule rule) { this(rule, null); } /** * Creates a new ErrorLocatingParseRunner instance for the given rule. * The given MatchHandler is used as a delegate for the actual match handling. * * @param rule the parser rule * @param inner another MatchHandler to delegate the actual match handling to, can be null */ public ErrorLocatingParseRunner(Rule rule, MatchHandler inner) { super(rule); this.inner = inner; } public ParsingResult run(InputBuffer inputBuffer) { checkArgNotNull(inputBuffer, "inputBuffer"); resetValueStack(); errorIndex = 0; // run without fast string matching to properly get the error location MatcherContext rootContext = createRootContext(inputBuffer, this, false); boolean matched = match(rootContext); if (!matched) { getParseErrors().add(new BasicParseError(inputBuffer, errorIndex, null)); } return createParsingResult(matched, rootContext); } public boolean match(MatcherContext context) { if (inner == null && context.getMatcher().match(context) || inner != null && inner.match(context)) { if (errorIndex < context.getCurrentIndex() && notTestNot(context)) { errorIndex = context.getCurrentIndex(); } return true; } return false; } private boolean notTestNot(MatcherContext context) { return !(context.getMatcher() instanceof TestNotMatcher) && (context.getParent() == null || notTestNot(context.getParent())); } } ErrorReportingParseRunner.java000066400000000000000000000076021421263112100342120ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import org.parboiled.MatchHandler; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.buffers.InputBuffer; import org.parboiled.errors.InvalidInputError; import org.parboiled.matchervisitors.IsSingleCharMatcherVisitor; import org.parboiled.support.MatcherPath; import org.parboiled.support.ParsingResult; import java.util.ArrayList; import java.util.List; import static org.parboiled.common.Preconditions.checkArgNotNull; /** * A {@link org.parboiled.parserunners.ParseRunner} implementation that creates an * {@link org.parboiled.errors.InvalidInputError} for the error at a known error location. * It never causes the parser to perform more than one parsing run and is rarely used directly. * Instead its functionality is relied upon by the {@link ReportingParseRunner} and {@link RecoveringParseRunner} classes. */ public class ErrorReportingParseRunner extends AbstractParseRunner implements MatchHandler { private final IsSingleCharMatcherVisitor isSingleCharMatcherVisitor = new IsSingleCharMatcherVisitor(); private final int errorIndex; private final MatchHandler inner; private final List failedMatchers = new ArrayList(); private boolean seeking; /** * Creates a new ErrorReportingParseRunner instance for the given rule and the given errorIndex. * * @param rule the parser rule * @param errorIndex the index of the error to report */ public ErrorReportingParseRunner(Rule rule, int errorIndex) { this(rule, errorIndex, null); } /** * Creates a new ErrorReportingParseRunner instance for the given rule and the given errorIndex. * The given MatchHandler is used as a delegate for the actual match handling. * * @param rule the parser rule * @param errorIndex the index of the error to report * @param inner another MatchHandler to delegate the actual match handling to, can be null */ public ErrorReportingParseRunner(Rule rule, int errorIndex, MatchHandler inner) { super(rule); this.errorIndex = errorIndex; this.inner = inner; } public ParsingResult run(InputBuffer inputBuffer) { checkArgNotNull(inputBuffer, "inputBuffer"); resetValueStack(); failedMatchers.clear(); seeking = errorIndex > 0; // run without fast string matching to properly get to the error location MatcherContext rootContext = createRootContext(inputBuffer, this, false); boolean matched = match(rootContext); if (!matched) { getParseErrors().add(new InvalidInputError(inputBuffer, errorIndex, failedMatchers, null)); } return createParsingResult(matched, rootContext); } public boolean match(MatcherContext context) { boolean matched = inner == null && context.getMatcher().match(context) || inner != null && inner.match(context); if (context.getCurrentIndex() == errorIndex) { if (matched && seeking) { seeking = false; } if (!matched && !seeking && context.getMatcher().accept(isSingleCharMatcherVisitor)) { failedMatchers.add(context.getPath()); } } return matched; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/ParseRunner.java000066400000000000000000000042011421263112100313550ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import org.parboiled.buffers.InputBuffer; import org.parboiled.errors.ParseError; import org.parboiled.support.ParsingResult; import org.parboiled.support.ValueStack; import java.util.List; /** * A ParseRunner performs the actual parsing run of a given parser rule on a given input text. */ public interface ParseRunner { /** * Initializes the parse runner with the given error list. * * @param parseErrors the error list to start off with * @return this instance */ ParseRunner withParseErrors(List parseErrors); /** * Initializes the parse runner with the given ValueStack instance. * * @param valueStack the ValueStack to use * @return this instance */ ParseRunner withValueStack(ValueStack valueStack); /** * Performs the actual parse and creates a corresponding ParsingResult instance. * * @param input the input text to parse * @return the ParsingResult for the run */ ParsingResult run(String input); /** * Performs the actual parse and creates a corresponding ParsingResult instance. * * @param input the input text to parse * @return the ParsingResult for the run */ ParsingResult run(char[] input); /** * Performs the actual parse and creates a corresponding ParsingResult instance. * * @param inputBuffer the inputBuffer to use * @return the ParsingResult for the run */ ParsingResult run(InputBuffer inputBuffer); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/ProfilingParseRunner.java000066400000000000000000000503761421263112100332450ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import org.parboiled.MatchHandler; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.Predicate; import org.parboiled.common.StringUtils; import org.parboiled.matchers.Matcher; import org.parboiled.matchervisitors.DoWithMatcherVisitor; import org.parboiled.support.ParsingResult; import java.text.DecimalFormat; import java.util.*; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.common.Utils.humanize; /** *

The ProfilingParseRunner is a special {@link ParseRunner} implementation that "watches" a parser digest a number * of inputs and collects all sorts of statistical data on the what rules have matched how many times, the number * of reincovations of rules at identical input locations, and so on.

*

The ProfilingParseRunner is typically used during parser debugging and optimization, not in production.

* * @param */ public class ProfilingParseRunner extends AbstractParseRunner implements MatchHandler { private final Map ruleReports = new HashMap(); private int runMatches; private int totalRuns; private int totalMatches; private int totalMismatches; private int totalRematches; private int totalRemismatches; private long totalNanoTime; private long timeCorrection; private final DoWithMatcherVisitor.Action updateStatsAction = new DoWithMatcherVisitor.Action() { public void process(Matcher matcher) { RuleStats ruleStats = (RuleStats) matcher.getTag(); int rematches = 0, remismatches = 0; for (Integer i : ruleStats.positionMatches.values()) { if (i > 0) { rematches += i - 1; } else if (i < 0) { remismatches += -(i + 1); } } totalMatches += ruleStats.matches; totalMismatches += ruleStats.mismatches; totalRematches += rematches; totalRemismatches += remismatches; RuleReport ruleReport = ruleReports.get(matcher); if (ruleReport == null) { ruleReport = new RuleReport(matcher); ruleReports.put(matcher, ruleReport); } ruleReport.update(ruleStats.matches, ruleStats.matchSubs, ruleStats.mismatches, ruleStats.mismatchSubs, rematches, ruleStats.rematchSubs, remismatches, ruleStats.remismatchSubs, ruleStats.nanoTime); } }; /** * Creates a new ProfilingParseRunner instance for the given rule. * * @param rule the parser rule */ public ProfilingParseRunner(Rule rule) { super(rule); } public ParsingResult run(InputBuffer inputBuffer) { checkArgNotNull(inputBuffer, "inputBuffer"); resetValueStack(); totalRuns++; MatcherContext rootContext = createRootContext(inputBuffer, this, true); rootContext.getMatcher().accept(new DoWithMatcherVisitor(new DoWithMatcherVisitor.Action() { public void process(Matcher matcher) { RuleStats ruleStats = (RuleStats) matcher.getTag(); if (ruleStats == null) { ruleStats = new RuleStats(); matcher.setTag(ruleStats); } else { ruleStats.clear(); } } })); runMatches = 0; long timeStamp = System.nanoTime() - timeCorrection; boolean matched = rootContext.runMatcher(); totalNanoTime += System.nanoTime() - timeCorrection - timeStamp; getRootMatcher().accept(new DoWithMatcherVisitor(updateStatsAction)); return createParsingResult(matched, rootContext); } public Report getReport() { return new Report(totalRuns, totalMatches, totalMismatches, totalRematches, totalRemismatches, totalNanoTime, new ArrayList(ruleReports.values())); } public boolean match(MatcherContext context) { long timeStamp = System.nanoTime(); Matcher matcher = context.getMatcher(); RuleStats ruleStats = ((RuleStats) matcher.getTag()); int pos = context.getCurrentIndex(); int subMatches = -++runMatches; int matchSubs = ruleStats.matchSubs; int rematchSubs = ruleStats.rematchSubs; int mismatchSubs = ruleStats.mismatchSubs; int remismatchSubs = ruleStats.remismatchSubs; long time = System.nanoTime(); timeCorrection += time - timeStamp; timeStamp = time - timeCorrection; boolean matched = matcher.match(context); time = System.nanoTime(); ruleStats.nanoTime += time - timeCorrection - timeStamp; timeStamp = time; subMatches += runMatches; Integer posMatches = ruleStats.positionMatches.get(pos); if (matched) { ruleStats.matches++; ruleStats.matchSubs = matchSubs + subMatches; if (posMatches == null) { posMatches = 1; } else if (posMatches > 0) { posMatches++; ruleStats.rematchSubs = rematchSubs + subMatches; } else if (posMatches < 0) { posMatches = 0; } } else { ruleStats.mismatches++; ruleStats.mismatchSubs = mismatchSubs + subMatches; if (posMatches == null) { posMatches = -1; } else if (posMatches < 0) { posMatches--; ruleStats.remismatchSubs = remismatchSubs + subMatches; } else if (posMatches > 0) { posMatches = 0; } } ruleStats.positionMatches.put(pos, posMatches); timeCorrection += System.nanoTime() - timeStamp; return matched; } private static class RuleStats { private int matches; private int mismatches; private int matchSubs; private int mismatchSubs; private int rematchSubs; private int remismatchSubs; private long nanoTime; // map Index -> matches at that position // no entry for a position means that the rule was never tried for that position // an entry n > 0 means that the rule matched n times // an entry n < 0 means that the rule failed n times // an entry of 0 for a position means that the rule matched as well as failed at the position (should happen // only for "strange" action rules) private final Map positionMatches = new HashMap(); private void clear() { matches = 0; mismatches = 0; matchSubs = 0; mismatchSubs = 0; rematchSubs = 0; remismatchSubs = 0; nanoTime = 0; positionMatches.clear(); } } public static class Report { private final static DecimalFormat fmt = new DecimalFormat("0.###"); public static final Predicate allRules = new Predicate() { public boolean apply(RuleReport rep) { return true; } }; public static final Predicate namedRules = new Predicate() { public boolean apply(RuleReport rep) { return rep.getMatcher().hasCustomLabel(); } }; public final int totalRuns; public final int totalInvocations; public final int totalMatches; public final int totalMismatches; public final double matchShare; public final int reinvocations; public final int rematches; public final int remismatches; public final double reinvocationShare; public final long totalNanoTime; public final List ruleReports; public Report(int totalRuns, int totalMatches, int totalMismatches, int rematches, int remismatches, long totalNanoTime, List ruleReports) { this.totalRuns = totalRuns; this.totalInvocations = totalMatches + totalMismatches; this.totalMatches = totalMatches; this.totalMismatches = totalMismatches; this.matchShare = ((double) totalMatches) / totalInvocations; this.reinvocations = rematches + remismatches; this.rematches = rematches; this.remismatches = remismatches; this.reinvocationShare = ((double) reinvocations) / totalInvocations; this.totalNanoTime = totalNanoTime; this.ruleReports = ruleReports; } public String print() { StringBuilder sb = new StringBuilder(); sb.append("Profiling Report\n"); sb.append("----------------\n"); sb.append(printBasics()); sb.append("\n"); sb.append("Top 20 named rules by invocations:\n"); sb.append(sortByInvocations().printTopRules(20, namedRules)); sb.append("\n"); sb.append("Top 20 named rules by sub-invocations:\n"); sb.append(sortBySubInvocations().printTopRules(20, namedRules)); sb.append("\n"); sb.append("Top 20 named rules by re-invocations:\n"); sb.append(sortByReinvocations().printTopRules(20, namedRules)); sb.append("\n"); sb.append("Top 20 named rules by re-sub-invocations:\n"); sb.append(sortByResubinvocations().printTopRules(20, namedRules)); sb.append("\n"); sb.append("Top 20 named rules by re-mismatches:\n"); sb.append(sortByRemismatches().printTopRules(20, namedRules)); sb.append("\n"); sb.append("Top 20 named rules by re-sub-mismatches:\n"); sb.append(sortByResubmismatches().printTopRules(20, namedRules)); return sb.toString(); } public String printBasics() { StringBuilder sb = new StringBuilder(); sb.append(String.format("Runs : %,15d\n", totalRuns)); sb.append(String.format("Active rules : %,15d\n", ruleReports.size())); sb.append(String.format("Total net rule time : %,15.3f s\n", totalNanoTime / 1000000000.0)); sb.append(String.format("Total rule invocations : %,15d\n", totalInvocations)); sb.append(String.format("Total rule matches : %,15d\n", totalMatches)); sb.append(String.format("Total rule mismatches : %,15d\n", totalMismatches)); sb.append(String.format("Total match share : %15.2f %%\n", 100.0 * matchShare)); sb.append(String.format("Rule re-invocations : %,15d\n", reinvocations)); sb.append(String.format("Rule re-matches : %,15d\n", rematches)); sb.append(String.format("Rule re-mismatches : %,15d\n", remismatches)); sb.append(String.format("Rule re-invocation share : %15.2f %%\n", 100.0 * reinvocationShare)); return sb.toString(); } public String printTopRules(int count, Predicate filter) { checkArgNotNull(filter, "filter"); StringBuilder sb = new StringBuilder(); sb.append( "Rule | Net-Time | Invocations | Matches | Mismatches | Time/Invoc. | Match % | Re-Invocs | Re-Matches | Re-Mismatch | Re-Invoc % \n"); sb.append( "-------------------------------|-----------|-----------------|-----------------|-----------------|-----------------|---------|-----------------|-----------------|-----------------|-------------------\n"); for (int i = 0; i < Math.min(ruleReports.size(), count); i++) { RuleReport rep = ruleReports.get(i); if (!filter.apply(rep)) { count++; continue; } sb.append(String.format( "%-30s | %6.0f ms | %6s / %6s | %6s / %6s | %6s / %6s | %,12.0f ns | %6.2f%% | %6s / %6s | %6s / %6s | %6s / %6s | %6.2f%% / %6.2f%%\n", StringUtils.left( rep.getMatcher().toString() + ": " + rep.getMatcher().getClass().getSimpleName() .replace("Matcher", ""), 30), rep.getNanoTime() / 1000000.0, humanize(rep.getInvocations()), humanize(rep.getInvocationSubs()), humanize(rep.getMatches()), humanize(rep.getMatchSubs()), humanize(rep.getMismatches()), humanize(rep.getMismatchSubs()), rep.getNanoTime() / (double) rep.getInvocations(), rep.getMatchShare() * 100, humanize(rep.getReinvocations()), humanize(rep.getReinvocationSubs()), humanize(rep.getRematches()), humanize(rep.getRematchSubs()), humanize(rep.getRemismatches()), humanize(rep.getRemismatchSubs()), rep.getReinvocationShare() * 100, rep.getReinvocationShare2() * 100 )); } return sb.toString(); } public Report sortByInvocations() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return intCompare(a.getInvocations(), b.getInvocations()); } }); return this; } public Report sortBySubInvocations() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return intCompare(a.getInvocationSubs(), b.getInvocationSubs()); } }); return this; } public Report sortByTime() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return longCompare(a.getNanoTime(), b.getNanoTime()); } }); return this; } public Report sortByTimePerInvocation() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return doubleCompare(a.getNanoTime() / (double) a.getInvocations(), b.getNanoTime() / (double) b.getInvocations()); } }); return this; } public Report sortByMatches() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return intCompare(a.getMatches(), b.getMatches()); } }); return this; } public Report sortByMismatches() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return intCompare(a.getMismatches(), b.getMismatches()); } }); return this; } public Report sortByReinvocations() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return intCompare(a.getReinvocations(), b.getReinvocations()); } }); return this; } public Report sortByResubinvocations() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return doubleCompare(a.getReinvocationSubs(), b.getReinvocationSubs()); } }); return this; } public Report sortByRematches() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return intCompare(a.getRematches(), b.getRematches()); } }); return this; } public Report sortByRemismatches() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return intCompare(a.getRemismatches(), b.getRemismatches()); } }); return this; } public Report sortByResubmismatches() { Collections.sort(ruleReports, new Comparator() { public int compare(RuleReport a, RuleReport b) { return doubleCompare(a.getRemismatchSubs(), b.getRemismatchSubs()); } }); return this; } private int intCompare(int a, int b) { return a < b ? 1 : a > b ? -1 : 0; } private int longCompare(long a, long b) { return a < b ? 1 : a > b ? -1 : 0; } private int doubleCompare(double a, double b) { return a < b ? 1 : a > b ? -1 : 0; } } public static class RuleReport { private final Matcher matcher; private int matches; private int matchSubs; private int mismatches; private int mismatchSubs; private int rematches; private int rematchSubs; private int remismatches; private int remismatchSubs; private long nanoTime; public RuleReport(Matcher matcher) { this.matcher = matcher; } public Matcher getMatcher() { return matcher; } public int getInvocations() { return matches + mismatches; } public int getInvocationSubs() { return matchSubs + mismatchSubs; } public int getMatches() { return matches; } public int getMatchSubs() { return matchSubs; } public int getMismatches() { return mismatches; } public int getMismatchSubs() { return mismatchSubs; } public double getMatchShare() { return ((double) matches) / getInvocations(); } public double getMatchShare2() { return ((double) matchSubs) / getInvocationSubs(); } public int getReinvocations() { return rematches + remismatches; } public int getReinvocationSubs() { return rematchSubs + remismatchSubs; } public int getRematches() { return rematches; } public int getRematchSubs() { return rematchSubs; } public int getRemismatches() { return remismatches; } public int getRemismatchSubs() { return remismatchSubs; } public double getReinvocationShare() { return ((double) getReinvocations()) / getInvocations(); } public double getReinvocationShare2() { return ((double) getReinvocationSubs()) / getInvocationSubs(); } public long getNanoTime() { return nanoTime; } public void update(int matchesDelta, int matchSubsDelta, int mismatchesDelta, int mismatchSubsDelta, int rematchesDelta, int rematchSubsDelta, int remismatchesDelta, int remismatchSubsDelta, long nanoTimeDelta) { matches += matchesDelta; matchSubs += matchSubsDelta; mismatches += mismatchesDelta; mismatchSubs += mismatchSubsDelta; rematches += rematchesDelta; rematchSubs += rematchSubsDelta; remismatches += remismatchesDelta; remismatchSubs += remismatchSubsDelta; nanoTime += nanoTimeDelta; } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/RecoveringParseRunner.java000066400000000000000000000607241421263112100334150ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import org.parboiled.MatchHandler; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.buffers.InputBuffer; import org.parboiled.buffers.MutableInputBuffer; import org.parboiled.common.ImmutableLinkedList; import org.parboiled.common.ImmutableList; import org.parboiled.common.Preconditions; import org.parboiled.errors.InvalidInputError; import org.parboiled.matchers.*; import org.parboiled.matchervisitors.*; import org.parboiled.support.Checks; import org.parboiled.support.MatcherPath; import org.parboiled.support.ParsingResult; import java.util.ArrayList; import java.util.List; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.common.Preconditions.checkState; import static org.parboiled.matchers.MatcherUtils.unwrap; import static org.parboiled.support.Chars.*; /** * A {@link org.parboiled.parserunners.ParseRunner} implementation that is able to recover from {@link org.parboiled.errors.InvalidInputError}s in the input and therefore * report more than just the first {@link org.parboiled.errors.InvalidInputError} if the input does not conform to the rule grammar. * Error recovery is done by attempting to either delete an error character, insert a potentially missing character * or do both at once (which is equivalent to a one char replace) whereby this implementation is able to determine * itself which of these options is the best strategy. * If the parse error cannot be overcome by either deleting, inserting or replacing one character a resynchronization * rule is determined and the parsing process resynchronized, so that parsing can still continue. * In this way the RecoveringParseRunner is able to completely parse all input texts (This ParseRunner never returns * an unmatched {@link org.parboiled.support.ParsingResult}). * If the input is error free this {@link org.parboiled.parserunners.ParseRunner} implementation will only perform one parsing run, with the same * speed as the {@link org.parboiled.parserunners.BasicParseRunner}. However, if there are {@link org.parboiled.errors.InvalidInputError}s in the input potentially * many more runs are performed to properly report all errors and test the various recovery strategies. */ public class RecoveringParseRunner extends AbstractParseRunner { public static class TimeoutException extends RuntimeException { public final Rule rule; public final InputBuffer inputBuffer; public final ParsingResult lastParsingResult; public TimeoutException(Rule rule, InputBuffer inputBuffer, ParsingResult lastParsingResult) { this.rule = rule; this.inputBuffer = inputBuffer; this.lastParsingResult = lastParsingResult; } } private final long timeoutNanos; private long startTimeStamp; private int errorIndex; private InvalidInputError currentError; private MutableInputBuffer buffer; private ParsingResult lastParsingResult; private Matcher rootMatcherWithoutPTB; // the root matcher with parse tree building disabled /** * Create a new RecoveringParseRunner instance with the given rule and input text and returns the result of * its {@link #run(String)} method invocation. * * @param rule the parser rule to run * @param input the input text to run on * @return the ParsingResult for the parsing run * @deprecated As of 0.11.0 you should use the "regular" constructor and one of the "run" methods rather than * this static method. This method will be removed in one of the coming releases. */ @Deprecated public static ParsingResult run(Rule rule, String input) { checkArgNotNull(rule, "rule"); checkArgNotNull(input, "input"); return new RecoveringParseRunner(rule).run(input); } /** * Creates a new RecoveringParseRunner instance for the given rule. * * @param rule the parser rule */ public RecoveringParseRunner(Rule rule) { this(rule, Long.MAX_VALUE); } /** * Creates a new RecoveringParseRunner instance for the given rule. * A parsing run will throw a TimeoutException if it takes longer than the given number if milliseconds. * * @param rule the parser rule * @param timeoutMillis the timeout value in milliseconds */ public RecoveringParseRunner(Rule rule, long timeoutMillis) { super(rule); if (timeoutMillis > Long.MAX_VALUE / 1000000) { this.timeoutNanos = Long.MAX_VALUE; } else { this.timeoutNanos = timeoutMillis * 1000000; } } public ParsingResult run(InputBuffer inputBuffer) { checkArgNotNull(inputBuffer, "inputBuffer"); startTimeStamp = System.nanoTime(); resetValueStack(); // first, run a basic match ParseRunner basicRunner = new BasicParseRunner(getRootMatcher()) .withParseErrors(getParseErrors()) .withValueStack(getValueStack()); lastParsingResult = basicRunner.run(inputBuffer); if (!lastParsingResult.matched) { // for better performance disable parse tree building during the recovery runs rootMatcherWithoutPTB = (Matcher) getRootMatcher().suppressNode(); // locate first error performLocatingRun(inputBuffer); checkState(errorIndex >= 0); // we failed before so we must fail again // in order to be able to apply fixes we need to wrap the input buffer with a mutability wrapper buffer = new MutableInputBuffer(inputBuffer); // report first error performReportingRun(); // fix and report until done while (!fixError(errorIndex)) { performReportingRun(); } // rerun once more with parse tree building enabled to create a parse tree for the fixed input if (!getRootMatcher().isNodeSuppressed()) { performFinalRun(); checkState(lastParsingResult.matched); } } return lastParsingResult; } private boolean performLocatingRun(InputBuffer inputBuffer) { resetValueStack(); ParseRunner locatingRunner = new ErrorLocatingParseRunner(rootMatcherWithoutPTB, getInnerHandler()) .withParseErrors(getParseErrors()) .withValueStack(getValueStack()); lastParsingResult = locatingRunner.run(inputBuffer); errorIndex = lastParsingResult.matched ? -1 : getParseErrors().remove(getParseErrors().size() - 1).getStartIndex(); return lastParsingResult.matched; } private void performReportingRun() { resetValueStack(); ParseRunner reportingRunner = new ErrorReportingParseRunner(rootMatcherWithoutPTB, errorIndex, getInnerHandler()) .withParseErrors(getParseErrors()) .withValueStack(getValueStack()); ParsingResult result = reportingRunner.run(buffer); Preconditions.checkState(!result.matched); // we failed before so we should really be failing again currentError = (InvalidInputError) getParseErrors().get(getParseErrors().size() - 1); } private void performFinalRun() { resetValueStack(); Handler handler = new Handler(); MatcherContext rootContext = createRootContext(buffer, handler, false); boolean matched = handler.match(rootContext); lastParsingResult = createParsingResult(matched, rootContext); } private MatchHandler getInnerHandler() { return errorIndex >= 0 ? new Handler() : null; } private boolean fixError(int fixIndex) { if (tryFixBySingleCharDeletion(fixIndex)) return true; int nextErrorAfterDeletion = errorIndex; Character bestInsertionCharacter = findBestSingleCharInsertion(fixIndex); if (bestInsertionCharacter == null) return true; int nextErrorAfterBestInsertion = errorIndex; Character bestReplacementCharacter = findBestSingleCharReplacement(fixIndex); if (bestReplacementCharacter == null) return true; int nextErrorAfterBestReplacement = errorIndex; int nextErrorAfterBestSingleCharFix = Math.max(Math.max(nextErrorAfterDeletion, nextErrorAfterBestInsertion), nextErrorAfterBestReplacement); if (nextErrorAfterBestSingleCharFix > fixIndex) { // we are able to overcome the error with a single char fix, so apply the best one found if (nextErrorAfterBestSingleCharFix == nextErrorAfterDeletion) { buffer.insertChar(fixIndex, DEL_ERROR); errorIndex = nextErrorAfterDeletion + 1; currentError.shiftIndexDeltaBy(1); } else if (nextErrorAfterBestSingleCharFix == nextErrorAfterBestInsertion) { // we need to insert the characters in reverse order, since we insert twice at the same location buffer.insertChar(fixIndex, bestInsertionCharacter); buffer.insertChar(fixIndex, INS_ERROR); errorIndex = nextErrorAfterBestInsertion + 2; currentError.shiftIndexDeltaBy(2); } else { // we need to insert the characters in reverse order, since we insert three times at the same location buffer.insertChar(fixIndex + 1, bestReplacementCharacter); buffer.insertChar(fixIndex + 1, INS_ERROR); buffer.insertChar(fixIndex, DEL_ERROR); errorIndex = nextErrorAfterBestReplacement + 5; currentError.shiftIndexDeltaBy(1); } } else { // we can't fix the error with a single char fix, so fall back to resynchronization if (buffer.charAt(fixIndex) == EOI) { buffer.insertChar(fixIndex, RESYNC_EOI); currentError.shiftIndexDeltaBy(1); return true; } buffer.insertChar(fixIndex, RESYNC); currentError.shiftIndexDeltaBy(1); performLocatingRun(buffer); // find the next parse error } return errorIndex == -1; } private boolean tryFixBySingleCharDeletion(int fixIndex) { buffer.insertChar(fixIndex, DEL_ERROR); boolean nowErrorFree = performLocatingRun(buffer); if (nowErrorFree) { currentError.shiftIndexDeltaBy(1); // compensate for the inserted DEL_ERROR char } else { buffer.undoCharInsertion(fixIndex); errorIndex = Math.max(errorIndex - 1, 0); } return nowErrorFree; } @SuppressWarnings( {"ConstantConditions"}) private Character findBestSingleCharInsertion(int fixIndex) { GetStarterCharVisitor getStarterCharVisitor = new GetStarterCharVisitor(); int bestNextErrorIndex = -1; Character bestChar = '\u0000'; // non-null default for (MatcherPath failedMatcherPath : currentError.getFailedMatchers()) { Character starterChar = failedMatcherPath.element.matcher.accept(getStarterCharVisitor); checkState(starterChar != null); // we should only have single character matchers if (starterChar == EOI) { continue; // we should never conjure up an EOI character (that would be cheating :) } buffer.insertChar(fixIndex, starterChar); buffer.insertChar(fixIndex, INS_ERROR); if (performLocatingRun(buffer)) { currentError.shiftIndexDeltaBy(2); // compensate for the inserted chars return null; // success, exit immediately } buffer.undoCharInsertion(fixIndex); buffer.undoCharInsertion(fixIndex); errorIndex = Math.max(errorIndex - 2, 0); if (bestNextErrorIndex < errorIndex) { bestNextErrorIndex = errorIndex; bestChar = starterChar; } } errorIndex = bestNextErrorIndex; return bestChar; } private Character findBestSingleCharReplacement(int fixIndex) { buffer.insertChar(fixIndex, DEL_ERROR); Character bestChar = findBestSingleCharInsertion(fixIndex + 2); if (bestChar == null) { // success, we found a fix that renders the complete input error free currentError .shiftIndexDeltaBy(-1); // delta from DEL_ERROR char insertion and index shift by insertion method } else { buffer.undoCharInsertion(fixIndex); errorIndex = Math.max(errorIndex - 3, 0); } return bestChar; } /** * A {@link org.parboiled.MatchHandler} implementation that recognizes the special * {@link org.parboiled.support.Chars#RESYNC} character to overcome {@link InvalidInputError}s at the respective * error indices. */ private class Handler implements MatchHandler { private final IsSingleCharMatcherVisitor isSingleCharMatcherVisitor = new IsSingleCharMatcherVisitor(); private int fringeIndex; private MatcherPath lastMatchPath; public boolean match(MatcherContext context) { Matcher matcher = context.getMatcher(); if (matcher.accept(isSingleCharMatcherVisitor)) { if (prepareErrorLocation(context) && matcher.match(context)) { if (fringeIndex < context.getCurrentIndex()) { fringeIndex = context.getCurrentIndex(); lastMatchPath = context.getPath(); } return true; } return false; } if (matcher.match(context)) { return true; } // if we didn't match we might have to resynchronize if (matcher instanceof SequenceMatcher) { switch(context.getCurrentChar()) { case RESYNC: case RESYNC_START: case RESYNC_EOI: // however we only resynchronize if we are at a RESYNC location and the matcher is a SequenceMatcher // that has already matched at least one character and that is a parent of the last match return qualifiesForResync(context) && resynchronize(context); } // check for timeout only on failures of sequences so as to not add too much overhead if (System.nanoTime() - startTimeStamp > timeoutNanos) { throw new TimeoutException(getRootMatcher(), buffer, lastParsingResult); } } return false; } private boolean qualifiesForResync(MatcherContext context) { if (context.getCurrentIndex() == context.getStartIndex() || !context.getPath().isPrefixOf(lastMatchPath)) { // if we have a sequence that hasn't match anything yet or is not a prefix we might still have to // resync on it if there is no other sequence parent anymore MatcherContext parent = context.getParent(); while (parent != null) { if (parent.getMatcher() instanceof SequenceMatcher) return false; parent = parent.getParent(); } } return true; } private boolean prepareErrorLocation(MatcherContext context) { switch (context.getCurrentChar()) { case DEL_ERROR: return willMatchDelError(context); case INS_ERROR: return willMatchInsError(context); case RESYNC: case RESYNC_START: case RESYNC_EOI: return false; default: return true; } } private boolean willMatchDelError(MatcherContext context) { int preSkipIndex = context.getCurrentIndex(); context.advanceIndex(2); // skip del marker char and illegal char if (!runTestMatch(context)) { // if we wouldn't succeed with the match do not swallow the ERROR char & Co context.setCurrentIndex(preSkipIndex); return false; } context.setStartIndex(context.getCurrentIndex()); if (context.getParent() != null) context.getParent().markError(); return true; } private boolean willMatchInsError(MatcherContext context) { int preSkipIndex = context.getCurrentIndex(); context.advanceIndex(1); // skip ins marker char if (!runTestMatch(context)) { // if we wouldn't succeed with the match do not swallow the ERROR char context.setCurrentIndex(preSkipIndex); return false; } context.setStartIndex(context.getCurrentIndex()); context.markError(); return true; } private boolean runTestMatch(MatcherContext context) { TestMatcher testMatcher = new TestMatcher(context.getMatcher()); MatcherContext testContext = testMatcher.getSubContext(context); return prepareErrorLocation(testContext) && testContext.runMatcher(); } private boolean resynchronize(MatcherContext context) { context.markError(); // create a node for the failed Sequence, taking ownership of all sub nodes created so far context.createNode(); // by resyncing we flip an unmatched sequence to a matched one, so in order to keep the value stack // consistent we go into a special "error action mode" and execute the minimal set of actions underneath // the resync sequence rerunAndExecuteErrorActions(context); // skip over all characters that are not legal followers of the failed Sequence switch (context.getCurrentChar()) { case RESYNC: // this RESYNC error is the last error, we establish the length of the bad sequence and // change this RESYNC marker to a RESYNC_START / RESYNC_END block context.advanceIndex(1); // gobble RESYNC marker List followMatchers = new FollowMatchersVisitor().getFollowMatchers(context); int endIndex = gobbleIllegalCharacters(context, followMatchers); currentError.setEndIndex(endIndex); buffer.replaceInsertedChar(currentError.getStartIndex() - 1, RESYNC_START); buffer.insertChar(endIndex, RESYNC_END); context.advanceIndex(1); // gobble RESYNC_END marker break; case RESYNC_START: // a RESYNC error we have already recovered from before context.advanceIndex(1); // gobble RESYNC_START while (context.getCurrentChar() != RESYNC_END) { context.advanceIndex(1); // skip all characters up to the RESYNC_END checkState(context.getCurrentChar() != EOI); // we MUST find a RESYNC_END before EOI } context.advanceIndex(1); // gobble RESYNC_END marker break; case RESYNC_EOI: // if we are resyncing on EOI we don't swallow anything // we also do not have to update the currentError since we only hit this code here // in the final run break; default: throw new IllegalStateException(); } return true; } @SuppressWarnings( {"ConstantConditions"}) private void rerunAndExecuteErrorActions(MatcherContext context) { // the context is for the resync action, which at this point has FAILED, i.e. ALL its sub actions haven't // had a chance to change the value stack, even the ones having run before the actual parse error matcher // so we need to rerun all sub matchers of the resync sequence up to the point of the parse error // and then run the minimal set of action in "error action mode" int savedCurrentIndex = context.getCurrentIndex(); context.setCurrentIndex(context.getStartIndex()); // restart matching the resync sequence boolean preError = true; for (Matcher child : context.getMatcher().getChildren()) { if (preError && !child.getSubContext(context).runMatcher()) { // run what will be the preceding matcher of all error actions new EmptyMatcher().getSubContext(context).runMatcher(); context.setIntTag(1); // signal that at least one rule has run before the error actions preError = false; } if (!preError) { context.setInErrorRecovery(true); List errorActions = child.accept(new CollectResyncActionsVisitor()); checkState(errorActions != null); for (ActionMatcher errorAction : errorActions) { // execute the error actions without looking at their boolean results !!! errorAction.getSubContext(context).runMatcher(); } context.setInErrorRecovery(false); } } context.setCurrentIndex(savedCurrentIndex); } private int gobbleIllegalCharacters(MatcherContext context, List followMatchers) { while_loop: while (true) { char currentChar = context.getCurrentChar(); if (currentChar == EOI) break; for (Matcher followMatcher : followMatchers) { if (followMatcher.accept(new IsStarterCharVisitor(currentChar))) { break while_loop; } } context.advanceIndex(1); } return context.getCurrentIndex(); } } /** * This MatcherVisitor collects the minimal set of actions that has to run underneath a resyncronization sequence * in order to maintain a consistent Value Stack state. */ private static class CollectResyncActionsVisitor extends DefaultMatcherVisitor> { private ImmutableLinkedList path = ImmutableLinkedList.nil(); @Override public List visit(ActionMatcher matcher) { return ImmutableList.of(matcher); } @Override public List visit(FirstOfMatcher matcher) { for (Matcher child : matcher.getChildren()) { List actions = child.accept(this); if (actions != null) return actions; } return null; } @Override public List visit(OneOrMoreMatcher matcher) { return matcher.subMatcher.accept(this); } @Override public List visit(SequenceMatcher matcher) { if (path.contains(matcher)) { return null; } ImmutableLinkedList previousPath = path; path = path.prepend(matcher); List actions = new ArrayList(); for (Matcher sub : matcher.getChildren()) { List subActions = sub.accept(this); if (subActions == null) return null; actions.addAll(subActions); } path = previousPath; return actions; } @Override public List defaultValue(AbstractMatcher matcher) { return ImmutableList.of(); } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/ReportingParseRunner.java000066400000000000000000000102741421263112100332560ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import org.parboiled.Rule; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.Preconditions; import org.parboiled.errors.InvalidInputError; import org.parboiled.support.ParsingResult; import static org.parboiled.common.Preconditions.checkArgNotNull; /** * A {@link ParseRunner} implementation that properly reports the first {@link InvalidInputError} if the input * does not conform to the rule grammar. * It performs exactly as the {@link BasicParseRunner} on valid input, however, on invalid input two more parsing * runs are initiated: one for recording the first parse error and one for collecting the error report information. */ public class ReportingParseRunner extends AbstractParseRunner { /** * Create a new ReportingParseRunner instance with the given rule and input text and returns the result of * its {@link #run(String)} method invocation. * * @param rule the parser rule to run * @param input the input text to run on * @return the ParsingResult for the parsing run * @deprecated As of 0.11.0 you should use the "regular" constructor and one of the "run" methods rather than * this static method. This method will be removed in one of the coming releases. */ @Deprecated public static ParsingResult run(Rule rule, String input) { checkArgNotNull(rule, "rule"); checkArgNotNull(input, "input"); return new ReportingParseRunner(rule).run(input); } /** * Creates a new ReportingParseRunner instance for the given rule. * * @param rule the parser rule */ public ReportingParseRunner(Rule rule) { super(rule); } public ParsingResult run(InputBuffer inputBuffer) { checkArgNotNull(inputBuffer, "inputBuffer"); resetValueStack(); // first, run a basic match ParsingResult result = runBasicMatch(inputBuffer); if (result.matched) return result; // all good // ok, we have a parse error, so determine the error location resetValueStack(); result = runLocatingMatch(inputBuffer); Preconditions.checkState(!result.matched); // we failed before so we should really be failing again Preconditions.checkState(result.parseErrors.size() >= 1); // may be more than one in case of custom ActionExceptions // finally perform a third, reporting run (now that we know the error location) resetValueStack(); result = runReportingMatch(inputBuffer, result.parseErrors.get(0).getStartIndex()); Preconditions.checkState(!result.matched); // we failed before so we should really be failing again return result; } protected ParsingResult runBasicMatch(InputBuffer inputBuffer) { ParseRunner basicRunner = new BasicParseRunner(getRootMatcher()) .withParseErrors(getParseErrors()) .withValueStack(getValueStack()); return basicRunner.run(inputBuffer); } protected ParsingResult runLocatingMatch(InputBuffer inputBuffer) { ParseRunner locatingRunner = new ErrorLocatingParseRunner(getRootMatcher()) .withValueStack(getValueStack()); return locatingRunner.run(inputBuffer); } protected ParsingResult runReportingMatch(InputBuffer inputBuffer, int errorIndex) { ParseRunner reportingRunner = new ErrorReportingParseRunner(getRootMatcher(), errorIndex) .withParseErrors(getParseErrors()) .withValueStack(getValueStack()); return reportingRunner.run(inputBuffer); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/parserunners/TracingParseRunner.java000066400000000000000000000117441421263112100326770ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.parserunners; import org.parboiled.Context; import org.parboiled.MatchHandler; import org.parboiled.MatcherContext; import org.parboiled.Rule; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.*; import org.parboiled.matchers.Matcher; import org.parboiled.support.MatcherPath; import org.parboiled.support.ParsingResult; import org.parboiled.support.Position; import static org.parboiled.common.Preconditions.checkArgNotNull; /** * A {@link org.parboiled.parserunners.ParseRunner} implementation used for debugging purposes. * It exhibits the same behavior as the {@link ReportingParseRunner} but collects debugging information as to which * rules did match and which didn't. */ public class TracingParseRunner extends ReportingParseRunner implements MatchHandler { private Predicate, Boolean>> filter; private Sink log; private MatcherPath lastPath; private int line; /** * Creates a new TracingParseRunner instance without filter and a console log for the given rule. * * @param rule the parser rule */ public TracingParseRunner(Rule rule) { super(rule); } /** * Attaches the given filter to this TracingParseRunner instance. * The given filter is used to select the matchers to print tracing statements for. * NOTE: The given filter must be of type Predicate, Boolean>>. The reason this type is not * directly specified in the constructors signature is that this would make predicate expressions using the * {@link Predicates} operations and the predefined predicate constructors in {@link org.parboiled.support.Filters} * much more cumbersome to write (due to Java limited type parameters inference logic you would have to explicitly * state the type parameters in many places). * * @param filter the matcher filter selecting the matchers to print tracing statements for. Must be of type * Predicate, Boolean>>. * @return this instance */ @SuppressWarnings( {"unchecked"}) public TracingParseRunner withFilter(Predicate filter) { this.filter = (Predicate, Boolean>>) checkArgNotNull(filter, "filter"); return this; } public Predicate, Boolean>> getFilter() { if (filter == null) { withFilter(Predicates.alwaysTrue()); } return filter; } /** * Attaches the given log to this TracingParseRunner instance. * * @param log the log to use * @return this instance */ public TracingParseRunner withLog(Sink log) { this.log = log; return this; } public Sink getLog() { if (log == null) { withLog(new ConsoleSink()); } return log; } @Override protected ParsingResult runBasicMatch(InputBuffer inputBuffer) { getLog().receive("Starting new parsing run\n"); lastPath = null; MatcherContext rootContext = createRootContext(inputBuffer, this, true); boolean matched = rootContext.runMatcher(); return createParsingResult(matched, rootContext); } @SuppressWarnings( {"unchecked"}) public boolean match(MatcherContext context) { Matcher matcher = context.getMatcher(); boolean matched = matcher.match(context); if (getFilter().apply(new Tuple2, Boolean>(context, matched))) { line++; print(context, matched); // set line-dependent breakpoint here } return matched; } private void print(MatcherContext context, boolean matched) { Position pos = context.getInputBuffer().getPosition(context.getCurrentIndex()); MatcherPath path = context.getPath(); MatcherPath prefix = lastPath != null ? path.commonPrefix(lastPath) : null; if (prefix != null && prefix.length() > 1) getLog().receive("..(" + (prefix.length() - 1) + ")../"); getLog().receive(path.toString(prefix != null ? prefix.parent : null)); String line = context.getInputBuffer().extractLine(pos.line); getLog().receive(", " + (matched ? "matched" : "failed") + ", cursor at " + pos.line + ':' + pos.column + " after \"" + line.substring(0, Math.min(line.length(), pos.column - 1)) + "\"\n"); lastPath = path; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/000077500000000000000000000000001421263112100252365ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/Characters.java000066400000000000000000000214261421263112100301650ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import static org.parboiled.common.Preconditions.*; import org.parboiled.common.StringUtils; import java.util.Arrays; /** * An immutable, set-like aggregation of (relatively few) characters that allows for an inverted semantic * ("all chars except these few"). */ public class Characters { private static final char[] NO_CHARS = new char[0]; /** * The empty Characters set */ public static final Characters NONE = new Characters(false, NO_CHARS); /** * The Characters set including all character. */ public static final Characters ALL = new Characters(true, NO_CHARS); // if the set is subtractive its semantics change from "includes all characters in the set" to // "includes all characters not in the set" private final boolean subtractive; private final char[] chars; private Characters(boolean subtractive, char[] chars) { this.subtractive = subtractive; this.chars = checkArgNotNull(chars, "chars"); } /** * @return true if the set is subtractive */ public boolean isSubtractive() { return subtractive; } /** * Returns the characters in this set, if it is additive. * If the set is subtractive the method returns the characters not in the set. * * @return the characters */ public char[] getChars() { return chars; } /** * Adds the given character to the set. * * @param c the character to add * @return a new Characters object */ public Characters add(char c) { return subtractive ? removeFromChars(c) : addToChars(c); } /** * Removes the given character from the set. * * @param c the character to remove * @return a new Characters object */ public Characters remove(char c) { return subtractive ? addToChars(c) : removeFromChars(c); } /** * Determines whether this instance contains the given character. * * @param c the character to check for * @return true if this instance contains c */ public boolean contains(char c) { return indexOf(chars, c) == -1 ? subtractive : !subtractive; } /** * Returns a new Characters object containing all the characters of this instance plus all characters of the * given instance. * * @param other the other Characters to add * @return a new Characters object */ public Characters add(Characters other) { checkArgNotNull(other, "other"); if (!subtractive && !other.subtractive) { return addToChars(other.chars); } if (subtractive && other.subtractive) { return retainAllChars(other.chars); } return subtractive ? removeFromChars(other.chars) : other.removeFromChars(chars); } /** * Returns a new Characters object containing all the characters of this instance minus all characters of the * given instance. * * @param other the other Characters to remove * @return a new Characters object */ public Characters remove(Characters other) { checkArgNotNull(other, "other"); if (!subtractive && !other.subtractive) { return removeFromChars(other.chars); } if (subtractive && other.subtractive) { return new Characters(false, other.removeFromChars(chars).chars); } return subtractive ? addToChars(other.chars) : retainAllChars(other.chars); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(subtractive ? "![" : "["); for (char c : chars) { sb.append(StringUtils.escape(c)); } sb.append(']'); return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Characters)) return false; Characters that = (Characters) o; return subtractive == that.subtractive && equivalent(chars, that.chars); } @Override public int hashCode() { int result = (subtractive ? 1 : 0); result = 31 * result + Arrays.hashCode(chars); return result; } private Characters addToChars(char[] chs) { Characters characters = this; for (char c : chs) { characters = characters.addToChars(c); } return characters; } private Characters addToChars(char c) { if (indexOf(chars, c) != -1) return this; char[] newChars = new char[chars.length + 1]; System.arraycopy(chars, 0, newChars, 0, chars.length); newChars[chars.length] = c; return new Characters(subtractive, newChars); } private Characters removeFromChars(char[] chs) { Characters characters = this; for (char c : chs) { characters = characters.removeFromChars(c); } return characters; } private Characters removeFromChars(char c) { int ix = indexOf(chars, c); if (ix == -1) return this; if (chars.length == 1) return subtractive ? Characters.ALL : Characters.NONE; char[] newChars = new char[chars.length - 1]; System.arraycopy(chars, 0, newChars, 0, ix); System.arraycopy(chars, ix + 1, newChars, ix, chars.length - ix - 1); return new Characters(subtractive, newChars); } private Characters retainAllChars(char[] chs) { Characters characters = this; for (char c : chars) { if (indexOf(chs, c) == -1) { characters = characters.removeFromChars(c); } } return characters; } private static int indexOf(char[] chars, char c) { for (int i = 0; i < chars.length; i++) { if (chars[i] == c) return i; } return -1; } // order independent Array.equals() private static boolean equivalent(char[] a, char[] b) { checkArgNotNull(a, "a"); checkArgNotNull(b, "b"); if (a == b) return true; int length = a.length; if (b.length != length) return false; outer: for (int i = 0; i < length; i++) { char ac = a[i]; for (int j = 0; j < length; j++) { if (ac == b[j]) continue outer; } return false; } return true; } /** * Creates a new Characters instance containing only the given char. * * @param c the char * @return a new Characters object */ public static Characters of(char c) { return new Characters(false, new char[] {c}); } /** * Creates a new Characters instance containing only the given chars. * * @param chars the chars * @return a new Characters object */ public static Characters of(char... chars) { return chars.length == 0 ? Characters.NONE : new Characters(false, chars.clone()); } /** * Creates a new Characters instance containing only the given chars. * * @param chars the chars * @return a new Characters object */ public static Characters of(String chars) { return StringUtils.isEmpty(chars) ? Characters.NONE : new Characters(false, chars.toCharArray()); } /** * Creates a new Characters instance containing all characters minus the given one. * * @param c the char to NOT include * @return a new Characters object */ public static Characters allBut(char c) { return new Characters(true, new char[] {c}); } /** * Creates a new Characters instance containing all characters minus the given ones. * * @param chars the chars to NOT include * @return a new Characters object */ public static Characters allBut(char... chars) { return chars.length == 0 ? Characters.ALL : new Characters(true, chars.clone()); } /** * Creates a new Characters instance containing all characters minus the given ones. * * @param chars the chars to NOT include * @return a new Characters object */ public static Characters allBut(String chars) { return StringUtils.isEmpty(chars) ? Characters.ALL : new Characters(true, chars.toCharArray()); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/Chars.java000066400000000000000000000044761421263112100271540ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; public class Chars { private Chars() {} /** * Special non-character used during error recovery. Signals that an illegal input character was skipped at this * input location. */ public static final char DEL_ERROR = '\uFDEA'; /** * Special non-character used during error recovery. Signals that the character at the following input location * was expected but not present in the input buffer. */ public static final char INS_ERROR = '\uFDEB'; /** * Special non-character used during error recovery. Signals that a rule resynchronization has to be performed * at the current input location. */ public static final char RESYNC = '\uFDEC'; /** * Special non-character used during error recovery. Signals that all characters up to the RESYNC_END * character need to be skipped as part of a resynchronization. */ public static final char RESYNC_START = '\uFDED'; /** * Special non-character used during error recovery. Signals the end of a resynchronization block. */ public static final char RESYNC_END = '\uFDEE'; /** * Special non-character used during error recovery. Signals a resynchronization at EOI. */ public static final char RESYNC_EOI = '\uFDEF'; /** * The End-of-Input non-character. */ public static final char EOI = '\uFFFF'; /** * Special non-character used by the {@link org.parboiled.buffers.IndentDedentInputBuffer}. */ public static final char INDENT = '\uFDD0'; /** * Special non-character used by the {@link org.parboiled.buffers.IndentDedentInputBuffer}. */ public static final char DEDENT = '\uFDD1'; } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/Checks.java000066400000000000000000000031331421263112100273010ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.errors.GrammarException; /** * Utility methods for grammar integrity checks. */ public final class Checks { private Checks() {} /** * Throws a GrammarException if the given condition is not met. * * @param condition the condition * @param errorMessageFormat the error message format * @param errorMessageArgs the error message arguments */ public static void ensure(boolean condition, String errorMessageFormat, Object... errorMessageArgs) { if (!condition) { throw new GrammarException(errorMessageFormat, errorMessageArgs); } } /** * Throws a GrammarException if the given condition is not met. * * @param condition the condition * @param errorMessage the error message */ public static void ensure(boolean condition, String errorMessage) { if (!condition) { throw new GrammarException(errorMessage); } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/DebuggingValueStack.java000066400000000000000000000055561421263112100317720ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.common.ConsoleSink; import org.parboiled.common.Sink; import org.parboiled.common.StringUtils; import java.util.LinkedList; public class DebuggingValueStack extends DefaultValueStack { public final Sink log; public DebuggingValueStack() { this(new ConsoleSink()); } public DebuggingValueStack(Sink log) { this.log = log; } public DebuggingValueStack(Iterable values) { this(values, new ConsoleSink()); } public DebuggingValueStack(Iterable values, Sink log) { super(values); this.log = log; } @Override public void clear() { if (head != null) { super.clear(); log("clear"); } } @Override public void restoreSnapshot(Object snapshot) { if (head == null && snapshot == null || head != null && head.equals(snapshot)) return; super.restoreSnapshot(snapshot); log("restoreSnapshot"); } @Override public void push(V value) { super.push(value); log("push"); } @Override public void push(int down, V value) { super.push(down, value); log("push"); } @Override public V pop(int down) { V v = super.pop(down); log("pop"); return v; } @Override public void poke(int down, V value) { super.poke(down, value); log("poke"); } @Override public void swap() { super.swap(); log("swap"); } @Override public void swap3() { super.swap3(); log("swap3"); } @Override public void swap4() { super.swap4(); log("swap4"); } @Override public void swap5() { super.swap5(); log("swap5"); } @Override public void swap6() { super.swap6(); log("swap6"); } protected void log(String action) { log.receive(action); log.receive(StringUtils.repeat(' ', 15 - action.length())); log.receive(": "); LinkedList elements = new LinkedList(); for (V v : this) elements.addFirst(v); log.receive(StringUtils.join(elements, ", ")); log.receive("\n"); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/DefaultValueStack.java000066400000000000000000000172601421263112100314560ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import static org.parboiled.common.Preconditions.*; import java.util.Iterator; /** * An implementation of a stack of value objects providing an efficient snapshot capability and a number of convenience * methods. The current state of the stack can be saved and restored in small constant time with the methods * {@link #takeSnapshot()} and {@link #restoreSnapshot(Object)} ()}. The implementation also serves as an Iterable * over the current stack values (the values are being provided with the last value (on top of the stack) first). * * @param the type of the value objects */ @SuppressWarnings({"ConstantConditions"}) public class DefaultValueStack implements ValueStack { protected static class Element { protected final Object value; protected final Element tail; protected Element(Object value, Element tail) { this.value = value; this.tail = tail; } } protected Element head; protected V tempValue; /** * Initializes an empty value stack. */ public DefaultValueStack() { } /** * Initializes a value stack containing the given values with the last value being at the top of the stack. * * @param values the initial stack values */ public DefaultValueStack(Iterable values) { pushAll(values); } public boolean isEmpty() { return head == null; } public int size() { Element cursor = head; int size = 0; while (cursor != null) { size++; cursor = cursor.tail; } return size; } public void clear() { head = null; } public Object takeSnapshot() { return head; } public void restoreSnapshot(Object snapshot) { try { head = (Element) snapshot; } catch (ClassCastException e) { throw new IllegalArgumentException("Given argument '" + snapshot + "' is not a valid snapshot element"); } } public void push(V value) { head = new Element(value, head); } public void push(int down, V value) { head = push(down, value, head); } private static Element push(int down, Object value, Element head) { if (down == 0) return new Element(value, head); checkArgument(head != null, "Cannot push beyond the bottom of the stack"); if (down > 0) return new Element(head.value, push(down - 1, value, head.tail)); throw new IllegalArgumentException("Argument 'down' must not be negative"); } public void pushAll(V firstValue, V... moreValues) { push(firstValue); for (V value : moreValues) push(value); } public void pushAll(Iterable values) { head = null; for (V value : values) push(value); } public V pop() { return pop(0); } public V pop(int down) { head = pop(down, head); V result = tempValue; tempValue = null; // avoid memory leak return result; } @SuppressWarnings("unchecked") private Element pop(int down, Element head) { checkArgument(head != null, "Cannot pop from beyond the bottom of the stack"); if (down == 0) { tempValue = (V) head.value; return head.tail; } if (down > 0) return new Element(head.value, pop(down - 1, head.tail)); throw new IllegalArgumentException("Argument 'down' must not be negative"); } public V peek() { return peek(0); } @SuppressWarnings({"unchecked"}) public V peek(int down) { return (V) peek(down, head); } @SuppressWarnings({"ConstantConditions"}) private static Object peek(int down, Element head) { checkArgument(head != null, "Cannot peek beyond the bottom of the stack"); if (down == 0) return head.value; if (down > 0) return peek(down - 1, head.tail); throw new IllegalArgumentException("Argument 'down' must not be negative"); } public void poke(V value) { poke(0, value); } public void poke(int down, V value) { head = poke(down, value, head); } private static Element poke(int down, Object value, Element head) { checkArgument(head != null, "Cannot poke beyond the bottom of the stack"); if (down == 0) return new Element(value, head.tail); if (down > 0) return new Element(head.value, poke(down - 1, value, head.tail)); throw new IllegalArgumentException("Argument 'down' must not be negative"); } public void dup() { push(peek()); } public void swap() { Checks.ensure(isSizeGTE(2, head), "Swap not allowed on stack with less than two elements"); Element down1 = head.tail; head = new Element(down1.value, new Element(head.value, down1.tail)); } public void swap3() { Checks.ensure(isSizeGTE(3, head), "Swap3 not allowed on stack with less than 3 elements"); Element down1 = head.tail; Element down2 = down1.tail; head = new Element(down2.value, new Element(down1.value, new Element(head.value, down2.tail))); } public void swap4() { Checks.ensure(isSizeGTE(4, head), "Swap4 not allowed on stack with less than 4 elements"); Element down1 = head.tail; Element down2 = down1.tail; Element down3 = down2.tail; head = new Element(down3.value, new Element(down2.value, new Element(down1.value, new Element(head.value, down3.tail)))); } public void swap5() { Checks.ensure(isSizeGTE(5, head), "Swap5 not allowed on stack with less than 5 elements"); Element down1 = head.tail; Element down2 = down1.tail; Element down3 = down2.tail; Element down4 = down3.tail; head = new Element(down4.value, new Element(down3.value, new Element(down2.value, new Element(down1.value, new Element(head.value, down4.tail))))); } public void swap6() { Checks.ensure(isSizeGTE(6, head), "Swap6 not allowed on stack with less than 6 elements"); Element down1 = head.tail; Element down2 = down1.tail; Element down3 = down2.tail; Element down4 = down3.tail; Element down5 = down4.tail; head = new Element(down5.value, new Element(down4.value, new Element(down3.value, new Element(down2.value, new Element(down1.value, new Element(head.value, down5.tail)))))); } private static boolean isSizeGTE(int minSize, Element head) { return minSize == 1 ? head != null : isSizeGTE(minSize - 1, head.tail); } public Iterator iterator() { return new Iterator() { private Element next = head; public boolean hasNext() { return next != null; } @SuppressWarnings({"unchecked"}) public V next() { V value = (V) next.value; next = next.tail; return value; } public void remove() { throw new UnsupportedOperationException(); } }; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/Filters.java000066400000000000000000000205601421263112100275140ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.Context; import org.parboiled.Node; import org.parboiled.Rule; import org.parboiled.common.Predicate; import org.parboiled.common.Predicates; import org.parboiled.common.Tuple2; import org.parboiled.matchers.Matcher; import org.parboiled.matchers.ProxyMatcher; import java.util.HashSet; import java.util.Set; import static org.parboiled.matchers.MatcherUtils.unwrap; import static org.parboiled.trees.GraphUtils.hasChildren; public class Filters { /** * A predicate for Node tree printing, suppresses printing of parse tree nodes for Optional rules that * do not have sub nodes. */ public static final Predicate> SKIP_EMPTY_OPTS = new Predicate>() { public boolean apply(Node node) { return hasChildren(node) || node.getEndIndex() != node.getStartIndex() || !"Optional".equals(node.getLabel()); } }; /** * A predicate for Node tree printing, suppresses printing of parse tree nodes for ZeroOrMore rules that * do not have sub nodes. */ public static final Predicate> SKIP_EMPTY_ZOMS = new Predicate>() { public boolean apply(Node node) { return hasChildren(node) || node.getEndIndex() != node.getStartIndex() || !"ZeroOrMore".equals(node.getLabel()); } }; /** * A predicate for Node tree printing, suppresses printing of parse tree nodes for Optional and ZeroOrMore rules * that do not have sub nodes. */ public static final Predicate> SKIP_EMPTY_OPTS_AND_ZOMS = Predicates.and(SKIP_EMPTY_OPTS, SKIP_EMPTY_ZOMS); /** * A predicate for rule tree printing. Prevents SOEs by detecting and suppressing loops in the rule tree. * * @return a predicate */ public static Predicate preventLoops() { return new Predicate() { private final Set visited = new HashSet(); public boolean apply(Matcher node) { node = unwrap(node); if (visited.contains(node)) { return false; } visited.add(node); return true; } }; } /** * A predicate usable as a filter (element) of a {@link org.parboiled.parserunners.TracingParseRunner}. * Enables printing of rule tracing log messages for all input in the given range of input lines. * * @param firstLine the number of the first input line to generate tracing message for * @param lastLine the number of the last input line to generate tracing message for * @return a predicate */ public static Predicate, Boolean>> lines(final int firstLine, final int lastLine) { return new Predicate, Boolean>>() { public boolean apply(Tuple2, Boolean> tuple) { int line = tuple.a.getInputBuffer().getPosition(tuple.a.getCurrentIndex()).line; return firstLine <= line && line <= lastLine; } }; } /** * A predicate usable as a filter (element) of a {@link org.parboiled.parserunners.TracingParseRunner}. * Enables printing of rule tracing log messages for all input in the given range of input lines. * * @param firstLine the number of the first input line to generate tracing message for * @return a predicate */ public static Predicate, Boolean>> fromLine(final int firstLine) { return new Predicate, Boolean>>() { public boolean apply(Tuple2, Boolean> tuple) { return tuple.a.getInputBuffer().getPosition(tuple.a.getCurrentIndex()).line >= firstLine; } }; } /** * A predicate usable as a filter (element) of a {@link org.parboiled.parserunners.TracingParseRunner}. * Enables printing of rule tracing log messages for all input in the given range of input lines. * * @param lastLine the number of the last input line to generate tracing message for * @return a predicate */ public static Predicate, Boolean>> untilLine(final int lastLine) { return new Predicate, Boolean>>() { public boolean apply(Tuple2, Boolean> tuple) { return tuple.a.getInputBuffer().getPosition(tuple.a.getCurrentIndex()).line <= lastLine; } }; } /** * A predicate usable as a filter (element) of a {@link org.parboiled.parserunners.TracingParseRunner}. * Enables printing of rule tracing log messages for all given rules and their sub rules. * * @param rules the rules to generate tracing message for * @return a predicate */ public static Predicate, Boolean>> rules(final Rule... rules) { return new Predicate, Boolean>>() { public boolean apply(Tuple2, Boolean> tuple) { MatcherPath path = tuple.a.getPath(); for (Rule rule : rules) if (path.contains((Matcher) rule)) return true; return false; } }; } /** * A predicate usable as a filter (element) of a {@link org.parboiled.parserunners.TracingParseRunner}. * Enables printing of rule tracing log messages for all given rules (without their sub rules). * * @param rules the rules to generate tracing message for * @return a predicate */ public static Predicate, Boolean>> onlyRules(final Rule... rules) { return new Predicate, Boolean>>() { public boolean apply(Tuple2, Boolean> tuple) { for (Rule rule : rules) if (tuple.a.getMatcher() == rule) return true; return false; } }; } /** * A predicate usable as a filter (element) of a {@link org.parboiled.parserunners.TracingParseRunner}. * Enables printing of rule tracing log messages for all sub rules of the given rules. * * @param rules the rules whose sub rules to generate tracing message for * @return a predicate */ public static Predicate, Boolean>> rulesBelow(final Rule... rules) { return new Predicate, Boolean>>() { public boolean apply(Tuple2, Boolean> tuple) { MatcherPath path = tuple.a.getPath(); for (Rule rule : rules) { Matcher matcher = (Matcher) rule; if (tuple.a.getMatcher() != matcher && path.contains(matcher)) return true; } return false; } }; } /** * A predicate usable as a filter (element) of a {@link org.parboiled.parserunners.TracingParseRunner}. * Enables printing of rule tracing log messages for all matched rules. * * @return a predicate */ public static Predicate, Boolean>> onlyMatches() { return new Predicate, Boolean>>() { public boolean apply(Tuple2, Boolean> tuple) { return tuple.b; } }; } /** * A predicate usable as a filter (element) of a {@link org.parboiled.parserunners.TracingParseRunner}. * Enables printing of rule tracing log messages for all mismatched rules. * * @return a predicate */ public static Predicate, Boolean>> onlyMismatches() { return new Predicate, Boolean>>() { public boolean apply(Tuple2, Boolean> tuple) { return !tuple.b; } }; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/IndexRange.java000066400000000000000000000101241421263112100301230ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.common.Preconditions.checkArgument; /** * A simple immutable container for a range of indices into an underlying InputBuffer. */ public final class IndexRange { public static final IndexRange EMPTY = new IndexRange(0, 0); /** * The index of the first character in the range. */ public final int start; /** * The index of the character following the last character of the range. */ public final int end; public IndexRange(int start, int end) { checkArgument(start >= 0, "start must be >= 0"); checkArgument(end >= start, "end must be >= start"); this.start = start; this.end = end; } /** * Determines whether this range contains no characters. * * @return true if the end matches the start of the range. */ public boolean isEmpty() { return start == end; } /** * @return the number of characters covered by this range */ public int length() { return end - start; } /** * Determines whether this range overlaps with the given other one. * * @param other the other range * @return true if there is at least one index that is contained in both ranges */ public boolean overlapsWith(IndexRange other) { checkArgNotNull(other, "other"); return end > other.start && other.end > start; } /** * Determines whether this range immediated follows the given other one. * * @param other the other range * @return true if this range immediated follows the given other one */ public boolean isPrecededBy(IndexRange other) { checkArgNotNull(other, "other"); return other.end == start; } /** * Determines whether this range is immediated followed by the given other one. * * @param other the other range * @return true if this range is immediated followed by the given other one */ public boolean isFollowedBy(IndexRange other) { checkArgNotNull(other, "other"); return end == other.start; } /** * Determines whether this range immediated follows or precedes the given other one. * * @param other the other range * @return true if this range immediated follows or precedes the given other one. */ public boolean touches(IndexRange other) { checkArgNotNull(other, "other"); return other.end == start || end == other.start; } /** * Created a new IndexRange that spans all characters between the smallest and the highest index of the two ranges. * * @param other the other range * @return a new IndexRange instance */ public IndexRange mergedWith(IndexRange other) { checkArgNotNull(other, "other"); return new IndexRange(Math.min(start, other.start), Math.max(end, other.end)); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof IndexRange)) return false; IndexRange that = (IndexRange) o; return end == that.end && start == that.start; } @Override public int hashCode() { int result = start; result = 31 * result + end; return result; } @Override public String toString() { return "IndexRange{" + "start=" + start + ", end=" + end + '}'; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/LabelPrefixPredicate.java000066400000000000000000000024521421263112100321220ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.Node; import org.parboiled.common.Predicate; import org.parboiled.common.StringUtils; /** * A simple Node predicate determining whether a Node matches a given label prefix. * Useful for example for various methods of the {@link ParseTreeUtils}. * * @param the type of the value field of a parse tree node */ public class LabelPrefixPredicate implements Predicate> { private final String labelPrefix; public LabelPrefixPredicate(String labelPrefix) { this.labelPrefix = labelPrefix; } public boolean apply(Node input) { return input != null && StringUtils.startsWith(input.getLabel(), labelPrefix); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/MatcherPath.java000066400000000000000000000101311421263112100302750ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import static org.parboiled.common.Preconditions.*; import org.parboiled.matchers.Matcher; /** * Holds a snapshot of the current {@link Matcher} stack at a certain point during the parsing process. * Implemented as a specialized, immutable single-linked list of Element objects with the deepest stack Element * in the first position and the root at the end. */ public class MatcherPath { public static class Element { public final Matcher matcher; public final int startIndex; public final int level; public Element(Matcher matcher, int startIndex, int level) { this.matcher = matcher; this.startIndex = startIndex; this.level = level; } } public final Element element; public final MatcherPath parent; /** * Constructs a new MatcherPath wrapping the given elements. * Normally you don't construct a MatcherPath directly but rather call {@link org.parboiled.Context#getPath()} to * get one. * * @param element the last element of this path * @param parent the parent path */ public MatcherPath(Element element, MatcherPath parent) { this.element = checkArgNotNull(element, "element"); this.parent = parent; } /** * @return the length of this path, i.e. the number of matchers contained in it */ public int length() { return element.level + 1; } /** * Determines whether this path is a prefix of the given other path. * * @param that the other path * @return true if this path is a prefix of the given other path */ public boolean isPrefixOf(MatcherPath that) { checkArgNotNull(that, "that"); return element.level <= that.element.level && (this == that || (that.parent != null && isPrefixOf(that.parent))); } /** * Returns the Element at the given level. * @param level the level to get the element from * @return the element */ public Element getElementAtLevel(int level) { checkArgument(level >= 0); if (level > element.level) return null; if (level < element.level) return parent.getElementAtLevel(level); return element; } /** * Returns the common prefix of this MatcherPath and the given other one. * * @param that the other path * @return the common prefix or null */ public MatcherPath commonPrefix(MatcherPath that) { checkArgNotNull(that, "that"); if (element.level > that.element.level) return parent.commonPrefix(that); if (element.level < that.element.level) return commonPrefix(that.parent); if (this == that) return this; return (parent != null && that.parent != null) ? parent.commonPrefix(that.parent) : null; } /** * Determines whether the given matcher is contained in this path. * * @param matcher the matcher * @return true if contained */ public boolean contains(Matcher matcher) { return element.matcher == matcher || (parent != null && parent.contains(matcher)); } @Override public String toString() { return toString(null); } public String toString(MatcherPath skipPrefix) { return print(new StringBuilder(), skipPrefix).toString(); } private StringBuilder print(StringBuilder sb, MatcherPath skipPrefix) { return (parent == skipPrefix ? sb : parent.print(sb, skipPrefix).append('/')).append(element.matcher); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/MatcherPosition.java000066400000000000000000000026551421263112100312210ustar00rootroot00000000000000/* * Copyright (C) 2013 Chris Leishman * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.matchers.Matcher; public class MatcherPosition { private final Matcher matcher; private final Integer index; protected MatcherPosition(Matcher matcher, Integer index) { this.matcher = matcher; this.index = index; } public static MatcherPosition at(Matcher matcher, Integer index) { return new MatcherPosition(matcher, index); } @Override public int hashCode() { return 31 * matcher.hashCode() * index; } @Override public boolean equals(Object obj) { if (!(obj instanceof MatcherPosition)) { return false; } if (this == obj) { return true; } MatcherPosition other = (MatcherPosition)obj; return matcher == other.matcher && index == other.index; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/NodeFormatter.java000066400000000000000000000031161421263112100306530ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import static org.parboiled.common.Preconditions.*; import org.parboiled.Node; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.Formatter; import org.parboiled.common.StringUtils; /** * A simple Formatter that provides String representation for parse tree nodes. */ public class NodeFormatter implements Formatter> { private final InputBuffer inputBuffer; /** * Creates a new NodeFormatter. * * @param inputBuffer the input buffer underlying the parse tree whose nodes are to be formatted. */ public NodeFormatter(InputBuffer inputBuffer) { this.inputBuffer = checkArgNotNull(inputBuffer, "inputBuffer"); } public String format(Node node) { String nodeLabel = node.toString(); String nodeText = StringUtils.escape(ParseTreeUtils.getNodeText(node, inputBuffer)); return StringUtils.isEmpty(nodeText) ? nodeLabel : nodeLabel + " '" + nodeText + '\''; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/ParseTreeUtils.java000066400000000000000000000362611421263112100310240ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.Node; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.Predicate; import org.parboiled.common.Predicates; import org.parboiled.common.StringUtils; import java.util.Collection; import java.util.List; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.trees.GraphUtils.hasChildren; import static org.parboiled.trees.GraphUtils.printTree; /** * General utility methods for operating on parse trees. */ public final class ParseTreeUtils { private ParseTreeUtils() {} /** *

Returns the parse tree node underneath the given parent that matches the given path.

*

The path is a '/' separated list of node label prefixes describing the ancestor chain of the node to look for * relative to the given parent node. If there are several nodes that match the given path the method * returns the first one unless the respective path segments has the special prefix "last:". In this case the * last matching node is returned. *

Example: "per/last:so/fix" will return the first node, whose label starts with "fix" under the last * node, whose label starts with "so" under the first node, whose label starts with "per".

* If parent is null or no node is found the method returns null. * * @param parent the parent Node * @param path the path to the Node being searched for * @return the Node if found or null if not found */ public static Node findNodeByPath(Node parent, String path) { checkArgNotNull(path, "path"); return parent != null && hasChildren(parent) ? findNodeByPath(parent.getChildren(), path) : null; } /** * Returns the node underneath the given parents that matches the given path. * See {@link #findNodeByPath(org.parboiled.Node, String)} )} for a description of the path argument. * If the given collections of parents is null or empty or no node is found the method returns null. * * @param parents the parent Nodes to look through * @param path the path to the Node being searched for * @return the Node if found or null if not found */ public static Node findNodeByPath(List> parents, String path) { checkArgNotNull(path, "path"); if (parents != null && !parents.isEmpty()) { int separatorIndex = path.indexOf('/'); String prefix = separatorIndex != -1 ? path.substring(0, separatorIndex) : path; int start = 0, step = 1; if (prefix.startsWith("last:")) { prefix = prefix.substring(5); start = parents.size() - 1; step = -1; } for (int i = start; 0 <= i && i < parents.size(); i += step) { Node child = parents.get(i); if (StringUtils.startsWith(child.getLabel(), prefix)) { return separatorIndex == -1 ? child : findNodeByPath(child, path.substring(separatorIndex + 1)); } } } return null; } /** * Collects all nodes underneath the given parent that match the given path. * The path is a '/' separated list of node label prefixes describing the ancestor chain of the node to look for * relative to the given parent node. * * @param parent the parent Node * @param path the path to the Nodes being searched for * @param collection the collection to collect the found Nodes into * @return the same collection instance passed as a parameter */ public static >> C collectNodesByPath(Node parent, String path, C collection) { checkArgNotNull(path, "path"); checkArgNotNull(collection, "collection"); return parent != null && hasChildren(parent) ? collectNodesByPath(parent.getChildren(), path, collection) : collection; } /** * Collects all nodes underneath the given parents that match the given path. * The path is a '/' separated list of node label prefixes describing the ancestor chain of the node to look for * relative to the given parent nodes. * * @param parents the parent Nodes to look through * @param path the path to the Nodes being searched for * @param collection the collection to collect the found Nodes into * @return the same collection instance passed as a parameter */ public static >> C collectNodesByPath(List> parents, String path, C collection) { checkArgNotNull(path, "path"); checkArgNotNull(collection, "collection"); if (parents != null && !parents.isEmpty()) { int separatorIndex = path.indexOf('/'); String prefix = separatorIndex != -1 ? path.substring(0, separatorIndex) : path; for (Node child : parents) { if (StringUtils.startsWith(child.getLabel(), prefix)) { if (separatorIndex == -1) { collection.add(child); } else { collectNodesByPath(child, path.substring(separatorIndex + 1), collection); } } } } return collection; } /** * Returns the first node underneath the given parent for which the given predicate evaluates to true. * If parent is null or no node is found the method returns null. * * @param parent the parent Node * @param predicate the predicate * @return the Node if found or null if not found */ public static Node findNode(Node parent, Predicate> predicate) { checkArgNotNull(predicate, "predicate"); if (parent != null) { if (predicate.apply(parent)) return parent; if (hasChildren(parent)) { Node found = findNode(parent.getChildren(), predicate); if (found != null) return found; } } return null; } /** * Returns the first node underneath the given parents for which the given predicate evaluates to true. * If parents is null or empty or no node is found the method returns null. * * @param parents the parent Nodes to look through * @param predicate the predicate * @return the Node if found or null if not found */ public static Node findNode(List> parents, Predicate> predicate) { checkArgNotNull(predicate, "predicate"); if (parents != null && !parents.isEmpty()) { for (Node child : parents) { Node found = findNode(child, predicate); if (found != null) return found; } } return null; } /** * Returns the first node underneath the given parent for which matches the given label prefix. * If parents is null or empty or no node is found the method returns null. * * @param parent the parent node * @param labelPrefix the label prefix to look for * @return the Node if found or null if not found */ public static Node findNodeByLabel(Node parent, String labelPrefix) { return findNode(parent, new LabelPrefixPredicate(labelPrefix)); } /** * Returns the first node underneath the given parents which matches the given label prefix. * If parents is null or empty or no node is found the method returns null. * * @param parents the parent Nodes to look through * @param labelPrefix the label prefix to look for * @return the Node if found or null if not found */ public static Node findNodeByLabel(List> parents, String labelPrefix) { return findNode(parents, new LabelPrefixPredicate(labelPrefix)); } /** * Returns the last node underneath the given parent for which the given predicate evaluates to true. * If parent is null or no node is found the method returns null. * * @param parent the parent Node * @param predicate the predicate * @return the Node if found or null if not found */ public static Node findLastNode(Node parent, Predicate> predicate) { checkArgNotNull(predicate, "predicate"); if (parent != null) { if (predicate.apply(parent)) return parent; if (hasChildren(parent)) { Node found = findLastNode(parent.getChildren(), predicate); if (found != null) return found; } } return null; } /** * Returns the last node underneath the given parents for which the given predicate evaluates to true. * If parents is null or empty or no node is found the method returns null. * * @param parents the parent Nodes to look through * @param predicate the predicate * @return the Node if found or null if not found */ public static Node findLastNode(List> parents, Predicate> predicate) { checkArgNotNull(predicate, "predicate"); if (parents != null && !parents.isEmpty()) { int parentsSize = parents.size(); for (int i = parentsSize - 1; i >= 0; i--) { Node found = findLastNode(parents.get(i), predicate); if (found != null) return found; } } return null; } /** * Collects all nodes underneath the given parent for which the given predicate evaluates to true. * * @param parent the parent Node * @param predicate the predicate * @param collection the collection to collect the found Nodes into * @return the same collection instance passed as a parameter */ public static >> C collectNodes(Node parent, Predicate> predicate, C collection) { checkArgNotNull(predicate, "predicate"); checkArgNotNull(collection, "collection"); return parent != null && hasChildren(parent) ? collectNodes(parent.getChildren(), predicate, collection) : collection; } /** * Returns the input text matched by the given node, with error correction. * * @param node the node * @param inputBuffer the underlying inputBuffer * @return null if node is null otherwise a string with the matched input text (which can be empty) */ public static String getNodeText(Node node, InputBuffer inputBuffer) { checkArgNotNull(node, "node"); checkArgNotNull(inputBuffer, "inputBuffer"); if (node.hasError()) { // if the node has a parse error we cannot simply cut a string out of the underlying input buffer, since we // would also include illegal characters, so we need to build it constructively StringBuilder sb = new StringBuilder(); for (int i = node.getStartIndex(); i < node.getEndIndex(); i++) { char c = inputBuffer.charAt(i); switch (c) { case Chars.DEL_ERROR: i++; break; case Chars.INS_ERROR: case Chars.EOI: break; case Chars.RESYNC_START: i++; while (inputBuffer.charAt(i) != Chars.RESYNC_END) i++; break; case Chars.RESYNC_END: case Chars.RESYNC_EOI: case Chars.RESYNC: // we should only see proper RESYNC_START / RESYNC_END blocks throw new IllegalStateException(); default: sb.append(c); } } return sb.toString(); } return inputBuffer.extract(node.getStartIndex(), node.getEndIndex()); } /** * Collects all nodes underneath the given parents for which the given predicate evaluates to true. * * @param parents the parent Nodes to look through * @param predicate the predicate * @param collection the collection to collect the found Nodes into * @return the same collection instance passed as a parameter */ public static >> C collectNodes(List> parents, Predicate> predicate, C collection) { checkArgNotNull(predicate, "predicate"); checkArgNotNull(collection, "collection"); if (parents != null && !parents.isEmpty()) { for (Node child : parents) { if (predicate.apply(child)) { collection.add(child); } collectNodes(child, predicate, collection); } } return collection; } /** * Creates a readable string represenation of the parse tree in the given {@link ParsingResult} object. * * @param parsingResult the parsing result containing the parse tree * @return a new String */ public static String printNodeTree(ParsingResult parsingResult) { checkArgNotNull(parsingResult, "parsingResult"); return printNodeTree(parsingResult, Predicates.>alwaysTrue(), Predicates.>alwaysTrue()); } /** * Creates a readable string represenation of the parse tree in thee given {@link ParsingResult} object. * The given filter predicate determines whether a particular node (incl. its subtree) is printed or not. * * @param parsingResult the parsing result containing the parse tree * @param nodeFilter the predicate selecting the nodes to print * @param subTreeFilter the predicate determining whether to descend into a given nodes subtree or not * @return a new String */ public static String printNodeTree(ParsingResult parsingResult, Predicate> nodeFilter, Predicate> subTreeFilter) { checkArgNotNull(parsingResult, "parsingResult"); checkArgNotNull(nodeFilter, "nodeFilter"); checkArgNotNull(subTreeFilter, "subTreeFilter"); return printTree(parsingResult.parseTreeRoot, new NodeFormatter(parsingResult.inputBuffer), nodeFilter, subTreeFilter); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/ParsingResult.java000066400000000000000000000054331421263112100307100ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import static org.parboiled.common.Preconditions.*; import org.parboiled.Node; import org.parboiled.buffers.InputBuffer; import org.parboiled.errors.ParseError; import java.util.List; /** * A simple container encapsulating the result of a parsing run. */ public class ParsingResult { /** * Indicates whether the input was successfully parsed. */ public final boolean matched; /** * The root node of the parse tree created by the parsing run. This field will only be non-null when * parse-tree-building has been enabled. */ public final Node parseTreeRoot; /** * The top value of the value stack at the end of the parsing run or null, if the value stack is empty. */ public final V resultValue; /** * The ValueStack used during the parsing run containing all values not popped of the stack by the parser. */ public final ValueStack valueStack; /** * The list of parse errors created during the parsing run. */ public final List parseErrors; /** * The underlying input buffer. */ public final InputBuffer inputBuffer; /** * Creates a new ParsingResult. * * @param matched true if the rule matched the input * @param parseTreeRoot the parse tree root node * @param valueStack the value stack of the parsing run * @param parseErrors the list of parse errors * @param inputBuffer the input buffer */ public ParsingResult(boolean matched, Node parseTreeRoot, ValueStack valueStack, List parseErrors, InputBuffer inputBuffer) { this.matched = matched; this.parseTreeRoot = parseTreeRoot; this.valueStack = checkArgNotNull(valueStack, "valueStack"); this.resultValue = valueStack.isEmpty() ? null : valueStack.peek(); this.parseErrors = checkArgNotNull(parseErrors, "parseErrors"); this.inputBuffer = checkArgNotNull(inputBuffer, "inputBuffer"); } /** * @return true if this parsing result contains parsing errors. */ public boolean hasErrors() { return !parseErrors.isEmpty(); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/Position.java000066400000000000000000000027111421263112100277060ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; /** * Simple container class for a line/column position in the input text. */ public final class Position { public final int line; public final int column; public Position(int line, int column) { this.line = line; this.column = column; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Position)) return false; Position position = (Position) o; return column == position.column && line == position.line; } @Override public int hashCode() { int result = line; result = 31 * result + column; return result; } @Override public String toString() { return "Position{" + "line=" + line + ", column=" + column + '}'; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/StringBuilderVar.java000066400000000000000000000075251421263112100313400ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.common.Utils; /** * Simple specialization of a {@link org.parboiled.support.Var} for StringBuilders. * Provides a few convenience helper methods. */ public class StringBuilderVar extends Var { /** * Initializes a new StringVar with a null initial value. */ public StringBuilderVar() { } /** * Initializes a new StringBuilderVar with the given initial StringBuilder instance. * * @param value the initial value */ public StringBuilderVar(StringBuilder value) { super(value); } /** * Returns true if the wrapped string is either null or empty. * * @return true if the wrapped string is either null or empty */ public boolean isEmpty() { return get() == null || get().length() == 0; } /** * @return the String representation of the underlying StringBuilder. */ public String getString() { return get() == null ? "" : get().toString(); } /** * @return the char[] representation of the underlying StringBuilder. */ public char[] getChars() { if (get() == null) return new char[0]; StringBuilder sb = get(); char[] buf = new char[sb.length()]; sb.getChars(0, buf.length, buf, 0); return buf; } /** * Appends the given string. * If this instance is currently uninitialized the given string is used for initialization. * * @param text the text to append * @return true */ public boolean append(String text) { if (get() == null) return set(new StringBuilder(text)); get().append(text); return true; } /** * Appends the given string. * If this instance is currently uninitialized the given string is used for initialization. * * @param text the text to append * @return this instance */ public StringBuilderVar appended(String text) { append(text); return this; } /** * Appends the given char. * If this instance is currently uninitialized the given char is used for initialization. * * @param c the char to append * @return true */ public boolean append(char c) { if (get() == null) return set(new StringBuilder().append(c)); get().append(c); return true; } /** * Appends the given char. * If this instance is currently uninitialized the given char is used for initialization. * * @param c the char to append * @return this instance */ public StringBuilderVar appended(char c) { append(c); return this; } /** * Clears the contents of the wrapped StringBuilder. * If the instance is currently unintialized this method does nothing. * @return true */ public boolean clearContents() { if (get() != null) get().setLength(0); return true; } /** * Clears the contents of the wrapped StringBuilder. * If the instance is currently unintialized this method does nothing. * @return this instance */ public StringBuilderVar contentsCleared() { if (get() != null) get().setLength(0); return this; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/StringVar.java000066400000000000000000000050351421263112100300230ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; /** * Simple specialization of a {@link Var} for Strings. Provides a few convenience helper methods. */ public class StringVar extends Var { /** * Initializes a new StringVar with a null initial value. */ public StringVar() { } /** * Initializes a new StringVar with the given initial value. * * @param value the initial value */ public StringVar(String value) { super(value); } /** * Returns true if the wrapped string is either null or empty. * * @return true if the wrapped string is either null or empty */ public boolean isEmpty() { return get() == null || get().length() == 0; } /** * Appends the given string. * If this instance is currently uninitialized the given string is used for initialization. * * @param text the text to append * @return true */ public boolean append(String text) { return set(get() == null ? text : get().concat(text)); } /** * Appends the given string. * If this instance is currently uninitialized the given string is used for initialization. * * @param text the text to append * @return this instance */ public StringVar appended(String text) { append(text); return this; } /** * Appends the given char. * If this instance is currently uninitialized the given char is used for initialization. * * @param c the char to append * @return true */ public boolean append(char c) { return set(get() == null ? String.valueOf(c) : get() + c); } /** * Appends the given char. * If this instance is currently uninitialized the given string is used for initialization. * * @param c the char to append * @return this instance */ public StringVar appended(char c) { append(c); return this; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/ToStringFormatter.java000066400000000000000000000021501421263112100315340ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.common.Formatter; /** * A simple Formatter falling back to the objects toString() method. * * @param */ public class ToStringFormatter implements Formatter { private final String nullString; public ToStringFormatter() { this("null"); } public ToStringFormatter(String nullString) { this.nullString = nullString; } public String format(T obj) { return obj != null ? obj.toString() : nullString; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/ValueStack.java000066400000000000000000000140011421263112100301370ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; /** * A ValueStack is a stack implementation for parser values. The current state of the stack can be saved and restored * with the methods {@link #takeSnapshot()} and {@link #restoreSnapshot(Object)} ()}, whose implementations should be * super efficient since they are being used extensively during a parsing run. A ValueStack also serves as an Iterable * over the current stack values (the values are being provided with the last value (on top of the stack) first). * * @param the type of the value objects */ public interface ValueStack extends Iterable { /** * Determines whether the stack is empty. * * @return true if empty */ boolean isEmpty(); /** * Returns the number of elements currently on the stack. * * @return the number of elements */ int size(); /** * Clears all values. */ void clear(); /** * Returns an object representing the current state of the stack. * This cost of running this operation is negligible and independent from the size of the stack. * * @return an object representing the current state of the stack */ Object takeSnapshot(); /** * Restores the stack state as previously returned by {@link #takeSnapshot()}. * This cost of running this operation is negligible and independent from the size of the stack. * * @param snapshot a snapshot object previously returned by {@link #takeSnapshot()} */ void restoreSnapshot(Object snapshot); /** * Pushes the given value onto the stack. Equivalent to push(0, value). * * @param value the value */ void push(V value); /** * Inserts the given value a given number of elements below the current top of the stack. * * @param down the number of elements to skip before inserting the value (0 being equivalent to push(value)) * @param value the value * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ void push(int down, V value); /** * Pushes all given elements onto the stack (in the order as given). * * @param firstValue the first value * @param moreValues the other values */ void pushAll(V firstValue, V... moreValues); /** * Pushes all given elements onto the stack (in the order as given). * * @param values the values */ void pushAll(Iterable values); /** * Removes the value at the top of the stack and returns it. * * @return the current top value * @throws IllegalArgumentException if the stack is empty */ V pop(); /** * Removes the value the given number of elements below the top of the stack. * * @param down the number of elements to skip before removing the value (0 being equivalent to pop()) * @return the value * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ V pop(int down); /** * Returns the value at the top of the stack without removing it. * * @return the current top value * @throws IllegalArgumentException if the stack is empty */ V peek(); /** * Returns the value the given number of elements below the top of the stack without removing it. * * @param down the number of elements to skip (0 being equivalent to peek()) * @return the value * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ V peek(int down); /** * Replaces the current top value with the given value. Equivalent to poke(0, value). * * @param value the value * @throws IllegalArgumentException if the stack is empty */ void poke(V value); /** * Replaces the element the given number of elements below the current top of the stack. * * @param down the number of elements to skip before replacing the value (0 being equivalent to poke(value)) * @param value the value to replace with * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ void poke(int down, V value); /** * Duplicates the top value. Equivalent to push(peek()). * * @throws IllegalArgumentException if the stack is empty */ void dup(); /** * Swaps the top two stack values. * * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least two elements */ void swap(); /** * Reverses the order of the top 3 stack values. * * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least 3 elements */ void swap3(); /** * Reverses the order of the top 4 stack values. * * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least 4 elements */ void swap4(); /** * Reverses the order of the top 5 stack values. * * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least 5 elements */ void swap5(); /** * Reverses the order of the top 5 stack values. * * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least 5 elements */ void swap6(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/support/Var.java000066400000000000000000000102601421263112100266300ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.parboiled.common.Factory; import org.parboiled.common.Reference; import java.util.LinkedList; import static org.parboiled.common.Preconditions.checkArgNotNull; /** *

This class provides a "local variable"-like construct for action expressions in parser rule methods. * Var objects wrap an internal value of an arbitrary (reference) type, can have an initial value, * allow read/write access to their values and can be passed around as parameters to nested rule methods. * Each rule invocation (i.e. rule matching attempt) receives its own Var scope (which is automatically * initialized with the initial value), so actions in recursive rules work just like expected.

*

Var objects generally behave just like local variables with one exception:
* When rule method A() passes a Var defined in its scope to another rule method B() as a parameter and an action * in rule method B() writes to this Var all actions in rule method A() running after B() will "see" this newly written * value (since values in Var objects are passed by reference)

* * @param the type wrapped by this Var */ public class Var extends Reference { private Factory initialValueFactory; private LinkedList stack; private int level; private String name; /** * Initializes a new Var with a null initial value. */ public Var() { this((T) null); } /** * Initializes a new Var with the given initial value. * * @param value the value */ public Var(final T value) { super(value); initialValueFactory = new Factory() { public T create() { return value; } }; } /** * Initializes a new Var. The given factory will be used to create the initial value for each "execution frame" * of the enclosing rule. * * @param initialValueFactory the factory used to create the initial value for a rule execution frame */ public Var(Factory initialValueFactory) { this.initialValueFactory = checkArgNotNull(initialValueFactory, "initialValueFactory"); } /** * Gets the name of this Var. * * @return the name */ public String getName() { return name; } /** * Sets the name of this Var. * * @param name the name */ public void setName(String name) { this.name = name; } /** * Returns the current frame level of this variable, the very first level corresponding to zero. * * @return the current level */ public int getLevel() { return level; } /** * Provides a new frame for the variable. * Potentially existing previous frames are saved. * Normally you do not have to call this method manually as parboiled provides for automatic Var frame management. * * @return true */ public boolean enterFrame() { if (level++ > 0) { if (stack == null) stack = new LinkedList(); stack.add(get()); } return set(initialValueFactory.create()); } /** * Exits a frame previously entered with {@link #enterFrame()}. * Normally you do not have to call this method manually as parboiled provides for automatic Var frame management. * * @return true */ public boolean exitFrame() { if (--level > 0) { set(stack.removeLast()); } return true; } @Override public String toString() { return name != null ? name : super.toString(); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/000077500000000000000000000000001421263112100246445ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/BinaryTreeNode.java000066400000000000000000000021001421263112100303520ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; /** * A {@link TreeNode} specialization with only two child nodes, left and right. * * @param the actual implementation type of this tree node */ public interface BinaryTreeNode> extends TreeNode { /** * Returns the left sub node. * * @return the left sub node */ T left(); /** * Returns the right sub node. * * @return the right sub node */ T right(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/GraphNode.java000066400000000000000000000017731421263112100273660ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; import java.util.List; /** * A node in a directed graph (that may have cycles). * The children list must not contain null entries. * * @param the actual implementation type of this graph node */ public interface GraphNode> { /** * Returns the sub nodes of this node. * * @return the sub nodes */ List getChildren(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/GraphUtils.java000066400000000000000000000136511421263112100275770ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; import static org.parboiled.common.Preconditions.*; import org.parboiled.common.Formatter; import org.parboiled.common.Predicate; import org.parboiled.common.Predicates; import java.util.Collection; import java.util.HashSet; /** * General utility methods for operating on directed graphs (consisting of {@link GraphNode}s). */ public final class GraphUtils { private GraphUtils() {} /** * Returns true if this node is not null and has at least one child node. * * @param node a node * @return true if this node is not null and has at least one child node. */ public static boolean hasChildren(GraphNode node) { return node != null && !node.getChildren().isEmpty(); } /** * Returns the first child node of the given node or null if node is null or does not have any children. * * @param node a node * @return the first child node of the given node or null if node is null or does not have any children */ public static > T getFirstChild(T node) { return hasChildren(node) ? node.getChildren().get(0) : null; } /** * Returns the last child node of the given node or null if node is null or does not have any children. * * @param node a node * @return the last child node of the given node or null if node is null or does not have any children */ public static > T getLastChild(T node) { return hasChildren(node) ? node.getChildren().get(node.getChildren().size() - 1) : null; } /** * Counts all distinct nodes in the graph reachable from the given node. * This method can properly deal with cycles in the graph. * * @param node the root node * @return the number of distinct nodes */ public static > int countAllDistinct(T node) { if (node == null) return 0; return collectAllNodes(node, new HashSet()).size(); } /** * Collects all nodes from the graph reachable from the given node in the given collection. * This method can properly deal with cycles in the graph. * * @param node the root node * @param collection the collection to collect into * @return the same collection passed as a parameter */ public static , C extends Collection> C collectAllNodes(T node, C collection) { // we don't recurse if the collecion already contains the node // this costs a bit of performance but prevents infinite recursion in the case of graph cycles checkArgNotNull(collection, "collection"); if (node != null && !collection.contains(node)) { collection.add(node); for (T child : node.getChildren()) { collectAllNodes(child, collection); } } return collection; } /** * Creates a string representation of the graph reachable from the given node using the given formatter. * * @param node the root node * @param formatter the node formatter * @return a new string */ public static > String printTree(T node, Formatter formatter) { checkArgNotNull(formatter, "formatter"); return printTree(node, formatter, Predicates.alwaysTrue(), Predicates.alwaysTrue()); } /** * Creates a string representation of the graph reachable from the given node using the given formatter. * The given filter predicated determines whether a particular node (and its subtree respectively) is to be * printed or not. * * @param node the root node * @param formatter the node formatter * @param nodeFilter the predicate selecting the nodes to print * @param subTreeFilter the predicate determining whether to descend into a given nodes subtree or not * @return a new string */ public static > String printTree(T node, Formatter formatter, Predicate nodeFilter, Predicate subTreeFilter) { checkArgNotNull(formatter, "formatter"); checkArgNotNull(nodeFilter, "nodeFilter"); checkArgNotNull(subTreeFilter, "subTreeFilter"); return node == null ? "" : printTree(node, formatter, "", new StringBuilder(), nodeFilter, subTreeFilter).toString(); } // private recursion helper private static > StringBuilder printTree(T node, Formatter formatter, String indent, StringBuilder sb, Predicate nodeFilter, Predicate subTreeFilter) { if (nodeFilter.apply(node)) { String line = formatter.format(node); if (line != null) { sb.append(indent).append(line).append("\n"); indent += " "; } } if (subTreeFilter.apply(node)) { for (T sub : node.getChildren()) { printTree(sub, formatter, indent, sb, nodeFilter, subTreeFilter); } } return sb; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/ImmutableBinaryTreeNode.java000066400000000000000000000027221421263112100322240ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; import org.parboiled.common.ImmutableList; /** * A simple immutable implementation of the {@link BinaryTreeNode} interface. * * @param the actual implementation type of this ImmutableBinaryTreeNode */ public class ImmutableBinaryTreeNode> extends ImmutableTreeNode implements BinaryTreeNode { private final T left; private final T right; @SuppressWarnings({"unchecked"}) public ImmutableBinaryTreeNode(T left, T right) { super(left == null ? right == null ? ImmutableList.of() : ImmutableList.of(right) : right == null ? ImmutableList.of(left) : ImmutableList.of(left, right)); this.left = left; this.right = right; } public T left() { return left; } public T right() { return right; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/ImmutableGraphNode.java000066400000000000000000000026651421263112100312270ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; import org.parboiled.common.ImmutableLinkedList; import org.parboiled.common.ImmutableList; import java.util.List; /** * A simple, immutable {@link GraphNode} implementation. * * @param the actual implementation type of this ImmutableGraphNode */ public class ImmutableGraphNode> implements GraphNode { private final List children; public ImmutableGraphNode() { this(null); } public ImmutableGraphNode(List children) { this.children = children == null ? ImmutableList.of() : children instanceof ImmutableList ? children : children instanceof ImmutableLinkedList ? children : ImmutableList.copyOf(children); } public List getChildren() { return children; } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/ImmutableTreeNode.java000066400000000000000000000032501421263112100310540ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; import java.util.List; /** * An {@link ImmutableGraphNode} specialization representing a tree node with a parent field linking back to the nodes * (only) parent. * * @param the actual implementation type of this ImmutableTreeNode */ public class ImmutableTreeNode> extends ImmutableGraphNode implements TreeNode { // we cannot make the parent field final since otherwise we can't create a tree hierarchy with parents linking to // their children and vice versa. So we design this for a bottom up tree construction strategy were children // are created first and then "acquired" by their parents private T parent; public ImmutableTreeNode() { } public ImmutableTreeNode(List children) { super(children); acquireChildren(); } public T getParent() { return parent; } @SuppressWarnings({"unchecked"}) protected void acquireChildren() { for (T children : getChildren()) { ((ImmutableTreeNode) children).parent = this; } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/MutableBinaryTreeNode.java000066400000000000000000000024141421263112100316740ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; /** * A {@link MutableTreeNode} specialization also satisfying the {@link BinaryTreeNode} interface * and providing mutability methods. * * @param the actual implementation type of this MutableBinaryTreeNode */ public interface MutableBinaryTreeNode> extends BinaryTreeNode, MutableTreeNode { /** * Sets the left child node to the given node. * * @param node the node to set as left child */ void setLeft(T node); /** * Sets the right child node to the given node. * * @param node the node to set as right child */ void setRight(T node); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/MutableBinaryTreeNodeImpl.java000066400000000000000000000031021421263112100325110ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; /** * A simple {@link MutableBinaryTreeNode} implementation based on the {@link MutableTreeNodeImpl}. * * @param the actual implementation type of this MutableBinaryTreeNodeImpl */ public class MutableBinaryTreeNodeImpl> extends MutableTreeNodeImpl implements BinaryTreeNode { public MutableBinaryTreeNodeImpl() { super.addChild(0, null); // left super.addChild(1, null); // right } public T left() { return getChildren().get(0); } public void setLeft(T node) { setChild(0, node); } public T right() { return getChildren().get(1); } public void setRight(T node) { setChild(1, node); } @Override public void addChild(int index, T child) { throw new UnsupportedOperationException(); } @Override public T removeChild(int index) { throw new UnsupportedOperationException(); } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/MutableTreeNode.java000066400000000000000000000044401421263112100305300ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; /** * A {@link TreeNode} specialiation that allow for mutability of the tree structure. * The three defined methods are all expected to properly uphold the trees "linking back" contract, where children * have their parent fields point to the node actually holding them in their children list at all times. * The three defined methods are the basic ones required, other convenience methods (like a simple addChild(child) * without index) are defined as static methods of the {@link TreeUtils} class. * * @param the actual implementation type of this TreeNode */ public interface MutableTreeNode> extends TreeNode { /** * Adds the given child to this nodes children list and setting the childs parent field to this node. * If the child is currently attached to another node it is first removed. * * @param index the index under which to insert this child into the children list * @param child the child node to add */ void addChild(int index, T child); /** * Sets the child node at the given index to the given node. The node previously existing at the given child index * is first properly removed by setting its parent field to null. If the child is currently attached to another * node it is first removed from its old parent. * * @param index the index under which to set this child into the children list * @param child the child node to set */ void setChild(int index, T child); /** * Removes the child with the given index. * * @param index the index of the child to remove. * @return the removed child */ T removeChild(int index); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/MutableTreeNodeImpl.java000066400000000000000000000052261421263112100313550ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static org.parboiled.common.Preconditions.*; /** * A base implementation of the {@link MutableTreeNode}. * * @param the actual implementation type of this MutableTreeNodeImpl */ public class MutableTreeNodeImpl> implements MutableTreeNode { private final List children = new ArrayList(); private final List childrenView = Collections.unmodifiableList(children); private T parent; public T getParent() { return parent; } public List getChildren() { return childrenView; } public void addChild(int index, T child) { checkElementIndex(index, children.size() + 1); // detach new child from old parent if (child != null) { if (child.getParent() == this) return; if (child.getParent() != null) { TreeUtils.removeChild(child.getParent(), child); } } // attach new child children.add(index, child); setParent(child, this); } public void setChild(int index, T child) { checkElementIndex(index, children.size()); // detach old child T old = children.get(index); if (old == child) return; setParent(old, null); // detach new child from old parent if (child != null && child.getParent() != this) { TreeUtils.removeChild(child.getParent(), child); } // attach new child children.set(index, child); setParent(child, this); } public T removeChild(int index) { checkElementIndex(index, children.size()); T removed = children.remove(index); setParent(removed, null); return removed; } @SuppressWarnings("unchecked") private static > void setParent(T node, MutableTreeNodeImpl parent) { if (node != null) { ((MutableTreeNodeImpl) node).parent = parent; } } } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/TreeNode.java000066400000000000000000000021061421263112100272130ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; /** * A specialization of a {@link GraphNode} that contains a reference to its parent, thereby making the graph a tree * (since each node can now have only one parent node). * * @param the actual implementation type of this TreeNode */ public interface TreeNode> extends GraphNode { /** * Returns the parent node or null if this node is the root. * * @return the parent node */ T getParent(); } parboiled-1.4.1/parboiled-core/src/main/java/org/parboiled/trees/TreeUtils.java000066400000000000000000000055071421263112100274360ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.trees; import static org.parboiled.common.Preconditions.*; /** * General utility methods for operating on tree, i.e. graphs consisting of {@link TreeNode}s. */ public final class TreeUtils { private TreeUtils() {} /** * Returns the root of the tree the given node is part of. * * @param node the node to get the root of * @return the root or null if the given node is null */ public static > T getRoot(T node) { if (node == null) return null; if (node.getParent() != null) return getRoot(node.getParent()); return node; } /** * Adds a new child node to a given MutableTreeNode parent. * * @param parent the parent node * @param child the child node to add */ public static > void addChild(T parent, T child) { checkArgNotNull(parent, "parent"); parent.addChild(parent.getChildren().size(), child); } /** * Removes the given child from the given parent node. * * @param parent the parent node * @param child the child node */ public static > void removeChild(T parent, T child) { checkArgNotNull(parent, "parent"); int index = parent.getChildren().indexOf(child); checkElementIndex(index, parent.getChildren().size()); parent.removeChild(index); } /** * Performs the following transformation on the given MutableBinaryTreeNode: *
     *        o1                    o2
     *       / \                   / \
     *      A   o2     ====>     o1   C
     *         / \              / \
     *        B   C            A   B
     * 
* * @param node the node to transform * @return the new root after the transformation, which is either the right sub node of the original root * or the original root, if the right sub node is null */ public static > N toLeftAssociativity(N node) { checkArgNotNull(node, "node"); N right = node.right(); if (right == null) return node; node.setRight(right.left()); right.setLeft(node); return right; } } parboiled-1.4.1/parboiled-core/src/test/000077500000000000000000000000001421263112100201045ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/test/java/000077500000000000000000000000001421263112100210255ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/test/java/CoreTest.java000066400000000000000000000023041421263112100234170ustar00rootroot00000000000000import org.parboiled.common.FileUtils; import org.parboiled.common.ImmutableList; import org.scalatestplus.testng.TestNGWrapperSuite; import java.io.File; import java.io.IOException; @SuppressWarnings("unchecked") public class CoreTest extends TestNGWrapperSuite { public CoreTest() throws IOException { super(new org.parboiled.JavaCollectionWrapper(ImmutableList.of(getSuiteFileName())).toList()); } public static String getSuiteFileName() throws IOException { File temp = File.createTempFile("parboiled_testng_suite", ".xml"); temp.deleteOnExit(); String xml = "" + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; FileUtils.writeAllText(xml, temp); return temp.getCanonicalPath(); } } parboiled-1.4.1/parboiled-core/src/test/java/org/000077500000000000000000000000001421263112100216145ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/000077500000000000000000000000001421263112100235555ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/JavaCollectionWrapper.scala000066400000000000000000000004501421263112100310170ustar00rootroot00000000000000package org.parboiled import scala.collection.JavaConverters._ final class JavaCollectionWrapper[A](underlying: java.util.Collection[A]) extends scala.Iterable[A] { def iterator = underlying.iterator.asScala override def size = underlying.size override def isEmpty = underlying.isEmpty } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/buffers/000077500000000000000000000000001421263112100252115ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/buffers/DefaultInputBufferTest.java000066400000000000000000000056031421263112100324560ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.buffers; import org.parboiled.support.Chars; import org.parboiled.support.Position; import org.testng.Assert; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; public class DefaultInputBufferTest { @Test public void testOneliner() { InputBuffer buf = new DefaultInputBuffer("abcdefgh".toCharArray()); Assert.assertEquals(buf.charAt(-4), Chars.EOI); assertEquals(buf.charAt(0), 'a'); assertEquals(buf.charAt(7), 'h'); assertEquals(buf.charAt(8), Chars.EOI); assertEquals(buf.charAt(26), Chars.EOI); assertEquals(buf.extractLine(1), "abcdefgh"); assertEquals(buf.getPosition(0), new Position(1,1)); assertEquals(buf.getPosition(1), new Position(1,2)); assertEquals(buf.getPosition(7), new Position(1,8)); } @Test public void testMultiliner() { InputBuffer buf = new DefaultInputBuffer(("" + "abcd\n" + "ef\r\n" + "\n" + "gh\n" + "\n").toCharArray() ); assertEquals(buf.charAt(0), 'a'); assertEquals(buf.charAt(7), '\r'); assertEquals(buf.charAt(8), '\n'); assertEquals(buf.extractLine(1), "abcd"); assertEquals(buf.extractLine(2), "ef"); assertEquals(buf.extractLine(3), ""); assertEquals(buf.extractLine(4), "gh"); assertEquals(buf.extractLine(5), ""); assertEquals(buf.getPosition(0), new Position(1,1)); assertEquals(buf.getPosition(1), new Position(1,2)); assertEquals(buf.getPosition(2), new Position(1,3)); assertEquals(buf.getPosition(3), new Position(1,4)); assertEquals(buf.getPosition(4), new Position(1,5)); assertEquals(buf.getPosition(5), new Position(2,1)); assertEquals(buf.getPosition(6), new Position(2,2)); assertEquals(buf.getPosition(7), new Position(2,3)); assertEquals(buf.getPosition(8), new Position(2,4)); assertEquals(buf.getPosition(9), new Position(3,1)); assertEquals(buf.getPosition(10), new Position(4,1)); assertEquals(buf.getPosition(11), new Position(4,2)); assertEquals(buf.getPosition(12), new Position(4,3)); assertEquals(buf.getPosition(13), new Position(5,1)); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/buffers/IndentDedentInputBufferTest.java000066400000000000000000000163151421263112100334410ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.buffers; import org.parboiled.common.FileUtils; import org.parboiled.errors.IllegalIndentationException; import org.parboiled.support.Chars; import org.testng.Assert; import org.testng.annotations.Test; import static org.parboiled.buffers.InputBufferUtils.collectContent; import static org.testng.Assert.assertEquals; public class IndentDedentInputBufferTest { @Test public void testIndentDedentInputBuffer0() { InputBuffer buf = new IndentDedentInputBuffer(("" + "###\n" + "\n" + "L1#X\n" + " \n" + " L2\n" + "L12").toCharArray(), 2, "#", false); String bufContent = collectContent(buf); assertEquals(bufContent, "" + "L1\n" + "»L2\n" + "«L12"); assertEquals(buf.extract(0, 2), "L1"); assertEquals(buf.extract(4, 7), "L2\n"); assertEquals(buf.extract(7, 11), "L12"); assertEquals(buf.getPosition(5).toString(), "Position{line=5, column=4}"); assertEquals(buf.extractLine(5), " L2"); } @Test public void testIndentDedentInputBufferEmptyLines() { InputBuffer buf = new IndentDedentInputBuffer(("" + "###\n" + "\n" + "A#X\n" + " \n" + " B\n" + "\n" + " C\n" + "DEF").toCharArray(), 2, "#", true, false); String bufContent = collectContent(buf); assertEquals(bufContent, "" + "\n" + "\n" + "A\n" + "\n" + "»B\n" + "\n" + "C\n" + "«DEF"); assertEquals(buf.extract(2, 4), "A#X\n "); assertEquals(buf.extract(6, 10), "B\n\n C"); assertEquals(buf.extract(11, 15), "DEF"); assertEquals(buf.getPosition(5).toString(), "Position{line=5, column=3}"); assertEquals(buf.extractLine(5), " B"); } @Test public void testIndentDedentInputBuffer1() { InputBuffer buf = new IndentDedentInputBuffer(("" + "level 1\n" + " \tlevel 2\n" + " still level 2\n" + "\t level 3\n" + " also 3\n" + " 2 again\n" + " another 3\n" + "and back to 1\n" + " another level 2 again").toCharArray(), 2, null, false); String bufContent = collectContent(buf); assertEquals(bufContent, "" + "level 1\n" + "»level 2\n" + "still level 2\n" + "»level 3\n" + "also 3\n" + "«2 again\n" + "»another 3\n" + "««and back to 1\n" + "»another level 2 again\n" + "«"); String text = "another 3"; int start = bufContent.indexOf(text); assertEquals(buf.extract(start, start + text.length()), text); text = "back to 1"; start = bufContent.indexOf(text); assertEquals(buf.extract(start, start + text.length()), text); } @Test public void testIndentDedentInputBuffer2() { String input = FileUtils.readAllTextFromResource("IndentDedentBuffer2.test"); InputBuffer buf = new IndentDedentInputBuffer(input.toCharArray(), 4, "#", false); String bufContent = collectContent(buf); assertEquals(bufContent, FileUtils.readAllTextFromResource("IndentDedentBuffer2.converted.test")); String text = "go deep"; int start = bufContent.indexOf(text); assertEquals(buf.extract(start, start + text.length()), text); } @Test public void testIndentDedentInputBuffer3() { String input = FileUtils.readAllTextFromResource("IndentDedentBuffer3.test"); InputBuffer buf = new IndentDedentInputBuffer(input.toCharArray(), 4, "//", false); String bufContent = collectContent(buf); assertEquals(bufContent, FileUtils.readAllTextFromResource("IndentDedentBuffer3.converted.test")); assertEquals(buf.extract(0, bufContent.length()), input); } @Test public void testIndentDedentInputBuffer4() { InputBuffer buf = new IndentDedentInputBuffer(("" + "level 1\n" + " level 2 # comment").toCharArray(), 2, "#", false); String bufContent = collectContent(buf); assertEquals(bufContent, "" + "level 1\n" + "»level 2 \n" + "«"); } @Test public void testIndentDedentInputBufferIllegalIndent1() { InputBuffer buf = new IndentDedentInputBuffer(("" + "level 1\n" + " \tlevel 2\n" + " still level 2\n" + "\t level 3\n" + " also 3\n" + // illegal dedentation " 2 again\n" + " another 3\n" + "and back to 1\n" + " another level 2 again").toCharArray(), 2, null, false); String bufContent = collectContent(buf); assertEquals(bufContent, "" + "level 1\n" + "»level 2\n" + "still level 2\n" + "»level 3\n" + "also 3\n" + "«2 again\n" + "»another 3\n" + "««and back to 1\n" + "»another level 2 again\n" + "«"); } @Test public void testIndentDedentInputBufferIllegalIndent2() { try { new IndentDedentInputBuffer(("" + "level 1\n" + " \tlevel 2\n" + " still level 2\n" + "\t level 3\n" + " illegal!!\n" + " 2 again\n" + " another 3\n" + "and back to 1\n" + " another level 2 again").toCharArray(), 2, null, true); } catch(IllegalIndentationException e) { assertEquals(e.getMessage(), "Illegal indentation in line 5:\n" + " illegal!!\n" + "^^^^^\n"); return; } Assert.fail("Incorrect or no IllegalIndentationException thrown"); } @Test public void testEmptyIndentDedentInputBuffer() { InputBuffer buf = new IndentDedentInputBuffer(new char[0], 2, "#", false); assertEquals(buf.extract(0, 1), ""); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/buffers/MutableInputBufferTest.java000066400000000000000000000207061421263112100324640ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.buffers; import org.parboiled.support.Position; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; public class MutableInputBufferTest { @Test public void testMutableInputBuffer() { MutableInputBuffer buf = new MutableInputBuffer(new DefaultInputBuffer(("" + "abcd\n" + "ef\r\n" + "\n" + "gh\n" + "\n").toCharArray() )); assertEquals(buf.charAt(0), 'a'); assertEquals(buf.charAt(5), 'e'); assertEquals(buf.charAt(8), '\n'); assertEquals(buf.extractLine(1), "abcd"); assertEquals(buf.extractLine(2), "ef"); assertEquals(buf.extractLine(3), ""); assertEquals(buf.extractLine(4), "gh"); assertEquals(buf.extractLine(5), ""); assertEquals(buf.getPosition(0), new Position(1,1)); assertEquals(buf.getPosition(1), new Position(1,2)); assertEquals(buf.getPosition(2), new Position(1,3)); assertEquals(buf.getPosition(3), new Position(1,4)); assertEquals(buf.getPosition(4), new Position(1,5)); assertEquals(buf.getPosition(5), new Position(2,1)); assertEquals(buf.getPosition(6), new Position(2,2)); assertEquals(buf.getPosition(7), new Position(2,3)); assertEquals(buf.getPosition(8), new Position(2,4)); assertEquals(buf.getPosition(9), new Position(3,1)); assertEquals(buf.getPosition(10), new Position(4,1)); assertEquals(buf.getPosition(11), new Position(4,2)); assertEquals(buf.getPosition(12), new Position(4,3)); assertEquals(buf.getPosition(13), new Position(5,1)); // ************* INSERT FIRST CHAR ************ buf.insertChar(6, 'X'); assertEquals(buf.charAt(0), 'a'); assertEquals(buf.charAt(5), 'e'); assertEquals(buf.charAt(6), 'X'); assertEquals(buf.charAt(7), 'f'); assertEquals(buf.charAt(12), 'h'); assertEquals(buf.extractLine(1), "abcd"); assertEquals(buf.extractLine(2), "ef"); assertEquals(buf.extractLine(3), ""); assertEquals(buf.extractLine(4), "gh"); assertEquals(buf.extractLine(5), ""); assertEquals(buf.getPosition(0), new Position(1,1)); assertEquals(buf.getPosition(1), new Position(1,2)); assertEquals(buf.getPosition(2), new Position(1,3)); assertEquals(buf.getPosition(3), new Position(1,4)); assertEquals(buf.getPosition(4), new Position(1,5)); assertEquals(buf.getPosition(5), new Position(2,1)); assertEquals(buf.getPosition(6), new Position(2,2)); assertEquals(buf.getPosition(7), new Position(2,2)); assertEquals(buf.getPosition(8), new Position(2,3)); assertEquals(buf.getPosition(9), new Position(2,4)); assertEquals(buf.getPosition(10), new Position(3,1)); assertEquals(buf.getPosition(11), new Position(4,1)); assertEquals(buf.getPosition(12), new Position(4,2)); assertEquals(buf.getPosition(13), new Position(4,3)); assertEquals(buf.getPosition(14), new Position(5,1)); // ************* INSERT SECOND CHAR ************ buf.insertChar(13, 'Y'); assertEquals(buf.charAt(0), 'a'); assertEquals(buf.charAt(5), 'e'); assertEquals(buf.charAt(6), 'X'); assertEquals(buf.charAt(7), 'f'); assertEquals(buf.charAt(13), 'Y'); assertEquals(buf.charAt(14), '\n'); assertEquals(buf.extractLine(1), "abcd"); assertEquals(buf.extractLine(2), "ef"); assertEquals(buf.extractLine(3), ""); assertEquals(buf.extractLine(4), "gh"); assertEquals(buf.extractLine(5), ""); assertEquals(buf.getPosition(0), new Position(1,1)); assertEquals(buf.getPosition(1), new Position(1,2)); assertEquals(buf.getPosition(2), new Position(1,3)); assertEquals(buf.getPosition(3), new Position(1,4)); assertEquals(buf.getPosition(4), new Position(1,5)); assertEquals(buf.getPosition(5), new Position(2,1)); assertEquals(buf.getPosition(6), new Position(2,2)); assertEquals(buf.getPosition(7), new Position(2,2)); assertEquals(buf.getPosition(8), new Position(2,3)); assertEquals(buf.getPosition(9), new Position(2,4)); assertEquals(buf.getPosition(10), new Position(3,1)); assertEquals(buf.getPosition(11), new Position(4,1)); assertEquals(buf.getPosition(12), new Position(4,2)); assertEquals(buf.getPosition(13), new Position(4,3)); assertEquals(buf.getPosition(14), new Position(4,3)); assertEquals(buf.getPosition(15), new Position(5,1)); // ************* INSERT THIRD CHAR AT SAME POSITION AS FIRST CHAR************ buf.insertChar(6, 'Z'); assertEquals(buf.charAt(0), 'a'); assertEquals(buf.charAt(5), 'e'); assertEquals(buf.charAt(6), 'Z'); assertEquals(buf.charAt(7), 'X'); assertEquals(buf.charAt(8), 'f'); assertEquals(buf.charAt(14), 'Y'); assertEquals(buf.charAt(15), '\n'); assertEquals(buf.extractLine(1), "abcd"); assertEquals(buf.extractLine(2), "ef"); assertEquals(buf.extractLine(3), ""); assertEquals(buf.extractLine(4), "gh"); assertEquals(buf.extractLine(5), ""); assertEquals(buf.getPosition(0), new Position(1,1)); assertEquals(buf.getPosition(1), new Position(1,2)); assertEquals(buf.getPosition(2), new Position(1,3)); assertEquals(buf.getPosition(3), new Position(1,4)); assertEquals(buf.getPosition(4), new Position(1,5)); assertEquals(buf.getPosition(5), new Position(2,1)); assertEquals(buf.getPosition(6), new Position(2,2)); assertEquals(buf.getPosition(7), new Position(2,2)); assertEquals(buf.getPosition(8), new Position(2,2)); assertEquals(buf.getPosition(9), new Position(2,3)); assertEquals(buf.getPosition(10), new Position(2,4)); assertEquals(buf.getPosition(11), new Position(3,1)); assertEquals(buf.getPosition(12), new Position(4,1)); assertEquals(buf.getPosition(13), new Position(4,2)); assertEquals(buf.getPosition(14), new Position(4,3)); assertEquals(buf.getPosition(15), new Position(4,3)); assertEquals(buf.getPosition(16), new Position(5,1)); // ************* UNDO INSERTION OF FIRST CHAR************ assertEquals(buf.undoCharInsertion(7), 'X'); assertEquals(buf.charAt(0), 'a'); assertEquals(buf.charAt(5), 'e'); assertEquals(buf.charAt(6), 'Z'); assertEquals(buf.charAt(7), 'f'); assertEquals(buf.charAt(13), 'Y'); assertEquals(buf.charAt(14), '\n'); assertEquals(buf.extractLine(1), "abcd"); assertEquals(buf.extractLine(2), "ef"); assertEquals(buf.extractLine(3), ""); assertEquals(buf.extractLine(4), "gh"); assertEquals(buf.extractLine(5), ""); assertEquals(buf.getPosition(0), new Position(1,1)); assertEquals(buf.getPosition(1), new Position(1,2)); assertEquals(buf.getPosition(2), new Position(1,3)); assertEquals(buf.getPosition(3), new Position(1,4)); assertEquals(buf.getPosition(4), new Position(1,5)); assertEquals(buf.getPosition(5), new Position(2,1)); assertEquals(buf.getPosition(6), new Position(2,2)); assertEquals(buf.getPosition(7), new Position(2,2)); assertEquals(buf.getPosition(8), new Position(2,3)); assertEquals(buf.getPosition(9), new Position(2,4)); assertEquals(buf.getPosition(10), new Position(3,1)); assertEquals(buf.getPosition(11), new Position(4,1)); assertEquals(buf.getPosition(12), new Position(4,2)); assertEquals(buf.getPosition(13), new Position(4,3)); assertEquals(buf.getPosition(14), new Position(4,3)); assertEquals(buf.getPosition(15), new Position(5,1)); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/common/000077500000000000000000000000001421263112100250455ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/common/ArrayBuilderTest.java000066400000000000000000000020471421263112100311400ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; public class ArrayBuilderTest { @Test public void testAdd() throws Exception { assertEquals(new ArrayBuilder().add(1, 2).add(3).add(4, 5, 6).get(), new Integer[] {1, 2, 3, 4, 5, 6}); assertEquals(new ArrayBuilder().add(1, 2).addNonNulls(3, null, 4, null).get(), new Integer[] {1, 2, 3, 4}); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/common/ImmutableLinkedListTest.java000066400000000000000000000023411421263112100324520ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import org.testng.annotations.Test; import static org.parboiled.common.ImmutableLinkedList.nil; import static org.testng.Assert.assertEquals; public class ImmutableLinkedListTest { @Test public void testImmutableLinkedList() { assertEquals(nil().size(), 0); assertEquals(nil().prepend(5).size(), 1); assertEquals(nil().prepend(5).prepend(7).size(), 2); ImmutableLinkedList abc = nil().prepend("c").prepend("b").prepend("a"); assertEquals(StringUtils.join(abc, ","), "a,b,c"); assertEquals(StringUtils.join(abc.reverse(), ","), "c,b,a"); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/common/IntArrayStackTest.java000066400000000000000000000030511421263112100312660ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import org.testng.annotations.Test; import java.util.Arrays; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; public class IntArrayStackTest { @Test public void testIntArrayStack() { IntArrayStack stack = new IntArrayStack(); stack.push(1); stack.push(2); stack.push(6); assertFalse(stack.isEmpty()); assertEquals(stack.size(), 3); assertEquals(stack.peek(), 6); assertEquals(stack.pop(), 6); assertEquals(stack.size(), 2); stack.push(8); assertEquals(stack.pop(), 8); assertEquals(stack.size(), 2); int[] buf = new int[5]; stack.getElements(buf, 1); assertTrue(Arrays.equals(buf, new int[]{ 0, 1, 2, 0, 0 })); stack.clear(); assertEquals(stack.size(), 0); assertTrue(stack.isEmpty()); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/common/StringUtilsTest.java000066400000000000000000000015741421263112100310460ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import static org.testng.Assert.assertEquals; import org.testng.annotations.Test; public class StringUtilsTest { @Test public void testStringEscaping() { assertEquals(StringUtils.escape("Hallo\r\nMeister!"), "Hallo\\nMeister!"); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/common/UtilsTest.java000066400000000000000000000052771421263112100276630ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.common; import org.parboiled.Node; import static org.parboiled.common.Utils.getTypeArguments; import org.parboiled.support.NodeFormatter; import static org.parboiled.common.Utils.humanize; import static org.testng.Assert.assertEquals; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Arrays; public class UtilsTest { @SuppressWarnings({"unchecked", "InstantiatingObjectToGetClassObject"}) @Test public void testGetTypeArguments() { assertEquals(getTypeArguments(ArrayList.class, new ArrayList() { }.getClass()), Arrays.asList(String.class)); assertEquals(getTypeArguments(Formatter.class, new Formatter() { public String format(Integer object) { return null; } }.getClass()), Arrays.asList(Integer.class)); assertEquals(getTypeArguments(Formatter.class, NodeFormatter.class), Arrays.asList(Node.class)); } @Test public void testHumanize() { assertEquals(humanize(0), "0 "); assertEquals(humanize(1), "1 "); assertEquals(humanize(12), "12 "); assertEquals(humanize(123), "123 "); assertEquals(humanize(1234), "1.234K"); assertEquals(humanize(1200), "1.2K"); assertEquals(humanize(12345), "12.35K"); assertEquals(humanize(123456), "123.5K"); assertEquals(humanize(1234567), "1.235M"); assertEquals(humanize(12345678), "12.35M"); assertEquals(humanize(123456789), "123.5M"); assertEquals(humanize(1234567890), "1.235G"); assertEquals(humanize(12345678901L), "12.35G"); assertEquals(humanize(123456789012L), "123.5G"); assertEquals(humanize(1234567890123L), "1.235T"); assertEquals(humanize(12345678901234L), "12.35T"); assertEquals(humanize(123456789012345L), "123.5T"); assertEquals(humanize(1234567890123456L), "1.235P"); assertEquals(humanize(12345678901234567L), "12.35P"); assertEquals(humanize(123456789012345678L), "123.5P"); assertEquals(humanize(1234567890123456789L), "1.235E"); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/support/000077500000000000000000000000001421263112100252715ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/support/CharactersTest.java000066400000000000000000000051541421263112100310600ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.testng.annotations.Test; import static org.testng.Assert.assertEquals; public class CharactersTest { @Test public void testSimpleCharactersOps() { assertEquals(Characters.NONE.toString(), "[]"); assertEquals(Characters.ALL.toString(), "![]"); assertEquals(Characters.NONE.add('A').toString(), "[A]"); assertEquals(Characters.ALL.add('A'), Characters.ALL); assertEquals(Characters.NONE.remove('A'), Characters.NONE); assertEquals(Characters.ALL.remove('A').toString(), "![A]"); assertEquals(Characters.of('A').remove('A'), Characters.NONE); } @Test public void testMultiCharactersOps() { assertEquals(Characters.NONE.add(Characters.ALL), Characters.ALL); assertEquals(Characters.ALL.add(Characters.NONE), Characters.ALL); assertEquals(Characters.NONE.remove(Characters.ALL), Characters.NONE); assertEquals(Characters.ALL.remove(Characters.NONE), Characters.ALL); assertEquals(Characters.ALL.remove(Characters.of('A', 'B')).toString(), "![AB]"); assertEquals(Characters.ALL.remove(Characters.allBut('A', 'B')).toString(), "[AB]"); assertEquals(Characters.of('A', 'B').add(Characters.of('B', 'C')), Characters.of('A', 'B', 'C')); assertEquals(Characters.allBut('A', 'B').add(Characters.of('B', 'C')), Characters.allBut('A')); assertEquals(Characters.of('A', 'B').add(Characters.allBut('B', 'C')), Characters.allBut('C')); assertEquals(Characters.allBut('A', 'B').add(Characters.allBut('B', 'C')), Characters.allBut('B')); assertEquals(Characters.of('A', 'B').remove(Characters.of('B', 'C')), Characters.of('A')); assertEquals(Characters.allBut('A', 'B').remove(Characters.of('B', 'C')), Characters.allBut('A', 'B', 'C')); assertEquals(Characters.of('A', 'B').remove(Characters.allBut('B', 'C')), Characters.of('B')); assertEquals(Characters.allBut('A', 'B').remove(Characters.allBut('B', 'C')), Characters.of('C')); } } parboiled-1.4.1/parboiled-core/src/test/java/org/parboiled/support/ValueStackTest.java000066400000000000000000000041051421263112100310360ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.support; import org.testng.annotations.Test; import java.util.*; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; public class ValueStackTest { @Test public void testValueStack() { ValueStack stack = new DefaultValueStack(); assertTrue(stack.isEmpty()); stack.push(18); assertEquals(stack.size(), 1); assertFalse(stack.isEmpty()); assertEquals(stack.peek(), (Integer)18); assertEquals(stack.pop(), (Integer)18); assertTrue(stack.isEmpty()); stack.pushAll(18, 26, 42); assertEquals(stack.size(), 3); assertFalse(stack.isEmpty()); assertEquals(stack.peek(), (Integer)42); assertEquals(stack.peek(2), (Integer)18); assertEquals(stack.pop(), (Integer)42); assertEquals(stack.size(), 2); stack.swap(); assertEquals(stack.size(), 2); assertEquals(stack.peek(), (Integer)18); assertEquals(stack.peek(1), (Integer)26); assertEquals(stack.pop(1), (Integer)26); assertEquals(stack.size(), 1); assertEquals(stack.peek(), (Integer)18); assertEquals(stack.peek(0), (Integer)18); stack.pushAll(19, 20); stack.swap3(); List list = new LinkedList(); for (Integer i : stack) list.add(i); assertEquals(list, Arrays.asList(18,19,20)); } } parboiled-1.4.1/parboiled-core/src/test/resources/000077500000000000000000000000001421263112100221165ustar00rootroot00000000000000parboiled-1.4.1/parboiled-core/src/test/resources/IndentDedentBuffer2.converted.test000066400000000000000000000002231421263112100305650ustar00rootroot00000000000000Level 1 »Level 2 »Level 3 more level 3 «back on 2 »3 again ««here we are at one again »and go deep right away still on 2 »three ««parboiled-1.4.1/parboiled-core/src/test/resources/IndentDedentBuffer2.test000066400000000000000000000007261421263112100266050ustar00rootroot00000000000000 # This is an initial comment # on two lines Level 1 # what about comments opening an indentation? Level 2 # another coment # and yet another one Level 3 more level 3 back on 2 3 again # with comment here we are at one again and go deep right away # comments don't matter # they can be anywhere still on 2 #start another one on 3 three # indented comment at the end?parboiled-1.4.1/parboiled-core/src/test/resources/IndentDedentBuffer3.converted.test000066400000000000000000000001721421263112100305710ustar00rootroot00000000000000Level 1 another 1 »Level 2 »# and on 3 ««back at one »reeeaally deep »and even more ««back on one before the endparboiled-1.4.1/parboiled-core/src/test/resources/IndentDedentBuffer3.test000066400000000000000000000003201421263112100265740ustar00rootroot00000000000000Level 1 // a comment another 1 Level 2 // level # and on 3 // not here back at one reeeaally deep // REALLY! // stray blank line and even more back on one before the endparboiled-1.4.1/parboiled-java/000077500000000000000000000000001421263112100163275ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/000077500000000000000000000000001421263112100171165ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/000077500000000000000000000000001421263112100200425ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/java/000077500000000000000000000000001421263112100207635ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/java/org/000077500000000000000000000000001421263112100215525ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/000077500000000000000000000000001421263112100235135ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/BaseActions.java000066400000000000000000000332531421263112100265570ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; import org.parboiled.support.Checks; import org.parboiled.support.IndexRange; import org.parboiled.support.Position; import static org.parboiled.common.Preconditions.checkArgNotNull; /** * Convenience context aware base class defining a number of useful helper methods. * * @param the type of the parser values */ @SuppressWarnings( {"UnusedDeclaration"}) public abstract class BaseActions implements ContextAware { private Context context; /** * The current context for use with action methods. Updated immediately before action calls. * * @return the current context */ public Context getContext() { return context; } /** * ContextAware interface implementation. * * @param context the context */ public void setContext(Context context) { this.context = checkArgNotNull(context, "context"); } /** * Returns the current index in the input buffer. * * @return the current index */ public int currentIndex() { check(); return context.getCurrentIndex(); } /** *

Returns the input text matched by the rule immediately preceding the action expression that is currently * being evaluated. This call can only be used in actions that are part of a Sequence rule and are not at first * position in this Sequence.

* * @return the input text matched by the immediately preceding subrule */ public String match() { check(); return context.getMatch(); } /** * Creates a new {@link IndexRange} instance covering the input text matched by the rule immediately preceding the * action expression that is currently being evaluated. This call can only be used in actions that are part of a * Sequence rule and are not at first position in this Sequence. * * @return a new IndexRange instance */ public IndexRange matchRange() { check(); return context.getMatchRange(); } /** *

Returns the input text matched by the rule immediately preceding the action expression that is currently * being evaluated. If the matched input text is empty the given default string is returned. * This call can only be used in actions that are part of a Sequence rule and are not at first * position in this Sequence.

* * @param defaultString the default string to return if the matched input text is empty * @return the input text matched by the immediately preceding subrule or the default string */ public String matchOrDefault(String defaultString) { check(); String match = context.getMatch(); return match.length() == 0 ? defaultString : match; } /** *

Returns the first character of the input text matched by the rule immediately preceding the action * expression that is currently being evaluated. This call can only be used in actions that are part of a Sequence * rule and are not at first position in this Sequence.

*

If the immediately preceding rule did not match anything this method throws a GrammarException. If you need * to able to handle that case use the getMatch() method.

* * @return the first input char of the input text matched by the immediately preceding subrule or null, * if the previous rule matched nothing */ public char matchedChar() { check(); return context.getFirstMatchChar(); } /** *

Returns the start index of the rule immediately preceding the action expression that is currently * being evaluated. This call can only be used in actions that are part of a Sequence rule and are not at first * position in this Sequence.

* * @return the start index of the context immediately preceding current action */ public int matchStart() { check(); return context.getMatchStartIndex(); } /** *

Returns the end location of the rule immediately preceding the action expression that is currently * being evaluated. This call can only be used in actions that are part of a Sequence rule and are not at first * position in this Sequence.

* * @return the end index of the context immediately preceding current action, i.e. the index of the character * immediately following the last matched character */ public int matchEnd() { check(); return context.getMatchEndIndex(); } /** *

Returns the number of characters matched by the rule immediately preceding the action expression that is * currently being evaluated. This call can only be used in actions that are part of a Sequence rule and are not * at first position in this Sequence.

* * @return the number of characters matched */ public int matchLength() { check(); return context.getMatchLength(); } /** *

Returns the current position in the underlying {@link org.parboiled.buffers.InputBuffer} as a * {@link Position} instance.

* * @return the current position in the underlying inputbuffer */ public Position position() { check(); return context.getPosition(); } /** * Pushes the given value onto the value stack. Equivalent to push(0, value). * * @param value the value to push * @return true */ public boolean push(V value) { check(); context.getValueStack().push(value); return true; } /** * Inserts the given value a given number of elements below the current top of the value stack. * * @param down the number of elements to skip before inserting the value (0 being equivalent to push(value)) * @param value the value * @return true * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ public boolean push(int down, V value) { check(); context.getValueStack().push(down, value); return true; } /** * Pushes all given elements onto the value stack (in the order as given). * * @param firstValue the first value * @param moreValues the other values * @return true */ public boolean pushAll(V firstValue, V... moreValues) { check(); context.getValueStack().pushAll(firstValue, moreValues); return true; } /** * Removes the value at the top of the value stack and returns it. * * @return the current top value * @throws IllegalArgumentException if the stack is empty */ public V pop() { check(); return context.getValueStack().pop(); } /** * Removes the value the given number of elements below the top of the value stack. * * @param down the number of elements to skip before removing the value (0 being equivalent to pop()) * @return the value * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ public V pop(int down) { check(); return context.getValueStack().pop(down); } /** * Removes the value at the top of the value stack. * * @return true * @throws IllegalArgumentException if the stack is empty */ public boolean drop() { check(); context.getValueStack().pop(); return true; } /** * Removes the value the given number of elements below the top of the value stack. * * @param down the number of elements to skip before removing the value (0 being equivalent to drop()) * @return true * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ public boolean drop(int down) { check(); context.getValueStack().pop(down); return true; } /** * Returns the value at the top of the value stack without removing it. * * @return the current top value * @throws IllegalArgumentException if the stack is empty */ public V peek() { check(); return context.getValueStack().peek(); } /** * Returns the value the given number of elements below the top of the value stack without removing it. * * @param down the number of elements to skip (0 being equivalent to peek()) * @return the value * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ public V peek(int down) { check(); return context.getValueStack().peek(down); } /** * Replaces the current top value of the value stack with the given value. Equivalent to poke(0, value). * * @param value the value * @return true * @throws IllegalArgumentException if the stack is empty */ public boolean poke(V value) { check(); context.getValueStack().poke(value); return true; } /** * Replaces the element the given number of elements below the current top of the value stack. * * @param down the number of elements to skip before replacing the value (0 being equivalent to poke(value)) * @param value the value to replace with * @return true * @throws IllegalArgumentException if the stack does not contain enough elements to perform this operation */ public boolean poke(int down, V value) { check(); context.getValueStack().poke(down, value); return true; } /** * Duplicates the top value of the value stack. Equivalent to push(peek()). * * @return true * @throws IllegalArgumentException if the stack is empty */ public boolean dup() { check(); context.getValueStack().dup(); return true; } /** * Swaps the top two elements of the value stack. * * @return true * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least two elements */ public boolean swap() { check(); context.getValueStack().swap(); return true; } /** * Reverses the order of the top 3 value stack elements. * * @return true * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least 3 elements */ public boolean swap3() { check(); context.getValueStack().swap3(); return true; } /** * Reverses the order of the top 4 value stack elements. * * @return true * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least 4 elements */ public boolean swap4() { check(); context.getValueStack().swap4(); return true; } /** * Reverses the order of the top 5 value stack elements. * * @return true * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least 5 elements */ public boolean swap5() { check(); context.getValueStack().swap5(); return true; } /** * Reverses the order of the top 6 value stack elements. * * @return true * @throws org.parboiled.errors.GrammarException * if the stack does not contain at least 6 elements */ public boolean swap6() { check(); context.getValueStack().swap6(); return true; } /** * Returns the next input character about to be matched. * * @return the next input character about to be matched */ public Character currentChar() { check(); return context.getCurrentChar(); } /** * Returns true if the current rule is running somewhere underneath a Test/TestNot rule. * Useful for example for making sure actions are not run inside of a predicate evaluation: * * return Sequence( * ..., * inPredicate() || actions.doSomething() * ); * * * @return true if in a predicate */ public boolean inPredicate() { check(); return context.inPredicate(); } /** * Returns true if the current context is for or below a rule marked @SuppressNode or below one * marked @SuppressSubnodes. * * @return true or false */ public boolean nodeSuppressed() { check(); return context.isNodeSuppressed(); } /** * Determines whether the current rule or a sub rule has recorded a parse error. * Useful for example for making sure actions are not run on erroneous input: * * return Sequence( * ..., * !hasError() && actions.doSomething() * ); * * * @return true if either the current rule or a sub rule has recorded a parse error */ public boolean hasError() { check(); return context.hasError(); } private void check() { Checks.ensure(context != null && context.getMatcher() != null, "Illegal rule definition: Unwrapped action expression!"); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/BaseParser.java000066400000000000000000000667711421263112100264260ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; import org.parboiled.annotations.*; import org.parboiled.common.Utils; import org.parboiled.errors.GrammarException; import org.parboiled.matchers.*; import org.parboiled.support.Characters; import org.parboiled.support.Chars; import org.parboiled.support.Checks; import java.util.Arrays; import static org.parboiled.common.Preconditions.*; /** * Base class of all parboiled parsers. Defines the basic rule creation methods. * * @param the type of the parser values */ @SuppressWarnings( {"UnusedDeclaration"}) public abstract class BaseParser extends BaseActions { /** * Matches the {@link Chars#EOI} (end of input) character. */ public static final Rule EOI = new CharMatcher(Chars.EOI); /** * Matches the special {@link Chars#INDENT} character produces by the org.parboiled.buffers.IndentDedentInputBuffer */ public static final Rule INDENT = new CharMatcher(Chars.INDENT); /** * Matches the special {@link Chars#DEDENT} character produces by the org.parboiled.buffers.IndentDedentInputBuffer */ public static final Rule DEDENT = new CharMatcher(Chars.DEDENT); /** * Matches any character except {@link Chars#EOI}. */ public static final Rule ANY = new AnyMatcher(); /** * Matches nothing and always succeeds. */ public static final Rule EMPTY = new EmptyMatcher(); /** * Matches nothing and always fails. */ public static final Rule NOTHING = new NothingMatcher(); /** * Creates a new instance of this parsers class using the no-arg constructor. If no no-arg constructor * exists this method will fail with a java.lang.NoSuchMethodError. * Using this method is faster than using {@link Parboiled#createParser(Class, Object...)} for creating * new parser instances since this method does not use reflection. * * @param

the parser class * @return a new parser instance */ public

> P newInstance() { throw new UnsupportedOperationException( "Illegal parser instance, you have to use Parboiled.createParser(...) to create your parser instance!"); } /** * Explicitly creates a rule matching the given character. Normally you can just specify the character literal * directly in you rule description. However, if you don't want to go through {@link #fromCharLiteral(char)}, * e.g. because you redefined it, you can also use this wrapper. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param c the char to match * @return a new rule */ @Cached @DontLabel public Rule Ch(char c) { return new CharMatcher(c); } /** * Explicitly creates a rule matching the given character case-independently. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param c the char to match independently of its case * @return a new rule */ @Cached @DontLabel public Rule IgnoreCase(char c) { if (Character.isLowerCase(c) == Character.isUpperCase(c)) { return Ch(c); } return new CharIgnoreCaseMatcher(c); } /** * Creates a rule matching a range of characters from cLow to cHigh (both inclusively). *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param cLow the start char of the range (inclusively) * @param cHigh the end char of the range (inclusively) * @return a new rule */ @Cached @DontLabel public Rule CharRange(char cLow, char cHigh) { return cLow == cHigh ? Ch(cLow) : new CharRangeMatcher(cLow, cHigh); } /** * Creates a new rule that matches any of the characters in the given string. *

Note: This methods provides caching, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param characters the characters * @return a new rule */ @DontLabel public Rule AnyOf(String characters) { checkArgNotNull(characters, "characters"); return AnyOf(characters.toCharArray()); } /** * Creates a new rule that matches any of the characters in the given char array. *

Note: This methods provides caching, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param characters the characters * @return a new rule */ @DontLabel public Rule AnyOf(char[] characters) { checkArgNotNull(characters, "characters"); checkArgument(characters.length > 0); return characters.length == 1 ? Ch(characters[0]) : AnyOf(Characters.of(characters)); } /** * Creates a new rule that matches any of the given characters. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param characters the characters * @return a new rule */ @Cached @DontLabel public Rule AnyOf(Characters characters) { checkArgNotNull(characters, "characters"); if (!characters.isSubtractive() && characters.getChars().length == 1) { return Ch(characters.getChars()[0]); } if (characters.equals(Characters.NONE)) return NOTHING; return new AnyOfMatcher(characters); } /** * Creates a new rule that matches all characters except the ones in the given string and EOI. *

Note: This methods provides caching, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param characters the characters * @return a new rule */ @DontLabel public Rule NoneOf(String characters) { checkArgNotNull(characters, "characters"); return NoneOf(characters.toCharArray()); } /** * Creates a new rule that matches all characters except the ones in the given char array and EOI. *

Note: This methods provides caching, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param characters the characters * @return a new rule */ @DontLabel public Rule NoneOf(char[] characters) { checkArgNotNull(characters, "characters"); checkArgument(characters.length > 0); // make sure to always exclude EOI as well boolean containsEOI = false; for (char c : characters) if (c == Chars.EOI) { containsEOI = true; break; } if (!containsEOI) { char[] withEOI = new char[characters.length + 1]; System.arraycopy(characters, 0, withEOI, 0, characters.length); withEOI[characters.length] = Chars.EOI; characters = withEOI; } return AnyOf(Characters.allBut(characters)); } /** * Explicitly creates a rule matching the given string. Normally you can just specify the string literal * directly in you rule description. However, if you want to not go through {@link #fromStringLiteral(String)}, * e.g. because you redefined it, you can also use this wrapper. *

Note: This methods provides caching, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param string the String to match * @return a new rule */ @DontLabel public Rule String(String string) { checkArgNotNull(string, "string"); return String(string.toCharArray()); } /** * Explicitly creates a rule matching the given string. Normally you can just specify the string literal * directly in you rule description. However, if you want to not go through {@link #fromStringLiteral(String)}, * e.g. because you redefined it, you can also use this wrapper. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param characters the characters of the string to match * @return a new rule */ @Cached @SuppressSubnodes @DontLabel public Rule String(char... characters) { if (characters.length == 1) return Ch(characters[0]); // optimize one-char strings Rule[] matchers = new Rule[characters.length]; for (int i = 0; i < characters.length; i++) { matchers[i] = Ch(characters[i]); } return new StringMatcher(matchers, characters); } /** * Explicitly creates a rule matching the given string in a case-independent fashion. *

Note: This methods provides caching, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param string the string to match * @return a new rule */ @DontLabel public Rule IgnoreCase(String string) { checkArgNotNull(string, "string"); return IgnoreCase(string.toCharArray()); } /** * Explicitly creates a rule matching the given string in a case-independent fashion. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param characters the characters of the string to match * @return a new rule */ @Cached @SuppressSubnodes @DontLabel public Rule IgnoreCase(char... characters) { if (characters.length == 1) return IgnoreCase(characters[0]); // optimize one-char strings Rule[] matchers = new Rule[characters.length]; for (int i = 0; i < characters.length; i++) { matchers[i] = IgnoreCase(characters[i]); } return ((SequenceMatcher) Sequence(matchers)).label('"' + String.valueOf(characters) + '"'); } /** * Creates a new rule that successively tries all of the given subrules and succeeds when the first one of * its subrules matches. If all subrules fail this rule fails as well. *

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param rule the first subrule * @param rule2 the second subrule * @param moreRules the other subrules * @return a new rule */ @DontLabel public Rule FirstOf(Object rule, Object rule2, Object... moreRules) { checkArgNotNull(moreRules, "moreRules"); return FirstOf(Utils.arrayOf(rule, rule2, moreRules)); } /** * Creates a new rule that successively tries all of the given subrules and succeeds when the first one of * its subrules matches. If all subrules fail this rule fails as well. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param rules the subrules * @return a new rule */ @Cached @DontLabel public Rule FirstOf(Object[] rules) { checkArgNotNull(rules, "rules"); if (rules.length == 1) { return toRule(rules[0]); } Rule[] convertedRules = toRules(rules); char[][] chars = new char[rules.length][]; for (int i = 0, convertedRulesLength = convertedRules.length; i < convertedRulesLength; i++) { Object rule = convertedRules[i]; if (rule instanceof StringMatcher) { chars[i] = ((StringMatcher) rule).characters; } else { return new FirstOfMatcher(convertedRules); } } return new FirstOfStringsMatcher(convertedRules, chars); } /** * Creates a new rule that tries repeated matches of its subrule and succeeds if the subrule matches at least once. * If the subrule does not match at least once this rule fails. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param rule the subrule * @return a new rule */ @Cached @DontLabel public Rule OneOrMore(Object rule) { return new OneOrMoreMatcher(toRule(rule)); } /** * Creates a new rule that tries repeated matches of a sequence of the given subrules and succeeds if the sequence * matches at least once. If the sequence does not match at least once this rule fails. *

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param rule the first subrule * @param rule2 the second subrule * @param moreRules the other subrules * @return a new rule */ @DontLabel public Rule OneOrMore(Object rule, Object rule2, Object... moreRules) { checkArgNotNull(moreRules, "moreRules"); return OneOrMore(Sequence(rule, rule2, moreRules)); } /** * Creates a new rule that tries a match on its subrule and always succeeds, independently of the matching * success of its sub rule. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param rule the subrule * @return a new rule */ @Cached @DontLabel public Rule Optional(Object rule) { return new OptionalMatcher(toRule(rule)); } /** * Creates a new rule that tries a match on the sequence of the given subrules and always succeeds, independently * of the matching success of its sub sequence. *

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param rule the first subrule * @param rule2 the second subrule * @param moreRules the other subrules * @return a new rule */ @DontLabel public Rule Optional(Object rule, Object rule2, Object... moreRules) { checkArgNotNull(moreRules, "moreRules"); return Optional(Sequence(rule, rule2, moreRules)); } /** * Creates a new rule that only succeeds if all of its subrule succeed, one after the other. *

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param rule the first subrule * @param rule2 the second subrule * @param moreRules the other subrules * @return a new rule */ @DontLabel public Rule Sequence(Object rule, Object rule2, Object... moreRules) { checkArgNotNull(moreRules, "moreRules"); return Sequence(Utils.arrayOf(rule, rule2, moreRules)); } /** * Creates a new rule that only succeeds if all of its subrule succeed, one after the other. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param rules the sub rules * @return a new rule */ @Cached @DontLabel public Rule Sequence(Object[] rules) { checkArgNotNull(rules, "rules"); return rules.length == 1 ? toRule(rules[0]) : new SequenceMatcher(toRules(rules)); } /** *

Creates a new rule that acts as a syntactic predicate, i.e. tests the given sub rule against the current * input position without actually matching any characters. Succeeds if the sub rule succeeds and fails if the * sub rule rails. Since this rule does not actually consume any input it will never create a parse tree node.

*

Also it carries a {@link SuppressNode} annotation, which means all sub nodes will also never create a parse * tree node. This can be important for actions contained in sub rules of this rule that otherwise expect the * presence of certain parse tree structures in their context. * Also see {@link org.parboiled.annotations.SkipActionsInPredicates}

*

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param rule the subrule * @return a new rule */ @Cached @SuppressNode @DontLabel public Rule Test(Object rule) { Rule subMatcher = toRule(rule); return new TestMatcher(subMatcher); } /** *

Creates a new rule that acts as a syntactic predicate, i.e. tests the sequence of the given sub rule against * the current input position without actually matching any characters. Succeeds if the sub sequence succeeds and * fails if the sub sequence rails. Since this rule does not actually consume any input it will never create a * parse tree node.

*

Also it carries a {@link SuppressNode} annotation, which means all sub nodes will also never create a parse * tree node. This can be important for actions contained in sub rules of this rule that otherwise expect the * presence of certain parse tree structures in their context. * Also see {@link org.parboiled.annotations.SkipActionsInPredicates}

*

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param rule the first subrule * @param rule2 the second subrule * @param moreRules the other subrules * @return a new rule */ @DontLabel public Rule Test(Object rule, Object rule2, Object... moreRules) { checkArgNotNull(moreRules, "moreRules"); return Test(Sequence(rule, rule2, moreRules)); } /** *

Creates a new rule that acts as an inverse syntactic predicate, i.e. tests the given sub rule against the * current input position without actually matching any characters. Succeeds if the sub rule fails and fails if the * sub rule succeeds. Since this rule does not actually consume any input it will never create a parse tree node.

*

Also it carries a {@link SuppressNode} annotation, which means all sub nodes will also never create a parse * tree node. This can be important for actions contained in sub rules of this rule that otherwise expect the * presence of certain parse tree structures in their context. * Also see {@link org.parboiled.annotations.SkipActionsInPredicates}

*

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param rule the subrule * @return a new rule */ @Cached @SuppressNode @DontLabel public Rule TestNot(Object rule) { Rule subMatcher = toRule(rule); return new TestNotMatcher(subMatcher); } /** *

Creates a new rule that acts as an inverse syntactic predicate, i.e. tests the sequence of the given sub rules * against the current input position without actually matching any characters. Succeeds if the sub sequence fails * and fails if the sub sequence succeeds. Since this rule does not actually consume any input it will never create * a parse tree node.

*

Also it carries a {@link SuppressNode} annotation, which means all sub nodes will also never create a parse * tree node. This can be important for actions contained in sub rules of this rule that otherwise expect the * presence of certain parse tree structures in their context. * Also see {@link org.parboiled.annotations.SkipActionsInPredicates}

*

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param rule the first subrule * @param rule2 the second subrule * @param moreRules the other subrules * @return a new rule */ @DontLabel public Rule TestNot(Object rule, Object rule2, Object... moreRules) { checkArgNotNull(moreRules, "moreRules"); return TestNot(Sequence(rule, rule2, moreRules)); } /** * Creates a new rule that tries repeated matches of its subrule. * Succeeds always, even if the subrule doesn't match even once. *

Note: This methods carries a {@link Cached} annotation, which means that multiple invocations with the same * argument will yield the same rule instance.

* * @param rule the subrule * @return a new rule */ @Cached @DontLabel public Rule ZeroOrMore(Object rule) { return new ZeroOrMoreMatcher(toRule(rule)); } /** * Creates a new rule that tries repeated matches of the sequence of the given sub rules. * Succeeds always, even if the sub sequence doesn't match even once. *

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param rule the first subrule * @param rule2 the second subrule * @param moreRules the other subrules * @return a new rule */ @DontLabel public Rule ZeroOrMore(Object rule, Object rule2, Object... moreRules) { checkArgNotNull(moreRules, "moreRules"); return ZeroOrMore(Sequence(rule, rule2, moreRules)); } /** * Creates a new rule that repeatedly matches a given sub rule a certain fixed number of times. *

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param repetitions The number of repetitions to match. Must be >= 0. * @param rule the sub rule to match repeatedly. * @return a new rule */ @Cached @DontLabel public Rule NTimes(int repetitions, Object rule) { return NTimes(repetitions, rule, null); } /** * Creates a new rule that repeatedly matches a given sub rule a certain fixed number of times, optionally * separated by a given separator rule. *

Note: This methods provides caching, which means that multiple invocations with the same * arguments will yield the same rule instance.

* * @param repetitions The number of repetitions to match. Must be >= 0. * @param rule the sub rule to match repeatedly. * @param separator the separator to match, if null the individual sub rules will be matched without separator. * @return a new rule */ @Cached @DontLabel public Rule NTimes(int repetitions, Object rule, Object separator) { checkArgNotNull(rule, "rule"); checkArgument(repetitions >= 0, "repetitions must be non-negative"); switch (repetitions) { case 0: return EMPTY; case 1: return toRule(rule); default: Object[] rules = new Object[separator == null ? repetitions : repetitions * 2 - 1]; if (separator != null) { for (int i = 0; i < rules.length; i++) rules[i] = i % 2 == 0 ? rule : separator; } else Arrays.fill(rules, rule); return Sequence(rules); } } ///************************* "MAGIC" METHODS ***************************/// /** * Explicitly marks the wrapped expression as an action expression. * parboiled transforms the wrapped expression into an {@link Action} instance during parser construction. * * @param expression the expression to turn into an Action * @return the Action wrapping the given expression */ public static Action ACTION(boolean expression) { throw new UnsupportedOperationException("ACTION(...) calls can only be used in Rule creating parser methods"); } ///************************* HELPER METHODS ***************************/// /** * Used internally to convert the given character literal to a parser rule. * You can override this method, e.g. for specifying a Sequence that automatically matches all trailing * whitespace after the character. * * @param c the character * @return the rule */ @DontExtend protected Rule fromCharLiteral(char c) { return Ch(c); } /** * Used internally to convert the given string literal to a parser rule. * You can override this method, e.g. for specifying a Sequence that automatically matches all trailing * whitespace after the string. * * @param string the string * @return the rule */ @DontExtend protected Rule fromStringLiteral(String string) { checkArgNotNull(string, "string"); return fromCharArray(string.toCharArray()); } /** * Used internally to convert the given char array to a parser rule. * You can override this method, e.g. for specifying a Sequence that automatically matches all trailing * whitespace after the characters. * * @param array the char array * @return the rule */ @DontExtend protected Rule fromCharArray(char[] array) { checkArgNotNull(array, "array"); return String(array); } /** * Converts the given object array to an array of rules. * * @param objects the objects to convert * @return the rules corresponding to the given objects */ @DontExtend public Rule[] toRules(Object... objects) { checkArgNotNull(objects, "objects"); Rule[] rules = new Rule[objects.length]; for (int i = 0; i < objects.length; i++) { rules[i] = toRule(objects[i]); } return rules; } /** * Converts the given object to a rule. * This method can be overriden to enable the use of custom objects directly in rule specifications. * * @param obj the object to convert * @return the rule corresponding to the given object */ @DontExtend public Rule toRule(Object obj) { if (obj instanceof Rule) return (Rule) obj; if (obj instanceof Character) return fromCharLiteral((Character) obj); if (obj instanceof String) return fromStringLiteral((String) obj); if (obj instanceof char[]) return fromCharArray((char[]) obj); if (obj instanceof Action) { Action action = (Action) obj; return new ActionMatcher(action); } Checks.ensure(!(obj instanceof Boolean), "Rule specification contains an unwrapped Boolean value, " + "if you were trying to specify a parser action wrap the expression with ACTION(...)"); throw new GrammarException("'" + obj + "' cannot be automatically converted to a parser Rule"); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/Parboiled.java000066400000000000000000000056031421263112100262630ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled; import static org.parboiled.common.Preconditions.*; import org.parboiled.transform.ParserTransformer; import java.lang.reflect.Constructor; import static org.parboiled.common.Utils.findConstructor; /** * Main class providing the high-level entry point into the parboiled library. */ public class Parboiled { protected Parboiled() {} /** *

Creates a parser object whose rule creation methods can then be used with one of the {@link org.parboiled.parserunners.ParseRunner} implementations.

*

Since parboiled needs to extend your parser with certain extra logic (e.g. to prevent infinite recursions * in recursive rule definitions) you cannot create your parser object yourself, but have to go through this method. * Also your parser class has to be derived from {@link BaseParser}. If you want to use a non-default constructor * you can provide its arguments to this method. Make sure your non-default constructor does not use primitive * type parameters (like "int") but rather their boxed counterparts (like "Integer"), otherwise the constructor * will not be found.

*

Performing the rule analysis and extending the parser class is an expensive process (time-wise) and can * take up to several hundred milliseconds for large grammars. However, this cost is only incurred once per * parser class and class loader. Subsequent calls to this method are therefore fast once the initial extension * has been performed.

* * @param parserClass the type of the parser to create * @param constructorArgs optional arguments to the parser class constructor * @return the ready to use parser instance */ @SuppressWarnings({"unchecked"}) public static

, V> P createParser(Class

parserClass, Object... constructorArgs) { checkArgNotNull(parserClass, "parserClass"); try { Class extendedClass = ParserTransformer.transformParser(parserClass); Constructor constructor = findConstructor(extendedClass, constructorArgs); return (P) constructor.newInstance(constructorArgs); } catch (Exception e) { throw new RuntimeException("Error creating extended parser class: " + e.getMessage(), e); } } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/000077500000000000000000000000001421263112100260505ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/BuildParseTree.java000066400000000000000000000032021421263112100315620ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** *

Annotation that can be used on the parser class itself. * Instructs parboiled to build a parse tree during a parsing run. When this annotation is present on the parser class * the parse tree building can be further tweaked by decorating certain rules with {@link SuppressNode}, * {@link SuppressSubnodes} and/or {@link SkipNode}. When this annotation is not present on the parser class the listed * rule annotations do not have any effect because no parse tree is build at all.

*

Note: If the input contains parse errors and you use the {@link org.parboiled.parserunners.RecoveringParseRunner} parboiled * will create parse tree nodes for all rules that have recorded parse errors (note that this always includes the root * rule)

*/ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface BuildParseTree { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/Cached.java000066400000000000000000000024531421263112100300660ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule}s and having parameters. * Instructs parboiled to automatically cache the method return value for the parameters it was created with. * Note that all parser methods returning {@link org.parboiled.Rule} objects and not taking any parameter are * automatically cached and are therefore not allowed to carry this annotation. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Cached { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/DontExtend.java000066400000000000000000000020761421263112100307740ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule} objects. * Instructs parboiled to completely ignore this method during parser extension. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DontExtend { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/DontLabel.java000066400000000000000000000021361421263112100305610ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule} objects or the * parser class itself. Instructs parboiled to not automatically label the created rules. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface DontLabel { } DontSkipActionsInPredicates.java000066400000000000000000000021571421263112100342100ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule} objects. * Overrides a global {@link SkipActionsInPredicates} annotation on the parser * class for the specific method. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface DontSkipActionsInPredicates { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/ExplicitActionsOnly.java000066400000000000000000000025761421263112100326710ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser rule methods (i.e. methods returning a {@link org.parboiled.Rule} or the * parser class itself. * Instructs parboiled to not perform implicit action expression wrapping, i.e. not treat expressions that form * parameters to Boolean.valueOf(boolean) calls as action expressions. * Instead only expressions wrapped by explicit calls to {@link org.parboiled.BaseParser#ACTION(boolean)} will be * treated as action expressions. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface ExplicitActionsOnly { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/Label.java000066400000000000000000000024451421263112100277370ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule} objects. * Instructs parboiled to automatically label the created rules with the given label. * If this annotation is not present on a rule method parboiled automatically names the created rules with the * method name, unless a {@link DontLabel} annotation is present on the method or the parser class. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Label { String value(); } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/MemoMismatches.java000066400000000000000000000020701421263112100316250ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule} objects. * Instructs parboiled to memoize consecutive mismatches of this rule. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface MemoMismatches { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/SkipActionsInPredicates.java000066400000000000000000000034131421263112100334360ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser rule methods (i.e. methods returning a {@link org.parboiled.Rule} or the * parser class itself. * Instructs parboiled to skip the evaluation of action expressions in the rule method (or all methods if the * annotation is used on the parser class itself) if the rule is currently being run inside a Test/TestNot rule * (no matter what the nesting depth is). * Note that this annotation only affects action expressions (explicit or implicit)! Custom action objects, be them * anonymous actions or instances of some other class implementing the {@link org.parboiled.Action} interface still * need to take care of their predicate sensitivities themselves. * If you use this annotation on the parser class itself you can override it on specific rule methods with the * {@link DontSkipActionsInPredicates} annotation. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface SkipActionsInPredicates { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/SkipNode.java000066400000000000000000000027651421263112100304410ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule} objects. * Instructs parboiled to not create a parse tree node for this rule. The parse tree nodes of all subrules are * directly attached to the parent of this rule (or more correctly: the first ancestor not carrying @SkipNode). * Note that, even though a rule carrying @SkipNode does not create a parse tree node of its own and is therefore * "invisible" in the parse tree, the rule still exists as a regular rule in the rule tree and is accompanied by * a "regular" rule {@link org.parboiled.Context} during rule matching. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface SkipNode { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/SuppressNode.java000066400000000000000000000022061421263112100313450ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule} objects. * Instructs parboiled to not create a parse tree node for this rule and all subrules, * which can significantly increase parsing performance. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface SuppressNode { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/annotations/SuppressSubnodes.java000066400000000000000000000022001421263112100322340ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that can be used on parser methods returning {@link org.parboiled.Rule} objects. * Instructs parboiled to not create parse tree nodes for the subrules of the rule, * which can significantly increase parsing performance. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface SuppressSubnodes { } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/test/000077500000000000000000000000001421263112100244725ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/test/ParboiledTest.java000066400000000000000000000066411421263112100301050ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.test; import org.parboiled.Node; import org.parboiled.Rule; import org.parboiled.buffers.InputBuffer; import org.parboiled.common.Predicate; import org.parboiled.parserunners.RecoveringParseRunner; import org.parboiled.parserunners.ReportingParseRunner; import org.parboiled.support.ParsingResult; import java.util.*; import static org.parboiled.errors.ErrorUtils.printParseErrors; import static org.parboiled.support.ParseTreeUtils.printNodeTree; public abstract class ParboiledTest { public class TestResult { public final ParsingResult result; public TestResult(ParsingResult result) { this.result = result; } public TestResult hasNoErrors() { if (result.hasErrors()) { fail("\n--- ParseErrors ---\n" + printParseErrors(result) + "\n--- ParseTree ---\n" + printNodeTree(result) ); } return this; } public TestResult hasErrors(String expectedErrors) { assertEquals(printParseErrors(result), expectedErrors); return this; } public TestResult hasParseTree(String expectedTree) { assertEquals(printNodeTree(result), expectedTree); return this; } public TestResult hasParseTree(Predicate> nodeFilter, Predicate> subTreeFilter, String expectedTree) { assertEquals(printNodeTree(result, nodeFilter, subTreeFilter), expectedTree); return this; } public TestResult hasResult(V... expectedResults) { assertEquals(toListReversed(result.valueStack), Arrays.asList(expectedResults)); return this; } private List toListReversed(Iterable iterable) { List list = new ArrayList(); for (T t : iterable) list.add(t); Collections.reverse(list); return list; } } public TestResult test(Rule rule, String input) { return new TestResult(new ReportingParseRunner(rule).run(input)); } public TestResult test(Rule rule, InputBuffer inputBuffer) { return new TestResult(new ReportingParseRunner(rule).run(inputBuffer)); } public TestResult testWithRecovery(Rule rule, String input) { return new TestResult(new RecoveringParseRunner(rule).run(input)); } public TestResult testWithRecovery(Rule rule, InputBuffer inputBuffer) { return new TestResult(new RecoveringParseRunner(rule).run(inputBuffer)); } protected abstract void fail(String message); protected abstract void assertEquals(Object actual, Object expected); } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/000077500000000000000000000000001421263112100255265ustar00rootroot00000000000000parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/ASMSettings.java000066400000000000000000000004761421263112100305410ustar00rootroot00000000000000package org.parboiled.transform; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; /** * */ public class ASMSettings { public static final int ASM_API = Opcodes.ASM7; public static final int JDK_VERSION = Opcodes.V1_7; public static final int FRAMES = ClassWriter.COMPUTE_FRAMES; } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/ActionClassGenerator.java000066400000000000000000000034411421263112100324450ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import static org.objectweb.asm.Opcodes.*; class ActionClassGenerator extends GroupClassGenerator { public ActionClassGenerator(boolean forceCodeBuilding) { super(forceCodeBuilding); } public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { checkArgNotNull(method, "method"); return method.containsExplicitActions(); } @Override protected boolean appliesTo(InstructionGraphNode node) { return node.isActionRoot(); } @Override protected Type getBaseType() { return Types.BASE_ACTION; } @Override protected void generateMethod(InstructionGroup group, ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "run", '(' + Types.CONTEXT_DESC + ")Z", null, null); insertSetContextCalls(group, 1); convertXLoads(group); group.getInstructions().accept(mv); mv.visitInsn(IRETURN); mv.visitMaxs(0, 0); // trigger automatic computing mv.visitEnd(); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/AsmUtils.java000066400000000000000000000354031421263112100301370ustar00rootroot00000000000000/* * Copyright (c) 2009-2011 Ken Wenzel and Mathias Doenitz * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.transform; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.VarInsnNode; import org.parboiled.BaseParser; import org.parboiled.ContextAware; import org.parboiled.support.Var; import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import static org.parboiled.common.Preconditions.checkArgNotNull; class AsmUtils { private static final LookupFactory lookupFactory = new LookupFactory(); public static ClassReader createClassReader(Class clazz) throws IOException { checkArgNotNull(clazz, "clazz"); String classFilename = clazz.getName().replace('.', '/') + ".class"; InputStream inputStream = clazz.getClassLoader().getResourceAsStream(classFilename); if (inputStream == null) { inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(classFilename); } return new ClassReader(inputStream); } public static String getExtendedParserClassName(String parserClassName) { checkArgNotNull(parserClassName, "parserClassName"); return parserClassName + "$$parboiled"; } private static final Map> classForDesc = new HashMap>(); public static synchronized Class getClassForInternalName(String classDesc) { checkArgNotNull(classDesc, "classDesc"); Class clazz = classForDesc.get(classDesc); if (clazz == null) { if (classDesc.charAt(0) == '[') { Class compType = getClassForType(Type.getType(classDesc.substring(1))); clazz = Array.newInstance(compType, 0).getClass(); } else { String className = classDesc.replace('/', '.'); try { clazz = AsmUtils.class.getClassLoader().loadClass(className); } catch (ClassNotFoundException e) { // If class not found trying the context classLoader try { clazz = Thread.currentThread().getContextClassLoader().loadClass(className); } catch (ClassNotFoundException e2) { throw new RuntimeException("Error loading class '" + className + "' for rule method analysis", e2); } } } classForDesc.put(classDesc, clazz); } return clazz; } public static synchronized void clearClassCache() { classForDesc.clear(); } public static Class getClassForType(Type type) { checkArgNotNull(type, "type"); switch (type.getSort()) { case Type.BOOLEAN: return boolean.class; case Type.BYTE: return byte.class; case Type.CHAR: return char.class; case Type.DOUBLE: return double.class; case Type.FLOAT: return float.class; case Type.INT: return int.class; case Type.LONG: return long.class; case Type.SHORT: return short.class; case Type.VOID: return void.class; case Type.OBJECT: case Type.ARRAY: return getClassForInternalName(type.getInternalName()); } throw new IllegalStateException(); // should be unreachable } public static Field getClassField(String classInternalName, String fieldName) { checkArgNotNull(classInternalName, "classInternalName"); checkArgNotNull(fieldName, "fieldName"); Class clazz = getClassForInternalName(classInternalName); Class current = clazz; while (true) { try { return current.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { current = current.getSuperclass(); if (Object.class.equals(current)) { throw new RuntimeException( "Field '" + fieldName + "' not found in '" + clazz + "\' or any superclass", e); } } } } public static Method getClassMethod(String classInternalName, String methodName, String methodDesc) { checkArgNotNull(classInternalName, "classInternalName"); checkArgNotNull(methodName, "methodName"); checkArgNotNull(methodDesc, "methodDesc"); Class clazz = getClassForInternalName(classInternalName); Type[] types = Type.getArgumentTypes(methodDesc); Class[] argTypes = new Class[types.length]; for (int i = 0; i < types.length; i++) { argTypes[i] = getClassForType(types[i]); } Method method = findMethod(clazz, methodName, argTypes); if (method == null) { throw new RuntimeException("Method '" + methodName + "' with descriptor '" + methodDesc + "' not found in '" + clazz + "\' or any supertype"); } return method; } private static Method findMethod(Class clazz, String methodName, Class[] argTypes) { Method found = null; if (clazz != null) { try { found = clazz.getDeclaredMethod(methodName, argTypes); } catch (NoSuchMethodException e) { found = findMethod(clazz.getSuperclass(), methodName, argTypes); if (found == null) { for (Class interfaceClass : clazz.getInterfaces()) { found = findMethod(interfaceClass, methodName, argTypes); if (found != null) break; } } } } return found; } public static Constructor getClassConstructor(String classInternalName, String constructorDesc) { checkArgNotNull(classInternalName, "classInternalName"); checkArgNotNull(constructorDesc, "constructorDesc"); Class clazz = getClassForInternalName(classInternalName); Type[] types = Type.getArgumentTypes(constructorDesc); Class[] argTypes = new Class[types.length]; for (int i = 0; i < types.length; i++) { argTypes[i] = getClassForType(types[i]); } try { return clazz.getDeclaredConstructor(argTypes); } catch (NoSuchMethodException e) { throw new RuntimeException("Constructor with descriptor '" + constructorDesc + "' not found in '" + clazz, e); } } /** * Returns the class with the given name if it has already been loaded by the given class loader. * Otherwise the method returns null. * * @param className the full name of the class to be loaded * @param parentClass the parent class of the class with the given className * @return the class instance or null */ public static Class loadClass(String className, Class parentClass) { checkArgNotNull(className, "className"); checkArgNotNull(parentClass, "parentClass"); try { try { return parentClass.getClassLoader().loadClass(className); } catch (ClassNotFoundException cnfe) { return null; } } catch (Exception e) { throw new RuntimeException("Could not determine whether class '" + className + "' has already been loaded", e); } } /** * Defines a new class with the given name and bytecode within the package of the given parent class. * Since package and class identity includes the ClassLoader instance used to load a class we use reflection * on the given class loader to define generated classes. If we used our own class loader (in order to be able * to access the protected "defineClass" method) we would likely still be able to load generated classes, * however, they would not have access to package-private classes and members of their super classes. * * @param className the full name of the class to be loaded * @param code the bytecode of the class to load * @param parentClass the parent class of the new class * @return the class instance */ public static Class defineClass(String className, byte[] code, Class parentClass) { checkArgNotNull(className, "className"); checkArgNotNull(code, "code"); checkArgNotNull(parentClass, "parentClass"); try { if (lookupFactory != null) { MethodHandles.Lookup lookup = lookupFactory.lookupFor(parentClass); if (lookup != null) { return lookup.defineClass(code); } } Class classLoaderBaseClass = Class.forName("java.lang.ClassLoader"); Method defineClassMethod = classLoaderBaseClass.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); // protected method invocation defineClassMethod.setAccessible(true); try { return (Class) defineClassMethod.invoke(parentClass.getClassLoader(), className, code, 0, code.length); } finally { defineClassMethod.setAccessible(false); } } catch (Exception e) { throw new RuntimeException("Could not load class '" + className + '\'', e); } } public static InsnList createArgumentLoaders(String methodDescriptor) { checkArgNotNull(methodDescriptor, "methodDescriptor"); InsnList instructions = new InsnList(); Type[] types = Type.getArgumentTypes(methodDescriptor); for (int i = 0; i < types.length; i++) { instructions.add(new VarInsnNode(getLoadingOpcode(types[i]), i + 1)); } return instructions; } public static int getLoadingOpcode(Type argType) { checkArgNotNull(argType, "argType"); switch (argType.getSort()) { case Type.BOOLEAN: case Type.BYTE: case Type.CHAR: case Type.SHORT: case Type.INT: return Opcodes.ILOAD; case Type.DOUBLE: return Opcodes.DLOAD; case Type.FLOAT: return Opcodes.FLOAD; case Type.LONG: return Opcodes.LLOAD; case Type.OBJECT: case Type.ARRAY: return Opcodes.ALOAD; default: throw new IllegalStateException(); } } /** * Determines whether the class with the given descriptor is assignable to the given type. * * @param classInternalName the class descriptor * @param type the type * @return true if the class with the given descriptor is assignable to the given type */ public static boolean isAssignableTo(String classInternalName, Class type) { checkArgNotNull(classInternalName, "classInternalName"); checkArgNotNull(type, "type"); return type.isAssignableFrom(getClassForInternalName(classInternalName)); } public static boolean isBooleanValueOfZ(AbstractInsnNode insn) { checkArgNotNull(insn, "insn"); if (insn.getOpcode() != Opcodes.INVOKESTATIC) return false; MethodInsnNode mi = (MethodInsnNode) insn; return isBooleanValueOfZ(mi.owner, mi.name, mi.desc); } public static boolean isBooleanValueOfZ(String methodOwner, String methodName, String methodDesc) { checkArgNotNull(methodOwner, "methodOwner"); checkArgNotNull(methodName, "methodName"); checkArgNotNull(methodDesc, "methodDesc"); return "java/lang/Boolean".equals(methodOwner) && "valueOf".equals(methodName) && "(Z)Ljava/lang/Boolean;".equals(methodDesc); } public static boolean isActionRoot(AbstractInsnNode insn) { checkArgNotNull(insn, "insn"); if (insn.getOpcode() != Opcodes.INVOKESTATIC) return false; MethodInsnNode mi = (MethodInsnNode) insn; return isActionRoot(mi.owner, mi.name); } public static boolean isActionRoot(String methodOwner, String methodName) { checkArgNotNull(methodOwner, "methodOwner"); checkArgNotNull(methodName, "methodName"); return "ACTION".equals(methodName) && isAssignableTo(methodOwner, BaseParser.class); } public static boolean isVarRoot(AbstractInsnNode insn) { checkArgNotNull(insn, "insn"); if (insn.getOpcode() != Opcodes.INVOKESPECIAL) return false; MethodInsnNode mi = (MethodInsnNode) insn; return isVarRoot(mi.owner, mi.name, mi.desc); } public static boolean isVarRoot(String methodOwner, String methodName, String methodDesc) { checkArgNotNull(methodOwner, "methodOwner"); checkArgNotNull(methodName, "methodName"); checkArgNotNull(methodDesc, "methodDesc"); return "".equals(methodName) && "(Ljava/lang/Object;)V".equals(methodDesc) && isAssignableTo(methodOwner, Var.class); } public static boolean isCallOnContextAware(AbstractInsnNode insn) { checkArgNotNull(insn, "insn"); if (insn.getOpcode() != Opcodes.INVOKEVIRTUAL && insn.getOpcode() != Opcodes.INVOKEINTERFACE) return false; MethodInsnNode mi = (MethodInsnNode) insn; return isAssignableTo(mi.owner, ContextAware.class); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/BaseAction.java000066400000000000000000000033431421263112100304040ustar00rootroot00000000000000/* * Copyright (c) 2009-2011 Ken Wenzel and Mathias Doenitz * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.parboiled.SkippableAction; /** * Base class of generated classes wrapping action expressions. */ @SuppressWarnings({"UnusedDeclaration"}) public abstract class BaseAction extends BaseGroupClass implements SkippableAction { private boolean skipInPredicates; protected BaseAction(String name) { super(checkArgNotNull(name, "name")); } public boolean skipInPredicates() { return skipInPredicates; } public void setSkipInPredicates() { this.skipInPredicates = true; } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/BaseGroupClass.java000066400000000000000000000017161421263112100312530ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; @SuppressWarnings({"UnusedDeclaration"}) abstract class BaseGroupClass { public final String name; protected BaseGroupClass(String name) { this.name = checkArgNotNull(name, "name"); } @Override public String toString() { return name; } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/BaseVarInit.java000066400000000000000000000017241421263112100305440ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.parboiled.common.Factory; /** * Base class of generated classes wrapping action var initializers. */ public abstract class BaseVarInit extends BaseGroupClass implements Factory { protected BaseVarInit(String name) { super(checkArgNotNull(name, "name")); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/BodyWithSuperCallReplacer.java000066400000000000000000000043041421263112100334140ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.VarInsnNode; import static org.objectweb.asm.Opcodes.*; import static org.parboiled.transform.AsmUtils.createArgumentLoaders; /** * Replaces the method code with a simple call to the super method. */ class BodyWithSuperCallReplacer implements RuleMethodProcessor { public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); return !method.isBodyRewritten() && method.getOwnerClass() == classNode.getParentClass() && method.getLocalVarVariables() == null; // if we have local variables we need to create a VarFramingMatcher // which needs access to the local variables } public void process(ParserClassNode classNode, RuleMethod method) throws Exception { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); // replace all method code with a simple call to the super method method.instructions.clear(); method.instructions.add(new VarInsnNode(ALOAD, 0)); method.instructions.add(createArgumentLoaders(method.desc)); method.instructions.add(new MethodInsnNode(INVOKESPECIAL, classNode.getParentType().getInternalName(), method.name, method.desc, classNode.isInterface())); method.instructions.add(new InsnNode(ARETURN)); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/CachingGenerator.java000066400000000000000000000371051421263112100316020ustar00rootroot00000000000000/* * Copyright (c) 2009-2010 Ken Wenzel and Mathias Doenitz * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.transform; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.objectweb.asm.Opcodes.*; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.common.Preconditions.checkState; import static org.parboiled.common.Utils.toObjectArray; /** * Wraps the method code with caching and proxying constructs. */ class CachingGenerator implements RuleMethodProcessor { private ParserClassNode classNode; private RuleMethod method; private InsnList instructions; private AbstractInsnNode current; private String cacheFieldName; public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); return method.hasCachedAnnotation(); } public void process(ParserClassNode classNode, RuleMethod method) throws Exception { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); checkState(!method.isSuperMethod()); // super methods have flag moved to the overriding method this.classNode = classNode; this.method = method; this.instructions = method.instructions; this.current = instructions.getFirst(); generateCacheHitReturn(); generateStoreNewProxyMatcher(); seekToReturnInstruction(); generateArmProxyMatcher(); generateStoreInCache(); } // if ( != null) return ; private void generateCacheHitReturn() { // stack: generateGetFromCache(); // stack: insert(new InsnNode(DUP)); // stack: :: LabelNode cacheMissLabel = new LabelNode(); insert(new JumpInsnNode(IFNULL, cacheMissLabel)); // stack: insert(new InsnNode(ARETURN)); // stack: insert(cacheMissLabel); // stack: insert(new InsnNode(POP)); // stack: } @SuppressWarnings( {"unchecked"}) private void generateGetFromCache() { Type[] paramTypes = Type.getArgumentTypes(method.desc); cacheFieldName = findUnusedCacheFieldName(); // if we have no parameters we use a simple Rule field as cache, otherwise a HashMap String cacheFieldDesc = paramTypes.length == 0 ? Types.RULE_DESC : "Ljava/util/HashMap;"; classNode.fields.add(new FieldNode(ACC_PRIVATE, cacheFieldName, cacheFieldDesc, null, null)); // stack: insert(new VarInsnNode(ALOAD, 0)); // stack: insert(new FieldInsnNode(GETFIELD, classNode.name, cacheFieldName, cacheFieldDesc)); // stack: if (paramTypes.length == 0) return; // if we have no parameters we are done // generate: if ( == null) = new HashMap(); // stack: insert(new InsnNode(DUP)); // stack: :: LabelNode alreadyInitialized = new LabelNode(); insert(new JumpInsnNode(IFNONNULL, alreadyInitialized)); // stack: insert(new InsnNode(POP)); // stack: insert(new VarInsnNode(ALOAD, 0)); // stack: insert(new TypeInsnNode(NEW, "java/util/HashMap")); // stack: :: insert(new InsnNode(DUP_X1)); // stack: :: :: insert(new InsnNode(DUP)); // stack: :: :: :: insert(new MethodInsnNode(INVOKESPECIAL, "java/util/HashMap", "", "()V", false)); // stack: :: :: insert(new FieldInsnNode(PUTFIELD, classNode.name, cacheFieldName, cacheFieldDesc)); // stack: insert(alreadyInitialized); // stack: // if we have more than one parameter or the parameter is an array we have to wrap with our Arguments class // since we need to unroll all inner arrays and apply custom hashCode(...) and equals(...) implementations if (paramTypes.length > 1 || paramTypes[0].getSort() == Type.ARRAY) { // generate: push new Arguments(new Object[] {}) String arguments = Type.getInternalName(Arguments.class); // stack: insert(new TypeInsnNode(NEW, arguments)); // stack: :: insert(new InsnNode(DUP)); // stack: :: :: generatePushNewParameterObjectArray(paramTypes); // stack: :: :: :: insert(new MethodInsnNode(INVOKESPECIAL, arguments, "", "([Ljava/lang/Object;)V", false)); // stack: :: } else { // stack: generatePushParameterAsObject(paramTypes, 0); // stack: :: } // generate: .get(...) // stack: :: insert(new InsnNode(DUP)); // stack: :: :: insert(new VarInsnNode(ASTORE, method.maxLocals)); // stack: :: insert(new MethodInsnNode(INVOKEVIRTUAL, "java/util/HashMap", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", false)); // stack: insert(new TypeInsnNode(CHECKCAST, Types.RULE.getInternalName())); // stack: } @SuppressWarnings( {"unchecked"}) private String findUnusedCacheFieldName() { String name = "cache$" + method.name; int i = 2; while (hasField(name)) { name = "cache$" + method.name + i++; } return name; } public boolean hasField(String fieldName) { for (Object field : classNode.fields) { if (fieldName.equals(((FieldNode) field).name)) return true; } return false; } private void generatePushNewParameterObjectArray(Type[] paramTypes) { // stack: ... insert(new IntInsnNode(BIPUSH, paramTypes.length)); // stack: ... :: insert(new TypeInsnNode(ANEWARRAY, "java/lang/Object")); // stack: ... :: for (int i = 0; i < paramTypes.length; i++) { // stack: ... :: insert(new InsnNode(DUP)); // stack: ... :: :: insert(new IntInsnNode(BIPUSH, i)); // stack: ... :: :: :: generatePushParameterAsObject(paramTypes, i); // stack: ... :: :: :: :: insert(new InsnNode(AASTORE)); // stack: ... :: } // stack: ... :: } private void generatePushParameterAsObject(Type[] paramTypes, int parameterNr) { switch (paramTypes[parameterNr++].getSort()) { case Type.BOOLEAN: insert(new VarInsnNode(ILOAD, parameterNr)); insert(new MethodInsnNode(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false)); return; case Type.CHAR: insert(new VarInsnNode(ILOAD, parameterNr)); insert(new MethodInsnNode(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false)); return; case Type.BYTE: insert(new VarInsnNode(ILOAD, parameterNr)); insert(new MethodInsnNode(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false)); return; case Type.SHORT: insert(new VarInsnNode(ILOAD, parameterNr)); insert(new MethodInsnNode(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false)); return; case Type.INT: insert(new VarInsnNode(ILOAD, parameterNr)); insert(new MethodInsnNode(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)); return; case Type.FLOAT: insert(new VarInsnNode(FLOAD, parameterNr)); insert(new MethodInsnNode(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false)); return; case Type.LONG: insert(new VarInsnNode(LLOAD, parameterNr)); insert(new MethodInsnNode(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false)); return; case Type.DOUBLE: insert(new VarInsnNode(DLOAD, parameterNr)); insert(new MethodInsnNode(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false)); return; case Type.ARRAY: case Type.OBJECT: insert(new VarInsnNode(ALOAD, parameterNr)); return; case Type.VOID: default: throw new IllegalStateException(); } } // = new ProxyMatcher(); private void generateStoreNewProxyMatcher() { String proxyMatcherType = Types.PROXY_MATCHER.getInternalName(); // stack: insert(new TypeInsnNode(NEW, proxyMatcherType)); // stack: insert(new InsnNode(DUP)); // stack: :: insert(new MethodInsnNode(INVOKESPECIAL, proxyMatcherType, "", "()V", false)); // stack: generateStoreInCache(); // stack: } private void seekToReturnInstruction() { while (current.getOpcode() != ARETURN) { current = current.getNext(); } } // .arm() private void generateArmProxyMatcher() { String proxyMatcherType = Types.PROXY_MATCHER.getInternalName(); // stack: :: insert(new InsnNode(DUP_X1)); // stack: :: :: insert(new TypeInsnNode(CHECKCAST, Types.MATCHER.getInternalName())); // stack: :: :: insert(new MethodInsnNode(INVOKEVIRTUAL, proxyMatcherType, "arm", '(' + Types.MATCHER_DESC + ")V", false)); // stack: } private void generateStoreInCache() { Type[] paramTypes = Type.getArgumentTypes(method.desc); // stack: insert(new InsnNode(DUP)); // stack: :: if (paramTypes.length == 0) { // stack: :: insert(new VarInsnNode(ALOAD, 0)); // stack: :: :: insert(new InsnNode(SWAP)); // stack: :: :: insert(new FieldInsnNode(PUTFIELD, classNode.name, cacheFieldName, Types.RULE_DESC)); // stack: return; } // stack: :: insert(new VarInsnNode(ALOAD, method.maxLocals)); // stack: :: :: insert(new InsnNode(SWAP)); // stack: :: :: insert(new VarInsnNode(ALOAD, 0)); // stack: :: :: :: insert(new FieldInsnNode(GETFIELD, classNode.name, cacheFieldName, "Ljava/util/HashMap;")); // stack: :: :: :: insert(new InsnNode(DUP_X2)); // stack: :: :: :: :: insert(new InsnNode(POP)); // stack: :: :: :: insert(new MethodInsnNode(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false)); // stack: :: insert(new InsnNode(POP)); // stack: } private void insert(AbstractInsnNode instruction) { instructions.insertBefore(current, instruction); } public static class Arguments { private final Object[] params; public Arguments(Object[] params) { // we need to "unroll" all inner Object arrays List list = new ArrayList(); unroll(params, list); this.params = list.toArray(); } private void unroll(Object[] params, List list) { for (Object param : params) { if (param != null && param.getClass().isArray()) { switch (Type.getType(param.getClass().getComponentType()).getSort()) { case Type.BOOLEAN: unroll(toObjectArray((boolean[]) param), list); continue; case Type.BYTE: unroll(toObjectArray((byte[]) param), list); continue; case Type.CHAR: unroll(toObjectArray((char[]) param), list); continue; case Type.DOUBLE: unroll(toObjectArray((double[]) param), list); continue; case Type.FLOAT: unroll(toObjectArray((float[]) param), list); continue; case Type.INT: unroll(toObjectArray((int[]) param), list); continue; case Type.LONG: unroll(toObjectArray((long[]) param), list); continue; case Type.SHORT: unroll(toObjectArray((short[]) param), list); continue; case Type.OBJECT: case Type.ARRAY: unroll((Object[]) param, list); continue; default: throw new IllegalStateException(); } } list.add(param); } } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Arguments)) return false; Arguments that = (Arguments) o; return Arrays.equals(params, that.params); } @Override public int hashCode() { return params != null ? Arrays.hashCode(params) : 0; } } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/ClassNodeInitializer.java000066400000000000000000000157621421263112100324630ustar00rootroot00000000000000/* * Copyright (c) 2009-2010 Ken Wenzel and Mathias Doenitz * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.*; import org.objectweb.asm.tree.MethodNode; import org.parboiled.support.Checks; import java.io.IOException; import static org.parboiled.transform.AsmUtils.createClassReader; import static org.parboiled.transform.AsmUtils.getExtendedParserClassName; import static org.objectweb.asm.Opcodes.*; /** * Initializes the basic ParserClassNode fields and collects all methods. */ class ClassNodeInitializer extends ClassVisitor { private ParserClassNode classNode; private Class ownerClass; private boolean hasBuildParseTree; private boolean hasExplicitActionOnlyAnnotation; private boolean hasDontLabelAnnotation; private boolean hasSkipActionsInPredicates; public ClassNodeInitializer() { super(ASMSettings.ASM_API); } public void process(ParserClassNode classNode) throws IOException { this.classNode = checkArgNotNull(classNode, "classNode"); // walk up the parser parent class chain ownerClass = classNode.getParentClass(); while (!Object.class.equals(ownerClass)) { hasExplicitActionOnlyAnnotation = false; hasDontLabelAnnotation = false; hasSkipActionsInPredicates = false; ClassReader classReader = createClassReader(ownerClass); classReader.accept(this, ClassReader.SKIP_FRAMES); ownerClass = ownerClass.getSuperclass(); } for (RuleMethod method : classNode.getRuleMethods().values()) { // move all flags from the super methods to their overriding methods if (method.isSuperMethod()) { RuleMethod overridingMethod = classNode.getRuleMethods().get(method.name.substring(1) + method.desc); method.moveFlagsTo(overridingMethod); } else { if (!hasBuildParseTree) { method.suppressNode(); } else { // as soon as we see the first non-super method we can break since the methods are sorted so that // the super methods precede all others break; } } } } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { if (ownerClass == classNode.getParentClass()) { Checks.ensure((access & ACC_PRIVATE) == 0, "Parser class '%s' must not be private", name); Checks.ensure((access & ACC_FINAL) == 0, "Parser class '%s' must not be final.", name); classNode.visit( ASMSettings.JDK_VERSION, ACC_PUBLIC, getExtendedParserClassName(name), null, classNode.getParentType().getInternalName(), null ); } } @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if (Types.EXPLICIT_ACTIONS_ONLY_DESC.equals(desc)) { hasExplicitActionOnlyAnnotation = true; return null; } if (Types.DONT_LABEL_DESC.equals(desc)) { hasDontLabelAnnotation = true; return null; } if (Types.SKIP_ACTIONS_IN_PREDICATES_DESC.equals(desc)) { hasSkipActionsInPredicates = true; return null; } if (Types.BUILD_PARSE_TREE_DESC.equals(desc)) { hasBuildParseTree = true; return null; } // only keep visible annotations on the parser class return visible && ownerClass == classNode.getParentClass() ? classNode.visitAnnotation(desc, true) : null; } @Override public void visitSource(String source, String debug) { classNode.visitSource(null, null); } @Override @SuppressWarnings("unchecked") public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if ("".equals(name)) { // do not add constructors from super classes or private constructors if (ownerClass != classNode.getParentClass() || (access & ACC_PRIVATE) > 0) { return null; } MethodNode constructor = new MethodNode(access, name, desc, signature, exceptions); classNode.getConstructors().add(constructor); return constructor; // return the newly created method in order to have it "filled" with the method code } // only add non-native, non-abstract methods returning Rules if (!Type.getReturnType(desc).equals(Types.RULE) || (access & (ACC_NATIVE | ACC_ABSTRACT)) > 0) { return null; } Checks.ensure((access & ACC_PRIVATE) == 0, "Rule method '%s'must not be private.\n" + "Mark the method protected or package-private if you want to prevent public access!", name); Checks.ensure((access & ACC_FINAL) == 0, "Rule method '%s' must not be final.", name); // check, whether we do not already have a method with that name and descriptor // if we do we add the method with a "$" prefix in order to have it processed and be able to reference it // later if we have to String methodKey = name.concat(desc); while (classNode.getRuleMethods().containsKey(methodKey)) { name = '$' + name; methodKey = name.concat(desc); } RuleMethod method = new RuleMethod(ownerClass, access, name, desc, signature, exceptions, hasExplicitActionOnlyAnnotation, hasDontLabelAnnotation, hasSkipActionsInPredicates); classNode.getRuleMethods().put(methodKey, method); return method; // return the newly created method in order to have it "filled" with the actual method code } @Override public void visitEnd() { classNode.visitEnd(); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/ConstructorGenerator.java000066400000000000000000000057241421263112100325750ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.tree.*; import org.parboiled.support.Checks; import static org.objectweb.asm.Opcodes.*; import static org.parboiled.transform.AsmUtils.createArgumentLoaders; /** * Adds one constructor for each of the ParserClassNode.constructors, * which simply delegates to the respective super constructor. */ class ConstructorGenerator { public void process(ParserClassNode classNode) { checkArgNotNull(classNode, "classNode"); Checks.ensure(!classNode.getConstructors().isEmpty(), "Could not extend parser class '%s', no constructor visible to derived classes found", classNode.getParentType().getClassName()); for (MethodNode constructor : classNode.getConstructors()) { createConstuctor(classNode, constructor); } createNewInstanceMethod(classNode); } @SuppressWarnings({"unchecked"}) private void createConstuctor(ParserClassNode classNode, MethodNode constructor) { MethodNode newConstructor = new MethodNode(ACC_PUBLIC, constructor.name, constructor.desc, constructor.signature, (String[]) constructor.exceptions.toArray(new String[constructor.exceptions.size()])); InsnList instructions = newConstructor.instructions; instructions.add(new VarInsnNode(ALOAD, 0)); instructions.add(createArgumentLoaders(constructor.desc)); instructions.add(new MethodInsnNode(INVOKESPECIAL, classNode.getParentType().getInternalName(), "", constructor.desc, classNode.isInterface())); instructions.add(new InsnNode(RETURN)); classNode.methods.add(newConstructor); } @SuppressWarnings({"unchecked"}) private void createNewInstanceMethod(ParserClassNode classNode) { MethodNode method = new MethodNode(ACC_PUBLIC, "newInstance", "()L" + Types.BASE_PARSER.getInternalName() + ';', null, null); InsnList instructions = method.instructions; instructions.add(new TypeInsnNode(NEW, classNode.name)); instructions.add(new InsnNode(DUP)); instructions.add(new MethodInsnNode(INVOKESPECIAL, classNode.name, "", "()V", classNode.isInterface())); instructions.add(new InsnNode(ARETURN)); classNode.methods.add(method); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/FlagMarkingGenerator.java000066400000000000000000000054161421263112100324300ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.tree.*; import static org.objectweb.asm.Opcodes.*; /** * Adds the required flag marking calls before the return instruction. */ class FlagMarkingGenerator implements RuleMethodProcessor { public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); return method.hasSuppressNodeAnnotation() || method.hasSuppressSubnodesAnnotation() || method.hasSkipNodeAnnotation() || method.hasMemoMismatchesAnnotation(); } public void process(ParserClassNode classNode, RuleMethod method) throws Exception { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); checkState(!method.isSuperMethod()); // super methods have flag moved to the overriding method InsnList instructions = method.instructions; AbstractInsnNode ret = instructions.getLast(); while (ret.getOpcode() != ARETURN) { ret = ret.getPrevious(); } // stack: instructions.insertBefore(ret, new InsnNode(DUP)); // stack: :: LabelNode isNullLabel = new LabelNode(); instructions.insertBefore(ret, new JumpInsnNode(IFNULL, isNullLabel)); // stack: if (method.hasSuppressNodeAnnotation()) generateMarkerCall(instructions, ret, "suppressNode"); if (method.hasSuppressSubnodesAnnotation()) generateMarkerCall(instructions, ret, "suppressSubnodes"); if (method.hasSkipNodeAnnotation()) generateMarkerCall(instructions, ret, "skipNode"); if (method.hasMemoMismatchesAnnotation()) generateMarkerCall(instructions, ret, "memoMismatches"); // stack: instructions.insertBefore(ret, isNullLabel); // stack: } private void generateMarkerCall(InsnList instructions, AbstractInsnNode ret, String call) { instructions.insertBefore(ret, new MethodInsnNode(INVOKEINTERFACE, Types.RULE.getInternalName(), call, "()" + Types.RULE.getDescriptor(), true)); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/GroupClassGenerator.java000066400000000000000000000154031421263112100323250ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import static org.objectweb.asm.Opcodes.*; import static org.parboiled.transform.AsmUtils.defineClass; abstract class GroupClassGenerator implements RuleMethodProcessor { private static final Object lock = new Object(); private final boolean forceCodeBuilding; protected ParserClassNode classNode; protected RuleMethod method; protected GroupClassGenerator(boolean forceCodeBuilding) { this.forceCodeBuilding = forceCodeBuilding; } public void process(ParserClassNode classNode, RuleMethod method) { this.classNode = checkArgNotNull(classNode, "classNode"); this.method = checkArgNotNull(method, "method"); for (InstructionGroup group : method.getGroups()) { if (appliesTo(group.getRoot())) { loadGroupClass(group); } } } protected abstract boolean appliesTo(InstructionGraphNode group); private void loadGroupClass(InstructionGroup group) { createGroupClassType(group); String className = group.getGroupClassType().getClassName(); Class groupClass; synchronized (lock) { groupClass = AsmUtils.loadClass(className, classNode.getParentClass()); if (groupClass == null || forceCodeBuilding) { byte[] groupClassCode = generateGroupClassCode(group); group.setGroupClassCode(groupClassCode); if (groupClass == null) { AsmUtils.defineClass(className, groupClassCode, classNode.getParentClass()); } } } } private void createGroupClassType(InstructionGroup group) { String s = classNode.name; int lastSlash = classNode.name.lastIndexOf('/'); // do not prepend a slash if class is in the default package (lastSlash == -1) String groupClassInternalName = (lastSlash >= 0 ? s.substring(0, lastSlash) + '/' : "") + group.getName(); group.setGroupClassType(Type.getObjectType(groupClassInternalName)); } protected byte[] generateGroupClassCode(InstructionGroup group) { ClassWriter classWriter = new ClassWriter(ASMSettings.FRAMES); generateClassBasics(group, classWriter); generateFields(group, classWriter); generateConstructor(classWriter); generateMethod(group, classWriter); classWriter.visitEnd(); return classWriter.toByteArray(); } private void generateClassBasics(InstructionGroup group, ClassWriter cw) { cw.visit(ASMSettings.JDK_VERSION, ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC, group.getGroupClassType().getInternalName(), null, getBaseType().getInternalName(), null); cw.visitSource(classNode.sourceFile, null); } protected abstract Type getBaseType(); private void generateFields(InstructionGroup group, ClassWriter cw) { for (FieldNode field : group.getFields()) { // CAUTION: the FieldNode has illegal access flags and an illegal value field since these two members // are reused for other purposes, so we need to write out the field "manually" here rather than // just call "field.accept(cw)" cw.visitField(ACC_PUBLIC + ACC_SYNTHETIC, field.name, field.desc, null, null); } } private void generateConstructor(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "(Ljava/lang/String;)V", null, null); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKESPECIAL, getBaseType().getInternalName(), "", "(Ljava/lang/String;)V", false); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); // trigger automatic computing mv.visitEnd(); } protected abstract void generateMethod(InstructionGroup group, ClassWriter cw); protected void insertSetContextCalls(InstructionGroup group, int localVarIx) { InsnList instructions = group.getInstructions(); for (InstructionGraphNode node : group.getNodes()) { if (node.isCallOnContextAware()) { AbstractInsnNode insn = node.getInstruction(); if (node.getPredecessors().size() > 1) { // store the target of the call in a new local variable AbstractInsnNode loadTarget = node.getPredecessors().get(0).getInstruction(); instructions.insert(loadTarget, new VarInsnNode(ASTORE, ++localVarIx)); instructions.insert(loadTarget, new InsnNode(DUP)); // the DUP is inserted BEFORE the ASTORE // immediately before the call get the target from the local var and set the context on it instructions.insertBefore(insn, new VarInsnNode(ALOAD, localVarIx)); } else { // if we have only one predecessor the call does not take any parameters and we can // skip the storing and loading of the invocation target instructions.insertBefore(insn, new InsnNode(DUP)); } instructions.insertBefore(insn, new VarInsnNode(ALOAD, 1)); instructions.insertBefore(insn, new MethodInsnNode(INVOKEINTERFACE, Types.CONTEXT_AWARE.getInternalName(), "setContext", "(" + Types.CONTEXT_DESC + ")V", true)); } } } protected void convertXLoads(InstructionGroup group) { String owner = group.getGroupClassType().getInternalName(); for (InstructionGraphNode node : group.getNodes()) { if (!node.isXLoad()) continue; VarInsnNode insn = (VarInsnNode) node.getInstruction(); FieldNode field = group.getFields().get(insn.var); // insert the correct GETFIELD after the xLoad group.getInstructions().insert(insn, new FieldInsnNode(GETFIELD, owner, field.name, field.desc)); // change the load to ALOAD 0 group.getInstructions().set(insn, new VarInsnNode(ALOAD, 0)); } } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/ImplicitActionsConverter.java000066400000000000000000000153761421263112100333700ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.TypeInsnNode; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.objectweb.asm.Opcodes.*; import static org.parboiled.common.Preconditions.checkArgNotNull; import static org.parboiled.common.Preconditions.checkState; import static org.parboiled.transform.AsmUtils.isBooleanValueOfZ; /** * Makes all implicit action expressions in a rule method explicit. */ class ImplicitActionsConverter implements RuleMethodProcessor { private final Set covered = new HashSet(); private RuleMethod method; public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); return method.containsImplicitActions(); } public void process(ParserClassNode classNode, RuleMethod method) throws Exception { this.method = checkArgNotNull(method, "method"); covered.clear(); walkNode(method.getReturnInstructionNode()); method.setContainsImplicitActions(false); } private void walkNode(InstructionGraphNode node) { if (covered.contains(node)) return; covered.add(node); if (isImplicitAction(node)) { replaceWithActionWrapper(node); method.setContainsExplicitActions(true); return; } if (!node.isActionRoot()) { for (InstructionGraphNode predecessor : node.getPredecessors()) { walkNode(predecessor); } } } private void replaceWithActionWrapper(InstructionGraphNode node) { MethodInsnNode insn = createActionWrappingInsn(); method.instructions.set(node.getInstruction(), insn); node.setIsActionRoot(); node.setInstruction(insn); } private boolean isImplicitAction(InstructionGraphNode node) { // an implicit action must be a call to Boolean.valueOf(boolean) if (!isBooleanValueOfZ(node.getInstruction())) return false; // it must have exactly one other instruction that depends on it List dependents = getDependents(node); if (dependents.size() != 1) return false; // this dependent instruction must be rule method call InstructionGraphNode dependent = dependents.get(0); return isObjectArgumentToRuleCreatingMethodCall(node, dependent) || isStoredIntoObjectArray(dependent); } private boolean isObjectArgumentToRuleCreatingMethodCall(InstructionGraphNode node, InstructionGraphNode dependent) { // is the single dependent a method call ? AbstractInsnNode insn = dependent.getInstruction(); if (insn.getType() != AbstractInsnNode.METHOD_INSN) return false; // Does this method call return a Rule ? MethodInsnNode mi = (MethodInsnNode) insn; if (!Types.RULE.equals(Type.getReturnType(mi.desc))) return false; // Doesthe result of the Boolean.valueOf(boolean) call correspond to an Object parameter ? Type[] argTypes = Type.getArgumentTypes(mi.desc); int argIndex = getArgumentIndex(dependent, node); checkState(argIndex < argTypes.length); return "java/lang/Object".equals(argTypes[argIndex].getInternalName()); } private boolean isStoredIntoObjectArray(InstructionGraphNode dependent) { // is the single dependent an AASTORE instruction ? AbstractInsnNode insn = dependent.getInstruction(); if (insn.getOpcode() != AASTORE) return false; // Does this instruction store into an array of Object ? List dependents = getDependents(dependent); checkState(dependents.size() == 1); // an AASTORE instruction should have exactly one dependent AbstractInsnNode newArrayInsn = dependents.get(0).getInstruction(); checkState(newArrayInsn.getOpcode() == ANEWARRAY); // which should be a n ANEWARRAY instruction return "java/lang/Object".equals(((TypeInsnNode) newArrayInsn).desc); } private int getArgumentIndex(InstructionGraphNode callNode, InstructionGraphNode predecessor) { int startIndex = callNode.getInstruction().getOpcode() == INVOKESTATIC ? 0 : 1; for (int i = startIndex; i < callNode.getPredecessors().size(); i++) { InstructionGraphNode argumentNode = callNode.getPredecessors().get(i); if (predecessor.equals(argumentNode)) { return i - startIndex; } } throw new IllegalStateException(); } private List getDependents(InstructionGraphNode predecessor) { List dependents = new ArrayList(); int index = 0; for (InstructionGraphNode node : method.getGraphNodes()) { if (node == null) { AbstractInsnNode insNode = method.instructions.get(index); if (insNode == null) { throw new IllegalStateException("sparse instruction node graph, missing node and missing instruction for index: " + index); } String name = method.name; String desc = method.desc; int opcode = insNode.getOpcode(); int type = insNode.getType(); String message = "sparse instruction node graph, missing node for method: %s( %s ), having instruction at index: %d, with opcode: %d , type: %d"; throw new IllegalStateException(String.format(message, name, desc, index, opcode, type)); } if (node.getPredecessors().contains(predecessor)) { dependents.add(node); } index++; } return dependents; } private MethodInsnNode createActionWrappingInsn() { return new MethodInsnNode(INVOKESTATIC, Types.BASE_PARSER.getInternalName(), "ACTION", "(Z)" + Types.ACTION_DESC, false); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/InstructionGraphCreator.java000066400000000000000000000047121421263112100332200ustar00rootroot00000000000000/* * Copyright (c) 2009-2010 Ken Wenzel and Mathias Doenitz * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.Type; import org.objectweb.asm.tree.analysis.Analyzer; /** * Performs data/control flow analysis and constructs the instructions graph. */ class InstructionGraphCreator implements RuleMethodProcessor { public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); return method.containsImplicitActions() || method.containsExplicitActions() || method.containsVars(); } public void process(ParserClassNode classNode, RuleMethod method) throws Exception { checkArgNotNull(method, "method"); final RuleMethodInterpreter interpreter = new RuleMethodInterpreter(method); new Analyzer(interpreter) { @Override protected void newControlFlowEdge(int insn, int successor) { interpreter.newControlFlowEdge(insn, successor); } @Override protected boolean newControlFlowExceptionEdge(int insn, int successor) { interpreter.newControlFlowEdge(insn, successor); return true; } }.analyze(Type.getInternalName(classNode.getParentClass()), method); interpreter.finish(); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/InstructionGraphNode.java000066400000000000000000000110611421263112100325010ustar00rootroot00000000000000/* * Copyright (c) 2009-2010 Ken Wenzel and Mathias Doenitz * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.analysis.BasicValue; import org.objectweb.asm.util.Printer; import java.util.ArrayList; import java.util.Collection; import java.util.List; import static org.objectweb.asm.Opcodes.*; /** * A node in the instruction dependency graph. */ class InstructionGraphNode extends BasicValue { private AbstractInsnNode instruction; private final BasicValue resultValue; private final List predecessors = new ArrayList(); private boolean isActionRoot; private final boolean isVarInitRoot; private final boolean isCallOnContextAware; private final boolean isXLoad; private final boolean isXStore; private InstructionGroup group; public InstructionGraphNode(AbstractInsnNode instruction, BasicValue resultValue) { super(null); this.instruction = instruction; this.resultValue = resultValue; this.isActionRoot = AsmUtils.isActionRoot(instruction); this.isVarInitRoot = AsmUtils.isVarRoot(instruction); this.isCallOnContextAware = AsmUtils.isCallOnContextAware(instruction); this.isXLoad = ILOAD <= instruction.getOpcode() && instruction.getOpcode() < IALOAD; this.isXStore = ISTORE <= instruction.getOpcode() && instruction.getOpcode() < IASTORE; } public int getSize() { return resultValue.getSize(); } public AbstractInsnNode getInstruction() { return instruction; } public void setInstruction(AbstractInsnNode instruction) { this.instruction = instruction; } public BasicValue getResultValue() { return resultValue; } public List getPredecessors() { return predecessors; } public InstructionGroup getGroup() { return group; } public void setGroup(InstructionGroup newGroup) { if (newGroup != group) { if (group != null) { group.getNodes().remove(this); } group = newGroup; if (group != null) { group.getNodes().add(this); } } } public boolean isActionRoot() { return isActionRoot; } public void setIsActionRoot() { isActionRoot = true; } public boolean isVarInitRoot() { return isVarInitRoot; } public boolean isCallOnContextAware() { return isCallOnContextAware; } public boolean isXLoad() { return isXLoad; } public boolean isXStore() { return isXStore; } public void addPredecessors(Collection preds) { checkArgNotNull(preds, "preds"); for (BasicValue pred : preds) { if (pred instanceof InstructionGraphNode) { addPredecessor(((InstructionGraphNode) pred)); } } } public void addPredecessor(InstructionGraphNode node) { if (!predecessors.contains(node)) { predecessors.add(node); } } @Override public boolean equals(Object value) { return value == this; } @Override public int hashCode() { return System.identityHashCode(this); } @Override public String toString() { return instruction.getOpcode() != -1 ? Printer.OPCODES[instruction.getOpcode()] : super.toString(); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/InstructionGroup.java000066400000000000000000000043351421263112100317340ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import org.objectweb.asm.Type; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InsnList; import java.util.ArrayList; import java.util.List; /** * A group of instructions belonging to a ACTION or Var initializer */ class InstructionGroup { private final List nodes = new ArrayList(); private final InsnList instructions = new InsnList(); private final InstructionGraphNode root; private final List fields = new ArrayList(); private String name; private Type groupClassType; private byte[] groupClassCode; public InstructionGroup(InstructionGraphNode root) { this.root = root; } public List getNodes() { return nodes; } public InsnList getInstructions() { return instructions; } public InstructionGraphNode getRoot() { return root; } public List getFields() { return fields; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Type getGroupClassType() { return groupClassType; } public void setGroupClassType(Type groupClassType) { this.groupClassType = groupClassType; } public byte[] getGroupClassCode() { return groupClassCode; } public void setGroupClassCode(byte[] groupClassCode) { this.groupClassCode = groupClassCode; } @Override public String toString() { return name != null ? name : super.toString(); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/InstructionGroupCreator.java000066400000000000000000000216721421263112100332570ustar00rootroot00000000000000/* * Copyright (c) 2009-2010 Ken Wenzel and Mathias Doenitz * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.MethodInsnNode; import org.parboiled.support.Checks; import java.lang.reflect.Modifier; import java.util.*; import static org.objectweb.asm.Opcodes.*; import static org.parboiled.transform.AsmUtils.*; class InstructionGroupCreator implements RuleMethodProcessor { private final Map memberModifiers = new HashMap(); private RuleMethod method; public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); return method.containsExplicitActions() || method.containsVars(); } public void process(ParserClassNode classNode, RuleMethod method) { this.method = checkArgNotNull(method, "method"); // create groups createGroups(); // prepare groups for later stages for (InstructionGroup group : method.getGroups()) { sort(group); markUngroupedEnclosedNodes(group); verify(group); } // check all non-group node for illegal accesses for (InstructionGraphNode node : method.getGraphNodes()) { if (node.getGroup() == null) { verifyAccess(node); } } } private void createGroups() { for (InstructionGraphNode node : method.getGraphNodes()) { if (node.isActionRoot() || node.isVarInitRoot()) { InstructionGroup group = new InstructionGroup(node); markGroup(node, group); method.getGroups().add(group); } } } private void markGroup(InstructionGraphNode node, InstructionGroup group) { Checks.ensure( node == group.getRoot() || (!node.isActionRoot() && !node.isVarInitRoot()), "Method '%s' contains illegal nesting of ACTION and/or Var initializer constructs", method.name); if (node.getGroup() != null) return; // already visited node.setGroup(group); if (!node.isXLoad()) { if (node.isVarInitRoot()) { checkState(node.getPredecessors().size() == 2); markGroup(node.getPredecessors().get(1), group); // only color the second predecessor branch } else { for (InstructionGraphNode pred : node.getPredecessors()) { markGroup(pred, group); } } } } // sort the group instructions according to their method index private void sort(InstructionGroup group) { final InsnList instructions = method.instructions; Collections.sort(group.getNodes(), new Comparator() { public int compare(InstructionGraphNode a, InstructionGraphNode b) { return Integer.valueOf(instructions.indexOf(a.getInstruction())) .compareTo(instructions.indexOf(b.getInstruction())); } }); } // also capture all group nodes "hidden" behind xLoads private void markUngroupedEnclosedNodes(InstructionGroup group) { while_: while (true) { for (int i = getIndexOfFirstInsn(group), max = getIndexOfLastInsn(group); i < max; i++) { InstructionGraphNode node = method.getGraphNodes().get(i); if (node.getGroup() == null) { markGroup(node, group); sort(group); continue while_; } } break; } } private void verify(InstructionGroup group) { List nodes = group.getNodes(); int sizeMinus1 = nodes.size() - 1; // verify all instruction except for the last one (which must be the root) checkState(nodes.get(sizeMinus1) == group.getRoot()); for (int i = 0; i < sizeMinus1; i++) { InstructionGraphNode node = nodes.get(i); Checks.ensure(!node.isXStore(), "An ACTION or Var initializer in rule method '%s' " + "contains illegal writes to a local variable or parameter", method.name); verifyAccess(node); } Checks.ensure(getIndexOfLastInsn(group) - getIndexOfFirstInsn(group) == sizeMinus1, "Error during bytecode analysis of rule method '%s': Incontinuous group block", method.name); } private void verifyAccess(InstructionGraphNode node) { switch (node.getInstruction().getOpcode()) { case GETFIELD: case GETSTATIC: FieldInsnNode field = (FieldInsnNode) node.getInstruction(); Checks.ensure(!isPrivateField(field.owner, field.name), "Rule method '%s' contains an illegal access to private field '%s'.\n" + "Mark the field protected or package-private if you want to prevent public access!", method.name, field.name); break; case INVOKEVIRTUAL: case INVOKESTATIC: case INVOKESPECIAL: case INVOKEINTERFACE: MethodInsnNode calledMethod = (MethodInsnNode) node.getInstruction(); Checks.ensure(!isPrivate(calledMethod.owner, calledMethod.name, calledMethod.desc), "Rule method '%s' contains an illegal call to private method '%s'.\nMark '%s' protected or " + "package-private if you want to prevent public access!", method.name, calledMethod.name, calledMethod.name); break; } } private int getIndexOfFirstInsn(InstructionGroup group) { return method.instructions.indexOf(group.getNodes().get(0).getInstruction()); } private int getIndexOfLastInsn(InstructionGroup group) { List graphNodes = group.getNodes(); return method.instructions.indexOf(graphNodes.get(graphNodes.size() - 1).getInstruction()); } private boolean isPrivateField(String owner, String name) { String key = owner + '#' + name; Integer modifiers = memberModifiers.get(key); if (modifiers == null) { modifiers = getClassField(owner, name).getModifiers(); memberModifiers.put(key, modifiers); } return Modifier.isPrivate(modifiers); } private boolean isPrivate(String owner, String name, String desc) { return "".equals(name) ? isPrivateInstantiation(owner, desc) : isPrivateMethod(owner, name, desc); } private boolean isPrivateMethod(String owner, String name, String desc) { String key = owner + '#' + name + '#' + desc; Integer modifiers = memberModifiers.get(key); if (modifiers == null) { modifiers = getClassMethod(owner, name, desc).getModifiers(); memberModifiers.put(key, modifiers); } return Modifier.isPrivate(modifiers); } private boolean isPrivateInstantiation(String owner, String desc) { // first check whether the class is private Integer modifiers = memberModifiers.get(owner); if (modifiers == null) { modifiers = getClassForInternalName(owner).getModifiers(); memberModifiers.put(owner, modifiers); } if (Modifier.isPrivate(modifiers)) return true; // then check whether the selected constructor is private String key = owner + "##" + desc; modifiers = memberModifiers.get(key); if (modifiers == null) { modifiers = getClassConstructor(owner, desc).getModifiers(); memberModifiers.put(key, modifiers); } return Modifier.isPrivate(modifiers); } } parboiled-1.4.1/parboiled-java/src/main/java/org/parboiled/transform/InstructionGroupPreparer.java000066400000000000000000000240561421263112100334370ustar00rootroot00000000000000/* * Copyright (C) 2009-2011 Mathias Doenitz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.parboiled.transform; import static org.parboiled.common.Preconditions.*; import org.objectweb.asm.*; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.VarInsnNode; import org.parboiled.common.Base64; import org.parboiled.common.StringUtils; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import static org.objectweb.asm.Opcodes.ALOAD; class InstructionGroupPreparer implements RuleMethodProcessor { private static final Base64 CUSTOM_BASE64 = new Base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789zzzz"); private RuleMethod method; public boolean appliesTo(ParserClassNode classNode, RuleMethod method) { checkArgNotNull(classNode, "classNode"); checkArgNotNull(method, "method"); return method.containsExplicitActions() || method.containsVars(); } public void process(ParserClassNode classNode, RuleMethod method) { this.method = checkArgNotNull(method, "method"); // prepare groups for later stages for (InstructionGroup group : method.getGroups()) { extractInstructions(group); extractFields(group); name(group, classNode); } } // move all group instructions except for the root from the underlying method into the groups Insnlist private void extractInstructions(InstructionGroup group) { for (InstructionGraphNode node : group.getNodes()) { if (node != group.getRoot()) { AbstractInsnNode insn = node.getInstruction(); method.instructions.remove(insn); group.getInstructions().add(insn); } } } // create FieldNodes for all xLoad instructions private void extractFields(InstructionGroup group) { List fields = group.getFields(); for (InstructionGraphNode node : group.getNodes()) { if (node.isXLoad()) { VarInsnNode insn = (VarInsnNode) node.getInstruction(); // check whether we already have a field for the var with this index int index; for (index = 0; index < fields.size(); index++) { if (fields.get(index).access == insn.var) break; } // if we don't, create a new field for the var if (index == fields.size()) { // CAUTION, HACK!: for brevity we reuse the access field and the value field of the FieldNode // for keeping track of the original var index as well as the FieldNodes Type (respectively) // so we need to make sure that we correct for this when the field is actually written Type type = node.getResultValue().getType(); fields.add(new FieldNode(insn.var, "field$" + index, type.getDescriptor(), null, type)); } // normalize the instruction so instruction groups that are identical except for the variable // indexes are still mapped to the same group class (name) insn.var = index; } } } // set a group name base on the hash across all group instructions and fields private synchronized void name(InstructionGroup group, ParserClassNode classNode) { // generate an MD5 hash across the buffer, use only the first 96 bit MD5Digester digester = new MD5Digester(classNode.name); group.getInstructions().accept(digester); for (FieldNode field: group.getFields()) digester.visitField(field); byte[] hash = digester.getMD5Hash(); byte[] hash96 = new byte[12]; System.arraycopy(hash, 0, hash96, 0, 12); // generate a name for the group based on the hash String name = group.getRoot().isActionRoot() ? "Action$" : "VarInit$"; name += CUSTOM_BASE64.encodeToString(hash96, false); group.setName(name); } private static class MD5Digester extends MethodVisitor { private static MessageDigest digest; private static ByteBuffer buffer; private final List