riml 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 by Luke Gruber
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included
12
+ in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ [![Build Status](https://travis-ci.org/luke-gru/riml.png)](https://travis-ci.org/luke-gru/riml)
2
+
3
+ Riml, a relaxed version of Vimscript
4
+ ====================================
5
+
6
+ Riml aims to be a superset of VimL that includes some nice features that I
7
+ enjoy in other scripting languages, including classes, string interpolation,
8
+ heredocs, default case-sensitive string comparison and other things most
9
+ programmers take for granted. Also, Riml takes some liberties and provides
10
+ some syntactic sugar for lots of VimL constructs. To see how Riml constructs
11
+ are compiled into VimL, just take a look in this README. The left side is Riml,
12
+ and the right side is the equivalent VimL after compilation.
13
+
14
+ Variables
15
+ ---------
16
+
17
+ count = 1 let s:count = 1
18
+ while count < 5 while s:count < 5
19
+ source other.vim source other.vim
20
+ count += 1 let s:count += 1
21
+ end endwhile
22
+
23
+ If you don't specify a scope modifier, it's script local by default in the
24
+ global namespace. Within a function, variables without scope modifiers are plain
25
+ old local variables.
26
+
27
+ ###globally
28
+
29
+ a = 3 let s:a = 3
30
+
31
+ ###locally
32
+
33
+ a = 3 let a = 3
34
+
35
+ ###Freeing memory
36
+
37
+ a = nil unlet! a
38
+
39
+ ###Checking for existence
40
+
41
+ unless s:callcount? if !exists("s:callcount")
42
+ callcount = 0 let s:callcount = 0
43
+ end endif
44
+ callcount += 1 let s:callcount += 1
45
+ puts "called #{callcount} times" echo "called " . s:callcount . " times"
46
+
47
+ Comparisons
48
+ -----------
49
+
50
+ a = "hi" == "hi" if ("hi" ==# "hi")
51
+ let s:a = 1
52
+ else
53
+ let s:a = 0
54
+ endif
55
+
56
+ Heredocs
57
+ --------
58
+
59
+ msg = <<EOS let s:msg = "a vim heredoc!\n"
60
+ a vim heredoc!
61
+ EOS
62
+
63
+ Classes
64
+ -------
65
+
66
+ ###Riml example 1
67
+
68
+ class MyClass
69
+ def initialize(arg1, arg2, *args)
70
+ end
71
+
72
+ defm getData
73
+ return self.data
74
+ end
75
+
76
+ defm getOtherData
77
+ return self.otherData
78
+ end
79
+ end
80
+
81
+ ###Viml example 1
82
+
83
+
84
+ function! g:MyClassConstructor(arg1, arg2, ...)
85
+ let myClassObj = {}
86
+ function! myClassObj.getData() dict
87
+ return self.data
88
+ endfunction
89
+ function! myClassObj.getOtherData() dict
90
+ return self.otherData
91
+ endfunction
92
+ return myClassObj
93
+ endfunction
94
+
95
+ ###Riml example 2
96
+
97
+ class Translation
98
+ def initialize(input)
99
+ self.input = input
100
+ end
101
+ end
102
+
103
+ class FrenchToEnglishTranslation < Translation
104
+ defm translate
105
+ if (self.input == "Bonjour!")
106
+ echo "Hello!"
107
+ else
108
+ echo "Sorry, I don't know that word."
109
+ end
110
+ end
111
+ end
112
+
113
+ translation = new FrenchToEnglishTranslation("Bonjour!")
114
+ translation.translate()
115
+
116
+ ###VimL example 2
117
+
118
+ function! g:TranslationConstructor(input)
119
+ let translationObj = {}
120
+ let translationObj.input = a:input
121
+ return translationObj
122
+ endfunction
123
+
124
+ function! g:FrenchToEnglishTranslationConstructor(input)
125
+ let frenchToEnglishTranslationObj = {}
126
+ let translationObj = g:TranslationConstructor(a:input)
127
+ call extend(frenchToEnglishTranslationObj, translationObj)
128
+ function! frenchToEnglishTranslationObj.translate() dict
129
+ if (self.input ==# "Bonjour!")
130
+ echo "Hello!"
131
+ else
132
+ echo "Sorry, I don't know that word."
133
+ endif
134
+ endfunction
135
+ return frenchToEnglishTranslationObj
136
+ endfunction
137
+
138
+ let s:translation = g:FrenchToEnglishTranslationConstructor("Bonjour!")
139
+ call s:translation.translate()
140
+
141
+ Hacking
142
+ -------
143
+
144
+ Make sure to generate the parser before running tests or developing on Riml.
145
+ Also, make sure to regenerate the parser after modifiying the grammar file.
146
+
147
+ 1. `bundle install`
148
+ 2. Go to the lib directory and enter `racc -o parser.rb grammar.y`
data/bin/riml ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: syntax=ruby
3
+
4
+ require File.expand_path("../../config/environment", __FILE__)
5
+
6
+ module Riml
7
+ include Environment
8
+ require File.join(ROOTDIR, 'version')
9
+ require File.join(LIBDIR, "helper")
10
+
11
+ require 'optparse'
12
+ require 'ostruct'
13
+
14
+ class Options
15
+ def self.parse(argv)
16
+
17
+ # defaults
18
+ options = OpenStruct.new
19
+ options.compile = []
20
+ options.riml_source_path = Dir.getwd
21
+
22
+ OptionParser.new do |opts|
23
+ opts.banner = "Usage: riml [options]"
24
+ opts.separator ""
25
+ opts.separator "Specific options:"
26
+
27
+ opts.on("-c", "--compile FILE", "Compile riml file to VimL") do |file|
28
+ if File.exists?(file)
29
+ options.compile << file
30
+ else
31
+ warn "Couldn't find file #{file.inspect}"
32
+ end
33
+ end
34
+
35
+ opts.on("-s", "--stdio", "pipe in riml to STDIN and get back VimL on STDOUT") do
36
+ options.stdio = true
37
+ end
38
+
39
+ opts.on_tail("-v", "--version", "Show riml version") do
40
+ puts VERSION.join('.')
41
+ exit
42
+ end
43
+
44
+ opts.on_tail("-h", "--help", "Show this message") do
45
+ puts opts
46
+ exit
47
+ end
48
+ end.parse!(argv)
49
+
50
+ options
51
+ end
52
+ end
53
+
54
+ class Runner
55
+ class << self
56
+ def start
57
+ options = Options.parse(ARGV)
58
+ if options.stdio
59
+ puts Riml.compile($stdin.gets)
60
+ elsif options.compile.any?
61
+ options.compile.each do |file|
62
+ Riml.compile_file(file)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ Runner.start
69
+ end
@@ -0,0 +1,10 @@
1
+ module Riml
2
+ module Environment
3
+ ROOTDIR = File.expand_path('../../', __FILE__)
4
+
5
+ LIBDIR = File.join(ROOTDIR, "lib")
6
+ BINDIR = File.join(ROOTDIR, "bin")
7
+
8
+ $:.unshift(LIBDIR) unless $:.include? LIBDIR
9
+ end
10
+ end
@@ -0,0 +1,268 @@
1
+ require File.expand_path("../constants", __FILE__)
2
+ require File.expand_path("../class_map", __FILE__)
3
+ require File.expand_path("../walker", __FILE__)
4
+
5
+ module Riml
6
+ class AST_Rewriter
7
+ include Riml::Constants
8
+
9
+ attr_reader :ast
10
+ def initialize(ast)
11
+ @ast = ast
12
+ end
13
+
14
+ # Map of {"ClassName" => ClassDefinitionNode}
15
+ # Can also query object for superclass of a named class, etc...
16
+ #
17
+ # Ex : classes["SomeClass"].superclass_name => "SomeClassBase"
18
+ def classes
19
+ @@classes ||= ClassMap.new
20
+ end
21
+
22
+ def rewrite
23
+ establish_parents(ast)
24
+ StrictEqualsComparisonOperator.new(ast).rewrite_on_match
25
+ VarEqualsComparisonOperator.new(ast).rewrite_on_match
26
+ ClassDefinitionToFunctions.new(ast).rewrite_on_match
27
+ ObjectInstantiationToCall.new(ast).rewrite_on_match
28
+ CallToExplicitCall.new(ast).rewrite_on_match
29
+ ast
30
+ end
31
+
32
+ def establish_parents(node)
33
+ Walker.walk_node(node, method(:do_establish_parents))
34
+ end
35
+ alias reestablish_parents establish_parents
36
+
37
+ def do_establish_parents(node)
38
+ node.children.each do |child|
39
+ child.parent_node = node if child.respond_to?(:parent_node=)
40
+ end if node.respond_to?(:children)
41
+ end
42
+
43
+ def rewrite_on_match(node = ast)
44
+ Walker.walk_node(node, method(:do_rewrite_on_match), lambda {|_| repeatable?})
45
+ end
46
+
47
+ def do_rewrite_on_match(node)
48
+ replace node if match?(node)
49
+ end
50
+
51
+ def repeatable?
52
+ true
53
+ end
54
+
55
+ class StrictEqualsComparisonOperator < AST_Rewriter
56
+ def match?(node)
57
+ BinaryOperatorNode === node && node.operator == '==='
58
+ end
59
+
60
+ def replace(node)
61
+ node.operator = '=='
62
+ node.operand1 = ListNode.wrap(node.operand1)
63
+ node.operand2 = ListNode.wrap(node.operand2)
64
+ reestablish_parents(node)
65
+ end
66
+ end
67
+
68
+ class VarEqualsComparisonOperator < AST_Rewriter
69
+ COMPARISON_OPERATOR_MATCH = Regexp.union(COMPARISON_OPERATORS)
70
+
71
+ def match?(node)
72
+ Nodes === node &&
73
+ AssignNode === node.nodes[0] &&
74
+ BinaryOperatorNode === (op = node.nodes[0].rhs) &&
75
+ op.operator =~ COMPARISON_OPERATOR_MATCH
76
+ end
77
+
78
+ def replace(node)
79
+ binary_op = node.nodes[0].rhs
80
+ old_set_var = node.nodes[0]
81
+ assign_true = old_set_var.clone.tap {|assign_t| assign_t.rhs = TrueNode.new }
82
+ assign_false = old_set_var.clone.tap {|assign_f| assign_f.rhs = FalseNode.new }
83
+ node.nodes = [
84
+ IfNode.new(binary_op, Nodes.new([
85
+ assign_true, ElseNode.new(Nodes.new([
86
+ assign_false
87
+ ]))
88
+ ]))
89
+ ]
90
+ reestablish_parents(node)
91
+ end
92
+ end
93
+
94
+ class ClassDefinitionToFunctions < AST_Rewriter
95
+ def match?(node)
96
+ ClassDefinitionNode === node
97
+ end
98
+
99
+ def replace(node)
100
+ classes[node.name] = node
101
+
102
+ name, expressions = node.name, node.expressions
103
+ InsertInitializeMethod.new(node).rewrite_on_match
104
+ constructor = node.constructor
105
+ constructor.scope_modifier = 'g:' unless constructor.scope_modifier
106
+ constructor.name = node.constructor_name
107
+ # set up dictionary variable at top of function
108
+ dict_name = node.constructor_obj_name
109
+ constructor.expressions.unshift(
110
+ AssignNode.new('=', GetVariableNode.new(nil, dict_name), DictionaryNode.new({}))
111
+ )
112
+
113
+ SuperToObjectExtension.new(constructor, node).rewrite_on_match
114
+ MethodToNestedFunction.new(node, constructor, dict_name).rewrite_on_match
115
+ SelfToDictName.new(dict_name).rewrite_on_match(constructor)
116
+
117
+ constructor.expressions.push(
118
+ ReturnNode.new(GetVariableNode.new(nil, dict_name))
119
+ )
120
+ reestablish_parents(constructor)
121
+ end
122
+
123
+ class MethodToNestedFunction < AST_Rewriter
124
+ attr_reader :constructor, :dict_name
125
+ def initialize(class_node, constructor, dict_name)
126
+ super(class_node)
127
+ @dict_name, @constructor = dict_name, constructor
128
+ end
129
+
130
+ def match?(node)
131
+ DefMethodNode === node
132
+ end
133
+
134
+ def replace(node)
135
+ def_node = node.to_def_node
136
+ node.parent_node = ast.expressions
137
+ node.remove
138
+ def_node.name.insert(0, "#{dict_name}.")
139
+ def_node.parent_node = constructor.expressions
140
+ constructor.expressions << def_node
141
+ reestablish_parents(node)
142
+ end
143
+ end
144
+
145
+ class SelfToDictName < AST_Rewriter
146
+ attr_reader :dict_name
147
+ def initialize(dict_name)
148
+ @dict_name = dict_name
149
+ end
150
+
151
+ def match?(node)
152
+ AssignNode === node && DictGetNode === node.lhs && node.lhs.dict.name == "self"
153
+ end
154
+
155
+ def replace(node)
156
+ node.lhs.dict.name = dict_name
157
+ end
158
+ end
159
+
160
+ class InsertInitializeMethod < AST_Rewriter
161
+ # if doesn't have an initialize method, put one at the beginning
162
+ # of the class definition
163
+ def match?(class_node)
164
+ ClassDefinitionNode === class_node && class_node.constructor.nil?
165
+ end
166
+
167
+ def replace(class_node)
168
+ if class_node.superclass?
169
+ def_node = DefNode.new(
170
+ '!', nil, "initialize", superclass_params, nil, Nodes.new([SuperNode.new([], false)])
171
+ )
172
+ else
173
+ def_node = DefNode.new(
174
+ '!', nil, "initialize", [], nil, Nodes.new([])
175
+ )
176
+ end
177
+ class_node.expressions.unshift(def_node)
178
+ reestablish_parents(class_node)
179
+ end
180
+
181
+ def superclass_params
182
+ classes.superclass(ast.name).constructor.parameters
183
+ end
184
+
185
+ def repeatable?
186
+ false
187
+ end
188
+ end
189
+
190
+ class SuperToObjectExtension < AST_Rewriter
191
+ attr_reader :class_node
192
+ def initialize(constructor, class_node)
193
+ super(constructor)
194
+ @class_node = class_node
195
+ end
196
+
197
+ def match?(constructor)
198
+ DefNode === constructor && constructor.super_node
199
+ end
200
+
201
+ def replace(constructor)
202
+ superclass = classes.superclass(class_node.name)
203
+ super_constructor = superclass.constructor
204
+
205
+ set_var_node = AssignNode.new('=', GetVariableNode.new(nil, superclass.constructor_obj_name),
206
+ CallNode.new(
207
+ super_constructor.scope_modifier,
208
+ super_constructor.name,
209
+ super_arguments(constructor.super_node)
210
+ )
211
+ )
212
+
213
+ constructor.super_node.replace_with(set_var_node)
214
+ constructor.expressions.insert_after(set_var_node,
215
+ ExplicitCallNode.new(
216
+ nil,
217
+ "extend",
218
+ [
219
+ GetVariableNode.new(nil, class_node.constructor_obj_name),
220
+ GetVariableNode.new(nil, superclass.constructor_obj_name)
221
+ ]
222
+ )
223
+ )
224
+ reestablish_parents(constructor)
225
+ end
226
+
227
+ def super_arguments(super_node)
228
+ if super_node.use_all_arguments?
229
+ # here, ast is 'constructor'
230
+ ast.parameters.map {|p| GetVariableNode.new(nil, p)}
231
+ else
232
+ super_node.arguments
233
+ end
234
+ end
235
+
236
+ def repeatable?
237
+ false
238
+ end
239
+ end
240
+ end # ClassDefinitionToFunctions
241
+
242
+ class ObjectInstantiationToCall < AST_Rewriter
243
+ def match?(node)
244
+ ObjectInstantiationNode === node
245
+ end
246
+
247
+ def replace(node)
248
+ constructor_name = node.call_node.name
249
+ class_node = classes[constructor_name]
250
+ call_node = node.call_node
251
+ call_node.name = class_node.constructor_name
252
+ call_node.scope_modifier = class_node.constructor.scope_modifier
253
+ end
254
+ end
255
+
256
+ class CallToExplicitCall < AST_Rewriter
257
+ def match?(node)
258
+ node.instance_of?(CallNode) && node.must_be_explicit_call?
259
+ end
260
+
261
+ def replace(node)
262
+ node.replace_with(ExplicitCallNode.new(node[0], node[1], node[2]))
263
+ reestablish_parents(node)
264
+ end
265
+ end
266
+
267
+ end
268
+ end
data/lib/class_map.rb ADDED
@@ -0,0 +1,46 @@
1
+ module Riml
2
+ class ClassNotFound < NameError; end
3
+
4
+ class ClassMap
5
+ def initialize
6
+ @map = {}
7
+ end
8
+
9
+ def [](key)
10
+ ensure_key_is_string!(key)
11
+ @map[key]
12
+ end
13
+
14
+ def []=(key, val)
15
+ ensure_key_is_string!(key)
16
+ @map[key] = val
17
+ end
18
+
19
+ def superclass(key)
20
+ ensure_key_is_string!(key)
21
+ super_key = @map[key].superclass_name
22
+ raise ClassNotFound.new(super_key) unless @map[super_key]
23
+ @map[super_key]
24
+ end
25
+
26
+ def classes
27
+ @map.values
28
+ end
29
+
30
+ def class_names
31
+ @map.keys
32
+ end
33
+
34
+ def clear
35
+ @map.clear
36
+ end
37
+
38
+ protected
39
+ def ensure_key_is_string!(key)
40
+ unless key.is_a?(String)
41
+ raise ArgumentError, "key must be name of class (String)"
42
+ end
43
+ end
44
+
45
+ end
46
+ end