Module: Riml

Defined in:
lib/riml/compiler.rb,
lib/riml.rb,
lib/riml/repl.rb,
lib/riml/lexer.rb,
lib/riml/nodes.rb,
lib/riml/errors.rb,
lib/riml/parser.rb,
lib/riml/walker.rb,
lib/riml/walkable.rb,
lib/riml/class_map.rb,
lib/riml/constants.rb,
lib/riml/path_cache.rb,
lib/riml/environment.rb,
lib/riml/ast_rewriter.rb,
lib/riml/file_rollback.rb,
lib/riml/include_cache.rb,
lib/riml/imported_class.rb,
lib/riml/warning_buffer.rb,
lib/riml/backtrace_filter.rb,
lib/riml/rewritten_ast_cache.rb,
lib/riml/class_dependency_graph.rb

Overview

visits AST nodes and translates them into VimL

Defined Under Namespace

Modules: Constants, Environment, ErrorWithoutNodeAvailable, FullyNameable, Indentable, NotNestedUnder, QuestionVariableExistence, Visitable, Walkable Classes: AST_Rewriter, AssignNode, BacktraceFilter, BinaryOperatorNode, BreakNode, CallNode, CatchNode, ClassDefinitionNode, ClassDependencyGraph, ClassMap, Compiler, ContinueNode, ControlStructure, CurlyBracePart, CurlyBraceVariable, DefMethodNode, DefNode, DefaultParamNode, DictGetBracketNode, DictGetDotNode, DictGetNode, DictionaryNode, ElseNode, ElseifNode, ExLiteralNode, ExplicitCallNode, FalseNode, FileRollback, FinishNode, ForNode, GetCurlyBraceNameNode, GetSpecialVariableNode, GetVariableByScopeAndDictNameNode, GetVariableNode, IfNode, ImportedClass, IncludeCache, KeywordNode, Lexer, ListNode, ListOrDictGetNode, ListUnpackNode, LiteralNode, MultiAssignNode, Nodes, NumberNode, ObjectInstantiationNode, OperatorNode, ParseError, Parser, PathCache, RegexpNode, Repl, ReturnNode, RewrittenASTCache, RimlClassCommandNode, RimlCommandNode, RimlError, RimlFileCommandNode, SIDNode, ScopeModifierLiteralNode, ScopeNode, SplatNode, StringLiteralConcatNode, StringNode, SublistNode, SuperNode, SyntaxError, TernaryOperatorNode, TrueNode, TryNode, UnaryOperatorNode, UnlessNode, UnletVariableNode, UntilNode, Walker, WarningBuffer, WhileNode, WrapInParensNode

Constant Summary collapse

DEFAULT_COMPILE_OPTIONS =
{ :readable => true }
DEFAULT_COMPILE_FILES_OPTIONS =
DEFAULT_COMPILE_OPTIONS.merge(
  :output_dir => nil
)
DEFAULT_PARSE_OPTIONS =
{
  :allow_undefined_global_classes => false,
  :include_reordering => true
}
EXTRACT_PARSE_OPTIONS =
lambda { |k,_| DEFAULT_PARSE_OPTIONS.keys.include?(k.to_sym) }
EXTRACT_COMPILE_OPTIONS =
lambda { |k,_| DEFAULT_COMPILE_OPTIONS.keys.include?(k.to_sym) }
EXTRACT_COMPILE_FILES_OPTIONS =
lambda { |k,_| DEFAULT_COMPILE_FILES_OPTIONS.keys.include?(k.to_sym) }
FILENAME_OPTION_KEYS =
[:commandline_filename, :sourced_filename]
EXTRACT_FILENAME_OPTIONS =
lambda { |k,_| FILENAME_OPTION_KEYS.include?(k.to_sym) }
CompileError =
Class.new(RimlError)
FileNotFound =
Class.new(RimlError)
IncludeFileLoop =
Class.new(RimlError)
SourceFileLoop =
Class.new(RimlError)
IncludeNotTopLevel =
Class.new(RimlError)
UserArgumentError =

bad user arguments to Riml functions

Class.new(RimlError)
InvalidSuper =

super is called in invalid context

Class.new(RimlError)
ClassNotFound =
Class.new(RimlError)
ClassRedefinitionError =
Class.new(RimlError)

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.debugObject

