Nim 중홍의 수수께끼 풀기.

언뜻 보기에, 짐의 원편은 틀림없이 너를 비명을 지르게 할 것이다. 그리고 너의 컴퓨터에서 가장 가까운 도랑으로 도망갈 것이다.다행히도, 매크로는 그리 복잡하지 않지만, 프로그래머가 코드로 코드를 작성하는 방식일 뿐이다. 그러면 우리는 프로그래밍을 통해 논리와 행동을 생성할 수 있다.본고는 매크로를 만드는 과정을 기록할 것이다.

바둑 코끼리 계산을 실현하다
Go에는 변수를 정의하고 키워드 없이 값을 설정하는 조작부호가 있습니다.다음은 Go의 모양새입니다.
a := 300
이것은 Nim에서 매우 간단하게 완성할 수 있지만, 우선 우리가 무엇을 사용해야 하는지 봅시다. dumpTree
import macros
dumpTree:
  var a = "Test"
컴파일러는 다음 내용 dumpTree 을 출력하기 때문에 이 코드에 필요한AST를 볼 수 있습니다.
StmtList
  VarSection
    IdentDefs
      Ident "a"
      Empty
      StrLit "Test"
따라서 이 과정을 자동화하려면 Varsection, ident,value가 필요합니다.두 가지 방법을 살펴봅시다.

메서드 1
import macros
macro `:=`(name, value: untyped): untyped = newVarStmt(name, value)
비록 우리가 .repr 프로그램을 사용하여 검사를 할 수 있지만, 우리는 확실히 해냈다.
import macros
macro `:=`(name, value: untyped): untyped = 
  result = newVarStmt(name, value)
  echo result.repr

a := "Test"
위의 코드를 실행하면 컴파일러가 필요로 하는 것을 볼 수 있습니다 var a = "Test"
방법#2
이 방법은 마찬가지로 간단하지만 아주 좋은 도구를 사용했다.quote do 코드를 작성할 수 있습니다. 이것은 AST를 생성할 것입니다. 따라서 아래와 같습니다.같은 검사도 위와 같이 진행할 수 있지만, 군더더기를 줄이기 위해서, 나는 그것들을 건너갈 것이다. 왜냐하면 군더기와 자신을 중복하는 것은 군더더기이기 때문이다.
import macros
macro `:=`(name, value: untyped): untyped = 
  quote do:
    var `name` = `value`
a := "Test"

치밀한 If 문장을 만들다
때때로 언어의 행동이나 기능을 바꾸고 싶을 때가 있다. 비록 Nim이 문법을 바꾸는 것을 허락하지 않지만, 너는 스스로 블록 논리를 만들 수 있다.그 중 하나는 맞춤형 if 성명이다.다음은 이상적인 실현이다
let a = "Yellow"
expandIf:
  a == "Hello": echo "Good bye"
  a == "Yellow":
    echo "Would be a lot cooler if you liked blue."
    echo "Yellow sucks"
  _: echo "You did not speak."
첫 번째 조건은 if이고 다음 조건은 elif이며 마지막_else 지점에 사용된다.이것은 우리의 forfun 실현에 중복된 키워드가 없다는 것을 의미합니다.
import macros
dumpTree:
  if a == "Hello": echo "Good bye."
  elif a == "Yellow":
    echo "Would be a lot cooler if you liked blue."
    echo "Yellow sucks."
  else: echo "You did not speak."
위의 아스트는 보기에 이렇다.
StmtList
  IfStmt
    ElifBranch
      Infix
        Ident "=="
        Ident "a"
        StrLit "Hello"
      StmtList
        Command
          Ident "echo"
          StrLit "Good bye."
    ElifBranch
      Infix
        Ident "=="
        Ident "a"
        StrLit "Yellow"
      StmtList
        Command
          Ident "echo"
          StrLit "Would be a lot cooler if you liked blue."
        Command
          Ident "echo"
          StrLit "Yellow sucks."
    Else
      StmtList
        Command
          Ident "echo"
          StrLit "You did not speak."
