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:
parent
cbe06cd350
commit
1a2546c2be
111 changed files with 75498 additions and 43 deletions
|
|
@ -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));
|
||||
|
||||
|
|
@ -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
7
ext/racc/extconf.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# $Id: 1e30abedf4eea155815d1efa5500ec817b10a2ab $
|
||||
|
||||
require 'mkmf'
|
||||
|
||||
have_func('rb_ary_subseq')
|
||||
|
||||
create_makefile 'racc/cparse'
|
||||
6
lib/racc.rb
Normal file
6
lib/racc.rb
Normal 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
32
lib/racc/compat.rb
Normal 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
59
lib/racc/debugflags.rb
Normal 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
13
lib/racc/exception.rb
Normal 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
1113
lib/racc/grammar.rb
Normal file
File diff suppressed because it is too large
Load diff
559
lib/racc/grammarfileparser.rb
Normal file
559
lib/racc/grammarfileparser.rb
Normal 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
14
lib/racc/info.rb
Normal 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
91
lib/racc/iset.rb
Normal 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
|
||||
211
lib/racc/logfilegenerator.rb
Normal file
211
lib/racc/logfilegenerator.rb
Normal 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
640
lib/racc/parser-text.rb
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
510
lib/racc/parserfilegenerator.rb
Normal file
510
lib/racc/parserfilegenerator.rb
Normal 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
13
lib/racc/pre-setup
Normal 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
34
lib/racc/sourcetext.rb
Normal 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
969
lib/racc/state.rb
Normal 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
|
||||
316
lib/racc/statetransitiontable.rb
Normal file
316
lib/racc/statetransitiontable.rb
Normal 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
5
lib/racc/static.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
require 'racc'
|
||||
require 'racc/parser'
|
||||
require 'racc/grammarfileparser'
|
||||
require 'racc/parserfilegenerator'
|
||||
require 'racc/logfilegenerator'
|
||||
306
libexec/racc
Executable file
306
libexec/racc
Executable 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
195
libexec/racc2y
Executable 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
339
libexec/y2racc
Executable 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
170
test/racc/assets/cadenza.y
Normal 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
926
test/racc/assets/cast.y
Normal 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
126
test/racc/assets/chk.y
Normal 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
16
test/racc/assets/conf.y
Normal 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
729
test/racc/assets/csspool.y
Normal 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
|
||||
29
test/racc/assets/digraph.y
Normal file
29
test/racc/assets/digraph.y
Normal 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
118
test/racc/assets/echk.y
Normal 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
583
test/racc/assets/edtf.y
Normal 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
60
test/racc/assets/err.y
Normal 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
|
||||
35
test/racc/assets/error_recovery.y
Normal file
35
test/racc/assets/error_recovery.y
Normal 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
|
||||
7
test/racc/assets/expect.y
Normal file
7
test/racc/assets/expect.y
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
class E
|
||||
expect 1
|
||||
rule
|
||||
list: inlist inlist
|
||||
inlist:
|
||||
| A
|
||||
end
|
||||
4
test/racc/assets/firstline.y
Normal file
4
test/racc/assets/firstline.y
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class T
|
||||
rule
|
||||
a: A B C
|
||||
end
|
||||
318
test/racc/assets/huia.y
Normal file
318
test/racc/assets/huia.y
Normal 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
102
test/racc/assets/ichk.y
Normal 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
546
test/racc/assets/intp.y
Normal 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
|
||||
47
test/racc/assets/journey.y
Normal file
47
test/racc/assets/journey.y
Normal 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
313
test/racc/assets/liquor.y
Normal 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
423
test/racc/assets/machete.y
Normal 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
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
437
test/racc/assets/mailp.y
Normal 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
|
||||
599
test/racc/assets/mediacloth.y
Normal file
599
test/racc/assets/mediacloth.y
Normal 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
649
test/racc/assets/mof.y
Normal 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
302
test/racc/assets/namae.y
Normal 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
626
test/racc/assets/nasl.y
Normal 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
25
test/racc/assets/newsyn.y
Normal 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
4
test/racc/assets/noend.y
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
class MyParser
|
||||
rule
|
||||
input: A B C
|
||||
end
|
||||
255
test/racc/assets/nokogiri-css.y
Normal file
255
test/racc/assets/nokogiri-css.y
Normal 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
41
test/racc/assets/nonass.y
Normal 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
27
test/racc/assets/normal.y
Normal 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
|
||||
4
test/racc/assets/norule.y
Normal file
4
test/racc/assets/norule.y
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
class A
|
||||
rule
|
||||
end
|
||||
25
test/racc/assets/nullbug1.y
Normal file
25
test/racc/assets/nullbug1.y
Normal 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
|
||||
15
test/racc/assets/nullbug2.y
Normal file
15
test/racc/assets/nullbug2.y
Normal 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
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
123
test/racc/assets/opt.y
Normal 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
|
||||
)
|
||||
35
test/racc/assets/percent.y
Normal file
35
test/racc/assets/percent.y
Normal 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
|
||||
98
test/racc/assets/php_serialization.y
Normal file
98
test/racc/assets/php_serialization.y
Normal 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
97
test/racc/assets/recv.y
Normal 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
665
test/racc/assets/riml.y
Normal 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
14
test/racc/assets/rrconf.y
Normal 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
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
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
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
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
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
72
test/racc/assets/scan.y
Normal 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
50
test/racc/assets/syntax.y
Normal 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
622
test/racc/assets/tp_plus.y
Normal 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
|
||||
278
test/racc/assets/twowaysql.y
Normal file
278
test/racc/assets/twowaysql.y
Normal 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
|
||||
5
test/racc/assets/unterm.y
Normal file
5
test/racc/assets/unterm.y
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# unterminated action
|
||||
|
||||
class A
|
||||
rule
|
||||
a: A {
|
||||
12
test/racc/assets/useless.y
Normal file
12
test/racc/assets/useless.y
Normal 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
46
test/racc/assets/yyerr.y
Normal 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
36
test/racc/bench.y
Normal 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
104
test/racc/helper.rb
Normal 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
8
test/racc/infini.y
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
class I
|
||||
|
||||
rule
|
||||
|
||||
list: list X
|
||||
|
||||
end
|
||||
7
test/racc/regress/README.txt
Normal file
7
test/racc/regress/README.txt
Normal 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
796
test/racc/regress/cadenza
Normal 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
3425
test/racc/regress/cast
Normal file
File diff suppressed because it is too large
Load diff
2318
test/racc/regress/csspool
Normal file
2318
test/racc/regress/csspool
Normal file
File diff suppressed because it is too large
Load diff
1794
test/racc/regress/edtf
Normal file
1794
test/racc/regress/edtf
Normal file
File diff suppressed because it is too large
Load diff
1392
test/racc/regress/huia
Normal file
1392
test/racc/regress/huia
Normal file
File diff suppressed because it is too large
Load diff
222
test/racc/regress/journey
Normal file
222
test/racc/regress/journey
Normal 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
885
test/racc/regress/liquor
Normal 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
833
test/racc/regress/machete
Normal 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
1463
test/racc/regress/mediacloth
Normal file
File diff suppressed because it is too large
Load diff
1368
test/racc/regress/mof
Normal file
1368
test/racc/regress/mof
Normal file
File diff suppressed because it is too large
Load diff
634
test/racc/regress/namae
Normal file
634
test/racc/regress/namae
Normal 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
2058
test/racc/regress/nasl
Normal file
File diff suppressed because it is too large
Load diff
836
test/racc/regress/nokogiri-css
Normal file
836
test/racc/regress/nokogiri-css
Normal 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
6429
test/racc/regress/opal
Normal file
File diff suppressed because it is too large
Load diff
336
test/racc/regress/php_serialization
Normal file
336
test/racc/regress/php_serialization
Normal 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
3297
test/racc/regress/riml
Normal file
File diff suppressed because it is too large
Load diff
6351
test/racc/regress/ruby18
Normal file
6351
test/racc/regress/ruby18
Normal file
File diff suppressed because it is too large
Load diff
7456
test/racc/regress/ruby22
Normal file
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
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
556
test/racc/regress/twowaysql
Normal 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
7
test/racc/scandata/brace
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{ {
|
||||
} { } {
|
||||
{ { { } } }
|
||||
{ { { {} } } }
|
||||
{} {} {}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue