/log-parser.rkt

http://github.com/elibarzilay/rudybot · Shell · 88 lines · 73 code · 8 blank · 7 comment · 0 complexity · e762f999dbd269db1b0354a1ba6e7c11 MD5 · raw file

  1. #! /bin/sh
  2. #| Hey Emacs, this is -*-scheme-*- code!
  3. exec racket -l errortrace --require "$0" --main -- ${1+"$@"}
  4. exec racket --require "$0" --main -- ${1+"$@"}
  5. |#
  6. #lang racket
  7. (require
  8. (only-in profile profile-thunk)
  9. (only-in mzlib/etc this-expression-source-directory)
  10. (planet schematics/schemeunit:3)
  11. (planet schematics/schemeunit:3/text-ui) )
  12. (provide (struct-out utterance))
  13. (struct utterance (timestamp speaker target text) #:prefab)
  14. (define (string->utterance s)
  15. (define (ensure-string x)
  16. (cond
  17. [(string? x)
  18. x]
  19. [(bytes? x)
  20. (bytes->string/utf-8 x)]))
  21. (match s
  22. ;; old style: the guts are an unparsed scheme string
  23. [(regexp #px"^([[:print:]]+) <= \":([^!]*)!([^@]*)@([^ ]*) PRIVMSG ([^:]+) :(.*)\"$"
  24. (list _ timestamp nick id host target text))
  25. (utterance timestamp nick target text)]
  26. ;; new style: the guts are an s-expression
  27. [(regexp #px"^([[:print:]]+) <= +(\\(.*\\))" (list _ timestamp raw-string))
  28. (match (read (open-input-string raw-string))
  29. [(list (list 'prefix (regexp #rx"(.*)!(.*)@(.*)" (list _ nick _ _)))
  30. (list 'command #"PRIVMSG")
  31. (list 'params
  32. (list 'param target)
  33. (list 'param text)))
  34. (apply utterance (map ensure-string (list timestamp nick target text)))])
  35. ]
  36. [_ #f]))
  37. (define-test-suite tests
  38. (for ([line '("2010-01-19T03:01:31Z <= \":offby1!n=user@pdpc/supporter/monthlybyte/offby1 PRIVMSG ##cinema :rudybot: uptime\""
  39. "2010-01-19T03:01:31Z <= ((prefix #\"offby1!n=user@pdpc/supporter/monthlybyte/offby1\") (command #\"PRIVMSG\") (params (param #\"##cinema\") (param #\"rudybot: uptime\")))")])
  40. (check-equal? (string->utterance line)
  41. #s(utterance "2010-01-19T03:01:31Z"
  42. "offby1"
  43. "##cinema"
  44. "rudybot: uptime")))
  45. )
  46. (define (pe fmt . args)
  47. (apply fprintf (current-error-port) fmt args))
  48. (provide main)
  49. (define (main . args)
  50. (define input-file-names
  51. (command-line
  52. #:program "log-parser"
  53. #:args input-file-names
  54. input-file-names))
  55. (cond
  56. [(null? input-file-names)
  57. (displayln "You didn't specify any input files; running unit tests instead of parsing" (current-error-port))
  58. (exit (if (positive? (run-tests tests)) 1 0))]
  59. [(< 1 (length input-file-names))
  60. (error 'log-parser "I want at most one input file name; instead you gave me ~s" input-file-names)]
  61. [else
  62. (let ([input-file-name (build-path (this-expression-source-directory) (car input-file-names))]
  63. [output-file-name "parsed-log"])
  64. (call-with-input-file
  65. input-file-name
  66. (lambda (ip)
  67. (pe "Reading from ~a; writing to ~a..." input-file-name output-file-name)
  68. (port-count-lines! ip)
  69. (profile-thunk
  70. (lambda ()
  71. (call-with-output-file output-file-name
  72. (lambda (op)
  73. (for ([line (in-lines ip)])
  74. (let ([utz (string->utterance line)])
  75. (when utz
  76. (write utz op)
  77. (newline op)))))
  78. #:exists 'truncate)))))
  79. (pe "done~%"))]))