pax_global_header00006660000000000000000000000064143511746130014517gustar00rootroot0000000000000052 comment=8d1c1ed79e2c85ec54b9fb470f7171b34ae3b7e2 racc-1.6.2/000077500000000000000000000000001435117461300124355ustar00rootroot00000000000000racc-1.6.2/.gemtest000066400000000000000000000000001435117461300140740ustar00rootroot00000000000000racc-1.6.2/.gitattributes000066400000000000000000000000301435117461300153210ustar00rootroot00000000000000*.rb ident *.rdoc ident racc-1.6.2/.github/000077500000000000000000000000001435117461300137755ustar00rootroot00000000000000racc-1.6.2/.github/dependabot.yml000066400000000000000000000001661435117461300166300ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: 'github-actions' directory: '/' schedule: interval: 'weekly' racc-1.6.2/.github/workflows/000077500000000000000000000000001435117461300160325ustar00rootroot00000000000000racc-1.6.2/.github/workflows/ubuntu.yml000066400000000000000000000016221435117461300201000ustar00rootroot00000000000000name: ubuntu concurrency: group: "${{github.workflow}}-${{github.ref}}" cancel-in-progress: true on: workflow_dispatch: pull_request: types: [opened, synchronize] branches: - '*' push: branches: - master tags: - v*.*.* schedule: - cron: "0 8 * * 3" # At 08:00 on Wednesday # https://crontab.guru/#0_8_*_*_3 jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby: - "2.5" - "2.6" - "2.7" - "3.0" - "3.1" - "head" - "jruby-9.2.21.0" - "jruby-9.3.9.0" - "jruby-head" - "truffleruby-22.3.0" - "truffleruby-head" steps: - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{matrix.ruby}} bundler-cache: true - run: bundle exec rake compile test racc-1.6.2/.gitignore000066400000000000000000000001721435117461300144250ustar00rootroot00000000000000*.bundle *.o *.so *.swp /pkg Makefile lib/racc/parser-text.rb tags tmp target lib/racc/cparse-jruby.jar Gemfile.lock html racc-1.6.2/COPYING000066400000000000000000000023751435117461300134770ustar00rootroot00000000000000Copyright (C) 2019 Yukihiro Matsumoto. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. racc-1.6.2/ChangeLog000066400000000000000000000501611435117461300142120ustar00rootroot00000000000000Tue Feb 20 18:45:45 2007 Minero Aoki * lib/racc/grammar.rb (separated_by): last commit was wrong. use optional default return value of #option. Tue Feb 20 18:27:48 2007 Minero Aoki * lib/racc/grammar.rb (separated_by): return [] for empty list. Tue Nov 7 07:13:47 2006 Minero Aoki * lib/racc/grammar.rb (Rule#prec): rule.prec{...} should set action. Tue Nov 7 06:38:57 2006 Minero Aoki * lib/racc/grammar.rb: system call error on writing log file should be ignored. * lib/racc/grammar.rb: never define lvar which have same name with block local variable. * lib/racc/iset.rb: ditto. * lib/racc/logfilegenerator.rb: ditto. * lib/racc/parser.rb: ditto. * lib/racc/state.rb: ditto. * lib/racc/statetransitiontable.rb: ditto. * test/test.rb: racc -c is obsolete, use --line-convert-all. Sun Oct 29 13:27:30 2006 Minero Aoki * lib/racc/grammarfileparser.rb: use String#lines instead of #to_a. * lib/racc/parserfilegenerator.rb: ditto. * lib/racc/compat.rb: provide Object#__send. * lib/racc/compat.rb: provide Object#__send!. * lib/racc/compat.rb: provide String#lines. Thu Aug 24 23:14:16 2006 Minero Aoki * lib/racc/grammar.rb: report conflicts/useless if $DEBUG. * lib/racc/statetransitiontable.rb: remove code for Ruby 1.4 compatibility. Fri Aug 4 01:02:36 2006 Minero Aoki * lib/racc/grammar.rb: #should_terminal should be called in #check_terminals. Fri Aug 4 00:44:56 2006 Minero Aoki * bin/racc: getopts -> optparse. * lib/racc/grammar.rb: value of error symbol is :error. * lib/racc/grammar.rb (check_terminals): string symbols are terminal. * lib/racc/grammarfileparser.rb (add_rule_block): specified-prec did not work. Fri Aug 4 00:29:53 2006 Minero Aoki * lib/racc/parserfilegenerator.rb (serialize_integer_list_compressed): fix typo. Thu Aug 3 22:20:34 2006 Minero Aoki * bin/y2racc: fix filename. Thu Aug 3 21:10:48 2006 Minero Aoki * bin/y2racc: getopts -> optparse. Thu Aug 3 19:35:34 2006 Minero Aoki * setup.rb: updated. Thu Aug 3 19:34:55 2006 Minero Aoki * bin/racc2y: getopts -> optparse. * bin/racc2y: rewrite code for new generator. * lib/racc/grammar.rb (_regist): did not check @delayed rules (it causes registering same dummy rules many times). * lib/racc/grammarfileparser.rb: refactoring: simplify syntax. * lib/racc/grammarfileparser.rb: new method GrammarFileParser.parse. * lib/racc/grammarfileparser.rb: new method GrammarFileParser.parse_file. Sat Jul 29 04:51:42 2006 Minero Aoki * lib/racc/pre-setup: We need not make grammarfileparser.rb. Sat Jul 29 04:30:33 2006 Minero Aoki * lib/racc/grammar.rb: allow '|' operation with meta rules (many, option...). Sat Jul 29 03:17:20 2006 Minero Aoki * lib/racc/grammar.rb (Grammar#parser_class): write log file when $DEBUG=true. * lib/racc/grammar.rb (Grammar.define): run block on a Racc::Grammar::DefinitionEnv object, instead of a Racc::Grammar object. * lib/racc/grammar.rb (DefinitionEnv): new method #null. * lib/racc/grammar.rb (DefinitionEnv): new method #many. * lib/racc/grammar.rb (DefinitionEnv): new method #many1. * lib/racc/grammar.rb (DefinitionEnv): new method #option. * lib/racc/grammar.rb (DefinitionEnv): new method #seperated_by. * lib/racc/grammar.rb (DefinitionEnv): new method #seperated_by1. * lib/racc/grammar.rb (DefinitionEnv): new method #action. Sat Jul 29 03:13:22 2006 Minero Aoki * lib/racc/compat.rb: reduce warning. Sun Jul 16 05:07:12 2006 Minero Aoki * lib/racc/compat.rb: implement Enumerable#each_slice for Ruby 1.8. * lib/racc/parserfilegenerator.rb: better output. * ext/racc/cparse/cparse.c: always use VALUE instead of struct cparse_params. * ext/racc/cparse/cparse.c: mark params->value_v. Thu Jul 6 20:44:48 2006 Minero Aoki * lib/racc/grammar.rb: on-the-fly generator implemented. * lib/racc/generator.rb -> statetransitiontable.rb, parserfilegenerator.rb, logfilegenerator.rb. * lib/racc/statetransitiontable.rb: new file. * lib/racc/parserfilegenerator.rb: new file. * lib/racc/logfilegenerator.rb: new file. * lib/racc/grammarfileparser.rb.in: removed. * lib/racc/grammarfileparser.rb: new file. uses on-the-fly generator. * misc/boot.rb: removed. * lib/racc/static.rb: new file, to import static generator (lib/racc.rb provides dynamic generator). * lib/racc/grammar.rb: grand refactoring. * lib/racc/sourcetext.rb: new method #to_s, #location. * lib/racc/state.rb: compute NFA/DFA on demand. * bin/racc: follow these changes. Thu Jul 6 20:39:42 2006 Minero Aoki * ext/racc/cparse/cparse.so: should mark VALUEs in cparse_params. Tue Jul 4 02:24:27 2006 Minero Aoki * bin/racc: simplify report code. * lib/racc/grammar.rb: introduce new methods for racc command. * lib/racc/states.rb: ditto. * lib/racc/generator.rb: class CodeGenerator -> ParserFileGenerator. * lib/racc/generator.rb: new class ParserFileGenerator::Params. * bin/racc: ditto. * misc/boot.rb: ditto. * lib/racc/grammarfileparser.rb.in: ditto. * lib/racc/grammarfileparser.rb.in: merge grammarfilescanner.rb. * lib/racc/grammarfilescanner.rb: removed. * lib/racc/grammarfileparser.rb.in: parses user code blocks. * lib/racc/usercodeparser.rb: removed. * lib/racc/generator.rb: remove user code parsing code. * lib/racc/grammarfileparser.rb.in: passes user code block by a SourceText object. * lib/racc/generator.rb: ditto. * lib/racc/sourcetext.rb: new file. * lib/racc/generator.rb: introduce DSL to describe file contents. Tue Jul 4 02:15:36 2006 Minero Aoki * lib/racc/debugflags.rb: remove unused class GenerationOptions. Tue Jul 4 02:14:48 2006 Minero Aoki * lib/racc/compat.rb: update coding style. Mon Jul 3 04:34:32 2006 Minero Aoki * lib/racc/compiler.rb: do not export Grammar/SymbolTable/States. * lib/racc/compiler.rb: make a new class for debug flags (Racc::DebugFlags). * lib/racc/compiler.rb: removed. * bin/racc: eliminate Racc::Compiler class. * bin/racc: refactor profiling code. * bin/racc: move file generation code to racc/generator.rb. * misc/boot.rb: does not emulate Racc::Compiler interface. * lib/racc.rb: new file to require whole generator. * lib/racc/grammar.rb: class RuleTable -> Grammar. * lib/racc/grammar.rb: Grammar.new does not acccept a Compiler. * lib/racc/grammar.rb: refactoring. * lib/racc/grammarfileparser.rb.in: GrammarFileParser.new does not accept a Compiler. * lib/racc/grammarfileparser.rb.in: #parser takes more 2 args, a filename and a base line number. * lib/racc/grammarfileparser.rb.in: refactoring. * lib/racc/output.rb -> generate.rb * lib/racc/generate.rb: class Formatter -> CodeGenerator. * lib/racc/generate.rb: CodeGenerator.new does not accept a Compiler. * lib/racc/generate.rb: a CodeGenerator got many parameters via setter method. * lib/racc/generate.rb: class VerboseOutputter -> LogFileGenerator. * lib/racc/generate.rb: LogFileGenerator.new does not accept a Compiler. * lib/racc/generate.rb: refactoring. * lib/racc/state.rb: class StateTable -> States. * lib/racc/state.rb: States.new does not acccept a Compiler. * lib/racc/state.rb: refactoring. * test/test.rb: -Da is obsolete (I forgot what this flag is). * test/test.rb: allow replacing racc via environment variable $RACC. Mon Jul 3 04:18:49 2006 Minero Aoki * Makefile: new task bootstrap-force. Sun Jul 2 19:46:58 2006 Minero Aoki * test/ichk.y: update coding style. Sun Jul 2 19:01:55 2006 Minero Aoki * ext/racc/cparse/cparse.c: must require version.h to get RUBY_VERSION_CODE. Sun Jul 2 18:33:32 2006 Minero Aoki * ext/racc/cparse/cparse.c: do not use rb_iterate to give a block to the method, use rb_block_call instead. [ruby-dev:28445] Mon Jun 19 02:38:18 2006 Minero Aoki * bin/racc: -g option is now -t. -g option is obsolete and is an alias of -t. Mon Jun 19 02:35:59 2006 Minero Aoki * ext/racc/cparse/cparse.c: K&R -> ANSI C. Mon Nov 21 02:37:10 2005 Minero Aoki * version 1.4.5 released. Mon Nov 21 02:31:18 2005 Minero Aoki * bin/racc: shebang line should include file extension. * lib/racc/compat.rb: method removed: bug!. * lib/racc/*.rb: racc compiler should not depend on Racc::ParseError. * lib/racc/*.rb: update copyright year. * lib/racc/*.rb: update coding style. * lib/racc/exception.rb: new file. Mon Nov 21 00:49:18 2005 Minero Aoki * Makefile: remove useless target `import'. * Makefile: generate parser-text.rb. * misc/dist.sh: setup.rb and COPYING is now in repository. * misc/dist.sh: generate parser-text.rb. Mon Nov 21 00:14:21 2005 Minero Aoki * bin/racc: read racc/parser.rb from parser-text.rb. * lib/racc/rubyloader.rb: no longer needed. * lib/racc/pre-setup: new file. * lib/racc/pre-setup: generate parser-text.rb. * lib/racc/pre-setup: generate grammarfileparser.rb. * misc/boot.rb: new method BootstrapCompiler.main. * misc/boot.rb: new method BootstrapCompiler.generate, which is used from pre-setup. Mon Nov 21 00:09:04 2005 Minero Aoki * bin/racc2y: refactoring. * bin/y2racc: refactoring. Sun Nov 20 23:46:42 2005 Minero Aoki * lib/racc/pre-setup: new file. Sun Nov 20 22:46:21 2005 Minero Aoki * COPYING: new file. Sun Nov 20 22:25:15 2005 Minero Aoki * setup.rb: import setup.rb 3.4.1. Thu Sep 29 02:51:56 2005 Minero Aoki * Makefile (clean): invoke `make clean' in ext. Thu Sep 29 02:50:56 2005 Minero Aoki * lib/racc/.cvsignore: removed. Thu Sep 29 02:46:30 2005 Minero Aoki * Makefile: use .makeparams system. * Makefile: unify lib/racc/Makefile. * Makefile: new target lib/racc/grammarfileparser.rb. * lib/racc/Makefile: unified by ./Makefile. * lib/racc/boot: removed (moved under misc). * misc/boot.rb: new file. Thu Sep 29 02:43:30 2005 Minero Aoki * setup.rb: new file. Tue Jul 26 23:37:46 2005 Minero Aoki * bin/racc: --no-omit-actions did not work (This patch is contributed by OHKUBO Takuya). Sun Jan 2 11:48:19 2005 Minero Aoki * lib/racc/grammer.rb (once_writer): bug! needs argument. Mon Feb 16 16:14:16 2004 Minero Aoki * test/echk.y: fix typo. * test/ichk.y: does not use amstd. * test/opt.y: untabify. Mon Feb 16 16:10:46 2004 Minero Aoki * lib/racc/boot: update coding style. * lib/racc/compat.rb: ditto. * lib/racc/compiler.rb: ditto. * lib/racc/grammar.rb: ditto. * lib/racc/grammarfileparser.rb.in: ditto. * lib/racc/grammarfilescanner.rb: ditto. * lib/racc/info.rb: ditto. * lib/racc/iset.rb: ditto. * lib/racc/output.rb: ditto. * lib/racc/parser.rb: ditto. * lib/racc/state.rb: ditto. * lib/racc/usercodeparser.rb: ditto. Mon Feb 16 16:01:34 2004 Minero Aoki * lib/racc/rubyloader.rb: imported rev1.6. Fri Dec 12 01:57:47 2003 Minero Aoki * sample/hash.y: use no_result_var option. * sample/array.y: use latest (my) coding style. * sample/array2.y: ditto. * sample/hash.y: ditto. * sample/lists.y: ditto. Wed Nov 5 19:50:35 2003 Minero Aoki * test/bench.y: remove dependency on amstd. * test/chk.y: ditto. * test/echk.y: ditto. * test/ichk.y: ditto. * test/intp.y: ditto. * test/opt.y: ditto. * test/percent.y: ditto. Wed Nov 5 19:11:15 2003 Minero Aoki * bin/racc (get_options): remove --no-extensions option; racc/parser is preloaded, Racc_No_Extension does not work. Mon Nov 3 22:41:42 2003 Minero Aoki * bin/racc: apply latest coding style. * lib/racc/parser.rb: ditto. * lib/racc/compat.rb: add File.read. Mon Nov 3 21:20:25 2003 Minero Aoki * ext/racc/cparse/cparse.c (parse_main): abort if length of state stack <=1, not ==0. * lib/racc/parser.rb: use <=1, not <2. * ext/racc/cparse/cparse.c: check_*() -> assert_*() * ext/racc/cparse/cparse.c (racc_cparse): define lvar `v' for debugging. * ext/racc/cparse/cparse.c (racc_yyparse): ditto. Mon Nov 3 17:21:55 2003 Minero Aoki * Makefile (all): make cparse.so. Mon Nov 3 17:19:26 2003 Minero Aoki * lib/racc/parser.rb: update version. * ext/racc/cparse/cparse.c: update version. Mon Nov 3 17:19:01 2003 Minero Aoki * Makefile: update version in parser.rb, cparse.c. Sun Oct 12 23:49:58 2003 Minero Aoki * version 1.4.4. Sun Oct 12 23:49:40 2003 Minero Aoki * bin/y2racc: did not work. * bin/y2racc: -u options did not work. Sun Oct 12 23:41:46 2003 Minero Aoki * misc/dist.sh: cd before make. Sun Oct 12 23:38:04 2003 Minero Aoki * Makefile (site): create $siteroot/{ja,en}/man/racc/*.html. Sun Oct 12 23:37:18 2003 Minero Aoki * doc/parser.rrd.m: missing 'j'. Sun Oct 12 23:29:11 2003 Minero Aoki * Makefile: new target `doc'. * Makefile: new target `clean'. * lib/racc/Makefile: new target `clean'. * misc/dist.sh: create documents before pack. Sun Oct 12 23:27:58 2003 Minero Aoki * doc/debug.rd.m: junk char was inserted. * doc/index.html.m: en/ja text were mixed. * doc/parser.rrd.m: add return values. * doc/usage.html.m: fix hyper link. Sun Oct 12 22:57:28 2003 Minero Aoki * doc.en/changes.html, doc.ja/changes.html -> doc/NEWS.rd.m * doc.en/command.html, doc.ja/command.html -> doc/command.html.m * doc.en/debug.html, doc.ja/debug.html -> doc/debug.rd.m * doc.en/grammar.html, doc.ja/grammar.html -> doc/grammar.rd.m * doc.en/index.html, doc.ja/index.html -> doc/index.html.m * doc.en/parser.html, doc.ja/parser.html -> doc/parser.rrd.m * doc.en/usage.html, doc.ja/usage.html -> doc/usage.html.m Sun Oct 12 18:46:21 2003 Minero Aoki * web/racc.ja.html: update descriptions. * web/racc.en.html: ditto. Sun Oct 12 18:43:45 2003 Minero Aoki * misc/dist.sh: remove web/ directory before distribute. Sun Oct 12 18:37:29 2003 Minero Aoki * Makefile: new target `site'. * web/racc.ja.html: new file. * web/racc.en.html: new file. Sun Oct 12 18:30:55 2003 Minero Aoki * misc/dist.sh: forgot to remove tmp comment out. Sun Oct 12 18:12:09 2003 Minero Aoki * lib/racc/info.rb: version 1.4.4. Sun Oct 12 18:11:42 2003 Minero Aoki * Makefile (dist): split out misc/dist.sh. * misc/dist.sh: new file. Sun Oct 12 17:18:47 2003 Minero Aoki * README.en: update documents. * README.ja: ditto. * doc.en/changes.html: ditto. * doc.en/command.html: ditto. * doc.en/debug.html: ditto. * doc.en/grammar.html: ditto. * doc.en/index.html: ditto. * doc.en/parser.html: ditto. * doc.en/usage.html: ditto. * doc.ja/changes.html: ditto. * doc.ja/command.html: ditto. * doc.ja/debug.html: ditto. * doc.ja/index.html: ditto. * doc.ja/parser.html: ditto. * doc.ja/usage.html: ditto. Sun Oct 12 16:24:46 2003 Minero Aoki * sameple/calc-ja.y: simplify. Sun Oct 12 16:24:16 2003 Minero Aoki * misc/y2racc -> bin/y2racc * misc/racc2y -> bin/racc2y Sun Oct 12 15:56:30 2003 Minero Aoki * bin/racc: follow method name change. Sun Oct 12 15:34:14 2003 Minero Aoki * Makefile: new target `test'. * Makefile: missing $datadir. Sun Oct 12 15:33:02 2003 Minero Aoki * README.ja: update description. * README.en: ditto. Sun Oct 12 15:25:23 2003 Minero Aoki * lib/racc/compiler.rb: adjust file names. * lib/racc/grammarfileparser.rb.in: ditto. * lib/racc/grammarfilescanner.rb: ditto. Sun Oct 12 15:24:53 2003 Minero Aoki * Makefile: new file. Sun Oct 12 15:19:57 2003 Minero Aoki * BUGS.en: removed. * BUGS.ja: removed. Sun Oct 12 15:10:38 2003 Minero Aoki * racc -> bin/racc * .cvsignore -> lib/racc/.cvsignore * lib/racc/Makefile: new file. * boot.rb -> lib/racc/boot * compat.rb -> lib/racc/compat.rb * compiler.rb -> lib/racc/compiler.rb * grammar.rb -> lib/racc/grammar.rb * in.raccp.rb -> lib/racc/grammarfileparser.rb.in * raccs.rb -> lib/racc/grammarfilescanner.rb * info.rb -> lib/racc/info.rb * iset.rb -> lib/racc/iset.rb * outpur.rb -> lib/racc/output.rb * parser.rb -> lib/racc/parser.rb * rubyloader.rb -> lib/racc/rubyloader.rb * state.rb -> lib/racc/state.rb * ucodep.rb -> lib/racc/usercodeparser.rb * cparse/MANIFEST -> ext/racc/cparse/MANIFEST * cparse/cparse.c -> ext/racc/cparse/cparse.c * cparse/depend -> ext/racc/cparse/depend * cparse/extconf.rb -> ext/racc/cparse/extconf.rb * cparse/.cvsignore -> ext/racc/cparse/.cvsignore Sun Oct 12 15:10:13 2003 Minero Aoki * test/test.rb: use /bin/rm if exists. Sun Oct 12 14:33:29 2003 Minero Aoki * rubyloader.rb: imported from amstd, rev 1.5. Sun Oct 12 14:24:47 2003 Minero Aoki * boot.rb: reformat only. * compiler.rb: ditto. * grammar.rb: ditto. * in.raccp.rb: ditto. * iset.rb: ditto. * output.rb: ditto. * raccs.rb: ditto. * state.rb: ditto. Sun Oct 12 14:17:22 2003 Minero Aoki * test/test.rb: refactoring. Tue Jun 24 03:14:01 2003 Minero Aoki * ucodep.rb: typo: Grammer -> Grammar Mon May 26 23:06:58 2003 Minero Aoki * compiler.rb: update copyright year. * grammar.rb: ditto. * in.raccp.rb: ditto. * info.rb: ditto. * iset.rb: ditto. * output.rb: ditto. * parser.rb: ditto. * raccs.rb: ditto. * state.rb: ditto. * ucodep.rb: ditto. Sun May 25 13:21:27 2003 Minero Aoki * raccs.rb: update coding style. Fri Nov 15 17:53:12 2002 Minero Aoki * racc: changes style. * parser.rb: ditto. Fri Nov 15 17:11:52 2002 Minero Aoki version 1.4.3. Fri Nov 15 17:08:01 2002 Minero Aoki * boot.rb, compiler.rb, grammar.rb, in.raccp.rb, iset.rb, output.rb, parser.rb, racc, raccs.rb, state.rb, ucodep.rb, misc/racc2y, misc/y2racc: follows (my) latest coding styles. Thu Nov 14 14:39:53 2002 Minero Aoki * raccs.rb: explicit method call for VCALL. Wed Oct 16 15:45:11 2002 Minero Aoki * parser.rb: reformat. Fri Aug 9 18:21:01 2002 Minero Aoki * cparse/cparse.c: use better variable/macro names. Wed Aug 7 08:39:19 2002 Minero Aoki * cparse/cparse.c: goto label requires stmt. Mon Aug 5 21:53:07 2002 Minero Aoki * cparse/cparse.c: grand refine. * cparse/depend: re-added from ruby/ext/racc/cparse. Tue Jun 4 00:15:28 2002 Minero Aoki * boot.rb: allow to omit last 'end'. Mon Jun 3 23:29:45 2002 Minero Aoki * racc (write_table_file): shebang must placed on first line. (reported by Hiroyuki Sato) racc-1.6.2/Gemfile000066400000000000000000000001761435117461300137340ustar00rootroot00000000000000source "https://rubygems.org" group :development do gem "bundler" gem "rake" gem "rake-compiler" gem "test-unit" end racc-1.6.2/README.ja.rdoc000066400000000000000000000055441435117461300146440ustar00rootroot00000000000000= Racc * http://i.loveruby.net/en/projects/racc/ == DESCRIPTION: Racc は LALR(1) パーサジェネレータです。 yacc の Ruby 版に相当します。 NOTE: Ruby 1.8.0 からは Racc のランタイムが標準添付されているので、 Racc で生成したパーサを安心して配布できます。また Ruby 1.6 系に 対応させたい場合は racc -E で生成してください。 == 必要環境 * Ruby 1.8 以降 (*) C コンパイラと make == インストール gem インストール: $ gem install racc setup.rb インストル: パッケージのトップディレクトリで次のように入力してください。 ($ は通常ユーザ、# はルートのプロンプトです) $ ruby setup.rb config $ ruby setup.rb setup ($ su) # ruby setup.rb install これで通常のパスに Racc がインストールされます。自分の好き なディレクトリにインストールしたいときは、setup.rb config に 各種オプションをつけて実行してください。オプションのリストは $ ruby setup.rb --help で見られます。 コンパイラがない場合 -------------------- config を以下のようにすれば、拡張モジュールなしで インストールできます。 $ ruby setup.rb config --without-ext == テスト sample/ 以下にいくつか Racc の文法ファイルのサンプルが用意 してあります。動くのも動かないのもありますが、少なくとも calc-ja.y は動くのでこれを処理してみましょう。Racc をインス トールしたあと $ racc -ocalc.rb calc-ja.y として下さい。処理は一瞬から数秒で終わるので、 $ ruby calc.rb を実行してください。ちゃんと動いてますか? Racc の文法など詳しいことは doc.ja/ ディレクトリ以下の HTML を 見てください。 == ライセンス このパッケージに付属するファイルの著作権は青木峰郎が保持します。 ライセンスは Ruby ライセンスです。ただしユーザが書いた規則 ファイルや、Racc がそこから生成した Ruby スクリプトはその対象 外です。好きなライセンスで配布してください。 == バグなど Racc を使っていてバグらしき現象に遭遇したら、下記のアドレスまで メールをください。作者にはバグを修正する義務はありませんがその 意思はあります。また、そのときはできるだけバグを再現できる文法 ファイルを付けてください。 青木峰郎(あおきみねろう) aamine@loveruby.net http://i.loveruby.net racc-1.6.2/README.rdoc000066400000000000000000000033571435117461300142530ustar00rootroot00000000000000= Racc * https://github.com/ruby/racc == DESCRIPTION: Racc is a LALR(1) parser generator. It is written in Ruby itself, and generates Ruby program. == Requirement * Ruby 2.5 or later. == Installation gem install: $ gem install racc setup.rb install: Type this in the top directory of the extracted archive: $ ruby setup.rb config $ ruby setup.rb setup ($ su) # ruby setup.rb install You can install Racc into your favorite directory by giving options to setup.rb. e.g. $ ruby setup.rb config --prefix=/usr For details, try "ruby setup.rb --help". If you don't have C Compiler ---------------------------- You can install Racc without C compilers. Type following command in config phase. $ ruby setup.rb config --without-ext == Testing Racc Racc comes with simple calculator. To compile this, on shell: $ racc -o calc calc.y This process costs few seconds (or less). Then type: $ ruby calc ... Does it work? For details of Racc, see HTML documents placed under 'doc/en/' and sample grammar files under 'sample/'. == License Racc is distributed under the same terms of ruby. (see the file COPYING). Note that you do NOT need to follow ruby license for your own parser (racc outputs). You can distribute those files under any licenses you want. == Bug Reports Any kind of bug report is welcome. If you find a bug of Racc, please report an issue at https://github.com/ruby/racc/issues. Your grammar file, debug output generated by "racc -g", are helpful. Minero Aoki aamine@loveruby.net http://i.loveruby.net racc-1.6.2/Rakefile000066400000000000000000000042311435117461300141020ustar00rootroot00000000000000# -*- ruby -*- require "bundler/gem_tasks" require 'rdoc/task' RDoc::Task.new(:docs) do |rd| spec = Gem::Specification.load("racc.gemspec") rd.main = "README.en.rdoc" rd.rdoc_files.include(spec.files.find_all { |file_name| file_name =~ /^(bin|lib|ext)/ || file_name !~ /\// }) title = "#{spec.name}-#{spec.version} Documentation" rd.options << "-t #{title}" end require 'rake/testtask' Rake::TestTask.new(:test) do |t| t.libs << "test/lib" t.ruby_opts << "-rhelper" t.test_files = FileList["test/**/test_*.rb"] if RUBY_VERSION >= "2.6" t.ruby_opts << "--enable-frozen-string-literal" t.ruby_opts << "--debug=frozen-string-literal" if RUBY_ENGINE != "truffleruby" end end gem 'rake-compiler', '>= 0.4.1' def java? /java/ === RUBY_PLATFORM end def jruby? Object.const_defined?(:RUBY_ENGINE) and 'jruby' == RUBY_ENGINE end file 'lib/racc/parser-text.rb' => ['lib/racc/parser.rb'] do |t| source = 'lib/racc/parser.rb' open(t.name, 'wb') { |io| io.write(<<-eorb) module Racc PARSER_TEXT = <<'__end_of_file__' #{File.read(source)} __end_of_file__ end eorb } end if jruby? # JRUBY require "rake/javaextensiontask" Rake::JavaExtensionTask.new("cparse") do |ext| jruby_home = RbConfig::CONFIG['prefix'] ext.lib_dir = File.join 'lib', 'racc' ext.ext_dir = File.join 'ext', 'racc' # source/target jvm ext.source_version = '1.6' ext.target_version = '1.6' jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar'] ext.classpath = jars.map { |x| File.expand_path x }.join( ':' ) ext.name = 'cparse-jruby' end task :compile => ['lib/racc/parser-text.rb'] else # MRI require "rake/extensiontask" Rake::ExtensionTask.new "cparse" do |ext| ext.lib_dir = File.join 'lib', 'racc' ext.ext_dir = File.join 'ext', 'racc', 'cparse' end task :compile => 'lib/racc/parser-text.rb' end task :sync_tool do require 'fileutils' FileUtils.cp "../ruby/tool/lib/core_assertions.rb", "./test/lib" FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib" FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib" end task :build => "lib/racc/parser-text.rb" task :test => :compile racc-1.6.2/TODO000066400000000000000000000002551435117461300131270ustar00rootroot00000000000000* check 'error' token handling. * interactive transition table monitor. * support backtracking. * output Ruby extention library? * LL(k)? (But it should not be called Racc) racc-1.6.2/bin/000077500000000000000000000000001435117461300132055ustar00rootroot00000000000000racc-1.6.2/bin/racc000077500000000000000000000207741435117461300140550ustar00rootroot00000000000000#!/usr/bin/env ruby # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". require 'racc/static' require 'optparse' def main output = nil debug_parser = false make_logfile = false logfilename = nil make_executable = false rubypath = nil embed_runtime = false debug_flags = Racc::DebugFlags.new line_convert = true line_convert_all = false omit_action_call = true superclass = nil check_only = false verbose = false profiler = RaccProfiler.new(false) parser = OptionParser.new parser.banner = "Usage: #{File.basename($0)} [options] " parser.on('-o', '--output-file=PATH', 'output file name [.tab.rb]') {|name| output = name } parser.on('-t', '--debug', 'Outputs debugging parser.') {|fl| debug_parser = fl } parser.on('-g', 'Equivalent to -t (obsolete).') {|fl| $stderr.puts "racc -g is obsolete. Use racc -t instead." if $VERBOSE debug_parser = fl } parser.on('-v', '--verbose', 'Creates .output log file.') {|fl| make_logfile = fl } parser.on('-O', '--log-file=PATH', 'Log file name [.output]') {|path| make_logfile = true logfilename = path } parser.on('-e', '--executable [RUBYPATH]', 'Makes executable parser.') {|path| make_executable = true rubypath = (path == 'ruby' ? nil : path) } parser.on('-E', '--embedded', "Embeds Racc runtime in output.") { embed_runtime = true } parser.on('--line-convert-all', 'Converts line numbers of user codes.') { line_convert_all = true } parser.on('-l', '--no-line-convert', 'Never convert line numbers.') { line_convert = false line_convert_all = false } parser.on('-a', '--no-omit-actions', 'Never omit actions.') { omit_action_call = false } parser.on('--superclass=CLASSNAME', 'Uses CLASSNAME instead of Racc::Parser.') {|name| superclass = name } parser.on('-C', '--check-only', 'Checks syntax and quit immediately.') {|fl| check_only = fl } parser.on('-S', '--output-status', 'Outputs internal status time to time.') { verbose = true } parser.on('-P', 'Enables generator profile') { profiler = RaccProfiler.new(true) } parser.on('-D flags', "Flags for Racc debugging (do not use).") {|flags| debug_flags = Racc::DebugFlags.parse_option_string(flags) } #parser.on('--no-extensions', 'Run Racc without any Ruby extension.') { # Racc.const_set :Racc_No_Extensions, true #} parser.on('--version', 'Prints version and quit.') { puts "racc version #{Racc::Version}" exit 0 } parser.on('--runtime-version', 'Prints runtime version and quit.') { printf "racc runtime version %s; %s\n", Racc::Parser::Racc_Runtime_Version, if Racc::Parser.racc_runtime_type == 'ruby' sprintf('ruby core version %s', Racc::Parser::Racc_Runtime_Core_Version_R) else sprintf('c core version %s', Racc::Parser::Racc_Runtime_Core_Version_C) end exit 0 } parser.on('--copyright', 'Prints copyright and quit.') { puts Racc::Copyright exit 0 } parser.on('--help', 'Prints this message and quit.') { puts parser.help exit 1 } begin parser.parse! rescue OptionParser::ParseError => err $stderr.puts err.message $stderr.puts parser.help exit 1 end if ARGV.empty? $stderr.puts 'no input' exit 1 end if ARGV.size > 1 $stderr.puts 'too many input' exit 1 end input = ARGV[0] begin $stderr.puts 'Parsing grammar file...' if verbose result = profiler.section('parse') { parser = Racc::GrammarFileParser.new(debug_flags) parser.parse(File.read(input), File.basename(input)) } if check_only $stderr.puts 'syntax ok' exit 0 end $stderr.puts 'Generating LALR states...' if verbose states = profiler.section('nfa') { Racc::States.new(result.grammar).nfa } $stderr.puts "Resolving #{states.size} states..." if verbose profiler.section('dfa') { states.dfa } $stderr.puts 'Creating parser file...' if verbose params = result.params.dup # Overwrites parameters given by a grammar file with command line options. params.superclass = superclass if superclass params.omit_action_call = true if omit_action_call # From command line option if make_executable params.make_executable = true params.interpreter = rubypath end params.debug_parser = debug_parser params.convert_line = line_convert params.convert_line_all = line_convert_all params.embed_runtime = embed_runtime profiler.section('generation') { generator = Racc::ParserFileGenerator.new(states, params) generator.generate_parser_file(output || make_filename(input, '.tab.rb')) } if make_logfile profiler.section('logging') { $stderr.puts 'Creating log file...' if verbose logfilename ||= make_filename(output || File.basename(input), '.output') File.open(logfilename, 'w') {|f| Racc::LogFileGenerator.new(states, debug_flags).output f } } end if debug_flags.status_logging log_useless states.grammar log_conflict states else has_useless = report_useless states.grammar has_conflicts = report_conflict states if has_useless || has_conflicts preamble = make_logfile ? 'C' : 'Turn on logging with "-v" and c' $stderr.puts %Q{#{preamble}heck ".output" file for details} end end profiler.report rescue Racc::Error, Errno::ENOENT, Errno::EPERM => err raise if $DEBUG or debug_flags.any? lineno = err.message.slice(/\A\d+:/).to_s $stderr.puts "#{File.basename $0}: #{input}:#{lineno} #{err.message.strip}" exit 1 end end def make_filename(path, suffix) path.sub(/(?:\..*?)?\z/, suffix) end LIST_LIMIT = 10 def report_list(enum, label) c = enum.count if c > 0 $stderr.puts "#{c} #{label}:" enum.first(LIST_LIMIT).each do |item| $stderr.puts " #{yield item}" end $stderr.puts " ..." if c > LIST_LIMIT end end # @return [Boolean] if anything was reported def report_conflict(states) if states.should_report_srconflict? reported = true $stderr.puts "#{states.n_srconflicts} shift/reduce conflicts" end if states.rrconflict_exist? reported = true $stderr.puts "#{states.n_rrconflicts} reduce/reduce conflicts" end reported end def log_conflict(states) logging('w') {|f| f.puts "ex#{states.grammar.n_expected_srconflicts}" if states.should_report_srconflict? f.puts "sr#{states.n_srconflicts}" end if states.rrconflict_exist? f.puts "rr#{states.n_rrconflicts}" end } end # @return [Boolean] if anything was reported def report_useless(grammar) reported = report_list(grammar.each_useless_nonterminal, 'useless nonterminals', &:to_s) reported ||= report_list(grammar.each_useless_rule, 'useless rules') { |r| "##{r.ident} (#{r.target})" } if grammar.start.useless? $stderr.puts 'fatal: start symbol does not derive any sentence' reported = true end reported end def log_useless(grammar) logging('a') {|f| if grammar.useless_nonterminal_exist? f.puts "un#{grammar.n_useless_nonterminals}" end if grammar.useless_rule_exist? f.puts "ur#{grammar.n_useless_rules}" end } end def logging(mode, &block) File.open("log/#{File.basename(ARGV[0])}", mode, &block) end class RaccProfiler def initialize(really) @really = really @log = [] unless ::Process.respond_to?(:times) # Ruby 1.6 @class = ::Time else @class = ::Process end end def section(name) if @really t1 = @class.times.utime result = yield t2 = @class.times.utime @log.push [name, t2 - t1] result else yield end end def report return unless @really f = $stderr total = cumulative_time() f.puts '--task-----------+--sec------+---%-' @log.each do |name, time| f.printf "%-19s %s %3d%%\n", name, pjust(time,4,4), (time/total*100).to_i end f.puts '-----------------+-----------+-----' f.printf "%-20s%s\n", 'total', pjust(total,4,4) end private def cumulative_time t = @log.inject(0) {|sum, (name, time)| sum + time } t == 0 ? 0.01 : t end def pjust(num, i, j) m = /(\d+)(\.\d+)?/.match(num.to_s) str = m[1].rjust(i) str.concat m[2].ljust(j+1)[0,j+1] if m[2] str end end main racc-1.6.2/doc/000077500000000000000000000000001435117461300132025ustar00rootroot00000000000000racc-1.6.2/doc/en/000077500000000000000000000000001435117461300136045ustar00rootroot00000000000000racc-1.6.2/doc/en/NEWS.en.rdoc000066400000000000000000000145041435117461300156360ustar00rootroot00000000000000= NEWS === 1.4.6 * Bugfixes * bin/racc -g option renamed to -t * racc/compiler.rb is removed * '|' is allowed with meta rules * Ruby 1.8.7 compatibility fixes * Ruby 1.9 compatibility fixes === 1.4.5 (2005-11-21) * [FEATURE CHANGE] --no-extensions option was removed. * [fix] racc command should not depend on `raccrt' package. * [fix] --no-omit-actions did not work. * setup.rb 3.4.1. === 1.4.4 (2003-10-12) * document changed. * -all packages does not include amstd and strscan. * setup.rb 3.2.1. === 1.4.3 (2002-11-14) * [fix] reduce ruby 1.8 warnings. === 1.4.2 (2002-01-29) * [new] new option --no-extentions === 1.4.1 (2001-12-02) * now Racc does not depend on amstd library. * update y2racc and racc2y for racc 1.4.1 === 1.4.0 (2001-11-30) * minor version up for checking in runtime library into ruby CVS repositry. * RaccParser, RaccScanner -> GrammarFileParser, GrammarFileScanner * modify typo (grammer -> grammar) === 1.3.12 (2001-11-22) * modify installer bug (thanks Tanaka Akira) * enhance regexp/%-strings/gvar detection in action block === 1.3.11 (2001-08-28) * modify scan error on $' $` $/ etc. === 1.3.10 (2001-08-12) * modify prototype missmatch in cparse.c === 1.3.9 (2001-04-07) * support Ruby 1.4 again. === 1.3.8 (2001-03-17) * output symbol name when error * Racc::Parser#token_to_str === 1.3.7 (2001-02-04) * allow nil for EndOfInput (experimental) * more sample grammar files === 1.3.6 (2001-01-22) * modify cparse.so for static link === 1.3.5 (2001-01-18) * %-string scanning was wrong * new directive "expect" === 1.3.4 (2001-01-11) * cparse: add type checks * cparse: rm depend * cparse: does not pass non-VALUE object to rb_iterate() === 1.3.3 (2000-12-25) * critical bug in generator (from 1.3.1) * racc --runtime-version === 1.3.2 (2000-12-21) * bug with racc -E * package strscan togather (again) === 1.3.1 (2000-12-17) * dynamically determine RE_DUP_MAX * ruby version routine was used always === 1.3.0 (2000-11-30) * can yield(sym,val) from scanner (Parser#yyparse) === 1.2.6 (2000-11-28) * class M::C === 1.2.5 (2000-11-20) * big changes in option; -h -f -p -i -n -c -A are incompatible * support long options * y2racc, racc2y leaves actions as default === 1.2.4 (2000-09-13) * updates installer and documents === 1.2.3 (2000-08-14) * output useless rules and nonterminals (version 2) * nonassoc makes error (never shift/reduce) === 1.2.2 (2000-08-12) * internal changes === 1.2.1 (2000-08-05) * racc2y, y2racc === 1.2.0 (2000-08-02) * uses bison's lookahead algorithm === 1.1.6 (2000-07-25) * new keyword "options" and its parameter "no_result_var" === 1.1.5 (2000-07-21) * [IMPORTANT] change keyword "token" to "convert" * NEW keyword "token" for token declearation === 1.1.4 (2000-07-13) * update installer * samples had bugs === 1.1.3 (2000-06-30) * new option -a; does not omit void action call === 1.1.2 (2000-06-29) * now racc does not use strscan.so * ScanError -> Racc::ScanError, ParseError -> Racc::ParseError * more friendly error messages === 1.1.1 (2000-06-15) * require miss * conflicts were not reported with -v === 1.1.0 (2000-06-12) * use other algolithm for generating state table === 1.0.4 (2000-06-04) * S/R conflict & -v flag causes unexpected exception (reported by Tosh) * output useless nonterminals/rules === 1.0.3 (2000-06-03) * use Array#collect! instead of #filter. === 1.0.2 (2000-05-16) * update installer (setup.rb) === 1.0.1 (2000-05-12) * state.rb: faster lookahead & debug lalr code * refine code * update amstd package (1.7.0) === 1.0.0 (2000-05-06) * version 1.0 === 0.14.6 (2000-05-05) * much more debug output === 0.14.5 (2000-05-01) === 0.14.4 (2000-04-09) * Racc_* are included in Racc_arg * faster state generation (a little) === 0.14.3 (2000-04-04) * check both of SYM2ID and ID2SYM (thanks Katsuyuki Komatsu) === 0.14.2 (2000-04-03) * "class" on first line causes parse error (thanks Yoshiki Wada) * new option "racc -V" === 0.14.1 (2000-03-31) === 0.14.0 (2000-03-21) * implement "fast" table (same to bison) * stop line no. conversion temporaliry because of ruby bug === 0.13.1 (2000-03-21) * racc --version --copyright did not work (thanks Tadayoshi Funaba) === 0.13.0 (2000-03-20) * implement yyerror/yyerrok/yyaccept === 0.12.2 (2000-03-19) * -E flag had bug === 0.12.1 (2000-03-16) * modify the way to decide default action === 0.12.0 (2000-03-15) * implement real LALR * use both SLR and LALR to resolve conflicts === 0.11.3 (2000-03-09) * modify lookahead routine again === 0.11.2 (2000-03-09) * bug in lookahead routine * modify cparse.so for Symbol class of ruby 1.5 === 0.11.1 (2000-03-08) * modify for Symbol * update strscan === 0.11.0 (2000-02-19) * if error is occured in action, ruby print line number of grammar file === 0.10.9 (2000-01-19) * change package/setup === 0.10.8 (2000-01-03) * (1-17 re-packed) add/modify documents === 0.10.7 (2000-01-03) * modify setup.rb, compile.rb, amstd/inst. (thanks: Koji Arai) === 0.10.6 (1999-12-24) * racc -e ruby * omit void action call === 0.10.5 (1999-12-21) * critical bug in embedded action implement * bug in setup.rb * modify calc[2].y for 0.10 === 0.10.4 (1999-12-19) * support error recover ('error' token) * can embed runtime by "racc -E" * Racc is module === 0.10.3 (1999-12-01) * support embedded action * modify .output bug === 0.10.2 (1999-11-27) * update document * separate libracc.rb === 0.10.1 (1999-11-19) * rewrite runtime routine in C * once next_token returns [false, *], not call next_token * action is only default, not call next_token * $end is obsolute * LALRactionTable === 0.10.0 (1999-11-06) * next_value, peep_token is obsolute * @__debug__ -> @yydebug * class...rule...end * refine libracc.rb * unify strscan library * *.rb are installed in lib/ruby/VERSION/racc/ === 0.9.5 (1999-10-03) * too few arguments for __show_stack__ * could not scan $end * typo in d.format.rb === 0.9.4 (1999-09-??) === 0.9.3 (1999-09-03) === 0.9.2 (1999-06-26) === 0.9.1 (1999-06-08) === 0.9.0 (1999-06-03) === 0.8.11 (?) === 0.8.10 (?) === 0.8.9 (1999-03-21) === 0.8.8 (1999-03-20) === 0.8.7 (1999-03-01) === 0.8.0 (1999-01-16) === 0.5.0 (1999-01-07) === 0.1.0 (1999-01-01) racc-1.6.2/doc/en/Overview-of-racc.md000066400000000000000000000257401435117461300172540ustar00rootroot00000000000000This post follows from [Overview of rexical part 2](https://github.com/tenderlove/rexical/wiki/Overview-of-rexical-part-2). Taken from Jeff Nyman's [blog post](http://webcache.googleusercontent.com/search?q=cache:http://testerstories.com/2012/06/a-tester-learns-rex-and-racc-part-3/&num=1&strip=1&vwsrc=0). # Combining rexical and racc Assuming you’ve been following along, you’ve broken your input into a stream of tokens. Now you need some way to recognize higher-level patterns. This is where Racc comes in: Racc lets you describe what you want to do with those tokens. That’s what I’ll be covering here: how the parser works with the lexer. You already know that you define a lexer specification with a rex file. This generates a lexer class. In a very similar fashion, you define a grammar with a racc file. This generates a parser class. If you want your own parser, you have to write grammar file. In the last post we started creating a new simple language to handle calculations of numbers. The language will consist of `ADD (+)`, `SUBTRACT (-)`, `MULTIPLY (*)`, and `DIVISION (/)`. Those operators are given as the symbol identifier as well as the actual symbol that maps to that identifier. The language also supports the notion of `DIGIT`. A `DIGIT` token must come before and after an operator. So there’s our language. Now we want to try to evaluate the code of this new language. You have to create a grammar file. The first part of this file, as with the Rex specification file, is a class block. In your project directory, create a file called `test_language.y`. This will be our grammar file. As with the start of our Rex file, we’ll use our class: ``` ruby class TestLanguage end ``` Then you will have a grammar block. In Rex, the grammar block is a way to specify the patterns for specific symbols. In Racc, the grammar block describes grammar that can be understood by the parser. As with Rex specification files, we’ll eventually add a rule clause to signify our grammar block. In fact, this clause will mark the start of what are called the “production rules.” To fill in these rules, you have to understand the Racc grammar. So let’s just look at the basics before we try to fill in our nascent grammar file. A grammar rule has the form: ``` some : THING ; ``` The symbols produced by a lexer are called terminals or tokens. The things assembled from them are called non-terminals. So in this schematic, ‘some’ represents a non-terminal while ‘THING’ is a terminal. So ‘THING’ must have been produced by the lexer and ‘some’ can only be created by assembling it from terminals. Here’s an example: ``` value : DIGIT ; ``` This means a construct called a value (a non-terminal) can be built up from a DIGIT (a terminal). Here’s another example: ``` expression : value '+' value ; ``` Here an expression can be built up from two values (which we know are DIGITS) and a plus sign. You can specify alternatives as well: ``` expression : value '+' value | value '-' value ; ``` Some people like to space all this out for better readability: ``` expression : value '+' value | value '-' value ; ``` There’s a lot of terminology coming your way here so, for now, just understand that terminals are all the basic symbols or tokens of which a given language is composed. Non-terminals are the syntactic variables in the grammar which represent a set of strings that the grammar is composed of. The general style used is to have all terminals in uppercase and all non-terminals in lowercase. As with Rex grammar, you can write actions inside curly braces. These actions are, once again, Ruby code. Since Racc needs tokens upon which to operate, Racc expects to call a method that yields tokens, where each token is a two element array with the first element being the type of token (matching the token declaration) and the second element the actual value of the token, which will usually be the actual text. This is a lot easier to show than talk about, although in reality it’s not so different from what you’ve seen with Rex files. So here’s an example: ``` expression : value '+' value { puts "Performing addition." } ``` But I could also do this: ``` expression : value '+' value { result = val[0] + val[2] } ``` If you ever read documentation on yacc, you’ll see mention of variables like `$$` and `$1` and so on. The equivalent of the above in yacc would be: ``` expression : value '+' value { $$ = $1 + $3; } ``` Here yacc’s `$$` is racc’s `result`, while yacc’s `$0`, `$1` would be the array `val`. Since Ruby is zero based, that’s why `$1` is `val[0]`. Okay, so let’s put this stuff to use. Change your grammar file so it looks like this: ``` ruby class TestLanguage rule expression : DIGIT | DIGIT ADD DIGIT { return val[0] + val[2] } end ``` What I’m basically saying here is that something I want to call an “expression” can be recognized if it’s a `DIGIT` or a `DIGIT ADD DIGIT`. From the lexer file, we know that `DIGIT` is going to correspond to an array like this: `[:DIGIT, text.to_i]`. We know that `ADD` is going to correspond to an array like this `[:ADD, text]`. Further, we know that `DIGIT` means `\d+` (i.e., any group of numbers) and `ADD` means `\+` (i.e., the literal plus sign). I’m also saying that when `DIGIT` is encountered nothing should happen. There is no action provided for that. But if `DIGIT ADD DIGIT` is encountered, then I want a return value provided, which is the addition of the first `DIGIT` with the second `DIGIT`. In order to compile this file, you can use the following command: ``` racc test_language.y -o parser.rb ``` To make that process more streamlined, let’s add a task to our Rakefile: ``` ruby desc "Generate Parser" task :parser do `racc test_language.y -o parser.rb` end ``` Now you can just type `rake parser` to generate the parser just as you can type `rake lexer` to generate the lexer. In fact, you might always want to force both things to happen, in which case you can add this task: ``` ruby desc "Generate Lexer and Parser" task :generate => [:lexer, :parser] ``` So how do we test this? Let’s create a file in our spec directory called `language_parser_spec.rb` and add the following to it: ``` ruby require './parser.rb' class TestLanguageParser describe 'Testing the Parser' do before do @evaluator = TestLanguage.new end it 'tests for a digit' do @result = @evaluator.parse("2") @result.should == 2 end end end ``` Structurally you can see that this is similar to the test format we established in our `language_lexer_spec.rb` file. Running this test will tell you that there is no parse method. That’s true. Just as we added a tokenize method to our rex specification, we have to add a parse method to our grammar file. Here’s how you can do that: ``` ruby class TestLanguage rule expression : DIGIT | DIGIT ADD DIGIT { return val[0] + val[2] } end ---- inner def parse(input) scan_str(input) end ``` Once again, we use an inner section just as we did with our rex specification. Notice a few major differences here, though. First, you must preface the section with four hyphens (-). Also, notice that the inner section is **OUTSIDE** of the class block. This is different from the rex specification where the inner section was inside the class block. Make sure to regenerate your files and then run the test again. Now you will be told that the `scan_str` method is undefined. This method is defined in the lexer and so you have to add another section to your grammar file: ``` ruby class TestLanguage rule expression : DIGIT | DIGIT ADD DIGIT { return val[0] + val[2] } end ---- header require_relative 'lexer' ---- inner def parse(input) scan_str(input) end ``` If you regenerate and run your test, the parser test should pass. However, we have not made it do a calculation yet. So add the following test: ``` ruby ... it 'tests for addition' do @result = @evaluator.parse("2+2") @result.should == 4 end ... ``` If that’s working for you, congratulations! You now have the start of a working language. Granted, it’s just a calculator example but let’s not leave it just doing addition. You can fill out the rest of the grammar file as such: ``` ruby class TestLanguage rule expression : DIGIT | DIGIT ADD DIGIT { return val[0] + val[2] } | DIGIT SUBTRACT DIGIT { return val[0] - val[2] } | DIGIT MULTIPLY DIGIT { return val[0] * val[2] } | DIGIT DIVIDE DIGIT { return val[0] / val[2] } end ---- header require_relative 'lexer' ---- inner def parse(input) scan_str(input) end ``` I’ll leave it as an exercise to fill in the existing tests to prove that the grammar for all calculation types is being read and processed correctly. This simple example still leaves a lot open. For example, while `2+2` will work `2 + 2` (with spaces) will not. This is because your lexer did not specify any white space and thus the parser does not know how to recognize it. Also, what about more complicated expressions? Can I do `2+2+2`? Try it and you’ll find that it does not. And even if I could support that, how would I handle operator precedence? For example would `2+3*2` give me `8`, as it should, or would it instead give me `10`? Keep in mind that in order to solve problems like this, you have to account for how your grammar can be constructed. For example, to allow for spaces, you just have to indicate that (1) blank spaces are allowed and (2) that no action occurs when a blank space is matched. In fact, you’ve already seen this in prior examples here. Just add the following to your rex specification file (`test_language.rex`): ``` ruby class TestLanguage macro BLANK [\ \t]+ DIGIT \d+ ADD \+ SUBTRACT \- MULTIPLY \* DIVIDE \/ rule {BLANK} # no action {DIGIT} { [:DIGIT, text.to_i] } {ADD} { [:ADD, text] } {SUBTRACT} { [:SUBTRACT, text] } {MULTIPLY} { [:MULTIPLY, text] } {DIVIDE} { [:DIVIDE, text] } inner def tokenize(code) scan_setup(code) tokens = [] while token = next_token tokens << token end tokens end end ``` That simple bit of extra information allows your parser to handle `2 + 2` as well as it does `2+2`. The other problems are a little more involved. And with that, perhaps you can see now that even with a simple calculator, it takes some work to get it to do what you want in terms of a language. And yet all programming languages that you work with are ultimately constructed of grammar specified just in the way we have been doing it here. I’ve learned a lot by trying to figure out how these tools work. One of my goals is to see if I can build an alternative to Gherkin, the language that is used by Cucumber to create what are called feature files. I think this will be a challenging exercise. Beyond that, I can see some good exercises here for constructing test grammars for test description languages. Playing around with concepts like these could, perhaps, lead to some interesting notions about how we communicate about tests.racc-1.6.2/doc/en/grammar.en.rdoc000066400000000000000000000113001435117461300164770ustar00rootroot00000000000000= Racc Grammar File Reference == Global Structure == Class Block and User Code Block There are two top-level blocks: the 'class' block, and the 'user code' block. The 'user code' block MUST be after the 'class' block. == Comment Comments can be added about everywhere. Two comment styles are supported: Ruby style (`# ...`) and C style (`/* ... */`). == Class Block The class block is formed like this: -- class CLASS_NAME [precedence table] [token declarations] [expected number of S/R conflict] [options] [semantic value conversion] [start rule] rule GRAMMARS -- CLASS_NAME is a name of parser class. This is the name of generating parser class. If CLASS_NAME includes '::', Racc outputs module clause. For example, writing "class M::C" causes creating the code bellow: -- module M class C : : end end -- == Grammar Block The grammar block describes the grammar to be understood by parser. Syntax is: -- (token): (token) (token) (token).... (action) (token): (token) (token) (token).... (action) | (token) (token) (token).... (action) | (token) (token) (token).... (action) -- (action) is an action which is executed when its (token)s are found. (action) is a ruby code block, which is surrounded by braces: -- { print val[0] puts val[1] } -- Note that you cannot use '%' string, here document, '%r' regexp in action. Actions can be omitted. When it is omitted, '' (empty string) is used. A return value of action is a value of left side value ($$). It is value of result, or returned value by "return" statement. Here is an example of whole grammar block. -- rule goal: definition rules source { result = val } definition: /* none */ { result = [] } | definition startdesig { result[0] = val[1] } | definition precrule # this line continue from upper line { result[1] = val[1] } startdesig: START TOKEN -- You can use following special local variables in action. * result ($$) The value of left-hand side (lhs). A default value is val[0]. * val ($1,$2,$3...) An array of value of right-hand side (rhs). * _values (...$-2,$-1,$0) A stack of values. DO NOT MODIFY this stack unless you know what you are doing. == Operator Precedence This function is equal to '%prec' in yacc. To designate this block: -- prechigh nonassoc '++' left '*' '/' left '+' '-' right '=' preclow -- `right' is yacc's %right, `left' is yacc's %left. `=' + (symbol) means yacc's %prec: -- prechigh nonassoc UMINUS left '*' '/' left '+' '-' preclow rule exp: exp '*' exp | exp '-' exp | '-' exp =UMINUS # equals to "%prec UMINUS" : : -- == expect Racc supports Bison's "expect" directive to declare the expected number of shift/reduce conflicts. -- class MyParser rule expect 3 : : -- Then warnings are issued only when the effective number of conflicts differs. == Declaring Tokens Declaring tokens avoids many bugs. Racc outputs warnings for declared tokens that do not exist, or existing tokens not declared. The syntax is: -- token TOKEN_NAME AND_IS_THIS ALSO_THIS_IS AGAIN_AND_AGAIN THIS_IS_LAST -- == Options You can write options for racc command in your racc file. -- options OPTION OPTION ... -- Options are: * omit_action_call omit empty action call or not. * result_var use/does not use local variable "result" You can use 'no_' prefix to invert its meanings. == Converting Token Symbol Token symbols are, as default, * naked token strings in racc file (TOK, XFILE, this_is_token, ...) --> symbol (:TOK, :XFILE, :this_is_token, ...) * quoted strings (':', '.', '(', ...) --> same string (':', '.', '(', ...) You can change this default using a "convert" block. Here is an example: -- convert PLUS 'PlusClass' # We use PlusClass for symbol of `PLUS' MIN 'MinusClass' # We use MinusClass for symbol of `MIN' end -- We can use almost all ruby value can be used by token symbol, except 'false' and 'nil'. These are causes unexpected parse error. If you want to use String as token symbol, special care is required. For example: -- convert class '"cls"' # in code, "cls" PLUS '"plus\n"' # in code, "plus\n" MIN "\"minus#{val}\"" # in code, \"minus#{val}\" end -- == Start Rule '%start' in yacc. This changes the start symbol. -- start real_target -- == User Code Block A "User Code Block" is a piece of Ruby source code copied in the output. There are three user code blocks, "header" "inner" and "footer". User code blocks are introduced by four '-' at the beginning of a line, followed by a single-word name: -- ---- header ruby statement ruby statement ruby statement ---- inner ruby statement : : -- racc-1.6.2/doc/en/grammar2.en.rdoc000066400000000000000000000117411435117461300165720ustar00rootroot00000000000000= Racc Grammar File Reference == Global Structure == Class Block and User Code Block There are two blocks on the toplevel. One is the 'class' block, the other is the 'user code' block. The 'user code' block MUST be placed after the 'class' block. == Comments You can insert comments about all places. Two styles of comments can be used, Ruby style '#.....' and C style '/\*......*\/'. == Class Block The class block is formed like this: class CLASS_NAME [precedence table] [token declarations] [expected number of S/R conflicts] [options] [semantic value conversion] [start rule] rule GRAMMARS CLASS_NAME is a name of the parser class. This is the name of the generating parser class. If CLASS_NAME includes '::', Racc outputs the module clause. For example, writing "class M::C" causes the code below to be created: module M class C : : end end == Grammar Block The grammar block describes grammar which is able to be understood by the parser. Syntax is: (token): (token) (token) (token).... (action) (token): (token) (token) (token).... (action) | (token) (token) (token).... (action) | (token) (token) (token).... (action) (action) is an action which is executed when its (token)s are found. (action) is a ruby code block, which is surrounded by braces: { print val[0] puts val[1] } Note that you cannot use '%' string, here document, '%r' regexp in action. Actions can be omitted. When it is omitted, '' (empty string) is used. A return value of action is a value of the left side value ($$). It is the value of the result, or the returned value by `return` statement. Here is an example of the whole grammar block. rule goal: definition rules source { result = val } definition: /* none */ { result = [] } | definition startdesig { result[0] = val[1] } | definition precrule # this line continues from upper line { result[1] = val[1] } startdesig: START TOKEN You can use the following special local variables in action: * result ($$) The value of the left-hand side (lhs). A default value is val[0]. * val ($1,$2,$3...) An array of value of the right-hand side (rhs). * _values (...$-2,$-1,$0) A stack of values. DO NOT MODIFY this stack unless you know what you are doing. == Operator Precedence This function is equal to '%prec' in yacc. To designate this block: prechigh nonassoc '++' left '*' '/' left '+' '-' right '=' preclow `right` is yacc's %right, `left` is yacc's %left. `=` + (symbol) means yacc's %prec: prechigh nonassoc UMINUS left '*' '/' left '+' '-' preclow rule exp: exp '*' exp | exp '-' exp | '-' exp =UMINUS # equals to "%prec UMINUS" : : == expect Racc has bison's "expect" directive. # Example class MyParser rule expect 3 : : This directive declares "expected" number of shift/reduce conflicts. If "expected" number is equal to real number of conflicts, Racc does not print conflict warning message. == Declaring Tokens By declaring tokens, you can avoid many meaningless bugs. If declared token does not exist or existing token does not decleared, Racc output warnings. Declaration syntax is: token TOKEN_NAME AND_IS_THIS ALSO_THIS_IS AGAIN_AND_AGAIN THIS_IS_LAST == Options You can write options for Racc command in your Racc file. options OPTION OPTION ... Options are: * omit_action_call omits empty action call or not. * result_var uses local variable "result" or not. You can use 'no_' prefix to invert their meanings. == Converting Token Symbol Token symbols are, as default, * naked token string in Racc file (TOK, XFILE, this_is_token, ...) --> symbol (:TOK, :XFILE, :this_is_token, ...) * quoted string (':', '.', '(', ...) --> same string (':', '.', '(', ...) You can change this default by "convert" block. Here is an example: convert PLUS 'PlusClass' # We use PlusClass for symbol of `PLUS' MIN 'MinusClass' # We use MinusClass for symbol of `MIN' end We can use almost all ruby value can be used by token symbol, except 'false' and 'nil'. These cause unexpected parse error. If you want to use String as token symbol, special care is required. For example: convert class '"cls"' # in code, "cls" PLUS '"plus\n"' # in code, "plus\n" MIN "\"minus#{val}\"" # in code, \"minus#{val}\" end == Start Rule '%start' in yacc. This changes start rule. start real_target == User Code Block "User Code Block" is a Ruby source code which is copied to output. There are three user code blocks, "header" "inner" and "footer". Format of user code is like this: ---- header ruby statement ruby statement ruby statement ---- inner ruby statement : : If four '-' exist on the line head, Racc treats it as the beginning of the user code block. The name of the user code block must be one word. racc-1.6.2/doc/en/racc.en.rhtml000066400000000000000000000020431435117461300161640ustar00rootroot00000000000000% require 'makefile' % version = Makefile.get_parameter('Makefile', 'version')

Racc

Version<%= version %>
TypeParser Generator
FormatRuby script + Ruby extension
Requirementruby (>=1.6)
LicenseLGPL

-- Download (.tar.gz) -- Old Versions -- Online Manual --

Racc (Ruby yACC) is a LALR(1) parser generator for Ruby. Version 1.4.x is stable release.

Parsers generated by Racc requires "Racc Runtime Module". Ruby 1.8.x comes with this runtime. If you want to run your parsers with ruby 1.6.x, use "racc -E" command. For details, see online manual.

Git

You can use the latest version of Racc using git. To clone out a working copy, type:

$ git clone https://github.com/ruby/racc.git
racc-1.6.2/doc/ja/000077500000000000000000000000001435117461300135745ustar00rootroot00000000000000racc-1.6.2/doc/ja/NEWS.ja.rdoc000066400000000000000000000232631435117461300156200ustar00rootroot00000000000000= NEWS === 1.4.6 * バグの修正 * bin/racc -g オプションを -t に改名 * racc/compiler.rb を削除 * '|' が meta rules によって許可 * Ruby 1.8.7 互換性を修正 * Ruby 1.9 互換性を修正 === 1.4.5 (2005-11-21) * [FEATURE CHANGE] --no-extensions オプションを削除 * [fix] racc パッケージのみで -E を使えるように修正 * [fix] --no-omit-actions が動作していなかったのを修正 * setup.rb 3.4.1. === 1.4.4 (2003-10-12) * Ruby 1.8.0 に対応するリリース。本体に変更はなし * -all パッケージに strscan, amstd の同梱するのをやめた * setup.rb 3.2.1 === 1.4.3 (2002-11-14) * [fix] ruby 1.8 の警告を消した === 1.4.2 (2002-01-29) * [new] 新しいオプション --no-extentions === 1.4.1 (2001-12-02) * amstd 非依存になった (ただし -all パッケージへバンドルは継続) * y2racc racc2y を 1.4 対応にした === 1.4.0 (2001-11-30) * ランタイムを Ruby の CVS に入れたのにあわせてマイナーバージョンアップ * RaccParser, RaccScanner → GrammarFileParser, GrammarFileScanner * ハズい typo を修正 (grammer → grammar) === 1.3.12 (2001-11-22) * インストーラのバグを修正 (thanks Tanaka Akira) * アクション中の正規表現や % 文字列、グローバル変数の検出を向上させた === 1.3.11 (2001-08-28) * アクション中の $' $` $/ などを正しくスキャン === 1.3.10 (2001-08-12) * cparse.c のプロトタイプ違いを直した === 1.3.9 (2001-04-07) * Ruby 1.4 に(再び)対応した === 1.3.8 (2001-03-17) * パースエラーの時に記号名も出力するようにした * Racc::Parser#token_to_s === 1.3.7 (2001-02-04) * サンプルを増やした === 1.3.6 (2001-01-22) * cparse がスタティックリンクされても動くようにした === 1.3.5 (2001-01-18) * % 文字列のスキャンがバグってた * 新しい命令 expect === 1.3.4 (2001-01-11) * cparse: シンボルのタイプチェックを入れた * cparse: depend を消した * cparse: rb_iterate 中の GC で落ちるバグを修正 === 1.3.3 (2000-12-25) * ジェネレータに致命的なバグ。1.3.1 から混入 (format.rb) * racc --runtime-version === 1.3.2 (2000-12-21) * -E が失敗するのを直した * 再度 strscan を同梱 (y2racc/racc2y に必要) === 1.3.1 (2000-12-17) * 正規表現の繰り返し指定の上限を動的に決定する (RE_DUP_MAX) * パースルーチンが常に Ruby 版になっていた (消し忘れ) === 1.3.0 (2000-11-30) * スキャナから yield でトークンを渡せるようになった === 1.2.6 (2000-11-28) * class M::C を許した === 1.2.5 (2000-11-20) * オプションに大変動。非互換オプションは -h -f -p -i -n -c -A * ロングオプションをサポート * y2racc, racc2y はデフォルトでアクションを残すようにした === 1.2.4 (2000-09-13) * インストーラとドキュメントを更新 === 1.2.3 (2000-08-14) * 使われない規則と非終端記号を出力 (強力版) * S/R conflict の時 nonassoc で解決するならばエラー === 1.2.2 (2000-08-12) * 内部の変更 === 1.2.1 (2000-08-05) * yacc との変換コマンド racc2y・y2racc を添付 === 1.2.0 (2000-08-02) * 先読みアルゴリズムを bison のものに変更 === 1.1.6 (2000-07-25) * 新たなキーワード options とその引数 no_result_var === 1.1.5 (2000-07-21) * [重要] token を convert に変更 * 「新たな」キーワード token (終端記号の宣言) === 1.1.4 (2000-07-13) * サンプルがバグってた === 1.1.3 (2000-06-30) * 空アクションの呼び出しを省略しないようにするオプション -a === 1.1.2 (2000-06-29) * スキャナで strscan を使わないようにした * ScanError -> Racc::ScanError, ParseError -> Racc::ParseError * エラーメッセージを強化 === 1.1.1 (2000-06-15) * requireミス (thanks Toshさん) * -v をつけるとconflictが報告されなくなっていた === 1.1.0 (2000-06-12) * 新しい 状態遷移表生成アルゴリズム === 1.0.4 (2000-06-04) * S/R conflict がおきると .output 出力で落ちるバグ修正 (Tosh さんの報告) * 使われない非終端記号・規則を表示 === 1.0.3 (2000-06-03) * filter -> collect! === 1.0.2 (2000-05-16) * インストーラをアップデート === 1.0.1 (2000-05-12) * state.rb: 先読みルーチンをちょっとだけ高速化 && 追加デバッグ * コードを整理した。著作権表示全体を全部のファイルにつけた。 * amstd アップデート (1.7.0) === 1.0.0 (2000-05-06) * バージョン 1.0 === 0.14.6 (2000-05-05) * デバッグ出力を詳細にした === 0.14.5 (2000-05-01) * インストーラを ruby 1.4.4 系の新しいパスに対応させた === 0.14.4 (2000-04-09) * パーサの定数を削減(Racc_arg にまとめた) * state 生成を微妙に高速化(コアを文字列に変換) === 0.14.3 (2000-04-04) * cparse の SYM2ID と ID2SYM のチェックを分離 (thanks 小松さん) === 0.14.2 (2000-04-03) * 一行目の class がパースエラーになっていた (thanks 和田さん) * 新しいフラグ racc -V === 0.14.1 (2000-03-31) === 0.14.0 (2000-03-21) * 高速テーブルを実装 * 一時的にファイル名/行番号の変換をやめた(Rubyのバグのため。) === 0.13.1 (2000-03-21) * --version --copyright などがうまく働いてなかった (thanks ふなばさん) === 0.13.0 (2000-03-20) * yyerror/yyerrok/yyaccept を実装 === 0.12.2 (2000-03-19) * -E フラグがバグってた (thanks ふなばさん) === 0.12.1 (2000-03-16) * デフォルトアクションの決め方をちょっと修正(元に戻しただけ) === 0.12.0 (2000-03-15) * 完全な LALR を実装したら遅くなったので SLR も併用するようにした。効果絶大。 === 0.11.3 (2000-03-09) * 状態遷移表生成のバグの修正がまだ甘かった。さらに別のバグもあるようだ。 === 0.11.2 (2000-03-09) * cparse が Symbol に対応できてなかった === 0.11.1 (2000-03-08) * ruby 1.5 の Symbol に対応 * strscan を最新に === 0.11.0 (2000-02-19) * 例外のとき、元のファイルの行番号が出るようにした === 0.10.9 (2000-01-19) * セットアップ方法など細かな変更 === 0.10.8 (2000-01-03) * 忘れてしまったけどたしかインストーラ関係の修正 * (1/17 repacked) ドキュメントの追加と修正 === 0.10.7 (2000-01-03) * setup.rb compile.rb amstd/inst などのバグ修正 === 0.10.6 (1999-12-24) * racc -e ruby でデフォルトパスを使用 * 空のアクションの呼びだしは省略するようにした === 0.10.5 (1999-12-21) * 埋めこみアクションの実装がすさまじくバグってた * setup.rb が inst.rb の変化に追従してなかった * calc.y calc2.y を 0.10 用に修正 === 0.10.4 (1999-12-19) * エラー回復モードを実装 * racc -E で単体で動作するパーサを生成 * Racc は class から module になった === 0.10.3 (1999-12-01) * 埋めこみアクションをサポート * .output の出力内容にバグがあったのを修正 === 0.10.2 (1999-11-27) * ドキュメントの訂正と更新 * libracc.rb を分割 === 0.10.1 (1999-11-19) * C でランタイムを書きなおした * next_token が false を返したらもう読みこまない * アクションがトークンによらず決まるときは next_token を呼ばない * $end 廃止 * LALRactionTable === 0.10.0 (1999-11-06) * next_* を next_token に一本化、peep_token 廃止 * @__debug__ -< @yydebug など変数名を大幅変更 * 文法ファイルの構造が class...rule...end に変わった * コアのコードを一新、高速化 * strscan を併合 * ライブラリを racc/ ディレクトリに移動 === 0.9.5 (1999-10-03) * 0.9.4 の変更がすごくバグってた * $end が通らなかったのを修正 * __show_stack__ の引数が違ってた === 0.9.4 (1999-09-??) * Parser::Reporter をなくしてメソッドに戻した * d.format.rb を再編成 === 0.9.3 (1999-09-03) * racc.rb -> racc === 0.9.2 (1999-06-26) * strscan使用 === 0.9.1 (1999-06-08) * アクション中の正規表現に対応 ( /= にも注意だ) * アクション中の # コメントに対応 === 0.9.0 (1999-06-03) * アクションを { } 形式にした * ユーザーコードを '----' を使う形式にした === 0.8.11 (?) * -g の出力をわかりやすくした === 0.8.10 (?) * アクションからreturnできるようにした === 0.8.9 (1999-03-21) * -g + @__debug__をつかったデバッグメッセージ操作 * エラー発生時のバグを修正 * TOKEN_TO_S_TABLEを付加するようにした === 0.8.8 (1999-03-20) * 100倍程度の高速化 * defaultトークンを加えた * デバッグ用ソースを出力するオプション-gをくわえた * user_initializeを廃止し、普通にinitializeを使えるようにした * parse_initialize/finalize,parseメソッドを廃止 * next_token,next_value,peep_tokenのデフォルトを廃止 * %precと同等の機能を加えた === 0.8.7 (1999-03-01) * 内部構造が大幅に変化 * マニュアルがHTMLになった === 0.8.0 (1999-01-16) * 文法がブロック型に変化 === 0.5.0 (1999-01-07) * 演算子優先順位が実装されたようだ * スタート規則が実装されたようだ * トークン値の置換が実装されたようだ(後に致命的なバグ発見) === 0.1.0 (1999-01-01) * とにかく動くようになった racc-1.6.2/doc/ja/command.ja.html000066400000000000000000000066111435117461300164750ustar00rootroot00000000000000

Raccコマンドリファレンス

racc [-ofilename] [--output-file=filename] [-erubypath] [--executable=rubypath] [-v] [--verbose] [-Ofilename] [--log-file=filename] [-g] [--debug] [-E] [--embedded] [-l] [--no-line-convert] [-c] [--line-convert-all] [-a] [--no-omit-actions] [-C] [--check-only] [-S] [--output-status] [--version] [--copyright] [--help] grammarfile

filename
Raccの文法ファイルを指定します。拡張子には特に制限はありません。
-ooutfile, --output-file=outfile
作成するクラスをかきこむファイル名を指定します。デフォルトは.tab.rbです。
-Ofilename, --log-file=filename
-v オプションをつけた時に生成するログファイルの名前を filename に変更します。 デフォルトは filename.output です。
-erubypath, --executable=rubypath
実行可能ファイルを生成します。rubypathは Ruby 本体のパスです。 rubypathを単に 'ruby' にした時には Racc が動作している Ruby のパスを使用します。
-v, --verbose
ファイル "filename".output に詳細な解析情報を出力します。
-g, --debug
出力するコードにデバッグ用コードを加えます。-g をつけて生成したパーサで @yydebug を true にセットすると、デバッグ用のコードが出力されます。
-g をつけるだけでは何もおこりませんので注意してください。
-E, --embedded
ランタイムルーチンをすべて含んだコードを生成します。 つまり、このオプションをつけて生成したコードは Ruby さえあれば動きます。
-C, --check-only
(文法ファイルの) 文法のチェックだけをして終了します。
-S, --output-status
進行状況を逐一報告します。
-l, --no-line-convert

Ruby では例外が発生した時のファイル名や行番号を表示してくれますが、 Racc の生成したパーサは、デフォルトではこの場合のファイル名・行番号を 文法ファイルでのものに置きかえます。このフラグはその機能をオフにします。

ruby 1.4.3 以前のバージョンではバグのために定数の参照に失敗する 場合があるので、定数参照に関してなにかおかしいことがおこったらこのフラグを 試してみてください。

-c, --line-convert-all
アクションと inner に加え header footer の行番号も変換します。 header と footer がつながっているような場合には使わないでください。
-a, --no-omit-actions
全てのアクションに対応するメソッド定義と呼び出しを行います。 例えアクションが省略されていても空のメソッドを生成します。
--version
Racc のバージョンを出力して終了します。
--copyright
著作権表示を出力して終了します。
--help
オプションの簡単な説明を出力して終了します。
racc-1.6.2/doc/ja/debug.ja.rdoc000066400000000000000000000040771435117461300161340ustar00rootroot00000000000000= パーサのデバッグ ここでは、Racc を使っていくうえで遭遇しそうな問題について書きます。 == 文法ファイルがパースエラーになる エラーメッセージに出ている行番号のあたりを見て間違いを 探してください。ブロックを閉じる行でエラーになる場合は、 どこかで開き括弧などを増やしてしまっている可能性が高いです。 == なんたら conflict って言われた 一番ありがちで一番面倒な問題は衝突 (conflict) でしょう。 文法中に衝突があると、racc はコンパイル後に 「5 shift/reduce conflict」のようなメッセージを表示します。 -v をつけると出力される .output ファイルからはさらに詳しい情報が得られます。 それをどう使うか、とかそういうことに関しては、それなりの本を読んでください。 とてもここに書けるような単純な話ではありません。 当然ながら『Ruby を 256 倍使うための本 無道編』(青木峰郎著)がお勧めです。 == パーサは問題なく生成できたけど予想どおりに動かない racc に -g オプションをつけてパーサを出力すると、デバッグ用のコードが 付加されます。ここで、パーサクラスのインスタンス変数 @yydebug を true に しておいてから do_parse/yyparse を呼ぶと、デバッグ用メッセージが出力 されます。パーサが動作する様子が直接見えますので、完全に現在の状態を 把握できます。これを見てどこがおかしいのかわかったらあとは直すだけ。 == next_token に関して いまだ自分でも忘れることが多いのが 「送るトークンが尽きたら [false,なにか] を送る」ということです。 ちなみに Racc 0.10.2 以降では一度 [false,なにか] を受け取ったら それ以上 next_token は呼ばないことが保証されています。 追記: 最近は [false,なにか] ではなく nil でもよいことになった。 racc-1.6.2/doc/ja/grammar.ja.rdoc000066400000000000000000000342661435117461300164770ustar00rootroot00000000000000= 規則ファイル文法リファレンス == 文法に関する前バージョンとの非互換 * (1.2.5) ユーザーコードを連結する時、外部ファイルよりも 埋めこんであるコードを先に連結します。 * (1.1.6) 新しいディレクティブ options が追加されました。 * (1.1.5) 予約語 token の意味が変更になりました。 * (0.14) ルールの最後のセミコロンが省略可能になりました。 また、token prechigh などが予約語でなくなりました。 * (10.2) prepare が header に driver が footer になりました。 今はそのままでも使えますが、2.0 からは対応しません。 * (0.10) class に対応する end がなくなりました。 * (0.9) ダサダサのピリオド方式をやめて { と } で囲むようにしました。 == 全体の構造 トップレベルは、規則部とユーザーコード部に分けられます。 ユーザーコード部はクラス定義の後に来なければいけません。 === コメント 文法ファイルには、一部例外を除いて、ほとんどどこにでもコメントを 書くことができます。コメントは、Rubyの #.....(行末) スタイルと、 Cの /*......*/ スタイルを使うことができます。 === 規則部 規則部は以下のような形をしています。 -- class クラス名 [< スーパークラス] [演算子順位] [トークン宣言] [オプション] [expect] [トークンシンボル値おきかえ] [スタート規則] rule 文法記述 -- "クラス名"はここで定義するパーサクラスの名前です。 これはそのままRubyのクラス名になります。 また M::C のように「::」を使った名前を使うと、クラス定義を モジュール M の中にネストさせます。つまり class M::C ならば -- module M class C < Racc::Parser いろいろ end end -- のように出力します。 さらに、Ruby と同じ構文でスーパークラスを指定できます。 ただしこの指定をするとパーサの動作に重大な影響を与えるので、 特に必要がない限り指定してはいけません。これは将来の拡張の ために用意したもので、現在指定する必然性はあまりありません。 === 文法の記述 racc で生成するパーサが理解できる文法を記述します。 文法は、予約語 rule と end の間に、以下のような書式で書きます。 -- トークン: トークンの並び アクション トークン: トークンの並び アクション | トークンの並び アクション | トークンの並び アクション (必要なだけ同じようにつづける) -- アクションは { } で囲みます。アクションでは Ruby の文はほとんど 使えますが、一部だけは非対応です。対応していないものは以下のとおり。 * ヒアドキュメント * =begin ... =end 型コメント * スペースで始まる正規表現 * ごくまれに % の演算。普通に演算子のまわりにスペースを入れていれば問題なし このあたりに関しては完全な対応はまず無理です。あきらめてください。 左辺の値($$)は、オプションによって返し方がかわります。まずデフォルトでは ローカル変数 result (そのデフォルト値は val[0])が 左辺値を表し、アクション ブロックを抜けた時の result の値が左辺値になります。または明示的に return で返した場合もこの値になります。一方、options で no_result_var を指定した 場合、左辺値はアクションブロックの最後の文の値になります (Ruby のメソッドと 同じ)。 どちらの場合でもアクションは省略でき、省略した場合の左辺値は常に val[0] です。 以下に文法記述の全体の例をしめします。 -- rule goal: def ruls source { result = val } def : /* none */ { result = [] } | def startdesig { result[0] = val[1] } | def precrule # これは上の行の続き { result[1] = val[1] } (略) -- アクション内では特別な意味をもった変数がいくつか使えます。 そのような変数を以下に示します。括弧の中は yacc での表記です。 * result ($$) 左辺の値。初期値は val[0] です。 * val ($1,$2,$3…) 右辺の記号の値の配列。Ruby の配列なので当然インデックスはゼロから始まります。 この配列は毎回作られるので自由に変更したり捨てたりして構いません。 * _values (...,$-2,$-1,$0) 値スタック。Racc コアが使っているオブジェクトがそのまま渡されます。 この変数の意味がわかる人以外は絶対に変更してはいけません。 またアクションの特別な形式に、埋めこみアクションというものがあります。 これはトークン列の途中の好きなところに記述することができます。 以下に埋めこみアクションの例を示します。 -- target: A B { puts 'test test' } C D { normal action } -- このように記述すると A B を検出した時点で puts が実行されます。 また、埋めこみアクションはそれ自体が値を持ちます。つまり、以下の例において -- target: A { result = 1 } B { p val[1] } -- 最後にある p val[1] は埋めこみアクションの値 1 を表示します。 B の値ではありません。 意味的には、埋めこみアクションは空の規則を持つ非終端記号を追加することと 全く同じ働きをします。つまり、上の例は次のコードと完全に同じ意味です。 -- target : A nonterm B { p val[1] } nonterm : /* 空の規則 */ { result = 1 } -- === 演算子優先順位 あるトークン上でシフト・還元衝突がおこったとき、そのトークンに 演算子優先順位が設定してあると衝突を解消できる場合があります。 そのようなものとして特に有名なのは数式の演算子と if...else 構文です。 優先順位で解決できる文法は、うまく文法をくみかえてやれば 優先順位なしでも同じ効果を得ることができます。しかしたいていの 場合は優先順位を設定して解決するほうが文法を簡単にできます。 シフト・還元衝突がおこったとき、Racc はまずその規則に順位が設定 されているか調べます。規則の順位は、その規則で一番うしろにある 終端トークンの優先順位です。たとえば -- target: TERM_A nonterm_a TERM_B nonterm_b -- のような規則の順位はTERM_Bの優先順位になります。もしTERM_Bに 優先順位が設定されていなかったら、優先順位で衝突を解決することは できないと判断し、「Shift/Reduce conflict」を報告します。 演算子の優先順位はつぎのように書いて定義します。 -- prechigh nonassoc PLUSPLUS left MULTI DEVIDE left PLUS MINUS right '=' preclow -- prechigh に近い行にあるほど優先順位の高いトークンです。上下をまるごと さかさまにして preclow...prechigh の順番に書くこともできます。left などは必ず行の最初になければいけません。 left right nonassoc はそれぞれ「結合性」を表します。結合性によって、 同じ順位の演算子の規則が衝突した場合にシフト還元のどちらをとるかが 決まります。たとえば -- a - b - c -- が -- (a - b) - c -- になるのが左結合 (left) です。四則演算は普通これです。 一方 -- a - (b - c) -- になるのが右結合 (right) です。代入のクオートは普通 right です。 またこのように演算子が重なるのはエラーである場合、非結合 (nonassoc) です。 C 言語の ++ や単項のマイナスなどがこれにあたります。 ところで、説明したとおり通常は還元する規則の最後のトークンが順位を 決めるのですが、ある規則に限ってそのトークンとは違う順位にしたいことも あります。例えば符号反転のマイナスは引き算のマイナスより順位を高く しないといけません。このような場合 yacc では %prec を使います。 racc ではイコール記号を使って同じことをできます。 -- prechigh nonassoc UMINUS left '*' '/' left '+' '-' preclow (略) exp: exp '*' exp | exp '-' exp | '-' exp = UMINUS # ここだけ順位を上げる -- このように記述すると、'-' exp の規則の順位が UMINUS の順位になります。 こうすることで符号反転の '-' は '*' よりも順位が高くなるので、 意図どおりになります。 === トークン宣言 トークン(終端記号)のつづりを間違えるというのはよくあることですが、 発見するのはなかなか難しいものです。1.1.5 からはトークンを明示的に 宣言することで、宣言にないトークン / 宣言にだけあるトークンに対して 警告が出るようになりました。yacc の %token と似ていますが最大の違いは racc では必須ではなく、しかもエラーにならず警告だけ、という点です。 トークン宣言は以下のように書きます。 -- token A B C D E F G H -- トークンのリストを複数行にわたって書けることに注目してください。 racc では一般に「予約語」は行の先頭に来た時だけ予約語とみなされるので prechigh などもシンボルとして使えます。ただし深淵な理由から end だけは どうやっても予約語になってしまいます。 === オプション racc のコマンドラインオプションの一部をファイル中にデフォルト値 として記述することができます。 -- options オプション オプション … -- 現在ここで使えるのは * omit_action_call 空のアクション呼び出しを省略する * result_var 変数 result を使う です。 それぞれ no_ を頭につけることで意味を反転できます。 === expect 実用になるパーサはたいてい無害な shift/reduce conflict を含みます。 しかし文法ファイルを書いた本人はそれを知っているからいいですが、 ユーザが文法ファイルを処理した時に「conflict」と表示されたら 不安に思うでしょう。そのような場合、以下のように書いておくと shift/reduce conflict のメッセージを抑制できます。 -- expect 3 -- この場合 shift/reduce conflict はぴったり三つでなければいけません。 三つでない場合はやはり表示が出ます (ゼロでも出ます)。 また reduce/reduce conflict の表示は抑制できません。 === トークンシンボル値の変更 トークンシンボルを表す値は、デフォルトでは * 文法中、引用符でかこまれていないもの (RULEとかXENDとか) →その名前の文字列を intern して得られるシンボル (1.4 では Fixnum) * 引用符でかこまれているもの(':'とか'.'とか) →その文字列そのまま となっていますが、たとえば他の形式のスキャナがすでに存在する場合などは、 これにあわせなければならず、このままでは不便です。このような場合には、 convert 節を加えることで、トークンシンボルを表す値を変えることができます。 以下がその例です。 -- convert PLUS 'PlusClass' #→ PlusClass MIN 'MinusClass' #→ MinusClass end -- デフォルトではトークンシンボル PLUS に対してはトークンシンボル値は :PLUS ですが、上のような記述がある場合は PlusClass になります。 変換後の値は false・nil 以外ならなんでも使えます。 変換後の値として文字列を使うときは、次のように引用符を重ねる必要があります。 -- convert PLUS '"plus"' #→ "plus" end -- また、「'」を使っても生成された Ruby のコード上では「"」になるので 注意してください。バックスラッシュによるクオートは有効ですが、バック スラッシュは消えずにそのまま残ります。 -- PLUS '"plus\n"' #→ "plus\n" MIN "\"minus#{val}\"" #→ \"minus#{val}\" -- === スタート規則 パーサをつくるためには、どの規則が「最初の」規則か、ということを Racc におしえて やらなければいけません。それを明示的に書くのがスタート規則です。スタート規則は 次のように書きます。 -- start real_target -- start は行の最初にこなければいけません。このように書くと、ファイルで 一番最初に出てくる real_target の規則をスタート規則として使います。 省略した場合は、ファイルの最初の規則がスタート規則になります。普通は 最初の規則を一番上にかくほうが書きやすく、わかりやすくなりますから、 この記法はあまりつかう必要はないでしょう。 === ユーザーコード部 ユーザーコードは、パーサクラスが書きこまれるファイルに、 アクションの他にもコードを含めたい時に使います。このようなものは 書きこまれる場所に応じて三つ存在し、パーサクラスの定義の前が header、クラスの定義中(の冒頭)が inner、定義の後が footer です。 ユーザコードとして書いたものは全く手を加えずにそのまま連結されます。 ユーザーコード部の書式は以下の通りです。 -- ---- 識別子 ruby の文 ruby の文 ruby の文 ---- 識別子 ruby の文 : -- 行の先頭から四つ以上連続した「-」(マイナス)があるとユーザーコードと みなされます。識別子は一つの単語で、そのあとには「=」以外なら何を 書いてもかまいません。 racc-1.6.2/doc/ja/index.ja.html000066400000000000000000000006711435117461300161660ustar00rootroot00000000000000

Racc ユーザマニュアル

バージョン 1.4 対応

racc-1.6.2/doc/ja/parser.ja.rdoc000066400000000000000000000116071435117461300163370ustar00rootroot00000000000000= class Racc::Parser Racc の生成するパーサはすべて Racc::Parser クラスを継承します。 Racc::Parser クラスにはパース中に使用するメソッドがいくつかあり、 そのようなメソッドをオーバーロードすると、パーサを初期化したり することができます。 == Super Class Object == Constants プリフィクス "Racc_" がついた定数はパーサの予約定数です。 そのような定数は使わないでください。動作不可能になります。 == Instance Methods ここに載っているもののほか、プリフィクス "racc_" および "_racc_" が ついたメソッドはパーサの予約名です。そのようなメソッドは使わないで ください。 : do_parse -> Object パースを開始します。 また、トークンが必要になった時は #next_token を呼び出します。 -- # Example ---- inner def parse @q = [[1,1], [2,2], [3,3], [false, '$']] do_parse end def next_token @q.shift end -- : next_token -> [Symbol, Object] [abstract method] パーサが次のトークンを読みこむ時に使います。 [記号, その値] の形式の配列を返してください。 記号はデフォルトでは * 文法中、引用符でかこまれていないもの → その名前の文字列のシンボル (例えば :ATOM ) * 引用符でかこまれているもの
→ その文字列そのまま (例えば '=' ) で表します。これを変更する方法については、 文法リファレンスを参照してください。 また、もう送るシンボルがなくなったときには [false, なにか] または nil を返してください。 このメソッドは抽象メソッドなので、#do_parse を使う場合は 必ずパーサクラス中で再定義する必要があります。 定義しないままパースを始めると例外 NotImplementedError が 発生します。 : yyparse( receiver, method_id ) パースを開始します。このメソッドでは始めてトークンが 必要になった時点で receiver に対して method_id メソッドを 呼び出してトークンを得ます。 receiver の method_id メソッドはトークンを yield しなければ なりません。形式は #next_token と同じで [記号, 値] です。 つまり、receiver の method_id メソッドの概形は以下のように なるはずです。 -- def method_id until end_of_file : yield 記号, 値 : end end -- 少し注意が必要なのは、method_id が呼び出されるのは始めて トークンが必要になった時点であるということです。method_id メソッドが呼び出されたときは既にパースが進行中なので、 アクション中で使う変数を method_id の冒頭で初期化すると まず失敗します。 トークンの終端を示す [false, なにか] を渡したらそれ以上は yield しないでください。その場合には例外が発生します。 最後に、method_id メソッドからは必ず yield してください。 しない場合は何が起きるかわかりません。 : on_error( error_token_id, error_value, value_stack ) パーサコアが文法エラーを検出すると呼び出します (yacc の yyerror)。 エラーメッセージを出すなり、例外を発生するなりしてください。 このメソッドから正常に戻った場合、パーサはエラー回復モード に移行します。 error_token はパースエラーを起こした記号の内部表現 (整数) です。 #token_to_str で文法ファイル上の文字列表現に直せます。 error_value はその値です。 value_stack はエラーの時点での値スタックです。 value_stack を変更してはいけません。 on_error のデフォルトの実装は例外 ParseError を発生します。 : token_to_str( t ) -> String Racc トークンの内部表現 (整数) を文法ファイル上の記号表現の文字列に変換します。 t が整数でない場合は TypeError を発生します。 t が範囲外の整数だった場合は nil を返します。 : yyerror エラー回復モードに入ります。このとき #on_error は呼ばれません。 アクション以外からは呼び出さないでください。 : yyerrok エラー回復モードから復帰します。 アクション以外からは呼び出さないでください。 : yyaccept すぐに値スタックの先頭の値を返して #do_parse、#yyparse を抜けます。 racc-1.6.2/doc/ja/racc.ja.rhtml000066400000000000000000000027541435117461300161550ustar00rootroot00000000000000% require 'makefile' % version = Makefile.get_parameter('Makefile', 'version')

Racc

$Id$

ǿ<%= version %>
parser generator
ruby script, ruby extention
ɬ״Ķruby (>=1.6)
۾LGPL

Ruby Ѥ LALR(1) ѡͥ졼Ǥ ѡϤʤ˹®ưޤ

Racc ѡư˥󥿥⥸塼뤬ɬפǤ Ruby 1.8 ˤϤΥ󥿥बǽ餫źդƤΤ ͤʤפǤRuby 1.6 оݤˤȤ racc -E ǥѡɬפޤ

ʤRacc 1.4.x Υ󥿥 Ruby 1.8 źդ Racc 󥿥ϡ ɾǤ̯˰㤤ޤ˸ߴޤ

⤦ŪʬϸϤƤϤǤ TODO ϤޤĤޤ¾ˤäƤΤ ʬ礭ѹĤϤޤ

CVS ݥȥ

CVS Ȥ Racc οκǿǤǤޤ ޥɥ饤ǼΤ褦ǤäƤ

$ cvs -d :pserver:anonymous@cvs.loveruby.net:/src login
Password: (EnterǤ)
$ cvs -d :pserver:anonymous@cvs.loveruby.net:/src co racc
racc-1.6.2/doc/ja/usage.ja.html000066400000000000000000000426501435117461300161660ustar00rootroot00000000000000

Racc の使い方

Racc は文法規則から Ruby で書かれたパーサを生成するパーサジェネレータです。 パーサ生成アルゴリズムには yacc などと同じ LALR(1) を使用しています。

yacc を知っている人は記述法の違いだけわかれば使えると思います。 yacc を知らない人は 拙著『Ruby を 256 倍使うための本 無道編』(青木峰郎著、ASCII) などを一読していただくのがよいかと思います。 他の UNIX コマンドなどとは異なり、 いきなり使うだけで Racc を理解するのはかなり困難です。

Racc とはなにか

Racc は文法を処理するツールです。 文字列はただの文字の列で、コンピュータにとっては意味を持ちません。 しかし人間はその文字の列の中になにか意味を見出すことができます。 コンピュータにもそのようなことを、部分的にでも、させられたら便利でしょう。 Racc はその手伝いをしてくれます。完全な自動化ではありませんが、 人間が全部やるよりも遥かに簡単になります。

Racc が自動化してくれる部分とは、文字列の含む「構造」の処理です。 たとえば Ruby の if 文を考えてみると、次のように定式化できます。

if 条件式 [then]
  文
  :
[elsif 条件式 [then]
  文
  :]
[else
  文
  :]
end

if 文では if という単語が最初になくてはならず、 elsif 節は else 節より前になくてはいけません。 このような配置の関係 (構造) が、Racc が処理する対象です。

一方、Racc で処理できないのはどういうことでしょうか。それは、たとえば if の条件式にあたる部分が「なんであるか」ということです。つまり、条件 式が if の条件だということです。これは、こっちで条件として扱うコードを 書いてやらないといけません。

と言っても、わかりにくいでしょう。こういう抽象的なものは実際にいじって みるのが一番です。

実際の話

実際に Racc をどのように使うかという話をします。Racc には独自のソース コードみたいなものがあって、この中に処理したい「構造」を記述しておきま す。このソースファイルを「文法ファイル」と呼ぶことにしましょう。この文 法ファイルの名前が parse.y と仮定すると、コマンドラインから以下のよう に打ちこめば、その構造を処理するためのクラスを含んだファイルが得られま す。

$ racc parse.y

生成されるファイルはデフォルトでは "ファイル名.tab.rb" です。他の名前 にしたいなら、-o オプションで変更できます。

$ racc parse.y -o myparser.rb

このようにして作ったクラス、またはそのような処理を担当するパート、 のことはパーサ (parser) と呼ぶことになっています。解析するヤツ、 というくらいに適当にとらえてください。

文法ファイルを書く

Racc は文法ファイルから Ruby のクラスを生成するツールだと言いました。 そのクラスは全て Racc::Parser の下位クラスで、名前は文法ファイル中で 指定します。以下、ここに書くべきことが「なんなのか」を説明します。 ここでは内容に重点を置くので、文法ファイル自体の文法の詳細は 文法リファレンスを見てください。

文法

まずは、全体の概形です。

class MyParser
rule

  if_stmt: IF expr then stmt_list elsif else END

  then   : THEN
         |

  elsif  :
         | ELSIF stmt_list

  else   :
         | ELSE stmt_list

  expr   : NUMBER
         | IDENT
         | STRING

  stmt_list : ふにゃふにゃ

end

Ruby スクリプトのように class でパーサクラス名を指定し、rule ... end の間にパーサに解析させたい文法を記述します。

文法は、記号の並びでもって表します。rule ... end の間にあるコロンとバー 以外のもの、if_stmt IF expr then などが全て「記号」です。そしてコロン が日本語で言う「〜は××だ」の「は」みたいなもんで、その左の記号が右の 記号の列と同じものを指す、というふうに定義します。また、バーは「または」 を意味します。それと、単純にコロンの左の記号のことを左辺、右を右辺とも 言います。以下はこちらのほうを使って説明しましょう。

少し注意が必要な点を述べます。まず、then の、バーのあとの定義 (規則) を 見てください。ここには何も書いていないので、これはその通り「無」であっ てもいい、ということを表しています。つまり、then は記号 THEN 一個か、 またはなにもなし(省略する)でよい、ということです。記号 then は実際の Ruby のソースコードにある then とは切り離して考えましょう (それは実は大文字の記号 THEN が表しています)。

さて、そろそろ「記号」というものがなんなのか書きましょう。 ただし順番に話をしないといけないので、まずは聞いていてください。 この文章の最初に、パーサとは文字の列から構造を見出す部分だと言いました。 しかし文字の列からいきなり構造を探すのは面倒なので、実際にはまず 文字の列を単語の列に分割します。その時点でスペースやコメントは捨てて しまい、以降は純粋にプログラムの一部をなす部分だけを相手にします。 たとえば文字列の入力が次のようだったとすると、

if flag then   # item found.
  puts 'ok'
end

単語の列は次のようになります。

if flag then puts 'ok' end

ここで、工夫が必要です。どうやら flag はローカル変数名だと思われますが、 変数名というのは他にもいろいろあります。しかし名前が i だろうが a だろ うが vvvvvvvvvvvv だろうが、「構造」は同じです。つまり同じ扱いをされる べきです。変数 a を書ける場所なら b も書けなくてはいけません。だったら 一時的に同じ名前で読んでもいいじゃん。ということで、この単語の列を以下 のように読みかえましょう。

IF IDENT THEN IDENT STRING END

これが「記号」の列です。パーサではこの記号列のほうを扱い、構造を見付け ていきます。

さらに記号について見ていきましょう。 記号は二種類に分けられます。「左辺にある記号」と「ない記号」です。 左辺にある記号は「非終端」記号と言います。ないほうは「終端」記号と 言います。最初の例では終端記号はすべて大文字、非終端記号は小文字で 書いてあるので、もう一度戻って例の文法を見てください。

なぜこの区分が重要かと言うと、入力の記号列はすべて終端記号だからです。 一方、非終端記号はパーサの中でだけ、終端記号の列から「作りだす」ことに よって始めて存在します。例えば次の規則をもう一度見てください。

  expr   : NUMBER
         | IDENT
         | STRING

expr は NUMBER か IDENT か STRING だと言っています。逆に言うと、 IDENT は expr に「なることができます」。文法上 expr が存在できる 場所に IDENT が来ると、それは expr になります。例えば if の条件式の 部分は expr ですから、ここに IDENT があると expr になります。その ように文法的に「大きい」記号を作っていって、最終的に一個になると、 その入力は文法を満たしていることになります。実際にさっきの入力で 試してみましょう。入力はこうでした。

IF IDENT THEN IDENT STRING END

まず、IDENT が expr になります。

IF expr THEN IDENT STRING END

次に THEN が then になります。

IF expr then IDENT STRING END

IDENT STRING がメソッドコールになります。この定義はさきほどの例には ないですが、実は省略されているんだと考えてください。そしていろいろな 過程を経て、最終的には stmt_list (文のリスト)になります。

IF expr then stmt_list END

elsif と else は省略できる、つまり無から生成できます。

IF expr then stmt_list elsif else END

最後に if_stmt を作ります。

if_stmt

ということでひとつになりました。 つまりこの入力は文法的に正しいということがわかりました。

アクション

ここまでで入力の文法が正しいかどうかを確認する方法はわかりましたが、 これだけではなんにもなりません。最初に説明したように、ここまででは 構造が見えただけで、プログラムは「意味」を理解できません。そしてその 部分は Racc では自動処理できないので、人間が書く、とも言いました。 それを書くのが以下に説明する「アクション」という部分です。

前項で、記号の列がだんだんと大きな単位にまとめられていく過程を見ました。 そのまとめる時に、同時になにかをやらせることができます。それが アクションです。アクションは、文法ファイルで以下のように書きます。

class MyParser
rule

  if_stmt: IF expr then stmt_list elsif else END
             { puts 'if_stmt found' }

  then   : THEN
             { puts 'then found' }
         |
             { puts 'then is omitted' }

  elsif  :
             { puts 'elsif is omitted' }
         | ELSIF stmt_list
             { puts 'elsif found' }

  else   :
             { puts 'else omitted' }
         | ELSE stmt_list
             { puts 'else found' }

  expr   : NUMBER
             { puts 'expr found (NUMBER)' }
         | IDENT
             { puts 'expr found (IDENT)' }
         | STRING
             { puts 'expr found (STRING)' }

  stmt_list : ふにゃふにゃ

end

見てのとおり、規則のあとに { と } で囲んで書きます。 アクションにはだいたい好きなように Ruby スクリプトが書けます。

(この節、未完)


yacc での $$ は Racc ではローカル変数 result で、$1,$2... は配列 valです。 resultval[0] ($1) の値に初期化され、 アクションを抜けたときの result の値が左辺値になります。 Racc ではアクション中の return はアクションから抜けるだけで、 パース自体は終わりません。アクション中からパースを終了するには、 メソッド yyaccept を使ってください。

演算子の優先順位、スタートルールなどの yacc の一般的な機能も用意されて います。ただしこちらも少し文法が違います。

yacc では生成されたコードに直接転写されるコードがありました。 Racc でも同じように、ユーザ指定のコードが書けます。 Racc ではクラスを生成するので、クラス定義の前/中/後の三個所があります。 Racc ではそれを上から順番に header inner footer と呼んでいます。

ユーザが用意すべきコード

パースのエントリポイントとなるメソッドは二つあります。ひとつは do_parseで、こちらはトークンを Parser#next_token から得ます。もうひとつは yyparse で、こちらはスキャナから yield され ることによってトークンを得ます。ユーザ側ではこのどちらか(両方でもいい けど)を起動する簡単なメソッドを inner に書いてください。これらメソッド の引数など、詳しいことはリファレンスを見てください。

どちらのメソッドにも共通なのはトークンの形式です。必ずトークンシンボル とその値の二要素を持つ配列を返すようにします。またスキャンが終了して、 もう送るものがない場合は [false,なにか] を返し てください。これは一回返せば十分です (逆に、yyparse を使 う場合は二回以上 yield してはいけない)。

パーサは別に文字列処理にだけ使われるものではありませんが、実際問題とし て、パーサを作る場面ではたいてい文字列のスキャナとセットで使うことが多 いでしょう。Ruby ならスキャナくらい楽勝で作れますが、高速なスキャナと なると実は難しかったりします。そこで高速なスキャナを作成するためのライ ブラリも作っています。詳しくは 「スキャナを作る」の項を見てください。

Racc には error トークンを使ったエラー回復機能もあります。yacc の yyerror() は Racc では Racc::Parser#on_error で、エラーが起きたトークンとその値、値スタック、の三つの引数をとります。 on_error のデフォルトの実装は例外 Racc::ParseError を発生します。

ユーザがアクション中でパースエラーを発見した場合は、メソッド yyerror を呼べばパーサがエラー回復モードに入ります。 ただしこのとき on_errorは呼ばれません。

パーサを生成する

これだけあればだいたい書けると思います。あとは、最初に示した方法で文法 ファイルを処理し、Ruby スクリプトを得ます。

うまくいけばいいのですが、大きいものだと最初からはうまくいかないでしょ う。racc に -g オプションをつけてコンパイルし、@yydebug を true にする とデバッグ用の出力が得られます。デバッグ出力はパーサの @racc_debug_out に出力されます(デフォルトは stderr)。また、racc に -v オプションをつけ ると、状態遷移表を読みやすい形で出力したファイル(*.output)が得られます。 どちらもデバッグの参考になるでしょう。

作ったパーサを配布する

Racc の生成したパーサは動作時にランタイムルーチンが必要です。 具体的には parser.rb と cparse.so です。 ただし cparse.so は単にパースを高速化するためのライブラリなので 必須ではありません。なくても動きます。

まず Ruby 1.8.0 以降にはこのランタイムが標準添付されているので、 Ruby 1.8 がある環境ならばランタイムについて考慮する必要はありません。 Racc 1.4.x のランタイムと Ruby 1.8 に添付されているランタイムは 完全互換です。

問題は Ruby 1.8 を仮定できない場合です。 Racc をユーザみんなにインストールしてもらうのも一つの手ですが、 これでは不親切です。そこでRacc では回避策を用意しました。

racc に -E オプションをつけてコンパイルすると、 パーサと racc/parser.rb を合体したファイルを出力できます。 これならばファイルは一つだけなので簡単に扱えます。 racc/parser.rb は擬似的に require したような扱いになるので、 この形式のパーサが複数あったとしてもクラスやメソッドが衝突することもありません。 ただし -E を使った場合は cparse.so が使えませんので、 必然的にパーサの速度は落ちます。

おまけ: スキャナを書く

パーサを使うときは、たいてい文字列をトークンに切りわけてくれるスキャナ が必要になります。しかし実は Ruby は文字列の最初からトークンに切りわけ ていくという作業があまり得意ではありません。 正確に言うと、簡単にできるのですが、それなりのオーバーヘッドがかかります。

そのオーバーヘッドを回避しつつ、 手軽にスキャナを作れるように strscan というパッケージを作りました。 Ruby 1.8 以降には標準添付されていますし、 筆者のホームページには 単体パッケージがあります。

racc-1.6.2/ext/000077500000000000000000000000001435117461300132355ustar00rootroot00000000000000racc-1.6.2/ext/racc/000077500000000000000000000000001435117461300141455ustar00rootroot00000000000000racc-1.6.2/ext/racc/MANIFEST000066400000000000000000000000441435117461300152740ustar00rootroot00000000000000MANIFEST cparse.c depend extconf.rb racc-1.6.2/ext/racc/com/000077500000000000000000000000001435117461300147235ustar00rootroot00000000000000racc-1.6.2/ext/racc/com/headius/000077500000000000000000000000001435117461300163455ustar00rootroot00000000000000racc-1.6.2/ext/racc/com/headius/racc/000077500000000000000000000000001435117461300172555ustar00rootroot00000000000000racc-1.6.2/ext/racc/com/headius/racc/Cparse.java000066400000000000000000001101641435117461300213400ustar00rootroot00000000000000/* Cparse.java -- Racc Runtime Core for JRuby Copyright (c) 2016 Charles Oliver Nutter Ported from and distributed under the same licence as cparse.c cparse.c -- Racc Runtime Core Copyright (c) 1999-2006 Minero Aoki This library is free software. You can distribute/modify this program under the same terms of ruby. */ package com.headius.racc; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyBasicObject; import org.jruby.RubyClass; import org.jruby.RubyFixnum; import org.jruby.RubyHash; import org.jruby.RubyKernel; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyObject; import org.jruby.RubySymbol; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.JumpException; import org.jruby.internal.runtime.methods.AttrReaderMethod; import org.jruby.internal.runtime.methods.AttrWriterMethod; import org.jruby.internal.runtime.methods.CallConfiguration; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.BlockCallback; import org.jruby.runtime.CallBlock19; import org.jruby.runtime.CallSite; import org.jruby.runtime.Helpers; import org.jruby.runtime.MethodIndex; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.Signature; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.load.Library; public class Cparse implements Library { public static final String RACC_VERSION = "1.6.2"; // TODO: parse from Cparse.c public enum TokenType { DEFAULT(-1), FINAL(0), ERROR(1); private final int id; TokenType(int id) { this.id = id; } } private RubyFixnum vDEFAULT_TOKEN; private RubyFixnum vERROR_TOKEN; private RubyFixnum vFINAL_TOKEN; private RubyClass RaccBug; private RubyClass CparseParams; private static final String ID_YYDEBUG = "@yydebug"; private static final String ID_NEXTTOKEN = "next_token"; private static final String ID_ONERROR = "on_error"; private static final String ID_NOREDUCE = "_reduce_none"; private static final String ID_ERRSTATUS = "@racc_error_status"; private static final String ID_D_SHIFT = "racc_shift"; private static final String ID_D_REDUCE = "racc_reduce"; private static final String ID_D_ACCEPT = "racc_accept"; private static final String ID_D_READ_TOKEN = "racc_read_token"; private static final String ID_D_NEXT_STATE = "racc_next_state"; private static final String ID_D_E_POP = "racc_e_pop"; private RubySymbol sym_noreduce; private CallSite call_nexttoken; private CallSite call_onerror; private CallSite call_d_shift; private CallSite call_d_reduce; private CallSite call_d_accept; private CallSite call_d_read_token; private CallSite call_d_next_state; private CallSite call_d_e_pop; private static RubySymbol value_to_id(ThreadContext context, IRubyObject v) { if (!(v instanceof RubySymbol)) { throw context.runtime.newTypeError("not symbol"); } return (RubySymbol)v; } private static int num_to_int(IRubyObject n) { return assert_integer(n); } private static IRubyObject AREF(ThreadContext context, IRubyObject s, int idx) { return ((0 <= idx && idx < ((RubyArray)s).size()) ? ((RubyArray)s).entry(idx) : context.nil); } private static IRubyObject get_stack_tail(ThreadContext context, RubyArray stack, int len) { if (len < 0) return context.nil; int size = stack.size(); len = Math.min(len, size); return stack.subseq(size - len, len); } private static void cut_stack_tail(ThreadContext context, RubyArray stack, int len) { while (len > 0) { stack.pop(context); len--; } } private static final int STACK_INIT_LEN = 64; private static RubyArray NEW_STACK(ThreadContext context) { return context.runtime.newArray(STACK_INIT_LEN); } private static IRubyObject PUSH(RubyArray stack, IRubyObject i) { return stack.append(i); } private static IRubyObject POP(ThreadContext context, RubyArray stack) { return stack.pop(context); } private static IRubyObject LAST_I(ThreadContext context, RubyArray stack) { return stack.size() > 0 ? stack.last() : context.nil; } private static IRubyObject GET_TAIL(ThreadContext context, RubyArray stack, int len) { return get_stack_tail(context, stack, len); } private static void CUT_TAIL(ThreadContext context, RubyArray stack, int len) { cut_stack_tail(context, stack, len); } static final int CP_FIN_ACCEPT = 1; static final int CP_FIN_EOT = 2; static final int CP_FIN_CANTPOP = 3; public class CparseParams extends RubyObject { public CparseParams(Ruby runtime, RubyClass rubyClass) { super(runtime, rubyClass); } public void initialize_params(ThreadContext context, Parser parser, IRubyObject arg, IRubyObject lexer, IRubyObject lexmid) { Ruby runtime = context.runtime; this.parser = parser; this.lexer = lexer; if (!lexmid.isNil()) { this.lexmid = value_to_id(context, lexmid); this.call_lexmid = MethodIndex.getFunctionalCallSite(this.lexmid.toString()); } this.debug = parser.getInstanceVariable(ID_YYDEBUG).isTrue(); RubyArray argAry = arg.convertToArray(); if (!(13 <= argAry.size() && argAry.size() <= 14)) { throw runtime.newRaiseException(RaccBug, "[Racc Bug] wrong arg.size " + argAry.size()); } this.action_table = assert_array(argAry.eltOk(0)); this.action_check = assert_array(argAry.eltOk(1)); this.action_default = assert_array(argAry.eltOk(2)); this.action_pointer = assert_array(argAry.eltOk(3)); this.goto_table = assert_array(argAry.eltOk(4)); this.goto_check = assert_array(argAry.eltOk(5)); this.goto_default = assert_array(argAry.eltOk(6)); this.goto_pointer = assert_array(argAry.eltOk(7)); this.nt_base = assert_integer(argAry.eltOk(8)); this.reduce_table = assert_array(argAry.eltOk(9)); this.token_table = assert_hash(argAry.eltOk(10)); this.shift_n = assert_integer(argAry.eltOk(11)); this.reduce_n = assert_integer(argAry.eltOk(12)); if (argAry.size() > 13) { this.use_result_var = argAry.eltOk(13).isTrue(); } else { this.use_result_var = true; } this.tstack = this.debug ? NEW_STACK(context) : null; this.vstack = NEW_STACK(context); this.state = NEW_STACK(context); this.curstate = 0; PUSH(this.state, RubyFixnum.zero(runtime)); this.t = runtime.newFixnum(TokenType.FINAL.id + 1); // must not init to FINAL_TOKEN this.nerr = 0; this.errstatus = 0; this.parser.setInstanceVariable(ID_ERRSTATUS, runtime.newFixnum(this.errstatus)); this.retval = context.nil; this.fin = 0; this.lex_is_iterator = false; parser.setInstanceVariable("@vstack", this.vstack); if (this.debug) { parser.setInstanceVariable("@tstack", this.tstack); } else { parser.setInstanceVariable("@tstack", context.nil); } } public void extract_user_token(ThreadContext context, IRubyObject block_args, IRubyObject[] tokVal) { if (block_args.isNil()) { /* EOF */ tokVal[0] = context.runtime.getFalse(); tokVal[1] = context.runtime.newString("$"); return; } if (!(block_args instanceof RubyArray)) { throw context.runtime.newTypeError( (lex_is_iterator ? lexmid.asJavaString() : "next_token") + " " + (lex_is_iterator ? "yielded" : "returned") + " " + block_args.getMetaClass().getName() + " (must be Array[2])"); } RubyArray block_args_ary = (RubyArray)block_args; if (block_args_ary.size() != 2) { throw context.runtime.newTypeError( (lex_is_iterator ? lexmid.asJavaString() : "next_token") + " " + (lex_is_iterator ? "yielded" : "returned") + " wrong size of array (" + block_args_ary.size() + " for 2)"); } tokVal[0] = ((RubyArray) block_args).eltOk(0); tokVal[1] = ((RubyArray) block_args).eltOk(1); } private static final int RESUME = 1; private static final int NOTFOUND = 2; private static final int ERROR_RECOVERED = 3; private static final int ERROR = 4; private static final int HANDLE_ACT = 5; private static final int ACT_FIXED = 6; private static final int ACCEPT = 7; private static final int USER_YYERROR = 8; private static final int ERROR_POP = 9; private static final int TRANSIT = 9; private void SHIFT(ThreadContext context, int act, IRubyObject tok, IRubyObject val) { shift(context, act, tok, val); } public void parse_main(ThreadContext context, IRubyObject tok, IRubyObject val, boolean resume) { Ruby runtime = context.runtime; int i = 0; /* table index */ int act = 0; /* action type */ IRubyObject act_value; /* action type, VALUE version */ boolean read_next = true; /* true if we need to read next token */ IRubyObject tmp; int branch = 0; if (resume) { branch = RESUME; } BRANCH: while (true) { switch (branch) { case 0: D_puts(""); D_puts("---- enter new loop ----"); D_puts(""); D_printf("(act) k1=%ld\n", this.curstate); tmp = AREF(context, this.action_pointer, this.curstate); if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;} D_puts("(act) pointer[k1] ok"); i = assert_integer(tmp); D_printf("read_next=%d\n", read_next); if (read_next && (!this.t.equals(vFINAL_TOKEN))) { if (this.lex_is_iterator) { D_puts("resuming..."); if (this.fin != 0) throw runtime.newArgumentError("token given after EOF"); this.i = i; /* save i */ return; // remainder of case duplicated from here for RESUME case //D_puts(this, "resumed"); //i = this.i; /* load i */ } else { D_puts("next_token"); tmp = call_nexttoken.call(context, this.parser, this.parser); IRubyObject[] tokVal = {tok, val}; extract_user_token(context, tmp, tokVal); tok = tokVal[0]; val = tokVal[1]; } /* convert token */ this.t = ((RubyHash)this.token_table).op_aref(context, tok); if (this.t.isNil()) { this.t = vERROR_TOKEN; } D_printf("(act) t(k2)=%ld\n", assert_integer(this.t)); if (this.debug) { call_d_read_token.call(context, this.parser, this.parser, this.t, tok, val); } } // duplicated logic from above for RESUME case case RESUME: if (branch == RESUME) { D_puts("resumed"); i = this.i; /* load i */ /* convert token */ this.t = ((RubyHash)this.token_table).op_aref(context, tok); if (this.t.isNil()) { this.t = vERROR_TOKEN; } D_printf("(act) t(k2)=%ld\n", assert_integer(this.t)); if (this.debug) { call_d_read_token.call(context, this.parser, this.parser, this.t, tok, val); } } read_next = false; i += assert_integer(this.t); D_printf("(act) i=%ld\n", i); if (i < 0) {branch = NOTFOUND; continue BRANCH;} act_value = AREF(context, this.action_table, i); if (act_value.isNil()) {branch = NOTFOUND; continue BRANCH;} act = assert_integer(act_value); D_printf("(act) table[i]=%ld\n", act); tmp = AREF(context, this.action_check, i); if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;} if (assert_integer(tmp) != this.curstate) {branch = NOTFOUND; continue BRANCH;} D_printf("(act) check[i]=%ld\n", assert_integer(tmp)); D_puts("(act) found"); case ACT_FIXED: D_printf("act=%ld\n", act); branch = HANDLE_ACT; continue BRANCH; case NOTFOUND: D_puts("(act) not found: use default"); act_value = AREF(context, this.action_default, this.curstate); act = assert_integer(act_value); branch = ACT_FIXED; continue BRANCH; case HANDLE_ACT: if (act > 0 && act < this.shift_n) { D_puts("shift"); if (this.errstatus > 0) { this.errstatus--; this.parser.setInstanceVariable(ID_ERRSTATUS, runtime.newFixnum(this.errstatus)); } SHIFT(context, act, this.t, val); read_next = true; } else if (act < 0 && act > -(this.reduce_n)) { D_puts("reduce"); { // macro REDUCE switch (reduce(context, act)) { case 0: /* normal */ break; case 1: /* yyerror */ branch = USER_YYERROR; continue BRANCH; case 2: /* yyaccept */ D_puts("u accept"); branch = ACCEPT; continue BRANCH; default: break; } } } else if (act == -(this.reduce_n)) { branch = ERROR; continue BRANCH; } else if (act == this.shift_n) { D_puts("accept"); branch = ACCEPT; continue BRANCH; } else { throw runtime.newRaiseException(RaccBug, "[Cparse Bug] unknown act value " + act); } // fall through case ERROR_RECOVERED: if (this.debug) { call_d_next_state.call(context, this.parser, this.parser, runtime.newFixnum(this.curstate), this.state); } branch = 0; continue BRANCH; /* not reach */ case ACCEPT: if (this.debug) call_d_accept.call(context, this.parser, this.parser); this.retval = this.vstack.eltOk(0); this.fin = CP_FIN_ACCEPT; return; case ERROR: D_printf("error detected, status=%ld\n", this.errstatus); if (this.errstatus == 0) { this.nerr++; call_onerror.call(context, this.parser, this.parser, this.t, val, this.vstack); } // fall through case USER_YYERROR: if (this.errstatus == 3) { if (this.t.equals(vFINAL_TOKEN)) { this.retval = runtime.getFalse(); this.fin = CP_FIN_EOT; return; } read_next = true; } this.errstatus = 3; this.parser.setInstanceVariable(ID_ERRSTATUS, runtime.newFixnum(this.errstatus)); /* check if we can shift/reduce error token */ D_printf("(err) k1=%ld\n", this.curstate); D_printf("(err) k2=%d (error)\n", TokenType.ERROR.id); int branch2 = 0; BRANCH2: while (true) { switch (branch2) { case 0: tmp = AREF(context, this.action_pointer, this.curstate); if (tmp.isNil()) {branch2 = ERROR_POP; continue BRANCH2;} D_puts("(err) pointer[k1] ok"); i = assert_integer(tmp) + TokenType.ERROR.id; D_printf("(err) i=%ld\n", i); if (i < 0) {branch2 = ERROR_POP; continue BRANCH2;} act_value = AREF(context, this.action_table, i); if (act_value.isNil()) { D_puts("(err) table[i] == nil"); branch2 = ERROR_POP; continue BRANCH2; } act = assert_integer(act_value); D_printf("(err) table[i]=%ld\n", act); tmp = AREF(context, this.action_check, i); if (tmp.isNil()) { D_puts("(err) check[i] == nil"); branch2 = ERROR_POP; continue BRANCH2; } if (assert_integer(tmp) != this.curstate) { D_puts("(err) check[i] != k1"); branch2 = ERROR_POP; continue BRANCH2; } D_puts("(err) found: can handle error token"); break BRANCH2; case ERROR_POP: D_puts("(err) act not found: can't handle error token; pop"); if (this.state.size() <= 1) { this.retval = context.nil; this.fin = CP_FIN_CANTPOP; return; } POP(context, this.state); POP(context, this.vstack); this.curstate = assert_integer(LAST_I(context, this.state)); if (this.debug) { POP(context, this.tstack); call_d_e_pop.call(context, this.parser, this.parser, this.state, this.tstack, this.vstack); } } } /* shift/reduce error token */ if (act > 0 && act < this.shift_n) { D_puts("e shift"); SHIFT(context, act, runtime.newFixnum(TokenType.ERROR.id), val); } else if (act < 0 && act > -(this.reduce_n)) { D_puts("e reduce"); { // macro REDUCE switch (reduce(context, act)) { case 0: /* normal */ break; case 1: /* yyerror */ branch = USER_YYERROR; continue BRANCH; case 2: /* yyaccept */ D_puts("u accept"); branch = ACCEPT; continue BRANCH; default: break; } } } else if (act == this.shift_n) { D_puts("e accept"); branch = ACCEPT; continue BRANCH; } else { throw runtime.newRaiseException(RaccBug, "[Cparse Bug] unknown act value " + act); } branch = ERROR_RECOVERED; continue BRANCH; } } } private void shift(ThreadContext context, int act, IRubyObject tok, IRubyObject val) { PUSH(vstack, val); if (debug) { PUSH(tstack, tok); call_d_shift.call(context, this.parser, this.parser, tok, tstack, vstack); } curstate = act; PUSH(state, context.runtime.newFixnum(curstate)); } private int reduce(ThreadContext context, int act) { ruleno = -act * 3; IRubyObject tag = context.runtime.newSymbol("racc_jump"); IRubyObject code = RubyKernel.rbCatch(context, this, tag, CallBlock19.newCallClosure(this, getMetaClass(), Signature.NO_ARGUMENTS, new BlockCallback() { @Override public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) { return reduce0(context); } }, context)); errstatus = assert_integer(parser.getInstanceVariable(ID_ERRSTATUS)); return assert_integer(code); } private IRubyObject reduce0(ThreadContext context) { Ruby runtime = context.runtime; IRubyObject reduce_to, reduce_len, method_id; int len; RubySymbol mid; IRubyObject tmp, tmp_t = RubyBasicObject.UNDEF, tmp_v = RubyBasicObject.UNDEF; int i, k1 = 0, k2; IRubyObject goto_state = context.nil; reduce_len = this.reduce_table.entry(this.ruleno); reduce_to = this.reduce_table.entry(this.ruleno+1); method_id = this.reduce_table.entry(this.ruleno+2); len = assert_integer(reduce_len); mid = value_to_id(context, method_id); int branch = 0; BRANCH: while (true) { switch (branch) { case 0: /* call action */ if (len == 0) { tmp = context.nil; if (!mid.equals(sym_noreduce)) tmp_v = runtime.newArray(); if (this.debug) tmp_t = runtime.newArray(); } else { if (!mid.equals(sym_noreduce)) { tmp_v = GET_TAIL(context, this.vstack, len); tmp = ((RubyArray)tmp_v).entry(0); } else { tmp = this.vstack.entry(this.vstack.size() - len); } CUT_TAIL(context, this.vstack, len); if (this.debug) { tmp_t = GET_TAIL(context, this.tstack, len); CUT_TAIL(context, this.tstack, len); } CUT_TAIL(context, this.state, len); } if (!mid.equals(sym_noreduce)) { if (this.use_result_var) { tmp = Helpers.invoke(context, this.parser, mid.toString(), tmp_v, this.vstack, tmp); } else { tmp = Helpers.invoke(context, this.parser, mid.toString(), tmp_v, this.vstack); } } /* then push result */ PUSH(this.vstack, tmp); if (this.debug) { PUSH(this.tstack, reduce_to); call_d_reduce.call(context, this.parser, this.parser, tmp_t, reduce_to, this.tstack, this.vstack); } /* calculate transition state */ if (state.size() == 0) throw runtime.newRaiseException(RaccBug, "state stack unexpectedly empty"); k2 = assert_integer(LAST_I(context, this.state)); k1 = assert_integer(reduce_to) - this.nt_base; D_printf("(goto) k1=%ld\n", k1); D_printf("(goto) k2=%ld\n", k2); tmp = AREF(context, this.goto_pointer, k1); if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;} i = assert_integer(tmp) + k2; D_printf("(goto) i=%ld\n", i); if (i < 0) {branch = NOTFOUND; continue BRANCH;} goto_state = AREF(context, this.goto_table, i); if (goto_state.isNil()) { D_puts("(goto) table[i] == nil"); branch = NOTFOUND; continue BRANCH; } D_printf("(goto) table[i]=%ld (goto_state)\n", goto_state.convertToInteger().getLongValue()); tmp = AREF(context, this.goto_check, i); if (tmp.isNil()) { D_puts("(goto) check[i] == nil"); branch = NOTFOUND; continue BRANCH; } if (!tmp.equals(runtime.newFixnum(k1))) { D_puts("(goto) check[i] != table[i]"); branch = NOTFOUND; continue BRANCH; } D_printf("(goto) check[i]=%ld\n", tmp.convertToInteger().getLongValue()); D_puts("(goto) found"); case TRANSIT: PUSH(this.state, goto_state); this.curstate = assert_integer(goto_state); return RubyFixnum.zero(runtime); case NOTFOUND: D_puts("(goto) not found: use default"); /* overwrite `goto-state' by default value */ goto_state = AREF(context, this.goto_default, k1); branch = TRANSIT; continue BRANCH; } } } private void D_puts(String msg) { if (sys_debug) { System.out.println(msg); } } private void D_printf(String fmt, long arg) { if (sys_debug) { System.out.println(fmt + ": " + arg); } } private void D_printf(String fmt, boolean arg) { if (sys_debug) { System.out.println(fmt + ": " + arg); } } Parser parser; /* parser object */ boolean lex_is_iterator; IRubyObject lexer; /* scanner object */ RubySymbol lexmid; /* name of scanner method (must be an iterator) */ CallSite call_lexmid; /* call site for scanner method */ /* State transition tables (immutable) Data structure is from Dragon Book 4.9 */ /* action table */ IRubyObject action_table; IRubyObject action_check; IRubyObject action_default; IRubyObject action_pointer; /* goto table */ IRubyObject goto_table; IRubyObject goto_check; IRubyObject goto_default; IRubyObject goto_pointer; int nt_base; /* NonTerminal BASE index */ RubyArray reduce_table; /* reduce data table */ IRubyObject token_table; /* token conversion table */ /* parser stacks and parameters */ RubyArray state; int curstate; RubyArray vstack; RubyArray tstack; IRubyObject t; int shift_n; int reduce_n; int ruleno; int errstatus; /* nonzero in error recovering mode */ int nerr; /* number of error */ boolean use_result_var; IRubyObject retval; /* return IRubyObject of parser routine */ int fin; /* parse result status */ boolean debug; /* user level debug */ boolean sys_debug; /* system level debug */ int i; /* table index */ } private static RubyArray assert_array(IRubyObject a) { return a.convertToArray(); } private static RubyHash assert_hash(IRubyObject h) { return h.convertToHash(); } private static int assert_integer(IRubyObject i) { return (int)i.convertToInteger().getLongValue(); } public class Parser extends RubyObject { public Parser(Ruby runtime, RubyClass rubyClass) { super(runtime, rubyClass); } public static final String Racc_Runtime_Core_Version_C = RACC_VERSION; @JRubyMethod(name = "_racc_do_parse_c", frame = true) public IRubyObject racc_cparse(ThreadContext context, IRubyObject arg, IRubyObject sysdebug) { CparseParams v = new CparseParams(context.runtime, CparseParams); v.D_puts("starting cparse"); v.sys_debug = sysdebug.isTrue(); v.initialize_params(context, this, arg, context.nil, context.nil); v.lex_is_iterator = false; v.parse_main(context, context.nil, context.nil, false); return v.retval; } @JRubyMethod(name = "_racc_yyparse_c", frame = true, required = 4) public IRubyObject racc_yyparse(ThreadContext context, IRubyObject[] args) { Ruby runtime = context.runtime; CparseParams v = new CparseParams(context.runtime, CparseParams); IRubyObject lexer = args[0], lexmid = args[1], arg = args[2], sysdebug = args[3]; v.sys_debug = sysdebug.isTrue(); v.D_puts("start C yyparse"); v.initialize_params(context, this, arg, lexer, lexmid); v.lex_is_iterator = true; v.D_puts("params initialized"); v.parse_main(context, context.nil, context.nil, false); call_lexer(context, v); if (v.fin == 0) { throw runtime.newArgumentError(v.lexmid + " is finished before EndOfToken"); } return v.retval; } private class LexerUnroll extends RuntimeException { public Throwable fillInStackTrace() { return this; } } private void call_lexer(ThreadContext context, final CparseParams v) { final LexerUnroll lexerUnroll = new LexerUnroll(); try { v.call_lexmid.call(context, v.lexer, v.lexer, CallBlock19.newCallClosure(v, v.getMetaClass(), Signature.ONE_ARGUMENT, new BlockCallback() { @Override public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) { Ruby runtime = context.getRuntime(); if (v.fin != 0) { throw runtime.newArgumentError("extra token after EndOfToken"); } IRubyObject[] tokVal = {null, null}; v.extract_user_token(context, args[0], tokVal); v.parse_main(context, tokVal[0], tokVal[1], true); if (v.fin != 0 && v.fin != CP_FIN_ACCEPT) { throw lexerUnroll; } return context.nil; } }, context)); } catch (LexerUnroll maybeOurs) { if (maybeOurs.equals(lexerUnroll)) { return; } } } } public void load(Ruby runtime, boolean wrap) { RubyModule racc = runtime.getOrCreateModule("Racc"); RubyClass parser = racc.defineOrGetClassUnder("Parser", runtime.getObject()); parser.setAllocator(new ObjectAllocator() { @Override public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) { return new Parser(ruby, rubyClass); } }); parser.defineAnnotatedMethods(Parser.class); parser.defineConstant("Racc_Runtime_Core_Version_C", runtime.newString(Parser.Racc_Runtime_Core_Version_C)); CparseParams = racc.defineClassUnder("CparseParams", runtime.getObject(), new ObjectAllocator() { @Override public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) { return new CparseParams(ruby, rubyClass); } }); RaccBug = runtime.getRuntimeError(); sym_noreduce = runtime.newSymbol(ID_NOREDUCE); call_nexttoken = MethodIndex.getFunctionalCallSite(ID_NEXTTOKEN); call_onerror = MethodIndex.getFunctionalCallSite(ID_ONERROR); call_d_shift = MethodIndex.getFunctionalCallSite(ID_D_SHIFT); call_d_reduce = MethodIndex.getFunctionalCallSite(ID_D_REDUCE); call_d_accept = MethodIndex.getFunctionalCallSite(ID_D_ACCEPT); call_d_read_token = MethodIndex.getFunctionalCallSite(ID_D_READ_TOKEN); call_d_next_state = MethodIndex.getFunctionalCallSite(ID_D_NEXT_STATE); call_d_e_pop = MethodIndex.getFunctionalCallSite(ID_D_E_POP); vDEFAULT_TOKEN = runtime.newFixnum(TokenType.DEFAULT.id); vERROR_TOKEN = runtime.newFixnum(TokenType.ERROR.id); vFINAL_TOKEN = runtime.newFixnum(TokenType.FINAL.id); } } racc-1.6.2/ext/racc/cparse/000077500000000000000000000000001435117461300154225ustar00rootroot00000000000000racc-1.6.2/ext/racc/cparse/cparse.c000066400000000000000000000600731435117461300170510ustar00rootroot00000000000000/* cparse.c -- Racc Runtime Core Copyright (c) 1999-2006 Minero Aoki This library is free software. You can distribute/modify this program under the same terms of ruby. */ #include #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /* ----------------------------------------------------------------------- Important Constants ----------------------------------------------------------------------- */ #define RACC_VERSION "1.6.2" #define DEFAULT_TOKEN -1 #define ERROR_TOKEN 1 #define FINAL_TOKEN 0 #define vDEFAULT_TOKEN INT2FIX(DEFAULT_TOKEN) #define vERROR_TOKEN INT2FIX(ERROR_TOKEN) #define vFINAL_TOKEN INT2FIX(FINAL_TOKEN) /* ----------------------------------------------------------------------- File Local Variables ----------------------------------------------------------------------- */ static VALUE RaccBug; static VALUE CparseParams; static ID id_yydebug; static ID id_nexttoken; static ID id_onerror; static ID id_noreduce; static ID id_errstatus; static ID id_d_shift; static ID id_d_reduce; static ID id_d_accept; static ID id_d_read_token; static ID id_d_next_state; static ID id_d_e_pop; /* ----------------------------------------------------------------------- Utils ----------------------------------------------------------------------- */ /* For backward compatibility */ #ifndef ID2SYM # define ID2SYM(i) ULONG2NUM(i) #endif #ifndef SYM2ID # define SYM2ID(v) ((ID)NUM2ULONG(v)) #endif #ifndef SYMBOL_P # define SYMBOL_P(v) FIXNUM_P(v) #endif #ifndef LONG2NUM # define LONG2NUM(i) INT2NUM(i) #endif #ifndef HAVE_RB_ARY_SUBSEQ # define rb_ary_subseq(ary, beg, len) rb_ary_new4(len, RARRAY_PTR(ary) + beg) #endif static ID value_to_id _((VALUE v)); static inline long num_to_long _((VALUE n)); static ID value_to_id(VALUE v) { if (! SYMBOL_P(v)) { rb_raise(rb_eTypeError, "not symbol"); } return SYM2ID(v); } static inline long num_to_long(VALUE n) { return NUM2LONG(n); } #define AREF(s, idx) \ ((0 <= idx && idx < RARRAY_LEN(s)) ? rb_ary_entry(s, idx) : Qnil) /* ----------------------------------------------------------------------- Parser Stack Interfaces ----------------------------------------------------------------------- */ static VALUE get_stack_tail _((VALUE stack, long len)); static void cut_stack_tail _((VALUE stack, long len)); static VALUE get_stack_tail(VALUE stack, long len) { if (len < 0) return Qnil; /* system error */ if (len > RARRAY_LEN(stack)) len = RARRAY_LEN(stack); return rb_ary_subseq(stack, RARRAY_LEN(stack) - len, len); } static void cut_stack_tail(VALUE stack, long len) { while (len > 0) { rb_ary_pop(stack); len--; } } #define STACK_INIT_LEN 64 #define NEW_STACK() rb_ary_new2(STACK_INIT_LEN) #define PUSH(s, i) rb_ary_store(s, RARRAY_LEN(s), i) #define POP(s) rb_ary_pop(s) #define LAST_I(s) \ ((RARRAY_LEN(s) > 0) ? rb_ary_entry(s, RARRAY_LEN(s) - 1) : Qnil) #define GET_TAIL(s, len) get_stack_tail(s, len) #define CUT_TAIL(s, len) cut_stack_tail(s, len) /* ----------------------------------------------------------------------- struct cparse_params ----------------------------------------------------------------------- */ struct cparse_params { VALUE value_v; /* VALUE version of this struct */ VALUE parser; /* parser object */ int lex_is_iterator; VALUE lexer; /* scanner object */ ID lexmid; /* name of scanner method (must be an iterator) */ /* State transition tables (immutable) Data structure is from Dragon Book 4.9 */ /* action table */ VALUE action_table; VALUE action_check; VALUE action_default; VALUE action_pointer; /* goto table */ VALUE goto_table; VALUE goto_check; VALUE goto_default; VALUE goto_pointer; long nt_base; /* NonTerminal BASE index */ VALUE reduce_table; /* reduce data table */ VALUE token_table; /* token conversion table */ /* parser stacks and parameters */ VALUE state; long curstate; VALUE vstack; VALUE tstack; VALUE t; long shift_n; long reduce_n; long ruleno; long errstatus; /* nonzero in error recovering mode */ long nerr; /* number of error */ int use_result_var; VALUE retval; /* return value of parser routine */ long fin; /* parse result status */ #define CP_FIN_ACCEPT 1 #define CP_FIN_EOT 2 #define CP_FIN_CANTPOP 3 int debug; /* user level debug */ int sys_debug; /* system level debug */ long i; /* table index */ }; /* ----------------------------------------------------------------------- Parser Main Routines ----------------------------------------------------------------------- */ static VALUE racc_cparse _((VALUE parser, VALUE arg, VALUE sysdebug)); static VALUE racc_yyparse _((VALUE parser, VALUE lexer, VALUE lexmid, VALUE arg, VALUE sysdebug)); static void call_lexer _((struct cparse_params *v)); static VALUE lexer_i _((RB_BLOCK_CALL_FUNC_ARGLIST(block_args, data))); static VALUE assert_array _((VALUE a)); static long assert_integer _((VALUE n)); static VALUE assert_hash _((VALUE h)); static VALUE initialize_params _((VALUE vparams, VALUE parser, VALUE arg, VALUE lexer, VALUE lexmid)); static void cparse_params_mark _((void *ptr)); static size_t cparse_params_memsize _((const void *ptr)); static void parse_main _((struct cparse_params *v, VALUE tok, VALUE val, int resume)); static void extract_user_token _((struct cparse_params *v, VALUE block_args, VALUE *tok, VALUE *val)); static void shift _((struct cparse_params* v, long act, VALUE tok, VALUE val)); static int reduce _((struct cparse_params* v, long act)); static rb_block_call_func reduce0; #ifdef DEBUG # define D_puts(msg) if (v->sys_debug) puts(msg) # define D_printf(fmt,arg) if (v->sys_debug) printf(fmt,arg) #else # define D_puts(msg) # define D_printf(fmt,arg) #endif #undef RUBY_UNTYPED_DATA_WARNING #define RUBY_UNTYPED_DATA_WARNING 1 static const rb_data_type_t cparse_params_type = { "racc/cparse", { cparse_params_mark, RUBY_TYPED_DEFAULT_FREE, cparse_params_memsize, }, #ifdef RUBY_TYPED_FREE_IMMEDIATELY 0, 0, RUBY_TYPED_FREE_IMMEDIATELY, #endif }; static VALUE racc_cparse(VALUE parser, VALUE arg, VALUE sysdebug) { VALUE vparams; struct cparse_params *v; vparams = TypedData_Make_Struct(CparseParams, struct cparse_params, &cparse_params_type, v); D_puts("starting cparse"); v->sys_debug = RTEST(sysdebug); vparams = initialize_params(vparams, parser, arg, Qnil, Qnil); v->lex_is_iterator = FALSE; parse_main(v, Qnil, Qnil, 0); RB_GC_GUARD(vparams); return v->retval; } static VALUE racc_yyparse(VALUE parser, VALUE lexer, VALUE lexmid, VALUE arg, VALUE sysdebug) { VALUE vparams; struct cparse_params *v; vparams = TypedData_Make_Struct(CparseParams, struct cparse_params, &cparse_params_type, v); v->sys_debug = RTEST(sysdebug); D_puts("start C yyparse"); vparams = initialize_params(vparams, parser, arg, lexer, lexmid); v->lex_is_iterator = TRUE; D_puts("params initialized"); parse_main(v, Qnil, Qnil, 0); call_lexer(v); if (!v->fin) { rb_raise(rb_eArgError, "%s() is finished before EndOfToken", rb_id2name(v->lexmid)); } RB_GC_GUARD(vparams); return v->retval; } #ifdef HAVE_RB_BLOCK_CALL static void call_lexer(struct cparse_params *v) { rb_block_call(v->lexer, v->lexmid, 0, NULL, lexer_i, v->value_v); } #else static VALUE lexer_iter(VALUE data) { struct cparse_params *v = rb_check_typeddata(data, &cparse_params_type); rb_funcall(v->lexer, v->lexmid, 0); return Qnil; } static void call_lexer(struct cparse_params *v) { rb_iterate(lexer_iter, v->value_v, lexer_i, v->value_v); } #endif static VALUE lexer_i(RB_BLOCK_CALL_FUNC_ARGLIST(block_args, data)) { struct cparse_params *v = rb_check_typeddata(data, &cparse_params_type); VALUE tok, val; if (v->fin) rb_raise(rb_eArgError, "extra token after EndOfToken"); extract_user_token(v, block_args, &tok, &val); parse_main(v, tok, val, 1); if (v->fin && v->fin != CP_FIN_ACCEPT) rb_iter_break(); return Qnil; } static VALUE assert_array(VALUE a) { Check_Type(a, T_ARRAY); return a; } static VALUE assert_hash(VALUE h) { Check_Type(h, T_HASH); return h; } static long assert_integer(VALUE n) { return NUM2LONG(n); } static VALUE initialize_params(VALUE vparams, VALUE parser, VALUE arg, VALUE lexer, VALUE lexmid) { struct cparse_params *v = rb_check_typeddata(vparams, &cparse_params_type); v->value_v = vparams; v->parser = parser; v->lexer = lexer; if (! NIL_P(lexmid)) v->lexmid = value_to_id(lexmid); v->debug = RTEST(rb_ivar_get(parser, id_yydebug)); Check_Type(arg, T_ARRAY); if (!(13 <= RARRAY_LEN(arg) && RARRAY_LEN(arg) <= 14)) rb_raise(RaccBug, "[Racc Bug] wrong arg.size %ld", RARRAY_LEN(arg)); v->action_table = assert_array (rb_ary_entry(arg, 0)); v->action_check = assert_array (rb_ary_entry(arg, 1)); v->action_default = assert_array (rb_ary_entry(arg, 2)); v->action_pointer = assert_array (rb_ary_entry(arg, 3)); v->goto_table = assert_array (rb_ary_entry(arg, 4)); v->goto_check = assert_array (rb_ary_entry(arg, 5)); v->goto_default = assert_array (rb_ary_entry(arg, 6)); v->goto_pointer = assert_array (rb_ary_entry(arg, 7)); v->nt_base = assert_integer(rb_ary_entry(arg, 8)); v->reduce_table = assert_array (rb_ary_entry(arg, 9)); v->token_table = assert_hash (rb_ary_entry(arg, 10)); v->shift_n = assert_integer(rb_ary_entry(arg, 11)); v->reduce_n = assert_integer(rb_ary_entry(arg, 12)); if (RARRAY_LEN(arg) > 13) { v->use_result_var = RTEST(rb_ary_entry(arg, 13)); } else { v->use_result_var = TRUE; } v->tstack = v->debug ? NEW_STACK() : Qnil; v->vstack = NEW_STACK(); v->state = NEW_STACK(); v->curstate = 0; PUSH(v->state, INT2FIX(0)); v->t = INT2FIX(FINAL_TOKEN + 1); /* must not init to FINAL_TOKEN */ v->nerr = 0; v->errstatus = 0; rb_ivar_set(parser, id_errstatus, LONG2NUM(v->errstatus)); v->retval = Qnil; v->fin = 0; v->lex_is_iterator = FALSE; rb_iv_set(parser, "@vstack", v->vstack); if (v->debug) { rb_iv_set(parser, "@tstack", v->tstack); } else { rb_iv_set(parser, "@tstack", Qnil); } return vparams; } static void cparse_params_mark(void *ptr) { struct cparse_params *v = (struct cparse_params*)ptr; rb_gc_mark(v->value_v); rb_gc_mark(v->parser); rb_gc_mark(v->lexer); rb_gc_mark(v->action_table); rb_gc_mark(v->action_check); rb_gc_mark(v->action_default); rb_gc_mark(v->action_pointer); rb_gc_mark(v->goto_table); rb_gc_mark(v->goto_check); rb_gc_mark(v->goto_default); rb_gc_mark(v->goto_pointer); rb_gc_mark(v->reduce_table); rb_gc_mark(v->token_table); rb_gc_mark(v->state); rb_gc_mark(v->vstack); rb_gc_mark(v->tstack); rb_gc_mark(v->t); rb_gc_mark(v->retval); } static size_t cparse_params_memsize(const void *ptr) { return sizeof(struct cparse_params); } static void extract_user_token(struct cparse_params *v, VALUE block_args, VALUE *tok, VALUE *val) { if (NIL_P(block_args)) { /* EOF */ *tok = Qfalse; *val = rb_str_new("$", 1); return; } if (!RB_TYPE_P(block_args, T_ARRAY)) { rb_raise(rb_eTypeError, "%s() %s %"PRIsVALUE" (must be Array[2])", v->lex_is_iterator ? rb_id2name(v->lexmid) : "next_token", v->lex_is_iterator ? "yielded" : "returned", rb_obj_class(block_args)); } if (RARRAY_LEN(block_args) != 2) { rb_raise(rb_eArgError, "%s() %s wrong size of array (%ld for 2)", v->lex_is_iterator ? rb_id2name(v->lexmid) : "next_token", v->lex_is_iterator ? "yielded" : "returned", RARRAY_LEN(block_args)); } *tok = AREF(block_args, 0); *val = AREF(block_args, 1); } #define SHIFT(v,act,tok,val) shift(v,act,tok,val) #define REDUCE(v,act) do {\ switch (reduce(v,act)) { \ case 0: /* normal */ \ break; \ case 1: /* yyerror */ \ goto user_yyerror; \ case 2: /* yyaccept */ \ D_puts("u accept"); \ goto accept; \ default: \ break; \ } \ } while (0) static void parse_main(struct cparse_params *v, VALUE tok, VALUE val, int resume) { long i; /* table index */ long act; /* action type */ VALUE act_value; /* action type, VALUE version */ int read_next = 1; /* true if we need to read next token */ VALUE tmp; if (resume) goto resume; while (1) { D_puts(""); D_puts("---- enter new loop ----"); D_puts(""); D_printf("(act) k1=%ld\n", v->curstate); tmp = AREF(v->action_pointer, v->curstate); if (NIL_P(tmp)) goto notfound; D_puts("(act) pointer[k1] ok"); i = NUM2LONG(tmp); D_printf("read_next=%d\n", read_next); if (read_next && (v->t != vFINAL_TOKEN)) { if (v->lex_is_iterator) { D_puts("resuming..."); if (v->fin) rb_raise(rb_eArgError, "token given after EOF"); v->i = i; /* save i */ return; resume: D_puts("resumed"); i = v->i; /* load i */ } else { D_puts("next_token"); tmp = rb_funcall(v->parser, id_nexttoken, 0); extract_user_token(v, tmp, &tok, &val); } /* convert token */ v->t = rb_hash_aref(v->token_table, tok); if (NIL_P(v->t)) { v->t = vERROR_TOKEN; } D_printf("(act) t(k2)=%ld\n", NUM2LONG(v->t)); if (v->debug) { rb_funcall(v->parser, id_d_read_token, 3, v->t, tok, val); } } read_next = 0; i += NUM2LONG(v->t); D_printf("(act) i=%ld\n", i); if (i < 0) goto notfound; act_value = AREF(v->action_table, i); if (NIL_P(act_value)) goto notfound; act = NUM2LONG(act_value); D_printf("(act) table[i]=%ld\n", act); tmp = AREF(v->action_check, i); if (NIL_P(tmp)) goto notfound; if (NUM2LONG(tmp) != v->curstate) goto notfound; D_printf("(act) check[i]=%ld\n", NUM2LONG(tmp)); D_puts("(act) found"); act_fixed: D_printf("act=%ld\n", act); goto handle_act; notfound: D_puts("(act) not found: use default"); act_value = AREF(v->action_default, v->curstate); act = NUM2LONG(act_value); goto act_fixed; handle_act: if (act > 0 && act < v->shift_n) { D_puts("shift"); if (v->errstatus > 0) { v->errstatus--; rb_ivar_set(v->parser, id_errstatus, LONG2NUM(v->errstatus)); } SHIFT(v, act, v->t, val); read_next = 1; } else if (act < 0 && act > -(v->reduce_n)) { D_puts("reduce"); REDUCE(v, act); } else if (act == -(v->reduce_n)) { goto error; error_recovered: ; /* goto label requires stmt */ } else if (act == v->shift_n) { D_puts("accept"); goto accept; } else { rb_raise(RaccBug, "[Racc Bug] unknown act value %ld", act); } if (v->debug) { rb_funcall(v->parser, id_d_next_state, 2, LONG2NUM(v->curstate), v->state); } } /* not reach */ accept: if (v->debug) rb_funcall(v->parser, id_d_accept, 0); v->retval = rb_ary_entry(v->vstack, 0); v->fin = CP_FIN_ACCEPT; return; error: D_printf("error detected, status=%ld\n", v->errstatus); if (v->errstatus == 0) { v->nerr++; rb_funcall(v->parser, id_onerror, 3, v->t, val, v->vstack); } user_yyerror: if (v->errstatus == 3) { if (v->t == vFINAL_TOKEN) { v->retval = Qnil; v->fin = CP_FIN_EOT; return; } read_next = 1; } v->errstatus = 3; rb_ivar_set(v->parser, id_errstatus, LONG2NUM(v->errstatus)); /* check if we can shift/reduce error token */ D_printf("(err) k1=%ld\n", v->curstate); D_printf("(err) k2=%d (error)\n", ERROR_TOKEN); while (1) { tmp = AREF(v->action_pointer, v->curstate); if (NIL_P(tmp)) goto error_pop; D_puts("(err) pointer[k1] ok"); i = NUM2LONG(tmp) + ERROR_TOKEN; D_printf("(err) i=%ld\n", i); if (i < 0) goto error_pop; act_value = AREF(v->action_table, i); if (NIL_P(act_value)) { D_puts("(err) table[i] == nil"); goto error_pop; } act = NUM2LONG(act_value); D_printf("(err) table[i]=%ld\n", act); tmp = AREF(v->action_check, i); if (NIL_P(tmp)) { D_puts("(err) check[i] == nil"); goto error_pop; } if (NUM2LONG(tmp) != v->curstate) { D_puts("(err) check[i] != k1"); goto error_pop; } D_puts("(err) found: can handle error token"); break; error_pop: D_puts("(err) act not found: can't handle error token; pop"); if (RARRAY_LEN(v->state) <= 1) { v->retval = Qnil; v->fin = CP_FIN_CANTPOP; return; } POP(v->state); POP(v->vstack); v->curstate = num_to_long(LAST_I(v->state)); if (v->debug) { POP(v->tstack); rb_funcall(v->parser, id_d_e_pop, 3, v->state, v->tstack, v->vstack); } } /* shift/reduce error token */ if (act > 0 && act < v->shift_n) { D_puts("e shift"); SHIFT(v, act, ERROR_TOKEN, val); } else if (act < 0 && act > -(v->reduce_n)) { D_puts("e reduce"); REDUCE(v, act); } else if (act == v->shift_n) { D_puts("e accept"); goto accept; } else { rb_raise(RaccBug, "[Racc Bug] unknown act value %ld", act); } goto error_recovered; } static void shift(struct cparse_params *v, long act, VALUE tok, VALUE val) { PUSH(v->vstack, val); if (v->debug) { PUSH(v->tstack, tok); rb_funcall(v->parser, id_d_shift, 3, tok, v->tstack, v->vstack); } v->curstate = act; PUSH(v->state, LONG2NUM(v->curstate)); } static int reduce(struct cparse_params *v, long act) { VALUE code; v->ruleno = -act * 3; code = rb_catch("racc_jump", reduce0, v->value_v); v->errstatus = num_to_long(rb_ivar_get(v->parser, id_errstatus)); return NUM2INT(code); } static VALUE reduce0(RB_BLOCK_CALL_FUNC_ARGLIST(_, data)) { struct cparse_params *v = rb_check_typeddata(data, &cparse_params_type); VALUE reduce_to, reduce_len, method_id; long len; ID mid; VALUE tmp, tmp_t = Qundef, tmp_v = Qundef; long i, k1, k2; VALUE goto_state; reduce_len = rb_ary_entry(v->reduce_table, v->ruleno); reduce_to = rb_ary_entry(v->reduce_table, v->ruleno+1); method_id = rb_ary_entry(v->reduce_table, v->ruleno+2); len = NUM2LONG(reduce_len); mid = value_to_id(method_id); /* call action */ if (len == 0) { tmp = Qnil; if (mid != id_noreduce) tmp_v = rb_ary_new(); if (v->debug) tmp_t = rb_ary_new(); } else { if (mid != id_noreduce) { tmp_v = GET_TAIL(v->vstack, len); tmp = rb_ary_entry(tmp_v, 0); } else { tmp = rb_ary_entry(v->vstack, RARRAY_LEN(v->vstack) - len); } CUT_TAIL(v->vstack, len); if (v->debug) { tmp_t = GET_TAIL(v->tstack, len); CUT_TAIL(v->tstack, len); } CUT_TAIL(v->state, len); } if (mid != id_noreduce) { if (v->use_result_var) { tmp = rb_funcall(v->parser, mid, 3, tmp_v, v->vstack, tmp); } else { tmp = rb_funcall(v->parser, mid, 2, tmp_v, v->vstack); } } /* then push result */ PUSH(v->vstack, tmp); if (v->debug) { PUSH(v->tstack, reduce_to); rb_funcall(v->parser, id_d_reduce, 4, tmp_t, reduce_to, v->tstack, v->vstack); } /* calculate transition state */ if (RARRAY_LEN(v->state) == 0) rb_raise(RaccBug, "state stack unexpectedly empty"); k2 = num_to_long(LAST_I(v->state)); k1 = num_to_long(reduce_to) - v->nt_base; D_printf("(goto) k1=%ld\n", k1); D_printf("(goto) k2=%ld\n", k2); tmp = AREF(v->goto_pointer, k1); if (NIL_P(tmp)) goto notfound; i = NUM2LONG(tmp) + k2; D_printf("(goto) i=%ld\n", i); if (i < 0) goto notfound; goto_state = AREF(v->goto_table, i); if (NIL_P(goto_state)) { D_puts("(goto) table[i] == nil"); goto notfound; } D_printf("(goto) table[i]=%ld (goto_state)\n", NUM2LONG(goto_state)); tmp = AREF(v->goto_check, i); if (NIL_P(tmp)) { D_puts("(goto) check[i] == nil"); goto notfound; } if (tmp != LONG2NUM(k1)) { D_puts("(goto) check[i] != table[i]"); goto notfound; } D_printf("(goto) check[i]=%ld\n", NUM2LONG(tmp)); D_puts("(goto) found"); transit: PUSH(v->state, goto_state); v->curstate = NUM2LONG(goto_state); return INT2FIX(0); notfound: D_puts("(goto) not found: use default"); /* overwrite `goto-state' by default value */ goto_state = AREF(v->goto_default, k1); goto transit; } /* ----------------------------------------------------------------------- Ruby Interface ----------------------------------------------------------------------- */ void Init_cparse(void) { #ifdef HAVE_RB_EXT_RACTOR_SAFE rb_ext_ractor_safe(true); #endif VALUE Racc, Parser; ID id_racc = rb_intern_const("Racc"); if (rb_const_defined(rb_cObject, id_racc)) { Racc = rb_const_get(rb_cObject, id_racc); Parser = rb_const_get_at(Racc, rb_intern_const("Parser")); } else { Racc = rb_define_module("Racc"); Parser = rb_define_class_under(Racc, "Parser", rb_cObject); } rb_define_private_method(Parser, "_racc_do_parse_c", racc_cparse, 2); rb_define_private_method(Parser, "_racc_yyparse_c", racc_yyparse, 4); rb_define_const(Parser, "Racc_Runtime_Core_Version_C", rb_str_new2(RACC_VERSION)); rb_define_const(Parser, "Racc_Runtime_Core_Id_C", rb_str_new2("$originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $")); CparseParams = rb_define_class_under(Racc, "CparseParams", rb_cObject); rb_undef_alloc_func(CparseParams); rb_undef_method(CparseParams, "initialize"); rb_undef_method(CparseParams, "initialize_copy"); RaccBug = rb_eRuntimeError; id_yydebug = rb_intern_const("@yydebug"); id_nexttoken = rb_intern_const("next_token"); id_onerror = rb_intern_const("on_error"); id_noreduce = rb_intern_const("_reduce_none"); id_errstatus = rb_intern_const("@racc_error_status"); id_d_shift = rb_intern_const("racc_shift"); id_d_reduce = rb_intern_const("racc_reduce"); id_d_accept = rb_intern_const("racc_accept"); id_d_read_token = rb_intern_const("racc_read_token"); id_d_next_state = rb_intern_const("racc_next_state"); id_d_e_pop = rb_intern_const("racc_e_pop"); } racc-1.6.2/ext/racc/cparse/extconf.rb000066400000000000000000000002071435117461300174140ustar00rootroot00000000000000# frozen_string_literal: false # require 'mkmf' have_func('rb_block_call') have_func('rb_ary_subseq') create_makefile 'racc/cparse' racc-1.6.2/lib/000077500000000000000000000000001435117461300132035ustar00rootroot00000000000000racc-1.6.2/lib/racc.rb000066400000000000000000000002111435117461300144320ustar00rootroot00000000000000require 'racc/compat' require 'racc/debugflags' require 'racc/grammar' require 'racc/state' require 'racc/exception' require 'racc/info' racc-1.6.2/lib/racc/000077500000000000000000000000001435117461300141135ustar00rootroot00000000000000racc-1.6.2/lib/racc/compat.rb000066400000000000000000000010321435117461300157170ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ unless Object.method_defined?(:__send) class Object alias __send __send__ end end unless Object.method_defined?(:__send!) class Object alias __send! __send__ end end unless Array.method_defined?(:map!) class Array if Array.method_defined?(:collect!) alias map! collect! else alias map! filter end end end racc-1.6.2/lib/racc/debugflags.rb000066400000000000000000000024551435117461300165510ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ module Racc class DebugFlags def DebugFlags.parse_option_string(s) parse = rule = token = state = la = prec = conf = false s.split(//).each do |ch| case ch when 'p' then parse = true when 'r' then rule = true when 't' then token = true when 's' then state = true when 'l' then la = true when 'c' then prec = true when 'o' then conf = true else raise "unknown debug flag char: #{ch.inspect}" end end new(parse, rule, token, state, la, prec, conf) end def initialize(parse = false, rule = false, token = false, state = false, la = false, prec = false, conf = false) @parse = parse @rule = rule @token = token @state = state @la = la @prec = prec @any = (parse || rule || token || state || la || prec) @status_logging = conf end attr_reader :parse attr_reader :rule attr_reader :token attr_reader :state attr_reader :la attr_reader :prec def any? @any end attr_reader :status_logging end end racc-1.6.2/lib/racc/exception.rb000066400000000000000000000004201435117461300164320ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ module Racc class Error < StandardError; end class CompileError < Error; end end racc-1.6.2/lib/racc/grammar.rb000066400000000000000000000542661435117461300161030ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ require 'racc/compat' require 'racc/iset' require 'racc/sourcetext' require 'racc/logfilegenerator' require 'racc/exception' require 'forwardable' module Racc class Grammar def initialize(debug_flags = DebugFlags.new) @symboltable = SymbolTable.new @debug_symbol = debug_flags.token @rules = [] # :: [Rule] @start = nil @n_expected_srconflicts = nil @prec_table = [] @prec_table_closed = false @closed = false @states = nil end attr_reader :start attr_reader :symboltable attr_accessor :n_expected_srconflicts def [](x) @rules[x] end def each_rule(&block) @rules.each(&block) end alias each each_rule def each_index(&block) @rules.each_index(&block) end def each_with_index(&block) @rules.each_with_index(&block) end def size @rules.size end def to_s "" end extend Forwardable def_delegator "@symboltable", :each, :each_symbol def_delegator "@symboltable", :each_terminal def_delegator "@symboltable", :each_nonterminal def intern(value, dummy = false) @symboltable.intern(value, dummy) end def symbols @symboltable.symbols end def nonterminal_base @symboltable.nt_base end def useless_nonterminal_exist? n_useless_nonterminals() != 0 end def n_useless_nonterminals @n_useless_nonterminals ||= each_useless_nonterminal.count end def each_useless_nonterminal return to_enum __method__ unless block_given? @symboltable.each_nonterminal do |sym| yield sym if sym.useless? end end def useless_rule_exist? n_useless_rules() != 0 end def n_useless_rules @n_useless_rules ||= each_useless_rule.count end def each_useless_rule return to_enum __method__ unless block_given? each do |r| yield r if r.useless? end end def nfa (@states ||= States.new(self)).nfa end def dfa (@states ||= States.new(self)).dfa end alias states dfa def state_transition_table states().state_transition_table end def parser_class states = states() # cache if $DEBUG srcfilename = caller(1).first.slice(/\A(.*?):/, 1) begin write_log srcfilename + ".output" rescue SystemCallError end report = lambda {|s| $stderr.puts "racc: #{srcfilename}: #{s}" } if states.should_report_srconflict? report["#{states.n_srconflicts} shift/reduce conflicts"] end if states.rrconflict_exist? report["#{states.n_rrconflicts} reduce/reduce conflicts"] end g = states.grammar if g.useless_nonterminal_exist? report["#{g.n_useless_nonterminals} useless nonterminals"] end if g.useless_rule_exist? report["#{g.n_useless_rules} useless rules"] end end states.state_transition_table.parser_class end def write_log(path) File.open(path, 'w') {|f| LogFileGenerator.new(states()).output f } end # # Grammar Definition Interface # def add(rule) raise ArgumentError, "rule added after the Grammar closed" if @closed @rules.push rule end def added?(sym) @rules.detect {|r| r.target == sym } end def start_symbol=(s) raise CompileError, "start symbol set twice'" if @start @start = s end def declare_precedence(assoc, syms) raise CompileError, "precedence table defined twice" if @prec_table_closed @prec_table.push [assoc, syms] end def end_precedence_declaration(reverse) @prec_table_closed = true return if @prec_table.empty? table = reverse ? @prec_table.reverse : @prec_table table.each_with_index do |(assoc, syms), idx| syms.each do |sym| sym.assoc = assoc sym.precedence = idx end end end # # Dynamic Generation Interface # def Grammar.define(&block) env = DefinitionEnv.new env.instance_eval(&block) env.grammar end class DefinitionEnv def initialize @grammar = Grammar.new @seqs = Hash.new(0) @delayed = [] end def grammar flush_delayed @grammar.each do |rule| if rule.specified_prec rule.specified_prec = @grammar.intern(rule.specified_prec) end end @grammar.init @grammar end def precedence_table(&block) env = PrecedenceDefinitionEnv.new(@grammar) env.instance_eval(&block) @grammar.end_precedence_declaration env.reverse end def method_missing(mid, *args, &block) unless mid.to_s[-1,1] == '=' super # raises NoMethodError end target = @grammar.intern(mid.to_s.chop.intern) unless args.size == 1 raise ArgumentError, "too many arguments for #{mid} (#{args.size} for 1)" end _add target, args.first end def _add(target, x) case x when Sym @delayed.each do |rule| rule.replace x, target if rule.target == x end @grammar.symboltable.delete x else x.each_rule do |r| r.target = target @grammar.add r end end flush_delayed end def _delayed_add(rule) @delayed.push rule end def _added?(sym) @grammar.added?(sym) or @delayed.detect {|r| r.target == sym } end def flush_delayed return if @delayed.empty? @delayed.each do |rule| @grammar.add rule end @delayed.clear end def seq(*list, &block) Rule.new(nil, list.map {|x| _intern(x) }, UserAction.proc(block)) end def null(&block) seq(&block) end def action(&block) id = "@#{@seqs["action"] += 1}".intern _delayed_add Rule.new(@grammar.intern(id), [], UserAction.proc(block)) id end alias _ action def option(sym, default = nil, &block) _defmetasyntax("option", _intern(sym), block) {|target| seq() { default } | seq(sym) } end def many(sym, &block) _defmetasyntax("many", _intern(sym), block) {|target| seq() { [] }\ | seq(target, sym) {|list, x| list.push x; list } } end def many1(sym, &block) _defmetasyntax("many1", _intern(sym), block) {|target| seq(sym) {|x| [x] }\ | seq(target, sym) {|list, x| list.push x; list } } end def separated_by(sep, sym, &block) option(separated_by1(sep, sym), [], &block) end def separated_by1(sep, sym, &block) _defmetasyntax("separated_by1", _intern(sym), block) {|target| seq(sym) {|x| [x] }\ | seq(target, sep, sym) {|list, _, x| list.push x; list } } end def _intern(x) case x when Symbol, String @grammar.intern(x) when Racc::Sym x else raise TypeError, "wrong type #{x.class} (expected Symbol/String/Racc::Sym)" end end private def _defmetasyntax(type, id, action, &block) if action idbase = "#{type}@#{id}-#{@seqs[type] += 1}" target = _wrap(idbase, "#{idbase}-core", action) _register("#{idbase}-core", &block) else target = _register("#{type}@#{id}", &block) end @grammar.intern(target) end def _register(target_name) target = target_name.intern unless _added?(@grammar.intern(target)) yield(target).each_rule do |rule| rule.target = @grammar.intern(target) _delayed_add rule end end target end def _wrap(target_name, sym, block) target = target_name.intern _delayed_add Rule.new(@grammar.intern(target), [@grammar.intern(sym.intern)], UserAction.proc(block)) target end end class PrecedenceDefinitionEnv def initialize(g) @grammar = g @prechigh_seen = false @preclow_seen = false @reverse = false end attr_reader :reverse def higher if @prechigh_seen raise CompileError, "prechigh used twice" end @prechigh_seen = true end def lower if @preclow_seen raise CompileError, "preclow used twice" end if @prechigh_seen @reverse = true end @preclow_seen = true end def left(*syms) @grammar.declare_precedence :Left, syms.map {|s| @grammar.intern(s) } end def right(*syms) @grammar.declare_precedence :Right, syms.map {|s| @grammar.intern(s) } end def nonassoc(*syms) @grammar.declare_precedence :Nonassoc, syms.map {|s| @grammar.intern(s)} end end # # Computation # def init return if @closed @closed = true @start ||= @rules.map {|r| r.target }.detect {|sym| not sym.dummy? } raise CompileError, 'no rule in input' if @rules.empty? add_start_rule @rules.freeze fix_ident compute_hash compute_heads determine_terminals compute_nullable_0 @symboltable.fix compute_locate @symboltable.each_nonterminal {|t| compute_expand t } compute_nullable compute_useless end private def add_start_rule r = Rule.new(@symboltable.dummy, [@start, @symboltable.anchor, @symboltable.anchor], UserAction.empty) r.ident = 0 r.hash = 0 r.precedence = nil @rules.unshift r end # Rule#ident # LocationPointer#ident def fix_ident @rules.each_with_index do |rule, idx| rule.ident = idx end end # Rule#hash def compute_hash hash = 4 # size of dummy rule @rules.each do |rule| rule.hash = hash hash += (rule.size + 1) end end # Sym#heads def compute_heads @rules.each do |rule| rule.target.heads.push rule.ptrs[0] end end # Sym#terminal? def determine_terminals @symboltable.each do |s| s.term = s.heads.empty? end end # Sym#self_null? def compute_nullable_0 @symboltable.each do |s| if s.terminal? s.snull = false else s.snull = s.heads.any? {|loc| loc.reduce? } end end end # Sym#locate def compute_locate @rules.each do |rule| t = nil rule.ptrs.each do |ptr| unless ptr.reduce? tok = ptr.dereference tok.locate.push ptr t = tok if tok.terminal? end end rule.precedence = t end end # Sym#expand def compute_expand(t) puts "expand> #{t.to_s}" if @debug_symbol t.expand = _compute_expand(t, ISet.new, []) puts "expand< #{t.to_s}: #{t.expand.to_s}" if @debug_symbol end def _compute_expand(t, set, lock) if tmp = t.expand set.update tmp return set end tok = nil set.update_a t.heads t.heads.each do |ptr| tok = ptr.dereference if tok and tok.nonterminal? unless lock[tok.ident] lock[tok.ident] = true _compute_expand tok, set, lock end end end set end # Sym#nullable?, Rule#nullable? def compute_nullable @rules.each {|r| r.null = false } @symboltable.each {|t| t.null = false } r = @rules.dup s = @symboltable.nonterminals begin rs = r.size ss = s.size check_rules_nullable r check_symbols_nullable s end until rs == r.size and ss == s.size end def check_rules_nullable(rules) rules.delete_if do |rule| rule.null = true rule.symbols.each do |t| unless t.nullable? rule.null = false break end end rule.nullable? end end def check_symbols_nullable(symbols) symbols.delete_if do |sym| sym.heads.each do |ptr| if ptr.rule.nullable? sym.null = true break end end sym.nullable? end end # Sym#useless?, Rule#useless? # FIXME: what means "useless"? def compute_useless @symboltable.each_terminal {|sym| sym.useless = false } @symboltable.each_nonterminal {|sym| sym.useless = true } @rules.each {|rule| rule.useless = true } r = @rules.dup s = @symboltable.nonterminals begin rs = r.size ss = s.size check_rules_useless r check_symbols_useless s end until r.size == rs and s.size == ss end def check_rules_useless(rules) rules.delete_if do |rule| rule.useless = false rule.symbols.each do |sym| if sym.useless? rule.useless = true break end end not rule.useless? end end def check_symbols_useless(s) s.delete_if do |t| t.heads.each do |ptr| unless ptr.rule.useless? t.useless = false break end end not t.useless? end end end # class Grammar class Rule def initialize(target, syms, act) @target = target @symbols = syms @action = act @alternatives = [] @ident = nil @hash = nil @ptrs = nil @precedence = nil @specified_prec = nil @null = nil @useless = nil end attr_accessor :target attr_reader :symbols attr_reader :action def |(x) @alternatives.push x.rule self end def rule self end def each_rule(&block) yield self @alternatives.each(&block) end attr_accessor :ident attr_reader :hash attr_reader :ptrs def hash=(n) @hash = n ptrs = [] @symbols.each_with_index do |sym, idx| ptrs.push LocationPointer.new(self, idx, sym) end ptrs.push LocationPointer.new(self, @symbols.size, nil) @ptrs = ptrs end def precedence @specified_prec || @precedence end def precedence=(sym) @precedence ||= sym end def prec(sym, &block) @specified_prec = sym if block unless @action.empty? raise CompileError, 'both of rule action block and prec block given' end @action = UserAction.proc(block) end self end attr_accessor :specified_prec def nullable?() @null end def null=(n) @null = n end def useless?() @useless end def useless=(u) @useless = u end def inspect "#" end def ==(other) other.kind_of?(Rule) and @ident == other.ident end def [](idx) @symbols[idx] end def size @symbols.size end def empty? @symbols.empty? end def to_s "#" end def accept? if tok = @symbols[-1] tok.anchor? else false end end def each(&block) @symbols.each(&block) end def replace(src, dest) @target = dest @symbols = @symbols.map {|s| s == src ? dest : s } end end # class Rule class UserAction def UserAction.source_text(src) new(src, nil) end def UserAction.proc(pr = nil, &block) if pr and block raise ArgumentError, "both of argument and block given" end new(nil, pr || block) end def UserAction.empty new(nil, nil) end private_class_method :new def initialize(src, proc) @source = src @proc = proc end attr_reader :source attr_reader :proc def source? not @proc end def proc? not @source end def empty? not @proc and not @source end def name "{action type=#{@source || @proc || 'nil'}}" end alias inspect name end class OrMark def initialize(lineno) @lineno = lineno end def name '|' end alias inspect name attr_reader :lineno end class Prec def initialize(symbol, lineno) @symbol = symbol @lineno = lineno end def name "=#{@symbol}" end alias inspect name attr_reader :symbol attr_reader :lineno end # # A set of rule and position in it's RHS. # Note that the number of pointers is more than rule's RHS array, # because pointer points right edge of the final symbol when reducing. # class LocationPointer def initialize(rule, i, sym) @rule = rule @index = i @symbol = sym @ident = @rule.hash + i @reduce = sym.nil? end attr_reader :rule attr_reader :index attr_reader :symbol alias dereference symbol attr_reader :ident alias hash ident attr_reader :reduce alias reduce? reduce def to_s sprintf('(%d,%d %s)', @rule.ident, @index, (reduce?() ? '#' : @symbol.to_s)) end alias inspect to_s def eql?(ot) @hash == ot.hash end alias == eql? def head? @index == 0 end def next @rule.ptrs[@index + 1] or ptr_bug! end alias increment next def before(len) @rule.ptrs[@index - len] or ptr_bug! end private def ptr_bug! raise "racc: fatal: pointer not exist: self: #{to_s}" end end # class LocationPointer class SymbolTable include Enumerable def initialize @symbols = [] # :: [Racc::Sym] @cache = {} # :: {(String|Symbol) => Racc::Sym} @dummy = intern(:$start, true) @anchor = intern(false, true) # Symbol ID = 0 @error = intern(:error, false) # Symbol ID = 1 end attr_reader :dummy attr_reader :anchor attr_reader :error def [](id) @symbols[id] end def intern(val, dummy = false) @cache[val] ||= begin sym = Sym.new(val, dummy) @symbols.push sym sym end end attr_reader :symbols alias to_a symbols def delete(sym) @symbols.delete sym @cache.delete sym.value end attr_reader :nt_base def nt_max @symbols.size end def each(&block) @symbols.each(&block) end def terminals(&block) @symbols[0, @nt_base] end def each_terminal(&block) @terms.each(&block) end def nonterminals @symbols[@nt_base, @symbols.size - @nt_base] end def each_nonterminal(&block) @nterms.each(&block) end def fix terms, nterms = @symbols.partition {|s| s.terminal? } @symbols = terms + nterms @terms = terms @nterms = nterms @nt_base = terms.size fix_ident check_terminals end private def fix_ident @symbols.each_with_index do |t, i| t.ident = i end end def check_terminals return unless @symbols.any? {|s| s.should_terminal? } @anchor.should_terminal @error.should_terminal each_terminal do |t| t.should_terminal if t.string_symbol? end each do |s| s.should_terminal if s.assoc end terminals().reject {|t| t.should_terminal? }.each do |t| raise CompileError, "terminal #{t} not declared as terminal" end nonterminals().select {|n| n.should_terminal? }.each do |n| raise CompileError, "symbol #{n} declared as terminal but is not terminal" end end end # class SymbolTable # Stands terminal and nonterminal symbols. class Sym def initialize(value, dummyp) @ident = nil @value = value @dummyp = dummyp @term = nil @nterm = nil @should_terminal = false @precedence = nil case value when Symbol @to_s = value.to_s @serialized = value.inspect @string = false when String @to_s = value.inspect @serialized = value.dump @string = true when false @to_s = '$end' @serialized = 'false' @string = false when ErrorSymbolValue @to_s = 'error' @serialized = 'Object.new' @string = false else raise ArgumentError, "unknown symbol value: #{value.class}" end @heads = [] @locate = [] @snull = nil @null = nil @expand = nil @useless = nil end class << self def once_writer(nm) nm = nm.id2name module_eval(<<-EOS) def #{nm}=(v) raise 'racc: fatal: @#{nm} != nil' unless @#{nm}.nil? @#{nm} = v end EOS end end once_writer :ident attr_reader :ident alias hash ident attr_reader :value def dummy? @dummyp end def terminal? @term end def nonterminal? @nterm end def term=(t) raise 'racc: fatal: term= called twice' unless @term.nil? @term = t @nterm = !t end def should_terminal @should_terminal = true end def should_terminal? @should_terminal end def string_symbol? @string end def serialize @serialized end attr_writer :serialized attr_accessor :precedence attr_accessor :assoc def to_s @to_s.dup end alias inspect to_s def |(x) rule() | x.rule end def rule Rule.new(nil, [self], UserAction.empty) end # # cache # attr_reader :heads attr_reader :locate def self_null? @snull end once_writer :snull def nullable? @null end def null=(n) @null = n end attr_reader :expand once_writer :expand def useless? @useless end def useless=(f) @useless = f end end # class Sym end # module Racc racc-1.6.2/lib/racc/grammarfileparser.rb000066400000000000000000000354771435117461300201630ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ require 'racc' require 'racc/compat' require 'racc/grammar' require 'racc/parserfilegenerator' require 'racc/sourcetext' require 'stringio' module Racc grammar = Grammar.define { g = self g.class = seq(:CLASS, :cname, many(:param), :RULE, :rules, option(:END)) g.cname = seq(:rubyconst) {|name| @result.params.classname = name }\ | seq(:rubyconst, "<", :rubyconst) {|c, _, s| @result.params.classname = c @result.params.superclass = s } g.rubyconst = separated_by1(:colon2, :SYMBOL) {|syms| syms.map {|s| s.to_s }.join('::') } g.colon2 = seq(':', ':') g.param = seq(:CONV, many1(:convdef), :END) {|*| #@grammar.end_convert_block # FIXME }\ | seq(:PRECHIGH, many1(:precdef), :PRECLOW) {|*| @grammar.end_precedence_declaration true }\ | seq(:PRECLOW, many1(:precdef), :PRECHIGH) {|*| @grammar.end_precedence_declaration false }\ | seq(:START, :symbol) {|_, sym| @grammar.start_symbol = sym }\ | seq(:TOKEN, :symbols) {|_, syms| syms.each do |s| s.should_terminal end }\ | seq(:OPTION, :options) {|_, syms| syms.each do |opt| case opt when 'result_var' @result.params.result_var = true when 'no_result_var' @result.params.result_var = false when 'omit_action_call' @result.params.omit_action_call = true when 'no_omit_action_call' @result.params.omit_action_call = false else raise CompileError, "unknown option: #{opt}" end end }\ | seq(:EXPECT, :DIGIT) {|_, num| if @grammar.n_expected_srconflicts raise CompileError, "`expect' seen twice" end @grammar.n_expected_srconflicts = num } g.convdef = seq(:symbol, :STRING) {|sym, code| sym.serialized = code } g.precdef = seq(:LEFT, :symbols) {|_, syms| @grammar.declare_precedence :Left, syms }\ | seq(:RIGHT, :symbols) {|_, syms| @grammar.declare_precedence :Right, syms }\ | seq(:NONASSOC, :symbols) {|_, syms| @grammar.declare_precedence :Nonassoc, syms } g.symbols = seq(:symbol) {|sym| [sym] }\ | seq(:symbols, :symbol) {|list, sym| list.push sym list }\ | seq(:symbols, "|") g.symbol = seq(:SYMBOL) {|sym| @grammar.intern(sym) }\ | seq(:STRING) {|str| @grammar.intern(str) } g.options = many(:SYMBOL) {|syms| syms.map {|s| s.to_s } } g.rules = option(:rules_core) {|list| add_rule_block list unless list.empty? nil } g.rules_core = seq(:symbol) {|sym| [sym] }\ | seq(:rules_core, :rule_item) {|list, i| list.push i list }\ | seq(:rules_core, ';') {|list, *| add_rule_block list unless list.empty? list.clear list }\ | seq(:rules_core, ':') {|list, *| next_target = list.pop add_rule_block list unless list.empty? [next_target] } g.rule_item = seq(:symbol)\ | seq("|") {|*| OrMark.new(@scanner.lineno) }\ | seq("=", :symbol) {|_, sym| Prec.new(sym, @scanner.lineno) }\ | seq(:ACTION) {|src| UserAction.source_text(src) } } GrammarFileParser = grammar.parser_class if grammar.states.srconflict_exist? raise 'Racc boot script fatal: S/R conflict in build' end if grammar.states.rrconflict_exist? raise 'Racc boot script fatal: R/R conflict in build' end class GrammarFileParser # reopen class Result def initialize(grammar) @grammar = grammar @params = ParserFileGenerator::Params.new end attr_reader :grammar attr_reader :params end def GrammarFileParser.parse_file(filename) parse(File.read(filename), filename, 1) end def GrammarFileParser.parse(src, filename = '-', lineno = 1) new().parse(src, filename, lineno) end def initialize(debug_flags = DebugFlags.new) @yydebug = debug_flags.parse end def parse(src, filename = '-', lineno = 1) @filename = filename @lineno = lineno @scanner = GrammarFileScanner.new(src, @filename) @scanner.debug = @yydebug @grammar = Grammar.new @result = Result.new(@grammar) @embedded_action_seq = 0 yyparse @scanner, :yylex parse_user_code @result.grammar.init @result end private def next_token @scanner.scan end def on_error(tok, val, _values) if val.respond_to?(:id2name) v = val.id2name elsif val.kind_of?(String) v = val else v = val.inspect end raise CompileError, "#{location()}: unexpected token '#{v}'" end def location "#{@filename}:#{@lineno - 1 + @scanner.lineno}" end def add_rule_block(list) sprec = nil target = list.shift case target when OrMark, UserAction, Prec raise CompileError, "#{target.lineno}: unexpected symbol #{target.name}" end curr = [] list.each do |i| case i when OrMark add_rule target, curr, sprec curr = [] sprec = nil when Prec raise CompileError, "'=' used twice in one rule" if sprec sprec = i.symbol else curr.push i end end add_rule target, curr, sprec end def add_rule(target, list, sprec) if list.last.kind_of?(UserAction) act = list.pop else act = UserAction.empty end list.map! {|s| s.kind_of?(UserAction) ? embedded_action(s) : s } rule = Rule.new(target, list, act) rule.specified_prec = sprec @grammar.add rule end def embedded_action(act) sym = @grammar.intern("@#{@embedded_action_seq += 1}".intern, true) @grammar.add Rule.new(sym, [], act) sym end # # User Code Block # def parse_user_code line = @scanner.lineno _, *blocks = *@scanner.epilogue.split(/^----/) blocks.each do |block| header, *body = block.lines.to_a label0, paths = *header.sub(/\A-+/, '').split('=', 2) label = canonical_label(label0) (paths ? paths.strip.split(' ') : []).each do |path| add_user_code label, SourceText.new(File.read(path), path, 1) end add_user_code label, SourceText.new(body.join(''), @filename, line + 1) line += (1 + body.size) end end USER_CODE_LABELS = { 'header' => :header, 'prepare' => :header, # obsolete 'inner' => :inner, 'footer' => :footer, 'driver' => :footer # obsolete } def canonical_label(src) label = src.to_s.strip.downcase.slice(/\w+/) unless USER_CODE_LABELS.key?(label) raise CompileError, "unknown user code type: #{label.inspect}" end label end def add_user_code(label, src) @result.params.public_send(USER_CODE_LABELS[label]).push src end end class GrammarFileScanner def initialize(str, filename = '-') @lines = str.b.split(/\n|\r\n|\r/) @filename = filename @lineno = -1 @line_head = true @in_rule_blk = false @in_conv_blk = false @in_block = nil @epilogue = '' @debug = false next_line end attr_reader :epilogue def lineno @lineno + 1 end attr_accessor :debug def yylex(&block) unless @debug yylex0(&block) else yylex0 do |sym, tok| $stderr.printf "%7d %-10s %s\n", lineno(), sym.inspect, tok.inspect yield [sym, tok] end end end private def yylex0 begin until @line.empty? @line.sub!(/\A\s+/, '') if /\A\#/ =~ @line break elsif /\A\/\*/ =~ @line skip_comment elsif s = reads(/\A[a-zA-Z_]\w*/) yield [atom_symbol(s), s.intern] elsif s = reads(/\A\d+/) yield [:DIGIT, s.to_i] elsif ch = reads(/\A./) case ch when '"', "'" yield [:STRING, eval(scan_quoted(ch))] when '{' lineno = lineno() yield [:ACTION, SourceText.new(scan_action(), @filename, lineno)] else if ch == '|' @line_head = false end yield [ch, ch] end else end end end while next_line() yield nil end def next_line @lineno += 1 @line = @lines[@lineno] if not @line or /\A----/ =~ @line @epilogue = @lines.join("\n") @lines.clear @line = nil if @in_block @lineno -= 1 scan_error! sprintf('unterminated %s', @in_block) end false else @line.sub!(/(?:\n|\r\n|\r)\z/, '') @line_head = true true end end ReservedWord = { 'right' => :RIGHT, 'left' => :LEFT, 'nonassoc' => :NONASSOC, 'preclow' => :PRECLOW, 'prechigh' => :PRECHIGH, 'token' => :TOKEN, 'convert' => :CONV, 'options' => :OPTION, 'start' => :START, 'expect' => :EXPECT, 'class' => :CLASS, 'rule' => :RULE, 'end' => :END } def atom_symbol(token) if token == 'end' symbol = :END @in_conv_blk = false @in_rule_blk = false else if @line_head and not @in_conv_blk and not @in_rule_blk symbol = ReservedWord[token] || :SYMBOL else symbol = :SYMBOL end case symbol when :RULE then @in_rule_blk = true when :CONV then @in_conv_blk = true end end @line_head = false symbol end def skip_comment @in_block = 'comment' until m = /\*\//.match(@line) next_line end @line = m.post_match @in_block = nil end $raccs_print_type = false def scan_action buf = String.new nest = 1 pre = nil @in_block = 'action' begin pre = nil if s = reads(/\A\s+/) # does not set 'pre' buf << s end until @line.empty? if s = reads(/\A[^'"`{}%#\/\$]+/) buf << (pre = s) next end case ch = read(1) when '{' nest += 1 buf << (pre = ch) when '}' nest -= 1 if nest == 0 @in_block = nil buf.sub!(/[ \t\f]+\z/, '') return buf end buf << (pre = ch) when '#' # comment buf << ch << @line break when "'", '"', '`' buf << (pre = scan_quoted(ch)) when '%' if literal_head? pre, @line # % string, regexp, array buf << ch case ch = read(1) when /[qQx]/n buf << ch << (pre = scan_quoted(read(1), '%string')) when /wW/n buf << ch << (pre = scan_quoted(read(1), '%array')) when /s/n buf << ch << (pre = scan_quoted(read(1), '%symbol')) when /r/n buf << ch << (pre = scan_quoted(read(1), '%regexp')) when /[a-zA-Z0-9= ]/n # does not include "_" scan_error! "unknown type of % literal '%#{ch}'" else buf << (pre = scan_quoted(ch, '%string')) end else # operator buf << '||op->' if $raccs_print_type buf << (pre = ch) end when '/' if literal_head? pre, @line # regexp buf << (pre = scan_quoted(ch, 'regexp')) else # operator buf << '||op->' if $raccs_print_type buf << (pre = ch) end when '$' # gvar buf << ch << (pre = read(1)) else raise 'racc: fatal: must not happen' end end buf << "\n" end while next_line() raise 'racc: fatal: scan finished before parser finished' end def literal_head?(pre, post) (!pre || /[a-zA-Z_0-9]/n !~ pre[-1,1]) && !post.empty? && /\A[\s\=]/n !~ post end def read(len) s = @line[0, len] @line = @line[len .. -1] s end def reads(re) m = re.match(@line) or return nil @line = m.post_match m[0] end def scan_quoted(left, tag = 'string') buf = left.dup buf = "||#{tag}->" + buf if $raccs_print_type re = get_quoted_re(left) sv, @in_block = @in_block, tag begin if s = reads(re) buf << s break else buf << @line end end while next_line() @in_block = sv buf << "<-#{tag}||" if $raccs_print_type buf end LEFT_TO_RIGHT = { '(' => ')', '{' => '}', '[' => ']', '<' => '>' } CACHE = {} def get_quoted_re(left) term = Regexp.quote(LEFT_TO_RIGHT[left] || left) CACHE[left] ||= /\A[^#{term}\\]*(?:\\.[^\\#{term}]*)*#{term}/ end def scan_error!(msg) raise CompileError, "#{lineno()}: #{msg}" end end end # module Racc racc-1.6.2/lib/racc/info.rb000066400000000000000000000004511435117461300153730ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ module Racc VERSION = '1.6.2' Version = VERSION Copyright = 'Copyright (c) 1999-2006 Minero Aoki' end racc-1.6.2/lib/racc/iset.rb000066400000000000000000000023641435117461300154110ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ module Racc # An "indexed" set. All items must respond to :ident. class ISet def initialize(a = []) @set = a end attr_reader :set def add(i) @set[i.ident] = i end def [](key) @set[key.ident] end def []=(key, val) @set[key.ident] = val end alias include? [] alias key? [] def update(other) s = @set o = other.set o.each_index do |idx| if t = o[idx] s[idx] = t end end end def update_a(a) s = @set a.each {|i| s[i.ident] = i } end def delete(key) i = @set[key.ident] @set[key.ident] = nil i end def each(&block) @set.compact.each(&block) end def to_a @set.compact end def to_s "[#{@set.compact.join(' ')}]" end alias inspect to_s def size @set.nitems end def empty? @set.nitems == 0 end def clear @set.clear end def dup ISet.new(@set.dup) end end # class ISet end # module Racc racc-1.6.2/lib/racc/logfilegenerator.rb000066400000000000000000000120551435117461300177730ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ module Racc class LogFileGenerator def initialize(states, debug_flags = DebugFlags.new) @states = states @grammar = states.grammar @debug_flags = debug_flags end def output(out) output_conflict out; out.puts output_useless out; out.puts output_rule out; out.puts output_token out; out.puts output_state out end # # Warnings # def output_conflict(out) @states.each do |state| if state.srconf out.printf "state %d contains %d shift/reduce conflicts\n", state.stateid, state.srconf.size end if state.rrconf out.printf "state %d contains %d reduce/reduce conflicts\n", state.stateid, state.rrconf.size end end end def output_useless(out) @grammar.each do |rl| if rl.useless? out.printf "rule %d (%s) never reduced\n", rl.ident, rl.target.to_s end end @grammar.each_nonterminal do |t| if t.useless? out.printf "useless nonterminal %s\n", t.to_s end end end # # States # def output_state(out) out << "--------- State ---------\n" showall = @debug_flags.la || @debug_flags.state @states.each do |state| out << "\nstate #{state.ident}\n\n" (showall ? state.closure : state.core).each do |ptr| pointer_out(out, ptr) if ptr.rule.ident != 0 or showall end out << "\n" action_out out, state end end def pointer_out(out, ptr) buf = sprintf("%4d) %s :", ptr.rule.ident, ptr.rule.target.to_s) ptr.rule.symbols.each_with_index do |tok, idx| buf << ' _' if idx == ptr.index buf << ' ' << tok.to_s end buf << ' _' if ptr.reduce? out.puts buf end def action_out(f, state) sr = state.srconf && state.srconf.dup rr = state.rrconf && state.rrconf.dup acts = state.action keys = acts.keys keys.sort! {|a,b| a.ident <=> b.ident } [ Shift, Reduce, Error, Accept ].each do |klass| keys.delete_if do |tok| act = acts[tok] if act.kind_of?(klass) outact f, tok, act if sr and c = sr.delete(tok) outsrconf f, c end if rr and c = rr.delete(tok) outrrconf f, c end true else false end end end sr.each {|tok, c| outsrconf f, c } if sr rr.each {|tok, c| outrrconf f, c } if rr act = state.defact if not act.kind_of?(Error) or @debug_flags.any? outact f, '$default', act end f.puts state.goto_table.each do |t, st| if t.nonterminal? f.printf " %-12s go to state %d\n", t.to_s, st.ident end end end def outact(f, t, act) case act when Shift f.printf " %-12s shift, and go to state %d\n", t.to_s, act.goto_id when Reduce f.printf " %-12s reduce using rule %d (%s)\n", t.to_s, act.ruleid, act.rule.target.to_s when Accept f.printf " %-12s accept\n", t.to_s when Error f.printf " %-12s error\n", t.to_s else raise "racc: fatal: wrong act for outact: act=#{act}(#{act.class})" end end def outsrconf(f, confs) confs.each do |c| r = c.reduce f.printf " %-12s [reduce using rule %d (%s)]\n", c.shift.to_s, r.ident, r.target.to_s end end def outrrconf(f, confs) confs.each do |c| r = c.low_prec f.printf " %-12s [reduce using rule %d (%s)]\n", c.token.to_s, r.ident, r.target.to_s end end # # Rules # def output_rule(out) out.print "-------- Grammar --------\n\n" @grammar.each do |rl| if @debug_flags.any? or rl.ident != 0 out.printf "rule %d %s: %s\n", rl.ident, rl.target.to_s, rl.symbols.join(' ') end end end # # Tokens # def output_token(out) out.print "------- Symbols -------\n\n" out.print "**Nonterminals, with rules where they appear\n\n" @grammar.each_nonterminal do |t| tmp = <filename] [--output-file=filename] # [-erubypath] [--executable=rubypath] # [-v] [--verbose] # [-Ofilename] [--log-file=filename] # [-g] [--debug] # [-E] [--embedded] # [-l] [--no-line-convert] # [-c] [--line-convert-all] # [-a] [--no-omit-actions] # [-C] [--check-only] # [-S] [--output-status] # [--version] [--copyright] [--help] grammarfile # # [+grammarfile+] # Racc grammar file. Any extension is permitted. # [-o+outfile+, --output-file=+outfile+] # A filename for output. default is <+filename+>.tab.rb # [-O+filename+, --log-file=+filename+] # Place logging output in file +filename+. # Default log file name is <+filename+>.output. # [-e+rubypath+, --executable=+rubypath+] # output executable file(mode 755). where +path+ is the Ruby interpreter. # [-v, --verbose] # verbose mode. create +filename+.output file, like yacc's y.output file. # [-g, --debug] # add debug code to parser class. To display debuggin information, # use this '-g' option and set @yydebug true in parser class. # [-E, --embedded] # Output parser which doesn't need runtime files (racc/parser.rb). # [-C, --check-only] # Check syntax of racc grammar file and quit. # [-S, --output-status] # Print messages time to time while compiling. # [-l, --no-line-convert] # turns off line number converting. # [-c, --line-convert-all] # Convert line number of actions, inner, header and footer. # [-a, --no-omit-actions] # Call all actions, even if an action is empty. # [--version] # print Racc version and quit. # [--copyright] # Print copyright and quit. # [--help] # Print usage and quit. # # == Generating Parser Using Racc # # To compile Racc grammar file, simply type: # # $ racc parse.y # # This creates Ruby script file "parse.tab.y". The -o option can change the output filename. # # == Writing A Racc Grammar File # # If you want your own parser, you have to write a grammar file. # A grammar file contains the name of your parser class, grammar for the parser, # user code, and anything else. # When writing a grammar file, yacc's knowledge is helpful. # If you have not used yacc before, Racc is not too difficult. # # Here's an example Racc grammar file. # # class Calcparser # rule # target: exp { print val[0] } # # exp: exp '+' exp # | exp '*' exp # | '(' exp ')' # | NUMBER # end # # Racc grammar files resemble yacc files. # But (of course), this is Ruby code. # yacc's $$ is the 'result', $0, $1... is # an array called 'val', and $-1, $-2... is an array called '_values'. # # See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for # more information on grammar files. # # == Parser # # Then you must prepare the parse entry method. There are two types of # parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse # # Racc::Parser#do_parse is simple. # # It's yyparse() of yacc, and Racc::Parser#next_token is yylex(). # This method must returns an array like [TOKENSYMBOL, ITS_VALUE]. # EOF is [false, false]. # (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default. # If you want to change this, see the grammar reference. # # Racc::Parser#yyparse is little complicated, but useful. # It does not use Racc::Parser#next_token, instead it gets tokens from any iterator. # # For example, yyparse(obj, :scan) causes # calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+. # # == Debugging # # When debugging, "-v" or/and the "-g" option is helpful. # # "-v" creates verbose log file (.output). # "-g" creates a "Verbose Parser". # Verbose Parser prints the internal status when parsing. # But it's _not_ automatic. # You must use -g option and set +@yydebug+ to +true+ in order to get output. # -g option only creates the verbose parser. # # === Racc reported syntax error. # # Isn't there too many "end"? # grammar of racc file is changed in v0.10. # # Racc does not use '%' mark, while yacc uses huge number of '%' marks.. # # === Racc reported "XXXX conflicts". # # Try "racc -v xxxx.y". # It causes producing racc's internal log file, xxxx.output. # # === Generated parsers does not work correctly # # Try "racc -g xxxx.y". # This command let racc generate "debugging parser". # Then set @yydebug=true in your parser. # It produces a working log of your parser. # # == Re-distributing Racc runtime # # A parser, which is created by Racc, requires the Racc runtime module; # racc/parser.rb. # # Ruby 1.8.x comes with Racc runtime module, # you need NOT distribute Racc runtime files. # # If you want to include the Racc runtime module with your parser. # This can be done by using '-E' option: # # $ racc -E -omyparser.rb myparser.y # # This command creates myparser.rb which `includes' Racc runtime. # Only you must do is to distribute your parser file (myparser.rb). # # Note: parser.rb is ruby license, but your parser is not. # Your own parser is completely yours. module Racc unless defined?(Racc_No_Extensions) Racc_No_Extensions = false # :nodoc: end class Parser Racc_Runtime_Version = ::Racc::VERSION Racc_Runtime_Core_Version_R = ::Racc::VERSION begin if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby' require 'jruby' require 'racc/cparse-jruby.jar' com.headius.racc.Cparse.new.load(JRuby.runtime, false) else require 'racc/cparse' end unless new.respond_to?(:_racc_do_parse_c, true) raise LoadError, 'old cparse.so' end if Racc_No_Extensions raise LoadError, 'selecting ruby version of racc runtime core' end Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc: Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc: Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc: Racc_Runtime_Type = 'c' # :nodoc: rescue LoadError Racc_Main_Parsing_Routine = :_racc_do_parse_rb Racc_YY_Parse_Method = :_racc_yyparse_rb Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R Racc_Runtime_Type = 'ruby' end def Parser.racc_runtime_type # :nodoc: Racc_Runtime_Type end def _racc_setup @yydebug = false unless self.class::Racc_debug_parser @yydebug = false unless defined?(@yydebug) if @yydebug @racc_debug_out = $stderr unless defined?(@racc_debug_out) @racc_debug_out ||= $stderr end arg = self.class::Racc_arg arg[13] = true if arg.size < 14 arg end def _racc_init_sysvars @racc_state = [0] @racc_tstack = [] @racc_vstack = [] @racc_t = nil @racc_val = nil @racc_read_next = true @racc_user_yyerror = false @racc_error_status = 0 end # The entry point of the parser. This method is used with #next_token. # If Racc wants to get token (and its value), calls next_token. # # Example: # def parse # @q = [[1,1], # [2,2], # [3,3], # [false, '$']] # do_parse # end # # def next_token # @q.shift # end class_eval %{ def do_parse #{Racc_Main_Parsing_Routine}(_racc_setup(), false) end } # The method to fetch next token. # If you use #do_parse method, you must implement #next_token. # # The format of return value is [TOKEN_SYMBOL, VALUE]. # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT # for 'IDENT'. ";" (String) for ';'. # # The final symbol (End of file) must be false. def next_token raise NotImplementedError, "#{self.class}\#next_token is not defined" end def _racc_do_parse_rb(arg, in_debug) action_table, action_check, action_default, action_pointer, _, _, _, _, _, _, token_table, * = arg _racc_init_sysvars tok = act = i = nil catch(:racc_end_parse) { while true if i = action_pointer[@racc_state[-1]] if @racc_read_next if @racc_t != 0 # not EOF tok, @racc_val = next_token() unless tok # EOF @racc_t = 0 else @racc_t = (token_table[tok] or 1) # error token end racc_read_token(@racc_t, tok, @racc_val) if @yydebug @racc_read_next = false end end i += @racc_t unless i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end else act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end end } end # Another entry point for the parser. # If you use this method, you must implement RECEIVER#METHOD_ID method. # # RECEIVER#METHOD_ID is a method to get next token. # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE]. class_eval %{ def yyparse(recv, mid) #{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false) end } def _racc_yyparse_rb(recv, mid, arg, c_debug) action_table, action_check, action_default, action_pointer, _, _, _, _, _, _, token_table, * = arg _racc_init_sysvars catch(:racc_end_parse) { until i = action_pointer[@racc_state[-1]] while act = _racc_evalact(action_default[@racc_state[-1]], arg) ; end end recv.__send__(mid) do |tok, val| unless tok @racc_t = 0 else @racc_t = (token_table[tok] or 1) # error token end @racc_val = val @racc_read_next = false i += @racc_t unless i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end while !(i = action_pointer[@racc_state[-1]]) || ! @racc_read_next || @racc_t == 0 # $ unless i and i += @racc_t and i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end end end } end ### ### common ### def _racc_evalact(act, arg) action_table, action_check, _, action_pointer, _, _, _, _, _, _, _, shift_n, reduce_n, * = arg nerr = 0 # tmp if act > 0 and act < shift_n # # shift # if @racc_error_status > 0 @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF end @racc_vstack.push @racc_val @racc_state.push act @racc_read_next = true if @yydebug @racc_tstack.push @racc_t racc_shift @racc_t, @racc_tstack, @racc_vstack end elsif act < 0 and act > -reduce_n # # reduce # code = catch(:racc_jump) { @racc_state.push _racc_do_reduce(arg, act) false } if code case code when 1 # yyerror @racc_user_yyerror = true # user_yyerror return -reduce_n when 2 # yyaccept return shift_n else raise '[Racc Bug] unknown jump code' end end elsif act == shift_n # # accept # racc_accept if @yydebug throw :racc_end_parse, @racc_vstack[0] elsif act == -reduce_n # # error # case @racc_error_status when 0 unless arg[21] # user_yyerror nerr += 1 on_error @racc_t, @racc_val, @racc_vstack end when 3 if @racc_t == 0 # is $ # We're at EOF, and another error occurred immediately after # attempting auto-recovery throw :racc_end_parse, nil end @racc_read_next = true end @racc_user_yyerror = false @racc_error_status = 3 while true if i = action_pointer[@racc_state[-1]] i += 1 # error token if i >= 0 and (act = action_table[i]) and action_check[i] == @racc_state[-1] break end end throw :racc_end_parse, nil if @racc_state.size <= 1 @racc_state.pop @racc_vstack.pop if @yydebug @racc_tstack.pop racc_e_pop @racc_state, @racc_tstack, @racc_vstack end end return act else raise "[Racc Bug] unknown action #{act.inspect}" end racc_next_state(@racc_state[-1], @racc_state) if @yydebug nil end def _racc_do_reduce(arg, act) _, _, _, _, goto_table, goto_check, goto_default, goto_pointer, nt_base, reduce_table, _, _, _, use_result, * = arg state = @racc_state vstack = @racc_vstack tstack = @racc_tstack i = act * -3 len = reduce_table[i] reduce_to = reduce_table[i+1] method_id = reduce_table[i+2] void_array = [] tmp_t = tstack[-len, len] if @yydebug tmp_v = vstack[-len, len] tstack[-len, len] = void_array if @yydebug vstack[-len, len] = void_array state[-len, len] = void_array # tstack must be updated AFTER method call if use_result vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0]) else vstack.push __send__(method_id, tmp_v, vstack) end tstack.push reduce_to racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug k1 = reduce_to - nt_base if i = goto_pointer[k1] i += state[-1] if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1 return curstate end end goto_default[k1] end # This method is called when a parse error is found. # # ERROR_TOKEN_ID is an internal ID of token which caused error. # You can get string representation of this ID by calling # #token_to_str. # # ERROR_VALUE is a value of error token. # # value_stack is a stack of symbol values. # DO NOT MODIFY this object. # # This method raises ParseError by default. # # If this method returns, parsers enter "error recovering mode". def on_error(t, val, vstack) raise ParseError, sprintf("\nparse error on value %s (%s)", val.inspect, token_to_str(t) || '?') end # Enter error recovering mode. # This method does not call #on_error. def yyerror throw :racc_jump, 1 end # Exit parser. # Return value is +Symbol_Value_Stack[0]+. def yyaccept throw :racc_jump, 2 end # Leave error recovering mode. def yyerrok @racc_error_status = 0 end # For debugging output def racc_read_token(t, tok, val) @racc_debug_out.print 'read ' @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') ' @racc_debug_out.puts val.inspect @racc_debug_out.puts end def racc_shift(tok, tstack, vstack) @racc_debug_out.puts "shift #{racc_token2str tok}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_reduce(toks, sim, tstack, vstack) out = @racc_debug_out out.print 'reduce ' if toks.empty? out.print ' ' else toks.each {|t| out.print ' ', racc_token2str(t) } end out.puts " --> #{racc_token2str(sim)}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_accept @racc_debug_out.puts 'accept' @racc_debug_out.puts end def racc_e_pop(state, tstack, vstack) @racc_debug_out.puts 'error recovering mode: pop token' racc_print_states state racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_next_state(curstate, state) @racc_debug_out.puts "goto #{curstate}" racc_print_states state @racc_debug_out.puts end def racc_print_stacks(t, v) out = @racc_debug_out out.print ' [' t.each_index do |i| out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')' end out.puts ' ]' end def racc_print_states(s) out = @racc_debug_out out.print ' [' s.each {|st| out.print ' ', st } out.puts ' ]' end def racc_token2str(tok) self.class::Racc_token_to_s_table[tok] or raise "[Racc Bug] can't convert token #{tok} to string" end # Convert internal ID of token symbol to the string. def token_to_str(t) self.class::Racc_token_to_s_table[t] end end end racc-1.6.2/lib/racc/parserfilegenerator.rb000066400000000000000000000251241435117461300205070ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ require 'racc/compat' require 'racc/sourcetext' require 'racc/parser-text' require 'rbconfig' module Racc class ParserFileGenerator class Params def self.bool_attr(name) module_eval(<<-End) def #{name}? @#{name} end def #{name}=(b) @#{name} = b end End end attr_accessor :filename attr_accessor :classname attr_accessor :superclass bool_attr :omit_action_call bool_attr :result_var attr_accessor :header attr_accessor :inner attr_accessor :footer bool_attr :debug_parser bool_attr :convert_line bool_attr :convert_line_all bool_attr :embed_runtime bool_attr :make_executable attr_accessor :interpreter def initialize # Parameters derived from parser self.filename = nil self.classname = nil self.superclass = 'Racc::Parser' self.omit_action_call = true self.result_var = true self.header = [] self.inner = [] self.footer = [] # Parameters derived from command line options self.debug_parser = false self.convert_line = true self.convert_line_all = false self.embed_runtime = false self.make_executable = false self.interpreter = nil end end def initialize(states, params) @states = states @grammar = states.grammar @params = params end def generate_parser string_io = StringIO.new init_line_conversion_system @f = string_io parser_file string_io.rewind string_io.read end def generate_parser_file(destpath) init_line_conversion_system File.open(destpath, 'w') {|f| @f = f parser_file } File.chmod 0755, destpath if @params.make_executable? end private def parser_file shebang @params.interpreter if @params.make_executable? notice line if @params.embed_runtime? embed_library runtime_source() else require 'racc/parser.rb' end header parser_class(@params.classname, @params.superclass) { inner state_transition_table } footer end c = ::RbConfig::CONFIG RUBY_PATH = "#{c['bindir']}/#{c['ruby_install_name']}#{c['EXEEXT']}" def shebang(path) line '#!' + (path == 'ruby' ? RUBY_PATH : path) end def notice line %q[#] line %q[# DO NOT MODIFY!!!!] line %Q[# This file is automatically generated by Racc #{Racc::Version}] line %Q[# from Racc grammar file "#{@params.filename}".] line %q[#] end def runtime_source SourceText.new(::Racc::PARSER_TEXT, 'racc/parser.rb', 1) end def embed_library(src) line %[###### #{src.filename} begin] line %[unless $".index '#{src.filename}'] line %[$".push '#{src.filename}'] put src, @params.convert_line? line %[end] line %[###### #{src.filename} end] end def require(feature) line "require '#{feature}'" end def parser_class(classname, superclass) mods = classname.split('::') classid = mods.pop mods.each do |mod| indent; line "module #{mod}" cref_push mod end indent; line "class #{classid} < #{superclass}" cref_push classid yield cref_pop indent; line "end \# class #{classid}" mods.reverse_each do |mod| cref_pop indent; line "end \# module #{mod}" end end def header @params.header.each do |src| line put src, @params.convert_line_all? end end def inner @params.inner.each do |src| line put src, @params.convert_line? end end def footer @params.footer.each do |src| line put src, @params.convert_line_all? end end # Low Level Routines def put(src, convert_line = false) if convert_line replace_location(src) { @f.puts src.text } else @f.puts src.text end end def line(str = '') @f.puts str end def init_line_conversion_system @cref = [] @used_separator = {} end def cref_push(name) @cref.push name end def cref_pop @cref.pop end def indent @f.print ' ' * @cref.size end def toplevel? @cref.empty? end def replace_location(src) sep = make_separator(src) @f.print 'self.class.' if toplevel? @f.puts "module_eval(<<'#{sep}', '#{src.filename}', #{src.lineno})" yield @f.puts sep end def make_separator(src) sep = unique_separator(src.filename) sep *= 2 while src.text.index(sep) sep end def unique_separator(id) sep = String.new "...end #{id}/module_eval..." while @used_separator.key?(sep) sep.concat sprintf('%02x', rand(255)) end @used_separator[sep] = true sep end # # State Transition Table Serialization # public def put_state_transition_table(f) @f = f state_transition_table end private def state_transition_table table = @states.state_transition_table table.use_result_var = @params.result_var? table.debug_parser = @params.debug_parser? line "##### State transition tables begin ###" line integer_list 'racc_action_table', table.action_table line integer_list 'racc_action_check', table.action_check line integer_list 'racc_action_pointer', table.action_pointer line integer_list 'racc_action_default', table.action_default line integer_list 'racc_goto_table', table.goto_table line integer_list 'racc_goto_check', table.goto_check line integer_list 'racc_goto_pointer', table.goto_pointer line integer_list 'racc_goto_default', table.goto_default line i_i_sym_list 'racc_reduce_table', table.reduce_table line line "racc_reduce_n = #{table.reduce_n}" line line "racc_shift_n = #{table.shift_n}" line sym_int_hash 'racc_token_table', table.token_table line line "racc_nt_base = #{table.nt_base}" line line "racc_use_result_var = #{table.use_result_var}" line @f.print(unindent_auto(<<-End)) Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] End line string_list 'Racc_token_to_s_table', table.token_to_s_table line line "Racc_debug_parser = #{table.debug_parser}" line line '##### State transition tables end #####' actions end def integer_list(name, table) sep = '' line "#{name} = [" table.each_slice(10) do |ns| @f.print sep; sep = ",\n" @f.print ns.map {|n| sprintf('%6s', n ? n.to_s : 'nil') }.join(',') end line ' ]' end def i_i_sym_list(name, table) sep = '' line "#{name} = [" table.each_slice(3) do |len, target, mid| @f.print sep; sep = ",\n" @f.printf ' %d, %d, %s', len, target, mid.inspect end line " ]" end def sym_int_hash(name, h) sep = "\n" @f.print "#{name} = {" h.to_a.sort_by {|sym, i| i }.each do |sym, i| @f.print sep; sep = ",\n" @f.printf " %s => %d", sym.serialize, i end line " }" end def string_list(name, list) sep = " " line "#{name} = [" list.each do |s| @f.print sep; sep = ",\n " @f.print s.dump end line ' ]' end def actions @grammar.each do |rule| unless rule.action.source? raise "racc: fatal: cannot generate parser file when any action is a Proc" end end if @params.result_var? decl = ', result' retval = "\n result" default_body = '' else decl = '' retval = '' default_body = 'val[0]' end @grammar.each do |rule| line if rule.action.empty? and @params.omit_action_call? line "# reduce #{rule.ident} omitted" else src0 = rule.action.source || SourceText.new(default_body, __FILE__, 0) if @params.convert_line? src = remove_blank_lines(src0) delim = make_delimiter(src.text) @f.printf unindent_auto(<<-End), module_eval(<<'%s', '%s', %d) def _reduce_%d(val, _values%s) %s%s end %s End delim, src.filename, src.lineno - 1, rule.ident, decl, src.text, retval, delim else src = remove_blank_lines(src0) @f.printf unindent_auto(<<-End), def _reduce_%d(val, _values%s) %s%s end End rule.ident, decl, src.text, retval end end end line @f.printf unindent_auto(<<-'End'), decl def _reduce_none(val, _values%s) val[0] end End line end def remove_blank_lines(src) body = src.text.dup line = src.lineno while body.slice!(/\A[ \t\f]*(?:\n|\r\n|\r)/) line += 1 end SourceText.new(body, src.filename, line) end def make_delimiter(body) delim = '.,.,' while body.index(delim) delim *= 2 end delim end def unindent_auto(str) lines = str.lines.to_a n = minimum_indent(lines) lines.map {|line| detab(line).sub(indent_re(n), '').rstrip + "\n" }.join('') end def minimum_indent(lines) lines.map {|line| n_indent(line) }.min end def n_indent(line) line.slice(/\A\s+/).size end RE_CACHE = {} def indent_re(n) RE_CACHE[n] ||= /\A {#{n}}/ end def detab(str, ts = 8) add = 0 len = nil str.gsub(/\t/) { len = ts - ($`.size + add) % ts add += len - 1 ' ' * len } end end end racc-1.6.2/lib/racc/sourcetext.rb000066400000000000000000000010331435117461300166420ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ module Racc class SourceText def initialize(text, filename, lineno) @text = text @filename = filename @lineno = lineno end attr_reader :text attr_reader :filename attr_reader :lineno def to_s "#" end def location "#{@filename}:#{@lineno}" end end end racc-1.6.2/lib/racc/state.rb000066400000000000000000000474711435117461300155750ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ require 'racc/iset' require 'racc/statetransitiontable' require 'racc/exception' require 'forwardable' module Racc # A table of LALR states. class States include Enumerable def initialize(grammar, debug_flags = DebugFlags.new) @grammar = grammar @symboltable = grammar.symboltable @d_state = debug_flags.state @d_la = debug_flags.la @d_prec = debug_flags.prec @states = [] @statecache = {} @actions = ActionTable.new(@grammar, self) @nfa_computed = false @dfa_computed = false end attr_reader :grammar attr_reader :actions def size @states.size end def inspect '#' end alias to_s inspect def [](i) @states[i] end def each_state(&block) @states.each(&block) end alias each each_state def each_index(&block) @states.each_index(&block) end extend Forwardable def_delegator "@actions", :shift_n def_delegator "@actions", :reduce_n def_delegator "@actions", :nt_base def should_report_srconflict? srconflict_exist? and (n_srconflicts() != @grammar.n_expected_srconflicts) end def srconflict_exist? n_srconflicts() != 0 end def n_srconflicts @n_srconflicts ||= inject(0) {|sum, st| sum + st.n_srconflicts } end def rrconflict_exist? n_rrconflicts() != 0 end def n_rrconflicts @n_rrconflicts ||= inject(0) {|sum, st| sum + st.n_rrconflicts } end def state_transition_table @state_transition_table ||= StateTransitionTable.generate(self.dfa) end # # NFA (Non-deterministic Finite Automaton) Computation # public def nfa return self if @nfa_computed compute_nfa @nfa_computed = true self end private def compute_nfa @grammar.init # add state 0 core_to_state [ @grammar[0].ptrs[0] ] # generate LALR states cur = 0 @gotos = [] while cur < @states.size generate_states @states[cur] # state is added here cur += 1 end @actions.init end def generate_states(state) puts "dstate: #{state}" if @d_state table = {} state.closure.each do |ptr| if sym = ptr.dereference addsym table, sym, ptr.next end end table.each do |sym, core| puts "dstate: sym=#{sym} ncore=#{core}" if @d_state dest = core_to_state(core.to_a) state.goto_table[sym] = dest id = sym.nonterminal?() ? @gotos.size : nil g = Goto.new(id, sym, state, dest) @gotos.push g if sym.nonterminal? state.gotos[sym] = g puts "dstate: #{state.ident} --#{sym}--> #{dest.ident}" if @d_state # check infinite recursion if state.ident == dest.ident and state.closure.size == 1 raise CompileError, sprintf("Infinite recursion: state %d, with rule %d", state.ident, state.ptrs[0].rule.ident) end end end def addsym(table, sym, ptr) unless s = table[sym] table[sym] = s = ISet.new end s.add ptr end def core_to_state(core) # # convert CORE to a State object. # If matching state does not exist, create it and add to the table. # k = fingerprint(core) unless dest = @statecache[k] # not registered yet dest = State.new(@states.size, core) @states.push dest @statecache[k] = dest puts "core_to_state: create state ID #{dest.ident}" if @d_state else if @d_state puts "core_to_state: dest is cached ID #{dest.ident}" puts "core_to_state: dest core #{dest.core.join(' ')}" end end dest end def fingerprint(arr) arr.map {|i| i.ident }.pack('L*') end # # DFA (Deterministic Finite Automaton) Generation # public def dfa return self if @dfa_computed nfa compute_dfa @dfa_computed = true self end private def compute_dfa la = lookahead() @states.each do |state| state.la = la resolve state end set_accept @states.each do |state| pack state end check_useless end def lookahead # # lookahead algorithm ver.3 -- from bison 1.26 # gotos = @gotos if @d_la puts "\n--- goto ---" gotos.each_with_index {|g, i| print i, ' '; p g } end ### initialize_LA() ### set_goto_map() la_rules = [] @states.each do |state| state.check_la la_rules end ### initialize_F() f = create_tmap(gotos.size) reads = [] edge = [] gotos.each do |goto| goto.to_state.goto_table.each do |t, st| if t.terminal? f[goto.ident] |= (1 << t.ident) elsif t.nullable? edge.push goto.to_state.gotos[t].ident end end if edge.empty? reads.push nil else reads.push edge edge = [] end end digraph f, reads if @d_la puts "\n--- F1 (reads) ---" print_tab gotos, reads, f end ### build_relations() ### compute_FOLLOWS path = nil edge = [] lookback = Array.new(la_rules.size, nil) includes = [] gotos.each do |goto| goto.symbol.heads.each do |ptr| path = record_path(goto.from_state, ptr.rule) lastgoto = path.last st = lastgoto ? lastgoto.to_state : goto.from_state if st.conflict? addrel lookback, st.rruleid(ptr.rule), goto end path.reverse_each do |g| break if g.symbol.terminal? edge.push g.ident break unless g.symbol.nullable? end end if edge.empty? includes.push nil else includes.push edge edge = [] end end includes = transpose(includes) digraph f, includes if @d_la puts "\n--- F2 (includes) ---" print_tab gotos, includes, f end ### compute_lookaheads la = create_tmap(la_rules.size) lookback.each_with_index do |arr, i| if arr arr.each do |g| la[i] |= f[g.ident] end end end if @d_la puts "\n--- LA (lookback) ---" print_tab la_rules, lookback, la end la end def create_tmap(size) Array.new(size, 0) # use Integer as bitmap end def addrel(tbl, i, item) if a = tbl[i] a.push item else tbl[i] = [item] end end def record_path(begst, rule) st = begst path = [] rule.symbols.each do |t| goto = st.gotos[t] path.push goto st = goto.to_state end path end def transpose(rel) new = Array.new(rel.size, nil) rel.each_with_index do |arr, idx| if arr arr.each do |i| addrel new, i, idx end end end new end def digraph(map, relation) n = relation.size index = Array.new(n, nil) vertices = [] @infinity = n + 2 index.each_index do |i| if not index[i] and relation[i] traverse i, index, vertices, map, relation end end end def traverse(i, index, vertices, map, relation) vertices.push i index[i] = height = vertices.size if rp = relation[i] rp.each do |proci| unless index[proci] traverse proci, index, vertices, map, relation end if index[i] > index[proci] # circulative recursion !!! index[i] = index[proci] end map[i] |= map[proci] end end if index[i] == height while true proci = vertices.pop index[proci] = @infinity break if i == proci map[proci] |= map[i] end end end # for debug def print_atab(idx, tab) tab.each_with_index do |i,ii| printf '%-20s', idx[ii].inspect p i end end def print_tab(idx, rel, tab) tab.each_with_index do |bin,i| print i, ' ', idx[i].inspect, ' << '; p rel[i] print ' ' each_t(@symboltable, bin) {|t| print ' ', t } puts end end # for debug def print_tab_i(idx, rel, tab, i) bin = tab[i] print i, ' ', idx[i].inspect, ' << '; p rel[i] print ' ' each_t(@symboltable, bin) {|t| print ' ', t } end # for debug def printb(i) each_t(@symboltable, i) do |t| print t, ' ' end puts end def each_t(tbl, set) 0.upto( set.size ) do |i| (0..7).each do |ii| if set[idx = i * 8 + ii] == 1 yield tbl[idx] end end end end # # resolve # def resolve(state) if state.conflict? resolve_rr state, state.ritems resolve_sr state, state.stokens else if state.rrules.empty? # shift state.stokens.each do |t| state.action[t] = @actions.shift(state.goto_table[t]) end else # reduce state.defact = @actions.reduce(state.rrules[0]) end end end def resolve_rr(state, r) r.each do |item| item.each_la(@symboltable) do |t| act = state.action[t] if act unless act.kind_of?(Reduce) raise "racc: fatal: #{act.class} in action table" end # Cannot resolve R/R conflict (on t). # Reduce with upper rule as default. state.rr_conflict act.rule, item.rule, t else # No conflict. state.action[t] = @actions.reduce(item.rule) end end end end def resolve_sr(state, s) s.each do |stok| goto = state.goto_table[stok] act = state.action[stok] unless act # no conflict state.action[stok] = @actions.shift(goto) else unless act.kind_of?(Reduce) puts 'DEBUG -------------------------------' p stok p act state.action.each do |k,v| print k.inspect, ' ', v.inspect, "\n" end raise "racc: fatal: #{act.class} in action table" end # conflict on stok rtok = act.rule.precedence case do_resolve_sr(stok, rtok) when :Reduce # action is already set when :Shift # overwrite act.decref state.action[stok] = @actions.shift(goto) when :Error act.decref state.action[stok] = @actions.error when :CantResolve # shift as default act.decref state.action[stok] = @actions.shift(goto) state.sr_conflict stok, act.rule end end end end ASSOC = { :Left => :Reduce, :Right => :Shift, :Nonassoc => :Error } def do_resolve_sr(stok, rtok) puts "resolve_sr: s/r conflict: rtok=#{rtok}, stok=#{stok}" if @d_prec unless rtok and rtok.precedence puts "resolve_sr: no prec for #{rtok}(R)" if @d_prec return :CantResolve end rprec = rtok.precedence unless stok and stok.precedence puts "resolve_sr: no prec for #{stok}(S)" if @d_prec return :CantResolve end sprec = stok.precedence ret = if rprec == sprec ASSOC[rtok.assoc] or raise "racc: fatal: #{rtok}.assoc is not Left/Right/Nonassoc" else (rprec > sprec) ? (:Reduce) : (:Shift) end puts "resolve_sr: resolved as #{ret.id2name}" if @d_prec ret end # # complete # def set_accept anch = @symboltable.anchor init_state = @states[0].goto_table[@grammar.start] targ_state = init_state.action[anch].goto_state acc_state = targ_state.action[anch].goto_state acc_state.action.clear acc_state.goto_table.clear acc_state.defact = @actions.accept end def pack(state) ### find most frequently used reduce rule act = state.action arr = Array.new(@grammar.size, 0) act.each do |t, a| arr[a.ruleid] += 1 if a.kind_of?(Reduce) end i = arr.max s = (i > 0) ? arr.index(i) : nil ### set & delete default action if s r = @actions.reduce(s) if not state.defact or state.defact == r act.delete_if {|t, a| a == r } state.defact = r end else state.defact ||= @actions.error end end def check_useless used = [] @actions.each_reduce do |act| if not act or act.refn == 0 act.rule.useless = true else t = act.rule.target used[t.ident] = t end end @symboltable.nt_base.upto(@symboltable.nt_max - 1) do |n| unless used[n] @symboltable[n].useless = true end end end end # class StateTable # A LALR state. class State def initialize(ident, core) @ident = ident @core = core @goto_table = {} @gotos = {} @stokens = nil @ritems = nil @action = {} @defact = nil @rrconf = nil @srconf = nil @closure = make_closure(@core) end attr_reader :ident alias stateid ident alias hash ident attr_reader :core attr_reader :closure attr_reader :goto_table attr_reader :gotos attr_reader :stokens attr_reader :ritems attr_reader :rrules attr_reader :action attr_accessor :defact # default action attr_reader :rrconf attr_reader :srconf def inspect "" end alias to_s inspect def ==(oth) @ident == oth.ident end alias eql? == def make_closure(core) set = ISet.new core.each do |ptr| set.add ptr if t = ptr.dereference and t.nonterminal? set.update_a t.expand end end set.to_a end def check_la(la_rules) @conflict = false s = [] r = [] @closure.each do |ptr| if t = ptr.dereference if t.terminal? s[t.ident] = t if t.ident == 1 # $error @conflict = true end end else r.push ptr.rule end end unless r.empty? if not s.empty? or r.size > 1 @conflict = true end end s.compact! @stokens = s @rrules = r if @conflict @la_rules_i = la_rules.size @la_rules = r.map {|i| i.ident } la_rules.concat r else @la_rules_i = @la_rules = nil end end def conflict? @conflict end def rruleid(rule) if i = @la_rules.index(rule.ident) @la_rules_i + i else puts '/// rruleid' p self p rule p @rrules p @la_rules_i raise 'racc: fatal: cannot get reduce rule id' end end def la=(la) return unless @conflict i = @la_rules_i @ritems = r = [] @rrules.each do |rule| r.push Item.new(rule, la[i]) i += 1 end end def rr_conflict(high, low, ctok) c = RRconflict.new(@ident, high, low, ctok) @rrconf ||= {} if a = @rrconf[ctok] a.push c else @rrconf[ctok] = [c] end end def sr_conflict(shift, reduce) c = SRconflict.new(@ident, shift, reduce) @srconf ||= {} if a = @srconf[shift] a.push c else @srconf[shift] = [c] end end def n_srconflicts @srconf ? @srconf.size : 0 end def n_rrconflicts @rrconf ? @rrconf.size : 0 end end # class State # # Represents a transition on the grammar. # "Real goto" means a transition by nonterminal, # but this class treats also terminal's. # If one is a terminal transition, .ident returns nil. # class Goto def initialize(ident, sym, from, to) @ident = ident @symbol = sym @from_state = from @to_state = to end attr_reader :ident attr_reader :symbol attr_reader :from_state attr_reader :to_state def inspect "(#{@from_state.ident}-#{@symbol}->#{@to_state.ident})" end end # LALR item. A set of rule and its lookahead tokens. class Item def initialize(rule, la) @rule = rule @la = la end attr_reader :rule attr_reader :la def each_la(tbl) la = @la 0.upto(la.size - 1) do |i| (0..7).each do |ii| if la[idx = i * 8 + ii] == 1 yield tbl[idx] end end end end end # The table of LALR actions. Actions are either of # Shift, Reduce, Accept and Error. class ActionTable def initialize(rt, st) @grammar = rt @statetable = st @reduce = [] @shift = [] @accept = nil @error = nil end def init @grammar.each do |rule| @reduce.push Reduce.new(rule) end @statetable.each do |state| @shift.push Shift.new(state) end @accept = Accept.new @error = Error.new end def reduce_n @reduce.size end def reduce(i) case i when Rule then i = i.ident when Integer then ; else raise "racc: fatal: wrong class #{i.class} for reduce" end r = @reduce[i] or raise "racc: fatal: reduce action #{i.inspect} not exist" r.incref r end def each_reduce(&block) @reduce.each(&block) end def shift_n @shift.size end def shift(i) case i when State then i = i.ident when Integer then ; else raise "racc: fatal: wrong class #{i.class} for shift" end @shift[i] or raise "racc: fatal: shift action #{i} does not exist" end def each_shift(&block) @shift.each(&block) end attr_reader :accept attr_reader :error end class Shift def initialize(goto) @goto_state = goto end attr_reader :goto_state def goto_id @goto_state.ident end def inspect "" end end class Reduce def initialize(rule) @rule = rule @refn = 0 end attr_reader :rule attr_reader :refn def ruleid @rule.ident end def inspect "" end def incref @refn += 1 end def decref @refn -= 1 raise 'racc: fatal: act.refn < 0' if @refn < 0 end end class Accept def inspect "" end end class Error def inspect "" end end class SRconflict def initialize(sid, shift, reduce) @stateid = sid @shift = shift @reduce = reduce end attr_reader :stateid attr_reader :shift attr_reader :reduce def to_s sprintf('state %d: S/R conflict rule %d reduce and shift %s', @stateid, @reduce.ruleid, @shift.to_s) end end class RRconflict def initialize(sid, high, low, tok) @stateid = sid @high_prec = high @low_prec = low @token = tok end attr_reader :stateid attr_reader :high_prec attr_reader :low_prec attr_reader :token def to_s sprintf('state %d: R/R conflict with rule %d and %d on %s', @stateid, @high_prec.ident, @low_prec.ident, @token.to_s) end end end racc-1.6.2/lib/racc/statetransitiontable.rb000066400000000000000000000175211435117461300207110ustar00rootroot00000000000000#-- # # # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # see the file "COPYING". # #++ require 'racc/parser' module Racc StateTransitionTable = Struct.new(:action_table, :action_check, :action_default, :action_pointer, :goto_table, :goto_check, :goto_default, :goto_pointer, :token_table, :reduce_table, :reduce_n, :shift_n, :nt_base, :token_to_s_table, :use_result_var, :debug_parser) class StateTransitionTable # reopen def StateTransitionTable.generate(states) StateTransitionTableGenerator.new(states).generate end def initialize(states) super() @states = states @grammar = states.grammar self.use_result_var = true self.debug_parser = true end attr_reader :states attr_reader :grammar def parser_class ParserClassGenerator.new(@states).generate end def token_value_table h = {} token_table().each do |sym, i| h[sym.value] = i end h end end class StateTransitionTableGenerator def initialize(states) @states = states @grammar = states.grammar end def generate t = StateTransitionTable.new(@states) gen_action_tables t, @states gen_goto_tables t, @grammar t.token_table = token_table(@grammar) t.reduce_table = reduce_table(@grammar) t.reduce_n = @states.reduce_n t.shift_n = @states.shift_n t.nt_base = @grammar.nonterminal_base t.token_to_s_table = @grammar.symbols.map {|sym| sym.to_s } t end def reduce_table(grammar) t = [0, 0, :racc_error] grammar.each_with_index do |rule, idx| next if idx == 0 t.push rule.size t.push rule.target.ident t.push(if rule.action.empty? # and @params.omit_action_call? then :_reduce_none else "_reduce_#{idx}".intern end) end t end def token_table(grammar) h = {} grammar.symboltable.terminals.each do |t| h[t] = t.ident end h end def gen_action_tables(t, states) t.action_table = yytable = [] t.action_check = yycheck = [] t.action_default = yydefact = [] t.action_pointer = yypact = [] e1 = [] e2 = [] states.each do |state| yydefact.push act2actid(state.defact) if state.action.empty? yypact.push nil next end vector = [] state.action.each do |tok, act| vector[tok.ident] = act2actid(act) end addent e1, vector, state.ident, yypact end set_table e1, e2, yytable, yycheck, yypact end def gen_goto_tables(t, grammar) t.goto_table = yytable2 = [] t.goto_check = yycheck2 = [] t.goto_pointer = yypgoto = [] t.goto_default = yydefgoto = [] e1 = [] e2 = [] grammar.each_nonterminal do |tok| tmp = [] # decide default freq = Array.new(@states.size, 0) @states.each do |state| st = state.goto_table[tok] if st st = st.ident freq[st] += 1 end tmp[state.ident] = st end max = freq.max if max > 1 default = freq.index(max) tmp.map! {|i| default == i ? nil : i } else default = nil end yydefgoto.push default # delete default value tmp.pop until tmp.last or tmp.empty? if tmp.compact.empty? # only default yypgoto.push nil next end addent e1, tmp, (tok.ident - grammar.nonterminal_base), yypgoto end set_table e1, e2, yytable2, yycheck2, yypgoto end def addent(all, arr, chkval, ptr) max = arr.size min = nil arr.each_with_index do |item, idx| if item min ||= idx end end ptr.push(-7777) # mark arr = arr[min...max] all.push [arr, chkval, mkmapexp(arr), min, ptr.size - 1] end n = 2 ** 16 begin Regexp.compile("a{#{n}}") RE_DUP_MAX = n rescue RegexpError n /= 2 retry end def mkmapexp(arr) i = ii = 0 as = arr.size map = String.new maxdup = RE_DUP_MAX curr = nil while i < as ii = i + 1 if arr[i] ii += 1 while ii < as and arr[ii] curr = '-' else ii += 1 while ii < as and not arr[ii] curr = '.' end offset = ii - i if offset == 1 map << curr else while offset > maxdup map << "#{curr}{#{maxdup}}" offset -= maxdup end map << "#{curr}{#{offset}}" if offset > 1 end i = ii end Regexp.compile(map, Regexp::NOENCODING) end def set_table(entries, dummy, tbl, chk, ptr) upper = 0 map = '-' * 10240 # sort long to short entries.sort_by!.with_index {|a,i| [-a[0].size, i] } entries.each do |arr, chkval, expr, min, ptri| if upper + arr.size > map.size map << '-' * (arr.size + 1024) end idx = map.index(expr) ptr[ptri] = idx - min arr.each_with_index do |item, i| if item i += idx tbl[i] = item chk[i] = chkval map[i] = ?o end end upper = idx + arr.size end end def act2actid(act) case act when Shift then act.goto_id when Reduce then -act.ruleid when Accept then @states.shift_n when Error then @states.reduce_n * -1 else raise "racc: fatal: wrong act type #{act.class} in action table" end end end class ParserClassGenerator def initialize(states) @states = states @grammar = states.grammar end def generate table = @states.state_transition_table c = Class.new(::Racc::Parser) c.const_set :Racc_arg, [table.action_table, table.action_check, table.action_default, table.action_pointer, table.goto_table, table.goto_check, table.goto_default, table.goto_pointer, table.nt_base, table.reduce_table, table.token_value_table, table.shift_n, table.reduce_n, false] c.const_set :Racc_token_to_s_table, table.token_to_s_table c.const_set :Racc_debug_parser, true define_actions c c end private def define_actions(c) c.module_eval "def _reduce_none(vals, vstack) vals[0] end" @grammar.each do |rule| if rule.action.empty? c.alias_method("_reduce_#{rule.ident}", :_reduce_none) else c.define_method("_racc_action_#{rule.ident}", &rule.action.proc) c.module_eval(<<-End, __FILE__, __LINE__ + 1) def _reduce_#{rule.ident}(vals, vstack) _racc_action_#{rule.ident}(*vals) end End end end end end end # module Racc racc-1.6.2/lib/racc/static.rb000066400000000000000000000002111435117461300157210ustar00rootroot00000000000000require 'racc' require 'racc/parser' require 'racc/grammarfileparser' require 'racc/parserfilegenerator' require 'racc/logfilegenerator' racc-1.6.2/racc.gemspec000066400000000000000000000037071435117461300147210ustar00rootroot00000000000000# -*- encoding: utf-8 -*- begin require_relative "lib/racc/info" rescue LoadError # Fallback to load version file in ruby core repository require_relative "info" end Gem::Specification.new do |s| s.name = "racc" s.version = Racc::VERSION s.summary = "Racc is a LALR(1) parser generator" s.description = <>> '; $stdout.flush str = $stdin.gets.strip break if /q/i =~ str begin p calc.evaluate(str) rescue ParseError puts 'parse error' end end racc-1.6.2/sample/calc.y000066400000000000000000000017551435117461300150220ustar00rootroot00000000000000# # # Very simple calculator. class Calcp prechigh nonassoc UMINUS left '*' '/' left '+' '-' preclow rule target: exp | /* none */ { result = 0 } exp: exp '+' exp { result += val[2] } | exp '-' exp { result -= val[2] } | exp '*' exp { result *= val[2] } | exp '/' exp { result /= val[2] } | '(' exp ')' { result = val[1] } | '-' NUMBER =UMINUS { result = -val[1] } | NUMBER end ---- header # ---- inner def parse(str) @q = [] until str.empty? case str when /\A\s+/ when /\A\d+/ @q.push [:NUMBER, $&.to_i] when /\A.|\n/o s = $& @q.push [s, s] end str = $' end @q.push [false, '$end'] do_parse end def next_token @q.shift end ---- footer parser = Calcp.new puts puts 'type "Q" to quit.' puts while true puts print '? ' str = gets.chop! break if /q/i =~ str begin puts "= #{parser.parse(str)}" rescue ParseError puts $! end end racc-1.6.2/sample/conflict.y000066400000000000000000000003331435117461300157100ustar00rootroot00000000000000# # # Example of conflicted grammar. # This grammar contains 1 Shift/Reduce conflict and 1 Reduce/Reduce conflict. class A rule target : outer outer : | outer inner inner : | inner ITEM end racc-1.6.2/sample/hash.y000066400000000000000000000020301435117461300150260ustar00rootroot00000000000000# # # Converting Hash-like string into Ruby's Hash. class HashParser options no_result_var rule hash : '{' contents '}' { val[1] } | '{' '}' { Hash.new } # Racc can handle string over 2 bytes. contents: IDENT '=>' IDENT { {val[0] => val[2]} } | contents ',' IDENT '=>' IDENT { val[0][val[2]] = val[4]; val[0] } end ---- inner def parse(str) @str = str yyparse self, :scan end private def scan str = @str until str.empty? case str when /\A\s+/ str = $' when /\A\w+/ yield :IDENT, $& str = $' when /\A=>/ yield '=>', '=>' str = $' else c = str[0,1] yield c, c str = str[1..-1] end end yield false, '$' # is optional from Racc 1.3.7 end ---- footer if $0 == __FILE__ src = < MyName, id => MyIdent } EOS puts 'Parsing (String):' print src puts puts 'Result (Ruby Object):' p HashParser.new.parse(src) end racc-1.6.2/sample/lalr.y000066400000000000000000000002171435117461300150420ustar00rootroot00000000000000# # # This is LALR grammar, and not LL/SLR. class A rule A : L '=' E L : i | R '^' i E : E '+' R | R | '@' L R : i end racc-1.6.2/sample/lists.y000066400000000000000000000020021435117461300152400ustar00rootroot00000000000000# # # Rules for verious lists. # This file is just an example, you cannot compile this file. class ExampleOnly rule # a list, which minimum number of items is 0 list0 : { result = [] } | list0 item { result.push val[1] } # a list, which minimum number of items is 1 list1 : item { result = val } | list1 item { result.push val[1] } # a list, separated by comma clist : item { result = val } | clist ',' item { result.push val[2] } # a hash. see also "hash.y". hash : '{' hash_contents '}' { result = val[1] } | '{' '}' { result = {} } hash_contents : item "=>" item { result = { val[0] => val[2] } } | hash_contents ',' item "=>" item { result[val[2]] = val[4] } racc-1.6.2/sample/syntax.y000066400000000000000000000012331435117461300154350ustar00rootroot00000000000000# # # Racc syntax checker. This grammar file generates # invalid ruby program, you cannot run this parser. class P token A B C convert A '5' end prechigh left B preclow options omit_action_call start target rule target: A B C { print 'abc' } | B C A | C B A { print 'cba' } | cont cont : A c2 B c2 C c2 : C C C C C end ---- inner junk code !!!! kjaljlajrlaolanbla /// %%% (*((( token rule akiurtlajluealjflaj @@@@ end end end end __END__ laieu2o879urkq96ga(Q#*&%Q# #&lkji END q395q?/// liutjqlkr7 racc-1.6.2/sample/yyerr.y000066400000000000000000000007361435117461300152700ustar00rootroot00000000000000# # # Test grammar file for error handling. class A rule target: a b c a : { yyerror raise ArgumentError, "yyerror failed" } | error b : { yyerrok } c : { yyaccept raise "yyaccept failed" } end ---- inner def parse do_parse end def next_token [false, '$end'] end def on_error(*args) $stderr.puts "on_error called: args=#{args.inspect}" end ---- footer A.new.parse racc-1.6.2/test/000077500000000000000000000000001435117461300134145ustar00rootroot00000000000000racc-1.6.2/test/assets/000077500000000000000000000000001435117461300147165ustar00rootroot00000000000000racc-1.6.2/test/assets/cadenza.y000066400000000000000000000141531435117461300165210ustar00rootroot00000000000000# This grammar is released under an MIT license # Author: William Howard (http://github.com/whoward) # Source: https://github.com/whoward/cadenza/blob/master/src/cadenza.y class Cadenza::RaccParser /* expect this many shift/reduce conflicts */ expect 37 rule target : document | /* none */ { result = nil } ; parameter_list : logical_expression { result = [val[0]] } | parameter_list ',' logical_expression { result = val[0].push(val[2]) } ; /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */ primary_expression : IDENTIFIER { result = VariableNode.new(val[0].value) } | IDENTIFIER parameter_list { result = VariableNode.new(val[0].value, val[1]) } | INTEGER { result = ConstantNode.new(val[0].value) } | REAL { result = ConstantNode.new(val[0].value) } | STRING { result = ConstantNode.new(val[0].value) } | '(' filtered_expression ')' { result = val[1] } ; multiplicative_expression : primary_expression | multiplicative_expression '*' primary_expression { result = OperationNode.new(val[0], "*", val[2]) } | multiplicative_expression '/' primary_expression { result = OperationNode.new(val[0], "/", val[2]) } ; additive_expression : multiplicative_expression | additive_expression '+' multiplicative_expression { result = OperationNode.new(val[0], "+", val[2]) } | additive_expression '-' multiplicative_expression { result = OperationNode.new(val[0], "-", val[2]) } ; boolean_expression : additive_expression | boolean_expression OP_EQ additive_expression { result = OperationNode.new(val[0], "==", val[2]) } | boolean_expression OP_NEQ additive_expression { result = OperationNode.new(val[0], "!=", val[2]) } | boolean_expression OP_LEQ additive_expression { result = OperationNode.new(val[0], "<=", val[2]) } | boolean_expression OP_GEQ additive_expression { result = OperationNode.new(val[0], ">=", val[2]) } | boolean_expression '>' additive_expression { result = OperationNode.new(val[0], ">", val[2]) } | boolean_expression '<' additive_expression { result = OperationNode.new(val[0], "<", val[2]) } ; inverse_expression : boolean_expression | NOT boolean_expression { result = BooleanInverseNode.new(val[1]) } ; logical_expression : inverse_expression | logical_expression AND inverse_expression { result = OperationNode.new(val[0], "and", val[2]) } | logical_expression OR inverse_expression { result = OperationNode.new(val[0], "or", val[2]) } ; filter : IDENTIFIER { result = FilterNode.new(val[0].value) } | IDENTIFIER ':' parameter_list { result = FilterNode.new(val[0].value, val[2]) } ; filter_list : filter { result = [val[0]] } | filter_list '|' filter { result = val[0].push(val[2]) } ; filtered_expression : logical_expression | logical_expression '|' filter_list { result = FilteredValueNode.new(val[0], val[2]) } ; inject_statement : VAR_OPEN filtered_expression VAR_CLOSE { result = val[1] } ; if_tag : STMT_OPEN IF logical_expression STMT_CLOSE { open_scope!; result = val[2] } | STMT_OPEN UNLESS logical_expression STMT_CLOSE { open_scope!; result = BooleanInverseNode.new(val[2]) } ; else_tag : STMT_OPEN ELSE STMT_CLOSE { result = close_scope!; open_scope! } ; end_if_tag : STMT_OPEN ENDIF STMT_CLOSE { result = close_scope! } | STMT_OPEN ENDUNLESS STMT_CLOSE { result = close_scope! } ; if_block : if_tag end_if_tag { result = IfNode.new(val[0], val[1]) } | if_tag document end_if_tag { result = IfNode.new(val[0], val[2]) } | if_tag else_tag document end_if_tag { result = IfNode.new(val[0], val[1], val[3]) } | if_tag document else_tag end_if_tag { result = IfNode.new(val[0], val[2], val[3]) } | if_tag document else_tag document end_if_tag { result = IfNode.new(val[0], val[2], val[4]) } ; for_tag : STMT_OPEN FOR IDENTIFIER IN filtered_expression STMT_CLOSE { open_scope!; result = [val[2].value, val[4]] } ; end_for_tag : STMT_OPEN ENDFOR STMT_CLOSE { result = close_scope! } ; /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */ for_block : for_tag end_for_tag { result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[1]) } | for_tag document end_for_tag { result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[2]) } ; block_tag : STMT_OPEN BLOCK IDENTIFIER STMT_CLOSE { result = open_block_scope!(val[2].value) } ; end_block_tag : STMT_OPEN ENDBLOCK STMT_CLOSE { result = close_block_scope! } ; /* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */ block_block : block_tag end_block_tag { result = BlockNode.new(val[0], val[1]) } | block_tag document end_block_tag { result = BlockNode.new(val[0], val[2]) } ; generic_block_tag : STMT_OPEN IDENTIFIER STMT_CLOSE { open_scope!; result = [val[1].value, []] } | STMT_OPEN IDENTIFIER parameter_list STMT_CLOSE { open_scope!; result = [val[1].value, val[2]] } ; end_generic_block_tag : STMT_OPEN END STMT_CLOSE { result = close_scope! } ; generic_block : generic_block_tag document end_generic_block_tag { result = GenericBlockNode.new(val[0].first, val[2], val[0].last) } ; extends_statement : STMT_OPEN EXTENDS STRING STMT_CLOSE { result = val[2].value } | STMT_OPEN EXTENDS IDENTIFIER STMT_CLOSE { result = VariableNode.new(val[2].value) } ; document_component : TEXT_BLOCK { result = TextNode.new(val[0].value) } | inject_statement | if_block | for_block | generic_block | block_block ; document : document_component { push val[0] } | document document_component { push val[1] } | extends_statement { document.extends = val[0] } | document extends_statement { document.extends = val[1] } ; ---- header ---- # racc_parser.rb : generated by racc ---- inner ---- racc-1.6.2/test/assets/cast.y000066400000000000000000001210401435117461300160400ustar00rootroot00000000000000# The MIT License # # Copyright (c) George Ogata # # 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. class C::Parser # shift/reduce conflict on "if (c) if (c) ; else ; else ;" expect 1 rule # A.2.4 External definitions # Returns TranslationUnit translation_unit : external_declaration {result = TranslationUnit.new_at(val[0].pos, NodeChain[val[0]])} | translation_unit external_declaration {result = val[0]; result.entities << val[1]} # Returns Declaration|FunctionDef external_declaration : function_definition {result = val[0]} | declaration {result = val[0]} # Returns FunctionDef function_definition : declaration_specifiers declarator declaration_list compound_statement {result = make_function_def(val[0][0], val[0][1], val[1], val[2], val[3])} | declaration_specifiers declarator compound_statement {result = make_function_def(val[0][0], val[0][1], val[1], nil , val[2])} # Returns [Declaration] declaration_list : declaration {result = [val[0]]} | declaration_list declaration {result = val[0] << val[1]} # A.2.3 Statements # Returns Statement statement : labeled_statement {result = val[0]} | compound_statement {result = val[0]} | expression_statement {result = val[0]} | selection_statement {result = val[0]} | iteration_statement {result = val[0]} | jump_statement {result = val[0]} # Returns Statement labeled_statement : identifier COLON statement {val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].val)); result = val[2]} | CASE constant_expression COLON statement {val[3].labels.unshift(Case .new_at(val[0].pos, val[1] )); result = val[3]} | DEFAULT COLON statement {val[2].labels.unshift(Default .new_at(val[0].pos )); result = val[2]} # type names can also be used as labels | typedef_name COLON statement {val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].name)); result = val[2]} # Returns Block compound_statement : LBRACE block_item_list RBRACE {result = Block.new_at(val[0].pos, val[1])} | LBRACE RBRACE {result = Block.new_at(val[0].pos )} # Returns NodeChain[Declaration|Statement] block_item_list : block_item {result = NodeChain[val[0]]} | block_item_list block_item {result = val[0] << val[1]} # Returns Declaration|Statement block_item : declaration {result = val[0]} | statement {result = val[0]} # Returns ExpressionStatement expression_statement : expression SEMICOLON {result = ExpressionStatement.new_at(val[0].pos, val[0])} | SEMICOLON {result = ExpressionStatement.new_at(val[0].pos )} # Returns Statement selection_statement : IF LPAREN expression RPAREN statement {result = If .new_at(val[0].pos, val[2], val[4] )} | IF LPAREN expression RPAREN statement ELSE statement {result = If .new_at(val[0].pos, val[2], val[4], val[6])} | SWITCH LPAREN expression RPAREN statement {result = Switch.new_at(val[0].pos, val[2], val[4] )} # Returns Statement iteration_statement : WHILE LPAREN expression RPAREN statement {result = While.new_at(val[0].pos, val[2], val[4] )} | DO statement WHILE LPAREN expression RPAREN SEMICOLON {result = While.new_at(val[0].pos, val[4], val[1], :do => true )} | FOR LPAREN expression SEMICOLON expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], val[4], val[6], val[8])} | FOR LPAREN expression SEMICOLON expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], val[4], nil , val[7])} | FOR LPAREN expression SEMICOLON SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , val[5], val[7])} | FOR LPAREN expression SEMICOLON SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , nil , val[6])} | FOR LPAREN SEMICOLON expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, nil , val[3], val[5], val[7])} | FOR LPAREN SEMICOLON expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, nil , val[3], nil , val[6])} | FOR LPAREN SEMICOLON SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, nil , nil , val[4], val[6])} | FOR LPAREN SEMICOLON SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, nil , nil , nil , val[5])} | FOR LPAREN declaration expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], val[3], val[5], val[7])} | FOR LPAREN declaration expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], val[3], nil , val[6])} | FOR LPAREN declaration SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , val[4], val[6])} | FOR LPAREN declaration SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , nil , val[5])} # Returns Statement jump_statement : GOTO identifier SEMICOLON {result = Goto .new_at(val[0].pos, val[1].val)} | CONTINUE SEMICOLON {result = Continue.new_at(val[0].pos )} | BREAK SEMICOLON {result = Break .new_at(val[0].pos )} | RETURN expression SEMICOLON {result = Return .new_at(val[0].pos, val[1] )} | RETURN SEMICOLON {result = Return .new_at(val[0].pos )} # type names can also be used as labels | GOTO typedef_name SEMICOLON {result = Goto .new_at(val[0].pos, val[1].name)} # A.2.2 Declarations # Returns Declaration declaration : declaration_specifiers init_declarator_list SEMICOLON {result = make_declaration(val[0][0], val[0][1], val[1])} | declaration_specifiers SEMICOLON {result = make_declaration(val[0][0], val[0][1], NodeArray[])} # Returns {Pos, [Symbol]} declaration_specifiers : storage_class_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]} | storage_class_specifier {result = [val[0][0], [val[0][1]]]} | type_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]} | type_specifier {result = [val[0][0], [val[0][1]]]} | type_qualifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]} | type_qualifier {result = [val[0][0], [val[0][1]]]} | function_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]} | function_specifier {result = [val[0][0], [val[0][1]]]} # Returns NodeArray[Declarator] init_declarator_list : init_declarator {result = NodeArray[val[0]]} | init_declarator_list COMMA init_declarator {result = val[0] << val[2]} # Returns Declarator init_declarator : declarator {result = val[0]} | declarator EQ initializer {val[0].init = val[2]; result = val[0]} # Returns [Pos, Symbol] storage_class_specifier : TYPEDEF {result = [val[0].pos, :typedef ]} | EXTERN {result = [val[0].pos, :extern ]} | STATIC {result = [val[0].pos, :static ]} | AUTO {result = [val[0].pos, :auto ]} | REGISTER {result = [val[0].pos, :register]} # Returns [Pos, Type|Symbol] type_specifier : VOID {result = [val[0].pos, :void ]} | CHAR {result = [val[0].pos, :char ]} | SHORT {result = [val[0].pos, :short ]} | INT {result = [val[0].pos, :int ]} | LONG {result = [val[0].pos, :long ]} | FLOAT {result = [val[0].pos, :float ]} | DOUBLE {result = [val[0].pos, :double ]} | SIGNED {result = [val[0].pos, :signed ]} | UNSIGNED {result = [val[0].pos, :unsigned ]} | BOOL {result = [val[0].pos, :_Bool ]} | COMPLEX {result = [val[0].pos, :_Complex ]} | IMAGINARY {result = [val[0].pos, :_Imaginary]} | struct_or_union_specifier {result = [val[0].pos, val[0] ]} | enum_specifier {result = [val[0].pos, val[0] ]} | typedef_name {result = [val[0].pos, val[0] ]} # Returns Struct|Union struct_or_union_specifier : struct_or_union identifier LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], val[1].val, val[3])} | struct_or_union LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], nil , val[2])} | struct_or_union identifier {result = val[0][1].new_at(val[0][0], val[1].val, nil )} # type names can also be used as struct identifiers | struct_or_union typedef_name LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], val[1].name, val[3])} | struct_or_union typedef_name {result = val[0][1].new_at(val[0][0], val[1].name, nil )} # Returns [Pos, Class] struct_or_union : STRUCT {result = [val[0].pos, Struct]} | UNION {result = [val[0].pos, Union ]} # Returns NodeArray[Declaration] struct_declaration_list : struct_declaration {result = NodeArray[val[0]]} | struct_declaration_list struct_declaration {val[0] << val[1]; result = val[0]} # Returns Declaration struct_declaration : specifier_qualifier_list struct_declarator_list SEMICOLON {result = make_declaration(val[0][0], val[0][1], val[1])} # Returns {Pos, [Symbol]} specifier_qualifier_list : type_specifier specifier_qualifier_list {val[1][1] << val[0][1]; result = val[1]} | type_specifier {result = [val[0][0], [val[0][1]]]} | type_qualifier specifier_qualifier_list {val[1][1] << val[0][1]; result = val[1]} | type_qualifier {result = [val[0][0], [val[0][1]]]} # Returns NodeArray[Declarator] struct_declarator_list : struct_declarator {result = NodeArray[val[0]]} | struct_declarator_list COMMA struct_declarator {result = val[0] << val[2]} # Returns Declarator struct_declarator : declarator {result = val[0]} | declarator COLON constant_expression {result = val[0]; val[0].num_bits = val[2]} | COLON constant_expression {result = Declarator.new_at(val[0].pos, :num_bits => val[1])} # Returns Enum enum_specifier : ENUM identifier LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, val[1].val, val[3])} | ENUM LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, nil , val[2])} | ENUM identifier LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, val[1].val, val[3])} | ENUM LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, nil , val[2])} | ENUM identifier {result = Enum.new_at(val[0].pos, val[1].val, nil )} # type names can also be used as enum names | ENUM typedef_name LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, val[1].name, val[3])} | ENUM typedef_name LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, val[1].name, val[3])} | ENUM typedef_name {result = Enum.new_at(val[0].pos, val[1].name, nil )} # Returns NodeArray[Enumerator] enumerator_list : enumerator {result = NodeArray[val[0]]} | enumerator_list COMMA enumerator {result = val[0] << val[2]} # Returns Enumerator enumerator : enumeration_constant {result = Enumerator.new_at(val[0].pos, val[0].val, nil )} | enumeration_constant EQ constant_expression {result = Enumerator.new_at(val[0].pos, val[0].val, val[2])} # Returns [Pos, Symbol] type_qualifier : CONST {result = [val[0].pos, :const ]} | RESTRICT {result = [val[0].pos, :restrict]} | VOLATILE {result = [val[0].pos, :volatile]} # Returns [Pos, Symbol] function_specifier : INLINE {result = [val[0].pos, :inline]} # Returns Declarator declarator : pointer direct_declarator {result = add_decl_type(val[1], val[0])} | direct_declarator {result = val[0]} # Returns Declarator direct_declarator : identifier {result = Declarator.new_at(val[0].pos, nil, val[0].val)} | LPAREN declarator RPAREN {result = val[1]} | direct_declarator LBRACKET type_qualifier_list assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO | direct_declarator LBRACKET type_qualifier_list RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO | direct_declarator LBRACKET assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos, nil, val[2]))} | direct_declarator LBRACKET RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} | direct_declarator LBRACKET STATIC type_qualifier_list assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO | direct_declarator LBRACKET STATIC assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO | direct_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO | direct_declarator LBRACKET type_qualifier_list MUL RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO | direct_declarator LBRACKET MUL RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO | direct_declarator LPAREN parameter_type_list RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, param_list(*val[2]), :var_args => val[2][1]))} | direct_declarator LPAREN identifier_list RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, val[2]))} | direct_declarator LPAREN RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos ))} # Returns Pointer pointer : MUL type_qualifier_list {result = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]) } | MUL {result = Pointer.new_at(val[0].pos) } | MUL type_qualifier_list pointer {p = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]); val[2].direct_type = p; result = val[2]} | MUL pointer {p = Pointer.new_at(val[0].pos) ; val[1].direct_type = p; result = val[1]} # Returns {Pos, [Symbol]} type_qualifier_list : type_qualifier {result = [val[0][0], [val[0][1]]]} | type_qualifier_list type_qualifier {val[0][1] << val[1][1]; result = val[0]} # Returns [NodeArray[Parameter], var_args?] parameter_type_list : parameter_list {result = [val[0], false]} | parameter_list COMMA ELLIPSIS {result = [val[0], true ]} # Returns NodeArray[Parameter] parameter_list : parameter_declaration {result = NodeArray[val[0]]} | parameter_list COMMA parameter_declaration {result = val[0] << val[2]} # Returns Parameter parameter_declaration : declaration_specifiers declarator {ind_type = val[1].indirect_type and ind_type.detach result = make_parameter(val[0][0], val[0][1], ind_type, val[1].name)} | declaration_specifiers abstract_declarator {result = make_parameter(val[0][0], val[0][1], val[1] , nil )} | declaration_specifiers {result = make_parameter(val[0][0], val[0][1], nil , nil )} # Returns NodeArray[Parameter] identifier_list : identifier {result = NodeArray[Parameter.new_at(val[0].pos, nil, val[0].val)]} | identifier_list COMMA identifier {result = val[0] << Parameter.new_at(val[2].pos, nil, val[2].val)} # Returns Type type_name : specifier_qualifier_list abstract_declarator {val[1].direct_type = make_direct_type(val[0][0], val[0][1]); result = val[1]} | specifier_qualifier_list {result = make_direct_type(val[0][0], val[0][1]) } # Returns Type abstract_declarator : pointer {result = val[0]} | pointer direct_abstract_declarator {val[1].direct_type = val[0]; result = val[1]} | direct_abstract_declarator {result = val[0]} # Returns Type direct_abstract_declarator : LPAREN abstract_declarator RPAREN {result = val[1]} | direct_abstract_declarator LBRACKET assignment_expression RBRACKET {val[0].direct_type = Array.new_at(val[0].pos, nil, val[2]); result = val[0]} | direct_abstract_declarator LBRACKET RBRACKET {val[0].direct_type = Array.new_at(val[0].pos, nil, nil ); result = val[0]} | LBRACKET assignment_expression RBRACKET {result = Array.new_at(val[0].pos, nil, val[1])} | LBRACKET RBRACKET {result = Array.new_at(val[0].pos )} | direct_abstract_declarator LBRACKET MUL RBRACKET {val[0].direct_type = Array.new_at(val[0].pos); result = val[0]} # TODO | LBRACKET MUL RBRACKET {result = Array.new_at(val[0].pos)} # TODO | direct_abstract_declarator LPAREN parameter_type_list RPAREN {val[0].direct_type = Function.new_at(val[0].pos, nil, param_list(*val[2]), val[2][1]); result = val[0]} | direct_abstract_declarator LPAREN RPAREN {val[0].direct_type = Function.new_at(val[0].pos ); result = val[0]} | LPAREN parameter_type_list RPAREN {result = Function.new_at(val[0].pos, nil, param_list(*val[1]), val[1][1])} | LPAREN RPAREN {result = Function.new_at(val[0].pos )} # Returns CustomType typedef_name #: identifier -- insufficient since we must distinguish between type # names and var names (otherwise we have a conflict) : TYPENAME {result = CustomType.new_at(val[0].pos, val[0].val)} # Returns Expression initializer : assignment_expression {result = val[0]} | LBRACE initializer_list RBRACE {result = CompoundLiteral.new_at(val[0].pos, nil, val[1])} | LBRACE initializer_list COMMA RBRACE {result = CompoundLiteral.new_at(val[0].pos, nil, val[1])} # Returns NodeArray[MemberInit] initializer_list : designation initializer {result = NodeArray[MemberInit.new_at(val[0][0] , val[0][1], val[1])]} | initializer {result = NodeArray[MemberInit.new_at(val[0].pos, nil , val[0])]} | initializer_list COMMA designation initializer {result = val[0] << MemberInit.new_at(val[2][0] , val[2][1], val[3])} | initializer_list COMMA initializer {result = val[0] << MemberInit.new_at(val[2].pos, nil , val[2])} # Returns {Pos, NodeArray[Expression|Token]} designation : designator_list EQ {result = val[0]} # Returns {Pos, NodeArray[Expression|Token]} designator_list : designator {result = val[0]; val[0][1] = NodeArray[val[0][1]]} | designator_list designator {result = val[0]; val[0][1] << val[1][1]} # Returns {Pos, Expression|Member} designator : LBRACKET constant_expression RBRACKET {result = [val[1].pos, val[1] ]} | DOT identifier {result = [val[1].pos, Member.new_at(val[1].pos, val[1].val)]} # A.2.1 Expressions # Returns Expression primary_expression : identifier {result = Variable.new_at(val[0].pos, val[0].val)} | constant {result = val[0]} | string_literal {result = val[0]} # GCC EXTENSION: allow a compound statement in parentheses as an expression | LPAREN expression RPAREN {result = val[1]} | LPAREN compound_statement RPAREN {block_expressions_enabled? or parse_error val[0].pos, "compound statement found where expression expected" result = BlockExpression.new(val[1]); result.pos = val[0].pos} # Returns Expression postfix_expression : primary_expression {result = val[0]} | postfix_expression LBRACKET expression RBRACKET {result = Index .new_at(val[0].pos, val[0], val[2])} | postfix_expression LPAREN argument_expression_list RPAREN {result = Call .new_at(val[0].pos, val[0], val[2] )} | postfix_expression LPAREN RPAREN {result = Call .new_at(val[0].pos, val[0], NodeArray[])} | postfix_expression DOT identifier {result = Dot .new_at(val[0].pos, val[0], Member.new(val[2].val))} | postfix_expression ARROW identifier {result = Arrow .new_at(val[0].pos, val[0], Member.new(val[2].val))} | postfix_expression INC {result = PostInc .new_at(val[0].pos, val[0] )} | postfix_expression DEC {result = PostDec .new_at(val[0].pos, val[0] )} | LPAREN type_name RPAREN LBRACE initializer_list RBRACE {result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])} | LPAREN type_name RPAREN LBRACE initializer_list COMMA RBRACE {result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])} # Returns [Expression|Type] argument_expression_list : argument_expression {result = NodeArray[val[0]]} | argument_expression_list COMMA argument_expression {result = val[0] << val[2]} # Returns Expression|Type -- EXTENSION: allow type names here too, to support some standard library macros (e.g., va_arg [7.15.1.1]) argument_expression : assignment_expression {result = val[0]} | type_name {result = val[0]} # Returns Expression unary_expression : postfix_expression {result = val[0]} | INC unary_expression {result = PreInc.new_at(val[0].pos, val[1])} | DEC unary_expression {result = PreDec.new_at(val[0].pos, val[1])} | unary_operator cast_expression {result = val[0][0].new_at(val[0][1], val[1])} | SIZEOF unary_expression {result = Sizeof.new_at(val[0].pos, val[1])} | SIZEOF LPAREN type_name RPAREN {result = Sizeof.new_at(val[0].pos, val[2])} # Returns [Class, Pos] unary_operator : AND {result = [Address , val[0].pos]} | MUL {result = [Dereference, val[0].pos]} | ADD {result = [Positive , val[0].pos]} | SUB {result = [Negative , val[0].pos]} | NOT {result = [BitNot , val[0].pos]} | BANG {result = [Not , val[0].pos]} # Returns Expression cast_expression : unary_expression {result = val[0]} | LPAREN type_name RPAREN cast_expression {result = Cast.new_at(val[0].pos, val[1], val[3])} # Returns Expression multiplicative_expression : cast_expression {result = val[0]} | multiplicative_expression MUL cast_expression {result = Multiply.new_at(val[0].pos, val[0], val[2])} | multiplicative_expression DIV cast_expression {result = Divide .new_at(val[0].pos, val[0], val[2])} | multiplicative_expression MOD cast_expression {result = Mod .new_at(val[0].pos, val[0], val[2])} # Returns Expression additive_expression : multiplicative_expression {result = val[0]} | additive_expression ADD multiplicative_expression {result = Add .new_at(val[0].pos, val[0], val[2])} | additive_expression SUB multiplicative_expression {result = Subtract.new_at(val[0].pos, val[0], val[2])} # Returns Expression shift_expression : additive_expression {result = val[0]} | shift_expression LSHIFT additive_expression {result = ShiftLeft .new_at(val[0].pos, val[0], val[2])} | shift_expression RSHIFT additive_expression {result = ShiftRight.new_at(val[0].pos, val[0], val[2])} # Returns Expression relational_expression : shift_expression {result = val[0]} | relational_expression LT shift_expression {result = Less.new_at(val[0].pos, val[0], val[2])} | relational_expression GT shift_expression {result = More.new_at(val[0].pos, val[0], val[2])} | relational_expression LEQ shift_expression {result = LessOrEqual.new_at(val[0].pos, val[0], val[2])} | relational_expression GEQ shift_expression {result = MoreOrEqual.new_at(val[0].pos, val[0], val[2])} # Returns Expression equality_expression : relational_expression {result = val[0]} | equality_expression EQEQ relational_expression {result = Equal .new_at(val[0].pos, val[0], val[2])} | equality_expression NEQ relational_expression {result = NotEqual.new_at(val[0].pos, val[0], val[2])} # Returns Expression and_expression : equality_expression {result = val[0]} | and_expression AND equality_expression {result = BitAnd.new_at(val[0].pos, val[0], val[2])} # Returns Expression exclusive_or_expression : and_expression {result = val[0]} | exclusive_or_expression XOR and_expression {result = BitXor.new_at(val[0].pos, val[0], val[2])} # Returns Expression inclusive_or_expression : exclusive_or_expression {result = val[0]} | inclusive_or_expression OR exclusive_or_expression {result = BitOr.new_at(val[0].pos, val[0], val[2])} # Returns Expression logical_and_expression : inclusive_or_expression {result = val[0]} | logical_and_expression ANDAND inclusive_or_expression {result = And.new_at(val[0].pos, val[0], val[2])} # Returns Expression logical_or_expression : logical_and_expression {result = val[0]} | logical_or_expression OROR logical_and_expression {result = Or.new_at(val[0].pos, val[0], val[2])} # Returns Expression conditional_expression : logical_or_expression {result = val[0]} | logical_or_expression QUESTION expression COLON conditional_expression {result = Conditional.new_at(val[0].pos, val[0], val[2], val[4])} # Returns Expression assignment_expression : conditional_expression {result = val[0]} | unary_expression assignment_operator assignment_expression {result = val[1].new_at(val[0].pos, val[0], val[2])} # Returns Class assignment_operator : EQ {result = Assign} | MULEQ {result = MultiplyAssign} | DIVEQ {result = DivideAssign} | MODEQ {result = ModAssign} | ADDEQ {result = AddAssign} | SUBEQ {result = SubtractAssign} | LSHIFTEQ {result = ShiftLeftAssign} | RSHIFTEQ {result = ShiftRightAssign} | ANDEQ {result = BitAndAssign} | XOREQ {result = BitXorAssign} | OREQ {result = BitOrAssign} # Returns Expression expression : assignment_expression {result = val[0]} | expression COMMA assignment_expression { if val[0].is_a? Comma if val[2].is_a? Comma val[0].exprs.push(*val[2].exprs) else val[0].exprs << val[2] end result = val[0] else if val[2].is_a? Comma val[2].exprs.unshift(val[0]) val[2].pos = val[0].pos result = val[2] else result = Comma.new_at(val[0].pos, NodeArray[val[0], val[2]]) end end } # Returns Expression constant_expression : conditional_expression {result = val[0]} # A.1.1 -- Lexical elements # # token # : keyword (raw string) # | identifier expanded below # | constant expanded below # | string_literal expanded below # | punctuator (raw string) # # preprocessing-token (skip) # Returns Token identifier : ID {result = val[0]} # Returns Literal constant : ICON {result = val[0].val; result.pos = val[0].pos} | FCON {result = val[0].val; result.pos = val[0].pos} #| enumeration_constant -- these are parsed as identifiers at all # places the `constant' nonterminal appears | CCON {result = val[0].val; result.pos = val[0].pos} # Returns Token enumeration_constant : ID {result = val[0]} # Returns StringLiteral # Also handles string literal concatenation (6.4.5.4) string_literal : string_literal SCON {val[0].val << val[1].val.val; result = val[0]} | SCON { result = val[0].val; result.pos = val[0].pos } ---- inner # A.1.9 -- Preprocessing numbers -- skip # A.1.8 -- Header names -- skip # A.1.7 -- Puncuators -- we don't bother with {##,#,%:,%:%:} since # we don't do preprocessing @@punctuators = %r'\+\+|-[->]|&&|\|\||\.\.\.|(?:<<|>>|[<>=!*/%+\-&^|])=?|[\[\](){}.~?:;,]' @@digraphs = %r'<[:%]|[:%]>' # A.1.6 -- String Literals -- simple for us because we don't decode # the string (and indeed accept some illegal strings) @@string_literal = %r'L?"(?:[^\\]|\\.)*?"'m # A.1.5 -- Constants @@decimal_floating_constant = %r'(?:(?:\d*\.\d+|\d+\.)(?:e[-+]?\d+)?|\d+e[-+]?\d+)[fl]?'i @@hexadecimal_floating_constant = %r'0x(?:(?:[0-9a-f]*\.[0-9a-f]+|[0-9a-f]+\.)|[0-9a-f]+)p[-+]?\d+[fl]?'i @@integer_constant = %r'(?:[1-9][0-9]*|0x[0-9a-f]+|0[0-7]*)(?:ul?l?|ll?u?)?'i @@floating_constant = %r'#{@@decimal_floating_constant}|#{@@hexadecimal_floating_constant}' @@enumeration_constant = %r'[a-zA-Z_\\][a-zA-Z_\\0-9]*' @@character_constant = %r"L?'(?:[^\\]|\\.)+?'" # (note that as with string-literals, we accept some illegal # character-constants) # A.1.4 -- Universal character names -- skip # A.1.3 -- Identifiers -- skip, since an identifier is lexically # identical to an enumeration constant # A.1.2 Keywords keywords = %w'auto break case char const continue default do double else enum extern float for goto if inline int long register restrict return short signed sizeof static struct switch typedef union unsigned void volatile while _Bool _Complex _Imaginary' @@keywords = %r"#{keywords.join('|')}" def initialize @type_names = ::Set.new @warning_proc = lambda{} @pos = C::Node::Pos.new(nil, 1, 0) end def initialize_copy(x) @pos = x.pos.dup @type_names = x.type_names.dup end attr_accessor :pos, :type_names def parse(str) if str.respond_to? :read str = str.read end @str = str begin prepare_lexer(str) return do_parse rescue ParseError => e e.set_backtrace(caller) raise end end # # Error handler, as used by racc. # def on_error(error_token_id, error_value, value_stack) if error_value == '$' parse_error @pos, "unexpected EOF" else parse_error(error_value.pos, "parse error on #{token_to_str(error_token_id)} (#{error_value.val})") end end def self.feature(name) attr_writer "#{name}_enabled" class_eval <<-EOS def enable_#{name} @#{name}_enabled = true end def #{name}_enabled? @#{name}_enabled end EOS end private_class_method :feature # # Allow blocks in parentheses as expressions, as per the gcc # extension. [http://rubyurl.com/iB7] # feature :block_expressions private # --------------------------------------------------------- class Token attr_accessor :pos, :val def initialize(pos, val) @pos = pos @val = val end end def eat(str) lines = str.split(/\r\n|[\r\n]/, -1) if lines.length == 1 @pos.col_num += lines[0].length else @pos.line_num += lines.length - 1 @pos.col_num = lines[-1].length end end # # Make a Declaration from the given specs and declarators. # def make_declaration(pos, specs, declarators) specs.all?{|x| x.is_a?(Symbol) || x.is_a?(Type)} or raise specs.map{|x| x.class}.inspect decl = Declaration.new_at(pos, nil, declarators) # set storage class storage_classes = specs.find_all do |x| [:typedef, :extern, :static, :auto, :register].include? x end # 6.7.1p2: at most, one storage-class specifier may be given in # the declaration specifiers in a declaration storage_classes.length <= 1 or begin if declarators.length == 0 for_name = '' else for_name = "for `#{declarators[0].name}'" end parse_error pos, "multiple or duplicate storage classes given #{for_name}'" end decl.storage = storage_classes[0] # set type (specifiers, qualifiers) decl.type = make_direct_type(pos, specs) # set function specifiers decl.inline = specs.include?(:inline) # look for new type names if decl.typedef? decl.declarators.each do |d| if d.name @type_names << d.name end end end return decl end def make_function_def(pos, specs, func_declarator, decl_list, defn) add_decl_type(func_declarator, make_direct_type(pos, specs)) # get types from decl_list if necessary function = func_declarator.indirect_type function.is_a? Function or parse_error pos, "non function type for function `#{func_declarator.name}'" params = function.params if decl_list params.all?{|p| p.type.nil?} or parse_error pos, "both prototype and declaration list given for `#{func_declarator.name}'" decl_list.each do |declaration| declaration.declarators.each do |declarator| param = params.find{|p| p.name == declarator.name} or parse_error pos, "no parameter named #{declarator.name}" if declarator.indirect_type param.type = declarator.indirect_type param.type.direct_type = declaration.type.dup else param.type = declaration.type.dup end end end params.all?{|p| p.type} or begin s = params.find_all{|p| p.type.nil?}.map{|p| "`#{p.name}'"}.join(' and ') parse_error pos, "types missing for parameters #{s}" end end fd = FunctionDef.new_at(pos, function.detach, func_declarator.name, defn, :no_prototype => !decl_list.nil?) # set storage class # 6.9.1p4: only extern or static allowed specs.each do |s| [:typedef, :auto, :register].include?(s) and "`#{s}' illegal for function" end storage_classes = specs.find_all do |s| s == :extern || s == :static end # 6.7.1p2: at most, one storage-class specifier may be given in # the declaration specifiers in a declaration storage_classes.length <= 1 or "multiple or duplicate storage classes given for `#{func_declarator.name}'" fd.storage = storage_classes[0] if storage_classes[0] # set function specifiers # 6.7.4p5 'inline' can be repeated fd.inline = specs.include?(:inline) return fd end # # Make a direct type from the list of type specifiers and type # qualifiers. # def make_direct_type(pos, specs) specs_order = [:signed, :unsigned, :short, :long, :double, :void, :char, :int, :float, :_Bool, :_Complex, :_Imaginary] type_specs = specs.find_all do |x| specs_order.include?(x) || !x.is_a?(Symbol) end type_specs.sort! do |a, b| (specs_order.index(a)||100) <=> (specs_order.index(b)||100) end # set type specifiers # 6.7.2p2: the specifier list should be one of these type = case type_specs when [:void] Void.new when [:char] Char.new when [:signed, :char] Char.new :signed => true when [:unsigned, :char] Char.new :signed => false when [:short], [:signed, :short], [:short, :int], [:signed, :short, :int] Int.new :longness => -1 when [:unsigned, :short], [:unsigned, :short, :int] Int.new :unsigned => true, :longness => -1 when [:int], [:signed], [:signed, :int] Int.new when [:unsigned], [:unsigned, :int] Int.new :unsigned => true when [:long], [:signed, :long], [:long, :int], [:signed, :long, :int] Int.new :longness => 1 when [:unsigned, :long], [:unsigned, :long, :int] Int.new :longness => 1, :unsigned => true when [:long, :long], [:signed, :long, :long], [:long, :long, :int], [:signed, :long, :long, :int] Int.new :longness => 2 when [:unsigned, :long, :long], [:unsigned, :long, :long, :int] Int.new :longness => 2, :unsigned => true when [:float] Float.new when [:double] Float.new :longness => 1 when [:long, :double] Float.new :longness => 2 when [:_Bool] Bool.new when [:float, :_Complex] Complex.new when [:double, :_Complex] Complex.new :longness => 1 when [:long, :double, :_Complex] Complex.new :longness => 2 when [:float, :_Imaginary] Imaginary.new when [:double, :_Imaginary] Imaginary.new :longness => 1 when [:long, :double, :_Imaginary] Imaginary.new :longness => 2 else if type_specs.length == 1 && [CustomType, Struct, Union, Enum].any?{|c| type_specs[0].is_a? c} type_specs[0] else if type_specs == [] parse_error pos, "no type specifiers given" else parse_error pos, "invalid type specifier combination: #{type_specs.join(' ')}" end end end type.pos ||= pos # set type qualifiers # 6.7.3p4: type qualifiers can be repeated type.const = specs.any?{|x| x.equal? :const } type.restrict = specs.any?{|x| x.equal? :restrict} type.volatile = specs.any?{|x| x.equal? :volatile} return type end def make_parameter(pos, specs, indirect_type, name) type = indirect_type if type type.direct_type = make_direct_type(pos, specs) else type = make_direct_type(pos, specs) end [:typedef, :extern, :static, :auto, :inline].each do |sym| specs.include? sym and parse_error pos, "parameter `#{declarator.name}' declared `#{sym}'" end return Parameter.new_at(pos, type, name, :register => specs.include?(:register)) end def add_type_quals(type, quals) type.const = quals.include?(:const ) type.restrict = quals.include?(:restrict) type.volatile = quals.include?(:volatile) return type end # # Add te given type as the "most direct" type to the given # declarator. Return the declarator. # def add_decl_type(declarator, type) if declarator.indirect_type declarator.indirect_type.direct_type = type else declarator.indirect_type = type end return declarator end def param_list(params, var_args) if params.length == 1 && params[0].type.is_a?(Void) && params[0].name.nil? return NodeArray[] elsif params.empty? return nil else return params end end def parse_error(pos, str) raise ParseError, "#{pos}: #{str}" end ---- header require 'set' # Error classes module C class ParseError < StandardError; end end # Local variables: # mode: ruby # end: racc-1.6.2/test/assets/chk.y000066400000000000000000000036211435117461300156570ustar00rootroot00000000000000# # racc tester # class Calcp prechigh left '*' '/' left '+' '-' preclow convert NUMBER 'Number' end rule target : exp | /* none */ { result = 0 } ; exp : exp '+' exp { result += val[2]; @plus = 'plus' } | exp '-' exp { result -= val[2]; @str = "string test" } | exp '*' exp { result *= val[2] } | exp '/' exp { result /= val[2] } | '(' { $emb = true } exp ')' { raise 'must not happen' unless $emb result = val[2] } | '-' NUMBER { result = -val[1] } | NUMBER ; end ----header class Number; end ----inner def parse( src ) $emb = false @plus = nil @str = nil @src = src result = do_parse if @plus raise 'string parse failed' unless @plus == 'plus' end if @str raise 'string parse failed' unless @str == 'string test' end result end def next_token @src.shift end def initialize @yydebug = true end ----footer $parser = Calcp.new $test_number = 1 def chk( src, ans ) result = $parser.parse(src) raise "test #{$test_number} fail" unless result == ans $test_number += 1 end chk( [ [Number, 9], [false, false], [false, false] ], 9 ) chk( [ [Number, 5], ['*', nil], [Number, 1], ['-', nil], [Number, 1], ['*', nil], [Number, 8], [false, false], [false, false] ], -3 ) chk( [ [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], [false, false], [false, false] ], -1 ) chk( [ ['-', nil], [Number, 4], [false, false], [false, false] ], -4 ) chk( [ [Number, 7], ['*', nil], ['(', nil], [Number, 4], ['+', nil], [Number, 3], [')', nil], ['-', nil], [Number, 9], [false, false], [false, false] ], 40 ) racc-1.6.2/test/assets/conf.y000066400000000000000000000001721435117461300160350ustar00rootroot00000000000000 class A rule a: A c C expr; b: A B; # useless c: A; c: A; expr: expr '+' expr expr: expr '-' expr expr: NUMBER end racc-1.6.2/test/assets/csspool.y000066400000000000000000000514631435117461300166030ustar00rootroot00000000000000class CSSPool::CSS::Parser token CHARSET_SYM IMPORT_SYM STRING SEMI IDENT S COMMA LBRACE RBRACE STAR HASH token LSQUARE RSQUARE EQUAL INCLUDES DASHMATCH LPAREN RPAREN FUNCTION GREATER PLUS token SLASH NUMBER MINUS LENGTH PERCENTAGE ANGLE TIME FREQ URI token IMPORTANT_SYM MEDIA_SYM NOT ONLY AND NTH_PSEUDO_CLASS token DOCUMENT_QUERY_SYM FUNCTION_NO_QUOTE token TILDE token PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH token NOT_PSEUDO_CLASS token KEYFRAMES_SYM token MATCHES_PSEUDO_CLASS token NAMESPACE_SYM token MOZ_PSEUDO_ELEMENT token RESOLUTION token COLON token SUPPORTS_SYM token OR token VARIABLE_NAME token CALC_SYM token FONTFACE_SYM token UNICODE_RANGE token RATIO rule document : { @handler.start_document } stylesheet { @handler.end_document } ; stylesheet : charset stylesheet | import stylesheet | namespace stylesheet | charset | import | namespace | body | ; charset : CHARSET_SYM STRING SEMI { @handler.charset interpret_string(val[1]), {} } ; import : IMPORT_SYM import_location medium SEMI { @handler.import_style val[2], val[1] } | IMPORT_SYM import_location SEMI { @handler.import_style [], val[1] } ; import_location : import_location S | STRING { result = Terms::String.new interpret_string val.first } | URI { result = Terms::URI.new interpret_uri val.first } ; namespace : NAMESPACE_SYM ident import_location SEMI { @handler.namespace val[1], val[2] } | NAMESPACE_SYM import_location SEMI { @handler.namespace nil, val[1] } ; medium : medium COMMA IDENT { result = val[0] << MediaType.new(val[2]) } | IDENT { result = [MediaType.new(val[0])] } ; media_query_list : media_query { result = MediaQueryList.new([ val[0] ]) } | media_query_list COMMA media_query { result = val[0] << val[2] } | { result = MediaQueryList.new } ; media_query : optional_only_or_not media_type optional_and_exprs { result = MediaQuery.new(val[0], val[1], val[2]) } | media_expr optional_and_exprs { result = MediaQuery.new(nil, val[0], val[1]) } ; optional_only_or_not : ONLY { result = :only } | NOT { result = :not } | { result = nil } ; media_type : IDENT { result = MediaType.new(val[0]) } ; media_expr : LPAREN optional_space IDENT optional_space RPAREN { result = MediaType.new(val[2]) } | LPAREN optional_space IDENT optional_space COLON optional_space expr RPAREN { result = MediaFeature.new(val[2], val[6][0]) } ; optional_space : S { result = val[0] } | { result = nil } ; optional_and_exprs : optional_and_exprs AND media_expr { result = val[0] << val[2] } | { result = [] } ; resolution : RESOLUTION { unit = val.first.gsub(/[\s\d.]/, '') number = numeric(val.first) result = Terms::Resolution.new(number, unit) } ; body : ruleset body | conditional_rule body | keyframes_rule body | fontface_rule body | ruleset | conditional_rule | keyframes_rule | fontface_rule ; conditional_rule : media | document_query | supports ; body_in_media : body | empty_ruleset ; media : start_media body_in_media RBRACE { @handler.end_media val.first } ; start_media : MEDIA_SYM media_query_list LBRACE { result = val[1] @handler.start_media result } ; document_query : start_document_query body RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) } | start_document_query RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) } ; start_document_query : start_document_query_pos url_match_fns LBRACE { @handler.start_document_query(val[1], after_pos(val)) } ; start_document_query_pos : DOCUMENT_QUERY_SYM { @handler.node_start_pos = before_pos(val) } ; url_match_fns : url_match_fn COMMA url_match_fns { result = [val[0], val[2]].flatten } | url_match_fn { result = val } ; url_match_fn : function_no_quote | function | uri ; supports : start_supports body RBRACE { @handler.end_supports } | start_supports RBRACE { @handler.end_supports } ; start_supports : SUPPORTS_SYM supports_condition_root LBRACE { @handler.start_supports val[1] } ; supports_condition_root : supports_negation { result = val.join('') } | supports_conjunction_or_disjunction { result = val.join('') } | supports_condition_in_parens { result = val.join('') } ; supports_condition : supports_negation { result = val.join('') } | supports_conjunction_or_disjunction { result = val.join('') } | supports_condition_in_parens { result = val.join('') } ; supports_condition_in_parens : LPAREN supports_condition RPAREN { result = val.join('') } | supports_declaration_condition { result = val.join('') } ; supports_negation : NOT supports_condition_in_parens { result = val.join('') } ; supports_conjunction_or_disjunction : supports_conjunction | supports_disjunction ; supports_conjunction : supports_condition_in_parens AND supports_condition_in_parens { result = val.join('') } | supports_conjunction_or_disjunction AND supports_condition_in_parens { result = val.join('') } ; supports_disjunction : supports_condition_in_parens OR supports_condition_in_parens { result = val.join('') } | supports_conjunction_or_disjunction OR supports_condition_in_parens { result = val.join('') } ; supports_declaration_condition : LPAREN declaration_internal RPAREN { result = val.join('') } | LPAREN S declaration_internal RPAREN { result = val.join('') } ; keyframes_rule : start_keyframes_rule keyframes_blocks RBRACE | start_keyframes_rule RBRACE ; start_keyframes_rule : KEYFRAMES_SYM IDENT LBRACE { @handler.start_keyframes_rule val[1] } ; keyframes_blocks : keyframes_block keyframes_blocks | keyframes_block ; keyframes_block : start_keyframes_block declarations RBRACE { @handler.end_keyframes_block } | start_keyframes_block RBRACE { @handler.end_keyframes_block } ; start_keyframes_block : keyframes_selectors LBRACE { @handler.start_keyframes_block val[0] } ; keyframes_selectors | keyframes_selector COMMA keyframes_selectors { result = val[0] + ', ' + val[2] } | keyframes_selector ; keyframes_selector : IDENT | PERCENTAGE { result = val[0].strip } ; fontface_rule : start_fontface_rule declarations RBRACE { @handler.end_fontface_rule } | start_fontface_rule RBRACE { @handler.end_fontface_rule } ; start_fontface_rule : FONTFACE_SYM LBRACE { @handler.start_fontface_rule } ; ruleset : start_selector declarations RBRACE { @handler.end_selector val.first } | start_selector RBRACE { @handler.end_selector val.first } ; empty_ruleset : optional_space { start = @handler.start_selector([]) @handler.end_selector(start) } ; start_selector : S start_selector { result = val.last } | selectors LBRACE { @handler.start_selector val.first } ; selectors : selector COMMA selectors { sel = Selector.new(val.first, {}) result = [sel].concat(val[2]) } | selector { result = [Selector.new(val.first, {})] } ; selector : simple_selector combinator selector { val.flatten! val[2].combinator = val.delete_at 1 result = val } | simple_selector ; combinator : S { result = :s } | GREATER { result = :> } | PLUS { result = :+ } | TILDE { result = :~ } ; simple_selector : element_name hcap { selector = val.first selector.additional_selectors = val.last result = [selector] } | element_name { result = val } | hcap { ss = Selectors::Simple.new nil, nil ss.additional_selectors = val.flatten result = [ss] } ; simple_selectors : simple_selector COMMA simple_selectors { result = [val[0], val[2]].flatten } | simple_selector ; ident_with_namespace : IDENT { result = [interpret_identifier(val[0]), nil] } | IDENT '|' IDENT { result = [interpret_identifier(val[2]), interpret_identifier(val[0])] } | '|' IDENT { result = [interpret_identifier(val[1]), nil] } | STAR '|' IDENT { result = [interpret_identifier(val[2]), '*'] } ; element_name : ident_with_namespace { result = Selectors::Type.new val.first[0], nil, val.first[1] } | STAR { result = Selectors::Universal.new val.first } | '|' STAR { result = Selectors::Universal.new val[1] } | STAR '|' STAR { result = Selectors::Universal.new val[2], nil, val[0] } | IDENT '|' STAR { result = Selectors::Universal.new val[2], nil, interpret_identifier(val[0]) } ; hcap : hash { result = val } | class { result = val } | attrib { result = val } | pseudo { result = val } | hash hcap { result = val.flatten } | class hcap { result = val.flatten } | attrib hcap { result = val.flatten } | pseudo hcap { result = val.flatten } ; hash : HASH { result = Selectors::Id.new interpret_identifier val.first.sub(/^#/, '') } class : '.' IDENT { result = Selectors::Class.new interpret_identifier val.last } ; attrib : LSQUARE ident_with_namespace EQUAL IDENT RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_identifier(val[3]), Selectors::Attribute::EQUALS, val[1][1] ) } | LSQUARE ident_with_namespace EQUAL STRING RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_string(val[3]), Selectors::Attribute::EQUALS, val[1][1] ) } | LSQUARE ident_with_namespace INCLUDES STRING RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_string(val[3]), Selectors::Attribute::INCLUDES, val[1][1] ) } | LSQUARE ident_with_namespace INCLUDES IDENT RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_identifier(val[3]), Selectors::Attribute::INCLUDES, val[1][1] ) } | LSQUARE ident_with_namespace DASHMATCH IDENT RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_identifier(val[3]), Selectors::Attribute::DASHMATCH, val[1][1] ) } | LSQUARE ident_with_namespace DASHMATCH STRING RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_string(val[3]), Selectors::Attribute::DASHMATCH, val[1][1] ) } | LSQUARE ident_with_namespace PREFIXMATCH IDENT RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_identifier(val[3]), Selectors::Attribute::PREFIXMATCH, val[1][1] ) } | LSQUARE ident_with_namespace PREFIXMATCH STRING RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_string(val[3]), Selectors::Attribute::PREFIXMATCH, val[1][1] ) } | LSQUARE ident_with_namespace SUFFIXMATCH IDENT RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_identifier(val[3]), Selectors::Attribute::SUFFIXMATCH, val[1][1] ) } | LSQUARE ident_with_namespace SUFFIXMATCH STRING RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_string(val[3]), Selectors::Attribute::SUFFIXMATCH, val[1][1] ) } | LSQUARE ident_with_namespace SUBSTRINGMATCH IDENT RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_identifier(val[3]), Selectors::Attribute::SUBSTRINGMATCH, val[1][1] ) } | LSQUARE ident_with_namespace SUBSTRINGMATCH STRING RSQUARE { result = Selectors::Attribute.new( val[1][0], interpret_string(val[3]), Selectors::Attribute::SUBSTRINGMATCH, val[1][1] ) } | LSQUARE ident_with_namespace RSQUARE { result = Selectors::Attribute.new( val[1][0], nil, Selectors::Attribute::SET, val[1][1] ) } ; pseudo : COLON IDENT { result = Selectors::pseudo interpret_identifier(val[1]) } | COLON COLON IDENT { result = Selectors::PseudoElement.new( interpret_identifier(val[2]) ) } | COLON FUNCTION RPAREN { result = Selectors::PseudoClass.new( interpret_identifier(val[1].sub(/\($/, '')), '' ) } | COLON FUNCTION IDENT RPAREN { result = Selectors::PseudoClass.new( interpret_identifier(val[1].sub(/\($/, '')), interpret_identifier(val[2]) ) } | COLON NOT_PSEUDO_CLASS simple_selector RPAREN { result = Selectors::PseudoClass.new( 'not', val[2].first.to_s ) } | COLON NTH_PSEUDO_CLASS { result = Selectors::PseudoClass.new( interpret_identifier(val[1].sub(/\(.*/, '')), interpret_identifier(val[1].sub(/.*\(/, '').sub(/\).*/, '')) ) } | COLON MATCHES_PSEUDO_CLASS simple_selectors RPAREN { result = Selectors::PseudoClass.new( val[1].split('(').first.strip, val[2].join(', ') ) } | COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN { result = Selectors::PseudoElement.new( interpret_identifier(val[1].sub(/\($/, '')) ) } | COLON COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN { result = Selectors::PseudoElement.new( interpret_identifier(val[2].sub(/\($/, '')) ) } ; any_number_of_idents : | multiple_idents ; multiple_idents : IDENT | IDENT COMMA multiple_idents ; # declarations can be separated by one *or more* semicolons. semi-colons at the start or end of a ruleset are also allowed one_or_more_semis : SEMI | SEMI one_or_more_semis ; declarations : declaration one_or_more_semis declarations | one_or_more_semis declarations | declaration one_or_more_semis | declaration | one_or_more_semis ; declaration : declaration_internal { @handler.property val.first } ; declaration_internal : property COLON expr prio { result = Declaration.new(val.first, val[2], val[3]) } | property COLON S expr prio { result = Declaration.new(val.first, val[3], val[4]) } | property S COLON expr prio { result = Declaration.new(val.first, val[3], val[4]) } | property S COLON S expr prio { result = Declaration.new(val.first, val[4], val[5]) } ; prio : IMPORTANT_SYM { result = true } | { result = false } ; property : IDENT { result = interpret_identifier val[0] } | STAR IDENT { result = interpret_identifier val.join } | VARIABLE_NAME { result = interpret_identifier val[0] } ; operator : COMMA | SLASH | EQUAL ; expr : term operator expr { result = [val.first, val.last].flatten val.last.first.operator = val[1] } | term expr { result = val.flatten } | term { result = val } ; term : ident | ratio | numeric | string | uri | hexcolor | calc | function | resolution | VARIABLE_NAME | uranges ; function : function S { result = val.first } | FUNCTION expr RPAREN { name = interpret_identifier val.first.sub(/\($/, '') if name == 'rgb' result = Terms::Rgb.new(*val[1]) else result = Terms::Function.new name, val[1] end } | FUNCTION RPAREN { name = interpret_identifier val.first.sub(/\($/, '') result = Terms::Function.new name } ; function_no_quote : function_no_quote S { result = val.first } | FUNCTION_NO_QUOTE { parts = val.first.split('(') name = interpret_identifier parts.first result = Terms::Function.new(name, [Terms::String.new(interpret_string_no_quote(parts.last))]) } ; uranges : UNICODE_RANGE COMMA uranges | UNICODE_RANGE ; calc : CALC_SYM calc_sum RPAREN optional_space { result = Terms::Math.new(val.first.split('(').first, val[1]) } ; # plus and minus are supposed to have whitespace around them, per http://dev.w3.org/csswg/css-values/#calc-syntax, but the numbers are eating trailing whitespace, so we inject it back in calc_sum : calc_product | calc_product PLUS calc_sum { val.insert(1, ' '); result = val.join('') } | calc_product MINUS calc_sum { val.insert(1, ' '); result = val.join('') } ; calc_product : calc_value | calc_value optional_space STAR calc_value { result = val.join('') } | calc_value optional_space SLASH calc_value { result = val.join('') } ; calc_value : numeric { result = val.join('') } | function { result = val.join('') } # for var() variable references | LPAREN calc_sum RPAREN { result = val.join('') } ; hexcolor : hexcolor S { result = val.first } | HASH { result = Terms::Hash.new val.first.sub(/^#/, '') } ; uri : uri S { result = val.first } | URI { result = Terms::URI.new interpret_uri val.first } ; string : string S { result = val.first } | STRING { result = Terms::String.new interpret_string val.first } ; numeric : unary_operator numeric { result = val[1] val[1].unary_operator = val.first } | NUMBER { result = Terms::Number.new numeric val.first } | PERCENTAGE { result = Terms::Number.new numeric(val.first), nil, '%' } | LENGTH { unit = val.first.gsub(/[\s\d.]/, '') result = Terms::Number.new numeric(val.first), nil, unit } | ANGLE { unit = val.first.gsub(/[\s\d.]/, '') result = Terms::Number.new numeric(val.first), nil, unit } | TIME { unit = val.first.gsub(/[\s\d.]/, '') result = Terms::Number.new numeric(val.first), nil, unit } | FREQ { unit = val.first.gsub(/[\s\d.]/, '') result = Terms::Number.new numeric(val.first), nil, unit } ; ratio : RATIO { result = Terms::Ratio.new(val[0], val[1]) } ; unary_operator : MINUS { result = :minus } | PLUS { result = :plus } ; ident : ident S { result = val.first } | IDENT { result = Terms::Ident.new interpret_identifier val.first } ; ---- inner def numeric thing thing = thing.gsub(/[^\d.]/, '') Integer(thing) rescue Float(thing) end def interpret_identifier s interpret_escapes s end def interpret_uri s interpret_escapes s.match(/^url\((.*)\)$/mui)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2] end def interpret_string_no_quote s interpret_escapes s.match(/^(.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2] end def interpret_string s interpret_escapes s.match(/^(['"])((?:\\.|.)*)\1$/mu)[2] end def interpret_escapes s token_exp = /\\(?:([0-9a-fA-F]{1,6}(?:\r\n|\s)?)|(.))/mu return s.gsub(token_exp) do |escape_sequence| if !$1.nil? code = $1.chomp.to_i 16 code = 0xFFFD if code > 0x10FFFF next [code].pack('U') end next '' if $2 == "\n" next $2 end end # override racc's on_error so we can have context in our error messages def on_error(t, val, vstack) errcontext = (@ss.pre_match[-10..-1] || @ss.pre_match) + @ss.matched + @ss.post_match[0..9] line_number = @ss.pre_match.lines.count raise ParseError, sprintf("parse error on value %s (%s) " + "on line %s around \"%s\"", val.inspect, token_to_str(t) || '?', line_number, errcontext) end def before_pos(val) # don't include leading whitespace return current_pos - val.last.length + val.last[/\A\s*/].size end def after_pos(val) # don't include trailing whitespace return current_pos - val.last[/\s*\z/].size end # charpos will work with multibyte strings but is not available until ruby 2 def current_pos @ss.respond_to?('charpos') ? @ss.charpos : @ss.pos end racc-1.6.2/test/assets/digraph.y000066400000000000000000000004331435117461300165260ustar00rootroot00000000000000# ? detect digraph bug class P token A B C D rule target : a b c d a : A | b : B | c : C | d : D | end ---- inner def parse do_parse end def next_token [false, '$'] end ---- footer P.new.parse racc-1.6.2/test/assets/echk.y000066400000000000000000000032271435117461300160260ustar00rootroot00000000000000# # racc tester # class Calcp prechigh left '*' '/' left '+' '-' preclow convert NUMBER 'Number' end rule target : exp | /* none */ { result = 0 } ; exp : exp '+' exp { result += val[2]; a = 'plus' } | exp '-' exp { result -= val[2]; "string test" } | exp '*' exp { result *= val[2] } | exp '/' exp { result /= val[2] } | '(' { $emb = true } exp ')' { raise 'must not happen' unless $emb result = val[2] } | '-' NUMBER { result = -val[1] } | NUMBER ; end ----header class Number ; end ----inner def parse( src ) @src = src do_parse end def next_token @src.shift end def initialize @yydebug = true end ----footer $parser = Calcp.new $tidx = 1 def chk( src, ans ) ret = $parser.parse( src ) unless ret == ans then bug! "test #{$tidx} fail" end $tidx += 1 end chk( [ [Number, 9], [false, false], [false, false] ], 9 ) chk( [ [Number, 5], ['*', nil], [Number, 1], ['-', nil], [Number, 1], ['*', nil], [Number, 8], [false, false], [false, false] ], -3 ) chk( [ [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], [false, false], [false, false] ], -1 ) chk( [ ['-', nil], [Number, 4], [false, false], [false, false] ], -4 ) chk( [ [Number, 7], ['*', nil], ['(', nil], [Number, 4], ['+', nil], [Number, 3], [')', nil], ['-', nil], [Number, 9], [false, false], [false, false] ], 40 ) racc-1.6.2/test/assets/edtf.y000066400000000000000000000420661435117461300160420ustar00rootroot00000000000000# -*- racc -*- # Copyright 2011 Sylvester Keil. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. 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. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``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 HOLDER 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. # # The views and conclusions contained in the software and documentation are # those of the authors and should not be interpreted as representing official # policies, either expressed or implied, of the copyright holder. class EDTF::Parser token T Z E X U UNKNOWN OPEN LONGYEAR UNMATCHED DOTS UA PUA expect 0 rule edtf : level_0_expression | level_1_expression | level_2_expression ; # ---- Level 0 / ISO 8601 Rules ---- # NB: level 0 intervals are covered by the level 1 interval rules level_0_expression : date | date_time ; date : positive_date | negative_date ; positive_date : year { result = Date.new(val[0]).year_precision! } | year_month { result = Date.new(*val.flatten).month_precision! } | year_month_day { result = Date.new(*val.flatten).day_precision! } ; negative_date : '-' positive_date { result = -val[1] } date_time : date T time { result = DateTime.new(val[0].year, val[0].month, val[0].day, *val[2]) result.skip_timezone = (val[2].length == 3) } time : base_time | base_time zone_offset { result = val.flatten } base_time : hour ':' minute ':' second { result = val.values_at(0, 2, 4) } | midnight midnight : '2' '4' ':' '0' '0' ':' '0' '0' { result = [24, 0, 0] } zone_offset : Z { result = 0 } | '-' zone_offset_hour { result = -1 * val[1] } | '+' positive_zone_offset { result = val[1] } ; positive_zone_offset : zone_offset_hour | '0' '0' ':' '0' '0' { result = 0 } ; zone_offset_hour : d01_13 ':' minute { result = Rational(val[0] * 60 + val[2], 1440) } | '1' '4' ':' '0' '0' { result = Rational(840, 1440) } | '0' '0' ':' d01_59 { result = Rational(val[3], 1440) } ; year : digit digit digit digit { result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b } } month : d01_12 day : d01_31 year_month : year '-' month { result = [val[0], val[2]] } # We raise an exception if there are two many days for the month, but # do not consider leap years, as the EDTF BNF did not either. # NB: an exception will be raised regardless, because the Ruby Date # implementation calculates leap years. year_month_day : year_month '-' day { result = val[0] << val[2] if result[2] > 31 || (result[2] > 30 && [2,4,6,9,11].include?(result[1])) || (result[2] > 29 && result[1] == 2) raise ArgumentError, "invalid date (invalid days #{result[2]} for month #{result[1]})" end } hour : d00_23 minute : d00_59 second : d00_59 # Completely covered by level_1_interval # level_0_interval : date '/' date { result = Interval.new(val[0], val[1]) } # ---- Level 1 Extension Rules ---- # NB: Uncertain/approximate Dates are covered by the Level 2 rules level_1_expression : unspecified | level_1_interval | long_year_simple | season # uncertain_or_approximate_date : date UA { result = uoa(val[0], val[1]) } unspecified : unspecified_year { result = Date.new(val[0][0]).year_precision! result.unspecified.year[2,2] = val[0][1] } | unspecified_month | unspecified_day | unspecified_day_and_month ; unspecified_year : digit digit digit U { result = [val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }, [false,true]] } | digit digit U U { result = [val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }, [true, true]] } unspecified_month : year '-' U U { result = Date.new(val[0]).unspecified!(:month) result.precision = :month } unspecified_day : year_month '-' U U { result = Date.new(*val[0]).unspecified!(:day) } unspecified_day_and_month : year '-' U U '-' U U { result = Date.new(val[0]).unspecified!([:day,:month]) } level_1_interval : level_1_start '/' level_1_end { result = Interval.new(val[0], val[2]) } level_1_start : date | partial_uncertain_or_approximate | unspecified | partial_unspecified | UNKNOWN level_1_end : level_1_start | OPEN long_year_simple : LONGYEAR long_year { result = Date.new(val[1]) result.precision = :year } | LONGYEAR '-' long_year { result = Date.new(-1 * val[2]) result.precision = :year } ; long_year : positive_digit digit digit digit digit { result = val.zip([10000,1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b } } | long_year digit { result = 10 * val[0] + val[1] } ; season : year '-' season_number ua { result = Season.new(val[0], val[2]) val[3].each { |ua| result.send(ua) } } season_number : '2' '1' { result = 21 } | '2' '2' { result = 22 } | '2' '3' { result = 23 } | '2' '4' { result = 24 } ; # ---- Level 2 Extension Rules ---- # NB: Level 2 Intervals are covered by the Level 1 Interval rules. level_2_expression : season_qualified | partial_uncertain_or_approximate | partial_unspecified | choice_list | inclusive_list | masked_precision | date_and_calendar | long_year_scientific ; season_qualified : season '^' { result = val[0]; result.qualifier = val[1] } long_year_scientific : long_year_simple E integer { result = Date.new(val[0].year * 10 ** val[2]).year_precision! } | LONGYEAR int1_4 E integer { result = Date.new(val[1] * 10 ** val[3]).year_precision! } | LONGYEAR '-' int1_4 E integer { result = Date.new(-1 * val[2] * 10 ** val[4]).year_precision! } ; date_and_calendar : date '^' { result = val[0]; result.calendar = val[1] } masked_precision : digit digit digit X { d = val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b } result = EDTF::Decade.new(d) } | digit digit X X { d = val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b } result = EDTF::Century.new(d) } ; choice_list : '[' list ']' { result = val[1].choice! } inclusive_list : '{' list '}' { result = val[1] } list : earlier { result = EDTF::Set.new(val[0]).earlier! } | earlier ',' list_elements ',' later { result = EDTF::Set.new([val[0]] + val[2] + [val[4]]).earlier!.later! } | earlier ',' list_elements { result = EDTF::Set.new([val[0]] + val[2]).earlier! } | earlier ',' later { result = EDTF::Set.new([val[0]] + [val[2]]).earlier!.later! } | list_elements ',' later { result = EDTF::Set.new(val[0] + [val[2]]).later! } | list_elements { result = EDTF::Set.new(*val[0]) } | later { result = EDTF::Set.new(val[0]).later! } ; list_elements : list_element { result = [val[0]].flatten } | list_elements ',' list_element { result = val[0] + [val[2]].flatten } ; list_element : atomic | consecutives ; atomic : date | partial_uncertain_or_approximate | unspecified ; earlier : DOTS date { result = val[1] } later : year_month_day DOTS { result = Date.new(*val[0]).year_precision! } | year_month DOTS { result = Date.new(*val[0]).month_precision! } | year DOTS { result = Date.new(val[0]).year_precision! } ; consecutives : year_month_day DOTS year_month_day { result = (Date.new(val[0]).day_precision! .. Date.new(val[2]).day_precision!) } | year_month DOTS year_month { result = (Date.new(val[0]).month_precision! .. Date.new(val[2]).month_precision!) } | year DOTS year { result = (Date.new(val[0]).year_precision! .. Date.new(val[2]).year_precision!) } ; partial_unspecified : unspecified_year '-' month '-' day { result = Date.new(val[0][0], val[2], val[4]) result.unspecified.year[2,2] = val[0][1] } | unspecified_year '-' U U '-' day { result = Date.new(val[0][0], 1, val[5]) result.unspecified.year[2,2] = val[0][1] result.unspecified!(:month) } | unspecified_year '-' U U '-' U U { result = Date.new(val[0][0], 1, 1) result.unspecified.year[2,2] = val[0][1] result.unspecified!([:month, :day]) } | unspecified_year '-' month '-' U U { result = Date.new(val[0][0], val[2], 1) result.unspecified.year[2,2] = val[0][1] result.unspecified!(:day) } | year '-' U U '-' day { result = Date.new(val[0], 1, val[5]) result.unspecified!(:month) } ; partial_uncertain_or_approximate : pua_base | '(' pua_base ')' UA { result = uoa(val[1], val[3]) } pua_base : pua_year { result = val[0].year_precision! } | pua_year_month { result = val[0][0].month_precision! } | pua_year_month_day { result = val[0].day_precision! } pua_year : year UA { result = uoa(Date.new(val[0]), val[1], :year) } pua_year_month : pua_year '-' month ua { result = [uoa(val[0].change(:month => val[2]), val[3], [:month, :year])] } | year '-' month UA { result = [uoa(Date.new(val[0], val[2]), val[3], [:year, :month])] } | year '-(' month ')' UA { result = [uoa(Date.new(val[0], val[2]), val[4], [:month]), true] } | pua_year '-(' month ')' UA { result = [uoa(val[0].change(:month => val[2]), val[4], [:month]), true] } ; pua_year_month_day : pua_year_month '-' day ua { result = uoa(val[0][0].change(:day => val[2]), val[3], val[0][1] ? [:day] : nil) } | pua_year_month '-(' day ')' UA { result = uoa(val[0][0].change(:day => val[2]), val[4], [:day]) } | year '-(' month ')' UA day ua { result = uoa(uoa(Date.new(val[0], val[2], val[5]), val[4], :month), val[6], :day) } | year_month '-' day UA { result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[3]) } | year_month '-(' day ')' UA { result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[4], [:day]) } | year '-(' month '-' day ')' UA { result = uoa(Date.new(val[0], val[2], val[4]), val[6], [:month, :day]) } | year '-(' month '-(' day ')' UA ')' UA { result = Date.new(val[0], val[2], val[4]) result = uoa(result, val[6], [:day]) result = uoa(result, val[8], [:month, :day]) } | pua_year '-(' month '-' day ')' UA { result = val[0].change(:month => val[2], :day => val[4]) result = uoa(result, val[6], [:month, :day]) } | pua_year '-(' month '-(' day ')' UA ')' UA { result = val[0].change(:month => val[2], :day => val[4]) result = uoa(result, val[6], [:day]) result = uoa(result, val[8], [:month, :day]) } # | '(' pua_year '-(' month ')' UA ')' UA '-' day ua { # result = val[1].change(:month => val[3], :day => val[9]) # result = uoa(result, val[5], [:month]) # result = [uoa(result, val[7], [:year]), true] # } ; ua : { result = [] } | UA # ---- Auxiliary Rules ---- digit : '0' { result = 0 } | positive_digit ; positive_digit : '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' d01_12 : '0' positive_digit { result = val[1] } | '1' '0' { result = 10 } | '1' '1' { result = 11 } | '1' '2' { result = 12 } ; d01_13 : d01_12 | '1' '3' { result = 13 } ; d01_23 : '0' positive_digit { result = val[1] } | '1' digit { result = 10 + val[1] } | '2' '0' { result = 20 } | '2' '1' { result = 21 } | '2' '2' { result = 22 } | '2' '3' { result = 23 } ; d00_23 : '0' '0' | d01_23 ; d01_29 : d01_23 | '2' '4' { result = 24 } | '2' '5' { result = 25 } | '2' '6' { result = 26 } | '2' '7' { result = 27 } | '2' '8' { result = 28 } | '2' '9' { result = 29 } ; d01_30 : d01_29 | '3' '0' { result = 30 } ; d01_31 : d01_30 | '3' '1' { result = 31 } ; d01_59 : d01_29 | '3' digit { result = 30 + val[1] } | '4' digit { result = 40 + val[1] } | '5' digit { result = 50 + val[1] } ; d00_59 : '0' '0' | d01_59 ; int1_4 : positive_digit { result = val[0] } | positive_digit digit { result = 10 * val[0] + val[1] } | positive_digit digit digit { result = val.zip([100,10,1]).reduce(0) { |s,(a,b)| s += a * b } } | positive_digit digit digit digit { result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b } } ; integer : positive_digit { result = val[0] } | integer digit { result = 10 * val[0] + val[1] } ; ---- header require 'strscan' ---- inner @defaults = { :level => 2, :debug => false }.freeze class << self; attr_reader :defaults; end attr_reader :options def initialize(options = {}) @options = Parser.defaults.merge(options) end def debug? !!(options[:debug] || ENV['DEBUG']) end def parse(input) parse!(input) rescue => e warn e.message if debug? nil end def parse!(input) @yydebug = debug? @src = StringScanner.new(input) do_parse end def on_error(tid, value, stack) raise ArgumentError, "failed to parse date: unexpected '#{value}' at #{stack.inspect}" end def apply_uncertainty(date, uncertainty, scope = nil) uncertainty.each do |u| scope.nil? ? date.send(u) : date.send(u, scope) end date end alias uoa apply_uncertainty def next_token case when @src.eos? nil # when @src.scan(/\s+/) # ignore whitespace when @src.scan(/\(/) ['(', @src.matched] # when @src.scan(/\)\?~-/) # [:PUA, [:uncertain!, :approximate!]] # when @src.scan(/\)\?-/) # [:PUA, [:uncertain!]] # when @src.scan(/\)~-/) # [:PUA, [:approximate!]] when @src.scan(/\)/) [')', @src.matched] when @src.scan(/\[/) ['[', @src.matched] when @src.scan(/\]/) [']', @src.matched] when @src.scan(/\{/) ['{', @src.matched] when @src.scan(/\}/) ['}', @src.matched] when @src.scan(/T/) [:T, @src.matched] when @src.scan(/Z/) [:Z, @src.matched] when @src.scan(/\?~/) [:UA, [:uncertain!, :approximate!]] when @src.scan(/\?/) [:UA, [:uncertain!]] when @src.scan(/~/) [:UA, [:approximate!]] when @src.scan(/open/i) [:OPEN, :open] when @src.scan(/unkn?own/i) # matches 'unkown' typo too [:UNKNOWN, :unknown] when @src.scan(/u/) [:U, @src.matched] when @src.scan(/x/i) [:X, @src.matched] when @src.scan(/y/) [:LONGYEAR, @src.matched] when @src.scan(/e/) [:E, @src.matched] when @src.scan(/\+/) ['+', @src.matched] when @src.scan(/-\(/) ['-(', @src.matched] when @src.scan(/-/) ['-', @src.matched] when @src.scan(/:/) [':', @src.matched] when @src.scan(/\//) ['/', @src.matched] when @src.scan(/\s*\.\.\s*/) [:DOTS, '..'] when @src.scan(/\s*,\s*/) [',', ','] when @src.scan(/\^\w+/) ['^', @src.matched[1..-1]] when @src.scan(/\d/) [@src.matched, @src.matched.to_i] else @src.scan(/./) [:UNMATCHED, @src.rest] end end # -*- racc -*- racc-1.6.2/test/assets/err.y000066400000000000000000000011501435117461300156750ustar00rootroot00000000000000 class ErrTestp rule target: lines ; lines: line | lines line ; line: A B C D E | error E ; end ---- inner def initialize @yydebug = false @q = [ [:A, 'a'], # [:B, 'b'], [:C, 'c'], [:D, 'd'], [:E, 'e'], [:A, 'a'], [:B, 'b'], [:C, 'c'], [:D, 'd'], [:E, 'e'], [:A, 'a'], [:B, 'b'], # [:C, 'c'], [:D, 'd'], [:E, 'e'], [false, nil] ] end def next_token @q.shift end def on_error( t, val, values ) $stderr.puts "error on token '#{val}'(#{t})" end def parse do_parse end ---- footer p = ErrTestp.new p.parse racc-1.6.2/test/assets/error_recovery.y000066400000000000000000000010411435117461300201530ustar00rootroot00000000000000# Regression test case for the bug discussed here: # https://github.com/whitequark/parser/issues/93 # In short, a Racc-generated parser could go into an infinite loop when # attempting error recovery at EOF class InfiniteLoop rule stmts: stmt | error stmt stmt: '%' stmt end ---- inner def parse @errors = [] do_parse end def next_token nil end def on_error(error_token, error_value, value_stack) # oh my, an error @errors << [error_token, error_value] end ---- footer InfiniteLoop.new.parseracc-1.6.2/test/assets/expect.y000066400000000000000000000001101435117461300163700ustar00rootroot00000000000000class E expect 1 rule list: inlist inlist inlist: | A end racc-1.6.2/test/assets/firstline.y000066400000000000000000000000341435117461300171040ustar00rootroot00000000000000class T rule a: A B C end racc-1.6.2/test/assets/huia.y000066400000000000000000000271051435117461300160430ustar00rootroot00000000000000# Copyright (c) 2014 James Harton # # MIT License # # 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. class Huia::Parser token IDENTIFIER EQUAL PLUS MINUS ASTERISK FWD_SLASH COLON FLOAT INTEGER STRING EXPO INDENT OUTDENT OPAREN CPAREN DOT SIGNATURE NL EOF PIPE COMMA NIL TRUE FALSE EQUALITY CALL SELF CONSTANT CHAR DOUBLE_TICK_STRING DOUBLE_TICK_STRING_END INTERPOLATE_START INTERPOLATE_END BOX LSQUARE RSQUARE FACES LFACE RFACE BANG TILDE RETURN NOT_EQUALITY OR AND GT LT GTE LTE AT prechigh left EXPO left BANG TILDE left ASTERISK FWD_SLASH PERCENT left PLUS MINUS right EQUAL preclow rule statements: statement | statements statement { return scope } statement: expr eol { return scope.append val[0] } | expr { return scope.append val[0] } | eol { return scope } eol: NL | EOF nlq: NL | expr: literal | grouped_expr | binary_op | unary_op | method_call | constant | variable | array | hash | return return: return_expr | return_nil return_expr: RETURN expr { return n(:Return, val[1]) } return_nil: RETURN { return n(:Return, n(:Nil)) } array: empty_array | array_list empty_array: BOX { return n :Array } array_list: LSQUARE array_items RSQUARE { return val[1] } array_items: expr { return n :Array, [val[0]] } | array_items COMMA expr { val[0].append(val[2]); return val[0] } hash: empty_hash | hash_list empty_hash: FACES { return n :Hash } hash_list: LFACE hash_items RFACE { return val[1] } hash_items: hash_item { return n :Hash, val[0] } | hash_items COMMA hash_item { val[0].append(val[2]); return val[0] } hash_item: expr COLON expr { return n :HashItem, val[0], val[2] } constant: CONSTANT { return constant val[0] } indented: indented_w_stmts | indented_w_expr | indented_wo_stmts indented_w_stmts: indent statements outdent { return val[0] } indented_w_expr: indent expr outdent { return val[0].append(val[1]) } indented_wo_stmts: indent outdent { return val[0] } outdent: OUTDENT { return pop_scope } indent_w_args: indent_pipe indent_args PIPE nlq INDENT { return val[0] } indent_pipe: PIPE { return push_scope } indent_wo_args: INDENT { return push_scope } indent: indent_w_args | indent_wo_args indent_args: indent_arg | indent_args COMMA indent_arg indent_arg: arg_var { return scope.add_argument val[0] } | arg_var EQUAL expr { return n :Assignment, val[0], val[2] } arg_var: IDENTIFIER { return n :Variable, val[0] } method_call: method_call_on_object | method_call_on_self | method_call_on_closure method_call_on_object: expr DOT call_signature { return n :MethodCall, val[0], val[2] } | expr DOT IDENTIFIER { return n :MethodCall, val[0], n(:CallSignature, val[2]) } method_call_on_self: call_signature { return n :MethodCall, scope_instance, val[0] } method_call_on_closure: AT call_signature { return n :MethodCall, this_closure, val[1] } | AT IDENTIFIER { return n :MethodCall, this_closure, n(:CallSignature, val[1]) } call_signature: call_arguments | call_simple_name call_simple_name: CALL { return n :CallSignature, val[0] } call_argument: SIGNATURE call_passed_arg { return n :CallSignature, val[0], [val[1]] } call_passed_arg: call_passed_simple | call_passed_indented call_passed_simple: expr | expr NL call_passed_indented: indented | indented NL call_arguments: call_argument { return val[0] } | call_arguments call_argument { return val[0].concat_signature val[1] } grouped_expr: OPAREN expr CPAREN { return n :Expression, val[1] } variable: IDENTIFIER { return allocate_local val[0] } binary_op: assignment | addition | subtraction | multiplication | division | exponentiation | modulo | equality | not_equality | logical_or | logical_and | greater_than | less_than | greater_or_eq | less_or_eq assignment: IDENTIFIER EQUAL expr { return allocate_local_assignment val[0], val[2] } addition: expr PLUS expr { return binary val[0], val[2], 'plus:' } subtraction: expr MINUS expr { return binary val[0], val[2], 'minus:' } multiplication: expr ASTERISK expr { return binary val[0], val[2], 'multiplyBy:' } division: expr FWD_SLASH expr { return binary val[0], val[2], 'divideBy:' } exponentiation: expr EXPO expr { return binary val[0], val[2], 'toThePowerOf:' } modulo: expr PERCENT expr { return binary val[0], val[2], 'moduloOf:' } equality: expr EQUALITY expr { return binary val[0], val[2], 'isEqualTo:' } not_equality: expr NOT_EQUALITY expr { return binary val[0], val[2], 'isNotEqualTo:' } logical_or: expr OR expr { return binary val[0], val[2], 'logicalOr:' } logical_and: expr AND expr { return binary val[0], val[2], 'logicalAnd:' } greater_than: expr GT expr { return binary val[0], val[2], 'isGreaterThan:' } less_than: expr LT expr { return binary val[0], val[2], 'isLessThan:' } greater_or_eq: expr GTE expr { return binary val[0], val[2], 'isGreaterOrEqualTo:' } less_or_eq: expr LTE expr { return binary val[0], val[2], 'isLessOrEqualTo:' } unary_op: unary_not | unary_plus | unary_minus | unary_complement unary_not: BANG expr { return unary val[1], 'unaryNot' } unary_plus: PLUS expr { return unary val[1], 'unaryPlus' } unary_minus: MINUS expr { return unary val[1], 'unaryMinus' } unary_complement: TILDE expr { return unary val[1], 'unaryComplement' } literal: integer | float | string | nil | true | false | self float: FLOAT { return n :Float, val[0] } integer: INTEGER { return n :Integer, val[0] } nil: NIL { return n :Nil } true: TRUE { return n :True } false: FALSE { return n :False } self: SELF { return n :Self } string: STRING { return n :String, val[0] } | interpolated_string | empty_string interpolated_string: DOUBLE_TICK_STRING interpolated_string_contents DOUBLE_TICK_STRING_END { return val[1] } interpolation: INTERPOLATE_START expr INTERPOLATE_END { return val[1] } interpolated_string_contents: interpolated_string_chunk { return n :InterpolatedString, val[0] } | interpolated_string_contents interpolated_string_chunk { val[0].append(val[1]); return val[0] } interpolated_string_chunk: chars { return val[0] } | interpolation { return to_string(val[0]) } empty_string: DOUBLE_TICK_STRING DOUBLE_TICK_STRING_END { return n :String, '' } chars: CHAR { return n :String, val[0] } | chars CHAR { val[0].append(val[1]); return val[0] } end ---- inner attr_accessor :lexer, :scopes, :state def initialize lexer @lexer = lexer @state = [] @scopes = [] push_scope end def ast @ast ||= do_parse @scopes.first end def on_error t, val, vstack line = lexer.line col = lexer.column message = "Unexpected #{token_to_str t} at #{lexer.filename} line #{line}:#{col}:\n\n" start = line - 5 > 0 ? line - 5 : 0 i_size = line.to_s.size (start..(start + 5)).each do |i| message << sprintf("\t%#{i_size}d: %s\n", i, lexer.get_line(i)) message << "\t#{' ' * i_size} #{'-' * (col - 1)}^\n" if i == line end raise SyntaxError, message end def next_token nt = lexer.next_computed_token # just use a state stack for now, we'll have to do something # more sophisticated soon. if nt && nt.first == :state if nt.last state.push << nt.last else state.pop end next_token else nt end end def push_scope new_scope = Huia::AST::Scope.new scope new_scope.file = lexer.filename new_scope.line = lexer.line new_scope.column = lexer.column scopes.push new_scope new_scope end def pop_scope scopes.pop end def scope scopes.last end def binary left, right, method node(:MethodCall, left, node(:CallSignature, method, [right])) end def unary left, method node(:MethodCall, left, node(:CallSignature, method)) end def node type, *args Huia::AST.const_get(type).new(*args).tap do |n| n.file = lexer.filename n.line = lexer.line n.column = lexer.column end end alias n node def allocate_local name node(:Variable, name).tap do |n| scope.allocate_local n end end def allocate_local_assignment name, value node(:Assignment, name, value).tap do |n| scope.allocate_local n end end def this_closure allocate_local('@') end def scope_instance node(:ScopeInstance, scope) end def constant name return scope_instance if name == 'self' node(:Constant, name) end def to_string expr node(:MethodCall, expr, node(:CallSignature, 'toString')) end racc-1.6.2/test/assets/ichk.y000066400000000000000000000032411435117461300160260ustar00rootroot00000000000000class Calculator prechigh left '*' '/' left '+' '-' preclow convert NUMBER 'Number' end rule target : exp | /* none */ { result = 0 } exp : exp '+' exp { result += val[2]; a = 'plus' } | exp '-' exp { result -= val[2]; a = "string test" } | exp '*' exp { result *= val[2] } | exp '/' exp { result /= val[2] } | '(' { $emb = true } exp ')' { raise 'must not happen' unless $emb result = val[2] } | '-' NUMBER { result = -val[1] } | NUMBER ----header class Number end ----inner def initialize @racc_debug_out = $stdout @yydebug = false end def validate(expected, src) result = parse(src) unless result == expected raise "test #{@test_number} fail" end @test_number += 1 end def parse(src) @src = src @test_number = 1 yyparse self, :scan end def scan(&block) @src.each(&block) end ----footer calc = Calculator.new calc.validate(9, [[Number, 9], nil]) calc.validate(-3, [[Number, 5], ['*', '*'], [Number, 1], ['-', '*'], [Number, 1], ['*', '*'], [Number, 8], nil]) calc.validate(-1, [[Number, 5], ['+', '+'], [Number, 2], ['-', '-'], [Number, 5], ['+', '+'], [Number, 2], ['-', '-'], [Number, 5], nil]) calc.validate(-4, [['-', 'UMINUS'], [Number, 4], nil]) calc.validate(40, [[Number, 7], ['*', '*'], ['(', '('], [Number, 4], ['+', '+'], [Number, 3], [')', ')'], ['-', '-'], [Number, 9], nil]) racc-1.6.2/test/assets/ifelse.y000066400000000000000000000003661435117461300163640ustar00rootroot00000000000000class C::Parser token tSOMETHING rule statement : tSOMETHING | 'if' statement 'then' statement | 'if' statement 'then' statement 'else' statement ; dummy : tSOMETHING '+' tSOMETHING | tSOMETHING '-' tSOMETHING ; racc-1.6.2/test/assets/intp.y000066400000000000000000000256461435117461300160770ustar00rootroot00000000000000# # intp # class Intp::Parser prechigh nonassoc UMINUS left '*' '/' left '+' '-' nonassoc EQ preclow rule program : stmt_list { result = RootNode.new( val[0] ) } stmt_list : { result = [] } | stmt_list stmt EOL { result.push val[1] } | stmt_list EOL stmt : expr | assign | IDENT realprim { result = FuncallNode.new( @fname, val[0][0], val[0][1], [val[1]] ) } | if_stmt | while_stmt | defun if_stmt : IF stmt THEN EOL stmt_list else_stmt END { result = IfNode.new( @fname, val[0][0], val[1], val[4], val[5] ) } else_stmt : ELSE EOL stmt_list { result = val[2] } | { result = nil } while_stmt: WHILE stmt DO EOL stmt_list END { result = WhileNode.new(@fname, val[0][0], val[1], val[4]) } defun : DEF IDENT param EOL stmt_list END { result = DefNode.new(@fname, val[0][0], val[1][1], Function.new(@fname, val[0][0], val[2], val[4])) } param : '(' name_list ')' { result = val[1] } | '(' ')' { result = [] } | { result = [] } name_list : IDENT { result = [ val[0][1] ] } | name_list ',' IDENT { result.push val[2][1] } assign : IDENT '=' expr { result = AssignNode.new(@fname, val[0][0], val[0][1], val[2]) } expr : expr '+' expr { result = FuncallNode.new(@fname, val[0].lineno, '+', [val[0], val[2]]) } | expr '-' expr { result = FuncallNode.new(@fname, val[0].lineno, '-', [val[0], val[2]]) } | expr '*' expr { result = FuncallNode.new(@fname, val[0].lineno, '*', [val[0], val[2]]) } | expr '/' expr { result = FuncallNode.new(@fname, val[0].lineno, '/', [val[0], val[2]]) } | expr EQ expr { result = FuncallNode.new(@fname, val[0].lineno, '==', [val[0], val[2]]) } | primary primary : realprim | '(' expr ')' { result = val[1] } | '-' expr =UMINUS { result = FuncallNode.new(@fname, val[0][0], '-@', [val[1]]) } realprim : IDENT { result = VarRefNode.new(@fname, val[0][0], val[0][1]) } | NUMBER { result = LiteralNode.new(@fname, *val[0]) } | STRING { result = StringNode.new(@fname, *val[0]) } | TRUE { result = LiteralNode.new(@fname, *val[0]) } | FALSE { result = LiteralNode.new(@fname, *val[0]) } | NIL { result = LiteralNode.new(@fname, *val[0]) } | funcall funcall : IDENT '(' args ')' { result = FuncallNode.new(@fname, val[0][0], val[0][1], val[2]) } | IDENT '(' ')' { result = FuncallNode.new(@fname, val[0][0], val[0][1], []) } args : expr { result = val } | args ',' expr { result.push val[2] } end ---- header # # intp/parser.rb # ---- inner def initialize @scope = {} end RESERVED = { 'if' => :IF, 'else' => :ELSE, 'while' => :WHILE, 'then' => :THEN, 'do' => :DO, 'def' => :DEF, 'true' => :TRUE, 'false' => :FALSE, 'nil' => :NIL, 'end' => :END } RESERVED_V = { 'true' => true, 'false' => false, 'nil' => nil } def parse(f, fname) @q = [] @fname = fname lineno = 1 f.each do |line| line.strip! until line.empty? case line when /\A\s+/, /\A\#.*/ ; when /\A[a-zA-Z_]\w*/ word = $& @q.push [(RESERVED[word] || :IDENT), [lineno, RESERVED_V.key?(word) ? RESERVED_V[word] : word.intern]] when /\A\d+/ @q.push [:NUMBER, [lineno, $&.to_i]] when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/ @q.push [:STRING, [lineno, eval($&)]] when /\A==/ @q.push [:EQ, [lineno, '==']] when /\A./ @q.push [$&, [lineno, $&]] else raise RuntimeError, 'must not happen' end line = $' end @q.push [:EOL, [lineno, nil]] lineno += 1 end @q.push [false, '$'] do_parse end def next_token @q.shift end def on_error(t, v, values) if v line = v[0] v = v[1] else line = 'last' end raise Racc::ParseError, "#{@fname}:#{line}: syntax error on #{v.inspect}" end ---- footer # intp/node.rb module Intp class IntpError < StandardError; end class IntpArgumentError < IntpError; end class Core def initialize @ftab = {} @obj = Object.new @stack = [] @stack.push Frame.new '(toplevel)' end def frame @stack[-1] end def define_function(fname, node) raise IntpError, "function #{fname} defined twice" if @ftab.key?(fname) @ftab[fname] = node end def call_function_or(fname, args) call_intp_function_or(fname, args) { call_ruby_toplevel_or(fname, args) { yield } } end def call_intp_function_or(fname, args) if func = @ftab[fname] frame = Frame.new(fname) @stack.push frame func.call self, frame, args @stack.pop else yield end end def call_ruby_toplevel_or(fname, args) if @obj.respond_to? fname, true @obj.send fname, *args else yield end end end class Frame def initialize(fname) @fname = fname @lvars = {} end attr :fname def lvar?(name) @lvars.key? name end def [](key) @lvars[key] end def []=(key, val) @lvars[key] = val end end class Node def initialize(fname, lineno) @filename = fname @lineno = lineno end attr_reader :filename attr_reader :lineno def exec_list(intp, nodes) v = nil nodes.each {|i| v = i.evaluate(intp) } v end def intp_error!(msg) raise IntpError, "in #{filename}:#{lineno}: #{msg}" end def inspect "#{self.class.name}/#{lineno}" end end class RootNode < Node def initialize(tree) super nil, nil @tree = tree end def evaluate exec_list Core.new, @tree end end class DefNode < Node def initialize(file, lineno, fname, func) super file, lineno @funcname = fname @funcobj = func end def evaluate(intp) intp.define_function @funcname, @funcobj end end class FuncallNode < Node def initialize(file, lineno, func, args) super file, lineno @funcname = func @args = args end def evaluate(intp) args = @args.map {|i| i.evaluate intp } begin intp.call_intp_function_or(@funcname, args) { if args.empty? or not args[0].respond_to?(@funcname) intp.call_ruby_toplevel_or(@funcname, args) { intp_error! "undefined function #{@funcname.id2name}" } else recv = args.shift recv.send @funcname, *args end } rescue IntpArgumentError, ArgumentError intp_error! $!.message end end end class Function < Node def initialize(file, lineno, params, body) super file, lineno @params = params @body = body end def call(intp, frame, args) unless args.size == @params.size raise IntpArgumentError, "wrong # of arg for #{frame.fname}() (#{args.size} for #{@params.size})" end args.each_with_index do |v,i| frame[@params[i]] = v end exec_list intp, @body end end class IfNode < Node def initialize(fname, lineno, cond, tstmt, fstmt) super fname, lineno @condition = cond @tstmt = tstmt @fstmt = fstmt end def evaluate(intp) if @condition.evaluate(intp) exec_list intp, @tstmt else exec_list intp, @fstmt if @fstmt end end end class WhileNode < Node def initialize(fname, lineno, cond, body) super fname, lineno @condition = cond @body = body end def evaluate(intp) while @condition.evaluate(intp) exec_list intp, @body end end end class AssignNode < Node def initialize(fname, lineno, vname, val) super fname, lineno @vname = vname @val = val end def evaluate(intp) intp.frame[@vname] = @val.evaluate(intp) end end class VarRefNode < Node def initialize(fname, lineno, vname) super fname, lineno @vname = vname end def evaluate(intp) if intp.frame.lvar?(@vname) intp.frame[@vname] else intp.call_function_or(@vname, []) { intp_error! "unknown method or local variable #{@vname.id2name}" } end end end class StringNode < Node def initialize(fname, lineno, str) super fname, lineno @val = str end def evaluate(intp) @val.dup end end class LiteralNode < Node def initialize(fname, lineno, val) super fname, lineno @val = val end def evaluate(intp) @val end end end # module Intp begin tree = nil fname = 'src.intp' File.open(fname) {|f| tree = Intp::Parser.new.parse(f, fname) } tree.evaluate rescue Racc::ParseError, Intp::IntpError, Errno::ENOENT raise #### $stderr.puts "#{File.basename $0}: #{$!}" exit 1 end racc-1.6.2/test/assets/journey.y000066400000000000000000000016411435117461300166050ustar00rootroot00000000000000class Journey::Parser token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR rule expressions : expressions expression { result = Cat.new(val.first, val.last) } | expression { result = val.first } | or ; expression : terminal | group | star ; group : LPAREN expressions RPAREN { result = Group.new(val[1]) } ; or : expressions OR expression { result = Or.new([val.first, val.last]) } ; star : STAR { result = Star.new(Symbol.new(val.last)) } ; terminal : symbol | literal | slash | dot ; slash : SLASH { result = Slash.new('/') } ; symbol : SYMBOL { result = Symbol.new(val.first) } ; literal : LITERAL { result = Literal.new(val.first) } dot : DOT { result = Dot.new(val.first) } ; end ---- header require 'journey/parser_extras' racc-1.6.2/test/assets/liquor.y000066400000000000000000000214551435117461300164320ustar00rootroot00000000000000# Copyright (c) 2012-2013 Peter Zotov # 2012 Yaroslav Markin # 2012 Nate Gadgibalaev # # MIT License # # 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. class Liquor::Parser token comma dot endtag ident integer keyword lblock lblock2 lbracket linterp lparen op_div op_eq op_gt op_geq op_lt op_leq op_minus op_mod op_mul op_neq op_not op_plus pipe plaintext rblock rbracket rinterp rparen string tag_ident prechigh left dot nonassoc op_uminus op_not left op_mul op_div op_mod left op_plus op_minus left op_eq op_neq op_lt op_leq op_gt op_geq left op_and left op_or preclow expect 15 start block rule block: /* empty */ { result = [] } | plaintext block { result = [ val[0], *val[1] ] } | interp block { result = [ val[0], *val[1] ] } | tag block { result = [ val[0], *val[1] ] } interp: linterp expr rinterp { result = [ :interp, retag(val), val[1] ] } | linterp filter_chain rinterp { result = [ :interp, retag(val), val[1] ] } primary_expr: ident | lparen expr rparen { result = [ val[1][0], retag(val), *val[1][2..-1] ] } expr: integer | string | tuple | ident function_args { result = [ :call, retag(val), val[0], val[1] ] } | expr lbracket expr rbracket { result = [ :index, retag(val), val[0], val[2] ] } | expr dot ident function_args { result = [ :external, retag(val), val[0], val[2], val[3] ] } | expr dot ident { result = [ :external, retag(val), val[0], val[2], nil ] } | op_minus expr =op_uminus { result = [ :uminus, retag(val), val[1] ] } | op_not expr { result = [ :not, retag(val), val[1] ] } | expr op_mul expr { result = [ :mul, retag(val), val[0], val[2] ] } | expr op_div expr { result = [ :div, retag(val), val[0], val[2] ] } | expr op_mod expr { result = [ :mod, retag(val), val[0], val[2] ] } | expr op_plus expr { result = [ :plus, retag(val), val[0], val[2] ] } | expr op_minus expr { result = [ :minus, retag(val), val[0], val[2] ] } | expr op_eq expr { result = [ :eq, retag(val), val[0], val[2] ] } | expr op_neq expr { result = [ :neq, retag(val), val[0], val[2] ] } | expr op_lt expr { result = [ :lt, retag(val), val[0], val[2] ] } | expr op_leq expr { result = [ :leq, retag(val), val[0], val[2] ] } | expr op_gt expr { result = [ :gt, retag(val), val[0], val[2] ] } | expr op_geq expr { result = [ :geq, retag(val), val[0], val[2] ] } | expr op_and expr { result = [ :and, retag(val), val[0], val[2] ] } | expr op_or expr { result = [ :or, retag(val), val[0], val[2] ] } | primary_expr tuple: lbracket tuple_content rbracket { result = [ :tuple, retag(val), val[1].compact ] } tuple_content: expr comma tuple_content { result = [ val[0], *val[2] ] } | expr { result = [ val[0] ] } | /* empty */ { result = [ ] } function_args: lparen function_args_inside rparen { result = [ :args, retag(val), *val[1] ] } function_args_inside: expr function_keywords { result = [ val[0], val[1][2] ] } | function_keywords { result = [ nil, val[0][2] ] } function_keywords: keyword expr function_keywords { name = val[0][2].to_sym tail = val[2][2] loc = retag([ val[0], val[1] ]) if tail.include? name @errors << SyntaxError.new("duplicate keyword argument `#{val[0][2]}'", tail[name][1]) end hash = { name => [ val[1][0], loc, *val[1][2..-1] ] }.merge(tail) result = [ :keywords, retag([ loc, val[2] ]), hash ] } | /* empty */ { result = [ :keywords, nil, {} ] } filter_chain: expr pipe filter_chain_cont { result = [ val[0], *val[2] ]. reduce { |tree, node| node[3][2] = tree; node } } filter_chain_cont: filter_call pipe filter_chain_cont { result = [ val[0], *val[2] ] } | filter_call { result = [ val[0] ] } filter_call: ident function_keywords { ident_loc = val[0][1] empty_args_loc = { line: ident_loc[:line], start: ident_loc[:end] + 1, end: ident_loc[:end] + 1, } result = [ :call, val[0][1], val[0], [ :args, val[1][1] || empty_args_loc, nil, val[1][2] ] ] } tag: lblock ident expr tag_first_cont { result = [ :tag, retag(val), val[1], val[2], *reduce_tag_args(val[3][2]) ] } | lblock ident tag_first_cont { result = [ :tag, retag(val), val[1], nil, *reduce_tag_args(val[2][2]) ] } # Racc cannot do lookahead across rules. I had to add states # explicitly to avoid S/R conflicts. You are not expected to # understand this. tag_first_cont: rblock { result = [ :cont, retag(val), [] ] } | keyword tag_first_cont2 { result = [ :cont, retag(val), [ val[0], *val[1][2] ] ] } tag_first_cont2: rblock block lblock2 tag_next_cont { result = [ :cont2, val[0][1], [ [:block, val[0][1], val[1] ], *val[3] ] ] } | expr tag_first_cont { result = [ :cont2, retag(val), [ val[0], *val[1][2] ] ] } tag_next_cont: endtag rblock { result = [] } | keyword tag_next_cont2 { result = [ val[0], *val[1] ] } tag_next_cont2: rblock block lblock2 tag_next_cont { result = [ [:block, val[0][1], val[1] ], *val[3] ] } | expr keyword tag_next_cont3 { result = [ val[0], val[1], *val[2] ] } tag_next_cont3: rblock block lblock2 tag_next_cont { result = [ [:block, val[0][1], val[1] ], *val[3] ] } | expr tag_next_cont { result = [ val[0], *val[1] ] } ---- inner attr_reader :errors, :ast def initialize(tags={}) super() @errors = [] @ast = nil @tags = tags end def success? @errors.empty? end def parse(string, name='(code)') @errors.clear @name = name @ast = nil begin @stream = Lexer.lex(string, @name, @tags) @ast = do_parse rescue Liquor::SyntaxError => e @errors << e end success? end def next_token tok = @stream.shift [ tok[0], tok ] if tok end TOKEN_NAME_MAP = { :comma => ',', :dot => '.', :lblock => '{%', :rblock => '%}', :linterp => '{{', :rinterp => '}}', :lbracket => '[', :rbracket => ']', :lparen => '(', :rparen => ')', :pipe => '|', :op_not => '!', :op_mul => '*', :op_div => '/', :op_mod => '%', :op_plus => '+', :op_minus => '-', :op_eq => '==', :op_neq => '!=', :op_lt => '<', :op_leq => '<=', :op_gt => '>', :op_geq => '>=', :keyword => 'keyword argument name', :kwarg => 'keyword argument', :ident => 'identifier', } def on_error(error_token_id, error_token, value_stack) if token_to_str(error_token_id) == "$end" raise Liquor::SyntaxError.new("unexpected end of program", { file: @name }) else type, (loc, value) = error_token type = TOKEN_NAME_MAP[type] || type raise Liquor::SyntaxError.new("unexpected token `#{type}'", loc) end end def retag(nodes) loc = nodes.map { |node| node[1] }.compact first, *, last = loc return first if last.nil? { file: first[:file], line: first[:line], start: first[:start], end: last[:end], } end def reduce_tag_args(list) list.each_slice(2).reduce([]) { |args, (k, v)| if v[0] == :block args << [ :blockarg, retag([ k, v ]), k, v[2] || [] ] else args << [ :kwarg, retag([ k, v ]), k, v ] end } endracc-1.6.2/test/assets/machete.y000066400000000000000000000250611435117461300165220ustar00rootroot00000000000000# Copyright (c) 2011 SUSE # # 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. class Machete::Parser token NIL token TRUE token FALSE token INTEGER token SYMBOL token STRING token REGEXP token ANY token EVEN token ODD token METHOD_NAME token CLASS_NAME start expression rule expression : primary | expression "|" primary { result = if val[0].is_a?(ChoiceMatcher) ChoiceMatcher.new(val[0].alternatives << val[2]) else ChoiceMatcher.new([val[0], val[2]]) end } primary : node | array | literal | any node : CLASS_NAME { result = NodeMatcher.new(val[0].to_sym) } | CLASS_NAME "<" attrs ">" { result = NodeMatcher.new(val[0].to_sym, val[2]) } attrs : attr | attrs "," attr { result = val[0].merge(val[2]) } attr : method_name "=" expression { result = { val[0].to_sym => val[2] } } | method_name "^=" SYMBOL { result = { val[0].to_sym => SymbolRegexpMatcher.new( Regexp.new("^" + Regexp.escape(symbol_value(val[2]).to_s)) ) } } | method_name "$=" SYMBOL { result = { val[0].to_sym => SymbolRegexpMatcher.new( Regexp.new(Regexp.escape(symbol_value(val[2]).to_s) + "$") ) } } | method_name "*=" SYMBOL { result = { val[0].to_sym => SymbolRegexpMatcher.new( Regexp.new(Regexp.escape(symbol_value(val[2]).to_s)) ) } } | method_name "^=" STRING { result = { val[0].to_sym => StringRegexpMatcher.new( Regexp.new("^" + Regexp.escape(string_value(val[2]))) ) } } | method_name "$=" STRING { result = { val[0].to_sym => StringRegexpMatcher.new( Regexp.new(Regexp.escape(string_value(val[2])) + "$") ) } } | method_name "*=" STRING { result = { val[0].to_sym => StringRegexpMatcher.new( Regexp.new(Regexp.escape(string_value(val[2]))) ) } } | method_name "*=" REGEXP { result = { val[0].to_sym => IndifferentRegexpMatcher.new( Regexp.new(regexp_value(val[2])) ) } } # Hack to overcome the fact that some tokens will lex as simple tokens, not # METHOD_NAME tokens, and that "reserved words" will lex as separate kinds of # tokens. method_name : METHOD_NAME | NIL | TRUE | FALSE | ANY | EVEN | ODD | "*" | "+" | "<" | ">" | "^" | "|" array : "[" items_opt "]" { result = ArrayMatcher.new(val[1]) } items_opt : /* empty */ { result = [] } | items items : item { result = [val[0]] } | items "," item { result = val[0] << val[2] } item : expression | expression quantifier { result = Quantifier.new(val[0], *val[1]) } quantifier : "*" { result = [0, nil, 1] } | "+" { result = [1, nil, 1] } | "?" { result = [0, 1, 1] } | "{" INTEGER "}" { result = [integer_value(val[1]), integer_value(val[1]), 1] } | "{" INTEGER "," "}" { result = [integer_value(val[1]), nil, 1] } | "{" "," INTEGER "}" { result = [0, integer_value(val[2]), 1] } | "{" INTEGER "," INTEGER "}" { result = [integer_value(val[1]), integer_value(val[3]), 1] } | "{" EVEN "}" { result = [0, nil, 2] } | "{" ODD "}" { result = [1, nil, 2] } literal : NIL { result = LiteralMatcher.new(nil) } | TRUE { result = LiteralMatcher.new(true) } | FALSE { result = LiteralMatcher.new(false) } | INTEGER { result = LiteralMatcher.new(integer_value(val[0])) } | SYMBOL { result = LiteralMatcher.new(symbol_value(val[0])) } | STRING { result = LiteralMatcher.new(string_value(val[0])) } | REGEXP { result = LiteralMatcher.new(regexp_value(val[0])) } any : ANY { result = AnyMatcher.new } ---- inner include Matchers class SyntaxError < StandardError; end def parse(input) @input = input @pos = 0 do_parse end private def integer_value(value) if value =~ /^0[bB]/ value[2..-1].to_i(2) elsif value =~ /^0[oO]/ value[2..-1].to_i(8) elsif value =~ /^0[dD]/ value[2..-1].to_i(10) elsif value =~ /^0[xX]/ value[2..-1].to_i(16) elsif value =~ /^0/ value.to_i(8) else value.to_i end end def symbol_value(value) value[1..-1].to_sym end def string_value(value) quote = value[0..0] if quote == "'" value[1..-2].gsub("\\\\", "\\").gsub("\\'", "'") elsif quote == '"' value[1..-2]. gsub("\\\\", "\\"). gsub('\\"', '"'). gsub("\\n", "\n"). gsub("\\t", "\t"). gsub("\\r", "\r"). gsub("\\f", "\f"). gsub("\\v", "\v"). gsub("\\a", "\a"). gsub("\\e", "\e"). gsub("\\b", "\b"). gsub("\\s", "\s"). gsub(/\\([0-7]{1,3})/) { $1.to_i(8).chr }. gsub(/\\x([0-9a-fA-F]{1,2})/) { $1.to_i(16).chr } else raise "Unknown quote: #{quote.inspect}." end end REGEXP_OPTIONS = { 'i' => Regexp::IGNORECASE, 'm' => Regexp::MULTILINE, 'x' => Regexp::EXTENDED } def regexp_value(value) /\A\/(.*)\/([imx]*)\z/ =~ value pattern, options = $1, $2 Regexp.new(pattern, options.chars.map { |ch| REGEXP_OPTIONS[ch] }.inject(:|)) end # "^" needs to be here because if it were among operators recognized by # METHOD_NAME, "^=" would be recognized as two tokens. SIMPLE_TOKENS = [ "|", "<", ">", ",", "=", "^=", "^", "$=", "[", "]", "*=", "*", "+", "?", "{", "}" ] COMPLEX_TOKENS = [ [:NIL, /^nil/], [:TRUE, /^true/], [:FALSE, /^false/], # INTEGER needs to be before METHOD_NAME, otherwise e.g. "+1" would be # recognized as two tokens. [ :INTEGER, /^ [+-]? # sign ( 0[bB][01]+(_[01]+)* # binary (prefixed) | 0[oO][0-7]+(_[0-7]+)* # octal (prefixed) | 0[dD]\d+(_\d+)* # decimal (prefixed) | 0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)* # hexadecimal (prefixed) | 0[0-7]*(_[0-7]+)* # octal (unprefixed) | [1-9]\d*(_\d+)* # decimal (unprefixed) ) /x ], [ :SYMBOL, /^ : ( # class name [A-Z][a-zA-Z0-9_]* | # regular method name [a-z_][a-zA-Z0-9_]*[?!=]? | # instance variable name @[a-zA-Z_][a-zA-Z0-9_]* | # class variable name @@[a-zA-Z_][a-zA-Z0-9_]* | # operator (sorted by length, then alphabetically) (<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&*+\-\/<>^`|~]) ) /x ], [ :STRING, /^ ( ' # sinqle-quoted string ( \\[\\'] # escape | [^'] # regular character )* ' | " # double-quoted string ( \\ # escape ( [\\"ntrfvaebs] # one-character escape | [0-7]{1,3} # octal number escape | x[0-9a-fA-F]{1,2} # hexadecimal number escape ) | [^"] # regular character )* " ) /x ], [ :REGEXP, /^ \/ ( \\ # escape ( [\\\/ntrfvaebs\(\)\[\]\{\}\-\.\?\*\+\|\^\$] # one-character escape | [0-7]{2,3} # octal number escape | x[0-9a-fA-F]{1,2} # hexadecimal number escape ) | [^\/] # regular character )* \/ [imx]* /x ], # ANY, EVEN and ODD need to be before METHOD_NAME, otherwise they would be # recognized as method names. [:ANY, /^any/], [:EVEN, /^even/], [:ODD, /^odd/], # We exclude "*", "+", "<", ">", "^" and "|" from method names since they are # lexed as simple tokens. This is because they have also other meanings in # Machette patterns beside Ruby method names. [ :METHOD_NAME, /^ ( # regular name [a-z_][a-zA-Z0-9_]*[?!=]? | # operator (sorted by length, then alphabetically) (<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&\-\/`~]) ) /x ], [:CLASS_NAME, /^[A-Z][a-zA-Z0-9_]*/] ] def next_token skip_whitespace return false if remaining_input.empty? # Complex tokens need to be before simple tokens, otherwise e.g. "<<" would be # recognized as two tokens. COMPLEX_TOKENS.each do |type, regexp| if remaining_input =~ regexp @pos += $&.length return [type, $&] end end SIMPLE_TOKENS.each do |token| if remaining_input[0...token.length] == token @pos += token.length return [token, token] end end raise SyntaxError, "Unexpected character: #{remaining_input[0..0].inspect}." end def skip_whitespace if remaining_input =~ /\A^[ \t\r\n]+/ @pos += $&.length end end def remaining_input @input[@pos..-1] end def on_error(error_token_id, error_value, value_stack) raise SyntaxError, "Unexpected token: #{error_value.inspect}." end racc-1.6.2/test/assets/macruby.y000066400000000000000000002320301435117461300165520ustar00rootroot00000000000000# Copyright (c) 2013 Peter Zotov # # Parts of the source are derived from ruby_parser: # Copyright (c) Ryan Davis, seattle.rb # # MIT License # # 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. class Parser::MacRuby token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS kTHEN kELSIF kELSE kCASE kWHEN kWHILE kUNTIL kFOR kBREAK kNEXT kREDO kRETRY kIN kDO kDO_COND kDO_BLOCK kDO_LAMBDA kRETURN kYIELD kSUPER kSELF kNIL kTRUE kFALSE kAND kOR kNOT kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD kRESCUE_MOD kALIAS kDEFINED klBEGIN klEND k__LINE__ k__FILE__ k__ENCODING__ tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tLABEL tCVAR tNTH_REF tBACK_REF tSTRING_CONTENT tINTEGER tFLOAT tREGEXP_END tUPLUS tUMINUS tUMINUS_NUM tPOW tCMP tEQ tEQQ tNEQ tGEQ tLEQ tANDOP tOROP tMATCH tNMATCH tDOT tDOT2 tDOT3 tAREF tASET tLSHFT tRSHFT tCOLON2 tCOLON3 tOP_ASGN tASSOC tLPAREN tLPAREN2 tRPAREN tLPAREN_ARG tLBRACK tLBRACK2 tRBRACK tLBRACE tLBRACE_ARG tSTAR tSTAR2 tAMPER tAMPER2 tTILDE tPERCENT tDIVIDE tPLUS tMINUS tLT tGT tPIPE tBANG tCARET tLCURLY tRCURLY tBACK_REF2 tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tREGEXP_OPT tWORDS_BEG tQWORDS_BEG tSTRING_DBEG tSTRING_DVAR tSTRING_END tSTRING tSYMBOL tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER prechigh right tBANG tTILDE tUPLUS right tPOW right tUMINUS_NUM tUMINUS left tSTAR2 tDIVIDE tPERCENT left tPLUS tMINUS left tLSHFT tRSHFT left tAMPER2 left tPIPE tCARET left tGT tGEQ tLT tLEQ nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH left tANDOP left tOROP nonassoc tDOT2 tDOT3 right tEH tCOLON left kRESCUE_MOD right tEQL tOP_ASGN nonassoc kDEFINED right kNOT left kOR kAND nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD nonassoc tLBRACE_ARG nonassoc tLOWEST preclow rule program: top_compstmt top_compstmt: top_stmts opt_terms { result = @builder.compstmt(val[0]) } top_stmts: # nothing { result = [] } | top_stmt { result = [ val[0] ] } | top_stmts terms top_stmt { result = val[0] << val[2] } | error top_stmt { result = [ val[1] ] } top_stmt: stmt | klBEGIN tLCURLY top_compstmt tRCURLY { result = @builder.preexe(val[0], val[1], val[2], val[3]) } bodystmt: compstmt opt_rescue opt_else opt_ensure { rescue_bodies = val[1] else_t, else_ = val[2] ensure_t, ensure_ = val[3] if rescue_bodies.empty? && !else_.nil? diagnostic :warning, :useless_else, nil, else_t end result = @builder.begin_body(val[0], rescue_bodies, else_t, else_, ensure_t, ensure_) } compstmt: stmts opt_terms { result = @builder.compstmt(val[0]) } stmts: # nothing { result = [] } | stmt { result = [ val[0] ] } | stmts terms stmt { result = val[0] << val[2] } | error stmt { result = [ val[1] ] } stmt: kALIAS fitem { @lexer.state = :expr_fname } fitem { result = @builder.alias(val[0], val[1], val[3]) } | kALIAS tGVAR tGVAR { result = @builder.alias(val[0], @builder.gvar(val[1]), @builder.gvar(val[2])) } | kALIAS tGVAR tBACK_REF { result = @builder.alias(val[0], @builder.gvar(val[1]), @builder.back_ref(val[2])) } | kALIAS tGVAR tNTH_REF { diagnostic :error, :nth_ref_alias, nil, val[2] } | kUNDEF undef_list { result = @builder.undef_method(val[0], val[1]) } | stmt kIF_MOD expr_value { result = @builder.condition_mod(val[0], nil, val[1], val[2]) } | stmt kUNLESS_MOD expr_value { result = @builder.condition_mod(nil, val[0], val[1], val[2]) } | stmt kWHILE_MOD expr_value { result = @builder.loop_mod(:while, val[0], val[1], val[2]) } | stmt kUNTIL_MOD expr_value { result = @builder.loop_mod(:until, val[0], val[1], val[2]) } | stmt kRESCUE_MOD stmt { rescue_body = @builder.rescue_body(val[1], nil, nil, nil, nil, val[2]) result = @builder.begin_body(val[0], [ rescue_body ]) } | klEND tLCURLY compstmt tRCURLY { result = @builder.postexe(val[0], val[1], val[2], val[3]) } | lhs tEQL command_call { result = @builder.assign(val[0], val[1], val[2]) } | mlhs tEQL command_call { result = @builder.multi_assign(val[0], val[1], val[2]) } | var_lhs tOP_ASGN command_call { result = @builder.op_assign(val[0], val[1], val[2]) } | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN command_call { result = @builder.op_assign( @builder.index( val[0], val[1], val[2], val[3]), val[4], val[5]) } | primary_value tDOT tIDENTIFIER tOP_ASGN command_call { result = @builder.op_assign( @builder.call_method( val[0], val[1], val[2]), val[3], val[4]) } | primary_value tDOT tCONSTANT tOP_ASGN command_call { result = @builder.op_assign( @builder.call_method( val[0], val[1], val[2]), val[3], val[4]) } | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call { result = @builder.op_assign( @builder.call_method( val[0], val[1], val[2]), val[3], val[4]) } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call { result = @builder.op_assign( @builder.call_method( val[0], val[1], val[2]), val[3], val[4]) } | backref tOP_ASGN command_call { @builder.op_assign(val[0], val[1], val[2]) } | lhs tEQL mrhs { result = @builder.assign(val[0], val[1], @builder.array(nil, val[2], nil)) } | mlhs tEQL arg_value { result = @builder.multi_assign(val[0], val[1], val[2]) } | mlhs tEQL mrhs { result = @builder.multi_assign(val[0], val[1], @builder.array(nil, val[2], nil)) } | expr expr: command_call | expr kAND expr { result = @builder.logical_op(:and, val[0], val[1], val[2]) } | expr kOR expr { result = @builder.logical_op(:or, val[0], val[1], val[2]) } | kNOT opt_nl expr { result = @builder.not_op(val[0], nil, val[2], nil) } | tBANG command_call { result = @builder.not_op(val[0], nil, val[1], nil) } | arg expr_value: expr command_call: command | block_command block_command: block_call | block_call tDOT operation2 command_args { result = @builder.call_method(val[0], val[1], val[2], *val[3]) } | block_call tCOLON2 operation2 command_args { result = @builder.call_method(val[0], val[1], val[2], *val[3]) } cmd_brace_block: tLBRACE_ARG { @static_env.extend_dynamic } opt_block_param compstmt tRCURLY { result = [ val[0], val[2], val[3], val[4] ] @static_env.unextend } command: operation command_args =tLOWEST { result = @builder.call_method(nil, nil, val[0], *val[1]) } | operation command_args cmd_brace_block { method_call = @builder.call_method(nil, nil, val[0], *val[1]) begin_t, args, body, end_t = val[2] result = @builder.block(method_call, begin_t, args, body, end_t) } | primary_value tDOT operation2 command_args =tLOWEST { result = @builder.call_method(val[0], val[1], val[2], *val[3]) } | primary_value tDOT operation2 command_args cmd_brace_block { method_call = @builder.call_method(val[0], val[1], val[2], *val[3]) begin_t, args, body, end_t = val[4] result = @builder.block(method_call, begin_t, args, body, end_t) } | primary_value tCOLON2 operation2 command_args =tLOWEST { result = @builder.call_method(val[0], val[1], val[2], *val[3]) } | primary_value tCOLON2 operation2 command_args cmd_brace_block { method_call = @builder.call_method(val[0], val[1], val[2], *val[3]) begin_t, args, body, end_t = val[4] result = @builder.block(method_call, begin_t, args, body, end_t) } | kSUPER command_args { result = @builder.keyword_cmd(:super, val[0], *val[1]) } | kYIELD command_args { result = @builder.keyword_cmd(:yield, val[0], *val[1]) } | kRETURN call_args { result = @builder.keyword_cmd(:return, val[0], nil, val[1], nil) } | kBREAK call_args { result = @builder.keyword_cmd(:break, val[0], nil, val[1], nil) } | kNEXT call_args { result = @builder.keyword_cmd(:next, val[0], nil, val[1], nil) } mlhs: mlhs_basic { result = @builder.multi_lhs(nil, val[0], nil) } | tLPAREN mlhs_inner rparen { result = @builder.begin(val[0], val[1], val[2]) } mlhs_inner: mlhs_basic { result = @builder.multi_lhs(nil, val[0], nil) } | tLPAREN mlhs_inner rparen { result = @builder.multi_lhs(val[0], val[1], val[2]) } mlhs_basic: mlhs_head | mlhs_head mlhs_item { result = val[0]. push(val[1]) } | mlhs_head tSTAR mlhs_node { result = val[0]. push(@builder.splat(val[1], val[2])) } | mlhs_head tSTAR mlhs_node tCOMMA mlhs_post { result = val[0]. push(@builder.splat(val[1], val[2])). concat(val[4]) } | mlhs_head tSTAR { result = val[0]. push(@builder.splat(val[1])) } | mlhs_head tSTAR tCOMMA mlhs_post { result = val[0]. push(@builder.splat(val[1])). concat(val[3]) } | tSTAR mlhs_node { result = [ @builder.splat(val[0], val[1]) ] } | tSTAR mlhs_node tCOMMA mlhs_post { result = [ @builder.splat(val[0], val[1]), *val[3] ] } | tSTAR { result = [ @builder.splat(val[0]) ] } | tSTAR tCOMMA mlhs_post { result = [ @builder.splat(val[0]), *val[2] ] } mlhs_item: mlhs_node | tLPAREN mlhs_inner rparen { result = @builder.begin(val[0], val[1], val[2]) } mlhs_head: mlhs_item tCOMMA { result = [ val[0] ] } | mlhs_head mlhs_item tCOMMA { result = val[0] << val[1] } mlhs_post: mlhs_item { result = [ val[0] ] } | mlhs_post tCOMMA mlhs_item { result = val[0] << val[2] } mlhs_node: variable { result = @builder.assignable(val[0]) } | primary_value tLBRACK2 opt_call_args rbracket { result = @builder.index_asgn(val[0], val[1], val[2], val[3]) } | primary_value tDOT tIDENTIFIER { result = @builder.attr_asgn(val[0], val[1], val[2]) } | primary_value tCOLON2 tIDENTIFIER { result = @builder.attr_asgn(val[0], val[1], val[2]) } | primary_value tDOT tCONSTANT { result = @builder.attr_asgn(val[0], val[1], val[2]) } | primary_value tCOLON2 tCONSTANT { result = @builder.assignable( @builder.const_fetch(val[0], val[1], val[2])) } | tCOLON3 tCONSTANT { result = @builder.assignable( @builder.const_global(val[0], val[1])) } | backref { result = @builder.assignable(val[0]) } lhs: variable { result = @builder.assignable(val[0]) } | primary_value tLBRACK2 opt_call_args rbracket { result = @builder.index_asgn(val[0], val[1], val[2], val[3]) } | primary_value tDOT tIDENTIFIER { result = @builder.attr_asgn(val[0], val[1], val[2]) } | primary_value tCOLON2 tIDENTIFIER { result = @builder.attr_asgn(val[0], val[1], val[2]) } | primary_value tDOT tCONSTANT { result = @builder.attr_asgn(val[0], val[1], val[2]) } | primary_value tCOLON2 tCONSTANT { result = @builder.assignable( @builder.const_fetch(val[0], val[1], val[2])) } | tCOLON3 tCONSTANT { result = @builder.assignable( @builder.const_global(val[0], val[1])) } | backref { result = @builder.assignable(val[0]) } cname: tIDENTIFIER { diagnostic :error, :module_name_const, nil, val[0] } | tCONSTANT cpath: tCOLON3 cname { result = @builder.const_global(val[0], val[1]) } | cname { result = @builder.const(val[0]) } | primary_value tCOLON2 cname { result = @builder.const_fetch(val[0], val[1], val[2]) } fname: tIDENTIFIER | tCONSTANT | tFID | op | reswords fsym: fname { result = @builder.symbol(val[0]) } | symbol fitem: fsym | dsym undef_list: fitem { result = [ val[0] ] } | undef_list tCOMMA { @lexer.state = :expr_fname } fitem { result = val[0] << val[3] } op: tPIPE | tCARET | tAMPER2 | tCMP | tEQ | tEQQ | tMATCH | tNMATCH | tGT | tGEQ | tLT | tLEQ | tNEQ | tLSHFT | tRSHFT | tPLUS | tMINUS | tSTAR2 | tSTAR | tDIVIDE | tPERCENT | tPOW | tBANG | tTILDE | tUPLUS | tUMINUS | tAREF | tASET | tBACK_REF2 reswords: k__LINE__ | k__FILE__ | k__ENCODING__ | klBEGIN | klEND | kALIAS | kAND | kBEGIN | kBREAK | kCASE | kCLASS | kDEF | kDEFINED | kDO | kELSE | kELSIF | kEND | kENSURE | kFALSE | kFOR | kIN | kMODULE | kNEXT | kNIL | kNOT | kOR | kREDO | kRESCUE | kRETRY | kRETURN | kSELF | kSUPER | kTHEN | kTRUE | kUNDEF | kWHEN | kYIELD | kIF | kUNLESS | kWHILE | kUNTIL arg: lhs tEQL arg { result = @builder.assign(val[0], val[1], val[2]) } | lhs tEQL arg kRESCUE_MOD arg { rescue_body = @builder.rescue_body(val[3], nil, nil, nil, nil, val[4]) rescue_ = @builder.begin_body(val[2], [ rescue_body ]) result = @builder.assign(val[0], val[1], rescue_) } | var_lhs tOP_ASGN arg { result = @builder.op_assign(val[0], val[1], val[2]) } | var_lhs tOP_ASGN arg kRESCUE_MOD arg { rescue_body = @builder.rescue_body(val[3], nil, nil, nil, nil, val[4]) rescue_ = @builder.begin_body(val[2], [ rescue_body ]) result = @builder.op_assign(val[0], val[1], rescue_) } | primary_value tLBRACK2 opt_call_args rbracket tOP_ASGN arg { result = @builder.op_assign( @builder.index( val[0], val[1], val[2], val[3]), val[4], val[5]) } | primary_value tDOT tIDENTIFIER tOP_ASGN arg { result = @builder.op_assign( @builder.call_method( val[0], val[1], val[2]), val[3], val[4]) } | primary_value tDOT tCONSTANT tOP_ASGN arg { result = @builder.op_assign( @builder.call_method( val[0], val[1], val[2]), val[3], val[4]) } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg { result = @builder.op_assign( @builder.call_method( val[0], val[1], val[2]), val[3], val[4]) } | primary_value tCOLON2 tCONSTANT tOP_ASGN arg { diagnostic :error, :dynamic_const, nil, val[2], [ val[3] ] } | tCOLON3 tCONSTANT tOP_ASGN arg { diagnostic :error, :dynamic_const, nil, val[1], [ val[2] ] } | backref tOP_ASGN arg { result = @builder.op_assign(val[0], val[1], val[2]) } | arg tDOT2 arg { result = @builder.range_inclusive(val[0], val[1], val[2]) } | arg tDOT3 arg { result = @builder.range_exclusive(val[0], val[1], val[2]) } | arg tPLUS arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tMINUS arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tSTAR2 arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tDIVIDE arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tPERCENT arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tPOW arg { result = @builder.binary_op(val[0], val[1], val[2]) } | tUMINUS_NUM tINTEGER tPOW arg { result = @builder.unary_op(val[0], @builder.binary_op( @builder.integer(val[1]), val[2], val[3])) } | tUMINUS_NUM tFLOAT tPOW arg { result = @builder.unary_op(val[0], @builder.binary_op( @builder.float(val[1]), val[2], val[3])) } | tUPLUS arg { result = @builder.unary_op(val[0], val[1]) } | tUMINUS arg { result = @builder.unary_op(val[0], val[1]) } | arg tPIPE arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tCARET arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tAMPER2 arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tCMP arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tGT arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tGEQ arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tLT arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tLEQ arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tEQ arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tEQQ arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tNEQ arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tMATCH arg { result = @builder.match_op(val[0], val[1], val[2]) } | arg tNMATCH arg { result = @builder.binary_op(val[0], val[1], val[2]) } | tBANG arg { result = @builder.not_op(val[0], nil, val[1], nil) } | tTILDE arg { result = @builder.unary_op(val[0], val[1]) } | arg tLSHFT arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tRSHFT arg { result = @builder.binary_op(val[0], val[1], val[2]) } | arg tANDOP arg { result = @builder.logical_op(:and, val[0], val[1], val[2]) } | arg tOROP arg { result = @builder.logical_op(:or, val[0], val[1], val[2]) } | kDEFINED opt_nl arg { result = @builder.keyword_cmd(:defined?, val[0], nil, [ val[2] ], nil) } | arg tEH arg opt_nl tCOLON arg { result = @builder.ternary(val[0], val[1], val[2], val[4], val[5]) } | primary arg_value: arg aref_args: none | args trailer | args tCOMMA assocs trailer { result = val[0] << @builder.associate(nil, val[2], nil) } | assocs trailer { result = [ @builder.associate(nil, val[0], nil) ] } paren_args: tLPAREN2 opt_call_args rparen { result = val } opt_paren_args: # nothing { result = [ nil, [], nil ] } | paren_args opt_call_args: # nothing { result = [] } | call_args call_args: command { result = [ val[0] ] } | args opt_block_arg { result = val[0].concat(val[1]) } | assocs opt_block_arg { result = [ @builder.associate(nil, val[0], nil) ] result.concat(val[1]) } | args tCOMMA assocs opt_block_arg { assocs = @builder.associate(nil, val[2], nil) result = val[0] << assocs result.concat(val[3]) } | args tCOMMA assocs tCOMMA args opt_block_arg { val[2][-1] = @builder.objc_varargs(val[2][-1], val[4]) assocs = @builder.associate(nil, val[2], nil) result = val[0] << assocs result.concat(val[5]) } | block_arg { result = [ val[0] ] } call_args2: arg_value tCOMMA args opt_block_arg { result = [ val[0], *val[2].concat(val[3]) ] } | arg_value tCOMMA block_arg { result = [ val[0], val[2] ] } | assocs opt_block_arg { result = [ @builder.associate(nil, val[0], nil), *val[1] ] } | arg_value tCOMMA assocs opt_block_arg { result = [ val[0], @builder.associate(nil, val[2], nil), *val[3] ] } | arg_value tCOMMA args tCOMMA assocs opt_block_arg { result = [ val[0], *val[2]. push(@builder.associate(nil, val[4], nil)). concat(val[5]) ] } | block_arg { result = [ val[0] ] } command_args: { result = @lexer.cmdarg.dup @lexer.cmdarg.push(true) } open_args { @lexer.cmdarg = val[0] result = val[1] } open_args: call_args { result = [ nil, val[0], nil ] } | tLPAREN_ARG { @lexer.state = :expr_endarg } rparen { result = [ val[0], [], val[2] ] } | tLPAREN_ARG call_args2 { @lexer.state = :expr_endarg } rparen { result = [ val[0], val[1], val[3] ] } block_arg: tAMPER arg_value { result = @builder.block_pass(val[0], val[1]) } opt_block_arg: tCOMMA block_arg { result = [ val[1] ] } | tCOMMA { result = [] } | # nothing { result = [] } args: arg_value { result = [ val[0] ] } | tSTAR arg_value { result = [ @builder.splat(val[0], val[1]) ] } | args tCOMMA arg_value { result = val[0] << val[2] } | args tCOMMA tSTAR arg_value { result = val[0] << @builder.splat(val[2], val[3]) } mrhs: args tCOMMA arg_value { result = val[0] << val[2] } | args tCOMMA tSTAR arg_value { result = val[0] << @builder.splat(val[2], val[3]) } | tSTAR arg_value { result = [ @builder.splat(val[0], val[1]) ] } primary: literal | strings | xstring | regexp | words | qwords | var_ref | backref | tFID { result = @builder.call_method(nil, nil, val[0]) } | kBEGIN bodystmt kEND { result = @builder.begin_keyword(val[0], val[1], val[2]) } | tLPAREN_ARG expr { @lexer.state = :expr_endarg } rparen { result = @builder.begin(val[0], val[1], val[3]) } | tLPAREN compstmt tRPAREN { result = @builder.begin(val[0], val[1], val[2]) } | primary_value tCOLON2 tCONSTANT { result = @builder.const_fetch(val[0], val[1], val[2]) } | tCOLON3 tCONSTANT { result = @builder.const_global(val[0], val[1]) } | tLBRACK aref_args tRBRACK { result = @builder.array(val[0], val[1], val[2]) } | tLBRACE assoc_list tRCURLY { result = @builder.associate(val[0], val[1], val[2]) } | kRETURN { result = @builder.keyword_cmd(:return, val[0]) } | kYIELD tLPAREN2 call_args rparen { result = @builder.keyword_cmd(:yield, val[0], val[1], val[2], val[3]) } | kYIELD tLPAREN2 rparen { result = @builder.keyword_cmd(:yield, val[0], val[1], [], val[2]) } | kYIELD { result = @builder.keyword_cmd(:yield, val[0]) } | kDEFINED opt_nl tLPAREN2 expr rparen { result = @builder.keyword_cmd(:defined?, val[0], val[2], [ val[3] ], val[4]) } | kNOT tLPAREN2 expr rparen { result = @builder.not_op(val[0], val[1], val[2], val[3]) } | kNOT tLPAREN2 rparen { result = @builder.not_op(val[0], val[1], nil, val[2]) } | operation brace_block { method_call = @builder.call_method(nil, nil, val[0]) begin_t, args, body, end_t = val[1] result = @builder.block(method_call, begin_t, args, body, end_t) } | method_call | method_call brace_block { begin_t, args, body, end_t = val[1] result = @builder.block(val[0], begin_t, args, body, end_t) } | tLAMBDA lambda { lambda_call = @builder.call_lambda(val[0]) args, (begin_t, body, end_t) = val[1] result = @builder.block(lambda_call, begin_t, args, body, end_t) } | kIF expr_value then compstmt if_tail kEND { else_t, else_ = val[4] result = @builder.condition(val[0], val[1], val[2], val[3], else_t, else_, val[5]) } | kUNLESS expr_value then compstmt opt_else kEND { else_t, else_ = val[4] result = @builder.condition(val[0], val[1], val[2], else_, else_t, val[3], val[5]) } | kWHILE { @lexer.cond.push(true) } expr_value do { @lexer.cond.pop } compstmt kEND { result = @builder.loop(:while, val[0], val[2], val[3], val[5], val[6]) } | kUNTIL { @lexer.cond.push(true) } expr_value do { @lexer.cond.pop } compstmt kEND { result = @builder.loop(:until, val[0], val[2], val[3], val[5], val[6]) } | kCASE expr_value opt_terms case_body kEND { *when_bodies, (else_t, else_body) = *val[3] result = @builder.case(val[0], val[1], when_bodies, else_t, else_body, val[4]) } | kCASE opt_terms case_body kEND { *when_bodies, (else_t, else_body) = *val[2] result = @builder.case(val[0], nil, when_bodies, else_t, else_body, val[3]) } | kFOR for_var kIN { @lexer.cond.push(true) } expr_value do { @lexer.cond.pop } compstmt kEND { result = @builder.for(val[0], val[1], val[2], val[4], val[5], val[7], val[8]) } | kCLASS cpath superclass { @static_env.extend_static @lexer.push_cmdarg } bodystmt kEND { if in_def? diagnostic :error, :class_in_def, nil, val[0] end lt_t, superclass = val[2] result = @builder.def_class(val[0], val[1], lt_t, superclass, val[4], val[5]) @lexer.pop_cmdarg @static_env.unextend } | kCLASS tLSHFT expr term { result = @def_level @def_level = 0 @static_env.extend_static @lexer.push_cmdarg } bodystmt kEND { result = @builder.def_sclass(val[0], val[1], val[2], val[5], val[6]) @lexer.pop_cmdarg @static_env.unextend @def_level = val[4] } | kMODULE cpath { @static_env.extend_static @lexer.push_cmdarg } bodystmt kEND { if in_def? diagnostic :error, :module_in_def, nil, val[0] end result = @builder.def_module(val[0], val[1], val[3], val[4]) @lexer.pop_cmdarg @static_env.unextend } | kDEF fname { @def_level += 1 @static_env.extend_static @lexer.push_cmdarg } f_arglist bodystmt kEND { result = @builder.def_method(val[0], val[1], val[3], val[4], val[5]) @lexer.pop_cmdarg @static_env.unextend @def_level -= 1 } | kDEF singleton dot_or_colon { @lexer.state = :expr_fname } fname { @def_level += 1 @static_env.extend_static @lexer.push_cmdarg } f_arglist bodystmt kEND { result = @builder.def_singleton(val[0], val[1], val[2], val[4], val[6], val[7], val[8]) @lexer.pop_cmdarg @static_env.unextend @def_level -= 1 } | kBREAK { result = @builder.keyword_cmd(:break, val[0]) } | kNEXT { result = @builder.keyword_cmd(:next, val[0]) } | kREDO { result = @builder.keyword_cmd(:redo, val[0]) } | kRETRY { result = @builder.keyword_cmd(:retry, val[0]) } primary_value: primary then: term | kTHEN | term kTHEN { result = val[1] } do: term | kDO_COND if_tail: opt_else | kELSIF expr_value then compstmt if_tail { else_t, else_ = val[4] result = [ val[0], @builder.condition(val[0], val[1], val[2], val[3], else_t, else_, nil), ] } opt_else: none | kELSE compstmt { result = val } for_var: lhs | mlhs f_marg: f_norm_arg | tLPAREN f_margs rparen { result = @builder.multi_lhs(val[0], val[1], val[2]) } f_marg_list: f_marg { result = [ val[0] ] } | f_marg_list tCOMMA f_marg { result = val[0] << val[2] } f_margs: f_marg_list | f_marg_list tCOMMA tSTAR f_norm_arg { result = val[0]. push(@builder.objc_restarg(val[2], val[3])) } | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list { result = val[0]. push(@builder.objc_restarg(val[2], val[3])). concat(val[5]) } | f_marg_list tCOMMA tSTAR { result = val[0]. push(@builder.objc_restarg(val[2])) } | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list { result = val[0]. push(@builder.objc_restarg(val[2])). concat(val[4]) } | tSTAR f_norm_arg { result = [ @builder.objc_restarg(val[0], val[1]) ] } | tSTAR f_norm_arg tCOMMA f_marg_list { result = [ @builder.objc_restarg(val[0], val[1]), *val[3] ] } | tSTAR { result = [ @builder.objc_restarg(val[0]) ] } | tSTAR tCOMMA f_marg_list { result = [ @builder.objc_restarg(val[0]), *val[2] ] } block_param: f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[5]) } | f_arg tCOMMA f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[6]). concat(val[7]) } | f_arg tCOMMA f_block_optarg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_arg tCOMMA f_block_optarg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[5]) } | f_arg tCOMMA f_rest_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_arg tCOMMA | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[5]) } | f_arg opt_f_block_arg { result = val[0].concat(val[1]) } | f_block_optarg tCOMMA f_rest_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_block_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[5]) } | f_block_optarg opt_f_block_arg { result = val[0]. concat(val[1]) } | f_block_optarg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_rest_arg opt_f_block_arg { result = val[0]. concat(val[1]) } | f_rest_arg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_block_arg { result = [ val[0] ] } opt_block_param: # nothing { result = @builder.args(nil, [], nil) } | block_param_def { @lexer.state = :expr_value } block_param_def: tPIPE opt_bv_decl tPIPE { result = @builder.args(val[0], val[1], val[2]) } | tOROP { result = @builder.args(val[0], [], val[0]) } | tPIPE block_param opt_bv_decl tPIPE { result = @builder.args(val[0], val[1].concat(val[2]), val[3]) } opt_bv_decl: # nothing { result = [] } | tSEMI bv_decls { result = val[1] } bv_decls: bvar { result = [ val[0] ] } | bv_decls tCOMMA bvar { result = val[0] << val[2] } bvar: tIDENTIFIER { result = @builder.shadowarg(val[0]) } | f_bad_arg lambda: { @static_env.extend_dynamic } f_larglist lambda_body { result = [ val[1], val[2] ] @static_env.unextend } f_larglist: tLPAREN2 f_args opt_bv_decl rparen { result = @builder.args(val[0], val[1].concat(val[2]), val[3]) } | f_args { result = @builder.args(nil, val[0], nil) } lambda_body: tLAMBEG compstmt tRCURLY { result = [ val[0], val[1], val[2] ] } | kDO_LAMBDA compstmt kEND { result = [ val[0], val[1], val[2] ] } do_block: kDO_BLOCK { @static_env.extend_dynamic } opt_block_param compstmt kEND { result = [ val[0], val[2], val[3], val[4] ] @static_env.unextend } block_call: command do_block { begin_t, block_args, body, end_t = val[1] result = @builder.block(val[0], begin_t, block_args, body, end_t) } | block_call tDOT operation2 opt_paren_args { lparen_t, args, rparen_t = val[3] result = @builder.call_method(val[0], val[1], val[2], lparen_t, args, rparen_t) } | block_call tCOLON2 operation2 opt_paren_args { lparen_t, args, rparen_t = val[3] result = @builder.call_method(val[0], val[1], val[2], lparen_t, args, rparen_t) } method_call: operation paren_args { lparen_t, args, rparen_t = val[1] result = @builder.call_method(nil, nil, val[0], lparen_t, args, rparen_t) } | primary_value tDOT operation2 opt_paren_args { lparen_t, args, rparen_t = val[3] result = @builder.call_method(val[0], val[1], val[2], lparen_t, args, rparen_t) } | primary_value tCOLON2 operation2 paren_args { lparen_t, args, rparen_t = val[3] result = @builder.call_method(val[0], val[1], val[2], lparen_t, args, rparen_t) } | primary_value tCOLON2 operation3 { result = @builder.call_method(val[0], val[1], val[2]) } | primary_value tDOT paren_args { lparen_t, args, rparen_t = val[2] result = @builder.call_method(val[0], val[1], nil, lparen_t, args, rparen_t) } | primary_value tCOLON2 paren_args { lparen_t, args, rparen_t = val[2] result = @builder.call_method(val[0], val[1], nil, lparen_t, args, rparen_t) } | kSUPER paren_args { lparen_t, args, rparen_t = val[1] result = @builder.keyword_cmd(:super, val[0], lparen_t, args, rparen_t) } | kSUPER { result = @builder.keyword_cmd(:zsuper, val[0]) } | primary_value tLBRACK2 opt_call_args rbracket { result = @builder.index(val[0], val[1], val[2], val[3]) } brace_block: tLCURLY { @static_env.extend_dynamic } opt_block_param compstmt tRCURLY { result = [ val[0], val[2], val[3], val[4] ] @static_env.unextend } | kDO { @static_env.extend_dynamic } opt_block_param compstmt kEND { result = [ val[0], val[2], val[3], val[4] ] @static_env.unextend } case_body: kWHEN args then compstmt cases { result = [ @builder.when(val[0], val[1], val[2], val[3]), *val[4] ] } cases: opt_else { result = [ val[0] ] } | case_body opt_rescue: kRESCUE exc_list exc_var then compstmt opt_rescue { assoc_t, exc_var = val[2] if val[1] exc_list = @builder.array(nil, val[1], nil) end result = [ @builder.rescue_body(val[0], exc_list, assoc_t, exc_var, val[3], val[4]), *val[5] ] } | { result = [] } exc_list: arg_value { result = [ val[0] ] } | mrhs | none exc_var: tASSOC lhs { result = [ val[0], val[1] ] } | none opt_ensure: kENSURE compstmt { result = [ val[0], val[1] ] } | none literal: numeric | symbol | dsym strings: string { result = @builder.string_compose(nil, val[0], nil) } string: string1 { result = [ val[0] ] } | string string1 { result = val[0] << val[1] } string1: tSTRING_BEG string_contents tSTRING_END { result = @builder.string_compose(val[0], val[1], val[2]) } | tSTRING { result = @builder.string(val[0]) } | tCHARACTER { result = @builder.character(val[0]) } xstring: tXSTRING_BEG xstring_contents tSTRING_END { result = @builder.xstring_compose(val[0], val[1], val[2]) } regexp: tREGEXP_BEG regexp_contents tSTRING_END tREGEXP_OPT { opts = @builder.regexp_options(val[3]) result = @builder.regexp_compose(val[0], val[1], val[2], opts) } words: tWORDS_BEG word_list tSTRING_END { result = @builder.words_compose(val[0], val[1], val[2]) } word_list: # nothing { result = [] } | word_list word tSPACE { result = val[0] << @builder.word(val[1]) } word: string_content { result = [ val[0] ] } | word string_content { result = val[0] << val[1] } qwords: tQWORDS_BEG qword_list tSTRING_END { result = @builder.words_compose(val[0], val[1], val[2]) } qword_list: # nothing { result = [] } | qword_list tSTRING_CONTENT tSPACE { result = val[0] << @builder.string_internal(val[1]) } string_contents: # nothing { result = [] } | string_contents string_content { result = val[0] << val[1] } xstring_contents: # nothing { result = [] } | xstring_contents string_content { result = val[0] << val[1] } regexp_contents: # nothing { result = [] } | regexp_contents string_content { result = val[0] << val[1] } string_content: tSTRING_CONTENT { result = @builder.string_internal(val[0]) } | tSTRING_DVAR string_dvar { result = val[1] } | tSTRING_DBEG { @lexer.cond.push(false) @lexer.cmdarg.push(false) } compstmt tRCURLY { @lexer.cond.lexpop @lexer.cmdarg.lexpop result = @builder.begin(val[0], val[2], val[3]) } string_dvar: tGVAR { result = @builder.gvar(val[0]) } | tIVAR { result = @builder.ivar(val[0]) } | tCVAR { result = @builder.cvar(val[0]) } | backref symbol: tSYMBOL { result = @builder.symbol(val[0]) } dsym: tSYMBEG xstring_contents tSTRING_END { result = @builder.symbol_compose(val[0], val[1], val[2]) } numeric: tINTEGER { result = @builder.integer(val[0]) } | tFLOAT { result = @builder.float(val[0]) } | tUMINUS_NUM tINTEGER =tLOWEST { result = @builder.negate(val[0], @builder.integer(val[1])) } | tUMINUS_NUM tFLOAT =tLOWEST { result = @builder.negate(val[0], @builder.float(val[1])) } variable: tIDENTIFIER { result = @builder.ident(val[0]) } | tIVAR { result = @builder.ivar(val[0]) } | tGVAR { result = @builder.gvar(val[0]) } | tCONSTANT { result = @builder.const(val[0]) } | tCVAR { result = @builder.cvar(val[0]) } | kNIL { result = @builder.nil(val[0]) } | kSELF { result = @builder.self(val[0]) } | kTRUE { result = @builder.true(val[0]) } | kFALSE { result = @builder.false(val[0]) } | k__FILE__ { result = @builder.__FILE__(val[0]) } | k__LINE__ { result = @builder.__LINE__(val[0]) } | k__ENCODING__ { result = @builder.__ENCODING__(val[0]) } var_ref: variable { result = @builder.accessible(val[0]) } var_lhs: variable { result = @builder.assignable(val[0]) } backref: tNTH_REF { result = @builder.nth_ref(val[0]) } | tBACK_REF { result = @builder.back_ref(val[0]) } superclass: term { result = nil } | tLT expr_value term { result = [ val[0], val[1] ] } | error term { yyerrok result = nil } f_arglist: tLPAREN2 f_args rparen { result = @builder.args(val[0], val[1], val[2]) @lexer.state = :expr_value } | f_args term { result = @builder.args(nil, val[0], nil) } f_args: f_arg tCOMMA f_optarg tCOMMA f_rest_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[5]) } | f_arg tCOMMA f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[6]). concat(val[7]) } | f_arg tCOMMA f_optarg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_arg tCOMMA f_optarg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[5]) } | f_arg tCOMMA f_rest_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_arg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[5]) } | f_arg opt_f_block_arg { result = val[0]. concat(val[1]) } | f_optarg tCOMMA f_rest_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_optarg tCOMMA f_rest_arg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[4]). concat(val[5]) } | f_optarg opt_f_block_arg { result = val[0]. concat(val[1]) } | f_optarg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_rest_arg opt_f_block_arg { result = val[0]. concat(val[1]) } | f_rest_arg tCOMMA f_arg opt_f_block_arg { result = val[0]. concat(val[2]). concat(val[3]) } | f_block_arg { result = [ val[0] ] } | # nothing { result = [] } f_bad_arg: tCONSTANT { diagnostic :error, :argument_const, nil, val[0] } | tIVAR { diagnostic :error, :argument_ivar, nil, val[0] } | tGVAR { diagnostic :error, :argument_gvar, nil, val[0] } | tCVAR { diagnostic :error, :argument_cvar, nil, val[0] } f_norm_arg: f_bad_arg | tIDENTIFIER { @static_env.declare val[0][0] result = @builder.arg(val[0]) } | tIDENTIFIER tASSOC tIDENTIFIER { @static_env.declare val[2][0] result = @builder.objc_kwarg(val[0], val[1], val[2]) } | tLABEL tIDENTIFIER { @static_env.declare val[1][0] result = @builder.objc_kwarg(val[0], nil, val[1]) } f_arg_item: f_norm_arg | tLPAREN f_margs rparen { result = @builder.multi_lhs(val[0], val[1], val[2]) } f_arg: f_arg_item { result = [ val[0] ] } | f_arg tCOMMA f_arg_item { result = val[0] << val[2] } f_opt: tIDENTIFIER tEQL arg_value { @static_env.declare val[0][0] result = @builder.optarg(val[0], val[1], val[2]) } f_block_opt: tIDENTIFIER tEQL primary_value { @static_env.declare val[0][0] result = @builder.optarg(val[0], val[1], val[2]) } f_block_optarg: f_block_opt { result = [ val[0] ] } | f_block_optarg tCOMMA f_block_opt { result = val[0] << val[2] } f_optarg: f_opt { result = [ val[0] ] } | f_optarg tCOMMA f_opt { result = val[0] << val[2] } restarg_mark: tSTAR2 | tSTAR f_rest_arg: restarg_mark tIDENTIFIER { @static_env.declare val[1][0] result = [ @builder.restarg(val[0], val[1]) ] } | restarg_mark { result = [ @builder.restarg(val[0]) ] } blkarg_mark: tAMPER2 | tAMPER f_block_arg: blkarg_mark tIDENTIFIER { @static_env.declare val[1][0] result = @builder.blockarg(val[0], val[1]) } opt_f_block_arg: tCOMMA f_block_arg { result = [ val[1] ] } | # nothing { result = [] } singleton: var_ref | tLPAREN2 expr rparen { result = val[1] } assoc_list: # nothing { result = [] } | assocs trailer assocs: assoc { result = [ val[0] ] } | assocs tCOMMA assoc { result = val[0] << val[2] } assoc: arg_value tASSOC arg_value { result = @builder.pair(val[0], val[1], val[2]) } | tLABEL arg_value { result = @builder.pair_keyword(val[0], val[1]) } operation: tIDENTIFIER | tCONSTANT | tFID operation2: tIDENTIFIER | tCONSTANT | tFID | op operation3: tIDENTIFIER | tFID | op dot_or_colon: tDOT | tCOLON2 opt_terms: | terms opt_nl: | tNL rparen: opt_nl tRPAREN { result = val[1] } rbracket: opt_nl tRBRACK { result = val[1] } trailer: | tNL | tCOMMA term: tSEMI { yyerrok } | tNL terms: term | terms tSEMI none: # nothing { result = nil } end ---- header require 'parser' Parser.check_for_encoding_support ---- inner def version 19 # closest released match: v1_9_0_2 end def default_encoding Encoding::BINARY end racc-1.6.2/test/assets/mailp.y000066400000000000000000000226441435117461300162220ustar00rootroot00000000000000# # mailp for test # class Testp rule content : DateH datetime { @field.date = val[1] } | RecvH received | RetpathH returnpath | MaddrH addrs { @field.addrs.replace val[1] } | SaddrH addr { @field.addr = val[1] } | MmboxH mboxes { @field.addrs.replace val[1] } | SmboxH mbox { @field.addr = val[1] } | MsgidH msgid { @field.msgid = val[1] } | KeyH keys { @field.keys.replace val[1] } | EncH enc | VersionH version | CTypeH ctype | CEncodingH cencode | CDispositionH cdisp | Mbox mbox { mb = val[1] @field.phrase = mb.phrase @field.setroute mb.route @field.local = mb.local @field.domain = mb.domain } | Spec spec { mb = val[1] @field.local = mb.local @field.domain = mb.domain } ; datetime : day DIGIT ATOM DIGIT hour zone # 0 1 2 3 4 5 # day month year { t = Time.gm( val[3].to_i, val[2], val[1].to_i, 0, 0, 0 ) result = (t + val[4] - val[5]).localtime } ; day : /* none */ | ATOM ',' ; hour : DIGIT ':' DIGIT { result = (result.to_i * 60 * 60) + (val[2].to_i * 60) } | DIGIT ':' DIGIT ':' DIGIT { result = (result.to_i * 60 * 60) + (val[2].to_i * 60) + val[4].to_i } ; zone : ATOM { result = ::TMail.zonestr2i( val[0] ) * 60 } ; received : from by via with id for recvdatetime ; from : /* none */ | FROM domain { @field.from = Address.join( val[1] ) } | FROM domain '@' domain { @field.from = Address.join( val[3] ) } | FROM domain DOMLIT { @field.from = Address.join( val[1] ) } ; by : /* none */ | BY domain { @field.by = Address.join( val[1] ) } ; via : /* none */ | VIA ATOM { @field.via = val[1] } ; with : /* none */ | WITH ATOM { @field.with.push val[1] } ; id : /* none */ | ID msgid { @field.msgid = val[1] } | ID ATOM { @field.msgid = val[1] } ; for : /* none */ | FOR addr { @field.for_ = val[1].address } ; recvdatetime : /* none */ | ';' datetime { @field.date = val[1] } ; returnpath: '<' '>' | routeaddr { @field.route.replace result.route @field.addr = result.addr } ; addrs : addr { result = val } | addrs ',' addr { result.push val[2] } ; addr : mbox | group ; mboxes : mbox { result = val } | mboxes ',' mbox { result.push val[2] } ; mbox : spec | routeaddr | phrase routeaddr { val[1].phrase = HFdecoder.decode( result ) result = val[1] } ; group : phrase ':' mboxes ';' { result = AddressGroup.new( result, val[2] ) } # | phrase ':' ';' { result = AddressGroup.new( result ) } ; routeaddr : '<' route spec '>' { result = val[2] result.route = val[1] } | '<' spec '>' { result = val[1] } ; route : at_domains ':' ; at_domains: '@' domain { result = [ val[1] ] } | at_domains ',' '@' domain { result.push val[3] } ; spec : local '@' domain { result = Address.new( val[0], val[2] ) } | local { result = Address.new( result, nil ) } ; local : word { result = val } | local '.' word { result.push val[2] } ; domain : domword { result = val } | domain '.' domword { result.push val[2] } ; domword : atom | DOMLIT | DIGIT ; msgid : '<' spec '>' { val[1] = val[1].addr result = val.join('') } ; phrase : word | phrase word { result << ' ' << val[1] } ; word : atom | QUOTED | DIGIT ; keys : phrase | keys ',' phrase ; enc : word { @field.encrypter = val[0] } | word word { @field.encrypter = val[0] @field.keyword = val[1] } ; version : DIGIT '.' DIGIT { @field.major = val[0].to_i @field.minor = val[2].to_i } ; ctype : TOKEN '/' TOKEN params { @field.main = val[0] @field.sub = val[2] } | TOKEN params { @field.main = val[0] @field.sub = '' } ; params : /* none */ | params ';' TOKEN '=' value { @field.params[ val[2].downcase ] = val[4] } ; value : TOKEN | QUOTED ; cencode : TOKEN { @field.encoding = val[0] } ; cdisp : TOKEN disp_params { @field.disposition = val[0] } ; disp_params : /* none */ | disp_params ';' disp_param ; disp_param: /* none */ | TOKEN '=' value { @field.params[ val[0].downcase ] = val[2] } ; atom : ATOM | FROM | BY | VIA | WITH | ID | FOR ; end ---- header # # mailp for test # require 'tmail/mails' module TMail ---- inner MAILP_DEBUG = false def initialize self.debug = MAILP_DEBUG end def debug=( flag ) @yydebug = flag && Racc_debug_parser @scanner_debug = flag end def debug @yydebug end def Mailp.parse( str, obj, ident ) new.parse( str, obj, ident ) end NATIVE_ROUTINE = { 'TMail::MsgidH' => :msgid_parse, 'TMail::RefH' => :refs_parse } def parse( str, obj, ident ) return if /\A\s*\z/ === str @field = obj if mid = NATIVE_ROUTINE[ obj.type.name ] then send mid, str else unless ident then ident = obj.type.name.split('::')[-1].to_s cmt = [] obj.comments.replace cmt else cmt = nil end @scanner = MailScanner.new( str, ident, cmt ) @scanner.debug = @scanner_debug @first = [ ident.intern, ident ] @pass_array = [nil, nil] do_parse end end private def next_token if @first then ret = @first @first = nil ret else @scanner.scan @pass_array end end def on_error( tok, val, vstack ) raise ParseError, "\nparse error in '#{@field.name}' header, on token #{val.inspect}" end def refs_parse( str ) arr = [] while mdata = ::TMail::MSGID.match( str ) do str = mdata.post_match pre = mdata.pre_match pre.strip! proc_phrase pre, arr unless pre.empty? arr.push mdata.to_s end str.strip! proc_phrase str, arr if not pre or pre.empty? @field.refs.replace arr end def proc_phrase( str, arr ) while mdata = /"([^\\]*(?:\\.[^"\\]*)*)"/.match( str ) do str = mdata.post_match pre = mdata.pre_match pre.strip! arr.push pre unless pre.empty? arr.push mdata[1] end str.strip! arr.push unless str.empty? end def msgid_parse( str ) if mdata = ::TMail::MSGID.match( str ) then @field.msgid = mdata.to_s else raise ParseError, "wrong Message-ID format: #{str}" end end ---- footer end # module TMail mp = TMail::Testp.new mp.parse racc-1.6.2/test/assets/mediacloth.y000066400000000000000000000370731435117461300172330ustar00rootroot00000000000000# Copyright (c) 2006 Pluron Inc. # # 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. # The parser for the MediaWiki language. # # Usage together with a lexer: # inputFile = File.new("data/input1", "r") # input = inputFile.read # parser = MediaWikiParser.new # parser.lexer = MediaWikiLexer.new # parser.parse(input) class MediaWikiParser token TEXT BOLD_START BOLD_END ITALIC_START ITALIC_END LINK_START LINK_END LINKSEP INTLINK_START INTLINK_END INTLINKSEP RESOURCESEP CHAR_ENT PRE_START PRE_END PREINDENT_START PREINDENT_END SECTION_START SECTION_END HLINE SIGNATURE_NAME SIGNATURE_DATE SIGNATURE_FULL PARA_START PARA_END UL_START UL_END OL_START OL_END LI_START LI_END DL_START DL_END DT_START DT_END DD_START DD_END TAG_START TAG_END ATTR_NAME ATTR_VALUE TABLE_START TABLE_END ROW_START ROW_END HEAD_START HEAD_END CELL_START CELL_END KEYWORD TEMPLATE_START TEMPLATE_END CATEGORY PASTE_START PASTE_END rule wiki: repeated_contents { @nodes.push WikiAST.new(0, @wiki_ast_length) #@nodes.last.children.insert(0, val[0]) #puts val[0] @nodes.last.children += val[0] } ; contents: text { result = val[0] } | bulleted_list { result = val[0] } | numbered_list { result = val[0] } | dictionary_list { list = ListAST.new(@ast_index, @ast_length) list.list_type = :Dictionary list.children = val[0] result = list } | preformatted { result = val[0] } | section { result = val[0] } | tag { result = val[0] } | template { result = val[0] } | KEYWORD { k = KeywordAST.new(@ast_index, @ast_length) k.text = val[0] result = k } | PARA_START para_contents PARA_END { p = ParagraphAST.new(@ast_index, @ast_length) p.children = val[1] result = p } | LINK_START link_contents LINK_END { l = LinkAST.new(@ast_index, @ast_length) l.link_type = val[0] l.url = val[1][0] l.children += val[1][1..-1] if val[1].length > 1 result = l } | PASTE_START para_contents PASTE_END { p = PasteAST.new(@ast_index, @ast_length) p.children = val[1] result = p } | INTLINK_START TEXT RESOURCESEP TEXT reslink_repeated_contents INTLINK_END { l = ResourceLinkAST.new(@ast_index, @ast_length) l.prefix = val[1] l.locator = val[3] l.children = val[4] unless val[4].nil? or val[4].empty? result = l } | INTLINK_START TEXT intlink_repeated_contents INTLINK_END { l = InternalLinkAST.new(@ast_index, @ast_length) l.locator = val[1] l.children = val[2] unless val[2].nil? or val[2].empty? result = l } | INTLINK_START CATEGORY TEXT cat_sort_contents INTLINK_END { l = CategoryAST.new(@ast_index, @ast_length) l.locator = val[2] l.sort_as = val[3] result = l } | INTLINK_START RESOURCESEP CATEGORY TEXT intlink_repeated_contents INTLINK_END { l = CategoryLinkAST.new(@ast_index, @ast_length) l.locator = val[3] l.children = val[4] unless val[4].nil? or val[4].empty? result = l } | table ; para_contents: { result = nil } | repeated_contents { result = val[0] } ; tag: TAG_START tag_attributes TAG_END { if val[0] != val[2] raise Racc::ParseError.new("XHTML end tag #{val[2]} does not match start tag #{val[0]}") end elem = ElementAST.new(@ast_index, @ast_length) elem.name = val[0] elem.attributes = val[1] result = elem } | TAG_START tag_attributes repeated_contents TAG_END { if val[0] != val[3] raise Racc::ParseError.new("XHTML end tag #{val[3]} does not match start tag #{val[0]}") end elem = ElementAST.new(@ast_index, @ast_length) elem.name = val[0] elem.attributes = val[1] elem.children += val[2] result = elem } ; tag_attributes: { result = nil } | ATTR_NAME tag_attributes { attr_map = val[2] ? val[2] : {} attr_map[val[0]] = true result = attr_map } | ATTR_NAME ATTR_VALUE tag_attributes { attr_map = val[2] ? val[2] : {} attr_map[val[0]] = val[1] result = attr_map } ; link_contents: TEXT { result = val } | TEXT LINKSEP link_repeated_contents { result = [val[0]] result += val[2] } ; link_repeated_contents: repeated_contents { result = val[0] } | repeated_contents LINKSEP link_repeated_contents { result = val[0] result += val[2] if val[2] } ; intlink_repeated_contents: { result = nil } | INTLINKSEP repeated_contents { result = val[1] } ; cat_sort_contents: { result = nil } | INTLINKSEP TEXT { result = val[1] } ; reslink_repeated_contents: { result = nil } | INTLINKSEP reslink_repeated_contents { result = val[1] } | INTLINKSEP repeated_contents reslink_repeated_contents { i = InternalLinkItemAST.new(@ast_index, @ast_length) i.children = val[1] result = [i] result += val[2] if val[2] } ; repeated_contents: contents { result = [] result << val[0] } | repeated_contents contents { result = [] result += val[0] result << val[1] } ; text: element { p = TextAST.new(@ast_index, @ast_length) p.formatting = val[0][0] p.contents = val[0][1] result = p } | formatted_element { result = val[0] } ; table: TABLE_START table_contents TABLE_END { table = TableAST.new(@ast_index, @ast_length) table.children = val[1] unless val[1].nil? or val[1].empty? result = table } | TABLE_START TEXT table_contents TABLE_END { table = TableAST.new(@ast_index, @ast_length) table.options = val[1] table.children = val[2] unless val[2].nil? or val[2].empty? result = table } table_contents: { result = nil } | ROW_START row_contents ROW_END table_contents { row = TableRowAST.new(@ast_index, @ast_length) row.children = val[1] unless val[1].nil? or val[1].empty? result = [row] result += val[3] unless val[3].nil? or val[3].empty? } | ROW_START TEXT row_contents ROW_END table_contents { row = TableRowAST.new(@ast_index, @ast_length) row.children = val[2] unless val[2].nil? or val[2].empty? row.options = val[1] result = [row] result += val[4] unless val[4].nil? or val[4].empty? } row_contents: { result = nil } | HEAD_START HEAD_END row_contents { cell = TableCellAST.new(@ast_index, @ast_length) cell.type = :head result = [cell] result += val[2] unless val[2].nil? or val[2].empty? } | HEAD_START repeated_contents HEAD_END row_contents { cell = TableCellAST.new(@ast_index, @ast_length) cell.children = val[1] unless val[1].nil? or val[1].empty? cell.type = :head result = [cell] result += val[3] unless val[3].nil? or val[3].empty? } | CELL_START CELL_END row_contents { cell = TableCellAST.new(@ast_index, @ast_length) cell.type = :body result = [cell] result += val[2] unless val[2].nil? or val[2].empty? } | CELL_START repeated_contents CELL_END row_contents { if val[2] == 'attributes' result = [] else cell = TableCellAST.new(@ast_index, @ast_length) cell.children = val[1] unless val[1].nil? or val[1].empty? cell.type = :body result = [cell] end result += val[3] unless val[3].nil? or val[3].empty? if val[2] == 'attributes' and val[3] and val[3].first.class == TableCellAST val[3].first.attributes = val[1] end result } element: TEXT { return [:None, val[0]] } | HLINE { return [:HLine, val[0]] } | CHAR_ENT { return [:CharacterEntity, val[0]] } | SIGNATURE_DATE { return [:SignatureDate, val[0]] } | SIGNATURE_NAME { return [:SignatureName, val[0]] } | SIGNATURE_FULL { return [:SignatureFull, val[0]] } ; formatted_element: BOLD_START BOLD_END { result = FormattedAST.new(@ast_index, @ast_length) result.formatting = :Bold result } | ITALIC_START ITALIC_END { result = FormattedAST.new(@ast_index, @ast_length) result.formatting = :Italic result } | BOLD_START repeated_contents BOLD_END { p = FormattedAST.new(@ast_index, @ast_length) p.formatting = :Bold p.children += val[1] result = p } | ITALIC_START repeated_contents ITALIC_END { p = FormattedAST.new(@ast_index, @ast_length) p.formatting = :Italic p.children += val[1] result = p } ; bulleted_list: UL_START list_item list_contents UL_END { list = ListAST.new(@ast_index, @ast_length) list.list_type = :Bulleted list.children << val[1] list.children += val[2] result = list } ; numbered_list: OL_START list_item list_contents OL_END { list = ListAST.new(@ast_index, @ast_length) list.list_type = :Numbered list.children << val[1] list.children += val[2] result = list } ; list_contents: { result = [] } list_item list_contents { result << val[1] result += val[2] } | { result = [] } ; list_item: LI_START LI_END { result = ListItemAST.new(@ast_index, @ast_length) } | LI_START repeated_contents LI_END { li = ListItemAST.new(@ast_index, @ast_length) li.children += val[1] result = li } ; dictionary_list: DL_START dictionary_term dictionary_contents DL_END { result = [val[1]] result += val[2] } | DL_START dictionary_contents DL_END { result = val[1] } ; dictionary_term: DT_START DT_END { result = ListTermAST.new(@ast_index, @ast_length) } | DT_START repeated_contents DT_END { term = ListTermAST.new(@ast_index, @ast_length) term.children += val[1] result = term } dictionary_contents: dictionary_definition dictionary_contents { result = [val[0]] result += val[1] if val[1] } | { result = [] } dictionary_definition: DD_START DD_END { result = ListDefinitionAST.new(@ast_index, @ast_length) } | DD_START repeated_contents DD_END { term = ListDefinitionAST.new(@ast_index, @ast_length) term.children += val[1] result = term } preformatted: PRE_START repeated_contents PRE_END { p = PreformattedAST.new(@ast_index, @ast_length) p.children += val[1] result = p } | PREINDENT_START repeated_contents PREINDENT_END { p = PreformattedAST.new(@ast_index, @ast_length) p.indented = true p.children += val[1] result = p } ; section: SECTION_START repeated_contents SECTION_END { result = [val[1], val[0].length] s = SectionAST.new(@ast_index, @ast_length) s.children = val[1] s.level = val[0].length result = s } ; template: TEMPLATE_START TEXT template_parameters TEMPLATE_END { t = TemplateAST.new(@ast_index, @ast_length) t.template_name = val[1] t.children = val[2] unless val[2].nil? or val[2].empty? result = t } ; template_parameters: { result = nil } | INTLINKSEP TEXT template_parameters { p = TemplateParameterAST.new(@ast_index, @ast_length) p.parameter_value = val[1] result = [p] result += val[2] if val[2] } | INTLINKSEP template template_parameters { p = TemplateParameterAST.new(@ast_index, @ast_length) p.children << val[1] result = [p] result += val[2] if val[2] } ; end ---- header ---- require 'mediacloth/mediawikiast' ---- inner ---- attr_accessor :lexer def initialize @nodes = [] @context = [] @wiki_ast_length = 0 super end #Tokenizes input string and parses it. def parse(input) @yydebug=true lexer.tokenize(input) do_parse return @nodes.last end #Asks the lexer to return the next token. def next_token token = @lexer.lex if token[0].to_s.upcase.include? "_START" @context << token[2..3] elsif token[0].to_s.upcase.include? "_END" @ast_index = @context.last[0] @ast_length = token[2] + token[3] - @context.last[0] @context.pop else @ast_index = token[2] @ast_length = token[3] end @wiki_ast_length += token[3] return token[0..1] end racc-1.6.2/test/assets/mof.y000066400000000000000000000326011435117461300156730ustar00rootroot00000000000000# Distributed under the Ruby license # See http://www.ruby-lang.org/en/LICENSE.txt for the full license text # Copyright (c) 2010 Klaus Kämpf /* * According to appendix A of * http://www.dmtf.org/standards/cim/cim_spec_v22 */ class MOF::Parser prechigh /* nonassoc UMINUS */ left '*' '/' left '+' '-' preclow token PRAGMA INCLUDE IDENTIFIER CLASS ASSOCIATION INDICATION AMENDED ENABLEOVERRIDE DISABLEOVERRIDE RESTRICTED TOSUBCLASS TOINSTANCE TRANSLATABLE QUALIFIER SCOPE SCHEMA PROPERTY REFERENCE METHOD PARAMETER FLAVOR INSTANCE AS REF ANY OF DT_VOID DT_UINT8 DT_SINT8 DT_UINT16 DT_SINT16 DT_UINT32 DT_SINT32 DT_UINT64 DT_SINT64 DT_REAL32 DT_REAL64 DT_CHAR16 DT_STR DT_BOOLEAN DT_DATETIME positiveDecimalValue stringValue realValue charValue booleanValue nullValue binaryValue octalValue decimalValue hexValue rule /* Returns a Hash of filename and MofResult */ mofSpecification : /* empty */ { result = Hash.new } | mofProduction { result = { @name => @result } } | mofSpecification mofProduction { result = val[0] result[@name] = @result } ; mofProduction : compilerDirective | classDeclaration { #puts "Class '#{val[0].name}'" @result.classes << val[0] } | qualifierDeclaration { @result.qualifiers << val[0] @qualifiers[val[0].name.downcase] = val[0] } | instanceDeclaration { @result.instances << val[0] } ; /*** * compilerDirective * */ compilerDirective : "#" PRAGMA INCLUDE pragmaParameters_opt { raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#pragma include'") unless val[3] open val[3], :pragma } | "#" PRAGMA pragmaName pragmaParameters_opt | "#" INCLUDE pragmaParameters_opt { raise StyleError.new(@name,@lineno,@line,"Use '#pragma include' instead of '#include'") unless @style == :wmi raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#include'") unless val[2] open val[2], :pragma } ; pragmaName : IDENTIFIER ; pragmaParameters_opt : /* empty */ { raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi } | "(" pragmaParameterValues ")" { result = val[1] } ; pragmaParameterValues : pragmaParameterValue | pragmaParameterValues "," pragmaParameterValue ; pragmaParameterValue : string | integerValue { raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi } | IDENTIFIER ; /*** * classDeclaration * */ classDeclaration : qualifierList_opt CLASS className alias_opt superClass_opt "{" classFeatures "}" ";" { qualifiers = val[0] features = val[6] # FIXME: features must not include references result = CIM::Class.new(val[2],qualifiers,val[3],val[4],features) } ; classFeatures : /* empty */ { result = [] } | classFeatures classFeature { result = val[0] << val[1] } ; classFeature : propertyDeclaration | methodDeclaration | referenceDeclaration /* must have association qualifier */ ; qualifierList_opt : /* empty */ | qualifierList { result = CIM::QualifierSet.new val[0] } ; qualifierList : "[" qualifier qualifiers "]" { result = val[2] result.unshift val[1] if val[1] } ; qualifiers : /* empty */ { result = [] } | qualifiers "," qualifier { result = val[0] result << val[2] if val[2] } ; qualifier : qualifierName qualifierParameter_opt flavor_opt { # Get qualifier decl qualifier = case val[0] when CIM::Qualifier then val[0].definition when CIM::QualifierDeclaration then val[0] when String then @qualifiers[val[0].downcase] else nil end raise MOF::Helper::Error.new(@name,@lineno,@line,"'#{val[0]}' is not a valid qualifier") unless qualifier value = val[1] raise MOF::Helper::Error.new(@name,@lineno,@line,"#{value.inspect} does not match qualifier type '#{qualifier.type}'") unless qualifier.type.matches?(value)||@style == :wmi # Don't propagate a boolean 'false' if qualifier.type == :boolean && value == false result = nil else result = CIM::Qualifier.new(qualifier,value,val[2]) end } ; flavor_opt : /* empty */ | ":" flavor { result = CIM::QualifierFlavors.new val[1] } ; qualifierParameter_opt : /* empty */ | qualifierParameter ; qualifierParameter : "(" constantValue ")" { result = val[1] } | arrayInitializer ; /* CIM::Flavors */ flavor : AMENDED | ENABLEOVERRIDE | DISABLEOVERRIDE | RESTRICTED | TOSUBCLASS | TRANSLATABLE | TOINSTANCE { case val[0].to_sym when :amended, :toinstance raise StyleError.new(@name,@lineno,@line,"'#{val[0]}' is not a valid flavor") unless @style == :wmi end } ; alias_opt : /* empty */ | alias ; superClass_opt : /* empty */ | superClass ; className : IDENTIFIER /* must be _ in CIM v2.x */ { raise ParseError.new("Class name must be prefixed by '_'") unless val[0].include?("_") || @style == :wmi } ; alias : AS aliasIdentifier { result = val[1] } ; aliasIdentifier : "$" IDENTIFIER /* NO whitespace ! */ { result = val[1] } ; superClass : ":" className { result = val[1] } ; propertyDeclaration : qualifierList_opt dataType propertyName array_opt defaultValue_opt ";" { if val[3] type = CIM::Array.new val[3],val[1] else type = val[1] end result = CIM::Property.new(type,val[2],val[0],val[4]) } ; referenceDeclaration : qualifierList_opt objectRef referenceName array_opt defaultValue_opt ";" { if val[4] raise StyleError.new(@name,@lineno,@line,"Array not allowed in reference declaration") unless @style == :wmi end result = CIM::Reference.new(val[1],val[2],val[0],val[4]) } ; methodDeclaration : qualifierList_opt dataType methodName "(" parameterList_opt ")" ";" { result = CIM::Method.new(val[1],val[2],val[0],val[4]) } ; propertyName : IDENTIFIER | PROPERTY { # tmplprov.mof has 'string Property;' raise StyleError.new(@name,@lineno,@line,"Invalid keyword '#{val[0]}' used for property name") unless @style == :wmi } ; referenceName : IDENTIFIER | INDICATION { result = "Indication" } ; methodName : IDENTIFIER ; dataType : DT_UINT8 | DT_SINT8 | DT_UINT16 | DT_SINT16 | DT_UINT32 | DT_SINT32 | DT_UINT64 | DT_SINT64 | DT_REAL32 | DT_REAL64 | DT_CHAR16 | DT_STR | DT_BOOLEAN | DT_DATETIME | DT_VOID { raise StyleError.new(@name,@lineno,@line,"'void' is not a valid datatype") unless @style == :wmi } ; objectRef : className { # WMI uses class names as data types (without REF ?!) raise StyleError.new(@name,@lineno,@line,"Expected 'ref' keyword after classname '#{val[0]}'") unless @style == :wmi result = CIM::ReferenceType.new val[0] } | className REF { result = CIM::ReferenceType.new val[0] } ; parameterList_opt : /* empty */ | parameterList ; parameterList : parameter parameters { result = val[1].unshift val[0] } ; parameters : /* empty */ { result = [] } | parameters "," parameter { result = val[0] << val[2] } ; parameter : qualifierList_opt typespec parameterName array_opt parameterValue_opt { if val[3] type = CIM::Array.new val[3], val[1] else type = val[1] end result = CIM::Property.new(type,val[2],val[0]) } ; typespec : dataType | objectRef ; parameterName : IDENTIFIER ; array_opt : /* empty */ | array ; parameterValue_opt : /* empty */ | defaultValue { raise "Default parameter value not allowed in syntax style '{@style}'" unless @style == :wmi } ; array : "[" positiveDecimalValue_opt "]" { result = val[1] } ; positiveDecimalValue_opt : /* empty */ { result = -1 } | positiveDecimalValue ; defaultValue_opt : /* empty */ | defaultValue ; defaultValue : "=" initializer { result = val[1] } ; initializer : constantValue | arrayInitializer | referenceInitializer ; arrayInitializer : "{" constantValues "}" { result = val[1] } ; constantValues : /* empty */ | constantValue { result = [ val[0] ] } | constantValues "," constantValue { result = val[0] << val[2] } ; constantValue : integerValue | realValue | charValue | string | booleanValue | nullValue | instance { raise "Instance as property value not allowed in syntax style '{@style}'" unless @style == :wmi } ; integerValue : binaryValue | octalValue | decimalValue | positiveDecimalValue | hexValue ; string : stringValue | string stringValue { result = val[0] + val[1] } ; referenceInitializer : objectHandle | aliasIdentifier ; objectHandle : namespace_opt modelPath ; namespace_opt : /* empty */ | namespaceHandle ":" ; namespaceHandle : IDENTIFIER ; /* * Note : structure depends on type of namespace */ modelPath : className "." keyValuePairList ; keyValuePairList : keyValuePair keyValuePairs ; keyValuePairs : /* empty */ | keyValuePairs "," keyValuePair ; keyValuePair : keyname "=" initializer ; keyname : propertyName | referenceName ; /*** * qualifierDeclaration * */ qualifierDeclaration /* 0 1 2 3 4 */ : QUALIFIER qualifierName qualifierType scope defaultFlavor_opt ";" { result = CIM::QualifierDeclaration.new( val[1], val[2][0], val[2][1], val[3], val[4]) } ; defaultFlavor_opt : /* empty */ | defaultFlavor ; qualifierName : IDENTIFIER | ASSOCIATION /* meta qualifier */ | INDICATION /* meta qualifier */ | REFERENCE /* Added in DSP0004 2.7.0 */ | SCHEMA ; /* [type, value] */ qualifierType : ":" dataType array_opt defaultValue_opt { type = val[2].nil? ? val[1] : CIM::Array.new(val[2],val[1]) result = [ type, val[3] ] } ; scope : "," SCOPE "(" metaElements ")" { result = CIM::QualifierScopes.new(val[3]) } ; metaElements : metaElement { result = [ val[0] ] } | metaElements "," metaElement { result = val[0] << val[2] } ; metaElement : SCHEMA | CLASS | ASSOCIATION | INDICATION | QUALIFIER | PROPERTY | REFERENCE | METHOD | PARAMETER | ANY ; defaultFlavor : "," FLAVOR "(" flavors ")" { result = CIM::QualifierFlavors.new val[3] } ; flavors : flavor { result = [ val[0] ] } | flavors "," flavor { result = val[0] << val[2] } ; /*** * instanceDeclaration * */ instanceDeclaration : instance ";" ; instance : qualifierList_opt INSTANCE OF className alias_opt "{" valueInitializers "}" ; valueInitializers : valueInitializer | valueInitializers valueInitializer ; valueInitializer : qualifierList_opt keyname "=" initializer ";" | qualifierList_opt keyname ";" { raise "Instance property '#{val[1]} must have a value" unless @style == :wmi } ; end # class Parser ---- header ---- # parser.rb - generated by racc require 'strscan' require 'rubygems' require 'cim' require File.join(__dir__, 'result') require File.join(__dir__, 'scanner') require File.join(__dir__, 'case') ---- inner ---- # # Initialize MOF::Parser # MOF::Parser.new options = {} # # options -> Hash of options # :debug -> boolean # :includes -> array of include dirs # :style -> :cim or :wmi # def initialize options = {} @yydebug = options[:debug] @includes = options[:includes] || [] @quiet = options[:quiet] @style = options[:style] || :cim # default to style CIM v2.2 syntax @lineno = 1 @file = nil @iconv = nil @eol = "\n" @fname = nil @fstack = [] @in_comment = false @seen_files = [] @qualifiers = {} end # # Make options hash from argv # # returns [ files, options ] # def self.argv_handler name, argv files = [] options = { :namespace => "" } while argv.size > 0 case opt = argv.shift when "-h" $stderr.puts "Ruby MOF compiler" $stderr.puts "#{name} [-h] [-d] [-I ] []" $stderr.puts "Compiles " $stderr.puts "\t-d debug" $stderr.puts "\t-h this help" $stderr.puts "\t-I include dir" $stderr.puts "\t-f force" $stderr.puts "\t-n " $stderr.puts "\t-o " $stderr.puts "\t-s