riml 0.3.6 → 0.3.7
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/Rakefile +4 -0
- data/bin/riml +12 -2
- data/lib/riml.rb +16 -10
- data/lib/riml/ast_rewriter.rb +46 -39
- data/lib/riml/backtrace_filter.rb +1 -1
- data/lib/riml/compiler.rb +21 -18
- data/lib/riml/constants.rb +5 -1
- data/lib/riml/errors.rb +49 -4
- data/lib/riml/file_rollback.rb +4 -2
- data/lib/riml/grammar.y +16 -11
- data/lib/riml/imported_class.rb +2 -2
- data/lib/riml/lexer.rb +116 -114
- data/lib/riml/nodes.rb +27 -12
- data/lib/riml/parser.rb +753 -723
- data/lib/riml/path_cache.rb +4 -0
- data/lib/riml/repl.rb +1 -1
- data/lib/riml/warning_buffer.rb +2 -0
- data/version.rb +2 -2
- metadata +10 -10
data/Rakefile
CHANGED
data/bin/riml
CHANGED
@@ -20,6 +20,7 @@ module Riml
|
|
20
20
|
options.check_syntax_files = []
|
21
21
|
options.repl = false
|
22
22
|
options.vi_readline = false
|
23
|
+
options.debug = false
|
23
24
|
options.allow_undefined_global_classes = DEFAULT_PARSE_OPTIONS[:allow_undefined_global_classes]
|
24
25
|
options.include_reordering = DEFAULT_PARSE_OPTIONS[:include_reordering]
|
25
26
|
options.readable = DEFAULT_COMPILE_OPTIONS[:readable]
|
@@ -82,6 +83,10 @@ module Riml
|
|
82
83
|
options.vi_readline = options.repl = true
|
83
84
|
end
|
84
85
|
|
86
|
+
opts.on("-D", "--debug", "Run in debug mode. Full stacktraces are shown on error.") do
|
87
|
+
options.debug = true
|
88
|
+
end
|
89
|
+
|
85
90
|
opts.on_tail("-v", "--version", "Show riml version.") do
|
86
91
|
puts VERSION.join('.')
|
87
92
|
exit
|
@@ -130,11 +135,12 @@ module Riml
|
|
130
135
|
compile_files_options = compile_options.merge(
|
131
136
|
:output_dir => options.output_dir
|
132
137
|
)
|
138
|
+
Riml.debug = options.debug
|
133
139
|
if options.stdio
|
134
140
|
puts Riml.compile($stdin.read, compile_options)
|
135
141
|
elsif options.compile_files.any?
|
136
142
|
FileRollback.trap(:INT, :QUIT, :KILL) { print("\n"); exit 1 }
|
137
|
-
Riml.compile_files(*options.compile_files
|
143
|
+
Riml.compile_files(*(options.compile_files + [compile_files_options]))
|
138
144
|
elsif options.check_syntax_files.any?
|
139
145
|
files = options.check_syntax_files.uniq
|
140
146
|
Riml.check_syntax_files(*files)
|
@@ -153,6 +159,10 @@ module Riml
|
|
153
159
|
begin
|
154
160
|
Runner.start
|
155
161
|
rescue RimlError => e
|
156
|
-
|
162
|
+
if Riml.debug
|
163
|
+
raise
|
164
|
+
else
|
165
|
+
abort e.verbose_message
|
166
|
+
end
|
157
167
|
end
|
158
168
|
end
|
data/lib/riml.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
|
+
if RUBY_VERSION < '1.9'
|
5
|
+
require 'thread'
|
6
|
+
end
|
7
|
+
|
4
8
|
require File.expand_path('../riml/environment', __FILE__)
|
5
|
-
require 'riml/nodes'
|
6
9
|
require 'riml/lexer'
|
10
|
+
require 'riml/nodes'
|
7
11
|
require 'riml/parser'
|
8
12
|
require 'riml/compiler'
|
9
13
|
require 'riml/warning_buffer'
|
@@ -43,8 +47,8 @@ module Riml
|
|
43
47
|
end
|
44
48
|
|
45
49
|
def self.compile(input, options = {})
|
46
|
-
parse_options = options.select(&EXTRACT_PARSE_OPTIONS)
|
47
|
-
compile_options = options.select(&EXTRACT_COMPILE_OPTIONS)
|
50
|
+
parse_options = Hash[options.select(&EXTRACT_PARSE_OPTIONS)]
|
51
|
+
compile_options = Hash[options.select(&EXTRACT_COMPILE_OPTIONS)]
|
48
52
|
parser = Parser.new
|
49
53
|
parser.options = DEFAULT_PARSE_OPTIONS.merge(parse_options)
|
50
54
|
compiler = Compiler.new
|
@@ -98,8 +102,8 @@ module Riml
|
|
98
102
|
# options
|
99
103
|
if filenames.last.is_a?(Hash)
|
100
104
|
options = filenames.pop
|
101
|
-
compile_options = options.select(&EXTRACT_COMPILE_FILES_OPTIONS)
|
102
|
-
parse_options = options.select(&EXTRACT_PARSE_OPTIONS)
|
105
|
+
compile_options = Hash[options.select(&EXTRACT_COMPILE_FILES_OPTIONS)]
|
106
|
+
parse_options = Hash[options.select(&EXTRACT_PARSE_OPTIONS)]
|
103
107
|
compiler.options = DEFAULT_COMPILE_FILES_OPTIONS.merge(compile_options)
|
104
108
|
parser.options = DEFAULT_PARSE_OPTIONS.merge(parse_options)
|
105
109
|
else
|
@@ -195,6 +199,7 @@ module Riml
|
|
195
199
|
@include_cache.clear
|
196
200
|
@path_cache.clear
|
197
201
|
@rewritten_ast_cache.clear
|
202
|
+
Parser.ast_cache.clear
|
198
203
|
end
|
199
204
|
|
200
205
|
# if error is thrown, all files that were created will be rolled back
|
@@ -205,11 +210,11 @@ module Riml
|
|
205
210
|
FileRollback.guard(&block)
|
206
211
|
end
|
207
212
|
|
208
|
-
# turn warnings on/off
|
209
213
|
class << self
|
210
|
-
attr_accessor :warnings
|
214
|
+
attr_accessor :warnings, :debug
|
211
215
|
end
|
212
216
|
self.warnings = true
|
217
|
+
self.debug = false
|
213
218
|
|
214
219
|
private
|
215
220
|
|
@@ -232,7 +237,7 @@ module Riml
|
|
232
237
|
return instance_variable_set("@#{name}", nil) if path.nil?
|
233
238
|
path = path.split(':') if path.is_a?(String)
|
234
239
|
path.each do |dir|
|
235
|
-
unless
|
240
|
+
unless File.directory?(dir)
|
236
241
|
raise UserArgumentError, "Error trying to set #{name.to_s}. " \
|
237
242
|
"Directory #{dir.inspect} doesn't exist"
|
238
243
|
end
|
@@ -283,7 +288,7 @@ module Riml
|
|
283
288
|
else
|
284
289
|
# absolute path for filename sent from cmdline or from riml_sourced files,
|
285
290
|
# output to that same directory if no --output-dir option is set
|
286
|
-
if fname[0] == File::SEPARATOR && !compiler.output_dir
|
291
|
+
if fname[0, 1] == File::SEPARATOR && !compiler.output_dir
|
287
292
|
Pathname.new(fname).parent.to_s
|
288
293
|
# relative path
|
289
294
|
else
|
@@ -304,4 +309,5 @@ module Riml
|
|
304
309
|
end
|
305
310
|
end
|
306
311
|
|
307
|
-
|
312
|
+
# ruby-1.8.7 guard condition
|
313
|
+
end unless defined?(Riml) && defined?(Riml::DEFAULT_COMPILE_OPTIONS)
|
data/lib/riml/ast_rewriter.rb
CHANGED
@@ -9,7 +9,7 @@ module Riml
|
|
9
9
|
include Riml::Constants
|
10
10
|
|
11
11
|
attr_accessor :ast, :options
|
12
|
-
attr_reader :classes
|
12
|
+
attr_reader :classes
|
13
13
|
|
14
14
|
def initialize(ast = nil, classes = nil, class_dependency_graph = nil)
|
15
15
|
@ast = ast
|
@@ -31,16 +31,15 @@ module Riml
|
|
31
31
|
|
32
32
|
def rewrite(filename = nil, included = false)
|
33
33
|
if filename && (rewritten_ast = Riml.rewritten_ast_cache[filename])
|
34
|
-
rewrite_included_and_sourced_files!(filename)
|
35
34
|
return rewritten_ast
|
36
35
|
end
|
36
|
+
|
37
37
|
establish_parents(ast)
|
38
38
|
if @options && @options[:allow_undefined_global_classes] && !@classes.has_global_import?
|
39
39
|
@classes.globbed_imports.unshift(ImportedClass.new('*'))
|
40
|
-
else
|
41
|
-
class_imports = RegisterImportedClasses.new(ast, classes)
|
42
|
-
class_imports.rewrite_on_match
|
43
40
|
end
|
41
|
+
class_imports = RegisterImportedClasses.new(ast, classes)
|
42
|
+
class_imports.rewrite_on_match
|
44
43
|
if resolve_class_dependencies?
|
45
44
|
resolve_class_dependencies!(filename)
|
46
45
|
return if @resolving_class_dependencies == true
|
@@ -67,6 +66,25 @@ module Riml
|
|
67
66
|
ast
|
68
67
|
end
|
69
68
|
|
69
|
+
def establish_parents(node)
|
70
|
+
Walker.walk_node(node, method(:do_establish_parents))
|
71
|
+
end
|
72
|
+
alias reestablish_parents establish_parents
|
73
|
+
|
74
|
+
def do_establish_parents(node)
|
75
|
+
node.children.each do |child|
|
76
|
+
child.parent_node = node if child.respond_to?(:parent_node=)
|
77
|
+
end if node.respond_to?(:children)
|
78
|
+
end
|
79
|
+
|
80
|
+
def rewrite_on_match(node = ast)
|
81
|
+
Walker.walk_node(node, method(:do_rewrite_on_match), max_recursion_lvl)
|
82
|
+
end
|
83
|
+
|
84
|
+
def do_rewrite_on_match(node)
|
85
|
+
replace node if match?(node)
|
86
|
+
end
|
87
|
+
|
70
88
|
def resolve_class_dependencies?
|
71
89
|
@resolving_class_dependencies = false if @options[:include_reordering] != true
|
72
90
|
@resolving_class_dependencies != false
|
@@ -85,9 +103,11 @@ module Riml
|
|
85
103
|
if filename && @included_and_sourced_file_refs[file].include?(filename)
|
86
104
|
msg = "#{filename.inspect} can't include #{file.inspect}, as " \
|
87
105
|
" #{file.inspect} already included #{filename.inspect}"
|
88
|
-
|
106
|
+
error = IncludeFileLoop.new(msg, node)
|
107
|
+
raise error
|
89
108
|
elsif filename == file
|
90
|
-
|
109
|
+
error = UserArgumentError.new("#{file.inspect} can't include itself", node)
|
110
|
+
raise error
|
91
111
|
end
|
92
112
|
@included_and_sourced_file_refs[filename] << file
|
93
113
|
riml_src = File.read(fullpath)
|
@@ -167,7 +187,7 @@ module Riml
|
|
167
187
|
|
168
188
|
def class_name_full_name(class_name)
|
169
189
|
return nil if class_name.nil?
|
170
|
-
if class_name[1] == ':'
|
190
|
+
if class_name[1..1] == ':'
|
171
191
|
class_name
|
172
192
|
else
|
173
193
|
ClassDefinitionNode::DEFAULT_SCOPE_MODIFIER + class_name
|
@@ -175,25 +195,6 @@ module Riml
|
|
175
195
|
end
|
176
196
|
end
|
177
197
|
|
178
|
-
def establish_parents(node)
|
179
|
-
Walker.walk_node(node, method(:do_establish_parents))
|
180
|
-
end
|
181
|
-
alias reestablish_parents establish_parents
|
182
|
-
|
183
|
-
def do_establish_parents(node)
|
184
|
-
node.children.each do |child|
|
185
|
-
child.parent_node = node if child.respond_to?(:parent_node=)
|
186
|
-
end if node.respond_to?(:children)
|
187
|
-
end
|
188
|
-
|
189
|
-
def rewrite_on_match(node = ast)
|
190
|
-
Walker.walk_node(node, method(:do_rewrite_on_match), max_recursion_lvl)
|
191
|
-
end
|
192
|
-
|
193
|
-
def do_rewrite_on_match(node)
|
194
|
-
replace node if match?(node)
|
195
|
-
end
|
196
|
-
|
197
198
|
# We need to rewrite the included/sourced files before anything else. This is in
|
198
199
|
# order to keep track of any classes defined in the included and sourced files (and
|
199
200
|
# files included/sourced in those, etc...). We keep a cache of rewritten asts
|
@@ -210,9 +211,11 @@ module Riml
|
|
210
211
|
msg = "#{filename.inspect} can't #{action} #{file.inspect}, as " \
|
211
212
|
" #{file.inspect} already included/sourced #{filename.inspect}"
|
212
213
|
# IncludeFileLoop/SourceFileLoop
|
213
|
-
|
214
|
+
error = Riml.const_get("#{action.capitalize}FileLoop").new(msg, node)
|
215
|
+
raise error
|
214
216
|
elsif filename == file
|
215
|
-
|
217
|
+
error = UserArgumentError.new("#{file.inspect} can't #{action} itself", node)
|
218
|
+
raise error
|
216
219
|
end
|
217
220
|
@included_and_sourced_file_refs[filename] << file
|
218
221
|
# recursively parse included files with this ast_rewriter in order
|
@@ -697,9 +700,9 @@ module Riml
|
|
697
700
|
]
|
698
701
|
),
|
699
702
|
StringNode.new("_s:#{def_node.name}", :s)
|
700
|
-
]
|
703
|
+
]
|
701
704
|
)
|
702
|
-
]
|
705
|
+
]
|
703
706
|
)
|
704
707
|
)
|
705
708
|
constructor.expressions << extension
|
@@ -779,9 +782,10 @@ module Riml
|
|
779
782
|
|
780
783
|
def replace(constructor)
|
781
784
|
unless class_node.superclass?
|
782
|
-
#
|
783
|
-
abort "class #{class_node.full_name.inspect} called super in its " \
|
785
|
+
error_msg "class #{class_node.full_name.inspect} called super in its " \
|
784
786
|
" initialize function, but it has no superclass."
|
787
|
+
error = InvalidSuper.new(error_msg, constructor)
|
788
|
+
raise error
|
785
789
|
end
|
786
790
|
|
787
791
|
superclass = classes.superclass(class_node.full_name)
|
@@ -841,10 +845,11 @@ module Riml
|
|
841
845
|
end
|
842
846
|
superclass_function = superclass.find_function(func_scope, superclass_func_name(superclass))
|
843
847
|
if superclass.nil? || !superclass_function
|
844
|
-
|
845
|
-
"super was called in class #{ast.full_name} in " \
|
848
|
+
error_msg = "super was called in class #{ast.full_name} in " \
|
846
849
|
"function #{@function_node.original_name}, but there are no " \
|
847
850
|
"functions with this name in that class's superclass hierarchy."
|
851
|
+
error = Riml::InvalidSuper.new(error_msg, node)
|
852
|
+
raise error
|
848
853
|
end
|
849
854
|
node_args = if node.arguments.empty? && !node.with_parens && superclass_function.splat
|
850
855
|
[SplatNode.new('*a:000')]
|
@@ -910,9 +915,9 @@ module Riml
|
|
910
915
|
]
|
911
916
|
),
|
912
917
|
StringNode.new("_s:#{super_func_name}", :s)
|
913
|
-
]
|
918
|
+
]
|
914
919
|
)
|
915
|
-
]
|
920
|
+
]
|
916
921
|
)
|
917
922
|
)
|
918
923
|
ast.constructor.expressions << assign_node
|
@@ -964,7 +969,9 @@ module Riml
|
|
964
969
|
|
965
970
|
while param = def_node.parameters[param_idx += 1]
|
966
971
|
unless param == def_node.splat || DefaultParamNode === param
|
967
|
-
|
972
|
+
error_msg = "can't have regular parameter after default parameter in function #{def_node.name.inspect}"
|
973
|
+
error = UserArgumentError.new(error_msg, def_node)
|
974
|
+
raise error
|
968
975
|
end
|
969
976
|
end
|
970
977
|
|
@@ -1043,5 +1050,5 @@ module Riml
|
|
1043
1050
|
end
|
1044
1051
|
end
|
1045
1052
|
|
1046
|
-
end
|
1053
|
+
end unless defined?(Riml::AST_Rewriter)
|
1047
1054
|
end
|
@@ -20,7 +20,7 @@ module Riml
|
|
20
20
|
raise ArgumentError, "first index must come before (or be equal to) last index"
|
21
21
|
end
|
22
22
|
|
23
|
-
unless
|
23
|
+
unless Riml.debug
|
24
24
|
add_to_head = @error.backtrace[0...first_i] || []
|
25
25
|
add_to_tail = @error.backtrace[last_i...-1] || []
|
26
26
|
backtrace = @error.backtrace[first_i..last_i] || []
|
data/lib/riml/compiler.rb
CHANGED
@@ -31,7 +31,7 @@ module Riml
|
|
31
31
|
|
32
32
|
def visit(node)
|
33
33
|
output = compile(node)
|
34
|
-
output << "\n" if node.force_newline and output[-1] != "\n"
|
34
|
+
output << "\n" if node.force_newline and output[-1, 1] != "\n"
|
35
35
|
propagate_up_tree(node, output)
|
36
36
|
end
|
37
37
|
|
@@ -44,7 +44,8 @@ module Riml
|
|
44
44
|
def visitor_for_node(node, params={})
|
45
45
|
Compiler.const_get("#{node.class.name.split('::').last}Visitor").new(params)
|
46
46
|
rescue NameError
|
47
|
-
|
47
|
+
error = CompileError.new('unexpected construct', node)
|
48
|
+
raise error
|
48
49
|
end
|
49
50
|
|
50
51
|
def root_node(node)
|
@@ -78,7 +79,7 @@ module Riml
|
|
78
79
|
node.compiled_output << node.indent + line
|
79
80
|
end
|
80
81
|
end
|
81
|
-
node.compiled_output << "\n" unless node.compiled_output[-1] == "\n"
|
82
|
+
node.compiled_output << "\n" unless node.compiled_output[-1..-1] == "\n"
|
82
83
|
node.compiled_output << "endif\n"
|
83
84
|
end
|
84
85
|
end
|
@@ -169,20 +170,21 @@ module Riml
|
|
169
170
|
when String
|
170
171
|
StringNode === node ? string_surround(node) : node.value
|
171
172
|
when Array
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
173
|
+
if ListNode === node
|
174
|
+
node.value.each {|n| n.parent_node = node}
|
175
|
+
'[' <<
|
176
|
+
node.value.map do |n|
|
177
|
+
n.accept(visitor_for_node(n))
|
178
|
+
n.compiled_output
|
179
|
+
end.join(', ') << ']'
|
180
|
+
elsif DictionaryNode === node
|
181
|
+
'{' <<
|
182
|
+
node.value.map do |(k, v)|
|
183
|
+
k.accept(visitor_for_node(k))
|
184
|
+
v.accept(visitor_for_node(v))
|
185
|
+
k.compiled_output << ': ' << v.compiled_output
|
186
|
+
end.join(', ') << '}'
|
187
|
+
end
|
186
188
|
end.to_s
|
187
189
|
|
188
190
|
node.compiled_output = value
|
@@ -607,7 +609,8 @@ module Riml
|
|
607
609
|
# riml_include has to be top-level
|
608
610
|
unless node.parent == root_node(node)
|
609
611
|
error_msg = %Q(riml_include error, has to be called at top-level)
|
610
|
-
|
612
|
+
error = IncludeNotTopLevel.new(error_msg, node)
|
613
|
+
raise error
|
611
614
|
end
|
612
615
|
node.each_existing_file! do |basename, full_path|
|
613
616
|
output = current_compiler(node).compile_include(basename, full_path)
|
data/lib/riml/constants.rb
CHANGED
@@ -51,8 +51,12 @@ module Riml
|
|
51
51
|
].flatten
|
52
52
|
|
53
53
|
# For when showing source location (file:lineno) during error
|
54
|
-
# and no file was given
|
54
|
+
# and no file was given, only a string to compile.
|
55
|
+
# Ex: # Riml.compile(source_code) would raise an error like
|
56
|
+
# '<String>:14 riml_include must be top-level'
|
55
57
|
COMPILED_STRING_LOCATION = '<String>'
|
58
|
+
# For when there is no location info associated with a node
|
59
|
+
UNKNOWN_LOCATION_INFO = '<unknown>'
|
56
60
|
|
57
61
|
# :help function-list
|
58
62
|
BUILTIN_FUNCTIONS =
|
data/lib/riml/errors.rb
CHANGED
@@ -1,19 +1,64 @@
|
|
1
|
+
require File.expand_path('../constants', __FILE__)
|
2
|
+
|
1
3
|
module Riml
|
2
|
-
RimlError
|
4
|
+
class RimlError < StandardError
|
3
5
|
attr_accessor :node
|
6
|
+
def initialize(msg = nil, node = nil)
|
7
|
+
super(msg)
|
8
|
+
@node = node
|
9
|
+
end
|
10
|
+
|
11
|
+
def verbose_message
|
12
|
+
"#{self.class}\n" <<
|
13
|
+
"location: #{location_info}\n" <<
|
14
|
+
"message: #{message.to_s.sub(/\A\n/, '')}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def location_info
|
18
|
+
if @node
|
19
|
+
@node.location_info
|
20
|
+
else
|
21
|
+
Constants::UNKNOWN_LOCATION_INFO
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module ErrorWithoutNodeAvailable
|
27
|
+
attr_accessor :filename, :lineno
|
28
|
+
def initialize(msg = nil, filename = nil, lineno = nil)
|
29
|
+
super(msg, nil)
|
30
|
+
@filename = filename
|
31
|
+
@lineno = lineno
|
32
|
+
end
|
33
|
+
|
34
|
+
def location_info
|
35
|
+
if @filename || @lineno
|
36
|
+
"#{@filename}:#{@lineno}"
|
37
|
+
else
|
38
|
+
Constants::UNKNOWN_LOCATION_INFO
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class SyntaxError < RimlError
|
44
|
+
include ErrorWithoutNodeAvailable
|
45
|
+
end
|
46
|
+
class ParseError < RimlError
|
47
|
+
include ErrorWithoutNodeAvailable
|
4
48
|
end
|
5
49
|
|
6
|
-
SyntaxError = Class.new(RimlError)
|
7
|
-
ParseError = Class.new(RimlError)
|
8
50
|
CompileError = Class.new(RimlError)
|
9
51
|
|
10
52
|
FileNotFound = Class.new(RimlError)
|
11
53
|
IncludeFileLoop = Class.new(RimlError)
|
12
54
|
SourceFileLoop = Class.new(RimlError)
|
13
55
|
IncludeNotTopLevel = Class.new(RimlError)
|
56
|
+
|
14
57
|
# bad user arguments to Riml functions
|
15
58
|
UserArgumentError = Class.new(RimlError)
|
16
|
-
|
59
|
+
|
60
|
+
# super is called in invalid context
|
61
|
+
InvalidSuper = Class.new(RimlError)
|
17
62
|
|
18
63
|
ClassNotFound = Class.new(RimlError)
|
19
64
|
ClassRedefinitionError = Class.new(RimlError)
|