Парсер для определения записи в pascal на c#

Код должен выполнить синтаксический анализ определения и объявления записи на языке Pascal. Если вы допускаете ошибки в записи по одной за раз, все работает корректно, но если вы допускаете несколько ошибок, появляется неожиданный результат. Например, ошибки, которые не были записаны отображаются, которые были - не отображаются или программа зависает и вылетает.

Помогите с логикой обработки ошибок. type,record,end выводят в ошибку только один раз, если написать несколько записей с ошибкой в этих словах. в целом если делать несколько ошибок в тексте, то выводятся ошибки вообще в другом месте, где их и в помине нет.

Ошибки

type Point = record
    x, y: real


type Person = record
   name: string;
   age: integer
end;
ошибки: отсутствие end;
результат: Ожидалось ';' после типа поля
           Ожидался идентификатор поля
           Ожидалось ключевое слово 'end'
ожидаемый результат: 
           Ожидалось ключевое слово 'end'



typ Person = recor
    name: string;
    age: integer
end;
ошибки: typ-type,recor-record
результат: Ожидалось ключевое слово 'type'
ожидаемый результат: Ожидалось ключевое слово 'type', 
                     Ожидалось ключевое слово 'record'

Примеры допустимых строк

type Point = record
   x, y: real
end;

type Person = record
   name: string;
   age: integer
end;

Разработанная грамматика

<OPR> → 'type' <SPACE>
<SPACE> → ' ' <ID>
<ID> → letter <IDREM>
<IDREM> → letter | digit <IDREM>
<IDREM> → '=' <REC>
<REC> → 'record' <SPACE>
<SPACE> → ' ' <FIELD>
<FIELD> → letter <FIELDREM>
<FIELDREM> → letter | digit <FIELDREM>
<FIELDREM> → ',' <FIELDREM>
<FIELDREM> → ':' <TYPE>
<TYPE> → type <END>
<END> → 'end' <SEMICOLON>
<SEMICOLON> → ';'

<Digit> → 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<Letter> → a | b | c | ... | z | A | B | C | ... | Z

Следуя введенному формальному определению грамматики, представим G[] ее составляющими:

Z = VT = {a, b, c, ..., z, A, B, C, ..., Z, "=", ":", ",", ";", 0, 1, 2, ..., 9} VN = {, , , , , , , , , , "type", "record", "end", "integer", "real", "char", "boolean", "string", "var" }

using System;
using System.Collections.Generic;

namespace Lexer
{
  public class RecordParser
  {
    private string input;
    private int position;
    private List<string> errors = new List<string>();

    public List<string> ParseRecord(string code)
    {
        input = code;
        position = 0;
        errors.Clear();

        try
        {
            SkipWhitespace();
            while (position < input.Length)
            {
                ParseTypeDeclaration();
                SkipWhitespace();
            }
        }
        catch (Exception ex)
        {
            errors.Add($"Критическая ошибка: {ex.Message}");
        }

        return errors;
    }

    private void ParseTypeDeclaration()
    {
        if (!MatchKeyword("type"))
        {
            AddError("Ожидалось ключевое слово 'type'");
            SkipToKeyword(new string[] { "type" });
            return;
        }

        SkipWhitespace();
        ParseIdentifier();
        SkipWhitespace();

        if (!MatchChar('='))
        {
            AddError("Ожидалось '=' после идентификатора");
            SkipToKeyword(new string[] { "type" });
            return;
        }

        SkipWhitespace();

        if (!MatchKeyword("record"))
        {
            AddError("Ожидалось ключевое слово 'record'");
            SkipToKeyword(new string[] { "type" });
            return;
        }

        SkipWhitespace();
        ParseFields();

        if (!MatchKeyword("end"))
        {
            AddError("Ожидалось ключевое слово 'end'");
            SkipToKeyword(new string[] { "type" });
            return;
        }

        SkipWhitespace();

        if (!MatchChar(';'))
        {
            AddError("Ожидалось ';' после 'end'");
        }
    }

    private void SkipToKeyword(string[] keywords)
    {
        int startPos = position;
        while (position < input.Length)
        {
            foreach (var keyword in keywords)
            {
                if (PeekKeyword(keyword)) return;
            }
            position++;

            if (position - startPos > 1000)
            {
                AddError("Не удалось найти ожидаемое ключевое слово");
                return;
            }
        }
    }

    private void ParseFields()
    {
        while (position < input.Length && !PeekKeyword("end"))
        {
            SkipWhitespace();
            if (PeekKeyword("end")) break;

            var identifiers = new List<string>();
            do
            {
                SkipWhitespace();
                if (position >= input.Length || !char.IsLetter(input[position]))
                {
                    AddError("Ожидался идентификатор поля");
                    SkipToKeyword(new string[] { "end", ";" });
                    return;
                }

                int start = position;
                ParseIdentifier();
                identifiers.Add(input.Substring(start, position - start));

                SkipWhitespace();
            }
            while (MatchChar(','));

            if (identifiers.Count > 0)
            {
                SkipWhitespace();
                if (!MatchChar(':'))
                {
                    AddError("Ожидалось ':' после списка полей");
                    SkipToKeyword(new string[] { "end", ";" });
                    continue;
                }

                SkipWhitespace();
                ParseType();
                SkipWhitespace();

                if (!MatchChar(';') && !PeekKeyword("end"))
                {
                    AddError("Ожидалось ';' после типа поля");
                    SkipToKeyword(new string[] { "end", ";" });
                }
            }
        }
    }

    private void ParseType()
    {
        string[] validTypes = { "integer", "real", "string", "boolean", "char" };
        bool typeFound = false;

        foreach (var type in validTypes)
        {
            if (MatchKeyword(type))
            {
                typeFound = true;
                break;
            }
        }

        if (!typeFound)
        {
            AddError($"Недопустимый тип данных. Ожидалось: {string.Join(", ", validTypes)}");
            SkipToNextField();
        }
    }

    private void ParseIdentifier()
    {
        if (position >= input.Length || !char.IsLetter(input[position]))
        {
            AddError("Ожидался идентификатор");
            return;
        }

        position++;
        while (position < input.Length && (char.IsLetterOrDigit(input[position]) || input[position] == '_'))
        {
            position++;
        }
    }

    private void SkipToNextField()
    {
        while (position < input.Length && input[position] != ';' && !PeekKeyword("end"))
        {
            position++;
        }
    }

    private void SkipWhitespace()
    {
        while (position < input.Length && char.IsWhiteSpace(input[position]))
        {
            position++;
        }
    }

    private bool PeekKeyword(string keyword)
    {
        if (position + keyword.Length > input.Length)
            return false;

        for (int i = 0; i < keyword.Length; i++)
        {
            if (char.ToLower(input[position + i]) != char.ToLower(keyword[i]))
                return false;
        }

        if (position + keyword.Length < input.Length)
        {
            char nextChar = input[position + keyword.Length];
            if (char.IsLetterOrDigit(nextChar) || nextChar == '_')
                return false;
        }

        return true;
    }

    private bool MatchKeyword(string keyword)
    {
        if (PeekKeyword(keyword))
        {
            position += keyword.Length;
            return true;
        }
        return false;
    }

    private bool MatchChar(char c)
    {
        if (position < input.Length && input[position] == c)
        {
            position++;
            return true;
        }
        return false;
    }

    private void AddError(string message)
    {
        errors.Add(message);
    }
  }
}

Ответы (0 шт):