Анимация
JavaScript
|
Главная Библионтека Для многосимвольной ситуации проще всего перехватывать левый ограничитель в GetChar. Мы можем "токенизировать" его прямо здесь, заменяя его одиночным символом. Давайте условимся, что мы используем ограничители C /* и */. Сначала мы должны возвратиться к методу GetCharX. В еще одной копии вашего компилятора переименуйте GetChar в GetCharX и затем введите следующую новую процедуру GetChar: { Read New Character. Intercept /* } procedure GetChar; begin if TempChar <> then begin Look := TempChar; TempChar := ; end else begin GetCharX; if Look = / then begin Read(TempChar); if TempChar = * then begin Look := {; TempChar := ; end; end; end; end; Как вы можете видеть эта процедура перехватывает каждое появление /. Затем она исследует следующий символ в потоке. Если это символ *, то мы нашли начало комментария и GetChar возвратит его односимвольный заменитель. (Для простоты я использую тот же самый символ { как я делал для Паскаля. Если бы вы писали компилятор C, вы без сомнения захотели бы использовать какой-то другой символ, не используемый где-то еще в C. Выберите что вам нравится... даже $FF, что-нибудь уникальное). Если символ, следующий за / не *, тогда GetChar прячет его в новой глобальной переменной TempChar и возвращает /. Обратите внимание, что вы должны объявить эту новую переменную и присвоить ей значение . Мне нравится делать подобные вещи с использование конструкции "типизированная константа" в Turbo Pascal: const TempChar: char = ; Теперь нам нужна новая версия SkipComment: { Skip A Comment Field } procedure SkipComment; begin repeat repeat GetCharX; until Look = *; GetCharX; until Look = /; GetChar; end; Обратите внимание на несколько вещей: прежде всего нет необходимости изменять функцию IsWhite и процедуру SkipWhite так как GetChar возвращает токен {. Если вы измените этот символ токена, тогда конечно вы также должны будете изменить символ в этих двух подпрограммах. Во-вторых, заметьте, что SkipComment вызывает в своем цикле не GetChar а GetCharX. Это означает, что завершающий / не перехватывается и обрабатывается SkipComment. В-третьих, хотя работу выполняет процедура GetChar, мы все же можем работать с символами комментариев вложенными в строки в кавычках, вызывая GetCharX вместо GetChar пока мы находимся внутри строки. Наконец, заметьте, что мы можем снова обеспечить вложенные комментарии добавив одиночное утверждение в SkipComment, точно также как мы делали прежде. ОДНОСТОРОННИЕ КОММЕНТАРИИ Пока что я показал вам как работать с любыми видами комментариев, ограниченных слева и справа. Остались только односторонние комментарии подобные используемым в ассемблере или Ada, которые завершаются концом строки. На практике этот способ проще. Единственная процедура, которая должна быть изменена - SkipComment, которая должна теперь завершаться на символе переноса строки: { Skip A Comment Field } procedure SkipComment; begin repeat GetCharX; until Look = CR; GetChar; end; Если ведущий символ - одиночный, как ";" в ассемблере, тогда мы по существу все сделали. Если это двухсимвольный токен, как "-- " из Ada, нам необходимо только изменить проверки в GetChar. В любом случае это более легкая проблема чем двухсторонние комментарии. ЗАКЛЮЧЕНИЕ К этому моменту у нас есть возможность работать и с комментариями и точками с запятой, так же как и с другими видами синтаксического сахара. Я показал вам несколько способов работы с каждым из них, в зависимости от желаемых соглашений. Остался единственный вопрос - какие из этих соглашений мы должны использовать в KISS/TINY? По причинам, которые я высказал по ходу дела, я выбираю следующее: • Точки с запятой - терминаторы а не разделители. • Точки с запятой необязательны. • Комментарии ограничены фигурными скобками. • Комментарии могут быть вложенными. Поместите код, соответствующий этим случаям в вашу копию TINY. Теперь у вас есть TINY Version 1.2. Теперь, когда мы разрешили эти побочные проблемы, мы можем наконец возвратиться в основное русло. В следующей главе мы поговорим о процедурах и передаче параметров и добавим эти важные возможности в TINY. Увидимся. 13. Процедуры ВВЕДЕНИЕ Наконец-то мы принимаемся за хорошую главу! К этому моменту мы изучили почти все основные особенности компиляторов и синтаксического анализа. Мы узнали как транслировать арифметические выражения, булевы выражения, управляющие конструкции, объявления данных и операторы ввода/вывода. Мы определили язык TINY 1.3, который воплощает все эти возможности, и написали элементарный компилятор, который может их транслировать. Добавив файловый ввод/вывод мы могли бы действительно иметь работающий компилятор, способный производить выполнимые объектные файлы из программ, написанных на TINY. С таким компилятором мы могли бы писать простые программы, способные считывать целочисленные данные, выполнять над ними вычисления и выводить результаты. Все это хорошо, но то, что у нас есть, это все еще только игрушечный язык. Мы не можем считывать и выводить даже одиночные символы текста и у нас все еще нет процедур. Эти возможности, которые будут обсуждены в следующих двух главах, так сказать отделят мужчин от игрушек. "Настоящие" языки имеют более одного типа данных и они поддерживают вызовы процедур. Более чем любые другие, именно эти две возможности дают языку большую часть его характера и индивидуальности. Как только мы предоставим их, наши языки, TINY и его преемники, перестанут быть игрушками и получат характер настоящих языков, пригодных для серъезного программирования. В течение нескольких предыдущих глав я обещал вам урок по этим двум важным темам. Каждый раз появлялись другие проблемы, которые требовали отклонения и работы с ними. Наконец у нас появилась возможность оставить все эти проблемы в покое и вернуться в основное русло. В этой главе я охвачу процедуры. В следующий раз мы поговорим об основных типах данных. ПОСЛЕДНЕЕ ОТКЛОНЕНИЕ Эта глава была необычайно трудной для меня. Причина не имеет никакого отношения непосредственно к теме... я знал, о чем хотел рассказать уже какое-то время, и фактически я представил большинство из этого на Software Development 89, в феврале. Больше это имело отношение к подходу. Позвольте мне объяснить. Когда я впервые начал эту серию, я сказал вам, что мы будем использовать некоторые "приемы" чтобы упростить себе жизнь и позволить нам получить общее представление не вдаваясь слишком подробно в детали. Среди этих приемов была идея рассмотрения отдельных частей компилятора в отдельные моменты времени, т.е. выполнения экспериментов, использующих Cradle как основу. Когда, например, мы исследовали выражения мы работали только с этой частью теории компиляции. Когда мы исследовали управляющие структуры, мы писали различные программы, все еще основанные на Cradle, для выполнения этой части. Мы включили эти понятия в полный язык довольно недавно. Эти методы служили нам действительно очень хорошо и привели нас к разработке компилятора для TINY версии 1.3. Вначале, когда я начал этот урок, я попытался основываться на том, что мы уже сделали и просто добавлять новые возможности в существующий компилятор. Это оказалось немного неудобным и сложным... слишком, чтобы удовлетворить меня. В конце концов я выяснил почему. В этой серии экспериментов я отказался от очень 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 [ 66 ] 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |