How do I parse a string at compile time in Nimrod?

你。 提交于 2019-12-06 03:11:01

问题


Going through the second part of Nimrod's tutorial I've reached the part were macros are explained. The documentation says they run at compile time, so I thought I could do some parsing of strings to create myself a domain specific language. However, there are no examples of how to do this, the debug macro example doesn't display how one deals with a string parameter.

I want to convert code like:

instantiate("""
    height,f,132.4
    weight,f,75.0
    age,i,25
    """)

…into something which by hand I would write like:

var height: float = 132.4
var weight: float = 75.0
var age: int = 25

Obviously this example is not very useful, but I want to look at something simple (multiline/comma splitting, then transformation) which could help me implement something more complex.

My issue here is how does the macro obtain the input string, parse it (at compile time!), and what kind of code can run at compile time (is it just a subset of a languaje? can I use macros/code from other imported modules)?

EDIT: Based on the answer here's a possible code solution to the question:

import macros, strutils

# Helper proc, macro inline lambdas don't seem to compile.
proc cleaner(x: var string) = x = x.strip()

macro declare(s: string): stmt =
  # First split all the input into separate lines.
  var
    rawLines = split(s.strVal, {char(0x0A), char(0x0D)})
    buf = ""

  for rawLine in rawLines:
    # Split the input line into three columns, stripped, and parse.
    var chunks = split(rawLine, ',')
    map(chunks, cleaner)

    if chunks.len != 3:
      error("Declare macro syntax is 3 comma separated values:\n" &
        "Got: '" & rawLine & "'")

    # Add the statement, preppending a block if the buffer is empty.
    if buf.len < 1: buf = "var\n"
    buf &= "  " & chunks[0] & ": "

    # Parse the input type, which is an abbreviation.
    case chunks[1]
    of "i": buf &= "int = "
    of "f": buf &= "float = "
    else: error("Unexpected type '" & chunks[1] & "'")
    buf &= chunks[2] & "\n"

  # Finally, check if we did add any variable!
  if buf.len > 0:
    result = parseStmt(buf)
  else:
    error("Didn't find any input values!")

declare("""
x, i, 314
y, f, 3.14
""")
echo x
echo y

回答1:


Macros can, by and large, utilize all pure Nimrod code that a procedure in the same place could see, too. E.g., you can import strutils or peg to parse your string, then construct output from that. Example:

import macros, strutils

macro declare(s: string): stmt =
  var parts = split(s.strVal, {' ', ','})
  if len(parts) != 3:
    error("declare macro requires three parts")
  result = parseStmt("var $1: $2 = $3" % parts)

declare("x, int, 314")
echo x

"Calling" a macro will basically evaluate it at compile time as though it were a procedure (with the caveat that the macro arguments will actually be ASTs, hence the need to use s.strVal above instead of s), then insert the AST that it returns at the position of the macro call.

The macro code is evaluated by the compiler's internal virtual machine.



来源:https://stackoverflow.com/questions/19955348/how-do-i-parse-a-string-at-compile-time-in-nimrod

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!