Каким должен быть язык программирования? Анализ и критика Описание языка Компилятор
Отечественные разработки Cтатьи на компьютерные темы Компьютерный юмор Новости и прочее

Разбор цепочек знаков операций

В языках типа Forth или APL знаки операций не могут идти подряд, они обязательно разделяются пробельными символами. Возможно, авторы этих языков просто решили упростить себе задачу, упростив разбор входного текста. Однако большинство других языков не перекладывает на программистов эту работу: её может выполнить компилятор. Нам следует поступить так же.

            Поэтому нам необходим алгоритм распознавания знаков операций, идущих во входном тексте подряд. Алгоритм должен работать на этапе лексического анализа, выдавая синтаксическому анализатору уже готовые лексемы. Как правильно разобрать цепочки, приведённые ниже?

    a ==--- b
    c +=- d
    e >>>=! f
    g !=-! h
            С одной стороны, правилами языка, которыми пользуется синтаксический анализатор, может пользоваться и анализатор лексический. Перечень правил можно, по идее, загрузить в лексер и делать разбор согласно им. Но надо ли? Есть в этом смысл? Чем меньше в лексере «интеллекта», тем он проще и быстрее.

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

            Префиксные операции не могут предшествовать инфиксным:
    операнд  -/  операнд   // унарный префиксный минус не может
                           // предшествовать бинарному инфиксному делению
            Соседство знаков операций может быть только таким (в расширенной форме Бэкуса — Наура):
    [инфиксная операция] префиксная операция {префиксная операция} 

Матрица предшествования унарных операций

