2013-07-14 2 views
3

저는 C++ 프로젝트 중 하나 인 단순한 DSL을 녹이기 운동에서 녹이기 위해 변환하고 있습니다. 중첩 된 구조와 소유권에 문제가 있습니다. 내가 좋아하는 몇 가지 일을 변환하는 힘든 시간을 보내고 있습니다 : 선택 소유 포인터를 녹스 (rust)로 관리하려면 어떻게합니까?

use std::str; 

pub struct FileData { 
    is_utf8 : bool, 
    file_name : ~str 
} 

pub fn FileData() -> FileData { 
    FileData { is_utf8 : true, file_name : ~"" } 
} 

enum TokenType { 
    REGULAR, 
    INCLUDE_FILE 
} 

pub struct Token { 
    priv _include_data : Option<~FileData>, 
    priv _type : TokenType 
} 

pub fn Token() -> Token { 
    Token { 
     _include_data: None, 
     _type : REGULAR 
    } 
} 

impl Token { 
    pub fn get_type(&self) -> TokenType { 
     self._type 
    } 

    pub fn beginIncludeFile(&mut self) { 
     self._type = INCLUDE_FILE; 
     self._include_data = Some(~FileData()); 
    } 

    pub fn is_utf8(&self) -> bool { 
     match self._include_data { 
      Some(ref data) => data.is_utf8, 
      _ => fail!("No FileData") 
     } 
    } 

    pub fn set_utf8(&mut self, value : bool) { 
     self._include_data.mutate(|mut data| { 
      data.is_utf8 = value; 
      data 
     }); 
    } 

    // Return immutable/read-only copy 
    pub fn get_file_name(&self) -> &~str { 
     match self._include_data { 
      Some(ref data) => &data.file_name, 
      _ => fail!("No FileData") 
     } 
    } 

    pub fn setFileNameToEmpty(&mut self) { 
     match self._include_data { 
      Some(ref data) => data.file_name = ~"", 
      _ => fail!("No FileData") 
     } 
     return; 
    } 

    pub fn appendToFileName(&mut self, c : char) { 
     match self._include_data { 
      Some(ref data) => data.file_name.push_char(c), 
      _ => fail!("No FileData") 
     } 
     return; 
    } 

    pub fn getIncludeData(&mut self) -> ~FileData { 
     match self._include_data { 
      Some(ref data) => *data, 
      _ => fail!("No FileData") 
     } 
    } 
} 

enum LexState { 
    INITIAL, 
    EXPECT_COLON, 
    EXPECT_ENCODING, 
    EXPECT_QUOTE, 
    IN_FILENAME_STRING, 
    EXPECT_SEMI 
} 

impl Eq for LexState { 
    fn eq(&self, other: &LexState) -> bool { 
     return (*self as int) == (*other as int); 
    } 
    fn ne(&self, other: &LexState) -> bool { 
     !self.eq(other) 
    } 
} 

fn main() { 
    let mut t = ~Token(); 
    let input = ~"include:utf8 \"file_path/file.foo\";"; 
    let iter = input.iter(); 
    let mut buf : ~str = ~""; 

    let mut state : LexState = INITIAL; 

    let buf_action = |action : &fn()| { 
     buf = ~""; 
     action(); 
    }; 

    while true { 
     let c = iter.next(); 
     match c { 
      None => break, 
      Some(_c) => buf.push_char(_c) 
     } 

     match buf { 
      // Initial state 
      ~"include" if state == INITIAL => buf_action(|| { 
       t.beginIncludeFile(); 
       state = EXPECT_COLON; 
      }), 

      // Expecting either an encoding, or the start of the file name 
      ~":" if state == EXPECT_COLON => buf_action(|| { state = EXPECT_ENCODING; }), 
      _ if state == EXPECT_COLON => state = EXPECT_QUOTE, // match WS 

      // utf8 is the only encoding accepted at the moment 
      ~"utf8" if state == EXPECT_ENCODING => buf_action(|| { 
       t.set_utf8(true); 
       state = EXPECT_QUOTE; 
      }), 
      _ if state == EXPECT_ENCODING => t.set_utf8(false), 

      // Looking for string start 
      ~"\"" if state == EXPECT_QUOTE => buf_action(||{ state = IN_FILENAME_STRING; }), 
      _ if state == EXPECT_QUOTE =>(), // ignore other chars 

      // Reading filename 
      ~"\"" if state == IN_FILENAME_STRING => buf_action(|| { 
       state = EXPECT_SEMI; 
      }), 
      _ if state == IN_FILENAME_STRING => t.appendToFileName(c.unwrap()), 

      // End of lex 
      ~":" if state == EXPECT_SEMI => break, 
      _ if state == EXPECT_SEMI => fail!("Expected semi"), 

      _ => fail!("Unexpected character: " + str::from_char(c.unwrap())) 

     } 
    } 
    return; 
} 

코드의 이러한 종류의 숙어 녹 방법은 무엇입니까 :

struct FileData { 
    bool is_utf8; 
    std::string file_name; 
}; 

class Token { 
public: 
    enum TokenType { 
     REGULAR, 
     INCLUDE_FILE, 

    } 

    Token() { 
     _type = REGULAR; 
    } 

    Type get_type() const { return _type; } 

    void beginIncludeFile() { 
     _type = INCLUDE_FILE; 
     _include_data = std::unique_ptr<FileData>(new FileData); 
    } 

    bool is_utf8() const { 
     assert(get_type() == INCLUDE_FILE); 
     return _include_data->is_utf8; 
    } 

    void set_utf8(bool value) { 
     assert(get_type() == INCLUDE_FILE); 
     _include_data->is_utf8 = value; 
    } 

    const std::string& get_file_name() const { 
     assert(get_type() == INCLUDE_FILE); 
     return _include_data->file_name; 
    } 

    void setFileNameToEmpty() { 
     assert(get_type() == INCLUDE_FILE); 
     _include_data->file_name = ""; 
    } 

    void appendToFileName(char c) { 
     assert(get_type() == INCLUDE_FILE); 
     _include_data->file_name += c; 
    } 

    FileData* releaseFileData() { return _include_data.release(); } 
private: 
    std::unique_ptr<FileData> _include_data; 
    TokenType _type; 
}; 

나는이 때문이다 작성합니다 녹을?

+0

은 실제로 '~ 자식'입니까? C++에서는 null을 검사하지 않습니다. 귀하의 예가 OOP 상용구 유형 코드 만 있고 명시적인 목적이없는 경우 관용적이라고 말하는 것은 어렵습니다. – u0b34a0f6ae

+1

@ u0b34a0f6ae 코드를 업데이트했습니다. DSL 용 간단한 파서를 변환하고 있습니다. – Naddiseo

답변

5

녹은 C + +와는 완전히 다른데, 라인 단위의 직선 변환은 비 관용적 인 코드를 제공합니다. str 's 및 [] 년대와


fn foo<'a>(&'a self) -> &'a SomeInformation로 함수를 작성, 구조 내부에서 정보를 반환하는 일반적인 방법이다 (이 정말 전체 응답, 비트와 조각의 단지 수집하지 않습니다 특수 처리) 그래서

pub fn get_file_name<'a>(&'a self) -> &'a str { 
    match self._include_data { 
     Some(ref data) => &data.file_name, 
     _ => fail!("No FileData") 
    } 
} 

pub fn getIncludeData<'a>(&'a self) -> &'a FileData { 
    match self._include_data { 
     Some(ref data) => &*data, 
     _ => fail!("No FileData") 
    } 
} 

'a 마커 연결하는 named lifetime,되는 반환 값은 오브젝트 self 유효하다는 기간 동안 유효 기간; 이는 매달려있는 포인터가 불가능하다는 것을 의미합니다 (컴파일러 버그를 무시함).

