2012-01-10 3 views
0

현재 8 개의 키워드 (대소 문자 구분 안함)와 4 개의 산술 연산자로 기본 인터프리터를 만드는 작업을하고 있습니다.여러 arraylists 또는 stringtokenizer와 배열을 사용하여 자바로 인터프리터 작성

# (signals start of a comment line) 
LET 
INTEGER 
STRING 
PRINT 
END 

를 어쨌든 나는 현재 구문 분석 할 수있는 텍스트의 라인을 토큰 화하기 위해 노력하고있어이 언어 프로그램은 (정말 BASIC의 구문과 유사)과 같이 보일 것입니다. 이미 모든 텍스트 행을 ArrayList로 구문 분석하고 문자열을 토큰 화했습니다. 내 현재 문제는 StringTokenizer가 모든 문자열을 미리 토큰 화한다는 것입니다. (필자는 분리 문자로 공백 문자를 사용하고 있습니다.) 필자가 필요로하는 것은 항상 키워드 줄을 찾는 것입니다.이 키워드는 항상 코드 줄 시작 부분의 첫 단어입니다 , 그리고 어떤 이슈들은 그것을 바람직하지 않게 만든다; 나는 String.split()을 사용하는 것이 많은 도움이 될 것이라고 생각하지 않는다.

내가 할 계획은 인터프리터에게 첫 번째 토큰을 찾아 HashMap을 통해 적절한 클래스로 이동시키는 것입니다 (cf. 내 인터프리터 용 switch 문 사용에 대한 이전 질문 참조). Switch or if statements in writing an interpreter in java, 다른 회원이지도를 사용하여 키워드 토큰을 제거하고 실행하도록 제안했습니다. 변수를 보관하기 위해 두 번째 임시 ArrayList 또는 배열을 설정하는 것이 좋습니다. 나는 그것이 지나치게 복잡하게되기를 원하지 않거나 필요로하지 않는다.

미리 제안 해 주셔서 감사합니다.

