├── lib ├── ruhax │ ├── version.rb │ ├── models │ │ └── var.rb │ └── parsers │ │ ├── array.rb │ │ ├── return.rb │ │ ├── base_type.rb │ │ ├── combined_operator.rb │ │ ├── str_concat.rb │ │ ├── args.rb │ │ ├── begin.rb │ │ ├── exec_string.rb │ │ ├── regexp.rb │ │ ├── call.rb │ │ ├── condition.rb │ │ ├── class.rb │ │ ├── master.rb │ │ ├── function.rb │ │ └── var.rb ├── ruhax.rb └── cli.rb ├── .travis.yml ├── examples ├── hello_world.rb └── class_declaration.rb ├── Gemfile ├── bin ├── setup └── console ├── Rakefile ├── dev_doc └── options.md ├── .gitignore ├── test ├── class_test.rb ├── method_test.rb ├── test_helper.rb ├── condition_test.rb └── string_test.rb ├── Gemfile.lock ├── ruhax.gemspec └── README.md /lib/ruhax/version.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | VERSION = "0.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /lib/ruhax.rb: -------------------------------------------------------------------------------- 1 | require "ruhax/version" 2 | 3 | module Ruhax 4 | 5 | end 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2.3 4 | before_install: gem install bundler -v 1.10.6 5 | -------------------------------------------------------------------------------- /examples/hello_world.rb: -------------------------------------------------------------------------------- 1 | class Main 2 | def self.main() 3 | puts "Hello World" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in ruhax.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "cli" 5 | 6 | Ruhax::RuhaxCli.start(ARGV) 7 | 8 | # require "irb" 9 | # IRB.start 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new(:test) do |t| 5 | t.libs << "test" 6 | t.libs << "lib" 7 | t.test_files = FileList['test/**/*_test.rb'] 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /lib/ruhax/models/var.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Model of a variable (of any kind) 4 | ### 5 | class VarModel 6 | attr_reader :name, :type, :value 7 | attr_writer :name, :type, :value 8 | 9 | ### 10 | # name : variable name 11 | # type : variable type (haxe) 12 | # value : default value of the variable 13 | ### 14 | def initialize(name, type = nil, value = nil) 15 | @name = name 16 | @type = type 17 | @value = value 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /dev_doc/options.md: -------------------------------------------------------------------------------- 1 | To know where you are, you can send or use tokens in "options" arguments. 2 | 3 | The existing tokens are : 4 | - `:in_function` In a function 5 | - `:in_condition` In a condition 6 | - `:current_class` Current class name 7 | - `:locale_variables` Locale variables of the current function 8 | - `:instance_variables` Current class variables 9 | - `:static_variables` Current class static var 10 | - `:has_constructor` Does the current class have a constructor ? 11 | - `:no_value` For combined operators 12 | - `:no_new_lines` Do not add new line between children 13 | 14 | Please, update this list when you add a token 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /test/tmp/ 9 | /test/version_tmp/ 10 | /tmp/ 11 | 12 | ## Specific to RubyMotion: 13 | .dat* 14 | .repl_history 15 | build/ 16 | 17 | ## Documentation cache and generated files: 18 | /.yardoc/ 19 | /_yardoc/ 20 | /doc/ 21 | /rdoc/ 22 | 23 | ## Environment normalisation: 24 | /.bundle/ 25 | /vendor/bundle 26 | /lib/bundler/man/ 27 | 28 | # for a library or gem, you might want to ignore these files since the code is 29 | # intended to run in multiple environments; otherwise, check them in: 30 | # Gemfile.lock 31 | # .ruby-version 32 | # .ruby-gemset 33 | 34 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 35 | .rvmrc 36 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/array.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Array parsing 4 | ### 5 | class ArrayParser < MasterParser 6 | def initialize(node, options) 7 | @node = node 8 | @options = options 9 | @content = "" 10 | end 11 | 12 | ### 13 | # Process parsing 14 | ### 15 | def parse 16 | @content << "[" 17 | @node.children.each_with_index do |child, index| 18 | result = parse_new_node(child, @options).to_s 19 | 20 | @content << result 21 | @content << ", " if index != @node.children.length - 1 22 | end 23 | @content << "]" 24 | end 25 | 26 | ### 27 | # Return string value of the parser 28 | ### 29 | def to_s 30 | @content 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/return.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Parse the :return token 4 | ### 5 | class ReturnParser < MasterParser 6 | def initialize(node, options) 7 | @node = node 8 | @options = options 9 | @content = "" 10 | end 11 | 12 | ### 13 | # Process parsing 14 | ### 15 | def parse 16 | @content << "return" 17 | 18 | @node.children.each do |child| 19 | @content << " " 20 | 21 | if child.is_a? AST::Node 22 | @content << parse_new_node(child, @options).to_s 23 | elsif child.is_a? Symbol 24 | @content << child.to_s 25 | end 26 | end 27 | 28 | @content << ";" unless @content[-1, 1] == ";" 29 | end 30 | 31 | ### 32 | # Return string value of the parser 33 | ### 34 | def to_s 35 | @content 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/base_type.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Parse basic types as str, int, float, nil 4 | # 5 | # Note : nil will be translated into null 6 | ### 7 | class BaseTypeParser < MasterParser 8 | # Constructor 9 | # @param node AST::Node node to parse 10 | # @param type Symbol ruby type 11 | def initialize(node, type) 12 | @content = "" 13 | @type = type 14 | @node = node 15 | end 16 | 17 | ### 18 | # Process parsing 19 | ### 20 | def parse 21 | case @type 22 | when :nil 23 | @content = "null" 24 | when :str, :sym 25 | @content << "\"" << @node.children[0].to_s << "\"" 26 | else 27 | @content = @node.children[0].to_s 28 | end 29 | end 30 | 31 | ### 32 | # Return string value of the parser 33 | ### 34 | def to_s 35 | @content 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/class_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ClassTest < RuhaxTester 4 | def test_class_attributes 5 | ruby = %q( 6 | class Test 7 | @@count = 0 8 | 9 | def initialize(name, age = 10) 10 | @name = name 11 | @age = age 12 | 13 | @@count += 1 14 | callable 15 | end 16 | 17 | def callable 18 | puts "Callable" 19 | end 20 | end 21 | ) 22 | 23 | haxe = %q( 24 | class Test { 25 | public var name : Dynamic; 26 | public var age : Dynamic; 27 | public static var count = 0; 28 | 29 | public function new(name, age = 10) { 30 | this.name = name; 31 | this.age = age; 32 | Test.count += 1; 33 | return callable(); 34 | } 35 | 36 | public function callable() { 37 | return trace("Callable"); 38 | } 39 | } 40 | ) 41 | 42 | check_return ruby, haxe 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/combined_operator.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Parse "combined operators" 4 | # 5 | # It means, all that kind of operators : +=, -= 6 | ### 7 | class CombinedOperatorParser < MasterParser 8 | def initialize(node, options) 9 | @node = node 10 | @options = options 11 | @content = "" 12 | end 13 | 14 | ### 15 | # Process parsing 16 | ### 17 | def parse 18 | if @node.children.length != 3 19 | return 20 | end 21 | 22 | @node.children.each_with_index do |child, k| 23 | if child.is_a? AST::Node 24 | result = parse_new_node(child, @options.merge({ 25 | no_value: true 26 | })).to_s 27 | else 28 | result = child.to_s 29 | end 30 | 31 | @content << result 32 | if k == 1 33 | @content << "=" 34 | end 35 | end 36 | 37 | @content << ";" 38 | end 39 | 40 | ### 41 | # Return string value of the parser 42 | ### 43 | def to_s 44 | @content 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/str_concat.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # String concatenation happens here. 4 | # 5 | # ATM, just "#{myvar}" or "ok" + "ok" are working 6 | ### 7 | class StrConcatParser < MasterParser 8 | def initialize(node, options) 9 | @node = node 10 | @options = options 11 | @content = "" 12 | end 13 | 14 | ### 15 | # Process parsing 16 | ### 17 | def parse 18 | previous_type = nil 19 | @node.children.each_with_index do |child, index| 20 | result = parse_new_node(child, @options.merge({ 21 | no_new_lines: true 22 | })).to_s 23 | 24 | if child.type != :str 25 | @content << " + " if previous_type == :str 26 | @content << result 27 | @content << " + " if index != @node.children.length - 1 28 | else 29 | @content << result 30 | end 31 | 32 | previous_type = child.type 33 | end 34 | end 35 | 36 | ### 37 | # Return string value of the parser 38 | ### 39 | def to_s 40 | @content 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/cli.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | require 'parser/current' 3 | 4 | require 'ruhax/parsers/master' 5 | require 'ruhax/parsers/base_type' 6 | require 'ruhax/parsers/call' 7 | require 'ruhax/parsers/function' 8 | require 'ruhax/parsers/args' 9 | require 'ruhax/parsers/var' 10 | require 'ruhax/parsers/class' 11 | require 'ruhax/parsers/combined_operator' 12 | require 'ruhax/parsers/return' 13 | require 'ruhax/parsers/begin' 14 | require 'ruhax/parsers/str_concat' 15 | require 'ruhax/parsers/exec_string' 16 | require 'ruhax/parsers/array' 17 | require 'ruhax/parsers/regexp' 18 | require 'ruhax/parsers/condition' 19 | 20 | require 'ruhax/models/var' 21 | 22 | module Ruhax 23 | class RuhaxCli < Thor 24 | option :src, :require => true 25 | option :debug, :type => :boolean 26 | desc "build", "Build the haxe code from your ruby code" 27 | def build 28 | src = options[:src] 29 | 30 | node = Parser::CurrentRuby.parse(File.read(src)) 31 | if options[:debug] 32 | puts "******** NODE *******" 33 | p node 34 | puts "*********************" 35 | end 36 | 37 | parser = Ruhax::MasterParser.new 38 | puts parser.parse_new_node(node).to_s 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/method_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MethodTest < RuhaxTester 4 | def test_hello_word 5 | ruby = %q( 6 | class Main 7 | def self.main() 8 | puts "Hello World" 9 | end 10 | end 11 | ) 12 | 13 | haxe = %q( 14 | class Main { 15 | public function new() {} 16 | public static function main() { 17 | return trace("Hello World"); 18 | } 19 | } 20 | ) 21 | 22 | check_return ruby, haxe 23 | end 24 | 25 | def test_different_methods 26 | ruby = %q( 27 | class Test 28 | def initialize 29 | test() 30 | end 31 | def test 32 | puts "test" 33 | hello 34 | end 35 | def hello 36 | puts "Hello World" 37 | end 38 | end 39 | ) 40 | 41 | haxe = %q( 42 | class Test { 43 | public function new() { 44 | return test(); 45 | } 46 | public function test() { 47 | trace("test"); 48 | return hello(); 49 | } 50 | public function hello() { 51 | return trace("Hello World"); 52 | } 53 | } 54 | ) 55 | 56 | check_return ruby, haxe 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/args.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Parse arguments from a function 4 | # 5 | # Register all this arguments into "locale_variables" of the function's 6 | # options 7 | ### 8 | class ArgsParser < MasterParser 9 | attr_reader :variables 10 | 11 | # constructor 12 | def initialize(node) 13 | @node = node 14 | @content = "" 15 | @variables = {} 16 | end 17 | 18 | ### 19 | # Process parsing 20 | ### 21 | def parse 22 | if @node.children.length == 0 23 | return 24 | end 25 | 26 | @node.children.each_with_index do |child, index| 27 | if child.children.length == 0 28 | return 29 | end 30 | 31 | variable, default_value = child.children 32 | @variables[variable] = VarModel.new(variable.to_s) 33 | @content << variable.to_s 34 | 35 | if child.type == :optarg && default_value 36 | @content << " = " << parse_new_node(default_value).to_s 37 | end 38 | 39 | if index != @node.children.length - 1 40 | @content << ", " 41 | end 42 | end 43 | end 44 | 45 | ### 46 | # Return string value of the parser 47 | ### 48 | def to_s 49 | @content 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 2 | require 'coveralls' 3 | Coveralls.wear! 4 | 5 | require 'ruhax' 6 | require 'minitest/autorun' 7 | 8 | require 'parser/current' 9 | 10 | require 'ruhax/parsers/master' 11 | require 'ruhax/parsers/base_type' 12 | require 'ruhax/parsers/call' 13 | require 'ruhax/parsers/function' 14 | require 'ruhax/parsers/args' 15 | require 'ruhax/parsers/var' 16 | require 'ruhax/parsers/class' 17 | require 'ruhax/parsers/combined_operator' 18 | require 'ruhax/parsers/return' 19 | require 'ruhax/parsers/begin' 20 | require 'ruhax/parsers/str_concat' 21 | require 'ruhax/parsers/exec_string' 22 | require 'ruhax/parsers/array' 23 | require 'ruhax/parsers/regexp' 24 | require 'ruhax/parsers/condition' 25 | 26 | require 'ruhax/models/var' 27 | 28 | class RuhaxTester < Minitest::Test 29 | def check_return(ruby, haxe) 30 | clean_haxe = haxe.gsub " ", "" 31 | clean_haxe = clean_haxe.gsub "\n", "" 32 | 33 | node = Parser::CurrentRuby.parse(ruby) 34 | parser = Ruhax::MasterParser.new 35 | result = parser.parse_new_node(node).to_s 36 | puts result 37 | 38 | clean_result = result.gsub "\n", "" 39 | clean_result = clean_result.gsub " ", "" 40 | 41 | assert_equal clean_haxe, clean_result 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | ruhax (0.1.0) 5 | parser 6 | thor 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | ast (2.1.0) 12 | coveralls (0.8.9) 13 | json (~> 1.8) 14 | rest-client (>= 1.6.8, < 2) 15 | simplecov (~> 0.10.0) 16 | term-ansicolor (~> 1.3) 17 | thor (~> 0.19.1) 18 | tins (~> 1.6.0) 19 | docile (1.1.5) 20 | domain_name (0.5.25) 21 | unf (>= 0.0.5, < 1.0.0) 22 | http-cookie (1.0.2) 23 | domain_name (~> 0.5) 24 | json (1.8.3) 25 | mime-types (2.99) 26 | minitest (5.8.2) 27 | netrc (0.11.0) 28 | parser (2.2.3.0) 29 | ast (>= 1.1, < 3.0) 30 | rake (10.4.2) 31 | rest-client (1.8.0) 32 | http-cookie (>= 1.0.2, < 2.0) 33 | mime-types (>= 1.16, < 3.0) 34 | netrc (~> 0.7) 35 | simplecov (0.10.0) 36 | docile (~> 1.1.0) 37 | json (~> 1.8) 38 | simplecov-html (~> 0.10.0) 39 | simplecov-html (0.10.0) 40 | term-ansicolor (1.3.2) 41 | tins (~> 1.0) 42 | thor (0.19.1) 43 | tins (1.6.0) 44 | unf (0.1.4) 45 | unf_ext 46 | unf_ext (0.0.7.1) 47 | 48 | PLATFORMS 49 | ruby 50 | 51 | DEPENDENCIES 52 | bundler (~> 1.10) 53 | coveralls 54 | minitest 55 | rake (~> 10.0) 56 | ruhax! 57 | 58 | BUNDLED WITH 59 | 1.10.6 60 | -------------------------------------------------------------------------------- /test/condition_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ConditionTest < RuhaxTester 4 | def test_conditions 5 | ruby = %q( 6 | class Test 7 | def compare(a, b) 8 | @a = a 9 | @b = b 10 | 11 | my_var = @a == 3 ? 0 : 1 12 | if my_var >= @b 13 | puts "foo" 14 | 15 | if my_var == @b 16 | puts "ok" 17 | end 18 | elsif @b > @a 19 | if my_var == 0 20 | puts "sub if" 21 | end 22 | else 23 | puts "else" 24 | end 25 | end 26 | end 27 | ) 28 | 29 | haxe = %q( 30 | class Test { 31 | public var a : Dynamic; 32 | public var b : Dynamic; 33 | 34 | public function new() {} 35 | public function compare(a, b) { 36 | this.a = a; 37 | this.b = b; 38 | 39 | var my_var = this.a == 3 ? 0 : 1; 40 | return if (my_var >= this.b) { 41 | trace("foo"); 42 | 43 | if (my_var == this.b) { 44 | trace("ok"); 45 | } 46 | } else if (this.b > this.a) { 47 | if (my_var == 0) { 48 | trace("sub if"); 49 | } 50 | } else { 51 | trace ("else"); 52 | } 53 | } 54 | } 55 | ) 56 | 57 | check_return ruby, haxe 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/begin.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # This parser will translate a block of multiple instructions 4 | # 5 | # It does not output anything except its children's content 6 | # Set :no_new_lines options to don't get a new line between each children's 7 | # instructions 8 | ### 9 | class BeginParser < MasterParser 10 | def initialize(node, options) 11 | @node = node 12 | @options = options 13 | @content = "" 14 | end 15 | 16 | ### 17 | # Process parsing 18 | ### 19 | def parse 20 | need_semilicon = (@options[:in_function] && !@options[:no_new_lines]) || 21 | (!@options[:in_function] && !@options[:current_class] && !@options[:no_new_lines]) 22 | 23 | @node.children.each_with_index do |child, index| 24 | result = parse_new_node(child, @options).to_s 25 | 26 | if (index == @node.children.length - 1 && 27 | (@options[:in_function] && !@options[:no_new_lines] && !@options[:in_condition])) 28 | @content << "return " 29 | end 30 | 31 | result << ";" if result[-1] != ";" && result[-1] != "\n" && result[-1] != "}" && need_semilicon 32 | if result.length > 0 33 | @content << result 34 | @content << "\n" if !@options[:no_new_lines] 35 | end 36 | end 37 | end 38 | 39 | def count_nodes 40 | return @node.children.length 41 | end 42 | 43 | ### 44 | # Return string value of the parser 45 | ### 46 | def to_s 47 | @content 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /ruhax.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'ruhax/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "ruhax" 8 | spec.version = Ruhax::VERSION 9 | spec.authors = ["Peekmo"] 10 | spec.email = ["axel.anceau@gmail.com"] 11 | 12 | spec.summary = %q{TODO: Write a short summary, because Rubygems requires one.} 13 | spec.description = %q{TODO: Write a longer description or delete this line.} 14 | spec.homepage = "TODO: Put your gem's website or public repo URL here." 15 | 16 | # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or 17 | # delete this section to allow pushing this gem to any host. 18 | if spec.respond_to?(:metadata) 19 | spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'" 20 | else 21 | raise "RubyGems 2.0 or newer is required to protect against public gem pushes." 22 | end 23 | 24 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 25 | spec.bindir = "exe" 26 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 27 | spec.require_paths = ["lib"] 28 | 29 | spec.add_dependency "parser" 30 | spec.add_dependency "thor" 31 | spec.add_development_dependency "bundler", "~> 1.10" 32 | spec.add_development_dependency "rake", "~> 10.0" 33 | spec.add_development_dependency "minitest" 34 | spec.add_development_dependency "coveralls" 35 | end 36 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/exec_string.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Executable string parsing 4 | # 5 | # `ls -a` => Sys.command("ls", ["-a"]) 6 | ### 7 | class ExecStringParser < MasterParser 8 | def initialize(node, options) 9 | @node = node 10 | @options = options 11 | @content = "" 12 | end 13 | 14 | ### 15 | # Process parsing 16 | ### 17 | def parse 18 | args = [] 19 | 20 | @node.children.each do |child| 21 | result = parse_new_node(child, @options.merge({ 22 | no_new_lines: true 23 | })).to_s 24 | 25 | commands = result.split("\n") 26 | commands.each_with_index do |command, index| 27 | if index > 0 28 | build_content args 29 | args = [] 30 | end 31 | 32 | is_string = child.type == :str 33 | command = command[1..-2] if is_string 34 | command.split(" ").each do |arg| 35 | arg = "\"" << arg << "\"" if is_string 36 | args.push(arg) if (arg != "" && arg != "\"\"") 37 | end 38 | end 39 | end 40 | 41 | build_content args 42 | end 43 | 44 | ### 45 | # Return string value of the parser 46 | ### 47 | def to_s 48 | @content 49 | end 50 | 51 | private 52 | ### 53 | # Builds a command call 54 | # @param array args Arguments for the command 55 | ### 56 | def build_content(args) 57 | if args.length > 0 58 | @content << "Sys.command(" << args.shift << ", [" << args.join(",") << "]);" 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/regexp.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # REGEXP parsing 4 | # 5 | # "/foo#{bar}/i" 6 | ### 7 | class RegexpParser < MasterParser 8 | def initialize(node, options) 9 | @node = node 10 | @options = options 11 | @content = "" 12 | end 13 | 14 | ### 15 | # Process parsing 16 | ### 17 | def parse 18 | if @node.children.length == 0 19 | return 20 | end 21 | 22 | @content << "new EReg(" 23 | previous_type = nil 24 | @node.children.each_with_index do |child, index| 25 | # Modifiers 26 | if child.is_a?(AST::Node) && child.type == :regopt 27 | @content << ",\"" 28 | child.children.each do |c| 29 | if c.is_a? AST::Node 30 | @content << parse_new_node(c, @options).to_s 31 | else 32 | @content << c.to_s 33 | end 34 | end 35 | 36 | @content << "\"" 37 | 38 | # Content of the regexp 39 | else 40 | result = parse_new_node(child, @options.merge({ 41 | no_new_lines: true 42 | })).to_s 43 | 44 | # Interpolation 45 | if child.type != :str 46 | @content << " + " if previous_type == :str 47 | @content << result 48 | else 49 | @content << " + " if index != 0 50 | @content << result 51 | end 52 | end 53 | 54 | previous_type = child.type 55 | end 56 | 57 | @content << ")" 58 | end 59 | 60 | ### 61 | # Return string value of the parser 62 | ### 63 | def to_s 64 | @content 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruhax 2 | 3 | [![Join the chat at https://gitter.im/Peekmo/ruhax](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Peekmo/ruhax?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Travis](https://travis-ci.org/Peekmo/ruhax.svg)](https://travis-ci.org/Peekmo/ruhax/builds) 4 | [![Coverage Status](https://coveralls.io/repos/Peekmo/ruhax/badge.svg?branch=master&service=github)](https://coveralls.io/github/Peekmo/ruhax?branch=master) 5 | 6 | Pur ruby code compiled into haxe code 7 | 8 | ## Example 9 | 10 | You can play examples from "examples" folder e.g : 11 | ```bin/console build --src examples/hello_world.rb``` 12 | 13 | And... magic ! You should see something like that 14 | ```haxe 15 | class Main{ 16 | public static function main() { 17 | trace("Hello World"); 18 | } 19 | 20 | } 21 | ``` 22 | 23 | Add ```--debug``` flag to get AST of the file 24 | 25 | Note : You'll need at least ruby 2.2 26 | Don't forget to use ```bin/setup``` to install projects dependencies (You'll need bundler) 27 | 28 | ## Tests 29 | 30 | To use tests, just use the given command : ```rake test``` 31 | 32 | ## Development 33 | 34 | Everything is build on top of [whitequark parser](https://github.com/whitequark/parser/blob/master/doc/AST_FORMAT.md) to get AST nodes. Feel free to read it and to try to improve it ;) 35 | 36 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 37 | 38 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 39 | 40 | ## Contributing 41 | 42 | Bug reports and pull requests are welcome on GitHub at https://github.com/peekmo/ruhax. 43 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/call.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Parse 'send' token 4 | # 5 | # Every function call or expression will enter in that parser 6 | ### 7 | class CallParser < MasterParser 8 | @@is_in_function_call = false 9 | 10 | # Constructor 11 | def initialize(node, options = {}) 12 | @node = node 13 | @content = "" 14 | @is_method = false 15 | @has_node = false 16 | end 17 | 18 | ### 19 | # Process parsing 20 | ### 21 | def parse 22 | if @node.children.length == 0 23 | return 24 | end 25 | 26 | @node.children.each_with_index do |child, index| 27 | is_symbol = child.is_a? Symbol 28 | 29 | # New node 30 | if child.is_a? AST::Node 31 | @has_node = true 32 | 33 | root_call = false 34 | if @is_method && !@@is_in_function_call 35 | root_call = true 36 | @@is_in_function_call = true 37 | end 38 | 39 | parse = parse_new_node(child).to_s 40 | @content << parse 41 | 42 | if @is_method && index != @node.children.length - 1 43 | @content << ", " 44 | end 45 | 46 | if root_call 47 | @@is_in_function_call = false 48 | end 49 | 50 | # Method call 51 | elsif is_symbol && !@has_node 52 | str = "" 53 | case child 54 | when :puts, :print 55 | str = "trace" 56 | else 57 | str = child.to_s 58 | end 59 | 60 | @content << str << "(" 61 | @is_method = true 62 | 63 | elsif is_symbol && @has_node 64 | @content << " " << child.to_s << " " 65 | end 66 | end 67 | 68 | if @is_method 69 | @content << ")" 70 | 71 | if !@@is_in_function_call 72 | @content << ";" 73 | end 74 | end 75 | end 76 | 77 | ### 78 | # Return string value of the parser 79 | ### 80 | def to_s 81 | @content 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /examples/class_declaration.rb: -------------------------------------------------------------------------------- 1 | # METTRE À JOUR L'EXEMPLE 2 | # 3 | # 4 | # class Test{ 5 | # public var final_score : Dynamic; 6 | # public static var static_var = 0; 7 | # public function new() {} 8 | # public function my_function(arg1, arg2, optarg = 3) { 9 | # var my_var = arg2 + arg1; 10 | # arg1 = 10; 11 | # my_var += optarg; 12 | # Test.static_var = 5; 13 | # Test.static_var -=1; 14 | # var interpol = "test " + arg1 + " interpolation"; 15 | # interpol = "test " + arg1; 16 | # interpol = arg1 + " interpolation"; 17 | # interpol = arg1; 18 | # interpol = "new_string_sym"; 19 | # my_var = my_var == 3 ? 0 : 1; 20 | # if (my_var == 0) { 21 | # trace("foo"); 22 | # if (interpol == "new_string_sym") { 23 | # trace("ok"); 24 | # } 25 | # } 26 | # else if (my_var >= 1) { 27 | # if (my_var >= 2) { 28 | # trace("sub if"); 29 | # } 30 | # } else { 31 | # trace("else"); 32 | # } 33 | # } 34 | # 35 | # public function regexp() { 36 | # var regexp = new EReg("^test" + this.final_score,"im"); 37 | # regexp = new EReg(this.final_score,"im"); 38 | # regexp = new EReg("^" + this.final_score + "test","im"); 39 | # return regexp = new EReg("^test" + this.final_score + "test","im"); 40 | # } 41 | # } 42 | 43 | class Test 44 | @@static_var = 0 45 | 46 | def my_function(arg1, arg2, optarg = 3) 47 | my_var = arg2 + arg1 48 | arg1 = 10 49 | my_var += optarg 50 | @@static_var = 5 51 | @@static_var -= 1 52 | 53 | interpol = "test #{arg1} interpolation" 54 | interpol = "test #{arg1}" 55 | interpol = "#{arg1} interpolation" 56 | interpol = "#{arg1}" 57 | interpol = :new_string_sym 58 | 59 | my_var = my_var == 3 ? 0 : 1 60 | if my_var == 0 61 | puts "foo" 62 | 63 | if interpol == :new_string_sym 64 | puts "ok" 65 | end 66 | elsif my_var >= 1 67 | if my_var >= 2 68 | puts "sub if" 69 | end 70 | else 71 | puts "else" 72 | end 73 | 74 | @final_score = my_var 75 | 76 | [5, my_var, "string"] 77 | end 78 | 79 | def regexp 80 | regexp = /^test#{@final_score}/im 81 | regexp = /#{@final_score}/im 82 | regexp = /^#{@final_score}test/im 83 | regexp = /^test#{@final_score}test/im 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /test/string_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StringTest < RuhaxTester 4 | def test_interpolation 5 | ruby = %q( 6 | class Test 7 | def self.main() 8 | arg1 = "test" 9 | 10 | puts "test #{arg1} interpolation" 11 | puts "test #{arg1}" 12 | puts "#{arg1} interpolation" 13 | puts "#{arg1}" 14 | puts :new_string_sym 15 | end 16 | end 17 | ) 18 | 19 | haxe = %q( 20 | class Test { 21 | public function new() {} 22 | public static function main() { 23 | var arg1 = "test"; 24 | trace("test " + arg1 + " interpolation"); 25 | trace("test " + arg1); 26 | trace(arg1 + " interpolation"); 27 | trace(arg1); 28 | return trace("new_string_sym"); 29 | } 30 | } 31 | ) 32 | 33 | check_return ruby, haxe 34 | end 35 | 36 | def test_regex_with_static_interpolation 37 | ruby = %q( 38 | class Test 39 | @@final_score = "10" 40 | 41 | def self.main() 42 | regexp = /^test#{@@final_score}/im 43 | regexp = /#{@@final_score}/im 44 | regexp = /^#{@@final_score}test/im 45 | regexp = /^test#{@@final_score}test/im 46 | end 47 | end 48 | ) 49 | 50 | haxe = %q( 51 | class Test { 52 | public static var final_score = "10"; 53 | 54 | public function new() {} 55 | public static function main() { 56 | var regexp = new EReg("^test" + Test.final_score,"im"); 57 | regexp = new EReg(Test.final_score,"im"); 58 | regexp = new EReg("^" + Test.final_score + "test","im"); 59 | return regexp = new EReg("^test" + Test.final_score + "test","im"); 60 | } 61 | } 62 | ) 63 | 64 | check_return ruby, haxe 65 | end 66 | 67 | def test_exec_string 68 | ruby = %q( 69 | class Test 70 | def self.main() 71 | `ls -a -l` 72 | %x(ls -al) 73 | end 74 | end 75 | ) 76 | 77 | haxe = %q( 78 | class Test { 79 | public function new() {} 80 | public static function main() { 81 | Sys.command("ls", ["-a", "-l"]); 82 | return Sys.command("ls", ["-al"]); 83 | } 84 | } 85 | ) 86 | 87 | check_return ruby, haxe 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/condition.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # If parsing 4 | # Elsif not yet implemented 5 | ### 6 | class ConditionParser < MasterParser 7 | 8 | # Constructor 9 | def initialize(node, options = {}) 10 | @node = node 11 | @options = options.merge({in_condition: true}) 12 | @has_elsif = false 13 | @content = "" 14 | @condition = "" 15 | @ternary_true_part = "" 16 | @ternary_false_part = "" 17 | end 18 | 19 | # Parse a (:if) process 20 | def parse 21 | if @node.children.length == 0 22 | return 23 | end 24 | 25 | # 'cause it's a frozen array 26 | children = @node.children.dup 27 | 28 | # Get the if statement 29 | @condition = parse_new_node(children.shift).to_s 30 | 31 | count_nodes = BeginParser.new(children.first, @options).count_nodes 32 | 33 | # Multiple instructions if statement 34 | if count_nodes > 1 35 | @condition = "if (" + @condition + ") {\n" 36 | has_begin = false # For elsif 37 | 38 | children.each_with_index do |child, index| 39 | if child.is_a? AST::Node 40 | # Elsif operator 41 | if child.type == :if && children.length > 1 && has_begin 42 | @has_elsif = true 43 | @content << "} else " 44 | @content << parse_new_node(child, @options).to_s 45 | # Else 46 | elsif index == children.length - 1 && children.length > 1 47 | @content << "\n} else {\n" 48 | @content << parse_new_node(child, @options).to_s 49 | else 50 | if child.type == :begin 51 | has_begin = true 52 | end 53 | 54 | @content << parse_new_node(child, @options).to_s 55 | end 56 | end 57 | end 58 | else # Ternary operator 59 | if children.length == 2 60 | @ternary_true_part << parse_new_node(children[0]).to_s 61 | @ternary_false_part << parse_new_node(children[1]).to_s 62 | end 63 | end 64 | end 65 | 66 | # Write function 67 | def to_s 68 | data = "" 69 | if @ternary_true_part.empty? 70 | data << @condition 71 | data << @content 72 | data << "\n}" if !@has_elsif 73 | else 74 | data << @condition << " ? " << @ternary_true_part << " : " << @ternary_false_part 75 | end 76 | data 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/class.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Parse a class declaration 4 | # 5 | # It builds all its options by giving them to its children. 6 | # The function to_s will build the final content 7 | ### 8 | class ClassParser < MasterParser 9 | def initialize(node) 10 | @node = node 11 | @options = { 12 | current_class: nil, 13 | instance_variables: {}, 14 | static_variables: {}, 15 | has_constructor: false 16 | } 17 | 18 | @inherit = "" 19 | @content = "" 20 | end 21 | 22 | ### 23 | # Process parsing 24 | ### 25 | def parse 26 | if @node.children.length == 0 27 | return 28 | end 29 | 30 | @node.children.each do |child| 31 | # Class metadata (name and inheritance) 32 | if child.is_a?(AST::Node) && child.type == :const 33 | child.children.each do |c| 34 | if c.is_a?(Symbol) 35 | if @options[:current_class] == nil 36 | @options[:current_class] = c.to_s 37 | else 38 | @inherit = c.to_s 39 | end 40 | end 41 | end 42 | elsif child.is_a?(AST::Node) 43 | @content << parse_new_node(child, @options).to_s 44 | end 45 | end 46 | end 47 | 48 | ### 49 | # Return string value of the parser 50 | ### 51 | def to_s 52 | data = "class " << @options[:current_class] 53 | if @inherit.length > 0 54 | data << " extends " << @inherit 55 | end 56 | data << " {\n" 57 | 58 | @options[:instance_variables].each do |k, v| 59 | data << "public var " << v.name 60 | 61 | if v.value 62 | data << " = " << v.value 63 | elsif v.type 64 | data << " : " << v.type 65 | else 66 | data << " : Dynamic" 67 | end 68 | 69 | data << ";\n" 70 | end 71 | 72 | @options[:static_variables].each do |k, v| 73 | data << "public static var " << v.name 74 | 75 | if v.value 76 | data << " = " << v.value 77 | elsif v.type 78 | data << " : " << v.type 79 | else 80 | data << " : Dynamic" 81 | end 82 | 83 | data << ";\n" 84 | end 85 | 86 | if !@options[:has_constructor] 87 | data << "public function new() {}\n" 88 | end 89 | 90 | data << @content 91 | data << "\n}" 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/master.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Entry point and class from which every other parser must inherit 4 | # 5 | # Call the right parser for the given node and returns the parser 6 | ### 7 | class MasterParser 8 | # Constructor 9 | def initialize 10 | end 11 | 12 | # Parse the given node 13 | def parse_new_node(node, options = {}) 14 | parser = nil 15 | 16 | case node.type 17 | # Any function call 18 | when :send 19 | parser = CallParser.new(node, options) 20 | 21 | # Function args 22 | when :args 23 | parser = ArgsParser.new(node) 24 | 25 | # Class declaration 26 | when :class 27 | parser = ClassParser.new(node) 28 | 29 | # Array parsing 30 | when :array 31 | parser = ArrayParser.new(node, options) 32 | 33 | # Regexp 34 | when :regexp 35 | parser = RegexpParser.new(node, options) 36 | 37 | # Assign var 38 | when :lvasgn, :ivasgn, :cvasgn 39 | parser = VarParser.new(node, options) 40 | 41 | when :lvar, :ivar, :cvar 42 | return unless node.children.length > 0 43 | name = node.children[0].to_s 44 | name = "this." << name[1..-1] if node.type == :ivar 45 | name = options[:current_class] + "." + name[2..-1] if node.type == :cvar 46 | 47 | return name 48 | 49 | # Function declaration 50 | when :def, :defs 51 | parser = FunctionParser.new(node, options) 52 | 53 | # Basic types 54 | when :str, :int, :float, :true, :false, :nil, :sym 55 | parser = BaseTypeParser.new(node, node.type) 56 | 57 | # Blocks 58 | when :begin 59 | parser = BeginParser.new(node, options) 60 | 61 | # x += z 62 | when :op_asgn 63 | parser = CombinedOperatorParser.new(node, options) 64 | 65 | # return .... 66 | when :return 67 | parser = ReturnParser.new(node, options) 68 | 69 | # String interpolation 70 | when :dstr, :dsym 71 | parser = StrConcatParser.new(node, options) 72 | 73 | # Executable string 74 | when :xstr 75 | parser = ExecStringParser.new(node, options) 76 | 77 | # Condition 78 | when :if 79 | parser = ConditionParser.new(node, options) 80 | 81 | # Else, error 82 | else 83 | raise "Unsupported type " + node.type.to_s 84 | end 85 | 86 | parser.parse 87 | parser 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/function.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Parse a function declaration 4 | # 5 | # It takes care of to do not have the same locale variable declaration 6 | # The function called "initialize" will be translated into "new" 7 | ### 8 | class FunctionParser < MasterParser 9 | attr_reader :visibility 10 | 11 | # Constructor 12 | def initialize(node, options = {}) 13 | @content = "" 14 | @node = node 15 | @is_static = false 16 | @args = "" 17 | @options = options 18 | @locale_options = @options.merge({ 19 | 'locale_variables': {}, 20 | 'in_function': true 21 | }) 22 | 23 | @visibility = options.has_key?(:visibility) ? options[:visibility] : "public" 24 | end 25 | 26 | # Parse a (:def) process 27 | def parse 28 | if @node.children.length == 0 29 | return 30 | end 31 | 32 | # 'cause it's a frozen array 33 | children = @node.children.dup 34 | 35 | @name = children.shift 36 | if @name.is_a?(AST::Node) 37 | if @name.type == :self 38 | @name = children.shift 39 | @is_static = true 40 | else 41 | raise "Unknown method type " << name.type.to_s 42 | end 43 | end 44 | 45 | # Transform 'initialize' into 'new' (haxe keyword) 46 | if @name == :initialize 47 | @options[:has_constructor] = true 48 | @name = :new 49 | end 50 | 51 | children.each do |child| 52 | is_node = child.is_a? AST::Node 53 | 54 | if is_node && child.type == :args 55 | @args = parse_new_node(child) 56 | @locale_options[:locale_variables] = @args.variables 57 | elsif is_node 58 | has_block = false 59 | if child.type == :begin 60 | has_block = true 61 | end 62 | 63 | if !has_block 64 | @content << "return " 65 | end 66 | 67 | @content << parse_new_node(child, @locale_options).to_s 68 | 69 | if !has_block 70 | @content << ";" unless @content[-1, 1] == ";" 71 | end 72 | end 73 | end 74 | end 75 | 76 | ### 77 | # Return string value of the parser 78 | ### 79 | def to_s 80 | if @is_static 81 | data = @visibility << " static function " << @name.to_s 82 | else 83 | data = @visibility << " function " << @name.to_s 84 | end 85 | 86 | data << "(" << @args.to_s << ") {\n" 87 | data << @content 88 | data << "\n}\n" 89 | 90 | data 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/ruhax/parsers/var.rb: -------------------------------------------------------------------------------- 1 | module Ruhax 2 | ### 3 | # Parse all kind of variable 4 | # 5 | # Registers in options : 6 | # - locale_variables 7 | # - instance_variables (@) 8 | # - static_variables (@@) 9 | ### 10 | class VarParser < MasterParser 11 | def initialize(node, options = {}) 12 | @options = options 13 | @node = node 14 | @content = "" 15 | 16 | @in_function = @options.has_key?(:in_function) ? @options[:in_function] : false 17 | @locale_variables = @options.has_key?(:locale_variables) ? @options[:locale_variables] : {} 18 | @instance_variables = @options.has_key?(:instance_variables) ? @options[:instance_variables] : {} 19 | @static_variables = @options.has_key?(:static_variables) ? @options[:static_variables] : {} 20 | @current_class = @options.has_key?(:current_class) ? @options[:current_class] : nil 21 | end 22 | 23 | ### 24 | # Process parsing 25 | ### 26 | def parse 27 | if @node.children.length == 0 28 | return 29 | end 30 | 31 | children = @node.children.dup 32 | var_name = children.shift.to_s 33 | 34 | current_var = nil 35 | case @node.type 36 | # locale variable 37 | when :lvasgn 38 | # Add 'var' keyword if it's a new var 39 | if !@locale_variables.has_key?(var_name.to_sym) 40 | @locale_variables[var_name.to_sym] = VarModel.new(var_name) 41 | @content << "var " 42 | end 43 | 44 | @content << var_name 45 | current_var = @locale_variables[var_name.to_sym] 46 | 47 | # instance variables 48 | when :ivasgn 49 | var_name = var_name[1.. var_name.length-1] 50 | if !@instance_variables.has_key?(var_name.to_sym) 51 | @instance_variables[var_name.to_sym] = VarModel.new(var_name) 52 | end 53 | 54 | if @in_function 55 | @content << "this." << var_name 56 | end 57 | current_var = @instance_variables[var_name.to_sym] 58 | 59 | # static variables 60 | when :cvasgn 61 | var_name = var_name[2.. var_name.length-1] 62 | if !@static_variables.has_key?(var_name.to_sym) 63 | @static_variables[var_name.to_sym] = VarModel.new(var_name) 64 | end 65 | 66 | if @current_class == nil 67 | raise "Class variable called in non class context" 68 | end 69 | 70 | if @in_function 71 | @content << @current_class << "." << var_name 72 | end 73 | current_var = @static_variables[var_name.to_sym] 74 | 75 | else 76 | @content << parse_new_node(node, @options).to_s 77 | end 78 | 79 | value = "" 80 | if children.length > 0 81 | @content << " = " if @in_function 82 | children.each {|child| value << parse_new_node(child, @options).to_s} 83 | end 84 | 85 | if @in_function && !@options[:no_value] 86 | @content << value << ";" 87 | elsif current_var && !@options[:no_value] 88 | current_var.value = value 89 | end 90 | end 91 | 92 | ### 93 | # Return string value of the parser 94 | ### 95 | def to_s 96 | @content 97 | end 98 | end 99 | end 100 | --------------------------------------------------------------------------------