from __future__ import division
import weakref
import re
import os
from math import cos, sin, radians
import operator

from fontTools.misc.py23 import basestring

from fontTools.misc.transform import Transform
from fontTools.pens.boundsPen import BoundsPen
from fontTools.pens.transformPen import TransformPen
from fontTools.pens.recordingPen import RecordingPen

try:
    # >= RF3.2
    from fontTools.ufoLib.pointPen import SegmentToPointPen
except ImportError:
    # < RF3.2
    from ufoLib.pointPen import SegmentToPointPen

__version__ = "0.0.3"

# splitters

glyphNameSplit = "="
unicodeSplit = "|"
baseGlyphSplit = "&"
markGlyphSplit = "+"
positionSplit = "@"
positionXYSplit = ","
positionBaseSplit = ":"
glyphSuffixSplit = "."
metricsSuffixSplit = "^"
glyphCommentSuffixSplit = "#"
glyphMarkSuffixSplit = "!"
flipMarkGlyphSplit = "~"
applyKerningSplit = "\\"
glyphAtrributeAlternateSplit = "'"

variableDeclarationStart = "$"
variableDeclarationEnd = ""

explicitMathStart = '`'
explicitMathEnd = '`'

explicitGlyphNameStart = '"'
explicitGlyphNameEnd = '"'


#flags

shouldCheckGlyphExists = "?"
shouldAddSourceGlyphIfExists = ">"
shouldDecomposeResult = "*"


"""
$variableName = n
# a comment
?*>Laringacute = L & a + ring@~center,~`top+10` + acute.cap@height,top ^ 100, `l*2` | 159AFFF ! 1, 0, 0, 1 # this is an example, and this is a variable {variableName}
"""

# legal positions

legalFontInfoAttributes = set("descender xHeight capHeight ascender".split(" "))
legalGlyphMetricHorizontalPositions = set("origin width".split(" "))
legalGlyphMetricVerticalPositions = set("origin height".split(" "))
legalBoundsPositions = set("left innerLeft right innerRight top innerTop bottom innerBottom".split(" "))
legalCalculatablePositions = legalBoundsPositions | set(["center"])


# math

def _intersectAngles(point1, angle1, point2, angle2):
    """
    Intersect two rays, described by a point and an angle.

    >>> _intersectAngles((100, 100), 45, (100, 200), -45)
    (150.00000000000094, 150.00000000000094)
    >>> _intersectAngles((100, 100), 45, (100, 200), 45) is None
    True
    """
    point1A = point1
    point2A = point2
    point1B = point1[0] + cos(radians(angle1)), point1[1] + sin(radians(angle1))
    point2B = point2[0] + cos(radians(angle2)), point2[1] + sin(radians(angle2))
    intersection = _intesectLines((point1A, point1B), (point2A, point2B))
    return intersection


def _intesectLines(pt1, pt2):
    """
    Intersect two lines.

    >>> _intesectLines(((100, 100), (200, 200)), ((100, 200), (200, 100)))
    (150.0, 150.0)
    >>> _intesectLines(((200, 100), (200, 200)), ((300, 200), (300, 200))) is None
    True
    """
    (pt1, pt2), (pt3, pt4) = pt1, pt2
    denom = (pt1[0] - pt2[0]) * (pt3[1] - pt4[1]) - (pt1[1] - pt2[1]) * (pt3[0] - pt4[0])
    if _roundFloat(denom) == 0:
        return None
    x = (pt1[0] * pt2[1] - pt1[1] * pt2[0]) * (pt3[0] - pt4[0]) - (pt1[0] - pt2[0]) * (pt3[0] * pt4[1] - pt3[1] * pt4[0])
    x /= denom
    y = (pt1[0] * pt2[1] - pt1[1] * pt2[0]) * (pt3[1] - pt4[1]) - (pt1[1] - pt2[1]) * (pt3[0] * pt4[1] - pt3[1] * pt4[0])
    y /= denom
    return (x, y)


def _roundFloat(f, error=10000.0):
    return round(f * error) / error


def _diffPoint(pt1, pt2):
    (x1, y1), (x2, y2) = pt1, pt2
    return (x1 - x2, y1 - y2)


# regex

if variableDeclarationEnd:
    variableDeclarationEnd = r"\%s" % variableDeclarationEnd
varialbesRE = re.compile(r"\%s\s*(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)\s*\=\s*(?P<value>.*)%s" % (variableDeclarationStart, variableDeclarationEnd))

simpleVariableRe = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")

percentageRe = re.compile(r"[0-9]*%")

glyphNameRe = re.compile(r'([a-zA-Z_][a-zA-Z0-9_.]*|.notdef)')

explicitMathRe = re.compile(r'\%s(?P<explicitMath>.*?)\%s' % (explicitMathStart, explicitMathEnd))

explicitGlyphNameRe = re.compile(r'\%s(?P<explicitGlyphName>.*?)\%s' % (explicitGlyphNameStart, explicitGlyphNameEnd))


# error

class GlyphBuilderError(Exception):
    pass


# glyph object


