Многие языки программирования позиционируют себя как почти естественные. Ruby не отстает и позиционирует себя как "natural to read and easy to write". Однако, первый же гайд по Rails (да, Ruby это всё еще Rails) предложит вам что–то такое:
class UserController < ApplicationController
endТо есть это вот абсолютно естественный (natural) английский текст, правда? Кажется, нас обманули. Было бы здорово писать код на полностью естественном языке, например таком:
assign variable a value 1
assign variable b value 2
sum a with bДавайте попробуем запустить эту программу!
Запускаем первую программу
Cкопируем код выше в файлик natural.rb и запустим его (irb ./natural.rb). Разумеется, программма должна упасть, однако ошибка, которую мы увидим, может показаться неожиданной: <main>: undefined method value for main:Object (NoMethodError). Почему интерпретатор не ругается на другие незнакомые ему слова, такие как assign или b?
Дело в том, что код выше является абсолютно корректным с точки зрения парсера. К примеру, строку sum a with b можно прочитать так:
происходит вызов метода
bлибо чтение значения переменнойb;результат передается методу
withв качестве аргумента;результат вызова
withпередается методуaв качестве аргумента;результат вызова
aпередается методуsumв качестве аргумента.
Когда интерпретатор начинает выполнять этот код (справа налево), он падает с ошибкой: метод value нигде не определен. Давайте убедимся, что порядок вызовов и правда такой:
def assign(*)
puts "assign"
end
def variable(*)
puts "variable"
end
def a
puts "a"
end
assign variable a
# => a
# => variable
# => assignБлагодаря * (который принимает любые аргументы) наш код больше не падает, однако он и не делает ничего полезного. Давайте это исправим с помощью максимально топорной реализации:
@variables = {}
@unknown_token = nil
@current_value = nil
@with = nil
def assign(*); end
def variable(*)
@variables[@unknown_token] = @current_value
end
def value(value)
@current_value = value
end
def method_missing(m, *args, &block)
@unknown_token = m
end
def sum(*)
result = @variables[@unknown_token] + @with
print "#{result}\n"
end
def with(*)
@with = @variables[@unknown_token]
end
# Program
assign variable a value 1
assign variable b value 2
sum a with bЗапуск этой программы приведет к выводу значения 3 в консоль.
Вам могло показаться, что язык, который мы пытаемся интерпретировать не очень–то натуральный. Я полностью разделяю ваше возмущение и хотел бы сделать что–то вроде
assign 1 to variable a. Проблема в том, что такой код не является валидным с точки зрения парсера Ruby, который ожидает запятую либо конец строки после числа
