apicheck.py 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. # apicheck.py
  2. # A simple source code scanner for finding patterns of the form
  3. # [ procname1 $arg1 $arg2 ]
  4. # and verifying the number of arguments
  5. #
  6. # Copyright (c) 2004-2016, Paul McGuire
  7. #
  8. from pyparsing import *
  9. # define punctuation and simple tokens for locating API calls
  10. LBRACK,RBRACK,LBRACE,RBRACE = map(Suppress,"[]{}")
  11. ident = Word(alphas,alphanums+"_") | QuotedString("{",endQuoteChar="}")
  12. arg = "$" + ident
  13. # define an API call with a specific number of arguments - using '-'
  14. # will ensure that after matching procname, an incorrect number of args will
  15. # raise a ParseSyntaxException, which will interrupt the scanString
  16. def apiProc(name, numargs):
  17. return LBRACK + Keyword(name)("procname") - arg*numargs + RBRACK
  18. # create an apiReference, listing all API functions to be scanned for, and
  19. # their respective number of arguments. Beginning the overall expression
  20. # with FollowedBy allows us to quickly rule out non-api calls while scanning,
  21. # since all of the api calls begin with a "["
  22. apiRef = FollowedBy("[") + MatchFirst([
  23. apiProc("procname1", 2),
  24. apiProc("procname2", 1),
  25. apiProc("procname3", 2),
  26. ])
  27. test = """[ procname1 $par1 $par2 ]
  28. other code here
  29. [ procname1 $par1 $par2 $par3 ]
  30. more code here
  31. [ procname1 $par1 ]
  32. [ procname3 ${arg with spaces} $par2 ]"""
  33. # now explicitly iterate through the scanner using next(), so that
  34. # we can trap ParseSyntaxException's that would be raised due to
  35. # an incorrect number of arguments. If an exception does occur,
  36. # then see how we reset the input text and scanner to advance to the
  37. # next line of source code
  38. api_scanner = apiRef.scanString(test)
  39. while 1:
  40. try:
  41. t,s,e = next(api_scanner)
  42. print("found %s on line %d" % (t.procname, lineno(s,test)))
  43. except ParseSyntaxException as pe:
  44. print("invalid arg count on line", pe.lineno)
  45. print(pe.lineno,':',pe.line)
  46. # reset api scanner to start after this exception location
  47. test = "\n"*(pe.lineno-1)+test[pe.loc+1:]
  48. api_scanner = apiRef.scanString(test)
  49. except StopIteration:
  50. break