class ConstructionGlyph(object):

    """
    A Glyph like object able set some basic attributes, add components and draw.
    """

    def __init__(self, glyphset):
        self._glyphset = weakref.ref(glyphset)
        self.name = None
        self.width = 0
        self.unicode = None
        self.components = []
        self.note = ""
        self.markColor = None
        self.source = RecordingPen()
        self._bounds = None
        self.shouldDecompose = False

    def _get_glyphset(self):
        return self._glyphset()

    glyphset = property(_get_glyphset, doc="Return the glyph set the glyph belongs to.")

    def getParent(self):
        """
        Deprecated fallback callback.
        """
        return self.glyphset

    def addComponent(self, glyphName, transformation):
        self.components.append((glyphName, transformation))

    def _get_bounds(self):
        if self._bounds is None:
            pen = BoundsPen(self.getParent())
            self.draw(pen)
            self._bounds = pen.bounds
        return self._bounds

    bounds = property(_get_bounds)

    def _get_leftMargin(self):
        bounds = self.bounds
        if bounds is None:
            return None
        xMin, yMin, xMax, yMax = bounds
        return xMin

    def _set_leftMargin(self, value):
        if value is None:
            return
        bounds = self.bounds
        if bounds is None:
            return
        xMin, yMin, xMax, yMax = bounds
        diff = value - xMin
        self.move((diff, 0))
        self.width += diff

    leftMargin = property(_get_leftMargin, _set_leftMargin)

    def _get_rightMargin(self):
        bounds = self.bounds
        if bounds is None:
            return None
        xMin, yMin, xMax, yMax = bounds
        return self.width - xMax

    def _set_rightMargin(self, value):
        if value is None:
            return
        bounds = self.bounds
        if bounds is None:
            return
        xMin, yMin, xMax, yMax = bounds
        self.width = xMax + value

    rightMargin = property(_get_rightMargin, _set_rightMargin)

    def move(self, move):
        moveX, moveY = move
        oldBounds = self._bounds
        for i in range(len(self.components)):
            glyphName, (xx, xy, yx, yy, x, y) = self.components[i]
            self.components[i] = glyphName, (xx, xy, yx, yy, x + moveX, y + moveY)
        source = RecordingPen()
        self.source.replay(TransformPen(source, [1, 0, 0, 1, moveX, moveY]))
        self.source = source
        if oldBounds:
            xMin, yMin, xMax, yMax = oldBounds
            xMin += moveX
            yMin += moveY
            xMax += moveX
            yMax += moveY
            self._bounds = (xMin, yMin, xMax, yMax)

    def draw(self, pen):
        self.source.replay(pen)
        for glyphName, transformation in self.components:
            if self.shouldDecompose:
                try:
                    glyph = self.glyphset[glyphName]
                except KeyError:
                    continue
                tPen = TransformPen(pen, transformation)
                glyph.draw(tPen)

            else:
                pen.addComponent(glyphName, transformation)

    def drawPoints(self, pointPen):
        pen = SegmentToPointPen(pointPen)
        self.draw(pen)


class MathPoint(tuple):

    """
    A math object for calculation with tuples.

    >>> point1 = MathPoint((100, 100))
    >>> point2 = MathPoint((200, 200))

    >>> point1 + point2
    (300, 300)
    >>> point1 - point2
    (-100, -100)
    >>> point1 * 2
    (200, 200)
    >>> point1 / 2
    (50.0, 50.0)
    >>> point1 += 30, 30
    >>> point1
    (130, 130)
    >>> point1 -= 40
    >>> point1
    (90, 90)
    >>> point1 *= 2
    >>> point1
    (180, 180)
    >>> point1 /= 2
    >>> point1
    (90.0, 90.0)
    """

    def __new__(cls, point, allowTupleMathOnly=False):
        return super(MathPoint, cls).__new__(cls, point)

    def __init__(self, point, allowTupleMathOnly=False):
        self.allowTupleMathOnly = allowTupleMathOnly

    def _operation(self, other, operation):
        x, y = self
        ox = oy = 0
        if isinstance(other, tuple):
            ox, oy = other
        elif not self.allowTupleMathOnly:
            ox = oy = other
        if ox != 0:
            x = operation(x, ox)
        if oy != 0:
            y = operation(y, oy)
        return self.__class__((x, y), self.allowTupleMathOnly)

    def __add__(self, other):
        return self._operation(other, operator.add)

    def __iadd__(self, other):
        return self._operation(other, operator.iadd)

    def __sub__(self, other):
        return self._operation(other, operator.sub)

    def __isub__(self, other):
        return self._operation(other, operator.isub)

    def __mul__(self, other):
        return self._operation(other, operator.mul)

    def __div__(self, other):
        return self._operation(other, operator.div)

    def __truediv__(self, other):
        return self._operation(other, operator.truediv)


def _parsePosition(name, position, angle, fixedPosition, glyph, font, direction, isBase, prefix, top, bottom, left, right, width, height):
    # glyph anchor + prefix
    found = _findAnchor(glyph, "%s%s" % (prefix, name))
    if found is not None:
        return found, angle, fixedPosition

    # glyph anchor
    found = _findAnchor(glyph, name)
    if found is not None:
        return found, angle, fixedPosition

    # glyph guide + prefix
    found = _findGuide(glyph, "%s%s" % (prefix, name))
    if found is not None:
        position, angle = found
        return position, angle, fixedPosition

    # glyph guide
    found = _findGuide(glyph, name)
    if found is not None:
        position, angle = found
        return position, angle, fixedPosition

    # font guide
    found = _findGuide(font, name)
    if found is not None:
        if isBase:
            if direction == "x":
                name = "center"
            elif direction == "y":
                name = "top"
        else:
            position, angle = found
            return position, angle, fixedPosition

    # glyph metrics
    if direction == "x" and name in legalGlyphMetricHorizontalPositions:
        if name == "origin":
            position = (0, 0)
        elif name == "width":
            position = (glyph.width, 0)
        fixedPosition = True
        return position, angle, fixedPosition

    if direction == "y" and name in legalGlyphMetricVerticalPositions:
        if name == "origin":
            position = (0, 0)
        elif name == "height":
            position = (0, getattr(glyph, "height", height))
        fixedPosition = True
        return position, angle, fixedPosition

    # font metrics
    if name in legalFontInfoAttributes:
        found = _findFontInfoValue(font, name)
        if found is not None:
            _, value = found
            position = (0, value - bottom)
            # if isBase:
            #     position = _diffPoint(found, (0, top))
            # else:
            #     position = _diffPoint(found, (0, bottom))
            fixedPosition = True
            return position, angle, fixedPosition

    # calculate
    centerValue = .5
    if name.endswith("%"):
        try:
            centerValue = float(name[:-1]) * 0.01
            name = "center"
        except Exception:
            pass

    if name in legalCalculatablePositions:
        if name == "center":
            if isBase:
                position = (left + width * centerValue, bottom + height * centerValue)
            else:
                position = (left + width * centerValue + width - width * centerValue * 2,
                            bottom + height * centerValue + height - height * centerValue * 2)

        elif name in legalBoundsPositions:

            if direction == "y" and name == "top":
                if isBase:
                    position = (0, top)
                else:
                    position = (0, bottom)
            if direction == "y" and name == "innerTop":
                position = (0, top)
            elif direction == "y" and name == "bottom":
                if isBase:
                    position = (0, bottom)
                else:
                    position = (0, top)
            elif direction == "y" and name == "innerBottom":
                position = (0, bottom)
            elif direction == "x" and name == "left":
                if isBase:
                    position = (left, 0)
                else:
                    position = (right, 0)
            elif direction == "x" and name == "innerLeft":
                position = (left, 0)
            elif direction == "x" and name == "right":
                if isBase:
                    position = (right, 0)
                else:
                    position = (left, 0)
            elif direction == "x" and name == "innerRight":
                position = (right, 0)
    return position, angle, fixedPosition


def parsePosition(markGlyph, font, positionName, direction, prefix="", isBase=False):
    position = (0, 0)
    fixedPosition = False

    if direction == "x":
        italicAngle = getattr(font.info, "italicAngle", 0)
        if italicAngle is None:
            italicAngle = 0
        angle = 90 + italicAngle
    else:
        angle = 0

    if markGlyph not in font:
        return position, angle, fixedPosition

    bounds = None
    if markGlyph in font:
        glyph = font[markGlyph]
        bounds = glyph.bounds

    left = bottom = right = top = width = height = 0
    if bounds:
        left, bottom, right, top = bounds
        width = right - left
        height = top - bottom

    names = simpleVariableRe.findall(positionName)
    percentage = percentageRe.findall(positionName)
    nameSpace = dict()

    # try to convert to float
    if not names and not percentage:
        # resolve simple math operations
        try:
            exec("positionName=%s" % positionName)
        except Exception:
            pass
    try:
        value = float(positionName)
        if direction == "x":
            position = (value - left, 0)
        elif direction == "y":
            position = (0, value - bottom)
        fixedPosition = True
        return position, angle, fixedPosition
    except Exception:
        pass

    data = dict(
        glyph=glyph,
        font=font,
        direction=direction,
        isBase=isBase,
        prefix=prefix,
        top=top,
        bottom=bottom,
        left=left,
        right=right,
        width=width,
        height=height
    )

    for name in names + percentage:
        position, angle, fixedPosition = _parsePosition(name, position, angle, fixedPosition, **data)
        nameSpace[name] = MathPoint(position, not isBase and not fixedPosition)

    try:
        exec("position=%s" % positionName, nameSpace)
    except ZeroDivisionError:
        raise GlyphBuilderError("ZeroDivisionError: integer division or modulo by zero in '%s'" % positionName)
    except SyntaxError:
        if not percentage:
            raise GlyphBuilderError("SyntaxError: invalid syntax in '%s'" % positionName)
    except Exception:
        raise GlyphBuilderError("Something went wrong in '%s'" % positionName)
    position = nameSpace.get("position", position)
    return position, angle, fixedPosition


def _findAnchor(glyph, name):
    anchors = glyph.anchors
    for anchor in anchors:
        if anchor.name == name:
            return (anchor.x, anchor.y)
    return None


def _findGuide(obj, name):
    guides = []
    if hasattr(obj, "guidelines"):
        guides = obj.guidelines
    elif hasattr(obj, "guides"):
        guides = obj.guides

    for guide in guides:
        if guide.name == name:
            return (guide.x, guide.y), guide.angle
    return None


def _findFontInfoValue(font, name):
    value = getattr(font.info, name)
    if value is None:
        value = 0
    return (0, value)


def parsePositions(baseGlyph, markGlyph, font, markTransformMap, advanceWidth, advanceHeight):
    xx, xy, yx, yy, x, y = 1, 0, 0, 1, advanceWidth, advanceHeight

    baseGlyphX = baseGlyphY = baseGlyph
    markFixedX = markFixedY = False

    flipX = flipY = False

    if positionSplit in markGlyph:
        markGlyph, position = markGlyph.split(positionSplit)

        if positionXYSplit in position:
            positions = position.split(positionXYSplit)
            if len(positions) == 6:
                xx, xy, yx, yy, positionX, positionY = positions
                xx = float(xx)
                xy = float(xy)
                yx = float(yx)
                yy = float(yy)
            elif len(positions) == 2:
                positionX, positionY = positions
            else:
                raise GlyphBuilderError("Mark positions should have 6 or 2 options")
        else:
            positionX = positionY = position

        explicitBaseSplit = explicitGlyphNameRe.search(positionX)
        if explicitBaseSplit is not None:
            explicitBaseGlyphX = positionX[explicitBaseSplit.start(): explicitBaseSplit.end()]
            positionX = positionX.replace(explicitBaseGlyphX + positionBaseSplit, "")
            baseGlyphX = explicitBaseSplit.group("explicitGlyphName")
        elif positionBaseSplit in positionX:
            baseGlyphX, positionX = positionX.split(positionBaseSplit)

        explicitBaseSplit = explicitGlyphNameRe.search(positionY)
        if explicitBaseSplit is not None:
            explicitBaseGlyphY = positionY[explicitBaseSplit.start(): explicitBaseSplit.end()]
            positionY = positionY.replace(explicitBaseGlyphY + positionBaseSplit, "")
            baseGlyphY = explicitBaseSplit.group("explicitGlyphName")
        elif positionBaseSplit in positionY:
            baseGlyphY, positionY = positionY.split(positionBaseSplit)

        if flipMarkGlyphSplit in positionX:
            flipY = True
            positionX = positionX.replace(flipMarkGlyphSplit, "")

        if flipMarkGlyphSplit in positionY:
            flipX = True
            positionY = positionY.replace(flipMarkGlyphSplit, "")

        if positionX and positionY:
            baseX = baseY = 0
            markX = markY = 0

            if markGlyph not in font:
                if glyphSuffixSplit in markGlyph:
                    markGlyph = markGlyph.split(glyphSuffixSplit)[0]

            markPoint1, markAngle1, markFixedX = parsePosition(markGlyph, font, positionX, direction="x", prefix="_")
            markPoint2, markAngle2, markFixedY = parsePosition(markGlyph, font, positionY, direction="y", prefix="_")
            intersection = _intersectAngles(markPoint1, markAngle1, markPoint2, markAngle2)
            if intersection is not None:
                markX, markY = intersection
            elif (markPoint1, markAngle1) == (markPoint2, markAngle2):
                markX, markY = markPoint1

            if baseGlyphX in font and baseGlyphY in font:
                basePoint1, baseAngle1, _ = parsePosition(baseGlyphX, font, positionX, direction="x", isBase=True)
                basePoint2, baseAngle2, _ = parsePosition(baseGlyphY, font, positionY, direction="y", isBase=True)
                intersection = _intersectAngles(basePoint1, baseAngle1, basePoint2, baseAngle2)
                if intersection is not None:
                    baseX, baseY = intersection
                elif (basePoint1, baseAngle1) == (basePoint2, baseAngle2):
                    baseX, baseY = basePoint1

            # calculate the offset
            if not markFixedX:
                x += baseX - markX
            else:
                x += markX

            if not markFixedY:
                y += baseY - markY
            else:
                y += markY

        if not markFixedX:
            baseTransform = markTransformMap.get(baseGlyphX)
            if baseTransform:
                x += baseTransform[4] - advanceWidth

        if not markFixedY:
            baseTransform = markTransformMap.get(baseGlyphY)
            if baseTransform:
                y += baseTransform[5] - advanceHeight

    transformMatrix = (xx, xy, yx, yy, x, y)
    unflippedMatrix = transformMatrix
    if flipX:
        bounds = None
        if markGlyph in font:
            bounds = font[markGlyph].bounds
        if bounds:
            minx, miny, maxx, maxy = bounds
            bt = Transform(*transformMatrix)
            minx, miny = bt.transformPoint((minx, miny))
            maxx, maxy = bt.transformPoint((maxx, maxy))
            t = Transform()
            t = t.translate(0, miny)
            t = t.scale(1, -1)
            t = t.translate(0, -maxy)
            t = t.transform(bt)
            transformMatrix = t[:]

    if flipY:
        bounds = None
        if markGlyph in font:
            bounds = font[markGlyph].bounds
        if bounds:
            minx, miny, maxx, maxy = bounds
            bt = Transform(*transformMatrix)
            minx, miny = bt.transformPoint((minx, miny))
            maxx, maxy = bt.transformPoint((maxx, maxy))
            t = Transform()
            t = t.translate(minx, 0)
            t = t.scale(-1, 1)
            t = t.translate(-maxx, 0)
            t = t.transform(bt)
            transformMatrix = t[:]

    markTransformMap[markGlyph] = unflippedMatrix
    return markGlyph, transformMatrix


glyphAttributeAlternateMap = dict(
    leftMargin="rightMargin",
    rightMargin="leftMargin"
)


def _parseGlyphMetric(construction, font, attr):
    value = None
    construction = reEscapeMathOperations(construction)
    if metricsSuffixSplit in construction:
        construction, value = construction.split(metricsSuffixSplit)
        try:
            value = float(value)
        except Exception:
            if value in font:
                value = getattr(font[value], attr)
            else:
                lastIndex = 0
                newText = "result="
                glyphAttribute = attr
                for i in glyphNameRe.finditer(value):
                    newText += value[lastIndex:i.start()]
                    glyphName = i.group()
                    try:
                        if value[i.end()] == glyphAtrributeAlternateSplit:
                            glyphAttribute = glyphAttributeAlternateMap.get(attr, attr)
                    except IndexError:
                        pass
                    if glyphName in font:
                        newText += "%s" % getattr(font[glyphName], glyphAttribute)
                    lastIndex = i.end()
                newText += value[lastIndex:]
                newText = newText.replace(glyphAtrributeAlternateSplit, "")
                try:
                    namespace = dict()
                    exec(newText, namespace)
                    value = namespace["result"]
                except Exception:
                    value = None
    return value, construction


def parseWidth(construction, font):
    return _parseGlyphMetric(construction, font, "width")


def parseLeftMargin(construction, font):
    glyphAttribute = "leftMargin"
    return _parseGlyphMetric(construction, font, glyphAttribute)


def parseRightMargin(construction, font):
    construction = "%s%s" % (metricsSuffixSplit, construction)
    glyphAttribute = "rightMargin"
    return _parseGlyphMetric(construction, font, glyphAttribute)


def parseUnicode(construction, font=None):
    """
    Parse unicode from construction.
    unicode splitter: |

    >>> parseUnicode(removeSpacesAndTabs("agrave = a + grave | 00E0"))
    (224, 'agrave=a+grave')
    """
    unicodeValue = None
    if unicodeSplit in construction:
        construction, unicodeValue = construction.split(unicodeSplit)
        try:
            unicodeValue = int(unicodeValue, 16)
        except Exception:
            unicodeValue = None
    return unicodeValue, construction


def parseMark(construction, font=None):
    """
    Parse mark from construction.
    mark splitter: !

    >>> parseMark(removeSpacesAndTabs("agrave = a + grave ! 1, 0, 0, 1"))
    ((1.0, 0.0, 0.0, 1.0), 'agrave=a+grave')
    """
    mark = None
    if glyphMarkSuffixSplit in construction:
        construction, markString = construction.split(glyphMarkSuffixSplit)
        markString = markString.split(positionXYSplit)
        if len(markString) == 4:
            try:
                r = float(markString[0])
                g = float(markString[1])
                b = float(markString[2])
                a = float(markString[3])
                mark = r, g, b, a
            except Exception:
                mark = None
    return mark, construction


glyphAttrFuncMap = {
    "unicode": parseUnicode,
    "markColor": parseMark,
    "width": parseWidth,
    "leftMargin": parseLeftMargin,
    "rightMargin": parseRightMargin
}


def parseBaseGlyphs(construction):
    """
    Parse all glyph parts from construction.
    base glyph splitter: &

    >>> parseBaseGlyphs("")
    []
    >>> parseBaseGlyphs(removeSpacesAndTabs("a & b"))
    ['a', 'b']
    >>> parseBaseGlyphs(removeSpacesAndTabs("a & b & c"))
    ['a', 'b', 'c']
    """
    if not construction:
        return []
    return construction.split(baseGlyphSplit)


