上次看书看到了第二章,书中实现了一个简单的中缀到后缀的翻译器。这个例程在书中图2-27,本身是 Java 代码,如果用 C 或 C++ 来实现,那是相当简单的,几乎就是抄过来,正好我也在学习 Rust 语言,于是想到了使用 Rust 来实现书中的例程。

这个翻译器只能处理一位数字间不带括号的加减法,其产生式为:

expr -> expr + term
expr -> expr - term
term -> [0-9] # 直接使用正则来节省篇幅,就不展开了

代码在 gist 上保存了,也搬运在这里:

use std::io::{Read, Error, ErrorKind};
use std::fmt;

enum ParseError {
    SyntaxError,
    Parse(Error)
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::SyntaxError => write!(f, "syntax error"),
            ParseError::Parse(ref e) => e.fmt(f),
        }
    }
}

struct Parser {
    lookahead: u8,
}

impl Parser {
    pub fn new() -> Self {
        let ch = read_char().expect("Cannot read charactor from input!");
        Self {
            lookahead: ch,
        }
    }

    pub fn expr(&mut self) -> Result<(), ParseError> {
        if let Err(e) = self.term() {
            return Err(e);
        }
        loop {
            if self.lookahead == b'+' {
                if let Err(e) = self.pmatch(b'+') {
                    return Err(e);
                }
                if let Err(e) = self.term() {
                    return Err(e);
                }
                print!("+");
            } else if self.lookahead == b'-' {
                if let Err(e) = self.pmatch(b'-') {
                    return Err(e);
                }
                if let Err(e) = self.term() {
                    return Err(e);
                }
                print!("-");
            } else {
                return Ok(());
            }
        }
    }

    pub fn term(&mut self) -> Result<(), ParseError> {
        if self.lookahead.is_ascii_digit() {
            print!("{}", char::from(self.lookahead));
            self.pmatch(self.lookahead)
        } else {
            Err(ParseError::SyntaxError)
        }
    }

    pub fn pmatch(&mut self, t: u8) -> Result<(), ParseError> {
        if self.lookahead == t {
            let input = read_char();
            match input {
                Ok(ch) => {
                    self.lookahead = ch;
                    Ok(())
                },
                Err(e) => Err(ParseError::Parse(e)),
            }
        } else {
            Err(ParseError::SyntaxError)
        }
    }
}

fn read_char() -> Result<u8, Error> {
    std::io::stdin().bytes().next()
        .unwrap_or(Err(Error::new(ErrorKind::Other, "Cannot read from stdin!")))
}

fn main() {
    let mut parse = Parser::new();
    match parse.expr() {
        Ok(_) => println!(""),
        Err(e) => println!("{}", e),
    };
}

顺手也写了一个 Python 版本

import readchar

class Parser:
    def __init__(self):
        self.lookahead = readchar.readchar()

    def expr(self):
        self.term()
        while True:
            if self.lookahead == b'+':
                self.match(b'+')
                self.term()
                print('+', end='')
            elif self.lookahead == b'-':
                self.match(b'-')
                self.term()
                print('-', end='')
            else:
                return

    def term(self):
        if self.lookahead.isdigit():
            print(self.lookahead.decode("utf-8"), end='')
            self.match(self.lookahead)
        else:
            raise Exception("syntax error")

    def match(self, t):
        if self.lookahead == t:
            self.lookahead = readchar.readchar()
        else:
            raise Exception("syntax error")

if __name__ == '__main__':
    parse = Parser()
    parse.expr()
    print("")