Лекция
Привет, Вы узнаете о том , что такое синтаксис в программировании, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое синтаксис в программировании , настоятельно рекомендую прочитать все из категории Языки и методы программирования. Теория трансляции.
В компьютьерных науках синтаксис компьютерного языка — это правила, определяющие комбинации символов, которые считаются правильно структурированными утверждениями или выражениями на этом языке. Это относится как к языкам программирования , где документ представляет исходный код , так и к языкам разметки , где документ представляет данные.
Синтаксис языка определяет его поверхностную форму. Текстовые компьютерные языки основаны на последовательностях символов , тогда как визуальные языки программирования основаны на пространственном расположении и связях между символами (которые могут быть текстовыми или графическими). О синтаксически недопустимых документах говорят, что они содержат синтаксическую ошибку . При разработке синтаксиса языка разработчик может начать с написания примеров как допустимых, так и недопустимых строк , прежде чем пытаться выяснить из этих примеров общие правила.
Синтаксис языка программирования - это набор правил, определяющих правильное построение программного кода в данном языке. Он определяет, каким образом должны быть составлены выражения, инструкции и конструкции языка, чтобы код был понятен и выполним компьютером.
Вот несколько основных элементов синтаксиса языка программирования:
Идентификаторы: Это имена переменных, функций, классов и других сущностей в программе. Идентификаторы могут состоять из букв, цифр и некоторых специальных символов, но должны следовать определенным правилам, таким как начинаться с буквы или символа подчеркивания.
Ключевые слова: Это зарезервированные слова в языке программирования, которые имеют специальное значение и не могут быть использованы в качестве идентификаторов. Примеры ключевых слов: if, else, for, while, и так далее.
Операторы: Операторы выполняют операции над данными. Например, арифметические операторы (+, -, *, /), логические операторы (&&, ||), операторы присваивания (=) и т.д.
Выражения: Выражения составляются из операторов, идентификаторов, литералов (например, чисел или строк) и других выражений, и представляют собой вычисляемое значение. Например, "a + b", "x > 5" и "2 * (y + 3)" - это выражения.
Конструкции языка: Языки программирования имеют различные конструкции для организации кода, такие как условные выражения (if-else), циклы (for, while), функции, классы и т.д. Каждая конструкция имеет свое собственное правило синтаксиса для использования их в коде.
Комментарии: Комментарии являются текстовыми фрагментами, которые игнорируются компилятором или интерпретатором языка программирования и служат для пояснения кода или оставления заметок. Обычно комментарии начинаются с определенного символа или последовательности символов, таких как "//" для однострочных комментариев или "/* ... */" для многострочных комментариев.
Это лишь общая информация о синтаксисе языка программирования. Конкретный синтаксис может сильно отличаться в разных языках программирования. Каждый язык имеет свои правила и соглашения, которые необходимо соблюдать при написании кода на этом языке.
Таким образом, синтаксис относится к форме кода и противопоставляется семантике — значению . При обработке компьютерных языков семантическая обработка обычно идет после синтаксической обработки; однако в некоторых случаях для полного синтаксического анализа необходима семантическая обработка, и они выполняются вместе или одновременно . В компиляторе синтаксический анализ включает интерфейс , а семантический анализ включает серверную часть (и среднюю часть, если выделена эта фаза).
Синтаксис компьютерного языка обычно делится на три уровня:
Различение таким образом приводит к модульности, позволяя описывать и обрабатывать каждый уровень отдельно и часто независимо. Во-первых, лексер превращает линейную последовательность символов в линейную последовательность токенов; это известно как « лексический анализ » или «лексинг». Во-вторых, синтаксический анализатор превращает линейную последовательность токенов в иерархическое синтаксическое дерево; в узком смысле это известно как « разбор ». В-третьих, контекстный анализ разрешает имена и проверяет типы. Эта модульность иногда возможна, но во многих реальных языках более ранний шаг зависит от более позднего шага — например, лексический хак в C связан с тем, что токенизация зависит от контекста. Даже в этих случаях синтаксический анализ часто рассматривается как приближение к этой идеальной модели.
Сам этап синтаксического анализа можно разделить на две части: дерево синтаксического анализа или «конкретное синтаксическое дерево», которое определяется грамматикой, но обычно слишком подробно для практического использования, и абстрактное синтаксическое дерево (AST), которое упрощает это в удобную форму. Шаги AST и контекстуального анализа можно рассматривать как форму семантического анализа, поскольку они добавляют значение и интерпретацию синтаксиса или, альтернативно, как неформальные, ручные реализации синтаксических правил, которые было бы трудно или неудобно описывать или реализовывать формально.
Уровни обычно соответствуют уровням в иерархии Хомского . Слова на обычном языке , указанные в лексической грамматике , которая является грамматикой типа 3, обычно задается в виде регулярных выражений . Фразы написаны на контекстно-свободном языке (CFL), обычно это детерминированный контекстно-свободный язык (DCFL), указанный в грамматике структуры фразы , которая является грамматикой типа 2, обычно задается в виде правил вывода в форме Бэкуса-Наура (BNF) . ). Грамматики фраз часто специфицируются в гораздо более ограниченных грамматиках, чем полные контекстно-свободные грамматики , чтобы их было легче анализировать; в то времяАнализатор LR может анализировать любой DCFL за линейное время, простой анализатор LALR и даже более простой анализатор LL более эффективны, но могут анализировать только грамматики, правила производства которых ограничены. Об этом говорит сайт https://intellect.icu . В принципе, контекстная структура может быть описана контекстно-зависимой грамматикой и автоматически проанализирована с помощью таких средств, как грамматика атрибутов , хотя, как правило, этот шаг выполняется вручную с помощью правил разрешения имен и проверки типов и реализуется с помощью таблицы символов . который хранит имена и типы для каждой области.
Были написаны инструменты, которые автоматически генерируют лексер из лексической спецификации, записанной в регулярных выражениях, и парсер из грамматики фраз, написанной в BNF: это позволяет использовать декларативное программирование , а не процедурное или функциональное программирование. Ярким примером является пара lex - yacc . Они автоматически создают конкретное синтаксическое дерево; затем автор синтаксического анализатора должен вручную написать код, описывающий, как это преобразуется в абстрактныйсинтаксическое дерево. Контекстный анализ также обычно выполняется вручную. Несмотря на существование этих автоматических инструментов, синтаксический анализ часто выполняется вручную по разным причинам - возможно, структура фразы не является контекстно-свободной, или альтернативная реализация улучшает производительность или сообщения об ошибках, или упрощает изменение грамматики. Парсеры часто пишутся на функциональных языках, таких как Haskell , или на языках сценариев, таких как Python или Perl , или на C или C++ .
Например, (add 1 1)
это синтаксически допустимая программа на Лиспе (при условии, что функция «добавить» существует, иначе разрешение имени не удастся), добавляющая 1 и 1. Однако следующее недопустимо:
(_ 1 1) lexical error: '_' is not valid (add 1 1 parsing error: missing closing ')'
Лексер не может идентифицировать первую ошибку — все, что он знает, это то, что после создания токена LEFT_PAREN '(' оставшаяся часть программы недействительна, так как ни одно правило слова не начинается с '_'. Вторая ошибка обнаруживается в этап синтаксического анализа: синтаксический анализатор идентифицировал производственное правило «список» из-за токена '(' (как единственное совпадение) и, таким образом, может выдать сообщение об ошибке; в общем случае оно может быть неоднозначным .
Ошибки типов и ошибки необъявленных переменных иногда считаются синтаксическими ошибками, когда они обнаруживаются во время компиляции (что обычно имеет место при компиляции строго типизированных языков), хотя вместо этого принято классифицировать эти виды ошибок как семантические ошибки .
Например, код Python
а + 1
содержит ошибку типа, потому что он добавляет строковый литерал к целочисленному литералу. Ошибки типа такого рода могут быть обнаружены во время компиляции: они могут быть обнаружены во время синтаксического анализа (фразового анализа), если компилятор использует отдельные правила, которые разрешают «integerLiteral + integerLiteral», но не «stringLiteral + integerLiteral», хотя более вероятно, что компилятор будет использовать правило синтаксического анализа, которое разрешает все выражения формы «LiteralOrIdentifier + LiteralOrIdentifier», и тогда ошибка будет обнаружена во время контекстного анализа (когда происходит проверка типов). В некоторых случаях эта проверка не выполняется компилятором, и эти ошибки обнаруживаются только во время выполнения.
В языке с динамической типизацией, где тип может быть определен только во время выполнения, многие ошибки типов могут быть обнаружены только во время выполнения. Например, код Python
а + б
синтаксически действителен на уровне фразы, но правильность типов a и b может быть определена только во время выполнения, поскольку переменные не имеют типов в Python, а только значения. Хотя существуют разногласия по поводу того, следует ли называть ошибку типа, обнаруженную компилятором, синтаксической ошибкой (а не статической семантической ошибкой), ошибки типов, которые могут быть обнаружены только во время выполнения программы, всегда рассматриваются как семантические, а не синтаксические ошибки.
Синтаксис текстовых языков программирования обычно определяется с помощью комбинации регулярных выражений (для лексической структуры) и формы Бэкуса-Наура (для грамматической структуры) для индуктивного определения синтаксических категорий (нетерминалов) и терминальных символов. Синтаксические категории определяются правилами, называемыми продукциями , которые определяют значения, принадлежащие определенной синтаксической категории. Терминальные символы — это конкретные символы или цепочки символов (например, такие ключевые слова , как define , if , let или void), из которых строятся синтаксически корректные программы.
Язык может иметь разные эквивалентные грамматики, такие как эквивалентные регулярные выражения (на лексическом уровне) или разные правила фраз, которые генерируют один и тот же язык. Использование более широкой категории грамматик, такой как грамматики LR, может позволить использовать более короткие или простые грамматики по сравнению с более ограниченными категориями, такими как грамматика LL, для которых могут потребоваться более длинные грамматики с большим количеством правил. Разные, но эквивалентные грамматики фраз дают разные деревья синтаксического анализа, хотя базовый язык (набор действительных документов) один и тот же.
Ниже приведена простая грамматика, определенная с использованием нотации регулярных выражений и расширенной формы Бэкуса–Наура . Он описывает синтаксис S-выражений , синтаксис данных языка программирования Lisp , который определяет продукцию для синтаксических категорий выражение , атом , число , символ и список :
expression = atom | list
atom = number | symbol
number = [+-]?['0'-'9']+
symbol = ['A'-'Z']['A'-'Z''0'-'9'].*
list = '(', expression*, ')'
Эта грамматика определяет следующее:
Здесь десятичные цифры, символы верхнего и нижнего регистра и круглые скобки являются конечными символами.
Ниже приведены примеры правильно сформированных последовательностей токенов в этой грамматике: ' 12345
', ' ()
', ' (A B C232 (1))
'
Грамматика, необходимая для определения языка программирования, может быть классифицирована по ее положению в иерархии Хомского . Грамматика фраз большинства языков программирования может быть определена с использованием грамматики типа 2, т. е. они являются контекстно-свободными грамматиками , хотя общий синтаксис является контекстно-зависимым (из-за объявлений переменных и вложенных областей видимости), следовательно, Type- 1. Однако есть исключения, и для некоторых языков фразовая грамматика относится к типу 0 (полному по Тьюрингу).
В некоторых языках, таких как Perl и Lisp, спецификация (или реализация) языка позволяет создавать конструкции, которые выполняются на этапе синтаксического анализа. Более того, в этих языках есть конструкции, которые позволяют программисту изменять поведение синтаксического анализатора. Эта комбинация эффективно стирает различие между синтаксическим анализом и выполнением и делает синтаксический анализ неразрешимой проблемой в этих языках, а это означает, что фаза синтаксического анализа может не завершиться. Например, в Perl можно выполнять код во время синтаксического анализа с помощью оператора BEGIN
, а прототипы функций Perl могут изменить синтаксическую интерпретацию и, возможно, даже синтаксическую достоверность остального кода. В просторечии это называется «только Perl может анализировать Perl» (поскольку код должен выполняться во время анализа и может изменять грамматику) или, более строго, «даже Perl не может анализировать Perl» (потому что это неразрешимо). Точно так же макросы Лиспа, представленные синтаксисом, defmacro
также выполняются во время синтаксического анализа, а это означает, что компилятор Лиспа должен иметь всю систему времени выполнения Лиспа. Напротив, макросы C представляют собой просто замену строк и не требуют выполнения кода.
Синтаксис языка описывает форму допустимой программы, но не предоставляет никакой информации о значении программы или результатах выполнения этой программы. Значение, придаваемое комбинации символов, обрабатывается семантикой (либо формальной , либо жестко закодированной в эталонной реализации ). Не все синтаксически правильные программы семантически правильны. Многие синтаксически правильные программы, тем не менее, имеют неправильный формат в соответствии с правилами языка; и может (в зависимости от спецификации языка и надежности реализации) привести к ошибке при переводе или выполнении. В некоторых случаях такие программы могут демонстрировать неопределенное поведение.. Даже когда программа хорошо определена в языке, она все равно может иметь значение, не предназначенное для человека, который ее написал.
Используя в качестве примера естественный язык , может быть невозможно придать значение грамматически правильному предложению, или предложение может быть ложным:
Следующий фрагмент языка C синтаксически корректен, но выполняет операцию, которая не определена семантически (поскольку p
это нулевой указатель , операции и не имеют смысла): p->real
p->im
complex *p = NULL;
complex abs_p = sqrt (p->real * p->real + p->im * p->im);
В качестве более простого примера
int x;
printf("%d", x);
синтаксически корректен, но не определен семантически, так как использует неинициализированную переменную . Несмотря на то, что компиляторы для некоторых языков программирования (например, Java и C#) могут обнаруживать такие ошибки неинициализированных переменных, их следует рассматривать как семантические , а не синтаксические ошибки.
Чтобы быстро сравнить синтаксис различных языков программирования, взгляните на список «Hello, World!». примеры программ :
Исследование, описанное в статье про синтаксис в программировании, подчеркивает ее значимость в современном мире. Надеюсь, что теперь ты понял что такое синтаксис в программировании и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Языки и методы программирования. Теория трансляции
Комментарии
Оставить комментарий
Языки и методы программирования. Теория трансляции
Термины: Языки и методы программирования. Теория трансляции