sith 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a1f92e5a162b84d586147fa800b6c8ef2b76c1d5
4
+ data.tar.gz: faa395bc3debbeb5caf891a6fdf6150fd704682d
5
+ SHA512:
6
+ metadata.gz: 03f0c6392a6bd10d7a034f664e112f07597314b9c5b99958632cdcb97f4f82027d40efba8028bc0b7e675df620834a1d63683bfb9dc317e05cbdc05af1f4b4b5
7
+ data.tar.gz: b59a612d590c693abdb8ea0b7f1f514e19fd05e4f2835aa373d2ade8458e4e57f3ed156cde6c4e46de99cdddbe5621994b3b45d4407c86bce57a2472ef4d1cc9
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'parser'
4
+ gem 'unparser'
5
+
6
+ group :development, :test do
7
+ gem 'rspec'
8
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Alexander Ivanov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # sith
2
+
3
+ sith is a macro preprocessor for Ruby
4
+
5
+ [![Build Status](https://travis-ci.org/alehander42/sith.svg)](https://travis-ci.org/alehander42/sith)
6
+
7
+ Still a prototype.
8
+
9
+ Example:
10
+
11
+ a macro definitions file:
12
+
13
+ ```ruby
14
+ macro_mapper attr_reader(label, delimiter: "\n")
15
+ def ~{label}
16
+ @~{label}
17
+ end
18
+ end
19
+
20
+ macro_mapper attr_writer(label, delimiter: "\n")
21
+ def ~{label}=(value)
22
+ @~{label} = value
23
+ end
24
+ end
25
+
26
+ macro attr_accessor(*labels)
27
+ attr_reader ~{labels}
28
+ attr_writer ~{labels}
29
+ end
30
+ ```
31
+
32
+ a ruby file
33
+ ```
34
+ class A
35
+ attr_accessor a
36
+ end
37
+ ```
38
+
39
+ ```zsh
40
+ sith ruby_file.rb macro_definitions.rb > output.rb`
41
+ ```
42
+
43
+ output.rb
44
+
45
+ ```ruby
46
+ class A
47
+ def a
48
+ @a
49
+ end
50
+ def a=(value)
51
+ @a = value
52
+ end
53
+ end
54
+ ```
55
+ # install
56
+
57
+ `gem install sith`
58
+
59
+ # thanks
60
+
61
+ built on top of [parser](https://github.com/whitequark/parser) and [unparser](https://github.com/mbj/unparser) gems.
62
+
63
+ # similar to
64
+
65
+ [rubymacros](https://github.com/coatl/rubymacros/)
66
+
67
+ however, the macros in `sith` are defined using a ruby-like template notation, not a lisp-like ast notation.
68
+
69
+ # built by
70
+
71
+ [Alexander Ivanov](http://alehander42.me)
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'rspec/core'
15
+ require 'rspec/core/rake_task'
16
+ RSpec::Core::RakeTask.new(:spec) do |spec|
17
+ spec.pattern = FileList['spec/**/*_spec.rb']
18
+ end
19
+
20
+ task :default => :spec
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ require 'sith'
6
+
7
+ ruby_file, filename = ARGV.first, ARGV[1]
8
+
9
+ ruby_source = File.read(ruby_file)
10
+ macro_source = File.read(filename)
11
+
12
+ expander = Sith::MacroExpander.new(Sith::load_macros(macro_source))
13
+ puts expander.expand_to_source(ruby_source)
14
+
15
+
@@ -0,0 +1,17 @@
1
+ macro_mapper attr_reader(label, delimiter: "\n")
2
+ def ~{label}
3
+ @~{label}
4
+ end
5
+ end
6
+
7
+ macro_mapper attr_writer(label, delimiter: "\n")
8
+ def ~{label}=(value)
9
+ @~{label} = value
10
+ end
11
+ end
12
+
13
+ macro attr_accessor(*labels)
14
+ attr_reader ~{labels}
15
+ attr_writer ~{labels}
16
+ end
17
+
@@ -0,0 +1,5 @@
1
+ class A
2
+ attr_reader a
3
+ attr_writer b
4
+ attr_accessor c
5
+ end
@@ -0,0 +1,3 @@
1
+ def a
2
+ simple 2, 4
3
+ end
@@ -0,0 +1,3 @@
1
+ macro simple(a, b)
2
+ ~{a} + ~{b}
3
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'sith/macro'
2
+ require_relative 'sith/loader'
3
+ require_relative 'sith/macro_expander'
@@ -0,0 +1,39 @@
1
+ module Sith
2
+ def self.load_macros(macro_source)
3
+ lines = macro_source.split("\n")
4
+ macros = {}
5
+ i = 0
6
+ while i < lines.length
7
+ line = lines[i]
8
+ if line.lstrip.start_with? 'macro_mapper'
9
+ offset = line.length - line.lstrip.length
10
+ a = line.index('(')
11
+ label = line[offset + 12...a].strip.to_sym
12
+ args = line[a + 1..-1].rstrip[0...-1].split(/[, \"]/)
13
+ arg = args[0].to_sym
14
+ delimiter = args.length >= 2 ? args[-1] : "\n"
15
+ end_index = lines[i..-1].find_index { |l| (l[offset..-1] || '').start_with? 'end' }
16
+ body = lines[i + 1...i + end_index].join("\n")
17
+ i = i + end_index + 1
18
+ macros[label] = MacroMapper.new(arg, delimiter, body)
19
+ elsif line.lstrip.start_with? 'macro'
20
+ offset = line.length - line.lstrip.length
21
+ a = line.index('(')
22
+ label = line[offset + 5...a].strip.to_sym
23
+ args = line[a + 1..-1].rstrip[0...-1].split(',').map(&:strip)
24
+ if args.length == 1 && args[0][0] == '*'
25
+ stararg, args = true, [args[0][1..-1].to_sym]
26
+ else
27
+ stararg, args = false, args.map(&:to_sym)
28
+ end
29
+ end_index = lines[i..-1].find_index { |l| (l[offset..-1] || '').start_with? 'end' }
30
+ body = lines[i + 1...i + end_index].join("\n")
31
+ i = i + end_index + 1
32
+ macros[label] = Macro.new(args, stararg, body)
33
+ else
34
+ i += 1
35
+ end
36
+ end
37
+ macros
38
+ end
39
+ end
@@ -0,0 +1,66 @@
1
+ require 'parser/current'
2
+
3
+ module Sith
4
+ class BaseMacro
5
+ def represent(node)
6
+ return node.to_s unless node.is_a?(Parser::AST::Node) || node.is_a?(Array)
7
+
8
+ if node.is_a?(Array)
9
+ "#{node.map(&method(:represent)).join(', ')}"
10
+ elsif node.type == :int
11
+ node.children[0].to_s
12
+ elsif node.type == :string
13
+ node.children[0]
14
+ elsif :send
15
+ node.children[1].to_s
16
+ else
17
+ '?'
18
+ end
19
+ end
20
+
21
+ def expand_macro(nodes)
22
+ a = expand_to_source(nodes)
23
+
24
+ Parser::CurrentRuby.parse a
25
+ end
26
+ end
27
+
28
+ class Macro < BaseMacro
29
+ attr_reader :labels, :stararg, :template
30
+
31
+ def initialize(labels, stararg=false, template='')
32
+ @stararg = stararg
33
+ @labels = labels
34
+ @template = template
35
+ end
36
+
37
+ def expand_to_source(nodes)
38
+ if @stararg
39
+ substitutions = {@labels[0] => represent(nodes)}
40
+ else
41
+ representations = nodes.map { |node| represent node }
42
+ substitutions = Hash[@labels.zip(representations)]
43
+ end
44
+ @template.gsub(/~\{(\w+)\}/) do |label|
45
+
46
+
47
+ substitutions[Regexp.last_match(1).to_sym]
48
+ end
49
+ end
50
+ end
51
+
52
+ class MacroMapper < BaseMacro
53
+ attr_reader :label, :delimiter, :body
54
+
55
+ def initialize(label, delimiter="\n", body='')
56
+ @label = label
57
+ @delimiter = delimiter
58
+ @body = body
59
+ end
60
+
61
+ def expand_to_source(nodes)
62
+ macros = nodes.map { |n| Macro.new([label], false, @body)}
63
+ macros.map { |m| m.expand_to_source(nodes) }.join(delimiter)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,30 @@
1
+ require 'parser/current'
2
+ require 'unparser'
3
+
4
+ module Sith
5
+ class MacroExpander
6
+ def initialize(macros)
7
+ @macros = macros
8
+ end
9
+
10
+ def expand(source)
11
+ ast = Parser::CurrentRuby.parse(source)
12
+ expand_node ast
13
+ end
14
+
15
+ def expand_to_source(source)
16
+ Unparser.unparse(expand(source))
17
+ end
18
+
19
+ def expand_node(node)
20
+ return node unless node.is_a?(Parser::AST::Node)
21
+
22
+ if node.type == :send && @macros.key?(node.children[1])
23
+ node = @macros[node.children[1]].expand_macro(node.children[2..-1])
24
+ end
25
+ children = node.children.map(&method(:expand_node))
26
+ Parser::AST::Node.new node.type, children
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'sith'
6
+ s.version = '0.1.0'
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Alexander Ivanov"]
9
+ s.email = ["alehander42@gmail.com"]
10
+ s.homepage = 'https://github.com/alehander42/sith'
11
+ s.summary = %q{A macro preprocessor for Ruby}
12
+ s.description = %q{A macro preprocessor for Ruby with ruby-like template notation}
13
+
14
+ s.add_development_dependency 'rspec', '~> 0'
15
+ s.add_runtime_dependency('parser', '~> 2.2')
16
+ s.add_runtime_dependency('unparser', '~> 0.2')
17
+
18
+ s.license = 'MIT'
19
+ s.executables = ["sith"]
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,75 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'parser'
5
+ require 'sith'
6
+
7
+ module Sith
8
+ describe Sith do
9
+ it 'can load macros from your macros definitions' do
10
+ source = <<-RUBY
11
+ macro simple(a, b)
12
+ ~{a} + ~{b}
13
+ end
14
+ RUBY
15
+
16
+ macros = Sith::load_macros(source)
17
+ macro = macros[:simple]
18
+ expect(macro).to be_a Macro
19
+ expect(macro.labels[0]).to be_a Symbol
20
+ expect(macro.labels[0]).to eq :a
21
+ expect(macro.template.strip).to eq '~{a} + ~{b}'
22
+ end
23
+
24
+ it 'can load macro mappers from your macros definitions' do
25
+ source = <<-RUBY
26
+ macro_mapper attr_reader(attr, delimiter: ";")
27
+ def ~{attr}
28
+ @~{attr}
29
+ end
30
+ end
31
+ RUBY
32
+
33
+ macros = Sith::load_macros(source)
34
+ macro = macros[:attr_reader]
35
+ expect(macro).to be_a MacroMapper
36
+ expect(macro.label).to eq :attr
37
+ expect(macro.body.strip).to eq "def ~{attr}\n @~{attr}\n end"
38
+ expect(macro.delimiter).to eq ";"
39
+ end
40
+
41
+ describe 'MacroExpander' do
42
+ it 'can expand macros' do
43
+ macros = {simple: Macro.new([:a, :b],
44
+ false,
45
+ '~{a} + ~{b}')}
46
+ source = <<-RUBY
47
+ a = x + 4
48
+ simple(2, 4)
49
+ RUBY
50
+ expanded = MacroExpander.new(macros).expand(source)
51
+ expect(expanded.children[1].type).to eq :send
52
+ expect(expanded.children[1].children[2].type).to eq :int
53
+ end
54
+
55
+ it 'can expand macro mappers' do
56
+ macros = {attr_reader: MacroMapper.new(:attr,
57
+ "\n",
58
+ "def ~{attr};@~{attr};end")}
59
+ source = <<-RUBY
60
+ class A
61
+ attr_reader a
62
+ end
63
+ RUBY
64
+ expanded = MacroExpander.new(macros).expand(source)
65
+ expect(expanded.children[2].type).to eq :def
66
+ expect(expanded.children[2].children[2].type).to eq :ivar
67
+ expect(expanded.children[2].children[2].children[0]).to eq :@a
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+
75
+
File without changes
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sith
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Ivanov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: unparser
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.2'
55
+ description: A macro preprocessor for Ruby with ruby-like template notation
56
+ email:
57
+ - alehander42@gmail.com
58
+ executables:
59
+ - sith
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - Gemfile
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - bin/sith
68
+ - examples/attributes.rb
69
+ - examples/example0.rb
70
+ - examples/example1.rb
71
+ - examples/simple.rb
72
+ - lib/sith.rb
73
+ - lib/sith/loader.rb
74
+ - lib/sith/macro.rb
75
+ - lib/sith/macro_expander.rb
76
+ - sith.gemspec
77
+ - spec/sith_spec.rb
78
+ - spec/spec_helper.rb
79
+ homepage: https://github.com/alehander42/sith
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.4.6
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: A macro preprocessor for Ruby
103
+ test_files:
104
+ - spec/sith_spec.rb
105
+ - spec/spec_helper.rb