extraction-translatable-strings.lisp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. ;; This software is Copyright (c) cage, 2012.
  2. ;; cage grants you the rights to distribute
  3. ;; and use this software as governed by the terms
  4. ;; of the Lisp Lesser GNU Public License
  5. ;; (http://opensource.franz.com/preamble.html),
  6. ;; known as the LLGPL
  7. (in-package :cl-i18n)
  8. ;; expression := epsilon |
  9. ;; string expression |
  10. ;; function expression
  11. ;; string := "^[^\\\"]+\""
  12. ;; function := function-prefix string
  13. ;; function-prefix := "\\(_\\p{white_space}+\"" string
  14. (defparameter *extr-function-re* "\\(_\\p{white_space}+\"")
  15. (alexandria:define-constant +extr-string-re+ "^[^\\\"]*\"" :test #'string=)
  16. (alexandria:define-constant +extr-escaped-stopper "\\\"" :test #'string=)
  17. (defclass extract-parsed-file (parsed-file) ())
  18. (define-parser-skeleton extract-parsed extract-parsed-file)
  19. ;; (scan tokens sorted-matches max-match)
  20. (defmethod next-token
  21. ((object extract-parsed-file)
  22. &key (hook-to-stringpos t) (return-first-match nil)
  23. (predicate-sort-tokens
  24. #'(lambda (a b) (< (length (first a)) (length (first b)))))
  25. (no-more-token-error t))
  26. (if (peek-valid-stream)
  27. (let ((tokens nil))
  28. (cond
  29. (t
  30. (block token-matching
  31. (let ((scan
  32. (multiple-value-list
  33. (regex-scan *file* *extr-function-re* hook-to-stringpos))))
  34. (when (first scan)
  35. (if return-first-match
  36. (progn
  37. (setf tokens
  38. (list (first scan) (second scan)
  39. (third scan)))
  40. (return-from token-matching))
  41. (push
  42. (list (first scan) (second scan)
  43. (third scan))
  44. tokens))))
  45. (let ((scan
  46. (multiple-value-list
  47. (regex-scan *file* +extr-string-re+ hook-to-stringpos))))
  48. (when (first scan)
  49. (if return-first-match
  50. (progn
  51. (setf tokens
  52. (list (first scan) (second scan) (third scan)))
  53. (return-from token-matching))
  54. (push
  55. (list (first scan) (second scan)
  56. (third scan))
  57. tokens)))))
  58. (if (not (null tokens))
  59. (let* ((sorted-matches (sort tokens predicate-sort-tokens))
  60. (max-match (first sorted-matches)))
  61. (seek *file* (third max-match))
  62. (values (first max-match) (second max-match)))
  63. (if no-more-token-error
  64. (if (peek-end-stream :pos-offset
  65. +peek-length-tokenizer-on-error+)
  66. (progn
  67. (setf *has-errors* t)
  68. (push "error: stream ended without valid token found"
  69. *parsing-errors*)
  70. (string (char@))
  71. nil)
  72. (progn
  73. (setf *has-errors* t)
  74. (push
  75. (format nil
  76. "error: stream ended without valid token found starting from ~s"
  77. (regex-scan *file* "(?s).{6}" :sticky t))
  78. *parsing-errors*)
  79. nil))
  80. nil)))))
  81. nil))
  82. (defun %next-token ()
  83. (next-token *file* :no-more-token-error nil))
  84. (defun just-peek-token ()
  85. (with-no-errors
  86. (multiple-value-bind (token start-token)
  87. (%next-token)
  88. (if token
  89. (progn
  90. (seek *file* start-token)
  91. token)
  92. nil))))
  93. (define-is-stuff-p cl-ppcre:scan *extr-function-re* +extr-string-re+)
  94. (defun escaped-stopper-p (str)
  95. (and (> (length str) 1)
  96. (string= +extr-escaped-stopper str
  97. :start1 0
  98. :end1 (length +extr-escaped-stopper)
  99. :start2 (- (length str) 2)
  100. :end2 (length str))))
  101. (defun parse-delimited-string ()
  102. (labels ((%cat-string ()
  103. (cl-i18n:with-no-errors
  104. (let ((token (%next-token)))
  105. (if (is-extr-string-re-p token)
  106. (if (escaped-stopper-p token)
  107. (concatenate 'string token (%cat-string))
  108. token)
  109. (progn
  110. (push (format nil
  111. "Error: expected delimited string (re: '.*\"') ~a found instead"
  112. token)
  113. *parsing-errors*)
  114. nil))))))
  115. (%cat-string)))
  116. (defun parse-function ()
  117. (cl-i18n:with-no-errors
  118. (parse-function-prefix)
  119. (parse-delimited-string)))
  120. (defun parse-function-prefix ()
  121. (cl-i18n:with-no-errors
  122. (let ((token (%next-token)))
  123. (when (not (is-extr-function-re-p token))
  124. (push (format nil "Error: expected trandslation function name ~a found instead" token)
  125. *parsing-errors*)))))
  126. (defun parse-extract-parsed-file (&optional (accum '()))
  127. (cl-i18n:with-no-errors
  128. (let ((token (just-peek-token))
  129. (translatable '()))
  130. (if (not token)
  131. accum
  132. (progn
  133. (if (is-extr-function-re-p token)
  134. (let ((possible-string (parse-function)))
  135. (when possible-string
  136. (setf translatable (subseq possible-string 0 (1- (length possible-string))))))
  137. (parse-delimited-string))
  138. (parse-extract-parsed-file (remove-if #'(lambda (a) (null a))
  139. (push translatable accum))))))))