Returns the value of attribute debug.



228
229
230
# File 'lib/riml.rb', line 228

def debug
  @debug
end

.warningsObject

Returns the value of attribute warnings.



228
229
230
# File 'lib/riml.rb', line 228

def warnings
  @warnings
end

Class Method Details

.check_syntax(input) ⇒ Object

checks syntax of ‘input` (String). lexes + parses without going through AST rewriting or compilation

Raises:

  • (ArgumentError)


164
165
166
167
168
# File 'lib/riml.rb', line 164

def self.check_syntax(input)
  raise ArgumentError.new(input) unless input.is_a?(String)
  parse(input, nil)
  true
end

.check_syntax_files(*filenames) ⇒ Object



170
171
172
173
174
175
# File 'lib/riml.rb', line 170

def self.check_syntax_files(*filenames)
  filenames.each do |fname|
    File.open(fname) {|f| check_syntax(f.read)}
  end
  true
end

.clear_cachesObject



212
213
214
215
216
217
# File 'lib/riml.rb', line 212

def self.clear_caches
  @include_cache.clear
  @path_cache.clear
  @rewritten_ast_cache.clear
  Parser.ast_cache.clear
end

.compile(input, options = {}) ⇒ Object



53
54
55
56
57
58
59
60
61
62
# File 'lib/riml.rb', line 53

def self.compile(input, options = {})
  parse_options = Hash[options.select(&EXTRACT_PARSE_OPTIONS)]
  compile_options = Hash[options.select(&EXTRACT_COMPILE_OPTIONS)]
  parser = Parser.new
  parser.options = DEFAULT_PARSE_OPTIONS.merge(parse_options)
  compiler = Compiler.new
  compiler.options = DEFAULT_COMPILE_OPTIONS.merge(compile_options)
  filename_options = Hash[options.select(&EXTRACT_FILENAME_OPTIONS)]
  do_compile(input, parser, compiler, filename_options)
end

.compile_files(*filenames) ⇒ Object

expects ‘filenames` (String) arguments, to be readable files. Optional options (Hash) as last argument.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/riml.rb', line 111

def self.compile_files(*filenames)
  filenames = filenames.dup
  parser, compiler = Parser.new, Compiler.new

  # extract parser and compiler options from last argument, or use default
  # options
  if filenames.last.is_a?(Hash)
    options = filenames.pop
    compile_options = Hash[options.select(&EXTRACT_COMPILE_FILES_OPTIONS)]
    parse_options = Hash[options.select(&EXTRACT_PARSE_OPTIONS)]
    compiler.options = DEFAULT_COMPILE_FILES_OPTIONS.merge(compile_options)
    parser.options = DEFAULT_PARSE_OPTIONS.merge(parse_options)
  else
    compiler.options = DEFAULT_COMPILE_FILES_OPTIONS.dup
    parser.options = DEFAULT_PARSE_OPTIONS.dup
  end

  filenames.uniq!
  # compile files using one thread per file, max 4 threads at once
  if filenames.size > 1
    threads = []
    with_file_rollback do
      while filenames.any?
        to_compile = filenames.shift(4)
        to_compile.each do |fname|
          _parser, _compiler = Parser.new, Compiler.new
          _compiler.options = compiler.options.dup
          _parser.options = parser.options.dup
          threads << Thread.new do
            f = File.open(fname)
            # `do_compile` will close file handle
            do_compile(f, _parser, _compiler, :commandline_filename => fname)
          end
        end
        threads.each(&:join)
        threads.clear
      end
    end
  elsif filenames.size == 1
    fname = filenames.first
    f = File.open(fname)
    # `do_compile` will close file handle
    with_file_rollback { do_compile(f, parser, compiler, :commandline_filename => fname) }
  else
    raise ArgumentError, "need filenames to compile"
  end
  true
ensure
  flush_warnings
end

.configObject

Returns OpenStruct|nil.

Returns:

  • OpenStruct|nil



234
235
236
# File 'lib/riml.rb', line 234

def self.config
  @config
end

.config=(config) ⇒ Object

possible values: OpenStruct|nil



239
240
241
242
243
244
# File 'lib/riml.rb', line 239

