Class: Aspen::Parser

Inherits:
AbstractParser show all
Defined in:
lib/aspen/parser.rb

Instance Attribute Summary

Attributes inherited from AbstractParser

#position, #tokens

Instance Method Summary collapse

Methods inherited from AbstractParser

#expect, #first, #initialize, #last, #need, #next_token, parse, parse_code, #peek

Constructor Details

This class inherits a constructor from Aspen::AbstractParser

Instance Method Details

#parseObject Also known as: parse_narrative

narrative = statements;

statements = { statement }
statement = COMMENT | CUSTOM_STATEMENT | list_statement | vanilla_statement

# Variant 1. TODO: Variant 2
list_statement = node, edge, [ list_label ], START_LIST, list_items, END_LIST
list_label = OPEN_PARENS, LABEL, CLOSE_PARENS
list_items = { list_item }
list_item = BULLET, CONTENT, [ list_item_label ]
list_item_label = OPEN_PARENS, LABEL, CLOSE_PARENS
vanilla_statement = node | node, edge, node, { END_STATEMENT }
node = node_short_form | node_grouped_form | node_cypher_form
node_short_form = OPEN_PARENS, CONTENT, CLOSE_PARENS
node_grouped_form = OPEN_PARENS, { CONTENT, [ COMMA ] }, CLOSE_PARENS
node_cypher_form = OPEN_PARENS, LABEL, OPEN_BRACES, { IDENTIFIER, literal, [ COMMA ] }, CLOSE_BRACES
literal = STRING | NUMBER
edge = OPEN_BRACKETS, CONTENT, CLOSE_BRACKETS


28
29
30
# File 'lib/aspen/parser.rb', line 28

def parse
  Aspen::AST::Nodes::Narrative.new(parse_statements)
end

#parse_commentObject



53
54
55
56
57
# File 'lib/aspen/parser.rb', line 53

def parse_comment
  if comment = expect(:COMMENT)
    Aspen::AST::Nodes::Comment.new(comment.first.last)
  end
end

#parse_custom_statementObject



59
60
61
62
63
64
65
# File 'lib/aspen/parser.rb', line 59

def parse_custom_statement
  if content = expect(:CUSTOM_GRAMMAR_STATEMENT)
    # FIXME: Why does this need a #first and a #last?
    # Seems unnecessarily nested. Maybe this happened in the lexer.
    Aspen::AST::Nodes::CustomStatement.new(content.first.last)
  end
end

#parse_edgeObject



170
171
172
173
174
# File 'lib/aspen/parser.rb', line 170

def parse_edge
  if (_, content, _ = expect(:OPEN_BRACKETS, :CONTENT, :CLOSE_BRACKETS))
    Aspen::AST::Nodes::Edge.new(content.last)
  end
end

#parse_list_itemObject



100
101
102
103
104
105
106
107
108
109
# File 'lib/aspen/parser.rb', line 100

def parse_list_item
  node = nil
  if (_, content = expect(:BULLET, :CONTENT))
    node = Aspen::AST::Nodes::Node.new(attribute: content.last)
  end
  if (label = parse_list_item_label)
    node.label = label
  end
  return node
end

#parse_list_item_labelObject



111
112
113
114
115
116
# File 'lib/aspen/parser.rb', line 111

def parse_list_item_label
  if (_, item_label, _  = expect(:OPEN_PARENS, :CONTENT, :CLOSE_PARENS))
    # If singularizing should be conditional, we need to introduce the env in the parser.
    return item_label.last
  end
end

#parse_list_itemsObject



90
91
92
93
94
95
96
97
98
# File 'lib/aspen/parser.rb', line 90

def parse_list_items
  if need(:START_LIST)
    results = []
    while target = parse_list_item
      results << target
    end
    results
  end
end

#parse_list_labelObject



83
84
85
86
87
88
# File 'lib/aspen/parser.rb', line 83

