typesafeprintf.nim 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. discard """
  2. output: '''test 10'''
  3. """
  4. # bug #1152
  5. import macros, typetraits
  6. proc printfImpl(formatstr: cstring) {.importc: "printf", varargs.}
  7. iterator tokenize(format: string): char =
  8. var i = 0
  9. while i < format.len:
  10. case format[i]
  11. of '%':
  12. case format[i+1]
  13. of '\0': break
  14. else: yield format[i+1]
  15. i.inc
  16. of '\0': break
  17. else: discard
  18. i.inc
  19. macro printf(formatString: string{lit}, args: varargs[typed]): untyped =
  20. var i = 0
  21. let err = getType(bindSym"ValueError")
  22. for c in tokenize(formatString.strVal):
  23. var expectedType = case c
  24. of 'c': getType(bindSym"char")
  25. of 'd', 'i', 'x', 'X': getType(bindSym"int")
  26. of 'f', 'e', 'E', 'g', 'G': getType(bindSym"float")
  27. of 's': getType(bindSym"string")
  28. of 'p': getType(bindSym"pointer")
  29. else: err
  30. var actualType = getType(args[i])
  31. inc i
  32. if sameType(expectedType, err):
  33. error c & " is not a valid format character"
  34. elif not sameType(expectedType, actualType):
  35. error "type mismatch for argument " & $i & ". expected type: " &
  36. $expectedType & ", actual type: " & $actualType
  37. # keep the original callsite, but use cprintf instead
  38. result = newCall(bindSym"printfImpl")
  39. result.add formatString
  40. for a in args: result.add a
  41. printf("test %d\n", 10)