- •Введение
- •Порождающие грамматики Хомского Цель
- •Неограниченные грамматики
- •Контекстно зависимые грамматики
- •Контекстно свободные грамматики
- •Автоматные грамматики
- •Эквивалентность грамматик
- •Однозначность грамматик
- •Задачи и упражнения
- •Контрольные вопросы
- •Конечный автомат
- •Детерминированный конечный автомат
- •Построение дка из -нка
- •Задачи и упражнения
- •Контрольные вопросы
- •Регулярные грамматики. Лексический анализатор. Роль лексических анализаторов
- •Лексические ошибки
- •Регулярные выражения
- •Преобразование регулярного выражения в автомат
- •Распознание токенов
- •Задачи и упражнения
- •Контрольные вопросы
- •Автоматы с магазинной памятью
- •Нисходящие методы разбора Устранение левой рекурсии
- •Левая факторизация
- •Метод рекурсивного спуска
- •Пример нисходящего интерпретатора
- •Задачи и упражнения
- •Контрольные вопросы
Нисходящие методы разбора Устранение левой рекурсии
Грамматика называется леворекурсивной, если в ней имеется нетерминал A, такой, что существует порождение для некоторой цепочки .
Вход:
A A |
Правило устранения левой рекурсии:
A A’
A’ A’ |
E E + T
T T * F | F
F (E) | id
Устранение непосредственной левой рекурсии:
E TE’
E’ +TE’ |
T FT’
T’ *FT’ |
F (E) | id
Вход: грамматика без циклов и -продукций.
Выход: эквивалентная грамматика без левой рекурсии.
Пример 11. Устранение левой рекурсии
Расположить нетерминалы в порядке A1, A2,…,An
for i:=1 to n do begin
for j:=1 to i-1 do begin
Заменить каждую продукцию вида Ai Aj продукциями Ai 1 | 2 | … |k где Aj 1 | 2 | … |k – все текущие Aj-продукции
end
Устранить непосредственную левую продукцию среди Ai-продукций
end
Левая факторизация
S if E then S else S | if E then S
Т.е. если A 1 | 2 представляют собой две A-продукции и входной поток начинается с непустой строки, порождаемой , то нельзя сказать будет ли использоваться первая или вторая продукция, можно только отложить решение, проведя замену:
A A’
A’ 1 | 2
S if E then S else S | if E then S | a
E b
Левофакторизованная грамматика:
S if E then ES’ | a
S’ else S |
E b
Метод рекурсивного спуска
Программа интерпретации арифметических выражений с грамматикой:
S A:=E;
E E+T | E-T | T
T T*F | T/F | F
F (E) | id | const
В презентации большой кусок кода….
Пример нисходящего интерпретатора
Пример 12 Простой нисходящий интерпретатор арифметических выражений
// Translator.cs using System; using System.Collections.Generic; using System.Linq; using System.Text;
namespace SimpleTranslator { public class Translator { enum Tokens { Undefined, Assigment, Add, Sub, Mul, Div, RBr, LBr, Id, Constant }
private int index = 0; private Tokens token; private string source; string currentId; private Dictionary<string, double> symbols = new Dictionary<string, double>();
private void Match(Tokens t) { if (token == t) { Scan(); } else { Error(); }
}
/// <summary> /// Лексический анализатор /// </summary> private void Scan() { char c; int state = 0; currentId = string.Empty; c = source[index];
while (c != ';') { c = source[index]; switch (state) { case 0: { if (c == ':') state = 1; if (Char.IsLetter(c)) { token = Tokens.Id; currentId += c; state = 3;
} if (c == '+') { token = Tokens.Add; index++; return; } if (c == '-') { token = Tokens.Sub; index++; return; } if (c == '*') { token = Tokens.Mul; index++; return; } if (c == '/') { token = Tokens.Div; index++; return; } if (c == ')') { token = Tokens.LBr; index++; return; } if (c == '(') { token = Tokens.RBr; index++; return; } if (Char.IsDigit(c)) { token = Tokens.Constant; currentId += c; state = 7; } break; } case 1: if (c == '=') { token = Tokens.Assigment; } else { token = Tokens.Undefined; } return; case 3: if (!Char.IsLetterOrDigit(c)) { symbols[currentId] = 10; return; } else currentId += c; break; case 7: if (!Char.IsDigit(c)) { symbols[currentId] = Convert.ToDouble(currentId); return; } else currentId += c; break; } ++index; } }
private void Assigment() { Match(Tokens.Undefined); Match(Tokens.Id); Match(Tokens.Assigment); Expression(); }
private void Expression() { Term(); Tokens t; while (true) { switch (token) { case Tokens.Add:case Tokens.Sub: t = token; Match(token); Term(); Emit(t, string.Empty); continue; default: return; } }
}
private void Term() { Tokens t; Factor(); while (true) { switch (token) { case Tokens.Mul:case Tokens.Div: t = token; Match(token); Factor(); Emit(t, string.Empty); continue; default: return; } } }
private void Factor() { switch (token) { case Tokens.RBr: Match(Tokens.RBr); Expression(); Match(Tokens.LBr); break; case Tokens.Constant: Emit(token, currentId); Match(Tokens.Constant); break; case Tokens.Id: Emit(token, currentId); Match(Tokens.Id); break; default: Error(); break; } }
private void Error() { throw new ApplicationException("Parsing error"); }
private Stack<double> stack = new Stack<double>(); private void Emit(Tokens t, string tval) { switch (t) { case Tokens.Add: Operation((x, y) => x + y); break; case Tokens.Sub: Operation((x, y) => x - y); break; case Tokens.Mul: Operation((x, y) => x * y); break; case Tokens.Div: Operation((x, y) => x / y); break; case Tokens.Constant: case Tokens.Id: stack.Push(symbols[tval]); break; default: Console.WriteLine("Token {0}, TokenVal {1}", t, tval); break; } } private void Operation(Func<double, double, double> operation) { double o1 = stack.Pop(); double o2 = stack.Pop(); stack.Push(operation(o1, o2)); }
public double Translate(string source) { token = Tokens.Undefined; this.source = source; Assigment(); return stack.Pop(); } } }
|
// Program.cs using System; using System.Collections.Generic; using System.Linq; using System.Text;
namespace SimpleTranslator { class Program { static void Main(string[] args) { const string source = "x:=(10+20/20)*2-22;"; Console.WriteLine("{0}\n",source);
Translator tran = new Translator();
try { Console.WriteLine(tran.Translate(source)); }
catch (ApplicationException e) { Console.WriteLine(e.ToString()); } } } } |