3

私は自分の C++ プロジェクトの 1 つである単純な DSL を、錆を学習する演習として錆に変換していますが、ネストされた構造と所有権に問題があります。次のようなものを変換するのに苦労しています:

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;
};

このために私が書くさびは次のとおりです。

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;
}

この種のコードの偶像的なさびた方法は何ですか?

4

1 に答える 1

5

Rust は C++ とは十分に異なっているため、行ごとの直線的な翻訳では非慣用的なコードが得られます。これは実際には完全な答えではなく、ほんの一部のコレクションです。


構造体の内部から情報を返す場合fn foo<'a>(&'a self) -> &'a SomeInformation、通常の方法で関数を記述します ( と は特別str[]扱われます):

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マーカーは名前付きのライフタイムであり、戻り値が有効である期間とselfオブジェクトが有効である期間を関連付けます。これは、ダングリング ポインターが不可能であることを意味します (コンパイラのバグを無視します)。

のコレクションmatch:

  • matchstates の完全性がチェックされるため、反転 (ではなくon のマッチングbuf) の方がタイプセーフです。

  • match戻り値があるため、「魔法のように」状態を設定できます。

  • buf_action関数は独特です(通常はもっと多くのことを行うと思いますか?)、 のように記述されるように変更するか、buf_action(foo)少なくともclear_buf(); foo内部クロージャーの値を返す必要があるため、

    let buf_action = |f| { buf = ~""; f() } // note the lack of semicolon after f
    
  • 最後の引数が関数である関数を呼び出すための特別なシュガーがあります: do buf_action { some; actions(); here; }. (クロージャに引数がある場合、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 trueする必要がありloopます。しかし実際には、ループは次のように書かれるべきです:

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_underscoresRustの命名規則のようです。

  • あなたが持っているEqimpl は、 を介してコンパイラによって自動的に作成されます#[deriving(Eq)] enum LexState { ... }(チュートリアルマニュアルで詳しく説明されています。)

  • 可能な限り割り当てを避けるのは慣用的であり、これには、文字を にプッシュするのではなく、にスライス ( s.slice(byte_start, byte_end))を使用することが含まれます。つまり、現在のトークンのインデックスを記録し、このインデックスを現在のインデックスに設定してバッファを「クリア」します。ただし、これを実装するのは少し難しいかもしれません。inputbufstart

于 2013-07-15T07:19:12.537 に答える