def parseGlyphattributes(construction, font):
    """
    Parse glyph attributes from constructtion.
    width splitter: ^ (could be a tuple referring to leftMargin and rightMargin)
    mark splitter:  !
    unicode splitter: |

    >>> font = testDummyFont()
    >>> data, name = parseGlyphattributes(removeSpacesAndTabs("name | 0000 ! 1, 0, 0, 1 ^ 100"), font)
    >>> sorted(data.items()), name
    ([('markColor', (1.0, 0.0, 0.0, 1.0)), ('unicode', 0), ('width', 100.0)], 'name')

    >>> data, name = parseGlyphattributes(removeSpacesAndTabs("name ! 1, 0, 0, 1 ^ 100 | 0000"), font)
    >>> sorted(data.items()), name
    ([('markColor', (1.0, 0.0, 0.0, 1.0)), ('unicode', 0), ('width', 100.0)], 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ 100 | 0000 ! 1, 0, 0, 1"), font)
    ({'width': 100.0, 'unicode': 0, 'markColor': (1.0, 0.0, 0.0, 1.0)}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ 100 | 0000"), font)
    ({'width': 100.0, 'unicode': 0}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ 100"), font)
    ({'width': 100.0}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name | 0000"), font)
    ({'unicode': 0}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ 100"), font)
    ({'width': 100.0}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ 10, 20"), font)
    ({'leftMargin': 10.0, 'rightMargin': 20.0}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ a, 20"), font)
    ({'leftMargin': 100, 'rightMargin': 20.0}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ a, agrave"), font)
    ({'leftMargin': 100, 'rightMargin': -180}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ a+10, agrave"), font)
    ({'leftMargin': 110, 'rightMargin': -180}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ a*2, agrave"), font)
    ({'leftMargin': 200, 'rightMargin': -180}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ a', agrave"), font)
    ({'leftMargin': -140, 'rightMargin': -180}, 'name')

    >>> parseGlyphattributes(removeSpacesAndTabs("name ^ a', agrave'"), font)
    ({'leftMargin': -140, 'rightMargin': 100}, 'name')
    """
    attrs = {}
    currentKey = None
    currentValue = ""
    newConstruction = ""
    for c in construction:
        if c == metricsSuffixSplit:
            currentKey = "width"
            currentValue = ""
        elif c == glyphMarkSuffixSplit:
            currentKey = "markColor"
            currentValue = ""
        elif c == unicodeSplit:
            currentKey = "unicode"
            currentValue = ""
        if currentKey:
            currentValue += c
            attrs[currentKey] = currentValue
        else:
            newConstruction += c
    values = {}
    if "width" in attrs:
        if positionXYSplit in attrs["width"]:
            margins = attrs["width"].split(positionXYSplit)
            if len(margins) == 2:
                attrs["leftMargin"], attrs["rightMargin"] = margins
                del attrs["width"]
    for attr, value in attrs.items():
        func = glyphAttrFuncMap[attr]
        value, _ = func(value, font)
        values[attr] = value
    return values, newConstruction


def parseNote(construction):
    """
    Parse note from construction.
    note splitter: #
        * and must be the last part of the construction

    >>> parseNote("agrave = a + grave # this is a note")
    ('this is a note', 'agrave = a + grave ')
    """
    note = ""
    if glyphCommentSuffixSplit in construction:
        construction, note = construction.split(glyphCommentSuffixSplit)
        # remove trailing spaces
        note = note.strip()
    return note, construction


def kernValueForGlyphPair(font, pair):
    """
    Return the kerning value of pair of glyph names.
    Also use groups to search for the kerning value.

    >>> import defcon
    >>> font = defcon.Font()
    >>> font.kerning["A", "V"] = -100
    >>> # groups
    >>> font.groups["public.kern1.A"] = ["A", "Agrave", "Atilde"]
    >>> font.groups["public.kern2.V"] = ["V", "V.alt1", "V.alt2"]
    >>> # group kerning
    >>> font.kerning[("public.kern1.A", "public.kern2.V")] = 200
    >>> # exceptions
    >>> font.kerning["Agrave", "public.kern2.V"] = 300
    >>> font.kerning["public.kern1.A", "V.alt2"] = 400

    >>> kernValueForGlyphPair(font, ("A", "V"))
    -100
    >>> kernValueForGlyphPair(font, ("A", "B"))
    0

    >>> kernValueForGlyphPair(font, ("A", "V.alt1"))
    200
    >>> kernValueForGlyphPair(font, ("A", "V.alt2"))
    400
    >>> kernValueForGlyphPair(font, ("Agrave", "V"))
    300
    >>> kernValueForGlyphPair(font, ("Atilde", "V"))
    200
    >>> kernValueForGlyphPair(font, ("Atilde", "V.alt2"))
    400
    >>> kernValueForGlyphPair(font, ("Agrave", "V"))
    300
    >>> kernValueForGlyphPair(font, ("Agrave", "V.alt1"))
    300
    >>> kernValueForGlyphPair(font, ("Atilde", "V.alt1"))
    200
    """
    side1Prefix = "public.kern1."
    side2Prefix = "public.kern2."
    kern = None

    if pair in font.kerning:
        # the pair exists
        kern = font.kerning[pair]
    else:
        side1, side2 = pair
        groupName1 = groupName2 = None
        groupName1Found = groupName2Found = False
        for groupName, group in font.groups.items():
            if not groupName1Found and groupName.startswith(side1Prefix) and side1 in group:
                groupName1 = groupName
                groupName1Found = True
            if not groupName2Found and groupName.startswith(side2Prefix) and side2 in group:
                groupName2 = groupName
                groupName2Found = True
            if groupName1Found and groupName2Found:
                break
        if groupName2 is not None and (side1, groupName2) in font.kerning:
            kern = font.kerning[side1, groupName2]
        elif groupName1 is not None and (groupName1, side2) in font.kerning:
            kern = font.kerning[groupName1, side2]
        elif groupName1 is not None and groupName2 is not None:
            kern = font.kerning.get((groupName1, groupName2))

    if kern is None:
        kern = 0
    return kern


def parseApplyKerning(construction):
    """
    Parse construction and return if the construction has the \\ prefix,
    indicating horizontal kerning should be applied.

    >>> parseApplyKerning(r"\\a+grave")
    (True, 'a+grave')
    >>> parseApplyKerning("a+grave")
    (False, 'a+grave')
    """
    return applyKerningSplit in construction, construction.replace(applyKerningSplit, "")


