grammar_nanny.nim 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. ## Simple tool to check for obvious mistakes in Nim's
  2. ## grammar.txt file.
  3. import std / [strutils, sets]
  4. when defined(nimPreviewSlimSystem):
  5. import std/syncio
  6. import ".." / compiler / [
  7. llstream, lexer, options, msgs, idents,
  8. lineinfos, pathutils]
  9. proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) =
  10. var f = AbsoluteFile"doc/grammar.txt"
  11. let data = readFile(f.string).multiReplace({"IND{=}": "SAME_IND", "'": "\""})
  12. var stream = llStreamOpen(data)
  13. var declaredSyms = initHashSet[string]()
  14. var usedSyms = initHashSet[string]()
  15. usedSyms.incl "module" # 'module' is the start rule.
  16. if stream != nil:
  17. declaredSyms.incl "section" # special case for 'section(RULE)' in the grammar
  18. var
  19. L: Lexer
  20. tok: Token
  21. initToken(tok)
  22. openLexer(L, f, stream, cache, config)
  23. # load the first token:
  24. rawGetTok(L, tok)
  25. var word = ""
  26. while tok.tokType != tkEof:
  27. #printTok(config, tok)
  28. if isKeyword(tok.tokType) or tok.tokType == tkSymbol:
  29. word = tok.ident.s
  30. rawGetTok(L, tok)
  31. if tok.tokType == tkEquals:
  32. declaredSyms.incl word
  33. rawGetTok(L, tok)
  34. elif not allCharsInSet(word, {'A'..'Z', '0'..'9', '_'}):
  35. usedSyms.incl word
  36. else:
  37. rawGetTok(L, tok)
  38. for u in declaredSyms:
  39. if u notin usedSyms:
  40. echo "Unused non-terminal: ", u
  41. for u in usedSyms:
  42. if u notin declaredSyms:
  43. echo "Undeclared non-terminal: ", u
  44. closeLexer(L)
  45. else:
  46. rawMessage(config, errGenerated, "cannot open file: " & f.string)
  47. proc checkGrammarFile* =
  48. checkGrammarFileImpl(newIdentCache(), newConfigRef())