Pull to refresh

Comments 29

Теперь дело осталось за малым — компилятор прикрутить.
Компилятора у меня в планах нет. А вот сделать что-то вроде синтаксического анализатора (типа JSLint) было бы интересно.
А для мультиплексирования лексем также можно использовать механизм каналов, встроенный в ANTLR v3, каждому распознанному токену можно сопоставить $channel.
Если я правильно помню, в этом случае один лексер может отправлять лексемы после распознания в разные каналы. Целью мультиплексирования было именно научить распознавать лексемы по-разному (в зависимости от контекста «код»/«текстовый мусор»), а не сортировать их по разным потокам.

Поэтому само по себе разделение лексем по каналам задачи бы не решило.
вы писали:
Изначально наиболее серьезным казался вопрос, как описать формльным языком тот факт, что исходным кодом, подлежащим распознаванию, следует считать лишь то, что находится в секции
<?php … ?>

сейчас вы уточнили, что вам нужно и в текстовом мусоре разобраться, это конечно меняет дело, и я с вами соглашусь.
Мне вот интересно, что мешало вам сделать в PHP token_get_all() от файла, а потом уже хоть на Java обрабатывать получившиеся токены :)?
UFO just landed and posted this here
А вы список их токенов видели? :) Это же зоопарк.
Да и потом, написать лексер труда большого не составило; так или иначе потом все равно писать парсер с правилами разбора.
Я не только видел, но и писал форматтер на основе этих токенов.
Интересно, что тернарный оператор в PHP имеет приоритет ниже операторов присваивания. То есть конструкция вида

a = test()? b = c: d = e;


Хотелось бы уточнить, тренарный оператор имеет приоритет ниже или выше?

Проверяем:
$a = 'old a';
$b = 'old b';
$c = 'old c';

$a = true ? $b = 'new b' : $c = 'new c';

Результат: $a === 'new b', $b === 'new b', $c === 'old c';

Второй эксперимент при тех же начальных условиях:
$a = false ? $b = 'new b' : $c = 'new c';

Результат: $a === 'new c', $b === 'old b', $c === 'new c';
Возможно, я неточно выразился. Присваивание внутри тернарного оператора в PHP допустимо (без скобок), что отличается от остальных C-style языков.
То есть в вашем примере
$a = true ? $b = 'new b' : $c = 'new c';

При вычислении правого дерева (то, что правее $a = ) Сначала вычисляются присваивания в каждом узле тернарного оператора, и лишь затем возвращается значение тернарного оператора. По такой логике приоритет присваивания выше. Или я неправ?
С логикой работы php согласен, мы оба правильно её понимаем. Но с формулировкой не согласен. Моё понимание формулировок следующее:
(рассматриваю всю строку, а не только правую часть после $a)

1. Формулировка "тернарный оператор в PHP имеет приоритет ниже операторов присваивания" эквивалентна следующей логике: сначала выполняем все присваивания: $a = true, $b = 'new b', $c = 'new c', затем вычисляем выражение тернарного оператора, который сводится к сроке «true? 'new b': 'new c'», что в итоге будет эквивалентно 'new b', но, к сожалению, этот результат никуда не будет записан или выведен.

2. Формулировка "тернарный оператор в PHP имеет приоритет выше операторов присваивания" эквивалентна следующей логике: найдём в строке тернарный оператор и «выполним» его, затем перейдём к присваиваниям, оставшимся за скобками тернарного оператора. В данном случае интерпретатор языка php определит фрагмент «true? $b = 'new b': $c = 'new c';» как выражение тернарного оператора. Далее, следуя логике этого оператора, нужно вычислить выражение перед знаком "?", а затем одно из выражений после знака "?". Это суть механизма тернарного оператора, а не приоритет присваивания над тернарным оператором.

Таким образом, продалжаю настаивать, что правильно говорить "тернарный оператор в PHP имеет приоритет выше операторов присваивания" в php.

Кстати, в вашем комментарии закралась ещё одна неточность:
Сначала вычисляются присваивания в каждом узле тернарного оператора

Выражения вычисляются не в каждому узле тернарного оператора, а только в том узле после знака "?", который должен быть возвращён как результат тернарного оператора. Другой узел не вычисляется.
Формулировка «тернарный оператор в PHP имеет приоритет выше операторов присваивания» эквивалентна следующей логике: найдём в строке тернарный оператор и «выполним» его, затем перейдём к присваиваниям, оставшимся за скобками тернарного оператора.

Боюсь, вы не совсем правильно себе представляете смысл приоритета в терминах разбора выражения. В парсере нет такой логики «найди оператор X и выполни его, а затем найди оператор Y..»
В парсере есть набор правил вида
expression ::= multiplication ('+' | '-' multiplication)*
multiplication ::= operand ('*' | '/' operand)*
operand ::= NUMBER | '(' expression ')'


В приведенном выше примере у умножения и деления приоритет выше: сначала ожидается правило суммирования, а уже при разборе правила суммирования может появиться сложение, а не наоборот.
При разборе правила «тернарный оператор» может появиться правило «присваивание», значит присваивание имеет более высокий приоритет, чем тернарный оператор.

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