public static void main (String[]args) 
    { 
     try 
     { 
      ArrayList<String> demo= new ArrayList <String>(); 
      FileReader fr= new FileReader("hi.tpl"); 
      BufferedReader reader= new BufferedReader(fr); 
      String line; 
      while ((line=reader.readLine()) !=null)//read file line by line 
       { 
        //Add to ArrayList 
        demo.add(line); 
       } 

      reader.close(); 

      boolean checkEnd= demo.contains("END");//check if arraylist contains END statement 
        if(line=null && checkEnd== false) 
         { 
          System.out.println(" Unexpected end of file: no END statement"); 
          System.exit(0); 
         } 

      ListIterator<String>arrayListIt=demo.listIterator(); 
      while (arrayListIt.hasNext()) 
      for (String file: demo)// begin interpreting the program file here 
       {    
        StringTokenizer st=new StringTokenizer(file); 
        while(st.hasMoreTokens()) 
         { 

          int firstWord=file.indexOf(); 
          String command = file; 
          if (firstSpace > 0) 
          { 
           command= file.substring(0, firstSpace); 
          } 
          TokenHandler tokens= tokens.get(command.toUpperCase()); 
          if(tokens != null) 
          { 
           tokens.execute(file); 
          } 

         } 
+0

귀하의 질문이 명확하지 않습니다. –

+0

@JBNizet 메소드에서 인수를 파싱하는 것을 고려해야하는지, 아니면 모든 인수를 구문 분석하여 스크립트 행을 보유하고있는 것과 별도의 ArrayList에 넣어야하는지 묻고있었습니다. – Luinithil

답변

1

그래서 내가 더 많이 OO 접근법을 사용하고 싶습니다.

모두 동일한 인터페이스를 구현 한 명령에 대해 "클래스"를 만든 경우 어떻게됩니까? 인터페이스 - CommandObject가 execute() 메소드를 갖도록 호출하겠습니다.

"Let"와 같은 명령을 Let 클래스의 인스턴스에 매핑 한 사전로드 된 맵을 사용할 수 있습니다.

는 이제 메인 루프이 (의사)과 같이된다 :

for(line:lineList) 
    CommandObject commandObject=map.get(line.split()[0]) // do this more clearly 
    commandObject.execute(variableHash, line) // Parse and execute the line 

이 명령 개체 변수 세트 공유해야 할 것 - 일하는 것이 싱글 톤을 만드는하지만 다소 안티 - 패턴입니다, 대신 해시 맵 (위의 variableHash)으로 전달하는 것이 좋습니다.

이 접근법에 대한 좋은 점은 새로운 "명령"을 추가하는 것이 매우 간단하고 대부분 독립 적이라는 것입니다.

편집 (. 다시 주석) :

당신이해야 할 첫 번째 일은 해시 맵을 작성하고 각 명령을 "설치"입니다. 예를 들어 :

map.put("LET", new LetCommand()); 
map.put("INTEGER", new Integercommand()); 

오른손을 :

map = new HashMap<String, CommandObject> 

다음지도에 각 클래스의 인스턴스를 추가 (아직 psudeo 코드, 난 당신이 할당 자신을 선호하는 거라고 가정) 손 클래스는 "CommandObject"인터페이스를 구현합니다.

각 CommandObject는 키워드가 발견 될 때마다 다시 사용되는 인스턴스이므로 ANY 상태 (인스턴스 변수가 없음)를 저장하지 않아야 함을 의미합니다. 이는 CommandObject가 단일 방법과 비슷합니다.

execute(String commandLine, HashMap variables); 

이것은 아마도 가장 쉬운 방법 일 것입니다. (이를 반영하기 위해 원래의 제안에서 위의 텍스트를 편집했습니다.)

이 파서가 복잡해지면 "CommandObject"에 더 많은 기능을 추가하는 것이 효과적 일 수 있습니다. reset() 메서드가있는 한 상태 변수를 유지할 수 있습니다 (내 원래 제안은 지나치게 복잡해 보이지만 당신이하고있는 일)

명령 개체에 대한 키워드지도는 리플렉션으로 대체 될 수 있지만 학교 과제를 위해 시도하지는 마십시오. 리플렉션의 복잡성으로 인해 시간을 투자 할 가치가 없으며 교사가 그것을 이해하지 못하기 때문에 다운 그레이드 될 것입니다. 나는 모든 키워드가 테스트에 연결되어 (테스트를 묶고, 테스트를 반복하고, 테스트에 전달되고 조작 된 변수를 정의하고 전달할 수 있도록하는 시스템을 구현했습니다.이 경우에는 리플렉션이 가치가있었습니다. 새 테스트에서 캐시를 업데이트 할 필요가 없음)

+0

이것은 내가 쏘고 자했던 것입니다. 나는 새로운 키워드를 추가하는 것이 매우 간단 할 무언가를 원했습니다. 하지만 해시 맵으로 명령을 전달하는 것에 대해 더 자세히 설명 할 수 있습니까? 감사! – Luinithil

+0

대단히 감사합니다. @Bill K, 이것은 대단합니다. 이미 HashMap을 시작했습니다. – Luinithil

0

정확한 디자인은 나중에 이러한 인수를 구문 분석하지 않는 것이 좋습니다. "STRING"또는 "PRINT"와 같은 명령은 공백에 관계없이 STRING S = "HELLO WORLD"가 실제로 STRING S = "HELLOWORLD"와 기능면에서 다를 수 있습니다. 이처럼 앞장서 서 "오버 엔지니어링"하고 싶지는 않습니다. 이제는 작동하는 가장 단순한 작업을 수행하고 명령 클래스 중 하나 또는 두 개를 작성한 다음 해당 명령 클래스가 공통으로 갖고있는 것을 파악하는 것이 좋습니다.

나중에 (또는 대부분의) 사용자 명령에서 이러한 인수를 특정 방식으로 목록으로 구문 분석하려는 경우 "목록 구문 분석 코드"를 정적 유틸리티 메서드 (또는 경우에 따라 비표준 메서드)로 다시 정의 할 수 있습니다. 상속을 사용하는 경우 부모 Command 클래스 자체에 대한 정적 유틸리티 메소드). 자동화 된 테스트 세트를 만드는 것이 현명하다면 범위가 벗어날 수 있습니다. 너의 임무 중.

0

좋은 방법 중 하나는 enum입니다. 그리고 split을 2 개 항목으로 제한하는 것을 삼가고 싶습니다.

enum Command { 
    LET { 
     @Override 
     public void execute(Context context, String args) { 
     } 
    }, 
    INTEGER { ... }, 
    STRING { ... }, 
    PRINT { ... }, 
    END { ... }; 

    public abstract void execute(Context context, String args); 
} 

private void executeLine(String line) { 
    String[] commandAndArgs = line.split("\\s+", 2); 
    String command = ""; 
    String args = ""; 
    if (commandAndArgs.length > 0) 
     command = commandArgs[0].toUpperCase(); 
    if (commandAndArgs.length > 1) 
     args = commandArgs[1]; 
    Command cmd = Command.valueOf(command); 
    Context context = ...; 
    cmd.execute(context, args); 
} 
관련 문제