Использование парсер-комбинаторов на Sprache для построения DSL

Содержание

Слайд 2

«Восстановить» объект из строки Шаблонизатор с подстановкой переменных Задача

«Восстановить» объект из строки
Шаблонизатор с подстановкой переменных

Задача

Слайд 3

Подходы к решению

Подходы к решению

Слайд 4

Подходят, когда решение очевидно (Split, IndexOf, Substring) public class SecurityToken {

Подходят, когда решение очевидно (Split, IndexOf, Substring)
public class SecurityToken
{
public string

UserId { get; set; }
public DateTime ValidTo { get; set; }
}
Нужно распарсить строку
"8cb9f238-e0e4-431c-86a6-9ac52b9cb6b9;2017-04-20T00:00:00Z"

1. Парсеры, написанные вручную

Слайд 5

Относительно простая грамматика в простых случаях Трудно читать и поддерживать Невозможно

Относительно простая грамматика в простых случаях
Трудно читать и поддерживать
Невозможно работать с

вложенными данными
Не масштабируется на более сложные грамматики

2. Регулярные выражения

Слайд 6

Генерирует классы на целевом языке (Java, C#, …) во время сборки

Генерирует классы на целевом языке (Java, C#, …) во время сборки

проекта, которые способные разбирать грамматику
Требует времени для изучения и интеграции
Подходит для очень сложных вещей, где важна скорость разбора
ANTLR (http://www.antlr.org)

3. Генераторы парсеров

Слайд 7

grammar Speak; /* Parser Rules */ chat : line line EOF

grammar Speak;
/* Parser Rules */
chat : line line EOF ;
line : name SAYS

word NEWLINE;
name : WORD ;
word : WORD ;
/* Lexer Rules */ 
fragment A          : ('A'|'a') ;
fragment S          : ('S'|'s') ;
fragment Y          : ('Y'|'y') ;
fragment LOWERCASE  : [a-z] ;
fragment UPPERCASE  : [A-Z] ;
SAYS  : S A Y S ;
WORD  : (LOWERCASE | UPPERCASE)+ ;
WHITESPACE  : (' '|'\t')+ -> skip ;
NEWLINE  : ('\r'? '\n' | '\r')+ ;

3. Генераторы парсеров (ANTLR)

Слайд 8

Мини-язык описания Логика написана на том же языке, что и остальное

Мини-язык описания
Логика написана на том же языке, что и остальное приложение

(C#)
Техника пришла из функциональных языков

4. Парсер-комбинаторы

Слайд 9

Легко писать Легко поддерживать Хорошо сочетается с TDD Маленький размер библиотеки

Легко писать
Легко поддерживать
Хорошо сочетается с TDD
Маленький размер библиотеки
https://github.com/sprache/Sprache

Sprache - C#

Parser Framework
Слайд 10

public delegate IResult Parser (IInput input); public interface IResult { T

public delegate IResult Parser(IInput input);
public interface IResult
{
T Value

{ get; }
bool WasSuccessful { get; }
IInput Remainder { get; }
}

Parser

Слайд 11

Parse.Char(' Parse.Digit Parse.WhiteSpace Parse.Numeric Parse.AnyChar Parse.LineEnd Parse.LetterOrDigit … Primitive parsers

Parse.Char('<')
Parse.Digit
Parse.WhiteSpace
Parse.Numeric
Parse.AnyChar
Parse.LineEnd
Parse.LetterOrDigit

Primitive parsers

Слайд 12

Parsers and combinators - Sequence in AB success failure from letter

Parsers and combinators - Sequence

in

AB

success

failure

from letter in Parse.Letter
from digit in Parse.Digit

Слайд 13

Parsers and combinators - Or in A or B success failure from letter in Parse.Letter.Or(Parse.Digit)

Parsers and combinators - Or

in

A or B

success

failure

from letter in Parse.Letter.Or(Parse.Digit)

Слайд 14

Parsers and combinators - Optional in A? success failure from letter in Parse.Letter.Optional()

Parsers and combinators - Optional

in

A?

success

failure

from letter in Parse.Letter.Optional()

Слайд 15

Parsers and combinators - Many in A* success failure from letters in Parse.Letter.Many()

Parsers and combinators - Many

in

A*

success

failure

from letters in Parse.Letter.Many()

Слайд 16

Parser identifier = from leading in Parse.WhiteSpace.Many() from first in Parse.Letter.Once()

Parser identifier =
from leading in Parse.WhiteSpace.Many()
from first in Parse.Letter.Once()

from rest in Parse.LetterOrDigit.Many()
from trailing in Parse.WhiteSpace.Many()
select new string(first.Concat(rest).ToArray());
var id = identifier.Parse(" abc123 ");
Assert.AreEqual("abc123", id);

Строим свои парсеры

O_O

Слайд 17

public static IEnumerable Select (this IEnumerable source, Func selector) public static

public static IEnumerable Select(this IEnumerable source, Func selector)
public static

IEnumerable SelectMany(this IEnumerable parser, Func> selector, Func projector)
public static IEnumerable Where(this IEnumerable source, Func predicate)

Off-topic: Select, SelectMany, Where C#

Слайд 18

public static Parser Select (this Parser parser, Func convert) public static

public static Parser Select(this Parser parser, Func convert)
public static

Parser SelectMany(this Parser parser, Func> selector, Func projector)
public static Parser Where(this Parser parser, Func predicate)

Off-topic: Select, SelectMany, Where C#

Слайд 19

Примеры задач

Примеры задач

Слайд 20

1. Парсинг из текста public class Person { public Person(…) {

1. Парсинг из текста

public class Person
{
public Person(…)
{
Type =

type;
Properties = properties;
}
public string Type { get; }
public IEnumerable Properties { get; }
}

public class Property
{
public Property(…)
{
Name = name;
Value = value;
}
public string Name { get; }
public string Value { get; }
}

Слайд 21

magician [ name ‘Merlin’ age 100500 ] 1. Парсинг из текста

magician [ name  ‘Merlin’ age  100500 ]

1. Парсинг из текста

Слайд 22

Demo 1 (парсинг из текста)

Demo 1 (парсинг из текста)

Слайд 23

var props = new Dictionary { {"Number", "MX-123"}, {"Date", new DateTime(2017,

var props = new Dictionary
{
{"Number", "MX-123"},
{"Date", new DateTime(2017,

04, 19)},
{"Caption", "Счет"}
};
string template = "{{Caption}} №{{Number}} от {{Date}}";
"Счет №MX-123 от 19.04.17"

2. Шаблонизатор

Слайд 24

Demo 2 (шаблонизатор)

Demo 2 (шаблонизатор)

Слайд 25

3. WWW-Authenticate challenge HTTP 401 Unauthorized # Basic challenge WWW-Authenticate: Basic

3. WWW-Authenticate challenge

HTTP 401 Unauthorized
# Basic challenge
WWW-Authenticate: Basic realm="FooCorp"
# OAuth 2.0

challenge after sending an expired token
WWW-Authenticate: Bearer realm="FooCorp", error=invalid_token, error_description="The access token has expired"
Слайд 26

3. WWW-Authenticate challenge # from RFC-2617 (HTTP Basic and Digest authentication)

3. WWW-Authenticate challenge

# from RFC-2617 (HTTP Basic and Digest authentication)
challenge      =

auth-scheme 1*SP 1#auth-param
auth-scheme    = token
auth-param     = token "=" ( token | quoted-string )
# from RFC2616 (HTTP/1.1)
token          = 1*
separators     = "(" | ")" | "<" | ">" | "@"
               | "," | ";" | ":" | "\" | <">
               | "/" | "[" | "]" | "?" | "="
               | "{" | "}" | SP | HT
quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
qdtext         = >
quoted-pair    = "\" CHAR
Слайд 27

Demo 3 (WWW-Authenticate challenge)

Demo 3 (WWW-Authenticate challenge)

Слайд 28

Автор библиотеки Nicholas Blumhardt Контрибьютор других известных библиотек Serilog Stateless Autofac …

Автор библиотеки Nicholas Blumhardt Контрибьютор других известных библиотек

Serilog
Stateless
Autofac

Слайд 29

Парсеры, построенные на Sprache The template parser in Octostache The XAML

Парсеры, построенные на Sprache

The template parser in Octostache
The XAML binding expression parser

in OmniXaml
The query parser in Seq
The connection string parser in EasyNetQ
Sprache appears in the credits for JetBrains ReSharper