стандартным образом для чтения из трубы (cat ... | python myprog.py
) является
import sys
for line in sys.stdin:
print ">", line
line
здесь будет включать окончательный '\n'
Если вместо этого вы хотите, чтобы иметь аргументы в командной строке (python myprog.py 3 4 1 + -
), можно использовать sys.argv[1:]
(sys.argv[0]
содержит myprog.py
).
Чтобы получить последовательный лексический на входе вы должны сначала проверить sys.argv
, а затем разделить sys.stdin
:
def lex_input():
"Returns a list of tokens."
tokens = []
if len(sys.argv) > 1:
tokens = sys.argv[1:]
else:
for line in sys.stdin:
tokens += line.split()
return tokens
тогда вам просто нужно изменить infixPostfix()
функцию, чтобы использовать этот маркер-массив (а не делать так синтаксический анализ и оценка в той же функции).
пс: более лаконичный способ написания отдельных положений будет:
elif token == '+':
push(pop() + pop())
, но это зависит от того, что вы пытаетесь сделать ..
Update: full'ish решение
Update2: отладочные отчетности визуализируют стек (удален класс стека в пользу обычного списка для краткости)
import sys
STACK = []
push = STACK.append
pop = STACK.pop
OPERATIONS = {
'+': lambda b, a: a + b,
'-': lambda b, a: a - b,
'*': lambda b, a: b * a,
'/': lambda b, a: b/a,
}
def infixtoPostfix(tokens):
print '%-15s %5s %-15s' % ('STACK before', 'token', 'STACK after')
print '-'*15, '-'*5, '-'*15
for token in tokens:
print '%15s %5r' % (STACK, token),
if token not in OPERATIONS:
push(int(token))
else:
push(OPERATIONS[token](pop(), pop()))
print '%15s' % STACK
def lex_input():
"Returns a list of tokens."
tokens = []
if len(sys.argv) > 1:
tokens = sys.argv[1:]
else:
for line in sys.stdin:
tokens += line.split()
return tokens
if __name__ == "__main__":
infixtoPostfix(lex_input())
# well formed programs should leave a single value on the STACK
print "\nResult is:", STACK[0]
испытания:
(dev) go|c:\srv> python rpn.py 3 4 1 + -
STACK before token STACK after
--------------- ----- ---------------
[] '3' [3]
[3] '4' [3, 4]
[3, 4] '1' [3, 4, 1]
[3, 4, 1] '+' [3, 5]
[3, 5] '-' [-2]
Result is: -2
(cat rpn.txt | python rpn.py
будет выводить то же самое, если rpn.txt
содержит 3 4 1 + -
).
Если попробовать программу RPN с синтаксической ошибкой, то программа сгенерирует исключение, например:
(dev) go|c:\srv> python rpn.py 3 4 + -
STACK before token STACK after
--------------- ----- ---------------
[] '3' [3]
[3] '4' [3, 4]
[3, 4] '+' [7]
[7] '-'
Traceback (most recent call last):
File "rpn.py", line 60, in <module>
infixtoPostfix(lex_input())
File "rpn.py", line 45, in infixtoPostfix
push(OPERATIONS[token](pop(), pop()))
File "rpn.py", line 26, in pop
return STACK.pop()
IndexError: pop from empty list
В реальной компилятор, который будет плохо, так как вы не хотите, чтобы конечный пользователь чтобы узнать подробности о вашей реализации. Вместо этого вы хотите дать им сообщение об ошибке диагностики с точным местом, где ваша программа его обнаружила.
В этом случае это не так сложно. Я опустил отладочные для печати стек:
def infixtoPostfix(tokens):
# make a copy of the input, for use in error handling
input_tokens = tokens[:]
try:
for i, token in enumerate(tokens):
if token not in OPERATIONS:
push(int(token))
else:
push(OPERATIONS[token](pop(), pop()))
except IndexError:
print 'Detected Syntax Error at token no.:', i + 1 # people count from 1..
print ' '.join(input_tokens)
print '%s%s' % ('-' * (1 + len(' '.join(input_tokens[:i]))), '^')
push('SYNTAX ERROR') # the top of the stack contains the result of the current operation..
небольшое изменение результатов печати необходимо, печатая последний элемент в списке (STACK[-1]
), который в верхней части стека вместо того, чтобы полагаться на список/стек только имея один элемент в конце:
if __name__ == "__main__":
infixtoPostfix(lex_input())
# well formed programs should leave a single value on the STACK
print "\nResult is:", STACK[-1]
если мы кормим эту версию нашей программы с синтаксической ошибки:
(dev) go|c:\srv> python rpn.py 34 4 + -
Detected Syntax Error at token no.: 4
34 4 + -
-------^
Result is: SYNTAX ERROR
мы получаем правильный е rror, с небольшим заостренным «графиком», указывающим, где была обнаружена ошибка.
Мы могли бы пойти дальше, так как мы знаем, что все наши операции принимают два элемента в стеке, и дать еще более подробное сообщение об ошибке, например:
Syntax Error at token "-": Stack underflow
The "-" operation requires two stack arguments and the stack
contained only one:
Stack token
---------- -----
[37] '-'
Я оставлю реализацию, что, как упражнение.
Как вы можете видеть, даже в этом простом примере есть больше кода обработки ошибок, чем оценочный код, и это не удивительно при написании простых компиляторов.
Я не вносил изменений, которые вы предложили, когда я впервые протестировал cat test.txt | python something.py, и я получил сообщение о том, что pop не определен. – syavatkar
Traceback (самый последний вызов последнего): -bash: ошибка синтаксиса около неожиданной лексемы 'наиболее ' Файл "something.py", строка 75, в -bash: ошибка синтаксиса около неожиданной лексемы' новой строки' S $ infixtoPostfix() > Файл "something.py", строка 43, в infixtoPostfix -bash: ошибка синтаксиса около неожиданной лексемы 'File ' FirstNum = initStack.pop() -bash: ошибка синтаксиса около неожиданной лексемы' (' File «something.py», строка 17, в pop –
syavatkar
Пс был, может быть, слишком абстрактным .. Это подразумевалось как: «если вы определяете push и pop как простые функции (поскольку вы работаете только с одним стеком), тогда вы можете упростить оценку ».Это было просто в сторону вашего вопроса о токенизации. – thebjorn