class SlideField::Interpreter
Attributes
root[RW]
Public Class Methods
new()
click to toggle source
# File lib/slidefield/interpreter.rb, line 4 def initialize @files = [] @parser = SlideField::Parser.new @root = SlideField::ObjectData.new(:ROOT, 'line 0 char 0') end
Public Instance Methods
interpret_tree(tree, object, child_path = nil, child_context = nil, close_object = true)
click to toggle source
# File lib/slidefield/interpreter.rb, line 77 def interpret_tree(tree, object, child_path = nil, child_context = nil, close_object = true) tree.respond_to? :each and tree.each {|stmt| if stmt_data = stmt[:assignment] interpret_assignment stmt_data, object elsif stmt_data = stmt[:object] interpret_object stmt_data, object, child_path, child_context else # we got strange data from the parser?! raise "Unsupported statement '#{stmt.keys.first}'" end } if close_object # finalize the object once all its content has been processed rules = object.rules rules.required_properties.each {|name| unless object.get name raise SlideField::InterpreterError, "Missing property '#{name}' for object '#{object.type}' at #{object.loc}" end } rules.optional_properties.each {|name| next unless object.get(name).nil? default = rules.default_value name type = rules.type_of_property name object.set name, default, 'default', type } rules.accepted_children.each {|type| min, max = rules.requirements_of_child type count = object[type].count if count < min raise SlideField::InterpreterError, "Object '#{object.type}' must have at least #{min} '#{type}', #{count} found at #{object.loc}" end if max > 0 && count > max raise SlideField::InterpreterError, "Object '#{object.type}' can not have more than #{max} '#{type}', #{count} found at #{object.loc}" end } end object end
run_file(path, parent_obj = nil)
click to toggle source
# File lib/slidefield/interpreter.rb, line 10 def run_file(path, parent_obj = nil) if @files.include? path raise SlideField::InterpreterError, "File already interpreted: '#{path}'" else @files << path end file = Pathname.new path begin input = file.read rescue => e raise SlideField::InterpreterError, e.message end include_path = file.dirname.to_s @rootpath = Pathname.new(include_path) if parent_obj.nil? || @rootpath.nil? context = file.relative_path_from(@rootpath).to_s run_string input, include_path, context, parent_obj end
run_string(input, include_path = '.', context = 'input', parent_obj = nil)
click to toggle source
# File lib/slidefield/interpreter.rb, line 33 def run_string(input, include_path = '.', context = 'input', parent_obj = nil) SlideField.debug "Parsing #{context}..." include_path = File.absolute_path(include_path) + File::SEPARATOR object = parent_obj || @root object.include_path = include_path unless object.include_path object.context = context unless object.context close = parent_obj.nil? begin tree = @parser.parse input, reporter: Parslet::ErrorReporter::Deepest.new rescue Parslet::ParseFailed => error cause = error.cause reason = nil while cause reason = cause.to_s cause = cause.children.last end raise SlideField::ParseError, reason end interpret_tree tree, object, include_path, context, close rescue SlideField::Error => error message = error.message if !message.start_with?('[') && message =~ /line (\d+) char (\d+)/ line = $1.to_i - 1 column = $2.to_i - 1 if line > -1 && source = input.lines[line] excerpt = source.strip column -= source.index excerpt arrow = "#{"\x20" * column}^" message += "\n\t#{excerpt}\n\t#{arrow}" end end raise error.class, "[#{context}] #{message}" end
Private Instance Methods
convert(type, token)
click to toggle source
# File lib/slidefield/interpreter.rb, line 342 def convert(type, token) case type when :identifier, :object token when :integer token.to_i when :point token.to_s.split('x').collect &:to_i when :string escape_sequences = { 'n'=>"\n" } token.to_s[1..-2].gsub(/\\(.)/) { escape_sequences[$1] || $1 } when :color int = token.to_s[1..-1].hex r = (int >> 24) & 255 g = (int >> 16) & 255 b = (int >> 8) & 255 a = (int) & 255 [r, g, b, a] when :boolean token == ':true' else # the parser gave us strange data?! raise "Unsupported type '#{type}' at #{get_loc token}" end end
extract_value(data, object)
click to toggle source
# File lib/slidefield/interpreter.rb, line 316 def extract_value(data, object) filters = data.delete :filters value_data = data.first type = value_data[0] token = value_data[1] value = convert type, token if type == :identifier if id_value = object.get(value.to_sym) type = object.var_type value.to_sym value = id_value else raise SlideField::InterpreterError, "Undefined variable '#{value}' at #{get_loc token}" end elsif type == :object token = token[:type] end filters.reverse_each {|filter_token| type, value = filter filter_token, type, value } return type, token, value end
filter(token, type, value)
click to toggle source
# File lib/slidefield/interpreter.rb, line 374 def filter(token, type, value) name_t = token[:name] name = name_t.to_sym case [type, name] when [:point, :x] type = :integer value = value[0] when [:point, :y] type = :integer value = value[1] when [:integer, :x] type = :point value = [value, 0] when [:integer, :y] type = :point value = [0, value] when [:string, :lines] type = :integer value = value.lines.count else raise SlideField::InterpreterError, "Invalid filter '#{name}' for type '#{type}' at #{get_loc name_t}" end return type, value end
get_loc(token)
click to toggle source
# File lib/slidefield/interpreter.rb, line 311 def get_loc(token) pos = token.line_and_column "line #{pos.first} char #{pos.last}" end
interpret_anon_value(value_data, object)
click to toggle source
# File lib/slidefield/interpreter.rb, line 294 def interpret_anon_value(value_data, object) val_type, value_t, value = extract_value value_data, object var_name = object.rules.matching_properties(val_type).first # guess variable name unless var_name raise SlideField::InterpreterError, "Unexpected '#{val_type}', expecting one of #{object.rules.properties_types} at #{get_loc value_t}" end if object.has? var_name raise SlideField::InterpreterError, "Variable '#{var_name}' is already defined at #{get_loc value_t}" end object.set var_name, value, get_loc(value_t), val_type end
interpret_assignment(stmt_data, object)
click to toggle source
# File lib/slidefield/interpreter.rb, line 129 def interpret_assignment(stmt_data, object) var_name_t = stmt_data[:variable] var_name = var_name_t.to_sym operator_t = stmt_data[:operator] operator = operator_t.to_s var_type, var_value_t, var_value = extract_value stmt_data[:value], object case operator when '=' if object.has? var_name raise SlideField::InterpreterError, "Variable '#{var_name}' is already defined at #{get_loc var_name_t}" end if valid_type = object.rules.type_of_property(var_name) if var_type != valid_type raise SlideField::InterpreterError, "Unexpected '#{var_type}', expecting '#{valid_type}' for property '#{var_name}' at #{get_loc var_value_t}" end end object.set var_name, var_value, get_loc(var_value_t), var_type when '+=', '-=', '*=', '/=' origin_val = object.get var_name unless origin_val raise SlideField::InterpreterError, "Undefined variable '#{var_name}' at #{get_loc var_name_t}" end origin_type = object.var_type var_name method = operator[0] if var_type != origin_type raise SlideField::InterpreterError, "Unexpected '#{var_type}', expecting '#{origin_type}' for variable or property '#{var_name}' at #{get_loc var_value_t}" end value = nil case origin_type when :integer value = origin_val.send method, var_value when :point, :color if origin_type != :color || ['+=', '-='].include?(operator) value = origin_val.collect.with_index {|v, i| v.send method, var_value[i] } if origin_type == :color # normalize value.collect! {|v| v = 0 if v < 0 v = 255 if v > 255 v } end end when :string case operator when '+=' value = origin_val + var_value when '-=' copy = origin_val.dup copy[var_value] = '' while copy.include? var_value value = copy when '*=' multiplier = var_value.to_i unless multiplier > 0 raise SlideField::InterpreterError, "Invalid string multiplier '#{var_value}', integer > 0 required at #{get_loc var_value_t}" end value = origin_val * multiplier end end unless value raise SlideField::InterpreterError, "Invalid operator '#{operator}' for type '#{origin_type}' at #{get_loc operator_t}" end object.set var_name, value, get_loc(var_value_t) else # the parser gave us strange data?! raise "Unsupported operator '#{operator}' at #{get_loc operator_t}" end rescue ZeroDivisionError raise SlideField::InterpreterError, "divided by zero at #{get_loc var_value_t}" end
interpret_object(stmt_data, object, include_path, context)
click to toggle source
# File lib/slidefield/interpreter.rb, line 219 def interpret_object(stmt_data, object, include_path, context) type_t = stmt_data[:type] type = type_t.to_sym body = stmt_data[:body] || [] anon_values = [] anon_values << stmt_data[:value] if stmt_data[:value] template_t = stmt_data[:template] template = stmt_data while template[:template] template = object.get type unless template raise SlideField::InterpreterError, "Undefined variable '#{type}' at #{get_loc type_t}" end unless :object == tpl_type = object.var_type(type) raise SlideField::InterpreterError, "Unexpected '#{tpl_type}', expecting 'object' at #{get_loc type_t}" end type = template[:type].to_sym if template[:value] tpl_value = rebind_tokens template[:value], template_t anon_values << tpl_value end if template[:body] tpl_body = rebind_tokens template[:body], template_t body += tpl_body end end unless object.rules.accepted_children.include?(type) raise SlideField::InterpreterError, "Unexpected object '#{type}', expecting one of #{object.rules.accepted_children.sort} at #{get_loc type_t}" end child = SlideField::ObjectData.new type, get_loc(type_t) child.include_path = include_path child.context = context child.parent = object # enable variable inheritance unless child.rules # the object was allowed but we don't know anything about it?! raise "Unsupported object '#{child.type}'" end anon_values.each {|value_data| interpret_anon_value value_data, child } interpret_tree body, child || [], include_path, context # process special objects case child.type when :include source = File.expand_path child.get(:source), include_path run_file source, object when :debug debug_infos = { :type=>child.var_type(:thing), :value=>child.get(:thing) } puts "DEBUG in #{child.context} at #{child.loc}:" ap debug_infos puts else object << child end end
rebind_tokens(tree, dest)
click to toggle source
# File lib/slidefield/interpreter.rb, line 402 def rebind_tokens(tree, dest) case tree when Array tree.collect {|h| rebind_tokens h, dest } when Hash tree = tree.dup tree.each {|k, v| tree[k] = rebind_tokens v, dest } when Parslet::Slice Parslet::Slice.new dest.position, tree.str, dest.line_cache end end