Управление отступами при разработке небольшого языка, похожего на питон

Я разрабатываю небольшой язык, похожий на python, с использованием flex, byacc (для лексического анализа и синтаксического анализа) и C++, но у меня есть несколько вопросов относительно управления областью действия.

точно так же, как python, он использует пробелы (или вкладки) для отступов, и не только это, но я хочу реализовать разрыв индекса, например, если вы наберете «break 2» внутри цикла while, который находится внутри другого цикла while, он не только сломается от последний, но тоже из первого цикла (отсюда цифра 2 после перерыва) и так далее.

пример:

while 1
    while 1
        break 2
        'hello world'!! #will never reach this. "!!" outputs with a newline
    end
    'hello world again'!! #also will never reach this. again "!!" used for cout
end
#after break 2 it would jump right here

но поскольку у меня нет символа табуляции «анти», чтобы проверить, когда заканчивается область действия (например, C, я бы просто использовал символ «}»), мне было интересно, будет ли этот метод лучшим:

Я бы определил глобальную переменную, такую ​​​​как «int tabIndex» в моем файле yacc, к которой я буду обращаться в своем файле lex, используя extern. затем каждый раз, когда я нахожу символ табуляции в моем файле lex, я увеличиваю эту переменную на 1. при анализе моего файла yacc, если я нахожу ключевое слово «break», я уменьшаю его на количество, введенное после него из переменной tabIndex, и когда я достигаю и EOF после компиляции, и я получаю tabIndex! = 0, я бы вывел ошибку компиляции.

теперь проблема в том, как лучше всего увидеть, уменьшился ли отступ, должен ли я прочитать символы \b (backspace) из lex, а затем уменьшить переменную tabIndex (когда пользователь не использует break)?

другой способ добиться этого?

также еще один небольшой вопрос: я хочу, чтобы каждый исполняемый файл имел начальную точку в функции, называемой start (), должен ли я жестко закодировать это в моем файле yacc?

извините за длинный вопрос, любая помощь очень ценится. также, если кто-то может предоставить файл yacc для python, было бы неплохо в качестве руководства (пытался искать в Google и не повезло).

заранее спасибо.


person sap    schedule 30.04.2010    source источник


Ответы (2)


В настоящее время я реализую язык программирования, довольно похожий на этот (включая многоуровневый разрыв, как ни странно). Мое решение состояло в том, чтобы токенизатор выдавал токены отступа и отступа на основе отступа. Например:

while 1: # colons help :)
    print('foo')
    break 1

становится:

["while", "1", ":",
    indent,
    "print", "(", "'foo'", ")",
    "break", "1",
    dedent]

Однако это несколько усложняет обработку токенизатором '\n'. Кроме того, я написал токенизатор и парсер с нуля, поэтому я не уверен, что это возможно в lex и yacc.

Редактировать:

Пример полурабочего псевдокода:

level = 0
levels = []
for c = getc():
    if c=='\n':
        emit('\n')
        n = 0
        while (c=getc())==' ':
            n += 1
        if n > level:
            emit(indent)
            push(levels,n)
        while n < level:
            emit(dedent)
            level = pop(levels)
            if level < n:
                error tokenize
        # fall through
    emit(c) #lazy example
person David X    schedule 30.04.2010
comment
спасибо за ответ, так что в основном вся моя область должна иметь какой-то конец (в то время как конец, если конец, конец func () и т. д.), поэтому я могу вернуть токен дедента из токенизатора. я пытался избежать этого, по крайней мере, в функциях, но, думаю, мне придется пойти по этому пути. Также таким образом мой язык не будет поддерживать небольшие области (без while, ifs или fors), как поддерживает C. или я не правильно понимаю? - person sap; 30.04.2010
comment
У вас должно быть end ключевых слов, если у вас есть токенизатор, который отслеживает уровень отступа (см. пример). indent и dedent используются так же, как '{' и '}' для C. - person David X; 01.05.2010
comment
Грррр, скорее, вам не нужно иметь end, если вы это сделаете. - person David X; 02.05.2010

Очень интересное упражнение. Разве вы не можете использовать ключевое слово end, чтобы проверить, когда область действия заканчивается?

С другой стороны, я никогда не видел языка, который позволяет вам выйти из нескольких вложенных циклов одновременно. Возможно, на то есть веская причина...

person Dima    schedule 30.04.2010
comment
Наш злословящий друг goto позволит это в C/C++... :) - person Jeremy Friesner; 30.04.2010
comment
... и есть веские причины, почему goto считается вредным... :) - person Dima; 30.04.2010
comment
Ну-ну, @Jeremy и @Dima, не злите нас всех! :) - person Kevin Little; 01.05.2010
comment
Технически вы можете сделать это в JavaScript с продолжением. for (..) { (function(){ for (..) { for (..) { return; } } })(); } - person Christopher Done; 05.10.2010
comment
вам не нужно продолжение в javascript, вы можете просто использовать break [label]; - person Breton; 04.05.2011
comment
@Breton Я думаю, что Java также поддерживает синтаксис break label;. - person Solomon Ucko; 22.07.2019