Comments 13
Я не рекомендую пересказывать своими словами куски учебников. Это здесь не в почете.
<sarcasm>Круто, теперь я смогу сделать свой идеальный язык программирования!</sarcasm>
Не обязательность ';' (разделителя операторов) решается легко?
JS, например, пытается его вставить всегда когда что-то не сходится.
Но здесь сугубо зависит от языка.
Но здесь сугубо зависит от языка.
Интересный вопрос. Вообще, можно попытаться придумать грамматику, где не окажется разделителей операторов вовсе. Но дело это неблагодарное. Скажем, из грамматики языка C так просто убрать ';' не выйдет:
Нетрудно придумать и другие примеры. Так что это неудачный вариант. Есть другой — оставить в грамматике разделители операторов, но не требовать их в коде, как это сделано в Javascript (automatic semicolon insertion). Но в этом случае появляются неприятные побочные эффекты. Потому в том же Javascript почти всегда явно ставят ';' Ну и есть Python, где роль разделителей выполняют символы перевода строки. У меня есть некоторый опыт написания кода и на Javascript, и на Python и могу сказать, что старые-добрые точки запятой из C мне нравятся куда больше, чем альтернативные решения.
a = b; *c;
a = b *c // Получилось умножение
Нетрудно придумать и другие примеры. Так что это неудачный вариант. Есть другой — оставить в грамматике разделители операторов, но не требовать их в коде, как это сделано в Javascript (automatic semicolon insertion). Но в этом случае появляются неприятные побочные эффекты. Потому в том же Javascript почти всегда явно ставят ';' Ну и есть Python, где роль разделителей выполняют символы перевода строки. У меня есть некоторый опыт написания кода и на Javascript, и на Python и могу сказать, что старые-добрые точки запятой из C мне нравятся куда больше, чем альтернативные решения.
Создание грамматики без явного разделителя операторов — очень трудоемкое дело. Приходится делать все возможные предложения языка легко различимыми, чтобы анализатор смог понять сам, где закончилось одно и началось другое. Также приходится отказываться от перечислений чего-либо(Lokken привел хороший пример). Не думаю, что отсутствие разделителя стоит всех этих усилий.
Насчет «режима паники», при котором анализатор старается вставить разделитель операторов, чтобы восстановить свою работу и продолжить анализ: разделителем не всегда должен выступать знак ";". Пример:
int a, b, c, d;
…
a = (b c) d;
Разобрав «a = (», анализатор ожидает какое-либо выражение с завершающей скобкой. Считав «b c», он выдает ошибку и пытается найти ")", чтобы закрыть данный блок и проигнорировать его, продолжив разбор. Правда, не уверен, что такое поведение присуще всем компиляторам. Вполне возможно, что оно проявляется только у единиц.
Насчет «режима паники», при котором анализатор старается вставить разделитель операторов, чтобы восстановить свою работу и продолжить анализ: разделителем не всегда должен выступать знак ";". Пример:
int a, b, c, d;
…
a = (b c) d;
Разобрав «a = (», анализатор ожидает какое-либо выражение с завершающей скобкой. Считав «b c», он выдает ошибку и пытается найти ")", чтобы закрыть данный блок и проигнорировать его, продолжив разбор. Правда, не уверен, что такое поведение присуще всем компиляторам. Вполне возможно, что оно проявляется только у единиц.
Я имел в виду — наподобие Ruby, т.е. если несколько операторов на одной строке — то обязателен ';', если разделяются crlf — не нужен.
Недостаточно завести тип лексемы «разделитель операторов» с определением (';' | '\n' | '\r')?
Недостаточно завести тип лексемы «разделитель операторов» с определением (';' | '\n' | '\r')?
Равно так как вы продумаете другой способ разделения операторов инструкций. Варианты навскидку:
— инструкция всегда начинается с одного из ключевых слов <список>, в других местах они встретиться не могут (может ошибаюсь, но некоторые диалекты FORTRAN этому критерию подходят, кажется)
— то же, но заканчиваются
— фиксированный формат инструкции (пример — ключевое слово и два обязательных параметра, или слово, а за ним в скобках список параметров — скобки служат ограничителем списка, а не инструкции :) )
— инструкция состоит из одного слова (частный случай предыдущего, реальный пример — FORTH)
— инструкции разделяются семантической завершенностью, исключающей неоднозначность.
Последний случай самый сложный, хотя бы потому что то, что мы исключим неоднозначность для парсера не означает, что мы исключим её для человека (if… if… else). Довольно просто реализуются для языков типа ассемблера, где мы не вводим новые «ключевые слова» типа имён функций/переменных и на каждом месте есть точное ожидания типа лексемы (после «NOP» однозначно следует новая инструкция, после «MOV A,» — имя регистра или 8-бит число).
— инструкция всегда начинается с одного из ключевых слов <список>, в других местах они встретиться не могут (может ошибаюсь, но некоторые диалекты FORTRAN этому критерию подходят, кажется)
— то же, но заканчиваются
— фиксированный формат инструкции (пример — ключевое слово и два обязательных параметра, или слово, а за ним в скобках список параметров — скобки служат ограничителем списка, а не инструкции :) )
— инструкция состоит из одного слова (частный случай предыдущего, реальный пример — FORTH)
— инструкции разделяются семантической завершенностью, исключающей неоднозначность.
Последний случай самый сложный, хотя бы потому что то, что мы исключим неоднозначность для парсера не означает, что мы исключим её для человека (if… if… else). Довольно просто реализуются для языков типа ассемблера, где мы не вводим новые «ключевые слова» типа имён функций/переменных и на каждом месте есть точное ожидания типа лексемы (после «NOP» однозначно следует новая инструкция, после «MOV A,» — имя регистра или 8-бит число).
На самом деле непонятно в чем тут проблемы. Все эти «проблемы» уже давно и многократно решены в существующих языках программирования. И если делать не студенческую курсовую работу, в которой нужно тупо следовать конспекту, а настоящий компилятор настоящего языка, то таких проблем просто не возникнет:) Существуют общепринятые стандарты де-факто, как разбирать if-else (else относится к ближайшему if), общепринятая последовательность операций одного приоритета (присваивания справа налево, остальные слева направо) и т.д. Если пользоваться алгоритмами рекурсивного спуска (ИМХО наиболее понятный способ построения компилятора), то все это более чем просто и прозначно программируется в явном виде.
Я говорил не про существующие языки программирования (если бы gcc не смог решить проблему «кочующего» else, то это было бы, кхм, печально), а про собственные, которые создаются с нуля. Извините, немного не понял фразу
> И если делать не студенческую курсовую работу, в которой нужно тупо следовать конспекту, а настоящий компилятор настоящего языка, то таких проблем просто не возникнет:)
Это как раз проблемы при разработке грамматики. Общепринятые стандарты есть, но это не избавляет от необходимости прорабатывать грамматику без рекурсий и прочего. А если Вам не подходит заданный в анализаторе по умолчанию приоритет операций(как в примере 3, где определенный частный случай обрабатывается особо, а если оставить всё с настройками по умолчанию, произойдет просто вызов двух функций)? При переписывании грамматики под себя как раз и можно столкнуться с данными ошибками. Другие виды ошибок описаны в статье, на которую я ссылаюсь в конце.
> И если делать не студенческую курсовую работу, в которой нужно тупо следовать конспекту, а настоящий компилятор настоящего языка, то таких проблем просто не возникнет:)
Это как раз проблемы при разработке грамматики. Общепринятые стандарты есть, но это не избавляет от необходимости прорабатывать грамматику без рекурсий и прочего. А если Вам не подходит заданный в анализаторе по умолчанию приоритет операций(как в примере 3, где определенный частный случай обрабатывается особо, а если оставить всё с настройками по умолчанию, произойдет просто вызов двух функций)? При переписывании грамматики под себя как раз и можно столкнуться с данными ошибками. Другие виды ошибок описаны в статье, на которую я ссылаюсь в конце.
Sign up to leave a comment.
Несколько проблем при создании собственного языка программирования