Module: Yarsh::Multiline
- Defined in:
- lib/yarsh/multiline.rb
Overview
Multiline input support: decides when a line is syntactically incomplete
(keep reading) and transforms a finished buffer so shell lines can live
inside Ruby blocks/defs/classes: shell lines are rewritten into sh calls.
Defined Under Namespace
Classes: IdentifierSurround
Constant Summary
collapse
- INCOMPLETE_MESSAGES =
[
/unexpected end-of-input/,
/unterminated string/,
/unterminated heredoc/,
/unterminated regexp/,
/can't find string ".*" anywhere before EOF/,
/expected a `when` or `in` clause after `case`/,
/expected an `end` to close the/
].freeze
- STRUCTURAL_KEYWORDS =
Keywords that must never be treated as shell, even though some of them
(break, next, return, times, ...) are also bash builtins.
%w[do end if else elsif when rescue ensure then begin
case class def module while until for unless
return break next yield].freeze
- KEYWORD_REGEX =
Regexp.new("\\A(?:#{STRUCTURAL_KEYWORDS.join('|')})\\b")
Class Method Summary
collapse
Class Method Details
.classify_line(line) ⇒ Object
:ruby - keep the line verbatim
:shell - rewrite the line into a sh call
:skip - drop the line (blank)
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
# File 'lib/yarsh/multiline.rb', line 73
def self.classify_line(line)
stripped = line.strip
return :skip if stripped.empty?
return :structural if structural?(stripped)
:yarsh
end
|
.compiles?(src) ⇒ Boolean
129
130
131
132
133
134
|
# File 'lib/yarsh/multiline.rb', line 129
def self.compiles?(src)
RubyVM::InstructionSequence.compile(src)
true
rescue SyntaxError
false
end
|
.heredoc_delimiter(line) ⇒ Object
136
137
138
139
140
141
142
143
144
145
|
# File 'lib/yarsh/multiline.rb', line 136
def self.heredoc_delimiter(line)
return nil if line.include?('-->')
return nil unless line.include?('<<')
m = line.match(/<<(-|~)?(['"])([^'"]+)\2(?:\s*\.\w+)?\s*\z/)
return m[3] if m
m = line.match(/<<(-|~)?([A-Za-z_]\w*)(?:\s*\.\w+)?\s*\z/)
m && m[2]
end
|
.incomplete?(src) ⇒ Boolean
63
64
65
66
67
68
|
# File 'lib/yarsh/multiline.rb', line 63
def self.incomplete?(src)
RubyVM::InstructionSequence.compile(src)
false
rescue SyntaxError => e
INCOMPLETE_MESSAGES.any? { |re| e.message =~ re }
end
|
.structural?(line) ⇒ Boolean
124
125
126
127
|
# File 'lib/yarsh/multiline.rb', line 124
def self.structural?(line)
Ripper.lex(line).any? { |token| STRUCTURAL_KEYWORDS.include?(token[2]) }
end
|
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/yarsh/multiline.rb', line 91
def self.transform(buffer, &shell_command)
heredoc_delim = nil
is_structural = false
print_binding = 'bind = binding'
ident_surround = IdentifierSurround.new
buffer.split("\n").map do |line|
if heredoc_delim
heredoc_delim = nil if line.strip == heredoc_delim
next line
end
if (delim = heredoc_delimiter(line))
heredoc_delim = delim
next line
end
case classify_line(line, &shell_command)
when :skip then nil
when :structural
next line if line.include? 'end'
if is_structural
line = ident_surround.surrond_with_bind(line, 'bind')
print_binding = 'bind = binding + bind'
end
is_structural = true
line + "\n" + print_binding
when :shell then "__sh__(#{line.strip.inspect})"
when :yarsh
ident_surround.save_local_vars(line)
"__yarsh__(#{line.strip.inspect}, #{is_structural ? 'bind' : 'nil'})"
end
end.join("\n")
end
|