Module: Brandish::Parser::Main

Included in:
Brandish::Parser
Defined in:
lib/brandish/parser/main.rb

Overview

The main parser for the document. This constructs a tree of nodes that can be used to represent the original document.

Constant Summary collapse

QUOTE_SYMBOL =

A set containing the kind symbol for a quote.

Returns:

  • (::Set<::Symbol>)
EQUAL_SYMBOL =

A set containing the kind symbol for an equal sign.

Returns:

  • (::Set<::Symbol>)
LESS_THAN_SYMBOL =

A set containing the kind symbol for a less than symbol.

Returns:

  • (::Set<::Symbol>)
GREATER_THAN_SYMBOL =

A set containing the kind symbol for a greater than symbol.

Returns:

  • (::Set<::Symbol>)
SLASH_SYMBOL =

A set containing the kind symbol for a forward slash symbol.

Returns:

  • (::Set<::Symbol>)
SLASH_OR_GREATER_THAN_SYMBOL =

A set containing the kind symbols for a forward slash or a greater than symbol.

Returns:

  • (::Set<::Symbol>)
SLASH_SYMBOL | GREATER_THAN_SYMBOL
TEXT_SYMBOL =

A set containing the kind symbols for a text symbol.

Returns:

  • (::Set<::Symbol>)
SPACE_SYMBOLS =

A set containing the kind symbols for a space symbol.

Returns:

  • (::Set<::Symbol>)
EOF_SYMBOL =

A set containing the kind symbols for a eof symbol.

Returns:

  • (::Set<::Symbol>)

Instance Method Summary collapse

Instance Method Details

#parse_documentParser::Node Also known as: parse_root

Parses the overall document. This parses a sequence of elements until it reaches the EOF token.

Returns:



14
15
16
17
18
# File 'lib/brandish/parser/main.rb', line 14

def parse_document
  body = collect(EOF_SYMBOL) { parse_document_element }
  expect(EOF_SYMBOL)
  Node::Root.new(children: body)
end

#parse_document_block(start, name, arguments) ⇒ Node::Block

Parses a "block." This is similar to a HTML tag in the sense that it has a name and a body; however, blocks do not have any sort of arguments to them.

block ::= '<' TEXT *command-argument '>' *element '</' TEXT '>'

Parameters:

  • start (Scanner::Token)

    The starting element for the block.

  • name (Scanner::Token)

    The name of the block.

  • arguments (<Node::Pair>)

    The arguments to the block.

Returns:



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/brandish/parser/main.rb', line 118

def parse_document_block(start, name, arguments)
  expect(GREATER_THAN_SYMBOL)
  body = parse_document_block_body
  expect(SLASH_SYMBOL)
  match = expect(TEXT_SYMBOL)
  stop = expect(GREATER_THAN_SYMBOL)

  unless name.value == match.value
    fail ParseError.new("Unexpected #{match.value.inspect}, expected" \
      " #{name.value.inspect}", match.location)
  end

  Node::Block.new(name: name, body: body, arguments: arguments,
    location: start.location.union(body.location, match.location,
      stop.location, *arguments.map(&:location)))
end

#parse_document_block_bodyNode::Root

Parses the body of a block tag. This keeps attempting to parse text and meta tags until it encounters the phrase </, at which point it will stop parsing and return to the parent.

Returns:



140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/brandish/parser/main.rb', line 140

def parse_document_block_body
  children = []
  loop do
    if peek?(LESS_THAN_SYMBOL)
      start = expect(LESS_THAN_SYMBOL)
      break if peek?(SLASH_SYMBOL)
      children << parse_document_meta(start)
    else
      children << parse_document_text
    end
  end

  Node::Root.new(children: children)
end

#parse_document_command(start, name, arguments) ⇒ Node::Command

Parses a document command. This is essentially a message to the compiler about the document, to alter how the document is processed.

command ::= '<' TEXT *command-argument '/' '>'

Parameters:

  • start (Scanner::Token)

    The starting element for the command.

  • name (Scanner::Token)

    The name of the command.

  • arguments (<Node::Pair>)

    The arguments to the command.

Returns:



57
58
59
60
61
62
63
# File 'lib/brandish/parser/main.rb', line 57

def parse_document_command(start, name, arguments)
  expect(SLASH_SYMBOL)
  stop = expect(GREATER_THAN_SYMBOL)

  Node::Command.new(name: name, arguments: arguments,
    location: start.location.union(stop.location))