Предшествующая
операция
Последующая операция
++ -- ! - ? & @
++ да нет
смысла
нет нет нет нет да
-- нет
смысла
да нет нет нет нет да
! да да нет
смысла
да нет нет да
- да да да нет
смысла
нет нет да
? да да нет нет да да нет
смысла
& да да нет нет нет нет да
@ нет нет нет нет нет
смысла
нет да

            Некоторые операции не могут соседствовать (в матрице предшествования это помечено как «нет»).
  • Цепочки «++?», «--?», «!?» и «-?» некорректны, потому что первые операции в таких сочетаниях применяются к адресу. А адресная арифметика в обсуждаемом языке урезана в целях безопасности и надёжности. Она включает в себя только две операции: получение адреса и разрешение адреса. Иные манипуляции с адресами запрещены.
  • Цепочки «?!» и «?-» некорректны, потому что вторые в цепочке операции создают временный объект. Получение адреса временного объекта некорректно, ибо объект временный.
    a = ?++ b   // получение адреса b, который ранее был увеличен на 1
    x = ?- y    // ошибка: (- y) возвращает временный объект
    
    Операция получения адреса так же не может брать адрес временного объекта после предыдущего получения адреса.
    m = ?? n    // первое получение адреса (правый «?») создаёт временный объект, 
                // второе получение адреса (левый «?») ошибочно: 
                // берётся адрес временного объекта
    
    Но в других случаях расположение рядом двух знаков операций получения адреса вполне законно, когда они определяют тип данных «адрес адреса»:
    функция (параметр: ?? _32)
    (структура
    	поле: ?? _31)
    
    В этом случае можно позволить лексическому анализатору считать данную последовательность символов «??» законной. А вывод о правильности или неправильности перекладываем с этапа лексического анализа на этап синтаксического анализа.
  • Сказанное в предыдущих пунктах во многом верно и для ссылок. В дополнение к этому, цепочки «&?» и «??» неправильны, ибо ссылка не может быть адресом адреса. Цепочку «&@» можно считать преобразованием указателя в ссылку.
  • Цепочки «@++», «@--», «@~» и «@-» некорректны, ибо операция разрешения адреса подразумевает, что она применяется к адресу. Но перед операцией «@» должны выполниться операции «++», «--», «!» или «-», которые тоже применяются к адресу, а операции над адресами в языке запрещены.
  • Цепочки «++!», «--!», «++-» и «---» некорректны, ибо правые операции в цепочке создают временные объекты, а операции инкремента и декремента не могут применяться к временным объектам. Ко всему прочему, цепочка «---» будет истолкована, как «-» и «--», а не «--» и «-».
            Так же есть операции, соседство которых бессмысленно:
  • Бессмысленно применение к операнду сначала инкремента, а затем декремента и наоборот.
    a = ++-- b
    
    и
    a = --++ b
    
    равносильны
    a = b
    
  • Нет смысла в двукратном инвертировании.
    a = !! b
    
    равносильно
    a = b
    
  • Нет смысла в двукратной смене знака.
    a = -- b
    
    К тому же эта операция будут истолкована как декремент.
  • Нет смысла в получении адреса, а затем в его разрешении и наоборот:
    // b — какой-то адрес
    a = ?@ b
    и
    a = @? b
    
    равносильны
    a = b
    
            Алгоритм распознавания цепочек знаков операций лексический анализатор должен быть таков:
  • выделяется последовательность символов, не содержащая ничего, кроме знаков операций;
  • делается разбор цепочки символов с конца, сравниваются очередные символы цепочки с элементами перечня унарных операций (от операций с самыми длинными обозначениями к операциям с односимвольным обозначением);
  • проверяется правильность цепочки на основании матрицы предшествования;
  • оставшаяся левая последовательность, от которой нельзя «отщипнуть» какую-нибудь префиксную операцию, является инфиксной операций (возможно неизвестной, т.е. определённой программистом-пользователем, либо ошибочной).
            Возможны ли при такой стратегии разбора коллизии, когда неясно, является ли последовательность повторяющихся знаков операций двумя операциями или одной операцией? Ведь одни и те же символы могут использоваться как в префиксных, так и в инфиксных операциях. Например:
  • «---» — это операции минус и инкремент или инкремента и минус? Ответ: первый вариант, поскольку от конца цепочки в первую очередь «отщипываются» операции с длинными обозначениями.
  • «--» — это инкремент или два минуса? Ответ: инкремент, как самое длинное обозначение.
  • «>>>» — это операция побитового вращения или две операции: «больше» и «сдвиг вправо»? Ответ: поскольку среди префиксных унарных операций нет операций «>>>», «>>» и «>», остаётся единственно возможная инфиксная операция «>>>».
            Предложенный выше разбор цепочек знаков операций накладывает некоторые ограничения на язык. Если мы хотим в языке иметь определяемые программистом новые операции (подобно Algol-68, в C++ такого нет), то обозначения таких операций не могут заканчиваться символами, входящими в перечень унарных операций. Однако определение новых операций — возможность небесспорная. Ведь даже переопределение существующих операций подвергается справедливой критике.

Опубликовано: 2016.06.01, последняя правка: 2018.10.29    16:02

ОценитеОценки посетителей
   █████████████████ 2 (40%)
   █████████ 1 (20%)
   █████████ 1 (20%)
   █████████ 1 (20%)

Отзывы

     2016/07/14 22:09, rst256          # 

Нет смысла в двукратном инвертировании.

Да, однозначно запретить, а то были случаи, когда в с++ такое применяли для приведения к логическому типу :-)

Если мы хотим в языке иметь определяемые программистом новые операции (подобно Algol-68, в C++ такого нет), то обозначения таких операций не могут заканчиваться символами, входящими в перечень унарных операций.

Как в случае с операцией "-", да :-)? С технической стороны тут все в порядке, однако это будет исключением из правил. Это не хорошо, придется с этим разобраться. Раз возникло исключение, значит либо эта операция не может быть однозначно распознана лексером, либо условие задано не верно. Вы доказали, что она может быть распознана, значит не совсем верно условие. Т.к. оно работает для других операций, то возможно, что данная операция чем-то от них отличается?

Нет смысла в двукратной смене знака.

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

     2017/05/05 15:13, utkin          # 

Чем меньше в лексере «интеллекта», тем он проще и быстрее.

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

Перечень правил можно, по идее, загрузить в лексер и делать разбор согласно им. Но надо ли? Есть в этом смысл?