match와 가지의 모음 :

  • match의 그래서 그것을 주변에 튀기고, 완성도 체크 (state 오히려 buf 이상을에 일치하는) 입력 - 안전합니다.

  • match에는 반환 값이 있으므로 "마술처럼"상태를 설정할 수 있습니다.

  • buf_action 기능은 특유의 (나는 그것이 일반적으로 더 많은 작업을 수행한다고 가정?) buf_action(foo)clear_buf(); foo로 작성되었는지, 또는, 적어도, 내부 폐쇄의 값을 반환해야하므로, 그것은 하나, 변경 될 수 있습니다 do buf_action { some; actions(); here; } : 그래서

    let buf_action = |f| { buf = ~""; f() } // note the lack of semicolon after f 
    
  • 마지막 인수는 함수가 어디 함수를 호출을위한 특별 설탕 있습니다. (언제 폐쇄, do f |a,b,c| { x; y; z }를 인수를 갖는다.)

또한
state = match state { 
     // Initial state 
     INITIAL if "include" == buf => do buf_action { 
      t.beginIncludeFile(); 
      EXPECT_COLON 
     }, 

     // Expecting either an encoding, or the start of the file name 
     EXPECT_COLON => if ":" == buf { 
      buf_action(|| EXPECT_ENCODING), 
     } else { 
      EXPECT_QUOTE 
     }, 

     // utf8 is the only encoding accepted at the moment 
     EXPECT_ENCODING => match buf { 
      ~"utf8" => do buf_action { t.set_utf(true); EXPECT_QUOTE }, 
      _ => { t.set_utf(false); EXPECT_ENCODING } // this is probably incorrect? 
     }, 

     // Looking for string start 
     EXPECT_QUOTE => if "\"" == buf { 
      buf_action(|| IN_FILENAME_STRING) 
     } else { 
      EXPECT_QUOTE // ignore other chars 
     }, 

     IN_FILENAME_STRING => if "\"" == buf { 
      buf_action(|| EXPECT_SEMI) 
     } else { 
      t.appendToFileName(c.unwrap()); 
      IN_FILENAME_STRING 
     } 

     // End of lex 
     EXPECT_SEMI => if ":" == buf {break} else {fail!("Expected semi")}, 

     _ => fail!("Unexpected character: %c", c) 
    }; 

, while trueloop가되어야한다 하지만 사실, 루프를 작성해야합니다

for input.iter().advance |c| { 
    buf.push_char(c); 
    state = match state { ... } 
} 

마이너 점 :

  • Option<~FileData>, let mut t = ~Token();Option<FileData>, let mut t = Token();을. 이러한 할당은 불필요합니다.

  • lowercase_with_underscores은 녹 이름 지정 규칙 인 것 같습니다.

  • Eq#[deriving(Eq)] enum LexState { ... }을 통해 컴파일러에서 자동으로 생성 할 수 있습니다.

  • 가능한 할당을 피하기 위해 관용적이다. (tutorialmanual에서 자세히 설명 함), 이는 slices (s.slice(byte_start, byte_end))input에를 사용하는 대신에 buf 캐릭터 추진 포함 할 것이다; 즉, 현재 토큰에 대해 start 색인을 기록하고이 색인을 현재 색인으로 설정하여 버퍼를 "지우는 것"; 그러나 구현하기에는 다소 까다로울 수 있습니다.

+0

늦게 답변을 드려 죄송합니다. 감사합니다. 몇 가지 후속 질문이 있습니다. 언급 한 for 루프는 반복기를 루프 내부로 "전진"시킬 수 있습니까? 슬라이스는 배열/문자열의 동적 "보기"입니까? – Naddiseo

+0

@ Naddiseo, 'for'를 사용할 때 iterator를 진행할 수 없습니다 (loop {match it.next() {...} ...}'trick); 예, 슬라이스는 다른 벡터/문자열로''보기 (view) '합니다. – huon

+0

이 답변은 Rust 프로그래머에게 유용한 정보를 제공합니다. 제안 (많은 Rust 지식에 기반하지 않음) :'state'와'buff' 튜플을 매칭하여 더 우아하게 작성할 수 있습니까? – Lii

관련 문제