From 7f1d9dd2d3cc6aba91df0a27aeeaa27fc4861878 Mon Sep 17 00:00:00 2001 From: Benjamin Attal Date: Sun, 27 Apr 2014 22:46:07 -0400 Subject: [PATCH] Add a working preliminary version of the ruby parser. Still need to add a rule for running it in the Rakefile. Add: parser_server.rb: - listens for connections from the python client process parser.rb: - creates a syntax tree from the input and returns relevant data about it to the client --- .gitignore | 7 ++- parsers/ruby/lib/parse_server.rb | 22 +++++++ parsers/ruby/lib/parser.rb | 125 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 parsers/ruby/lib/parse_server.rb create mode 100644 parsers/ruby/lib/parser.rb diff --git a/.gitignore b/.gitignore index 216b1f5..319057d 100644 --- a/.gitignore +++ b/.gitignore @@ -42,8 +42,11 @@ nosetests.xml .pydevproject # Maven -parsers/java/target/* +target + +# Ruby +!parsers/ruby/lib # Ctags */tags -logs/* +log diff --git a/parsers/ruby/lib/parse_server.rb b/parsers/ruby/lib/parse_server.rb new file mode 100644 index 0000000..bcc605f --- /dev/null +++ b/parsers/ruby/lib/parse_server.rb @@ -0,0 +1,22 @@ +require 'socket' +require File.expand_path('../parser.rb', __FILE__) + +server = TCPServer.new 5003 + +loop do + # Start a new thread for each client accepted + Thread.start(server.accept) do |client| + begin + # Get the amount of data to be read + size = (client.readline).to_i + p = Bitshift::Parser.new client.read(size) + # Get the parsed result + symbols = p.parse.to_s + client.puts [symbols.length].pack('c') + client.puts symbols + ensure + # Close the socket + client.close + end + end +end diff --git a/parsers/ruby/lib/parser.rb b/parsers/ruby/lib/parser.rb new file mode 100644 index 0000000..5751ce0 --- /dev/null +++ b/parsers/ruby/lib/parser.rb @@ -0,0 +1,125 @@ +require 'socket' +require 'ruby_parser' +require 'sexp_processor' + +module Bitshift + class Parser + def initialize(source) + @source = source + end + + def parse + parser = RubyParser.new + tree = parser.parse(@source) + offset = tree.line - 1 + processor = NodeVisitor.new offset + processor.process tree + return processor.symbols + end + end + + class NodeVisitor < SexpProcessor + attr_accessor :symbols + attr_accessor :offset + + def initialize(offset) + super() + @require_empty = false + @offset = offset + + module_hash = Hash.new {|hash, key| hash[key] = Hash.new} + class_hash = module_hash.clone + function_hash = Hash.new {|hash, key| hash[key] = { calls: [] } } + var_hash = Hash.new {|hash, key| hash[key] = [] } + + @symbols = { + modules: module_hash, + classes: class_hash, + functions: function_hash, + vars: var_hash + } + end + + def block_position(exp) + pos = Hash.new + end_ln = (start_ln = exp.line - offset) + cur_exp = exp + + while cur_exp.is_a? Sexp + end_ln = cur_exp.line - offset + cur_exp = cur_exp.last + break if cur_exp == nil + end + + pos[:coord] = { + start_ln: start_ln, + end_ln: end_ln } + return pos + end + + def statement_position(exp) + pos = Hash.new + end_ln = start_ln = exp.line - offset + + pos[:coord] = { + start_ln: start_ln, + end_ln: end_ln } + return pos + end + + def process_module(exp) + pos = block_position exp + exp.shift + name = exp.shift + symbols[:modules][name] = pos + exp.each_sexp {|s| process(s)} + return exp.clear + end + + def process_class(exp) + pos = block_position exp + exp.shift + name = exp.shift + symbols[:classes][name] = pos + exp.each_sexp {|s| process(s)} + return exp.clear + end + + def process_defn(exp) + pos = block_position exp + exp.shift + name = exp.shift + symbols[:functions][name][:declaration] = pos + exp.each_sexp {|s| process(s)} + return exp.clear + end + + def process_call(exp) + pos = statement_position exp + exp.shift + exp.shift + name = exp.shift + symbols[:functions][name][:calls] << pos + exp.each_sexp {|s| process(s)} + return exp.clear + end + + def process_iasgn(exp) + pos = statement_position exp + exp.shift + name = exp.shift + symbols[:vars][name] << pos + exp.each_sexp {|s| process(s)} + return exp.clear + end + + def process_lasgn(exp) + pos = statement_position exp + exp.shift + name = exp.shift + symbols[:vars][name] << pos + exp.each_sexp {|s| process(s)} + return exp.clear + end + end +end