А обязательно «загружать»? Если Лексер и Синтаксический анализатор — в рамках одной программы, то что мешает обращаться к правилам синтаксического анализатора? Про смысл — это зависит от Вас.

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

Иногда нужно попробовать другую точку зрения. Представьте, что у Вас нет унарных операций. Ну например —а это 0-а. Вот такая краткая запись. Тогда ---а это 0-(а-1) или как Вам больше нравится. Вы можете это делать, например как в OCaml символически, или уже когда анализируете лексемы. Первый вариант вроде как проще, второй классический и «мощней» (в смысле можно больше наоптимизировать).

Бессмысленно применение к операнду сначала инкремента, а затем декремента и наоборот.
a = ++-- b

Символическая замена тут по OCaml прям вот просится.

     2017/05/15 11:10, Автор сайта          # 

Большое количество операций сравнения косвенно свидетельствует о сложной логике, оно же замедляет работу процессора — из-за большого количества ветвлений. В общем случае можно говорить о простоте, как о спутнице скорости.

Если Лексер и Синтаксический анализатор — в рамках одной программы, то что мешает обращаться к правилам синтаксического анализатора?

Правила хорошего тона. Если лексический анализ и синтаксический разделены (существуют в виде разных функций одной программы), то логично разделить их данные.

     2017/05/16 14:52, utkin          # 

Можно дать возможность "попросить" один объект представить сведения другому, инкапсулировав их в классе. Все красиво и никто никому не мешает.

     2017/09/16 02:42, Comdiv          # 

На мой взгляд это ужасно. Стоит ли рассуждать о довольно сложной логике распознавания лексем, если ещё нет толкового описания самого языка? Или я просто его не нашёл? Если же грамотно выбрать необходимый минимум лексем, то будет достаточно простого механизма распознавания без "таблицы осмысленности".

     2017/09/16 22:50, Автор сайта          # 

Вообще-то считается, что лексический анализ проще синтаксического. Лексемы чаще всего распознаются регулярными грамматиками (типа 3 — по иерархии Хомского) — самыми простыми из формальных грамматик. Так что сложность распознавания лексем, на мой взгляд, Вами преувеличивается. Описания пока что и вправду нет. Но есть стремление следовать алголоподобной традиции языков, в которой находится место и приоритетам операций, и традиционной записи вида x = 2+2*2. Количество лексем конечно желательно уменьшать, но до определённого предела. В алголоподобных языках есть два вида минуса: унарный и бинарный. От которого надо избавиться в угоду простоте? Да они оба нужны, таковы сложившиеся традиции.

     2017/09/18 11:50, Comdiv          # 

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

Так же, анализ контекстно свободной грамматики (синтаксис) сложней разбора регулярных выражений (лексика) только тем, что в нём добавляются рекурсивные определения. Если Вы делаете разбор, к примеру, с помощью рекурсивного спуска, то отличия можете даже не заметить. Попробуйте, не рассуждайте отстранённо.

     2017/09/18 22:30, Автор сайта          # 

Процитирую Евгения Александровича Зуева, «Редкая профессия»:

в синтаксисе есть неоднозначности. Это надо оценить: в Стандарте (!) языка программирования прямо написано, что некоторые конструкции можно трактовать двояко — либо как объявление, либо как оператор! В несколько упрощенном виде формулировка из стандарта выглядит так: "выражение, содержащее в качестве своего самого левого подвыражения явное преобразование типа, которое записано в функциональном стиле, может было неотличимо от объявления, в котором первый декларатор начнается с левой круглой скобки". Классический пример: что такое T(a); если T — некоторый тип? С одной стороны, это как бы объявление переменной с именем a, тип которой задан как T. С другой — конструкцию можно трактовать как преобразование типа уже объявленной где-то ранее переменной a к типу T. Все дело в том, что в Си++ статус операторов и объявлений полностью уравнен; последние даже и называются declaration-statements — операторы-объявления, то есть традиционные операторы и объявления могут записываться вперемежку. Все же радости с круглыми скобками перекочевали в Си++ прямо из Си, в котором типы конструируются подобно выражениям, и тривиальное объявление можно задать либо как "int a;", либо как "int(a);". Все это понятно, но от этого не легче. И такой язык любят миллионы программистов?! Мир сошел с ума. Яду мне, яду!..

