Class: SlideField::Interpreter

Inherits:
Object
  • Object
show all
Defined in:
lib/slidefield/interpreter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeInterpreter

Returns a new instance of Interpreter.

[View source]

4
5
6
7
8
# File 'lib/slidefield/interpreter.rb', line 4

def initialize
  @parser = SlideField::Parser.new
  @files = []
  @root = SlideField::ObjectData.new(:ROOT, 'line 0 char 0')
end

Instance Attribute Details

#rootObject

Returns the value of attribute root.


2
3
4
# File 'lib/slidefield/interpreter.rb', line 2

def root
  @root
end

Instance Method Details

#interpret_tree(tree, object, child_path = nil, child_context = nil, close_object = true) ⇒ Object

[View source]

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/slidefield/interpreter.rb', line 76

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) ⇒ Object

[View source]

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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) ⇒ Object

[View source]

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/slidefield/interpreter.rb', line 33

def run_string(input, include_path = '.', context = 'input', parent_obj = nil)
  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