def parse_list_label
  if (_, plural_label, _  = expect(:OPEN_PARENS, :CONTENT, :CLOSE_PARENS))
    # If singularizing should be conditional, we need to introduce the env in the parser.
    return plural_label.last.singularize
  end
end

#parse_list_statementObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/aspen/parser.rb', line 67

def parse_list_statement
  if expect(:PREPARE_START_LIST)
    origin  = parse_node
    edge    = parse_edge
    label   = parse_list_label
    targets = parse_list_items
    expect(:END_LIST)
    targets.map do |target|
      # puts "TARGET #{target.attribute.content.inner_content} had label #{target.label.content.inner_content.inspect}"
      target.label = label if target.label.content.inner_content.nil?
      # puts "TARGET #{target.attribute.content.inner_content} has label #{target.label.content.inner_content.inspect}"
      Aspen::AST::Nodes::Statement.new(origin: origin, edge: edge, target: target)
    end
  end
end

#parse_literalObject

Raises:

  • (NotImplementedError)


166
167
168
# File 'lib/aspen/parser.rb', line 166

def parse_literal
  raise NotImplementedError, "#parse_literal not yet implemented"
end

#parse_nodeObject



135
136
137
138
# File 'lib/aspen/parser.rb', line 135

def parse_node
  # parse_node_cypher_form ||
  parse_node_grouped_form || parse_node_short_form
end

#parse_node_cypher_formObject

This complicates things greatly. Can we skip this for now, by rewriting the tests to get rid of this case, and come back to it?



160
161
162
163
164
# File 'lib/aspen/parser.rb', line 160

def parse_node_cypher_form
  if (_, label, _, content, _ = expect(:OPEN_PARENS, :CONTENT, :SEPARATOR, :CONTENT, :CLOSE_PARENS))
    Aspen::AST::Nodes::Node.new(content: content.last, label: label.last)
  end
end

#parse_node_grouped_formObject



140
141
142
143
144
145
146
147
# File 'lib/aspen/parser.rb', line 140

def parse_node_grouped_form
  if (_, label, sep, content, _ = expect(:OPEN_PARENS, :LABEL, :SEPARATOR, :CONTENT, :CLOSE_PARENS))
    Aspen::AST::Nodes::Node.new(
      attribute: content.last,
      label: label.last
    )
  end
end

#parse_node_labeled_formObject

Raises:

  • (NotImplementedError)


119
120
121
# File 'lib/aspen/parser.rb', line 119

def parse_node_labeled_form
  raise NotImplementedError, "#parse_node_labeled_form not yet implemented"
end

#parse_node_short_formObject



149
150
151
152
153
154
155
156
# File 'lib/aspen/parser.rb', line 149

def parse_node_short_form
  # Terminal instructions require a "need"
  _, content, _ = need(:OPEN_PARENS, :CONTENT, :CLOSE_PARENS)
  Aspen::AST::Nodes::Node.new(
    attribute: content.last,
    label: nil
  )
end

#parse_statementObject



46
47
48
49
50
51
# File 'lib/aspen/parser.rb', line 46

def parse_statement
  parse_comment           ||
  parse_custom_statement  ||
  parse_list_statement    ||
  parse_vanilla_statement
end

#parse_statementsObject



34
35
36
37
38
39
40
41
42
43
44
# File 'lib/aspen/parser.rb', line 34

def parse_statements
  results = []

  # Make sure this returns on empty
  while result = parse_statement
    results.push(*result)
    break if tokens[position].nil?
  end

  results
end

#parse_vanilla_statementObject



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/aspen/parser.rb', line 123

def parse_vanilla_statement
  # TODO: Might benefit from a condition when doing non-vanilla statements?
  origin = parse_node
  edge   = parse_edge
  target = parse_node

  # SMELL: Nil check
  advance if peek && peek.first == :END_STATEMENT

  Aspen::AST::Nodes::Statement.new(origin: origin, edge: edge, target: target)
end