class ConstructionFlags(set):

    allFlags = set((shouldDecomposeResult, shouldAddSourceGlyphIfExists))

    @property
    def shouldDecompose(self):
        return shouldDecomposeResult in self

    @property
    def shouldAddSourceGlyphIfExists(self):
        return shouldAddSourceGlyphIfExists in self


def parseFlags(construction):
    """
    Parse construction and return all optional flags.
    * shouldDecomposeResult
    * shouldAddSourceGlyphIfExists

    >>> flags, construction = parseFlags("foo=bar")
    >>> flags.shouldDecompose, flags.shouldAddSourceGlyphIfExists, construction
    (False, False, 'foo=bar')

    >>> flags, construction = parseFlags("*foo=bar")
    >>> flags.shouldDecompose, flags.shouldAddSourceGlyphIfExists, construction
    (True, False, 'foo=bar')

    >>> flags, construction = parseFlags(">foo=bar")
    >>> flags.shouldDecompose, flags.shouldAddSourceGlyphIfExists, construction
    (False, True, 'foo=bar')

    >>> flags, construction = parseFlags("*>foo=bar")
    >>> flags.shouldDecompose, flags.shouldAddSourceGlyphIfExists, construction
    (True, True, 'foo=bar')

    >>> flags, construction = parseFlags(">*foo=bar")
    >>> flags.shouldDecompose,flags.shouldAddSourceGlyphIfExists, construction
    (True, True, 'foo=bar')
    """
    foundFlags = ConstructionFlags()
    while construction[0] in ConstructionFlags.allFlags:
        foundFlags.add(construction[0])
        construction = construction[1:]
    return foundFlags, construction

# def parseShouldDecompose(construction):
#     """
#     Parse construction and return if the construction should be decomposed at the end.

#     >>> parseShouldDecompose("*agrave=a+grave")
#     (True, 'agrave=a+grave')

#     >>> parseShouldDecompose("agrave=a+grave")
#     (False, 'agrave=a+grave')

#     >>> parseShouldDecompose("agrave=a+grave^10*10")
#     (False, 'agrave=a+grave^10*10')
#     """
#     shouldDecompose = construction[0] == shouldDecomposeResult
#     if shouldDecompose:
#         construction = construction[1:]
#     return shouldDecompose, construction


def parseGlyphName(construction):
    """
    Parse glyph name from construction.
    glyph name splitter: =

    >>> parseGlyphName("agrave = a + grave")
    ('agrave', ' a + grave')
    """
    glyphName, construction = construction.split(glyphNameSplit)
    glyphName = glyphName.strip()
    return glyphName, construction


escapeMathOperatorMap = {
    "+": "<<add>>",
    "-": "<<sub>>"
}

escapeMathOperatorMapReversed = {value: key for key, value in escapeMathOperatorMap.items()}


def forceEscapingMathOperations(data):
    """
    force escape math to restore it lateron,
    as some operators are used inside a glyph construction,
    especially the + and - .

    >>> forceEscapingMathOperations("`10 + 5`")
    '10 <<add>> 5'
    >>> forceEscapingMathOperations("`10 - 5`")
    '10 <<sub>> 5'
    """
    result = ""
    index = 0
    for found in explicitMathRe.finditer(data):
        result += data[index:found.start()]
        seq = found.group("explicitMath")
        for find, replace in escapeMathOperatorMap.items():
            seq = seq.replace(find, replace)
        result += seq
        index = found.end()
    result += data[index:]
    return result


def reEscapeMathOperations(data):
    """
    Re escape math back to readable and executable math.

    >>> reEscapeMathOperations("10 <<add>> 5")
    '10 + 5'
    >>> reEscapeMathOperations("10 <<sub>> 5")
    '10 - 5'
    """
    for find, replace in escapeMathOperatorMapReversed.items():
        data = data.replace(find, replace)
    return data


def removeSpacesAndTabs(data):
    """
    Remove all tabs and spaces.

    >>> removeSpacesAndTabs("a Sting With Spaces")
    'aStingWithSpaces'
    >>> removeSpacesAndTabs("a\tSting\tWith\tTabs")
    'aStingWithTabs'
    """
    return data.replace(" ", "").replace("\t", "")


def GlyphConstructionBuilder(construction, font, characterMap=None):
    # create a construction glyph
    destination = ConstructionGlyph(font)
    # test if the input is a proper string
    try:
        construction = str(construction)
    except Exception:
        return destination
    # parse flags
    flags, construction = parseFlags(construction)
    # parse the note
    destination.note, construction = parseNote(construction)
    # check if there is a = sing
    if glyphNameSplit not in construction:
        return destination
    # remove all spaces and tabs
    construction = removeSpacesAndTabs(construction)
    # escape math formulas inside a ` `
    construction = forceEscapingMathOperations(construction)
    # extract the name
    destination.name, construction = parseGlyphName(construction)
    # extract glyph attributes
    glyphAttributes, construction = parseGlyphattributes(construction, font)
    # extract base glyphs, ligatures
    baseGlyphs = parseBaseGlyphs(construction)

    advanceWidth = 0
    previousBaseGlyph = None
    # start
    if flags.shouldAddSourceGlyphIfExists and destination.name in font:
        font[destination.name].draw(destination.source)
    for baseGlyph in baseGlyphs:
        applyKerning, baseGlyph = parseApplyKerning(baseGlyph)
        # split into mark glyphs
        markGlyphs = baseGlyph.split(markGlyphSplit)
        baseGlyph = None
        baseMarkGlyph = None
        baseTransformMatrix = [1, 0, 0, 1, 0, 0]
        markTransformMap = {}

        advanceHeight = 0

        for markGlyph in markGlyphs:
            markGlyph = reEscapeMathOperations(markGlyph)
            component, transformMatrix = parsePositions(baseMarkGlyph, markGlyph, font, markTransformMap, advanceWidth, advanceHeight)

            baseMarkGlyph = component

            if baseGlyph is None:
                baseGlyph = component
                if applyKerning:
                    kern = kernValueForGlyphPair(font, (previousBaseGlyph, baseGlyph))
                    if kern:
                        t = Transform(*transformMatrix).translate(kern, 0)
                        transformMatrix = t[:]
                        advanceWidth += kern

                baseTransformMatrix = transformMatrix
            destination.addComponent(component, transformMatrix)

        if baseGlyph in font:
            width = font[baseGlyph].width
            t = Transform(*baseTransformMatrix)
            x0, y = t.transformPoint((0, 0))
            x1, y = t.transformPoint((width, 0))
            advanceWidth += abs(x1 - x0)

        previousBaseGlyph = baseGlyph

    destination.width = advanceWidth
    for key, value in glyphAttributes.items():
        setattr(destination, key, value)

    if characterMap and destination.name in characterMap:
        destination.unicodes = [characterMap[destination.name]]

    destination.shouldDecompose = flags.shouldDecompose
    return destination


