parse_requires.nim 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. ## Utility API for Nim package managers.
  2. ## (c) 2021 Andreas Rumpf
  3. import std / strutils
  4. import ".." / ".." / compiler / [ast, idents, msgs, syntaxes, options, pathutils]
  5. type
  6. NimbleFileInfo* = object
  7. requires*: seq[string]
  8. srcDir*: string
  9. tasks*: seq[(string, string)]
  10. proc extract(n: PNode; conf: ConfigRef; result: var NimbleFileInfo) =
  11. case n.kind
  12. of nkStmtList, nkStmtListExpr:
  13. for child in n:
  14. extract(child, conf, result)
  15. of nkCallKinds:
  16. if n[0].kind == nkIdent:
  17. case n[0].ident.s
  18. of "requires":
  19. for i in 1..<n.len:
  20. var ch = n[i]
  21. while ch.kind in {nkStmtListExpr, nkStmtList} and ch.len > 0: ch = ch.lastSon
  22. if ch.kind in {nkStrLit..nkTripleStrLit}:
  23. result.requires.add ch.strVal
  24. else:
  25. localError(conf, ch.info, "'requires' takes string literals")
  26. of "task":
  27. if n.len >= 3 and n[1].kind == nkIdent and n[2].kind in {nkStrLit..nkTripleStrLit}:
  28. result.tasks.add((n[1].ident.s, n[2].strVal))
  29. else: discard
  30. of nkAsgn, nkFastAsgn:
  31. if n[0].kind == nkIdent and cmpIgnoreCase(n[0].ident.s, "srcDir") == 0:
  32. if n[1].kind in {nkStrLit..nkTripleStrLit}:
  33. result.srcDir = n[1].strVal
  34. else:
  35. localError(conf, n[1].info, "assignments to 'srcDir' must be string literals")
  36. else:
  37. discard
  38. proc extractRequiresInfo*(nimbleFile: string): NimbleFileInfo =
  39. ## Extract the `requires` information from a Nimble file. This does **not**
  40. ## evaluate the Nimble file. Errors are produced on stderr/stdout and are
  41. ## formatted as the Nim compiler does it. The parser uses the Nim compiler
  42. ## as an API. The result can be empty, this is not an error, only parsing
  43. ## errors are reported.
  44. var conf = newConfigRef()
  45. conf.foreignPackageNotes = {}
  46. conf.notes = {}
  47. conf.mainPackageNotes = {}
  48. let fileIdx = fileInfoIdx(conf, AbsoluteFile nimbleFile)
  49. var parser: Parser
  50. if setupParser(parser, fileIdx, newIdentCache(), conf):
  51. extract(parseAll(parser), conf, result)
  52. closeParser(parser)
  53. const Operators* = {'<', '>', '=', '&', '@', '!', '^'}
  54. proc token(s: string; idx: int; lit: var string): int =
  55. var i = idx
  56. if i >= s.len: return i
  57. while s[i] in Whitespace: inc(i)
  58. case s[i]
  59. of Letters, '#':
  60. lit.add s[i]
  61. inc i
  62. while i < s.len and s[i] notin (Whitespace + {'@', '#'}):
  63. lit.add s[i]
  64. inc i
  65. of '0'..'9':
  66. while i < s.len and s[i] in {'0'..'9', '.'}:
  67. lit.add s[i]
  68. inc i
  69. of '"':
  70. inc i
  71. while i < s.len and s[i] != '"':
  72. lit.add s[i]
  73. inc i
  74. inc i
  75. of Operators:
  76. while i < s.len and s[i] in Operators:
  77. lit.add s[i]
  78. inc i
  79. else:
  80. lit.add s[i]
  81. inc i
  82. result = i
  83. iterator tokenizeRequires*(s: string): string =
  84. var start = 0
  85. var tok = ""
  86. while start < s.len:
  87. tok.setLen 0
  88. start = token(s, start, tok)
  89. yield tok
  90. when isMainModule:
  91. for x in tokenizeRequires("jester@#head >= 1.5 & <= 1.8"):
  92. echo x