| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- MODULE Parser;
- IMPORT Out, Files, S := Scanner, ST := SymTable, Gen := Generator;
- VAR sym: INTEGER;
- dc: INTEGER; (*Счётчик данных (data counter)*)
- dummy: ST.Object; (*Болванка, см. QualIdent*)
- PROCEDURE ^Expression(VAR x: Gen.Item);
- (*импорт = идент [":=" идент].*)
- PROCEDURE Import;
- VAR ident1, ident2: ARRAY 32 OF CHAR;
- BEGIN
- IF sym = S.ident THEN
- ident1 := S.id;
- S.Get(sym);
- IF sym = S.becomes THEN
- S.Get(sym);
- IF sym = S.ident THEN
- ident2 := S.id;
- S.Get(sym)
- ELSE
- S.Mark("Ожидается идентификатор")
- END
- ELSE
- ident2 := ident1
- END;
- ST.Import(ident1, ident2)
- ELSE
- S.Mark("Ожидается идентификатор")
- END
- END Import;
- (*СписокИмпорта = IMPORT импорт {"," импорт} ";".*)
- PROCEDURE ImportList;
- BEGIN
- IF sym = S.import THEN
- S.Get(sym);
- Import;
- WHILE sym = S.comma DO
- S.Get(sym);
- Import
- END;
- IF sym = S.semicolon THEN
- S.Get(sym)
- ELSE
- S.Mark("Ожидается точка с запятой")
- END
- END
- END ImportList;
- PROCEDURE Type(VAR tp: ST.Type);
- VAR o: ST.Object;
- BEGIN
- tp := ST.intType; (*Тип по умолчанию, используется в случае ошибки*)
- IF sym = S.ident THEN
- o := ST.ThisObj();
- S.Get(sym);
- IF o = NIL THEN
- S.Mark("Нет такого типа")
- ELSIF o.class # ST.Typ THEN
- S.Mark("Это не тип")
- ELSE
- tp := o.type
- END
- ELSE (*!TODO учесть ARRAY, RECORD и др.*)
- S.Mark("Ожидается тип")
- END
- END Type;
- (*Объявления = [CONST {ОбъявлениеКонстант ";"}]
- [TYPE {ОбъявлениеТипов ";"}]
- [VAR {ОбъявлениеПеременных ";"}]
- {ОбъявлениеПроцедуры ";"}.
- ОбъявлениеПеременных = Идентификаторы ":" Тип.
- Идентификаторы = Идентификатор {"," Идентификатор}.
- Тип = уточнИдент | ТипМассив | ТипЗапись | ТипУказатель | ПроцедурныйТип.
- Но в начале будет только VAR, поэтому используется упрощённый вариант:
- Объявления = [VAR {ОбъявлениеПеременных ";"}].
- ОбъявлениеПеременных = Идентификаторы ":" Тип.
- Идентификаторы = Идентификатор {"," Идентификатор}.
- Тип = Идентификатор.*)
- PROCEDURE Declarations(VAR varsize: INTEGER);
- VAR tp: ST.Type;
- i, n: INTEGER;
- o: ST.Object;
- names: ARRAY 30, 32 OF CHAR;
- BEGIN
- IF sym = S.var THEN (*1. Если считано слово VAR, то:*)
- S.Get(sym); (*Переходим к названию первой переменной*)
- WHILE sym = S.ident DO
- names[0] := S.id;
- S.Get(sym); (*Переходим к запятой или к двоеточию*)
- n := 1;
- WHILE sym = S.comma DO
- S.Get(sym); (*Переходим к названию следующей перменной*)
- IF sym = S.ident THEN
- IF n < LEN(names) THEN
- names[n] := S.id;
- INC(n)
- ELSE
- S.Mark("Слишком много переменных")
- END
- ELSE
- S.Mark("Ожидается идентификатор")
- END;
- S.Get(sym) (*Переходим к запятой или к двоеточию*)
- END;
- IF sym = S.colon THEN
- S.Get(sym);
- Type(tp);
- FOR i := 0 TO n - 1 DO
- o := ST.NewObj(names[i], ST.Var);
- o.type := tp;
- o.val := varsize;
- INC(varsize, 4);(*!TODO учитывать размер типа и выравнивание*)
- Out.String("Объявлена переменная "); Out.String(names[i]);
- Out.String(" типа "); ST.OutType(o.type);
- Out.String(" (адрес "); Out.Int(o.val, 0); Out.String(").");
- Out.Ln
- END;
- IF sym = S.semicolon THEN
- S.Get(sym)
- ELSE
- S.Mark("Ожидается точка с запятой")
- END
- ELSE
- S.Mark("Ожидается двоеточие")
- END
- END
- END
- END Declarations;
- (*УПРОЩЕНО: множитель = число | литера | идент | "(" выражение ")".*)
- PROCEDURE Factor(VAR x: Gen.Item);
- VAR o: ST.Object;
- BEGIN
- IF sym = S.int THEN
- x.mode := ST.Const;
- x.type := ST.intType;
- x.val := S.ival;
- Out.String("Прочитано число ");
- Out.Int(S.ival, 0); Out.Ln;
- S.Get(sym)
- ELSIF sym = S.char THEN
- x.mode := ST.Const;
- x.type := ST.charType;
- x.val := S.ival;
- Out.String("Прочитана литера ");
- Out.Char(CHR(S.ival)); Out.Ln;
- S.Get(sym)
- ELSIF sym = S.string THEN
- x.mode := ST.Const;
- x.type := ST.charType;
- x.val := ORD(S.sval[0]);
- IF S.slen = 1 THEN
- Out.String("Прочитана литера ");
- Out.Char(S.sval[0]); Out.Ln
- ELSE
- S.Mark("Допускается только CHAR")
- END;
- S.Get(sym)
- ELSIF sym = S.ident THEN
- o := ST.ThisObj();
- S.Get(sym);
- IF o = NIL THEN
- S.Mark("Идентификатор не объявлен")
- ELSE
- x.mode := o.class;
- x.type := o.type;
- x.val := o.val
- END
- ELSIF sym = S.lparen THEN
- S.Get(sym);
- Expression(x);
- IF sym = S.rparen THEN S.Get(sym)
- ELSE S.Mark("Ожидается )")
- END
- ELSE
- S.Mark("Ожидается число, идентификатор или (")
- END
- END Factor;
- (*слагаемое = множитель {ОперацияУмножения множитель}.*)
- PROCEDURE Term(VAR x: Gen.Item);
- VAR op: INTEGER;
- y: Gen.Item;
- BEGIN
- Factor(x);
- WHILE (sym = S.times) OR (sym = S.div) OR (sym = S.mod) DO
- op := sym;
- S.Get(sym);
- Factor(y);
- IF (x.type.form = ST.Int) & (y.type.form = ST.Int) THEN
- IF (x.mode = ST.Const) & (y.mode = ST.Const) THEN
- IF op = S.times THEN
- x.val := x.val * y.val (*!TODO проверить переполнение*)
- ELSE (*DIV OR MOD*)
- IF op = S.div THEN (*!TODO проверить деление на нуль*)
- x.val := x.val DIV y.val
- ELSE
- x.val := x.val MOD y.val
- END
- END
- ELSE
- IF op = S.times THEN
- Out.String("умножение")
- ELSIF op = S.div THEN
- Out.String("деление")
- ELSE
- Out.String("остаток от деления")
- END
- END
- ELSE
- S.Mark("Операция неприменима")
- END;
- Out.Ln
- END
- END Term;
- (*ПростоеВыражение = ["+"|"-"] слагаемое {ОператорСложения слагаемое}.*)
- PROCEDURE SimpleExpression(VAR x: Gen.Item);
- VAR minus: BOOLEAN;
- op: INTEGER;
- y: Gen.Item;
- BEGIN
- minus := FALSE;
- IF sym = S.plus THEN
- S.Get(sym)
- ELSIF sym = S.minus THEN
- minus := TRUE;
- S.Get(sym)
- END;
- Term(x);
- IF minus THEN
- IF x.type.form = ST.Int THEN
- IF x.mode = ST.Const THEN
- x.val := -x.val
- ELSE
- Out.String("Унарный минус"); Out.Ln
- END
- ELSE
- S.Mark("Операция унарного минуса неприменима")
- END
- END;
- WHILE (sym = S.plus) OR (sym = S.minus) DO
- op := sym;
- S.Get(sym);
- Term(y);
- IF (x.type.form = ST.Int) & (y.type.form = ST.Int) THEN
- IF (x.mode = ST.Const) & (y.mode = ST.Const) THEN
- IF op = S.plus THEN
- INC(x.val, y.val) (*!TODO проверить переполнение*)
- ELSE
- DEC(x.val, y.val) (*!TODO проверить переполнение*)
- END
- ELSE
- IF op = S.plus THEN
- Out.String("плюс")
- ELSE
- Out.String("минус")
- END
- END
- ELSE
- S.Mark("Операция неприменима")
- END;
- Out.Ln
- END
- END SimpleExpression;
- (*выражение = ПростоеВыражение [отношение ПростоеВыражение].*)
- PROCEDURE Expression(VAR x: Gen.Item);
- BEGIN
- SimpleExpression(x)
- END Expression;
- (*Присваивание = идент ":=" выражение.
- x - то, что слева от знака присваивания,
- y - то, что справа от знака присваивания.*)
- PROCEDURE Assignment(VAR x: Gen.Item);
- VAR y: Gen.Item;
- BEGIN
- S.Get(sym); (*пропускаем :=*)
- Expression(y);
- IF x.type = y.type THEN
- Out.String("Переменной по адресу ");
- Out.Int(x.val, 0);
- Out.String(" присвоено значение ");
- Out.Int(y.val, 0); Out.Char("."); Out.Ln
- ELSE
- S.Mark("Не совпадают типы")
- END
- END Assignment;
- (*уточнИдент = [идент "."] идент.
- Первый идент - это модуль. *)
- PROCEDURE QualIdent(VAR o: ST.Object);
- BEGIN
- o := ST.ThisObj();
- S.Get(sym);
- IF o = NIL THEN
- S.Mark("Нет такого объекта")
- ELSE (*Объект найден...*)
- IF (sym = S.period) & (o.class = ST.Mod) THEN (*...и это модуль*)
- S.Get(sym);
- o := ST.ThisObjInModule(o);
- S.Get(sym);
- IF o = NIL THEN S.Mark("В модуле нет такого объекта") END
- END
- END;
- IF o = NIL THEN o := dummy END
- END QualIdent;
- (*Designator - обозначение.
- Обозначение = уточнИдент {селектор}.
- селектор = "[" выражение "]" | ...
- Но здесь упрощено: Обозначение = уточнИдент.*)
- PROCEDURE Designator(VAR o: ST.Object);
- BEGIN
- QualIdent(o) (*!TODO обработать селекторы*)
- END Designator;
- PROCEDURE Parameter(par: ST.Object);
- VAR x: Gen.Item;
- BEGIN
- Expression(x);
- IF par # NIL THEN
- IF x.type.form # par.type.form THEN
- S.Mark("Несогласование типов")
- (*!TODO вывести типы формального и фактического параметров*)
- ELSIF (par.class = ST.Par) & (x.mode # ST.Var) THEN
- S.Mark("Требуется вар-параметр")
- ELSE
- Out.String("Параметр "); Out.Int(x.val, 0); Out.Char("."); Out.Ln
- END
- END
- END Parameter;
- (*ФактическиеПараметры = "(" [выражение {"," выражение}] ")".
- Предусловие: sym = S.lparen. Обрабатывает до ")" включительно.*)
- PROCEDURE ActualParameters(VAR x: Gen.Item);
- VAR y: Gen.Item;
- par: ST.Object;
- n: INTEGER; (*Фактическое количество параметров*)
- BEGIN
- S.Get(sym);(*Пропускаем "("*)
- n := 0;
- IF sym # S.rparen THEN
- par := x.type.dsc;
- Parameter(par);
- INC(n);
- WHILE sym = S.comma DO
- S.Get(sym); (*Пропускаем ","*)
- IF par # NIL THEN par := par.next END;
- Parameter(par);
- INC(n)
- END
- END;
- IF sym = S.rparen THEN S.Get(sym)
- ELSE S.Mark("Не закрыта скобка")
- END;
- (*Проверка совпадения количества параметров*)
- IF n > x.type.nofpar THEN
- S.Mark("Слишком много фактических параметров")
- ELSIF n < x.type.nofpar THEN
- S.Mark("Недостаточно фактических параметров")
- END
- END ActualParameters;
- (*Операторы = Оператор {";" Оператор}.
- Оператор = [Присваивание | ВызовПроцедуры | ОператорIf |
- ОператорCase | ОператорWhile | ОператорRepeat | ОператорFor].
- Statements/Операторы означает последовательность операторов.*)
- PROCEDURE Statements;
- VAR x: Gen.Item;
- o: ST.Object;
- BEGIN
- REPEAT
- WHILE sym = S.semicolon DO S.Get(sym) END;
- IF sym = S.ident THEN
- Designator(o);
- Gen.MakeItem(x, o);
- IF sym = S.eql THEN sym := S.becomes; S.Mark("Ожидается :=") END;
- IF sym = S.becomes THEN Assignment(x)
- ELSIF sym = S.lparen THEN (*Например: Out.Int(... *)
- IF x.type.form = ST.Proc THEN (*Это действительно процедура*)
- IF x.type.base.form # ST.NoTyp THEN (*Функциональная*)
- S.Mark("Не используется возвращаемое значение")
- ELSE
- ActualParameters(x); (*Обрабатывает до ")" включительно*)
- Gen.Call(x)
- END
- ELSE S.Mark("Это не процедура")
- END
- ELSIF x.type.form = ST.Proc THEN(*Вызов процедуры без параметров*)
- IF x.type.nofpar > 0 THEN S.Mark("Требуются параметры") END;
- IF x.type.base.form # ST.NoTyp THEN (*Функциональная*)
- S.Mark("Не используется возвращаемое значение")
- ELSE
- Gen.Call(x)
- END
- ELSE S.Mark("Инструкция неопознана")
- END
- END
- UNTIL sym # S.semicolon
- END Statements;
- (*Модуль = MODULE идент ";" [СписокИмпорта] Объявления
- [BEGIN Операторы] END идент ".".*)
- PROCEDURE Module;
- VAR name: ARRAY 32 OF CHAR;
- BEGIN
- IF sym = S.module THEN
- S.Get(sym);
- IF sym = S.ident THEN name := S.id; S.Get(sym)
- ELSE S.Mark("Ожидается идентификатор"); name[0] := 0X
- END;
- IF sym = S.semicolon THEN S.Get(sym)
- ELSE S.Mark("Ожидается точка с запятой")
- END;
- ImportList;
- dc := 80002000;
- Declarations(dc);
- IF sym = S.begin THEN S.Get(sym); Statements END;
- IF sym = S.end THEN
- S.Get(sym);
- IF sym = S.ident THEN
- IF name # S.id THEN S.Mark("Название модуля не совпадает") END;
- S.Get(sym)
- ELSE
- S.Mark("Ожидается название модуля")
- END;
- IF sym # S.period THEN S.Mark("Нет точки в конце модуля") END
- ELSE S.Mark("Ожидается END")
- END
- ELSE S.Mark("Ожидается модуль")
- END
- END Module;
- PROCEDURE Init;
- BEGIN
- ST.Init;
- NEW(dummy); dummy.class := ST.Var; dummy.type := ST.intType
- END Init;
- (*Разбирает лексемы, получаемые из модуля Scanner, в соответствии
- с синтаксисом языка Оберон. Возвращает TRUE в случае
- успешной компиляции. Вызывается из модуля Compiler.
- В будущем будет вызывать кодогенератор (из отдельного модуля). *)
- PROCEDURE Compile*(): BOOLEAN;
- BEGIN
- Init;
- Gen.WriteDummy; (*Сгенерировать исполнимый файл. Хардкод.*)
- S.Get(sym); (* олучить наперёд одну лексему*)
- Module; (*Запустить разбор синтаксической структуры МОДУЛЬ*)
- ST.Display;
- RETURN ~S.error
- END Compile;
- END Parser.
|