Смысл правил разрешения неоднозначностей сводится, по существу, к поразительной фразе, простодушно выведенной в "Зеленой книге": "если конструкция выглядит как объявление, то это и есть объявление. В противном случае это оператор". Иными словами, чтобы разрешить неоднозначность, следует рассмотреть всю конструкцию целиком; фрагмент "T(a)" для анализа недостаточен — за ним сразу может следовать либо точка с запятой, тогда выбор делается в пользу объявления, либо "что-то ещё". Например, вся конструкция может выглядеть как "T(a)->m = 7;" или "T(a)++;" — это, конечно, операторы (точнее, операторы-выражения, в терминах стандарта). Ну а как понимать следующее: "T(e)[5];" или "T(c)=7;"? А это, будьте уверены, ещё не самые разительные примеры — загляните в разд. 6.8 Стандарта.

Человеку хорошо, он ко всему привыкает, рано или поздно он разберется, но как заставить анализатор понимать эту чехарду? Пока он не доберется до точки с запятой, он, в общем случае, ничего не сможет сказать о конструкции. Друзья, не пишите объявления, которые невозможно отличить от операторов! Пожалейте компилятор, ему же тяжело! Кроме того, можно запросто ошибиться и самому...

Несколько дней прошли в бесплодных попытках выразить неоднозначности на входном языке YACC. Выход был похоже, только в организации просмотра вперед, причем на заранее не известное количество лексем. Алгоритм разбора, заложенный в YACC, этого делать не умеет. В принципе известны и доступны системы, в которых заявлена подобная возможность, однако мы были ограничены требованием: синтаксический анализатор писать на YACCе, более того, на его версии, сделанной в одном европейском университете... Пришлось пойти на ухищрения и "сломать" классическую схему разбора: делать предварительный анализ ещё на уровне разбора лексем и, встретив левую скобку после имени типа (а ещё пойди распознай, что идентификатор — имя типа, а не какой-то другой сущности!), "отменять" автоматический анализ и организовывать "ручной" перебор последующих лексем, складывая их про запас в буфер.

Спасибо, в "Зеленой книге" подсказали схему такого анализа. Не знаем, как и благодарить, сами бы ни за что не придумали…

Как видите, в упомянутом случае оказалось, что анализ контекста лексическим анализатором не есть табу.

     2017/09/20 01:55, Comdiv          # 

При чём тут табу? Зуев решал задачу, заданную внешними обстоятельствами — стандартом С++, поэтому требовались ухищрения. Вы решаете задачу, которую сами формируете, поскольку пишите не только транслятор, но и язык. Так зачем же усложнять? Если угодно сделать сложно, то никто Вас не останавливает. Но зачем?

     2017/09/22 23:23, Автор сайта          # 

Ну да, для Зуева синтаксис C++ — это внешнее обстоятельство. А тут своя рука — владыка, что хочу, то и ворочу. Авторы языков J и K так и поступили: унарный «минус» в этих языках — это символ нижнего подчёркивания. Ну и пусть, что не интуитивно, зато компилятору легче. Не хочется идти по такому пути. К примеру, есть задумка сделать идентификаторы, состоящими из нескольких слов, между которыми есть разделители. Обычному лексеру это будет не по плечу. Но есть мысль, как сделать несложный «хак», чтобы лексер выдавал одну лексему «идентификатор» для нескольких слов.

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

     2017/09/25 01:33, Comdiv          # 

1. Я уже приводил пример грамматики, где "одиночный" минус — это лишь часть суммы, что воплощается элементарно.
2. Идентификаторы с пробелами — это совершенно тривиальная задача для обычного лексера. Но в зависимости от того, какими ещё возможностями будет наделён язык, это может привести к плохой ошибкоустойчивости.
3. Когда речь идёт о сложном алгоритме разбора для того чтобы облегчить работу программиста, на самом деле может оказываться медвежья услуга. Сложные правила разбора не только сложней воплощать, но и программисту трудней их воспринимать. Расчёт на интуитивное понимание текста не лучшее решение, когда речь о точности и о желании сотворить идеальный язык. Создателю синтаксиса нужно соблюсти баланс между интуитивным пониманием, простотой формальных правил и ошибкоустойчивостью.

     2017/10/01 23:33, Автор сайта          # 

1.1. С точки зрения C++ — это разные операции:
type operator -(type const&);			// унарная
type operator -(type const&, type const&); // бинарная
1.2. Унарный минус реализуется одной операцией NEG (для целочисленных операций, в архитектуре x86), с бинарноым минусом одна операция не всегда возможна.
1.3. Я не отвергаю Ваше предложение, я его намотал на ус.
2. И как бы вы это реализовали? Чтобы лексер видел разницу между «int a» (один идентификатор) и «int» + «a» (два идентификатора)?
3. Собственно говоря, пока нет самой грамматики, рассуждения о ней носят схоластический характер. Когда она появится — разговор можно сделать более предметным.

     2017/10/02 13:23, Comdiv          # 

1. Я уже писал, что операции разные, а знак один и тот же.
1.3. Хорошо, только учтите, что тогда будут доступны записи такого вида:
-a * b
и не будут такого:
a * -b
C моей точки зрения, это хорошо, но у других людей может быть иное мнение.

2. Грамотной грамматикой, естественно.
int a: int
int a — идентификатор
где int — тип

Мы же не хотим, чтобы это решалось на другом уровне вместе с нежелательными последствиями для понимания
int a int
Впрочем, сами идентификаторы с пробелами в языке общего назначения — тоже, на мой взгляд, затея сомнительная.

3. 1. Тогда не совсем понятно, зачем вообще была статья о разборе цепочек операций
3. 2. Не совсем, если грамматика будет создаваться без учёта, того как она может быть воплощена, в фантазиях можно зайти слишком далеко

     2017/10/02 13:27, Comdiv          # 

Кстати, вот регулярная грамматика для идентификаторов с пробелами для того, чтобы можно было прочувствовать её на пальцах:
%%
[a-z]+([ ][a-z]+)* puts(yytext);
. ;
%%
Сохраняете её в wf.l и собираете командой:
lex -o wf.c wf.l && cc wf.c -lfl -o wf

     2017/10/04 22:17, Автор сайта          # 

Идентификаторы типа «СКАЗОЧНОЕБАЛИ» смотрятся неестественно. Разделители облегчат чтение и устранят двусмысленности.

Хорошо, давайте уточню постановку задачи насчет идентификаторов, состоящих из нескольких слов. 1) Слова могут быть разделены последовательностью пробелов или символов табуляции. 2) Лидирующие слова, если они являются ключевыми словами или именами встроенных типов. В этом случае не всё так просто. Если поставить задачу различать не только встроенные типы, но и пользовательские, то это уже контекстно-зависимая грамматика. Ещё сложнее, если тип не указан явно, а хранится в типовой переменной.

     2017/10/06 00:03, Comdiv          # 

Чем Вам не нравится СКАЗОЧНОЕ_БАЛИ? Не нужно создавать проблемы на ровном месте.
wf.l можете подредактировать под свои нужды. Если же Вы вступаете на дорогу контекстно зависимой грамматики, то вряд ли у Вас получится хороший язык.

     2017/10/06 00:03, Автор сайта          # 

Чем Вам не нравится СКАЗОЧНОЕ_БАЛИ?

Тем, что когда убраны естественные разделители, то обратная расстановка разделителей, выполняемая в мозгах, может быть неожиданной. Ведь пробел можно вставить не только между «Е» и «Б», но и «О» и «Е».

     2017/10/06 00:14, Comdiv          # 

А знак нижнего подчёркивания мозг не замечает? Почему его недостаточно в ЯП?

     2017/10/09 21:03, Автор сайта          # 

