rpl-edb.el 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. ;;; -*- mode: emacs-lisp; lexical-binding: t -*-
  2. ;;; rpl-edb.el -- utilities to parse the entries database
  3. ;; Copyright (C) 2014 Paul Onions
  4. ;; Author: Paul Onions <paul.onions@acm.org>
  5. ;; Keywords: RPL, UserRPL, SysRPL, HP48, HP49, HP50
  6. ;; This file is free software, see the LICENCE file in this directory
  7. ;; for copying terms.
  8. ;;; Commentary:
  9. ;; Functions to parse the entries.db file.
  10. ;;; Code:
  11. (require 'cl-lib)
  12. (defun rpl-edb-get-line ()
  13. "Get line that point is on from the current buffer.
  14. Return a string containing the line, or nil if at end of buffer.
  15. As a side-effect set point to the start of the next line."
  16. (cond ((eobp)
  17. nil)
  18. (t
  19. (beginning-of-line)
  20. (let ((start (point)))
  21. (end-of-line)
  22. (let ((line (buffer-substring-no-properties start (point))))
  23. (forward-char)
  24. line)))))
  25. ;;; Parsing identifier lines
  26. ;;;
  27. (defun rpl-trim-stack-effect-description (lines)
  28. "Trim leading and trailing fluff from strings in LINES list."
  29. (let ((left-edge 1000))
  30. (dolist (s lines)
  31. (string-match "[[:blank:]]*" s)
  32. (when (< (match-end 0) left-edge)
  33. (setq left-edge (match-end 0))))
  34. (mapcar (lambda (s)
  35. (if (string-match "\\([[:blank:]]*\\(\\\\\\)*[[:blank:]]*$\\)" s)
  36. (substring s left-edge (max left-edge (match-beginning 1)))
  37. (substring s left-edge)))
  38. lines)))
  39. (defun rpl-edb-consume-ident-line ()
  40. "Consume an EDB identifier line.
  41. Return a list of two strings: the identifier and its stack effect
  42. description. Move point to the start of the next line."
  43. (let ((line (rpl-edb-get-line)))
  44. (cond ((string-match "^[[:graph:]]+" line)
  45. (let* ((name (match-string 0 line))
  46. (desc (list (concat (make-string (match-end 0) 32)
  47. (substring line (match-end 0))))))
  48. ;; Automatically consume continuation lines
  49. ;; (after line ends with a backslash)
  50. (while (and (> (length (car desc)) 0)
  51. (string-match ".*\\\\[[:blank:]]*$" (car desc)))
  52. (setq desc (cons (rpl-edb-get-line) desc)))
  53. (list name (rpl-trim-stack-effect-description (reverse desc)))))
  54. (t
  55. (list "" "")))))
  56. ;;; Parsing keyword lines
  57. ;;;
  58. (defun rpl-edb-parse-keyword-line (line)
  59. "Parse the given EDB keyword line.
  60. Return a list consisting of the EDB keyword as a keyword symbol
  61. and a parameter string (to be further parsed later)."
  62. (cond ((string-match "\\.[[:blank:]]+\\([[:alnum:]]+\\):" line)
  63. (let ((keyword (intern (concat ":" (match-string 1 line))))
  64. (param-str (substring line (match-end 0))))
  65. (list keyword param-str)))
  66. (t
  67. (list nil ""))))
  68. (defun rpl-edb-parse-calc-param-str (str)
  69. (cond ((string-match "[[:blank:]]*\\([[:alnum:]]+\\)[[:blank:]]*\\(\\\\\\([[:graph:]]+?\\)\\\\\\)?" str)
  70. (let ((addr (match-string 1 str))
  71. (fmt (match-string 3 str))
  72. (flags nil))
  73. (setq str (substring str (match-end 0)))
  74. (while (string-match "[[:blank:]]*\\[\\([[:graph:]]+\\)\\]" str)
  75. (setq flags (cons (intern (concat ":" (match-string 1 str))) flags))
  76. (setq str (substring str (match-end 1))))
  77. (list addr fmt (reverse flags))))
  78. (t
  79. (list "" "" nil))))
  80. (defun rpl-edb-parse-aka-param-str (str)
  81. (let ((names nil))
  82. (while (string-match "[[:blank:]]*\\([[:graph:]]+\\)" str)
  83. (setq names (cons (match-string 1 str) names))
  84. (setq str (substring str (match-end 1))))
  85. (reverse names)))
  86. (defun rpl-edb-parse-userrpl-param-str (str)
  87. (let ((names nil))
  88. (while (string-match "[[:blank:]]*\\([[:graph:]]+\\)" str)
  89. (setq names (cons (match-string 1 str) names))
  90. (setq str (substring str (match-end 1))))
  91. (reverse names)))
  92. (defun rpl-edb-consume-keyword-line ()
  93. (let ((line (rpl-edb-get-line)))
  94. (cl-destructuring-bind (keyword param-str)
  95. (rpl-edb-parse-keyword-line line)
  96. (cond ((member keyword '(:38G :39G :48G :49G))
  97. (cl-destructuring-bind (addr fmt flags)
  98. (rpl-edb-parse-calc-param-str param-str)
  99. (append (list keyword addr fmt) flags)))
  100. ((eql keyword :AKA)
  101. (let ((names (rpl-edb-parse-aka-param-str param-str)))
  102. (cons keyword names)))
  103. ((eql keyword :UserRPL)
  104. (let ((names (rpl-edb-parse-userrpl-param-str param-str)))
  105. (cons keyword names)))
  106. (t
  107. (error "Illegal EDB keyword, %s" keyword))))))
  108. ;;; Parsing extended description lines
  109. ;;;
  110. (defun rpl-edb-consume-description-line ()
  111. "Consume an EDB extended description line.
  112. Return a string. Move point to the start of the next line."
  113. (let ((line (rpl-edb-get-line)))
  114. (substring line 80)))
  115. ;;; Parsing the entries.db buffer
  116. ;;;
  117. (defun rpl-edb-parse-buffer ()
  118. "Parse the current buffer, assumed to be the entries.db file.
  119. Return a list of EDB entries, where each entry has the format:
  120. (NAMES STACK-EFFECT DESCRIPTION CALC-INFOS)
  121. where NAMES is a list of strings representing the different names
  122. under which the entry is known, STACK-EFFECT and DESCRIPTION are
  123. lists of strings -- one for each line of text in their respective
  124. desciptions -- and CALC-INFOS is a list of entries of the form:
  125. (CALC-KEY ADDRESS NAME-FORMAT &rest FLAG-KEYS)
  126. where CALC-KEY is a keyword specifying a calculator
  127. model (:38G, :39G, :48G or :49G), ADDRESS is a string containing
  128. a hexadecimal address (5 digits for a ROM address, 6 digits for a
  129. library/flash pointer), NAME-FORMAT is a FORMAT string allowing
  130. the name of the entry to be modified for this particular
  131. calculator, and FLAG-KEYS are keyword symbols specifying certain
  132. flags for this calculator."
  133. (let ((entry-names nil)
  134. (entry-stack-effect nil)
  135. (entry-description nil)
  136. (entry-calc-infos nil)
  137. (entries nil))
  138. (beginning-of-buffer)
  139. (while (not (eobp))
  140. (cond ((eql (char-after) ?*)
  141. ;; A comment line -- ignore it
  142. (forward-line))
  143. ((eql (char-after) ?@)
  144. ;; A directive -- ignore it
  145. (forward-line))
  146. ((eql (char-after) ?\;)
  147. ;; An extended description line
  148. (setq entry-description (cons (rpl-edb-consume-description-line) entry-description)))
  149. ((eql (char-after) ?.)
  150. ;; A keyword line
  151. (cl-destructuring-bind (keyword &rest params)
  152. (rpl-edb-consume-keyword-line)
  153. (cond ((eql keyword :AKA)
  154. (dolist (name params)
  155. (push name entry-names)))
  156. ((eql keyword :UserRPL)
  157. (dolist (name params)
  158. (push name entry-names)))
  159. (t
  160. (push (cons keyword params) entry-calc-infos)))))
  161. (t
  162. ;; An identifier/stack-effect line
  163. (when entry-names
  164. (push (list entry-names entry-stack-effect (reverse entry-description) entry-calc-infos) entries))
  165. (cl-destructuring-bind (name stack-effect)
  166. (rpl-edb-consume-ident-line)
  167. (setq entry-names (list name))
  168. (setq entry-stack-effect stack-effect)
  169. (setq entry-calc-infos nil)
  170. (setq entry-description nil)))))
  171. (when entry-names
  172. (push (list entry-names entry-stack-effect (reverse entry-description) entry-calc-infos) entries))
  173. (reverse entries)))
  174. ;;; Creating calculator data files
  175. ;;;
  176. (defun rpl-edb-generate-calculator-data (edb-entries calculator)
  177. "Generate data for CALCULATOR (a keyword identifying the model).
  178. Return a hash-table whose entries are keyed by entry name and
  179. whose values are lists of the form:
  180. (STACK-EFFECT DESCRIPTION ADDRESS &rest FLAGS)."
  181. (cl-assert (keywordp calculator))
  182. (let ((table (make-hash-table)))
  183. (dolist (entry edb-entries)
  184. (cl-destructuring-bind (names stack-effect description calc-infos) entry
  185. (let ((calc-info (car (cl-member calculator calc-infos
  186. :test (lambda (key info) (equal key (car info)))))))
  187. (when calc-info
  188. (let* ((addr-str (cadr calc-info))
  189. (fmt-str (if (caddr calc-info) (caddr calc-info) "%s"))
  190. (flags (cdddr calc-info))
  191. (stack-str (apply 'concat (mapcar (lambda (s) (concat s "\n")) stack-effect)))
  192. (descrip-str (apply 'concat (mapcar (lambda (s) (concat s "\n")) description)))
  193. (data (cons stack-str (cons descrip-str (cons addr-str flags)))))
  194. (dolist (name names)
  195. (puthash (format fmt-str name) data table)))))))
  196. table))
  197. (defun rpl-edb-make-calculator-data-file (edb-entries calculator)
  198. ""
  199. (cl-assert (keywordp calculator))
  200. (rpl-write-data-file (rpl-edb-generate-calculator-data edb-entries calculator)
  201. (rpl-make-sysrpl-data-filename calculator)))
  202. (defvar rpl-edb-entries nil
  203. "A place on which to push the parsed entries.")
  204. (defun rpl-edb-make-all-data-files ()
  205. ""
  206. (interactive)
  207. (setq rpl-edb-entries (rpl-edb-parse-buffer))
  208. (dolist (calculator '(:38G :39G :48G :49G))
  209. (rpl-edb-make-calculator-data-file rpl-edb-entries calculator)))