mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
made test green again
This commit is contained in:
@@ -2,344 +2,344 @@ discard """
|
||||
output: "Success"
|
||||
"""
|
||||
|
||||
# Ref:
|
||||
# http://nim-lang.org/macros.html
|
||||
# http://nim-lang.org/parseutils.html
|
||||
|
||||
|
||||
# Imports
|
||||
import tables, parseutils, macros, strutils
|
||||
import annotate
|
||||
export annotate
|
||||
|
||||
|
||||
# Fields
|
||||
const identChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
|
||||
|
||||
|
||||
# Procedure Declarations
|
||||
proc parse_template(node: NimNode, value: string) {.compiletime.}
|
||||
|
||||
|
||||
# Procedure Definitions
|
||||
proc substring(value: string, index: int, length = -1): string {.compiletime.} =
|
||||
## Returns a string at most `length` characters long, starting at `index`.
|
||||
return if length < 0: value.substr(index)
|
||||
elif length == 0: ""
|
||||
else: value.substr(index, index + length-1)
|
||||
|
||||
|
||||
proc parse_thru_eol(value: string, index: int): int {.compiletime.} =
|
||||
## Reads until and past the end of the current line, unless
|
||||
## a non-whitespace character is encountered first
|
||||
var remainder: string
|
||||
var read = value.parseUntil(remainder, {0x0A.char}, index)
|
||||
if remainder.skipWhitespace() == read:
|
||||
return read + 1
|
||||
|
||||
|
||||
proc trim_after_eol(value: var string) {.compiletime.} =
|
||||
## Trims any whitespace at end after \n
|
||||
var toTrim = 0
|
||||
for i in countdown(value.len-1, 0):
|
||||
# If \n, return
|
||||
if value[i] in [' ', '\t']: inc(toTrim)
|
||||
else: break
|
||||
|
||||
if toTrim > 0:
|
||||
value = value.substring(0, value.len - toTrim)
|
||||
|
||||
|
||||
proc trim_eol(value: var string) {.compiletime.} =
|
||||
## Removes everything after the last line if it contains nothing but whitespace
|
||||
for i in countdown(value.len - 1, 0):
|
||||
# If \n, trim and return
|
||||
if value[i] == 0x0A.char:
|
||||
value = value.substr(0, i)
|
||||
break
|
||||
|
||||
# This is the first character
|
||||
if i == 0:
|
||||
value = ""
|
||||
break
|
||||
|
||||
# Skip change
|
||||
if not (value[i] in [' ', '\t']): break
|
||||
|
||||
|
||||
proc detect_indent(value: string, index: int): int {.compiletime.} =
|
||||
## Detects how indented the line at `index` is.
|
||||
# Seek to the beginning of the line.
|
||||
var lastChar = index
|
||||
for i in countdown(index, 0):
|
||||
if value[i] == 0x0A.char:
|
||||
# if \n, return the indentation level
|
||||
return lastChar - i
|
||||
elif not (value[i] in [' ', '\t']):
|
||||
# if non-whitespace char, decrement lastChar
|
||||
dec(lastChar)
|
||||
|
||||
|
||||
proc parse_thru_string(value: string, i: var int, strType = '"') {.compiletime.} =
|
||||
## Parses until ending " or ' is reached.
|
||||
inc(i)
|
||||
if i < value.len-1:
|
||||
inc(i, value.skipUntil({'\\', strType}, i))
|
||||
|
||||
|
||||
proc parse_to_close(value: string, index: int, open='(', close=')', opened=0): int {.compiletime.} =
|
||||
## Reads until all opened braces are closed
|
||||
## ignoring any strings "" or ''
|
||||
var remainder = value.substring(index)
|
||||
var open_braces = opened
|
||||
result = 0
|
||||
|
||||
while result < remainder.len:
|
||||
var c = remainder[result]
|
||||
|
||||
if c == open: inc(open_braces)
|
||||
elif c == close: dec(open_braces)
|
||||
elif c == '"': remainder.parse_thru_string(result)
|
||||
elif c == '\'': remainder.parse_thru_string(result, '\'')
|
||||
|
||||
if open_braces == 0: break
|
||||
else: inc(result)
|
||||
|
||||
|
||||
iterator parse_stmt_list(value: string, index: var int): string =
|
||||
## Parses unguided ${..} block
|
||||
var read = value.parse_to_close(index, open='{', close='}')
|
||||
var expressions = value.substring(index + 1, read - 1).split({ ';', 0x0A.char })
|
||||
|
||||
for expression in expressions:
|
||||
let value = expression.strip
|
||||
if value.len > 0:
|
||||
yield value
|
||||
|
||||
#Increment index & parse thru EOL
|
||||
inc(index, read + 1)
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
|
||||
|
||||
iterator parse_compound_statements(value, identifier: string, index: int): string =
|
||||
## Parses through several statements, i.e. if {} elif {} else {}
|
||||
## and returns the initialization of each as an empty statement
|
||||
## i.e. if x == 5 { ... } becomes if x == 5: nil.
|
||||
|
||||
template get_next_ident(expected): stmt =
|
||||
var nextIdent: string
|
||||
discard value.parseWhile(nextIdent, {'$'} + identChars, i)
|
||||
|
||||
var next: string
|
||||
var read: int
|
||||
|
||||
if nextIdent == "case":
|
||||
# We have to handle case a bit differently
|
||||
read = value.parseUntil(next, '$', i)
|
||||
inc(i, read)
|
||||
yield next.strip(leading=false) & "\n"
|
||||
|
||||
else:
|
||||
read = value.parseUntil(next, '{', i)
|
||||
|
||||
if nextIdent in expected:
|
||||
inc(i, read)
|
||||
# Parse until closing }, then skip whitespace afterwards
|
||||
read = value.parse_to_close(i, open='{', close='}')
|
||||
inc(i, read + 1)
|
||||
inc(i, value.skipWhitespace(i))
|
||||
yield next & ": nil\n"
|
||||
|
||||
else: break
|
||||
|
||||
|
||||
var i = index
|
||||
while true:
|
||||
# Check if next statement would be valid, given the identifier
|
||||
if identifier in ["if", "when"]:
|
||||
get_next_ident([identifier, "$elif", "$else"])
|
||||
|
||||
elif identifier == "case":
|
||||
get_next_ident(["case", "$of", "$elif", "$else"])
|
||||
|
||||
elif identifier == "try":
|
||||
get_next_ident(["try", "$except", "$finally"])
|
||||
|
||||
|
||||
proc parse_complex_stmt(value, identifier: string, index: var int): NimNode {.compiletime.} =
|
||||
## Parses if/when/try /elif /else /except /finally statements
|
||||
|
||||
# Build up complex statement string
|
||||
var stmtString = newString(0)
|
||||
var numStatements = 0
|
||||
for statement in value.parse_compound_statements(identifier, index):
|
||||
if statement[0] == '$': stmtString.add(statement.substr(1))
|
||||
else: stmtString.add(statement)
|
||||
inc(numStatements)
|
||||
|
||||
# Parse stmt string
|
||||
result = parseExpr(stmtString)
|
||||
|
||||
var resultIndex = 0
|
||||
|
||||
# Fast forward a bit if this is a case statement
|
||||
if identifier == "case":
|
||||
inc(resultIndex)
|
||||
|
||||
while resultIndex < numStatements:
|
||||
|
||||
# Detect indentation
|
||||
let indent = detect_indent(value, index)
|
||||
|
||||
# Parse until an open brace `{`
|
||||
var read = value.skipUntil('{', index)
|
||||
inc(index, read + 1)
|
||||
|
||||
# Parse through EOL
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
|
||||
# Parse through { .. }
|
||||
read = value.parse_to_close(index, open='{', close='}', opened=1)
|
||||
|
||||
# Add parsed sub-expression into body
|
||||
var body = newStmtList()
|
||||
var stmtString = value.substring(index, read)
|
||||
trim_after_eol(stmtString)
|
||||
stmtString = reindent(stmtString, indent)
|
||||
parse_template(body, stmtString)
|
||||
inc(index, read + 1)
|
||||
|
||||
# Insert body into result
|
||||
var stmtIndex = macros.high(result[resultIndex])
|
||||
result[resultIndex][stmtIndex] = body
|
||||
|
||||
# Parse through EOL again & increment result index
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
inc(resultIndex)
|
||||
|
||||
|
||||
proc parse_simple_statement(value: string, index: var int): NimNode {.compiletime.} =
|
||||
## Parses for/while
|
||||
|
||||
# Detect indentation
|
||||
let indent = detect_indent(value, index)
|
||||
|
||||
# Parse until an open brace `{`
|
||||
var splitValue: string
|
||||
var read = value.parseUntil(splitValue, '{', index)
|
||||
result = parseExpr(splitValue & ":nil")
|
||||
inc(index, read + 1)
|
||||
|
||||
# Parse through EOL
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
|
||||
# Parse through { .. }
|
||||
read = value.parse_to_close(index, open='{', close='}', opened=1)
|
||||
|
||||
# Add parsed sub-expression into body
|
||||
var body = newStmtList()
|
||||
var stmtString = value.substring(index, read)
|
||||
trim_after_eol(stmtString)
|
||||
stmtString = reindent(stmtString, indent)
|
||||
parse_template(body, stmtString)
|
||||
inc(index, read + 1)
|
||||
|
||||
# Insert body into result
|
||||
var stmtIndex = macros.high(result)
|
||||
result[stmtIndex] = body
|
||||
|
||||
# Parse through EOL again
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
|
||||
|
||||
proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} =
|
||||
## Parses a string until a $ symbol is encountered, if
|
||||
## two $$'s are encountered in a row, a split will happen
|
||||
## removing one of the $'s from the resulting output
|
||||
var splitValue: string
|
||||
var read = value.parseUntil(splitValue, '$', index)
|
||||
var insertionPoint = node.len
|
||||
|
||||
inc(index, read + 1)
|
||||
if index < value.len:
|
||||
|
||||
case value[index]
|
||||
of '$':
|
||||
# Check for duplicate `$`, meaning this is an escaped $
|
||||
node.add newCall("add", ident("result"), newStrLitNode("$"))
|
||||
inc(index)
|
||||
|
||||
of '(':
|
||||
# Check for open `(`, which means parse as simple single-line expression.
|
||||
trim_eol(splitValue)
|
||||
read = value.parse_to_close(index) + 1
|
||||
node.add newCall("add", ident("result"),
|
||||
newCall(bindSym"strip", parseExpr("$" & value.substring(index, read)))
|
||||
)
|
||||
inc(index, read)
|
||||
|
||||
of '{':
|
||||
# Check for open `{`, which means open statement list
|
||||
trim_eol(splitValue)
|
||||
for s in value.parse_stmt_list(index):
|
||||
node.add parseExpr(s)
|
||||
|
||||
else:
|
||||
# Otherwise parse while valid `identChars` and make expression w/ $
|
||||
var identifier: string
|
||||
read = value.parseWhile(identifier, identChars, index)
|
||||
|
||||
if identifier in ["for", "while"]:
|
||||
## for/while means open simple statement
|
||||
trim_eol(splitValue)
|
||||
node.add value.parse_simple_statement(index)
|
||||
|
||||
elif identifier in ["if", "when", "case", "try"]:
|
||||
## if/when/case/try means complex statement
|
||||
trim_eol(splitValue)
|
||||
node.add value.parse_complex_stmt(identifier, index)
|
||||
|
||||
elif identifier.len > 0:
|
||||
## Treat as simple variable
|
||||
node.add newCall("add", ident("result"), newCall("$", ident(identifier)))
|
||||
inc(index, read)
|
||||
|
||||
result = true
|
||||
|
||||
# Insert
|
||||
if splitValue.len > 0:
|
||||
node.insert insertionPoint, newCall("add", ident("result"), newStrLitNode(splitValue))
|
||||
|
||||
|
||||
proc parse_template(node: NimNode, value: string) =
|
||||
## Parses through entire template, outputing valid
|
||||
## Nim code into the input `node` AST.
|
||||
var index = 0
|
||||
while index < value.len and
|
||||
parse_until_symbol(node, value, index): nil
|
||||
|
||||
|
||||
macro tmpli*(body: expr): stmt =
|
||||
result = newStmtList()
|
||||
|
||||
result.add parseExpr("result = \"\"")
|
||||
|
||||
var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal
|
||||
else: body[1].strVal
|
||||
|
||||
parse_template(result, reindent(value))
|
||||
|
||||
|
||||
macro tmpl*(body: expr): stmt =
|
||||
result = newStmtList()
|
||||
|
||||
var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal
|
||||
else: body[1].strVal
|
||||
|
||||
parse_template(result, reindent(value))
|
||||
|
||||
|
||||
# Run tests
|
||||
when isMainModule:
|
||||
# Ref:
|
||||
# http://nim-lang.org/macros.html
|
||||
# http://nim-lang.org/parseutils.html
|
||||
|
||||
|
||||
# Imports
|
||||
import tables, parseutils, macros, strutils
|
||||
import annotate
|
||||
export annotate
|
||||
|
||||
|
||||
# Fields
|
||||
const identChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
|
||||
|
||||
|
||||
# Procedure Declarations
|
||||
proc parse_template(node: NimNode, value: string) {.compiletime.}
|
||||
|
||||
|
||||
# Procedure Definitions
|
||||
proc substring(value: string, index: int, length = -1): string {.compiletime.} =
|
||||
## Returns a string at most `length` characters long, starting at `index`.
|
||||
return if length < 0: value.substr(index)
|
||||
elif length == 0: ""
|
||||
else: value.substr(index, index + length-1)
|
||||
|
||||
|
||||
proc parse_thru_eol(value: string, index: int): int {.compiletime.} =
|
||||
## Reads until and past the end of the current line, unless
|
||||
## a non-whitespace character is encountered first
|
||||
var remainder: string
|
||||
var read = value.parseUntil(remainder, {0x0A.char}, index)
|
||||
if remainder.skipWhitespace() == read:
|
||||
return read + 1
|
||||
|
||||
|
||||
proc trim_after_eol(value: var string) {.compiletime.} =
|
||||
## Trims any whitespace at end after \n
|
||||
var toTrim = 0
|
||||
for i in countdown(value.len-1, 0):
|
||||
# If \n, return
|
||||
if value[i] in [' ', '\t']: inc(toTrim)
|
||||
else: break
|
||||
|
||||
if toTrim > 0:
|
||||
value = value.substring(0, value.len - toTrim)
|
||||
|
||||
|
||||
proc trim_eol(value: var string) {.compiletime.} =
|
||||
## Removes everything after the last line if it contains nothing but whitespace
|
||||
for i in countdown(value.len - 1, 0):
|
||||
# If \n, trim and return
|
||||
if value[i] == 0x0A.char:
|
||||
value = value.substr(0, i)
|
||||
break
|
||||
|
||||
# This is the first character
|
||||
if i == 0:
|
||||
value = ""
|
||||
break
|
||||
|
||||
# Skip change
|
||||
if not (value[i] in [' ', '\t']): break
|
||||
|
||||
|
||||
proc detect_indent(value: string, index: int): int {.compiletime.} =
|
||||
## Detects how indented the line at `index` is.
|
||||
# Seek to the beginning of the line.
|
||||
var lastChar = index
|
||||
for i in countdown(index, 0):
|
||||
if value[i] == 0x0A.char:
|
||||
# if \n, return the indentation level
|
||||
return lastChar - i
|
||||
elif not (value[i] in [' ', '\t']):
|
||||
# if non-whitespace char, decrement lastChar
|
||||
dec(lastChar)
|
||||
|
||||
|
||||
proc parse_thru_string(value: string, i: var int, strType = '"') {.compiletime.} =
|
||||
## Parses until ending " or ' is reached.
|
||||
inc(i)
|
||||
if i < value.len-1:
|
||||
inc(i, value.skipUntil({'\\', strType}, i))
|
||||
|
||||
|
||||
proc parse_to_close(value: string, index: int, open='(', close=')', opened=0): int {.compiletime.} =
|
||||
## Reads until all opened braces are closed
|
||||
## ignoring any strings "" or ''
|
||||
var remainder = value.substring(index)
|
||||
var open_braces = opened
|
||||
result = 0
|
||||
|
||||
while result < remainder.len:
|
||||
var c = remainder[result]
|
||||
|
||||
if c == open: inc(open_braces)
|
||||
elif c == close: dec(open_braces)
|
||||
elif c == '"': remainder.parse_thru_string(result)
|
||||
elif c == '\'': remainder.parse_thru_string(result, '\'')
|
||||
|
||||
if open_braces == 0: break
|
||||
else: inc(result)
|
||||
|
||||
|
||||
iterator parse_stmt_list(value: string, index: var int): string =
|
||||
## Parses unguided ${..} block
|
||||
var read = value.parse_to_close(index, open='{', close='}')
|
||||
var expressions = value.substring(index + 1, read - 1).split({ ';', 0x0A.char })
|
||||
|
||||
for expression in expressions:
|
||||
let value = expression.strip
|
||||
if value.len > 0:
|
||||
yield value
|
||||
|
||||
#Increment index & parse thru EOL
|
||||
inc(index, read + 1)
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
|
||||
|
||||
iterator parse_compound_statements(value, identifier: string, index: int): string =
|
||||
## Parses through several statements, i.e. if {} elif {} else {}
|
||||
## and returns the initialization of each as an empty statement
|
||||
## i.e. if x == 5 { ... } becomes if x == 5: nil.
|
||||
|
||||
template get_next_ident(expected): stmt =
|
||||
var nextIdent: string
|
||||
discard value.parseWhile(nextIdent, {'$'} + identChars, i)
|
||||
|
||||
var next: string
|
||||
var read: int
|
||||
|
||||
if nextIdent == "case":
|
||||
# We have to handle case a bit differently
|
||||
read = value.parseUntil(next, '$', i)
|
||||
inc(i, read)
|
||||
yield next.strip(leading=false) & "\n"
|
||||
|
||||
else:
|
||||
read = value.parseUntil(next, '{', i)
|
||||
|
||||
if nextIdent in expected:
|
||||
inc(i, read)
|
||||
# Parse until closing }, then skip whitespace afterwards
|
||||
read = value.parse_to_close(i, open='{', close='}')
|
||||
inc(i, read + 1)
|
||||
inc(i, value.skipWhitespace(i))
|
||||
yield next & ": nil\n"
|
||||
|
||||
else: break
|
||||
|
||||
|
||||
var i = index
|
||||
while true:
|
||||
# Check if next statement would be valid, given the identifier
|
||||
if identifier in ["if", "when"]:
|
||||
get_next_ident([identifier, "$elif", "$else"])
|
||||
|
||||
elif identifier == "case":
|
||||
get_next_ident(["case", "$of", "$elif", "$else"])
|
||||
|
||||
elif identifier == "try":
|
||||
get_next_ident(["try", "$except", "$finally"])
|
||||
|
||||
|
||||
proc parse_complex_stmt(value, identifier: string, index: var int): NimNode {.compiletime.} =
|
||||
## Parses if/when/try /elif /else /except /finally statements
|
||||
|
||||
# Build up complex statement string
|
||||
var stmtString = newString(0)
|
||||
var numStatements = 0
|
||||
for statement in value.parse_compound_statements(identifier, index):
|
||||
if statement[0] == '$': stmtString.add(statement.substr(1))
|
||||
else: stmtString.add(statement)
|
||||
inc(numStatements)
|
||||
|
||||
# Parse stmt string
|
||||
result = parseExpr(stmtString)
|
||||
|
||||
var resultIndex = 0
|
||||
|
||||
# Fast forward a bit if this is a case statement
|
||||
if identifier == "case":
|
||||
inc(resultIndex)
|
||||
|
||||
while resultIndex < numStatements:
|
||||
|
||||
# Detect indentation
|
||||
let indent = detect_indent(value, index)
|
||||
|
||||
# Parse until an open brace `{`
|
||||
var read = value.skipUntil('{', index)
|
||||
inc(index, read + 1)
|
||||
|
||||
# Parse through EOL
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
|
||||
# Parse through { .. }
|
||||
read = value.parse_to_close(index, open='{', close='}', opened=1)
|
||||
|
||||
# Add parsed sub-expression into body
|
||||
var body = newStmtList()
|
||||
var stmtString = value.substring(index, read)
|
||||
trim_after_eol(stmtString)
|
||||
stmtString = reindent(stmtString, indent)
|
||||
parse_template(body, stmtString)
|
||||
inc(index, read + 1)
|
||||
|
||||
# Insert body into result
|
||||
var stmtIndex = result[resultIndex].len-1
|
||||
result[resultIndex][stmtIndex] = body
|
||||
|
||||
# Parse through EOL again & increment result index
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
inc(resultIndex)
|
||||
|
||||
|
||||
proc parse_simple_statement(value: string, index: var int): NimNode {.compiletime.} =
|
||||
## Parses for/while
|
||||
|
||||
# Detect indentation
|
||||
let indent = detect_indent(value, index)
|
||||
|
||||
# Parse until an open brace `{`
|
||||
var splitValue: string
|
||||
var read = value.parseUntil(splitValue, '{', index)
|
||||
result = parseExpr(splitValue & ":nil")
|
||||
inc(index, read + 1)
|
||||
|
||||
# Parse through EOL
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
|
||||
# Parse through { .. }
|
||||
read = value.parse_to_close(index, open='{', close='}', opened=1)
|
||||
|
||||
# Add parsed sub-expression into body
|
||||
var body = newStmtList()
|
||||
var stmtString = value.substring(index, read)
|
||||
trim_after_eol(stmtString)
|
||||
stmtString = reindent(stmtString, indent)
|
||||
parse_template(body, stmtString)
|
||||
inc(index, read + 1)
|
||||
|
||||
# Insert body into result
|
||||
var stmtIndex = result.len-1
|
||||
result[stmtIndex] = body
|
||||
|
||||
# Parse through EOL again
|
||||
inc(index, value.parse_thru_eol(index))
|
||||
|
||||
|
||||
proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} =
|
||||
## Parses a string until a $ symbol is encountered, if
|
||||
## two $$'s are encountered in a row, a split will happen
|
||||
## removing one of the $'s from the resulting output
|
||||
var splitValue: string
|
||||
var read = value.parseUntil(splitValue, '$', index)
|
||||
var insertionPoint = node.len
|
||||
|
||||
inc(index, read + 1)
|
||||
if index < value.len:
|
||||
|
||||
case value[index]
|
||||
of '$':
|
||||
# Check for duplicate `$`, meaning this is an escaped $
|
||||
node.add newCall("add", ident("result"), newStrLitNode("$"))
|
||||
inc(index)
|
||||
|
||||
of '(':
|
||||
# Check for open `(`, which means parse as simple single-line expression.
|
||||
trim_eol(splitValue)
|
||||
read = value.parse_to_close(index) + 1
|
||||
node.add newCall("add", ident("result"),
|
||||
newCall(bindSym"strip", parseExpr("$" & value.substring(index, read)))
|
||||
)
|
||||
inc(index, read)
|
||||
|
||||
of '{':
|
||||
# Check for open `{`, which means open statement list
|
||||
trim_eol(splitValue)
|
||||
for s in value.parse_stmt_list(index):
|
||||
node.add parseExpr(s)
|
||||
|
||||
else:
|
||||
# Otherwise parse while valid `identChars` and make expression w/ $
|
||||
var identifier: string
|
||||
read = value.parseWhile(identifier, identChars, index)
|
||||
|
||||
if identifier in ["for", "while"]:
|
||||
## for/while means open simple statement
|
||||
trim_eol(splitValue)
|
||||
node.add value.parse_simple_statement(index)
|
||||
|
||||
elif identifier in ["if", "when", "case", "try"]:
|
||||
## if/when/case/try means complex statement
|
||||
trim_eol(splitValue)
|
||||
node.add value.parse_complex_stmt(identifier, index)
|
||||
|
||||
elif identifier.len > 0:
|
||||
## Treat as simple variable
|
||||
node.add newCall("add", ident("result"), newCall("$", ident(identifier)))
|
||||
inc(index, read)
|
||||
|
||||
result = true
|
||||
|
||||
# Insert
|
||||
if splitValue.len > 0:
|
||||
node.insert insertionPoint, newCall("add", ident("result"), newStrLitNode(splitValue))
|
||||
|
||||
|
||||
proc parse_template(node: NimNode, value: string) =
|
||||
## Parses through entire template, outputing valid
|
||||
## Nim code into the input `node` AST.
|
||||
var index = 0
|
||||
while index < value.len and
|
||||
parse_until_symbol(node, value, index): nil
|
||||
|
||||
|
||||
macro tmpli*(body: expr): stmt =
|
||||
result = newStmtList()
|
||||
|
||||
result.add parseExpr("result = \"\"")
|
||||
|
||||
var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal
|
||||
else: body[1].strVal
|
||||
|
||||
parse_template(result, reindent(value))
|
||||
|
||||
|
||||
macro tmpl*(body: expr): stmt =
|
||||
result = newStmtList()
|
||||
|
||||
var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal
|
||||
else: body[1].strVal
|
||||
|
||||
parse_template(result, reindent(value))
|
||||
|
||||
|
||||
# Run tests
|
||||
when isMainModule:
|
||||
include otests
|
||||
echo "Success"
|
||||
|
||||
Reference in New Issue
Block a user