А знак нижнего подчёркивания мозг не замечает?

У кого как. Почему-то всё равно пишут «GetElementById». Подобными примерами кишит любая библиотека. Видимо, нижнее подчёркивание — плохой заменитель пробела, раз придерживаются стиля CamelCase

     2017/10/10 13:32, Comdiv          # 

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

     2017/10/26 22:35, Автор сайта          # 

А мне ситуация видится по-другому: есть и такая попытка обойтись без разделителей, и сякая, и третья, и десятая (CamelCase, lowerCamelCase, Snake case, kebab-case, Train-Case, SCREAMING_SNAKE_CASE), а счастия всё нет. А можно прекратить метания и вернуться к докомпьютерным истокам: разные слова надо разделять пробелами. Человеческий глаз привык к этому

     2018/05/28 18:22, Александр Коновалов aka Маздайщик          # 

Существует алгоритм разбора выражений с префиксными, инфиксными и постфиксными операторами. Причём допускаются случаи, когда один знак операции может быть и одноместным и двуместным (как например «минус»). Алгоритм описан вот здесь на странице 23: http://refal.botik.ru/library/refal2014_issue-I.pdf

Алгоритм управляется только таблицей приоритетов, т.е. допускает возможность пользователям определять свои операции (которые тоже могут быть одновременно двуместными и одноместными).

Насчёт идентификаторов с пробелами. Я согласен с мнением Comdiv’а: нужно тогда описывать грамматику так, чтобы два идентификатора (включая зарезервированные идентификаторы для ключевых слов) никогда не стояли рядом:
text length : unsigned long int;
text : utf8 string;
Как вариант, можно понятие «идентификатор» перенести с уровня лексики на уровень синтаксиса. Лексической единицей будет «слово» — последовательность букв и цифр, начинающаяся с буквы, синтаксической единицей — идентификатор — последовательность слов и чисел, начинающаяся со слова.

Ключевыми словами нужно будет считать не зарезервированные идентификаторы, а зарезервированные слова — они будут просто разделителями:
if text length > max length then
text length := max length
endif

(if text length > max length
text length = max length
)
Кстати, если подумать, то в Паскаль можно добавить идентификаторы с пробелами как последовательности слов, неоднозначностей вроде быть не должно. При этом придётся запретить идентификаторам содержать внутри себя зарезервированные слова языка: if, begin, end…

     2018/05/30 17:14, Автор сайта          # 

За ссылку спасибо, надо почитать. С идентификаторами, содержащими пробелы, можно поступить по-другому. Считывается слово. Оно ключевое? Если да, то отмечаем этот факт и идём дальше. Если нет, то значит начался идентификатор, и теперь он может содержать в себе много чего, в том числе ключевые слова, которые теперь ключевыми не являются. Т.е. вводим правило, что все зарезервированные слова идут вначале. Минус: лишаемся операций, записанных словами, нельзя будет писать «здесь и там», только «здесь & там».

     2018/05/31 17:50, Comdiv          # 

Хуже того, создаётся ещё поле для дополнительных ошибок. Если есть переменные "тут" и "там" и программист всё-таки забудется и напишет "тут и там", то транслятор вместо сообщения об ошибке может пропустить такой код.

     2019/01/14 07:43, utkin          # 

За ссылку спасибо, надо почитать. С идентификаторами, содержащими пробелы, можно поступить по-другому. Считывается слово. Оно ключевое? Если да, то отмечаем этот факт и идём дальше. Если нет, то значит начался идентификатор, и теперь он может содержать в себе много чего, в том числе ключевые слова, которые теперь ключевыми не являются. Т.е. вводим правило, что все зарезервированные слова идут вначале. Минус: лишаемся операций, записанных словами, нельзя будет писать «здесь и там», только «здесь & там».

