-
Notifications
You must be signed in to change notification settings - Fork 64
Создание_лексического_анализатора_простого_языка_программирования
; := id num begin end cycle
Здесь
id - идентификатор
num - целое без знака
begin end cycle - ключевые слова
public int LexRow, LexCol; // Строка-столбец начала лексемы. Конец лексемы = LexCol+Length(LexText)
public Tok LexKind; // Тип лексемы
public string LexText; // Текст лексемы
public int LexValue; // Целое значение, связанное с лексемой lexNum
Тип Tok является перечислимым и содержит все возможные лексемы нашего языка, а также специальную лексему EOF конца текста:
public enum Tok { EOF, ID, INUM, COLON, SEMICOLON, ASSIGN, BEGIN, END, CYCLE }
private char currentCh // Текущий символ
private TextReader inputReader; // Текущий поток ввода
private int row, col; // Текущие строка и столбец в файле
private Dictionary<string, Tok> keywordsMap; // Словарь, сопоставляющий ключевым словам константы типа TLex.
// Инициализируется процедурой InitKeywords
Помимо считывания следующего символа ch, процедура NextCh поддерживает в актуальном состоянии текущие строку-столбец (row,col). При достижении конца файла в переменную ch возвращается специальный символ #0
Процедура InitKeywords инициализирует словарь ключевых слов KeywordsMap. Этот словарь ставит в соответствие строке ключевого слова константу типа TLex:
keywordsMap["begin"] = Tok.BEGIN;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace SimpleLangLexer
{
public class LexerException : System.Exception
{
public LexerException(string msg)
: base(msg)
{
}
}
public enum Tok
{
EOF,
ID,
INUM,
COLON,
SEMICOLON,
ASSIGN,
BEGIN,
END,
CYCLE
}
public class Lexer
{
private int position;
private char currentCh; // Текущий символ
public int LexRow, LexCol; // Строка-столбец начала лексемы. Конец лексемы = LexCol+LexText.Length
private int row, col; // текущие строка и столбец в файле
private TextReader inputReader;
private Dictionary<string, Tok> keywordsMap; // Словарь, сопоставляющий ключевым словам константы типа TLex. Инициализируется процедурой InitKeywords
public Tok LexKind; // Тип лексемы
public string LexText; // Текст лексемы
public int LexValue; // Целое значение, связанное с лексемой LexNum
private string CurrentLineText; // Накапливает символы текущей строки для сообщений об ошибках
public Lexer(TextReader input)
{
CurrentLineText = "";
inputReader = input;
keywordsMap = new Dictionary<string, Tok>();
InitKeywords();
row = 1; col = 0;
NextCh(); // Считать первый символ в ch
NextLexem(); // Считать первую лексему, заполнив LexText, LexKind и, возможно, LexValue
}
public void Init() {
}
private void PassSpaces()
{
while (char.IsWhiteSpace(currentCh))
{
NextCh();
}
}
private void InitKeywords()
{
keywordsMap["begin"] = Tok.BEGIN;
keywordsMap["end"] = Tok.END;
keywordsMap["cycle"] = Tok.CYCLE;
}
public string FinishCurrentLine()
{
return CurrentLineText + inputReader.ReadLine();
}
private void LexError(string message)
{
System.Text.StringBuilder errorDescription = new System.Text.StringBuilder();
errorDescription.AppendFormat("Lexical error in line {0}:", row);
errorDescription.Append("\n");
errorDescription.Append(FinishCurrentLine());
errorDescription.Append("\n");
errorDescription.Append(new String(' ', col - 1) + '^');
errorDescription.Append('\n');
if (message != "")
{
errorDescription.Append(message);
}
throw new LexerException(errorDescription.ToString());
}
private void NextCh()
{
// В LexText накапливается предыдущий символ и считывается следующий символ
LexText += currentCh;
var nextChar = inputReader.Read();
if (nextChar != -1)
{
currentCh = (char)nextChar;
if (currentCh != '\n')
{
col += 1;
CurrentLineText += currentCh;
}
else
{
row += 1;
col = 0;
CurrentLineText = "";
}
}
else
{
currentCh = (char)0; // если достигнут конец файла, то возвращается #0
}
}
public void NextLexem()
{
PassSpaces();
// R К этому моменту первый символ лексемы считан в ch
LexText = "";
LexRow = row;
LexCol = col;
// Тип лексемы определяется по ее первому символу
// Для каждой лексемы строится синтаксическая диаграмма
if (currentCh == ';')
{
NextCh();
LexKind = Tok.SEMICOLON;
}
else if (currentCh == ':')
{
NextCh();
if (currentCh != '=')
{
LexError("= was expected");
}
NextCh();
LexKind = Tok.ASSIGN;
}
else if (char.IsLetter(currentCh))
{
while (char.IsLetterOrDigit(currentCh))
{
NextCh();
}
if (keywordsMap.ContainsKey(LexText))
{
LexKind = keywordsMap[LexText];
}
else
{
LexKind = Tok.ID;
}
}
else if (char.IsDigit(currentCh))
{
while (char.IsDigit(currentCh))
{
NextCh();
}
LexValue = Int32.Parse(LexText);
LexKind = Tok.INUM;
}
else if ((int)currentCh == 0)
{
LexKind = Tok.EOF;
}
else
{
LexError("Incorrect symbol " + currentCh);
}
}
public virtual void ParseToConsole()
{
do
{
Console.WriteLine(TokToString(LexKind));
NextLexem();
} while (LexKind != Tok.EOF);
}
public string TokToString(Tok t)
{
var result = t.ToString();
switch (t)
{
case Tok.ID: result += ' ' + LexText;
break;
case Tok.INUM: result += ' ' + LexValue.ToString();
break;
}
return result;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SimpleLangLexer;
namespace SimpleLangLexerTest
{
class Program
{
public static void Main()
{
string fileContents = @"begin
id23 := 24;
cycle ; 2 id258 id29 ;
end";
TextReader inputReader = new StringReader(fileContents);
Lexer l = new Lexer(inputReader);
try
{
do
{
Console.WriteLine(l.TokToString(l.LexKind));
l.NextLexem();
} while (l.LexKind != Tok.EOF);
}
catch (LexerException e)
{
Console.WriteLine("lexer error: " + e.Message);
}
}
}
}
Разобраться в программе. Откомпилировать программу (0 баллов)
- Добавить распознавание лексем , : + - * / div mod and or not
- Добавить распознавание лексем += -= *= /=. Тщательно продумать, как совместить распознавание лексем с одинаковым префиксом: например, + и +=
- Добавить распознавание лексем > < >= <= = <>
- Добавить пропуск комментариев // - до конца строки
- Добавить пропуск комментариев { комментарий до закрывающей фигурной скобки }. Обратить внимание, что незакрытый до конца файла комментарий - это синтаксическая ошибка