Как все началось
Дело было утром. Я захотел создать что-то на питоне, сложное, но посильное для меня. И тут я понял, что хочу создать язык программирования…
Как я создавал его...
Lexer
Куда же без него! Он требуется для «разделения» всего на токены. Если объяснить зачем он тогда представим: у нас есть код (CoffeScript).
a = true
if a
console.log('Hello, lexer')
И лексер превращает этот код в это(сокращенная запись):
[IDENTIFIER:"a"]
[ASSIGN:"="]
[BOOLEAN:"true"]
[NEWLINE:"n"]
[NEWLINE:"n"]
[KEYWORD:"if"]
[IDENTIFIER:"a"]
[NEWLINE:"n"]
[INDENT:" "]
[IDENTIFIER:"console"]
[DOT:"."]
[IDENTIFIER:"log"]
[ROUND_BRAKET_START:"("]
[STRING:"'Hello, lexer'"]
[ROUND_BRAKET_END:")"]
[NEWLINE:"n"]
[OUTDENT:""]
[EOF:"EOF"]
Но в моем случае я делаю все проще, т.к. это будет излишком трудности, а также язык программирования у меня простой. У меня все просто:
def lexer(code):
code = code.split(";") # Токенезация
code = code[0:-1] # Т.к. есть баг, что последний элемент пустой
return parse(code, number=0) # "Отсылаем" это все парсеру
И код (моего «ЯП»):
printf Test; exit;
Он превратит в читабельное (!):
["printf Test", "exit"]
Парсер
Самое сложное только начинается… Сделать токенезацию легко, а обработать это сложно. В теории мы должны проверять команду, потом ее аргументы. Кажется это легко, но нет! По началу все было примерно так:
number = 0
if code[number].startswith("printf"):
print(code[number][7:-0]
number += 1
Но ничего не работало, точнее не печатало текст, потом я попробовал так:
number = 0
if code[number].startswith("printf"):
print(code[number][7:-1]
number += 1
Но приходилось писать в конце любой символ. Потом я понял, что, если узнать длину строки и обрезать с 7-го символа по последний все должно работать.
number = 0
if code[number].startswith("printf"):
l = len(code[number])
print(code[number][7:l]
number += 1
Вроде работает, но если выводить текст боле два и более раз то в начале идет лишний пробел… Но даже с помощью переменной проверяющей печатался ли раньше текст, ничего не работало правильно. После нескольких десятков минут и кружки кофе я придумал, что и как.
if code[number][7] == " ":
l = len(code[number])
print(code[number][8:l]
else:
l = len(code[number])
print(code[number][7:l]
Но все равно ничего не работало :| и с таким лицом я пытался что-то сделать… Целый час. И спустя около полутора часа я сделал это!
l = len(code[number]) # Получаем длину
if code[number][6] == " ": # Если 6-ой символ это пробел
print(code[number][7:l]) # Печатаем все с 7-го символа
else: # Иначе
print(code[number][8:l]) #
Потом полчаса шаманства и… Все работает на ура!
P.S.
- Не весь код с комментариями, т.к. он ориентирован на продвинутых программистов.
- Код в спойлере, т.к. он длинный и в «главный» текст статьи не входит.
- Прошу не ругаться насчет кода, мне 11 лет.
- Это моя первая статья на Хабре и вообще.
- Код в начале был взят из одной статьи на Хабре.
def parse(code, number=0):
try:
# Print function #
if code[number].startswith("printf") or code[number].startswith(" printf"):
# Get len
l = len(code[number])
# If text starts with space
if code[number][6] == " ":
print(code[number][7:l])
# Else
else:
print(code[number][8:l])
number += 1
parse(code, number)
# Input function #
if code[number].startswith("input") or code[number].startswith(" input"):
# Get len
l = len(code[number])
# If text starts with space
if code[number][6] == " ":
input(code[number][7:l])
# Else
else:
input(code[number][8:l])
number += 1
parse(code, number)
# Exit function #
elif code[number].startswith("exit") or code[number].startswith(" exit"):
input("nPress "Enter" to exit.")
exit()
else:
cl = len(code[number])
command = code[number]
command = command[1:cl]
print("n", "=" * 10)
print("Error!")
print("Undefined command " + '"' + command + '"' + ".")
print("=" * 10)
input("Press "Enter" to exit...")
exit()
except IndexError:
input("n[!] Press "Enter" to exit.")
exit()
def lexer(code):
code = code.split(";")
code = code[0:-1]
return parse(code, number=0)
code = input()
lexer(code)
Автор: isisTance