lib/delorean/engine.rb in delorean_lang-0.5.1 vs lib/delorean/engine.rb in delorean_lang-0.5.2
- old
+ new
@@ -1,26 +1,31 @@
+# frozen_string_literal: true
+
require 'delorean/const'
require 'delorean/base'
require 'set'
require 'pp'
module Delorean
class Engine
attr_reader :last_node, :module_name, :line_no,
- :comp_set, :pm, :m, :imports, :sset
+ :comp_set, :pm, :m, :imports, :sset
- def initialize(module_name, sset=nil)
+ def initialize(module_name, sset = nil)
# name of current module
@module_name = module_name
@sset = sset
reset
end
def reset
- @m, @pm = nil, nil
- @last_node, @node_attrs = nil, {}
- @line_no, @multi_no = 0, nil
+ @m = nil
+ @pm = nil
+ @last_node = nil
+ @node_attrs = {}
+ @line_no = 0
+ @multi_no = nil
# set of comprehension vars
@comp_set = Set.new
# set of all params
@@ -39,67 +44,69 @@
def curr_line
@multi_no || @line_no
end
def parse_import(name)
- err(ParseError, "No script set") unless sset
+ err(ParseError, 'No script set') unless sset
err(ParseError, "Module #{name} importing itself") if
name == module_name
begin
@imports[name] = sset.get_engine(name)
- rescue => exc
+ rescue StandardError => exc
err(ImportError, exc.to_s)
end
- @pm.const_set("#{MOD}#{name}", @imports[name].pm)
+ @pm.const_set("#{MOD}#{name.gsub('::', '__')}", @imports[name].pm)
end
def gen_import(name)
@imports.merge!(@imports[name].imports)
- @m.const_set("#{MOD}#{name}", @imports[name].m)
+ @m.const_set("#{MOD}#{name.gsub('::', '__')}", @imports[name].m)
end
def get_import_engine(name)
err(ParseError, "#{name} not imported") unless @imports[name]
@imports[name]
end
- def is_node_defined(name)
+ def node_defined?(name)
@pm.constants.member? name.to_sym
end
# Check to see if node with given name is defined. flag tells the
# method about our expectation. flag=true means that we make sure
# that name is defined. flag=false is the opposite.
def parse_check_defined_node(name, flag)
- isdef = is_node_defined(name)
+ isdef = node_defined?(name)
if isdef != flag
isdef ? err(RedefinedError, "#{name} already defined") :
err(UndefinedError, "#{name} not defined yet")
end
end
def super_name(pname, mname)
- mname ? "#{MOD}#{mname}::#{pname}" : pname
+ mname ? "#{MOD}#{mname.gsub('::', '__')}::#{pname}" : pname
end
def parse_check_defined_mod_node(pname, mname)
engine = mname ? get_import_engine(mname) : self
engine.parse_check_defined_node(pname, true)
end
- def parse_define_node(name, pname, mname=nil)
+ def parse_define_node(name, pname, mname = nil)
parse_check_defined_node(name, false)
parse_check_defined_mod_node(pname, mname) if pname
sname = pname ? super_name(pname, mname) : 'Object'
- @pm.module_eval("class #{name} < #{sname}; end")
+ @pm.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ class #{name} < #{sname}; end
+ RUBY
# latest defined node
@last_node = name
# mapping of node name to list of attrs it defines
@@ -123,24 +130,25 @@
end
end
# Parse-time check to see if attr is available on current node.
def parse_call_last_node_attr(attr_name)
- err(ParseError, "Not inside a node") unless @last_node
+ err(ParseError, 'Not inside a node') unless @last_node
parse_call_attr(@last_node, attr_name)
end
def parse_define_var(var_name)
- err(RedefinedError,
- "List comprehension can't redefine variable '#{var_name}'") if
- comp_set.member? var_name
+ if comp_set.member? var_name
+ err(RedefinedError,
+ "List comprehension can't redefine variable '#{var_name}'")
+ end
comp_set.add var_name
end
def parse_undef_var(var_name)
- err(ParseError, "internal error") unless comp_set.member? var_name
+ err(ParseError, 'internal error') unless comp_set.member? var_name
comp_set.delete var_name
end
# parse-time attr definition
def parse_define_attr(name, spec)
@@ -150,14 +158,14 @@
err(RedefinedError, "Can't redefine '#{name}' in node #{@last_node}") if
@node_attrs[@last_node].member? name
@node_attrs[@last_node] << name
- checks = spec.map { |a|
+ checks = spec.map do |a|
n = a.index('.') ? a : "#{@last_node}.#{a}"
"_x.member?('#{n}') ? raise('#{n}') : #{a}#{POST}(_x + ['#{n}'])"
- }.join(';')
+ end.join(';')
code =
"class #{@last_node}; def self.#{name}#{POST}(_x); #{checks}; end; end"
# pp code
@@ -193,11 +201,11 @@
def err(exc, msg)
raise exc.new(msg, @module_name, curr_line)
end
- def parse_check_call_fn(fn, argcount, class_name=nil)
+ def parse_check_call_fn(fn, argcount, class_name = nil)
klass = case class_name
when nil
@m::BaseClass
when String
parse_class(class_name)
@@ -232,74 +240,77 @@
begin
# generate ruby code
gen = t.rewrite(self)
rescue RuntimeError => exc
- err(ParseError, "codegen error: " + exc.message)
+ err(ParseError, 'codegen error: ' + exc.message)
end
# puts gen
begin
# evaluate generated code in @m
@m.module_eval(gen, "#{MOD}#{module_name}", curr_line)
- rescue => exc
+ rescue StandardError => exc
# bad ruby code generated, shoudn't happen
- err(ParseError, "codegen error: " + exc.message)
+ err(ParseError, 'codegen error: ' + exc.message)
end
end
def parse(source)
raise "can't call parse again without reset" if @pm
# @m module is used at runtime for code evaluation. @pm module
# is only used during parsing to check for errors.
- @m, @pm = BaseModule.clone, Module.new
+ @m = BaseModule.clone
+ @pm = Module.new
- multi_line, @multi_no = nil, nil
+ multi_line = nil
+ @multi_no = nil
source.each_line do |line|
@line_no += 1
# skip comments
- next if line.match(/^\s*\#/)
+ next if line =~ /^\s*\#/
# remove trailing blanks
line.rstrip!
- next if line.length == 0
+ next if line.empty?
if multi_line
# if line starts with >4 spaces, assume it's a multline
# continuation.
if line =~ /\A {5}/
multi_line += line
next
else
t = parser.parse(multi_line)
- err(ParseError, "syntax error") unless t
+ err(ParseError, 'syntax error') unless t
generate(t)
- multi_line, @multi_no = nil, nil
+ multi_line = nil
+ @multi_no = nil
end
end
t = parser.parse(line)
if !t
- err(ParseError, "syntax error") unless line =~ /^\s+/
+ err(ParseError, 'syntax error') unless line =~ /^\s+/
multi_line = line
@multi_no = @line_no
else
generate(t)
end
end
if multi_line
t = parser.parse(multi_line)
- err(ParseError, "syntax error") unless t
+ err(ParseError, 'syntax error') unless t
generate(t)
end
end
######################################################################
@@ -311,52 +322,52 @@
SortedSet[* @node_attrs.keys]
end
# enumerate qualified list of all attrs
def enumerate_attrs
- @node_attrs.keys.each_with_object({}) { |node, h|
+ @node_attrs.keys.each_with_object({}) do |node, h|
h[node] = enumerate_attrs_by_node(node)
- }
+ end
end
# enumerate qualified list of attrs by node
def enumerate_attrs_by_node(node)
- raise "bad node" unless node
+ raise 'bad node' unless node
begin
klass = node.is_a?(String) ? @m.module_eval(node) : node
rescue NameError
# FIXME: a little hacky. Should raise an exception.
return []
end
raise "bad node class #{klass}" unless klass.is_a?(Class)
- klass.methods.map(&:to_s).select { |x|
+ klass.methods.map(&:to_s).select do |x|
x.end_with?(POST)
- }.map { |x|
+ end.map do |x|
x.sub(/#{POST}$/, '')
- }
+ end
end
# enumerate all params
def enumerate_params
@param_set
end
# enumerate params by a single node
def enumerate_params_by_node(node)
attrs = enumerate_attrs_by_node(node)
- Set.new( attrs.select {|a| @param_set.include?(a)} )
+ Set.new(attrs.select { |a| @param_set.include?(a) })
end
######################################################################
# Runtime
######################################################################
- def evaluate(node, attrs, params={})
- raise "bad params" unless params.is_a?(Hash)
+ def evaluate(node, attrs, params = {})
+ raise 'bad params' unless params.is_a?(Hash)
if node.is_a?(Class)
klass = node
else
raise "bad node '#{node}'" unless node =~ /^[A-Z][a-zA-Z0-9_]*$/
@@ -371,31 +382,31 @@
params[:_engine] = self
type_arr = attrs.is_a?(Array)
attrs = [attrs] unless type_arr
- res = attrs.map { |attr|
+ res = attrs.map do |attr|
raise "bad attribute '#{attr}'" unless attr =~ /^[a-z][A-Za-z0-9_]*$/
+
klass.send("#{attr}#{POST}".to_sym, params)
- }
+ end
type_arr ? res : res[0]
end
- def eval_to_hash(node, attrs, params={})
+ def eval_to_hash(node, attrs, params = {})
res = evaluate(node, attrs, params)
Hash[* attrs.zip(res).flatten(1)]
end
def self.grok_runtime_exception(exc)
# parse out the delorean-related backtrace records
- bt = exc.backtrace.map{ |x|
- x.match(/^#{MOD}(.+?):(\d+)(|:in `(.+)')$/);
+ bt = exc.backtrace.map do |x|
+ x =~ /^#{MOD}(.+?):(\d+)(|:in `(.+)')$/
$1 && [$1, $2.to_i, $4.sub(/#{POST}$/, '')]
- }.reject(&:!)
+ end.reject(&:!)
- {"error" => exc.message, "backtrace" => bt}
+ { 'error' => exc.message, 'backtrace' => bt }
end
######################################################################
-
end
end