-
Notifications
You must be signed in to change notification settings - Fork 0
concepts
OCaml -- статически-типизированный язык программирования. Мы не можем себе позволить добавлять методы к классу в рантайме, чтобы сделать удобную работу с моделями, например. Много где потребовалось бы дублирование сущностей, связанное с отсутствием reflection'а в OCaml. Да и многие вещи не слишком гламурно выглядят в чистом OCaml-синтаксисе. Поэтому в этом проекте плотно используется кодогенерация. (уж простите, не camlp4, так как не хочется нырять в это, и хочется видеть, что именно сгенерировалось.)
В зависимости от контекста бывает нужна разная кодогенерация, но синтаксис .mlt
-файлов подчиняется общему синтаксическому описанию.
-
просто текст -- доступен кодогенератору в чистом виде, имеет в себе line-директиву для правильной простановки места в
.mlt
, которое вызвало ошибку. С текстом кодогенератор особо ничего не может делать, разве что вывести его, проигнорировать его, или перегруппировать в нужном порядке для последующего вывода. -
директива без тела. Имеет синтаксис
% директива аргумент1 .. аргументN
Для неё генерируется вызов функции
директиваN аргумент1 .. аргументN
, где N -- количество аргументов, а сами аргументы переданы в виде строк. -
директива с телом. Имеет синтаксис
% begin директива аргумент1 .. аргументN <тело> % end
Для неё вызывается функция
директиваNb аргумент1 .. аргументN тело
, подобно "директиве без тела". Отличие в аргументетело
, который представляет собой строку, содержащую указанное тело, имеющую line-директиву в начале.
Кодогенератор обрабатывает список значений с типом
type mldir =
| Ml of string
| Dir of (context -> unit)
С помощью функции out
возможно вывести строку из Ml
, как и другой код. Функцию из конструктора Dir
можно только вызвать, передав ей контекст (он должен быть определён в кодогенераторе).
Эта функция либо сразу генерирует код через out
, либо модифицирует значение
с типом context
для последующей кодогенерации.
Кодогенератору на вход даётся список из mldir
, и типично скелет генератора может
выглядеть как-то так:
let ctx = <начальный контекст>
let generate mlt =
List.iter
(function
| Ml code -> out
| Dir f -> f ctx
)
mlt
В последних версиях есть возможность генерить сразу в несколько файлов, см.
Staging.stage_multi[_paths]
, при этом будут доступны функции out_<суффикс>
для вывода в соответствующие файлы.
(* кишки роутов *)
let resources path =
object
method index = path
method show = path ^ "/show"
method parse controller uri_segments =
match uri_segments with
| [] -> controller#index
| ["show"; x] -> controller#show x
| _ -> raise Failure
end
let path path =
object
method path = path
method parse action uri_segments =
match uri_segments with
| [x] when x = path -> action
| _ -> raise Failure
end
(* сами роуты *)
module Routes
(* resources :users, :as => users *) (* рельсовый аналог: *)
(* :as => users, resources :users *) (* так понятнее *)
let users = resources "users"
let ebat_gusei = path "ebat_gusei"
end
(* контроллеры *)
let users_controller = object
...
val index =
...
link_to Routes.users#show id
...
link_to Routes.ebat_gusei#path
...
val show = ...
end
let gooses_controller = object
...
val ebat = ...
end
(* связывание *)
bind Routes.users users_controller;
bind Routes.ebat_gusei gooses_controller#ebat