This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
Copyright (C) 1993-2013 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.
|
||||
@@ -0,0 +1,56 @@
|
||||
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
|
||||
You can redistribute it and/or modify it under either the terms of the
|
||||
2-clause BSDL (see the file BSDL), or the conditions below:
|
||||
|
||||
1. You may make and give away verbatim copies of the source form of the
|
||||
software without restriction, provided that you duplicate all of the
|
||||
original copyright notices and associated disclaimers.
|
||||
|
||||
2. You may modify your copy of the software in any way, provided that
|
||||
you do at least ONE of the following:
|
||||
|
||||
a. place your modifications in the Public Domain or otherwise
|
||||
make them Freely Available, such as by posting said
|
||||
modifications to Usenet or an equivalent medium, or by allowing
|
||||
the author to include your modifications in the software.
|
||||
|
||||
b. use the modified software only within your corporation or
|
||||
organization.
|
||||
|
||||
c. give non-standard binaries non-standard names, with
|
||||
instructions on where to get the original software distribution.
|
||||
|
||||
d. make other distribution arrangements with the author.
|
||||
|
||||
3. You may distribute the software in object code or binary form,
|
||||
provided that you do at least ONE of the following:
|
||||
|
||||
a. distribute the binaries and library files of the software,
|
||||
together with instructions (in the manual page or equivalent)
|
||||
on where to get the original distribution.
|
||||
|
||||
b. accompany the distribution with the machine-readable source of
|
||||
the software.
|
||||
|
||||
c. give non-standard binaries non-standard names, with
|
||||
instructions on where to get the original software distribution.
|
||||
|
||||
d. make other distribution arrangements with the author.
|
||||
|
||||
4. You may modify and include the part of the software into any other
|
||||
software (possibly commercial). But some files in the distribution
|
||||
are not written by the author, so that they are not under these terms.
|
||||
|
||||
For the list of those files and their copying conditions, see the
|
||||
file LEGAL.
|
||||
|
||||
5. The scripts and library files supplied as input to or produced as
|
||||
output from the software do not automatically fall under the
|
||||
copyright of the software, but belong to whomever generated them,
|
||||
and may be sold commercially, and may be aggregated with this
|
||||
software.
|
||||
|
||||
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE.
|
||||
@@ -0,0 +1,846 @@
|
||||
Tue Feb 20 18:45:45 2007 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/grammar.rb (separated_by): return [] for empty list.
|
||||
|
||||
Tue Nov 7 07:13:47 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/grammar.rb (Rule#prec): rule.prec{...} should set
|
||||
action.
|
||||
|
||||
Tue Nov 7 06:38:57 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/grammar.rb: #should_terminal should be called in
|
||||
#check_terminals.
|
||||
|
||||
Fri Aug 4 00:44:56 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/parserfilegenerator.rb
|
||||
(serialize_integer_list_compressed): fix typo.
|
||||
|
||||
Thu Aug 3 22:20:34 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* bin/y2racc: fix filename.
|
||||
|
||||
Thu Aug 3 21:10:48 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* bin/y2racc: getopts -> optparse.
|
||||
|
||||
Thu Aug 3 19:35:34 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* setup.rb: updated.
|
||||
|
||||
Thu Aug 3 19:34:55 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/pre-setup: We need not make grammarfileparser.rb.
|
||||
|
||||
Sat Jul 29 04:30:33 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/grammar.rb: allow '|' operation with meta rules
|
||||
(many, option...).
|
||||
|
||||
Sat Jul 29 03:17:20 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/compat.rb: reduce warning.
|
||||
|
||||
Sun Jul 16 05:07:12 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* ext/racc/cparse/cparse.so: should mark VALUEs in cparse_params.
|
||||
|
||||
Tue Jul 4 02:24:27 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/debugflags.rb: remove unused class GenerationOptions.
|
||||
|
||||
Tue Jul 4 02:14:48 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/compat.rb: update coding style.
|
||||
|
||||
Mon Jul 3 04:34:32 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 accept 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 accept 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 <aamine@loveruby.net>
|
||||
|
||||
* Makefile: new task bootstrap-force.
|
||||
|
||||
Sun Jul 2 19:46:58 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* test/ichk.y: update coding style.
|
||||
|
||||
Sun Jul 2 19:01:55 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* ext/racc/cparse/cparse.c: must require version.h to get
|
||||
RUBY_VERSION_CODE.
|
||||
|
||||
Sun Jul 2 18:33:32 2006 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* ext/racc/cparse/cparse.c: K&R -> ANSI C.
|
||||
|
||||
Mon Nov 21 02:37:10 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* version 1.4.5 released.
|
||||
|
||||
Mon Nov 21 02:31:18 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* bin/racc2y: refactoring.
|
||||
|
||||
* bin/y2racc: refactoring.
|
||||
|
||||
Sun Nov 20 23:46:42 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/pre-setup: new file.
|
||||
|
||||
Sun Nov 20 22:46:21 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* COPYING: new file.
|
||||
|
||||
Sun Nov 20 22:25:15 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* setup.rb: import setup.rb 3.4.1.
|
||||
|
||||
Thu Sep 29 02:51:56 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* Makefile (clean): invoke `make clean' in ext.
|
||||
|
||||
Thu Sep 29 02:50:56 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/.cvsignore: removed.
|
||||
|
||||
Thu Sep 29 02:46:30 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* setup.rb: new file.
|
||||
|
||||
Tue Jul 26 23:37:46 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* bin/racc: --no-omit-actions did not work (This patch is
|
||||
contributed by OHKUBO Takuya).
|
||||
|
||||
Sun Jan 2 11:48:19 2005 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/grammar.rb (once_writer): bug! needs argument.
|
||||
|
||||
Mon Feb 16 16:14:16 2004 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/rubyloader.rb: imported rev1.6.
|
||||
|
||||
Fri Dec 12 01:57:47 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* Makefile (all): make cparse.so.
|
||||
|
||||
Mon Nov 3 17:19:26 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/parser.rb: update version.
|
||||
|
||||
* ext/racc/cparse/cparse.c: update version.
|
||||
|
||||
Mon Nov 3 17:19:01 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* Makefile: update version in parser.rb, cparse.c.
|
||||
|
||||
Sun Oct 12 23:49:58 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* version 1.4.4.
|
||||
|
||||
Sun Oct 12 23:49:40 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* bin/y2racc: did not work.
|
||||
|
||||
* bin/y2racc: -u options did not work.
|
||||
|
||||
Sun Oct 12 23:41:46 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* misc/dist.sh: cd before make.
|
||||
|
||||
Sun Oct 12 23:38:04 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* Makefile (site): create $siteroot/{ja,en}/man/racc/*.html.
|
||||
|
||||
Sun Oct 12 23:37:18 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* doc/parser.rrd.m: missing 'j'.
|
||||
|
||||
Sun Oct 12 23:29:11 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* web/racc.ja.html: update descriptions.
|
||||
|
||||
* web/racc.en.html: ditto.
|
||||
|
||||
Sun Oct 12 18:43:45 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* misc/dist.sh: remove web/ directory before distribute.
|
||||
|
||||
Sun Oct 12 18:37:29 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* misc/dist.sh: forgot to remove tmp comment out.
|
||||
|
||||
Sun Oct 12 18:12:09 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* lib/racc/info.rb: version 1.4.4.
|
||||
|
||||
Sun Oct 12 18:11:42 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* Makefile (dist): split out misc/dist.sh.
|
||||
|
||||
* misc/dist.sh: new file.
|
||||
|
||||
Sun Oct 12 17:18:47 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* sameple/calc-ja.y: simplify.
|
||||
|
||||
Sun Oct 12 16:24:16 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* misc/y2racc -> bin/y2racc
|
||||
|
||||
* misc/racc2y -> bin/racc2y
|
||||
|
||||
Sun Oct 12 15:56:30 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* bin/racc: follow method name change.
|
||||
|
||||
Sun Oct 12 15:34:14 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* Makefile: new target `test'.
|
||||
|
||||
* Makefile: missing $datadir.
|
||||
|
||||
Sun Oct 12 15:33:02 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* README.ja: update description.
|
||||
|
||||
* README.en: ditto.
|
||||
|
||||
Sun Oct 12 15:25:23 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* Makefile: new file.
|
||||
|
||||
Sun Oct 12 15:19:57 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* BUGS.en: removed.
|
||||
|
||||
* BUGS.ja: removed.
|
||||
|
||||
Sun Oct 12 15:10:38 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* test/test.rb: use /bin/rm if exists.
|
||||
|
||||
Sun Oct 12 14:33:29 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* rubyloader.rb: imported from amstd, rev 1.5.
|
||||
|
||||
Sun Oct 12 14:24:47 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* test/test.rb: refactoring.
|
||||
|
||||
Tue Jun 24 03:14:01 2003 Minero Aoki <aamine@as600.loveruby.net>
|
||||
|
||||
* ucodep.rb: typo: Grammer -> Grammar
|
||||
|
||||
Mon May 26 23:06:58 2003 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* raccs.rb: update coding style.
|
||||
|
||||
Fri Nov 15 17:53:12 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* racc: changes style.
|
||||
|
||||
* parser.rb: ditto.
|
||||
|
||||
Fri Nov 15 17:11:52 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
version 1.4.3.
|
||||
|
||||
Fri Nov 15 17:08:01 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* 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 <aamine@loveruby.net>
|
||||
|
||||
* raccs.rb: explicit method call for VCALL.
|
||||
|
||||
Wed Oct 16 15:45:11 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* parser.rb: reformat.
|
||||
|
||||
Fri Aug 9 18:21:01 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* cparse/cparse.c: use better variable/macro names.
|
||||
|
||||
Wed Aug 7 08:39:19 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* cparse/cparse.c: goto label requires stmt.
|
||||
|
||||
Mon Aug 5 21:53:07 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* cparse/cparse.c: grand refine.
|
||||
|
||||
* cparse/depend: re-added from ruby/ext/racc/cparse.
|
||||
|
||||
Tue Jun 4 00:15:28 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* boot.rb: allow to omit last 'end'.
|
||||
|
||||
Mon Jun 3 23:29:45 2002 Minero Aoki <aamine@loveruby.net>
|
||||
|
||||
* racc (write_table_file): shebang must placed on first line.
|
||||
(reported by Hiroyuki Sato)
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
= Racc
|
||||
|
||||
* https://github.com/ruby/racc
|
||||
|
||||
== DESCRIPTION:
|
||||
|
||||
Racc は LALR(1) パーサジェネレータです。
|
||||
yacc の Ruby 版に相当します。
|
||||
|
||||
== 必要環境
|
||||
|
||||
* Ruby 2.5 以降
|
||||
|
||||
== インストール
|
||||
|
||||
gem インストール:
|
||||
|
||||
$ gem install racc
|
||||
|
||||
== テスト
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
= Racc
|
||||
|
||||
* https://github.com/ruby/racc
|
||||
|
||||
== DESCRIPTION:
|
||||
|
||||
Racc is an 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
|
||||
|
||||
== 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/'.
|
||||
|
||||
== Release flow
|
||||
|
||||
* Update VERSION number of these files
|
||||
* <code>RACC_VERSION</code> in "ext/racc/com/headius/racc/Cparse.java"
|
||||
* <code>VERSION</code> in "lib/racc/info.rb"
|
||||
* Release as a gem by <code>rake release</code> with CRuby and JRuby because Racc gem provides 2 packages
|
||||
* Create new release on {GitHub}[https://github.com/ruby/racc/releases]
|
||||
|
||||
== 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
|
||||
@@ -0,0 +1,5 @@
|
||||
* check 'error' token handling.
|
||||
* interactive transition table monitor.
|
||||
* support backtracking.
|
||||
* output Ruby extension library?
|
||||
* LL(k)? (But it should not be called Racc)
|
||||
Executable
+320
@@ -0,0 +1,320 @@
|
||||
#!/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
|
||||
frozen_strings = 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] [input]"
|
||||
parser.on('-o', '--output-file=PATH',
|
||||
'output file name [<input>.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 <filename>.output log file.') {|fl|
|
||||
make_logfile = fl
|
||||
}
|
||||
parser.on('-O', '--log-file=PATH',
|
||||
'Log file name [<input>.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('-F', '--frozen', "Add frozen_string_literals: true.") {
|
||||
frozen_strings = 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
|
||||
}
|
||||
parser.on('--runtime-version', 'Prints runtime version and quit.') {
|
||||
printf "racc runtime version %s; %s core version %s\n",
|
||||
Racc::Parser::Racc_Runtime_Version,
|
||||
Racc::Parser.racc_runtime_type,
|
||||
if Racc::Parser.racc_runtime_type == 'ruby'
|
||||
Racc::Parser::Racc_Runtime_Core_Version_R
|
||||
else
|
||||
Racc::Parser::Racc_Runtime_Core_Version_C
|
||||
end
|
||||
exit
|
||||
}
|
||||
parser.on('--copyright', 'Prints copyright and quit.') {
|
||||
puts Racc::Copyright
|
||||
exit
|
||||
}
|
||||
parser.on('--help', 'Prints this message and quit.') {
|
||||
puts parser.help
|
||||
exit
|
||||
}
|
||||
begin
|
||||
parser.parse!
|
||||
rescue OptionParser::ParseError => err
|
||||
abort [err.message, parser.help].join("\n")
|
||||
end
|
||||
if ARGV.size > 1
|
||||
abort 'too many input'
|
||||
end
|
||||
|
||||
input = ARGV[0] || "stdin"
|
||||
|
||||
if input == "stdin" && !output then
|
||||
abort 'You must specify a path to read or use -o <path> for output.'
|
||||
end
|
||||
|
||||
begin
|
||||
$stderr.puts 'Parsing grammar file...' if verbose
|
||||
result = profiler.section('parse') {
|
||||
parser = Racc::GrammarFileParser.new(debug_flags)
|
||||
content = input == "stdin" ? ARGF.read : File.read(input)
|
||||
parser.parse(content, File.basename(input))
|
||||
}
|
||||
if check_only
|
||||
$stderr.puts 'syntax ok'
|
||||
exit
|
||||
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
|
||||
params.filename = File.basename(input)
|
||||
# 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
|
||||
params.frozen_strings = frozen_strings
|
||||
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
|
||||
if states.should_error_on_expect_mismatch?
|
||||
raise Racc::CompileError, "#{states.grammar.n_expected_srconflicts} shift/reduce conflicts are expected but #{states.n_srconflicts} shift/reduce conflicts exist"
|
||||
end
|
||||
rescue Racc::Error, Errno::ENOENT, Errno::EPERM => err
|
||||
raise if $DEBUG or debug_flags.any?
|
||||
lineno = err.message.slice(/\A\d+:/).to_s
|
||||
abort "#{File.basename $0}: #{input}:#{lineno} #{err.message.strip}"
|
||||
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 = []
|
||||
end
|
||||
|
||||
def section(name)
|
||||
if @really
|
||||
t1 = ::Process.times.utime
|
||||
result = yield
|
||||
t2 = ::Process.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
|
||||
@@ -0,0 +1,218 @@
|
||||
= 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 below:
|
||||
|
||||
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
|
||||
expect 3
|
||||
rule
|
||||
:
|
||||
:
|
||||
|
||||
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
|
||||
:
|
||||
:
|
||||
@@ -0,0 +1,219 @@
|
||||
= 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
|
||||
expect 3
|
||||
rule
|
||||
:
|
||||
:
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,99 @@
|
||||
<h1>Raccコマンドリファレンス</h1>
|
||||
<p>
|
||||
racc [-o<var>filename</var>] [--output-file=<var>filename</var>]
|
||||
[-e<var>rubypath</var>] [--executable=<var>rubypath</var>]
|
||||
[-v] [--verbose]
|
||||
[-O<var>filename</var>] [--log-file=<var>filename</var>]
|
||||
[-g] [--debug]
|
||||
[-E] [--embedded]
|
||||
[-F] [--frozen]
|
||||
[-l] [--no-line-convert]
|
||||
[-c] [--line-convert-all]
|
||||
[-a] [--no-omit-actions]
|
||||
[-C] [--check-only]
|
||||
[-S] [--output-status]
|
||||
[--version] [--copyright] [--help] <var>grammarfile</var>
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<dt><var>filename</var>
|
||||
<dd>
|
||||
Raccの文法ファイルを指定します。拡張子には特に制限はありません。
|
||||
</dd>
|
||||
<dt>-o<var>outfile</var>, --output-file=<var>outfile</var>
|
||||
<dd>
|
||||
作成するクラスをかきこむファイル名を指定します。デフォルトは<filename>.tab.rbです。
|
||||
</dd>
|
||||
<dt>-O<var>filename</var>, --log-file=<var>filename</var>
|
||||
<dd>
|
||||
-v オプションをつけた時に生成するログファイルの名前を
|
||||
<var>filename</var> に変更します。
|
||||
デフォルトは <var>filename</var>.output です。
|
||||
</dd>
|
||||
<dt>-e<var>rubypath</var>, --executable=<var>rubypath</var>
|
||||
<dd>
|
||||
実行可能ファイルを生成します。<var>rubypath</var>は Ruby 本体のパスです。
|
||||
<var>rubypath</var>を単に 'ruby' にした時には Racc が動作している
|
||||
Ruby のパスを使用します。
|
||||
</dd>
|
||||
<dt>-v, --verbose
|
||||
<dd>
|
||||
ファイル "filename".output に詳細な解析情報を出力します。
|
||||
</dd>
|
||||
<dt>-g, --debug
|
||||
<dd>
|
||||
出力するコードにデバッグ用コードを加えます。-g をつけて生成したパーサで
|
||||
@yydebug を true にセットすると、デバッグ用のコードが出力されます。<br>
|
||||
-g をつけるだけでは何もおこりませんので注意してください。
|
||||
</dd>
|
||||
<dt>-E, --embedded
|
||||
<dd>
|
||||
ランタイムルーチンをすべて含んだコードを生成します。
|
||||
つまり、このオプションをつけて生成したコードは Ruby さえあれば動きます。
|
||||
</dd>
|
||||
<dt>-F, --frozen
|
||||
<dd>
|
||||
Add frozen_string_literals: true.
|
||||
</dd>
|
||||
<dt>-C, --check-only
|
||||
<dd>
|
||||
(文法ファイルの) 文法のチェックだけをして終了します。
|
||||
</dd>
|
||||
<dt>-S, --output-status
|
||||
<dd>
|
||||
進行状況を逐一報告します。
|
||||
</dd>
|
||||
<dt>-l, --no-line-convert
|
||||
<dd>
|
||||
<p>
|
||||
Ruby では例外が発生した時のファイル名や行番号を表示してくれますが、
|
||||
Racc の生成したパーサは、デフォルトではこの場合のファイル名・行番号を
|
||||
文法ファイルでのものに置きかえます。このフラグはその機能をオフにします。
|
||||
</p>
|
||||
<p>
|
||||
ruby 1.4.3 以前のバージョンではバグのために定数の参照に失敗する
|
||||
場合があるので、定数参照に関してなにかおかしいことがおこったらこのフラグを
|
||||
試してみてください。
|
||||
</p>
|
||||
</dd>
|
||||
<dt>-c, --line-convert-all
|
||||
<dd>
|
||||
アクションと inner に加え header footer の行番号も変換します。
|
||||
header と footer がつながっているような場合には使わないでください。
|
||||
<dt>-a, --no-omit-actions
|
||||
<dd>
|
||||
全てのアクションに対応するメソッド定義と呼び出しを行います。
|
||||
例えアクションが省略されていても空のメソッドを生成します。
|
||||
</dd>
|
||||
<dt>--version
|
||||
<dd>
|
||||
Racc のバージョンを出力して終了します。
|
||||
</dd>
|
||||
<dt>--copyright
|
||||
<dd>
|
||||
著作権表示を出力して終了します。
|
||||
<dt>--help
|
||||
<dd>
|
||||
オプションの簡単な説明を出力して終了します。
|
||||
</dd>
|
||||
</dl>
|
||||
@@ -0,0 +1,36 @@
|
||||
= パーサのデバッグ
|
||||
|
||||
ここでは、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 でもよいことになった。
|
||||
@@ -0,0 +1,348 @@
|
||||
= 規則ファイル文法リファレンス
|
||||
|
||||
== 文法に関する前バージョンとの非互換
|
||||
|
||||
* (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 コアが使っているオブジェクトがそのまま渡されます。
|
||||
この変数の意味がわかる人以外は<em>絶対に</em>変更してはいけません。
|
||||
|
||||
またアクションの特別な形式に、埋めこみアクションというものがあります。
|
||||
これはトークン列の途中の好きなところに記述することができます。
|
||||
以下に埋めこみアクションの例を示します。
|
||||
--
|
||||
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 DIVIDE
|
||||
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 の文
|
||||
:
|
||||
--
|
||||
行の先頭から四つ以上連続した「-」(マイナス)があるとユーザーコードと
|
||||
みなされます。識別子は一つの単語で、そのあとには「=」以外なら何を
|
||||
書いてもかまいません。
|
||||
@@ -0,0 +1,10 @@
|
||||
<h1>Racc ユーザマニュアル</h1>
|
||||
<p>バージョン 1.4 対応</p>
|
||||
<ul>
|
||||
<li><a href="usage.html">Racc の使い方</a>
|
||||
<li><a href="command.html">racc コマンドリファレンス</a>
|
||||
<li><a href="grammar.html">規則ファイル文法リファレンス</a>
|
||||
<li><a href="parser.html">Parser クラスリファレンス</a>
|
||||
<li><a href="debug.html">パーサのデバッグ</a>
|
||||
<li><a href="NEWS.html">リリースノート</a>
|
||||
</ul>
|
||||
@@ -0,0 +1,125 @@
|
||||
= 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 )
|
||||
* 引用符でかこまれているもの<br>
|
||||
→ その文字列そのまま (例えば '=' )
|
||||
|
||||
で表します。これを変更する方法については、
|
||||
文法リファレンスを参照してください。
|
||||
|
||||
また、もう送るシンボルがなくなったときには
|
||||
[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_id はパースエラーを起こした記号の内部表現 (整数) です。
|
||||
#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 を抜けます。
|
||||
@@ -0,0 +1,414 @@
|
||||
<h1>Racc の使い方</h1>
|
||||
<p>
|
||||
Racc は文法規則から Ruby で書かれたパーサを生成するパーサジェネレータです。
|
||||
パーサ生成アルゴリズムには yacc などと同じ LALR(1) を使用しています。
|
||||
</p>
|
||||
<p>
|
||||
yacc を知っている人は記述法の違いだけわかれば使えると思います。
|
||||
yacc を知らない人は
|
||||
拙著『Ruby を 256 倍使うための本 無道編』(青木峰郎著、ASCII)
|
||||
などを一読していただくのがよいかと思います。
|
||||
他の UNIX コマンドなどとは異なり、
|
||||
いきなり使うだけで Racc を理解するのはかなり困難です。
|
||||
</p>
|
||||
|
||||
<h2>Racc とはなにか</h2>
|
||||
<p>
|
||||
Racc は文法を処理するツールです。
|
||||
文字列はただの文字の列で、コンピュータにとっては意味を持ちません。
|
||||
しかし人間はその文字の列の中になにか意味を見出すことができます。
|
||||
コンピュータにもそのようなことを、部分的にでも、させられたら便利でしょう。
|
||||
Racc はその手伝いをしてくれます。完全な自動化ではありませんが、
|
||||
人間が全部やるよりも遥かに簡単になります。
|
||||
</p>
|
||||
<p>
|
||||
Racc が自動化してくれる部分とは、文字列の含む「構造」の処理です。
|
||||
たとえば Ruby の if 文を考えてみると、次のように定式化できます。
|
||||
</p>
|
||||
<pre>
|
||||
if 条件式 [then]
|
||||
文
|
||||
:
|
||||
[elsif 条件式 [then]
|
||||
文
|
||||
:]
|
||||
[else
|
||||
文
|
||||
:]
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
if 文では if という単語が最初になくてはならず、
|
||||
elsif 節は else 節より前になくてはいけません。
|
||||
このような配置の関係 (構造) が、Racc が処理する対象です。
|
||||
</p>
|
||||
<p>
|
||||
一方、Racc で処理できないのはどういうことでしょうか。それは、たとえば
|
||||
if の条件式にあたる部分が「なんであるか」ということです。つまり、条件
|
||||
式が if の条件だということです。これは、こっちで条件として扱うコードを
|
||||
書いてやらないといけません。
|
||||
</p>
|
||||
<p>
|
||||
と言っても、わかりにくいでしょう。こういう抽象的なものは実際にいじって
|
||||
みるのが一番です。
|
||||
</p>
|
||||
|
||||
<h2>実際の話</h2>
|
||||
<p>
|
||||
実際に Racc をどのように使うかという話をします。Racc には独自のソース
|
||||
コードみたいなものがあって、この中に処理したい「構造」を記述しておきま
|
||||
す。このソースファイルを「文法ファイル」と呼ぶことにしましょう。この文
|
||||
法ファイルの名前が parse.y と仮定すると、コマンドラインから以下のよう
|
||||
に打ちこめば、その構造を処理するためのクラスを含んだファイルが得られま
|
||||
す。
|
||||
</p>
|
||||
<pre>
|
||||
$ racc parse.y
|
||||
</pre>
|
||||
<p>
|
||||
生成されるファイルはデフォルトでは "ファイル名.tab.rb" です。他の名前
|
||||
にしたいなら、-o オプションで変更できます。
|
||||
</p>
|
||||
<pre>
|
||||
$ racc parse.y -o myparser.rb
|
||||
</pre>
|
||||
<p>
|
||||
このようにして作ったクラス、またはそのような処理を担当するパート、
|
||||
のことはパーサ (parser) と呼ぶことになっています。解析するヤツ、
|
||||
というくらいに適当にとらえてください。
|
||||
</p>
|
||||
|
||||
<h2>文法ファイルを書く</h2>
|
||||
<p>
|
||||
Racc は文法ファイルから Ruby のクラスを生成するツールだと言いました。
|
||||
そのクラスは全て Racc::Parser の下位クラスで、名前は文法ファイル中で
|
||||
指定します。以下、ここに書くべきことが「なんなのか」を説明します。
|
||||
ここでは内容に重点を置くので、文法ファイル自体の文法の詳細は
|
||||
<a href="grammar.html">文法リファレンス</a>を見てください。
|
||||
</p>
|
||||
|
||||
<h3>文法</h3>
|
||||
<p>
|
||||
まずは、全体の概形です。
|
||||
</p>
|
||||
<pre>
|
||||
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
|
||||
</pre>
|
||||
<p>
|
||||
Ruby スクリプトのように class でパーサクラス名を指定し、rule ... end
|
||||
の間にパーサに解析させたい文法を記述します。
|
||||
</p>
|
||||
<p>
|
||||
文法は、記号の並びでもって表します。rule ... end の間にあるコロンとバー
|
||||
以外のもの、if_stmt IF expr then などが全て「記号」です。そしてコロン
|
||||
が日本語で言う「〜は××だ」の「は」みたいなもんで、その左の記号が右の
|
||||
記号の列と同じものを指す、というふうに定義します。また、バーは「または」
|
||||
を意味します。それと、単純にコロンの左の記号のことを左辺、右を右辺とも
|
||||
言います。以下はこちらのほうを使って説明しましょう。
|
||||
</p>
|
||||
<p>
|
||||
少し注意が必要な点を述べます。まず、then の、バーのあとの定義 (規則) を
|
||||
見てください。ここには何も書いていないので、これはその通り「無」であっ
|
||||
てもいい、ということを表しています。つまり、then は記号 THEN 一個か、
|
||||
またはなにもなし(省略する)でよい、ということです。記号 then は実際の
|
||||
Ruby のソースコードにある then とは切り離して考えましょう
|
||||
(それは実は大文字の記号 THEN が表しています)。
|
||||
</p>
|
||||
<p>
|
||||
さて、そろそろ「記号」というものがなんなのか書きましょう。
|
||||
ただし順番に話をしないといけないので、まずは聞いていてください。
|
||||
この文章の最初に、パーサとは文字の列から構造を見出す部分だと言いました。
|
||||
しかし文字の列からいきなり構造を探すのは面倒なので、実際にはまず
|
||||
文字の列を単語の列に分割します。その時点でスペースやコメントは捨てて
|
||||
しまい、以降は純粋にプログラムの一部をなす部分だけを相手にします。
|
||||
たとえば文字列の入力が次のようだったとすると、
|
||||
</p>
|
||||
<pre>
|
||||
if flag then # item found.
|
||||
puts 'ok'
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
単語の列は次のようになります。
|
||||
</p>
|
||||
<pre>
|
||||
if flag then puts 'ok' end
|
||||
</pre>
|
||||
<p>
|
||||
ここで、工夫が必要です。どうやら flag はローカル変数名だと思われますが、
|
||||
変数名というのは他にもいろいろあります。しかし名前が i だろうが a だろ
|
||||
うが vvvvvvvvvvvv だろうが、「構造」は同じです。つまり同じ扱いをされる
|
||||
べきです。変数 a を書ける場所なら b も書けなくてはいけません。だったら
|
||||
一時的に同じ名前で読んでもいいじゃん。ということで、この単語の列を以下
|
||||
のように読みかえましょう。
|
||||
</p>
|
||||
<pre>
|
||||
IF IDENT THEN IDENT STRING END
|
||||
</pre>
|
||||
<p>
|
||||
これが「記号」の列です。パーサではこの記号列のほうを扱い、構造を見付け
|
||||
ていきます。
|
||||
</p>
|
||||
<p>
|
||||
さらに記号について見ていきましょう。
|
||||
記号は二種類に分けられます。「左辺にある記号」と「ない記号」です。
|
||||
左辺にある記号は「非終端」記号と言います。ないほうは「終端」記号と
|
||||
言います。最初の例では終端記号はすべて大文字、非終端記号は小文字で
|
||||
書いてあるので、もう一度戻って例の文法を見てください。
|
||||
</p>
|
||||
<p>
|
||||
なぜこの区分が重要かと言うと、入力の記号列はすべて終端記号だからです。
|
||||
一方、非終端記号はパーサの中でだけ、終端記号の列から「作りだす」ことに
|
||||
よって始めて存在します。例えば次の規則をもう一度見てください。
|
||||
</p>
|
||||
<pre>
|
||||
expr : NUMBER
|
||||
| IDENT
|
||||
| STRING
|
||||
</pre>
|
||||
<p>
|
||||
expr は NUMBER か IDENT か STRING だと言っています。逆に言うと、
|
||||
IDENT は expr に「なることができます」。文法上 expr が存在できる
|
||||
場所に IDENT が来ると、それは expr になります。例えば if の条件式の
|
||||
部分は expr ですから、ここに IDENT があると expr になります。その
|
||||
ように文法的に「大きい」記号を作っていって、最終的に一個になると、
|
||||
その入力は文法を満たしていることになります。実際にさっきの入力で
|
||||
試してみましょう。入力はこうでした。
|
||||
</p>
|
||||
<pre>
|
||||
IF IDENT THEN IDENT STRING END
|
||||
</pre>
|
||||
<p>
|
||||
まず、IDENT が expr になります。
|
||||
</p>
|
||||
<pre>
|
||||
IF expr THEN IDENT STRING END
|
||||
</pre>
|
||||
<p>
|
||||
次に THEN が then になります。
|
||||
</p>
|
||||
<pre>
|
||||
IF expr then IDENT STRING END
|
||||
</pre>
|
||||
<p>
|
||||
IDENT STRING がメソッドコールになります。この定義はさきほどの例には
|
||||
ないですが、実は省略されているんだと考えてください。そしていろいろな
|
||||
過程を経て、最終的には stmt_list (文のリスト)になります。
|
||||
</p>
|
||||
<pre>
|
||||
IF expr then stmt_list END
|
||||
</pre>
|
||||
<p>
|
||||
elsif と else は省略できる、つまり無から生成できます。
|
||||
</p>
|
||||
<pre>
|
||||
IF expr then stmt_list elsif else END
|
||||
</pre>
|
||||
<p>
|
||||
最後に if_stmt を作ります。
|
||||
</p>
|
||||
<pre>
|
||||
if_stmt
|
||||
</pre>
|
||||
<p>
|
||||
ということでひとつになりました。
|
||||
つまりこの入力は文法的に正しいということがわかりました。
|
||||
</p>
|
||||
|
||||
<h3>アクション</h3>
|
||||
<p>
|
||||
ここまでで入力の文法が正しいかどうかを確認する方法はわかりましたが、
|
||||
これだけではなんにもなりません。最初に説明したように、ここまででは
|
||||
構造が見えただけで、プログラムは「意味」を理解できません。そしてその
|
||||
部分は Racc では自動処理できないので、人間が書く、とも言いました。
|
||||
それを書くのが以下に説明する「アクション」という部分です。
|
||||
</p>
|
||||
<p>
|
||||
前項で、記号の列がだんだんと大きな単位にまとめられていく過程を見ました。
|
||||
そのまとめる時に、同時になにかをやらせることができます。それが
|
||||
アクションです。アクションは、文法ファイルで以下のように書きます。
|
||||
</p>
|
||||
<pre>
|
||||
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
|
||||
</pre>
|
||||
<p>
|
||||
見てのとおり、規則のあとに { と } で囲んで書きます。
|
||||
アクションにはだいたい好きなように Ruby スクリプトが書けます。
|
||||
</p>
|
||||
<p>
|
||||
(この節、未完)
|
||||
</p>
|
||||
<hr>
|
||||
|
||||
<p>
|
||||
yacc での <code>$$</code> は Racc ではローカル変数 <code>result</code>
|
||||
で、<code>$1,$2...</code> は配列 <var>val</var>です。
|
||||
<code>result</code> は <code>val[0]</code> ($1) の値に初期化され、
|
||||
アクションを抜けたときの <code>result</code> の値が左辺値になります。
|
||||
Racc ではアクション中の <code>return</code> はアクションから抜けるだけで、
|
||||
パース自体は終わりません。アクション中からパースを終了するには、
|
||||
メソッド <code>yyaccept</code> を使ってください。
|
||||
</p>
|
||||
<p>
|
||||
演算子の優先順位、スタートルールなどの yacc の一般的な機能も用意されて
|
||||
います。ただしこちらも少し文法が違います。
|
||||
</p>
|
||||
<p>
|
||||
yacc では生成されたコードに直接転写されるコードがありました。
|
||||
Racc でも同じように、ユーザ指定のコードが書けます。
|
||||
Racc ではクラスを生成するので、クラス定義の前/中/後の三個所があります。
|
||||
Racc ではそれを上から順番に header inner footer と呼んでいます。
|
||||
</p>
|
||||
|
||||
<h3>ユーザが用意すべきコード</h3>
|
||||
<p>
|
||||
パースのエントリポイントとなるメソッドは二つあります。ひとつは
|
||||
<code>do_parse</code>で、こちらはトークンを
|
||||
<code>Parser#next_token</code> から得ます。もうひとつは
|
||||
<code>yyparse</code> で、こちらはスキャナから <code>yield</code> され
|
||||
ることによってトークンを得ます。ユーザ側ではこのどちらか(両方でもいい
|
||||
けど)を起動する簡単なメソッドを inner に書いてください。これらメソッド
|
||||
の引数など、詳しいことはリファレンスを見てください。
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="parser.html#Racc%3a%3aParser-do_parse">do_parse</a>
|
||||
<li><a href="parser.html#Racc%3a%3aParser-yyparse">yyparse</a>
|
||||
</ul>
|
||||
<p>
|
||||
どちらのメソッドにも共通なのはトークンの形式です。必ずトークンシンボル
|
||||
とその値の二要素を持つ配列を返すようにします。またスキャンが終了して、
|
||||
もう送るものがない場合は <code>[false,<var>なにか</var>]</code> を返し
|
||||
てください。これは一回返せば十分です (逆に、<code>yyparse</code> を使
|
||||
う場合は二回以上 <code>yield</code> してはいけない)。
|
||||
</p>
|
||||
<p>
|
||||
パーサは別に文字列処理にだけ使われるものではありませんが、実際問題とし
|
||||
て、パーサを作る場面ではたいてい文字列のスキャナとセットで使うことが多
|
||||
いでしょう。Ruby ならスキャナくらい楽勝で作れますが、高速なスキャナと
|
||||
なると実は難しかったりします。そこで高速なスキャナを作成するためのライ
|
||||
ブラリも作っています。詳しくは
|
||||
<a href="#WritingScanner">「スキャナを作る」の項</a>を見てください。
|
||||
</p>
|
||||
<p>
|
||||
Racc には error トークンを使ったエラー回復機能もあります。yacc の
|
||||
<code>yyerror()</code> は Racc では
|
||||
<a href="parser.html#Racc%3a%3aParser-on_error"><code>Racc::Parser#on_error</code></a>
|
||||
で、エラーが起きたトークンとその値、値スタック、の三つの引数をとります。
|
||||
<code>on_error</code> のデフォルトの実装は例外
|
||||
<code>Racc::ParseError</code> を発生します。
|
||||
</p>
|
||||
<p>
|
||||
ユーザがアクション中でパースエラーを発見した場合は、メソッド
|
||||
<a href="parser.html#Racc%3a%3aParser-yyerror"><code>yyerror</code></a>
|
||||
を呼べばパーサがエラー回復モードに入ります。
|
||||
ただしこのとき <code>on_error</code>は呼ばれません。
|
||||
</p>
|
||||
|
||||
<h3>パーサを生成する</h3>
|
||||
<p>
|
||||
これだけあればだいたい書けると思います。あとは、最初に示した方法で文法
|
||||
ファイルを処理し、Ruby スクリプトを得ます。
|
||||
</p>
|
||||
<p>
|
||||
うまくいけばいいのですが、大きいものだと最初からはうまくいかないでしょ
|
||||
う。racc に -g オプションをつけてコンパイルし、@yydebug を true にする
|
||||
とデバッグ用の出力が得られます。デバッグ出力はパーサの @racc_debug_out
|
||||
に出力されます(デフォルトは stderr)。また、racc に -v オプションをつけ
|
||||
ると、状態遷移表を読みやすい形で出力したファイル(*.output)が得られます。
|
||||
どちらもデバッグの参考になるでしょう。
|
||||
</p>
|
||||
|
||||
|
||||
<h2>作ったパーサを配布する</h2>
|
||||
<p>
|
||||
Racc の生成したパーサは動作時にランタイムルーチンが必要です。
|
||||
具体的には parser.rb と cparse.so です。
|
||||
ただし cparse.so は単にパースを高速化するためのライブラリなので
|
||||
必須ではありません。なくても動きます。
|
||||
</p>
|
||||
<p>
|
||||
まず Ruby 1.8.0 以降にはこのランタイムが標準添付されているので、
|
||||
Ruby 1.8 がある環境ならばランタイムについて考慮する必要はありません。
|
||||
Racc 1.4.x のランタイムと Ruby 1.8 に添付されているランタイムは
|
||||
完全互換です。
|
||||
</p>
|
||||
<p>
|
||||
問題は Ruby 1.8 を仮定できない場合です。
|
||||
Racc をユーザみんなにインストールしてもらうのも一つの手ですが、
|
||||
これでは不親切です。そこでRacc では回避策を用意しました。
|
||||
</p>
|
||||
<p>
|
||||
racc に -E オプションをつけてコンパイルすると、
|
||||
パーサと racc/parser.rb を合体したファイルを出力できます。
|
||||
これならばファイルは一つだけなので簡単に扱えます。
|
||||
racc/parser.rb は擬似的に require したような扱いになるので、
|
||||
この形式のパーサが複数あったとしてもクラスやメソッドが衝突することもありません。
|
||||
ただし -E を使った場合は cparse.so が使えませんので、
|
||||
必然的にパーサの速度は落ちます。
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a name="WritingScanner">おまけ: スキャナを書く</a></h2>
|
||||
<p>
|
||||
パーサを使うときは、たいてい文字列をトークンに切りわけてくれるスキャナ
|
||||
が必要になります。しかし実は Ruby は文字列の最初からトークンに切りわけ
|
||||
ていくという作業があまり得意ではありません。
|
||||
正確に言うと、簡単にできるのですが、それなりのオーバーヘッドがかかります。
|
||||
</p>
|
||||
<p>
|
||||
そのオーバーヘッドを回避しつつ、
|
||||
手軽にスキャナを作れるように strscan というパッケージを作りました。
|
||||
Ruby 1.8 以降には標準添付されていますし、
|
||||
<a href="http://i.loveruby.net/ja/">筆者のホームページ</a>には
|
||||
単体パッケージがあります。
|
||||
</p>
|
||||
@@ -0,0 +1,269 @@
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
# V=0 quiet, V=1 verbose. other values don't work.
|
||||
V = 0
|
||||
V0 = $(V:0=)
|
||||
Q1 = $(V:1=)
|
||||
Q = $(Q1:0=@)
|
||||
ECHO1 = $(V:1=@ :)
|
||||
ECHO = $(ECHO1:0=@ echo)
|
||||
NULLCMD = :
|
||||
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = .
|
||||
topdir = /usr/include/ruby-3.2.0
|
||||
hdrdir = $(topdir)
|
||||
arch_hdrdir = /usr/include/x86_64-linux-gnu/ruby-3.2.0
|
||||
PATH_SEPARATOR = :
|
||||
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
||||
prefix = $(DESTDIR)/usr
|
||||
rubysitearchprefix = $(sitearchlibdir)/$(RUBY_BASE_NAME)
|
||||
rubyarchprefix = $(archlibdir)/$(RUBY_BASE_NAME)
|
||||
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
||||
exec_prefix = $(prefix)
|
||||
vendorarchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/vendor_ruby
|
||||
sitearchhdrdir = $(sitearchincludedir)/$(RUBY_VERSION_NAME)/site_ruby
|
||||
rubyarchhdrdir = $(archincludedir)/$(RUBY_VERSION_NAME)
|
||||
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
||||
sitehdrdir = $(rubyhdrdir)/site_ruby
|
||||
rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
|
||||
vendorarchdir = $(rubysitearchprefix)/vendor_ruby/$(ruby_version)
|
||||
vendorlibdir = $(vendordir)/$(ruby_version)
|
||||
vendordir = $(rubylibprefix)/vendor_ruby
|
||||
sitearchdir = $(DESTDIR)/usr/local/lib/x86_64-linux-gnu/site_ruby
|
||||
sitelibdir = $(sitedir)/$(ruby_version)
|
||||
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
|
||||
rubyarchdir = $(rubyarchprefix)/$(ruby_version)
|
||||
rubylibdir = $(rubylibprefix)/$(ruby_version)
|
||||
sitearchincludedir = $(includedir)/$(sitearch)
|
||||
archincludedir = $(includedir)/$(arch)
|
||||
sitearchlibdir = $(libdir)/$(sitearch)
|
||||
archlibdir = $(libdir)/$(arch)
|
||||
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
||||
mandir = $(datarootdir)/man
|
||||
localedir = $(datarootdir)/locale
|
||||
libdir = $(exec_prefix)/lib
|
||||
psdir = $(docdir)
|
||||
pdfdir = $(docdir)
|
||||
dvidir = $(docdir)
|
||||
htmldir = $(docdir)
|
||||
infodir = $(datarootdir)/info
|
||||
docdir = $(datarootdir)/doc/$(PACKAGE)
|
||||
oldincludedir = $(DESTDIR)/usr/include
|
||||
includedir = $(prefix)/include
|
||||
runstatedir = $(DESTDIR)/var/run
|
||||
localstatedir = $(DESTDIR)/var
|
||||
sharedstatedir = $(prefix)/com
|
||||
sysconfdir = $(DESTDIR)/etc
|
||||
datadir = $(datarootdir)
|
||||
datarootdir = $(prefix)/share
|
||||
libexecdir = $(exec_prefix)/libexec
|
||||
sbindir = $(exec_prefix)/sbin
|
||||
bindir = $(exec_prefix)/bin
|
||||
archdir = $(rubyarchdir)
|
||||
|
||||
|
||||
CC_WRAPPER =
|
||||
CC = x86_64-linux-gnu-gcc
|
||||
CXX = x86_64-linux-gnu-g++
|
||||
LIBRUBY = $(LIBRUBY_SO)
|
||||
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
||||
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
||||
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static $(MAINLIBS)
|
||||
empty =
|
||||
OUTFLAG = -o $(empty)
|
||||
COUTFLAG = -o $(empty)
|
||||
CSRCFLAG = $(empty)
|
||||
|
||||
RUBY_EXTCONF_H =
|
||||
cflags = $(optflags) $(debugflags) $(warnflags)
|
||||
cxxflags =
|
||||
optflags = -O3 -fno-fast-math
|
||||
debugflags = -ggdb3
|
||||
warnflags = -Wall -Wextra -Wdeprecated-declarations -Wdiv-by-zero -Wduplicated-cond -Wimplicit-function-declaration -Wimplicit-int -Wmisleading-indentation -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wimplicit-fallthrough=0 -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable -Wundef
|
||||
cppflags =
|
||||
CCDLFLAGS = -fPIC
|
||||
CFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 -fPIC $(ARCH_FLAG)
|
||||
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
||||
DEFS =
|
||||
CPPFLAGS = -DRACC_INFO_VERSION=1.8.1 -Wdate-time -D_FORTIFY_SOURCE=3 $(DEFS) $(cppflags)
|
||||
CXXFLAGS = $(CCDLFLAGS) -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=BUILDDIR=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -fdebug-prefix-map=BUILDDIR=/usr/src/ruby3.2-3.2.3-1ubuntu0.24.04.8 $(ARCH_FLAG)
|
||||
ldflags = -L. -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fstack-protector-strong -rdynamic -Wl,-export-dynamic -Wl,--no-as-needed
|
||||
dldflags = -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now
|
||||
ARCH_FLAG =
|
||||
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
|
||||
LDSHARED = $(CC) -shared
|
||||
LDSHAREDXX = $(CXX) -shared
|
||||
AR = x86_64-linux-gnu-gcc-ar
|
||||
EXEEXT =
|
||||
|
||||
RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)3.2
|
||||
RUBY_SO_NAME = ruby-3.2
|
||||
RUBYW_INSTALL_NAME =
|
||||
RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
|
||||
RUBYW_BASE_NAME = rubyw
|
||||
RUBY_BASE_NAME = ruby
|
||||
|
||||
arch = x86_64-linux-gnu
|
||||
sitearch = $(arch)
|
||||
ruby_version = 3.2.0
|
||||
ruby = $(bindir)/$(RUBY_BASE_NAME)3.2
|
||||
RUBY = $(ruby)
|
||||
BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)3.2
|
||||
ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
|
||||
|
||||
RM = rm -f
|
||||
RM_RF = rm -fr
|
||||
RMDIRS = rmdir --ignore-fail-on-non-empty -p
|
||||
MAKEDIRS = /bin/mkdir -p
|
||||
INSTALL = /usr/bin/install -c
|
||||
INSTALL_PROG = $(INSTALL) -m 0755
|
||||
INSTALL_DATA = $(INSTALL) -m 644
|
||||
COPY = cp
|
||||
TOUCH = exit >
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
preload =
|
||||
libpath = . $(archlibdir)
|
||||
LIBPATH = -L. -L$(archlibdir)
|
||||
DEFFILE =
|
||||
|
||||
CLEANFILES = mkmf.log
|
||||
DISTCLEANFILES =
|
||||
DISTCLEANDIRS =
|
||||
|
||||
extout =
|
||||
extout_prefix =
|
||||
target_prefix = /racc
|
||||
LOCAL_LIBS =
|
||||
LIBS = $(LIBRUBYARG_SHARED) -lm -lpthread -lc
|
||||
ORIG_SRCS = cparse.c
|
||||
SRCS = $(ORIG_SRCS)
|
||||
OBJS = cparse.o
|
||||
HDRS =
|
||||
LOCAL_HDRS =
|
||||
TARGET = cparse
|
||||
TARGET_NAME = cparse
|
||||
TARGET_ENTRY = Init_$(TARGET_NAME)
|
||||
DLLIB = $(TARGET).so
|
||||
EXTSTATIC =
|
||||
STATIC_LIB =
|
||||
|
||||
TIMESTAMP_DIR = .
|
||||
BINDIR = $(bindir)
|
||||
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
||||
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
||||
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
||||
HDRDIR = $(sitehdrdir)$(target_prefix)
|
||||
ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
|
||||
TARGET_SO_DIR =
|
||||
TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
|
||||
CLEANLIBS = $(TARGET_SO) false
|
||||
CLEANOBJS = $(OBJS) *.bak
|
||||
TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.-.racc.time
|
||||
|
||||
all: $(DLLIB)
|
||||
static: $(STATIC_LIB)
|
||||
.PHONY: all install static install-so install-rb
|
||||
.PHONY: clean clean-so clean-static clean-rb
|
||||
|
||||
clean-static::
|
||||
clean-rb-default::
|
||||
clean-rb::
|
||||
clean-so::
|
||||
clean: clean-so clean-static clean-rb-default clean-rb
|
||||
-$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
|
||||
|
||||
distclean-rb-default::
|
||||
distclean-rb::
|
||||
distclean-so::
|
||||
distclean-static::
|
||||
distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
|
||||
-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
||||
-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
||||
-$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
|
||||
|
||||
realclean: distclean
|
||||
install: install-so install-rb
|
||||
|
||||
install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
|
||||
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
||||
clean-static::
|
||||
-$(Q)$(RM) $(STATIC_LIB)
|
||||
install-rb: pre-install-rb do-install-rb install-rb-default
|
||||
install-rb-default: pre-install-rb-default do-install-rb-default
|
||||
pre-install-rb: Makefile
|
||||
pre-install-rb-default: Makefile
|
||||
do-install-rb:
|
||||
do-install-rb-default:
|
||||
pre-install-rb-default:
|
||||
@$(NULLCMD)
|
||||
$(TARGET_SO_DIR_TIMESTAMP):
|
||||
$(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
|
||||
$(Q) $(TOUCH) $@
|
||||
|
||||
site-install: site-install-so site-install-rb
|
||||
site-install-so: install-so
|
||||
site-install-rb: install-rb
|
||||
|
||||
.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
|
||||
|
||||
.cc.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cc.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.mm.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.mm.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.cxx.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cxx.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.cpp.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.cpp.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.c.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.c.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
.m.o:
|
||||
$(ECHO) compiling $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
|
||||
|
||||
.m.S:
|
||||
$(ECHO) translating $(<)
|
||||
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
|
||||
|
||||
$(TARGET_SO): $(OBJS) Makefile
|
||||
$(ECHO) linking shared-object racc/$(DLLIB)
|
||||
-$(Q)$(RM) $(@)
|
||||
$(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
||||
|
||||
|
||||
|
||||
$(OBJS): $(HDRS) $(ruby_headers)
|
||||
@@ -0,0 +1,840 @@
|
||||
/*
|
||||
|
||||
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 <ruby.h>
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
Important Constants
|
||||
----------------------------------------------------------------------- */
|
||||
|
||||
#define RACC_VERSION STRINGIZE(RACC_INFO_VERSION)
|
||||
|
||||
#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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
call_lexer(struct cparse_params *v)
|
||||
{
|
||||
rb_block_call(v->lexer, v->lexmid, 0, NULL, lexer_i, v->value_v);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
#
|
||||
|
||||
require 'mkmf'
|
||||
require_relative '../../../lib/racc/info'
|
||||
|
||||
$defs << "-D""RACC_INFO_VERSION=#{Racc::VERSION}"
|
||||
create_makefile 'racc/cparse'
|
||||
@@ -0,0 +1,6 @@
|
||||
require_relative 'racc/compat'
|
||||
require_relative 'racc/debugflags'
|
||||
require_relative 'racc/grammar'
|
||||
require_relative 'racc/state'
|
||||
require_relative 'racc/exception'
|
||||
require_relative 'racc/info'
|
||||
@@ -0,0 +1,33 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,60 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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
|
||||
@@ -0,0 +1,16 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,667 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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_relative '../racc'
|
||||
require_relative 'compat'
|
||||
require_relative 'grammar'
|
||||
require_relative 'parserfilegenerator'
|
||||
require_relative '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
|
||||
}\
|
||||
| seq(:ERROR_ON_EXPECT_MISMATCH) {|*|
|
||||
@grammar.error_on_expect_mismatch = true
|
||||
}
|
||||
|
||||
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("?") {|*|
|
||||
OptionMark.new(@scanner.lineno)
|
||||
}\
|
||||
| seq("*") {|*|
|
||||
ManyMark.new(@scanner.lineno)
|
||||
}\
|
||||
| seq("+") {|*|
|
||||
Many1Mark.new(@scanner.lineno)
|
||||
}\
|
||||
| seq("(") {|*|
|
||||
GroupStartMark.new(@scanner.lineno)
|
||||
}\
|
||||
| seq(")") {|*|
|
||||
GroupEndMark.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)
|
||||
target = list.shift
|
||||
case target
|
||||
when OrMark, OptionMark, ManyMark, Many1Mark, GroupStartMark, GroupEndMark, UserAction, Prec
|
||||
raise CompileError, "#{target.lineno}: unexpected symbol #{target.name}"
|
||||
end
|
||||
enum = list.each.with_index
|
||||
_, sym, idx = _add_rule_block(target, enum)
|
||||
if idx
|
||||
# sym is Racc::GroupEndMark
|
||||
raise "#{sym.lineno}: unexpected symbol ')' at pos=#{idx}"
|
||||
end
|
||||
end
|
||||
|
||||
def _add_rule_block(target, enum)
|
||||
rules = [] # [ [seqs, sprec], .. ]
|
||||
curr = []
|
||||
sprec = nil
|
||||
while (sym, idx = enum.next rescue nil)
|
||||
case sym
|
||||
when OrMark
|
||||
rules << [curr, sprec]
|
||||
curr = []
|
||||
sprec = nil
|
||||
when OptionMark
|
||||
curr << _add_option_rule(curr.pop)
|
||||
when ManyMark
|
||||
curr << _add_many_rule(curr.pop)
|
||||
when Many1Mark
|
||||
curr << _add_many1_rule(curr.pop)
|
||||
when GroupStartMark
|
||||
curr << _add_group_rule(enum)
|
||||
when GroupEndMark
|
||||
rules << [curr, sprec]
|
||||
return rules, sym, idx
|
||||
when Prec
|
||||
raise CompileError, "'=<prec>' used twice in one rule" if sprec
|
||||
sprec = sym.symbol
|
||||
else
|
||||
curr.push sym
|
||||
end
|
||||
end
|
||||
rules << [curr, sprec]
|
||||
rules.each do |syms, sprec|
|
||||
add_rule target, syms, sprec
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
def _add_option_rule(prev)
|
||||
@option_rule_registry ||= {}
|
||||
target = @option_rule_registry[prev.to_s]
|
||||
return target if target
|
||||
target = _gen_target_name("option", prev)
|
||||
@option_rule_registry[prev.to_s] = target
|
||||
act = UserAction.empty
|
||||
@grammar.add Rule.new(target, [], act)
|
||||
@grammar.add Rule.new(target, [prev], act)
|
||||
target
|
||||
end
|
||||
|
||||
def _add_many_rule(prev)
|
||||
@many_rule_registry ||= {}
|
||||
target = @many_rule_registry[prev.to_s]
|
||||
return target if target
|
||||
target = _gen_target_name("many", prev)
|
||||
@many_rule_registry[prev.to_s] = target
|
||||
src = SourceText.new("result = val[1] ? val[1].unshift(val[0]) : val", @filename, @scanner.lineno + 1)
|
||||
act = UserAction.source_text(src)
|
||||
@grammar.add Rule.new(target, [], act)
|
||||
@grammar.add Rule.new(target, [prev, target], act)
|
||||
target
|
||||
end
|
||||
|
||||
def _add_many1_rule(prev)
|
||||
@many1_rule_registry ||= {}
|
||||
target = @many1_rule_registry[prev.to_s]
|
||||
return target if target
|
||||
target = _gen_target_name("many1", prev)
|
||||
@many1_rule_registry[prev.to_s] = target
|
||||
src = SourceText.new("result = val[1] ? val[1].unshift(val[0]) : val", @filename, @scanner.lineno + 1)
|
||||
act = UserAction.source_text(src)
|
||||
@grammar.add Rule.new(target, [prev], act)
|
||||
@grammar.add Rule.new(target, [prev, target], act)
|
||||
target
|
||||
end
|
||||
|
||||
def _add_group_rule(enum)
|
||||
target = @grammar.intern("-temp-group", true)
|
||||
rules, _ = _add_rule_block(target, enum)
|
||||
target_name = rules.map{|syms, sprec| syms.join("-")}.join("|")
|
||||
@group_rule_registry ||= {}
|
||||
unless target = @group_rule_registry[target_name]
|
||||
target = @grammar.intern("-group@#{target_name}", true)
|
||||
@group_rule_registry[target_name] = target
|
||||
src = SourceText.new("result = val", @filename, @scanner.lineno + 1)
|
||||
act = UserAction.source_text(src)
|
||||
rules.each do |syms, sprec|
|
||||
rule = Rule.new(target, syms, act)
|
||||
rule.specified_prec = sprec
|
||||
@grammar.add rule
|
||||
end
|
||||
end
|
||||
target
|
||||
end
|
||||
|
||||
def _gen_target_name(type, sym)
|
||||
@grammar.intern("-#{type}@#{sym.value}", true)
|
||||
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,
|
||||
'error_on_expect_mismatch' => :ERROR_ON_EXPECT_MISMATCH,
|
||||
'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
|
||||
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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.8.1'
|
||||
Version = VERSION
|
||||
Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
|
||||
end
|
||||
@@ -0,0 +1,92 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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
|
||||
@@ -0,0 +1,212 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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 = <<SRC
|
||||
%s (%d)
|
||||
on right: %s
|
||||
on left : %s
|
||||
SRC
|
||||
out.printf tmp, t.to_s, t.ident,
|
||||
symbol_locations(t.locate).join(' '),
|
||||
symbol_locations(t.heads).join(' ')
|
||||
end
|
||||
|
||||
out.print "\n**Terminals, with rules where they appear\n\n"
|
||||
@grammar.each_terminal do |t|
|
||||
out.printf " %s (%d) %s\n",
|
||||
t.to_s, t.ident, symbol_locations(t.locate).join(' ')
|
||||
end
|
||||
end
|
||||
|
||||
def symbol_locations(locs)
|
||||
locs.map {|loc| loc.rule.ident }.reject {|n| n == 0 }.uniq
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end # module Racc
|
||||
@@ -0,0 +1,644 @@
|
||||
module Racc
|
||||
PARSER_TEXT = <<'__end_of_file__'
|
||||
#--
|
||||
# Copyright (c) 1999-2006 Minero Aoki
|
||||
#
|
||||
# This program is free software.
|
||||
# You can distribute/modify this program under the same terms of ruby.
|
||||
#
|
||||
# As a special exception, when this code is copied by Racc
|
||||
# into a Racc output file, you may use that output file
|
||||
# without restriction.
|
||||
#++
|
||||
|
||||
unless $".find {|p| p.end_with?('/racc/info.rb')}
|
||||
$".push "#{__dir__}/racc/info.rb"
|
||||
|
||||
module Racc
|
||||
VERSION = '1.8.1'
|
||||
Version = VERSION
|
||||
Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
module Racc
|
||||
class ParseError < StandardError; end
|
||||
end
|
||||
unless defined?(::ParseError)
|
||||
ParseError = Racc::ParseError # :nodoc:
|
||||
end
|
||||
|
||||
# Racc is an LALR(1) parser generator.
|
||||
# It is written in Ruby itself, and generates Ruby programs.
|
||||
#
|
||||
# == Command-line Reference
|
||||
#
|
||||
# racc [-o<var>filename</var>] [--output-file=<var>filename</var>]
|
||||
# [-e<var>rubypath</var>] [--executable=<var>rubypath</var>]
|
||||
# [-v] [--verbose]
|
||||
# [-O<var>filename</var>] [--log-file=<var>filename</var>]
|
||||
# [-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] <var>grammarfile</var>
|
||||
#
|
||||
# [+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 debugging 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).
|
||||
# [-F, --frozen]
|
||||
# Output parser which declares frozen_string_literals: true
|
||||
# [-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, <code>yyparse(obj, :scan)</code> 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 <<~RUBY, __FILE__, __LINE__ + 1
|
||||
def do_parse
|
||||
#{Racc_Main_Parsing_Routine}(_racc_setup(), false)
|
||||
end
|
||||
RUBY
|
||||
|
||||
# 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 <<~RUBY, __FILE__, __LINE__ + 1
|
||||
def yyparse(recv, mid)
|
||||
#{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false)
|
||||
end
|
||||
RUBY
|
||||
|
||||
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("parse 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 ' <none>'
|
||||
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
|
||||
|
||||
__end_of_file__
|
||||
end
|
||||
@@ -0,0 +1,630 @@
|
||||
# frozen_string_literal: true
|
||||
#--
|
||||
# Copyright (c) 1999-2006 Minero Aoki
|
||||
#
|
||||
# This program is free software.
|
||||
# You can distribute/modify this program under the same terms of ruby.
|
||||
#
|
||||
# As a special exception, when this code is copied by Racc
|
||||
# into a Racc output file, you may use that output file
|
||||
# without restriction.
|
||||
#++
|
||||
|
||||
require 'racc/info'
|
||||
|
||||
module Racc
|
||||
class ParseError < StandardError; end
|
||||
end
|
||||
unless defined?(::ParseError)
|
||||
ParseError = Racc::ParseError # :nodoc:
|
||||
end
|
||||
|
||||
# Racc is an LALR(1) parser generator.
|
||||
# It is written in Ruby itself, and generates Ruby programs.
|
||||
#
|
||||
# == Command-line Reference
|
||||
#
|
||||
# racc [-o<var>filename</var>] [--output-file=<var>filename</var>]
|
||||
# [-e<var>rubypath</var>] [--executable=<var>rubypath</var>]
|
||||
# [-v] [--verbose]
|
||||
# [-O<var>filename</var>] [--log-file=<var>filename</var>]
|
||||
# [-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] <var>grammarfile</var>
|
||||
#
|
||||
# [+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 debugging 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).
|
||||
# [-F, --frozen]
|
||||
# Output parser which declares frozen_string_literals: true
|
||||
# [-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, <code>yyparse(obj, :scan)</code> 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 <<~RUBY, __FILE__, __LINE__ + 1
|
||||
def do_parse
|
||||
#{Racc_Main_Parsing_Routine}(_racc_setup(), false)
|
||||
end
|
||||
RUBY
|
||||
|
||||
# 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 <<~RUBY, __FILE__, __LINE__ + 1
|
||||
def yyparse(recv, mid)
|
||||
#{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), false)
|
||||
end
|
||||
RUBY
|
||||
|
||||
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("parse 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 ' <none>'
|
||||
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
|
||||
@@ -0,0 +1,473 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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_relative 'compat'
|
||||
require_relative 'sourcetext'
|
||||
require_relative '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 :frozen_strings
|
||||
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.frozen_strings = 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[# frozen_string_literal: true] if @params.frozen_strings?
|
||||
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 $".find {|p| p.end_with?('/#{src.filename}')}]
|
||||
line %[$".push "\#{__dir__}/#{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 "Ractor.make_shareable(Racc_arg) if defined?(Ractor)"
|
||||
line
|
||||
string_list 'Racc_token_to_s_table', table.token_to_s_table
|
||||
line "Ractor.make_shareable(Racc_token_to_s_table) if defined?(Ractor)"
|
||||
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
|
||||
@@ -0,0 +1,35 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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
|
||||
"#<SourceText #{location()}>"
|
||||
end
|
||||
|
||||
def location
|
||||
"#{@filename}:#{@lineno}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,976 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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_relative 'iset'
|
||||
require_relative 'statetransitiontable'
|
||||
require_relative '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
|
||||
'#<state table>'
|
||||
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 should_error_on_expect_mismatch?
|
||||
should_report_srconflict? && @grammar.error_on_expect_mismatch
|
||||
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
|
||||
"<state #{@ident}>"
|
||||
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
|
||||
"<shift #{@goto_state.ident}>"
|
||||
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
|
||||
"<reduce #{@rule.ident}>"
|
||||
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
|
||||
"<accept>"
|
||||
end
|
||||
end
|
||||
|
||||
class Error
|
||||
def inspect
|
||||
"<error>"
|
||||
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
|
||||
@@ -0,0 +1,311 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# 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_relative '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
|
||||
@@ -0,0 +1,5 @@
|
||||
require_relative '../racc'
|
||||
require_relative 'parser'
|
||||
require_relative 'grammarfileparser'
|
||||
require_relative 'parserfilegenerator'
|
||||
require_relative 'logfilegenerator'
|
||||
Reference in New Issue
Block a user