우리는 또한 우리의 최초expandIf 생각의 주체를 저장하고 우리가 샘플을 채취할 수 있는 결과ast 노드를 볼 수 있다.
StmtList
      Infix
        Ident "=="
        Ident "a"
        StrLit "Hello"
        StmtList
          Command
            Ident "echo"
            StrLit "Good bye"
      Infix
        Ident "=="
        Ident "a"
        StrLit "Yellow"
        StmtList
          Command
            Ident "echo"
            StrLit "Would be a lot cooler if you liked blue."
          Command
            Ident "echo"
            StrLit "Yellow sucks"
      Call
        Ident "_"
        StmtList
          Command
            Ident "echo"
            StrLit "You did not speak."
이 두 노드를 보면 expandIf 주체에서 ElseIfBranch 노드에 필요한 접미사를 얻을 수 있습니다.만약 우리가 StmtList를 모든 접미사와 분리할 수 있다면, 우리는 newIfStmt 매크로를 사용하여 elif 지점을 생성할 수 있다. 그 중에서 cond는 접미사이고 body는StmtList이다.
import macros
macro expandIf(statement: untyped): untyped=
  var
    branches: seq[(NimNode, NimNode)] #Condition, Body
    elseBody: NimNode #Else code
  for cond in statement:
    #Based off the dumpTree, we know this is the else body.
    let ifBody = cond.findChild(it.kind == nnkStmtList)
    if cond.kind == nnkInfix:
      cond.del 3 #Removes Stmtlist
      branches.add((cond, ifBody))
    elif cond.kind == nnkCall and $cond[0] == "_":
      #Based off the dumpTree, we know this is the else body.
      elseBody = ifBody

  result = newIfStmt(branches) #Generates if stmt
  result.add newNimNode(nnkElse).add(elseBody) #Appends else body
  echo result.repr



expandIf:
  11 == 13 : echo "Test"
  12 == 14: echo "Huh"
  _ : echo "duh"
위 코드에서 findChild 로 추출한 것을 볼 수 있습니다.cond.del 3로 StmtList를 삭제하고 마지막으로 if합니다.우리가 컴파일할 때, 컴파일러는 우리에게 아주 좋은 소식을 보낼 것이다. 감사 echo result.repr. 이것이 바로 우리가 원하는 완전한 구조의if/else 문장이다.
if 11 == 13:
  echo "Test"
elif 12 == 14:
  echo "Huh"
else:
  echo "duh"

비유형 및 유형 매크로
지금까지 우리는 비유형 매크로의 사용법만 연구했는데 이런 매크로는 어떠한 유형 정보도 없고 해석된 후에 우리에게 전달된 매크로이다.다른 종류의 매크로는 유형화 매크로입니다. 이 매크로들은 의미 검사를 거쳤는데 이것은 유형 정보를 가지고 코드가 유효해야 한다는 것을 의미합니다.유형화 매크로는 내성이 필요한 모든 것에 유용하다.

첫 번째 매크로 입력
나에게 부호를 하나 주어라. 나는 너에게 전 세계를 보여줄 것이다.Nim宏에서 기호나'syms'는 스파게티를 만드는 신기한 소스로 의미 검사를 거쳐 내부에 저장된 유형을 의미한다.이런 힘은 만능이다.첫 번째 유형 매크로는 변수를 선언 값으로 재설정하는 매크로입니다.
사용법은 다음과 같습니다.
var a = 100
a *= 3
assert a == 300
resetToDecl(a)
assert a == 100
우선, 우리는 typed 매개 변수가 필요하다는 것을 알고 있기 때문에 우리의 매크로 헤더는 macro resetToDecl(val: typed): untyped이다.typeduntyped와 매우 비슷하다. 왜냐하면 모든 코드를 받아들이기 때문이다. 그러나 이 예에서 우리는 그것이 의미 검사를 거친 것을 안다.따라서 변수만 처리할 수 있도록 다른 종류의 필터 nnkSym 를 모두 삭제해야 합니다.nifty error 도구를 사용하여 유용한 정보를 제공합니다. 이 메시지는 문자열과 선택할 수 있는 NimNode를 두 번째 인자로 받아들여 오류에 대한 줄 정보를 제공합니다.
macro resetToDecl*(val: typed): untyped = 
  if val.kind == nnkSym:
   # We will implement code here later
  else:
   error("This macro only works with variables." val)
