일반적으로 파싱되는 일반적인 파서는 파서가 입력에 닿기 전에 렉서 단계를 갖습니다. 렉서 ( "스캐너"또는 "토큰 라이저")는 입력을 유형이 주석이 달린 작은 토큰으로 자릅니다. 이를 통해 주 파서는 각 문자를 터미널로 취급하지 않고 토큰을 터미널 요소로 사용할 수 있으므로 효율성이 현저히 향상됩니다. 특히, 어휘 분석기는 모든 주석과 공백을 제거 할 수 있습니다. 그러나 별도의 토크 나이저 단계는 키워드가 식별자로 사용될 수 없음을 의미합니다 (언어가 선호 하지 않는 스트로 핑 을 지원 하거나 모든 식별자 앞에시길이가 붙지 않는 경우 $foo
).
왜? 다음 토큰을 이해하는 간단한 토크 나이저가 있다고 가정 해 봅시다.
FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'
토크 나이 저는 항상 가장 긴 토큰과 일치하며 식별자보다 키워드를 선호합니다. 그래서 interesting
으로 lexed됩니다 IDENT:interesting
만, in
같은 lexed되지 않습니다 IN
으로, 결코 IDENT:interesting
. 같은 코드 스 니펫
for(var in expression)
토큰 스트림으로 변환됩니다
FOR LPAREN IDENT:var IN IDENT:expression RPAREN
지금까지는 효과가 있습니다. 그러나 모든 변수 in
는 변수 가 IN
아닌 키워드로 표현되어 코드가 손상됩니다. 어휘 분석기는 토큰들 사이에 어떠한 상태도 유지하지 않으며, in
우리가 for 루프에있을 때를 제외하고는 보통 변수 여야한다는 것을 알 수 없습니다 . 또한 다음 코드는 합법적이어야합니다.
for(in in expression)
첫 번째 in
는 식별자이고 두 번째는 키워드입니다.
이 문제에 대한 두 가지 반응이 있습니다.
문맥 키워드가 혼동되기 때문에 대신 키워드를 다시 사용하겠습니다
Java에는 많은 예약어가 있으며, 그 중 일부는 C ++에서 Java로 전환하는 프로그래머에게 더 유용한 오류 메시지를 제공하는 것 외에는 사용되지 않습니다. 새 키워드를 추가하면 코드가 손상됩니다. 문맥 강조 표시 키워드를 추가하면 구문 강조 표시가 우수하지 않으면 코드 독자에게 혼란스러워지며 고급 구문 분석 기술을 사용해야하므로 툴링을 구현하기가 어렵습니다 (아래 참조).
우리가 언어를 확장하고 싶을 때, 유일한 건전한 접근법은 이전에는 그 언어에서 합법적이지 않은 기호를 사용하는 것입니다. 특히 이들은 식별자가 될 수 없습니다. foreach 루프 구문을 사용하여 Java는 기존 :
키워드를 새로운 의미로 재사용했습니다 . 람다를 사용하여 Java는 ->
이전에 어떤 법적 프로그램에서도 발생할 수 없었던 키워드를 추가했습니다 ( -->
여전히 '--' '>'
합법적 인 ->
것으로 어휘 화되고 이전에는로 어휘 화 '-', '>'
되었지만 파서가 해당 시퀀스를 거부합니다).
문맥 키워드는 언어를 단순화하고 구현합니다
Lexers는 틀림없이 유용합니다. 그러나 파서 전에 렉서를 실행하는 대신 파서와 함께 실행할 수 있습니다. 상향식 파서는 항상 특정 위치에서 사용할 수있는 일련의 토큰 유형을 알고 있습니다. 그런 다음 파서는 현재 위치에서 이러한 유형과 일치하도록 어휘 분석기를 요청할 수 있습니다. for-each 루프에서 파서는 ·
변수가 발견 된 후 (단순화 된) 문법으로 표시된 위치에 있습니다 .
for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'
그 위치에서, 법적 토큰은 SEMICOLON
또는 IN
,하지만 IDENT
. 키워드 in
는 전적으로 모호하지 않습니다.
이 특정 예에서 위의 문법을 다음과 같이 다시 작성할 수 있으므로 하향식 파서는 문제가 없습니다.
for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest = · ';' expression ';' expression
for_loop_rest = · 'in' expression
결정에 필요한 모든 토큰은 역 추적없이 볼 수 있습니다.
사용성 고려
자바는 항상 의미 론적 및 구문 적 단순성으로 향했다. 예를 들어, 언어는 코드를 훨씬 더 복잡하게 만들므로 연산자 오버로드를 지원하지 않습니다. 사이에 결정할 때 그래서 in
및 :
A에 대한-각 루프 구문, 우리는 덜 혼란과 사용자에게 더 분명하다 고려해야합니다. 극단적 인 경우는 아마도
for (in in in in())
for (in in : in())
(참고 : Java에는 유형 이름, 변수 및 메소드에 대한 별도의 네임 스페이스가 있습니다. 이것이 대부분 실수라고 생각합니다. 이것은 나중에 언어 디자인이 더 많은 실수 를 추가해야한다는 의미는 아닙니다 .)
어떤 대안이 반복 변수와 반복 컬렉션 사이에 명확한 시각적 분리를 제공합니까? 코드를 살펴보면 어떤 대안을 더 빨리 인식 할 수 있습니까? 이러한 기준에서 기호를 분리하는 것이 단어의 문자열보다 낫다는 것을 알았습니다. 다른 언어는 다른 값을 갖습니다. 예를 들어 파이썬은 자연스럽게 읽을 수 있고 이해하기 쉽도록 많은 연산자를 영어로 설명하지만, 동일한 속성으로 인해 한 조각의 파이썬을 한눈에 이해하기가 매우 어려울 수 있습니다.