смежность как оператор - может ли с этим справиться любой лексер?

Скажем, язык определяет смежность двух математических буквенно-цифровых символов Unicode как оператор. Скажем, ????????+1 означает ???? %adj ???? + 1, где %adj обозначает любое определение смежности оператора, в данном случае умножение. Мне интересно, справится ли с этим любой существующий инструмент лексического анализа?


person D. Huang    schedule 09.01.2017    source источник
comment
Вы предполагаете, что все имена переменных состоят из одной буквы, или вы хотите, чтобы лексер устранял неоднозначность в зависимости от того, какие переменные определены (например, если xy определено как переменная, xy должно быть одним токеном, иначе их должно быть два)?   -  person sepp2k    schedule 09.01.2017
comment
Этот вопрос подробно рассматривался в документе, который, возможно, уже забыт (с пресловутая крупица соли.)   -  person shinobi    schedule 09.01.2017
comment
@shinobi: Эту статью по-прежнему интересно читать спустя почти два десятилетия, но случайные читатели должны быть предупреждены, что дата ее публикации была 1 апреля 1998 г.   -  person rici    schedule 10.01.2017
comment
@sepp2k: здесь ???? и ???? - это буквенно-цифровые символы Юникода, отличные от обычных x и y. xy рассматривается как одиночный токен, но ???????? должно означать умножение   -  person D. Huang    schedule 10.01.2017


Ответы (3)


Невидимые операторы не могут быть распознаны с помощью лексического анализа по причинам, которые должны быть более или менее очевидными. Вы можете определить наличие невидимого оператора, только проанализировав синтаксический контекст, который является ролью синтаксического анализатора.

Конечно, большинство инструментов лексического анализа позволяют выполнять произвольный код для каждой распознанной лексемы, поэтому ничто не мешает вам встроить конечный автомат или даже полный синтаксический анализатор в лексический сканер. Это редко бывает хорошим дизайном.

Если ваш язык однозначен, то в вашей грамматике нет проблем с обработкой смежности. Но нужно соблюдать некоторую осторожность. Например, вы редко хотели бы, чтобы x-4 анализировалось как умножение x и -4, но наивная грамматика, которая включала, например,

expr -> term | expr '-' term
term -> factor | term factor | term '*' factor
factor -> ID | NUMBER | '(' expr ')' | '-' factor

будет включать эту двусмысленность. Чтобы решить эту проблему, вам нужно запретить производство смежности со вторым операндом, начинающимся с унарного оператора:

expr -> term | expr '-' term
term -> factor | term item | term '*' factor
factor -> item | '-' factor
item -> ID | NUMBER | '(' expr ')'

Обратите внимание на разницу между term -> term '*' factor, которая допускает x * - y, и term -> term base, которая не допускает x - y (expr -> expr '-' term распознает x - y как вычитание).

Примеры контекстно-свободных грамматик, допускающих смежность в качестве оператора, см., например, в Awk, в котором смежность представляет собой конкатенацию строк, и в Haskell, в котором она представляет собой применение функции.


Поскольку этот вопрос возникает время от времени, на SO уже есть ряд соответствующих ответов. Вот некоторые из них:

person rici    schedule 09.01.2017

Вот один пример использования pyparsing в Python:

import pyparsing as pp

integer = pp.pyparsing_common.integer()
variable = pp.oneOf(list("abcdefghijklmnopqrstuvwxyz"))

base_operand = integer | variable

implied_multiplication = pp.Empty().addParseAction(lambda: "*")
expr = pp.infixNotation(base_operand,
                [
                    ("**", 2, pp.opAssoc.LEFT),
                    (implied_multiplication, 2, pp.opAssoc.LEFT),
                    (pp.oneOf("+ -"), 1, pp.opAssoc.RIGHT),
                    (pp.oneOf("* /"), 2, pp.opAssoc.LEFT),
                    (pp.oneOf("+ -"), 2, pp.opAssoc.LEFT),
                ])

Это предполагает, что переменные - это просто отдельные символы. Существует также некоторая подтасовка приоритета операций, чтобы заставить работать смежность, возведение в степень и опережающие знаки. Действие синтаксического анализа, добавленное к выражению implied_multiplication, должно показать вставку оператора умножения.

Вот некоторый тестовый вывод:

tests = """
    x-4
    ax**2 + bx +c
    ax**2-bx+c
    mx+b
    """
expr.runTests(tests, fullDump=False)

печатает:

x-4
[['x', '-', 4]]

ax**2 + bx +c
[[['a', '*', ['x', '**', 2]], '+', ['b', '*', 'x'], '+', 'c']]

ax**2-bx+c
[[['a', '*', ['x', '**', 2]], '-', ['b', '*', 'x'], '+', 'c']]

mx+b
[[['m', '*', 'x'], '+', 'b']]
person PaulMcG    schedule 02.04.2017

Если токены не имеют фиксированной длины, вы должны разделять соседние токены одного типа каким-либо другим токеном или пробелом. Язык программирования Gosu включает смежность для реализации " выражения", поддерживающие единицы измерения:

var length = 10m  // 10 meters

var work = 5kg * 9.8 m/s/s * 10m
print( work )  // prints 490 J

var investment = 5000 EUR + 10000 USD

var date = 1966-May-5 2:35:53:909 PM PST
person Scott    schedule 14.09.2019