Created
January 6, 2012 10:08
-
-
Save vmi/1569958 to your computer and use it in GitHub Desktop.
xpash - Interactive XPath Shell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
# -*- coding: utf-8 -*- | |
# | |
# xpash - Interactive XPath Shell | |
require 'readline' | |
require 'rexml/document' | |
include REXML | |
prompt = '> ' | |
def help | |
print <<-EOF | |
Usage: xpash [XMLFILE] | |
Commands: | |
- load FILE | |
load XML file. | |
- parse STRING | |
parse STRING as XML. | |
- QUERY, $VAR/QUERY | |
query xpath and show result. | |
- VAR = QUERY | |
and assign to VAR. | |
- ?, h, help | |
show help. | |
- ^D, q, quit, exit | |
quit shell. | |
EOF | |
end | |
def dump_text(e) | |
str = '' | |
e.each_child do |c| | |
case c.node_type | |
when :text | |
str += c.value | |
when :element, :document | |
str += dump_text(c) | |
end | |
end | |
str | |
end | |
class XPashRunner | |
def initialize(file = nil) | |
@doc = nil | |
@vars = {} | |
load(file) if file | |
end | |
def load(file) | |
$stderr.print "Loading [#{file}]..." | |
begin | |
@doc = Document.new(File.new file) | |
$stderr.puts "done." | |
rescue => e | |
$stderr.puts e, "Failed." | |
end | |
end | |
def parse(str) | |
begin | |
@doc = Document.new(str) | |
$stderr.puts "Parsed: #{@doc}" | |
rescue => e | |
$stderr.puts e, "Failed." | |
end | |
end | |
def run(line) | |
if /\A([A-Za-z_]\w*)\s*=\s*(.*)\z/ =~ line | |
var = $1 | |
expr = $2 | |
else | |
var = nil | |
expr = line | |
end | |
case expr | |
when 'q', 'quit', 'exit' | |
puts "Quit." | |
exit | |
when '?', 'h', 'help' | |
help | |
when /load\s+(.+)/ | |
load($1) | |
when /parse\s+(.+)/ | |
parse($1) | |
else | |
if @doc.nil? | |
$stderr.puts "Error: Any XML files are not loaded." | |
break | |
end | |
result = query(expr) | |
if var | |
@vars[var] = result | |
$stderr.puts "Assigned to '#{var}'" | |
end | |
end | |
end | |
def query(expr) | |
if /\A\$([A-Za-z_]\w*)(?:\[(\d+)\])?(?:\/(.*))?\z/ =~ expr | |
var = $1 | |
index = $2 | |
expr = $3 | |
if [email protected]_key?(var) | |
$stderr.puts "Error: Missing variable '#{var}'" | |
return | |
end | |
d = @vars[var] | |
if index | |
if d.instance_of? Array | |
d = d[index.to_i - 1] | |
else | |
$stderr.puts "Error: '#{var}' is not array." | |
return | |
end | |
$stderr.puts "Element from '#{var}[#{index}]'" | |
else | |
if !d.respond_to?(:elements) | |
$stderr.puts "Error: '#{var}' is not element. (#{d.class})" | |
return | |
end | |
$stderr.puts "Element from '#{var}'" | |
end | |
else | |
d = @doc | |
end | |
text = false | |
attr = nil | |
case expr | |
when /\A(?:(.*)\/)?text\(\)\z/ | |
expr = $1 || '.' | |
text = true | |
$stderr.puts "Query: #{expr}/text()" | |
when /\A(?:(.*)\/)?@(\w+)\z/ | |
expr = $1 || '.' | |
attr = $2 | |
$stderr.puts "Query: #{expr}/@#{attr}" | |
else | |
expr ||= '*' | |
$stderr.puts "Query: #{expr}" | |
end | |
i = 0 | |
result = [] | |
begin | |
d.elements.each(expr) do |e| | |
case | |
when text | |
next unless e.has_text? | |
value = dump_text(e) | |
when attr | |
next unless e.attributes.has_key?(attr) | |
value = e.attributes[attr] | |
else | |
value = e | |
end | |
i += 1 | |
$stderr.print "#{i}: " | |
puts value | |
result.push(value) | |
end | |
rescue => e | |
$stderr.puts "Error: #{e}" | |
return nil | |
end | |
case result.length | |
when 0 | |
$stderr.puts "Empty." | |
nil | |
when 1 | |
result[0] | |
else | |
result | |
end | |
end | |
end | |
$stdout.sync = true | |
runner = XPashRunner.new(*ARGV) | |
while line = Readline.readline(prompt, true) | |
line.gsub!(/\A\s+|\s+\z/, '') # trim blank | |
runner.run(line) unless line.empty? | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment