어휘 분석과 자바 : 1 부

어휘 분석 및 구문 분석

Java 애플리케이션을 작성할 때 생성해야하는 일반적인 것 중 하나는 파서입니다. 파서는 단순한 것부터 복잡한 것까지 다양하며 명령 줄 옵션에서 Java 소스 코드 해석에 이르기까지 모든 작업에 사용됩니다. 에서 JavaWorld 의 12 월호, 나는 당신에게 파서를 구현하는 자바 클래스로 변환 높은 수준의 문법 사양 그 사양에 의해 기술 된 것을 자동 파서 생성기를 잭을 보였다. 이번 달에는 Java가 대상 어휘 분석기 및 파서를 작성하기 위해 제공하는 리소스를 보여 드리겠습니다. 이 다소 간단한 파서는 간단한 문자열 비교와 Jack이 컴파일하는 복잡한 문법 사이의 간격을 메 웁니다.

어휘 분석기의 목적은 입력 문자 스트림을 가져 와서 파서가 이해할 수있는 더 높은 수준의 토큰으로 디코딩하는 것입니다. 파서는 어휘 분석기의 출력을 사용하고 반환 된 토큰 시퀀스를 분석하여 작동합니다. 구문 분석기는 이러한 시퀀스를 종료 상태에 일치 시키며, 이는 여러 종료 상태 중 하나 일 수 있습니다. 최종 상태는 목표를 정의합니다.파서의. 종료 상태에 도달하면 파서를 사용하는 프로그램은 데이터 구조를 설정하거나 일부 작업 별 코드를 실행하는 등 몇 가지 작업을 수행합니다. 또한 파서는 법적 최종 상태에 도달 할 수없는 경우 처리 된 토큰 시퀀스에서 감지 할 수 있습니다. 이 시점에서 파서는 현재 상태를 오류 상태로 식별합니다. 파서가 종료 상태 또는 오류 상태를 식별 할 때 취할 조치를 결정하는 것은 애플리케이션의 몫입니다.

표준 Java 클래스베이스에는 두 개의 어휘 분석기 클래스가 포함되어 있지만 범용 파서 클래스는 정의하지 않습니다. 이 칼럼에서는 Java와 함께 제공되는 어휘 분석기에 대해 자세히 살펴 보겠습니다.

자바의 어휘 분석기

Java 언어 사양 버전 1.0.2는 두 개의 어휘 분석기 클래스 StringTokenizerStreamTokenizer. 이름에서 당신은 추론 할 수 StringTokenizer사용의 String입력과 같은 개체 및 StreamTokenizer사용의 InputStream객체.

StringTokenizer 클래스

사용 가능한 두 가지 어휘 분석기 클래스 중 이해하기 가장 쉬운 것은 StringTokenizer. 새 StringTokenizer객체 를 생성 할 때 생성자 메서드는 명목상 입력 문자열과 구분자 문자열의 두 값을 사용합니다. 그런 다음 클래스는 구분 문자 사이의 문자를 나타내는 일련의 토큰을 생성합니다.

어휘 분석기 StringTokenizer로서 아래와 같이 공식적으로 정의 할 수 있습니다.

[~ delim1, delim2, ..., delim N ] :: 토큰

이 정의는 구분 문자를 제외한 모든 문자와 일치하는 정규식으로 구성됩니다 . 인접한 모든 일치하는 문자는 단일 토큰으로 수집되어 토큰으로 반환됩니다.

StringTokenizer클래스 의 가장 일반적인 용도 는 쉼표로 구분 된 숫자 목록과 같은 매개 변수 집합을 구분하는 것입니다. StringTokenizer구분 기호를 제거하고 데이터를 반환하므로이 역할에 이상적입니다. 이 StringTokenizer클래스는 또한 "null"토큰이있는 목록을 식별하는 메커니즘을 제공합니다. 일부 매개 변수에 기본값이 있거나 모든 경우에 존재하지 않아도되는 애플리케이션에서 널 토큰을 사용합니다.

아래 애플릿은 간단한 StringTokenizer연습기입니다. StringTokenizer 애플릿의 소스는 여기에 있습니다. 애플릿을 사용하려면 분석 할 텍스트를 입력 문자열 영역에 입력 한 다음 구분 문자로 구성된 문자열을 구분 문자열 영역에 입력합니다. 마지막으로 Tokenize! 단추. 결과는 입력 문자열 아래의 토큰 목록에 표시되며 한 줄에 하나의 토큰으로 구성됩니다.

이 애플릿을 보려면 Java 사용 가능 브라우저가 필요합니다.

StringTokenizer쉼표 (,)를 구분 문자로 사용하여 구성된 객체에 전달 된 문자열 "a, b, d"를 예로 들어 보겠습니다 . 위의 연습기 애플릿에 이러한 값을 입력하면 Tokenizer객체가 문자열 "a", "b"및 "d"를 반환하는 것을 볼 수 있습니다. 하나의 매개 변수가 누락되었음을 지적하려는 의도가 있었다면 토큰 시퀀스에서 이에 대한 표시가 없다는 사실에 놀랐을 것입니다. 누락 된 토큰을 감지하는 기능은 Tokenizer객체 를 생성 할 때 설정할 수있는 Return Separator 부울을 통해 활성화됩니다 . Tokenizer이 구성 될 때이 매개 변수를 설정하면 각 구분 기호도 반환됩니다. 위의 애플릿에서 Return Separator 확인란을 클릭하고 문자열과 구분 기호는 그대로 둡니다. 이제Tokenizer"a, comma, b, comma, comma 및 d"를 반환합니다. 두 개의 구분 문자를 순서대로 가져 오면 "null"토큰이 입력 문자열에 포함되었는지 확인할 수 있습니다.

StringTokenizer파서에서 성공적으로 사용하는 비결 은 구분 문자가 데이터에 나타나지 않는 방식으로 입력을 정의하는 것입니다. 응용 프로그램에서 디자인하여 이러한 제한을 피할 수 있습니다. 아래의 메소드 정의는 매개 변수 스트림에서 빨강, 녹색 및 파랑 값의 형태로 색상을 받아들이는 애플릿의 일부로 사용할 수 있습니다.

/ ** * "10,20,30"형식의 매개 변수를 색상 값에 대한 * RGB 튜플로 구문 분석합니다. * / 1 색상 getColor (문자열 이름) {2 문자열 데이터; 3 StringTokenizer st; 4 int 빨강, 녹색, 파랑; 5 6 데이터 = getParameter (name); 7 if (data == null) 8 return null; 9 10 st = new StringTokenizer (data, ","); 11 시도 {12 red = Integer.parseInt (st.nextToken ()); 13 녹색 = Integer.parseInt (st.nextToken ()); 14 파란색 = Integer.parseInt (st.nextToken ()); 15} catch (예외 e) {16 return null; // (오류 상태) 파싱 할 수 없습니다. 17} 18 return new Color (red, green, blue); // (END STATE) 완료. 19}

위의 코드는 "number, number, number"문자열을 읽고 새 Color객체를 반환하는 매우 간단한 파서를 구현 합니다. 10 행에서 코드 StringTokenizer는 매개 변수 데이터 (이 메서드가 애플릿의 일부라고 가정)를 포함 하는 새 개체와 쉼표로 구성된 구분 문자 목록을 만듭니다. 그런 다음 12, 13 및 14 행에서 각 토큰은 문자열에서 추출되고 Integer parseInt메서드를 사용하여 숫자로 변환됩니다 . 이러한 변환은 try/catch숫자 문자열이 유효한 숫자가 아니거나 Tokenizer토큰이 부족하여 예외가 발생하는 경우 블록 으로 둘러싸여 있습니다. 모든 숫자가 변환되면 최종 상태에 도달하고 Color객체가 반환됩니다. 그렇지 않으면 오류 상태에 도달하고 null 이 반환됩니다.

StringTokenizer클래스 의 한 가지 특징 은 쉽게 쌓을 수 있다는 것입니다. getColor위의 방법의 10 ~ 18 행인 아래에 명명 된 방법을보십시오.

/ ** * 컬러 튜플 "r, g, b"를 AWT Color객체 로 구문 분석 합니다. * / 1 Color getColor (String data) {2 int red, green, blue; 3 StringTokenizer st = new StringTokenizer (data, ","); 4 시도 {5 red = Integer.parseInt (st.nextToken ()); 6 녹색 = Integer.parseInt (st.nextToken ()); 7 파란색 = Integer.parseInt (st.nextToken ()); 8} catch (예외 e) {9 return null; // (ERROR STATE) 파싱 할 수 없음 10} 11 return new Color (red, green, blue); // (END STATE) 완료. 12}

약간 더 복잡한 파서가 아래 코드에 나와 있습니다. 이 파서는 객체 getColors배열을 반환하도록 정의 된 메서드에서 구현 Color됩니다.

/ ** * "r1, g1, b1 : r2, g2, b2 : ... : rn, gn, bn"색상 세트를 * AWT 색상 객체의 배열로 구문 분석합니다. * / 1 Color [] getColors (String data) {2 Vector accum = new Vector (); 3 색 cl, 결과 []; 4 StringTokenizer st = new StringTokenizer (data, ":"); 5 while (st.hasMoreTokens ()) {6 cl = getColor (st.nextToken ()); 7 if (cl! = null) {8 accum.addElement (cl); 9} else {10 System.out.println ( "오류-잘못된 색상."); 11} 12} 13 if (accum.size () == 0) 14 return null; 15 결과 = new Color [accum.size ()]; 16 for (int i = 0; i <accum.size (); i ++) {17 result [i] = (Color) accum.elementAt (i); 18} 19 반환 결과; 20}

위의 방법은 방법과 약간만 다르지만 getColor4 ~ 12 행의 코드 Tokenizer는 콜론 (:) 문자로 둘러싸인 토큰을 추출하기 위해 새로 만듭니다 . 메서드에 대한 문서 주석에서 읽을 수 있듯이이 메서드는 컬러 튜플이 콜론으로 구분 될 것으로 예상합니다. 를 호출 할 때마다 nextToken에서 StringTokenizer문자열이 소진 될 때까지 클래스는 새 토큰을 반환합니다. 반환되는 토큰은 쉼표로 구분 된 숫자 문자열입니다. 이 토큰 문자열은에 공급되고 getColor세 숫자에서 색상을 추출합니다. StringTokenizer다른 StringTokenizer객체가 반환 한 토큰을 사용하여 새 객체를 생성하면 우리가 작성한 파서 코드가 문자열 입력을 해석하는 방법에 대해 좀 더 정교해질 수 있습니다.

유용할수록 결국 StringTokenizer클래스 의 능력을 고갈시키고 큰 형으로 이동해야합니다 StreamTokenizer.

StreamTokenizer 클래스

클래스 이름에서 알 수 있듯이 StreamTokenizer객체는 입력이 InputStream클래스 에서 나올 것으로 예상합니다 . 등 StringTokenizer위,이 클래스는 구문 분석 코드가 해석 할 수있는 덩어리로 입력 스트림을 변환,하지만 그건 어디 유사성 끝입니다.

StreamTokenizerA는 테이블 중심의 어휘 분석기. 즉, 가능한 모든 입력 문자에 중요도가 할당되고 스캐너는 현재 문자의 중요도를 사용하여 수행 할 작업을 결정합니다. 이 클래스의 구현에서 문자에는 세 가지 범주 중 하나가 할당됩니다. 이것들은:

  • Whitespace characters -- their lexical significance is limited to separating words

  • Word characters -- they should be aggregated when they are adjacent to another word character

  • Ordinary characters -- they should be returned immediately to the parser

Imagine the implementation of this class as a simple state machine that has two states -- idle and accumulate. In each state the input is a character from one of the above categories. The class reads the character, checks its category and does some action, and moves on to the next state. The following table shows this state machine.

State Input Action New state
idle word character push back character accumulate
ordinary character return character idle
whitespace character consume character idle
accumulate word character add to current word accumulate
ordinary character

return current word

push back character

idle
whitespace character

return current word

consume character

idle

On top of this simple mechanism the StreamTokenizer class adds several heuristics. These include number processing, quoted string processing, comment processing, and end-of-line processing.

The first example is number processing. Certain character sequences can be interpreted as representing a numerical value. For example, the sequence of characters 1, 0, 0, ., and 0 adjacent to each other in the input stream represent the numerical value 100.0. When all of the digit characters (0 through 9), the dot character (.), and the minus (-) character are specified as being part of the word set, the StreamTokenizer class can be told to interpret the word it is about to return as a possible number. Setting this mode is achieved by calling the parseNumbers method on the tokenizer object that you instantiated (this is the default). If the analyzer is in the accumulate state, and the next character would not be part of a number, the currently accumulated word is checked to see if it is a valid number. If it is valid, it is returned, and the scanner moves to the next appropriate state.

다음 예는 인용 문자열 처리입니다. 따옴표 문자 (일반적으로 큰 따옴표 ( ") 또는 작은 따옴표 ( '))로 묶인 문자열을 단일 토큰으로 전달하는 것이 바람직한 경우가 많습니다.이 StreamTokenizer클래스를 사용하면 모든 문자를 따옴표 문자로 지정할 수 있습니다. 기본적으로 이들은 작은 따옴표 ( ') 및 큰 따옴표 ( ") 문자입니다. 상태 시스템은 다른 따옴표 또는 줄 끝 문자가 처리 될 때까지 누적 상태의 문자를 사용하도록 수정됩니다. 따옴표 문자를 인용 할 수 있도록 분석기는 입력 스트림 및 따옴표 안에있는 백 슬래시 (\)가 앞에 오는 따옴표 문자를 단어 문자로 처리합니다.