def self.config=(config)
  unless config.nil? || OpenStruct === config
    raise ArgumentError, "config must be OpenStruct or NilClass, is #{config.class}"
  end
  @config = config
end

.do_compile(input, parser = Parser.new, compiler = Compiler.new, filename_options = {}) ⇒ Object

compile AST (Nodes), tokens (Array), code (String) or object that returns String from :read to output code (String). Writes file(s) if ‘input` is a File.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/riml.rb', line 67

def self.do_compile(input, parser = Parser.new, compiler = Compiler.new, filename_options = {})
  if input.is_a?(Nodes)
    nodes = input
  elsif input.is_a?(String) || input.is_a?(Array)
    nodes = parser.parse(input)
  elsif input.respond_to?(:read)
    source = input.read
    path = input.respond_to?(:path) ? input.path : nil
    nodes = parser.parse(source, parser.ast_rewriter || AST_Rewriter.new, path)
  else
    raise ArgumentError, "input must be one of AST (Nodes), tokens (Array), " \
      "code (String) or respond_to?(:read), is #{input.inspect}"
  end

  compiler.parser = parser

  # This is to avoid cases where the file we're compiling from the
  # commandline gets recompiled but put in a different location because
  # it's also sourced, and `Riml.source_path` is set to a non-default value.
  if input.is_a?(File)
    pathname = Pathname.new(input.path)
    full_path =
      if pathname.absolute?
        pathname.to_s
      else
        File.expand_path(input.path, compiler.output_dir || Dir.getwd)
      end
    compiler.sourced_files_compiled << full_path
  end

  output = compiler.compile(nodes)

  if input.is_a?(File)
    write_file(compiler, output, input.path, filename_options)
  else
    output
  end
ensure
  input.close if input.is_a?(File)
  process_compile_queue!(compiler)
end

.include_cacheObject



195
196
197
# File 'lib/riml.rb', line 195

def self.include_cache
  @include_cache
end

.include_pathObject



184
185
186
# File 'lib/riml.rb', line 184

def self.include_path
  get_path(:include_path)
end

.include_path=(path, force_cache_bust = false) ⇒ Object



187
188
189
# File 'lib/riml.rb', line 187

def self.include_path=(path, force_cache_bust = false)
  set_path(:include_path, path, force_cache_bust)
end

.lex(code) ⇒ Object

lex code (String) into tokens (Array)



40
41
42
# File 'lib/riml.rb', line 40

def self.lex(code)
  Lexer.new(code).tokenize
end

.parse(input, ast_rewriter = AST_Rewriter.new, filename = nil) ⇒ Object

parse tokens (Array) or code (String) into AST (Nodes)



45
46
47
48
49
50
51
# File 'lib/riml.rb', line 45

def self.parse(input, ast_rewriter = AST_Rewriter.new, filename = nil)
  unless input.is_a?(Array) || input.is_a?(String)
    raise ArgumentError, "input must be tokens (Array) or code (String), " \
      "is #{input.inspect}"
  end
  Parser.new.parse(input, ast_rewriter, filename)
end

.path_cacheObject



202
203
204
# File 'lib/riml.rb', line 202

def self.path_cache
  @path_cache
end

.rewritten_ast_cacheObject



207
208
209
# File 'lib/riml.rb', line 207

def self.rewritten_ast_cache
  @rewritten_ast_cache
end

.source_pathObject



177
178
179
# File 'lib/riml.rb', line 177

def self.source_path
  get_path(:source_path)
end

.source_path=(path, force_cache_bust = false) ⇒ Object



180
181
182
# File 'lib/riml.rb', line 180

def self.source_path=(path, force_cache_bust = false)
  set_path(:source_path, path, force_cache_bust)
end

.warn(warning) ⇒ Object



191
192
193
# File 'lib/riml.rb', line 191

def self.warn(warning)
  warning_buffer << warning
end

.with_file_rollback(&block) ⇒ Object

if error is thrown, all files that were created will be rolled back to their previous state. If the file existed previously, it will be the same as it was. If the file didn’t exist, it will be removed if it was created.



223
224
225
# File 'lib/riml.rb', line 223

def self.with_file_rollback(&block)
  FileRollback.guard(&block)
end