могу ли я задать вопрос о проблеме, с которой я столкнулся в эти дни? Буду очень признателен, если вы, ребята, поможете мне решить эту проблему :)
Итак, у меня есть эта простая строка, которую я хочу разобрать, используя ключевое слово '@' (анализируйте это только в том случае, если '@' находится вне строки, которая находится внутри строки). Причина этого в том, что я пытаюсь научиться анализировать некоторые строки на основе определенных ключевых слов для анализа/разделения, потому что я пытаюсь реализовать свой собственный "простой язык программирования"...
Вот пример, который я сделал с помощью регулярного выражения: (пробелы после ключевого слова '@' не имеют значения)
# Ignore the 'println(' thing, it's basically a builtin print statement that I made, so
# you can only focus on the string itself :)
# (?!\B"[^"]*)@(?![^"]*"\B)
# As I looking up how to use this thing with regex, I found this one that basically
# split the strings into elements by '@' keyword, but not splitting it if '@' is found
# inside a string. Here's what I mean:
# '"[email protected]"' --- found '@' inside a string, so don't parse it
# '"[email protected]" @ x' --- found '@' outside a string, so after being parsed would be like this:
# ['"[email protected]", x']
print_args = re.split(r'(?!\B"[^"]*)@(?![^"]*"\B)', codes[x].split('println(')[-1].removesuffix(')\n' or ')'))
vars: list[str] = []
result_for_testing: list[str] = []
for arg in range(0, len(print_args)):
# I don't know if this works because it's split the string for each space, but
# if there are some spaces inside a string, it would be considered as the spaces
# that should've been split, but it should not be going to be split because
# because that space is inside a string that is inside a string, not outside a
# string that is inside a string.
# Example 1: '"Hello, World!" @ x @ y' => ['"Hello, World!"', x, y]
# Example 2: '"Hello, World! " @ x @ y' => ['"Hello, World! "', x, y]
# At this point, the parsing doesn't have to worry about unnecessary spaces inside a string, just like the example 2 is...
compare: list[str] = print_args[arg].split()
# This one is basically checking if '"'is not in a string that has been parsed (in this
# case is a word that doesn't have '"'). Else, append the whole thing for the rest of
# the comparison elements
# Here's the string: '"Value of a is: " @ a @ "String"' [for example 1]
# Example 1: ['"Value of a is: "', 'a', '"String"'] (This one is correct)
# Here's the string: '" Value of a is: " @ a @ " String"'
# Example 2: ['" Value of a is: " @ a @ " String"'] (This one is incorrect)
vars.append(compare[0]) if '"' not in compare[0] else vars.append(" ".join(compare[0:]))
for v in range(0, len(vars)):
# This thing is just doing it job, appending the same elements in 'vars'
# to the 'result_for_testing'
result_for_testing.append(vars[v])
print(result_for_testing)
После таких операций вывод, который я получаю для анализа основных вещей без лишних пробелов, выглядит следующим образом:
string_to_be_parsed: str = '"Value of a is: " @ a @ "String"'
Output > ['"Value of a is: "', 'a', '"String"'] # As what I'm expected to be...
Но как-то ломается, когда что-то вроде этого (с ненужными пробелами):
string_to_be_parsed: str = '" Value of a is: " @ a @ " String "'
Output > ['" Value of a is: " @ a @ " String "']
# Incorrect result and I'm hoping the result will be like this:
Expected Output > [" Value of a is: ", a, " String "]
# If there are spaces inside a string, it just has to be ignored, but I don't know how to do it
Хорошо, ребята, это проблемы, с которыми я столкнулся, и вывод:
Example: '"@ in a string inside a string" @ is_out_from_a_string'
The result should be: ['"@ in a string inside a string"', is_out_from_a_string]
Example: '" unnecessary spaces here too" @ x @ y @ z " this one too"'
The result should be: ['" unnecessary spaces here too"', x, y, z, '" this one too"']
Еще раз, я был бы очень признателен за вашу тяжелую работу, чтобы помочь мне найти решения для проблем, которые у меня возникли, и если есть что-то, что я сделал неправильно или неправильно, скажите мне, где и как я должен это исправить :)
Спасибо :)
^ Используйте regex101 для создания и тестирования ваших регулярных выражений, прежде чем внедрять их в свой токенизатор, это сэкономит вам много времени на отладку.
Хм, я не использую регулярные выражения все время, но сейчас я хочу придумать что-то новое, например, попытаться обойти регулярные выражения, и ваши предложения могут мне очень помочь, спасибо :)
Говоря о языках программирования, string.split() и вложенных циклов будет недостаточно. Языки программирования обычно разбивают это на два этапа: токенизатор или лексер и синтаксический анализатор. Токенизатор принимает входную строку (код на вашем языке) и возвращает список токенов, представляющих ключевые слова, идентификаторы и т. д. В вашем коде это каждый элемент результата.
В любом случае, вы, вероятно, захотите немного изменить структуру своего кода. Для токенизатора вот некоторый псевдокод в стиле python:
yourcode = input
tokens = []
cursor = 0
while cursor < len(yourcode):
yourcode = yourcode[cursor:-1] # remove previously scanned tokens
match token regex from list of regexes
if match == token:
add add token of matched type to tokens
cursor += len(matched string)
elif match == whitespace:
cursor += len(matched whitespace)
else throw error invalid token
Это использует курсор для продвижения по вашей входной строке и извлечения токенов в качестве прямого ответа на ваш вопрос. Для списка регулярных выражений просто используйте список пар, где каждая пара включает регулярное выражение и строку, описывающую тип токена.
Тем не менее, для первого проекта языка программирования создание токенизатора и синтаксического анализатора вручную, вероятно, не лучший способ, поскольку он может очень быстро стать чрезвычайно сложным, хотя это отличный опыт, когда вы освоитесь с основами. Я бы рассмотрел возможность использования генератора парсеров. Я использовал один из них под названием СЛИ с Python, а также PLY (предшественник SLY) с хорошими результатами. Генераторы синтаксических анализаторов берут grammar
, описание вашего языка в определенном формате, и выводят программу, которая может анализировать ваш язык, чтобы вы могли больше беспокоиться о функциональности самого языка, чем о том, как вы анализируете ввод текста/кода.
Также, возможно, стоит провести дополнительные исследования, прежде чем приступать к реализации. В частности, я бы порекомендовал прочитать об Abstract Syntax Trees
и алгоритмах синтаксического анализа, в частности recursive descent
, который вы бы написали, если бы создали парсер вручную, и LALR(1)
(просмотр слева направо), который генерирует SLY.
AST — это выходные данные синтаксического анализатора (то, что делает для вас генератор синтаксического анализатора) и используются для интерпретации или компиляции вашего языка. Они имеют фундаментальное значение для создания языков программирования, поэтому я бы начал с них. Это видео объясняет синтаксические деревья, а также есть много видео, посвященных разбору Python. Эта серия также охватывает использование SLY для создания простого языка на python.
РЕДАКТИРОВАТЬ: Что касается конкретного разбора знака @ перед строкой, я бы рекомендовал использовать один тип токена для знака @, а другой — для вашего строкового литерала. В вашем синтаксическом анализаторе вы можете проверить, является ли следующий токен строковым литералом, когда синтаксический анализатор встречает символ @. Это уменьшит сложность за счет разделения ваших регулярных выражений, а также позволит вам повторно использовать токены, если вы реализуете функциональность, которая также использует @ или строковые литералы в будущем.
Позвольте мне обратить ваше внимание на онлайн-парсер регулярных выражений по адресу regex101.com. Позвольте мне также указать вам на
\s
и\s+
, регулярные выражения для одного пробела или одного или нескольких пробелов.