Я проводил такие эксперименты. Для меня это плохой путь. Разбирайте шаблоны выражений. Тогда всё получится проще. Например, есть такой шаблон цикла:
Цикл от <выражение1> до <выражение2>
Именно в таком ключе. Если будете разбирать отдельно лексемами Цикл, от и т.д. это будет сложно в том плане, сделать именно как Вы хотите. Здесь алгоритм разбора намного проще — в выражение1 у Вас попадет все до лексемы "до". И там могут быть любые идентификаторы (если Вам требуется) даже можно так:
Цикл от переменная Цикл до переменная от
Ваш транслятор корректно прочтет такое выражение, ЕСЛИ Вы будете разбирать ШАБЛОН, а не строку с лексемами. Это из моего практического опыта.

     2019/01/15 15:45, Автор сайта          # 

Вообще-то я писал про идентификаторы, состоящие из слов, разделённых пробелами. Типа
содержимое файла = читать файл (имя файла)
При чём здесь шаблоны и циклы?

     2019/01/16 07:09, utkin          # 

Я просто Вам дал пример на циклах. Там же в примере показаны две переменных с именами: Переменная Цикл, Переменная От. То есть переменные, имена которых состоят из двух слов. И более того в именах переменных допускается использовать ключевые слова языка.

     2019/01/16 14:00, Автор сайта          # 

Да вообще эта тема сложная с пробелом в идентификаторах. И в моих предыдущих рассуждениях есть ошибки. Предлагали построить правила языка так, чтобы идентификаторы не соседствовали — но это приводит к тому, что хвост виляет собакой, язык пляшет под дудку компилятора. Не уверен, что шаблоны помогут. Очень не уверен.

     2022/05/01 20:34, stein47          # 

«---» — это операции минус и инкремент или инкремента и минус? Ответ: первый вариант, поскольку от конца цепочки в первую очередь «отщипываются» операции с длинными обозначениями.

В С кстати с точностью до наоборот. Разбор идет слева направо и сначала находится инкремент, а затем минус. Само собой такой вариант вызывает ошибку компилятора. А вот вариант вполне законный:
b = - --a;
И кстати у меня вопрос: а какой можно подобрать краткий русскоязычный синоним lvalue?

     2022/05/01 20:48, Gudleifr          # 

И кстати у меня вопрос: а какой можно подобрать краткий русскоязычный синоним lvalue?

Адресуемый.

     2022/05/02 16:10, Автор сайта          # 

Краткого обозначения нет, к сожалению. А полное я бы назвал «переменная слева от присвоения». «Адресуемый» — неплохо, но термин должен быть существительным. Много таких терминов, которые непонятно как перевести. Англичане привыкли к терминам с расплывчатым значением, а русские хотят точности.

     2022/05/02 17:36, MihalNik          # 

но термин должен быть существительным

Но не в русском языке.

     2022/05/02 20:44, stein47          # 

«переменная слева от присвоения»

"Переменная" в широком смысле. Это может быть элемент массива или запись в структуре. Объект, имеющий адрес, которому может быть присвоено значение. "Хранитель значения"?

     2022/05/02 21:34, Gudleifr          # 

Переменная" в широком смысле. Это может быть элемент массива или запись в структуре. Объект, имеющий адрес, которому может быть присвоено значение.

Крайне неудачный подбор слов:

"Переменная" — это частный случай lvalue, но не наоборот. В любых смыслах.

"Элемент массива/структуры" — это простейший случай. Может быть и наоборот — [временная] структура элементов.

"Объект" — если разрешить это слово, то ничего больше и не надо. Есть объект (адрес) и есть значение объекта (константа).

"Присвоено" — приведет к рекурсивному определению присваивания.

     2022/05/02 21:57, stein47          # 

Объект — термин слишком абстрактный. Адресуемый объект значительно лучше. Но точно ли он передаёт смысл термина lvalue?

     2022/05/02 22:41, Gudleifr          # 

Объект — термин слишком абстрактный

Как сказать. По нынешним временам речь может идти даже о получении значений слотами фрейма в процессе унификации. Например, уже в некоторых языках можно упаковать переменные во временный список и присвоить им значения в результате разбора регулярного выражения — в одном операторе.

Сложность термина "объект", скорее, в избыточной его конкретизации, как элемента объектно-ориентированного программирования.

