Skip to content
Dmitry Grebeniuk edited this page Dec 4, 2013 · 3 revisions

Общие концепции

OCaml code generation templates (.mlt)

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
Clone this wiki locally