Я подозреваю, что ваш подход к синтаксическому анализу оператора if не является хорошим способом для этого. Вы также обнаружите, что не можете вкладывать свои утверждения IF или ставить их все в одну строку. Вот как я бы это сделал.
а) Преобразуйте ваш язык в форму BNF. Например, для оценки if это может быть
ifstmnt ::= IF <condiftion> THEN <trueblock> [ ELSE <elseblock> ]
statment ::= ifstmnt | assignstment | whilestmnt | forstmnt
и т.д.
б) Выясните, что вы ждете в каждой точке. После IF вы читаете условие, пока не увидите THEN.
в) Напишите подпрограмму getNextToken, которая читает символы из вашего источника, пока у него не будет полный токен. Токен - это узнаваемая единица - ЕСЛИ, ТО, А, число, символ. Каждый раз, когда он вызывается, он возвращает токен в буфер. Также полезно иметь тип и возвращаемое значение - это сэкономит вам преобразование чисел во многих местах. Настольный подход очень быстрый и гибкий.
d) Затем напишите много маленьких подпрограмм, чтобы распознать каждый тип утверждения. Один для IF, один для условия, один для блока, один для оператора, один для выражения и т. Д. Они будут вызывать друг друга и возвращаться, когда они распознают оператор. Например, распознаватель выражений условия будет читать логическое выражение и принимать все имена, AND, OR и т. Д., Пока не заглянет в будущее и не увидит THEN. Он не может обработать THEN, поэтому он существует, и программа повторного распознавания IF обнаруживает, что токен THEN, читает следующее и вызывает распознаватель для блока (который может ожидать BEGIN, много статистики и затем END).
e) Каждая подпрограмма собирает необходимые данные - условие, истинный блок, ложный блок и обрабатывает их по мере необходимости. Очень распространенным методом является создание древовидного представления программы в памяти. Определенные программистом имена собираются в словаре по мере их определения и проверяются по мере их использования.
f) Затем реальный компилятор попытается изменить расположение дерева, чтобы сделать его более эффективным - но я полагаю, что это будущее развитие
ж) Последнее действие - пройтись по дереву, оценивая его каким-то образом. Если вы пишете интерпретатор, вы можете вычислять значения и сохранять их по ходу дела - сохранять значения в доктрине. Если вы пишете настоящий компилятор, вам нужно сгенерировать подходящий вывод для компоновщика. Это главная задача.
Проверьте YACC и LEX. Это инструменты, предназначенные для выполнения части работы, и сэкономят ваше время. Термины, которые помогут вашему исследованию: «лексический анализ», «анализатор» и тому подобное.
И удачи. Ваш проект нетривиален!
См. Также http://nedbatchelder.com/text/python-parsers.html