Академический Документы
Профессиональный Документы
Культура Документы
Синтаксический анализ
Джон Харрисон
Университет Кембриджа
10 сентября 2008 г.
Темы
term −→ name(termlist)
| name
| (term)
| numeral
| −term
| term+term
| term∗term
termlist −→ term,termlist
| term
Неоднозначность
Задача синтаксического анализа (разбора), в целом, является
обратной рассмотренной ранее задаче составления грамматики.
Цель синтаксического анализа — найти последовательность
применения продукций, порождающую данную строку.
К сожалению, наша грамматика является неоднозначной, поскольку
некоторые строки могут порождаться различными путями, например
term −→ term+term
−→ term+term∗term
term −→ term∗term
−→ term+term∗term
Назначение приоритетов
Приоритеты операций могут быть выражены путём введения
дополнительных категорий, например
atom −→ name(termlist)
| name
| numeral
| (term)
| −atom
mulexp −→ atom∗mulexp
| atom
term −→ mulexp+term
| mulexp
termlist −→ term,termlist
| term
(α)list → β × (α)list
Определение комбинаторов
l e t p r e f i x ++ p a r s e r 1 p a r s e r 2 i n p u t =
let result1 , rest1 = parser1 input in
let result2 , rest2 = parser2 rest1 in
( result1 , result2 ) , rest2 ; ;
l e t r e c many p a r s e r i n p u t =
try let r e s u l t , next = parser input in
l e t r e s u l t s , r e s t = many p a r s e r n e x t i n
( result :: results ) , rest
with N o p a r s e −> [ ] , i n p u t ; ;
l e t p r e f i x >> p a r s e r t r e a t m e n t i n p u t =
let result , r e s t = parser input in
treatment ( r e s u l t ) , r e s t ; ;
Вспомогательные функции
Введём следующие универсальные функции, которыми
воспользуемся в дальнейшем:
l e t rec i t l i s t f =
fun [ ] b −> b
| ( h : : t ) b −> f h ( i t l i s t f t b);;
let uncurry f (x , y ) = f x y ; ;
let K x y = x ; ;
let C f x y = f y x ; ;
let o f g x = f (g x ) ; ;
#i n f i x " o " ; ;
let explode s =
l e t r e c ex ap n l =
i f n < 0 then l e l s e
exap ( n − 1 ) ( ( s u b _ s t r i n g s n 1 ) : : l ) i n
exap ( s t r i n g _ l e n g t h s − 1 ) [ ] ; ;
Джон Харрисон Введение в Функциональное программирование
Лекция 10. Примеры на ML II. Синтаксический анализ
Элементарные анализаторы
Для начала, определим несколько примитивных синтаксических
анализаторов:
l e t some p =
fun [ ] −> r a i s e N o p a r s e
| ( h : : t ) −> i f p h then ( h , t )
e l s e r a i s e Noparse ; ;
l e t a tok =
some ( fun i t e m −> i t e m = t o k ) ; ;
Лексический анализ
Для начала нам потребуется провести лексический анализ, т. е.
разбить входную последовательность символов на лексемы (токены).
Это также возможно сделать, используя наши комбинаторы в
сочетании с несколькими функциями классификации символов.
Прежде всего, определим тип, представляющий лексемы:
type t o k e n = Name o f s t r i n g
| Num o f s t r i n g
| Other of s t r i n g ; ;
Анализатор термов
Реализацию анализа языка термов начнем с определения базовых
анализаторов отдельных лексем заданного класса:
l e t name =
fun ( Name s : : r e s t ) −> s , r e s t
| _ −> r a i s e N o p a r s e ; ;
l e t numeral =
fun (Num s : : r e s t ) −> s , r e s t
| _ −> r a i s e N o p a r s e ; ;
let other =
fun ( O t h e r s : : r e s t ) −> s , r e s t
| _ −> r a i s e N o p a r s e ; ;
Примеры
Объединим определённые ранее примитивы в единую функцию:
let parser =
f s t o ( term ++ f i n i s h e d >> f s t ) o l e x ; ;
let findmin l = i t l i s t
( fun (_, p r 1 as p1 ) (_, p r 2 as p2 ) −>
i f p r 1 <= p r 2 then p1 e l s e p2 ) ( t l l ) ( hd l ) ; ;
l e t rec d e l e t e x ( h : : t ) =
i f h = x then t e l s e h : : ( d e l e t e x t ) ; ;
l e t rec precedence i l i s t p a r s e r i n p u t =
i f i l i s t = [ ] then p a r s e r i n p u t e l s e
l e t opp = f i n d m i n i l i s t i n
l e t i l i s t ’ = d e l e t e opp i l i s t i n
b i n o p ( f s t opp ) ( p r e c e d e n c e i l i s t ’ p a r s e r ) i n p u t ; ;
term −→ name(termlist)
| name
| ···
Устранение избыточности
Заменим
let . . .
and t e r m l i s t i n p u t
= ( term ++ a ( O t h e r " , " ) ++ t e r m l i s t
>> ( fun ( ( h ,_) , t ) −> h : : t )
| | term
>> ( fun h −> [ h ] ) ) i n p u t ; ;
на
let . . .
and t e r m l i s t i n p u t
= term ++
many ( a ( O t h e r " , " ) ++ term >> snd )
>> ( fun ( h , t ) −> h : : t ) i n p u t ; ;
l e t r e c atom i n p u t
= ( name ++
a ( O t h e r " ( " ) ++ t e r m l i s t ++ a ( O t h e r " ) " )
>> ( fun ( ( ( f ,_) , a ) ,_) −> Fn ( f , a ) )
| | name
>> ( fun s −> Var s )
| | numeral
>> ( fun s −> C o n s t s )
| | a ( O t h e r " ( " ) ++ term ++ a ( O t h e r " ) " )
>> ( snd o f s t )
| | a ( O t h e r "−" ) ++ atom
>> snd ) i n p u t
and term i n p u t = p r e c e d e n c e ( ! i n f i x e s ) atom i n p u t
and t e r m l i s t i n p u t
= ( term ++ many ( a ( O t h e r " , " ) ++ term >> snd )
>> ( fun ( h , t ) −> h : : t ) ) i n p u t ; ;
Общие замечания
Рассмотренный метод синтаксического анализа может с разумной
осторожностью эффективно использоваться в различных приложениях.
Он служит хорошей иллюстрацией обширных возможностей функций
высших порядков.
Код анализатора тщательно структурирован и соответствует
грамматике, что обеспечивает лёгкость его модификации.
Однако, он не так эффективен, как хорошие LR-анализаторы, в
частности те, которые могут быть автоматически сгенерированы
CAML-Yacc.
Ещё одним ограничением метода рекурсивного спуска являются
проблемы с обработкой левосторонней рекурсии в продукциях.
Например, если бы мы попытались сделать левоассоциативной
операцию сложения, определённую в рассмотренных ранее примерах,
это можно было бы выразить так:
term −→ term+mulexp
| mulexp