Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 5386e9c529d13653c6bea9eb4ecdbda54b07352e @dmajda dmajda committed Jan 22, 2013
Showing with 483 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +7 −0 Gemfile
  3. +22 −0 LICENSE
  4. +11 −0 README.md
  5. +5 −0 Rakefile
  6. +1 −0 VERSION
  7. +9 −0 lib/y2r.rb
  8. +62 −0 lib/y2r/ast.rb
  9. +93 −0 lib/y2r/parser.rb
  10. +3 −0 lib/y2r/version.rb
  11. +12 −0 spec/spec_helper.rb
  12. +135 −0 spec/y2r/ast_spec.rb
  13. +65 −0 spec/y2r/parser_spec.rb
  14. +23 −0 spec/y2r_spec.rb
  15. +33 −0 y2r.gemspec
@@ -0,0 +1,2 @@
+Gemfile.lock
+.rbenv-version
@@ -0,0 +1,7 @@
+source "http://rubygems.org"
+
+gemspec
+
+group :test do
+ gem "rake"
+end
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013 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.
@@ -0,0 +1,11 @@
+Y2R
+===
+
+Y2R is a transpiler translating YCP code into Ruby.
+
+[YCP](http://doc.opensuse.org/projects/YaST/SLES10/tdg/Book-YCPLanguage.html) is
+a legacy language that is used internally in SUSE to write parts of
+[YaST](http://en.opensuse.org/Portal:YaST). This tool will help to get rid of
+it.
+
+**This is currently work in progress and it's probably not useful yet.**
@@ -0,0 +1,5 @@
+require "rspec/core/rake_task"
+
+RSpec::Core::RakeTask.new
+
+task :default => :spec
@@ -0,0 +1 @@
+0.1.0
@@ -0,0 +1,9 @@
+require File.expand_path(File.dirname(__FILE__) + "/y2r/ast")
+require File.expand_path(File.dirname(__FILE__) + "/y2r/parser")
+require File.expand_path(File.dirname(__FILE__) + "/y2r/version")
+
+module Y2R
+ def self.compile(input)
+ Parser.new.parse(input).to_ruby
+ end
+end
@@ -0,0 +1,62 @@
+require "ostruct"
+
+module Y2R
+ module AST
+ # Sorted alphabetically.
+
+ class Assign < OpenStruct
+ def to_ruby
+ "#{name} = #{child.to_ruby}"
+ end
+ end
+
+ class Block < OpenStruct
+ def to_ruby
+ statements.to_ruby
+ end
+ end
+
+ class Const < OpenStruct
+ def to_ruby
+ case type
+ when "void"
+ "nil"
+ when "int"
+ value
+ else
+ raise "Unknown const type: #{type}."
+ end
+ end
+ end
+
+ class Statements < OpenStruct
+ def to_ruby
+ children.map(&:to_ruby).join("\n")
+ end
+ end
+
+ class Stmt < OpenStruct
+ def to_ruby
+ child.to_ruby
+ end
+ end
+
+ class Symbol < OpenStruct
+ def to_ruby
+ return ""
+ end
+ end
+
+ class Symbols < OpenStruct
+ def to_ruby
+ children.map(&:to_ruby).join("")
+ end
+ end
+
+ class YCP < OpenStruct
+ def to_ruby
+ child.to_ruby
+ end
+ end
+ end
+end
@@ -0,0 +1,93 @@
+require "cheetah"
+require "rexml/document"
+require "tempfile"
+
+module Y2R
+ class Parser
+ class SyntaxError < StandardError
+ end
+
+ # Sorted alphabetically.
+ ELEMENT_INFO = {
+ :assign => { :type => :wrapper, :class => AST::Assign, :filter => [] },
+ :block => { :type => :struct, :class => AST::Block, :filter => [] },
+ :const => { :type => :leaf, :class => AST::Const, :filter => [] },
+ :statements => { :type => :collection, :class => AST::Statements, :filter => [] },
+ :stmt => { :type => :wrapper, :class => AST::Stmt, :filter => [] },
+ :symbol => { :type => :leaf, :class => AST::Symbol, :filter => [:global, :category, :type, :name] },
+ :symbols => { :type => :collection, :class => AST::Symbols, :filter => [] },
+ :ycp => { :type => :wrapper, :class => AST::YCP, :filter => [:version] }
+ }
+
+ def parse(input)
+ xml_to_ast(ycp_to_xml(input))
+ end
+
+ private
+
+ def ycp_to_xml(ycp)
+ ycp_file = Tempfile.new("y2r")
+ begin
+ begin
+ ycp_file.write(ycp)
+ ensure
+ ycp_file.close
+ end
+
+ xml_file = Tempfile.new("y2r")
+ xml_file.close
+ begin
+ begin
+ Cheetah.run("ycpc", "-c", "-x", "-o", xml_file.path, ycp_file.path)
+ rescue Cheetah::ExecutionFailed => e
+ raise SyntaxError.new(e.stderr)
+ end
+
+ File.read(xml_file.path)
+ ensure
+ xml_file.unlink
+ end
+ ensure
+ ycp_file.unlink
+ end
+ end
+
+ def xml_to_ast(xml)
+ element_to_node(REXML::Document.new(xml).root)
+ end
+
+ def element_to_node(element)
+ info = ELEMENT_INFO[element.name.to_sym]
+ raise "Invalid element: <#{element.name}>." unless info
+
+ node = info[:class].new
+
+ element.attributes.each do |name, value|
+ node.send("#{name}=", value) unless info[:filter].include?(name.to_sym)
+ end
+
+ case info[:type]
+ when :leaf
+ # Don't do nothing, we're done.
+
+ when :wrapper
+ node.child = element_to_node(element.elements[1])
+
+ when :collection
+ node.children = element.elements.map { |e| element_to_node(e) }
+
+ when :struct
+ element.elements.each do |element|
+ unless info[:filter].include?(element.name.to_sym)
+ node.send("#{element.name}=", element_to_node(element))
+ end
+ end
+
+ else
+ raise "Invalid node type: #{info[:type]}."
+ end
+
+ node
+ end
+ end
+end
@@ -0,0 +1,3 @@
+module Y2R
+ VERSION = File.read(File.dirname(__FILE__) + "/../../VERSION").strip
+end
@@ -0,0 +1,12 @@
+require "y2r"
+
+RSpec.configure do |c|
+ c.color_enabled = true
+end
+
+def cleanup(s)
+ s =~ /^(\s*)/
+ prefix = $1
+
+ s.split("\n").map { |line| line.sub(prefix, "") }.join("\n").strip
+end
@@ -0,0 +1,135 @@
+require "spec_helper"
+
+module Y2R::AST
+ describe Assign do
+ describe "#to_ruby" do
+ it "emits correct code" do
+ node = Assign.new(
+ :name => "i",
+ :child => Const.new(:type => "int", :value => "42")
+ )
+
+ node.to_ruby.should == "i = 42"
+ end
+ end
+ end
+
+ describe Block do
+ describe "#to_ruby" do
+ it "emits correct code" do
+ node = Block.new(
+ :statements => Statements.new(
+ :children => [
+ Stmt.new(
+ :child => Assign.new(
+ :name => "i",
+ :child => Const.new(:type => "int", :value => "42")
+ )
+ ),
+ Stmt.new(
+ :child => Assign.new(
+ :name => "j",
+ :child => Const.new(:type => "int", :value => "43")
+ )
+ ),
+ Stmt.new(
+ :child => Assign.new(
+ :name => "k",
+ :child => Const.new(:type => "int", :value => "44")
+ )
+ )
+ ]
+ )
+ )
+
+ node.to_ruby.should == "i = 42\nj = 43\nk = 44"
+ end
+ end
+ end
+
+ describe Const do
+ describe "#to_ruby" do
+ it "emits correct code" do
+ node = Const.new(:type => "int", :value => "42")
+
+ node.to_ruby.should == "42"
+ end
+ end
+ end
+
+ describe Statements do
+ describe "#to_ruby" do
+ it "emits correct code" do
+ node = Statements.new(
+ :children => [
+ Stmt.new(
+ :child => Assign.new(
+ :name => "i",
+ :child => Const.new(:type => "int", :value => "42")
+ )
+ ),
+ Stmt.new(
+ :child => Assign.new(
+ :name => "j",
+ :child => Const.new(:type => "int", :value => "43")
+ )
+ ),
+ Stmt.new(
+ :child => Assign.new(
+ :name => "k",
+ :child => Const.new(:type => "int", :value => "44")
+ )
+ )
+ ]
+ )
+
+ node.to_ruby.should == "i = 42\nj = 43\nk = 44"
+ end
+ end
+ end
+
+ describe Stmt do
+ describe "#to_ruby" do
+ it "emits correct code" do
+ node = Stmt.new(
+ :child => Assign.new(
+ :name => "i",
+ :child => Const.new(:type => "int", :value => "42")
+ )
+ )
+
+ node.to_ruby.should == "i = 42"
+ end
+ end
+ end
+
+ describe Symbol do
+ describe "#to_ruby" do
+ it "emits correct code" do
+ node = Symbol.new
+
+ node.to_ruby.should == ""
+ end
+ end
+ end
+
+ describe Symbols do
+ describe "#to_ruby" do
+ it "emits correct code" do
+ node = Symbols.new(:children => [Symbol.new, Symbol.new, Symbol.new])
+
+ node.to_ruby.should == ""
+ end
+ end
+ end
+
+ describe YCP do
+ describe "#to_ruby" do
+ it "emits correct code" do
+ node = YCP.new(:child => Const.new(:type => "void"))
+
+ node.to_ruby.should == "nil"
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 5386e9c

Please sign in to comment.