riml 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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