перевод Nim 2014 в Nim 2019 - PullRequest
       48

перевод Nim 2014 в Nim 2019

4 голосов
/ 06 апреля 2019

Я наткнулся на этот код, определяющий DSL для html:

template html(matter: stmt) {.dirty.} =
  var result = ""
  matter

template tag(tagName) =
  template `tagName`(attrs: varargs[expr], matter: stmt = nil) {.dirty.} =
    # formatAttrs closes the tag and adds the inner tag if necessary
    result.add("<" & astToStr(tagName) & formatAttrs(attrs))
    matter
    result.add("</" & astToStr(tagName) & ">")

tag head; tag link; tag body
tag ul; tag li; tag title
tag p; tag h2

template css(file) {.dirty.} =
  link(relation = "stylesheet", href = file)

macro formatAttrs(args: seq[expr]): expr =
  result = newCall("&")
  var innerTexts = newSeq[string]()
  for arg in args:
    if arg.kind == nnkExprEqExpr:
      result.addParams($arg[0], "=", quoteString($arg[1]))
    else:
      innerTexts.add($arg)

  result.addParams ">"
  result.addParams innerTexts

И способ его использования таков:

type Article = object
  title, body: string

proc myPage(articles: seq[Article]): string =
  return html:
    head:
      title "govnokod.ru"
      css "moar-blink.css"

    body:
      ul:
        for article in articles:
          li:
            h2 article.title
            p article.body

Желаемый конечный результат после расширения шаблонаэтот код Nim:

proc myPage(articles: seq[Article]): string =
  var result = ""
  result.add("<" & "head" & ">")
  result.add("<" & "title" & ">")
  result.add("govnokod.ru")
  result.add("</" & "title" & ">")
  result.add("<link " & "relation" & "=" & "\"stylesheet\"" &
    "href" & "=" & "\"moar-blink.css\"" & ">")
  result.add("</" & "link" & ">")
  result.add("</" & "head" & ">")
  result.add("<" & "body" & ">")
  result.add("<" & "ul" & ">")
  for article in articles:
    result.add("<" & "li" & ">")
    result.add("<" & "h2" & ">")
    result.add article.title
    result.add("</" & "h2" & ">")
    result.add("<" & "p" & ">")
    result.add article.body
    result.add("</" & "p" & ">")
    result.add("</" & "li" & ">")
  result.add("</" & "ul" & ">")
  result.add("</" & "body" & ">")

Я нашел его на следующих слайдах: http://ibob.github.io/slides/nimrodbg/#/12

Также используется сопоставление с выражением: http://ibob.github.io/slides/nimrodbg/#/16

template optAdd1 {x = y; x.add(z)} (x, y, z: string) =
  x = y & z

template optAdd2 {x.add(y); x.add(z)} (x, y, z: string) =
  x.add(y & z)

, поэтомуокончательный код C в конечном итоге становится чем-то эквивалентным этому (без ненужных конкатенаций строк):

NimString myPage(const Sequence<Article>& articles) {
    NimString result =
        "<head><title>govnokod.ru</title>"
        "<link relation=\"\stylesheet\" href=\"moar-blink.css\></link></head>"
        "<body><ul>";

    for(const auto& article: articles) {
        result.add(Concat("<li><h2>",
                   article.title, "</h2><p>",
                   article.body, "</p></li>"));
    }
    result.add("</ul></body>");

    return result;
}

Это синтаксис Nim 5 лет назад - как он будет выглядеть в 2019 году?Это все еще возможно?Я знаком с основами Nim, но очень хочу использовать этот пример, если я обновлю его, когда показываю язык другим.

РЕДАКТИРОВАТЬ: Я получил егоработать благодаря @xbello!

import macros, strformat, strutils

template tag(tagName) =
  template `tagName`(body: untyped) =
    result.add("<" & astToStr(tagName) & ">")
    body
    result.add("</" & astToStr(tagName) & ">")

  template `tagName`(attrs: varargs[untyped]) =
    result.add("<" & astToStr(tagName) & " " & formatAttrs(attrs))
    result.add("</" & astToStr(tagName) & ">")

  template `tagName`(content: string) =
    result.add("<" & astToStr(tagName) & ">" & content)
    result.add("</" & astToStr(tagName) & ">")

macro formatAttrs(args: varargs[untyped]): untyped =
  result = newCall("&")
  var arg_list: seq[string] = @[]

  for arg in args:
    if arg.kind == nnkExprEqExpr:
      arg_list.add(&"{arg[0]}=\"{arg[1]}\"")

  arg_list.add(">")

  result.add(newLit(join(arg_list, " ")))

template html(matter: untyped) =
  result = "<html>"
  matter
  result.add("</html>")

template css(file) =
  link(relation = "stylesheet", href = file)

tag head; tag link; tag body
tag ul; tag li; tag title
tag p; tag h2

type Article = object
  title, body: string

proc myPage(articles: seq[Article]): string =
  html:
    head:
      title "govnokod.ru"
      css "moar-blink.css"

    body:
      ul:
        for article in articles:
          li:
            h2 article.title
            p article.body

let articles = @[Article(title: "omg", body: "omg_body"), Article(title: "wtf", body: "wtf_body")]

echo myPage(articles)

1 Ответ

2 голосов
/ 15 апреля 2019

Это единственный способ заставить меня работать.Шаблоны разделены только на тело (заголовок), attrs (ссылка) и контент (li):

template tag(tagName) =
  template `tagName`(body: untyped) =
    result.add("<" & astToStr(tagName) & ">")
    body
    result.add("</" & astToStr(tagName) & ">")

  template `tagName`(attrs: varargs[untyped]) =
    result.add("<" & astToStr(tagName) & " " & formatAttrs(attrs))
    result.add("</" & astToStr(tagName) & ">")

  template `tagName`(content: string) =
    result.add("<" & astToStr(tagName) & ">" & content)
    result.add("</" & astToStr(tagName) & ">")

Макрос записывается так (необходимо import strformat и strutils):

macro formatAttrs(args: varargs[untyped]): untyped =
  result = newCall("&")
  var arg_list: seq[string] = @[]

  for arg in args:
    if arg.kind == nnkExprEqExpr:
      arg_list.add(&"{arg[0]}=\"{arg[1]}\"")

   arg_list.add(">")

   result.add(newLit(join(arg_list, " ")))
...