blob: b934eb9725c8243088d227d9c96f5b033e61da0f [file] [log] [blame]
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Various types shared through multiple passes of parsing.
This module contains types used as interfaces between parts of the Emboss front
end. These types do not really "belong" to either the producers or consumers,
and in a few cases placing them in one or the other creates unnecessary
dependencies, so they are defined here.
"""
import collections
from public import ir_pb2
def _make_position(line, column):
"""Makes an ir_pb2.Position from line, column ints."""
if not isinstance(line, int):
raise ValueError("Bad line {!r}".format(line))
elif not isinstance(column, int):
raise ValueError("Bad column {!r}".format(column))
return ir_pb2.Position(line=line, column=column)
def _parse_position(text):
"""Parses an ir_pb2.Position from "line:column" (e.g., "1:2")."""
line, column = text.split(":")
return _make_position(int(line), int(column))
def format_position(position):
"""formats an ir_pb2.Position to "line:column" form."""
return "{}:{}".format(position.line, position.column)
def make_location(start, end, is_synthetic=False):
"""Makes an ir_pb2.Location from (line, column) tuples or ir_pb2.Positions."""
if isinstance(start, tuple):
start = _make_position(*start)
if isinstance(end, tuple):
end = _make_position(*end)
if not isinstance(start, ir_pb2.Position):
raise ValueError("Bad start {!r}".format(start))
elif not isinstance(end, ir_pb2.Position):
raise ValueError("Bad end {!r}".format(end))
elif start.line > end.line or (
start.line == end.line and start.column > end.column):
raise ValueError("Start {} is after end {}".format(format_position(start),
format_position(end)))
return ir_pb2.Location(start=start, end=end, is_synthetic=is_synthetic)
def format_location(location):
"""Formats an ir_pb2.Location in format "1:2-3:4" ("start-end")."""
return "{}-{}".format(format_position(location.start),
format_position(location.end))
def parse_location(text):
"""Parses an ir_pb2.Location from format "1:2-3:4" ("start-end")."""
start, end = text.split("-")
return make_location(_parse_position(start), _parse_position(end))
class Token(
collections.namedtuple("Token", ["symbol", "text", "source_location"])):
"""A Token is a chunk of text from a source file, and a classification.
Attributes:
symbol: The name of this token ("Indent", "SnakeWord", etc.)
text: The original text ("1234", "some_name", etc.)
source_location: Where this token came from in the original source file.
"""
def __str__(self):
return "{} {} {}".format(self.symbol, repr(self.text),
format_location(self.source_location))
class Production(collections.namedtuple("Production", ["lhs", "rhs"])):
"""A Production is a simple production from a context-free grammar.
A Production takes the form:
nonterminal -> symbol*
where "nonterminal" is an implicitly non-terminal symbol in the language,
and "symbol*" is zero or more terminal or non-terminal symbols which form the
non-terminal on the left.
Attributes:
lhs: The non-terminal symbol on the left-hand-side of the production.
rhs: The sequence of symbols on the right-hand-side of the production.
"""
def __str__(self):
return str(self.lhs) + " -> " + " ".join([str(r) for r in self.rhs])
@staticmethod
def parse(production_text):
"""Parses a Production from a "symbol -> symbol symbol symbol" string."""
words = production_text.split()
if words[1] != "->":
raise SyntaxError
return Production(words[0], tuple(words[2:]))