Но точно ли он передаёт смысл

Точно передает смысл только пара бит в машинной инструкции, указывающая тип адресации. Остальное — только потребные для удобства конкретной задачи описательные соглашения.

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

     2022/05/03 12:25, MihalNik          # 

Но точно ли он передаёт смысл термина lvalue?

А точно ли lvalue передает необходимый смысл? Костыль, наспех прибитый к синтаксису это плохо:
берем другой язык, где сей объект находится справа — получаем путаницу в сравнении и обсуждении.

     2022/05/03 14:09, Автор сайта          # 

но термин должен быть существительным

Но не в русском языке.

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

"Переменная" в широком смысле.

Да, именно в широком. Потому что если переменную понимать буквально, в узком смысле, то она является тем, что изменяет значение. Но константы тоже являются переменными в широком смысле, хотя в узком смысле константа — антоним переменной.

"Переменная" — это частный случай lvalue, но не наоборот.

Так ведь переменная может быть ещё и rvalue.

Адресуемый объект значительно лучше

Что-то не приходят в голову примеры объектов, которые не адресуются. Ну может описания классов/типов не адресуются. Произвольный оператор в программе не адресуется. Ну есть ли необходимость в получении их адреса? В некоторых языках вообще нет такой сущности — адрес. Значит с формальной точки зрения там нет и адресуемых объектов.

Хотя… отдельные биты в памяти можно, в принципе, считать объектом, но у них нет адреса. Минимальной адресуемой единицей является байт.

берем другой язык, где сей объект находится справа — получаем путаницу в сравнении и обсуждении.

Да, Вы правы. Хотя в «lvalue» первая буква и означает «left» — «левый». Противоречивости в терминологии много.

     2022/05/03 14:31, Gudleifr          # 

Так ведь переменная может быть ещё и rvalue

Да, все, что имеет адрес, имеет и значение. lvalue — частный случай rvalue.

Что-то не приходят в голову примеры объектов, которые не адресуются

Именно. Но, это, если понимать "объект" по-человечески, а не согласно ООП.

В некоторых языках вообще нет такой сущности — адрес

Там есть некий эквивалент "ячейка", "хранилище"... Например, "клетка Машины Тьюринга".

Добавить свой отзыв

Написать автору можно на электронную почту
mail(аt)compiler.su

Авторизация

Регистрация

Выслать пароль

Карта сайта


Содержание

Каким должен быть язык программирования?

Анализ и критика

Описание языка

Компилятор

●  Надо ли использовать YACC, LEX и подобные инструменты

●  Выбор кодировки для компилятора

●  Раскрутка компилятора

●  Лексический анализатор

●●  Разбор цепочек знаков операций

●●  Как отличить унарный минус от бинарного

Отечественные разработки

Cтатьи на компьютерные темы

Компьютерный юмор

Новости и прочее




Последние отзывы

2024/04/23 15:57 ••• Ivan
Энтузиасты-разработчики компиляторов и их проекты

2024/04/23 00:00 ••• alextretyak
Признаки устаревшего языка

2024/04/21 00:00 ••• alextretyak
Постфиксные инкремент и декремент

2024/04/20 21:28 ••• Бурановский дедушка
Русский язык и программирование

2024/04/07 15:33 ••• MihalNik
Все языки эквивалентны. Но некоторые из них эквивалентнее других

2024/04/01 23:39 ••• Бурановский дедушка
Новости и прочее

2024/04/01 23:32 ••• Бурановский дедушка
Русской операционной системой должна стать ReactOS

2024/03/22 20:41 ••• void
Раскрутка компилятора

2024/03/20 19:54 ••• kt
О многократном резервировании функций

2024/03/20 13:13 ••• Неслучайный читатель
Надёжные программы из ненадёжных компонентов

2024/03/07 14:16 ••• Неслучайный читатель
«Двухмерный» синтаксис Python

2024/03/03 16:49 ••• Автор сайта
О неправомерном доступе к памяти через указатели

2024/02/28 18:59 ••• Вежливый Лис
Про лебедей, раков и щук