1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Backport racc-1.4.15 from upstream.

This commit is contained in:
Hiroshi SHIBATA 2019-05-13 21:25:22 +09:00
parent cbe06cd350
commit 1a2546c2be
111 changed files with 75498 additions and 43 deletions

View file

@ -11,7 +11,7 @@
*/
#include "ruby/ruby.h"
#include <ruby.h>
#ifndef FALSE
#define FALSE 0
@ -24,7 +24,7 @@
Important Constants
----------------------------------------------------------------------- */
#define RACC_VERSION "1.4.5"
#define RACC_VERSION "1.4.15"
#define DEFAULT_TOKEN -1
#define ERROR_TOKEN 1
@ -72,6 +72,10 @@ static ID id_d_e_pop;
# define LONG2NUM(i) INT2NUM(i)
#endif
#ifndef HAVE_RB_ARY_SUBSEQ
# define rb_ary_subseq(ary, beg, len) rb_ary_new4(len, RARRAY_PTR(ary) + beg)
#endif
static ID value_to_id _((VALUE v));
static inline long num_to_long _((VALUE n));

View file

@ -1,6 +0,0 @@
# frozen_string_literal: false
# $Id$
require 'mkmf'
have_func('rb_block_call', 'ruby/ruby.h')
create_makefile 'racc/cparse'

7
ext/racc/extconf.rb Normal file
View file

@ -0,0 +1,7 @@
# $Id: 1e30abedf4eea155815d1efa5500ec817b10a2ab $
require 'mkmf'
have_func('rb_ary_subseq')
create_makefile 'racc/cparse'

6
lib/racc.rb Normal file
View file

@ -0,0 +1,6 @@
require 'racc/compat'
require 'racc/debugflags'
require 'racc/grammar'
require 'racc/state'
require 'racc/exception'
require 'racc/info'

32
lib/racc/compat.rb Normal file
View file

@ -0,0 +1,32 @@
#
# $Id: 14fa1118eb3a23e85265e4f7afe2d5a297d69f9c $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of the GNU LGPL, 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

59
lib/racc/debugflags.rb Normal file
View file

@ -0,0 +1,59 @@
#
# $Id: 74ff4369ce53c7f45cfc2644ce907785104ebf6e $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of LGPL, 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

13
lib/racc/exception.rb Normal file
View file

@ -0,0 +1,13 @@
#
# $Id: d5858363d1d4a4b5a2ca8d193b5153a49312188e $
#
# 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

1113
lib/racc/grammar.rb Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,559 @@
#
# $Id: 5e1871defa15d288d2252e6a76bb2c4cf2119ed3 $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of the GNU LGPL, see the file "COPYING".
#
require 'racc'
require 'racc/compat'
require 'racc/grammar'
require 'racc/parserfilegenerator'
require 'racc/sourcetext'
require 'stringio'
module Racc
grammar = Grammar.define {
g = self
g.class = seq(:CLASS, :cname, many(:param), :RULE, :rules, option(:END))
g.cname = seq(:rubyconst) {|name|
@result.params.classname = name
}\
| seq(:rubyconst, "<", :rubyconst) {|c, _, s|
@result.params.classname = c
@result.params.superclass = s
}
g.rubyconst = separated_by1(:colon2, :SYMBOL) {|syms|
syms.map {|s| s.to_s }.join('::')
}
g.colon2 = seq(':', ':')
g.param = seq(:CONV, many1(:convdef), :END) {|*|
#@grammar.end_convert_block # FIXME
}\
| seq(:PRECHIGH, many1(:precdef), :PRECLOW) {|*|
@grammar.end_precedence_declaration true
}\
| seq(:PRECLOW, many1(:precdef), :PRECHIGH) {|*|
@grammar.end_precedence_declaration false
}\
| seq(:START, :symbol) {|_, sym|
@grammar.start_symbol = sym
}\
| seq(:TOKEN, :symbols) {|_, syms|
syms.each do |s|
s.should_terminal
end
}\
| seq(:OPTION, :options) {|_, syms|
syms.each do |opt|
case opt
when 'result_var'
@result.params.result_var = true
when 'no_result_var'
@result.params.result_var = false
when 'omit_action_call'
@result.params.omit_action_call = true
when 'no_omit_action_call'
@result.params.omit_action_call = false
else
raise CompileError, "unknown option: #{opt}"
end
end
}\
| seq(:EXPECT, :DIGIT) {|_, num|
if @grammar.n_expected_srconflicts
raise CompileError, "`expect' seen twice"
end
@grammar.n_expected_srconflicts = num
}
g.convdef = seq(:symbol, :STRING) {|sym, code|
sym.serialized = code
}
g.precdef = seq(:LEFT, :symbols) {|_, syms|
@grammar.declare_precedence :Left, syms
}\
| seq(:RIGHT, :symbols) {|_, syms|
@grammar.declare_precedence :Right, syms
}\
| seq(:NONASSOC, :symbols) {|_, syms|
@grammar.declare_precedence :Nonassoc, syms
}
g.symbols = seq(:symbol) {|sym|
[sym]
}\
| seq(:symbols, :symbol) {|list, sym|
list.push sym
list
}\
| seq(:symbols, "|")
g.symbol = seq(:SYMBOL) {|sym| @grammar.intern(sym) }\
| seq(:STRING) {|str| @grammar.intern(str) }
g.options = many(:SYMBOL) {|syms| syms.map {|s| s.to_s } }
g.rules = option(:rules_core) {|list|
add_rule_block list unless list.empty?
nil
}
g.rules_core = seq(:symbol) {|sym|
[sym]
}\
| seq(:rules_core, :rule_item) {|list, i|
list.push i
list
}\
| seq(:rules_core, ';') {|list, *|
add_rule_block list unless list.empty?
list.clear
list
}\
| seq(:rules_core, ':') {|list, *|
next_target = list.pop
add_rule_block list unless list.empty?
[next_target]
}
g.rule_item = seq(:symbol)\
| seq("|") {|*|
OrMark.new(@scanner.lineno)
}\
| seq("=", :symbol) {|_, sym|
Prec.new(sym, @scanner.lineno)
}\
| seq(:ACTION) {|src|
UserAction.source_text(src)
}
}
GrammarFileParser = grammar.parser_class
if grammar.states.srconflict_exist?
raise 'Racc boot script fatal: S/R conflict in build'
end
if grammar.states.rrconflict_exist?
raise 'Racc boot script fatal: R/R conflict in build'
end
class GrammarFileParser # reopen
class Result
def initialize(grammar)
@grammar = grammar
@params = ParserFileGenerator::Params.new
end
attr_reader :grammar
attr_reader :params
end
def GrammarFileParser.parse_file(filename)
parse(File.read(filename), filename, 1)
end
def GrammarFileParser.parse(src, filename = '-', lineno = 1)
new().parse(src, filename, lineno)
end
def initialize(debug_flags = DebugFlags.new)
@yydebug = debug_flags.parse
end
def parse(src, filename = '-', lineno = 1)
@filename = filename
@lineno = lineno
@scanner = GrammarFileScanner.new(src, @filename)
@scanner.debug = @yydebug
@grammar = Grammar.new
@result = Result.new(@grammar)
@embedded_action_seq = 0
yyparse @scanner, :yylex
parse_user_code
@result.grammar.init
@result
end
private
def next_token
@scanner.scan
end
def on_error(tok, val, _values)
if val.respond_to?(:id2name)
v = val.id2name
elsif val.kind_of?(String)
v = val
else
v = val.inspect
end
raise CompileError, "#{location()}: unexpected token '#{v}'"
end
def location
"#{@filename}:#{@lineno - 1 + @scanner.lineno}"
end
def add_rule_block(list)
sprec = nil
target = list.shift
case target
when OrMark, UserAction, Prec
raise CompileError, "#{target.lineno}: unexpected symbol #{target.name}"
end
curr = []
list.each do |i|
case i
when OrMark
add_rule target, curr, sprec
curr = []
sprec = nil
when Prec
raise CompileError, "'=<prec>' used twice in one rule" if sprec
sprec = i.symbol
else
curr.push i
end
end
add_rule target, curr, sprec
end
def add_rule(target, list, sprec)
if list.last.kind_of?(UserAction)
act = list.pop
else
act = UserAction.empty
end
list.map! {|s| s.kind_of?(UserAction) ? embedded_action(s) : s }
rule = Rule.new(target, list, act)
rule.specified_prec = sprec
@grammar.add rule
end
def embedded_action(act)
sym = @grammar.intern("@#{@embedded_action_seq += 1}".intern, true)
@grammar.add Rule.new(sym, [], act)
sym
end
#
# User Code Block
#
def parse_user_code
line = @scanner.lineno
_, *blocks = *@scanner.epilogue.split(/^----/)
blocks.each do |block|
header, *body = block.lines.to_a
label0, pathes = *header.sub(/\A-+/, '').split('=', 2)
label = canonical_label(label0)
(pathes ? pathes.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.send(USER_CODE_LABELS[label]).push src
end
end
class GrammarFileScanner
def initialize(str, filename = '-')
@lines = str.split(/\n|\r\n|\r/)
@filename = filename
@lineno = -1
@line_head = true
@in_rule_blk = false
@in_conv_blk = false
@in_block = nil
@epilogue = ''
@debug = false
next_line
end
attr_reader :epilogue
def lineno
@lineno + 1
end
attr_accessor :debug
def yylex(&block)
unless @debug
yylex0(&block)
else
yylex0 do |sym, tok|
$stderr.printf "%7d %-10s %s\n", lineno(), sym.inspect, tok.inspect
yield [sym, tok]
end
end
end
private
def yylex0
begin
until @line.empty?
@line.sub!(/\A\s+/, '')
if /\A\#/ =~ @line
break
elsif /\A\/\*/ =~ @line
skip_comment
elsif s = reads(/\A[a-zA-Z_]\w*/)
yield [atom_symbol(s), s.intern]
elsif s = reads(/\A\d+/)
yield [:DIGIT, s.to_i]
elsif ch = reads(/\A./)
case ch
when '"', "'"
yield [:STRING, eval(scan_quoted(ch))]
when '{'
lineno = lineno()
yield [:ACTION, SourceText.new(scan_action(), @filename, lineno)]
else
if ch == '|'
@line_head = false
end
yield [ch, ch]
end
else
end
end
end while next_line()
yield nil
end
def next_line
@lineno += 1
@line = @lines[@lineno]
if not @line or /\A----/ =~ @line
@epilogue = @lines.join("\n")
@lines.clear
@line = nil
if @in_block
@lineno -= 1
scan_error! sprintf('unterminated %s', @in_block)
end
false
else
@line.sub!(/(?:\n|\r\n|\r)\z/, '')
@line_head = true
true
end
end
ReservedWord = {
'right' => :RIGHT,
'left' => :LEFT,
'nonassoc' => :NONASSOC,
'preclow' => :PRECLOW,
'prechigh' => :PRECHIGH,
'token' => :TOKEN,
'convert' => :CONV,
'options' => :OPTION,
'start' => :START,
'expect' => :EXPECT,
'class' => :CLASS,
'rule' => :RULE,
'end' => :END
}
def atom_symbol(token)
if token == 'end'
symbol = :END
@in_conv_blk = false
@in_rule_blk = false
else
if @line_head and not @in_conv_blk and not @in_rule_blk
symbol = ReservedWord[token] || :SYMBOL
else
symbol = :SYMBOL
end
case symbol
when :RULE then @in_rule_blk = true
when :CONV then @in_conv_blk = true
end
end
@line_head = false
symbol
end
def skip_comment
@in_block = 'comment'
until m = /\*\//.match(@line)
next_line
end
@line = m.post_match
@in_block = nil
end
$raccs_print_type = false
def scan_action
buf = ''
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
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

14
lib/racc/info.rb Normal file
View file

@ -0,0 +1,14 @@
#
# $Id: 10d9595b388ab1ba061c08c038901ff632a0c3c3 $
#
# 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.4.15'
Version = VERSION
Copyright = 'Copyright (c) 1999-2006 Minero Aoki'
end

91
lib/racc/iset.rb Normal file
View file

@ -0,0 +1,91 @@
#
# $Id: de638608cfd72d3ed9819d87b65a89ee6a57b589 $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of the GNU LGPL, 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

View file

@ -0,0 +1,211 @@
#
# $Id: a7e9663605afdda065d305b250a9805e3bd3fa70 $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of the GNU LGPL, 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

640
lib/racc/parser-text.rb Normal file
View file

@ -0,0 +1,640 @@
module Racc
PARSER_TEXT = <<'__end_of_file__'
#
# $Id: 1c0ef52c0f41acc465725e9e44b5b9d74d392ba5 $
#
# 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'
unless defined?(NotImplementedError)
NotImplementedError = NotImplementError # :nodoc:
end
module Racc
class ParseError < StandardError; end
end
unless defined?(::ParseError)
ParseError = Racc::ParseError
end
# Racc is a 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>] [--embedded=<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>
#
# [+filename+]
# Racc grammar file. Any extention is permitted.
# [-o+outfile+, --output-file=+outfile+]
# A filename for output. default is <+filename+>.tab.rb
# [-O+filename+, --log-file=+filename+]
# Place logging output in file +filename+.
# Default log file name is <+filename+>.output.
# [-e+rubypath+, --executable=+rubypath+]
# output executable file(mode 755). where +path+ is the Ruby interpreter.
# [-v, --verbose]
# verbose mode. create +filename+.output file, like yacc's y.output file.
# [-g, --debug]
# add debug code to parser class. To display debuggin information,
# use this '-g' option and set @yydebug true in parser class.
# [-E, --embedded]
# Output parser which doesn't need runtime files (racc/parser.rb).
# [-C, --check-only]
# Check syntax of racc grammer 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_Extentions)
Racc_No_Extentions = false # :nodoc:
end
class Parser
Racc_Runtime_Version = ::Racc::VERSION
Racc_Runtime_Revision = '$Id: 1c0ef52c0f41acc465725e9e44b5b9d74d392ba5 $'
Racc_Runtime_Core_Version_R = ::Racc::VERSION
Racc_Runtime_Core_Revision_R = '$Id: 1c0ef52c0f41acc465725e9e44b5b9d74d392ba5 $'.split[1]
begin
if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
require 'racc/cparse-jruby.jar'
com.headius.racc.Cparse.new.load(JRuby.runtime, false)
else
require 'racc/cparse'
end
# Racc_Runtime_Core_Version_C = (defined in extention)
Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2]
unless new.respond_to?(:_racc_do_parse_c, true)
raise LoadError, 'old cparse.so'
end
if Racc_No_Extentions
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_Core_Revision = Racc_Runtime_Core_Revision_C # :nodoc:
Racc_Runtime_Type = 'c' # :nodoc:
rescue LoadError
puts $!
puts $!.backtrace
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_Core_Revision = Racc_Runtime_Core_Revision_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
def do_parse
__send__(Racc_Main_Parsing_Routine, _racc_setup(), false)
end
# The method to fetch next token.
# If you use #do_parse method, you must implement #next_token.
#
# The format of return value is [TOKEN_SYMBOL, VALUE].
# +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT
# for 'IDENT'. ";" (String) for ';'.
#
# The final symbol (End of file) must be false.
def next_token
raise NotImplementedError, "#{self.class}\#next_token is not defined"
end
def _racc_do_parse_rb(arg, in_debug)
action_table, action_check, action_default, action_pointer,
_, _, _, _,
_, _, token_table, * = arg
_racc_init_sysvars
tok = act = i = nil
catch(:racc_end_parse) {
while true
if i = action_pointer[@racc_state[-1]]
if @racc_read_next
if @racc_t != 0 # not EOF
tok, @racc_val = next_token()
unless tok # EOF
@racc_t = 0
else
@racc_t = (token_table[tok] or 1) # error token
end
racc_read_token(@racc_t, tok, @racc_val) if @yydebug
@racc_read_next = false
end
end
i += @racc_t
unless i >= 0 and
act = action_table[i] and
action_check[i] == @racc_state[-1]
act = action_default[@racc_state[-1]]
end
else
act = action_default[@racc_state[-1]]
end
while act = _racc_evalact(act, arg)
;
end
end
}
end
# Another entry point for the parser.
# If you use this method, you must implement RECEIVER#METHOD_ID method.
#
# RECEIVER#METHOD_ID is a method to get next token.
# It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE].
def yyparse(recv, mid)
__send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), false)
end
def _racc_yyparse_rb(recv, mid, arg, c_debug)
action_table, action_check, action_default, action_pointer,
_, _, _, _,
_, _, token_table, * = arg
_racc_init_sysvars
catch(:racc_end_parse) {
until i = action_pointer[@racc_state[-1]]
while act = _racc_evalact(action_default[@racc_state[-1]], arg)
;
end
end
recv.__send__(mid) do |tok, val|
unless tok
@racc_t = 0
else
@racc_t = (token_table[tok] or 1) # error token
end
@racc_val = val
@racc_read_next = false
i += @racc_t
unless i >= 0 and
act = action_table[i] and
action_check[i] == @racc_state[-1]
act = action_default[@racc_state[-1]]
end
while act = _racc_evalact(act, arg)
;
end
while !(i = action_pointer[@racc_state[-1]]) ||
! @racc_read_next ||
@racc_t == 0 # $
unless i and i += @racc_t and
i >= 0 and
act = action_table[i] and
action_check[i] == @racc_state[-1]
act = action_default[@racc_state[-1]]
end
while act = _racc_evalact(act, arg)
;
end
end
end
}
end
###
### common
###
def _racc_evalact(act, arg)
action_table, action_check, _, action_pointer,
_, _, _, _,
_, _, _, shift_n,
reduce_n, * = arg
nerr = 0 # tmp
if act > 0 and act < shift_n
#
# shift
#
if @racc_error_status > 0
@racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF
end
@racc_vstack.push @racc_val
@racc_state.push act
@racc_read_next = true
if @yydebug
@racc_tstack.push @racc_t
racc_shift @racc_t, @racc_tstack, @racc_vstack
end
elsif act < 0 and act > -reduce_n
#
# reduce
#
code = catch(:racc_jump) {
@racc_state.push _racc_do_reduce(arg, act)
false
}
if code
case code
when 1 # yyerror
@racc_user_yyerror = true # user_yyerror
return -reduce_n
when 2 # yyaccept
return shift_n
else
raise '[Racc Bug] unknown jump code'
end
end
elsif act == shift_n
#
# accept
#
racc_accept if @yydebug
throw :racc_end_parse, @racc_vstack[0]
elsif act == -reduce_n
#
# error
#
case @racc_error_status
when 0
unless arg[21] # user_yyerror
nerr += 1
on_error @racc_t, @racc_val, @racc_vstack
end
when 3
if @racc_t == 0 # is $
# We're at EOF, and another error occurred immediately after
# attempting auto-recovery
throw :racc_end_parse, nil
end
@racc_read_next = true
end
@racc_user_yyerror = false
@racc_error_status = 3
while true
if i = action_pointer[@racc_state[-1]]
i += 1 # error token
if i >= 0 and
(act = action_table[i]) and
action_check[i] == @racc_state[-1]
break
end
end
throw :racc_end_parse, nil if @racc_state.size <= 1
@racc_state.pop
@racc_vstack.pop
if @yydebug
@racc_tstack.pop
racc_e_pop @racc_state, @racc_tstack, @racc_vstack
end
end
return act
else
raise "[Racc Bug] unknown action #{act.inspect}"
end
racc_next_state(@racc_state[-1], @racc_state) if @yydebug
nil
end
def _racc_do_reduce(arg, act)
_, _, _, _,
goto_table, goto_check, goto_default, goto_pointer,
nt_base, reduce_table, _, _,
_, use_result, * = arg
state = @racc_state
vstack = @racc_vstack
tstack = @racc_tstack
i = act * -3
len = reduce_table[i]
reduce_to = reduce_table[i+1]
method_id = reduce_table[i+2]
void_array = []
tmp_t = tstack[-len, len] if @yydebug
tmp_v = vstack[-len, len]
tstack[-len, len] = void_array if @yydebug
vstack[-len, len] = void_array
state[-len, len] = void_array
# tstack must be updated AFTER method call
if use_result
vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
else
vstack.push __send__(method_id, tmp_v, vstack)
end
tstack.push reduce_to
racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
k1 = reduce_to - nt_base
if i = goto_pointer[k1]
i += state[-1]
if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
return curstate
end
end
goto_default[k1]
end
# This method is called when a parse error is found.
#
# ERROR_TOKEN_ID is an internal ID of token which caused error.
# You can get string representation of this ID by calling
# #token_to_str.
#
# ERROR_VALUE is a value of error token.
#
# value_stack is a stack of symbol values.
# DO NOT MODIFY this object.
#
# This method raises ParseError by default.
#
# If this method returns, parsers enter "error recovering mode".
def on_error(t, val, vstack)
raise ParseError, sprintf("\nparse error on value %s (%s)",
val.inspect, token_to_str(t) || '?')
end
# Enter error recovering mode.
# This method does not call #on_error.
def yyerror
throw :racc_jump, 1
end
# Exit parser.
# Return value is Symbol_Value_Stack[0].
def yyaccept
throw :racc_jump, 2
end
# Leave error recovering mode.
def yyerrok
@racc_error_status = 0
end
# For debugging output
def racc_read_token(t, tok, val)
@racc_debug_out.print 'read '
@racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
@racc_debug_out.puts val.inspect
@racc_debug_out.puts
end
def racc_shift(tok, tstack, vstack)
@racc_debug_out.puts "shift #{racc_token2str tok}"
racc_print_stacks tstack, vstack
@racc_debug_out.puts
end
def racc_reduce(toks, sim, tstack, vstack)
out = @racc_debug_out
out.print 'reduce '
if toks.empty?
out.print ' <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

View file

@ -1,7 +1,5 @@
# frozen_string_literal: false
#--
# $originalId: parser.rb,v 1.8 2006/07/06 11:42:07 aamine Exp $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
@ -12,6 +10,12 @@
# without restriction.
#++
require 'racc/info'
unless defined?(NotImplementedError)
NotImplementedError = NotImplementError # :nodoc:
end
module Racc
class ParseError < StandardError; end
end
@ -49,12 +53,12 @@ end
# [-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,
# add debug code to parser class. To display debuggin information,
# use this '-g' option and set @yydebug true in parser class.
# [-E, --embedded]
# Output parser which doesn't need runtime files (racc/parser.rb).
# [-C, --check-only]
# Check syntax of racc grammar file and quit.
# Check syntax of racc grammer file and quit.
# [-S, --output-status]
# Print messages time to time while compiling.
# [-l, --no-line-convert]
@ -171,29 +175,34 @@ end
# 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 LGPL, but your parser is not.
# 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:
unless defined?(Racc_No_Extentions)
Racc_No_Extentions = false # :nodoc:
end
class Parser
Racc_Runtime_Version = '1.4.6'
Racc_Runtime_Revision = %w$originalRevision: 1.8 $[1]
Racc_Runtime_Version = ::Racc::VERSION
Racc_Runtime_Revision = '$Id: 87af5c09d4467cae567837b4162ec2145417a90e $'
Racc_Runtime_Core_Version_R = '1.4.6'
Racc_Runtime_Core_Revision_R = %w$originalRevision: 1.8 $[1]
Racc_Runtime_Core_Version_R = ::Racc::VERSION
Racc_Runtime_Core_Revision_R = '$Id: 87af5c09d4467cae567837b4162ec2145417a90e $'.split[1]
begin
require 'racc/cparse'
# Racc_Runtime_Core_Version_C = (defined in extension)
if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby'
require 'racc/cparse-jruby.jar'
com.headius.racc.Cparse.new.load(JRuby.runtime, false)
else
require 'racc/cparse'
end
# Racc_Runtime_Core_Version_C = (defined in extention)
Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2]
unless new.respond_to?(:_racc_do_parse_c, true)
raise LoadError, 'old cparse.so'
end
if Racc_No_Extensions
if Racc_No_Extentions
raise LoadError, 'selecting ruby version of racc runtime core'
end
@ -203,6 +212,8 @@ module Racc
Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C # :nodoc:
Racc_Runtime_Type = 'c' # :nodoc:
rescue LoadError
puts $!
puts $!.backtrace
Racc_Main_Parsing_Routine = :_racc_do_parse_rb
Racc_YY_Parse_Method = :_racc_yyparse_rb
Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
@ -255,9 +266,11 @@ module Racc
# def next_token
# @q.shift
# end
class_eval %{
def do_parse
__send__(Racc_Main_Parsing_Routine, _racc_setup(), false)
#{Racc_Main_Parsing_Routine}(_racc_setup(), false)
end
}
# The method to fetch next token.
# If you use #do_parse method, you must implement #next_token.
@ -274,8 +287,7 @@ module Racc
def _racc_do_parse_rb(arg, in_debug)
action_table, action_check, action_default, action_pointer,
_, _, _, _,
_, _, token_table, _,
_, _, * = arg
_, _, token_table, * = arg
_racc_init_sysvars
tok = act = i = nil
@ -316,19 +328,18 @@ module Racc
#
# RECEIVER#METHOD_ID is a method to get next token.
# It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE].
class_eval %{
def yyparse(recv, mid)
__send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), true)
#{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), true)
end
}
def _racc_yyparse_rb(recv, mid, arg, c_debug)
action_table, action_check, action_default, action_pointer,
_, _, _, _,
_, _, token_table, _,
_, _, * = arg
_, _, _, _,
_, _, token_table, * = arg
_racc_init_sysvars
act = nil
i = nil
catch(:racc_end_parse) {
until i = action_pointer[@racc_state[-1]]
@ -355,9 +366,9 @@ module Racc
;
end
while not(i = action_pointer[@racc_state[-1]]) or
not @racc_read_next or
@racc_t == 0 # $
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
@ -378,16 +389,17 @@ module Racc
def _racc_evalact(act, arg)
action_table, action_check, _, action_pointer,
_, _, _, _,
_, _, _, shift_n, reduce_n,
_, _, * = arg
_, _, _, _,
_, _, _, 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
@racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF
end
@racc_vstack.push @racc_val
@racc_state.push act
@ -431,10 +443,13 @@ module Racc
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
@ -470,10 +485,11 @@ module Racc
end
def _racc_do_reduce(arg, act)
_, _, _, _,
goto_table, goto_check, goto_default, goto_pointer,
nt_base, reduce_table, _, _,
_, use_result, * = arg
_, _, _, _,
goto_table, goto_check, goto_default, goto_pointer,
nt_base, reduce_table, _, _,
_, use_result, * = arg
state = @racc_state
vstack = @racc_vstack
tstack = @racc_tstack
@ -569,7 +585,6 @@ module Racc
toks.each {|t| out.print ' ', racc_token2str(t) }
end
out.puts " --> #{racc_token2str(sim)}"
racc_print_stacks tstack, vstack
@racc_debug_out.puts
end

View file

@ -0,0 +1,510 @@
#
# $Id: 19fb5debfd07d70f6bc2ddc79ef43fbb3d27f15e $
#
# 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 'enumerator'
require 'racc/compat'
require 'racc/sourcetext'
require 'racc/parser-text'
require 'rbconfig'
module Racc
class ParserFileGenerator
class Params
def self.bool_attr(name)
module_eval(<<-End)
def #{name}?
@#{name}
end
def #{name}=(b)
@#{name} = b
end
End
end
attr_accessor :filename
attr_accessor :classname
attr_accessor :superclass
bool_attr :omit_action_call
bool_attr :result_var
attr_accessor :header
attr_accessor :inner
attr_accessor :footer
bool_attr :debug_parser
bool_attr :convert_line
bool_attr :convert_line_all
bool_attr :embed_runtime
bool_attr :make_executable
attr_accessor :interpreter
def initialize
# Parameters derived from parser
self.filename = nil
self.classname = nil
self.superclass = 'Racc::Parser'
self.omit_action_call = true
self.result_var = true
self.header = []
self.inner = []
self.footer = []
# Parameters derived from command line options
self.debug_parser = false
self.convert_line = true
self.convert_line_all = false
self.embed_runtime = false
self.make_executable = false
self.interpreter = nil
end
end
def initialize(states, params)
@states = states
@grammar = states.grammar
@params = params
end
def generate_parser
string_io = StringIO.new
init_line_conversion_system
@f = string_io
parser_file
string_io.rewind
string_io.read
end
def generate_parser_file(destpath)
init_line_conversion_system
File.open(destpath, 'w') {|f|
@f = f
parser_file
}
File.chmod 0755, destpath if @params.make_executable?
end
private
def parser_file
shebang @params.interpreter if @params.make_executable?
notice
line
if @params.embed_runtime?
embed_library runtime_source()
else
require 'racc/parser.rb'
end
header
parser_class(@params.classname, @params.superclass) {
inner
state_transition_table
}
footer
end
c = ::RbConfig::CONFIG
RUBY_PATH = "#{c['bindir']}/#{c['ruby_install_name']}#{c['EXEEXT']}"
def shebang(path)
line '#!' + (path == 'ruby' ? RUBY_PATH : path)
end
def notice
line %q[#]
line %q[# DO NOT MODIFY!!!!]
line %Q[# This file is automatically generated by Racc #{Racc::Version}]
line %Q[# from Racc grammer file "#{@params.filename}".]
line %q[#]
end
def runtime_source
SourceText.new(::Racc::PARSER_TEXT, 'racc/parser.rb', 1)
end
def embed_library(src)
line %[###### #{src.filename} begin]
line %[unless $".index '#{src.filename}']
line %[$".push '#{src.filename}']
put src, @params.convert_line?
line %[end]
line %[###### #{src.filename} end]
end
def require(feature)
line "require '#{feature}'"
end
def parser_class(classname, superclass)
mods = classname.split('::')
classid = mods.pop
mods.each do |mod|
indent; line "module #{mod}"
cref_push mod
end
indent; line "class #{classid} < #{superclass}"
cref_push classid
yield
cref_pop
indent; line "end \# class #{classid}"
mods.reverse_each do |mod|
indent; line "end \# module #{mod}"
cref_pop
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 = "...end #{id}/module_eval..."
while @used_separator.key?(sep)
sep.concat sprintf('%02x', rand(255))
end
@used_separator[sep] = true
sep
end
#
# State Transition Table Serialization
#
public
def put_state_transition_table(f)
@f = f
state_transition_table
end
private
def state_transition_table
table = @states.state_transition_table
table.use_result_var = @params.result_var?
table.debug_parser = @params.debug_parser?
line "##### State transition tables begin ###"
line
integer_list 'racc_action_table', table.action_table
line
integer_list 'racc_action_check', table.action_check
line
integer_list 'racc_action_pointer', table.action_pointer
line
integer_list 'racc_action_default', table.action_default
line
integer_list 'racc_goto_table', table.goto_table
line
integer_list 'racc_goto_check', table.goto_check
line
integer_list 'racc_goto_pointer', table.goto_pointer
line
integer_list 'racc_goto_default', table.goto_default
line
i_i_sym_list 'racc_reduce_table', table.reduce_table
line
line "racc_reduce_n = #{table.reduce_n}"
line
line "racc_shift_n = #{table.shift_n}"
line
sym_int_hash 'racc_token_table', table.token_table
line
line "racc_nt_base = #{table.nt_base}"
line
line "racc_use_result_var = #{table.use_result_var}"
line
@f.print(unindent_auto(<<-End))
Racc_arg = [
racc_action_table,
racc_action_check,
racc_action_default,
racc_action_pointer,
racc_goto_table,
racc_goto_check,
racc_goto_default,
racc_goto_pointer,
racc_nt_base,
racc_reduce_table,
racc_token_table,
racc_shift_n,
racc_reduce_n,
racc_use_result_var ]
End
line
string_list 'Racc_token_to_s_table', table.token_to_s_table
line
line "Racc_debug_parser = #{table.debug_parser}"
line
line '##### State transition tables end #####'
actions
end
def integer_list(name, table)
if table.size > 2000
serialize_integer_list_compressed name, table
else
serialize_integer_list_std name, table
end
end
def serialize_integer_list_compressed(name, table)
# TODO: this can be made a LOT more clean with a simple split/map
sep = "\n"
nsep = ",\n"
buf = ''
com = ''
ncom = ','
co = com
@f.print 'clist = ['
table.each do |i|
buf << co << i.to_s; co = ncom
if buf.size > 66
@f.print sep; sep = nsep
@f.print "'", buf, "'"
buf = ''
co = com
end
end
unless buf.empty?
@f.print sep
@f.print "'", buf, "'"
end
line ' ]'
@f.print(<<-End)
#{name} = arr = ::Array.new(#{table.size}, nil)
idx = 0
clist.each do |str|
str.split(',', -1).each do |i|
arr[idx] = i.to_i unless i.empty?
idx += 1
end
end
End
end
def serialize_integer_list_std(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

13
lib/racc/pre-setup Normal file
View file

@ -0,0 +1,13 @@
def generate_parser_text_rb(target)
return if File.exist?(srcfile(target))
$stderr.puts "generating #{target}..."
File.open(target, 'w') {|f|
f.puts "module Racc"
f.puts " PARSER_TEXT = <<'__end_of_file__'"
f.puts File.read(srcfile('parser.rb'))
f.puts "__end_of_file__"
f.puts "end"
}
end
generate_parser_text_rb 'parser-text.rb'

34
lib/racc/sourcetext.rb Normal file
View file

@ -0,0 +1,34 @@
#
# $Id: 3b2d89d9ada2f5fcb043837dcc5c9631856d5b70 $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of LGPL, 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

969
lib/racc/state.rb Normal file
View file

@ -0,0 +1,969 @@
#
# $Id: a101d6acb72abc392f7757cda89bf6f0a683a43d $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the same terms of ruby.
# see the file "COPYING".
require 'racc/iset'
require 'racc/statetransitiontable'
require 'racc/exception'
require 'forwardable'
module Racc
# A table of LALR states.
class States
include Enumerable
def initialize(grammar, debug_flags = DebugFlags.new)
@grammar = grammar
@symboltable = grammar.symboltable
@d_state = debug_flags.state
@d_la = debug_flags.la
@d_prec = debug_flags.prec
@states = []
@statecache = {}
@actions = ActionTable.new(@grammar, self)
@nfa_computed = false
@dfa_computed = false
end
attr_reader :grammar
attr_reader :actions
def size
@states.size
end
def inspect
'#<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 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

View file

@ -0,0 +1,316 @@
#
# $Id: 4c5f4311663b6d03050953d64d6a0e7905ff2216 $
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of LGPL, see the file "COPYING".
#
require 'racc/parser'
unless Object.method_defined?(:funcall)
class Object
alias funcall __send__
end
end
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 = ''
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, 'n')
end
def set_table(entries, dummy, tbl, chk, ptr)
upper = 0
map = '-' * 10240
# sort long to short
entries.sort! {|a,b| b[0].size <=> a[0].size }
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.funcall(:alias_method, "_reduce_#{rule.ident}", :_reduce_none)
else
c.funcall(: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

5
lib/racc/static.rb Normal file
View file

@ -0,0 +1,5 @@
require 'racc'
require 'racc/parser'
require 'racc/grammarfileparser'
require 'racc/parserfilegenerator'
require 'racc/logfilegenerator'

306
libexec/racc Executable file
View file

@ -0,0 +1,306 @@
#!/usr/bin/env ruby
#
# $Id$
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the same terms of ruby.
# see the file "COPYING".
require 'racc/static'
require 'optparse'
def main
output = nil
debug_parser = false
make_logfile = false
logfilename = nil
make_executable = false
rubypath = nil
embed_runtime = false
debug_flags = Racc::DebugFlags.new
line_convert = true
line_convert_all = false
omit_action_call = true
superclass = nil
check_only = false
verbose = false
profiler = RaccProfiler.new(false)
parser = OptionParser.new
parser.banner = "Usage: #{File.basename($0)} [options] <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|
executable = true
rubypath = (path == 'ruby' ? nil : path)
}
parser.on('-E', '--embedded', "Embeds Racc runtime in output.") {
embed_runtime = true
}
parser.on('--line-convert-all', 'Converts line numbers of user codes.') {
line_convert_all = true
}
parser.on('-l', '--no-line-convert', 'Never convert line numbers.') {
line_convert = false
line_convert_all = false
}
parser.on('-a', '--no-omit-actions', 'Never omit actions.') {
omit_action_call = false
}
parser.on('--superclass=CLASSNAME',
'Uses CLASSNAME instead of Racc::Parser.') {|name|
superclass = name
}
parser.on('--runtime=FEATURE',
"Uses FEATURE instead of 'racc/parser'") {|feat|
runtime = feature
}
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_Extentions, true
#}
parser.on('--version', 'Prints version and quit.') {
puts "racc version #{Racc::Version}"
exit 0
}
parser.on('--runtime-version', 'Prints runtime version and quit.') {
printf "racc runtime version %s (rev. %s); %s\n",
Racc::Parser::Racc_Runtime_Version,
Racc::Parser::Racc_Runtime_Revision,
if Racc::Parser.racc_runtime_type == 'ruby'
sprintf('ruby core version %s (rev. %s)',
Racc::Parser::Racc_Runtime_Core_Version_R,
Racc::Parser::Racc_Runtime_Core_Revision_R)
else
sprintf('c core version %s (rev. %s)',
Racc::Parser::Racc_Runtime_Core_Version_C,
Racc::Parser::Racc_Runtime_Core_Revision_C)
end
exit 0
}
parser.on('--copyright', 'Prints copyright and quit.') {
puts Racc::Copyright
exit 0
}
parser.on('--help', 'Prints this message and quit.') {
puts parser.help
exit 1
}
begin
parser.parse!
rescue OptionParser::ParseError => err
$stderr.puts err.message
$stderr.puts parser.help
exit 1
end
if ARGV.empty?
$stderr.puts 'no input'
exit 1
end
if ARGV.size > 1
$stderr.puts 'too many input'
exit 1
end
input = ARGV[0]
begin
$stderr.puts 'Parsing grammar file...' if verbose
result = profiler.section('parse') {
parser = Racc::GrammarFileParser.new(debug_flags)
parser.parse(File.read(input), File.basename(input))
}
if check_only
$stderr.puts 'syntax ok'
exit 0
end
$stderr.puts 'Generating LALR states...' if verbose
states = profiler.section('nfa') {
Racc::States.new(result.grammar).nfa
}
$stderr.puts "Resolving #{states.size} states..." if verbose
profiler.section('dfa') {
states.dfa
}
$stderr.puts 'Creating parser file...' if verbose
params = result.params.dup
# Overwrites parameters given by a grammar file with command line options.
params.superclass = superclass if superclass
params.omit_action_call = true if omit_action_call
# From command line option
if make_executable
params.make_executable = true
params.interpreter = rubypath
end
params.debug_parser = debug_parser
params.convert_line = line_convert
params.convert_line_all = line_convert_all
params.embed_runtime = embed_runtime
profiler.section('generation') {
generator = Racc::ParserFileGenerator.new(states, params)
generator.generate_parser_file(output || make_filename(input, '.tab.rb'))
}
if make_logfile
profiler.section('logging') {
$stderr.puts 'Creating log file...' if verbose
logfilename ||= make_filename(output || File.basename(input), '.output')
File.open(logfilename, 'w') {|f|
Racc::LogFileGenerator.new(states, debug_flags).output f
}
}
end
if debug_flags.status_logging
log_useless states.grammar
log_conflict states
else
report_useless states.grammar
report_conflict states
end
profiler.report
rescue Racc::Error, Errno::ENOENT, Errno::EPERM => err
raise if $DEBUG or debug_flags.any?
lineno = err.message.slice(/\A\d+:/).to_s
$stderr.puts "#{File.basename $0}: #{input}:#{lineno} #{err.message.strip}"
exit 1
end
end
def make_filename(path, suffix)
path.sub(/(?:\..*?)?\z/, suffix)
end
def report_conflict(states)
if states.should_report_srconflict?
$stderr.puts "#{states.n_srconflicts} shift/reduce conflicts"
end
if states.rrconflict_exist?
$stderr.puts "#{states.n_rrconflicts} reduce/reduce conflicts"
end
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
def report_useless(grammar)
if grammar.useless_nonterminal_exist?
$stderr.puts "#{grammar.n_useless_nonterminals} useless nonterminals"
end
if grammar.useless_rule_exist?
$stderr.puts "#{grammar.n_useless_rules} useless rules"
end
if grammar.start.useless?
$stderr.puts 'fatal: start symbol does not derive any sentence'
end
end
def log_useless(grammar)
logging('a') {|f|
if grammar.useless_nonterminal_exist?
f.puts "un#{grammar.n_useless_nonterminals}"
end
if grammar.useless_rule_exist?
f.puts "ur#{grammar.n_useless_rules}"
end
}
end
def logging(mode, &block)
File.open("log/#{File.basename(ARGV[0])}", mode, &block)
end
class RaccProfiler
def initialize(really)
@really = really
@log = []
unless ::Process.respond_to?(:times)
# Ruby 1.6
@class = ::Time
else
@class = ::Process
end
end
def section(name)
if @really
t1 = @class.times.utime
result = yield
t2 = @class.times.utime
@log.push [name, t2 - t1]
result
else
yield
end
end
def report
return unless @really
f = $stderr
total = cumulative_time()
f.puts '--task-----------+--sec------+---%-'
@log.each do |name, time|
f.printf "%-19s %s %3d%%\n", name, pjust(time,4,4), (time/total*100).to_i
end
f.puts '-----------------+-----------+-----'
f.printf "%-20s%s\n", 'total', pjust(total,4,4)
end
private
def cumulative_time
t = @log.inject(0) {|sum, (name, time)| sum + time }
t == 0 ? 0.01 : t
end
def pjust(num, i, j)
m = /(\d+)(\.\d+)?/.match(num.to_s)
str = m[1].rjust(i)
str.concat m[2].ljust(j+1)[0,j+1] if m[2]
str
end
end
main

195
libexec/racc2y Executable file
View file

@ -0,0 +1,195 @@
#!/usr/local/bin/ruby
#
# $Id$
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is feee software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
# For details of the LGPL, see the file "COPYING".
#
require 'racc/grammarfileparser'
require 'racc/info'
require 'optparse'
def main
@with_action = true
with_header = false
with_inner = false
with_footer = false
output = nil
parser = OptionParser.new
parser.banner = "Usage: #{File.basename($0)} [-AHIF] [-oFILENAME] GRAMMARFILE"
parser.on('-o', '--output=FILENAME', 'output file name [<input>.yacc]') {|name|
output = name
}
parser.on('-A', '--without-action', 'Does not include actions.') {
@with_action = false
}
parser.on('-H', '--with-header', 'Includes header part.') {
with_header = true
}
parser.on('-I', '--with-inner', 'Includes inner part.') {
with_inner = true
}
parser.on('-F', '--with-footer', 'Includes footer part.') {
with_footer = true
}
parser.on('--version', 'Prints version and quit.') {
puts "racc2y version #{Racc::Version}"
exit 0
}
parser.on('--copyright', 'Prints copyright and quit.') {
puts Racc::Copyright
exit 0
}
parser.on('--help', 'Prints this message and quit.') {
puts parser.help
exit 1
}
begin
parser.parse!
rescue OptionParser::ParseError => err
$stderr.puts err.message
$stderr.puts parser.help
exit 1
end
if ARGV.empty?
$stderr.puts "no input file"
exit 1
end
unless ARGV.size == 1
$stderr.puts "too many inputs"
exit 1
end
input = ARGV[0]
begin
result = Racc::GrammarFileParser.parse_file(input)
result.grammar.init
File.open(output || "#{input}.yacc", 'w') {|f|
f.puts "/* generated from #{input} */"
if with_header
f.puts
f.puts '%{'
print_user_codes f, result.params.header
f.puts '%}'
end
f.puts
print_terminals f, result.grammar
f.puts
print_precedence_table f, precedence_table(result.grammar)
f.puts
f.puts '%%'
print_grammar f, result.grammar
f.puts '%%'
if with_inner
f.puts '/*---- inner ----*/'
print_user_codes f, result.params.inner
end
if with_footer
f.puts '/*---- footer ----*/'
print_user_codes f, result.params.footer
end
}
rescue SystemCallError => err
$stderr.puts err.message
exit 1
end
end
def print_terminals(f, grammar)
init_indent = '%token'.size
f.print '%token'
columns = init_indent
grammar.symboltable.each_terminal do |t|
next unless t.terminal?
next if t.dummy?
next if t == grammar.symboltable.anchor
next if t == grammar.symboltable.error
unless t.value.kind_of?(String)
if columns > 60
f.puts
f.print ' ' * init_indent
columns = init_indent
end
columns += f.write(" #{yacc_symbol(t)}")
end
end
f.puts
end
def precedence_table(grammar)
table = []
grammar.symboltable.select {|sym| sym.precedence }.each do |sym|
(table[sym.prec] ||= [sym.assoc]).push sym
end
table.compact
end
def print_precedence_table(f, table)
return if table.empty?
f.puts '/* precedance table */'
table.each do |syms|
assoc = syms.shift
f.printf '%%%-8s ', assoc.to_s.downcase
f.puts syms.map {|s| yacc_symbol(s) }.join(' ')
end
f.puts
end
def print_grammar(f, grammar)
prev_target = nil
indent = 10
embactions = []
grammar.each do |rule|
if rule.target.dummy?
embactions.push rule.action unless rule.action.empty?
next
end
if rule.target == prev_target
f.print ' ' * indent, '|'
else
prev_target = rule.target
f.printf "\n%-10s:", yacc_symbol(prev_target)
end
rule.symbols.each do |s|
if s.dummy? # target of dummy rule for embedded action
f.puts
print_action f, embactions.shift, indent
f.print ' ' * (indent + 1)
else
f.print ' ', yacc_symbol(s)
end
end
if rule.specified_prec
f.print ' %prec ', yacc_symbol(rule.specified_prec)
end
f.puts
unless rule.action.empty?
print_action f, rule.action, indent
end
end
end
def print_action(f, action, indent)
return unless @with_action
f.print ' ' * (indent + 4), "{\n"
f.print ' ' * (indent + 6), action.source.text.strip, "\n"
f.print ' ' * (indent + 4) , "}\n"
end
def print_user_codes(f, srcs)
return if srcs.empty?
srcs.each do |src|
f.puts src.text
end
end
def yacc_symbol(s)
s.to_s.gsub('"', "'")
end
main

339
libexec/y2racc Executable file
View file

@ -0,0 +1,339 @@
#!/usr/local/bin/ruby
#
# $Id$
#
# Copyright (c) 1999-2006 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public Lisence version 2.1.
# For details of the GNU LGPL, see the file "COPYING".
#
require 'racc/info'
require 'strscan'
require 'forwardable'
require 'optparse'
def main
@with_action = true
@with_header = false
@with_usercode = false
cname = 'MyParser'
input = nil
output = nil
parser = OptionParser.new
parser.banner = "Usage: #{File.basename($0)} [-Ahu] [-c <classname>] [-o <filename>] <input>"
parser.on('-o', '--output=FILENAME', 'output file name [<input>.racc]') {|name|
output = name
}
parser.on('-c', '--classname=NAME', "Name of the parser class. [#{cname}]") {|name|
cname = name
}
parser.on('-A', '--without-action', 'Does not include actions.') {
@with_action = false
}
parser.on('-h', '--with-header', 'Includes header (%{...%}).') {
@with_header = true
}
parser.on('-u', '--with-user-code', 'Includes user code.') {
@with_usercode = true
}
parser.on('--version', 'Prints version and quit.') {
puts "y2racc version #{Racc::Version}"
exit 0
}
parser.on('--copyright', 'Prints copyright and quit.') {
puts Racc::Copyright
exit 0
}
parser.on('--help', 'Prints this message and quit.') {
puts parser.help
exit 1
}
begin
parser.parse!
rescue OptionParser::ParseError => err
$stderr.puts err.message
$stderr.puts parser.help
exit 1
end
if ARGV.empty?
$stderr.puts 'no input'
exit 1
end
if ARGV.size > 1
$stderr.puts 'too many input'
exit 1
end
input = ARGV[0]
begin
result = YaccFileParser.parse_file(input)
File.open(output || "#{input}.racc", 'w') {|f|
convert cname, result, f
}
rescue SystemCallError => err
$stderr.puts err.message
exit 1
end
end
def convert(classname, result, f)
init_indent = 'token'.size
f.puts %<# Converted from "#{result.filename}" by y2racc version #{Racc::Version}>
f.puts
f.puts "class #{classname}"
unless result.terminals.empty?
f.puts
f.print 'token'
columns = init_indent
result.terminals.each do |t|
if columns > 60
f.puts
f.print ' ' * init_indent
columns = init_indent
end
columns += f.write(" #{t}")
end
f.puts
end
unless result.precedence_table.empty?
f.puts
f.puts 'preclow'
result.precedence_table.each do |assoc, toks|
f.printf " %-8s %s\n", assoc, toks.join(' ') unless toks.empty?
end
f.puts 'prechigh'
end
if result.start
f.puts
f.puts "start #{@start}"
end
f.puts
f.puts 'rule'
texts = @with_action ? result.grammar : result.grammar_without_actions
texts.each do |text|
f.print text
end
if @with_header and result.header
f.puts
f.puts '---- header'
f.puts result.header
end
if @with_usercode and result.usercode
f.puts
f.puts '---- footer'
f.puts result.usercode
end
end
class ParseError < StandardError; end
class StringScanner_withlineno
def initialize(src)
@s = StringScanner.new(src)
@lineno = 1
end
extend Forwardable
def_delegator "@s", :eos?
def_delegator "@s", :rest
attr_reader :lineno
def scan(re)
advance_lineno(@s.scan(re))
end
def scan_until(re)
advance_lineno(@s.scan_until(re))
end
def skip(re)
str = advance_lineno(@s.scan(re))
str ? str.size : nil
end
def getch
advance_lineno(@s.getch)
end
private
def advance_lineno(str)
@lineno += str.count("\n") if str
str
end
end
class YaccFileParser
Result = Struct.new(:terminals, :precedence_table, :start,
:header, :grammar, :usercode, :filename)
class Result # reopen
def initialize
super
self.terminals = []
self.precedence_table = []
self.start = nil
self.grammar = []
self.header = nil
self.usercode = nil
self.filename = nil
end
def grammar_without_actions
grammar().map {|text| text[0,1] == '{' ? '{}' : text }
end
end
def YaccFileParser.parse_file(filename)
new().parse(File.read(filename), filename)
end
def parse(src, filename = '-')
@result = Result.new
@filename = filename
@result.filename = filename
s = StringScanner_withlineno.new(src)
parse_header s
parse_grammar s
@result
end
private
COMMENT = %r</\*[^*]*\*+(?:[^/*][^*]*\*+)*/>
CHAR = /'((?:[^'\\]+|\\.)*)'/
STRING = /"((?:[^"\\]+|\\.)*)"/
def parse_header(s)
skip_until_percent s
until s.eos?
case
when t = s.scan(/left/)
@result.precedence_table.push ['left', scan_symbols(s)]
when t = s.scan(/right/)
@result.precedence_table.push ['right', scan_symbols(s)]
when t = s.scan(/nonassoc/)
@result.precedence_table.push ['nonassoc', scan_symbols(s)]
when t = s.scan(/token/)
list = scan_symbols(s)
list.shift if /\A<(.*)>\z/ =~ list[0]
@result.terminals.concat list
when t = s.scan(/start/)
@result.start = scan_symbols(s)[0]
when s.skip(%r<(?:
type | union | expect | thong | binary |
semantic_parser | pure_parser | no_lines |
raw | token_table
)\b>x)
skip_until_percent s
when s.skip(/\{/) # header (%{...%})
str = s.scan_until(/\%\}/)
str.chop!
str.chop!
@result.header = str
skip_until_percent s
when s.skip(/\%/) # grammar (%%...)
return
else
raise ParseError, "#{@filename}:#{s.lineno}: scan error"
end
end
end
def skip_until_percent(s)
until s.eos?
s.skip /[^\%\/]+/
next if s.skip(COMMENT)
return if s.getch == '%'
end
end
def scan_symbols(s)
list = []
until s.eos?
s.skip /\s+/
if s.skip(COMMENT)
;
elsif t = s.scan(CHAR)
list.push t
elsif t = s.scan(STRING)
list.push t
elsif s.skip(/\%/)
break
elsif t = s.scan(/\S+/)
list.push t
else
raise ParseError, "#{@filename}:#{@lineno}: scan error"
end
end
list
end
def parse_grammar(s)
buf = []
until s.eos?
if t = s.scan(/[^%'"{\/]+/)
buf.push t
break if s.eos?
end
if s.skip(/\{/)
buf.push scan_action(s)
elsif t = s.scan(/'(?:[^'\\]+|\\.)*'/) then buf.push t
elsif t = s.scan(/"(?:[^"\\]+|\\.)*"/) then buf.push t
elsif t = s.scan(COMMENT) then buf.push t
elsif s.skip(/%prec\b/) then buf.push '='
elsif s.skip(/%%/)
@result.usercode = s.rest
break
else
buf.push s.getch
end
end
@result.grammar = buf
end
def scan_action(s)
buf = '{'
nest = 1
until s.eos?
if t = s.scan(%r<[^/{}'"]+>)
buf << t
break if s.eos?
elsif t = s.scan(COMMENT)
buf << t
elsif t = s.scan(CHAR)
buf << t
elsif t = s.scan(STRING)
buf << t
else
c = s.getch
buf << c
case c
when '{'
nest += 1
when '}'
nest -= 1
return buf if nest == 0
end
end
end
$stderr.puts "warning: unterminated action in #{@filename}"
buf
end
end
unless Object.method_defined?(:funcall)
class Object
alias funcall __send__
end
end
main

170
test/racc/assets/cadenza.y Normal file
View file

@ -0,0 +1,170 @@
# This grammar is released under an MIT license
# Author: William Howard (http://github.com/whoward)
# Source: https://github.com/whoward/cadenza/blob/master/src/cadenza.y
class Cadenza::RaccParser
/* expect this many shift/reduce conflicts */
expect 37
rule
target
: document
| /* none */ { result = nil }
;
parameter_list
: logical_expression { result = [val[0]] }
| parameter_list ',' logical_expression { result = val[0].push(val[2]) }
;
/* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
primary_expression
: IDENTIFIER { result = VariableNode.new(val[0].value) }
| IDENTIFIER parameter_list { result = VariableNode.new(val[0].value, val[1]) }
| INTEGER { result = ConstantNode.new(val[0].value) }
| REAL { result = ConstantNode.new(val[0].value) }
| STRING { result = ConstantNode.new(val[0].value) }
| '(' filtered_expression ')' { result = val[1] }
;
multiplicative_expression
: primary_expression
| multiplicative_expression '*' primary_expression { result = OperationNode.new(val[0], "*", val[2]) }
| multiplicative_expression '/' primary_expression { result = OperationNode.new(val[0], "/", val[2]) }
;
additive_expression
: multiplicative_expression
| additive_expression '+' multiplicative_expression { result = OperationNode.new(val[0], "+", val[2]) }
| additive_expression '-' multiplicative_expression { result = OperationNode.new(val[0], "-", val[2]) }
;
boolean_expression
: additive_expression
| boolean_expression OP_EQ additive_expression { result = OperationNode.new(val[0], "==", val[2]) }
| boolean_expression OP_NEQ additive_expression { result = OperationNode.new(val[0], "!=", val[2]) }
| boolean_expression OP_LEQ additive_expression { result = OperationNode.new(val[0], "<=", val[2]) }
| boolean_expression OP_GEQ additive_expression { result = OperationNode.new(val[0], ">=", val[2]) }
| boolean_expression '>' additive_expression { result = OperationNode.new(val[0], ">", val[2]) }
| boolean_expression '<' additive_expression { result = OperationNode.new(val[0], "<", val[2]) }
;
inverse_expression
: boolean_expression
| NOT boolean_expression { result = BooleanInverseNode.new(val[1]) }
;
logical_expression
: inverse_expression
| logical_expression AND inverse_expression { result = OperationNode.new(val[0], "and", val[2]) }
| logical_expression OR inverse_expression { result = OperationNode.new(val[0], "or", val[2]) }
;
filter
: IDENTIFIER { result = FilterNode.new(val[0].value) }
| IDENTIFIER ':' parameter_list { result = FilterNode.new(val[0].value, val[2]) }
;
filter_list
: filter { result = [val[0]] }
| filter_list '|' filter { result = val[0].push(val[2]) }
;
filtered_expression
: logical_expression
| logical_expression '|' filter_list { result = FilteredValueNode.new(val[0], val[2]) }
;
inject_statement
: VAR_OPEN filtered_expression VAR_CLOSE { result = val[1] }
;
if_tag
: STMT_OPEN IF logical_expression STMT_CLOSE { open_scope!; result = val[2] }
| STMT_OPEN UNLESS logical_expression STMT_CLOSE { open_scope!; result = BooleanInverseNode.new(val[2]) }
;
else_tag
: STMT_OPEN ELSE STMT_CLOSE { result = close_scope!; open_scope! }
;
end_if_tag
: STMT_OPEN ENDIF STMT_CLOSE { result = close_scope! }
| STMT_OPEN ENDUNLESS STMT_CLOSE { result = close_scope! }
;
if_block
: if_tag end_if_tag { result = IfNode.new(val[0], val[1]) }
| if_tag document end_if_tag { result = IfNode.new(val[0], val[2]) }
| if_tag else_tag document end_if_tag { result = IfNode.new(val[0], val[1], val[3]) }
| if_tag document else_tag end_if_tag { result = IfNode.new(val[0], val[2], val[3]) }
| if_tag document else_tag document end_if_tag { result = IfNode.new(val[0], val[2], val[4]) }
;
for_tag
: STMT_OPEN FOR IDENTIFIER IN filtered_expression STMT_CLOSE { open_scope!; result = [val[2].value, val[4]] }
;
end_for_tag
: STMT_OPEN ENDFOR STMT_CLOSE { result = close_scope! }
;
/* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
for_block
: for_tag end_for_tag { result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[1]) }
| for_tag document end_for_tag { result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[2]) }
;
block_tag
: STMT_OPEN BLOCK IDENTIFIER STMT_CLOSE { result = open_block_scope!(val[2].value) }
;
end_block_tag
: STMT_OPEN ENDBLOCK STMT_CLOSE { result = close_block_scope! }
;
/* this has a shift/reduce conflict but since Racc will shift in this case it is the correct behavior */
block_block
: block_tag end_block_tag { result = BlockNode.new(val[0], val[1]) }
| block_tag document end_block_tag { result = BlockNode.new(val[0], val[2]) }
;
generic_block_tag
: STMT_OPEN IDENTIFIER STMT_CLOSE { open_scope!; result = [val[1].value, []] }
| STMT_OPEN IDENTIFIER parameter_list STMT_CLOSE { open_scope!; result = [val[1].value, val[2]] }
;
end_generic_block_tag
: STMT_OPEN END STMT_CLOSE { result = close_scope! }
;
generic_block
: generic_block_tag document end_generic_block_tag { result = GenericBlockNode.new(val[0].first, val[2], val[0].last) }
;
extends_statement
: STMT_OPEN EXTENDS STRING STMT_CLOSE { result = val[2].value }
| STMT_OPEN EXTENDS IDENTIFIER STMT_CLOSE { result = VariableNode.new(val[2].value) }
;
document_component
: TEXT_BLOCK { result = TextNode.new(val[0].value) }
| inject_statement
| if_block
| for_block
| generic_block
| block_block
;
document
: document_component { push val[0] }
| document document_component { push val[1] }
| extends_statement { document.extends = val[0] }
| document extends_statement { document.extends = val[1] }
;
---- header ----
# racc_parser.rb : generated by racc
---- inner ----

926
test/racc/assets/cast.y Normal file
View file

@ -0,0 +1,926 @@
# The MIT License
#
# Copyright (c) George Ogata
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class C::Parser
# shift/reduce conflict on "if (c) if (c) ; else ; else ;"
expect 1
rule
# A.2.4 External definitions
# Returns TranslationUnit
translation_unit
: external_declaration {result = TranslationUnit.new_at(val[0].pos, NodeChain[val[0]])}
| translation_unit external_declaration {result = val[0]; result.entities << val[1]}
# Returns Declaration|FunctionDef
external_declaration
: function_definition {result = val[0]}
| declaration {result = val[0]}
# Returns FunctionDef
function_definition
: declaration_specifiers declarator declaration_list compound_statement {result = make_function_def(val[0][0], val[0][1], val[1], val[2], val[3])}
| declaration_specifiers declarator compound_statement {result = make_function_def(val[0][0], val[0][1], val[1], nil , val[2])}
# Returns [Declaration]
declaration_list
: declaration {result = [val[0]]}
| declaration_list declaration {result = val[0] << val[1]}
# A.2.3 Statements
# Returns Statement
statement
: labeled_statement {result = val[0]}
| compound_statement {result = val[0]}
| expression_statement {result = val[0]}
| selection_statement {result = val[0]}
| iteration_statement {result = val[0]}
| jump_statement {result = val[0]}
# Returns Statement
labeled_statement
: identifier COLON statement {val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].val)); result = val[2]}
| CASE constant_expression COLON statement {val[3].labels.unshift(Case .new_at(val[0].pos, val[1] )); result = val[3]}
| DEFAULT COLON statement {val[2].labels.unshift(Default .new_at(val[0].pos )); result = val[2]}
# type names can also be used as labels
| typedef_name COLON statement {val[2].labels.unshift(PlainLabel.new_at(val[0].pos, val[0].name)); result = val[2]}
# Returns Block
compound_statement
: LBRACE block_item_list RBRACE {result = Block.new_at(val[0].pos, val[1])}
| LBRACE RBRACE {result = Block.new_at(val[0].pos )}
# Returns NodeChain[Declaration|Statement]
block_item_list
: block_item {result = NodeChain[val[0]]}
| block_item_list block_item {result = val[0] << val[1]}
# Returns Declaration|Statement
block_item
: declaration {result = val[0]}
| statement {result = val[0]}
# Returns ExpressionStatement
expression_statement
: expression SEMICOLON {result = ExpressionStatement.new_at(val[0].pos, val[0])}
| SEMICOLON {result = ExpressionStatement.new_at(val[0].pos )}
# Returns Statement
selection_statement
: IF LPAREN expression RPAREN statement {result = If .new_at(val[0].pos, val[2], val[4] )}
| IF LPAREN expression RPAREN statement ELSE statement {result = If .new_at(val[0].pos, val[2], val[4], val[6])}
| SWITCH LPAREN expression RPAREN statement {result = Switch.new_at(val[0].pos, val[2], val[4] )}
# Returns Statement
iteration_statement
: WHILE LPAREN expression RPAREN statement {result = While.new_at(val[0].pos, val[2], val[4] )}
| DO statement WHILE LPAREN expression RPAREN SEMICOLON {result = While.new_at(val[0].pos, val[4], val[1], :do => true )}
| FOR LPAREN expression SEMICOLON expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], val[4], val[6], val[8])}
| FOR LPAREN expression SEMICOLON expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], val[4], nil , val[7])}
| FOR LPAREN expression SEMICOLON SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , val[5], val[7])}
| FOR LPAREN expression SEMICOLON SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , nil , val[6])}
| FOR LPAREN SEMICOLON expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, nil , val[3], val[5], val[7])}
| FOR LPAREN SEMICOLON expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, nil , val[3], nil , val[6])}
| FOR LPAREN SEMICOLON SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, nil , nil , val[4], val[6])}
| FOR LPAREN SEMICOLON SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, nil , nil , nil , val[5])}
| FOR LPAREN declaration expression SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], val[3], val[5], val[7])}
| FOR LPAREN declaration expression SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], val[3], nil , val[6])}
| FOR LPAREN declaration SEMICOLON expression RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , val[4], val[6])}
| FOR LPAREN declaration SEMICOLON RPAREN statement {result = For.new_at(val[0].pos, val[2], nil , nil , val[5])}
# Returns Statement
jump_statement
: GOTO identifier SEMICOLON {result = Goto .new_at(val[0].pos, val[1].val)}
| CONTINUE SEMICOLON {result = Continue.new_at(val[0].pos )}
| BREAK SEMICOLON {result = Break .new_at(val[0].pos )}
| RETURN expression SEMICOLON {result = Return .new_at(val[0].pos, val[1] )}
| RETURN SEMICOLON {result = Return .new_at(val[0].pos )}
# type names can also be used as labels
| GOTO typedef_name SEMICOLON {result = Goto .new_at(val[0].pos, val[1].name)}
# A.2.2 Declarations
# Returns Declaration
declaration
: declaration_specifiers init_declarator_list SEMICOLON {result = make_declaration(val[0][0], val[0][1], val[1])}
| declaration_specifiers SEMICOLON {result = make_declaration(val[0][0], val[0][1], NodeArray[])}
# Returns {Pos, [Symbol]}
declaration_specifiers
: storage_class_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
| storage_class_specifier {result = [val[0][0], [val[0][1]]]}
| type_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
| type_specifier {result = [val[0][0], [val[0][1]]]}
| type_qualifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
| type_qualifier {result = [val[0][0], [val[0][1]]]}
| function_specifier declaration_specifiers {val[1][1] << val[0][1]; result = val[1]}
| function_specifier {result = [val[0][0], [val[0][1]]]}
# Returns NodeArray[Declarator]
init_declarator_list
: init_declarator {result = NodeArray[val[0]]}
| init_declarator_list COMMA init_declarator {result = val[0] << val[2]}
# Returns Declarator
init_declarator
: declarator {result = val[0]}
| declarator EQ initializer {val[0].init = val[2]; result = val[0]}
# Returns [Pos, Symbol]
storage_class_specifier
: TYPEDEF {result = [val[0].pos, :typedef ]}
| EXTERN {result = [val[0].pos, :extern ]}
| STATIC {result = [val[0].pos, :static ]}
| AUTO {result = [val[0].pos, :auto ]}
| REGISTER {result = [val[0].pos, :register]}
# Returns [Pos, Type|Symbol]
type_specifier
: VOID {result = [val[0].pos, :void ]}
| CHAR {result = [val[0].pos, :char ]}
| SHORT {result = [val[0].pos, :short ]}
| INT {result = [val[0].pos, :int ]}
| LONG {result = [val[0].pos, :long ]}
| FLOAT {result = [val[0].pos, :float ]}
| DOUBLE {result = [val[0].pos, :double ]}
| SIGNED {result = [val[0].pos, :signed ]}
| UNSIGNED {result = [val[0].pos, :unsigned ]}
| BOOL {result = [val[0].pos, :_Bool ]}
| COMPLEX {result = [val[0].pos, :_Complex ]}
| IMAGINARY {result = [val[0].pos, :_Imaginary]}
| struct_or_union_specifier {result = [val[0].pos, val[0] ]}
| enum_specifier {result = [val[0].pos, val[0] ]}
| typedef_name {result = [val[0].pos, val[0] ]}
# Returns Struct|Union
struct_or_union_specifier
: struct_or_union identifier LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], val[1].val, val[3])}
| struct_or_union LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], nil , val[2])}
| struct_or_union identifier {result = val[0][1].new_at(val[0][0], val[1].val, nil )}
# type names can also be used as struct identifiers
| struct_or_union typedef_name LBRACE struct_declaration_list RBRACE {result = val[0][1].new_at(val[0][0], val[1].name, val[3])}
| struct_or_union typedef_name {result = val[0][1].new_at(val[0][0], val[1].name, nil )}
# Returns [Pos, Class]
struct_or_union
: STRUCT {result = [val[0].pos, Struct]}
| UNION {result = [val[0].pos, Union ]}
# Returns NodeArray[Declaration]
struct_declaration_list
: struct_declaration {result = NodeArray[val[0]]}
| struct_declaration_list struct_declaration {val[0] << val[1]; result = val[0]}
# Returns Declaration
struct_declaration
: specifier_qualifier_list struct_declarator_list SEMICOLON {result = make_declaration(val[0][0], val[0][1], val[1])}
# Returns {Pos, [Symbol]}
specifier_qualifier_list
: type_specifier specifier_qualifier_list {val[1][1] << val[0][1]; result = val[1]}
| type_specifier {result = [val[0][0], [val[0][1]]]}
| type_qualifier specifier_qualifier_list {val[1][1] << val[0][1]; result = val[1]}
| type_qualifier {result = [val[0][0], [val[0][1]]]}
# Returns NodeArray[Declarator]
struct_declarator_list
: struct_declarator {result = NodeArray[val[0]]}
| struct_declarator_list COMMA struct_declarator {result = val[0] << val[2]}
# Returns Declarator
struct_declarator
: declarator {result = val[0]}
| declarator COLON constant_expression {result = val[0]; val[0].num_bits = val[2]}
| COLON constant_expression {result = Declarator.new_at(val[0].pos, :num_bits => val[1])}
# Returns Enum
enum_specifier
: ENUM identifier LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, val[1].val, val[3])}
| ENUM LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, nil , val[2])}
| ENUM identifier LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, val[1].val, val[3])}
| ENUM LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, nil , val[2])}
| ENUM identifier {result = Enum.new_at(val[0].pos, val[1].val, nil )}
# type names can also be used as enum names
| ENUM typedef_name LBRACE enumerator_list RBRACE {result = Enum.new_at(val[0].pos, val[1].name, val[3])}
| ENUM typedef_name LBRACE enumerator_list COMMA RBRACE {result = Enum.new_at(val[0].pos, val[1].name, val[3])}
| ENUM typedef_name {result = Enum.new_at(val[0].pos, val[1].name, nil )}
# Returns NodeArray[Enumerator]
enumerator_list
: enumerator {result = NodeArray[val[0]]}
| enumerator_list COMMA enumerator {result = val[0] << val[2]}
# Returns Enumerator
enumerator
: enumeration_constant {result = Enumerator.new_at(val[0].pos, val[0].val, nil )}
| enumeration_constant EQ constant_expression {result = Enumerator.new_at(val[0].pos, val[0].val, val[2])}
# Returns [Pos, Symbol]
type_qualifier
: CONST {result = [val[0].pos, :const ]}
| RESTRICT {result = [val[0].pos, :restrict]}
| VOLATILE {result = [val[0].pos, :volatile]}
# Returns [Pos, Symbol]
function_specifier
: INLINE {result = [val[0].pos, :inline]}
# Returns Declarator
declarator
: pointer direct_declarator {result = add_decl_type(val[1], val[0])}
| direct_declarator {result = val[0]}
# Returns Declarator
direct_declarator
: identifier {result = Declarator.new_at(val[0].pos, nil, val[0].val)}
| LPAREN declarator RPAREN {result = val[1]}
| direct_declarator LBRACKET type_qualifier_list assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
| direct_declarator LBRACKET type_qualifier_list RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
| direct_declarator LBRACKET assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos, nil, val[2]))}
| direct_declarator LBRACKET RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))}
| direct_declarator LBRACKET STATIC type_qualifier_list assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
| direct_declarator LBRACKET STATIC assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
| direct_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
| direct_declarator LBRACKET type_qualifier_list MUL RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
| direct_declarator LBRACKET MUL RBRACKET {result = add_decl_type(val[0], Array.new_at(val[0].pos ))} # TODO
| direct_declarator LPAREN parameter_type_list RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, param_list(*val[2]), :var_args => val[2][1]))}
| direct_declarator LPAREN identifier_list RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos, nil, val[2]))}
| direct_declarator LPAREN RPAREN {result = add_decl_type(val[0], Function.new_at(val[0].pos ))}
# Returns Pointer
pointer
: MUL type_qualifier_list {result = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]) }
| MUL {result = Pointer.new_at(val[0].pos) }
| MUL type_qualifier_list pointer {p = add_type_quals(Pointer.new_at(val[0].pos), val[1][1]); val[2].direct_type = p; result = val[2]}
| MUL pointer {p = Pointer.new_at(val[0].pos) ; val[1].direct_type = p; result = val[1]}
# Returns {Pos, [Symbol]}
type_qualifier_list
: type_qualifier {result = [val[0][0], [val[0][1]]]}
| type_qualifier_list type_qualifier {val[0][1] << val[1][1]; result = val[0]}
# Returns [NodeArray[Parameter], var_args?]
parameter_type_list
: parameter_list {result = [val[0], false]}
| parameter_list COMMA ELLIPSIS {result = [val[0], true ]}
# Returns NodeArray[Parameter]
parameter_list
: parameter_declaration {result = NodeArray[val[0]]}
| parameter_list COMMA parameter_declaration {result = val[0] << val[2]}
# Returns Parameter
parameter_declaration
: declaration_specifiers declarator {ind_type = val[1].indirect_type and ind_type.detach
result = make_parameter(val[0][0], val[0][1], ind_type, val[1].name)}
| declaration_specifiers abstract_declarator {result = make_parameter(val[0][0], val[0][1], val[1] , nil )}
| declaration_specifiers {result = make_parameter(val[0][0], val[0][1], nil , nil )}
# Returns NodeArray[Parameter]
identifier_list
: identifier {result = NodeArray[Parameter.new_at(val[0].pos, nil, val[0].val)]}
| identifier_list COMMA identifier {result = val[0] << Parameter.new_at(val[2].pos, nil, val[2].val)}
# Returns Type
type_name
: specifier_qualifier_list abstract_declarator {val[1].direct_type = make_direct_type(val[0][0], val[0][1]); result = val[1]}
| specifier_qualifier_list {result = make_direct_type(val[0][0], val[0][1]) }
# Returns Type
abstract_declarator
: pointer {result = val[0]}
| pointer direct_abstract_declarator {val[1].direct_type = val[0]; result = val[1]}
| direct_abstract_declarator {result = val[0]}
# Returns Type
direct_abstract_declarator
: LPAREN abstract_declarator RPAREN {result = val[1]}
| direct_abstract_declarator LBRACKET assignment_expression RBRACKET {val[0].direct_type = Array.new_at(val[0].pos, nil, val[2]); result = val[0]}
| direct_abstract_declarator LBRACKET RBRACKET {val[0].direct_type = Array.new_at(val[0].pos, nil, nil ); result = val[0]}
| LBRACKET assignment_expression RBRACKET {result = Array.new_at(val[0].pos, nil, val[1])}
| LBRACKET RBRACKET {result = Array.new_at(val[0].pos )}
| direct_abstract_declarator LBRACKET MUL RBRACKET {val[0].direct_type = Array.new_at(val[0].pos); result = val[0]} # TODO
| LBRACKET MUL RBRACKET {result = Array.new_at(val[0].pos)} # TODO
| direct_abstract_declarator LPAREN parameter_type_list RPAREN {val[0].direct_type = Function.new_at(val[0].pos, nil, param_list(*val[2]), val[2][1]); result = val[0]}
| direct_abstract_declarator LPAREN RPAREN {val[0].direct_type = Function.new_at(val[0].pos ); result = val[0]}
| LPAREN parameter_type_list RPAREN {result = Function.new_at(val[0].pos, nil, param_list(*val[1]), val[1][1])}
| LPAREN RPAREN {result = Function.new_at(val[0].pos )}
# Returns CustomType
typedef_name
#: identifier -- insufficient since we must distinguish between type
# names and var names (otherwise we have a conflict)
: TYPENAME {result = CustomType.new_at(val[0].pos, val[0].val)}
# Returns Expression
initializer
: assignment_expression {result = val[0]}
| LBRACE initializer_list RBRACE {result = CompoundLiteral.new_at(val[0].pos, nil, val[1])}
| LBRACE initializer_list COMMA RBRACE {result = CompoundLiteral.new_at(val[0].pos, nil, val[1])}
# Returns NodeArray[MemberInit]
initializer_list
: designation initializer {result = NodeArray[MemberInit.new_at(val[0][0] , val[0][1], val[1])]}
| initializer {result = NodeArray[MemberInit.new_at(val[0].pos, nil , val[0])]}
| initializer_list COMMA designation initializer {result = val[0] << MemberInit.new_at(val[2][0] , val[2][1], val[3])}
| initializer_list COMMA initializer {result = val[0] << MemberInit.new_at(val[2].pos, nil , val[2])}
# Returns {Pos, NodeArray[Expression|Token]}
designation
: designator_list EQ {result = val[0]}
# Returns {Pos, NodeArray[Expression|Token]}
designator_list
: designator {result = val[0]; val[0][1] = NodeArray[val[0][1]]}
| designator_list designator {result = val[0]; val[0][1] << val[1][1]}
# Returns {Pos, Expression|Member}
designator
: LBRACKET constant_expression RBRACKET {result = [val[1].pos, val[1] ]}
| DOT identifier {result = [val[1].pos, Member.new_at(val[1].pos, val[1].val)]}
# A.2.1 Expressions
# Returns Expression
primary_expression
: identifier {result = Variable.new_at(val[0].pos, val[0].val)}
| constant {result = val[0]}
| string_literal {result = val[0]}
# GCC EXTENSION: allow a compound statement in parentheses as an expression
| LPAREN expression RPAREN {result = val[1]}
| LPAREN compound_statement RPAREN {block_expressions_enabled? or parse_error val[0].pos, "compound statement found where expression expected"
result = BlockExpression.new(val[1]); result.pos = val[0].pos}
# Returns Expression
postfix_expression
: primary_expression {result = val[0]}
| postfix_expression LBRACKET expression RBRACKET {result = Index .new_at(val[0].pos, val[0], val[2])}
| postfix_expression LPAREN argument_expression_list RPAREN {result = Call .new_at(val[0].pos, val[0], val[2] )}
| postfix_expression LPAREN RPAREN {result = Call .new_at(val[0].pos, val[0], NodeArray[])}
| postfix_expression DOT identifier {result = Dot .new_at(val[0].pos, val[0], Member.new(val[2].val))}
| postfix_expression ARROW identifier {result = Arrow .new_at(val[0].pos, val[0], Member.new(val[2].val))}
| postfix_expression INC {result = PostInc .new_at(val[0].pos, val[0] )}
| postfix_expression DEC {result = PostDec .new_at(val[0].pos, val[0] )}
| LPAREN type_name RPAREN LBRACE initializer_list RBRACE {result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])}
| LPAREN type_name RPAREN LBRACE initializer_list COMMA RBRACE {result = CompoundLiteral.new_at(val[0].pos, val[1], val[4])}
# Returns [Expression|Type]
argument_expression_list
: argument_expression {result = NodeArray[val[0]]}
| argument_expression_list COMMA argument_expression {result = val[0] << val[2]}
# Returns Expression|Type -- EXTENSION: allow type names here too, to support some standard library macros (e.g., va_arg [7.15.1.1])
argument_expression
: assignment_expression {result = val[0]}
| type_name {result = val[0]}
# Returns Expression
unary_expression
: postfix_expression {result = val[0]}
| INC unary_expression {result = PreInc.new_at(val[0].pos, val[1])}
| DEC unary_expression {result = PreDec.new_at(val[0].pos, val[1])}
| unary_operator cast_expression {result = val[0][0].new_at(val[0][1], val[1])}
| SIZEOF unary_expression {result = Sizeof.new_at(val[0].pos, val[1])}
| SIZEOF LPAREN type_name RPAREN {result = Sizeof.new_at(val[0].pos, val[2])}
# Returns [Class, Pos]
unary_operator
: AND {result = [Address , val[0].pos]}
| MUL {result = [Dereference, val[0].pos]}
| ADD {result = [Positive , val[0].pos]}
| SUB {result = [Negative , val[0].pos]}
| NOT {result = [BitNot , val[0].pos]}
| BANG {result = [Not , val[0].pos]}
# Returns Expression
cast_expression
: unary_expression {result = val[0]}
| LPAREN type_name RPAREN cast_expression {result = Cast.new_at(val[0].pos, val[1], val[3])}
# Returns Expression
multiplicative_expression
: cast_expression {result = val[0]}
| multiplicative_expression MUL cast_expression {result = Multiply.new_at(val[0].pos, val[0], val[2])}
| multiplicative_expression DIV cast_expression {result = Divide .new_at(val[0].pos, val[0], val[2])}
| multiplicative_expression MOD cast_expression {result = Mod .new_at(val[0].pos, val[0], val[2])}
# Returns Expression
additive_expression
: multiplicative_expression {result = val[0]}
| additive_expression ADD multiplicative_expression {result = Add .new_at(val[0].pos, val[0], val[2])}
| additive_expression SUB multiplicative_expression {result = Subtract.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
shift_expression
: additive_expression {result = val[0]}
| shift_expression LSHIFT additive_expression {result = ShiftLeft .new_at(val[0].pos, val[0], val[2])}
| shift_expression RSHIFT additive_expression {result = ShiftRight.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
relational_expression
: shift_expression {result = val[0]}
| relational_expression LT shift_expression {result = Less.new_at(val[0].pos, val[0], val[2])}
| relational_expression GT shift_expression {result = More.new_at(val[0].pos, val[0], val[2])}
| relational_expression LEQ shift_expression {result = LessOrEqual.new_at(val[0].pos, val[0], val[2])}
| relational_expression GEQ shift_expression {result = MoreOrEqual.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
equality_expression
: relational_expression {result = val[0]}
| equality_expression EQEQ relational_expression {result = Equal .new_at(val[0].pos, val[0], val[2])}
| equality_expression NEQ relational_expression {result = NotEqual.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
and_expression
: equality_expression {result = val[0]}
| and_expression AND equality_expression {result = BitAnd.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
exclusive_or_expression
: and_expression {result = val[0]}
| exclusive_or_expression XOR and_expression {result = BitXor.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
inclusive_or_expression
: exclusive_or_expression {result = val[0]}
| inclusive_or_expression OR exclusive_or_expression {result = BitOr.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
logical_and_expression
: inclusive_or_expression {result = val[0]}
| logical_and_expression ANDAND inclusive_or_expression {result = And.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
logical_or_expression
: logical_and_expression {result = val[0]}
| logical_or_expression OROR logical_and_expression {result = Or.new_at(val[0].pos, val[0], val[2])}
# Returns Expression
conditional_expression
: logical_or_expression {result = val[0]}
| logical_or_expression QUESTION expression COLON conditional_expression {result = Conditional.new_at(val[0].pos, val[0], val[2], val[4])}
# Returns Expression
assignment_expression
: conditional_expression {result = val[0]}
| unary_expression assignment_operator assignment_expression {result = val[1].new_at(val[0].pos, val[0], val[2])}
# Returns Class
assignment_operator
: EQ {result = Assign}
| MULEQ {result = MultiplyAssign}
| DIVEQ {result = DivideAssign}
| MODEQ {result = ModAssign}
| ADDEQ {result = AddAssign}
| SUBEQ {result = SubtractAssign}
| LSHIFTEQ {result = ShiftLeftAssign}
| RSHIFTEQ {result = ShiftRightAssign}
| ANDEQ {result = BitAndAssign}
| XOREQ {result = BitXorAssign}
| OREQ {result = BitOrAssign}
# Returns Expression
expression
: assignment_expression {result = val[0]}
| expression COMMA assignment_expression {
if val[0].is_a? Comma
if val[2].is_a? Comma
val[0].exprs.push(*val[2].exprs)
else
val[0].exprs << val[2]
end
result = val[0]
else
if val[2].is_a? Comma
val[2].exprs.unshift(val[0])
val[2].pos = val[0].pos
result = val[2]
else
result = Comma.new_at(val[0].pos, NodeArray[val[0], val[2]])
end
end
}
# Returns Expression
constant_expression
: conditional_expression {result = val[0]}
# A.1.1 -- Lexical elements
#
# token
# : keyword (raw string)
# | identifier expanded below
# | constant expanded below
# | string_literal expanded below
# | punctuator (raw string)
#
# preprocessing-token (skip)
# Returns Token
identifier
: ID {result = val[0]}
# Returns Literal
constant
: ICON {result = val[0].val; result.pos = val[0].pos}
| FCON {result = val[0].val; result.pos = val[0].pos}
#| enumeration_constant -- these are parsed as identifiers at all
# places the `constant' nonterminal appears
| CCON {result = val[0].val; result.pos = val[0].pos}
# Returns Token
enumeration_constant
: ID {result = val[0]}
# Returns StringLiteral
# Also handles string literal concatenation (6.4.5.4)
string_literal
: string_literal SCON {val[0].val << val[1].val.val; result = val[0]}
| SCON { result = val[0].val; result.pos = val[0].pos }
---- inner
# A.1.9 -- Preprocessing numbers -- skip
# A.1.8 -- Header names -- skip
# A.1.7 -- Puncuators -- we don't bother with {##,#,%:,%:%:} since
# we don't do preprocessing
@@punctuators = %r'\+\+|-[->]|&&|\|\||\.\.\.|(?:<<|>>|[<>=!*/%+\-&^|])=?|[\[\](){}.~?:;,]'
@@digraphs = %r'<[:%]|[:%]>'
# A.1.6 -- String Literals -- simple for us because we don't decode
# the string (and indeed accept some illegal strings)
@@string_literal = %r'L?"(?:[^\\]|\\.)*?"'m
# A.1.5 -- Constants
@@decimal_floating_constant = %r'(?:(?:\d*\.\d+|\d+\.)(?:e[-+]?\d+)?|\d+e[-+]?\d+)[fl]?'i
@@hexadecimal_floating_constant = %r'0x(?:(?:[0-9a-f]*\.[0-9a-f]+|[0-9a-f]+\.)|[0-9a-f]+)p[-+]?\d+[fl]?'i
@@integer_constant = %r'(?:[1-9][0-9]*|0x[0-9a-f]+|0[0-7]*)(?:ul?l?|ll?u?)?'i
@@floating_constant = %r'#{@@decimal_floating_constant}|#{@@hexadecimal_floating_constant}'
@@enumeration_constant = %r'[a-zA-Z_\\][a-zA-Z_\\0-9]*'
@@character_constant = %r"L?'(?:[^\\]|\\.)+?'"
# (note that as with string-literals, we accept some illegal
# character-constants)
# A.1.4 -- Universal character names -- skip
# A.1.3 -- Identifiers -- skip, since an identifier is lexically
# identical to an enumeration constant
# A.1.2 Keywords
keywords = %w'auto break case char const continue default do
double else enum extern float for goto if inline int long register
restrict return short signed sizeof static struct switch typedef union
unsigned void volatile while _Bool _Complex _Imaginary'
@@keywords = %r"#{keywords.join('|')}"
def initialize
@type_names = ::Set.new
@warning_proc = lambda{}
@pos = C::Node::Pos.new(nil, 1, 0)
end
def initialize_copy(x)
@pos = x.pos.dup
@type_names = x.type_names.dup
end
attr_accessor :pos, :type_names
def parse(str)
if str.respond_to? :read
str = str.read
end
@str = str
begin
prepare_lexer(str)
return do_parse
rescue ParseError => e
e.set_backtrace(caller)
raise
end
end
#
# Error handler, as used by racc.
#
def on_error(error_token_id, error_value, value_stack)
if error_value == '$'
parse_error @pos, "unexpected EOF"
else
parse_error(error_value.pos,
"parse error on #{token_to_str(error_token_id)} (#{error_value.val})")
end
end
def self.feature(name)
attr_writer "#{name}_enabled"
class_eval <<-EOS
def enable_#{name}
@#{name}_enabled = true
end
def #{name}_enabled?
@#{name}_enabled
end
EOS
end
private_class_method :feature
#
# Allow blocks in parentheses as expressions, as per the gcc
# extension. [http://rubyurl.com/iB7]
#
feature :block_expressions
private # ---------------------------------------------------------
class Token
attr_accessor :pos, :val
def initialize(pos, val)
@pos = pos
@val = val
end
end
def eat(str)
lines = str.split(/\r\n|[\r\n]/, -1)
if lines.length == 1
@pos.col_num += lines[0].length
else
@pos.line_num += lines.length - 1
@pos.col_num = lines[-1].length
end
end
#
# Make a Declaration from the given specs and declarators.
#
def make_declaration(pos, specs, declarators)
specs.all?{|x| x.is_a?(Symbol) || x.is_a?(Type)} or raise specs.map{|x| x.class}.inspect
decl = Declaration.new_at(pos, nil, declarators)
# set storage class
storage_classes = specs.find_all do |x|
[:typedef, :extern, :static, :auto, :register].include? x
end
# 6.7.1p2: at most, one storage-class specifier may be given in
# the declaration specifiers in a declaration
storage_classes.length <= 1 or
begin
if declarators.length == 0
for_name = ''
else
for_name = "for `#{declarators[0].name}'"
end
parse_error pos, "multiple or duplicate storage classes given #{for_name}'"
end
decl.storage = storage_classes[0]
# set type (specifiers, qualifiers)
decl.type = make_direct_type(pos, specs)
# set function specifiers
decl.inline = specs.include?(:inline)
# look for new type names
if decl.typedef?
decl.declarators.each do |d|
if d.name
@type_names << d.name
end
end
end
return decl
end
def make_function_def(pos, specs, func_declarator, decl_list, defn)
add_decl_type(func_declarator, make_direct_type(pos, specs))
# get types from decl_list if necessary
function = func_declarator.indirect_type
function.is_a? Function or
parse_error pos, "non function type for function `#{func_declarator.name}'"
params = function.params
if decl_list
params.all?{|p| p.type.nil?} or
parse_error pos, "both prototype and declaration list given for `#{func_declarator.name}'"
decl_list.each do |declaration|
declaration.declarators.each do |declarator|
param = params.find{|p| p.name == declarator.name} or
parse_error pos, "no parameter named #{declarator.name}"
if declarator.indirect_type
param.type = declarator.indirect_type
param.type.direct_type = declaration.type.dup
else
param.type = declaration.type.dup
end
end
end
params.all?{|p| p.type} or
begin
s = params.find_all{|p| p.type.nil?}.map{|p| "`#{p.name}'"}.join(' and ')
parse_error pos, "types missing for parameters #{s}"
end
end
fd = FunctionDef.new_at(pos,
function.detach,
func_declarator.name,
defn,
:no_prototype => !decl_list.nil?)
# set storage class
# 6.9.1p4: only extern or static allowed
specs.each do |s|
[:typedef, :auto, :register].include?(s) and
"`#{s}' illegal for function"
end
storage_classes = specs.find_all do |s|
s == :extern || s == :static
end
# 6.7.1p2: at most, one storage-class specifier may be given in
# the declaration specifiers in a declaration
storage_classes.length <= 1 or
"multiple or duplicate storage classes given for `#{func_declarator.name}'"
fd.storage = storage_classes[0] if storage_classes[0]
# set function specifiers
# 6.7.4p5 'inline' can be repeated
fd.inline = specs.include?(:inline)
return fd
end
#
# Make a direct type from the list of type specifiers and type
# qualifiers.
#
def make_direct_type(pos, specs)
specs_order = [:signed, :unsigned, :short, :long, :double, :void,
:char, :int, :float, :_Bool, :_Complex, :_Imaginary]
type_specs = specs.find_all do |x|
specs_order.include?(x) || !x.is_a?(Symbol)
end
type_specs.sort! do |a, b|
(specs_order.index(a)||100) <=> (specs_order.index(b)||100)
end
# set type specifiers
# 6.7.2p2: the specifier list should be one of these
type =
case type_specs
when [:void]
Void.new
when [:char]
Char.new
when [:signed, :char]
Char.new :signed => true
when [:unsigned, :char]
Char.new :signed => false
when [:short], [:signed, :short], [:short, :int],
[:signed, :short, :int]
Int.new :longness => -1
when [:unsigned, :short], [:unsigned, :short, :int]
Int.new :unsigned => true, :longness => -1
when [:int], [:signed], [:signed, :int]
Int.new
when [:unsigned], [:unsigned, :int]
Int.new :unsigned => true
when [:long], [:signed, :long], [:long, :int],
[:signed, :long, :int]
Int.new :longness => 1
when [:unsigned, :long], [:unsigned, :long, :int]
Int.new :longness => 1, :unsigned => true
when [:long, :long], [:signed, :long, :long],
[:long, :long, :int], [:signed, :long, :long, :int]
Int.new :longness => 2
when [:unsigned, :long, :long], [:unsigned, :long, :long, :int]
Int.new :longness => 2, :unsigned => true
when [:float]
Float.new
when [:double]
Float.new :longness => 1
when [:long, :double]
Float.new :longness => 2
when [:_Bool]
Bool.new
when [:float, :_Complex]
Complex.new
when [:double, :_Complex]
Complex.new :longness => 1
when [:long, :double, :_Complex]
Complex.new :longness => 2
when [:float, :_Imaginary]
Imaginary.new
when [:double, :_Imaginary]
Imaginary.new :longness => 1
when [:long, :double, :_Imaginary]
Imaginary.new :longness => 2
else
if type_specs.length == 1 &&
[CustomType, Struct, Union, Enum].any?{|c| type_specs[0].is_a? c}
type_specs[0]
else
if type_specs == []
parse_error pos, "no type specifiers given"
else
parse_error pos, "invalid type specifier combination: #{type_specs.join(' ')}"
end
end
end
type.pos ||= pos
# set type qualifiers
# 6.7.3p4: type qualifiers can be repeated
type.const = specs.any?{|x| x.equal? :const }
type.restrict = specs.any?{|x| x.equal? :restrict}
type.volatile = specs.any?{|x| x.equal? :volatile}
return type
end
def make_parameter(pos, specs, indirect_type, name)
type = indirect_type
if type
type.direct_type = make_direct_type(pos, specs)
else
type = make_direct_type(pos, specs)
end
[:typedef, :extern, :static, :auto, :inline].each do |sym|
specs.include? sym and
parse_error pos, "parameter `#{declarator.name}' declared `#{sym}'"
end
return Parameter.new_at(pos, type, name,
:register => specs.include?(:register))
end
def add_type_quals(type, quals)
type.const = quals.include?(:const )
type.restrict = quals.include?(:restrict)
type.volatile = quals.include?(:volatile)
return type
end
#
# Add te given type as the "most direct" type to the given
# declarator. Return the declarator.
#
def add_decl_type(declarator, type)
if declarator.indirect_type
declarator.indirect_type.direct_type = type
else
declarator.indirect_type = type
end
return declarator
end
def param_list(params, var_args)
if params.length == 1 &&
params[0].type.is_a?(Void) &&
params[0].name.nil?
return NodeArray[]
elsif params.empty?
return nil
else
return params
end
end
def parse_error(pos, str)
raise ParseError, "#{pos}: #{str}"
end
---- header
require 'set'
# Error classes
module C
class ParseError < StandardError; end
end
# Local variables:
# mode: ruby
# end:

126
test/racc/assets/chk.y Normal file
View file

@ -0,0 +1,126 @@
#
# racc tester
#
class Calcp
prechigh
left '*' '/'
left '+' '-'
preclow
convert
NUMBER 'Number'
end
rule
target : exp | /* none */ { result = 0 } ;
exp : exp '+' exp { result += val[2]; @plus = 'plus' }
| exp '-' exp { result -= val[2]; @str = "string test" }
| exp '*' exp { result *= val[2] }
| exp '/' exp { result /= val[2] }
| '(' { $emb = true } exp ')'
{
raise 'must not happen' unless $emb
result = val[2]
}
| '-' NUMBER { result = -val[1] }
| NUMBER
;
end
----header
class Number; end
----inner
def parse( src )
$emb = false
@plus = nil
@str = nil
@src = src
result = do_parse
if @plus
raise 'string parse failed' unless @plus == 'plus'
end
if @str
raise 'string parse failed' unless @str == 'string test'
end
result
end
def next_token
@src.shift
end
def initialize
@yydebug = true
end
----footer
$parser = Calcp.new
$test_number = 1
def chk( src, ans )
result = $parser.parse(src)
raise "test #{$test_number} fail" unless result == ans
$test_number += 1
end
chk(
[ [Number, 9],
[false, false],
[false, false] ], 9
)
chk(
[ [Number, 5],
['*', nil],
[Number, 1],
['-', nil],
[Number, 1],
['*', nil],
[Number, 8],
[false, false],
[false, false] ], -3
)
chk(
[ [Number, 5],
['+', nil],
[Number, 2],
['-', nil],
[Number, 5],
['+', nil],
[Number, 2],
['-', nil],
[Number, 5],
[false, false],
[false, false] ], -1
)
chk(
[ ['-', nil],
[Number, 4],
[false, false],
[false, false] ], -4
)
chk(
[ [Number, 7],
['*', nil],
['(', nil],
[Number, 4],
['+', nil],
[Number, 3],
[')', nil],
['-', nil],
[Number, 9],
[false, false],
[false, false] ], 40
)

16
test/racc/assets/conf.y Normal file
View file

@ -0,0 +1,16 @@
class A
rule
a: A c C expr;
b: A B; # useless
c: A;
c: A;
expr: expr '+' expr
expr: expr '-' expr
expr: NUMBER
end

729
test/racc/assets/csspool.y Normal file
View file

@ -0,0 +1,729 @@
class CSSPool::CSS::Parser
token CHARSET_SYM IMPORT_SYM STRING SEMI IDENT S COMMA LBRACE RBRACE STAR HASH
token LSQUARE RSQUARE EQUAL INCLUDES DASHMATCH LPAREN RPAREN FUNCTION GREATER PLUS
token SLASH NUMBER MINUS LENGTH PERCENTAGE ANGLE TIME FREQ URI
token IMPORTANT_SYM MEDIA_SYM NOT ONLY AND NTH_PSEUDO_CLASS
token DOCUMENT_QUERY_SYM FUNCTION_NO_QUOTE
token TILDE
token PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH
token NOT_PSEUDO_CLASS
token KEYFRAMES_SYM
token MATCHES_PSEUDO_CLASS
token NAMESPACE_SYM
token MOZ_PSEUDO_ELEMENT
token RESOLUTION
token COLON
token SUPPORTS_SYM
token OR
token VARIABLE_NAME
token CALC_SYM
token FONTFACE_SYM
token UNICODE_RANGE
token RATIO
rule
document
: { @handler.start_document }
stylesheet
{ @handler.end_document }
;
stylesheet
: charset stylesheet
| import stylesheet
| namespace stylesheet
| charset
| import
| namespace
| body
|
;
charset
: CHARSET_SYM STRING SEMI { @handler.charset interpret_string(val[1]), {} }
;
import
: IMPORT_SYM import_location medium SEMI {
@handler.import_style val[2], val[1]
}
| IMPORT_SYM import_location SEMI {
@handler.import_style [], val[1]
}
;
import_location
: import_location S
| STRING { result = Terms::String.new interpret_string val.first }
| URI { result = Terms::URI.new interpret_uri val.first }
;
namespace
: NAMESPACE_SYM ident import_location SEMI {
@handler.namespace val[1], val[2]
}
| NAMESPACE_SYM import_location SEMI {
@handler.namespace nil, val[1]
}
;
medium
: medium COMMA IDENT {
result = val[0] << MediaType.new(val[2])
}
| IDENT {
result = [MediaType.new(val[0])]
}
;
media_query_list
: media_query { result = MediaQueryList.new([ val[0] ]) }
| media_query_list COMMA media_query { result = val[0] << val[2] }
| { result = MediaQueryList.new }
;
media_query
: optional_only_or_not media_type optional_and_exprs { result = MediaQuery.new(val[0], val[1], val[2]) }
| media_expr optional_and_exprs { result = MediaQuery.new(nil, val[0], val[1]) }
;
optional_only_or_not
: ONLY { result = :only }
| NOT { result = :not }
| { result = nil }
;
media_type
: IDENT { result = MediaType.new(val[0]) }
;
media_expr
: LPAREN optional_space IDENT optional_space RPAREN { result = MediaType.new(val[2]) }
| LPAREN optional_space IDENT optional_space COLON optional_space expr RPAREN { result = MediaFeature.new(val[2], val[6][0]) }
;
optional_space
: S { result = val[0] }
| { result = nil }
;
optional_and_exprs
: optional_and_exprs AND media_expr { result = val[0] << val[2] }
| { result = [] }
;
resolution
: RESOLUTION {
unit = val.first.gsub(/[\s\d.]/, '')
number = numeric(val.first)
result = Terms::Resolution.new(number, unit)
}
;
body
: ruleset body
| conditional_rule body
| keyframes_rule body
| fontface_rule body
| ruleset
| conditional_rule
| keyframes_rule
| fontface_rule
;
conditional_rule
: media
| document_query
| supports
;
body_in_media
: body
| empty_ruleset
;
media
: start_media body_in_media RBRACE { @handler.end_media val.first }
;
start_media
: MEDIA_SYM media_query_list LBRACE {
result = val[1]
@handler.start_media result
}
;
document_query
: start_document_query body RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) }
| start_document_query RBRACE { @handler.end_document_query(before_pos(val), after_pos(val)) }
;
start_document_query
: start_document_query_pos url_match_fns LBRACE {
@handler.start_document_query(val[1], after_pos(val))
}
;
start_document_query_pos
: DOCUMENT_QUERY_SYM {
@handler.node_start_pos = before_pos(val)
}
;
url_match_fns
: url_match_fn COMMA url_match_fns {
result = [val[0], val[2]].flatten
}
| url_match_fn {
result = val
}
;
url_match_fn
: function_no_quote
| function
| uri
;
supports
: start_supports body RBRACE { @handler.end_supports }
| start_supports RBRACE { @handler.end_supports }
;
start_supports
: SUPPORTS_SYM supports_condition_root LBRACE {
@handler.start_supports val[1]
}
;
supports_condition_root
: supports_negation { result = val.join('') }
| supports_conjunction_or_disjunction { result = val.join('') }
| supports_condition_in_parens { result = val.join('') }
;
supports_condition
: supports_negation { result = val.join('') }
| supports_conjunction_or_disjunction { result = val.join('') }
| supports_condition_in_parens { result = val.join('') }
;
supports_condition_in_parens
: LPAREN supports_condition RPAREN { result = val.join('') }
| supports_declaration_condition { result = val.join('') }
;
supports_negation
: NOT supports_condition_in_parens { result = val.join('') }
;
supports_conjunction_or_disjunction
: supports_conjunction
| supports_disjunction
;
supports_conjunction
: supports_condition_in_parens AND supports_condition_in_parens { result = val.join('') }
| supports_conjunction_or_disjunction AND supports_condition_in_parens { result = val.join('') }
;
supports_disjunction
: supports_condition_in_parens OR supports_condition_in_parens { result = val.join('') }
| supports_conjunction_or_disjunction OR supports_condition_in_parens { result = val.join('') }
;
supports_declaration_condition
: LPAREN declaration_internal RPAREN { result = val.join('') }
| LPAREN S declaration_internal RPAREN { result = val.join('') }
;
keyframes_rule
: start_keyframes_rule keyframes_blocks RBRACE
| start_keyframes_rule RBRACE
;
start_keyframes_rule
: KEYFRAMES_SYM IDENT LBRACE {
@handler.start_keyframes_rule val[1]
}
;
keyframes_blocks
: keyframes_block keyframes_blocks
| keyframes_block
;
keyframes_block
: start_keyframes_block declarations RBRACE { @handler.end_keyframes_block }
| start_keyframes_block RBRACE { @handler.end_keyframes_block }
;
start_keyframes_block
: keyframes_selectors LBRACE {
@handler.start_keyframes_block val[0]
}
;
keyframes_selectors
| keyframes_selector COMMA keyframes_selectors {
result = val[0] + ', ' + val[2]
}
| keyframes_selector
;
keyframes_selector
: IDENT
| PERCENTAGE { result = val[0].strip }
;
fontface_rule
: start_fontface_rule declarations RBRACE { @handler.end_fontface_rule }
| start_fontface_rule RBRACE { @handler.end_fontface_rule }
;
start_fontface_rule
: FONTFACE_SYM LBRACE {
@handler.start_fontface_rule
}
;
ruleset
: start_selector declarations RBRACE {
@handler.end_selector val.first
}
| start_selector RBRACE {
@handler.end_selector val.first
}
;
empty_ruleset
: optional_space {
start = @handler.start_selector([])
@handler.end_selector(start)
}
;
start_selector
: S start_selector { result = val.last }
| selectors LBRACE {
@handler.start_selector val.first
}
;
selectors
: selector COMMA selectors
{
sel = Selector.new(val.first, {})
result = [sel].concat(val[2])
}
| selector
{
result = [Selector.new(val.first, {})]
}
;
selector
: simple_selector combinator selector
{
val.flatten!
val[2].combinator = val.delete_at 1
result = val
}
| simple_selector
;
combinator
: S { result = :s }
| GREATER { result = :> }
| PLUS { result = :+ }
| TILDE { result = :~ }
;
simple_selector
: element_name hcap {
selector = val.first
selector.additional_selectors = val.last
result = [selector]
}
| element_name { result = val }
| hcap
{
ss = Selectors::Simple.new nil, nil
ss.additional_selectors = val.flatten
result = [ss]
}
;
simple_selectors
: simple_selector COMMA simple_selectors { result = [val[0], val[2]].flatten }
| simple_selector
;
ident_with_namespace
: IDENT { result = [interpret_identifier(val[0]), nil] }
| IDENT '|' IDENT { result = [interpret_identifier(val[2]), interpret_identifier(val[0])] }
| '|' IDENT { result = [interpret_identifier(val[1]), nil] }
| STAR '|' IDENT { result = [interpret_identifier(val[2]), '*'] }
;
element_name
: ident_with_namespace { result = Selectors::Type.new val.first[0], nil, val.first[1] }
| STAR { result = Selectors::Universal.new val.first }
| '|' STAR { result = Selectors::Universal.new val[1] }
| STAR '|' STAR { result = Selectors::Universal.new val[2], nil, val[0] }
| IDENT '|' STAR { result = Selectors::Universal.new val[2], nil, interpret_identifier(val[0]) }
;
hcap
: hash { result = val }
| class { result = val }
| attrib { result = val }
| pseudo { result = val }
| hash hcap { result = val.flatten }
| class hcap { result = val.flatten }
| attrib hcap { result = val.flatten }
| pseudo hcap { result = val.flatten }
;
hash
: HASH {
result = Selectors::Id.new interpret_identifier val.first.sub(/^#/, '')
}
class
: '.' IDENT {
result = Selectors::Class.new interpret_identifier val.last
}
;
attrib
: LSQUARE ident_with_namespace EQUAL IDENT RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_identifier(val[3]),
Selectors::Attribute::EQUALS,
val[1][1]
)
}
| LSQUARE ident_with_namespace EQUAL STRING RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_string(val[3]),
Selectors::Attribute::EQUALS,
val[1][1]
)
}
| LSQUARE ident_with_namespace INCLUDES STRING RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_string(val[3]),
Selectors::Attribute::INCLUDES,
val[1][1]
)
}
| LSQUARE ident_with_namespace INCLUDES IDENT RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_identifier(val[3]),
Selectors::Attribute::INCLUDES,
val[1][1]
)
}
| LSQUARE ident_with_namespace DASHMATCH IDENT RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_identifier(val[3]),
Selectors::Attribute::DASHMATCH,
val[1][1]
)
}
| LSQUARE ident_with_namespace DASHMATCH STRING RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_string(val[3]),
Selectors::Attribute::DASHMATCH,
val[1][1]
)
}
| LSQUARE ident_with_namespace PREFIXMATCH IDENT RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_identifier(val[3]),
Selectors::Attribute::PREFIXMATCH,
val[1][1]
)
}
| LSQUARE ident_with_namespace PREFIXMATCH STRING RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_string(val[3]),
Selectors::Attribute::PREFIXMATCH,
val[1][1]
)
}
| LSQUARE ident_with_namespace SUFFIXMATCH IDENT RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_identifier(val[3]),
Selectors::Attribute::SUFFIXMATCH,
val[1][1]
)
}
| LSQUARE ident_with_namespace SUFFIXMATCH STRING RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_string(val[3]),
Selectors::Attribute::SUFFIXMATCH,
val[1][1]
)
}
| LSQUARE ident_with_namespace SUBSTRINGMATCH IDENT RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_identifier(val[3]),
Selectors::Attribute::SUBSTRINGMATCH,
val[1][1]
)
}
| LSQUARE ident_with_namespace SUBSTRINGMATCH STRING RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
interpret_string(val[3]),
Selectors::Attribute::SUBSTRINGMATCH,
val[1][1]
)
}
| LSQUARE ident_with_namespace RSQUARE {
result = Selectors::Attribute.new(
val[1][0],
nil,
Selectors::Attribute::SET,
val[1][1]
)
}
;
pseudo
: COLON IDENT {
result = Selectors::pseudo interpret_identifier(val[1])
}
| COLON COLON IDENT {
result = Selectors::PseudoElement.new(
interpret_identifier(val[2])
)
}
| COLON FUNCTION RPAREN {
result = Selectors::PseudoClass.new(
interpret_identifier(val[1].sub(/\($/, '')),
''
)
}
| COLON FUNCTION IDENT RPAREN {
result = Selectors::PseudoClass.new(
interpret_identifier(val[1].sub(/\($/, '')),
interpret_identifier(val[2])
)
}
| COLON NOT_PSEUDO_CLASS simple_selector RPAREN {
result = Selectors::PseudoClass.new(
'not',
val[2].first.to_s
)
}
| COLON NTH_PSEUDO_CLASS {
result = Selectors::PseudoClass.new(
interpret_identifier(val[1].sub(/\(.*/, '')),
interpret_identifier(val[1].sub(/.*\(/, '').sub(/\).*/, ''))
)
}
| COLON MATCHES_PSEUDO_CLASS simple_selectors RPAREN {
result = Selectors::PseudoClass.new(
val[1].split('(').first.strip,
val[2].join(', ')
)
}
| COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN {
result = Selectors::PseudoElement.new(
interpret_identifier(val[1].sub(/\($/, ''))
)
}
| COLON COLON MOZ_PSEUDO_ELEMENT optional_space any_number_of_idents optional_space RPAREN {
result = Selectors::PseudoElement.new(
interpret_identifier(val[2].sub(/\($/, ''))
)
}
;
any_number_of_idents
:
| multiple_idents
;
multiple_idents
: IDENT
| IDENT COMMA multiple_idents
;
# declarations can be separated by one *or more* semicolons. semi-colons at the start or end of a ruleset are also allowed
one_or_more_semis
: SEMI
| SEMI one_or_more_semis
;
declarations
: declaration one_or_more_semis declarations
| one_or_more_semis declarations
| declaration one_or_more_semis
| declaration
| one_or_more_semis
;
declaration
: declaration_internal { @handler.property val.first }
;
declaration_internal
: property COLON expr prio
{ result = Declaration.new(val.first, val[2], val[3]) }
| property COLON S expr prio
{ result = Declaration.new(val.first, val[3], val[4]) }
| property S COLON expr prio
{ result = Declaration.new(val.first, val[3], val[4]) }
| property S COLON S expr prio
{ result = Declaration.new(val.first, val[4], val[5]) }
;
prio
: IMPORTANT_SYM { result = true }
| { result = false }
;
property
: IDENT { result = interpret_identifier val[0] }
| STAR IDENT { result = interpret_identifier val.join }
| VARIABLE_NAME { result = interpret_identifier val[0] }
;
operator
: COMMA
| SLASH
| EQUAL
;
expr
: term operator expr {
result = [val.first, val.last].flatten
val.last.first.operator = val[1]
}
| term expr { result = val.flatten }
| term { result = val }
;
term
: ident
| ratio
| numeric
| string
| uri
| hexcolor
| calc
| function
| resolution
| VARIABLE_NAME
| uranges
;
function
: function S { result = val.first }
| FUNCTION expr RPAREN {
name = interpret_identifier val.first.sub(/\($/, '')
if name == 'rgb'
result = Terms::Rgb.new(*val[1])
else
result = Terms::Function.new name, val[1]
end
}
| FUNCTION RPAREN {
name = interpret_identifier val.first.sub(/\($/, '')
result = Terms::Function.new name
}
;
function_no_quote
: function_no_quote S { result = val.first }
| FUNCTION_NO_QUOTE {
parts = val.first.split('(')
name = interpret_identifier parts.first
result = Terms::Function.new(name, [Terms::String.new(interpret_string_no_quote(parts.last))])
}
;
uranges
: UNICODE_RANGE COMMA uranges
| UNICODE_RANGE
;
calc
: CALC_SYM calc_sum RPAREN optional_space {
result = Terms::Math.new(val.first.split('(').first, val[1])
}
;
# plus and minus are supposed to have whitespace around them, per http://dev.w3.org/csswg/css-values/#calc-syntax, but the numbers are eating trailing whitespace, so we inject it back in
calc_sum
: calc_product
| calc_product PLUS calc_sum { val.insert(1, ' '); result = val.join('') }
| calc_product MINUS calc_sum { val.insert(1, ' '); result = val.join('') }
;
calc_product
: calc_value
| calc_value optional_space STAR calc_value { result = val.join('') }
| calc_value optional_space SLASH calc_value { result = val.join('') }
;
calc_value
: numeric { result = val.join('') }
| function { result = val.join('') } # for var() variable references
| LPAREN calc_sum RPAREN { result = val.join('') }
;
hexcolor
: hexcolor S { result = val.first }
| HASH { result = Terms::Hash.new val.first.sub(/^#/, '') }
;
uri
: uri S { result = val.first }
| URI { result = Terms::URI.new interpret_uri val.first }
;
string
: string S { result = val.first }
| STRING { result = Terms::String.new interpret_string val.first }
;
numeric
: unary_operator numeric {
result = val[1]
val[1].unary_operator = val.first
}
| NUMBER {
result = Terms::Number.new numeric val.first
}
| PERCENTAGE {
result = Terms::Number.new numeric(val.first), nil, '%'
}
| LENGTH {
unit = val.first.gsub(/[\s\d.]/, '')
result = Terms::Number.new numeric(val.first), nil, unit
}
| ANGLE {
unit = val.first.gsub(/[\s\d.]/, '')
result = Terms::Number.new numeric(val.first), nil, unit
}
| TIME {
unit = val.first.gsub(/[\s\d.]/, '')
result = Terms::Number.new numeric(val.first), nil, unit
}
| FREQ {
unit = val.first.gsub(/[\s\d.]/, '')
result = Terms::Number.new numeric(val.first), nil, unit
}
;
ratio
: RATIO {
result = Terms::Ratio.new(val[0], val[1])
}
;
unary_operator
: MINUS { result = :minus }
| PLUS { result = :plus }
;
ident
: ident S { result = val.first }
| IDENT { result = Terms::Ident.new interpret_identifier val.first }
;
---- inner
def numeric thing
thing = thing.gsub(/[^\d.]/, '')
Integer(thing) rescue Float(thing)
end
def interpret_identifier s
interpret_escapes s
end
def interpret_uri s
interpret_escapes s.match(/^url\((.*)\)$/mui)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
end
def interpret_string_no_quote s
interpret_escapes s.match(/^(.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
end
def interpret_string s
interpret_escapes s.match(/^(['"])((?:\\.|.)*)\1$/mu)[2]
end
def interpret_escapes s
token_exp = /\\(?:([0-9a-fA-F]{1,6}(?:\r\n|\s)?)|(.))/mu
return s.gsub(token_exp) do |escape_sequence|
if !$1.nil?
code = $1.chomp.to_i 16
code = 0xFFFD if code > 0x10FFFF
next [code].pack('U')
end
next '' if $2 == "\n"
next $2
end
end
# override racc's on_error so we can have context in our error messages
def on_error(t, val, vstack)
errcontext = (@ss.pre_match[-10..-1] || @ss.pre_match) +
@ss.matched + @ss.post_match[0..9]
line_number = @ss.pre_match.lines.count
raise ParseError, sprintf("parse error on value %s (%s) " +
"on line %s around \"%s\"",
val.inspect, token_to_str(t) || '?',
line_number, errcontext)
end
def before_pos(val)
# don't include leading whitespace
return current_pos - val.last.length + val.last[/\A\s*/].size
end
def after_pos(val)
# don't include trailing whitespace
return current_pos - val.last[/\s*\z/].size
end
# charpos will work with multibyte strings but is not available until ruby 2
def current_pos
@ss.respond_to?('charpos') ? @ss.charpos : @ss.pos
end

View file

@ -0,0 +1,29 @@
# ? detect digraph bug
class P
token A B C D
rule
target : a b c d
a : A
|
b : B
|
c : C
|
d : D
|
end
---- inner
def parse
do_parse
end
def next_token
[false, '$']
end
---- footer
P.new.parse

118
test/racc/assets/echk.y Normal file
View file

@ -0,0 +1,118 @@
#
# racc tester
#
class Calcp
prechigh
left '*' '/'
left '+' '-'
preclow
convert
NUMBER 'Number'
end
rule
target : exp | /* none */ { result = 0 } ;
exp : exp '+' exp { result += val[2]; a = 'plus' }
| exp '-' exp { result -= val[2]; "string test" }
| exp '*' exp { result *= val[2] }
| exp '/' exp { result /= val[2] }
| '(' { $emb = true } exp ')'
{
raise 'must not happen' unless $emb
result = val[2]
}
| '-' NUMBER { result = -val[1] }
| NUMBER
;
end
----header
class Number ; end
----inner
def parse( src )
@src = src
do_parse
end
def next_token
@src.shift
end
def initialize
@yydebug = true
end
----footer
$parser = Calcp.new
$tidx = 1
def chk( src, ans )
ret = $parser.parse( src )
unless ret == ans then
bug! "test #{$tidx} fail"
end
$tidx += 1
end
chk(
[ [Number, 9],
[false, false],
[false, false] ], 9
)
chk(
[ [Number, 5],
['*', nil],
[Number, 1],
['-', nil],
[Number, 1],
['*', nil],
[Number, 8],
[false, false],
[false, false] ], -3
)
chk(
[ [Number, 5],
['+', nil],
[Number, 2],
['-', nil],
[Number, 5],
['+', nil],
[Number, 2],
['-', nil],
[Number, 5],
[false, false],
[false, false] ], -1
)
chk(
[ ['-', nil],
[Number, 4],
[false, false],
[false, false] ], -4
)
chk(
[ [Number, 7],
['*', nil],
['(', nil],
[Number, 4],
['+', nil],
[Number, 3],
[')', nil],
['-', nil],
[Number, 9],
[false, false],
[false, false] ], 40
)

583
test/racc/assets/edtf.y Normal file
View file

@ -0,0 +1,583 @@
# -*- racc -*-
# Copyright 2011 Sylvester Keil. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of the copyright holder.
class EDTF::Parser
token T Z E X U UNKNOWN OPEN LONGYEAR UNMATCHED DOTS UA PUA
expect 0
rule
edtf : level_0_expression
| level_1_expression
| level_2_expression
;
# ---- Level 0 / ISO 8601 Rules ----
# NB: level 0 intervals are covered by the level 1 interval rules
level_0_expression : date
| date_time
;
date : positive_date
| negative_date
;
positive_date :
year { result = Date.new(val[0]).year_precision! }
| year_month { result = Date.new(*val.flatten).month_precision! }
| year_month_day { result = Date.new(*val.flatten).day_precision! }
;
negative_date : '-' positive_date { result = -val[1] }
date_time : date T time {
result = DateTime.new(val[0].year, val[0].month, val[0].day, *val[2])
result.skip_timezone = (val[2].length == 3)
}
time : base_time
| base_time zone_offset { result = val.flatten }
base_time : hour ':' minute ':' second { result = val.values_at(0, 2, 4) }
| midnight
midnight : '2' '4' ':' '0' '0' ':' '0' '0' { result = [24, 0, 0] }
zone_offset : Z { result = 0 }
| '-' zone_offset_hour { result = -1 * val[1] }
| '+' positive_zone_offset { result = val[1] }
;
positive_zone_offset : zone_offset_hour
| '0' '0' ':' '0' '0' { result = 0 }
;
zone_offset_hour : d01_13 ':' minute { result = Rational(val[0] * 60 + val[2], 1440) }
| '1' '4' ':' '0' '0' { result = Rational(840, 1440) }
| '0' '0' ':' d01_59 { result = Rational(val[3], 1440) }
;
year : digit digit digit digit {
result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
}
month : d01_12
day : d01_31
year_month : year '-' month { result = [val[0], val[2]] }
# We raise an exception if there are two many days for the month, but
# do not consider leap years, as the EDTF BNF did not either.
# NB: an exception will be raised regardless, because the Ruby Date
# implementation calculates leap years.
year_month_day : year_month '-' day {
result = val[0] << val[2]
if result[2] > 31 || (result[2] > 30 && [2,4,6,9,11].include?(result[1])) || (result[2] > 29 && result[1] == 2)
raise ArgumentError, "invalid date (invalid days #{result[2]} for month #{result[1]})"
end
}
hour : d00_23
minute : d00_59
second : d00_59
# Completely covered by level_1_interval
# level_0_interval : date '/' date { result = Interval.new(val[0], val[1]) }
# ---- Level 1 Extension Rules ----
# NB: Uncertain/approximate Dates are covered by the Level 2 rules
level_1_expression : unspecified | level_1_interval | long_year_simple | season
# uncertain_or_approximate_date : date UA { result = uoa(val[0], val[1]) }
unspecified : unspecified_year
{
result = Date.new(val[0][0]).year_precision!
result.unspecified.year[2,2] = val[0][1]
}
| unspecified_month
| unspecified_day
| unspecified_day_and_month
;
unspecified_year :
digit digit digit U
{
result = [val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }, [false,true]]
}
| digit digit U U
{
result = [val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }, [true, true]]
}
unspecified_month : year '-' U U {
result = Date.new(val[0]).unspecified!(:month)
result.precision = :month
}
unspecified_day : year_month '-' U U {
result = Date.new(*val[0]).unspecified!(:day)
}
unspecified_day_and_month : year '-' U U '-' U U {
result = Date.new(val[0]).unspecified!([:day,:month])
}
level_1_interval : level_1_start '/' level_1_end {
result = Interval.new(val[0], val[2])
}
level_1_start : date | partial_uncertain_or_approximate | unspecified | partial_unspecified | UNKNOWN
level_1_end : level_1_start | OPEN
long_year_simple :
LONGYEAR long_year
{
result = Date.new(val[1])
result.precision = :year
}
| LONGYEAR '-' long_year
{
result = Date.new(-1 * val[2])
result.precision = :year
}
;
long_year :
positive_digit digit digit digit digit {
result = val.zip([10000,1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
}
| long_year digit { result = 10 * val[0] + val[1] }
;
season : year '-' season_number ua {
result = Season.new(val[0], val[2])
val[3].each { |ua| result.send(ua) }
}
season_number : '2' '1' { result = 21 }
| '2' '2' { result = 22 }
| '2' '3' { result = 23 }
| '2' '4' { result = 24 }
;
# ---- Level 2 Extension Rules ----
# NB: Level 2 Intervals are covered by the Level 1 Interval rules.
level_2_expression : season_qualified
| partial_uncertain_or_approximate
| partial_unspecified
| choice_list
| inclusive_list
| masked_precision
| date_and_calendar
| long_year_scientific
;
season_qualified : season '^' { result = val[0]; result.qualifier = val[1] }
long_year_scientific :
long_year_simple E integer
{
result = Date.new(val[0].year * 10 ** val[2]).year_precision!
}
| LONGYEAR int1_4 E integer
{
result = Date.new(val[1] * 10 ** val[3]).year_precision!
}
| LONGYEAR '-' int1_4 E integer
{
result = Date.new(-1 * val[2] * 10 ** val[4]).year_precision!
}
;
date_and_calendar : date '^' { result = val[0]; result.calendar = val[1] }
masked_precision :
digit digit digit X
{
d = val[0,3].zip([1000,100,10]).reduce(0) { |s,(a,b)| s += a * b }
result = EDTF::Decade.new(d)
}
| digit digit X X
{
d = val[0,2].zip([1000,100]).reduce(0) { |s,(a,b)| s += a * b }
result = EDTF::Century.new(d)
}
;
choice_list : '[' list ']' { result = val[1].choice! }
inclusive_list : '{' list '}' { result = val[1] }
list : earlier { result = EDTF::Set.new(val[0]).earlier! }
| earlier ',' list_elements ',' later { result = EDTF::Set.new([val[0]] + val[2] + [val[4]]).earlier!.later! }
| earlier ',' list_elements { result = EDTF::Set.new([val[0]] + val[2]).earlier! }
| earlier ',' later { result = EDTF::Set.new([val[0]] + [val[2]]).earlier!.later! }
| list_elements ',' later { result = EDTF::Set.new(val[0] + [val[2]]).later! }
| list_elements { result = EDTF::Set.new(*val[0]) }
| later { result = EDTF::Set.new(val[0]).later! }
;
list_elements : list_element { result = [val[0]].flatten }
| list_elements ',' list_element { result = val[0] + [val[2]].flatten }
;
list_element : atomic
| consecutives
;
atomic : date
| partial_uncertain_or_approximate
| unspecified
;
earlier : DOTS date { result = val[1] }
later : year_month_day DOTS { result = Date.new(*val[0]).year_precision! }
| year_month DOTS { result = Date.new(*val[0]).month_precision! }
| year DOTS { result = Date.new(val[0]).year_precision! }
;
consecutives : year_month_day DOTS year_month_day { result = (Date.new(val[0]).day_precision! .. Date.new(val[2]).day_precision!) }
| year_month DOTS year_month { result = (Date.new(val[0]).month_precision! .. Date.new(val[2]).month_precision!) }
| year DOTS year { result = (Date.new(val[0]).year_precision! .. Date.new(val[2]).year_precision!) }
;
partial_unspecified :
unspecified_year '-' month '-' day
{
result = Date.new(val[0][0], val[2], val[4])
result.unspecified.year[2,2] = val[0][1]
}
| unspecified_year '-' U U '-' day
{
result = Date.new(val[0][0], 1, val[5])
result.unspecified.year[2,2] = val[0][1]
result.unspecified!(:month)
}
| unspecified_year '-' U U '-' U U
{
result = Date.new(val[0][0], 1, 1)
result.unspecified.year[2,2] = val[0][1]
result.unspecified!([:month, :day])
}
| unspecified_year '-' month '-' U U
{
result = Date.new(val[0][0], val[2], 1)
result.unspecified.year[2,2] = val[0][1]
result.unspecified!(:day)
}
| year '-' U U '-' day
{
result = Date.new(val[0], 1, val[5])
result.unspecified!(:month)
}
;
partial_uncertain_or_approximate : pua_base
| '(' pua_base ')' UA { result = uoa(val[1], val[3]) }
pua_base :
pua_year { result = val[0].year_precision! }
| pua_year_month { result = val[0][0].month_precision! }
| pua_year_month_day { result = val[0].day_precision! }
pua_year : year UA { result = uoa(Date.new(val[0]), val[1], :year) }
pua_year_month :
pua_year '-' month ua {
result = [uoa(val[0].change(:month => val[2]), val[3], [:month, :year])]
}
| year '-' month UA {
result = [uoa(Date.new(val[0], val[2]), val[3], [:year, :month])]
}
| year '-(' month ')' UA {
result = [uoa(Date.new(val[0], val[2]), val[4], [:month]), true]
}
| pua_year '-(' month ')' UA {
result = [uoa(val[0].change(:month => val[2]), val[4], [:month]), true]
}
;
pua_year_month_day :
pua_year_month '-' day ua {
result = uoa(val[0][0].change(:day => val[2]), val[3], val[0][1] ? [:day] : nil)
}
| pua_year_month '-(' day ')' UA {
result = uoa(val[0][0].change(:day => val[2]), val[4], [:day])
}
| year '-(' month ')' UA day ua {
result = uoa(uoa(Date.new(val[0], val[2], val[5]), val[4], :month), val[6], :day)
}
| year_month '-' day UA {
result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[3])
}
| year_month '-(' day ')' UA {
result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[4], [:day])
}
| year '-(' month '-' day ')' UA {
result = uoa(Date.new(val[0], val[2], val[4]), val[6], [:month, :day])
}
| year '-(' month '-(' day ')' UA ')' UA {
result = Date.new(val[0], val[2], val[4])
result = uoa(result, val[6], [:day])
result = uoa(result, val[8], [:month, :day])
}
| pua_year '-(' month '-' day ')' UA {
result = val[0].change(:month => val[2], :day => val[4])
result = uoa(result, val[6], [:month, :day])
}
| pua_year '-(' month '-(' day ')' UA ')' UA {
result = val[0].change(:month => val[2], :day => val[4])
result = uoa(result, val[6], [:day])
result = uoa(result, val[8], [:month, :day])
}
# | '(' pua_year '-(' month ')' UA ')' UA '-' day ua {
# result = val[1].change(:month => val[3], :day => val[9])
# result = uoa(result, val[5], [:month])
# result = [uoa(result, val[7], [:year]), true]
# }
;
ua : { result = [] } | UA
# ---- Auxiliary Rules ----
digit : '0' { result = 0 }
| positive_digit
;
positive_digit : '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
d01_12 : '0' positive_digit { result = val[1] }
| '1' '0' { result = 10 }
| '1' '1' { result = 11 }
| '1' '2' { result = 12 }
;
d01_13 : d01_12
| '1' '3' { result = 13 }
;
d01_23 : '0' positive_digit { result = val[1] }
| '1' digit { result = 10 + val[1] }
| '2' '0' { result = 20 }
| '2' '1' { result = 21 }
| '2' '2' { result = 22 }
| '2' '3' { result = 23 }
;
d00_23 : '0' '0'
| d01_23
;
d01_29 : d01_23
| '2' '4' { result = 24 }
| '2' '5' { result = 25 }
| '2' '6' { result = 26 }
| '2' '7' { result = 27 }
| '2' '8' { result = 28 }
| '2' '9' { result = 29 }
;
d01_30 : d01_29
| '3' '0' { result = 30 }
;
d01_31 : d01_30
| '3' '1' { result = 31 }
;
d01_59 : d01_29
| '3' digit { result = 30 + val[1] }
| '4' digit { result = 40 + val[1] }
| '5' digit { result = 50 + val[1] }
;
d00_59 : '0' '0'
| d01_59
;
int1_4 : positive_digit { result = val[0] }
| positive_digit digit { result = 10 * val[0] + val[1] }
| positive_digit digit digit
{
result = val.zip([100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
}
| positive_digit digit digit digit
{
result = val.zip([1000,100,10,1]).reduce(0) { |s,(a,b)| s += a * b }
}
;
integer : positive_digit { result = val[0] }
| integer digit { result = 10 * val[0] + val[1] }
;
---- header
require 'strscan'
---- inner
@defaults = {
:level => 2,
:debug => false
}.freeze
class << self; attr_reader :defaults; end
attr_reader :options
def initialize(options = {})
@options = Parser.defaults.merge(options)
end
def debug?
!!(options[:debug] || ENV['DEBUG'])
end
def parse(input)
parse!(input)
rescue => e
warn e.message if debug?
nil
end
def parse!(input)
@yydebug = debug?
@src = StringScanner.new(input)
do_parse
end
def on_error(tid, value, stack)
raise ArgumentError,
"failed to parse date: unexpected '#{value}' at #{stack.inspect}"
end
def apply_uncertainty(date, uncertainty, scope = nil)
uncertainty.each do |u|
scope.nil? ? date.send(u) : date.send(u, scope)
end
date
end
alias uoa apply_uncertainty
def next_token
case
when @src.eos?
nil
# when @src.scan(/\s+/)
# ignore whitespace
when @src.scan(/\(/)
['(', @src.matched]
# when @src.scan(/\)\?~-/)
# [:PUA, [:uncertain!, :approximate!]]
# when @src.scan(/\)\?-/)
# [:PUA, [:uncertain!]]
# when @src.scan(/\)~-/)
# [:PUA, [:approximate!]]
when @src.scan(/\)/)
[')', @src.matched]
when @src.scan(/\[/)
['[', @src.matched]
when @src.scan(/\]/)
[']', @src.matched]
when @src.scan(/\{/)
['{', @src.matched]
when @src.scan(/\}/)
['}', @src.matched]
when @src.scan(/T/)
[:T, @src.matched]
when @src.scan(/Z/)
[:Z, @src.matched]
when @src.scan(/\?~/)
[:UA, [:uncertain!, :approximate!]]
when @src.scan(/\?/)
[:UA, [:uncertain!]]
when @src.scan(/~/)
[:UA, [:approximate!]]
when @src.scan(/open/i)
[:OPEN, :open]
when @src.scan(/unkn?own/i) # matches 'unkown' typo too
[:UNKNOWN, :unknown]
when @src.scan(/u/)
[:U, @src.matched]
when @src.scan(/x/i)
[:X, @src.matched]
when @src.scan(/y/)
[:LONGYEAR, @src.matched]
when @src.scan(/e/)
[:E, @src.matched]
when @src.scan(/\+/)
['+', @src.matched]
when @src.scan(/-\(/)
['-(', @src.matched]
when @src.scan(/-/)
['-', @src.matched]
when @src.scan(/:/)
[':', @src.matched]
when @src.scan(/\//)
['/', @src.matched]
when @src.scan(/\s*\.\.\s*/)
[:DOTS, '..']
when @src.scan(/\s*,\s*/)
[',', ',']
when @src.scan(/\^\w+/)
['^', @src.matched[1..-1]]
when @src.scan(/\d/)
[@src.matched, @src.matched.to_i]
else @src.scan(/./)
[:UNMATCHED, @src.rest]
end
end
# -*- racc -*-

60
test/racc/assets/err.y Normal file
View file

@ -0,0 +1,60 @@
class ErrTestp
rule
target: lines
;
lines: line
| lines line
;
line: A B C D E
| error E
;
end
---- inner
def initialize
@yydebug = false
@q = [
[:A, 'a'],
# [:B, 'b'],
[:C, 'c'],
[:D, 'd'],
[:E, 'e'],
[:A, 'a'],
[:B, 'b'],
[:C, 'c'],
[:D, 'd'],
[:E, 'e'],
[:A, 'a'],
[:B, 'b'],
# [:C, 'c'],
[:D, 'd'],
[:E, 'e'],
[false, nil]
]
end
def next_token
@q.shift
end
def on_error( t, val, values )
$stderr.puts "error on token '#{val}'(#{t})"
end
def parse
do_parse
end
---- footer
p = ErrTestp.new
p.parse

View file

@ -0,0 +1,35 @@
# Regression test case for the bug discussed here:
# https://github.com/whitequark/parser/issues/93
# In short, a Racc-generated parser could go into an infinite loop when
# attempting error recovery at EOF
class InfiniteLoop
rule
stmts: stmt
| error stmt
stmt: '%' stmt
end
---- inner
def parse
@errors = []
do_parse
end
def next_token
nil
end
def on_error(error_token, error_value, value_stack)
# oh my, an error
@errors << [error_token, error_value]
end
---- footer
InfiniteLoop.new.parse

View file

@ -0,0 +1,7 @@
class E
expect 1
rule
list: inlist inlist
inlist:
| A
end

View file

@ -0,0 +1,4 @@
class T
rule
a: A B C
end

318
test/racc/assets/huia.y Normal file
View file

@ -0,0 +1,318 @@
# Copyright (c) 2014 James Harton
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class Huia::Parser
token
IDENTIFIER EQUAL PLUS MINUS ASTERISK FWD_SLASH COLON FLOAT INTEGER STRING
EXPO INDENT OUTDENT OPAREN CPAREN DOT SIGNATURE NL EOF PIPE COMMA NIL TRUE
FALSE EQUALITY CALL SELF CONSTANT CHAR DOUBLE_TICK_STRING
DOUBLE_TICK_STRING_END INTERPOLATE_START INTERPOLATE_END BOX LSQUARE
RSQUARE FACES LFACE RFACE BANG TILDE RETURN NOT_EQUALITY OR AND GT LT
GTE LTE AT
prechigh
left EXPO
left BANG TILDE
left ASTERISK FWD_SLASH PERCENT
left PLUS MINUS
right EQUAL
preclow
rule
statements: statement
| statements statement { return scope }
statement: expr eol { return scope.append val[0] }
| expr { return scope.append val[0] }
| eol { return scope }
eol: NL | EOF
nlq: NL |
expr: literal
| grouped_expr
| binary_op
| unary_op
| method_call
| constant
| variable
| array
| hash
| return
return: return_expr
| return_nil
return_expr: RETURN expr { return n(:Return, val[1]) }
return_nil: RETURN { return n(:Return, n(:Nil)) }
array: empty_array
| array_list
empty_array: BOX { return n :Array }
array_list: LSQUARE array_items RSQUARE { return val[1] }
array_items: expr { return n :Array, [val[0]] }
| array_items COMMA expr { val[0].append(val[2]); return val[0] }
hash: empty_hash
| hash_list
empty_hash: FACES { return n :Hash }
hash_list: LFACE hash_items RFACE { return val[1] }
hash_items: hash_item { return n :Hash, val[0] }
| hash_items COMMA hash_item { val[0].append(val[2]); return val[0] }
hash_item: expr COLON expr { return n :HashItem, val[0], val[2] }
constant: CONSTANT { return constant val[0] }
indented: indented_w_stmts
| indented_w_expr
| indented_wo_stmts
indented_w_stmts: indent statements outdent { return val[0] }
indented_w_expr: indent expr outdent { return val[0].append(val[1]) }
indented_wo_stmts: indent outdent { return val[0] }
outdent: OUTDENT { return pop_scope }
indent_w_args: indent_pipe indent_args PIPE nlq INDENT { return val[0] }
indent_pipe: PIPE { return push_scope }
indent_wo_args: INDENT { return push_scope }
indent: indent_w_args
| indent_wo_args
indent_args: indent_arg
| indent_args COMMA indent_arg
indent_arg: arg_var { return scope.add_argument val[0] }
| arg_var EQUAL expr { return n :Assignment, val[0], val[2] }
arg_var: IDENTIFIER { return n :Variable, val[0] }
method_call: method_call_on_object
| method_call_on_self
| method_call_on_closure
method_call_on_object: expr DOT call_signature { return n :MethodCall, val[0], val[2] }
| expr DOT IDENTIFIER { return n :MethodCall, val[0], n(:CallSignature, val[2]) }
method_call_on_self: call_signature { return n :MethodCall, scope_instance, val[0] }
method_call_on_closure: AT call_signature { return n :MethodCall, this_closure, val[1] }
| AT IDENTIFIER { return n :MethodCall, this_closure, n(:CallSignature, val[1]) }
call_signature: call_arguments
| call_simple_name
call_simple_name: CALL { return n :CallSignature, val[0] }
call_argument: SIGNATURE call_passed_arg { return n :CallSignature, val[0], [val[1]] }
call_passed_arg: call_passed_simple
| call_passed_indented
call_passed_simple: expr
| expr NL
call_passed_indented: indented
| indented NL
call_arguments: call_argument { return val[0] }
| call_arguments call_argument { return val[0].concat_signature val[1] }
grouped_expr: OPAREN expr CPAREN { return n :Expression, val[1] }
variable: IDENTIFIER { return allocate_local val[0] }
binary_op: assignment
| addition
| subtraction
| multiplication
| division
| exponentiation
| modulo
| equality
| not_equality
| logical_or
| logical_and
| greater_than
| less_than
| greater_or_eq
| less_or_eq
assignment: IDENTIFIER EQUAL expr { return allocate_local_assignment val[0], val[2] }
addition: expr PLUS expr { return binary val[0], val[2], 'plus:' }
subtraction: expr MINUS expr { return binary val[0], val[2], 'minus:' }
multiplication: expr ASTERISK expr { return binary val[0], val[2], 'multiplyBy:' }
division: expr FWD_SLASH expr { return binary val[0], val[2], 'divideBy:' }
exponentiation: expr EXPO expr { return binary val[0], val[2], 'toThePowerOf:' }
modulo: expr PERCENT expr { return binary val[0], val[2], 'moduloOf:' }
equality: expr EQUALITY expr { return binary val[0], val[2], 'isEqualTo:' }
not_equality: expr NOT_EQUALITY expr { return binary val[0], val[2], 'isNotEqualTo:' }
logical_or: expr OR expr { return binary val[0], val[2], 'logicalOr:' }
logical_and: expr AND expr { return binary val[0], val[2], 'logicalAnd:' }
greater_than: expr GT expr { return binary val[0], val[2], 'isGreaterThan:' }
less_than: expr LT expr { return binary val[0], val[2], 'isLessThan:' }
greater_or_eq: expr GTE expr { return binary val[0], val[2], 'isGreaterOrEqualTo:' }
less_or_eq: expr LTE expr { return binary val[0], val[2], 'isLessOrEqualTo:' }
unary_op: unary_not
| unary_plus
| unary_minus
| unary_complement
unary_not: BANG expr { return unary val[1], 'unaryNot' }
unary_plus: PLUS expr { return unary val[1], 'unaryPlus' }
unary_minus: MINUS expr { return unary val[1], 'unaryMinus' }
unary_complement: TILDE expr { return unary val[1], 'unaryComplement' }
literal: integer
| float
| string
| nil
| true
| false
| self
float: FLOAT { return n :Float, val[0] }
integer: INTEGER { return n :Integer, val[0] }
nil: NIL { return n :Nil }
true: TRUE { return n :True }
false: FALSE { return n :False }
self: SELF { return n :Self }
string: STRING { return n :String, val[0] }
| interpolated_string
| empty_string
interpolated_string: DOUBLE_TICK_STRING interpolated_string_contents DOUBLE_TICK_STRING_END { return val[1] }
interpolation: INTERPOLATE_START expr INTERPOLATE_END { return val[1] }
interpolated_string_contents: interpolated_string_chunk { return n :InterpolatedString, val[0] }
| interpolated_string_contents interpolated_string_chunk { val[0].append(val[1]); return val[0] }
interpolated_string_chunk: chars { return val[0] }
| interpolation { return to_string(val[0]) }
empty_string: DOUBLE_TICK_STRING DOUBLE_TICK_STRING_END { return n :String, '' }
chars: CHAR { return n :String, val[0] }
| chars CHAR { val[0].append(val[1]); return val[0] }
end
---- inner
attr_accessor :lexer, :scopes, :state
def initialize lexer
@lexer = lexer
@state = []
@scopes = []
push_scope
end
def ast
@ast ||= do_parse
@scopes.first
end
def on_error t, val, vstack
line = lexer.line
col = lexer.column
message = "Unexpected #{token_to_str t} at #{lexer.filename} line #{line}:#{col}:\n\n"
start = line - 5 > 0 ? line - 5 : 0
i_size = line.to_s.size
(start..(start + 5)).each do |i|
message << sprintf("\t%#{i_size}d: %s\n", i, lexer.get_line(i))
message << "\t#{' ' * i_size} #{'-' * (col - 1)}^\n" if i == line
end
raise SyntaxError, message
end
def next_token
nt = lexer.next_computed_token
# just use a state stack for now, we'll have to do something
# more sophisticated soon.
if nt && nt.first == :state
if nt.last
state.push << nt.last
else
state.pop
end
next_token
else
nt
end
end
def push_scope
new_scope = Huia::AST::Scope.new scope
new_scope.file = lexer.filename
new_scope.line = lexer.line
new_scope.column = lexer.column
scopes.push new_scope
new_scope
end
def pop_scope
scopes.pop
end
def scope
scopes.last
end
def binary left, right, method
node(:MethodCall, left, node(:CallSignature, method, [right]))
end
def unary left, method
node(:MethodCall, left, node(:CallSignature, method))
end
def node type, *args
Huia::AST.const_get(type).new(*args).tap do |n|
n.file = lexer.filename
n.line = lexer.line
n.column = lexer.column
end
end
alias n node
def allocate_local name
node(:Variable, name).tap do |n|
scope.allocate_local n
end
end
def allocate_local_assignment name, value
node(:Assignment, name, value).tap do |n|
scope.allocate_local n
end
end
def this_closure
allocate_local('@')
end
def scope_instance
node(:ScopeInstance, scope)
end
def constant name
return scope_instance if name == 'self'
node(:Constant, name)
end
def to_string expr
node(:MethodCall, expr, node(:CallSignature, 'toString'))
end

102
test/racc/assets/ichk.y Normal file
View file

@ -0,0 +1,102 @@
class Calculator
prechigh
left '*' '/'
left '+' '-'
preclow
convert
NUMBER 'Number'
end
rule
target : exp
| /* none */ { result = 0 }
exp : exp '+' exp { result += val[2]; a = 'plus' }
| exp '-' exp { result -= val[2]; a = "string test" }
| exp '*' exp { result *= val[2] }
| exp '/' exp { result /= val[2] }
| '(' { $emb = true } exp ')'
{
raise 'must not happen' unless $emb
result = val[2]
}
| '-' NUMBER { result = -val[1] }
| NUMBER
----header
class Number
end
----inner
def initialize
@racc_debug_out = $stdout
@yydebug = false
end
def validate(expected, src)
result = parse(src)
unless result == expected
raise "test #{@test_number} fail"
end
@test_number += 1
end
def parse(src)
@src = src
@test_number = 1
yyparse self, :scan
end
def scan(&block)
@src.each(&block)
end
----footer
calc = Calculator.new
calc.validate(9, [[Number, 9], nil])
calc.validate(-3,
[[Number, 5],
['*', '*'],
[Number, 1],
['-', '*'],
[Number, 1],
['*', '*'],
[Number, 8],
nil])
calc.validate(-1,
[[Number, 5],
['+', '+'],
[Number, 2],
['-', '-'],
[Number, 5],
['+', '+'],
[Number, 2],
['-', '-'],
[Number, 5],
nil])
calc.validate(-4,
[['-', 'UMINUS'],
[Number, 4],
nil])
calc.validate(40,
[[Number, 7],
['*', '*'],
['(', '('],
[Number, 4],
['+', '+'],
[Number, 3],
[')', ')'],
['-', '-'],
[Number, 9],
nil])

546
test/racc/assets/intp.y Normal file
View file

@ -0,0 +1,546 @@
#
# intp
#
class Intp::Parser
prechigh
nonassoc UMINUS
left '*' '/'
left '+' '-'
nonassoc EQ
preclow
rule
program : stmt_list
{
result = RootNode.new( val[0] )
}
stmt_list :
{
result = []
}
| stmt_list stmt EOL
{
result.push val[1]
}
| stmt_list EOL
stmt : expr
| assign
| IDENT realprim
{
result = FuncallNode.new( @fname, val[0][0],
val[0][1], [val[1]] )
}
| if_stmt
| while_stmt
| defun
if_stmt : IF stmt THEN EOL stmt_list else_stmt END
{
result = IfNode.new( @fname, val[0][0],
val[1], val[4], val[5] )
}
else_stmt : ELSE EOL stmt_list
{
result = val[2]
}
|
{
result = nil
}
while_stmt: WHILE stmt DO EOL stmt_list END
{
result = WhileNode.new(@fname, val[0][0],
val[1], val[4])
}
defun : DEF IDENT param EOL stmt_list END
{
result = DefNode.new(@fname, val[0][0], val[1][1],
Function.new(@fname, val[0][0], val[2], val[4]))
}
param : '(' name_list ')'
{
result = val[1]
}
| '(' ')'
{
result = []
}
|
{
result = []
}
name_list : IDENT
{
result = [ val[0][1] ]
}
| name_list ',' IDENT
{
result.push val[2][1]
}
assign : IDENT '=' expr
{
result = AssignNode.new(@fname, val[0][0], val[0][1], val[2])
}
expr : expr '+' expr
{
result = FuncallNode.new(@fname, val[0].lineno, '+', [val[0], val[2]])
}
| expr '-' expr
{
result = FuncallNode.new(@fname, val[0].lineno, '-', [val[0], val[2]])
}
| expr '*' expr
{
result = FuncallNode.new(@fname, val[0].lineno, '*', [val[0], val[2]])
}
| expr '/' expr
{
result = FuncallNode.new(@fname, val[0].lineno,
'/', [val[0], val[2]])
}
| expr EQ expr
{
result = FuncallNode.new(@fname, val[0].lineno, '==', [val[0], val[2]])
}
| primary
primary : realprim
| '(' expr ')'
{
result = val[1]
}
| '-' expr =UMINUS
{
result = FuncallNode.new(@fname, val[0][0], '-@', [val[1]])
}
realprim : IDENT
{
result = VarRefNode.new(@fname, val[0][0],
val[0][1])
}
| NUMBER
{
result = LiteralNode.new(@fname, *val[0])
}
| STRING
{
result = StringNode.new(@fname, *val[0])
}
| TRUE
{
result = LiteralNode.new(@fname, *val[0])
}
| FALSE
{
result = LiteralNode.new(@fname, *val[0])
}
| NIL
{
result = LiteralNode.new(@fname, *val[0])
}
| funcall
funcall : IDENT '(' args ')'
{
result = FuncallNode.new(@fname, val[0][0], val[0][1], val[2])
}
| IDENT '(' ')'
{
result = FuncallNode.new(@fname, val[0][0], val[0][1], [])
}
args : expr
{
result = val
}
| args ',' expr
{
result.push val[2]
}
end
---- header
#
# intp/parser.rb
#
---- inner
def initialize
@scope = {}
end
RESERVED = {
'if' => :IF,
'else' => :ELSE,
'while' => :WHILE,
'then' => :THEN,
'do' => :DO,
'def' => :DEF,
'true' => :TRUE,
'false' => :FALSE,
'nil' => :NIL,
'end' => :END
}
RESERVED_V = {
'true' => true,
'false' => false,
'nil' => nil
}
def parse(f, fname)
@q = []
@fname = fname
lineno = 1
f.each do |line|
line.strip!
until line.empty?
case line
when /\A\s+/, /\A\#.*/
;
when /\A[a-zA-Z_]\w*/
word = $&
@q.push [(RESERVED[word] || :IDENT),
[lineno, RESERVED_V.key?(word) ? RESERVED_V[word] : word.intern]]
when /\A\d+/
@q.push [:NUMBER, [lineno, $&.to_i]]
when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/
@q.push [:STRING, [lineno, eval($&)]]
when /\A==/
@q.push [:EQ, [lineno, '==']]
when /\A./
@q.push [$&, [lineno, $&]]
else
raise RuntimeError, 'must not happen'
end
line = $'
end
@q.push [:EOL, [lineno, nil]]
lineno += 1
end
@q.push [false, '$']
do_parse
end
def next_token
@q.shift
end
def on_error(t, v, values)
if v
line = v[0]
v = v[1]
else
line = 'last'
end
raise Racc::ParseError, "#{@fname}:#{line}: syntax error on #{v.inspect}"
end
---- footer
# intp/node.rb
module Intp
class IntpError < StandardError; end
class IntpArgumentError < IntpError; end
class Core
def initialize
@ftab = {}
@obj = Object.new
@stack = []
@stack.push Frame.new '(toplevel)'
end
def frame
@stack[-1]
end
def define_function(fname, node)
raise IntpError, "function #{fname} defined twice" if @ftab.key?(fname)
@ftab[fname] = node
end
def call_function_or(fname, args)
call_intp_function_or(fname, args) {
call_ruby_toplevel_or(fname, args) {
yield
}
}
end
def call_intp_function_or(fname, args)
if func = @ftab[fname]
frame = Frame.new(fname)
@stack.push frame
func.call self, frame, args
@stack.pop
else
yield
end
end
def call_ruby_toplevel_or(fname, args)
if @obj.respond_to? fname, true
@obj.send fname, *args
else
yield
end
end
end
class Frame
def initialize(fname)
@fname = fname
@lvars = {}
end
attr :fname
def lvar?(name)
@lvars.key? name
end
def [](key)
@lvars[key]
end
def []=(key, val)
@lvars[key] = val
end
end
class Node
def initialize(fname, lineno)
@filename = fname
@lineno = lineno
end
attr_reader :filename
attr_reader :lineno
def exec_list(intp, nodes)
v = nil
nodes.each {|i| v = i.evaluate(intp) }
v
end
def intp_error!(msg)
raise IntpError, "in #{filename}:#{lineno}: #{msg}"
end
def inspect
"#{self.class.name}/#{lineno}"
end
end
class RootNode < Node
def initialize(tree)
super nil, nil
@tree = tree
end
def evaluate
exec_list Core.new, @tree
end
end
class DefNode < Node
def initialize(file, lineno, fname, func)
super file, lineno
@funcname = fname
@funcobj = func
end
def evaluate(intp)
intp.define_function @funcname, @funcobj
end
end
class FuncallNode < Node
def initialize(file, lineno, func, args)
super file, lineno
@funcname = func
@args = args
end
def evaluate(intp)
args = @args.map {|i| i.evaluate intp }
begin
intp.call_intp_function_or(@funcname, args) {
if args.empty? or not args[0].respond_to?(@funcname)
intp.call_ruby_toplevel_or(@funcname, args) {
intp_error! "undefined function #{@funcname.id2name}"
}
else
recv = args.shift
recv.send @funcname, *args
end
}
rescue IntpArgumentError, ArgumentError
intp_error! $!.message
end
end
end
class Function < Node
def initialize(file, lineno, params, body)
super file, lineno
@params = params
@body = body
end
def call(intp, frame, args)
unless args.size == @params.size
raise IntpArgumentError,
"wrong # of arg for #{frame.fname}() (#{args.size} for #{@params.size})"
end
args.each_with_index do |v,i|
frame[@params[i]] = v
end
exec_list intp, @body
end
end
class IfNode < Node
def initialize(fname, lineno, cond, tstmt, fstmt)
super fname, lineno
@condition = cond
@tstmt = tstmt
@fstmt = fstmt
end
def evaluate(intp)
if @condition.evaluate(intp)
exec_list intp, @tstmt
else
exec_list intp, @fstmt if @fstmt
end
end
end
class WhileNode < Node
def initialize(fname, lineno, cond, body)
super fname, lineno
@condition = cond
@body = body
end
def evaluate(intp)
while @condition.evaluate(intp)
exec_list intp, @body
end
end
end
class AssignNode < Node
def initialize(fname, lineno, vname, val)
super fname, lineno
@vname = vname
@val = val
end
def evaluate(intp)
intp.frame[@vname] = @val.evaluate(intp)
end
end
class VarRefNode < Node
def initialize(fname, lineno, vname)
super fname, lineno
@vname = vname
end
def evaluate(intp)
if intp.frame.lvar?(@vname)
intp.frame[@vname]
else
intp.call_function_or(@vname, []) {
intp_error! "unknown method or local variable #{@vname.id2name}"
}
end
end
end
class StringNode < Node
def initialize(fname, lineno, str)
super fname, lineno
@val = str
end
def evaluate(intp)
@val.dup
end
end
class LiteralNode < Node
def initialize(fname, lineno, val)
super fname, lineno
@val = val
end
def evaluate(intp)
@val
end
end
end # module Intp
begin
tree = nil
fname = 'src.intp'
File.open(fname) {|f|
tree = Intp::Parser.new.parse(f, fname)
}
tree.evaluate
rescue Racc::ParseError, Intp::IntpError, Errno::ENOENT
raise ####
$stderr.puts "#{File.basename $0}: #{$!}"
exit 1
end

View file

@ -0,0 +1,47 @@
class Journey::Parser
token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR
rule
expressions
: expressions expression { result = Cat.new(val.first, val.last) }
| expression { result = val.first }
| or
;
expression
: terminal
| group
| star
;
group
: LPAREN expressions RPAREN { result = Group.new(val[1]) }
;
or
: expressions OR expression { result = Or.new([val.first, val.last]) }
;
star
: STAR { result = Star.new(Symbol.new(val.last)) }
;
terminal
: symbol
| literal
| slash
| dot
;
slash
: SLASH { result = Slash.new('/') }
;
symbol
: SYMBOL { result = Symbol.new(val.first) }
;
literal
: LITERAL { result = Literal.new(val.first) }
dot
: DOT { result = Dot.new(val.first) }
;
end
---- header
require 'journey/parser_extras'

313
test/racc/assets/liquor.y Normal file
View file

@ -0,0 +1,313 @@
# Copyright (c) 2012-2013 Peter Zotov <whitequark@whitequark.org>
# 2012 Yaroslav Markin <yaroslav@markin.net>
# 2012 Nate Gadgibalaev <nat@xnsv.ru>
#
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class Liquor::Parser
token comma dot endtag ident integer keyword lblock lblock2 lbracket
linterp lparen op_div op_eq op_gt op_geq op_lt op_leq op_minus
op_mod op_mul op_neq op_not op_plus pipe plaintext rblock
rbracket rinterp rparen string tag_ident
prechigh
left dot
nonassoc op_uminus op_not
left op_mul op_div op_mod
left op_plus op_minus
left op_eq op_neq op_lt op_leq op_gt op_geq
left op_and
left op_or
preclow
expect 15
start block
rule
block: /* empty */
{ result = [] }
| plaintext block
{ result = [ val[0], *val[1] ] }
| interp block
{ result = [ val[0], *val[1] ] }
| tag block
{ result = [ val[0], *val[1] ] }
interp:
linterp expr rinterp
{ result = [ :interp, retag(val), val[1] ] }
| linterp filter_chain rinterp
{ result = [ :interp, retag(val), val[1] ] }
primary_expr:
ident
| lparen expr rparen
{ result = [ val[1][0], retag(val), *val[1][2..-1] ] }
expr:
integer
| string
| tuple
| ident function_args
{ result = [ :call, retag(val), val[0], val[1] ] }
| expr lbracket expr rbracket
{ result = [ :index, retag(val), val[0], val[2] ] }
| expr dot ident function_args
{ result = [ :external, retag(val), val[0], val[2], val[3] ] }
| expr dot ident
{ result = [ :external, retag(val), val[0], val[2], nil ] }
| op_minus expr =op_uminus
{ result = [ :uminus, retag(val), val[1] ] }
| op_not expr
{ result = [ :not, retag(val), val[1] ] }
| expr op_mul expr
{ result = [ :mul, retag(val), val[0], val[2] ] }
| expr op_div expr
{ result = [ :div, retag(val), val[0], val[2] ] }
| expr op_mod expr
{ result = [ :mod, retag(val), val[0], val[2] ] }
| expr op_plus expr
{ result = [ :plus, retag(val), val[0], val[2] ] }
| expr op_minus expr
{ result = [ :minus, retag(val), val[0], val[2] ] }
| expr op_eq expr
{ result = [ :eq, retag(val), val[0], val[2] ] }
| expr op_neq expr
{ result = [ :neq, retag(val), val[0], val[2] ] }
| expr op_lt expr
{ result = [ :lt, retag(val), val[0], val[2] ] }
| expr op_leq expr
{ result = [ :leq, retag(val), val[0], val[2] ] }
| expr op_gt expr
{ result = [ :gt, retag(val), val[0], val[2] ] }
| expr op_geq expr
{ result = [ :geq, retag(val), val[0], val[2] ] }
| expr op_and expr
{ result = [ :and, retag(val), val[0], val[2] ] }
| expr op_or expr
{ result = [ :or, retag(val), val[0], val[2] ] }
| primary_expr
tuple:
lbracket tuple_content rbracket
{ result = [ :tuple, retag(val), val[1].compact ] }
tuple_content:
expr comma tuple_content
{ result = [ val[0], *val[2] ] }
| expr
{ result = [ val[0] ] }
| /* empty */
{ result = [ ] }
function_args:
lparen function_args_inside rparen
{ result = [ :args, retag(val), *val[1] ] }
function_args_inside:
expr function_keywords
{ result = [ val[0], val[1][2] ] }
| function_keywords
{ result = [ nil, val[0][2] ] }
function_keywords:
keyword expr function_keywords
{ name = val[0][2].to_sym
tail = val[2][2]
loc = retag([ val[0], val[1] ])
if tail.include? name
@errors << SyntaxError.new("duplicate keyword argument `#{val[0][2]}'",
tail[name][1])
end
hash = {
name => [ val[1][0], loc, *val[1][2..-1] ]
}.merge(tail)
result = [ :keywords, retag([ loc, val[2] ]), hash ]
}
| /* empty */
{ result = [ :keywords, nil, {} ] }
filter_chain:
expr pipe filter_chain_cont
{ result = [ val[0], *val[2] ].
reduce { |tree, node| node[3][2] = tree; node }
}
filter_chain_cont:
filter_call pipe filter_chain_cont
{ result = [ val[0], *val[2] ] }
| filter_call
{ result = [ val[0] ] }
filter_call:
ident function_keywords
{ ident_loc = val[0][1]
empty_args_loc = { line: ident_loc[:line],
start: ident_loc[:end] + 1,
end: ident_loc[:end] + 1, }
result = [ :call, val[0][1], val[0],
[ :args, val[1][1] || empty_args_loc, nil, val[1][2] ] ]
}
tag:
lblock ident expr tag_first_cont
{ result = [ :tag, retag(val), val[1], val[2], *reduce_tag_args(val[3][2]) ] }
| lblock ident tag_first_cont
{ result = [ :tag, retag(val), val[1], nil, *reduce_tag_args(val[2][2]) ] }
# Racc cannot do lookahead across rules. I had to add states
# explicitly to avoid S/R conflicts. You are not expected to
# understand this.
tag_first_cont:
rblock
{ result = [ :cont, retag(val), [] ] }
| keyword tag_first_cont2
{ result = [ :cont, retag(val), [ val[0], *val[1][2] ] ] }
tag_first_cont2:
rblock block lblock2 tag_next_cont
{ result = [ :cont2, val[0][1], [ [:block, val[0][1], val[1] ], *val[3] ] ] }
| expr tag_first_cont
{ result = [ :cont2, retag(val), [ val[0], *val[1][2] ] ] }
tag_next_cont:
endtag rblock
{ result = [] }
| keyword tag_next_cont2
{ result = [ val[0], *val[1] ] }
tag_next_cont2:
rblock block lblock2 tag_next_cont
{ result = [ [:block, val[0][1], val[1] ], *val[3] ] }
| expr keyword tag_next_cont3
{ result = [ val[0], val[1], *val[2] ] }
tag_next_cont3:
rblock block lblock2 tag_next_cont
{ result = [ [:block, val[0][1], val[1] ], *val[3] ] }
| expr tag_next_cont
{ result = [ val[0], *val[1] ] }
---- inner
attr_reader :errors, :ast
def initialize(tags={})
super()
@errors = []
@ast = nil
@tags = tags
end
def success?
@errors.empty?
end
def parse(string, name='(code)')
@errors.clear
@name = name
@ast = nil
begin
@stream = Lexer.lex(string, @name, @tags)
@ast = do_parse
rescue Liquor::SyntaxError => e
@errors << e
end
success?
end
def next_token
tok = @stream.shift
[ tok[0], tok ] if tok
end
TOKEN_NAME_MAP = {
:comma => ',',
:dot => '.',
:lblock => '{%',
:rblock => '%}',
:linterp => '{{',
:rinterp => '}}',
:lbracket => '[',
:rbracket => ']',
:lparen => '(',
:rparen => ')',
:pipe => '|',
:op_not => '!',
:op_mul => '*',
:op_div => '/',
:op_mod => '%',
:op_plus => '+',
:op_minus => '-',
:op_eq => '==',
:op_neq => '!=',
:op_lt => '<',
:op_leq => '<=',
:op_gt => '>',
:op_geq => '>=',
:keyword => 'keyword argument name',
:kwarg => 'keyword argument',
:ident => 'identifier',
}
def on_error(error_token_id, error_token, value_stack)
if token_to_str(error_token_id) == "$end"
raise Liquor::SyntaxError.new("unexpected end of program", {
file: @name
})
else
type, (loc, value) = error_token
type = TOKEN_NAME_MAP[type] || type
raise Liquor::SyntaxError.new("unexpected token `#{type}'", loc)
end
end
def retag(nodes)
loc = nodes.map { |node| node[1] }.compact
first, *, last = loc
return first if last.nil?
{
file: first[:file],
line: first[:line],
start: first[:start],
end: last[:end],
}
end
def reduce_tag_args(list)
list.each_slice(2).reduce([]) { |args, (k, v)|
if v[0] == :block
args << [ :blockarg, retag([ k, v ]), k, v[2] || [] ]
else
args << [ :kwarg, retag([ k, v ]), k, v ]
end
}
end

423
test/racc/assets/machete.y Normal file
View file

@ -0,0 +1,423 @@
# Copyright (c) 2011 SUSE
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
class Machete::Parser
token NIL
token TRUE
token FALSE
token INTEGER
token SYMBOL
token STRING
token REGEXP
token ANY
token EVEN
token ODD
token METHOD_NAME
token CLASS_NAME
start expression
rule
expression : primary
| expression "|" primary {
result = if val[0].is_a?(ChoiceMatcher)
ChoiceMatcher.new(val[0].alternatives << val[2])
else
ChoiceMatcher.new([val[0], val[2]])
end
}
primary : node
| array
| literal
| any
node : CLASS_NAME {
result = NodeMatcher.new(val[0].to_sym)
}
| CLASS_NAME "<" attrs ">" {
result = NodeMatcher.new(val[0].to_sym, val[2])
}
attrs : attr
| attrs "," attr { result = val[0].merge(val[2]) }
attr : method_name "=" expression { result = { val[0].to_sym => val[2] } }
| method_name "^=" SYMBOL {
result = {
val[0].to_sym => SymbolRegexpMatcher.new(
Regexp.new("^" + Regexp.escape(symbol_value(val[2]).to_s))
)
}
}
| method_name "$=" SYMBOL {
result = {
val[0].to_sym => SymbolRegexpMatcher.new(
Regexp.new(Regexp.escape(symbol_value(val[2]).to_s) + "$")
)
}
}
| method_name "*=" SYMBOL {
result = {
val[0].to_sym => SymbolRegexpMatcher.new(
Regexp.new(Regexp.escape(symbol_value(val[2]).to_s))
)
}
}
| method_name "^=" STRING {
result = {
val[0].to_sym => StringRegexpMatcher.new(
Regexp.new("^" + Regexp.escape(string_value(val[2])))
)
}
}
| method_name "$=" STRING {
result = {
val[0].to_sym => StringRegexpMatcher.new(
Regexp.new(Regexp.escape(string_value(val[2])) + "$")
)
}
}
| method_name "*=" STRING {
result = {
val[0].to_sym => StringRegexpMatcher.new(
Regexp.new(Regexp.escape(string_value(val[2])))
)
}
}
| method_name "*=" REGEXP {
result = {
val[0].to_sym => IndifferentRegexpMatcher.new(
Regexp.new(regexp_value(val[2]))
)
}
}
# Hack to overcome the fact that some tokens will lex as simple tokens, not
# METHOD_NAME tokens, and that "reserved words" will lex as separate kinds of
# tokens.
method_name : METHOD_NAME
| NIL
| TRUE
| FALSE
| ANY
| EVEN
| ODD
| "*"
| "+"
| "<"
| ">"
| "^"
| "|"
array : "[" items_opt "]" { result = ArrayMatcher.new(val[1]) }
items_opt : /* empty */ { result = [] }
| items
items : item { result = [val[0]] }
| items "," item { result = val[0] << val[2] }
item : expression
| expression quantifier { result = Quantifier.new(val[0], *val[1]) }
quantifier : "*" { result = [0, nil, 1] }
| "+" { result = [1, nil, 1] }
| "?" { result = [0, 1, 1] }
| "{" INTEGER "}" {
result = [integer_value(val[1]), integer_value(val[1]), 1]
}
| "{" INTEGER "," "}" {
result = [integer_value(val[1]), nil, 1]
}
| "{" "," INTEGER "}" {
result = [0, integer_value(val[2]), 1]
}
| "{" INTEGER "," INTEGER "}" {
result = [integer_value(val[1]), integer_value(val[3]), 1]
}
| "{" EVEN "}" { result = [0, nil, 2] }
| "{" ODD "}" { result = [1, nil, 2] }
literal : NIL { result = LiteralMatcher.new(nil) }
| TRUE { result = LiteralMatcher.new(true) }
| FALSE { result = LiteralMatcher.new(false) }
| INTEGER { result = LiteralMatcher.new(integer_value(val[0])) }
| SYMBOL { result = LiteralMatcher.new(symbol_value(val[0])) }
| STRING { result = LiteralMatcher.new(string_value(val[0])) }
| REGEXP { result = LiteralMatcher.new(regexp_value(val[0])) }
any : ANY { result = AnyMatcher.new }
---- inner
include Matchers
class SyntaxError < StandardError; end
def parse(input)
@input = input
@pos = 0
do_parse
end
private
def integer_value(value)
if value =~ /^0[bB]/
value[2..-1].to_i(2)
elsif value =~ /^0[oO]/
value[2..-1].to_i(8)
elsif value =~ /^0[dD]/
value[2..-1].to_i(10)
elsif value =~ /^0[xX]/
value[2..-1].to_i(16)
elsif value =~ /^0/
value.to_i(8)
else
value.to_i
end
end
def symbol_value(value)
value[1..-1].to_sym
end
def string_value(value)
quote = value[0..0]
if quote == "'"
value[1..-2].gsub("\\\\", "\\").gsub("\\'", "'")
elsif quote == '"'
value[1..-2].
gsub("\\\\", "\\").
gsub('\\"', '"').
gsub("\\n", "\n").
gsub("\\t", "\t").
gsub("\\r", "\r").
gsub("\\f", "\f").
gsub("\\v", "\v").
gsub("\\a", "\a").
gsub("\\e", "\e").
gsub("\\b", "\b").
gsub("\\s", "\s").
gsub(/\\([0-7]{1,3})/) { $1.to_i(8).chr }.
gsub(/\\x([0-9a-fA-F]{1,2})/) { $1.to_i(16).chr }
else
raise "Unknown quote: #{quote.inspect}."
end
end
REGEXP_OPTIONS = {
'i' => Regexp::IGNORECASE,
'm' => Regexp::MULTILINE,
'x' => Regexp::EXTENDED
}
def regexp_value(value)
/\A\/(.*)\/([imx]*)\z/ =~ value
pattern, options = $1, $2
Regexp.new(pattern, options.chars.map { |ch| REGEXP_OPTIONS[ch] }.inject(:|))
end
# "^" needs to be here because if it were among operators recognized by
# METHOD_NAME, "^=" would be recognized as two tokens.
SIMPLE_TOKENS = [
"|",
"<",
">",
",",
"=",
"^=",
"^",
"$=",
"[",
"]",
"*=",
"*",
"+",
"?",
"{",
"}"
]
COMPLEX_TOKENS = [
[:NIL, /^nil/],
[:TRUE, /^true/],
[:FALSE, /^false/],
# INTEGER needs to be before METHOD_NAME, otherwise e.g. "+1" would be
# recognized as two tokens.
[
:INTEGER,
/^
[+-]? # sign
(
0[bB][01]+(_[01]+)* # binary (prefixed)
|
0[oO][0-7]+(_[0-7]+)* # octal (prefixed)
|
0[dD]\d+(_\d+)* # decimal (prefixed)
|
0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)* # hexadecimal (prefixed)
|
0[0-7]*(_[0-7]+)* # octal (unprefixed)
|
[1-9]\d*(_\d+)* # decimal (unprefixed)
)
/x
],
[
:SYMBOL,
/^
:
(
# class name
[A-Z][a-zA-Z0-9_]*
|
# regular method name
[a-z_][a-zA-Z0-9_]*[?!=]?
|
# instance variable name
@[a-zA-Z_][a-zA-Z0-9_]*
|
# class variable name
@@[a-zA-Z_][a-zA-Z0-9_]*
|
# operator (sorted by length, then alphabetically)
(<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&*+\-\/<>^`|~])
)
/x
],
[
:STRING,
/^
(
' # sinqle-quoted string
(
\\[\\'] # escape
|
[^'] # regular character
)*
'
|
" # double-quoted string
(
\\ # escape
(
[\\"ntrfvaebs] # one-character escape
|
[0-7]{1,3} # octal number escape
|
x[0-9a-fA-F]{1,2} # hexadecimal number escape
)
|
[^"] # regular character
)*
"
)
/x
],
[
:REGEXP,
/^
\/
(
\\ # escape
(
[\\\/ntrfvaebs\(\)\[\]\{\}\-\.\?\*\+\|\^\$] # one-character escape
|
[0-7]{2,3} # octal number escape
|
x[0-9a-fA-F]{1,2} # hexadecimal number escape
)
|
[^\/] # regular character
)*
\/
[imx]*
/x
],
# ANY, EVEN and ODD need to be before METHOD_NAME, otherwise they would be
# recognized as method names.
[:ANY, /^any/],
[:EVEN, /^even/],
[:ODD, /^odd/],
# We exclude "*", "+", "<", ">", "^" and "|" from method names since they are
# lexed as simple tokens. This is because they have also other meanings in
# Machette patterns beside Ruby method names.
[
:METHOD_NAME,
/^
(
# regular name
[a-z_][a-zA-Z0-9_]*[?!=]?
|
# operator (sorted by length, then alphabetically)
(<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&\-\/`~])
)
/x
],
[:CLASS_NAME, /^[A-Z][a-zA-Z0-9_]*/]
]
def next_token
skip_whitespace
return false if remaining_input.empty?
# Complex tokens need to be before simple tokens, otherwise e.g. "<<" would be
# recognized as two tokens.
COMPLEX_TOKENS.each do |type, regexp|
if remaining_input =~ regexp
@pos += $&.length
return [type, $&]
end
end
SIMPLE_TOKENS.each do |token|
if remaining_input[0...token.length] == token
@pos += token.length
return [token, token]
end
end
raise SyntaxError, "Unexpected character: #{remaining_input[0..0].inspect}."
end
def skip_whitespace
if remaining_input =~ /\A^[ \t\r\n]+/
@pos += $&.length
end
end
def remaining_input
@input[@pos..-1]
end
def on_error(error_token_id, error_value, value_stack)
raise SyntaxError, "Unexpected token: #{error_value.inspect}."
end

2197
test/racc/assets/macruby.y Normal file

File diff suppressed because it is too large Load diff

437
test/racc/assets/mailp.y Normal file
View file

@ -0,0 +1,437 @@
#
# mailp for test
#
class Testp
rule
content : DateH datetime { @field.date = val[1] }
| RecvH received
| RetpathH returnpath
| MaddrH addrs { @field.addrs.replace val[1] }
| SaddrH addr { @field.addr = val[1] }
| MmboxH mboxes { @field.addrs.replace val[1] }
| SmboxH mbox { @field.addr = val[1] }
| MsgidH msgid { @field.msgid = val[1] }
| KeyH keys { @field.keys.replace val[1] }
| EncH enc
| VersionH version
| CTypeH ctype
| CEncodingH cencode
| CDispositionH cdisp
| Mbox mbox
{
mb = val[1]
@field.phrase = mb.phrase
@field.setroute mb.route
@field.local = mb.local
@field.domain = mb.domain
}
| Spec spec
{
mb = val[1]
@field.local = mb.local
@field.domain = mb.domain
}
;
datetime : day DIGIT ATOM DIGIT hour zone
# 0 1 2 3 4 5
# day month year
{
t = Time.gm( val[3].to_i, val[2], val[1].to_i, 0, 0, 0 )
result = (t + val[4] - val[5]).localtime
}
;
day : /* none */
| ATOM ','
;
hour : DIGIT ':' DIGIT
{
result = (result.to_i * 60 * 60) + (val[2].to_i * 60)
}
| DIGIT ':' DIGIT ':' DIGIT
{
result = (result.to_i * 60 * 60) +
(val[2].to_i * 60)
+ val[4].to_i
}
;
zone : ATOM
{
result = ::TMail.zonestr2i( val[0] ) * 60
}
;
received : from by via with id for recvdatetime
;
from : /* none */
| FROM domain
{
@field.from = Address.join( val[1] )
}
| FROM domain '@' domain
{
@field.from = Address.join( val[3] )
}
| FROM domain DOMLIT
{
@field.from = Address.join( val[1] )
}
;
by : /* none */
| BY domain
{
@field.by = Address.join( val[1] )
}
;
via : /* none */
| VIA ATOM
{
@field.via = val[1]
}
;
with : /* none */
| WITH ATOM
{
@field.with.push val[1]
}
;
id : /* none */
| ID msgid
{
@field.msgid = val[1]
}
| ID ATOM
{
@field.msgid = val[1]
}
;
for : /* none */
| FOR addr
{
@field.for_ = val[1].address
}
;
recvdatetime
: /* none */
| ';' datetime
{
@field.date = val[1]
}
;
returnpath: '<' '>'
| routeaddr
{
@field.route.replace result.route
@field.addr = result.addr
}
;
addrs : addr { result = val }
| addrs ',' addr { result.push val[2] }
;
addr : mbox
| group
;
mboxes : mbox
{
result = val
}
| mboxes ',' mbox
{
result.push val[2]
}
;
mbox : spec
| routeaddr
| phrase routeaddr
{
val[1].phrase = HFdecoder.decode( result )
result = val[1]
}
;
group : phrase ':' mboxes ';'
{
result = AddressGroup.new( result, val[2] )
}
# | phrase ':' ';' { result = AddressGroup.new( result ) }
;
routeaddr : '<' route spec '>'
{
result = val[2]
result.route = val[1]
}
| '<' spec '>'
{
result = val[1]
}
;
route : at_domains ':'
;
at_domains: '@' domain { result = [ val[1] ] }
| at_domains ',' '@' domain { result.push val[3] }
;
spec : local '@' domain { result = Address.new( val[0], val[2] ) }
| local { result = Address.new( result, nil ) }
;
local : word { result = val }
| local '.' word { result.push val[2] }
;
domain : domword { result = val }
| domain '.' domword { result.push val[2] }
;
domword : atom
| DOMLIT
| DIGIT
;
msgid : '<' spec '>'
{
val[1] = val[1].addr
result = val.join('')
}
;
phrase : word
| phrase word { result << ' ' << val[1] }
;
word : atom
| QUOTED
| DIGIT
;
keys : phrase
| keys ',' phrase
;
enc : word
{
@field.encrypter = val[0]
}
| word word
{
@field.encrypter = val[0]
@field.keyword = val[1]
}
;
version : DIGIT '.' DIGIT
{
@field.major = val[0].to_i
@field.minor = val[2].to_i
}
;
ctype : TOKEN '/' TOKEN params
{
@field.main = val[0]
@field.sub = val[2]
}
| TOKEN params
{
@field.main = val[0]
@field.sub = ''
}
;
params : /* none */
| params ';' TOKEN '=' value
{
@field.params[ val[2].downcase ] = val[4]
}
;
value : TOKEN
| QUOTED
;
cencode : TOKEN
{
@field.encoding = val[0]
}
;
cdisp : TOKEN disp_params
{
@field.disposition = val[0]
}
;
disp_params
: /* none */
| disp_params ';' disp_param
;
disp_param: /* none */
| TOKEN '=' value
{
@field.params[ val[0].downcase ] = val[2]
}
;
atom : ATOM
| FROM
| BY
| VIA
| WITH
| ID
| FOR
;
end
---- header
#
# mailp for test
#
require 'tmail/mails'
module TMail
---- inner
MAILP_DEBUG = false
def initialize
self.debug = MAILP_DEBUG
end
def debug=( flag )
@yydebug = flag && Racc_debug_parser
@scanner_debug = flag
end
def debug
@yydebug
end
def Mailp.parse( str, obj, ident )
new.parse( str, obj, ident )
end
NATIVE_ROUTINE = {
'TMail::MsgidH' => :msgid_parse,
'TMail::RefH' => :refs_parse
}
def parse( str, obj, ident )
return if /\A\s*\z/ === str
@field = obj
if mid = NATIVE_ROUTINE[ obj.type.name ] then
send mid, str
else
unless ident then
ident = obj.type.name.split('::')[-1].to_s
cmt = []
obj.comments.replace cmt
else
cmt = nil
end
@scanner = MailScanner.new( str, ident, cmt )
@scanner.debug = @scanner_debug
@first = [ ident.intern, ident ]
@pass_array = [nil, nil]
do_parse
end
end
private
def next_token
if @first then
ret = @first
@first = nil
ret
else
@scanner.scan @pass_array
end
end
def on_error( tok, val, vstack )
raise ParseError,
"\nparse error in '#{@field.name}' header, on token #{val.inspect}"
end
def refs_parse( str )
arr = []
while mdata = ::TMail::MSGID.match( str ) do
str = mdata.post_match
pre = mdata.pre_match
pre.strip!
proc_phrase pre, arr unless pre.empty?
arr.push mdata.to_s
end
str.strip!
proc_phrase str, arr if not pre or pre.empty?
@field.refs.replace arr
end
def proc_phrase( str, arr )
while mdata = /"([^\\]*(?:\\.[^"\\]*)*)"/.match( str ) do
str = mdata.post_match
pre = mdata.pre_match
pre.strip!
arr.push pre unless pre.empty?
arr.push mdata[1]
end
str.strip!
arr.push unless str.empty?
end
def msgid_parse( str )
if mdata = ::TMail::MSGID.match( str ) then
@field.msgid = mdata.to_s
else
raise ParseError, "wrong Message-ID format: #{str}"
end
end
---- footer
end # module TMail
mp = TMail::Testp.new
mp.parse

View file

@ -0,0 +1,599 @@
# Copyright (c) 2006 Pluron Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# The parser for the MediaWiki language.
#
# Usage together with a lexer:
# inputFile = File.new("data/input1", "r")
# input = inputFile.read
# parser = MediaWikiParser.new
# parser.lexer = MediaWikiLexer.new
# parser.parse(input)
class MediaWikiParser
token TEXT BOLD_START BOLD_END ITALIC_START ITALIC_END LINK_START LINK_END LINKSEP
INTLINK_START INTLINK_END INTLINKSEP RESOURCESEP CHAR_ENT
PRE_START PRE_END PREINDENT_START PREINDENT_END
SECTION_START SECTION_END HLINE SIGNATURE_NAME SIGNATURE_DATE SIGNATURE_FULL
PARA_START PARA_END UL_START UL_END OL_START OL_END LI_START LI_END
DL_START DL_END DT_START DT_END DD_START DD_END TAG_START TAG_END ATTR_NAME ATTR_VALUE
TABLE_START TABLE_END ROW_START ROW_END HEAD_START HEAD_END CELL_START CELL_END
KEYWORD TEMPLATE_START TEMPLATE_END CATEGORY PASTE_START PASTE_END
rule
wiki:
repeated_contents
{
@nodes.push WikiAST.new(0, @wiki_ast_length)
#@nodes.last.children.insert(0, val[0])
#puts val[0]
@nodes.last.children += val[0]
}
;
contents:
text
{
result = val[0]
}
| bulleted_list
{
result = val[0]
}
| numbered_list
{
result = val[0]
}
| dictionary_list
{
list = ListAST.new(@ast_index, @ast_length)
list.list_type = :Dictionary
list.children = val[0]
result = list
}
| preformatted
{
result = val[0]
}
| section
{
result = val[0]
}
| tag
{
result = val[0]
}
| template
{
result = val[0]
}
| KEYWORD
{
k = KeywordAST.new(@ast_index, @ast_length)
k.text = val[0]
result = k
}
| PARA_START para_contents PARA_END
{
p = ParagraphAST.new(@ast_index, @ast_length)
p.children = val[1]
result = p
}
| LINK_START link_contents LINK_END
{
l = LinkAST.new(@ast_index, @ast_length)
l.link_type = val[0]
l.url = val[1][0]
l.children += val[1][1..-1] if val[1].length > 1
result = l
}
| PASTE_START para_contents PASTE_END
{
p = PasteAST.new(@ast_index, @ast_length)
p.children = val[1]
result = p
}
| INTLINK_START TEXT RESOURCESEP TEXT reslink_repeated_contents INTLINK_END
{
l = ResourceLinkAST.new(@ast_index, @ast_length)
l.prefix = val[1]
l.locator = val[3]
l.children = val[4] unless val[4].nil? or val[4].empty?
result = l
}
| INTLINK_START TEXT intlink_repeated_contents INTLINK_END
{
l = InternalLinkAST.new(@ast_index, @ast_length)
l.locator = val[1]
l.children = val[2] unless val[2].nil? or val[2].empty?
result = l
}
| INTLINK_START CATEGORY TEXT cat_sort_contents INTLINK_END
{
l = CategoryAST.new(@ast_index, @ast_length)
l.locator = val[2]
l.sort_as = val[3]
result = l
}
| INTLINK_START RESOURCESEP CATEGORY TEXT intlink_repeated_contents INTLINK_END
{
l = CategoryLinkAST.new(@ast_index, @ast_length)
l.locator = val[3]
l.children = val[4] unless val[4].nil? or val[4].empty?
result = l
}
| table
;
para_contents:
{
result = nil
}
| repeated_contents
{
result = val[0]
}
;
tag:
TAG_START tag_attributes TAG_END
{
if val[0] != val[2]
raise Racc::ParseError.new("XHTML end tag #{val[2]} does not match start tag #{val[0]}")
end
elem = ElementAST.new(@ast_index, @ast_length)
elem.name = val[0]
elem.attributes = val[1]
result = elem
}
| TAG_START tag_attributes repeated_contents TAG_END
{
if val[0] != val[3]
raise Racc::ParseError.new("XHTML end tag #{val[3]} does not match start tag #{val[0]}")
end
elem = ElementAST.new(@ast_index, @ast_length)
elem.name = val[0]
elem.attributes = val[1]
elem.children += val[2]
result = elem
}
;
tag_attributes:
{
result = nil
}
| ATTR_NAME tag_attributes
{
attr_map = val[2] ? val[2] : {}
attr_map[val[0]] = true
result = attr_map
}
| ATTR_NAME ATTR_VALUE tag_attributes
{
attr_map = val[2] ? val[2] : {}
attr_map[val[0]] = val[1]
result = attr_map
}
;
link_contents:
TEXT
{
result = val
}
| TEXT LINKSEP link_repeated_contents
{
result = [val[0]]
result += val[2]
}
;
link_repeated_contents:
repeated_contents
{
result = val[0]
}
| repeated_contents LINKSEP link_repeated_contents
{
result = val[0]
result += val[2] if val[2]
}
;
intlink_repeated_contents:
{
result = nil
}
| INTLINKSEP repeated_contents
{
result = val[1]
}
;
cat_sort_contents:
{
result = nil
}
| INTLINKSEP TEXT
{
result = val[1]
}
;
reslink_repeated_contents:
{
result = nil
}
| INTLINKSEP reslink_repeated_contents
{
result = val[1]
}
| INTLINKSEP repeated_contents reslink_repeated_contents
{
i = InternalLinkItemAST.new(@ast_index, @ast_length)
i.children = val[1]
result = [i]
result += val[2] if val[2]
}
;
repeated_contents: contents
{
result = []
result << val[0]
}
| repeated_contents contents
{
result = []
result += val[0]
result << val[1]
}
;
text: element
{
p = TextAST.new(@ast_index, @ast_length)
p.formatting = val[0][0]
p.contents = val[0][1]
result = p
}
| formatted_element
{
result = val[0]
}
;
table:
TABLE_START table_contents TABLE_END
{
table = TableAST.new(@ast_index, @ast_length)
table.children = val[1] unless val[1].nil? or val[1].empty?
result = table
}
| TABLE_START TEXT table_contents TABLE_END
{
table = TableAST.new(@ast_index, @ast_length)
table.options = val[1]
table.children = val[2] unless val[2].nil? or val[2].empty?
result = table
}
table_contents:
{
result = nil
}
| ROW_START row_contents ROW_END table_contents
{
row = TableRowAST.new(@ast_index, @ast_length)
row.children = val[1] unless val[1].nil? or val[1].empty?
result = [row]
result += val[3] unless val[3].nil? or val[3].empty?
}
| ROW_START TEXT row_contents ROW_END table_contents
{
row = TableRowAST.new(@ast_index, @ast_length)
row.children = val[2] unless val[2].nil? or val[2].empty?
row.options = val[1]
result = [row]
result += val[4] unless val[4].nil? or val[4].empty?
}
row_contents:
{
result = nil
}
| HEAD_START HEAD_END row_contents
{
cell = TableCellAST.new(@ast_index, @ast_length)
cell.type = :head
result = [cell]
result += val[2] unless val[2].nil? or val[2].empty?
}
| HEAD_START repeated_contents HEAD_END row_contents
{
cell = TableCellAST.new(@ast_index, @ast_length)
cell.children = val[1] unless val[1].nil? or val[1].empty?
cell.type = :head
result = [cell]
result += val[3] unless val[3].nil? or val[3].empty?
}
| CELL_START CELL_END row_contents
{
cell = TableCellAST.new(@ast_index, @ast_length)
cell.type = :body
result = [cell]
result += val[2] unless val[2].nil? or val[2].empty?
}
| CELL_START repeated_contents CELL_END row_contents
{
if val[2] == 'attributes'
result = []
else
cell = TableCellAST.new(@ast_index, @ast_length)
cell.children = val[1] unless val[1].nil? or val[1].empty?
cell.type = :body
result = [cell]
end
result += val[3] unless val[3].nil? or val[3].empty?
if val[2] == 'attributes' and val[3] and val[3].first.class == TableCellAST
val[3].first.attributes = val[1]
end
result
}
element:
TEXT
{ return [:None, val[0]] }
| HLINE
{ return [:HLine, val[0]] }
| CHAR_ENT
{ return [:CharacterEntity, val[0]] }
| SIGNATURE_DATE
{ return [:SignatureDate, val[0]] }
| SIGNATURE_NAME
{ return [:SignatureName, val[0]] }
| SIGNATURE_FULL
{ return [:SignatureFull, val[0]] }
;
formatted_element:
BOLD_START BOLD_END
{
result = FormattedAST.new(@ast_index, @ast_length)
result.formatting = :Bold
result
}
| ITALIC_START ITALIC_END
{
result = FormattedAST.new(@ast_index, @ast_length)
result.formatting = :Italic
result
}
| BOLD_START repeated_contents BOLD_END
{
p = FormattedAST.new(@ast_index, @ast_length)
p.formatting = :Bold
p.children += val[1]
result = p
}
| ITALIC_START repeated_contents ITALIC_END
{
p = FormattedAST.new(@ast_index, @ast_length)
p.formatting = :Italic
p.children += val[1]
result = p
}
;
bulleted_list: UL_START list_item list_contents UL_END
{
list = ListAST.new(@ast_index, @ast_length)
list.list_type = :Bulleted
list.children << val[1]
list.children += val[2]
result = list
}
;
numbered_list: OL_START list_item list_contents OL_END
{
list = ListAST.new(@ast_index, @ast_length)
list.list_type = :Numbered
list.children << val[1]
list.children += val[2]
result = list
}
;
list_contents:
{ result = [] }
list_item list_contents
{
result << val[1]
result += val[2]
}
|
{ result = [] }
;
list_item:
LI_START LI_END
{
result = ListItemAST.new(@ast_index, @ast_length)
}
| LI_START repeated_contents LI_END
{
li = ListItemAST.new(@ast_index, @ast_length)
li.children += val[1]
result = li
}
;
dictionary_list:
DL_START dictionary_term dictionary_contents DL_END
{
result = [val[1]]
result += val[2]
}
| DL_START dictionary_contents DL_END
{
result = val[1]
}
;
dictionary_term:
DT_START DT_END
{
result = ListTermAST.new(@ast_index, @ast_length)
}
| DT_START repeated_contents DT_END
{
term = ListTermAST.new(@ast_index, @ast_length)
term.children += val[1]
result = term
}
dictionary_contents:
dictionary_definition dictionary_contents
{
result = [val[0]]
result += val[1] if val[1]
}
|
{
result = []
}
dictionary_definition:
DD_START DD_END
{
result = ListDefinitionAST.new(@ast_index, @ast_length)
}
| DD_START repeated_contents DD_END
{
term = ListDefinitionAST.new(@ast_index, @ast_length)
term.children += val[1]
result = term
}
preformatted: PRE_START repeated_contents PRE_END
{
p = PreformattedAST.new(@ast_index, @ast_length)
p.children += val[1]
result = p
}
| PREINDENT_START repeated_contents PREINDENT_END
{
p = PreformattedAST.new(@ast_index, @ast_length)
p.indented = true
p.children += val[1]
result = p
}
;
section: SECTION_START repeated_contents SECTION_END
{ result = [val[1], val[0].length]
s = SectionAST.new(@ast_index, @ast_length)
s.children = val[1]
s.level = val[0].length
result = s
}
;
template: TEMPLATE_START TEXT template_parameters TEMPLATE_END
{
t = TemplateAST.new(@ast_index, @ast_length)
t.template_name = val[1]
t.children = val[2] unless val[2].nil? or val[2].empty?
result = t
}
;
template_parameters:
{
result = nil
}
| INTLINKSEP TEXT template_parameters
{
p = TemplateParameterAST.new(@ast_index, @ast_length)
p.parameter_value = val[1]
result = [p]
result += val[2] if val[2]
}
| INTLINKSEP template template_parameters
{
p = TemplateParameterAST.new(@ast_index, @ast_length)
p.children << val[1]
result = [p]
result += val[2] if val[2]
}
;
end
---- header ----
require 'mediacloth/mediawikiast'
---- inner ----
attr_accessor :lexer
def initialize
@nodes = []
@context = []
@wiki_ast_length = 0
super
end
#Tokenizes input string and parses it.
def parse(input)
@yydebug=true
lexer.tokenize(input)
do_parse
return @nodes.last
end
#Asks the lexer to return the next token.
def next_token
token = @lexer.lex
if token[0].to_s.upcase.include? "_START"
@context << token[2..3]
elsif token[0].to_s.upcase.include? "_END"
@ast_index = @context.last[0]
@ast_length = token[2] + token[3] - @context.last[0]
@context.pop
else
@ast_index = token[2]
@ast_length = token[3]
end
@wiki_ast_length += token[3]
return token[0..1]
end

649
test/racc/assets/mof.y Normal file
View file

@ -0,0 +1,649 @@
# Distributed under the Ruby license
# See http://www.ruby-lang.org/en/LICENSE.txt for the full license text
# Copyright (c) 2010 Klaus Kämpf <kkaempf@suse.de>
/*
* According to appendix A of
* http://www.dmtf.org/standards/cim/cim_spec_v22
*/
class MOF::Parser
prechigh
/* nonassoc UMINUS */
left '*' '/'
left '+' '-'
preclow
token PRAGMA INCLUDE IDENTIFIER CLASS ASSOCIATION INDICATION
AMENDED ENABLEOVERRIDE DISABLEOVERRIDE RESTRICTED TOSUBCLASS TOINSTANCE
TRANSLATABLE QUALIFIER SCOPE SCHEMA PROPERTY REFERENCE
METHOD PARAMETER FLAVOR INSTANCE
AS REF ANY OF
DT_VOID
DT_UINT8 DT_SINT8 DT_UINT16 DT_SINT16 DT_UINT32 DT_SINT32
DT_UINT64 DT_SINT64 DT_REAL32 DT_REAL64 DT_CHAR16 DT_STR
DT_BOOLEAN DT_DATETIME
positiveDecimalValue
stringValue
realValue
charValue
booleanValue
nullValue
binaryValue
octalValue
decimalValue
hexValue
rule
/* Returns a Hash of filename and MofResult */
mofSpecification
: /* empty */
{ result = Hash.new }
| mofProduction
{ result = { @name => @result } }
| mofSpecification mofProduction
{ result = val[0]
result[@name] = @result
}
;
mofProduction
: compilerDirective
| classDeclaration
{ #puts "Class '#{val[0].name}'"
@result.classes << val[0]
}
| qualifierDeclaration
{ @result.qualifiers << val[0]
@qualifiers[val[0].name.downcase] = val[0]
}
| instanceDeclaration
{ @result.instances << val[0] }
;
/***
* compilerDirective
*
*/
compilerDirective
: "#" PRAGMA INCLUDE pragmaParameters_opt
{ raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#pragma include'") unless val[3]
open val[3], :pragma
}
| "#" PRAGMA pragmaName pragmaParameters_opt
| "#" INCLUDE pragmaParameters_opt
{ raise StyleError.new(@name,@lineno,@line,"Use '#pragma include' instead of '#include'") unless @style == :wmi
raise MOF::Helper::Error.new(@name,@lineno,@line,"Missing filename after '#include'") unless val[2]
open val[2], :pragma
}
;
pragmaName
: IDENTIFIER
;
pragmaParameters_opt
: /* empty */
{ raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi }
| "(" pragmaParameterValues ")"
{ result = val[1] }
;
pragmaParameterValues
: pragmaParameterValue
| pragmaParameterValues "," pragmaParameterValue
;
pragmaParameterValue
: string
| integerValue
{ raise StyleError.new(@name,@lineno,@line,"#pragma parameter missing") unless @style == :wmi }
| IDENTIFIER
;
/***
* classDeclaration
*
*/
classDeclaration
: qualifierList_opt CLASS className alias_opt superClass_opt "{" classFeatures "}" ";"
{ qualifiers = val[0]
features = val[6]
# FIXME: features must not include references
result = CIM::Class.new(val[2],qualifiers,val[3],val[4],features)
}
;
classFeatures
: /* empty */
{ result = [] }
| classFeatures classFeature
{ result = val[0] << val[1] }
;
classFeature
: propertyDeclaration
| methodDeclaration
| referenceDeclaration /* must have association qualifier */
;
qualifierList_opt
: /* empty */
| qualifierList
{ result = CIM::QualifierSet.new val[0] }
;
qualifierList
: "[" qualifier qualifiers "]"
{ result = val[2]
result.unshift val[1] if val[1] }
;
qualifiers
: /* empty */
{ result = [] }
| qualifiers "," qualifier
{ result = val[0]
result << val[2] if val[2]
}
;
qualifier
: qualifierName qualifierParameter_opt flavor_opt
{ # Get qualifier decl
qualifier = case val[0]
when CIM::Qualifier then val[0].definition
when CIM::QualifierDeclaration then val[0]
when String then @qualifiers[val[0].downcase]
else
nil
end
raise MOF::Helper::Error.new(@name,@lineno,@line,"'#{val[0]}' is not a valid qualifier") unless qualifier
value = val[1]
raise MOF::Helper::Error.new(@name,@lineno,@line,"#{value.inspect} does not match qualifier type '#{qualifier.type}'") unless qualifier.type.matches?(value)||@style == :wmi
# Don't propagate a boolean 'false'
if qualifier.type == :boolean && value == false
result = nil
else
result = CIM::Qualifier.new(qualifier,value,val[2])
end
}
;
flavor_opt
: /* empty */
| ":" flavor
{ result = CIM::QualifierFlavors.new val[1] }
;
qualifierParameter_opt
: /* empty */
| qualifierParameter
;
qualifierParameter
: "(" constantValue ")"
{ result = val[1] }
| arrayInitializer
;
/* CIM::Flavors */
flavor
: AMENDED | ENABLEOVERRIDE | DISABLEOVERRIDE | RESTRICTED | TOSUBCLASS | TRANSLATABLE | TOINSTANCE
{ case val[0].to_sym
when :amended, :toinstance
raise StyleError.new(@name,@lineno,@line,"'#{val[0]}' is not a valid flavor") unless @style == :wmi
end
}
;
alias_opt
: /* empty */
| alias
;
superClass_opt
: /* empty */
| superClass
;
className
: IDENTIFIER /* must be <schema>_<classname> in CIM v2.x */
{ raise ParseError.new("Class name must be prefixed by '<schema>_'") unless val[0].include?("_") || @style == :wmi }
;
alias
: AS aliasIdentifier
{ result = val[1] }
;
aliasIdentifier
: "$" IDENTIFIER /* NO whitespace ! */
{ result = val[1] }
;
superClass
: ":" className
{ result = val[1] }
;
propertyDeclaration
: qualifierList_opt dataType propertyName array_opt defaultValue_opt ";"
{ if val[3]
type = CIM::Array.new val[3],val[1]
else
type = val[1]
end
result = CIM::Property.new(type,val[2],val[0],val[4])
}
;
referenceDeclaration
: qualifierList_opt objectRef referenceName array_opt defaultValue_opt ";"
{ if val[4]
raise StyleError.new(@name,@lineno,@line,"Array not allowed in reference declaration") unless @style == :wmi
end
result = CIM::Reference.new(val[1],val[2],val[0],val[4]) }
;
methodDeclaration
: qualifierList_opt dataType methodName "(" parameterList_opt ")" ";"
{ result = CIM::Method.new(val[1],val[2],val[0],val[4]) }
;
propertyName
: IDENTIFIER
| PROPERTY
{ # tmplprov.mof has 'string Property;'
raise StyleError.new(@name,@lineno,@line,"Invalid keyword '#{val[0]}' used for property name") unless @style == :wmi
}
;
referenceName
: IDENTIFIER
| INDICATION
{ result = "Indication" }
;
methodName
: IDENTIFIER
;
dataType
: DT_UINT8
| DT_SINT8
| DT_UINT16
| DT_SINT16
| DT_UINT32
| DT_SINT32
| DT_UINT64
| DT_SINT64
| DT_REAL32
| DT_REAL64
| DT_CHAR16
| DT_STR
| DT_BOOLEAN
| DT_DATETIME
| DT_VOID
{ raise StyleError.new(@name,@lineno,@line,"'void' is not a valid datatype") unless @style == :wmi }
;
objectRef
: className
{ # WMI uses class names as data types (without REF ?!)
raise StyleError.new(@name,@lineno,@line,"Expected 'ref' keyword after classname '#{val[0]}'") unless @style == :wmi
result = CIM::ReferenceType.new val[0]
}
| className REF
{ result = CIM::ReferenceType.new val[0] }
;
parameterList_opt
: /* empty */
| parameterList
;
parameterList
: parameter parameters
{ result = val[1].unshift val[0] }
;
parameters
: /* empty */
{ result = [] }
| parameters "," parameter
{ result = val[0] << val[2] }
;
parameter
: qualifierList_opt typespec parameterName array_opt parameterValue_opt
{ if val[3]
type = CIM::Array.new val[3], val[1]
else
type = val[1]
end
result = CIM::Property.new(type,val[2],val[0])
}
;
typespec
: dataType
| objectRef
;
parameterName
: IDENTIFIER
;
array_opt
: /* empty */
| array
;
parameterValue_opt
: /* empty */
| defaultValue
{ raise "Default parameter value not allowed in syntax style '{@style}'" unless @style == :wmi }
;
array
: "[" positiveDecimalValue_opt "]"
{ result = val[1] }
;
positiveDecimalValue_opt
: /* empty */
{ result = -1 }
| positiveDecimalValue
;
defaultValue_opt
: /* empty */
| defaultValue
;
defaultValue
: "=" initializer
{ result = val[1] }
;
initializer
: constantValue
| arrayInitializer
| referenceInitializer
;
arrayInitializer
: "{" constantValues "}"
{ result = val[1] }
;
constantValues
: /* empty */
| constantValue
{ result = [ val[0] ] }
| constantValues "," constantValue
{ result = val[0] << val[2] }
;
constantValue
: integerValue
| realValue
| charValue
| string
| booleanValue
| nullValue
| instance
{ raise "Instance as property value not allowed in syntax style '{@style}'" unless @style == :wmi }
;
integerValue
: binaryValue
| octalValue
| decimalValue
| positiveDecimalValue
| hexValue
;
string
: stringValue
| string stringValue
{ result = val[0] + val[1] }
;
referenceInitializer
: objectHandle
| aliasIdentifier
;
objectHandle
: namespace_opt modelPath
;
namespace_opt
: /* empty */
| namespaceHandle ":"
;
namespaceHandle
: IDENTIFIER
;
/*
* Note
: structure depends on type of namespace
*/
modelPath
: className "." keyValuePairList
;
keyValuePairList
: keyValuePair keyValuePairs
;
keyValuePairs
: /* empty */
| keyValuePairs "," keyValuePair
;
keyValuePair
: keyname "=" initializer
;
keyname
: propertyName | referenceName
;
/***
* qualifierDeclaration
*
*/
qualifierDeclaration
/* 0 1 2 3 4 */
: QUALIFIER qualifierName qualifierType scope defaultFlavor_opt ";"
{ result = CIM::QualifierDeclaration.new( val[1], val[2][0], val[2][1], val[3], val[4]) }
;
defaultFlavor_opt
: /* empty */
| defaultFlavor
;
qualifierName
: IDENTIFIER
| ASSOCIATION /* meta qualifier */
| INDICATION /* meta qualifier */
| REFERENCE /* Added in DSP0004 2.7.0 */
| SCHEMA
;
/* [type, value] */
qualifierType
: ":" dataType array_opt defaultValue_opt
{ type = val[2].nil? ? val[1] : CIM::Array.new(val[2],val[1])
result = [ type, val[3] ]
}
;
scope
: "," SCOPE "(" metaElements ")"
{ result = CIM::QualifierScopes.new(val[3]) }
;
metaElements
: metaElement
{ result = [ val[0] ] }
| metaElements "," metaElement
{ result = val[0] << val[2] }
;
metaElement
: SCHEMA
| CLASS
| ASSOCIATION
| INDICATION
| QUALIFIER
| PROPERTY
| REFERENCE
| METHOD
| PARAMETER
| ANY
;
defaultFlavor
: "," FLAVOR "(" flavors ")"
{ result = CIM::QualifierFlavors.new val[3] }
;
flavors
: flavor
{ result = [ val[0] ] }
| flavors "," flavor
{ result = val[0] << val[2] }
;
/***
* instanceDeclaration
*
*/
instanceDeclaration
: instance ";"
;
instance
: qualifierList_opt INSTANCE OF className alias_opt "{" valueInitializers "}"
;
valueInitializers
: valueInitializer
| valueInitializers valueInitializer
;
valueInitializer
: qualifierList_opt keyname "=" initializer ";"
| qualifierList_opt keyname ";"
{ raise "Instance property '#{val[1]} must have a value" unless @style == :wmi }
;
end # class Parser
---- header ----
# parser.rb - generated by racc
require 'strscan'
require 'rubygems'
require 'cim'
require File.join(File.dirname(__FILE__), 'result')
require File.join(File.dirname(__FILE__), 'scanner')
require File.join(File.dirname(__FILE__), 'helper')
---- inner ----
#
# Initialize MOF::Parser
# MOF::Parser.new options = {}
#
# options -> Hash of options
# :debug -> boolean
# :includes -> array of include dirs
# :style -> :cim or :wmi
#
def initialize options = {}
@yydebug = options[:debug]
@includes = options[:includes] || []
@quiet = options[:quiet]
@style = options[:style] || :cim # default to style CIM v2.2 syntax
@lineno = 1
@file = nil
@iconv = nil
@eol = "\n"
@fname = nil
@fstack = []
@in_comment = false
@seen_files = []
@qualifiers = {}
end
#
# Make options hash from argv
#
# returns [ files, options ]
#
def self.argv_handler name, argv
files = []
options = { :namespace => "" }
while argv.size > 0
case opt = argv.shift
when "-h"
$stderr.puts "Ruby MOF compiler"
$stderr.puts "#{name} [-h] [-d] [-I <dir>] [<moffiles>]"
$stderr.puts "Compiles <moffile>"
$stderr.puts "\t-d debug"
$stderr.puts "\t-h this help"
$stderr.puts "\t-I <dir> include dir"
$stderr.puts "\t-f force"
$stderr.puts "\t-n <namespace>"
$stderr.puts "\t-o <output>"
$stderr.puts "\t-s <style> syntax style (wmi,cim)"
$stderr.puts "\t-q quiet"
$stderr.puts "\t<moffiles> file(s) to read (else use $stdin)"
exit 0
when "-f" then options[:force] = true
when "-s" then options[:style] = argv.shift.to_sym
when "-d" then options[:debug] = true
when "-q" then options[:quiet] = true
when "-I"
options[:includes] ||= []
dirname = argv.shift
unless File.directory?(dirname)
files << dirname
dirname = File.dirname(dirname)
end
options[:includes] << Pathname.new(dirname)
when "-n" then options[:namespace] = argv.shift
when "-o" then options[:output] = argv.shift
when /^-.+/
$stderr.puts "Undefined option #{opt}"
else
files << opt
end
end
[ files, options ]
end
include Helper
include Scanner
---- footer ----

302
test/racc/assets/namae.y Normal file
View file

@ -0,0 +1,302 @@
# -*- ruby -*-
# vi: set ft=ruby :
# Copyright (C) 2012 President and Fellows of Harvard College
# Copyright (C) 2013-2014 Sylvester Keil
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of the copyright holder.
class Namae::Parser
token COMMA UWORD LWORD PWORD NICK AND APPELLATION TITLE SUFFIX
expect 0
rule
names : { result = [] }
| name { result = [val[0]] }
| names AND name { result = val[0] << val[2] }
name : word { result = Name.new(:given => val[0]) }
| display_order
| honorific word { result = val[0].merge(:family => val[1]) }
| honorific display_order { result = val[1].merge(val[0]) }
| sort_order
honorific : APPELLATION { result = Name.new(:appellation => val[0]) }
| TITLE { result = Name.new(:title => val[0]) }
display_order : u_words word opt_suffices opt_titles
{
result = Name.new(:given => val[0], :family => val[1],
:suffix => val[2], :title => val[3])
}
| u_words NICK last opt_suffices opt_titles
{
result = Name.new(:given => val[0], :nick => val[1],
:family => val[2], :suffix => val[3], :title => val[4])
}
| u_words NICK von last opt_suffices opt_titles
{
result = Name.new(:given => val[0], :nick => val[1],
:particle => val[2], :family => val[3],
:suffix => val[4], :title => val[5])
}
| u_words von last
{
result = Name.new(:given => val[0], :particle => val[1],
:family => val[2])
}
| von last
{
result = Name.new(:particle => val[0], :family => val[1])
}
sort_order : last COMMA first
{
result = Name.new({ :family => val[0], :suffix => val[2][0],
:given => val[2][1] }, !!val[2][0])
}
| von last COMMA first
{
result = Name.new({ :particle => val[0], :family => val[1],
:suffix => val[3][0], :given => val[3][1] }, !!val[3][0])
}
| u_words von last COMMA first
{
result = Name.new({ :particle => val[0,2].join(' '), :family => val[2],
:suffix => val[4][0], :given => val[4][1] }, !!val[4][0])
}
;
von : LWORD
| von LWORD { result = val.join(' ') }
| von u_words LWORD { result = val.join(' ') }
last : LWORD | u_words
first : opt_words { result = [nil,val[0]] }
| words opt_comma suffices { result = [val[2],val[0]] }
| suffices { result = [val[0],nil] }
| suffices COMMA words { result = [val[0],val[2]] }
u_words : u_word
| u_words u_word { result = val.join(' ') }
u_word : UWORD | PWORD
words : word
| words word { result = val.join(' ') }
opt_comma : /* empty */ | COMMA
opt_words : /* empty */ | words
word : LWORD | UWORD | PWORD
opt_suffices : /* empty */ | suffices
suffices : SUFFIX
| suffices SUFFIX { result = val.join(' ') }
opt_titles : /* empty */ | titles
titles : TITLE
| titles TITLE { result = val.join(' ') }
---- header
require 'singleton'
require 'strscan'
---- inner
include Singleton
attr_reader :options, :input
def initialize
@input, @options = StringScanner.new(''), {
:debug => false,
:prefer_comma_as_separator => false,
:comma => ',',
:stops => ',;',
:separator => /\s*(\band\b|\&|;)\s*/i,
:title => /\s*\b(sir|lord|count(ess)?|(gen|adm|col|maj|capt|cmdr|lt|sgt|cpl|pvt|prof|dr|md|ph\.?d)\.?)(\s+|$)/i,
:suffix => /\s*\b(JR|Jr|jr|SR|Sr|sr|[IVX]{2,})(\.|\b)/,
:appellation => /\s*\b((mrs?|ms|fr|hr)\.?|miss|herr|frau)(\s+|$)/i
}
end
def debug?
options[:debug] || ENV['DEBUG']
end
def separator
options[:separator]
end
def comma
options[:comma]
end
def stops
options[:stops]
end
def title
options[:title]
end
def suffix
options[:suffix]
end
def appellation
options[:appellation]
end
def prefer_comma_as_separator?
options[:prefer_comma_as_separator]
end
def parse(input)
parse!(input)
rescue => e
warn e.message if debug?
[]
end
def parse!(string)
input.string = normalize(string)
reset
do_parse
end
def normalize(string)
string = string.strip
string
end
def reset
@commas, @words, @initials, @suffices, @yydebug = 0, 0, 0, 0, debug?
self
end
private
def stack
@vstack || @racc_vstack || []
end
def last_token
stack[-1]
end
def consume_separator
return next_token if seen_separator?
@commas, @words, @initials, @suffices = 0, 0, 0, 0
[:AND, :AND]
end
def consume_comma
@commas += 1
[:COMMA, :COMMA]
end
def consume_word(type, word)
@words += 1
case type
when :UWORD
@initials += 1 if word =~ /^[[:upper:]]+\b/
when :SUFFIX
@suffices += 1
end
[type, word]
end
def seen_separator?
!stack.empty? && last_token == :AND
end
def suffix?
!@suffices.zero? || will_see_suffix?
end
def will_see_suffix?
input.peek(8).to_s.strip.split(/\s+/)[0] =~ suffix
end
def will_see_initial?
input.peek(6).to_s.strip.split(/\s+/)[0] =~ /^[[:upper:]]+\b/
end
def seen_full_name?
prefer_comma_as_separator? && @words > 1 &&
(@initials > 0 || !will_see_initial?) && !will_see_suffix?
end
def next_token
case
when input.nil?, input.eos?
nil
when input.scan(separator)
consume_separator
when input.scan(/\s*#{comma}\s*/)
if @commas.zero? && !seen_full_name? || @commas == 1 && suffix?
consume_comma
else
consume_separator
end
when input.scan(/\s+/)
next_token
when input.scan(title)
consume_word(:TITLE, input.matched.strip)
when input.scan(suffix)
consume_word(:SUFFIX, input.matched.strip)
when input.scan(appellation)
[:APPELLATION, input.matched.strip]
when input.scan(/((\\\w+)?\{[^\}]*\})*[[:upper:]][^\s#{stops}]*/)
consume_word(:UWORD, input.matched)
when input.scan(/((\\\w+)?\{[^\}]*\})*[[:lower:]][^\s#{stops}]*/)
consume_word(:LWORD, input.matched)
when input.scan(/(\\\w+)?\{[^\}]*\}[^\s#{stops}]*/)
consume_word(:PWORD, input.matched)
when input.scan(/('[^'\n]+')|("[^"\n]+")/)
consume_word(:NICK, input.matched[1...-1])
else
raise ArgumentError,
"Failed to parse name #{input.string.inspect}: unmatched data at offset #{input.pos}"
end
end
def on_error(tid, value, stack)
raise ArgumentError,
"Failed to parse name: unexpected '#{value}' at #{stack.inspect}"
end
# -*- racc -*-

626
test/racc/assets/nasl.y Normal file
View file

@ -0,0 +1,626 @@
################################################################################
# Copyright (c) 2011-2014, Tenable Network Security
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
################################################################################
class Nasl::Grammar
preclow
right ASS_EQ ADD_EQ SUB_EQ MUL_EQ DIV_EQ MOD_EQ SLL_EQ SRA_EQ SRL_EQ
left OR
left AND
left CMP_LT CMP_GT CMP_EQ CMP_NE CMP_GE CMP_LE SUBSTR_EQ SUBSTR_NE REGEX_EQ REGEX_NE
left BIT_OR
left BIT_XOR
left AMPERSAND
left BIT_SRA BIT_SRL BIT_SLL
left ADD SUB
left MUL DIV MOD
right NOT
right UMINUS BIT_NOT
right EXP
right INCR DECR
prechigh
# Tell the parser generator that we don't wish to use the result variable in the
# action section of rules. Instead, the result of the rule will be the value of
# evaluating the action block.
options no_result_var
# Tell the parser generator that we expect one shift/reduce conflict due to the
# well-known dangling else problem. We could make the grammar solve this
# problem, but this is how the NASL YACC file solves it, so we'll follow suit.
expect 1
rule
##############################################################################
# Aggregate Statements
##############################################################################
start : roots
{ val[0] }
| /* Blank */
{ [] }
;
roots : root roots
{ [val[0]] + val[1] }
| root
{ [val[0]] }
;
root : COMMENT export
{ c(*val) }
| export
{ val[0] }
| COMMENT function
{ c(*val) }
| function
{ val[0] }
| statement
{ val[0] }
;
statement : simple
{ val[0] }
| compound
{ val[0] }
;
##############################################################################
# Root Statements
##############################################################################
export : EXPORT function
{ n(:Export, *val) }
;
function : FUNCTION ident LPAREN params RPAREN block
{ n(:Function, *val) }
| FUNCTION ident LPAREN RPAREN block
{ n(:Function, *val) }
;
simple : assign
{ val[0] }
| break
{ val[0] }
| call
{ val[0] }
| continue
{ val[0] }
| decr
{ val[0] }
| empty
{ val[0] }
| COMMENT global
{ c(*val) }
| global
{ val[0] }
| import
{ val[0] }
| include
{ val[0] }
| incr
{ val[0] }
| local
{ val[0] }
| rep
{ val[0] }
| return
{ val[0] }
;
compound : block
{ val[0] }
| for
{ val[0] }
| foreach
{ val[0] }
| if
{ val[0] }
| repeat
{ val[0] }
| while
{ val[0] }
;
##############################################################################
# Simple Statements
##############################################################################
assign : assign_exp SEMICOLON
{ val[0] }
;
break : BREAK SEMICOLON
{ n(:Break, *val) }
;
call : call_exp SEMICOLON
{ val[0] }
;
continue : CONTINUE SEMICOLON
{ n(:Continue, *val) }
;
decr : decr_exp SEMICOLON
{ val[0] }
;
empty : SEMICOLON
{ n(:Empty, *val) }
;
global : GLOBAL var_decls SEMICOLON
{ n(:Global, *val) }
;
incr : incr_exp SEMICOLON
{ val[0] }
;
import : IMPORT LPAREN string RPAREN SEMICOLON
{ n(:Import, *val) }
;
include : INCLUDE LPAREN string RPAREN SEMICOLON
{ n(:Include, *val) }
;
local : LOCAL var_decls SEMICOLON
{ n(:Local, *val) }
;
rep : call_exp REP expr SEMICOLON
{ n(:Repetition, *val[0..-1]) }
;
return : RETURN expr SEMICOLON
{ n(:Return, *val) }
| RETURN ref SEMICOLON
{ n(:Return, *val) }
| RETURN SEMICOLON
{ n(:Return, *val) }
;
##############################################################################
# Compound Statements
##############################################################################
block : LBRACE statements RBRACE
{ n(:Block, *val) }
| LBRACE RBRACE
{ n(:Block, *val) }
;
for : FOR LPAREN field SEMICOLON expr SEMICOLON field RPAREN statement
{ n(:For, *val) }
;
foreach : FOREACH ident LPAREN expr RPAREN statement
{ n(:Foreach, val[0], val[1], val[3], val[5]) }
| FOREACH LPAREN ident IN expr RPAREN statement
{ n(:Foreach, val[0], val[2], val[4], val[6]) }
;
if : IF LPAREN expr RPAREN statement
{ n(:If, *val) }
| IF LPAREN expr RPAREN statement ELSE statement
{ n(:If, *val) }
;
repeat : REPEAT statement UNTIL expr SEMICOLON
{ n(:Repeat, *val) }
;
while : WHILE LPAREN expr RPAREN statement
{ n(:While, *val) }
;
##############################################################################
# Expressions
##############################################################################
assign_exp : lval ASS_EQ expr
{ n(:Assignment, *val) }
| lval ASS_EQ ref
{ n(:Assignment, *val) }
| lval ADD_EQ expr
{ n(:Assignment, *val) }
| lval SUB_EQ expr
{ n(:Assignment, *val) }
| lval MUL_EQ expr
{ n(:Assignment, *val) }
| lval DIV_EQ expr
{ n(:Assignment, *val) }
| lval MOD_EQ expr
{ n(:Assignment, *val) }
| lval SRL_EQ expr
{ n(:Assignment, *val) }
| lval SRA_EQ expr
{ n(:Assignment, *val) }
| lval SLL_EQ expr
{ n(:Assignment, *val) }
;
call_exp : lval LPAREN args RPAREN
{ n(:Call, *val) }
| lval LPAREN RPAREN
{ n(:Call, *val) }
;
decr_exp : DECR lval
{ n(:Decrement, val[0]) }
| lval DECR
{ n(:Decrement, val[0]) }
;
incr_exp : INCR lval
{ n(:Increment, val[0]) }
| lval INCR
{ n(:Increment, val[0]) }
;
expr : LPAREN expr RPAREN
{ n(:Expression, *val) }
| expr AND expr
{ n(:Expression, *val) }
| NOT expr
{ n(:Expression, *val) }
| expr OR expr
{ n(:Expression, *val) }
| expr ADD expr
{ n(:Expression, *val) }
| expr SUB expr
{ n(:Expression, *val) }
| SUB expr =UMINUS
{ n(:Expression, *val) }
| BIT_NOT expr
{ n(:Expression, *val) }
| expr MUL expr
{ n(:Expression, *val) }
| expr EXP expr
{ n(:Expression, *val) }
| expr DIV expr
{ n(:Expression, *val) }
| expr MOD expr
{ n(:Expression, *val) }
| expr AMPERSAND expr
{ n(:Expression, *val) }
| expr BIT_XOR expr
{ n(:Expression, *val) }
| expr BIT_OR expr
{ n(:Expression, *val) }
| expr BIT_SRA expr
{ n(:Expression, *val) }
| expr BIT_SRL expr
{ n(:Expression, *val) }
| expr BIT_SLL expr
{ n(:Expression, *val) }
| incr_exp
{ val[0] }
| decr_exp
{ val[0] }
| expr SUBSTR_EQ expr
{ n(:Expression, *val) }
| expr SUBSTR_NE expr
{ n(:Expression, *val) }
| expr REGEX_EQ expr
{ n(:Expression, *val) }
| expr REGEX_NE expr
{ n(:Expression, *val) }
| expr CMP_LT expr
{ n(:Expression, *val) }
| expr CMP_GT expr
{ n(:Expression, *val) }
| expr CMP_EQ expr
{ n(:Expression, *val) }
| expr CMP_NE expr
{ n(:Expression, *val) }
| expr CMP_GE expr
{ n(:Expression, *val) }
| expr CMP_LE expr
{ n(:Expression, *val) }
| assign_exp
{ val[0] }
| string
{ val[0] }
| call_exp
{ val[0] }
| lval
{ val[0] }
| ip
{ val[0] }
| int
{ val[0] }
| undef
{ val[0] }
| list_expr
{ val[0] }
| array_expr
{ val[0] }
;
##############################################################################
# Named Components
##############################################################################
arg : ident COLON expr
{ n(:Argument, *val) }
| ident COLON ref
{ n(:Argument, *val) }
| expr
{ n(:Argument, *val) }
| ref
{ n(:Argument, *val) }
;
kv_pair : string COLON expr
{ n(:KeyValuePair, *val) }
| int COLON expr
{ n(:KeyValuePair, *val) }
| ident COLON expr
{ n(:KeyValuePair, *val) }
| string COLON ref
{ n(:KeyValuePair, *val) }
| int COLON ref
{ n(:KeyValuePair, *val) }
| ident COLON ref
{ n(:KeyValuePair, *val) }
;
kv_pairs : kv_pair COMMA kv_pairs
{ [val[0]] + val[2] }
| kv_pair COMMA
{ [val[0]] }
| kv_pair
{ [val[0]] }
;
lval : ident indexes
{ n(:Lvalue, *val) }
| ident
{ n(:Lvalue, *val) }
;
ref : AT_SIGN ident
{ n(:Reference, val[1]) }
;
##############################################################################
# Anonymous Components
##############################################################################
args : arg COMMA args
{ [val[0]] + val[2] }
| arg
{ [val[0]] }
;
array_expr : LBRACE kv_pairs RBRACE
{ n(:Array, *val) }
| LBRACE RBRACE
{ n(:Array, *val) }
;
field : assign_exp
{ val[0] }
| call_exp
{ val[0] }
| decr_exp
{ val[0] }
| incr_exp
{ val[0] }
| /* Blank */
{ nil }
;
index : LBRACK expr RBRACK
{ val[1] }
| PERIOD ident
{ val[1] }
;
indexes : index indexes
{ [val[0]] + val[1] }
| index
{ [val[0]] }
;
list_elem : expr
{ val[0] }
| ref
{ val[0] }
;
list_elems : list_elem COMMA list_elems
{ [val[0]] + val[2] }
| list_elem
{ [val[0]] }
;
list_expr : LBRACK list_elems RBRACK
{ n(:List, *val) }
| LBRACK RBRACK
{ n(:List, *val) }
;
param : AMPERSAND ident
{ n(:Parameter, val[1], 'reference') }
| ident
{ n(:Parameter, val[0], 'value') }
;
params : param COMMA params
{ [val[0]] + val[2] }
| param
{ [val[0]] }
;
statements : statement statements
{ [val[0]] + val[1] }
| statement
{ [val[0]] }
;
var_decl : ident ASS_EQ expr
{ n(:Assignment, *val) }
| ident ASS_EQ ref
{ n(:Assignment, *val) }
| ident
{ val[0] }
;
var_decls : var_decl COMMA var_decls
{ [val[0]] + val[2] }
| var_decl
{ [val[0]] }
;
##############################################################################
# Literals
##############################################################################
ident : IDENT
{ n(:Identifier, *val) }
| REP
{ n(:Identifier, *val) }
| IN
{ n(:Identifier, *val) }
;
int : INT_DEC
{ n(:Integer, *val) }
| INT_HEX
{ n(:Integer, *val) }
| INT_OCT
{ n(:Integer, *val) }
| FALSE
{ n(:Integer, *val) }
| TRUE
{ n(:Integer, *val) }
;
ip : int PERIOD int PERIOD int PERIOD int
{ n(:Ip, *val) }
string : DATA
{ n(:String, *val) }
| STRING
{ n(:String, *val) }
;
undef : UNDEF
{ n(:Undefined, *val) }
;
end
---- header ----
require 'nasl/parser/tree'
require 'nasl/parser/argument'
require 'nasl/parser/array'
require 'nasl/parser/assigment'
require 'nasl/parser/block'
require 'nasl/parser/break'
require 'nasl/parser/call'
require 'nasl/parser/comment'
require 'nasl/parser/continue'
require 'nasl/parser/decrement'
require 'nasl/parser/empty'
require 'nasl/parser/export'
require 'nasl/parser/expression'
require 'nasl/parser/for'
require 'nasl/parser/foreach'
require 'nasl/parser/function'
require 'nasl/parser/global'
require 'nasl/parser/identifier'
require 'nasl/parser/if'
require 'nasl/parser/import'
require 'nasl/parser/include'
require 'nasl/parser/increment'
require 'nasl/parser/integer'
require 'nasl/parser/ip'
require 'nasl/parser/key_value_pair'
require 'nasl/parser/list'
require 'nasl/parser/local'
require 'nasl/parser/lvalue'
require 'nasl/parser/parameter'
require 'nasl/parser/reference'
require 'nasl/parser/repeat'
require 'nasl/parser/repetition'
require 'nasl/parser/return'
require 'nasl/parser/string'
require 'nasl/parser/undefined'
require 'nasl/parser/while'
---- inner ----
def n(cls, *args)
begin
Nasl.const_get(cls).new(@tree, *args)
rescue
puts "An exception occurred during the creation of a #{cls} instance."
puts
puts "The arguments passed to the constructer were:"
puts args
puts
puts @tok.last.context
puts
raise
end
end
def c(*args)
n(:Comment, *args)
args[1]
end
def on_error(type, value, stack)
raise ParseException, "The language's grammar does not permit #{value.name} to appear here", value.context
end
def next_token
@tok = @tkz.get_token
if @first && @tok.first == :COMMENT
n(:Comment, @tok.last)
@tok = @tkz.get_token
end
@first = false
return @tok
end
def parse(env, code, path)
@first = true
@tree = Tree.new(env)
@tkz = Tokenizer.new(code, path)
@tree.concat(do_parse)
end
---- footer ----

25
test/racc/assets/newsyn.y Normal file
View file

@ -0,0 +1,25 @@
class A
preclow
left preclow prechigh right left nonassoc token
right preclow prechigh right left nonassoc token
nonassoc preclow prechigh right left nonassoc token
prechigh
convert
left 'a'
right 'b'
preclow 'c'
nonassoc 'd'
preclow 'e'
prechigh 'f'
end
rule
left: right nonassoc preclow prechigh
right: A B C
end

4
test/racc/assets/noend.y Normal file
View file

@ -0,0 +1,4 @@
class MyParser
rule
input: A B C
end

View file

@ -0,0 +1,255 @@
class Nokogiri::CSS::Parser
token FUNCTION INCLUDES DASHMATCH LBRACE HASH PLUS GREATER S STRING IDENT
token COMMA NUMBER PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH TILDE NOT_EQUAL
token SLASH DOUBLESLASH NOT EQUAL RPAREN LSQUARE RSQUARE HAS
rule
selector
: selector COMMA simple_selector_1toN {
result = [val.first, val.last].flatten
}
| prefixless_combinator_selector { result = val.flatten }
| optional_S simple_selector_1toN { result = [val.last].flatten }
;
combinator
: PLUS { result = :DIRECT_ADJACENT_SELECTOR }
| GREATER { result = :CHILD_SELECTOR }
| TILDE { result = :FOLLOWING_SELECTOR }
| DOUBLESLASH { result = :DESCENDANT_SELECTOR }
| SLASH { result = :CHILD_SELECTOR }
;
simple_selector
: element_name hcap_0toN {
result = if val[1].nil?
val.first
else
Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
end
}
| function
| function pseudo {
result = Node.new(:CONDITIONAL_SELECTOR, val)
}
| function attrib {
result = Node.new(:CONDITIONAL_SELECTOR, val)
}
| hcap_1toN {
result = Node.new(:CONDITIONAL_SELECTOR,
[Node.new(:ELEMENT_NAME, ['*']), val.first]
)
}
;
prefixless_combinator_selector
: combinator simple_selector_1toN {
result = Node.new(val.first, [nil, val.last])
}
;
simple_selector_1toN
: simple_selector combinator simple_selector_1toN {
result = Node.new(val[1], [val.first, val.last])
}
| simple_selector S simple_selector_1toN {
result = Node.new(:DESCENDANT_SELECTOR, [val.first, val.last])
}
| simple_selector
;
class
: '.' IDENT { result = Node.new(:CLASS_CONDITION, [val[1]]) }
;
element_name
: namespaced_ident
| '*' { result = Node.new(:ELEMENT_NAME, val) }
;
namespaced_ident
: namespace '|' IDENT {
result = Node.new(:ELEMENT_NAME,
[[val.first, val.last].compact.join(':')]
)
}
| IDENT {
name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first
result = Node.new(:ELEMENT_NAME, [name])
}
;
namespace
: IDENT { result = val[0] }
|
;
attrib
: LSQUARE attrib_name attrib_val_0or1 RSQUARE {
result = Node.new(:ATTRIBUTE_CONDITION,
[val[1]] + (val[2] || [])
)
}
| LSQUARE function attrib_val_0or1 RSQUARE {
result = Node.new(:ATTRIBUTE_CONDITION,
[val[1]] + (val[2] || [])
)
}
| LSQUARE NUMBER RSQUARE {
# Non standard, but hpricot supports it.
result = Node.new(:PSEUDO_CLASS,
[Node.new(:FUNCTION, ['nth-child(', val[1]])]
)
}
;
attrib_name
: namespace '|' IDENT {
result = Node.new(:ELEMENT_NAME,
[[val.first, val.last].compact.join(':')]
)
}
| IDENT {
# Default namespace is not applied to attributes.
# So we don't add prefix "xmlns:" as in namespaced_ident.
result = Node.new(:ELEMENT_NAME, [val.first])
}
;
function
: FUNCTION RPAREN {
result = Node.new(:FUNCTION, [val.first.strip])
}
| FUNCTION expr RPAREN {
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
}
| FUNCTION nth RPAREN {
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
}
| NOT expr RPAREN {
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
}
| HAS selector RPAREN {
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
}
;
expr
: NUMBER COMMA expr { result = [val.first, val.last] }
| STRING COMMA expr { result = [val.first, val.last] }
| IDENT COMMA expr { result = [val.first, val.last] }
| NUMBER
| STRING
| IDENT # even, odd
{
case val[0]
when 'even'
result = Node.new(:NTH, ['2','n','+','0'])
when 'odd'
result = Node.new(:NTH, ['2','n','+','1'])
when 'n'
result = Node.new(:NTH, ['1','n','+','0'])
else
# This is not CSS standard. It allows us to support this:
# assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
# assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
# assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
result = val
end
}
;
nth
: NUMBER IDENT PLUS NUMBER # 5n+3 -5n+3
{
if val[1] == 'n'
result = Node.new(:NTH, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
}
| IDENT PLUS NUMBER { # n+3, -n+3
if val[0] == 'n'
val.unshift("1")
result = Node.new(:NTH, val)
elsif val[0] == '-n'
val[0] = 'n'
val.unshift("-1")
result = Node.new(:NTH, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
}
| NUMBER IDENT { # 5n, -5n, 10n-1
n = val[1]
if n[0, 2] == 'n-'
val[1] = 'n'
val << "-"
# b is contained in n as n is the string "n-b"
val << n[2, n.size]
result = Node.new(:NTH, val)
elsif n == 'n'
val << "+"
val << "0"
result = Node.new(:NTH, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
}
;
pseudo
: ':' function {
result = Node.new(:PSEUDO_CLASS, [val[1]])
}
| ':' IDENT { result = Node.new(:PSEUDO_CLASS, [val[1]]) }
;
hcap_0toN
: hcap_1toN
|
;
hcap_1toN
: attribute_id hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| class hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| attrib hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| pseudo hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| negation hcap_1toN {
result = Node.new(:COMBINATOR, val)
}
| attribute_id
| class
| attrib
| pseudo
| negation
;
attribute_id
: HASH { result = Node.new(:ID, val) }
;
attrib_val_0or1
: eql_incl_dash IDENT { result = [val.first, val[1]] }
| eql_incl_dash STRING { result = [val.first, val[1]] }
|
;
eql_incl_dash
: EQUAL { result = :equal }
| PREFIXMATCH { result = :prefix_match }
| SUFFIXMATCH { result = :suffix_match }
| SUBSTRINGMATCH { result = :substring_match }
| NOT_EQUAL { result = :not_equal }
| INCLUDES { result = :includes }
| DASHMATCH { result = :dash_match }
;
negation
: NOT negation_arg RPAREN {
result = Node.new(:NOT, [val[1]])
}
;
negation_arg
: element_name
| element_name hcap_1toN
| hcap_1toN
;
optional_S
: S
|
;
end
---- header
require 'nokogiri/css/parser_extras'

41
test/racc/assets/nonass.y Normal file
View file

@ -0,0 +1,41 @@
#
# nonassoc test
#
class P
preclow
nonassoc N
left P
prechigh
rule
target : exp
exp : exp N exp
| exp P exp
| T
end
---- inner
def parse
@src = [[:T,'T'], [:N,'N'], [:T,'T'], [:N,'N'], [:T,'T']]
do_parse
end
def next_token
@src.shift
end
---- footer
begin
P.new.parse
rescue ParseError
exit 0
else
$stderr.puts 'parse error not raised: nonassoc not work'
exit 1
end

27
test/racc/assets/normal.y Normal file
View file

@ -0,0 +1,27 @@
class Testp
convert
A '2'
B '3'
end
prechigh
left B
preclow
rule
/* comment */
target: A B C nonterminal { action "string" == /regexp/o
1 /= 3 }
; # comment
nonterminal: A '+' B = A;
/* end */
end
---- driver
# driver is old name

View file

@ -0,0 +1,4 @@
class A
rule
end

View file

@ -0,0 +1,25 @@
#
# number of conflicts must be ZERO.
#
class T
rule
targ : dummy
| a b c
dummy : V v
V : E e
| F f
|
;
E :
;
F :
;
end

View file

@ -0,0 +1,15 @@
#
# number of conflicts must be ZERO.
#
class A
rule
targ: operation voidhead
| variable
voidhead : void B
void:
operation: A
variable : A
end

1807
test/racc/assets/opal.y Normal file

File diff suppressed because it is too large Load diff

123
test/racc/assets/opt.y Normal file
View file

@ -0,0 +1,123 @@
#
# check options working
#
class Calcp
prechigh
left '*' '/'
left '+' '-'
preclow
convert
NUMBER 'Number'
end
options no_omit_action_call no_result_var
rule
target : exp | /* none */ { 0 } ;
exp : exp '+' exp { chk(val[0] + val[2]) }
| exp '-' exp { chk(val[0] - val[2]) }
| exp '*' exp { chk(val[0] * val[2]) }
| exp '/' exp { chk(val[0] / val[2]) }
| '(' { $emb = true } exp ')'
{
raise 'must not happen' unless $emb
val[2]
}
| '-' NUMBER { -val[1] }
| NUMBER
;
end
----header
class Number; end
----inner
def parse( src )
@src = src
do_parse
end
def next_token
@src.shift
end
def initialize
@yydebug = true
end
def chk( i )
# p i
i
end
----footer
$parser = Calcp.new
$test_number = 1
def chk( src, ans )
result = $parser.parse(src)
raise "test #{$test_number} failed" unless result == ans
$test_number += 1
end
chk(
[ [Number, 9],
[false, false],
[false, false] ], 9
)
chk(
[ [Number, 5],
['*', nil],
[Number, 1],
['-', nil],
[Number, 1],
['*', nil],
[Number, 8],
[false, false],
[false, false] ], -3
)
chk(
[ [Number, 5],
['+', nil],
[Number, 2],
['-', nil],
[Number, 5],
['+', nil],
[Number, 2],
['-', nil],
[Number, 5],
[false, false],
[false, false] ], -1
)
chk(
[ ['-', nil],
[Number, 4],
[false, false],
[false, false] ], -4
)
chk(
[ [Number, 7],
['*', nil],
['(', nil],
[Number, 4],
['+', nil],
[Number, 3],
[')', nil],
['-', nil],
[Number, 9],
[false, false],
[false, false] ], 40
)

View file

@ -0,0 +1,35 @@
class ScannerChecker
rule
target: A
{
i = 7
i %= 4
raise 'assert failed' unless i == 3
tmp = %-This is percent string.-
raise 'assert failed' unless tmp == 'This is percent string.'
a = 5; b = 3
assert_equal(2,(a%b)) #A
# assert_equal(2,(a %b)) # is %-string
assert_equal(2,(a% b)) #B
assert_equal(2,(a % b)) #C
}
end
---- inner ----
def parse
@q = [[:A, 'A'], [false, '$']]
do_parse
end
def next_token
@q.shift
end
def assert_equal( expect, real )
raise "expect #{expect.inspect} but #{real.inspect}" unless expect == real
end
---- footer ----
parser = ScannerChecker.new.parse

View file

@ -0,0 +1,98 @@
# MIT License
# See https://github.com/divoxx/ruby-php-serialization/blob/master/LICENSE.txt
class PhpSerialization::Unserializer
rule
data : null ';' { @object = val[0] }
| bool ';' { @object = val[0] }
| integer ';' { @object = val[0] }
| double ';' { @object = val[0] }
| string ';' { @object = val[0] }
| assoc_array { @object = val[0] }
| object { @object = val[0] }
;
null : 'N' { result = nil }
;
bool : 'b' ':' NUMBER { result = Integer(val[2]) > 0 }
;
integer : 'i' ':' NUMBER { result = Integer(val[2]) }
;
double : 'd' ':' NUMBER { result = Float(val[2]) }
;
string : 's' ':' NUMBER ':' STRING { result = val[4] }
;
object : 'O' ':' NUMBER ':' STRING ':' NUMBER ':' '{' attribute_list '}'
{
if eval("defined?(#{val[4]})")
result = Object.const_get(val[4]).new
val[9].each do |(attr_name, value)|
# Protected and private attributes will have a \0..\0 prefix
attr_name = attr_name.gsub(/\A\\0[^\\]+\\0/, '')
result.instance_variable_set("@#{attr_name}", value)
end
else
klass_name = val[4].gsub(/^Struct::/, '')
attr_names, values = [], []
val[9].each do |(attr_name, value)|
# Protected and private attributes will have a \0..\0 prefix
attr_names << attr_name.gsub(/\A\\0[^\\]+\\0/, '')
values << value
end
result = Struct.new(klass_name, *attr_names).new(*values)
result.instance_variable_set("@_php_class", klass_name)
end
}
;
attribute_list : attribute_list attribute { result = val[0] << val[1] }
| { result = [] }
;
attribute : data data { result = val }
;
assoc_array : 'a' ':' NUMBER ':' '{' attribute_list '}'
{
# Checks if the keys are a sequence of integers
idx = -1
arr = val[5].all? { |(k,v)| k == (idx += 1) }
if arr
result = val[5].map { |(k,v)| v }
else
result = Hash[val[5]]
end
}
;
end
---- header ----
require 'php_serialization/tokenizer'
---- inner ----
def initialize(tokenizer_klass = Tokenizer)
@tokenizer_klass = tokenizer_klass
end
def run(string)
@tokenizer = @tokenizer_klass.new(string)
yyparse(@tokenizer, :each)
return @object
ensure
@tokenizer = nil
end
def next_token
@tokenizer.next_token
end

97
test/racc/assets/recv.y Normal file
View file

@ -0,0 +1,97 @@
# s/r 5, r/r 10
class A
rule
content: RecvH received
;
datetime: day
;
msgid: '<' spec '>';
day:
| ATOM ','
;
received: recvitem_list recvdatetime
;
recvitem_list:
| recvitem_list recvitem
;
recvitem: by | via | with | for ;
by:
| BY domain
;
via:
| VIA ATOM
;
with: WITH ATOM
;
for:
| FOR addr
;
recvdatetime:
| ';' datetime
;
addr: mbox | group ;
mboxes: mbox
| mboxes ',' mbox
;
mbox: spec
| routeaddr
| phrase routeaddr
;
group: phrase ':' mboxes ';'
;
routeaddr: '<' route spec '>'
| '<' spec '>'
;
route: at_domains ':' ;
at_domains: '@' domain
| at_domains ',' '@' domain
;
spec: local '@' domain
| local
;
local: word
| local '.' word
;
domain: domword
| domain '.' domword
;
domword: atom
| DOMLIT
| DIGIT
;
phrase: word
| phrase word
;
word: atom
| QUOTED
| DIGIT
;
atom: ATOM | FROM | BY | VIA | WITH | ID | FOR ;
end

665
test/racc/assets/riml.y Normal file
View file

@ -0,0 +1,665 @@
# Copyright (c) 2012-2014 by Luke Gruber
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class Riml::Parser
token IF ELSE ELSEIF THEN UNLESS END
token WHILE UNTIL BREAK CONTINUE
token TRY CATCH FINALLY
token FOR IN
token DEF DEF_BANG SPLAT_PARAM SPLAT_ARG CALL BUILTIN_COMMAND # such as echo "hi"
token CLASS NEW DEFM DEFM_BANG SUPER
token RIML_FILE_COMMAND RIML_CLASS_COMMAND
token RETURN
token NEWLINE
token NUMBER
token STRING_D STRING_S # single- and double-quoted
token EX_LITERAL
token REGEXP
token TRUE FALSE
token LET UNLET UNLET_BANG IDENTIFIER
token DICT_VAL # like dict.key, 'key' is a DICT_VAL
token SCOPE_MODIFIER SCOPE_MODIFIER_LITERAL SPECIAL_VAR_PREFIX
token FINISH
prechigh
right '!'
left '*' '/' '%'
left '+' '-' '.'
left '>' '>#' '>?' '<' '<#' '<?' '>=' '>=#' '>=?' '<=' '<=#' '<=?'
left '==' '==?' '==#' '=~' '=~?' '=~#' '!~' '!~?' '!~#' '!=' '!=?' '!=#'
left IS ISNOT
left '&&'
left '||'
right '?'
right '=' '+=' '-=' '.='
left ','
left IF UNLESS
preclow
# All rules
rule
Root:
/* nothing */ { result = make_node(val) { |_| Riml::Nodes.new([]) } }
| Terminator { result = make_node(val) { |_| Riml::Nodes.new([]) } }
| Statements { result = val[0] }
;
# any list of expressions
Statements:
Statement { result = make_node(val) { |v| Riml::Nodes.new([ v[0] ]) } }
| Statements Terminator Statement { result = val[0] << val[2] }
| Statements Terminator { result = val[0] }
| Terminator Statements { result = make_node(val) { |v| Riml::Nodes.new(v[1]) } }
;
# All types of expressions in Riml
Statement:
ExplicitCall { result = val[0] }
| Def { result = val[0] }
| Return { result = val[0] }
| UnletVariable { result = val[0] }
| ExLiteral { result = val[0] }
| For { result = val[0] }
| While { result = val[0] }
| Until { result = val[0] }
| Try { result = val[0] }
| ClassDefinition { result = val[0] }
| LoopKeyword { result = val[0] }
| EndScript { result = val[0] }
| RimlFileCommand { result = val[0] }
| RimlClassCommand { result = val[0] }
| MultiAssign { result = val[0] }
| If { result = val[0] }
| Unless { result = val[0] }
| Expression { result = val[0] }
;
Expression:
ExpressionWithoutDictLiteral { result = val[0] }
| Dictionary { result = val[0] }
| Dictionary DictGetWithDotLiteral { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
| BinaryOperator { result = val[0] }
| Ternary { result = val[0] }
| Assign { result = val[0] }
| Super { result = val[0] }
| '(' Expression ')' { result = make_node(val) { |v| Riml::WrapInParensNode.new(v[1]) } }
;
ExpressionWithoutDictLiteral:
UnaryOperator { result = val[0] }
| DictGet { result = val[0] }
| ListOrDictGet { result = val[0] }
| AllVariableRetrieval { result = val[0] }
| LiteralWithoutDictLiteral { result = val[0] }
| Call { result = val[0] }
| ObjectInstantiation { result = val[0] }
| '(' ExpressionWithoutDictLiteral ')' { result = make_node(val) { |v| Riml::WrapInParensNode.new(v[1]) } }
;
# for inside curly-brace variable names
PossibleStringValue:
String { result = val[0] }
| DictGet { result = val[0] }
| ListOrDictGet { result = val[0] }
| AllVariableRetrieval { result = val[0] }
| BinaryOperator { result = val[0] }
| Ternary { result = val[0] }
| Call { result = val[0] }
;
Terminator:
NEWLINE { result = nil }
| ';' { result = nil }
;
LiteralWithoutDictLiteral:
Number { result = val[0] }
| String { result = val[0] }
| Regexp { result = val[0] }
| List { result = val[0] }
| ScopeModifierLiteral { result = val[0] }
| TRUE { result = make_node(val) { |_| Riml::TrueNode.new } }
| FALSE { result = make_node(val) { |_| Riml::FalseNode.new } }
;
Number:
NUMBER { result = make_node(val) { |v| Riml::NumberNode.new(v[0]) } }
;
String:
STRING_S { result = make_node(val) { |v| Riml::StringNode.new(v[0], :s) } }
| STRING_D { result = make_node(val) { |v| Riml::StringNode.new(v[0], :d) } }
| String STRING_S { result = make_node(val) { |v| Riml::StringLiteralConcatNode.new(v[0], Riml::StringNode.new(v[1], :s)) } }
| String STRING_D { result = make_node(val) { |v| Riml::StringLiteralConcatNode.new(v[0], Riml::StringNode.new(v[1], :d)) } }
;
Regexp:
REGEXP { result = make_node(val) { |v| Riml::RegexpNode.new(v[0]) } }
;
ScopeModifierLiteral:
SCOPE_MODIFIER_LITERAL { result = make_node(val) { |v| Riml::ScopeModifierLiteralNode.new(v[0]) } }
;
List:
ListLiteral { result = make_node(val) { |v| Riml::ListNode.new(v[0]) } }
;
ListUnpack:
'[' ListItems ';' Expression ']' { result = make_node(val) { |v| Riml::ListUnpackNode.new(v[1] << v[3]) } }
;
ListLiteral:
'[' ListItems ']' { result = val[1] }
| '[' ListItems ',' ']' { result = val[1] }
;
ListItems:
/* nothing */ { result = [] }
| Expression { result = [val[0]] }
| ListItems ',' Expression { result = val[0] << val[2] }
;
Dictionary:
DictionaryLiteral { result = make_node(val) { |v| Riml::DictionaryNode.new(v[0]) } }
;
# {'key': 'value', 'key2': 'value2'}
# Save as [['key', 'value'], ['key2', 'value2']] because ruby-1.8.7 offers
# no guarantee for key-value pair ordering.
DictionaryLiteral:
'{' DictItems '}' { result = val[1] }
| '{' DictItems ',' '}' { result = val[1] }
;
# [[key, value], [key, value]]
DictItems:
/* nothing */ { result = [] }
| DictItem { result = val }
| DictItems ',' DictItem { result = val[0] << val[2] }
;
# [key, value]
DictItem:
Expression ':' Expression { result = [val[0], val[2]] }
;
DictGet:
AllVariableRetrieval DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
| ListOrDictGet DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
| Call DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(v[0], v[1]) } }
| '(' Expression ')' DictGetWithDot { result = make_node(val) { |v| Riml::DictGetDotNode.new(Riml::WrapInParensNode.new(v[1]), v[3]) } }
;
ListOrDictGet:
ExpressionWithoutDictLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(v[0], v[1]) } }
| '(' Expression ')' ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(Riml::WrapInParensNode.new(v[1]), v[3]) } }
;
ListOrDictGetAssign:
ExpressionWithoutDictLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::ListOrDictGetNode.new(v[0], v[1]) } }
;
ListOrDictGetWithBrackets:
'[' Expression ']' { result = [val[1]] }
| '[' SubList ']' { result = [val[1]] }
| ListOrDictGetWithBrackets '[' Expression ']' { result = val[0] << val[2] }
| ListOrDictGetWithBrackets '[' SubList ']' { result = val[0] << val[2] }
;
SubList:
Expression ':' Expression { result = make_node(val) { |v| Riml::SublistNode.new([v[0], Riml::LiteralNode.new(' : '), v[2]]) } }
| Expression ':' { result = make_node(val) { |v| Riml::SublistNode.new([v[0], Riml::LiteralNode.new(' :')]) } }
| ':' Expression { result = make_node(val) { |v| Riml::SublistNode.new([Riml::LiteralNode.new(': '), v[1]]) } }
| ':' { result = make_node(val) { |_| Riml::SublistNode.new([Riml::LiteralNode.new(':')]) } }
;
DictGetWithDot:
DICT_VAL { result = [val[0]] }
| DictGetWithDot DICT_VAL { result = val[0] << val[1] }
;
DictGetWithDotLiteral:
'.' IDENTIFIER { result = [val[1]] }
| DictGetWithDotLiteral DICT_VAL { result = val[0] << val[1] }
;
Call:
Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], v[3]) } }
| DictGet '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[2]) } }
| BUILTIN_COMMAND '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[2]) } }
| BUILTIN_COMMAND ArgListWithoutNothing { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], v[1]) } }
| BUILTIN_COMMAND NEWLINE { result = make_node(val) { |v| Riml::CallNode.new(nil, v[0], []) } }
| CALL '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(nil, nil, v[2]) } }
;
ObjectInstantiationCall:
Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], v[3]) } }
| Scope DefCallIdentifier { result = make_node(val) { |v| Riml::CallNode.new(v[0], v[1], []) } }
;
RimlFileCommand:
RIML_FILE_COMMAND '(' ArgList ')' { result = make_node(val) { |v| Riml::RimlFileCommandNode.new(nil, v[0], v[2]) } }
| RIML_FILE_COMMAND ArgList { result = make_node(val) { |v| Riml::RimlFileCommandNode.new(nil, v[0], v[1]) } }
;
RimlClassCommand:
RIML_CLASS_COMMAND '(' ClassArgList ')' { result = make_node(val) { |v| Riml::RimlClassCommandNode.new(nil, v[0], v[2]) } }
| RIML_CLASS_COMMAND ClassArgList { result = make_node(val) { |v| Riml::RimlClassCommandNode.new(nil, v[0], v[1]) } }
;
ClassArgList:
Scope IDENTIFIER { result = ["#{val[0]}#{val[1]}"] }
| String { result = val }
| ClassArgList ',' Scope IDENTIFIER { result = val[0].concat ["#{val[2]}#{val[3]}"] }
;
ExplicitCall:
CALL Scope DefCallIdentifier '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(v[1], v[2], v[4]) } }
| CALL DictGet '(' ArgList ')' { result = make_node(val) { |v| Riml::ExplicitCallNode.new(nil, v[1], v[3]) } }
;
Scope:
SCOPE_MODIFIER { result = val[0] }
| /* nothing */ { result = nil }
;
# [SID, scope_modifier]
SIDAndScope:
Scope { result = [ nil, val[0] ] }
| '<' IDENTIFIER '>' Scope { result = [ make_node(val) { |v| Riml::SIDNode.new(v[1]) }, val[3] ] }
;
ArgList:
/* nothing */ { result = [] }
| ArgListWithoutNothingWithSplat { result = val[0] }
;
ArgListWithSplat:
/* nothing */ { result = [] }
| ArgListWithoutNothingWithSplat { result = val[0] }
;
ArgListWithoutNothingWithSplat:
Expression { result = val }
| SPLAT_ARG Expression { result = [ make_node(val) { |v| Riml::SplatNode.new(v[1]) } ] }
| ArgListWithoutNothingWithSplat "," Expression { result = val[0] << val[2] }
| ArgListWithoutNothingWithSplat "," SPLAT_ARG Expression { result = val[0] << make_node(val) { |v| Riml::SplatNode.new(v[3]) } }
;
ArgListWithoutNothing:
Expression { result = val }
| ArgListWithoutNothing "," Expression { result = val[0] << val[2] }
;
BinaryOperator:
Expression '||' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '&&' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '==' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '==#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '==?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
# added by riml
| Expression '===' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '!=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '!=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '!=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '=~' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '=~#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '=~?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '!~' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '!~#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '!~?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '>' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '>#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '>?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '>=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '>=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '>=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '<' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '<#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '<?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '<=' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '<=#' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '<=?' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '+' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '-' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '*' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '/' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '.' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression '%' Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression IS Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
| Expression ISNOT Expression { result = make_node(val) { |v| Riml::BinaryOperatorNode.new(v[1], [v[0], v[2]]) } }
;
UnaryOperator:
'!' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
| '+' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
| '-' Expression { result = make_node(val) { |v| Riml::UnaryOperatorNode.new(val[0], [val[1]]) } }
;
# ['=', LHS, RHS]
Assign:
LET AssignExpression { result = make_node(val) { |v| Riml::AssignNode.new(v[1][0], v[1][1], v[1][2]) } }
| AssignExpression { result = make_node(val) { |v| Riml::AssignNode.new(v[0][0], v[0][1], v[0][2]) } }
;
MultiAssign:
Assign ',' Assign { result = make_node(val) { |v| Riml::MultiAssignNode.new([v[0], v[2]]) } }
| MultiAssign ',' Assign { val[0].assigns << val[2]; result = val[0] }
;
# ['=', AssignLHS, Expression]
AssignExpression:
AssignLHS '=' Expression { result = [val[1], val[0], val[2]] }
| AssignLHS '+=' Expression { result = [val[1], val[0], val[2]] }
| AssignLHS '-=' Expression { result = [val[1], val[0], val[2]] }
| AssignLHS '.=' Expression { result = [val[1], val[0], val[2]] }
;
AssignLHS:
AllVariableRetrieval { result = val[0] }
| List { result = val[0] }
| ListUnpack { result = val[0] }
| DictGet { result = val[0] }
| ListOrDictGetAssign { result = val[0] }
;
# retrieving the value of a variable
VariableRetrieval:
SimpleVariableRetrieval { result = val[0] }
| SPECIAL_VAR_PREFIX IDENTIFIER { result = make_node(val) { |v| Riml::GetSpecialVariableNode.new(v[0], v[1]) } }
| ScopeModifierLiteral ListOrDictGetWithBrackets { result = make_node(val) { |v| Riml::GetVariableByScopeAndDictNameNode.new(v[0], v[1]) } }
;
SimpleVariableRetrieval:
Scope IDENTIFIER { result = make_node(val) { |v| Riml::GetVariableNode.new(v[0], v[1]) } }
;
AllVariableRetrieval:
VariableRetrieval { result = val[0] }
| Scope CurlyBraceName { result = make_node(val) { |v| Riml::GetCurlyBraceNameNode.new(v[0], v[1]) } }
;
UnletVariable:
UNLET VariableRetrieval { result = make_node(val) { |v| Riml::UnletVariableNode.new('!', [ v[1] ]) } }
| UNLET_BANG VariableRetrieval { result = make_node(val) { |v| Riml::UnletVariableNode.new('!', [ v[1] ]) } }
| UnletVariable VariableRetrieval { result = val[0] << val[1] }
;
CurlyBraceName:
CurlyBraceVarPart { result = make_node(val) { |v| Riml::CurlyBraceVariable.new([ v[0] ]) } }
| IDENTIFIER CurlyBraceName { result = make_node(val) { |v| Riml::CurlyBraceVariable.new([ Riml::CurlyBracePart.new(v[0]), v[1] ]) } }
| CurlyBraceName IDENTIFIER { result = val[0] << make_node(val) { |v| Riml::CurlyBracePart.new(v[1]) } }
| CurlyBraceName CurlyBraceVarPart { result = val[0] << val[1] }
;
CurlyBraceVarPart:
'{' PossibleStringValue '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new(v[1]) } }
| '{' PossibleStringValue CurlyBraceVarPart '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new([v[1], v[2]]) } }
| '{' CurlyBraceVarPart PossibleStringValue '}' { result = make_node(val) { |v| Riml::CurlyBracePart.new([v[1], v[2]]) } }
;
# Method definition
# [SID, scope_modifier, name, parameters, keyword, expressions]
Def:
FunctionType SIDAndScope DefCallIdentifier DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], [], v[3], v[4]) } }
| FunctionType SIDAndScope DefCallIdentifier '(' ParamList ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], v[4], v[6], v[7]) } }
| FunctionType SIDAndScope DefCallIdentifier '(' SPLAT_PARAM ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], [v[4]], v[6], v[7]) } }
| FunctionType SIDAndScope DefCallIdentifier '(' ParamList ',' SPLAT_PARAM ')' DefKeywords Block END { result = make_node(val) { |v| Riml.const_get(val[0]).new('!', v[1][0], v[1][1], v[2], v[4] << v[6], v[8], v[9]) } }
;
FunctionType:
DEF { result = "DefNode" }
| DEF_BANG { result = "DefNode" }
| DEFM { result = "DefMethodNode" }
;
DefCallIdentifier:
# use '' for first argument instead of nil in order to avoid a double scope-modifier
CurlyBraceName { result = make_node(val) { |v| Riml::GetCurlyBraceNameNode.new('', v[0]) } }
| IDENTIFIER { result = val[0] }
;
# Example: 'range', 'dict' or 'abort' after function definition
DefKeywords:
IDENTIFIER { result = [val[0]] }
| DefKeywords IDENTIFIER { result = val[0] << val[1] }
| /* nothing */ { result = nil }
;
ParamList:
/* nothing */ { result = [] }
| IDENTIFIER { result = val }
| DefaultParam { result = val }
| ParamList ',' IDENTIFIER { result = val[0] << val[2] }
| ParamList ',' DefaultParam { result = val[0] << val[2] }
;
DefaultParam:
IDENTIFIER '=' Expression { result = make_node(val) { |v| Riml::DefaultParamNode.new(v[0], v[2]) } }
;
Return:
RETURN Returnable { result = make_node(val) { |v| Riml::ReturnNode.new(v[1]) } }
| RETURN Returnable IF Expression { result = make_node(val) { |v| Riml::IfNode.new(v[3], Nodes.new([ReturnNode.new(v[1])])) } }
| RETURN Returnable UNLESS Expression { result = make_node(val) { |v| Riml::UnlessNode.new(v[3], Nodes.new([ReturnNode.new(v[1])])) } }
;
Returnable:
/* nothing */ { result = nil }
| Expression { result = val[0] }
;
EndScript:
FINISH { result = make_node(val) { |_| Riml::FinishNode.new } }
;
# [expression, expressions]
If:
IF Expression IfBlock END { result = make_node(val) { |v| Riml::IfNode.new(v[1], v[2]) } }
| IF Expression THEN Expression END { result = make_node(val) { |v| Riml::IfNode.new(v[1], Riml::Nodes.new([v[3]])) } }
| Expression IF Expression { result = make_node(val) { |v| Riml::IfNode.new(v[2], Riml::Nodes.new([v[0]])) } }
;
Unless:
UNLESS Expression IfBlock END { result = make_node(val) { |v| Riml::UnlessNode.new(v[1], v[2]) } }
| UNLESS Expression THEN Expression END { result = make_node(val) { |v| Riml::UnlessNode.new(v[1], Riml::Nodes.new([v[3]])) } }
| Expression UNLESS Expression { result = make_node(val) { |v| Riml::UnlessNode.new(v[2], Riml::Nodes.new([v[0]])) } }
;
Ternary:
Expression '?' Expression ':' Expression { result = make_node(val) { |v| Riml::TernaryOperatorNode.new([v[0], v[2], v[4]]) } }
;
While:
WHILE Expression Block END { result = make_node(val) { |v| Riml::WhileNode.new(v[1], v[2]) } }
;
LoopKeyword:
BREAK { result = make_node(val) { |_| Riml::BreakNode.new } }
| CONTINUE { result = make_node(val) { |_| Riml::ContinueNode.new } }
;
Until:
UNTIL Expression Block END { result = make_node(val) { |v| Riml::UntilNode.new(v[1], v[2]) } }
;
For:
FOR SimpleVariableRetrieval IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
| FOR List IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
| FOR ListUnpack IN Expression Block END { result = make_node(val) { |v| Riml::ForNode.new(v[1], v[3], v[4]) } }
;
Try:
TRY Block END { result = make_node(val) { |v| Riml::TryNode.new(v[1], nil, nil) } }
| TRY Block Catch END { result = make_node(val) { |v| Riml::TryNode.new(v[1], v[2], nil) } }
| TRY Block Catch FINALLY Block END { result = make_node(val) { |v| Riml::TryNode.new(v[1], v[2], v[4]) } }
;
Catch:
/* nothing */ { result = nil }
| CATCH Block { result = [ make_node(val) { |v| Riml::CatchNode.new(nil, v[1]) } ] }
| CATCH Catchable Block { result = [ make_node(val) { |v| Riml::CatchNode.new(v[1], v[2]) } ] }
| Catch CATCH Block { result = val[0] << make_node(val) { |v| Riml::CatchNode.new(nil, v[2]) } }
| Catch CATCH Catchable Block { result = val[0] << make_node(val) { |v| Riml::CatchNode.new(v[2], v[3]) } }
;
Catchable:
Regexp { result = val[0] }
| String { result = val[0] }
;
# [expressions]
# expressions list could contain an ElseNode, which contains expressions
# itself
Block:
NEWLINE Statements { result = val[1] }
| NEWLINE { result = make_node(val) { |_| Riml::Nodes.new([]) } }
;
IfBlock:
Block { result = val[0] }
| NEWLINE Statements ElseBlock { result = val[1] << val[2] }
| NEWLINE Statements ElseifBlock { result = val[1] << val[2] }
| NEWLINE Statements ElseifBlock ElseBlock { result = val[1] << val[2] << val[3] }
;
ElseBlock:
ELSE NEWLINE Statements { result = make_node(val) { |v| Riml::ElseNode.new(v[2]) } }
;
ElseifBlock:
ELSEIF Expression NEWLINE Statements { result = make_node(val) { |v| Riml::Nodes.new([Riml::ElseifNode.new(v[1], v[3])]) } }
| ElseifBlock ELSEIF Expression NEWLINE Statements { result = val[0] << make_node(val) { |v| Riml::ElseifNode.new(v[2], v[4]) } }
;
ClassDefinition:
CLASS Scope IDENTIFIER Block END { result = make_node(val) { |v| Riml::ClassDefinitionNode.new(v[1], v[2], nil, v[3]) } }
| CLASS Scope IDENTIFIER '<' Scope IDENTIFIER Block END { result = make_node(val) { |v| Riml::ClassDefinitionNode.new(v[1], v[2], (v[4] || ClassDefinitionNode::DEFAULT_SCOPE_MODIFIER) + v[5], v[6]) } }
;
ObjectInstantiation:
NEW ObjectInstantiationCall { result = make_node(val) { |v| Riml::ObjectInstantiationNode.new(v[1]) } }
;
Super:
SUPER '(' ArgListWithSplat ')' { result = make_node(val) { |v| Riml::SuperNode.new(v[2], true) } }
| SUPER { result = make_node(val) { |_| Riml::SuperNode.new([], false) } }
;
ExLiteral:
EX_LITERAL { result = make_node(val) { |v| Riml::ExLiteralNode.new(v[0]) } }
;
end
---- header
require File.expand_path("../lexer", __FILE__)
require File.expand_path("../nodes", __FILE__)
require File.expand_path("../errors", __FILE__)
require File.expand_path("../ast_rewriter", __FILE__)
---- inner
# This code will be put as-is in the parser class
attr_accessor :ast_rewriter
attr_writer :options
# The Parser and AST_Rewriter share this same hash of options
def options
@options ||= {}
end
def self.ast_cache
@ast_cache
end
@ast_cache = {}
# parses tokens or code into output nodes
def parse(object, ast_rewriter = Riml::AST_Rewriter.new, filename = nil, included = false)
if (ast = self.class.ast_cache[filename])
else
if tokens?(object)
@tokens = object
elsif code?(object)
@lexer = Riml::Lexer.new(object, filename, true)
end
begin
ast = do_parse
rescue Racc::ParseError => e
raise unless @lexer
if (invalid_token = @lexer.prev_token_is_keyword?)
warning = "#{invalid_token.inspect} is a keyword, and cannot " \
"be used as a variable name"
end
error_msg = e.message
error_msg << "\nWARNING: #{warning}" if warning
error = Riml::ParseError.new(error_msg, @lexer.filename, @lexer.lineno)
raise error
end
self.class.ast_cache[filename] = ast if filename
end
@ast_rewriter ||= ast_rewriter
return ast unless @ast_rewriter
@ast_rewriter.ast = ast.dup
@ast_rewriter.options ||= options
@ast_rewriter.rewrite(filename, included)
@ast_rewriter.ast
end
# get the next token from either the list of tokens provided, or
# the lexer getting the next token
def next_token
return @tokens.shift unless @lexer
token = @lexer.next_token
if token && @lexer.parser_info
@current_parser_info = token.pop
end
token
end
private
def tokens?(object)
Array === object
end
def code?(object)
String === object
end
def make_node(racc_val)
node = yield racc_val
node.parser_info = @current_parser_info
node
end

14
test/racc/assets/rrconf.y Normal file
View file

@ -0,0 +1,14 @@
# 1 s/r conflict and 1 r/r conflict
class A
rule
target: a
a :
| a list
list :
| list ITEM
end

1943
test/racc/assets/ruby18.y Normal file

File diff suppressed because it is too large Load diff

2174
test/racc/assets/ruby19.y Normal file

File diff suppressed because it is too large Load diff

2350
test/racc/assets/ruby20.y Normal file

File diff suppressed because it is too large Load diff

2359
test/racc/assets/ruby21.y Normal file

File diff suppressed because it is too large Load diff

2381
test/racc/assets/ruby22.y Normal file

File diff suppressed because it is too large Load diff

72
test/racc/assets/scan.y Normal file
View file

@ -0,0 +1,72 @@
class P
rule
a: A
{
# comment test
# comment test
# string
@sstring = 'squote string'
@dstring = 'dquote string'
# regexp
@regexp = /some regexp with spaces/
# gvar
/regexp/ === 'some regexp matches to this string'
@pre_match = $`
@matched = $&
@post_match = $'
@m = $~
# braces
@array = []
[1,2,3].each {|i|
@array.push i
}
3.times { @array.push 10 }
}
end
---- inner
def parse
@sstring = @dstring = nil
@regexp = nil
@pre_match = @matched = @post_match = @m = nil
@src = [[:A, 'A'], [false, '$']]
do_parse
assert_equal 'squote string', @sstring
assert_equal 'dquote string', @dstring
assert_equal(/some regexp with spaces/, @regexp)
assert_equal 'some ', @pre_match
assert_equal 'regexp', @matched
assert_equal ' matches to this string', @post_match
assert_instance_of MatchData, @m
end
def assert_equal(ok, data)
unless ok == data
raise "expected <#{ok.inspect}> but is <#{data.inspect}>"
end
end
def assert_instance_of(klass, obj)
unless obj.instance_of?(klass)
raise "expected #{klass} but is #{obj.class}"
end
end
def next_token
@src.shift
end
---- footer
P.new.parse

50
test/racc/assets/syntax.y Normal file
View file

@ -0,0 +1,50 @@
#
# racc syntax checker
#
class M1::M2::ParserClass < S1::S2::SuperClass
token A
| B C
convert
A '5'
end
prechigh
left B
preclow
start target
expect 0
rule
target: A B C
{
print 'abc'
}
| B C A
| C B A
{
print 'cba'
}
| cont
cont : A c2 B c2 C
c2 : C C C C C
end
---- inner
junk code !!!!
kjaljlajrlaolanbla /// %%% (*((( token rule
akiurtlajluealjflaj @@@@ end end end end __END__
laieu2o879urkq96ga(Q#*&%Q#
#&lkji END
q395q?/// liutjqlkr7

622
test/racc/assets/tp_plus.y Normal file
View file

@ -0,0 +1,622 @@
# Released under an MIT License (http://www.opensource.org/licenses/MIT)
# By Jay Strybis (https://github.com/unreal)
class TPPlus::Parser
token ASSIGN AT_SYM COMMENT JUMP IO_METHOD INPUT OUTPUT
token NUMREG POSREG VREG SREG TIME_SEGMENT ARG UALM
token MOVE DOT TO AT TERM OFFSET SKIP GROUP
token SEMICOLON NEWLINE STRING
token REAL DIGIT WORD EQUAL
token EEQUAL NOTEQUAL GTE LTE LT GT BANG
token PLUS MINUS STAR SLASH DIV AND OR MOD
token IF ELSE END UNLESS FOR IN WHILE
token WAIT_FOR WAIT_UNTIL TIMEOUT AFTER
token FANUC_USE SET_SKIP_CONDITION NAMESPACE
token CASE WHEN INDIRECT POSITION
token EVAL TIMER TIMER_METHOD RAISE ABORT
token POSITION_DATA TRUE_FALSE RUN TP_HEADER PAUSE
token LPAREN RPAREN COLON COMMA LBRACK RBRACK LBRACE RBRACE
token LABEL ADDRESS
token false
prechigh
right BANG
left STAR SLASH DIV MOD
left PLUS MINUS
left GT GTE LT LTE
left EEQUAL NOTEQUAL
left AND
left OR
right EQUAL
preclow
rule
program
#: statements { @interpreter.nodes = val[0].flatten }
: statements { @interpreter.nodes = val[0] }
|
;
statements
: statement terminator {
result = [val[0]]
result << val[1] unless val[1].nil?
}
| statements statement terminator {
result = val[0] << val[1]
result << val[2] unless val[2].nil?
}
;
block
: NEWLINE statements { result = val[1] }
;
optional_newline
: NEWLINE
|
;
statement
: comment
| definition
| namespace
#| assignment
| motion_statement
#| jump
#| io_method
| label_definition
| address
| conditional
| inline_conditional
| forloop
| while_loop
#| program_call
| use_statement
| set_skip_statement
| wait_statement
| case_statement
| fanuc_eval
| timer_method
| position_data
| raise
| tp_header_definition
| empty_stmt
| PAUSE { result = PauseNode.new }
| ABORT { result = AbortNode.new }
;
empty_stmt
: NEWLINE { result = EmptyStmtNode.new() }
;
tp_header_definition
: TP_HEADER EQUAL tp_header_value { result = HeaderNode.new(val[0],val[2]) }
;
tp_header_value
: STRING
| TRUE_FALSE
;
raise
: RAISE var_or_indirect { result = RaiseNode.new(val[1]) }
;
timer_method
: TIMER_METHOD var_or_indirect { result = TimerMethodNode.new(val[0],val[1]) }
;
fanuc_eval
: EVAL STRING { result = EvalNode.new(val[1]) }
;
wait_statement
: WAIT_FOR LPAREN indirectable COMMA STRING RPAREN
{ result = WaitForNode.new(val[2], val[4]) }
| WAIT_UNTIL LPAREN expression RPAREN
{ result = WaitUntilNode.new(val[2], nil) }
| WAIT_UNTIL LPAREN expression RPAREN DOT wait_modifier
{ result = WaitUntilNode.new(val[2],val[5]) }
| WAIT_UNTIL LPAREN expression RPAREN DOT wait_modifier DOT wait_modifier
{ result = WaitUntilNode.new(val[2],val[5].merge(val[7])) }
;
wait_modifier
: timeout_modifier
| after_modifier
;
timeout_modifier
: swallow_newlines TIMEOUT LPAREN label RPAREN
{ result = { label: val[3] } }
;
after_modifier
: swallow_newlines AFTER LPAREN indirectable COMMA STRING RPAREN
{ result = { timeout: [val[3],val[5]] } }
;
label
: LABEL { result = val[0] }
;
use_statement
: FANUC_USE indirectable { result = UseNode.new(val[0],val[1]) }
;
# set_skip_condition x
set_skip_statement
: SET_SKIP_CONDITION expression { result = SetSkipNode.new(val[1]) }
;
program_call
: WORD LPAREN args RPAREN { result = CallNode.new(val[0],val[2]) }
| RUN WORD LPAREN args RPAREN { result = CallNode.new(val[1],val[3],async: true) }
;
args
: arg { result = [val[0]] }
| args COMMA arg { result = val[0] << val[2] }
| { result = [] }
;
arg
: number
| var
| string
| address
;
string
: STRING { result = StringNode.new(val[0]) }
;
io_method
: IO_METHOD var_or_indirect { result = IOMethodNode.new(val[0],val[1]) }
| IO_METHOD LPAREN var_or_indirect RPAREN
{ result = IOMethodNode.new(val[0],val[2]) }
| IO_METHOD LPAREN var_or_indirect COMMA number COMMA STRING RPAREN
{ result = IOMethodNode.new(val[0],val[2],{ pulse_time: val[4], pulse_units: val[6] }) }
;
var_or_indirect
: var
| indirect_thing
;
jump
: JUMP label { result = JumpNode.new(val[1]) }
;
conditional
: IF expression block else_block END
{ result = ConditionalNode.new("if",val[1],val[2],val[3]) }
| UNLESS expression block else_block END
{ result = ConditionalNode.new("unless",val[1],val[2],val[3]) }
;
forloop
: FOR var IN LPAREN minmax_val TO minmax_val RPAREN block END
{ result = ForNode.new(val[1],val[4],val[6],val[8]) }
;
while_loop
: WHILE expression block END { result = WhileNode.new(val[1],val[2]) }
;
minmax_val
: integer
| var
;
namespace
: NAMESPACE WORD block END { result = NamespaceNode.new(val[1],val[2]) }
;
case_statement
: CASE var swallow_newlines
case_conditions
case_else
END { result = CaseNode.new(val[1],val[3],val[4]) }
;
case_conditions
: case_condition { result = val }
| case_conditions case_condition
{ result = val[0] << val[1] << val[2] }
;
case_condition
: WHEN case_allowed_condition swallow_newlines case_allowed_statement
terminator { result = CaseConditionNode.new(val[1],val[3]) }
;
case_allowed_condition
: number
| var
;
case_else
: ELSE swallow_newlines case_allowed_statement terminator
{ result = CaseConditionNode.new(nil,val[2]) }
|
;
case_allowed_statement
: program_call
| jump
;
inline_conditional
: inlineable
| inlineable IF expression { result = InlineConditionalNode.new(val[1], val[2], val[0]) }
| inlineable UNLESS expression { result = InlineConditionalNode.new(val[1], val[2], val[0]) }
;
inlineable
: jump
| assignment
| io_method
| program_call
;
else_block
: ELSE block { result = val[1] }
| { result = [] }
;
motion_statement
: MOVE DOT swallow_newlines TO LPAREN var RPAREN motion_modifiers
{ result = MotionNode.new(val[0],val[5],val[7]) }
;
motion_modifiers
: motion_modifier { result = val }
| motion_modifiers motion_modifier
{ result = val[0] << val[1] }
;
motion_modifier
: DOT swallow_newlines AT LPAREN speed RPAREN
{ result = SpeedNode.new(val[4]) }
| DOT swallow_newlines TERM LPAREN valid_terminations RPAREN
{ result = TerminationNode.new(val[4]) }
| DOT swallow_newlines OFFSET LPAREN var RPAREN
{ result = OffsetNode.new(val[2],val[4]) }
| DOT swallow_newlines TIME_SEGMENT LPAREN time COMMA time_seg_actions RPAREN
{ result = TimeNode.new(val[2],val[4],val[6]) }
| DOT swallow_newlines SKIP LPAREN label optional_lpos_arg RPAREN
{ result = SkipNode.new(val[4],val[5]) }
;
valid_terminations
: integer
| var
| MINUS DIGIT {
raise Racc::ParseError, sprintf("\ninvalid termination type: (%s)", val[1]) if val[1] != 1
result = DigitNode.new(val[1].to_i * -1)
}
;
optional_lpos_arg
: COMMA var { result = val[1] }
|
;
indirectable
: number
| var
;
time_seg_actions
: program_call
| io_method
;
time
: var
| number
;
speed
: indirectable COMMA STRING { result = { speed: val[0], units: val[2] } }
| STRING { result = { speed: val[0], units: nil } }
;
label_definition
: label { result = LabelDefinitionNode.new(val[0]) }#@interpreter.add_label(val[1]) }
;
definition
: WORD ASSIGN definable { result = DefinitionNode.new(val[0],val[2]) }
;
assignment
: var_or_indirect EQUAL expression { result = AssignmentNode.new(val[0],val[2]) }
| var_or_indirect PLUS EQUAL expression { result = AssignmentNode.new(
val[0],
ExpressionNode.new(val[0],"+",val[3])
)
}
| var_or_indirect MINUS EQUAL expression { result = AssignmentNode.new(
val[0],
ExpressionNode.new(val[0],"-",val[3])
)
}
;
var
: var_without_namespaces
| var_with_namespaces
;
var_without_namespaces
: WORD { result = VarNode.new(val[0]) }
| WORD var_method_modifiers { result = VarMethodNode.new(val[0],val[1]) }
;
var_with_namespaces
: namespaces var_without_namespaces
{ result = NamespacedVarNode.new(val[0],val[1]) }
;
var_method_modifiers
: var_method_modifier { result = val[0] }
| var_method_modifiers var_method_modifier
{ result = val[0].merge(val[1]) }
;
var_method_modifier
: DOT swallow_newlines WORD { result = { method: val[2] } }
| DOT swallow_newlines GROUP LPAREN integer RPAREN
{ result = { group: val[4] } }
;
namespaces
: ns { result = [val[0]] }
| namespaces ns { result = val[0] << val[1] }
;
ns
: WORD COLON COLON { result = val[0] }
;
expression
: unary_expression
| binary_expression
;
unary_expression
: factor { result = val[0] }
| address
| BANG factor { result = ExpressionNode.new(val[1], "!", nil) }
;
binary_expression
: expression operator expression
{ result = ExpressionNode.new(val[0], val[1], val[2]) }
;
operator
: EEQUAL { result = "==" }
| NOTEQUAL { result = "<>" }
| LT { result = "<" }
| GT { result = ">" }
| GTE { result = ">=" }
| LTE { result = "<=" }
| PLUS { result = "+" }
| MINUS { result = "-" }
| OR { result = "||" }
| STAR { result = "*" }
| SLASH { result = "/" }
| DIV { result = "DIV" }
| MOD { result = "%" }
| AND { result = "&&" }
;
factor
: number
| signed_number
| var
| indirect_thing
| paren_expr
;
paren_expr
: LPAREN expression RPAREN { result = ParenExpressionNode.new(val[1]) }
;
indirect_thing
: INDIRECT LPAREN STRING COMMA indirectable RPAREN
{ result = IndirectNode.new(val[2].to_sym, val[4]) }
;
signed_number
: sign DIGIT {
val[1] = val[1].to_i * -1 if val[0] == "-"
result = DigitNode.new(val[1])
}
| sign REAL { val[1] = val[1].to_f * -1 if val[0] == "-"; result = RealNode.new(val[1]) }
;
sign
: MINUS { result = "-" }
;
number
: integer
| REAL { result = RealNode.new(val[0]) }
;
integer
: DIGIT { result = DigitNode.new(val[0]) }
;
definable
: numreg
| output
| input
| posreg
| position
| vreg
| number
| signed_number
| argument
| timer
| ualm
| sreg
;
sreg
: SREG LBRACK DIGIT RBRACK { result = StringRegisterNode.new(val[2].to_i) }
;
ualm
: UALM LBRACK DIGIT RBRACK { result = UserAlarmNode.new(val[2].to_i) }
;
timer
: TIMER LBRACK DIGIT RBRACK { result = TimerNode.new(val[2].to_i) }
;
argument
: ARG LBRACK DIGIT RBRACK { result = ArgumentNode.new(val[2].to_i) }
;
vreg
: VREG LBRACK DIGIT RBRACK { result = VisionRegisterNode.new(val[2].to_i) }
;
position
: POSITION LBRACK DIGIT RBRACK { result = PositionNode.new(val[2].to_i) }
;
numreg
: NUMREG LBRACK DIGIT RBRACK { result = NumregNode.new(val[2].to_i) }
;
posreg
: POSREG LBRACK DIGIT RBRACK { result = PosregNode.new(val[2].to_i) }
;
output
: OUTPUT LBRACK DIGIT RBRACK { result = IONode.new(val[0], val[2].to_i) }
;
input
: INPUT LBRACK DIGIT RBRACK { result = IONode.new(val[0], val[2].to_i) }
;
address
: ADDRESS { result = AddressNode.new(val[0]) }
;
comment
: COMMENT { result = CommentNode.new(val[0]) }
;
terminator
: NEWLINE { result = TerminatorNode.new }
| comment optional_newline { result = val[0] }
# ^-- consume newlines or else we will get an extra space from EmptyStmt in the output
| false
|
;
swallow_newlines
: NEWLINE { result = TerminatorNode.new }
|
;
position_data
: POSITION_DATA sn hash sn END
{ result = PositionDataNode.new(val[2]) }
;
sn
: swallow_newlines
;
hash
: LBRACE sn hash_attributes sn RBRACE { result = val[2] }
| LBRACE sn RBRACE { result = {} }
;
hash_attributes
: hash_attribute { result = val[0] }
| hash_attributes COMMA sn hash_attribute
{ result = val[0].merge(val[3]) }
;
hash_attribute
: STRING COLON hash_value { result = { val[0].to_sym => val[2] } }
;
hash_value
: STRING
| hash
| array
| optional_sign DIGIT { val[1] = val[1].to_i * -1 if val[0] == "-"; result = val[1] }
| optional_sign REAL { val[1] = val[1].to_f * -1 if val[0] == "-"; result = val[1] }
| TRUE_FALSE { result = val[0] == "true" }
;
optional_sign
: sign
|
;
array
: LBRACK sn array_values sn RBRACK { result = val[2] }
;
array_values
: array_value { result = val }
| array_values COMMA sn array_value { result = val[0] << val[3] }
;
array_value
: hash_value
;
end
---- inner
include TPPlus::Nodes
attr_reader :interpreter
def initialize(scanner, interpreter = TPPlus::Interpreter.new)
@scanner = scanner
@interpreter = interpreter
super()
end
def next_token
t = @scanner.next_token
@interpreter.line_count += 1 if t && t[0] == :NEWLINE
#puts t.inspect
t
end
def parse
#@yydebug =true
do_parse
@interpreter
end
def on_error(t, val, vstack)
raise ParseError, sprintf("Parse error on line #{@scanner.tok_line} column #{@scanner.tok_col}: %s (%s)",
val.inspect, token_to_str(t) || '?')
end
class ParseError < StandardError ; end

View file

@ -0,0 +1,278 @@
# Copyright 2008-2015 Takuto Wada
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class TwoWaySQL::Parser
rule
sql : stmt_list
{
result = RootNode.new( val[0] )
}
stmt_list :
{
result = []
}
| stmt_list stmt
{
result.push val[1]
}
stmt : primary
| if_stmt
| begin_stmt
begin_stmt : BEGIN stmt_list END
{
result = BeginNode.new( val[1] )
}
if_stmt : IF sub_stmt else_stmt END
{
result = IfNode.new( val[0][1], val[1], val[2] )
}
else_stmt : ELSE sub_stmt
{
result = val[1]
}
|
{
result = nil
}
sub_stmt : and_stmt
| or_stmt
| stmt_list
and_stmt : AND stmt_list
{
result = SubStatementNode.new( val[0][1], val[1] )
}
or_stmt : OR stmt_list
{
result = SubStatementNode.new( val[0][1], val[1] )
}
primary : IDENT
{
result = LiteralNode.new( val[0][1] )
}
| STRING_LITERAL
{
result = LiteralNode.new( val[0][1] )
}
| AND
{
result = LiteralNode.new( val[0][1] )
}
| OR
{
result = LiteralNode.new( val[0][1] )
}
| SPACES
{
result = WhiteSpaceNode.new( val[0][1], @preserve_space )
}
| COMMA
{
result = LiteralNode.new( val[0][1] )
}
| LPAREN
{
result = LiteralNode.new( val[0][1] )
}
| RPAREN
{
result = LiteralNode.new( val[0][1] )
}
| QUESTION
{
@num_questions += 1
result = QuestionNode.new( @num_questions )
}
| ACTUAL_COMMENT
{
result = ActualCommentNode.new( val[0][1] , val[0][2] )
}
| bind_var
| embed_var
bind_var : BIND_VARIABLE STRING_LITERAL
{
result = BindVariableNode.new( val[0][1] )
}
| BIND_VARIABLE SPACES STRING_LITERAL
{
result = BindVariableNode.new( val[0][1] )
}
| BIND_VARIABLE IDENT
{
result = BindVariableNode.new( val[0][1] )
}
| BIND_VARIABLE SPACES IDENT
{
result = BindVariableNode.new( val[0][1] )
}
| PAREN_BIND_VARIABLE
{
result = ParenBindVariableNode.new( val[0][1] )
}
embed_var : EMBED_VARIABLE IDENT
{
result = EmbedVariableNode.new( val[0][1] )
}
| EMBED_VARIABLE SPACES IDENT
{
result = EmbedVariableNode.new( val[0][1] )
}
end
---- inner
require 'strscan'
def initialize(opts={})
opts = {
:debug => false,
:preserve_space => true,
:preserve_comment => false
}.merge(opts)
@yydebug = opts[:debug]
@preserve_space = opts[:preserve_space]
@preserve_comment = opts[:preserve_comment]
@num_questions = 0
end
PAREN_EXAMPLE = '\([^\)]+\)'
BEGIN_BIND_VARIABLE = '(\/|\#)\*([^\*]+)\*\1'
BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*/
PAREN_BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*#{PAREN_EXAMPLE}/
EMBED_VARIABLE_PATTERN = /\A(\/|\#)\*\$([^\*]+)\*\1\s*/
CONDITIONAL_PATTERN = /\A(\/|\#)\*(IF)\s+([^\*]+)\s*\*\1/
BEGIN_END_PATTERN = /\A(\/|\#)\*(BEGIN|END)\s*\*\1/
STRING_LITERAL_PATTERN = /\A(\'(?:[^\']+|\'\')*\')/ ## quoted string
SPLIT_TOKEN_PATTERN = /\A(\S+?)(?=\s*(?:(?:\/|\#)\*|-{2,}|\(|\)|\,))/ ## stop on delimiters --,/*,#*,',',(,)
LITERAL_PATTERN = /\A([^;\s]+)/
SPACES_PATTERN = /\A(\s+)/
QUESTION_PATTERN = /\A\?/
COMMA_PATTERN = /\A\,/
LPAREN_PATTERN = /\A\(/
RPAREN_PATTERN = /\A\)/
ACTUAL_COMMENT_PATTERN = /\A(\/|\#)\*(\s{1,}(?:.*?))\*\1/m ## start with spaces
SEMICOLON_AT_INPUT_END_PATTERN = /\A\;\s*\Z/
UNMATCHED_COMMENT_START_PATTERN = /\A(?:(?:\/|\#)\*)/
#TODO: remove trailing spaces for S2Dao compatibility, but this spec sometimes causes SQL bugs...
ELSE_PATTERN = /\A\-{2,}\s*ELSE\s*/
AND_PATTERN = /\A(\ *AND)\b/i
OR_PATTERN = /\A(\ *OR)\b/i
def parse( io )
@q = []
io.each_line(nil) do |whole|
@s = StringScanner.new(whole)
end
scan_str
# @q.push [ false, nil ]
@q.push [ false, [@s.pos, nil] ]
## call racc's private parse method
do_parse
end
## called by racc
def next_token
@q.shift
end
def scan_str
until @s.eos? do
case
when @s.scan(AND_PATTERN)
@q.push [ :AND, [@s.pos, @s[1]] ]
when @s.scan(OR_PATTERN)
@q.push [ :OR, [@s.pos, @s[1]] ]
when @s.scan(SPACES_PATTERN)
@q.push [ :SPACES, [@s.pos, @s[1]] ]
when @s.scan(QUESTION_PATTERN)
@q.push [ :QUESTION, [@s.pos, nil] ]
when @s.scan(COMMA_PATTERN)
@q.push [ :COMMA, [@s.pos, ','] ]
when @s.scan(LPAREN_PATTERN)
@q.push [ :LPAREN, [@s.pos, '('] ]
when @s.scan(RPAREN_PATTERN)
@q.push [ :RPAREN, [@s.pos, ')'] ]
when @s.scan(ELSE_PATTERN)
@q.push [ :ELSE, [@s.pos, nil] ]
when @s.scan(ACTUAL_COMMENT_PATTERN)
@q.push [ :ACTUAL_COMMENT, [@s.pos, @s[1], @s[2]] ] if @preserve_comment
when @s.scan(BEGIN_END_PATTERN)
@q.push [ @s[2].intern, [@s.pos, nil] ]
when @s.scan(CONDITIONAL_PATTERN)
@q.push [ @s[2].intern, [@s.pos, @s[3]] ]
when @s.scan(EMBED_VARIABLE_PATTERN)
@q.push [ :EMBED_VARIABLE, [@s.pos, @s[2]] ]
when @s.scan(PAREN_BIND_VARIABLE_PATTERN)
@q.push [ :PAREN_BIND_VARIABLE, [@s.pos, @s[2]] ]
when @s.scan(BIND_VARIABLE_PATTERN)
@q.push [ :BIND_VARIABLE, [@s.pos, @s[2]] ]
when @s.scan(STRING_LITERAL_PATTERN)
@q.push [ :STRING_LITERAL, [@s.pos, @s[1]] ]
when @s.scan(SPLIT_TOKEN_PATTERN)
@q.push [ :IDENT, [@s.pos, @s[1]] ]
when @s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
raise Racc::ParseError, "unmatched comment. line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
when @s.scan(LITERAL_PATTERN) ## other string token
@q.push [ :IDENT, [@s.pos, @s[1]] ]
when @s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
#drop semicolon at input end
else
raise Racc::ParseError, "syntax error at or near line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
end
end
end
## override racc's default on_error method
def on_error(t, v, vstack)
## cursor in value-stack is an array of two items,
## that have position value as 0th item. like [731, "ctx[:limit] "]
cursor = vstack.find do |tokens|
tokens.size == 2 and tokens[0].kind_of?(Fixnum)
end
pos = cursor[0]
line = line_no(pos)
rest = @s.string[pos .. -1]
raise Racc::ParseError, "syntax error at or near line:[#{line}], str:[#{rest}]"
end
def line_no(pos)
lines = 0
scanned = @s.string[0..(pos)]
scanned.each_line { lines += 1 }
lines
end

View file

@ -0,0 +1,5 @@
# unterminated action
class A
rule
a: A {

View file

@ -0,0 +1,12 @@
class A
token A B C X
rule
targ : A list B
| A B C
list: list X
end

46
test/racc/assets/yyerr.y Normal file
View file

@ -0,0 +1,46 @@
#
# yyerror/yyerrok/yyaccept test
#
class A
rule
target: a b c
a:
{
yyerror
raise ArgumentError, "yyerror failed"
}
| error
b:
{
yyerrok
}
c:
{
yyaccept
raise ArgumentError, "yyaccept failed"
}
end
---- inner
def parse
do_parse
end
def next_token
[false, '$end']
end
def on_error( *args )
$stderr.puts "on_error called: args=#{args.inspect}"
end
---- footer
A.new.parse

36
test/racc/bench.y Normal file
View file

@ -0,0 +1,36 @@
class BenchmarkParser
rule
target: a a a a a a a a a a;
a: b b b b b b b b b b;
b: c c c c c c c c c c;
c: d d d d d d d d d d;
d: e e e e e e e e e e;
end
---- inner
def initialize
@old = [ :e, 'e' ]
@i = 0
end
def next_token
return [false, '$'] if @i >= 10_0000
@i += 1
@old
end
def parse
do_parse
end
---- footer
require 'benchmark'
Benchmark.bm do |x|
x.report { BenchmarkParser.new.parse }
end

104
test/racc/helper.rb Normal file
View file

@ -0,0 +1,104 @@
$VERBOSE = true
require 'minitest/autorun'
require 'racc/static'
require 'fileutils'
require 'tempfile'
require 'timeout'
module Racc
class TestCase < MiniTest::Unit::TestCase
PROJECT_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
TEST_DIR = File.join(PROJECT_DIR, 'test')
RACC = File.join(PROJECT_DIR, 'bin', 'racc')
OUT_DIR = File.join(TEST_DIR, 'out')
TAB_DIR = File.join(TEST_DIR, 'tab') # generated parsers go here
LOG_DIR = File.join(TEST_DIR, 'log')
ERR_DIR = File.join(TEST_DIR, 'err')
ASSET_DIR = File.join(TEST_DIR, 'assets') # test grammars
REGRESS_DIR = File.join(TEST_DIR, 'regress') # known-good generated outputs
INC = [
File.join(PROJECT_DIR, 'lib'),
File.join(PROJECT_DIR, 'ext'),
].join(':')
def setup
[OUT_DIR, TAB_DIR, LOG_DIR, ERR_DIR].each do |dir|
FileUtils.mkdir_p(dir)
end
end
def teardown
[OUT_DIR, TAB_DIR, LOG_DIR, ERR_DIR].each do |dir|
FileUtils.rm_rf(dir)
end
end
def assert_compile(asset, args = [])
file = File.basename(asset, '.y')
args = ([args].flatten) + [
"#{ASSET_DIR}/#{file}.y",
'-Do',
"-O#{OUT_DIR}/#{file}",
"-o#{TAB_DIR}/#{file}",
]
racc "#{args.join(' ')}"
end
def assert_debugfile(asset, ok)
file = File.basename(asset, '.y')
Dir.chdir(TEST_DIR) do
File.foreach("log/#{file}.y") do |line|
line.strip!
case line
when /sr/ then assert_equal "sr#{ok[0]}", line
when /rr/ then assert_equal "rr#{ok[1]}", line
when /un/ then assert_equal "un#{ok[2]}", line
when /ur/ then assert_equal "ur#{ok[3]}", line
when /ex/ then assert_equal "ex#{ok[4]}", line
else
raise TestFailed, 'racc outputs unknown debug report???'
end
end
end
end
def assert_exec(asset)
file = File.basename(asset, '.y')
Dir.chdir(TEST_DIR) do
ruby("#{TAB_DIR}/#{file}")
end
end
def strip_version(source)
source.sub(/This file is automatically generated by Racc \d+\.\d+\.\d+/, '')
end
def assert_output_unchanged(asset)
file = File.basename(asset, '.y')
expected = File.read("#{REGRESS_DIR}/#{file}")
actual = File.read("#{TAB_DIR}/#{file}")
result = (strip_version(expected) == strip_version(actual))
assert(result, "Output of test/assets/#{file}.y differed from " \
"expectation. Try compiling it and diff with test/regress/#{file}.")
end
def racc(arg)
ruby "-S #{RACC} #{arg}"
end
def ruby(arg)
Dir.chdir(TEST_DIR) do
Tempfile.open 'test' do |io|
cmd = "#{ENV['_'] || Gem.ruby} -I #{INC} #{arg} 2>#{io.path}"
result = system(cmd)
assert(result, io.read)
end
end
end
end
end

8
test/racc/infini.y Normal file
View file

@ -0,0 +1,8 @@
class I
rule
list: list X
end

View file

@ -0,0 +1,7 @@
These files are "known-good" compiler output, generated from a stable version of
Racc. Whenever Racc is refactored, or changes are made which should not affect the
compiler output, running "rake test" checks that the compiler output is exactly
the same as these files.
If a change is made which *should* change the compiler output, these files will
have to be regenerated from the source in test/assets, and the results committed.

796
test/racc/regress/cadenza Normal file
View file

@ -0,0 +1,796 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
require 'racc/parser.rb'
# racc_parser.rb : generated by racc
module Cadenza
class RaccParser < Racc::Parser
module_eval(<<'...end cadenza.y/module_eval...', 'cadenza.y', 171)
...end cadenza.y/module_eval...
##### State transition tables begin ###
racc_action_table = [
37, 89, 90, 20, 21, 22, 23, 24, 121, 3,
89, 4, 72, 37, 71, 3, 68, 39, 3, 29,
43, 37, 65, 66, 33, 9, 34, 110, 74, 50,
35, 9, 37, 36, 9, 122, 38, 33, 128, 34,
77, 78, 79, 35, 89, 33, 36, 34, 37, 38,
3, 35, 46, 17, 36, 85, 33, 38, 34, 37,
76, 103, 35, 75, 83, 36, 9, 131, 38, 54,
55, 3, 33, 4, 34, 124, 78, 79, 35, 65,
66, 36, 67, 33, 38, 34, 125, 9, 109, 35,
56, 57, 36, 54, 55, 38, 20, 21, 22, 23,
24, 20, 21, 22, 23, 24, 20, 21, 22, 23,
24, 108, 29, 65, 66, 54, 55, 29, 56, 57,
111, 107, 29, 20, 21, 22, 23, 24, 20, 21,
22, 23, 24, 20, 21, 22, 23, 24, 112, 29,
3, 113, 116, 114, 29, 115, 3, 103, 39, 29,
20, 21, 22, 23, 24, 120, 9, 20, 21, 22,
23, 24, 9, 3, nil, 4, 29, 3, 3, 43,
46, nil, 3, 29, 87, 3, 3, 4, 116, 9,
56, 57, nil, 9, 9, 56, 57, 3, 9, 116,
nil, 9, 9, 20, 21, 22, 23, 24, 20, 21,
22, 23, 24, 9, 65, 66, 56, 57, nil, 29,
56, 57, 106, nil, 29, 58, 59, 60, 61, 62,
63, 58, 59, 60, 61, 62, 63, 20, 21, 22,
23, 24, 20, 21, 22, 23, 24, 20, 21, 22,
23, 24, 20, 21, 22, 23, 24, 20, 21, 22,
23, 24, 20, 21, 22, 23, 24, 20, 21, 22,
23, 24, 20, 21, 22, 23, 24, 20, 21, 22,
23, 24, 20, 21, 22, 23, 24, 20, 21, 22,
23, 24, 56, 57, 65, 66 ]
racc_action_check = [
87, 73, 53, 37, 37, 37, 37, 37, 85, 2,
52, 2, 36, 39, 35, 5, 32, 5, 6, 37,
6, 46, 51, 51, 87, 2, 87, 73, 37, 17,
87, 5, 43, 87, 6, 87, 87, 39, 122, 39,
39, 39, 39, 39, 129, 46, 39, 46, 116, 39,
7, 46, 7, 1, 46, 46, 43, 46, 43, 4,
38, 125, 43, 38, 43, 43, 7, 126, 43, 26,
26, 8, 116, 8, 116, 103, 116, 116, 116, 31,
31, 116, 31, 4, 116, 4, 105, 8, 72, 4,
27, 27, 4, 93, 93, 4, 24, 24, 24, 24,
24, 33, 33, 33, 33, 33, 34, 34, 34, 34,
34, 71, 24, 70, 70, 94, 94, 33, 95, 95,
75, 70, 34, 108, 108, 108, 108, 108, 89, 89,
89, 89, 89, 124, 124, 124, 124, 124, 76, 108,
118, 77, 118, 78, 89, 79, 41, 67, 41, 124,
3, 3, 3, 3, 3, 83, 118, 20, 20, 20,
20, 20, 41, 42, nil, 42, 3, 45, 48, 45,
48, nil, 49, 20, 49, 0, 82, 0, 82, 42,
96, 96, nil, 45, 48, 97, 97, 81, 49, 81,
nil, 0, 82, 65, 65, 65, 65, 65, 66, 66,
66, 66, 66, 81, 69, 69, 98, 98, nil, 65,
99, 99, 69, nil, 66, 28, 28, 28, 28, 28,
28, 64, 64, 64, 64, 64, 64, 57, 57, 57,
57, 57, 29, 29, 29, 29, 29, 58, 58, 58,
58, 58, 59, 59, 59, 59, 59, 63, 63, 63,
63, 63, 54, 54, 54, 54, 54, 55, 55, 55,
55, 55, 56, 56, 56, 56, 56, 61, 61, 61,
61, 61, 62, 62, 62, 62, 62, 60, 60, 60,
60, 60, 100, 100, 123, 123 ]
racc_action_pointer = [
151, 53, -15, 147, 56, -9, -6, 26, 47, nil,
nil, nil, nil, nil, nil, nil, nil, 29, nil, nil,
154, nil, nil, nil, 93, nil, 60, 79, 202, 229,
nil, 59, -9, 98, 103, 11, 9, 0, 57, 10,
nil, 122, 139, 29, nil, 143, 18, nil, 144, 148,
nil, 2, 8, -6, 249, 254, 259, 224, 234, 239,
274, 264, 269, 244, 208, 190, 195, 144, nil, 184,
93, 77, 60, -1, nil, 92, 110, 113, 115, 117,
nil, 163, 152, 127, nil, -20, nil, -3, nil, 125,
nil, nil, nil, 84, 106, 107, 169, 174, 195, 199,
271, nil, nil, 53, nil, 63, nil, nil, 120, nil,
nil, nil, nil, nil, nil, nil, 45, nil, 116, nil,
nil, nil, 10, 264, 130, 58, 39, nil, nil, 42,
nil, nil ]
racc_action_default = [
-2, -70, -1, -70, -70, -70, -70, -70, -70, -60,
-61, -62, -63, -64, -65, -66, -68, -70, -67, -69,
-5, -7, -8, -9, -70, -11, -14, -17, -24, -70,
-26, -33, -70, -70, -70, -70, -70, -70, -70, -70,
-41, -70, -70, -70, -48, -70, -70, -52, -70, -70,
132, -3, -6, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -25, -70, -70, -70, -35, -70,
-70, -70, -70, -70, -54, -70, -70, -70, -70, -70,
-42, -70, -70, -70, -49, -70, -53, -70, -57, -70,
-10, -12, -13, -15, -16, -18, -19, -20, -21, -22,
-23, -27, -28, -29, -31, -34, -36, -37, -70, -50,
-55, -58, -59, -38, -39, -40, -70, -44, -70, -43,
-47, -51, -70, -4, -70, -70, -70, -45, -56, -30,
-32, -46 ]
racc_goto_table = [
18, 40, 19, 32, 104, 51, 52, 105, 2, 88,
47, 101, 102, 41, 45, 48, 49, 44, 69, 70,
1, 42, 51, 73, 53, 95, 96, 97, 98, 99,
100, 91, 92, 93, 94, 64, nil, 80, nil, 18,
nil, 19, nil, 18, nil, 19, 18, 18, 19, 19,
82, 86, nil, nil, nil, nil, 84, 81, nil, nil,
nil, nil, 130, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, 123, nil, nil, 117, 119, nil,
18, nil, 19, nil, nil, nil, nil, nil, nil, 118,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, 126, 51,
129, nil, nil, nil, 127, nil, 18, nil, 19 ]
racc_goto_check = [
28, 16, 27, 6, 11, 4, 3, 12, 2, 25,
22, 10, 10, 2, 2, 2, 2, 19, 4, 4,
1, 15, 4, 3, 6, 8, 8, 8, 8, 8,
8, 5, 5, 7, 7, 9, nil, 16, nil, 28,
nil, 27, nil, 28, nil, 27, 28, 28, 27, 27,
2, 22, nil, nil, nil, nil, 19, 15, nil, nil,
nil, nil, 11, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, 4, nil, nil, 16, 16, nil,
28, nil, 27, nil, nil, nil, nil, nil, nil, 2,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, 6, 4,
3, nil, nil, nil, 16, nil, 28, nil, 27 ]
racc_goto_pointer = [
nil, 20, 8, -14, -15, -23, 0, -23, -33, 6,
-54, -63, -60, nil, nil, 16, -4, nil, nil, 11,
nil, nil, 3, nil, nil, -40, nil, 0, -2 ]
racc_goto_default = [
nil, nil, nil, nil, 31, 25, nil, 26, 27, 28,
30, nil, nil, 10, 5, nil, nil, 11, 6, nil,
12, 7, nil, 14, 8, nil, 13, 16, 15 ]
racc_reduce_table = [
0, 0, :racc_error,
1, 42, :_reduce_none,
0, 42, :_reduce_2,
1, 44, :_reduce_3,
3, 44, :_reduce_4,
1, 46, :_reduce_5,
2, 46, :_reduce_6,
1, 46, :_reduce_7,
1, 46, :_reduce_8,
1, 46, :_reduce_9,
3, 46, :_reduce_10,
1, 48, :_reduce_none,
3, 48, :_reduce_12,
3, 48, :_reduce_13,
1, 49, :_reduce_none,
3, 49, :_reduce_15,
3, 49, :_reduce_16,
1, 50, :_reduce_none,
3, 50, :_reduce_18,
3, 50, :_reduce_19,
3, 50, :_reduce_20,
3, 50, :_reduce_21,
3, 50, :_reduce_22,
3, 50, :_reduce_23,
1, 51, :_reduce_none,
2, 51, :_reduce_25,
1, 45, :_reduce_none,
3, 45, :_reduce_27,
3, 45, :_reduce_28,
1, 52, :_reduce_29,
3, 52, :_reduce_30,
1, 53, :_reduce_31,
3, 53, :_reduce_32,
1, 47, :_reduce_none,
3, 47, :_reduce_34,
3, 54, :_reduce_35,
4, 55, :_reduce_36,
4, 55, :_reduce_37,
3, 56, :_reduce_38,
3, 57, :_reduce_39,
3, 57, :_reduce_40,
2, 58, :_reduce_41,
3, 58, :_reduce_42,
4, 58, :_reduce_43,
4, 58, :_reduce_44,
5, 58, :_reduce_45,
6, 59, :_reduce_46,
3, 60, :_reduce_47,
2, 61, :_reduce_48,
3, 61, :_reduce_49,
4, 62, :_reduce_50,
3, 63, :_reduce_51,
2, 64, :_reduce_52,
3, 64, :_reduce_53,
3, 65, :_reduce_54,
4, 65, :_reduce_55,
3, 66, :_reduce_56,
3, 67, :_reduce_57,
4, 68, :_reduce_58,
4, 68, :_reduce_59,
1, 69, :_reduce_60,
1, 69, :_reduce_none,
1, 69, :_reduce_none,
1, 69, :_reduce_none,
1, 69, :_reduce_none,
1, 69, :_reduce_none,
1, 43, :_reduce_66,
2, 43, :_reduce_67,
1, 43, :_reduce_68,
2, 43, :_reduce_69 ]
racc_reduce_n = 70
racc_shift_n = 132
racc_token_table = {
false => 0,
:error => 1,
"," => 2,
:IDENTIFIER => 3,
:INTEGER => 4,
:REAL => 5,
:STRING => 6,
"(" => 7,
")" => 8,
"*" => 9,
"/" => 10,
"+" => 11,
"-" => 12,
:OP_EQ => 13,
:OP_NEQ => 14,
:OP_LEQ => 15,
:OP_GEQ => 16,
">" => 17,
"<" => 18,
:NOT => 19,
:AND => 20,
:OR => 21,
":" => 22,
"|" => 23,
:VAR_OPEN => 24,
:VAR_CLOSE => 25,
:STMT_OPEN => 26,
:IF => 27,
:STMT_CLOSE => 28,
:UNLESS => 29,
:ELSE => 30,
:ENDIF => 31,
:ENDUNLESS => 32,
:FOR => 33,
:IN => 34,
:ENDFOR => 35,
:BLOCK => 36,
:ENDBLOCK => 37,
:END => 38,
:EXTENDS => 39,
:TEXT_BLOCK => 40 }
racc_nt_base = 41
racc_use_result_var = true
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 ]
Racc_token_to_s_table = [
"$end",
"error",
"\",\"",
"IDENTIFIER",
"INTEGER",
"REAL",
"STRING",
"\"(\"",
"\")\"",
"\"*\"",
"\"/\"",
"\"+\"",
"\"-\"",
"OP_EQ",
"OP_NEQ",
"OP_LEQ",
"OP_GEQ",
"\">\"",
"\"<\"",
"NOT",
"AND",
"OR",
"\":\"",
"\"|\"",
"VAR_OPEN",
"VAR_CLOSE",
"STMT_OPEN",
"IF",
"STMT_CLOSE",
"UNLESS",
"ELSE",
"ENDIF",
"ENDUNLESS",
"FOR",
"IN",
"ENDFOR",
"BLOCK",
"ENDBLOCK",
"END",
"EXTENDS",
"TEXT_BLOCK",
"$start",
"target",
"document",
"parameter_list",
"logical_expression",
"primary_expression",
"filtered_expression",
"multiplicative_expression",
"additive_expression",
"boolean_expression",
"inverse_expression",
"filter",
"filter_list",
"inject_statement",
"if_tag",
"else_tag",
"end_if_tag",
"if_block",
"for_tag",
"end_for_tag",
"for_block",
"block_tag",
"end_block_tag",
"block_block",
"generic_block_tag",
"end_generic_block_tag",
"generic_block",
"extends_statement",
"document_component" ]
Racc_debug_parser = false
##### State transition tables end #####
# reduce 0 omitted
# reduce 1 omitted
module_eval(<<'.,.,', 'cadenza.y', 12)
def _reduce_2(val, _values, result)
result = nil
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 16)
def _reduce_3(val, _values, result)
result = [val[0]]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 17)
def _reduce_4(val, _values, result)
result = val[0].push(val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 22)
def _reduce_5(val, _values, result)
result = VariableNode.new(val[0].value)
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 23)
def _reduce_6(val, _values, result)
result = VariableNode.new(val[0].value, val[1])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 24)
def _reduce_7(val, _values, result)
result = ConstantNode.new(val[0].value)
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 25)
def _reduce_8(val, _values, result)
result = ConstantNode.new(val[0].value)
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 26)
def _reduce_9(val, _values, result)
result = ConstantNode.new(val[0].value)
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 27)
def _reduce_10(val, _values, result)
result = val[1]
result
end
.,.,
# reduce 11 omitted
module_eval(<<'.,.,', 'cadenza.y', 32)
def _reduce_12(val, _values, result)
result = OperationNode.new(val[0], "*", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 33)
def _reduce_13(val, _values, result)
result = OperationNode.new(val[0], "/", val[2])
result
end
.,.,
# reduce 14 omitted
module_eval(<<'.,.,', 'cadenza.y', 38)
def _reduce_15(val, _values, result)
result = OperationNode.new(val[0], "+", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 39)
def _reduce_16(val, _values, result)
result = OperationNode.new(val[0], "-", val[2])
result
end
.,.,
# reduce 17 omitted
module_eval(<<'.,.,', 'cadenza.y', 44)
def _reduce_18(val, _values, result)
result = OperationNode.new(val[0], "==", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 45)
def _reduce_19(val, _values, result)
result = OperationNode.new(val[0], "!=", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 46)
def _reduce_20(val, _values, result)
result = OperationNode.new(val[0], "<=", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 47)
def _reduce_21(val, _values, result)
result = OperationNode.new(val[0], ">=", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 48)
def _reduce_22(val, _values, result)
result = OperationNode.new(val[0], ">", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 49)
def _reduce_23(val, _values, result)
result = OperationNode.new(val[0], "<", val[2])
result
end
.,.,
# reduce 24 omitted
module_eval(<<'.,.,', 'cadenza.y', 54)
def _reduce_25(val, _values, result)
result = BooleanInverseNode.new(val[1])
result
end
.,.,
# reduce 26 omitted
module_eval(<<'.,.,', 'cadenza.y', 59)
def _reduce_27(val, _values, result)
result = OperationNode.new(val[0], "and", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 60)
def _reduce_28(val, _values, result)
result = OperationNode.new(val[0], "or", val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 64)
def _reduce_29(val, _values, result)
result = FilterNode.new(val[0].value)
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 65)
def _reduce_30(val, _values, result)
result = FilterNode.new(val[0].value, val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 69)
def _reduce_31(val, _values, result)
result = [val[0]]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 70)
def _reduce_32(val, _values, result)
result = val[0].push(val[2])
result
end
.,.,
# reduce 33 omitted
module_eval(<<'.,.,', 'cadenza.y', 75)
def _reduce_34(val, _values, result)
result = FilteredValueNode.new(val[0], val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 79)
def _reduce_35(val, _values, result)
result = val[1]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 83)
def _reduce_36(val, _values, result)
open_scope!; result = val[2]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 84)
def _reduce_37(val, _values, result)
open_scope!; result = BooleanInverseNode.new(val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 88)
def _reduce_38(val, _values, result)
result = close_scope!; open_scope!
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 92)
def _reduce_39(val, _values, result)
result = close_scope!
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 93)
def _reduce_40(val, _values, result)
result = close_scope!
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 97)
def _reduce_41(val, _values, result)
result = IfNode.new(val[0], val[1])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 98)
def _reduce_42(val, _values, result)
result = IfNode.new(val[0], val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 99)
def _reduce_43(val, _values, result)
result = IfNode.new(val[0], val[1], val[3])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 100)
def _reduce_44(val, _values, result)
result = IfNode.new(val[0], val[2], val[3])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 101)
def _reduce_45(val, _values, result)
result = IfNode.new(val[0], val[2], val[4])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 105)
def _reduce_46(val, _values, result)
open_scope!; result = [val[2].value, val[4]]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 109)
def _reduce_47(val, _values, result)
result = close_scope!
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 114)
def _reduce_48(val, _values, result)
result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[1])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 115)
def _reduce_49(val, _values, result)
result = ForNode.new(VariableNode.new(val[0].first), val[0].last, val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 119)
def _reduce_50(val, _values, result)
result = open_block_scope!(val[2].value)
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 123)
def _reduce_51(val, _values, result)
result = close_block_scope!
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 128)
def _reduce_52(val, _values, result)
result = BlockNode.new(val[0], val[1])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 129)
def _reduce_53(val, _values, result)
result = BlockNode.new(val[0], val[2])
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 133)
def _reduce_54(val, _values, result)
open_scope!; result = [val[1].value, []]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 134)
def _reduce_55(val, _values, result)
open_scope!; result = [val[1].value, val[2]]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 138)
def _reduce_56(val, _values, result)
result = close_scope!
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 142)
def _reduce_57(val, _values, result)
result = GenericBlockNode.new(val[0].first, val[2], val[0].last)
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 146)
def _reduce_58(val, _values, result)
result = val[2].value
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 147)
def _reduce_59(val, _values, result)
result = VariableNode.new(val[2].value)
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 151)
def _reduce_60(val, _values, result)
result = TextNode.new(val[0].value)
result
end
.,.,
# reduce 61 omitted
# reduce 62 omitted
# reduce 63 omitted
# reduce 64 omitted
# reduce 65 omitted
module_eval(<<'.,.,', 'cadenza.y', 160)
def _reduce_66(val, _values, result)
push val[0]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 161)
def _reduce_67(val, _values, result)
push val[1]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 162)
def _reduce_68(val, _values, result)
document.extends = val[0]
result
end
.,.,
module_eval(<<'.,.,', 'cadenza.y', 163)
def _reduce_69(val, _values, result)
document.extends = val[1]
result
end
.,.,
def _reduce_none(val, _values, result)
val[0]
end
end # class RaccParser
end # module Cadenza

3425
test/racc/regress/cast Normal file

File diff suppressed because it is too large Load diff

2318
test/racc/regress/csspool Normal file

File diff suppressed because it is too large Load diff

1794
test/racc/regress/edtf Normal file

File diff suppressed because it is too large Load diff

1392
test/racc/regress/huia Normal file

File diff suppressed because it is too large Load diff

222
test/racc/regress/journey Normal file
View file

@ -0,0 +1,222 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
require 'racc/parser.rb'
require 'journey/parser_extras'
module Journey
class Parser < Racc::Parser
##### State transition tables begin ###
racc_action_table = [
17, 21, 13, 15, 14, 7, nil, 16, 8, 19,
13, 15, 14, 7, 23, 16, 8, 19, 13, 15,
14, 7, nil, 16, 8, 13, 15, 14, 7, nil,
16, 8, 13, 15, 14, 7, nil, 16, 8 ]
racc_action_check = [
1, 17, 1, 1, 1, 1, nil, 1, 1, 1,
20, 20, 20, 20, 20, 20, 20, 20, 0, 0,
0, 0, nil, 0, 0, 7, 7, 7, 7, nil,
7, 7, 19, 19, 19, 19, nil, 19, 19 ]
racc_action_pointer = [
16, 0, nil, nil, nil, nil, nil, 23, nil, nil,
nil, nil, nil, nil, nil, nil, nil, 1, nil, 30,
8, nil, nil, nil ]
racc_action_default = [
-18, -18, -2, -3, -4, -5, -6, -18, -9, -10,
-11, -12, -13, -14, -15, -16, -17, -18, -1, -18,
-18, 24, -8, -7 ]
racc_goto_table = [
18, 1, nil, nil, nil, nil, nil, nil, 20, nil,
nil, nil, nil, nil, nil, nil, nil, nil, 22, 18 ]
racc_goto_check = [
2, 1, nil, nil, nil, nil, nil, nil, 1, nil,
nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ]
racc_goto_pointer = [
nil, 1, -1, nil, nil, nil, nil, nil, nil, nil,
nil ]
racc_goto_default = [
nil, nil, 2, 3, 4, 5, 6, 9, 10, 11,
12 ]
racc_reduce_table = [
0, 0, :racc_error,
2, 11, :_reduce_1,
1, 11, :_reduce_2,
1, 11, :_reduce_none,
1, 12, :_reduce_none,
1, 12, :_reduce_none,
1, 12, :_reduce_none,
3, 15, :_reduce_7,
3, 13, :_reduce_8,
1, 16, :_reduce_9,
1, 14, :_reduce_none,
1, 14, :_reduce_none,
1, 14, :_reduce_none,
1, 14, :_reduce_none,
1, 19, :_reduce_14,
1, 17, :_reduce_15,
1, 18, :_reduce_16,
1, 20, :_reduce_17 ]
racc_reduce_n = 18
racc_shift_n = 24
racc_token_table = {
false => 0,
:error => 1,
:SLASH => 2,
:LITERAL => 3,
:SYMBOL => 4,
:LPAREN => 5,
:RPAREN => 6,
:DOT => 7,
:STAR => 8,
:OR => 9 }
racc_nt_base = 10
racc_use_result_var = true
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 ]
Racc_token_to_s_table = [
"$end",
"error",
"SLASH",
"LITERAL",
"SYMBOL",
"LPAREN",
"RPAREN",
"DOT",
"STAR",
"OR",
"$start",
"expressions",
"expression",
"or",
"terminal",
"group",
"star",
"symbol",
"literal",
"slash",
"dot" ]
Racc_debug_parser = false
##### State transition tables end #####
# reduce 0 omitted
module_eval(<<'.,.,', 'journey.y', 6)
def _reduce_1(val, _values, result)
result = Cat.new(val.first, val.last)
result
end
.,.,
module_eval(<<'.,.,', 'journey.y', 7)
def _reduce_2(val, _values, result)
result = val.first
result
end
.,.,
# reduce 3 omitted
# reduce 4 omitted
# reduce 5 omitted
# reduce 6 omitted
module_eval(<<'.,.,', 'journey.y', 16)
def _reduce_7(val, _values, result)
result = Group.new(val[1])
result
end
.,.,
module_eval(<<'.,.,', 'journey.y', 19)
def _reduce_8(val, _values, result)
result = Or.new([val.first, val.last])
result
end
.,.,
module_eval(<<'.,.,', 'journey.y', 22)
def _reduce_9(val, _values, result)
result = Star.new(Symbol.new(val.last))
result
end
.,.,
# reduce 10 omitted
# reduce 11 omitted
# reduce 12 omitted
# reduce 13 omitted
module_eval(<<'.,.,', 'journey.y', 31)
def _reduce_14(val, _values, result)
result = Slash.new('/')
result
end
.,.,
module_eval(<<'.,.,', 'journey.y', 34)
def _reduce_15(val, _values, result)
result = Symbol.new(val.first)
result
end
.,.,
module_eval(<<'.,.,', 'journey.y', 37)
def _reduce_16(val, _values, result)
result = Literal.new(val.first)
result
end
.,.,
module_eval(<<'.,.,', 'journey.y', 39)
def _reduce_17(val, _values, result)
result = Dot.new(val.first)
result
end
.,.,
def _reduce_none(val, _values, result)
val[0]
end
end # class Parser
end # module Journey

885
test/racc/regress/liquor Normal file
View file

@ -0,0 +1,885 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
require 'racc/parser.rb'
module Liquor
class Parser < Racc::Parser
module_eval(<<'...end liquor.y/module_eval...', 'liquor.y', 216)
attr_reader :errors, :ast
def initialize(tags={})
super()
@errors = []
@ast = nil
@tags = tags
end
def success?
@errors.empty?
end
def parse(string, name='(code)')
@errors.clear
@name = name
@ast = nil
begin
@stream = Lexer.lex(string, @name, @tags)
@ast = do_parse
rescue Liquor::SyntaxError => e
@errors << e
end
success?
end
def next_token
tok = @stream.shift
[ tok[0], tok ] if tok
end
TOKEN_NAME_MAP = {
:comma => ',',
:dot => '.',
:lblock => '{%',
:rblock => '%}',
:linterp => '{{',
:rinterp => '}}',
:lbracket => '[',
:rbracket => ']',
:lparen => '(',
:rparen => ')',
:pipe => '|',
:op_not => '!',
:op_mul => '*',
:op_div => '/',
:op_mod => '%',
:op_plus => '+',
:op_minus => '-',
:op_eq => '==',
:op_neq => '!=',
:op_lt => '<',
:op_leq => '<=',
:op_gt => '>',
:op_geq => '>=',
:keyword => 'keyword argument name',
:kwarg => 'keyword argument',
:ident => 'identifier',
}
def on_error(error_token_id, error_token, value_stack)
if token_to_str(error_token_id) == "$end"
raise Liquor::SyntaxError.new("unexpected end of program", {
file: @name
})
else
type, (loc, value) = error_token
type = TOKEN_NAME_MAP[type] || type
raise Liquor::SyntaxError.new("unexpected token `#{type}'", loc)
end
end
def retag(nodes)
loc = nodes.map { |node| node[1] }.compact
first, *, last = loc
return first if last.nil?
{
file: first[:file],
line: first[:line],
start: first[:start],
end: last[:end],
}
end
def reduce_tag_args(list)
list.each_slice(2).reduce([]) { |args, (k, v)|
if v[0] == :block
args << [ :blockarg, retag([ k, v ]), k, v[2] || [] ]
else
args << [ :kwarg, retag([ k, v ]), k, v ]
end
}
end
...end liquor.y/module_eval...
##### State transition tables begin ###
racc_action_table = [
76, 26, 26, 6, 77, 70, 5, 6, 25, 25,
5, 28, 32, 36, 37, 34, 35, 31, 29, 27,
33, 2, 30, 26, 26, 2, 6, 43, 52, 5,
25, 25, 38, 39, 28, 32, 36, 37, 34, 35,
31, 29, 27, 33, 2, 30, 26, 94, 51, 98,
26, 96, 26, 25, 97, 38, 39, 25, 111, 25,
28, 32, 36, 37, 34, 35, 31, 29, 27, 33,
41, 30, 26, 26, 43, 6, 107, 74, 5, 25,
25, 38, 39, 28, 32, 36, 37, 34, 35, 31,
29, 27, 33, 2, 30, 7, 26, 70, 6, 96,
102, 5, 97, 25, 38, 39, 28, 32, 36, 37,
34, 35, 31, 29, 27, 33, 2, 30, 54, 26,
74, 6, 96, 74, 5, 97, 25, 38, 39, 28,
32, 36, 37, 34, 35, 31, 29, 27, 33, 2,
30, 87, 26, 96, 6, 22, 97, 5, 84, 25,
38, 39, 28, 32, 36, 37, 34, 35, 31, 29,
27, 33, 2, 30, 40, 26, 23, nil, 24, nil,
nil, nil, 25, 38, 39, 28, 32, 36, 37, 34,
35, 31, 29, 27, 33, nil, 30, nil, 26, nil,
82, nil, 52, nil, nil, 25, 38, 39, 28, 32,
36, 37, 34, 35, 31, 29, 27, 33, nil, 30,
nil, 26, 51, nil, nil, nil, nil, nil, 25, 38,
39, 28, 32, 36, 37, 34, 35, 31, 29, 27,
33, nil, 30, nil, 26, nil, nil, nil, 75, nil,
nil, 25, 38, 39, 28, 32, 36, 37, 34, 35,
31, 29, 27, 33, nil, 30, 13, 15, nil, 13,
15, 21, nil, 14, 21, 38, 14, nil, nil, nil,
18, nil, nil, 18, 19, nil, nil, 19, nil, 13,
15, nil, 16, nil, 21, 16, 14, nil, nil, 13,
15, nil, nil, 18, 21, nil, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, 74, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, 52, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, 51, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, nil, 18, 21, 16, 14, 19, nil, 13,
15, 81, nil, 18, 21, 16, 14, 19, nil, 13,
15, nil, 26, 18, 21, 16, 14, 19, nil, 25,
nil, 101, 28, 18, nil, 16, 26, 19, nil, 29,
27, 106, nil, 25, nil, 16, 28, 32, 36, 37,
34, 35, 31, 29, 27, 33, 26, 30, nil, nil,
nil, nil, nil, 25, nil, nil, 28, nil, 26, nil,
nil, nil, 31, 29, 27, 25, nil, 30, 28, nil,
26, nil, nil, nil, 31, 29, 27, 25, nil, 30,
28, nil, 26, nil, nil, nil, 31, 29, 27, 25,
nil, 30, 28, nil, 26, nil, nil, nil, 31, 29,
27, 25, nil, 30, 28, nil, 26, nil, nil, nil,
31, 29, 27, 25, nil, 30, 28, nil, 26, nil,
nil, nil, 31, 29, 27, 25, nil, 30, 28, nil,
nil, nil, nil, nil, nil, 29, 27 ]
racc_action_check = [
47, 47, 55, 101, 48, 84, 101, 4, 47, 55,
4, 47, 47, 47, 47, 47, 47, 47, 47, 47,
47, 101, 47, 56, 79, 4, 0, 54, 79, 0,
56, 79, 47, 47, 79, 79, 79, 79, 79, 79,
79, 79, 79, 79, 0, 79, 57, 91, 79, 96,
104, 104, 45, 57, 104, 79, 79, 104, 109, 45,
104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
12, 104, 46, 71, 13, 2, 103, 71, 2, 46,
71, 104, 104, 71, 71, 71, 71, 71, 71, 71,
71, 71, 71, 2, 71, 1, 99, 40, 3, 107,
99, 3, 107, 99, 71, 71, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 3, 99, 26, 88,
70, 81, 94, 88, 81, 94, 88, 99, 99, 88,
88, 88, 88, 88, 88, 88, 88, 88, 88, 81,
88, 72, 11, 111, 106, 6, 111, 106, 69, 11,
88, 88, 11, 11, 11, 11, 11, 11, 11, 11,
11, 11, 106, 11, 11, 53, 7, nil, 11, nil,
nil, nil, 53, 11, 11, 53, 53, 53, 53, 53,
53, 53, 53, 53, 53, nil, 53, nil, 49, nil,
53, nil, 49, nil, nil, 49, 53, 53, 49, 49,
49, 49, 49, 49, 49, 49, 49, 49, nil, 49,
nil, 44, 49, nil, nil, nil, nil, nil, 44, 49,
49, 44, 44, 44, 44, 44, 44, 44, 44, 44,
44, nil, 44, nil, 67, nil, nil, nil, 44, nil,
nil, 67, 44, 44, 67, 67, 67, 67, 67, 67,
67, 67, 67, 67, nil, 67, 30, 30, nil, 31,
31, 30, nil, 30, 31, 67, 31, nil, nil, nil,
30, nil, nil, 31, 30, nil, nil, 31, nil, 32,
32, nil, 30, nil, 32, 31, 32, nil, nil, 33,
33, nil, nil, 32, 33, nil, 33, 32, nil, 34,
34, nil, nil, 33, 34, 32, 34, 33, nil, 35,
35, nil, nil, 34, 35, 33, 35, 34, nil, 36,
36, nil, nil, 35, 36, 34, 36, 35, nil, 37,
37, nil, nil, 36, 37, 35, 37, 36, nil, 38,
38, nil, nil, 37, 38, 36, 38, 37, nil, 39,
39, nil, nil, 38, 39, 37, 39, 38, nil, 43,
43, 43, nil, 39, 43, 38, 43, 39, nil, 74,
74, nil, nil, 43, 74, 39, 74, 43, nil, 5,
5, nil, nil, 74, 5, 43, 5, 74, nil, 14,
14, nil, nil, 5, 14, 74, 14, 5, nil, 18,
18, nil, nil, 14, 18, 5, 18, 14, nil, 19,
19, nil, nil, 18, 19, 14, 19, 18, nil, 21,
21, nil, nil, 19, 21, 18, 21, 19, nil, 22,
22, 22, nil, 21, 22, 19, 22, 21, nil, 25,
25, nil, nil, 22, 25, 21, 25, 22, nil, 27,
27, 22, nil, 25, 27, 22, 27, 25, nil, 28,
28, nil, nil, 27, 28, 25, 28, 27, nil, 29,
29, nil, nil, 28, 29, 27, 29, 28, nil, 52,
52, nil, nil, 29, 52, 28, 52, 29, nil, 76,
76, nil, nil, 52, 76, 29, 76, 52, nil, 97,
97, 52, nil, 76, 97, 52, 97, 76, nil, 102,
102, nil, 58, 97, 102, 76, 102, 97, nil, 58,
nil, 97, 58, 102, nil, 97, 66, 102, nil, 58,
58, 102, nil, 66, nil, 102, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 60, 66, nil, nil,
nil, nil, nil, 60, nil, nil, 60, nil, 61, nil,
nil, nil, 60, 60, 60, 61, nil, 60, 61, nil,
62, nil, nil, nil, 61, 61, 61, 62, nil, 61,
62, nil, 63, nil, nil, nil, 62, 62, 62, 63,
nil, 62, 63, nil, 64, nil, nil, nil, 63, 63,
63, 64, nil, 63, 64, nil, 65, nil, nil, nil,
64, 64, 64, 65, nil, 64, 65, nil, 59, nil,
nil, nil, 65, 65, 65, 59, nil, 65, 59, nil,
nil, nil, nil, nil, nil, 59, 59 ]
racc_action_pointer = [
18, 95, 67, 90, -1, 374, 140, 166, nil, nil,
nil, 139, 41, 62, 384, nil, nil, nil, 394, 404,
nil, 414, 424, nil, nil, 434, 113, 444, 454, 464,
251, 254, 274, 284, 294, 304, 314, 324, 334, 344,
92, nil, nil, 354, 208, 49, 69, -2, -24, 185,
nil, nil, 474, 162, 15, -1, 20, 43, 509, 615,
543, 555, 567, 579, 591, 603, 523, 231, nil, 123,
113, 70, 111, nil, 364, nil, 484, nil, nil, 21,
nil, 113, nil, nil, 0, nil, nil, nil, 116, nil,
nil, 38, nil, nil, 118, nil, 22, 494, nil, 93,
nil, -5, 504, 67, 47, nil, 136, 95, nil, 49,
nil, 139, nil ]
racc_action_default = [
-1, -57, -1, -1, -1, -57, -57, -57, -2, -3,
-4, -57, -57, -7, -57, -9, -10, -11, -57, -57,
-31, -35, -57, 113, -5, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -6, -12, -40, -57, -16, -17, -34, -57, -57,
-46, -47, -57, -57, -15, -18, -19, -20, -21, -22,
-23, -24, -25, -26, -27, -28, -29, -30, -41, -43,
-40, -40, -57, -38, -57, -8, -35, -32, -45, -57,
-48, -1, -13, -14, -57, -44, -37, -36, -40, -33,
-50, -57, -42, -39, -57, -49, -57, -57, -51, -57,
-52, -1, -57, -57, -57, -54, -1, -57, -56, -57,
-53, -57, -55 ]
racc_goto_table = [
1, 11, 8, 9, 10, 48, 68, 100, 42, 50,
44, 72, 105, 73, 45, 46, 12, 80, 49, nil,
nil, 53, nil, 55, 56, 57, 58, 59, 60, 61,
62, 63, 64, 65, 66, 67, 78, nil, nil, 71,
85, 86, 95, nil, nil, nil, nil, nil, 79, 83,
92, nil, 108, nil, nil, 110, nil, nil, 93, 112,
89, nil, nil, nil, nil, nil, 90, nil, nil, nil,
88, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, 91, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, 99, nil, nil, nil, nil, 104, nil,
nil, 103, nil, nil, nil, nil, 109 ]
racc_goto_check = [
1, 4, 1, 1, 1, 9, 12, 17, 8, 14,
4, 10, 18, 11, 4, 4, 5, 15, 4, nil,
nil, 4, nil, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 14, nil, nil, 4,
11, 11, 16, nil, nil, nil, nil, nil, 4, 8,
12, nil, 16, nil, nil, 16, nil, nil, 11, 16,
9, nil, nil, nil, nil, nil, 14, nil, nil, nil,
4, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, 1, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, 4, nil, nil, nil, nil, 4, nil,
nil, 1, nil, nil, nil, nil, 1 ]
racc_goto_pointer = [
nil, 0, nil, nil, -4, 11, nil, nil, -5, -16,
-32, -30, -34, nil, -13, -35, -52, -90, -90 ]
racc_goto_default = [
nil, nil, 3, 4, 47, nil, 20, 17, nil, nil,
nil, nil, nil, 69, nil, nil, nil, nil, nil ]
racc_reduce_table = [
0, 0, :racc_error,
0, 37, :_reduce_1,
2, 37, :_reduce_2,
2, 37, :_reduce_3,
2, 37, :_reduce_4,
3, 38, :_reduce_5,
3, 38, :_reduce_6,
1, 42, :_reduce_none,
3, 42, :_reduce_8,
1, 40, :_reduce_none,
1, 40, :_reduce_none,
1, 40, :_reduce_none,
2, 40, :_reduce_12,
4, 40, :_reduce_13,
4, 40, :_reduce_14,
3, 40, :_reduce_15,
2, 40, :_reduce_16,
2, 40, :_reduce_17,
3, 40, :_reduce_18,
3, 40, :_reduce_19,
3, 40, :_reduce_20,
3, 40, :_reduce_21,
3, 40, :_reduce_22,
3, 40, :_reduce_23,
3, 40, :_reduce_24,
3, 40, :_reduce_25,
3, 40, :_reduce_26,
3, 40, :_reduce_27,
3, 40, :_reduce_28,
3, 40, :_reduce_29,
3, 40, :_reduce_30,
1, 40, :_reduce_none,
3, 43, :_reduce_32,
3, 45, :_reduce_33,
1, 45, :_reduce_34,
0, 45, :_reduce_35,
3, 44, :_reduce_36,
2, 46, :_reduce_37,
1, 46, :_reduce_38,
3, 47, :_reduce_39,
0, 47, :_reduce_40,
3, 41, :_reduce_41,
3, 48, :_reduce_42,
1, 48, :_reduce_43,
2, 49, :_reduce_44,
4, 39, :_reduce_45,
3, 39, :_reduce_46,
1, 50, :_reduce_47,
2, 50, :_reduce_48,
4, 51, :_reduce_49,
2, 51, :_reduce_50,
2, 52, :_reduce_51,
2, 52, :_reduce_52,
4, 53, :_reduce_53,
3, 53, :_reduce_54,
4, 54, :_reduce_55,
2, 54, :_reduce_56 ]
racc_reduce_n = 57
racc_shift_n = 113
racc_token_table = {
false => 0,
:error => 1,
:comma => 2,
:dot => 3,
:endtag => 4,
:ident => 5,
:integer => 6,
:keyword => 7,
:lblock => 8,
:lblock2 => 9,
:lbracket => 10,
:linterp => 11,
:lparen => 12,
:op_div => 13,
:op_eq => 14,
:op_gt => 15,
:op_geq => 16,
:op_lt => 17,
:op_leq => 18,
:op_minus => 19,
:op_mod => 20,
:op_mul => 21,
:op_neq => 22,
:op_not => 23,
:op_plus => 24,
:pipe => 25,
:plaintext => 26,
:rblock => 27,
:rbracket => 28,
:rinterp => 29,
:rparen => 30,
:string => 31,
:tag_ident => 32,
:op_uminus => 33,
:op_and => 34,
:op_or => 35 }
racc_nt_base = 36
racc_use_result_var = true
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 ]
Racc_token_to_s_table = [
"$end",
"error",
"comma",
"dot",
"endtag",
"ident",
"integer",
"keyword",
"lblock",
"lblock2",
"lbracket",
"linterp",
"lparen",
"op_div",
"op_eq",
"op_gt",
"op_geq",
"op_lt",
"op_leq",
"op_minus",
"op_mod",
"op_mul",
"op_neq",
"op_not",
"op_plus",
"pipe",
"plaintext",
"rblock",
"rbracket",
"rinterp",
"rparen",
"string",
"tag_ident",
"op_uminus",
"op_and",
"op_or",
"$start",
"block",
"interp",
"tag",
"expr",
"filter_chain",
"primary_expr",
"tuple",
"function_args",
"tuple_content",
"function_args_inside",
"function_keywords",
"filter_chain_cont",
"filter_call",
"tag_first_cont",
"tag_first_cont2",
"tag_next_cont",
"tag_next_cont2",
"tag_next_cont3" ]
Racc_debug_parser = false
##### State transition tables end #####
# reduce 0 omitted
module_eval(<<'.,.,', 'liquor.y', 47)
def _reduce_1(val, _values, result)
result = []
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 49)
def _reduce_2(val, _values, result)
result = [ val[0], *val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 51)
def _reduce_3(val, _values, result)
result = [ val[0], *val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 53)
def _reduce_4(val, _values, result)
result = [ val[0], *val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 57)
def _reduce_5(val, _values, result)
result = [ :interp, retag(val), val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 59)
def _reduce_6(val, _values, result)
result = [ :interp, retag(val), val[1] ]
result
end
.,.,
# reduce 7 omitted
module_eval(<<'.,.,', 'liquor.y', 64)
def _reduce_8(val, _values, result)
result = [ val[1][0], retag(val), *val[1][2..-1] ]
result
end
.,.,
# reduce 9 omitted
# reduce 10 omitted
# reduce 11 omitted
module_eval(<<'.,.,', 'liquor.y', 71)
def _reduce_12(val, _values, result)
result = [ :call, retag(val), val[0], val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 73)
def _reduce_13(val, _values, result)
result = [ :index, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 75)
def _reduce_14(val, _values, result)
result = [ :external, retag(val), val[0], val[2], val[3] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 77)
def _reduce_15(val, _values, result)
result = [ :external, retag(val), val[0], val[2], nil ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 79)
def _reduce_16(val, _values, result)
result = [ :uminus, retag(val), val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 81)
def _reduce_17(val, _values, result)
result = [ :not, retag(val), val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 83)
def _reduce_18(val, _values, result)
result = [ :mul, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 85)
def _reduce_19(val, _values, result)
result = [ :div, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 87)
def _reduce_20(val, _values, result)
result = [ :mod, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 89)
def _reduce_21(val, _values, result)
result = [ :plus, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 91)
def _reduce_22(val, _values, result)
result = [ :minus, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 93)
def _reduce_23(val, _values, result)
result = [ :eq, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 95)
def _reduce_24(val, _values, result)
result = [ :neq, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 97)
def _reduce_25(val, _values, result)
result = [ :lt, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 99)
def _reduce_26(val, _values, result)
result = [ :leq, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 101)
def _reduce_27(val, _values, result)
result = [ :gt, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 103)
def _reduce_28(val, _values, result)
result = [ :geq, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 105)
def _reduce_29(val, _values, result)
result = [ :and, retag(val), val[0], val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 107)
def _reduce_30(val, _values, result)
result = [ :or, retag(val), val[0], val[2] ]
result
end
.,.,
# reduce 31 omitted
module_eval(<<'.,.,', 'liquor.y', 112)
def _reduce_32(val, _values, result)
result = [ :tuple, retag(val), val[1].compact ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 116)
def _reduce_33(val, _values, result)
result = [ val[0], *val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 118)
def _reduce_34(val, _values, result)
result = [ val[0] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 120)
def _reduce_35(val, _values, result)
result = [ ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 124)
def _reduce_36(val, _values, result)
result = [ :args, retag(val), *val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 128)
def _reduce_37(val, _values, result)
result = [ val[0], val[1][2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 130)
def _reduce_38(val, _values, result)
result = [ nil, val[0][2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 134)
def _reduce_39(val, _values, result)
name = val[0][2].to_sym
tail = val[2][2]
loc = retag([ val[0], val[1] ])
if tail.include? name
@errors << SyntaxError.new("duplicate keyword argument `#{val[0][2]}'",
tail[name][1])
end
hash = {
name => [ val[1][0], loc, *val[1][2..-1] ]
}.merge(tail)
result = [ :keywords, retag([ loc, val[2] ]), hash ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 150)
def _reduce_40(val, _values, result)
result = [ :keywords, nil, {} ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 154)
def _reduce_41(val, _values, result)
result = [ val[0], *val[2] ].
reduce { |tree, node| node[3][2] = tree; node }
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 160)
def _reduce_42(val, _values, result)
result = [ val[0], *val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 162)
def _reduce_43(val, _values, result)
result = [ val[0] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 166)
def _reduce_44(val, _values, result)
ident_loc = val[0][1]
empty_args_loc = { line: ident_loc[:line],
start: ident_loc[:end] + 1,
end: ident_loc[:end] + 1, }
result = [ :call, val[0][1], val[0],
[ :args, val[1][1] || empty_args_loc, nil, val[1][2] ] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 176)
def _reduce_45(val, _values, result)
result = [ :tag, retag(val), val[1], val[2], *reduce_tag_args(val[3][2]) ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 178)
def _reduce_46(val, _values, result)
result = [ :tag, retag(val), val[1], nil, *reduce_tag_args(val[2][2]) ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 186)
def _reduce_47(val, _values, result)
result = [ :cont, retag(val), [] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 188)
def _reduce_48(val, _values, result)
result = [ :cont, retag(val), [ val[0], *val[1][2] ] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 192)
def _reduce_49(val, _values, result)
result = [ :cont2, val[0][1], [ [:block, val[0][1], val[1] ], *val[3] ] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 194)
def _reduce_50(val, _values, result)
result = [ :cont2, retag(val), [ val[0], *val[1][2] ] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 198)
def _reduce_51(val, _values, result)
result = []
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 200)
def _reduce_52(val, _values, result)
result = [ val[0], *val[1] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 204)
def _reduce_53(val, _values, result)
result = [ [:block, val[0][1], val[1] ], *val[3] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 206)
def _reduce_54(val, _values, result)
result = [ val[0], val[1], *val[2] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 210)
def _reduce_55(val, _values, result)
result = [ [:block, val[0][1], val[1] ], *val[3] ]
result
end
.,.,
module_eval(<<'.,.,', 'liquor.y', 212)
def _reduce_56(val, _values, result)
result = [ val[0], *val[1] ]
result
end
.,.,
def _reduce_none(val, _values, result)
val[0]
end
end # class Parser
end # module Liquor

833
test/racc/regress/machete Normal file
View file

@ -0,0 +1,833 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
require 'racc/parser.rb'
module Machete
class Parser < Racc::Parser
module_eval(<<'...end machete.y/module_eval...', 'machete.y', 175)
include Matchers
class SyntaxError < StandardError; end
def parse(input)
@input = input
@pos = 0
do_parse
end
private
def integer_value(value)
if value =~ /^0[bB]/
value[2..-1].to_i(2)
elsif value =~ /^0[oO]/
value[2..-1].to_i(8)
elsif value =~ /^0[dD]/
value[2..-1].to_i(10)
elsif value =~ /^0[xX]/
value[2..-1].to_i(16)
elsif value =~ /^0/
value.to_i(8)
else
value.to_i
end
end
def symbol_value(value)
value[1..-1].to_sym
end
def string_value(value)
quote = value[0..0]
if quote == "'"
value[1..-2].gsub("\\\\", "\\").gsub("\\'", "'")
elsif quote == '"'
value[1..-2].
gsub("\\\\", "\\").
gsub('\\"', '"').
gsub("\\n", "\n").
gsub("\\t", "\t").
gsub("\\r", "\r").
gsub("\\f", "\f").
gsub("\\v", "\v").
gsub("\\a", "\a").
gsub("\\e", "\e").
gsub("\\b", "\b").
gsub("\\s", "\s").
gsub(/\\([0-7]{1,3})/) { $1.to_i(8).chr }.
gsub(/\\x([0-9a-fA-F]{1,2})/) { $1.to_i(16).chr }
else
raise "Unknown quote: #{quote.inspect}."
end
end
REGEXP_OPTIONS = {
'i' => Regexp::IGNORECASE,
'm' => Regexp::MULTILINE,
'x' => Regexp::EXTENDED
}
def regexp_value(value)
/\A\/(.*)\/([imx]*)\z/ =~ value
pattern, options = $1, $2
Regexp.new(pattern, options.chars.map { |ch| REGEXP_OPTIONS[ch] }.inject(:|))
end
# "^" needs to be here because if it were among operators recognized by
# METHOD_NAME, "^=" would be recognized as two tokens.
SIMPLE_TOKENS = [
"|",
"<",
">",
",",
"=",
"^=",
"^",
"$=",
"[",
"]",
"*=",
"*",
"+",
"?",
"{",
"}"
]
COMPLEX_TOKENS = [
[:NIL, /^nil/],
[:TRUE, /^true/],
[:FALSE, /^false/],
# INTEGER needs to be before METHOD_NAME, otherwise e.g. "+1" would be
# recognized as two tokens.
[
:INTEGER,
/^
[+-]? # sign
(
0[bB][01]+(_[01]+)* # binary (prefixed)
|
0[oO][0-7]+(_[0-7]+)* # octal (prefixed)
|
0[dD]\d+(_\d+)* # decimal (prefixed)
|
0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)* # hexadecimal (prefixed)
|
0[0-7]*(_[0-7]+)* # octal (unprefixed)
|
[1-9]\d*(_\d+)* # decimal (unprefixed)
)
/x
],
[
:SYMBOL,
/^
:
(
# class name
[A-Z][a-zA-Z0-9_]*
|
# regular method name
[a-z_][a-zA-Z0-9_]*[?!=]?
|
# instance variable name
@[a-zA-Z_][a-zA-Z0-9_]*
|
# class variable name
@@[a-zA-Z_][a-zA-Z0-9_]*
|
# operator (sorted by length, then alphabetically)
(<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&*+\-\/<>^`|~])
)
/x
],
[
:STRING,
/^
(
' # sinqle-quoted string
(
\\[\\'] # escape
|
[^'] # regular character
)*
'
|
" # double-quoted string
(
\\ # escape
(
[\\"ntrfvaebs] # one-character escape
|
[0-7]{1,3} # octal number escape
|
x[0-9a-fA-F]{1,2} # hexadecimal number escape
)
|
[^"] # regular character
)*
"
)
/x
],
[
:REGEXP,
/^
\/
(
\\ # escape
(
[\\\/ntrfvaebs\(\)\[\]\{\}\-\.\?\*\+\|\^\$] # one-character escape
|
[0-7]{2,3} # octal number escape
|
x[0-9a-fA-F]{1,2} # hexadecimal number escape
)
|
[^\/] # regular character
)*
\/
[imx]*
/x
],
# ANY, EVEN and ODD need to be before METHOD_NAME, otherwise they would be
# recognized as method names.
[:ANY, /^any/],
[:EVEN, /^even/],
[:ODD, /^odd/],
# We exclude "*", "+", "<", ">", "^" and "|" from method names since they are
# lexed as simple tokens. This is because they have also other meanings in
# Machette patterns beside Ruby method names.
[
:METHOD_NAME,
/^
(
# regular name
[a-z_][a-zA-Z0-9_]*[?!=]?
|
# operator (sorted by length, then alphabetically)
(<=>|===|\[\]=|\*\*|\+@|-@|<<|<=|==|=~|>=|>>|\[\]|[%&\-\/`~])
)
/x
],
[:CLASS_NAME, /^[A-Z][a-zA-Z0-9_]*/]
]
def next_token
skip_whitespace
return false if remaining_input.empty?
# Complex tokens need to be before simple tokens, otherwise e.g. "<<" would be
# recognized as two tokens.
COMPLEX_TOKENS.each do |type, regexp|
if remaining_input =~ regexp
@pos += $&.length
return [type, $&]
end
end
SIMPLE_TOKENS.each do |token|
if remaining_input[0...token.length] == token
@pos += token.length
return [token, token]
end
end
raise SyntaxError, "Unexpected character: #{remaining_input[0..0].inspect}."
end
def skip_whitespace
if remaining_input =~ /\A^[ \t\r\n]+/
@pos += $&.length
end
end
def remaining_input
@input[@pos..-1]
end
def on_error(error_token_id, error_value, value_stack)
raise SyntaxError, "Unexpected token: #{error_value.inspect}."
end
...end machete.y/module_eval...
##### State transition tables begin ###
racc_action_table = [
75, 24, 9, 10, 11, 12, 13, 14, 15, 16,
66, 67, 68, 7, 48, 9, 10, 11, 12, 13,
14, 15, 16, 17, 74, 8, 7, 76, 9, 10,
11, 12, 13, 14, 15, 16, 71, 18, 8, 7,
72, 9, 10, 11, 12, 13, 14, 15, 16, 73,
70, 8, 7, 19, 9, 10, 11, 12, 13, 14,
15, 16, 69, 18, 8, 7, 30, 31, 32, 51,
52, 53, 54, 33, 34, 35, 29, 8, 41, 38,
39, 77, 30, 31, 32, 47, 36, 37, 40, 33,
34, 35, 29, nil, 41, 38, 39, 18, 49, 50,
62, 63, 36, 37, 40, 43, 44, 55, 64, 65,
45, 46, 57, 58, nil, nil, nil, nil, nil, 56 ]
racc_action_check = [
70, 17, 0, 0, 0, 0, 0, 0, 0, 0,
54, 54, 54, 0, 22, 8, 8, 8, 8, 8,
8, 8, 8, 1, 70, 0, 8, 71, 18, 18,
18, 18, 18, 18, 18, 18, 56, 1, 8, 18,
57, 48, 48, 48, 48, 48, 48, 48, 48, 58,
55, 18, 48, 7, 51, 51, 51, 51, 51, 51,
51, 51, 55, 61, 48, 51, 19, 19, 19, 28,
28, 28, 28, 19, 19, 19, 19, 51, 19, 19,
19, 75, 50, 50, 50, 21, 19, 19, 19, 50,
50, 50, 50, nil, 50, 50, 50, 20, 26, 26,
52, 52, 50, 50, 50, 20, 20, 46, 53, 53,
20, 20, 46, 46, nil, nil, nil, nil, nil, 46 ]
racc_action_pointer = [
0, 23, nil, nil, nil, nil, nil, 38, 13, nil,
nil, nil, nil, nil, nil, nil, nil, 1, 26, 64,
83, 59, -3, nil, nil, nil, 82, nil, 51, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, 102, nil, 39, nil,
80, 52, 94, 102, 4, 33, 31, 11, 20, nil,
nil, 49, nil, nil, nil, nil, nil, nil, nil, nil,
-5, -2, nil, nil, nil, 52, nil, nil ]
racc_action_default = [
-56, -56, -1, -3, -4, -5, -6, -7, -33, -48,
-49, -50, -51, -52, -53, -54, -55, -56, -56, -56,
-37, -56, -34, -35, 78, -2, -56, -9, -56, -19,
-20, -21, -22, -23, -24, -25, -26, -27, -28, -29,
-30, -31, -38, -39, -40, -41, -56, -32, -56, -8,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -36,
-10, -11, -12, -15, -13, -16, -14, -17, -18, -42,
-56, -56, -46, -47, -43, -56, -44, -45 ]
racc_goto_table = [
1, 23, 27, 21, 22, 42, 25, 26, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, 60, nil, nil, nil, nil, nil, nil,
nil, 59, nil, nil, nil, nil, nil, nil, nil, nil,
nil, 61 ]
racc_goto_check = [
1, 12, 8, 10, 11, 13, 2, 7, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, 8, nil, nil, nil, nil, nil, nil,
nil, 12, nil, nil, nil, nil, nil, nil, nil, nil,
nil, 1 ]
racc_goto_pointer = [
nil, 0, -12, nil, nil, nil, nil, -12, -17, nil,
-5, -4, -7, -15 ]
racc_goto_default = [
nil, 20, 2, 3, 4, 5, 6, nil, nil, 28,
nil, nil, nil, nil ]
racc_reduce_table = [
0, 0, :racc_error,
1, 31, :_reduce_none,
3, 31, :_reduce_2,
1, 32, :_reduce_none,
1, 32, :_reduce_none,
1, 32, :_reduce_none,
1, 32, :_reduce_none,
1, 33, :_reduce_7,
4, 33, :_reduce_8,
1, 37, :_reduce_none,
3, 37, :_reduce_10,
3, 38, :_reduce_11,
3, 38, :_reduce_12,
3, 38, :_reduce_13,
3, 38, :_reduce_14,
3, 38, :_reduce_15,
3, 38, :_reduce_16,
3, 38, :_reduce_17,
3, 38, :_reduce_18,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
1, 39, :_reduce_none,
3, 34, :_reduce_32,
0, 40, :_reduce_33,
1, 40, :_reduce_none,
1, 41, :_reduce_35,
3, 41, :_reduce_36,
1, 42, :_reduce_none,
2, 42, :_reduce_38,
1, 43, :_reduce_39,
1, 43, :_reduce_40,
1, 43, :_reduce_41,
3, 43, :_reduce_42,
4, 43, :_reduce_43,
4, 43, :_reduce_44,
5, 43, :_reduce_45,
3, 43, :_reduce_46,
3, 43, :_reduce_47,
1, 35, :_reduce_48,
1, 35, :_reduce_49,
1, 35, :_reduce_50,
1, 35, :_reduce_51,
1, 35, :_reduce_52,
1, 35, :_reduce_53,
1, 35, :_reduce_54,
1, 36, :_reduce_55 ]
racc_reduce_n = 56
racc_shift_n = 78
racc_token_table = {
false => 0,
:error => 1,
:NIL => 2,
:TRUE => 3,
:FALSE => 4,
:INTEGER => 5,
:SYMBOL => 6,
:STRING => 7,
:REGEXP => 8,
:ANY => 9,
:EVEN => 10,
:ODD => 11,
:METHOD_NAME => 12,
:CLASS_NAME => 13,
"|" => 14,
"<" => 15,
">" => 16,
"," => 17,
"=" => 18,
"^=" => 19,
"$=" => 20,
"*=" => 21,
"*" => 22,
"+" => 23,
"^" => 24,
"[" => 25,
"]" => 26,
"?" => 27,
"{" => 28,
"}" => 29 }
racc_nt_base = 30
racc_use_result_var = true
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 ]
Racc_token_to_s_table = [
"$end",
"error",
"NIL",
"TRUE",
"FALSE",
"INTEGER",
"SYMBOL",
"STRING",
"REGEXP",
"ANY",
"EVEN",
"ODD",
"METHOD_NAME",
"CLASS_NAME",
"\"|\"",
"\"<\"",
"\">\"",
"\",\"",
"\"=\"",
"\"^=\"",
"\"$=\"",
"\"*=\"",
"\"*\"",
"\"+\"",
"\"^\"",
"\"[\"",
"\"]\"",
"\"?\"",
"\"{\"",
"\"}\"",
"$start",
"expression",
"primary",
"node",
"array",
"literal",
"any",
"attrs",
"attr",
"method_name",
"items_opt",
"items",
"item",
"quantifier" ]
Racc_debug_parser = false
##### State transition tables end #####
# reduce 0 omitted
# reduce 1 omitted
module_eval(<<'.,.,', 'machete.y', 44)
def _reduce_2(val, _values, result)
result = if val[0].is_a?(ChoiceMatcher)
ChoiceMatcher.new(val[0].alternatives << val[2])
else
ChoiceMatcher.new([val[0], val[2]])
end
result
end
.,.,
# reduce 3 omitted
# reduce 4 omitted
# reduce 5 omitted
# reduce 6 omitted
module_eval(<<'.,.,', 'machete.y', 57)
def _reduce_7(val, _values, result)
result = NodeMatcher.new(val[0].to_sym)
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 60)
def _reduce_8(val, _values, result)
result = NodeMatcher.new(val[0].to_sym, val[2])
result
end
.,.,
# reduce 9 omitted
module_eval(<<'.,.,', 'machete.y', 64)
def _reduce_10(val, _values, result)
result = val[0].merge(val[2])
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 66)
def _reduce_11(val, _values, result)
result = { val[0].to_sym => val[2] }
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 68)
def _reduce_12(val, _values, result)
result = {
val[0].to_sym => SymbolRegexpMatcher.new(
Regexp.new("^" + Regexp.escape(symbol_value(val[2]).to_s))
)
}
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 75)
def _reduce_13(val, _values, result)
result = {
val[0].to_sym => SymbolRegexpMatcher.new(
Regexp.new(Regexp.escape(symbol_value(val[2]).to_s) + "$")
)
}
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 82)
def _reduce_14(val, _values, result)
result = {
val[0].to_sym => SymbolRegexpMatcher.new(
Regexp.new(Regexp.escape(symbol_value(val[2]).to_s))
)
}
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 89)
def _reduce_15(val, _values, result)
result = {
val[0].to_sym => StringRegexpMatcher.new(
Regexp.new("^" + Regexp.escape(string_value(val[2])))
)
}
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 96)
def _reduce_16(val, _values, result)
result = {
val[0].to_sym => StringRegexpMatcher.new(
Regexp.new(Regexp.escape(string_value(val[2])) + "$")
)
}
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 103)
def _reduce_17(val, _values, result)
result = {
val[0].to_sym => StringRegexpMatcher.new(
Regexp.new(Regexp.escape(string_value(val[2])))
)
}
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 110)
def _reduce_18(val, _values, result)
result = {
val[0].to_sym => IndifferentRegexpMatcher.new(
Regexp.new(regexp_value(val[2]))
)
}
result
end
.,.,
# reduce 19 omitted
# reduce 20 omitted
# reduce 21 omitted
# reduce 22 omitted
# reduce 23 omitted
# reduce 24 omitted
# reduce 25 omitted
# reduce 26 omitted
# reduce 27 omitted
# reduce 28 omitted
# reduce 29 omitted
# reduce 30 omitted
# reduce 31 omitted
module_eval(<<'.,.,', 'machete.y', 134)
def _reduce_32(val, _values, result)
result = ArrayMatcher.new(val[1])
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 136)
def _reduce_33(val, _values, result)
result = []
result
end
.,.,
# reduce 34 omitted
module_eval(<<'.,.,', 'machete.y', 139)
def _reduce_35(val, _values, result)
result = [val[0]]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 140)
def _reduce_36(val, _values, result)
result = val[0] << val[2]
result
end
.,.,
# reduce 37 omitted
module_eval(<<'.,.,', 'machete.y', 143)
def _reduce_38(val, _values, result)
result = Quantifier.new(val[0], *val[1])
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 145)
def _reduce_39(val, _values, result)
result = [0, nil, 1]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 146)
def _reduce_40(val, _values, result)
result = [1, nil, 1]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 147)
def _reduce_41(val, _values, result)
result = [0, 1, 1]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 149)
def _reduce_42(val, _values, result)
result = [integer_value(val[1]), integer_value(val[1]), 1]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 152)
def _reduce_43(val, _values, result)
result = [integer_value(val[1]), nil, 1]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 155)
def _reduce_44(val, _values, result)
result = [0, integer_value(val[2]), 1]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 158)
def _reduce_45(val, _values, result)
result = [integer_value(val[1]), integer_value(val[3]), 1]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 160)
def _reduce_46(val, _values, result)
result = [0, nil, 2]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 161)
def _reduce_47(val, _values, result)
result = [1, nil, 2]
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 163)
def _reduce_48(val, _values, result)
result = LiteralMatcher.new(nil)
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 164)
def _reduce_49(val, _values, result)
result = LiteralMatcher.new(true)
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 165)
def _reduce_50(val, _values, result)
result = LiteralMatcher.new(false)
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 166)
def _reduce_51(val, _values, result)
result = LiteralMatcher.new(integer_value(val[0]))
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 167)
def _reduce_52(val, _values, result)
result = LiteralMatcher.new(symbol_value(val[0]))
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 168)
def _reduce_53(val, _values, result)
result = LiteralMatcher.new(string_value(val[0]))
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 169)
def _reduce_54(val, _values, result)
result = LiteralMatcher.new(regexp_value(val[0]))
result
end
.,.,
module_eval(<<'.,.,', 'machete.y', 171)
def _reduce_55(val, _values, result)
result = AnyMatcher.new
result
end
.,.,
def _reduce_none(val, _values, result)
val[0]
end
end # class Parser
end # module Machete

1463
test/racc/regress/mediacloth Normal file

File diff suppressed because it is too large Load diff

1368
test/racc/regress/mof Normal file

File diff suppressed because it is too large Load diff

634
test/racc/regress/namae Normal file
View file

@ -0,0 +1,634 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
require 'racc/parser.rb'
require 'singleton'
require 'strscan'
module Namae
class Parser < Racc::Parser
module_eval(<<'...end namae.y/module_eval...', 'namae.y', 135)
include Singleton
attr_reader :options, :input
def initialize
@input, @options = StringScanner.new(''), {
:debug => false,
:prefer_comma_as_separator => false,
:comma => ',',
:stops => ',;',
:separator => /\s*(\band\b|\&|;)\s*/i,
:title => /\s*\b(sir|lord|count(ess)?|(gen|adm|col|maj|capt|cmdr|lt|sgt|cpl|pvt|prof|dr|md|ph\.?d)\.?)(\s+|$)/i,
:suffix => /\s*\b(JR|Jr|jr|SR|Sr|sr|[IVX]{2,})(\.|\b)/,
:appellation => /\s*\b((mrs?|ms|fr|hr)\.?|miss|herr|frau)(\s+|$)/i
}
end
def debug?
options[:debug] || ENV['DEBUG']
end
def separator
options[:separator]
end
def comma
options[:comma]
end
def stops
options[:stops]
end
def title
options[:title]
end
def suffix
options[:suffix]
end
def appellation
options[:appellation]
end
def prefer_comma_as_separator?
options[:prefer_comma_as_separator]
end
def parse(input)
parse!(input)
rescue => e
warn e.message if debug?
[]
end
def parse!(string)
input.string = normalize(string)
reset
do_parse
end
def normalize(string)
string = string.strip
string
end
def reset
@commas, @words, @initials, @suffices, @yydebug = 0, 0, 0, 0, debug?
self
end
private
def stack
@vstack || @racc_vstack || []
end
def last_token
stack[-1]
end
def consume_separator
return next_token if seen_separator?
@commas, @words, @initials, @suffices = 0, 0, 0, 0
[:AND, :AND]
end
def consume_comma
@commas += 1
[:COMMA, :COMMA]
end
def consume_word(type, word)
@words += 1
case type
when :UWORD
@initials += 1 if word =~ /^[[:upper:]]+\b/
when :SUFFIX
@suffices += 1
end
[type, word]
end
def seen_separator?
!stack.empty? && last_token == :AND
end
def suffix?
!@suffices.zero? || will_see_suffix?
end
def will_see_suffix?
input.peek(8).to_s.strip.split(/\s+/)[0] =~ suffix
end
def will_see_initial?
input.peek(6).to_s.strip.split(/\s+/)[0] =~ /^[[:upper:]]+\b/
end
def seen_full_name?
prefer_comma_as_separator? && @words > 1 &&
(@initials > 0 || !will_see_initial?) && !will_see_suffix?
end
def next_token
case
when input.nil?, input.eos?
nil
when input.scan(separator)
consume_separator
when input.scan(/\s*#{comma}\s*/)
if @commas.zero? && !seen_full_name? || @commas == 1 && suffix?
consume_comma
else
consume_separator
end
when input.scan(/\s+/)
next_token
when input.scan(title)
consume_word(:TITLE, input.matched.strip)
when input.scan(suffix)
consume_word(:SUFFIX, input.matched.strip)
when input.scan(appellation)
[:APPELLATION, input.matched.strip]
when input.scan(/((\\\w+)?\{[^\}]*\})*[[:upper:]][^\s#{stops}]*/)
consume_word(:UWORD, input.matched)
when input.scan(/((\\\w+)?\{[^\}]*\})*[[:lower:]][^\s#{stops}]*/)
consume_word(:LWORD, input.matched)
when input.scan(/(\\\w+)?\{[^\}]*\}[^\s#{stops}]*/)
consume_word(:PWORD, input.matched)
when input.scan(/('[^'\n]+')|("[^"\n]+")/)
consume_word(:NICK, input.matched[1...-1])
else
raise ArgumentError,
"Failed to parse name #{input.string.inspect}: unmatched data at offset #{input.pos}"
end
end
def on_error(tid, value, stack)
raise ArgumentError,
"Failed to parse name: unexpected '#{value}' at #{stack.inspect}"
end
# -*- racc -*-
...end namae.y/module_eval...
##### State transition tables begin ###
racc_action_table = [
-39, 53, 52, 54, -40, 39, 62, -39, 39, -39,
-39, -40, 67, -40, -40, 66, 53, 52, 54, 32,
59, 16, 58, -34, 53, 52, 54, -38, 17, -22,
30, 39, 31, 45, -38, 53, 52, 54, 14, 12,
15, 68, 39, 7, 8, 14, 12, 15, 58, 33,
7, 8, 14, 22, 15, 24, 14, 22, 15, 24,
-19, -19, -19, 30, 42, 31, 30, 28, 31, -20,
-20, -20, 30, 46, 31, 53, 52, 54, 30, 28,
31, 30, 28, 31, 30, 28, 31, -19, -19, -19,
30, 28, 31, 14, 22, 15, 53, 52, 54, 39,
58, 59, 39, 59, 39 ]
racc_action_check = [
14, 32, 32, 32, 15, 64, 44, 14, 32, 14,
14, 15, 50, 15, 15, 49, 49, 49, 49, 11,
50, 1, 70, 49, 45, 45, 45, 12, 1, 12,
43, 45, 43, 27, 12, 62, 62, 62, 0, 0,
0, 57, 62, 0, 0, 17, 17, 17, 60, 16,
17, 17, 20, 20, 20, 20, 9, 9, 9, 9,
22, 22, 22, 24, 24, 24, 25, 25, 25, 28,
28, 28, 29, 29, 29, 73, 73, 73, 21, 21,
21, 35, 35, 35, 41, 41, 41, 42, 42, 42,
10, 10, 10, 5, 5, 5, 67, 67, 67, 61,
37, 38, 40, 72, 23 ]
racc_action_pointer = [
35, 21, nil, nil, nil, 90, nil, nil, nil, 53,
87, 17, 27, nil, 0, 4, 49, 42, nil, nil,
49, 75, 57, 94, 60, 63, nil, 31, 66, 69,
nil, nil, -2, nil, nil, 78, nil, 91, 91, nil,
92, 81, 84, 27, 4, 21, nil, nil, nil, 13,
10, nil, nil, nil, nil, nil, nil, 32, nil, nil,
39, 89, 32, nil, -5, nil, nil, 93, nil, nil,
13, nil, 93, 72, nil ]
racc_action_default = [
-1, -49, -2, -4, -5, -49, -8, -9, -10, -23,
-49, -49, -19, -28, -30, -31, -49, -49, -6, -7,
-49, -49, -38, -41, -49, -49, -29, -15, -22, -23,
-30, -31, -36, 75, -3, -49, -15, -45, -42, -43,
-41, -49, -22, -23, -14, -36, -21, -16, -24, -37,
-26, -32, -38, -39, -40, -14, -11, -46, -47, -44,
-45, -41, -36, -17, -49, -33, -35, -49, -48, -12,
-45, -18, -25, -27, -13 ]
racc_goto_table = [
3, 37, 26, 50, 56, 18, 2, 9, 47, 23,
73, 64, 20, 26, 19, 27, 50, 3, 60, 1,
23, 63, 26, 34, 9, nil, 36, 69, 21, 40,
44, 43, 25, 50, nil, 72, 26, 74, 71, 70,
55, nil, nil, 35, nil, nil, 61, 41, nil, 65,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, 65 ]
racc_goto_check = [
3, 8, 17, 16, 9, 3, 2, 7, 12, 3,
14, 15, 7, 17, 4, 10, 16, 3, 8, 1,
3, 12, 17, 2, 7, nil, 10, 9, 11, 10,
10, 7, 11, 16, nil, 16, 17, 9, 12, 8,
10, nil, nil, 11, nil, nil, 10, 11, nil, 3,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, 3 ]
racc_goto_pointer = [
nil, 19, 6, 0, 9, nil, nil, 7, -22, -33,
5, 23, -24, nil, -57, -38, -29, -7, nil ]
racc_goto_default = [
nil, nil, nil, 51, 4, 5, 6, 29, nil, nil,
11, 10, nil, 48, 49, nil, 38, 13, 57 ]
racc_reduce_table = [
0, 0, :racc_error,
0, 12, :_reduce_1,
1, 12, :_reduce_2,
3, 12, :_reduce_3,
1, 13, :_reduce_4,
1, 13, :_reduce_none,
2, 13, :_reduce_6,
2, 13, :_reduce_7,
1, 13, :_reduce_none,
1, 16, :_reduce_9,
1, 16, :_reduce_10,
4, 15, :_reduce_11,
5, 15, :_reduce_12,
6, 15, :_reduce_13,
3, 15, :_reduce_14,
2, 15, :_reduce_15,
3, 17, :_reduce_16,
4, 17, :_reduce_17,
5, 17, :_reduce_18,
1, 22, :_reduce_none,
2, 22, :_reduce_20,
3, 22, :_reduce_21,
1, 21, :_reduce_none,
1, 21, :_reduce_none,
1, 23, :_reduce_24,
3, 23, :_reduce_25,
1, 23, :_reduce_26,
3, 23, :_reduce_27,
1, 18, :_reduce_none,
2, 18, :_reduce_29,
1, 28, :_reduce_none,
1, 28, :_reduce_none,
1, 25, :_reduce_none,
2, 25, :_reduce_33,
0, 26, :_reduce_none,
1, 26, :_reduce_none,
0, 24, :_reduce_none,
1, 24, :_reduce_none,
1, 14, :_reduce_none,
1, 14, :_reduce_none,
1, 14, :_reduce_none,
0, 19, :_reduce_none,
1, 19, :_reduce_none,
1, 27, :_reduce_none,
2, 27, :_reduce_44,
0, 20, :_reduce_none,
1, 20, :_reduce_none,
1, 29, :_reduce_none,
2, 29, :_reduce_48 ]
racc_reduce_n = 49
racc_shift_n = 75
racc_token_table = {
false => 0,
:error => 1,
:COMMA => 2,
:UWORD => 3,
:LWORD => 4,
:PWORD => 5,
:NICK => 6,
:AND => 7,
:APPELLATION => 8,
:TITLE => 9,
:SUFFIX => 10 }
racc_nt_base = 11
racc_use_result_var = true
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 ]
Racc_token_to_s_table = [
"$end",
"error",
"COMMA",
"UWORD",
"LWORD",
"PWORD",
"NICK",
"AND",
"APPELLATION",
"TITLE",
"SUFFIX",
"$start",
"names",
"name",
"word",
"display_order",
"honorific",
"sort_order",
"u_words",
"opt_suffices",
"opt_titles",
"last",
"von",
"first",
"opt_words",
"words",
"opt_comma",
"suffices",
"u_word",
"titles" ]
Racc_debug_parser = false
##### State transition tables end #####
# reduce 0 omitted
module_eval(<<'.,.,', 'namae.y', 39)
def _reduce_1(val, _values, result)
result = []
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 40)
def _reduce_2(val, _values, result)
result = [val[0]]
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 41)
def _reduce_3(val, _values, result)
result = val[0] << val[2]
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 43)
def _reduce_4(val, _values, result)
result = Name.new(:given => val[0])
result
end
.,.,
# reduce 5 omitted
module_eval(<<'.,.,', 'namae.y', 45)
def _reduce_6(val, _values, result)
result = val[0].merge(:family => val[1])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 46)
def _reduce_7(val, _values, result)
result = val[1].merge(val[0])
result
end
.,.,
# reduce 8 omitted
module_eval(<<'.,.,', 'namae.y', 49)
def _reduce_9(val, _values, result)
result = Name.new(:appellation => val[0])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 50)
def _reduce_10(val, _values, result)
result = Name.new(:title => val[0])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 54)
def _reduce_11(val, _values, result)
result = Name.new(:given => val[0], :family => val[1],
:suffix => val[2], :title => val[3])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 59)
def _reduce_12(val, _values, result)
result = Name.new(:given => val[0], :nick => val[1],
:family => val[2], :suffix => val[3], :title => val[4])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 64)
def _reduce_13(val, _values, result)
result = Name.new(:given => val[0], :nick => val[1],
:particle => val[2], :family => val[3],
:suffix => val[4], :title => val[5])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 70)
def _reduce_14(val, _values, result)
result = Name.new(:given => val[0], :particle => val[1],
:family => val[2])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 75)
def _reduce_15(val, _values, result)
result = Name.new(:particle => val[0], :family => val[1])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 80)
def _reduce_16(val, _values, result)
result = Name.new({ :family => val[0], :suffix => val[2][0],
:given => val[2][1] }, !!val[2][0])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 85)
def _reduce_17(val, _values, result)
result = Name.new({ :particle => val[0], :family => val[1],
:suffix => val[3][0], :given => val[3][1] }, !!val[3][0])
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 90)
def _reduce_18(val, _values, result)
result = Name.new({ :particle => val[0,2].join(' '), :family => val[2],
:suffix => val[4][0], :given => val[4][1] }, !!val[4][0])
result
end
.,.,
# reduce 19 omitted
module_eval(<<'.,.,', 'namae.y', 96)
def _reduce_20(val, _values, result)
result = val.join(' ')
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 97)
def _reduce_21(val, _values, result)
result = val.join(' ')
result
end
.,.,
# reduce 22 omitted
# reduce 23 omitted
module_eval(<<'.,.,', 'namae.y', 101)
def _reduce_24(val, _values, result)
result = [nil,val[0]]
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 102)
def _reduce_25(val, _values, result)
result = [val[2],val[0]]
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 103)
def _reduce_26(val, _values, result)
result = [val[0],nil]
result
end
.,.,
module_eval(<<'.,.,', 'namae.y', 104)
def _reduce_27(val, _values, result)
result = [val[0],val[2]]
result
end
.,.,
# reduce 28 omitted
module_eval(<<'.,.,', 'namae.y', 107)
def _reduce_29(val, _values, result)
result = val.join(' ')
result
end
.,.,
# reduce 30 omitted
# reduce 31 omitted
# reduce 32 omitted
module_eval(<<'.,.,', 'namae.y', 112)
def _reduce_33(val, _values, result)
result = val.join(' ')
result
end
.,.,
# reduce 34 omitted
# reduce 35 omitted
# reduce 36 omitted
# reduce 37 omitted
# reduce 38 omitted
# reduce 39 omitted
# reduce 40 omitted
# reduce 41 omitted
# reduce 42 omitted
# reduce 43 omitted
module_eval(<<'.,.,', 'namae.y', 122)
def _reduce_44(val, _values, result)
result = val.join(' ')
result
end
.,.,
# reduce 45 omitted
# reduce 46 omitted
# reduce 47 omitted
module_eval(<<'.,.,', 'namae.y', 127)
def _reduce_48(val, _values, result)
result = val.join(' ')
result
end
.,.,
def _reduce_none(val, _values, result)
val[0]
end
end # class Parser
end # module Namae

2058
test/racc/regress/nasl Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,836 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
require 'racc/parser.rb'
require 'nokogiri/css/parser_extras'
module Nokogiri
module CSS
class Parser < Racc::Parser
##### State transition tables begin ###
racc_action_table = [
24, 93, 56, 57, 33, 55, 94, 23, 24, 22,
12, 93, 33, 27, 89, 52, 92, 22, -23, 25,
108, 98, 23, 33, 26, 18, 20, 25, 27, -23,
23, 24, 26, 18, 20, 33, 27, 11, 39, 24,
22, 23, 95, 33, 18, 101, 100, 27, 22, 12,
25, 91, 90, 23, 33, 26, 18, 20, 25, 27,
90, 23, 24, 26, 18, 20, 33, 27, 96, 39,
-23, 22, 23, 74, 24, 18, 33, 99, 27, 56,
87, 25, 60, 46, 23, 49, 26, 18, 20, 102,
27, 39, 24, 51, 23, 103, 93, 18, 26, 33,
27, 66, 44, 56, 58, 105, 60, 33, 35, 33,
109, 51, 56, 87, 39, 60, 26, 23, 85, 33,
18, 20, 39, 27, 39, 23, 45, 23, 18, 33,
18, 27, 86, 27, 39, 88, 33, 23, nil, nil,
18, 22, nil, 27, 39, 56, 87, 23, 60, nil,
18, 39, nil, 27, 23, 82, 83, 18, 20, nil,
27, nil, nil, nil, 82, 83, 78, 79, 80, nil,
81, nil, nil, nil, 77, 78, 79, 80, nil, 81,
4, 5, 43, 77, 4, 5, 10, nil, 56, 87,
6, 60, 8, 7, 6, nil, 8, 7, 4, 5,
10, nil, nil, nil, nil, nil, nil, nil, 6, nil,
8, 7 ]
racc_action_check = [
3, 58, 24, 24, 3, 24, 57, 15, 42, 3,
64, 57, 42, 15, 54, 24, 56, 42, 58, 3,
94, 64, 3, 31, 3, 3, 3, 42, 3, 22,
42, 9, 42, 42, 42, 9, 42, 1, 31, 43,
9, 31, 59, 43, 31, 76, 76, 31, 43, 1,
9, 55, 55, 9, 30, 9, 9, 9, 43, 9,
60, 43, 12, 43, 43, 43, 12, 43, 61, 30,
46, 12, 30, 45, 23, 30, 29, 75, 30, 93,
93, 12, 93, 23, 12, 23, 12, 12, 12, 84,
12, 29, 27, 23, 29, 86, 87, 29, 23, 25,
29, 27, 18, 25, 25, 91, 25, 28, 11, 62,
105, 27, 51, 51, 25, 51, 27, 25, 49, 14,
25, 25, 28, 25, 62, 28, 21, 62, 28, 32,
62, 28, 50, 62, 14, 53, 39, 14, nil, nil,
14, 39, nil, 14, 32, 90, 90, 32, 90, nil,
32, 39, nil, 32, 39, 47, 47, 39, 39, nil,
39, nil, nil, nil, 48, 48, 47, 47, 47, nil,
47, nil, nil, nil, 47, 48, 48, 48, nil, 48,
17, 17, 17, 48, 0, 0, 0, nil, 92, 92,
17, 92, 17, 17, 0, nil, 0, 0, 26, 26,
26, nil, nil, nil, nil, nil, nil, nil, 26, nil,
26, 26 ]
racc_action_pointer = [
177, 37, nil, -2, nil, nil, nil, nil, nil, 29,
nil, 108, 60, nil, 113, -17, nil, 173, 91, nil,
nil, 97, 0, 72, -8, 93, 191, 90, 101, 70,
48, 17, 123, nil, nil, nil, nil, nil, nil, 130,
nil, nil, 6, 37, nil, 62, 41, 152, 161, 93,
103, 102, nil, 112, -9, 40, 4, -1, -11, 19,
48, 45, 103, nil, -2, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, 52, 35, nil, nil, nil,
nil, nil, nil, nil, 64, nil, 84, 84, nil, nil,
135, 98, 178, 69, 7, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, 97, nil, nil, nil, nil ]
racc_action_default = [
-74, -75, -2, -24, -4, -5, -6, -7, -8, -24,
-73, -75, -24, -3, -47, -10, -13, -17, -75, -19,
-20, -75, -22, -24, -75, -24, -74, -75, -53, -54,
-55, -56, -57, -58, -14, 110, -1, -9, -46, -24,
-11, -12, -24, -24, -18, -75, -29, -61, -61, -75,
-75, -75, -30, -75, -75, -38, -39, -40, -22, -75,
-38, -75, -70, -72, -75, -44, -45, -48, -49, -50,
-51, -52, -15, -16, -21, -75, -75, -62, -63, -64,
-65, -66, -67, -68, -75, -27, -75, -40, -31, -32,
-75, -43, -75, -75, -75, -33, -69, -71, -34, -25,
-59, -60, -26, -28, -35, -75, -36, -37, -42, -41 ]
racc_goto_table = [
53, 38, 13, 1, 54, 48, 62, 42, 34, 65,
37, 36, 63, 75, 84, 67, 68, 69, 70, 71,
62, 40, 41, 50, 47, nil, 63, nil, nil, 64,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, 72, 73, nil, nil, nil, nil, nil, nil, 97,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, 104, nil, 106, 107 ]
racc_goto_check = [
18, 12, 2, 1, 19, 9, 7, 5, 2, 9,
8, 2, 12, 17, 17, 12, 12, 12, 12, 12,
7, 10, 11, 15, 16, nil, 12, nil, nil, 1,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, 2, 2, nil, nil, nil, nil, nil, nil, 12,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, 18, nil, 18, 18 ]
racc_goto_pointer = [
nil, 3, -1, nil, nil, -10, nil, -19, -4, -18,
6, 7, -13, nil, nil, 0, 1, -34, -24, -20,
nil, nil, nil, nil ]
racc_goto_default = [
nil, nil, nil, 2, 3, 9, 17, 14, nil, 15,
31, 30, 16, 29, 19, 21, nil, nil, 59, nil,
28, 32, 76, 61 ]
racc_reduce_table = [
0, 0, :racc_error,
3, 32, :_reduce_1,
1, 32, :_reduce_2,
2, 32, :_reduce_3,
1, 36, :_reduce_4,
1, 36, :_reduce_5,
1, 36, :_reduce_6,
1, 36, :_reduce_7,
1, 36, :_reduce_8,
2, 37, :_reduce_9,
1, 37, :_reduce_none,
2, 37, :_reduce_11,
2, 37, :_reduce_12,
1, 37, :_reduce_13,
2, 34, :_reduce_14,
3, 33, :_reduce_15,
3, 33, :_reduce_16,
1, 33, :_reduce_none,
2, 44, :_reduce_18,
1, 38, :_reduce_none,
1, 38, :_reduce_20,
3, 45, :_reduce_21,
1, 45, :_reduce_22,
1, 46, :_reduce_23,
0, 46, :_reduce_none,
4, 42, :_reduce_25,
4, 42, :_reduce_26,
3, 42, :_reduce_27,
3, 47, :_reduce_28,
1, 47, :_reduce_29,
2, 40, :_reduce_30,
3, 40, :_reduce_31,
3, 40, :_reduce_32,
3, 40, :_reduce_33,
3, 40, :_reduce_34,
3, 49, :_reduce_35,
3, 49, :_reduce_36,
3, 49, :_reduce_37,
1, 49, :_reduce_none,
1, 49, :_reduce_none,
1, 49, :_reduce_40,
4, 50, :_reduce_41,
3, 50, :_reduce_42,
2, 50, :_reduce_43,
2, 41, :_reduce_44,
2, 41, :_reduce_45,
1, 39, :_reduce_none,
0, 39, :_reduce_none,
2, 43, :_reduce_48,
2, 43, :_reduce_49,
2, 43, :_reduce_50,
2, 43, :_reduce_51,
2, 43, :_reduce_52,
1, 43, :_reduce_none,
1, 43, :_reduce_none,
1, 43, :_reduce_none,
1, 43, :_reduce_none,
1, 43, :_reduce_none,
1, 51, :_reduce_58,
2, 48, :_reduce_59,
2, 48, :_reduce_60,
0, 48, :_reduce_none,
1, 53, :_reduce_62,
1, 53, :_reduce_63,
1, 53, :_reduce_64,
1, 53, :_reduce_65,
1, 53, :_reduce_66,
1, 53, :_reduce_67,
1, 53, :_reduce_68,
3, 52, :_reduce_69,
1, 54, :_reduce_none,
2, 54, :_reduce_none,
1, 54, :_reduce_none,
1, 35, :_reduce_none,
0, 35, :_reduce_none ]
racc_reduce_n = 75
racc_shift_n = 110
racc_token_table = {
false => 0,
:error => 1,
:FUNCTION => 2,
:INCLUDES => 3,
:DASHMATCH => 4,
:LBRACE => 5,
:HASH => 6,
:PLUS => 7,
:GREATER => 8,
:S => 9,
:STRING => 10,
:IDENT => 11,
:COMMA => 12,
:NUMBER => 13,
:PREFIXMATCH => 14,
:SUFFIXMATCH => 15,
:SUBSTRINGMATCH => 16,
:TILDE => 17,
:NOT_EQUAL => 18,
:SLASH => 19,
:DOUBLESLASH => 20,
:NOT => 21,
:EQUAL => 22,
:RPAREN => 23,
:LSQUARE => 24,
:RSQUARE => 25,
:HAS => 26,
"." => 27,
"*" => 28,
"|" => 29,
":" => 30 }
racc_nt_base = 31
racc_use_result_var = true
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 ]
Racc_token_to_s_table = [
"$end",
"error",
"FUNCTION",
"INCLUDES",
"DASHMATCH",
"LBRACE",
"HASH",
"PLUS",
"GREATER",
"S",
"STRING",
"IDENT",
"COMMA",
"NUMBER",
"PREFIXMATCH",
"SUFFIXMATCH",
"SUBSTRINGMATCH",
"TILDE",
"NOT_EQUAL",
"SLASH",
"DOUBLESLASH",
"NOT",
"EQUAL",
"RPAREN",
"LSQUARE",
"RSQUARE",
"HAS",
"\".\"",
"\"*\"",
"\"|\"",
"\":\"",
"$start",
"selector",
"simple_selector_1toN",
"prefixless_combinator_selector",
"optional_S",
"combinator",
"simple_selector",
"element_name",
"hcap_0toN",
"function",
"pseudo",
"attrib",
"hcap_1toN",
"class",
"namespaced_ident",
"namespace",
"attrib_name",
"attrib_val_0or1",
"expr",
"nth",
"attribute_id",
"negation",
"eql_incl_dash",
"negation_arg" ]
Racc_debug_parser = false
##### State transition tables end #####
# reduce 0 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 9)
def _reduce_1(val, _values, result)
result = [val.first, val.last].flatten
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 11)
def _reduce_2(val, _values, result)
result = val.flatten
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 12)
def _reduce_3(val, _values, result)
result = [val.last].flatten
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 15)
def _reduce_4(val, _values, result)
result = :DIRECT_ADJACENT_SELECTOR
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 16)
def _reduce_5(val, _values, result)
result = :CHILD_SELECTOR
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 17)
def _reduce_6(val, _values, result)
result = :FOLLOWING_SELECTOR
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 18)
def _reduce_7(val, _values, result)
result = :DESCENDANT_SELECTOR
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 19)
def _reduce_8(val, _values, result)
result = :CHILD_SELECTOR
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 23)
def _reduce_9(val, _values, result)
result = if val[1].nil?
val.first
else
Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
end
result
end
.,.,
# reduce 10 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 31)
def _reduce_11(val, _values, result)
result = Node.new(:CONDITIONAL_SELECTOR, val)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 34)
def _reduce_12(val, _values, result)
result = Node.new(:CONDITIONAL_SELECTOR, val)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 37)
def _reduce_13(val, _values, result)
result = Node.new(:CONDITIONAL_SELECTOR,
[Node.new(:ELEMENT_NAME, ['*']), val.first]
)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 44)
def _reduce_14(val, _values, result)
result = Node.new(val.first, [nil, val.last])
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 49)
def _reduce_15(val, _values, result)
result = Node.new(val[1], [val.first, val.last])
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 52)
def _reduce_16(val, _values, result)
result = Node.new(:DESCENDANT_SELECTOR, [val.first, val.last])
result
end
.,.,
# reduce 17 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 57)
def _reduce_18(val, _values, result)
result = Node.new(:CLASS_CONDITION, [val[1]])
result
end
.,.,
# reduce 19 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 61)
def _reduce_20(val, _values, result)
result = Node.new(:ELEMENT_NAME, val)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 65)
def _reduce_21(val, _values, result)
result = Node.new(:ELEMENT_NAME,
[[val.first, val.last].compact.join(':')]
)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 70)
def _reduce_22(val, _values, result)
name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first
result = Node.new(:ELEMENT_NAME, [name])
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 75)
def _reduce_23(val, _values, result)
result = val[0]
result
end
.,.,
# reduce 24 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 80)
def _reduce_25(val, _values, result)
result = Node.new(:ATTRIBUTE_CONDITION,
[val[1]] + (val[2] || [])
)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 85)
def _reduce_26(val, _values, result)
result = Node.new(:ATTRIBUTE_CONDITION,
[val[1]] + (val[2] || [])
)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 90)
def _reduce_27(val, _values, result)
# Non standard, but hpricot supports it.
result = Node.new(:PSEUDO_CLASS,
[Node.new(:FUNCTION, ['nth-child(', val[1]])]
)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 98)
def _reduce_28(val, _values, result)
result = Node.new(:ELEMENT_NAME,
[[val.first, val.last].compact.join(':')]
)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 103)
def _reduce_29(val, _values, result)
# Default namespace is not applied to attributes.
# So we don't add prefix "xmlns:" as in namespaced_ident.
result = Node.new(:ELEMENT_NAME, [val.first])
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 110)
def _reduce_30(val, _values, result)
result = Node.new(:FUNCTION, [val.first.strip])
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 113)
def _reduce_31(val, _values, result)
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 116)
def _reduce_32(val, _values, result)
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 119)
def _reduce_33(val, _values, result)
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 122)
def _reduce_34(val, _values, result)
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 126)
def _reduce_35(val, _values, result)
result = [val.first, val.last]
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 127)
def _reduce_36(val, _values, result)
result = [val.first, val.last]
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 128)
def _reduce_37(val, _values, result)
result = [val.first, val.last]
result
end
.,.,
# reduce 38 omitted
# reduce 39 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 133)
def _reduce_40(val, _values, result)
case val[0]
when 'even'
result = Node.new(:NTH, ['2','n','+','0'])
when 'odd'
result = Node.new(:NTH, ['2','n','+','1'])
when 'n'
result = Node.new(:NTH, ['1','n','+','0'])
else
# This is not CSS standard. It allows us to support this:
# assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
# assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
# assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
result = val
end
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 152)
def _reduce_41(val, _values, result)
if val[1] == 'n'
result = Node.new(:NTH, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 158)
def _reduce_42(val, _values, result)
# n+3, -n+3
if val[0] == 'n'
val.unshift("1")
result = Node.new(:NTH, val)
elsif val[0] == '-n'
val[0] = 'n'
val.unshift("-1")
result = Node.new(:NTH, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 170)
def _reduce_43(val, _values, result)
# 5n, -5n, 10n-1
n = val[1]
if n[0, 2] == 'n-'
val[1] = 'n'
val << "-"
# b is contained in n as n is the string "n-b"
val << n[2, n.size]
result = Node.new(:NTH, val)
elsif n == 'n'
val << "+"
val << "0"
result = Node.new(:NTH, val)
else
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
end
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 189)
def _reduce_44(val, _values, result)
result = Node.new(:PSEUDO_CLASS, [val[1]])
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 191)
def _reduce_45(val, _values, result)
result = Node.new(:PSEUDO_CLASS, [val[1]])
result
end
.,.,
# reduce 46 omitted
# reduce 47 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 199)
def _reduce_48(val, _values, result)
result = Node.new(:COMBINATOR, val)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 202)
def _reduce_49(val, _values, result)
result = Node.new(:COMBINATOR, val)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 205)
def _reduce_50(val, _values, result)
result = Node.new(:COMBINATOR, val)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 208)
def _reduce_51(val, _values, result)
result = Node.new(:COMBINATOR, val)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 211)
def _reduce_52(val, _values, result)
result = Node.new(:COMBINATOR, val)
result
end
.,.,
# reduce 53 omitted
# reduce 54 omitted
# reduce 55 omitted
# reduce 56 omitted
# reduce 57 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 220)
def _reduce_58(val, _values, result)
result = Node.new(:ID, val)
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 223)
def _reduce_59(val, _values, result)
result = [val.first, val[1]]
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 224)
def _reduce_60(val, _values, result)
result = [val.first, val[1]]
result
end
.,.,
# reduce 61 omitted
module_eval(<<'.,.,', 'nokogiri-css.y', 228)
def _reduce_62(val, _values, result)
result = :equal
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 229)
def _reduce_63(val, _values, result)
result = :prefix_match
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 230)
def _reduce_64(val, _values, result)
result = :suffix_match
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 231)
def _reduce_65(val, _values, result)
result = :substring_match
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 232)
def _reduce_66(val, _values, result)
result = :not_equal
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 233)
def _reduce_67(val, _values, result)
result = :includes
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 234)
def _reduce_68(val, _values, result)
result = :dash_match
result
end
.,.,
module_eval(<<'.,.,', 'nokogiri-css.y', 238)
def _reduce_69(val, _values, result)
result = Node.new(:NOT, [val[1]])
result
end
.,.,
# reduce 70 omitted
# reduce 71 omitted
# reduce 72 omitted
# reduce 73 omitted
# reduce 74 omitted
def _reduce_none(val, _values, result)
val[0]
end
end # class Parser
end # module CSS
end # module Nokogiri

6429
test/racc/regress/opal Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,336 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
require 'racc/parser.rb'
require 'php_serialization/tokenizer'
module PhpSerialization
class Unserializer < Racc::Parser
module_eval(<<'...end php_serialization.y/module_eval...', 'php_serialization.y', 84)
def initialize(tokenizer_klass = Tokenizer)
@tokenizer_klass = tokenizer_klass
end
def run(string)
@tokenizer = @tokenizer_klass.new(string)
yyparse(@tokenizer, :each)
return @object
ensure
@tokenizer = nil
end
def next_token
@tokenizer.next_token
end
...end php_serialization.y/module_eval...
##### State transition tables begin ###
racc_action_table = [
9, 10, 18, 20, 11, 12, 13, 21, 14, 9,
10, 15, 22, 11, 12, 13, 23, 14, 24, 46,
15, 9, 10, 25, 26, 11, 12, 13, 27, 14,
9, 10, 15, 28, 11, 12, 13, 29, 14, 30,
51, 15, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 43, 49, 47, 16, 17, 19 ]
racc_action_check = [
0, 0, 3, 5, 0, 0, 0, 6, 0, 42,
42, 0, 10, 42, 42, 42, 11, 42, 12, 42,
42, 45, 45, 13, 14, 45, 45, 45, 15, 45,
50, 50, 45, 16, 50, 50, 50, 22, 50, 23,
50, 50, 24, 25, 26, 27, 32, 33, 34, 35,
36, 37, 39, 41, 47, 43, 1, 2, 4 ]
racc_action_pointer = [
-3, 56, 55, 0, 56, 1, 5, nil, nil, nil,
7, 11, 13, 18, 19, 23, 33, nil, nil, nil,
nil, nil, 31, 33, 36, 37, 38, 39, nil, nil,
nil, nil, 41, 42, 43, 39, 40, 39, nil, 47,
nil, 47, 6, 50, nil, 18, nil, 42, nil, nil,
27, nil ]
racc_action_default = [
-18, -18, -18, -18, -18, -18, -18, -6, -7, -8,
-18, -18, -18, -18, -18, -18, -18, -1, -2, -3,
-4, -5, -18, -18, -18, -18, -18, -18, 52, -9,
-10, -11, -18, -18, -18, -18, -18, -18, -12, -18,
-15, -18, -18, -18, -14, -18, -17, -18, -16, -15,
-18, -13 ]
racc_goto_table = [
1, 42, nil, nil, nil, nil, nil, nil, nil, nil,
50, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, 48 ]
racc_goto_check = [
1, 9, nil, nil, nil, nil, nil, nil, nil, nil,
9, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, 1 ]
racc_goto_pointer = [
nil, 0, nil, nil, nil, nil, nil, nil, nil, -39,
nil ]
racc_goto_default = [
nil, 45, 2, 3, 4, 5, 6, 7, 8, nil,
44 ]
racc_reduce_table = [
0, 0, :racc_error,
2, 16, :_reduce_1,
2, 16, :_reduce_2,
2, 16, :_reduce_3,
2, 16, :_reduce_4,
2, 16, :_reduce_5,
1, 16, :_reduce_6,
1, 16, :_reduce_7,
1, 17, :_reduce_8,
3, 18, :_reduce_9,
3, 19, :_reduce_10,
3, 20, :_reduce_11,
5, 21, :_reduce_12,
11, 23, :_reduce_13,
2, 24, :_reduce_14,
0, 24, :_reduce_15,
2, 25, :_reduce_16,
7, 22, :_reduce_17 ]
racc_reduce_n = 18
racc_shift_n = 52
racc_token_table = {
false => 0,
:error => 1,
";" => 2,
"N" => 3,
"b" => 4,
":" => 5,
:NUMBER => 6,
"i" => 7,
"d" => 8,
"s" => 9,
:STRING => 10,
"O" => 11,
"{" => 12,
"}" => 13,
"a" => 14 }
racc_nt_base = 15
racc_use_result_var = true
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 ]
Racc_token_to_s_table = [
"$end",
"error",
"\";\"",
"\"N\"",
"\"b\"",
"\":\"",
"NUMBER",
"\"i\"",
"\"d\"",
"\"s\"",
"STRING",
"\"O\"",
"\"{\"",
"\"}\"",
"\"a\"",
"$start",
"data",
"null",
"bool",
"integer",
"double",
"string",
"assoc_array",
"object",
"attribute_list",
"attribute" ]
Racc_debug_parser = false
##### State transition tables end #####
# reduce 0 omitted
module_eval(<<'.,.,', 'php_serialization.y', 6)
def _reduce_1(val, _values, result)
@object = val[0]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 7)
def _reduce_2(val, _values, result)
@object = val[0]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 8)
def _reduce_3(val, _values, result)
@object = val[0]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 9)
def _reduce_4(val, _values, result)
@object = val[0]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 10)
def _reduce_5(val, _values, result)
@object = val[0]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 11)
def _reduce_6(val, _values, result)
@object = val[0]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 12)
def _reduce_7(val, _values, result)
@object = val[0]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 15)
def _reduce_8(val, _values, result)
result = nil
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 18)
def _reduce_9(val, _values, result)
result = Integer(val[2]) > 0
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 21)
def _reduce_10(val, _values, result)
result = Integer(val[2])
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 24)
def _reduce_11(val, _values, result)
result = Float(val[2])
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 27)
def _reduce_12(val, _values, result)
result = val[4]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 32)
def _reduce_13(val, _values, result)
if eval("defined?(#{val[4]})")
result = Object.const_get(val[4]).new
val[9].each do |(attr_name, value)|
# Protected and private attributes will have a \0..\0 prefix
attr_name = attr_name.gsub(/\A\\0[^\\]+\\0/, '')
result.instance_variable_set("@#{attr_name}", value)
end
else
klass_name = val[4].gsub(/^Struct::/, '')
attr_names, values = [], []
val[9].each do |(attr_name, value)|
# Protected and private attributes will have a \0..\0 prefix
attr_names << attr_name.gsub(/\A\\0[^\\]+\\0/, '')
values << value
end
result = Struct.new(klass_name, *attr_names).new(*values)
result.instance_variable_set("@_php_class", klass_name)
end
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 56)
def _reduce_14(val, _values, result)
result = val[0] << val[1]
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 57)
def _reduce_15(val, _values, result)
result = []
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 60)
def _reduce_16(val, _values, result)
result = val
result
end
.,.,
module_eval(<<'.,.,', 'php_serialization.y', 65)
def _reduce_17(val, _values, result)
# Checks if the keys are a sequence of integers
idx = -1
arr = val[5].all? { |(k,v)| k == (idx += 1) }
if arr
result = val[5].map { |(k,v)| v }
else
result = Hash[val[5]]
end
result
end
.,.,
def _reduce_none(val, _values, result)
val[0]
end
end # class Unserializer
end # module PhpSerialization

3297
test/racc/regress/riml Normal file

File diff suppressed because it is too large Load diff

6351
test/racc/regress/ruby18 Normal file

File diff suppressed because it is too large Load diff

7456
test/racc/regress/ruby22 Normal file

File diff suppressed because it is too large Load diff

1933
test/racc/regress/tp_plus Normal file

File diff suppressed because it is too large Load diff

556
test/racc/regress/twowaysql Normal file
View file

@ -0,0 +1,556 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.14
# from Racc grammer file "".
#
require 'racc/parser.rb'
module TwoWaySQL
class Parser < Racc::Parser
module_eval(<<'...end twowaysql.y/module_eval...', 'twowaysql.y', 148)
require 'strscan'
def initialize(opts={})
opts = {
:debug => false,
:preserve_space => true,
:preserve_comment => false
}.merge(opts)
@yydebug = opts[:debug]
@preserve_space = opts[:preserve_space]
@preserve_comment = opts[:preserve_comment]
@num_questions = 0
end
PAREN_EXAMPLE = '\([^\)]+\)'
BEGIN_BIND_VARIABLE = '(\/|\#)\*([^\*]+)\*\1'
BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*/
PAREN_BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*#{PAREN_EXAMPLE}/
EMBED_VARIABLE_PATTERN = /\A(\/|\#)\*\$([^\*]+)\*\1\s*/
CONDITIONAL_PATTERN = /\A(\/|\#)\*(IF)\s+([^\*]+)\s*\*\1/
BEGIN_END_PATTERN = /\A(\/|\#)\*(BEGIN|END)\s*\*\1/
STRING_LITERAL_PATTERN = /\A(\'(?:[^\']+|\'\')*\')/ ## quoted string
SPLIT_TOKEN_PATTERN = /\A(\S+?)(?=\s*(?:(?:\/|\#)\*|-{2,}|\(|\)|\,))/ ## stop on delimiters --,/*,#*,',',(,)
LITERAL_PATTERN = /\A([^;\s]+)/
SPACES_PATTERN = /\A(\s+)/
QUESTION_PATTERN = /\A\?/
COMMA_PATTERN = /\A\,/
LPAREN_PATTERN = /\A\(/
RPAREN_PATTERN = /\A\)/
ACTUAL_COMMENT_PATTERN = /\A(\/|\#)\*(\s{1,}(?:.*?))\*\1/m ## start with spaces
SEMICOLON_AT_INPUT_END_PATTERN = /\A\;\s*\Z/
UNMATCHED_COMMENT_START_PATTERN = /\A(?:(?:\/|\#)\*)/
#TODO: remove trailing spaces for S2Dao compatibility, but this spec sometimes causes SQL bugs...
ELSE_PATTERN = /\A\-{2,}\s*ELSE\s*/
AND_PATTERN = /\A(\ *AND)\b/i
OR_PATTERN = /\A(\ *OR)\b/i
def parse( io )
@q = []
io.each_line(nil) do |whole|
@s = StringScanner.new(whole)
end
scan_str
# @q.push [ false, nil ]
@q.push [ false, [@s.pos, nil] ]
## call racc's private parse method
do_parse
end
## called by racc
def next_token
@q.shift
end
def scan_str
until @s.eos? do
case
when @s.scan(AND_PATTERN)
@q.push [ :AND, [@s.pos, @s[1]] ]
when @s.scan(OR_PATTERN)
@q.push [ :OR, [@s.pos, @s[1]] ]
when @s.scan(SPACES_PATTERN)
@q.push [ :SPACES, [@s.pos, @s[1]] ]
when @s.scan(QUESTION_PATTERN)
@q.push [ :QUESTION, [@s.pos, nil] ]
when @s.scan(COMMA_PATTERN)
@q.push [ :COMMA, [@s.pos, ','] ]
when @s.scan(LPAREN_PATTERN)
@q.push [ :LPAREN, [@s.pos, '('] ]
when @s.scan(RPAREN_PATTERN)
@q.push [ :RPAREN, [@s.pos, ')'] ]
when @s.scan(ELSE_PATTERN)
@q.push [ :ELSE, [@s.pos, nil] ]
when @s.scan(ACTUAL_COMMENT_PATTERN)
@q.push [ :ACTUAL_COMMENT, [@s.pos, @s[1], @s[2]] ] if @preserve_comment
when @s.scan(BEGIN_END_PATTERN)
@q.push [ @s[2].intern, [@s.pos, nil] ]
when @s.scan(CONDITIONAL_PATTERN)
@q.push [ @s[2].intern, [@s.pos, @s[3]] ]
when @s.scan(EMBED_VARIABLE_PATTERN)
@q.push [ :EMBED_VARIABLE, [@s.pos, @s[2]] ]
when @s.scan(PAREN_BIND_VARIABLE_PATTERN)
@q.push [ :PAREN_BIND_VARIABLE, [@s.pos, @s[2]] ]
when @s.scan(BIND_VARIABLE_PATTERN)
@q.push [ :BIND_VARIABLE, [@s.pos, @s[2]] ]
when @s.scan(STRING_LITERAL_PATTERN)
@q.push [ :STRING_LITERAL, [@s.pos, @s[1]] ]
when @s.scan(SPLIT_TOKEN_PATTERN)
@q.push [ :IDENT, [@s.pos, @s[1]] ]
when @s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
raise Racc::ParseError, "unmatched comment. line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
when @s.scan(LITERAL_PATTERN) ## other string token
@q.push [ :IDENT, [@s.pos, @s[1]] ]
when @s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
#drop semicolon at input end
else
raise Racc::ParseError, "syntax error at or near line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
end
end
end
## override racc's default on_error method
def on_error(t, v, vstack)
## cursor in value-stack is an array of two items,
## that have position value as 0th item. like [731, "ctx[:limit] "]
cursor = vstack.find do |tokens|
tokens.size == 2 and tokens[0].kind_of?(Fixnum)
end
pos = cursor[0]
line = line_no(pos)
rest = @s.string[pos .. -1]
raise Racc::ParseError, "syntax error at or near line:[#{line}], str:[#{rest}]"
end
def line_no(pos)
lines = 0
scanned = @s.string[0..(pos)]
scanned.each_line { lines += 1 }
lines
end
...end twowaysql.y/module_eval...
##### State transition tables begin ###
racc_action_table = [
8, 36, 9, 37, 12, 13, 10, 11, 14, 15,
16, 17, 18, 19, 22, 23, 24, 8, 3, 9,
25, 12, 13, 10, 11, 14, 15, 16, 17, 18,
19, 22, 23, 24, 8, 38, 9, 46, 12, 13,
10, 11, 14, 15, 16, 17, 18, 19, 22, 23,
24, 8, 40, 9, 45, 12, 13, 10, 11, 14,
15, 16, 17, 18, 19, 22, 23, 24, 8, nil,
9, nil, 12, 13, 10, 11, 14, 15, 16, 17,
18, 19, 22, 23, 24, 35, 33, 34, 44, 43,
31, 32, 31, 32 ]
racc_action_check = [
42, 24, 42, 24, 42, 42, 42, 42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 2, 1, 2,
3, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 26, 26, 26, 39, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 27, 28, 27, 37, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 41, nil,
41, nil, 41, 41, 41, 41, 41, 41, 41, 41,
41, 41, 41, 41, 41, 22, 22, 22, 34, 34,
9, 9, 40, 40 ]
racc_action_pointer = [
nil, 18, 15, 20, nil, nil, nil, nil, nil, 84,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, 77, nil, -7, nil, 32, 49, 47, nil,
nil, nil, nil, nil, 80, nil, nil, 46, nil, 34,
86, 66, -2, nil, nil, nil, nil, nil ]
racc_action_default = [
-2, -35, -1, -35, -3, -4, -5, -6, -2, -2,
-16, -17, -18, -19, -20, -21, -22, -23, -24, -25,
-26, -27, -35, -32, -35, 48, -35, -13, -10, -11,
-12, -2, -2, -28, -35, -30, -33, -35, -7, -35,
-2, -14, -15, -29, -31, -34, -8, -9 ]
racc_goto_table = [
2, 1, 28, 39, nil, nil, nil, nil, 26, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, 41, 42, 47 ]
racc_goto_check = [
2, 1, 7, 8, nil, nil, nil, nil, 2, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, 2, 2, 7 ]
racc_goto_pointer = [
nil, 1, 0, nil, nil, nil, nil, -7, -25, nil,
nil, nil, nil ]
racc_goto_default = [
nil, nil, 27, 4, 5, 6, 7, nil, nil, 29,
30, 20, 21 ]
racc_reduce_table = [
0, 0, :racc_error,
1, 20, :_reduce_1,
0, 21, :_reduce_2,
2, 21, :_reduce_3,
1, 22, :_reduce_none,
1, 22, :_reduce_none,
1, 22, :_reduce_none,
3, 25, :_reduce_7,
4, 24, :_reduce_8,
2, 27, :_reduce_9,
0, 27, :_reduce_10,
1, 26, :_reduce_none,
1, 26, :_reduce_none,
1, 26, :_reduce_none,
2, 28, :_reduce_14,
2, 29, :_reduce_15,
1, 23, :_reduce_16,
1, 23, :_reduce_17,
1, 23, :_reduce_18,
1, 23, :_reduce_19,
1, 23, :_reduce_20,
1, 23, :_reduce_21,
1, 23, :_reduce_22,
1, 23, :_reduce_23,
1, 23, :_reduce_24,
1, 23, :_reduce_25,
1, 23, :_reduce_none,
1, 23, :_reduce_none,
2, 30, :_reduce_28,
3, 30, :_reduce_29,
2, 30, :_reduce_30,
3, 30, :_reduce_31,
1, 30, :_reduce_32,
2, 31, :_reduce_33,
3, 31, :_reduce_34 ]
racc_reduce_n = 35
racc_shift_n = 48
racc_token_table = {
false => 0,
:error => 1,
:BEGIN => 2,
:END => 3,
:IF => 4,
:ELSE => 5,
:AND => 6,
:OR => 7,
:IDENT => 8,
:STRING_LITERAL => 9,
:SPACES => 10,
:COMMA => 11,
:LPAREN => 12,
:RPAREN => 13,
:QUESTION => 14,
:ACTUAL_COMMENT => 15,
:BIND_VARIABLE => 16,
:PAREN_BIND_VARIABLE => 17,
:EMBED_VARIABLE => 18 }
racc_nt_base = 19
racc_use_result_var = true
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 ]
Racc_token_to_s_table = [
"$end",
"error",
"BEGIN",
"END",
"IF",
"ELSE",
"AND",
"OR",
"IDENT",
"STRING_LITERAL",
"SPACES",
"COMMA",
"LPAREN",
"RPAREN",
"QUESTION",
"ACTUAL_COMMENT",
"BIND_VARIABLE",
"PAREN_BIND_VARIABLE",
"EMBED_VARIABLE",
"$start",
"sql",
"stmt_list",
"stmt",
"primary",
"if_stmt",
"begin_stmt",
"sub_stmt",
"else_stmt",
"and_stmt",
"or_stmt",
"bind_var",
"embed_var" ]
Racc_debug_parser = false
##### State transition tables end #####
# reduce 0 omitted
module_eval(<<'.,.,', 'twowaysql.y', 20)
def _reduce_1(val, _values, result)
result = RootNode.new( val[0] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 25)
def _reduce_2(val, _values, result)
result = []
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 29)
def _reduce_3(val, _values, result)
result.push val[1]
result
end
.,.,
# reduce 4 omitted
# reduce 5 omitted
# reduce 6 omitted
module_eval(<<'.,.,', 'twowaysql.y', 38)
def _reduce_7(val, _values, result)
result = BeginNode.new( val[1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 43)
def _reduce_8(val, _values, result)
result = IfNode.new( val[0][1], val[1], val[2] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 48)
def _reduce_9(val, _values, result)
result = val[1]
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 52)
def _reduce_10(val, _values, result)
result = nil
result
end
.,.,
# reduce 11 omitted
# reduce 12 omitted
# reduce 13 omitted
module_eval(<<'.,.,', 'twowaysql.y', 61)
def _reduce_14(val, _values, result)
result = SubStatementNode.new( val[0][1], val[1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 66)
def _reduce_15(val, _values, result)
result = SubStatementNode.new( val[0][1], val[1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 71)
def _reduce_16(val, _values, result)
result = LiteralNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 75)
def _reduce_17(val, _values, result)
result = LiteralNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 79)
def _reduce_18(val, _values, result)
result = LiteralNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 83)
def _reduce_19(val, _values, result)
result = LiteralNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 87)
def _reduce_20(val, _values, result)
result = WhiteSpaceNode.new( val[0][1], @preserve_space )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 91)
def _reduce_21(val, _values, result)
result = LiteralNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 95)
def _reduce_22(val, _values, result)
result = LiteralNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 99)
def _reduce_23(val, _values, result)
result = LiteralNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 103)
def _reduce_24(val, _values, result)
@num_questions += 1
result = QuestionNode.new( @num_questions )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 108)
def _reduce_25(val, _values, result)
result = ActualCommentNode.new( val[0][1] , val[0][2] )
result
end
.,.,
# reduce 26 omitted
# reduce 27 omitted
module_eval(<<'.,.,', 'twowaysql.y', 115)
def _reduce_28(val, _values, result)
result = BindVariableNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 119)
def _reduce_29(val, _values, result)
result = BindVariableNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 123)
def _reduce_30(val, _values, result)
result = BindVariableNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 127)
def _reduce_31(val, _values, result)
result = BindVariableNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 131)
def _reduce_32(val, _values, result)
result = ParenBindVariableNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 136)
def _reduce_33(val, _values, result)
result = EmbedVariableNode.new( val[0][1] )
result
end
.,.,
module_eval(<<'.,.,', 'twowaysql.y', 140)
def _reduce_34(val, _values, result)
result = EmbedVariableNode.new( val[0][1] )
result
end
.,.,
def _reduce_none(val, _values, result)
val[0]
end
end # class Parser
end # module TwoWaySQL

7
test/racc/scandata/brace Normal file
View file

@ -0,0 +1,7 @@
{ {
} { } {
{ { { } } }
{ { { {} } } }
{} {} {}
}
}

Some files were not shown because too many files have changed in this diff Show more