기호는 모든 종류, 과정, 변수가 될 수 있기 때문에, 비var 기호를 필터해야 합니다.네가 알 수 있는 모든 이름.따라서, 우리는 기호의 symKind 를 사용하여 var을 제외한 모든 내용을 필터할 것입니다.
macro resetToDecl*(val: typed): untyped = 
  if val.kind == nnkSym and val.symKind == nskVar:
    ## We'll continue here in next block
  else:
    error("This macro only works on variables", val)
우리는 이미 해냈다. 우리는 변수밖에 없다. 그러나 지금의 문제는'우리가 어떻게 성명 성명을 얻는가?!'이다.내성 마법의 힘을 통해!짐의 macros 모듈은 symKind를 제외한 다른 여러 가지 내성 경로를 제시했는데 getImpl, getTypeImpl, getType 등도 있다.우리가 사용할 프로그램은 당연히 getImpl이다. 왜냐하면 우리는 기호의 실현을 원하기 때문이다(네, 기호가 있어야 합니다. 그렇지 않으면 배터리가 포함되지 않습니다).
우리는AST가 어떤 모양인지 빠르게 테스트할 수 있다echo val.getImpl. 우리가 본 것은 귀여운 성명이다.
IdentDefs
  Sym "a"
  Empty
  IntLit 100
만약 네가 자세히 관찰한다면 이것은 예측할 수 있는 상황이다. 우리가 해야 할 일은 valval.getImpl[^1]에 분배하는 것이다. 그러므로 우리가 이렇게 하자.
macro resetToDecl*(val: typed): untyped = 
  if val.kind = nnkSym and val.symKind == nskVar:
    result = nnkAsgn.newTree(val, val.getImpl[^1])
  else:
    error("This macro only works with variables", val) 
나는 이미 "헤이, 그렇습니까? 이것은 매우 간단합니다. 틀림없이 함정이 있을 것입니까?"네가 옳다. 간단하게 실행하면 var a: int use에 오류가 발생할 수 있다. Error: illformed AST: 라고 쓰여 있다.이 오류가 발생한 이유는 매크로가 기호의 마지막 항목이 nnkEmpty 인지 확인하지 않았기 때문에, 매크로는 빈 항목만 검사하고, 마지막 항목이 비어 있으면 default(typeof(val)) 하나를 보낼 것입니다.
다음과 같이 하십시오.
import std/macros

macro resetToDecl*(val: typed): untyped = 
   if val.kind == nnkSym and val.symKind == nskVar:
     let impl = val.getImpl
     result = nnkAsgn.newTree(val):
       if impl[^1].kind == nnkEmpty:
         newCall("default", newCall("typeOf", val))
       else:
         impl[^1]
   else:
     error("This macro only works on variables", val)
이제 테스트해 봅시다. 우리는 이미 잘 했습니다.
var a = 100
a *= 3
assert a == 300
resetToDecl(a)
assert a == 100


var b: int
b = 300
assert b == 300
b *= 3
assert b == 900
b.resetToDecl
assert b == 0
예를 들어 보려면 다음과 같은 복잡한 패키지가 많습니다.
Slicerator
Oopsie
Constructor
Nimscripter
Nettyrpc
Kashae

좋은 웹페이지 즐겨찾기