def ParseVariables(txt):
    """
    Parse all variables from all constructions and remove them.
    >>> from fontTools.misc.py23 import unichr
    >>> txt = unichr(10).join([
    ...    "$name = test",
    ...    "$positionX = center",
    ...    "$positionY = 100",
    ...    "aacute = a + acute@positionX, positionY"
    ...    ])
    >>> txt, variables = ParseVariables(txt)
    >>> variables == {'positionX': 'center', 'positionY': '100', 'name': 'test'}
    True
    >>> txt.replace(unichr(10), "") == 'aacute = a + acute@positionX, positionY'
    True
    """
    variables = {}
    for i in varialbesRE.finditer(txt):
        name = i.group("name")
        value = i.group("value")
        variables[name] = value
        txt = txt.replace(i.group(), "")
    return txt, variables


def ParseGlyphConstructionListFromString(source, font=None):
    """
    Parse glyph constructions from a big text, could be a file path, file object or a string.
    Optionally a font can be provided to check and ignore existing glyph names.

    This returns a list of optimized glyph constructions.

    # Basic parsing with comment
    >>> from fontTools.misc.py23 import unichr
    >>> txt = unichr(10).join([
    ...    "agrave = a + grave",
    ...    "# a comment",
    ...    "aacute = a + acute"
    ...    ])
    >>> result = ParseGlyphConstructionListFromString(txt)
    >>> result == ['agrave = a + grave', 'aacute = a + acute']
    True

    # Basic parsing with variable
    >>> txt = unichr(10).join([
    ...    "$name = grave",
    ...    "agrave = a + {name}",
    ...    "# a comment",
    ...    "aacute = a + acute"
    ...    ])
    >>> result = ParseGlyphConstructionListFromString(txt)
    >>> result == ['agrave = a + grave', 'aacute = a + acute']
    True

    # Parsing with ignore glyph existing glyph names without a font
    >>> font = testDummyFont()

    >>> txt = unichr(10).join([
    ...    "$name = grave",
    ...    "?agrave = a + {name}",
    ...    "# a comment",
    ...    "aacute = a + acute"
    ...    ])
    >>> result = ParseGlyphConstructionListFromString(txt)
    >>> result == ['agrave = a + grave', 'aacute = a + acute']
    True

    # Parsing with ignore glyph existing glyph names
    >>> txt = unichr(10).join([
    ...    "$name = grave",
    ...    # the next line will be ignored",
    ...    "?agrave = a + {name}",
    ...    "# a comment",
    ...    "aacute = a + acute"
    ...    ])
    >>> result = ParseGlyphConstructionListFromString(txt, font)
    >>> result == ['aacute = a + acute']
    True
    """
    txt = None
    if isinstance(source, basestring):
        if os.path.exists(source):
            with open(source) as f:
                txt = f.read()
        else:
            txt = source
    elif hasattr(source, "read"):
        txt = source.read()
    else:
        raise GlyphBuilderError("Unreadable source: '%s'" % source)

    # parse all variable out of the text
    txt, variables = ParseVariables(txt)
    try:
        # try to format the text with all the variables
        txt = txt.format(**variables)
    except KeyError as err:
        raise GlyphBuilderError("Variable %s is missing" % err)
    # split all the lines, one line -> one construction
    lines = []
    for line in txt.split("\n"):
        # strip it
        line = line.strip()
        # do nothing if it is a comment
        if line:
            if line[0] == glyphCommentSuffixSplit:
                continue
            elif line[0] == shouldCheckGlyphExists:
                if font:
                    glyphName, _ = parseGlyphName(line[1:])
                    if glyphName in font:
                        continue
                line = line[1:]
        # do nothing with empty lines when there is no line added
        if not line and not lines:
            continue
        lines.append(line)
    # remove trailing empty lines
    while lines and not lines[-1]:
        lines.pop()
    return lines


# -----
# Tests
# -----

def testDummyGlyph(glyph, i):
    add = 20 * i
    pen = glyph.getPen()
    pen.moveTo((100, 100))
    pen.lineTo((200 + add, 100 + add))
    pen.lineTo((200 + add, 200 + add))
    pen.closePath()


def testDummyFont():
    from defcon import Font
    font = Font()
    for i, glyphName in enumerate(("a", "grave", "f", "i", "agrave")):
        font.newGlyph(glyphName)
        testDummyGlyph(font[glyphName], i)
        font[glyphName].width = 60 + 10 * i
    return font


def testDigestGlyph(glyph):
    from fontPens.digestPointPen import DigestPointPen
    pen = DigestPointPen()
    glyph.drawPoints(pen)
    return (
        glyph.name,
        glyph.width,
        glyph.unicode,
        glyph.markColor,
        glyph.note,
        pen.getDigest()
    )


