Created
November 17, 2012 02:49
-
-
Save inaz2/4092867 to your computer and use it in GitHub Desktop.
2012-11-16 g6k.rb #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys | |
class PolandParser: | |
""" | |
EXPR := '(' EXPR ')' | OP TERM TERM | |
TERM := EXPR | NUM | |
OP := '+' | '-' | '*' | '/' | |
NUM := (any integer) | |
""" | |
def __init__(self, line): | |
""" | |
>>> parser = PolandParser('+ 1 (+ + 1 2 (* 3 4))') | |
>>> parser.tokens | |
['+', '1', '(', '+', '+', '1', '2', '(', '*', '3', '4', ')', ')'] | |
""" | |
line = line.replace('(', ' ( ').replace(')', ' ) ') | |
self.tokens = line.split() | |
def run(self): | |
""" | |
>>> parser = PolandParser('+ 1 (+ + 1 2 (* 3 4))') | |
>>> parser.run() | |
16 | |
>>> parser = PolandParser('(+ (+ 1 1) (+ 1 1))') | |
>>> parser.run() | |
4 | |
>>> parser = PolandParser('(+ (+ (- 99 11) (+ 11 11)) -111)') | |
>>> parser.run() | |
-1 | |
>>> parser = PolandParser('(+ (+ 1 1) (+ 1 1)') | |
>>> parser.run() | |
Traceback (most recent call last): | |
Exception: tokens are insufficient | |
""" | |
try: | |
return self.parse_expr() | |
except IndexError: | |
raise Exception('tokens are insufficient') | |
def parse_expr(self): | |
"""" | |
>>> parser = PolandParser('(+ 7 5)') | |
>>> parser.parse_expr() | |
12 | |
>>> parser = PolandParser('+ 7 5') | |
>>> parser.parse_expr() | |
12 | |
>>> parser = PolandParser('(+ 7 5 ]') | |
>>> parser.parse_expr() | |
Traceback (most recent call last): | |
Exception: unclosed parenthesis: ] | |
>>> parser = PolandParser('7 5') | |
>>> parser.parse_expr() | |
Traceback (most recent call last): | |
Exception: no operator: 7 | |
""" | |
token = self.tokens.pop(0) | |
if token == '(': | |
value = self.parse_expr() | |
token2 = self.tokens.pop(0) | |
if token2 == ')': | |
return value | |
else: | |
raise Exception("unclosed parenthesis: %s" % token2) | |
elif token == '+': | |
value1 = self.parse_term() | |
value2 = self.parse_term() | |
return value1 + value2 | |
elif token == '-': | |
value1 = self.parse_term() | |
value2 = self.parse_term() | |
return value1 - value2 | |
elif token == '*': | |
value1 = self.parse_term() | |
value2 = self.parse_term() | |
return value1 * value2 | |
elif token == '/': | |
value1 = self.parse_term() | |
value2 = self.parse_term() | |
return value1 / value2 | |
else: | |
raise Exception("no operator: %s" % token) | |
def parse_term(self): | |
"""" | |
>>> parser = PolandParser('7') | |
>>> parser.parse_term() | |
7 | |
>>> parser = PolandParser('(+ 7 5)') | |
>>> parser.parse_term() | |
12 | |
""" | |
try: | |
num = int(self.tokens[0]) | |
self.tokens.pop(0) | |
return num | |
except ValueError: | |
return self.parse_expr() | |
if __name__ == '__main__': | |
line = sys.stdin.readline().strip() | |
parser = PolandParser(line) | |
print parser.run() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ python -m doctest -v 01-poland.py | |
Trying: | |
parser = PolandParser('+ 1 (+ + 1 2 (* 3 4))') | |
Expecting nothing | |
ok | |
Trying: | |
parser.tokens | |
Expecting: | |
['+', '1', '(', '+', '+', '1', '2', '(', '*', '3', '4', ')', ')'] | |
ok | |
Trying: | |
parser = PolandParser('(+ 7 5)') | |
Expecting nothing | |
ok | |
Trying: | |
parser.parse_expr() | |
Expecting: | |
12 | |
ok | |
Trying: | |
parser = PolandParser('+ 7 5') | |
Expecting nothing | |
ok | |
Trying: | |
parser.parse_expr() | |
Expecting: | |
12 | |
ok | |
Trying: | |
parser = PolandParser('(+ 7 5 ]') | |
Expecting nothing | |
ok | |
Trying: | |
parser.parse_expr() | |
Expecting: | |
Traceback (most recent call last): | |
Exception: unclosed parenthesis: ] | |
ok | |
Trying: | |
parser = PolandParser('7 5') | |
Expecting nothing | |
ok | |
Trying: | |
parser.parse_expr() | |
Expecting: | |
Traceback (most recent call last): | |
Exception: no operator: 7 | |
ok | |
Trying: | |
parser = PolandParser('7') | |
Expecting nothing | |
ok | |
Trying: | |
parser.parse_term() | |
Expecting: | |
7 | |
ok | |
Trying: | |
parser = PolandParser('(+ 7 5)') | |
Expecting nothing | |
ok | |
Trying: | |
parser.parse_term() | |
Expecting: | |
12 | |
ok | |
Trying: | |
parser = PolandParser('+ 1 (+ + 1 2 (* 3 4))') | |
Expecting nothing | |
ok | |
Trying: | |
parser.run() | |
Expecting: | |
16 | |
ok | |
Trying: | |
parser = PolandParser('(+ (+ 1 1) (+ 1 1))') | |
Expecting nothing | |
ok | |
Trying: | |
parser.run() | |
Expecting: | |
4 | |
ok | |
Trying: | |
parser = PolandParser('(+ (+ (- 99 11) (+ 11 11)) -111)') | |
Expecting nothing | |
ok | |
Trying: | |
parser.run() | |
Expecting: | |
-1 | |
ok | |
Trying: | |
parser = PolandParser('(+ (+ 1 1) (+ 1 1)') | |
Expecting nothing | |
ok | |
Trying: | |
parser.run() | |
Expecting: | |
Traceback (most recent call last): | |
Exception: tokens are insufficient | |
ok | |
2 items had no tests: | |
01-poland | |
01-poland.PolandParser | |
4 items passed all tests: | |
2 tests in 01-poland.PolandParser.__init__ | |
8 tests in 01-poland.PolandParser.parse_expr | |
4 tests in 01-poland.PolandParser.parse_term | |
8 tests in 01-poland.PolandParser.run | |
22 tests in 6 items. | |
22 passed and 0 failed. | |
Test passed. |
理解しました!
丁寧にありがとうございます。
直接でも間接でも使えるようにする配慮なのですね。たまに起動スクリプトがPythonが使われてて、覗くと↑みたいなのが書かれててなんだろうと思っていたのでした。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
if name == '__main__':
がなくても、コードは動きます。この1行は、このファイルが他のスクリプトから import されたときのためにつけています。
この1行がない場合、他のスクリプトから import されたときにも PolandParser が実際に走ってしまいます。
たいてい import される場合は単にクラスを定義するだけにとどめておきたいので、
この1行をつけることで「直接コードを実行したときのみ、標準入力やコマンドライン引数をもとに走らせる」ことができます。
こうしておけば、他のスクリプトから次のようにPolandParserクラスが再利用できるので便利です。
(ただしこの場合ファイル名を数字から始めることはできないので、poland.pyとして保存したものとします)