Анимация
JavaScript
|
Главная Библионтека Это должно работать. Испытайте ее и проверьте, что вы действительно можете использовать многосимвольные имена переменных. СНОВА ОПЕРАТОРЫ ОТНОШЕНИЙ У нас осталось последнее односимвольное ограничение - ограничение операторов отношений. Некоторые из операторов отношений действительно состоят из одиночных символов, но другие требуют двух. Это <= и >=. Я также предпочитаю Паскалевское <> для "не равно" вместо #. Как вы помните, в главе 7 я указал, что стандартный способ работы с операторами отношений - включить их в список ключевых слов и позволить лексическому анализатору отыскивать их. Но, опять, это требует выполнение полного анализа выражения, тогда как до этого мы у нас была возможность ограничить использование сканера началом утверждения. Я упомянул тогда, что мы все же можем избежать неприятностей с этим, так как многосимвольных операторов отношений немного и они ограничены в применении. Было бы легко обрабатывать их просто как специальные случаи и поддерживать их специальным способом. Требуемые изменения влияют только на подпрограммы генерации кода и процедуры Relation и ее друзей. Сперва, нам понадобятся еще две подпрограммы генерации кода: { Set D0 If Compare was <= } procedure SetLessOrEqual; begin EmitLnCSGE D0); EmitLnCEXT D0); end; { Set D0 If Compare was >= } procedure SetGreaterOrEqual; begin EmitLnCSLE D0); EmitLnCEXT D0); end; Затем измените подпрограммы анализа отношений как показано ниже: { Recognize and Translate a Relational "Less Than or Equal" } procedure LessOrEqual; begin MatchC = ); Expression; PopCompare; SetLessOrEqual; end; { Recognize and Translate a Relational "Not Equals" } procedure NotEqual; begin MatchC>); Expression; PopCompare; SetNEqual; end; procedure Less; begin Match(<); case Look of =: LessOrEqual; >: NotEqual; else begin Expression; PopCompare; SetLess; end; end; end; { Recognize and Translate a Relational "Greater Than" } procedure Greater; begin Match(>); if Look = = then begin Match(=); Expression; PopCompare; SetGreaterOrEqual; end else begin Expression; PopCompare; SetGreater; end; end; Это все, что требуется. Теперь вы можете обрабатывать все операторы отношений. Попробуйте. ВВОД/ВЫВОД Теперь у нас есть полный, работающий язык, за исключением одного небольшого смущающего факта: у нас нет никакого способа получить или вывести данные. Нам нужны подпрограммы ввода/вывода. Современное соглашение, установленное в C и продолженное в Ada и Modula-2, состоит в том, чтобы вывести I/O операторы из самого языка и просто включить их в библиотеку подпрограмм. Это было бы прекрасно, за исключением того, что мы пока не имеем никаких средств поддержки подпрограмм. В любом случае, с этим подходом вы столкнетесь с проблемой переменной длины списка параметров. В Паскале I/O операторы встроены в язык, поэтому это единственные операторы, для которых список параметров может иметь переменное число элементов. В C мы примиряемся с клуджами типа scanf и printf и должны передавать количество параметров в вызываемую процедуру. В Ada и Modula-2 мы должны использовать неудобный (и медленный!) способ отдельного вызова для каждого аргумента. Так что я думаю, что предпочитаю Паскалевский подход встраивания подпрограмм ввода/вывода, даже если мы не нуждаемся в этом. Как обычно, для этого нам нужны еще несколько подпрограмм генерации кода. Они, оказывается, самые простые из всех, потому что все, что мы делаем это вызываем библиотечные процедуры для выполнения работы. { Read Variable to Primary Register } procedure ReadVar; begin EmitLnCBSR READ); Store(Value); end; { Write Variable from Primary Register } procedure WriteVar; begin EmitLnCBSR WRITE); end; Идея состоит в том, что READ загружает значение из входного потока в D0, а WRITE выводит его оттуда. Эти две процедуры представляют собой нашу первую встречу с потребностью в библиотечных процедурах... компонентах Run Time Library (RTL). Конечно кто-то (а именно мы) должен написать эти подпрограммы, но они не являются непосредственно частью компилятора. Я даже не буду беспокоиться о том, чтобы показать здесь эти подпрограммы, так как они очевидно очень ОС-зависимы. Я просто скажу, что для SK*DOS они особенно просты... почти тривиальны. Одна из причин, по которым я не буду показывать их здесь в том, что вы можете добавлять новые виды возможностей, например приглашение в READ или возможность пользователю повторить ошибочный ввод. Но это действительно отдельный от компилятора проект, так что теперь я буду подразумевать что библиотека, называемая TINYLIB.LIB, существует. Так как нам теперь нужно загружать ее, мы должны добавить ее загрузку в процедуру Header: { Write Header Info } procedure Header; begin WriteLnCWARMST, TAB, EQU $A01E); EmitLnCLIB TINYLIB); end; Она возьмет на себя эту часть работы. Теперь нам также необходимо распознавать команды ввода и вывода. Мы можем сделать это добавив еще два ключевых слова в наш список: { Definition of Keywords and Token Types } const NKW = 11; NKW1 = 12; const KWlist: array[1..NKW] of Symbol = (IF, ELSE, ENDIF, WHILE, ENDWHILE, READ, WRITE, VAR, BEGIN, END, PROGRAM); const KWcode: string[NKW1] = xileweRWvbep; (Обратите внимание, что здесь я использую кода в верхнем регистре чтобы избежать конфликта с w из WHILE.) Затем нам нужны процедуры для обработки оператора ввода/вывода и его списка параметров: { Process a Read Statement } 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 |