Эта статья требует дополнительных ссылок для проверки . ( ноябрь 2008 г. ) ( Узнайте, как и когда удалить этот шаблон сообщения ) |
В компьютерном программировании , то картина интерпретатора является шаблоном дизайна , который определяет , как оценивать предложения в языке. Основная идея состоит в том, чтобы иметь класс для каждого символа ( терминального или нетерминального ) на специализированном компьютерном языке . Синтаксическое дерево из предложения языка является экземпляром составного узора , и используется для оценки (интерпретации) предложения для клиента. [1] : 243 См. Также Составной узор .
Обзор [ править ]
Шаблон проектирования Interpreter [2] - один из двадцати трех хорошо известных шаблонов проектирования GoF, которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, тестирование и повторное использование.
Какие проблемы может решить шаблон проектирования Interpreter? [3]
- Грамматика для простого языка должен быть определен
- так что предложения на языке можно интерпретировать.
Когда проблема возникает очень часто, ее можно представить как предложение на простом языке ( предметно-зависимые языки ), чтобы интерпретатор мог решить проблему, интерпретируя предложение.
Например, когда необходимо указать много разных или сложных поисковых выражений. Реализация (жесткое подключение) их непосредственно в класс негибкая, потому что она связывает класс с определенными выражениями и делает невозможным определение новых выражений или изменение существующих независимо от (без необходимости изменения) класса.
Какое решение описывает шаблон проектирования Interpreter?
- Определите грамматику для простого языка, определив
Expression
иерархию классов и выполнивinterpret()
операцию. - Представляйте предложение на языке абстрактным синтаксическим деревом (AST), состоящим из
Expression
экземпляров. - Интерпретируйте предложение, позвонив
interpret()
в AST.
Объекты выражения рекурсивно составляются в составную / древовидную структуру, которая называется абстрактным синтаксическим деревом (см. Составной шаблон ).
Шаблон интерпретатора не описывает, как построить абстрактное синтаксическое дерево. Это может быть сделано либо вручную клиентом, либо автоматически парсером .
См. Также схему классов и объектов UML ниже.
Использует [ редактировать ]
- Специализированные языки запросов к базам данных, такие как SQL .
- Специализированные компьютерные языки, которые часто используются для описания протоколов связи.
- Большинство компьютерных языков общего назначения фактически включают в себя несколько специализированных языков.
Структура [ править ]
Схема классов и объектов UML [ править ]
В приведенной выше UML - диаграмме класса , то Client
класс относится к общему AbstractExpression
интерфейсу для интерпретации выражения interpret(context)
.
Класс не имеет детей и интерпретирует выражение непосредственно.
Класс поддерживает контейнер дочерних выражений ( ) и форвардов интерпретировать запросы на них .TerminalExpression
NonTerminalExpression
expressions
expressions
Диаграмма взаимодействия объектов показывает взаимодействия во время выполнения: Client
объект отправляет запрос интерпретации абстрактному синтаксическому дереву. Запрос направляется (выполняется) ко всем объектам вниз по древовидной структуре.
Эти NonTerminalExpression
объекты ( ntExpr1,ntExpr2
) направить запрос на их детях выражения.
Эти TerminalExpression
объекты ( tExpr1,tExpr2,…
) выполняют интерпретацию непосредственно.
Диаграмма классов UML [ править ]
Примеры [ править ]
BNF [ править ]
Следующий пример формы Бэкуса – Наура иллюстрирует шаблон интерпретатора. Грамматика
выражение :: = плюс | минус | переменная | номерплюс :: = выражение выражение '+'минус :: = выражение выражение '-'переменная :: = 'a' | 'b' | 'c' | ... | 'z'цифра = '0' | '1' | ... | '9'число :: = цифра | цифровой номер
определяет язык, который содержит выражения обратной польской нотации, например:
ab +abc + -ab + ca - -
C # [ править ]
Этот структурный код демонстрирует шаблоны интерпретатора, которые, используя определенную грамматику, предоставляют интерпретатор, обрабатывающий проанализированные операторы.
используя Систему ; using System.Collections.Generic ;пространство имен ООП { класс Программа { статическая недействительность Main () { var context = new Context (); var input = new MyExpression (); var expression = new OrExpression { Left = new EqualsExpression { Left = input , Right = new MyExpression { Value = "4" } }, Right = new EqualsExpression { Left = input , Right = new MyExpression { Value = "четыре" } } } ; ввод . Значение = «четыре» ; выражение . Интерпретировать ( контекст ); // Вывод: "true" Консоль . WriteLine ( context . Result . Pop ()); ввод . Значение = «44» ; выражение . Интерпретировать ( контекст ); // Вывод: "false" Консоль . WriteLine ( context . Result . Pop ()); } } класс Context { публичный стек < строка > Результат = новый стек < строка > (); } интерфейс Expression { void Interpret ( контекстный контекст ); } абстрактный класс OperatorExpression : Expression { public Expression Left { private get ; набор ; } общественное право выражения { частное получение ; набор ; } public void Interpret ( Context context ) { Left . Интерпретировать ( контекст ); строка leftValue = context . Результат . Поп (); Верно . Интерпретировать ( контекст ); строка rightValue = context . Результат . Поп (); DoInterpret ( контекст , leftValue , rightValue ); } защищенная абстрактная пустота DoInterpret ( контекст контекста , строка leftValue , строка rightValue ); } class EqualsExpression : OperatorExpression { защищенное переопределение void DoInterpret ( контекст контекста , строка leftValue , строка rightValue ) { контекст . Результат . Push ( leftValue == rightValue ? "True" : "false" ); } } class OrExpression : OperatorExpression { защищенное переопределение void DoInterpret ( контекст контекста , строка leftValue , строка rightValue ) { контекст . Результат . Push ( leftValue == "true" || rightValue == "true" ? "True" : "false" ); } } класс MyExpression : Выражение { общедоступная строка Значение { частное получение ; набор ; } public void Interpret ( Context context ) { context . Результат . Нажать ( Значение ); } } }
Java [ править ]
Следуя шаблону интерпретатора, нам нужно реализовать интерфейс Expr с лямбда (это может быть класс) для каждого правила грамматики.
открытый класс Interpreter { @FunctionalInterface открытый интерфейс Expr { int интерпретация ( контекст Map < String , Integer > ); Статическая Expr номер ( INT номер ) { возвращение контекста -> номер ; } static Expr plus ( Expr left , Expr right ) { return context -> left . интерпретировать ( контекст ) + право . интерпретировать ( контекст ); } static Expr minus ( Expr left , Expr right ) { return context -> left . интерпретировать ( контекст ) - правильно . интерпретировать ( контекст ); } статическая переменная Expr ( имя строки ) { контекст возврата -> контекст . getOrDefault ( имя , 0 ); } }
Хотя шаблон интерпретатора не обращается к синтаксическому анализу, [1] : 247 синтаксический анализатор предоставляется для полноты.
частный статический Expr parseToken ( String token , ArrayDeque < Expr > stack ) { Expr left , right ; switch ( token ) { case "+" : // Сначала необходимо удалить правый операнд из стека right = stack . pop (); // ... а потом левый left = stack . pop (); return Expr . плюс (слева , справа ); case "-" : right = stack . pop (); left = стек . pop (); return Expr . минус ( слева , справа ); по умолчанию : вернуть Expr . переменная ( токен ); } } Общественности статической Expr синтаксического анализа ( Строка выражение ) { ArrayDeque < Expr > стек = новый ArrayDeque < Выражение > (); for ( Строковый токен : выражение . split ( "" )) { стек . push ( parseToken ( токен , стек )); } стек возврата . pop (); }
Наконец, вычисляем выражение «wxz - +» с w = 5, x = 10 и z = 42.
public static void main ( final String [] args ) { Expr expr = parse ( "wxz - +" ); Карта < String , Integer > context = Map . of ( "w" , 5 , "x" , 10 , "z" , 42 ); int result = expr . интерпретировать ( контекст ); Система .из . println ( результат ); // -27 } }
PHP (Пример 1) [ править ]
/ ** * AbstractExpression * / interface Expression { интерпретация публичной функции ( массив $ context ) : int ; }
/ ** * TerminalExpression * / class TerminalExpression реализует Expression { / ** @var string * / private $ name ; публичная функция __construct ( строка $ name ) { $ this -> name = $ name ; } интерпретация публичной функции ( массив $ context ) : int { return intval ( $ context [ $ this -> name ]); } }
/ ** * NonTerminalExpression * / абстрактный класс NonTerminalExpression реализует Expression { / ** @var Expression $ left * / protected $ left ; / ** @var? Выражение $ right * / protected $ right ; общественная функция __construct ( Expression $ влево , ? Выражение $ право ) { $ это -> влево = $ влево ; $ это -> право = $ право ; } абстрактная публичная интерпретация функции ( массив $ context ) : int ; публичная функция getRight () { return $ this -> right ; } публичная функция setRight ( $ right ) : void { $ this -> right = $ right ; } }
/ ** * NonTerminalExpression - PlusExpression * / class PlusExpression расширяет NonTerminalExpression { интерпретация публичной функции ( массив $ context ) : int { return intval ( $ this -> left -> интерпретировать ( $ context ) + $ this -> right -> интерпретировать ( $ context )); } }
/ ** * NonTerminalExpression - MinusExpression * / class MinusExpression расширяет NonTerminalExpression { интерпретацию публичной функции ( массив $ context ) : int { return intval ( $ this -> left -> интерпретировать ( $ context ) - $ this -> right -> интерпретировать ( $ context )); } }
/ ** * Клиент * / class InterpreterClient { защищенная функция parseList ( массив и $ стек , массив $ list , int & $ index ) { / ** @var string $ token * / $ token = $ list [ $ index ]; переключатель ( $ токен ) { case '-' : list ( $ left , $ right ) = $ this -> fetchArguments ( $ stack , $ list , $ index ); вернуть новое MinusExpression ( $ left , $ right ); case '+' : list ( $ left , $ right ) = $ this -> fetchArguments ( $ stack , $ list , $ index ); return new PlusExpression ( $ left , $ right ); по умолчанию : вернуть новое выражение TerminalExpression ( $ token ); } } защищенная функция fetchArguments ( массив & $ stack , array $ list , int & $ index ) : array { / ** @var Expression $ left * / $ left = array_pop ( $ stack ); / ** @var Выражение $ right * / $ right = array_pop ( $ stack ); если ( $ right === null ) { ++ $ index ; $ this -> parseListAndPush ( $ stack, $ список , $ индекс ); $ right = array_pop ( $ стек ); } вернуть массив ( $ left , $ right ); } защищенная функция parseListAndPush ( массив & $ stack , массив $ list , int & $ index ) { array_push ( $ stack , $ this -> parseList ( $ stack , $ list , $ index )); } синтаксический анализ защищенной функции ( строка $ data ) : Выражение { $ stack = []; $ list = explode ( ' , $ данные ); for ( $ index = 0 ; $ index < count ( $ list ); $ index ++ ) { $ this -> parseListAndPush ( $ stack , $ list , $ index ); } вернуть array_pop ( $ stack ); } публичная функция main () { $ data = "u + v - w + z" ; $ expr = $ this -> parse ( $ data ); $ context = [ 'u' => 3 , 'v' => 7 , 'w' => 35 , 'z' => 9 ]; $ res = $ expr -> интерпретировать ( $ context ); echo "результат: $ res " . PHP_EOL ; } }
// test.phpфункция loadClass ( $ className ) { require_once __DIR__ . "/ $ className .php" ; }spl_autoload_register ( 'loadClass' );( новый InterpreterClient ()) -> main (); // результат: -16
PHP (Пример 2) [ править ]
На основе приведенного выше примера с другой реализацией клиента.
/ ** * Клиент * / class InterpreterClient { публичная функция parseToken ( строка $ token , массив и $ stack ) : Expression { switch ( $ token ) { case '-' : / ** @var Expression $ left * / $ left = array_pop ( $ стек ); / ** @var Выражение $ right * / $ right = array_pop ( $ stack ); вернуть новое MinusExpression ($ влево , $ вправо ); case '+' : / ** @var Выражение $ left * / $ left = array_pop ( $ stack ); / ** @var Выражение $ right * / $ right = array_pop ( $ stack ); return new PlusExpression ( $ left , $ right ); по умолчанию : вернуть новое выражение TerminalExpression ( $ token ); } } синтаксический анализ публичной функции ( строка $ data ) : Выражение { $ notinishedData = null ; $ stack = []; $ list = explode ( ' , $ данные ); foreach ( $ list as $ token ) { $ data = $ this -> parseToken ( $ token , $ stack ); if ( ( $ notinishedData instanceof NonTerminalExpression ) && ( $ data instanceof Терминального выражения ) ) { $ UnfinishedData -> setRight ( $ data ); array_push ( $ stack , $ unfinishedData ); $ undefinishedData = ноль ; продолжить ; } if ( $ data instanceof NonTerminalExpression ) { if ( $ data -> getRight () === null ) { $ notinishedData = $ data ; продолжить ; } } array_push ( $ stack , $ data ); } вернуть array_pop ( $ stack ); } публичная функция main () { $ data = "u + v - w + z" ; $ expr = $ this -> parse ( $ data ); $ context = [ 'u' => 3 , 'v' => 7 , 'w' => 35 , 'z' => 9 ]; $ res = $ expr -> интерпретировать ( $ context ); echo "результат: $ res " . PHP_EOL ; } }
JavaScript [ править ]
Поскольку JavaScript не поддерживает реальную объектную ориентацию, мы не реализуем интерфейс.
// Нетерминальное выражение class Plus { a ; б ; конструктор ( а , б ) { это . а = а ; это . б = б ; } интерпретировать ( контекст ) { вернуть это . а . интерпретировать ( контекст ) + это . б . интерпретировать ( контекст ); } }// Нетерминальное выражение class Minus { a ; б ; конструктор ( а , б ) { это . а = а ; это . б = б ; } интерпретировать ( контекст ) { вернуть это . а . интерпретировать ( контекст ) - это . б . интерпретировать ( контекст ); } }// Нетерминальное выражение class Times { a ; б ; конструктор ( а , б ) { это . а = а ; это . б = б ; } интерпретировать ( контекст ) { вернуть это . а . интерпретировать ( контекст ) * this . б . интерпретировать ( контекст ); } }// Нетерминальное выражение class Divide { a ; б ; конструктор ( а , б ) { это . а = а ; это . б = б ; } интерпретировать ( контекст ) { вернуть это . а . интерпретировать ( контекст ) / это . б . интерпретировать ( контекст ); } }// Терминальное выражение class Number { a ; конструктор ( а , б ) { это . а = а ; } интерпретировать ( контекст ) { вернуть это . а ; } } // Терминальное выражение class Variable { a ; конструктор ( а ) { это . а = а ; } интерпретировать (context ) { return context [ this . а ] || 0 ; } } // Клиентский класс Parse { context ; конструктор ( контекст ) { это . context = context ; } parse ( выражение ) { let tokens = expression . сплит ( "" ); пусть очередь = []; для ( Пусть маркер из лексем ) { переключатель ( маркер ) { случай «+» : вар б = очередь . pop (); var a = очередь . pop (); var exp = new Plus ( a , b ); очередь . push ( exp ); перерыв ; case "/" : var b = очередь .pop (); var a = очередь . pop (); var exp = new Divide ( a , b ); очередь . push ( exp ); перерыв ; case "*" : var b = queue . pop (); var a = очередь . pop (); var exp = новое время ( a , b ); очередь. push ( exp ); перерыв ; case "-" : var b = queue . pop (); var a = очередь . pop (); var exp = новый минус ( a , b ); очередь . push ( exp ); перерыв ; по умолчанию : if ( isNaN ( токен )) { var exp = new Переменная ( токен ); очередь . push ( exp ); } Иначе { вар число = ParseInt ( маркер ); var exp = новое число ( число ); очередь . push ( exp ); } перерыв ; } } let main = queue . pop (); вернуть main . интерпретировать ( this .контекст ); } } var res = new Parse ({ v : 45 }). синтаксический анализ ( "16 v * 76 22 - -" ); консоль . журнал ( res ) // 666
См. Также [ править ]
- Форма Бэкуса – Наура
- Комбинаторная логика в вычислениях
- Шаблоны проектирования
- Доменный язык
- Переводчик (вычислитель)
Ссылки [ править ]
- ^ а б Гамма, Эрих ; Хелм, Ричард ; Джонсон, Ральф; Влиссидес, Джон (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон-Уэсли. ISBN 0-201-63361-2.
- ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. стр. 243ff . ISBN 0-201-63361-2.CS1 maint: несколько имен: список авторов ( ссылка )
- ^ «Шаблон проектирования интерпретатора - проблема, решение и применимость» . w3sDesign.com . Проверено 12 августа 2017 .
- ^ "Шаблон проектирования интерпретатора - структура и сотрудничество" . w3sDesign.com . Проверено 12 августа 2017 .
Внешние ссылки [ править ]
В Wikibook Computer Science Design Patterns есть страница по теме: Интерпретации на разных языках |
- Реализация интерпретатора в Ruby
- Реализация интерпретатора на C ++
- SourceMaking руководство
- Описание паттернов интерпретатора из Портлендского репозитория паттернов