🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

ruby_parser

Package Overview
Dependencies
Maintainers
1
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ruby_parser - rubygems Package Compare versions

Comparing version
3.3.0
to
3.4.0
lib/.document

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

+263
#--
# This file is automatically generated. Do not modify it.
# Generated by: oedipus_lex version 2.1.0.
# Source: lib/ruby_lexer.rex
#++
# encoding: UTF-8
# TODO: this needs to be put on the first line
#
# new_ruby_parser.rex
# lexical scanner definition for ruby
class RubyLexer
require 'strscan'
IDENT = /^#{IDENT_CHAR}+/o
ESC = /\\((?>[0-7]{1,3}|x[0-9a-fA-F]{1,2}|M-[^\\]|(C-|c)[^\\]|u[0-9a-fA-F]+|u\{[0-9a-fA-F]+\}|[^0-7xMCc]))/
SIMPLE_STRING = /(#{ESC}|\#(#{ESC}|[^\{\#\@\$\"\\])|[^\"\\\#])*/o
SSTRING = /(\\.|[^\'])*/
INT_DEC = /[+]?(?:(?:[1-9][\d_]*|0)(?!\.\d)\b|0d[0-9_]+)/i
INT_HEX = /[+]?0x[a-f0-9_]+/i
INT_BIN = /[+]?0b[01_]+/i
INT_OCT = /[+]?0o?[0-7_]+|0o/i
FLOAT = /[+]?\d[\d_]*\.[\d_]+(e[+-]?[\d_]+)?\b|[+]?[\d_]+e[+-]?[\d_]+\b/i
INT_DEC2 = /[+]?\d[0-9_]*(?![e])/i
NUM_BAD = /[+]?0[xbd]\b/i
INT_OCT_BAD = /[+]?0o?[0-7_]*[89]/i
FLOAT_BAD = /[+]?\d[\d_]*_(e|\.)/i
class ScanError < StandardError ; end
attr_accessor :lineno
attr_accessor :filename
attr_accessor :ss
attr_accessor :state
alias :match :ss
def matches
m = (1..9).map { |i| ss[i] }
m.pop until m[-1] or m.empty?
m
end
def action
yield
end
def scanner_class
StringScanner
end unless instance_methods(false).map(&:to_s).include?("scanner_class")
def parse str
self.ss = scanner_class.new str
self.lineno = 1
self.state ||= nil
do_parse
end
def parse_file path
self.filename = path
open path do |f|
parse f.read
end
end
def next_token
return process_string if lex_strterm
self.command_state = self.command_start
self.command_start = false
self.space_seen = false
self.last_state = lex_state
token = nil
until ss.eos? or token do
token =
case state
when nil then
case
when text = ss.scan(/[\ \t\r\f\v]/) then
action { self.space_seen = true; next }
when text = ss.scan(/\n|\#/) then
process_newline_or_comment text
when text = ss.scan(/[\]\)\}]/) then
process_bracing text
when text = ss.scan(/\!/) then
process_bang text
when text = ss.scan(/\.\.\.?|,|![=~]?/) then
action { result :expr_beg, TOKENS[text], text }
when text = ss.scan(/\.\d/) then
action { rb_compile_error "no .<digit> floating literal anymore put 0 before dot" }
when text = ss.scan(/\./) then
action { result :expr_dot, :tDOT, "." }
when text = ss.scan(/\(/) then
process_paren text
when text = ss.scan(/\=\=\=|\=\=|\=~|\=>|\=(?!begin\b)/) then
action { result arg_state, TOKENS[text], text }
when bol? && (text = ss.scan(/\=begin(?=\s)/)) then
process_begin text
when text = ss.scan(/\=(?=begin\b)/) then
action { result arg_state, TOKENS[text], text }
when text = ss.scan(/\"(#{SIMPLE_STRING})\"/o) then
action { result :expr_end, :tSTRING, text[1..-2].gsub(ESC) { unescape $1 } }
when text = ss.scan(/\"/) then
action { string STR_DQUOTE; result nil, :tSTRING_BEG, text }
when text = ss.scan(/\@\@?\d/) then
action { rb_compile_error "`#{text}` is not allowed as a variable name" }
when text = ss.scan(/\@\@?#{IDENT_CHAR}+/o) then
process_ivar text
when not_end? && (text = ss.scan(/:([a-zA-Z_]#{IDENT_CHAR}*(?:[?]|[!](?!=)|=(?==>)|=(?![=>]))?)/o)) then
process_symbol text
when not_end? && (text = ss.scan(/\:\"(#{SIMPLE_STRING})\"/o)) then
process_symbol text
when not_end? && (text = ss.scan(/\:\'(#{SSTRING})\'/o)) then
process_symbol text
when text = ss.scan(/\:\:/) then
process_colon2 text
when text = ss.scan(/\:/) then
process_colon1 text
when text = ss.scan(/->/) then
action { result :expr_endfn, :tLAMBDA, nil }
when text = ss.scan(/[+-]/) then
process_plus_minus text
when text = ss.scan(/#{NUM_BAD}/o) then
action { rb_compile_error "Invalid numeric format" }
when text = ss.scan(/#{INT_DEC}/o) then
action { int_with_base 10 }
when text = ss.scan(/#{INT_HEX}/o) then
action { int_with_base 16 }
when text = ss.scan(/#{INT_BIN}/o) then
action { int_with_base 2 }
when text = ss.scan(/#{INT_OCT_BAD}/o) then
action { rb_compile_error "Illegal octal digit." }
when text = ss.scan(/#{INT_OCT}/o) then
action { int_with_base 8 }
when text = ss.scan(/#{FLOAT_BAD}/o) then
action { rb_compile_error "Trailing '_' in number." }
when text = ss.scan(/#{FLOAT}/o) then
process_float text
when text = ss.scan(/#{INT_DEC2}/o) then
action { int_with_base 10 }
when text = ss.scan(/[0-9]/) then
action { rb_compile_error "Bad number format" }
when text = ss.scan(/\[/) then
process_square_bracket text
when text = ss.scan(/\'#{SSTRING}\'/o) then
action { result :expr_end, :tSTRING, matched[1..-2].gsub(/\\\\/, "\\").gsub(/\\'/, "'") } # " stupid emacs
when text = ss.scan(/\|\|\=/) then
action { result :expr_beg, :tOP_ASGN, "||" }
when text = ss.scan(/\|\|/) then
action { result :expr_beg, :tOROP, "||" }
when text = ss.scan(/\|\=/) then
action { result :expr_beg, :tOP_ASGN, "|" }
when text = ss.scan(/\|/) then
action { result :arg_state, :tPIPE, "|" }
when text = ss.scan(/\{/) then
process_curly_brace text
when text = ss.scan(/\*\*=/) then
action { result :expr_beg, :tOP_ASGN, "**" }
when text = ss.scan(/\*\*/) then
action { result(:arg_state, space_vs_beginning(:tDSTAR, :tDSTAR, :tPOW), "**") }
when text = ss.scan(/\*\=/) then
action { result(:expr_beg, :tOP_ASGN, "*") }
when text = ss.scan(/\*/) then
action { result(:arg_state, space_vs_beginning(:tSTAR, :tSTAR, :tSTAR2), "*") }
when text = ss.scan(/\<\=\>/) then
action { result :arg_state, :tCMP, "<=>" }
when text = ss.scan(/\<\=/) then
action { result :arg_state, :tLEQ, "<=" }
when text = ss.scan(/\<\<\=/) then
action { result :arg_state, :tOP_ASGN, "<<" }
when text = ss.scan(/\<\</) then
process_lchevron text
when text = ss.scan(/\</) then
action { result :arg_state, :tLT, "<" }
when text = ss.scan(/\>\=/) then
action { result :arg_state, :tGEQ, ">=" }
when text = ss.scan(/\>\>=/) then
action { result :arg_state, :tOP_ASGN, ">>" }
when text = ss.scan(/\>\>/) then
action { result :arg_state, :tRSHFT, ">>" }
when text = ss.scan(/\>/) then
action { result :arg_state, :tGT, ">" }
when text = ss.scan(/\`/) then
process_backtick text
when text = ss.scan(/\?/) then
process_questionmark text
when text = ss.scan(/\&\&\=/) then
action { result(:expr_beg, :tOP_ASGN, "&&") }
when text = ss.scan(/\&\&/) then
action { result(:expr_beg, :tANDOP, "&&") }
when text = ss.scan(/\&\=/) then
action { result(:expr_beg, :tOP_ASGN, "&" ) }
when text = ss.scan(/\&/) then
process_amper text
when text = ss.scan(/\//) then
process_slash text
when text = ss.scan(/\^=/) then
action { result(:expr_beg, :tOP_ASGN, "^") }
when text = ss.scan(/\^/) then
action { result(:arg_state, :tCARET, "^") }
when text = ss.scan(/\;/) then
action { self.command_start = true; result(:expr_beg, :tSEMI, ";") }
when in_arg_state? && (text = ss.scan(/\~@/)) then
action { result(:arg_state, :tTILDE, "~") }
when text = ss.scan(/\~/) then
action { result(:arg_state, :tTILDE, "~") }
when text = ss.scan(/\\\r?\n/) then
action { self.lineno += 1; self.space_seen = true; next }
when text = ss.scan(/\\/) then
action { rb_compile_error "bare backslash only allowed before newline" }
when text = ss.scan(/\%/) then
process_percent text
when text = ss.scan(/\$_\w+/) then
process_gvar text
when text = ss.scan(/\$_/) then
process_gvar text
when text = ss.scan(/\$[~*$?!@\/\\;,.=:<>\"]|\$-\w?/) then
process_gvar text
when in_fname? && (text = ss.scan(/\$([\&\`\'\+])/)) then
process_gvar text
when text = ss.scan(/\$([\&\`\'\+])/) then
process_backref text
when in_fname? && (text = ss.scan(/\$([1-9]\d*)/)) then
process_gvar text
when text = ss.scan(/\$([1-9]\d*)/) then
process_nthref text
when text = ss.scan(/\$0/) then
process_gvar text
when text = ss.scan(/\$\W|\$\z/) then
process_gvar_oddity text
when text = ss.scan(/\$\w+/) then
process_gvar text
when text = ss.scan(/\_/) then
process_underscore text
when text = ss.scan(/#{IDENT}/o) then
process_token text
when text = ss.scan(/\004|\032|\000|\Z/) then
action { [RubyLexer::EOF, RubyLexer::EOF] }
when text = ss.scan(/./) then
action { rb_compile_error "Invalid char #{text.inspect} in expression" }
else
text = ss.string[ss.pos .. -1]
raise ScanError, "can not match (#{state.inspect}): '#{text}'"
end
else
raise ScanError, "undefined state: '#{state}'"
end # token = case state
next unless token # allow functions to trigger redo w/ nil
end # while
raise "bad lexical result: #{token.inspect}" unless
token.nil? || (Array === token && token.size >= 2)
# auto-switch state
self.state = token.last if token && token.first == :state
token
end # def _next_token
end # class
+50
-0

@@ -0,1 +1,51 @@

=== 3.4.0 / 2014-02-04
* 1 major enhancement:
* Replaced hand-written/optimized f'd-up lexer with an oedipus_lex
generated lexer. This makes it roughly 40-50% faster.
* 30 minor enhancements:
* 2.0: Added support for a.b c() do d end.e do |f| g end
* 2.0: Added support for a.b c() do d end.e f do |g| h end
* Added -s flag to ruby_parse_extract_error to output timings.
* Added RubyLexer #command_state and #last_state to deal with oedipus_lex differences.
* Added String#lineno and #lineno= because I'm a bad bad person.
* Added a bunch of RubyLexer scanning methods: beginning_of_line?, check, scan, etc.
* Added a bunch of process_* methods extracted from old yylex. process_amper, etc.
* Added lib/.document to save my laptop's battery from pain and suffering
* Adjust lineno when we lex a bunch of blank lines.
* Attach lineno to tIDENTIFIER values (strings, ugh)
* Cleaned up and re-ordered node_assign to be faster (ordered by actual occurrance).
* Extend RubyParserStuff#gettable to set the lineno if it comes in with the id.
* Extended RubyParserStuff#new_case to take line number.
* Finally dropped RPStringScanner's BS #current_line.
* Finally dropped RPStringScanner's BS line number calculation (lineno).
* Implemented Sexp#add_all since we now have a test case for it.
* Removed :call case of node_assign. I don't think it is possible.
* Removed RubyLexer #extra_lines_added. No longer used. Complex heredoc lineno's possible screwed up.
* Removed RubyLexer#parse_number. Handled by oedipus_lex.
* Removed RubyLexer#yacc_value now that next_token returns pairs.
* Removed RubyLexer's @src. Now taken care of by oedipus_lex.
* Removed RubyParser#advance. RubyParser#next_token takes care of everything now.
* Removed RubyParserExtras#arg_add. (presidentbeef! YAY!)
* Removed lib/gauntlet_rubyparser.rb. I just don't use it anymore. Too slow.
* RubyLexer#is_label_possible? doesn't need an arg
* RubyLexer#process_token is now a normal oedipal lexer method.
* RubyParser#next_token now expects RubyLexer#next_token to return a pair (type, val).
* TRYING a new scheme to figure out encodings... but I'm about to throw in the towel. I hate this stuff so much.
* Turned off oedipus_lex's automatic line counting. (pushing to oedipus_lex soon).
* Updated to oedipus_lex 2.1+.
* 7 bug fixes:
* 1.8: Properly parse `a (:b, :c, :d => :e)`. (presidentbeef)
* Fixed lexing symbol!= vs symbol!. Please use your spacebar. Think of the children.
* Fixed line for dstr spanning multiple lines via backslash. (presidentbeef)
* Fixed line numbers for odd cases with trailing whitespace. (presidentbeef)
* Fixed line numbers on ambiguous calls w/ gvar/ivar args. (presidentbeef)
* Max out unicode hex values to 2-4 or 2-6 chars or pack will overflow and puke.
* Removed ESC_RE from RubyLexer. Must have slipped through.
=== 3.3.0 / 2014-01-14

@@ -2,0 +52,0 @@

+501
-663

@@ -14,7 +14,2 @@ # encoding: UTF-8

IDENT = /^#{IDENT_CHAR}+/o
ESC = /\\((?>[0-7]{1,3}|x[0-9a-fA-F]{1,2}|M-[^\\]|(C-|c)[^\\]|u[0-9a-fA-F]+|u\{[0-9a-fA-F]+\}|[^0-7xMCc]))/u
SIMPLE_STRING = /(#{ESC}|#(#{ESC}|[^\{\#\@\$\"\\])|[^\"\\\#])*/o
SIMPLE_SSTRING = /(\\.|[^\'])*/
EOF = :eof_haha!

@@ -79,2 +74,4 @@

attr_accessor :command_start
attr_accessor :command_state
attr_accessor :last_state
attr_accessor :cond

@@ -96,7 +93,3 @@

# Stream of data that yylex examines.
attr_reader :src
alias :ss :src
# Last token read via yylex.
# Last token read via next_token.
attr_accessor :token

@@ -110,7 +103,2 @@

# Value of last token which had a value associated with it.
attr_accessor :yacc_value
attr_writer :lineno # reader is lazy initalizer
attr_writer :comments

@@ -124,16 +112,2 @@

##
# How the parser advances to the next token.
#
# @return true if not at end of file (EOF).
def advance
r = yylex
self.token = r
raise "yylex returned nil, near #{ss.rest[0,10].inspect}" unless r
return RubyLexer::EOF != r
end
def arg_ambiguous

@@ -150,2 +124,3 @@ self.warning("Ambiguous first argument. make sure.")

end
alias :bol? :beginning_of_line? # to make .rex file more readable

@@ -183,5 +158,5 @@ def check re

if beginning_of_line? && scan(eos_re) then
self.lineno += 1
ss.unread_many last_line # TODO: figure out how to remove this
self.yacc_value = eos
return :tSTRING_END
return :tSTRING_END, eos
end

@@ -195,7 +170,5 @@

ss.pos -= 1 # FIX omg stupid
self.yacc_value = matched
return :tSTRING_DVAR
return :tSTRING_DVAR, matched
when scan(/#[{]/) then
self.yacc_value = matched
return :tSTRING_DBEG
return :tSTRING_DBEG, matched
when scan(/#/) then

@@ -212,5 +185,5 @@ string_buffer << '#'

if c != "\n" then
self.yacc_value = string_buffer.join.delete("\r")
return :tSTRING_CONTENT
return :tSTRING_CONTENT, string_buffer.join.delete("\r")
else
self.lineno += 1
string_buffer << scan(/\n/)

@@ -230,4 +203,3 @@ end

self.yacc_value = string_buffer.join.delete("\r")
return :tSTRING_CONTENT
return :tSTRING_CONTENT, string_buffer.join.delete("\r")
end

@@ -268,3 +240,2 @@

line = matched
ss.extra_lines_added += 1 # FIX: ugh
else

@@ -277,10 +248,12 @@ line = nil

if term == '`' then
self.yacc_value = "`"
return :tXSTRING_BEG
result nil, :tXSTRING_BEG, "`"
else
self.yacc_value = "\""
return :tSTRING_BEG
result nil, :tSTRING_BEG, "\""
end
end
def in_fname?
in_lex_state? :expr_fname
end
def in_arg_state? # TODO: rename is_after_operator?

@@ -296,5 +269,3 @@ in_lex_state? :expr_fname, :expr_dot

rb_compile_error "Invalid numeric format" if matched =~ /__/
self.yacc_value = matched.to_i(base)
return :tINTEGER
return result(:expr_end, :tINTEGER, matched.to_i(base))
end

@@ -314,3 +285,3 @@

def is_label_possible? command_state
def is_label_possible?
(in_lex_state?(:expr_beg, :expr_endfn) && !command_state) || is_arg?

@@ -323,6 +294,2 @@ end

def lineno
@lineno ||= ss.lineno
end
def matched

@@ -332,158 +299,388 @@ ss.matched

##
# Parse a number from the input stream.
#
# @param c The first character of the number.
# @return A int constant wich represents a token.
def not_end?
not is_end?
end
def parse_number
self.lex_state = :expr_end
def process_amper text
token = if is_arg? && space_seen && !check(/\s/) then
warning("`&' interpreted as argument prefix")
:tAMPER
elsif in_lex_state? :expr_beg, :expr_mid then
:tAMPER
else
:tAMPER2
end
return result(:arg_state, token, "&")
end
def process_backref text
token = ss[1].to_sym
# TODO: can't do lineno hack w/ symbol
result :expr_end, :tBACK_REF, token
end
def process_backtick text
case lex_state
when :expr_fname then
result :expr_end, :tBACK_REF2, "`"
when :expr_dot then
result((command_state ? :expr_cmdarg : :expr_arg), :tBACK_REF2, "`")
else
string STR_XQUOTE
result nil, :tXSTRING_BEG, "`"
end
end
def process_bang text
if in_arg_state? then
return result(:expr_arg, :tUBANG, "!@") if scan(/@/)
end
text = scan(/[=~]/) ? "!#{matched}" : "!"
return result(arg_state, TOKENS[text], text)
end
def process_begin text
@comments << matched
unless scan(/.*?\n=end( |\t|\f)*[^\n]*(\n|\z)/m) then
@comments.clear
rb_compile_error("embedded document meets end of file")
end
@comments << matched
nil # TODO
end
def process_bracing text
cond.lexpop
cmdarg.lexpop
case matched
when "}" then
self.brace_nest -= 1
self.lex_state = :expr_endarg
return :tRCURLY, matched
when "]" then
self.paren_nest -= 1
self.lex_state = :expr_endarg
return :tRBRACK, matched
when ")" then
self.paren_nest -= 1
self.lex_state = :expr_endfn
return :tRPAREN, matched
else
raise "Unknown bracing: #{matched.inspect}"
end
end
def process_colon1 text
# ?: / then / when
if is_end? || check(/\s/) then
return result :expr_beg, :tCOLON, text
end
case
when scan(/[+-]?0[xXbBdD]\b/) then
rb_compile_error "Invalid numeric format"
when scan(/[+-]?(?:(?:[1-9][\d_]*|0)(?!\.\d)\b|0[Dd][0-9_]+)/) then
int_with_base(10)
when scan(/[+-]?0x[a-f0-9_]+/i) then
int_with_base(16)
when scan(/[+-]?0[Bb][01_]+/) then
int_with_base(2)
when scan(/[+-]?0[Oo]?[0-7_]*[89]/) then
rb_compile_error "Illegal octal digit."
when scan(/[+-]?0[Oo]?[0-7_]+|0[Oo]/) then
int_with_base(8)
when scan(/[+-]?[\d_]+_(e|\.)/) then
rb_compile_error "Trailing '_' in number."
when scan(/[+-]?[\d_]+\.[\d_]+(e[+-]?[\d_]+)?\b|[+-]?[\d_]+e[+-]?[\d_]+\b/i) then
number = matched
if number =~ /__/ then
rb_compile_error "Invalid numeric format"
end
self.yacc_value = number.to_f
:tFLOAT
when scan(/[+-]?[0-9_]+(?![e])/) then
int_with_base(10)
when scan(/\'/) then
string STR_SSYM
when scan(/\"/) then
string STR_DSYM
end
result :expr_fname, :tSYMBEG, text
end
def process_colon2 text
if is_beg? || in_lex_state?(:expr_class) || is_space_arg? then
result :expr_beg, :tCOLON3, text
else
rb_compile_error "Bad number format"
result :expr_dot, :tCOLON2, text
end
end
def parse_quote # TODO: remove / rewrite
beg, nnd, short_hand, c = nil, nil, false, nil
def process_curly_brace text
self.brace_nest += 1
if lpar_beg && lpar_beg == paren_nest then
self.lpar_beg = nil
self.paren_nest -= 1
if scan(/[a-z0-9]{1,2}/i) then # Long-hand (e.g. %Q{}).
rb_compile_error "unknown type of %string" if ss.matched_size == 2
c, beg, short_hand = matched, ss.getch, false
else # Short-hand (e.g. %{, %., %!, etc)
c, beg, short_hand = 'Q', ss.getch, true
return expr_result(:tLAMBEG, "{")
end
if end_of_stream? or c == RubyLexer::EOF or beg == RubyLexer::EOF then
rb_compile_error "unterminated quoted string meets end of file"
token = if is_arg? || in_lex_state?(:expr_end, :expr_endfn) then
:tLCURLY # block (primary)
elsif in_lex_state?(:expr_endarg) then
:tLBRACE_ARG # block (expr)
else
:tLBRACE # hash
end
self.command_start = true unless token == :tLBRACE
return expr_result(token, "{")
end
def process_float text
rb_compile_error "Invalid numeric format" if text =~ /__/
return result(:expr_end, :tFLOAT, text.to_f)
end
def process_gvar text
text.lineno = self.lineno
result(:expr_end, :tGVAR, text)
end
def process_gvar_oddity text
result :expr_end, "$", "$" # TODO: wtf is this?
end
def process_ivar text
tok_id = text =~ /^@@/ ? :tCVAR : :tIVAR
text.lineno = self.lineno
return result(:expr_end, tok_id, text)
end
def process_lchevron text
if (!in_lex_state?(:expr_dot, :expr_class) &&
!is_end? &&
(!is_arg? || space_seen)) then
tok = self.heredoc_identifier
return tok if tok
end
# Figure nnd-char. "\0" is special to indicate beg=nnd and that no nesting?
nnd = { "(" => ")", "[" => "]", "{" => "}", "<" => ">" }[beg]
nnd, beg = beg, "\0" if nnd.nil?
return result(:arg_state, :tLSHFT, "\<\<")
end
token_type, text = nil, "%#{c}#{beg}"
token_type, string_type = case c
when 'Q' then
ch = short_hand ? nnd : c + beg
text = "%#{ch}"
[:tSTRING_BEG, STR_DQUOTE]
when 'q' then
[:tSTRING_BEG, STR_SQUOTE]
when 'W' then
scan(/\s*/)
[:tWORDS_BEG, STR_DQUOTE | STR_FUNC_QWORDS]
when 'w' then
scan(/\s*/)
[:tQWORDS_BEG, STR_SQUOTE | STR_FUNC_QWORDS]
when 'x' then
[:tXSTRING_BEG, STR_XQUOTE]
when 'r' then
[:tREGEXP_BEG, STR_REGEXP]
when 's' then
self.lex_state = :expr_fname
[:tSYMBEG, STR_SSYM]
when 'I' then
src.scan(/\s*/)
[:tSYMBOLS_BEG, STR_DQUOTE | STR_FUNC_QWORDS]
when 'i' then
src.scan(/\s*/)
[:tQSYMBOLS_BEG, STR_SQUOTE | STR_FUNC_QWORDS]
end
def process_newline_or_comment text
c = matched
hit = false
rb_compile_error "Bad %string type. Expected [QqWwIixrs], found '#{c}'." if
token_type.nil?
if c == '#' then
ss.pos -= 1
raise "huh" unless string_type
while scan(/\s*\#.*(\n+|\z)/) do
hit = true
self.lineno += matched.lines.to_a.size
@comments << matched.gsub(/^ +#/, '#').gsub(/^ +$/, '')
end
string string_type, nnd, beg
return nil if end_of_stream?
end
self.yacc_value = text
return token_type
self.lineno += 1 unless hit
# Replace a string of newlines with a single one
self.lineno += matched.lines.to_a.size if scan(/\n+/)
return if in_lex_state?(:expr_beg, :expr_value, :expr_class,
:expr_fname, :expr_dot)
if scan(/([\ \t\r\f\v]*)\./) then
self.space_seen = true unless ss[1].empty?
ss.pos -= 1
return unless check(/\.\./)
end
self.command_start = true
return result(:expr_beg, :tNL, nil)
end
def parse_string quote # TODO: rewrite / remove
_, string_type, term, open = quote
def process_nthref text
# TODO: can't do lineno hack w/ number
result :expr_end, :tNTH_REF, ss[1].to_i
end
space = false # FIX: remove these
func = string_type
paren = open
term_re = @@regexp_cache[term]
def process_paren text
token = if ruby18 then
process_paren18
else
process_paren19
end
qwords = (func & STR_FUNC_QWORDS) != 0
regexp = (func & STR_FUNC_REGEXP) != 0
expand = (func & STR_FUNC_EXPAND) != 0
self.paren_nest += 1
unless func then # nil'ed from qwords below. *sigh*
self.lineno = nil
return :tSTRING_END
return expr_result(token, "(")
end
def process_paren18
self.command_start = true
token = :tLPAREN2
if in_lex_state? :expr_beg, :expr_mid then
token = :tLPAREN
elsif space_seen then
if in_lex_state? :expr_cmdarg then
token = :tLPAREN_ARG
elsif in_lex_state? :expr_arg then
warning "don't put space before argument parentheses"
end
else
# not a ternary -- do nothing?
end
space = true if qwords and scan(/\s+/)
token
end
if self.string_nest == 0 && scan(/#{term_re}/) then
if qwords then
quote[1] = nil
return :tSPACE
elsif regexp then
self.lineno = nil
self.yacc_value = self.regx_options
return :tREGEXP_END
def process_paren19
if is_beg? then
:tLPAREN
elsif is_space_arg? then
:tLPAREN_ARG
else
:tLPAREN2 # plain '(' in parse.y
end
end
def process_percent text
return parse_quote if is_beg?
return result(:expr_beg, :tOP_ASGN, "%") if scan(/\=/)
return parse_quote if is_arg? && space_seen && ! check(/\s/)
return result(:arg_state, :tPERCENT, "%")
end
def process_plus_minus text
sign = matched
utype, type = if sign == "+" then
[:tUPLUS, :tPLUS]
else
[:tUMINUS, :tMINUS]
end
if in_arg_state? then
if scan(/@/) then
return result(:expr_arg, utype, "#{sign}@")
else
self.lineno = nil
self.yacc_value = term
return :tSTRING_END
return result(:expr_arg, type, sign)
end
end
return :tSPACE if space
return result(:expr_beg, :tOP_ASGN, sign) if scan(/\=/)
self.string_buffer = []
if (is_beg? || (is_arg? && space_seen && !check(/\s/))) then
arg_ambiguous if is_arg?
if expand
case
when scan(/#(?=[$@])/) then
return :tSTRING_DVAR
when scan(/#[{]/) then
return :tSTRING_DBEG
when scan(/#/) then
string_buffer << '#'
if check(/\d/) then
return nil if utype == :tUPLUS
return result(:expr_beg, :tUMINUS_NUM, sign)
end
return result(:expr_beg, utype, sign)
end
if tokadd_string(func, term, paren) == RubyLexer::EOF then
rb_compile_error "unterminated string meets end of file"
return result(:expr_beg, type, sign)
end
def process_questionmark text
if is_end? then
state = ruby18 ? :expr_beg : :expr_value # HACK?
return result(state, :tEH, "?")
end
self.yacc_value = string_buffer.join
if end_of_stream? then
rb_compile_error "incomplete character syntax: parsed #{text.inspect}"
end
return :tSTRING_CONTENT
if check(/\s|\v/) then
unless is_arg? then
c2 = { " " => 's',
"\n" => 'n',
"\t" => 't',
"\v" => 'v',
"\r" => 'r',
"\f" => 'f' }[matched]
if c2 then
warning("invalid character syntax; use ?\\" + c2)
end
end
# ternary
state = ruby18 ? :expr_beg : :expr_value # HACK?
return result(state, :tEH, "?")
elsif check(/\w(?=\w)/) then # ternary, also
return result(:expr_beg, :tEH, "?")
end
c = if scan(/\\/) then
self.read_escape
else
ss.getch
end
if version == 18 then
return result(:expr_end, :tINTEGER, c[0].ord & 0xff)
else
return result(:expr_end, :tSTRING, c)
end
end
def process_token command_state, last_state
token = self.token
def process_slash text
if is_beg? then
string STR_REGEXP
return result(nil, :tREGEXP_BEG, "/")
end
if scan(/\=/) then
return result(:expr_beg, :tOP_ASGN, "/")
end
if is_arg? && space_seen then
unless scan(/\s/) then
arg_ambiguous
string STR_REGEXP, "/"
return result(nil, :tREGEXP_BEG, "/")
end
end
return result(:arg_state, :tDIVIDE, "/")
end
def process_square_bracket text
self.paren_nest += 1
token = nil
if in_arg_state? then
case
when scan(/\]\=/) then
self.paren_nest -= 1 # HACK? I dunno, or bug in MRI
return result(:expr_arg, :tASET, "[]=")
when scan(/\]/) then
self.paren_nest -= 1 # HACK? I dunno, or bug in MRI
return result(:expr_arg, :tAREF, "[]")
else
rb_compile_error "unexpected '['"
end
elsif is_beg? then
token = :tLBRACK
elsif is_arg? && space_seen then
token = :tLBRACK
else
token = :tLBRACK2
end
return expr_result(token, "[")
end
def process_symbol text
symbol = match[1].gsub(ESC) { unescape $1 }
rb_compile_error "symbol cannot contain '\\0'" if
ruby18 && symbol =~ /\0/
return result(:expr_end, :tSYMBOL, symbol)
end
def process_token text
# TODO: make this always return [token, lineno]
token = self.token = text
token << matched if scan(/[\!\?](?!=)/)

@@ -506,4 +703,4 @@

if !ruby18 and is_label_possible?(command_state) and scan(/:(?!:)/) then
return result(:expr_beg, :tLABEL, [token, ss.lineno]) # HACK: array? TODO: self.lineno
if !ruby18 and is_label_possible? and scan(/:(?!:)/) then
return result(:expr_beg, :tLABEL, [token, self.lineno])
end

@@ -538,2 +735,4 @@

token.lineno = self.lineno # yes, on a string. I know... I know...
return result(state, tok_id, token)

@@ -544,4 +743,5 @@ end

state = keyword.state
value = [token, ss.lineno] # TODO: use self.lineno ?
value = [token, self.lineno]
self.command_start = true if state == :expr_beg and lex_state != :expr_fname

@@ -576,2 +776,12 @@

def process_underscore text
ss.unscan # put back "_"
if beginning_of_line? && scan(/\__END__(\r?\n|\Z)/) then
return [RubyLexer::EOF, RubyLexer::EOF]
elsif scan(/\_\w*/) then
return process_token matched
end
end
def rb_compile_error msg

@@ -630,3 +840,3 @@ msg += ". near line #{self.lineno}: #{ss.rest[/^.*/].inspect}"

matched
when scan(/u([0-9a-fA-F]+|\{[0-9a-fA-F]+\})/) then
when scan(/u([0-9a-fA-F]{2,4}|\{[0-9a-fA-F]{2,6}\})/) then
[ss[1].delete("{}").to_i(16)].pack("U")

@@ -667,8 +877,5 @@ when scan(/[McCx0-9]/) || end_of_stream? then

self.token = nil
self.yacc_value = nil
self.cmdarg = RubyParserStuff::StackState.new(:cmdarg)
self.cond = RubyParserStuff::StackState.new(:cond)
@src = nil
end

@@ -679,4 +886,3 @@

self.lex_state = lex_state if lex_state
self.yacc_value = text
token
[token, text]
end

@@ -696,2 +902,6 @@

def scanner_class # TODO: design this out of oedipus_lex. or something.
RPStringScanner
end
def space_vs_beginning space_type, beg_type, fallback

@@ -713,6 +923,7 @@ if is_space_arg? check(/./m) then

def src= src
raise "bad src: #{src.inspect}" unless String === src
@src = RPStringScanner.new(src)
end
# TODO: consider
# def src= src
# raise "bad src: #{src.inspect}" unless String === src
# @src = RPStringScanner.new(src)
# end

@@ -848,3 +1059,3 @@ def tokadd_escape term # TODO: rewrite / remove

rb_compile_error("Invalid escape character syntax")
when /u([0-9a-fA-F]+|\{[0-9a-fA-F]+\})/ then
when /u([0-9a-fA-F]{2,4}|\{[0-9a-fA-F]{2,6}\})/ then
[$1.delete("{}").to_i(16)].pack("U")

@@ -862,517 +1073,144 @@ else

##
# Returns the next token. Also sets yy_val is needed.
#
# @return Description of the Returned Value
def yylex # 461 lines
c = ''
self.space_seen = false
command_state = false
ss = self.src
self.token = nil
self.yacc_value = nil
return yylex_string if lex_strterm
command_state = self.command_start
self.command_start = false
last_state = lex_state
loop do # START OF CASE
if scan(/[\ \t\r\f\v]/) then # \s - \n + \v
self.space_seen = true
next
elsif check(/[^a-zA-Z]/) then
if scan(/\n|\#/) then
self.lineno = nil
c = matched
if c == '#' then
ss.pos -= 1
while scan(/\s*#.*(\n+|\z)/) do
# TODO: self.lineno += matched.lines.to_a.size
@comments << matched.gsub(/^ +#/, '#').gsub(/^ +$/, '')
end
return RubyLexer::EOF if end_of_stream?
end
# Replace a string of newlines with a single one
scan(/\n+/)
next if in_lex_state?(:expr_beg, :expr_value, :expr_class,
:expr_fname, :expr_dot)
if scan(/([\ \t\r\f\v]*)\./) then
self.space_seen = true unless ss[1].empty?
ss.pos -= 1
next unless check(/\.\./)
end
self.command_start = true
return result(:expr_beg, :tNL, nil)
elsif scan(/[\]\)\}]/) then
if matched == "}" then
self.brace_nest -= 1
else
self.paren_nest -= 1
end
cond.lexpop
cmdarg.lexpop
text = matched
state = text == ")" ? :expr_endfn : :expr_endarg
token = {
")" => :tRPAREN,
"]" => :tRBRACK,
"}" => :tRCURLY
}[text]
return result(state, token, text)
elsif scan(/\!/) then
if in_arg_state? then
return result(:expr_arg, :tUBANG, "!@") if scan(/@/)
end
text = scan(/[=~]/) ? "!#{matched}" : "!"
return result(arg_state, TOKENS[text], text)
elsif scan(/\.\.\.?|,|![=~]?/) then
return result(:expr_beg, TOKENS[matched], matched)
elsif check(/\./) then
if scan(/\.\d/) then
rb_compile_error "no .<digit> floating literal anymore put 0 before dot"
elsif scan(/\./) then
return result(:expr_dot, :tDOT, ".")
end
elsif scan(/\(/) then
token = if ruby18 then
yylex_paren18
else
yylex_paren19
end
self.paren_nest += 1
return expr_result(token, "(")
elsif check(/\=/) then
if scan(/\=\=\=|\=\=|\=~|\=>|\=(?!begin\b)/) then
tok = matched
return result(:arg_state, TOKENS[tok], tok)
elsif beginning_of_line? and scan(/\=begin(?=\s)/) then
@comments << matched
unless scan(/.*?\n=end( |\t|\f)*[^\n]*(\n|\z)/m) then
@comments.clear
rb_compile_error("embedded document meets end of file")
end
@comments << matched
next
elsif scan(/\=(?=begin\b)/) then # h[k]=begin ... end
tok = matched
return result(:arg_state, TOKENS[tok], tok)
else
raise "you shouldn't be able to get here"
end
elsif scan(/\"(#{SIMPLE_STRING})\"/o) then
string = matched[1..-2].gsub(ESC) { unescape $1 }
return result(:expr_end, :tSTRING, string)
elsif scan(/\"/) then # FALLBACK
string STR_DQUOTE, '"' # TODO: question this
return result(nil, :tSTRING_BEG, '"')
elsif scan(/\@\@?#{IDENT_CHAR}+/o) then
self.token = matched
rb_compile_error "`#{self.token}` is not allowed as a variable name" if
self.token =~ /\@\d/
tok_id = matched =~ /^@@/ ? :tCVAR : :tIVAR
return result(:expr_end, tok_id, self.token)
elsif scan(/\:\:/) then
if is_beg? || in_lex_state?(:expr_class) || is_space_arg? then
return result(:expr_beg, :tCOLON3, "::")
end
return result(:expr_dot, :tCOLON2, "::")
elsif ! is_end? && scan(/:([a-zA-Z_]#{IDENT_CHAR}*(?:[?!]|=(?==>)|=(?![=>]))?)/) then
# scanning shortcut to symbols
return result(:expr_end, :tSYMBOL, ss[1])
elsif ! is_end? && (scan(/\:\"(#{SIMPLE_STRING})\"/) ||
scan(/\:\'(#{SIMPLE_SSTRING})\'/)) then
symbol = ss[1].gsub(ESC) { unescape $1 }
rb_compile_error "symbol cannot contain '\\0'" if
ruby18 && symbol =~ /\0/
return result(:expr_end, :tSYMBOL, symbol)
elsif scan(/\:/) then
# ?: / then / when
if is_end? || check(/\s/) then
# TODO warn_balanced(":", "symbol literal");
return result(:expr_beg, :tCOLON, ":")
end
case
when scan(/\'/) then
string STR_SSYM, matched
when scan(/\"/) then
string STR_DSYM, matched
end
return result(:expr_fname, :tSYMBEG, ":")
elsif check(/[0-9]/) then
return parse_number
elsif scan(/\[/) then
self.paren_nest += 1
token = nil
if in_lex_state? :expr_fname, :expr_dot then
case
when scan(/\]\=/) then
self.paren_nest -= 1 # HACK? I dunno, or bug in MRI
return result(:expr_arg, :tASET, "[]=")
when scan(/\]/) then
self.paren_nest -= 1 # HACK? I dunno, or bug in MRI
return result(:expr_arg, :tAREF, "[]")
def process_string # TODO: rewrite / remove
token = if lex_strterm[0] == :heredoc then
self.heredoc lex_strterm
else
rb_compile_error "unexpected '['"
self.parse_string lex_strterm
end
elsif is_beg? then
token = :tLBRACK
elsif is_arg? && space_seen then
token = :tLBRACK
else
token = :tLBRACK2
end
return expr_result(token, "[")
elsif scan(/\'#{SIMPLE_SSTRING}\'/) then
text = matched[1..-2].gsub(/\\\\/, "\\").gsub(/\\'/, "'") # "
return result(:expr_end, :tSTRING, text)
elsif check(/\|/) then
if scan(/\|\|\=/) then
return result(:expr_beg, :tOP_ASGN, "||")
elsif scan(/\|\|/) then
return result(:expr_beg, :tOROP, "||")
elsif scan(/\|\=/) then
return result(:expr_beg, :tOP_ASGN, "|")
elsif scan(/\|/) then
return result(:arg_state, :tPIPE, "|")
end
elsif scan(/\{/) then
self.brace_nest += 1
if lpar_beg && lpar_beg == paren_nest then
self.lpar_beg = nil
self.paren_nest -= 1
token_type, _ = token
return expr_result(:tLAMBEG, "{")
end
if token_type == :tSTRING_END || token_type == :tREGEXP_END then
self.lex_strterm = nil
self.lex_state = :expr_end
end
token = if is_arg? || in_lex_state?(:expr_end, :expr_endfn) then
:tLCURLY # block (primary)
elsif in_lex_state?(:expr_endarg) then
:tLBRACE_ARG # block (expr)
else
:tLBRACE # hash
end
return token
end
self.command_start = true unless token == :tLBRACE
def parse_quote # TODO: remove / rewrite
beg, nnd, short_hand, c = nil, nil, false, nil
return expr_result(token, "{")
elsif scan(/->/) then
return result(:expr_endfn, :tLAMBDA, nil)
elsif scan(/[+-]/) then
sign = matched
utype, type = if sign == "+" then
[:tUPLUS, :tPLUS]
else
[:tUMINUS, :tMINUS]
end
if scan(/[a-z0-9]{1,2}/i) then # Long-hand (e.g. %Q{}).
rb_compile_error "unknown type of %string" if ss.matched_size == 2
c, beg, short_hand = matched, ss.getch, false
else # Short-hand (e.g. %{, %., %!, etc)
c, beg, short_hand = 'Q', ss.getch, true
end
if in_arg_state? then
if scan(/@/) then
return result(:expr_arg, utype, "#{sign}@")
else
return result(:expr_arg, type, sign)
end
end
if end_of_stream? or c == RubyLexer::EOF or beg == RubyLexer::EOF then
rb_compile_error "unterminated quoted string meets end of file"
end
return result(:expr_beg, :tOP_ASGN, sign) if scan(/\=/)
# Figure nnd-char. "\0" is special to indicate beg=nnd and that no nesting?
nnd = { "(" => ")", "[" => "]", "{" => "}", "<" => ">" }[beg]
nnd, beg = beg, "\0" if nnd.nil?
if (is_beg? || (is_arg? && space_seen && !check(/\s/))) then
arg_ambiguous if is_arg?
token_type, text = nil, "%#{c}#{beg}"
token_type, string_type = case c
when 'Q' then
ch = short_hand ? nnd : c + beg
text = "%#{ch}"
[:tSTRING_BEG, STR_DQUOTE]
when 'q' then
[:tSTRING_BEG, STR_SQUOTE]
when 'W' then
scan(/\s*/)
[:tWORDS_BEG, STR_DQUOTE | STR_FUNC_QWORDS]
when 'w' then
scan(/\s*/)
[:tQWORDS_BEG, STR_SQUOTE | STR_FUNC_QWORDS]
when 'x' then
[:tXSTRING_BEG, STR_XQUOTE]
when 'r' then
[:tREGEXP_BEG, STR_REGEXP]
when 's' then
self.lex_state = :expr_fname
[:tSYMBEG, STR_SSYM]
when 'I' then
scan(/\s*/)
[:tSYMBOLS_BEG, STR_DQUOTE | STR_FUNC_QWORDS]
when 'i' then
scan(/\s*/)
[:tQSYMBOLS_BEG, STR_SQUOTE | STR_FUNC_QWORDS]
end
if check(/\d/) then
return self.parse_number if utype == :tUPLUS
return result(:expr_beg, :tUMINUS_NUM, sign)
end
rb_compile_error "Bad %string type. Expected [QqWwIixrs], found '#{c}'." if
token_type.nil?
return result(:expr_beg, utype, sign)
end
raise "huh" unless string_type
return result(:expr_beg, type, sign)
elsif check(/\*/) then
if scan(/\*\*=/) then
return result(:expr_beg, :tOP_ASGN, "**")
elsif scan(/\*\*/) then
token = space_vs_beginning :tDSTAR, :tDSTAR, :tPOW
string string_type, nnd, beg
return result(:arg_state, token, "**")
elsif scan(/\*\=/) then
return result(:expr_beg, :tOP_ASGN, "*")
elsif scan(/\*/) then
token = space_vs_beginning :tSTAR, :tSTAR, :tSTAR2
return token_type, text
end
return result(:arg_state, token, "*")
end
elsif check(/\</) then
if scan(/\<\=\>/) then
return result(:arg_state, :tCMP, "<=>")
elsif scan(/\<\=/) then
return result(:arg_state, :tLEQ, "<=")
elsif scan(/\<\<\=/) then
return result(:arg_state, :tOP_ASGN, "<<")
elsif scan(/\<\</) then
if (!in_lex_state?(:expr_dot, :expr_class) &&
!is_end? &&
(!is_arg? || space_seen)) then
tok = self.heredoc_identifier
return tok if tok
end
def parse_string quote # TODO: rewrite / remove
_, string_type, term, open = quote
return result(:arg_state, :tLSHFT, "\<\<")
elsif scan(/\</) then
return result(:arg_state, :tLT, "<")
end
elsif check(/\>/) then
if scan(/\>\=/) then
return result(:arg_state, :tGEQ, ">=")
elsif scan(/\>\>=/) then
return result(:arg_state, :tOP_ASGN, ">>")
elsif scan(/\>\>/) then
return result(:arg_state, :tRSHFT, ">>")
elsif scan(/\>/) then
return result(:arg_state, :tGT, ">")
end
elsif scan(/\`/) then
case lex_state
when :expr_fname then
return result(:expr_end, :tBACK_REF2, "`")
when :expr_dot then
state = command_state ? :expr_cmdarg : :expr_arg
return result(state, :tBACK_REF2, "`")
else
string STR_XQUOTE, '`'
return result(nil, :tXSTRING_BEG, "`")
end
elsif scan(/\?/) then
if is_end? then
state = ruby18 ? :expr_beg : :expr_value # HACK?
return result(state, :tEH, "?")
end
space = false # FIX: remove these
func = string_type
paren = open
term_re = @@regexp_cache[term]
if end_of_stream? then
rb_compile_error "incomplete character syntax"
end
qwords = (func & STR_FUNC_QWORDS) != 0
regexp = (func & STR_FUNC_REGEXP) != 0
expand = (func & STR_FUNC_EXPAND) != 0
if check(/\s|\v/) then
unless is_arg? then
c2 = { " " => 's',
"\n" => 'n',
"\t" => 't',
"\v" => 'v',
"\r" => 'r',
"\f" => 'f' }[matched]
unless func then # nil'ed from qwords below. *sigh*
return :tSTRING_END, nil
end
if c2 then
warning("invalid character syntax; use ?\\" + c2)
end
end
space = true if qwords and scan(/\s+/)
# ternary
state = ruby18 ? :expr_beg : :expr_value # HACK?
return result(state, :tEH, "?")
elsif check(/\w(?=\w)/) then # ternary, also
return result(:expr_beg, :tEH, "?")
end
if self.string_nest == 0 && scan(/#{term_re}/) then
if qwords then
quote[1] = nil
return :tSPACE, nil
elsif regexp then
return :tREGEXP_END, self.regx_options
else
return :tSTRING_END, term
end
end
c = if scan(/\\/) then
self.read_escape
else
ss.getch
end
return :tSPACE, nil if space
if version == 18 then
return result(:expr_end, :tINTEGER, c[0].ord & 0xff)
else
return result(:expr_end, :tSTRING, c)
end
elsif check(/\&/) then
if scan(/\&\&\=/) then
return result(:expr_beg, :tOP_ASGN, "&&")
elsif scan(/\&\&/) then
return result(:expr_beg, :tANDOP, "&&")
elsif scan(/\&\=/) then
return result(:expr_beg, :tOP_ASGN, "&")
elsif scan(/&/) then
token = if is_arg? && space_seen && !check(/\s/) then
warning("`&' interpreted as argument prefix")
:tAMPER
elsif in_lex_state? :expr_beg, :expr_mid then
:tAMPER
else
:tAMPER2
end
self.string_buffer = []
return result(:arg_state, token, "&")
end
elsif scan(/\//) then
if is_beg? then
string STR_REGEXP, '/'
return result(nil, :tREGEXP_BEG, "/")
end
if scan(/\=/) then
return result(:expr_beg, :tOP_ASGN, "/")
end
if is_arg? && space_seen then
unless scan(/\s/) then
arg_ambiguous
string STR_REGEXP, '/'
return result(nil, :tREGEXP_BEG, "/")
end
end
return result(:arg_state, :tDIVIDE, "/")
elsif scan(/\^=/) then
return result(:expr_beg, :tOP_ASGN, "^")
elsif scan(/\^/) then
return result(:arg_state, :tCARET, "^")
elsif scan(/\;/) then
self.command_start = true
return result(:expr_beg, :tSEMI, ";")
elsif scan(/\~/) then
scan(/@/) if in_lex_state? :expr_fname, :expr_dot
return result(:arg_state, :tTILDE, "~")
elsif scan(/\\/) then
if scan(/\r?\n/) then
self.lineno = nil
self.space_seen = true
next
end
rb_compile_error "bare backslash only allowed before newline"
elsif scan(/\%/) then
return parse_quote if is_beg?
return result(:expr_beg, :tOP_ASGN, "%") if scan(/\=/)
return parse_quote if is_arg? && space_seen && ! check(/\s/)
return result(:arg_state, :tPERCENT, "%")
elsif check(/\$/) then
if scan(/(\$_)(\w+)/) then
self.token = matched
return result(:expr_end, :tGVAR, matched)
elsif scan(/\$_/) then
return result(:expr_end, :tGVAR, matched)
elsif scan(/\$[~*$?!@\/\\;,.=:<>\"]|\$-\w?/) then
return result(:expr_end, :tGVAR, matched)
elsif scan(/\$([\&\`\'\+])/) then
# Explicit reference to these vars as symbols...
if lex_state == :expr_fname then
return result(:expr_end, :tGVAR, matched)
else
return result(:expr_end, :tBACK_REF, ss[1].to_sym)
end
elsif scan(/\$([1-9]\d*)/) then
if lex_state == :expr_fname then
return result(:expr_end, :tGVAR, matched)
else
return result(:expr_end, :tNTH_REF, ss[1].to_i)
end
elsif scan(/\$0/) then
return result(:expr_end, :tGVAR, matched)
elsif scan(/\$\W|\$\z/) then # TODO: remove?
return result(:expr_end, "$", "$") # FIX: "$"??
elsif scan(/\$\w+/)
return result(:expr_end, :tGVAR, matched)
end
elsif check(/\_/) then
if beginning_of_line? && scan(/\__END__(\r?\n|\Z)/) then
self.lineno = nil
return RubyLexer::EOF
elsif scan(/\_\w*/) then
self.token = matched
return process_token command_state, last_state
end
end
end # END OF CASE
if scan(/\004|\032|\000/) || end_of_stream? then # ^D, ^Z, EOF
return RubyLexer::EOF
else # alpha check
rb_compile_error "Invalid char #{ss.rest[0].chr} in expression" unless
check IDENT
if expand
case
when scan(/#(?=[$@])/) then
return :tSTRING_DVAR, nil
when scan(/#[{]/) then
return :tSTRING_DBEG, nil
when scan(/#/) then
string_buffer << '#'
end
self.token = matched if self.scan IDENT
return process_token command_state, last_state
end
end
def yylex_paren18
self.command_start = true
token = :tLPAREN2
if in_lex_state? :expr_beg, :expr_mid then
token = :tLPAREN
elsif space_seen then
if in_lex_state? :expr_cmdarg then
token = :tLPAREN_ARG
elsif in_lex_state? :expr_arg then
warning "don't put space before argument parentheses"
end
else
# not a ternary -- do nothing?
if tokadd_string(func, term, paren) == RubyLexer::EOF then
rb_compile_error "unterminated string meets end of file"
end
token
return :tSTRING_CONTENT, string_buffer.join
end
end
def yylex_paren19
if is_beg? then
:tLPAREN
elsif is_space_arg? then
:tLPAREN_ARG
else
:tLPAREN2 # plain '(' in parse.y
end
end
require "ruby_lexer.rex"
def yylex_string # TODO: rewrite / remove
token = if lex_strterm[0] == :heredoc then
self.heredoc lex_strterm
else
self.parse_string lex_strterm
end
if ENV["DEBUG"] then
class RubyLexer
alias :old_lineno= :lineno=
if token == :tSTRING_END || token == :tREGEXP_END then
self.lineno = nil
self.lex_strterm = nil
self.lex_state = :expr_end
def d o
$stderr.puts o.inspect
end
return token
def lineno= n
self.old_lineno= n
where = caller.first.split(/:/).first(2).join(":")
d :lineno => [n, where, ss && ss.rest[0,40]]
end
end
end

@@ -62,24 +62,4 @@ # encoding: ASCII-8BIT

def current_line # HAHA fuck you (HACK)
string_to_pos[/\A.*__LINE__/m].split(/\n/).size
end
def extra_lines_added
@extra_lines_added ||= 0
end
def extra_lines_added= val
@extra_lines_added = val
end
def lineno
string[0...charpos].count("\n") + 1 - extra_lines_added
end
# TODO: once we get rid of these, we can make things like
# TODO: current_line and lineno much more accurate and easy to do
def unread_many str # TODO: remove this entirely - we should not need it
warn({:unread_many => caller[0]}.inspect) if ENV['TALLY']
self.extra_lines_added += str.count("\n") - 1
begin

@@ -115,3 +95,3 @@ string[charpos, 0] = str

module RubyParserStuff
VERSION = "3.3.0" unless constants.include? "VERSION" # SIGH
VERSION = "3.4.0" unless constants.include? "VERSION" # SIGH

@@ -153,11 +133,2 @@ attr_accessor :lexer, :in_def, :in_single, :file

def arg_add(node1, node2) # TODO: nuke
return s(:arglist, node2) unless node1
node1[0] = :arglist if node1[0] == :array
return node1 << node2 if node1[0] == :arglist
return s(:arglist, node1, node2)
end
def arg_blk_pass node1, node2 # TODO: nuke

@@ -397,2 +368,3 @@ node1 = s(:arglist, node1) unless [:arglist, :call_args, :array, :args].include? node1.first

def gettable(id)
lineno = id.lineno if id.respond_to? :lineno
id = id.to_sym if String === id

@@ -418,3 +390,3 @@

result.line(result.line - 1) if result.line and lexer.src.bol?
result.line lineno if lineno

@@ -465,3 +437,3 @@ raise "identifier #{id.inspect} is not valid" unless result

def literal_concat head, tail
def literal_concat head, tail # TODO: ugh. rewrite
return tail unless head

@@ -485,4 +457,6 @@ return head unless tail

if htype == :str then
lineno = head.line
tail[1] = head[-1] + tail[1]
head = tail
head.line = lineno
else

@@ -573,3 +547,3 @@ tail[0] = :array

def argl x
x = s(:arglist, x) if x and x[0] != :arglist
x = s(:arglist, x) if x and x[0] == :array
x

@@ -598,9 +572,10 @@ end

args ||= s(:arglist)
args[0] = :arglist if [:args, :array, :call_args].include? args.first
args = s(:arglist, args) unless args.first == :arglist
if args
if [:arglist, :args, :array, :call_args].include? args.first
result.concat args.sexp_body
else
result << args
end
end
# HACK quick hack to make this work quickly... easy to clean up above
result.concat args[1..-1]
line = result.grep(Sexp).map(&:line).compact.min

@@ -612,5 +587,4 @@ result.line = line if line

def new_case expr, body
def new_case expr, body, line
result = s(:case, expr)
line = (expr || body).line

@@ -807,4 +781,10 @@ while body and body.node_type == :when

rescue RegexpError => e
warn "Ignoring: #{e.message}"
Regexp.new(node[1], Regexp::ENC_NONE)
warn "WA\RNING: #{e.message} for #{node[1].inspect} #{options.inspect}"
begin
warn "WA\RNING: trying to recover with ENC_UTF8"
Regexp.new(node[1], Regexp::ENC_UTF8)
rescue RegexpError => e
warn "WA\RNING: trying to recover with ENC_NONE"
Regexp.new(node[1], Regexp::ENC_NONE)
end
end

@@ -933,4 +913,6 @@ end

def next_token
if self.lexer.advance then
return self.lexer.token, self.lexer.yacc_value
token = self.lexer.next_token
if token and token.first != RubyLexer::EOF then
return token
else

@@ -947,9 +929,4 @@ return [false, '$end']

case lhs[0]
when :gasgn, :iasgn, :lasgn, :masgn, :cdecl, :cvdecl, :cvasgn then
when :lasgn, :iasgn, :cdecl, :cvdecl, :gasgn, :cvasgn, :attrasgn then
lhs << rhs
when :attrasgn then
lhs << rhs
when :call then
args = lhs.pop unless Symbol === lhs.last
lhs.concat arg_add(args, rhs)[1..-1]
when :const then

@@ -959,3 +936,3 @@ lhs[0] = :cdecl

else
raise "unknown lhs #{lhs.inspect}"
raise "unknown lhs #{lhs.inspect} w/ #{rhs.inspect}"
end

@@ -1050,6 +1027,8 @@

self.file = file.dup
self.lexer.src = str
@yydebug = ENV.has_key? 'DEBUG'
# HACK -- need to get tests passing more than have graceful code
self.lexer.ss = RPStringScanner.new str
do_parse

@@ -1104,3 +1083,3 @@ end

result = Sexp.new(*args)
result.line ||= lexer.lineno if lexer.src # otherwise...
result.line ||= lexer.lineno if lexer.ss # otherwise...
result.file = self.file

@@ -1388,2 +1367,12 @@ result

class String
##
# This is a hack used by the lexer to sneak in line numbers at the
# identifier level. This should be MUCH smaller than making
# process_token return [value, lineno] and modifying EVERYTHING that
# reduces tIDENTIFIER.
attr_accessor :lineno
end
class Sexp

@@ -1406,8 +1395,6 @@ attr_writer :paren

def add x
concat x
end
alias :add :<<
def add_all x
raise "no: #{self.inspect}.add_all #{x.inspect}" # TODO: need a test to trigger this
self.concat x.sexp_body
end

@@ -1414,0 +1401,0 @@

@@ -8,3 +8,3 @@ .autotest

bin/ruby_parse_extract_error
lib/gauntlet_rubyparser.rb
lib/.document
lib/ruby18_parser.rb

@@ -17,2 +17,4 @@ lib/ruby18_parser.y

lib/ruby_lexer.rb
lib/ruby_lexer.rex
lib/ruby_lexer.rex.rb
lib/ruby_parser.rb

@@ -19,0 +21,0 @@ lib/ruby_parser_extras.rb

+20
-14
# -*- ruby -*-
require 'rubygems'
require 'hoe'
$:.unshift "../../hoe/dev/lib"
require "rubygems"
require "hoe"
Hoe.plugin :seattlerb

@@ -13,9 +15,10 @@ Hoe.plugin :racc

Hoe.spec 'ruby_parser' do
developer 'Ryan Davis', 'ryand-ruby@zenspider.com'
Hoe.spec "ruby_parser" do
developer "Ryan Davis", "ryand-ruby@zenspider.com"
license "MIT"
dependency 'sexp_processor', '~> 4.1'
dependency 'rake', '< 11', :developer
dependency "sexp_processor", "~> 4.1"
dependency "rake", "< 11", :developer
dependency "oedipus_lex", "~> 2.1", :developer

@@ -26,2 +29,3 @@ if plugin? :perforce then

self.perforce_ignore << "lib/ruby20_parser.rb"
self.perforce_ignore << "lib/ruby_lexer.rex.rb"
end

@@ -35,6 +39,7 @@

file "lib/ruby20_parser.rb" => "lib/ruby20_parser.y"
file "lib/ruby_lexer.rex.rb" => "lib/ruby_lexer.rex"
task :clean do
rm_rf(Dir["**/*~"] +
Dir["**/*.diff"] +
Dir["diff.diff"] + # not all diffs. bit me too many times
Dir["coverage.info"] +

@@ -57,8 +62,8 @@ Dir["coverage"] +

end
system 'find -d unit -type d -empty -exec rmdir {} \;'
system "find -d unit -type d -empty -exec rmdir {} \;"
end
task :sort do
sh 'grepsort "^ +def" lib/ruby_lexer.rb'
sh 'grepsort "^ +def (test|util)" test/test_ruby_lexer.rb'
sh "grepsort '^ +def' lib/ruby_lexer.rb"
sh "grepsort '^ +def (test|util)' test/test_ruby_lexer.rb"
end

@@ -85,3 +90,3 @@

def run_and_log cmd, prefix
files = ENV['FILES'] || 'unit/*.rb'
files = ENV["FILES"] || "unit/*.rb"
p, x = prefix, "txt"

@@ -148,6 +153,7 @@ n = Dir["#{p}.*.#{x}"].map { |s| s[/\d+/].to_i }.max + 1 rescue 1

Rake.application[:parser].invoke # this way we can have DEBUG set
Rake.application[:lexer].invoke # this way we can have DEBUG set
$: << "lib"
require 'ruby_parser'
require 'pp'
require "ruby_parser"
require "pp"

@@ -178,3 +184,3 @@ parser = case ENV["V"]

p e
ss = parser.lexer.src
ss = parser.lexer.ss
src = ss.string

@@ -181,0 +187,0 @@ lines = src[0..ss.pos].split(/\n/)

#!/usr/bin/ruby -ws
$f ||= false
$:.unshift "../../ruby_parser/dev/lib"
$:.unshift "../../ruby2ruby/dev/lib"
require 'rubygems'
require 'ruby2ruby'
require 'ruby_parser'
require 'gauntlet'
class RubyParserGauntlet < Gauntlet
def initialize
super
self.data = Hash.new { |h,k| h[k] = {} }
old_data = load_yaml data_file
self.data.merge! old_data
end
def should_skip? name
if $f then
if Hash === data[name] then
! data[name].empty?
else
data[name]
end
else
data[name] == true # yes, == true on purpose
end
end
def diff_pp o1, o2
require 'pp'
Tempfile.new('ruby_parser_a') do |file_a|
PP.pp o1, file_a
Tempfile.new('ruby_parser_b') do |file_b|
PP.pp o2, file_b
`diff -u #{file_a.path} #{file_b.path}`
end
end
end
def broke name, file, msg
warn "bad"
self.data[name][file] = msg
self.dirty = true
end
def process path, name
begin
$stderr.print " #{path}: "
rp = RubyParser.new
r2r = Ruby2Ruby.new
old_ruby = File.read(path)
begin
old_sexp = rp.process old_ruby
rescue Racc::ParseError => e
self.data[name][path] = :unparsable
self.dirty = true
return
end
new_ruby = r2r.process old_sexp.deep_clone
begin
new_sexp = rp.process new_ruby
rescue Racc::ParseError => e
broke name, path, "couldn't parse new_ruby: #{e.message.strip}"
return
end
if old_sexp != new_sexp then
broke name, path, diff_pp(old_sexp, new_sexp)
return
end
self.data[name][path] = true
self.dirty = true
warn "good"
rescue Interrupt
puts "User cancelled"
exit 1
rescue Exception => e
broke name, path, " UNKNOWN ERROR: #{e}: #{e.message.strip}"
end
end
def run name
warn name
Dir["**/*.rb"].sort.each do |path|
next if path =~ /gemspec.rb/ # HACK
next if data[name][path] == true
process path, name
end
if self.data[name].values.all? { |v| v == true } then
warn " ALL GOOD!"
self.data[name] = true
self.dirty = true
end
end
end
filter = ARGV.shift
filter = Regexp.new filter if filter
gauntlet = RubyParserGauntlet.new
gauntlet.run_the_gauntlet filter

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display