def testGlyphConstructionBuilder_GlyphAttributes():
    """
    >>> font = testDummyFont()

    >>> result = GlyphConstructionBuilder("agrave = a + grave", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 0, 0), None)))

    # unicode

    >>> result = GlyphConstructionBuilder("agrave = a + grave | 00E0", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, 224, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 0, 0), None)))

    # mark

    >>> result = GlyphConstructionBuilder("agrave = a + grave ! 1, 0, 0, 1", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, (1.0, 0.0, 0.0, 1.0), '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 0, 0), None)))

    # note

    >>> result = GlyphConstructionBuilder("agrave = a + grave # this is a note", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, 'this is a note', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 0, 0), None)))

    # width

    >>> result = GlyphConstructionBuilder("agrave = a + grave ^ 300", font)
    >>> testDigestGlyph(result)
    ('agrave', 300.0, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 0, 0), None)))

    # left and right margin

    >>> result = GlyphConstructionBuilder("agrave = a + grave ^ 300, 200", font)
    >>> testDigestGlyph(result)
    ('agrave', 620.0, None, None, '', (('a', (1, 0, 0, 1, 200, 0), None), ('grave', (1, 0, 0, 1, 200, 0), None)))

    # all glyph attributes

    >>> result = GlyphConstructionBuilder("agrave = a + grave ^ 300, 200 ! 1, 0, 0, 1 | 00E0 # this is a note ", font)
    >>> testDigestGlyph(result)
    ('agrave', 620.0, 224, (1.0, 0.0, 0.0, 1.0), 'this is a note', (('a', (1, 0, 0, 1, 200, 0), None), ('grave', (1, 0, 0, 1, 200, 0), None)))

    # all glyph attributes, different order

    >>> result = GlyphConstructionBuilder("agrave = a + grave | 00E0 ! 1, 0, 0, 1 ^ 300, 200 # this is a note ", font)
    >>> testDigestGlyph(result)
    ('agrave', 620.0, 224, (1.0, 0.0, 0.0, 1.0), 'this is a note', (('a', (1, 0, 0, 1, 200, 0), None), ('grave', (1, 0, 0, 1, 200, 0), None)))
    """


def testGlyphConstructionBuilder_Marks():
    """
    >>> font = testDummyFont()

    >>> result = GlyphConstructionBuilder("agrave = a + grave", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 0, 0), None)))

    Multiple marks, same width, like staked accents

    >>> result = GlyphConstructionBuilder("agrave = a + grave + anotherGlyph", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 0, 0), None), ('anotherGlyph', (1, 0, 0, 1, 0, 0), None)))

    Multiple marks, adjust width, like ligatures

    >>> result = GlyphConstructionBuilder("f_f_i = f & f & i", font)
    >>> testDigestGlyph(result)
    ('f_f_i', 250, None, None, '', (('f', (1, 0, 0, 1, 0, 0), None), ('f', (1, 0, 0, 1, 80, 0), None), ('i', (1, 0, 0, 1, 160, 0), None)))
    """


def testGlyphConstructionBuilder_Positioning():
    """
    >>> font = testDummyFont()

    # set mark at 0, 0

    >>> result = GlyphConstructionBuilder("agrave = a + grave @ 0, 0", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, -100, -100), None)))

    # postion mark at center, top

    >>> result = GlyphConstructionBuilder("agrave = a + grave @ center, top", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, -10, 100), None)))

    # postion mark at center of 'i', top

    >>> result = GlyphConstructionBuilder("agrave = a + grave @i: center, top", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 20, 100), None)))

    # postion mark at center of 'i', top of 'i'

    >>> result = GlyphConstructionBuilder("agrave = a + grave @i: center, i: top", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 20, 160), None)))

    # postion mark at 100%

    >>> result = GlyphConstructionBuilder("agrave = a + grave @100%", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 100, 100), None)))

    # decompose

    >>> result = GlyphConstructionBuilder("agrave = a + grave", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 0, 0), None)))

    >>> result = GlyphConstructionBuilder("*agrave = a + grave", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('beginPath', None), ((100, 100), 'line', False, None), ((200, 100), 'line', False, None), ((200, 200), 'line', False, None), 'endPath', ('beginPath', None), ((100, 100), 'line', False, None), ((220, 120), 'line', False, None), ((220, 220), 'line', False, None), 'endPath'))

    # font guide position

    >>> font.appendGuideline(dict(x=100, y=200, angle=0, name="guide"))

    >>> result = GlyphConstructionBuilder("agrave = a + grave@guide", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 50, 0), None)))

    >>> result = GlyphConstructionBuilder("agrave = a + grave@guide,guide", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, 50, 0), None)))

    >>> result = GlyphConstructionBuilder("agrave = a + grave@0,guide", font)
    >>> testDigestGlyph(result)
    ('agrave', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('grave', (1, 0, 0, 1, -100, 0), None)))
    """


def testGlyphConstructionBuilder_kerning():
    """
    >>> font = testDummyFont()
    >>> font.kerning["a", "i"] = -100

    >>> result = GlyphConstructionBuilder("r = a & i", font)
    >>> testDigestGlyph(result)
    ('r', 150, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('i', (1, 0, 0, 1, 60, 0), None)))

    >>> result = GlyphConstructionBuilder(r"dest = a & \\i", font)
    >>> testDigestGlyph(result)
    ('dest', 50, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('i', (1, 0, 0, 1, -40, 0), None)))

    >>> result = GlyphConstructionBuilder(r"dest = a & \\foo", font)
    >>> testDigestGlyph(result)
    ('dest', 60, None, None, '', (('a', (1, 0, 0, 1, 0, 0), None), ('foo', (1, 0, 0, 1, 60, 0), None)))
    """


def testGlyphConstructionBuilder():
    """
    >>> font = testDummyFont()

    >>> result = GlyphConstructionBuilder("space = ^ 100", font)
    >>> testDigestGlyph(result)
    ('space', 100.0, None, None, '', ())

    >>> result = GlyphConstructionBuilder(">a = ^ 11, 22", font)
    >>> testDigestGlyph(result)
    ('a', 133.0, None, None, '', (('beginPath', None), ((11.0, 100), 'line', False, None), ((111.0, 100), 'line', False, None), ((111.0, 200), 'line', False, None), 'endPath'))
    >>> result.leftMargin
    11.0
    >>> result.rightMargin
    22.0

    >>> result = GlyphConstructionBuilder(">doesNotExits = ^ 11, 22", font)
    >>> testDigestGlyph(result)
    ('doesNotExits', 0, None, None, '', ())
    """


if __name__ == "__main__":
    import sys
    import doctest
    sys.exit(doctest.testmod().failed)