Информация об изменениях

Сообщение Re[4]: [Nitra] Парсинг языков базирующихся на отсупах от 02.10.2014 11:59

Изменено 02.10.2014 12:01 STDray

VD>Часть из этих языков уже довольно старые. Ну, и не сложно найти еще большую тучу скобочных языков.
Согласен, не сложно. С другой стороны, я просматривал различные генераторы парсеров и практически нигде проблема разбора языков с синтаксисом на отступах из коробки не решена. Хотя устойчивый интерес есть.

VD>Как я уже говорил, можно относительно не сложно, и полностью автоматически сделать аналог немерлововго решения, когда некий препроцессор расставляет вымышлинные скобки на базе некоторого алгоритма, а далее парсинг происходит по обычным принципам.


Я делал что-то вроде
#pragma indent
using Nemerle
using Nemerle.Imperative
using System
using System.IO
using System.Text
using SCG = System.Collections.Generic

namespace SampleParserApplication2

    [Record] class IndentPreprocessor
        public Indent    : char
        public Dedent    : char 
        public BadDedent : char
    
        public Preprocess(input : string) : string 
            input |> StringReader |> Preprocess
        
        public Preprocess(reader : TextReader) : string 
            def builder     = StringBuilder()
            def indentSizes = SCG.Stack()
            mutable prevIndentSize : int? = null
            for(mutable line  = reader.ReadLine(); line != null; line = reader.ReadLine())
                def currentIndentSize = GetIndentSize(line)
                when(currentIndentSize == line.Length && line.Length >= 0)
                    continue;
                when(!prevIndentSize.HasValue) 
                    prevIndentSize = currentIndentSize
                if(prevIndentSize.Value < currentIndentSize)
                    indentSizes.Push(prevIndentSize.Value)
                    _ = builder.Append(Indent)
                else when(prevIndentSize.Value > currentIndentSize) 
                    _ = builder.Append <| 
                        if(indentSizes.Count > 0) 
                            match(indentSizes.Peek())
                                | x when x == currentIndentSize => _ = indentSizes.Pop(); Dedent
                                | x when x >  currentIndentSize => _ = indentSizes.Pop(); BadDedent
                                | _                             =>                        BadDedent
                        else BadDedent
                prevIndentSize = currentIndentSize
                _ = builder.Append(Environment.NewLine).Append(line)
            foreach(currentIndentSize in indentSizes)
                _ = builder.Append <| 
                    if(currentIndentSize < prevIndentSize.Value) Dedent
                    else                                         BadDedent
            builder.ToString();
    
        static GetIndentSize(line : string) : int
            mutable size = 0
            while(size < line.Length && line[size] == ' ')
                size++
            size


и использование
        def preprocessor = IndentPreprocessor('\uE001', '\uE002', '\uE003');
        def result = preprocessor.Preprocess(str);
        def source = SourceSnapshot(result);

  token INDENT  = '\uE001';
  token BADDENT = '\uE003';
  token DEDENT  = BADDENT? '\uE002';


То есть была какая-то идея, чтобы
— использовать символы из приватного диапазона юникода для расстановки виртуальных скобок.
— использовать некий BadDedent для разметки кривых отступов, чтобы парсер мог сгенерировать ошибку вида "Dedent expected", восстановиться и разбирать дальше.
— использовать this(originalText : string, text : string, fileIndex : int, fileName : string, lineIndexes : array[int], textOffset : int); для корректного позиционирования ошибок после расстановки виртуальных скобок.

Но потом я от этой идеи отказался, потому что непонятно, как дружить все это дело со студийной интеграцией. Да и то, что надо в двух местах указывать символы для виртуальных скобок мне не понравилось. Так что я считаю, пусть какое-то простенькое решение, но должно быть в коробке.
Re[4]: [Nitra] Парсинг языков базирующихся на отсупах
VD>Часть из этих языков уже довольно старые. Ну, и не сложно найти еще большую тучу скобочных языков.
Согласен, не сложно. С другой стороны, я просматривал различные генераторы парсеров и практически нигде проблема разбора языков с синтаксисом на отступах из коробки не решена. Хотя устойчивый интерес есть.

VD>Как я уже говорил, можно относительно не сложно, и полностью автоматически сделать аналог немерлововго решения, когда некий препроцессор расставляет вымышлинные скобки на базе некоторого алгоритма, а далее парсинг происходит по обычным принципам.


Я делал что-то вроде
#pragma indent
using Nemerle
using Nemerle.Imperative
using System
using System.IO
using System.Text
using SCG = System.Collections.Generic

namespace SampleParserApplication2

    [Record] class IndentPreprocessor
        public Indent    : char
        public Dedent    : char 
        public BadDedent : char
    
        public Preprocess(input : string) : string 
            input |> StringReader |> Preprocess
        
        public Preprocess(reader : TextReader) : string 
            def builder     = StringBuilder()
            def indentSizes = SCG.Stack()
            mutable prevIndentSize : int? = null
            for(mutable line  = reader.ReadLine(); line != null; line = reader.ReadLine())
                def currentIndentSize = GetIndentSize(line)
                when(currentIndentSize == line.Length && line.Length >= 0)
                    continue;
                when(!prevIndentSize.HasValue) 
                    prevIndentSize = currentIndentSize
                if(prevIndentSize.Value < currentIndentSize)
                    indentSizes.Push(prevIndentSize.Value)
                    _ = builder.Append(Indent)
                else when(prevIndentSize.Value > currentIndentSize) 
                    _ = builder.Append <| 
                        if(indentSizes.Count > 0) 
                            match(indentSizes.Peek())
                                | x when x == currentIndentSize => _ = indentSizes.Pop(); Dedent
                                | x when x >  currentIndentSize => _ = indentSizes.Pop(); BadDedent
                                | _                             =>                        BadDedent
                        else BadDedent
                prevIndentSize = currentIndentSize
                _ = builder.Append(Environment.NewLine).Append(line)
            foreach(currentIndentSize in indentSizes)
                _ = builder.Append <| 
                    if(currentIndentSize < prevIndentSize.Value) Dedent
                    else                                         BadDedent
            builder.ToString();
    
        static GetIndentSize(line : string) : int
            mutable size = 0
            while(size < line.Length && line[size] == ' ')
                size++
            size


и использование
        def preprocessor = IndentPreprocessor('\uE001', '\uE002', '\uE003');
        def result = preprocessor.Preprocess(str);
        def source = SourceSnapshot(result);

  token INDENT  = '\uE001';
  token BADDENT = '\uE003';
  token DEDENT  = BADDENT? '\uE002';


То есть была какая-то идея, чтобы
— использовать символы из приватного диапазона юникода для расстановки виртуальных скобок.
— использовать некий BadDedent для разметки кривых отступов, чтобы парсер мог сгенерировать ошибку вида "Dedent expected", восстановиться и разбирать дальше.
— использовать конструктор SourceSnapshot this(originalText : string, text : string, fileIndex : int, fileName : string, lineIndexes : array[int], textOffset : int); для корректного позиционирования ошибок после расстановки виртуальных скобок.

Но потом я от этой идеи отказался, потому что непонятно, как дружить все это дело со студийной интеграцией. Да и то, что надо в двух местах указывать символы для виртуальных скобок мне не понравилось. Так что я считаю, пусть какое-то простенькое решение, но должно быть в коробке.