Да, тут согласен, вычисляется только то одно выражение из двух, в зависимости от условия в тернарнике. Но имхо, это срабатывает оптимизатор, поэтому это не аргумент за или против приоритетов операций.
Боюсь, вы не совсем правильно себе представляете смысл приоритета в терминах разбора выражения. В парсере нет такой логики «найди оператор X и выполни его, а затем найди оператор Y..»

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

Но разговорной или письменной речи относительно программирования я бы говорил «тернарный оператор имеет больший приоритет операции, чем присваивание». Кстати, заглянул в официальную документацию PHP — там такой же подход php.net/manual/en/language.operators.precedence.php (тернарный оператор на одну строку выше чем присваивание в таблице приоритетов).

Но имхо, это срабатывает оптимизатор, поэтому это не аргумент за или против приоритетов операций.

Согласен, эта моя придирка к формулировкам никак не относилась к основной теме приоритета операций :)
Ниже mark_ablov таки нашел ошибку. Так что спасибо за дискуссию :)
UFO just landed and posted this here
Выражать одну и ту же семантику синтаксически разными способами вполне допустимо, по-моему. Человек должен думать над семантикой своего «послания» машине, а не над тем как её выразить в ограничениях синтаксиса. Должен быть (и то сомнительно) один способ оптимально в конкретных условиях сделать то, что нужно, но может быть множество способов это «нужно» выразить так как удобнее. Удобнее писать, удобнее читать, удобнее тестировать, удобнее отлаживать и т. п. Всё зависит от приоритетов человека.

UFO just landed and posted this here
К неоднозначности со стороны машины он ведёт, по-моему, только в случае грубых ошибок в проектировании (или вообще отсутствии такого этапа) синтаксиса. Усложнение «понимания» (трансляции) — да, но затем и машина, чтобы «думать» за нас. Взять ассемблер или FORTH — трансляторы в килобайты умещаются, всё транслируется практически один к одному, чуть ли не через таблицы без всякого разбора. Но вы же не будете на них писать, скажем, бухгалтерию?

Насчёт неоднозначности со стороны человека — спорно. Вернее если человек языка не знает, то он, конечно, может написать на нём что-то, что машина выполнит не так, как он имел в виду. Ну, так даже на русском я могу сказать что-то так, что меня поймут не правильно, например употребив слово, значение которого в данном контексте я не знаю (как в анекдоте: «Сразу бы сказали, что „козлы“ слово в тюрьме очень плохое, а то налетели как петухи...»). А говнокод я могу и на ассемблере написать (Идеальный, по-моему, для вас язык, нет? Или сразу в машинных кодах пишите, чтоб исключить любую возможность неоднозначной для машины «трансляции»?).

По мне так идеальный язык должен исключать неоднозначности чтения кода человеком, очевидное понимание им этого кода семантики. Дальше дело техники — научить понимать этот язык машину.
> отличается от остальных C-style языков.
Это Java и C/C++/Obj-C?
Ибо в JavaScript поведение аналогично:
var a = 3;
var b = true ? a = 1 : 2;
console.log(a); // 1
console.log(b); // 1


А вообще высказывание некорректно полностью :)
Ибо приоритет здесь не используется.
Приоритет задаётся так: если правило операции A порождает B (возможно с помощью промежуточных нетерминалов), то операция A ниже по приоритету чем B, так B как свернется раньше, соответственно и операция будет выполнена тоже раньше.
В данном случае то что внутри тернарного оператора никак не связано с его приоритетом.
Описать можно примерно так:
Expression = AssignExpr
AssignExpr = T_IDENT '=' AssignExpr | TernaryExpr
TernaryExpr = Expression ? Expression : Expression

То есть внутри тернарного оператора приоритет начинается исчисляться заново, и на сам оператор уже никак не опирается.
Спасибо за ценное замечание.

> отличается от остальных C-style языков.
Это Java и C/C++/Obj-C?
Ибо в JavaScript поведение аналогично

Проверял на gcc. В Java присваивание внутри тернарного оператора тоже допустимо.

То есть внутри тернарного оператора приоритет начинается исчисляться заново, и на сам оператор уже никак не опирается.

Похоже, действительно начинается заново. Следующий код так же скомпилировался (а у AND приоритет еще ниже):
<?php
$a = $b > 1 ? $c AND $d : $e AND $f;


В тексте исправлю.
Как у Java удается превышать производительность по сравнению с С?
Полагаю речь идет о специально подготовленных тестах, с замерами на уже «разогретой» JVM.
Но на самом деле интересно было бы пощупать эту «игрушку» в действии.
Отсутствие формальной спецификации языка

php-5.4.0.tar.gz\php-5.4.0\Zend\zend_language_parser.y
не подойдет?
Для реверс-инжиниринга — вполне. Но это не документ, согласитесь.
Его можно использовать в качестве основы, например, для расширения функциональности CheckStyle или для реализации собственного php beautifier.
<
Использовал для этого такую штуку pear.php.net/package/PHP_CodeSniffer
Да, я смотрел исходники CodeSniffer. Чисто архитектурно они заранее проигрывают чекстайлу.

CheckStyle честно разбирает исходник и строит синтаксическое дерево, которое затем и анализируется Checker-классами. CodeSniffer использует только лексер, поэтому я сильно сомневаюсь, что его средствами можно построить вменяемые правила проверки стиля.
Sign up to leave a comment.

Articles