grammar_nanny.nim 1.6 KB

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