end

#parse_document_command_argumentNode::Pair

Parses a document command argument. This is an argument to a command. All arguments are key-value pairs.

command-argument ::= *space command-argument-key *space '=' *space command-argument-value *space

Returns:



71
72
73
74
75
76
77
78
79
80
81
# File 'lib/brandish/parser/main.rb', line 71

def parse_document_command_argument
  parse_skip_space
  key = parse_document_command_argument_key
  parse_skip_space
  expect(EQUAL_SYMBOL)
  parse_skip_space
  value = parse_document_command_argument_value
  parse_skip_space

  Node::Pair.new(key: key, value: value)
end

#parse_document_command_argument_keyScanner::Token

The key for the command argument.

command-argument-key ::= TEXT

Returns:

  • (Scanner::Token)


88
89
90
# File 'lib/brandish/parser/main.rb', line 88

def parse_document_command_argument_key
  expect(TEXT_SYMBOL)
end

#parse_document_command_argument_valueNode

The value for the command argument. This can be either a string, or a Node::Text with a single token. If it's a string, it's parsed with #parse_document_string; otherwise, it grabs one token that's valid for a Node::Text node.

command-argument-value ::= string / TEXT

Returns:



100
101
102
103
104
105
106
# File 'lib/brandish/parser/main.rb', line 100

def parse_document_command_argument_value
  if peek?(QUOTE_SYMBOL)
    parse_document_string
  else
    Node::Text.new(tokens: [expect(Node::Text::TOKENS)])
  end
end

#parse_document_elementNode

Parses a single element in the docuemnt. If the next token is a less-than sign, it calls #parse_document_meta; otherwise, it calls #parse_document_text.

Returns:

  • (Node)

    The node for the element.



27
28
29
# File 'lib/brandish/parser/main.rb', line 27

def parse_document_element
  peek?(LESS_THAN_SYMBOL) ? parse_document_meta : parse_document_text
end

#parse_document_meta(start = expect(LESS_THAN_SYMBOL)) ⇒ Node

parses a "meta" element from the document. In this sense, it is anything that doesn't correspond 1-to-1 to the destination document. If the next token after the less-than sign is an at-sign, it calls #parse_document_command; otherwise, it calls #parse_document_block.

Returns:



37
38
39
40
41
42
43
44
45
46
# File 'lib/brandish/parser/main.rb', line 37

def parse_document_meta(start = expect(LESS_THAN_SYMBOL))
  name = expect(TEXT_SYMBOL)
  parse_skip_space
  arguments = collect(SLASH_OR_GREATER_THAN_SYMBOL) { parse_document_command_argument }
  if peek?(SLASH_SYMBOL)
    parse_document_command(start, name, arguments)
  else
    parse_document_block(start, name, arguments)
  end
end

#parse_document_stringNode::String

Parses a "string." This is a series of text encapulated by quotes. Strings can contain more characters than just regular text, but right now, strings are only used for command argument values.

string ::= '"' *(TEXT / SPACE / LINE / NUMERIC / ESCAPE / '<' / '>' / '=') '"'

Returns:



162
163
164
165
166
167
168
169
# File 'lib/brandish/parser/main.rb', line 162

def parse_document_string
  start = expect(QUOTE_SYMBOL)
  children = collect(QUOTE_SYMBOL) { expect(Node::String::TOKENS) }
  stop = expect(QUOTE_SYMBOL)
  location = start.location.union(stop.location)

  Node::String.new(tokens: children, location: location)
end

#parse_document_textNode::Text

Parses a document for text. This is just regular text tokens. For a list of tokens that are allowed, see Node::Text::TOKENS.

text ::= *(TEXT / SPACE / LINE / NUMERIC / ESCAPE / '/' / '"' / '=')

Returns:



177
178
179
180
181
# File 'lib/brandish/parser/main.rb', line 177

def parse_document_text
  children = [expect(Node::Text::TOKENS)]
  children << expect(Node::Text::TOKENS) while peek?(Node::Text::TOKENS)
  Node::Text.new(tokens: children)
end

#parse_skip_spacevoid

This method returns an undefined value.

Skips over nodes as long as they're space nodes.



186
187
188
# File 'lib/brandish/parser/main.rb', line 186

def parse_skip_space
  expect(SPACE_SYMBOLS) while peek?(SPACE_SYMBOLS)
end