rpl-edb.el 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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 and create accessible
  10. ;; databases of SysRPL information.
  11. ;;; Code:
  12. (require 'cl-lib)
  13. (require 'rpl-base)
  14. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  15. ;;; Functions for parsing the EDB file
  16. (defun rpl-edb-get-line ()
  17. "Get line that point is on from the current buffer.
  18. Return a string containing the line, or nil if at end of buffer.
  19. As a side-effect set point to the start of the next line."
  20. (cond ((eobp)
  21. nil)
  22. (t
  23. (beginning-of-line)
  24. (let ((start (point)))
  25. (end-of-line)
  26. (let ((line (buffer-substring-no-properties start (point))))
  27. (forward-char)
  28. line)))))
  29. ;;; Parsing identifier lines
  30. ;;;
  31. (defun rpl-trim-stack-effect-lines (lines)
  32. "Trim leading and trailing fluff from strings in LINES list."
  33. (let ((left-edge 1000))
  34. (dolist (s lines)
  35. (string-match "[[:blank:]]*" s)
  36. (when (< (match-end 0) left-edge)
  37. (setq left-edge (match-end 0))))
  38. (mapcar (lambda (s)
  39. (if (string-match "\\([[:blank:]]*\\(\\\\\\)*[[:blank:]]*$\\)" s)
  40. (substring s left-edge (max left-edge (match-beginning 1)))
  41. (substring s left-edge)))
  42. lines)))
  43. (defun rpl-tidy-stack-effect-lines (lines)
  44. "Tidy-up stack-effect lines."
  45. (rpl-trim-stack-effect-lines
  46. (mapcar (lambda (ln)
  47. (replace-regexp-in-string "\\\\->" "-->" ln))
  48. lines)))
  49. (defun rpl-edb-consume-ident-line ()
  50. "Consume an EDB identifier line.
  51. Return a list of two strings: the identifier and its stack effect
  52. description. Move point to the start of the next line."
  53. (let ((line (rpl-edb-get-line)))
  54. (cond ((string-match "^[[:graph:]]+" line)
  55. (let* ((name (match-string 0 line))
  56. (desc (list (concat (make-string (match-end 0) 32)
  57. (substring line (match-end 0))))))
  58. ;; Automatically consume continuation lines
  59. ;; (after line ends with a backslash)
  60. (while (and (> (length (car desc)) 0)
  61. (string-match ".*\\\\[[:blank:]]*$" (car desc)))
  62. (setq desc (cons (rpl-edb-get-line) desc)))
  63. (list name (rpl-tidy-stack-effect-lines (reverse desc)))))
  64. (t
  65. (list nil nil)))))
  66. ;;; Parsing keyword lines
  67. ;;;
  68. (defun rpl-edb-parse-keyword-line (line)
  69. "Parse the given EDB keyword line.
  70. Return a list consisting of the EDB keyword as a keyword symbol
  71. and a parameter string (to be further parsed later)."
  72. (cond ((string-match "\\.[[:blank:]]+\\([[:alnum:]]+\\):" line)
  73. (let ((keyword (intern (concat ":" (match-string 1 line))))
  74. (param-str (substring line (match-end 0))))
  75. (list keyword param-str)))
  76. (t
  77. (list nil ""))))
  78. (defun rpl-edb-parse-calc-param-str (str)
  79. (cond ((string-match "[[:blank:]]*\\([[:alnum:]]+\\)[[:blank:]]*\\(\\\\\\([[:graph:]]+?\\)\\\\\\)?" str)
  80. (let ((addr (match-string 1 str))
  81. (fmt (match-string 3 str))
  82. (flags nil))
  83. (setq str (substring str (match-end 0)))
  84. (while (string-match "[[:blank:]]*\\[\\([[:graph:]]+\\)\\]" str)
  85. (setq flags (cons (intern (concat ":" (match-string 1 str))) flags))
  86. (setq str (substring str (match-end 1))))
  87. (list addr fmt (reverse flags))))
  88. (t
  89. (list "" "" nil))))
  90. (defun rpl-edb-parse-aka-param-str (str)
  91. (let ((names nil))
  92. (while (string-match "[[:blank:]]*\\([[:graph:]]+\\)" str)
  93. (setq names (cons (match-string 1 str) names))
  94. (setq str (substring str (match-end 1))))
  95. (reverse names)))
  96. (defun rpl-edb-parse-userrpl-param-str (str)
  97. (let ((names nil))
  98. (while (string-match "[[:blank:]]*\\([[:graph:]]+\\)" str)
  99. (setq names (cons (match-string 1 str) names))
  100. (setq str (substring str (match-end 1))))
  101. (reverse names)))
  102. (defun rpl-edb-consume-keyword-line ()
  103. (let ((line (rpl-edb-get-line)))
  104. (cl-destructuring-bind (keyword param-str)
  105. (rpl-edb-parse-keyword-line line)
  106. (cond ((member keyword '(:38G :39G :48G :49G))
  107. (cl-destructuring-bind (addr fmt flags)
  108. (rpl-edb-parse-calc-param-str param-str)
  109. (append (list keyword addr fmt) flags)))
  110. ((eql keyword :AKA)
  111. (let ((names (rpl-edb-parse-aka-param-str param-str)))
  112. (cons keyword names)))
  113. ((eql keyword :UserRPL)
  114. (let ((names (rpl-edb-parse-userrpl-param-str param-str)))
  115. (cons keyword names)))
  116. (t
  117. (error "Illegal EDB keyword, %s" keyword))))))
  118. ;;; Parsing extended description lines
  119. ;;;
  120. (defun rpl-edb-consume-description-line ()
  121. "Consume an EDB extended description line.
  122. Return a string. Move point to the start of the next line."
  123. (let ((line (rpl-edb-get-line)))
  124. (substring line 80)))
  125. ;;; Parsing the entries.db buffer
  126. ;;;
  127. (defvar rpl-edb-entries nil
  128. "A place on which to push the entries parsed from the EDB file.")
  129. (defun rpl-edb-parse-buffer ()
  130. "Parse the current buffer, assumed to be the ``entries.db'' file.
  131. Set `rpl-edb-entries' to the parsed results, a list of EDB
  132. entries, where each entry has the format:
  133. (NAMES STACK-EFFECT DESCRIPTION CALC-INFOS)
  134. where NAMES is a list of strings representing the different names
  135. under which the entry is known, STACK-EFFECT and DESCRIPTION are
  136. lists of strings -- one for each line of text in their respective
  137. desciptions -- and CALC-INFOS is a list of entries of the form:
  138. (CALC-KEY ADDRESS NAME-FORMAT &rest FLAG-KEYS)
  139. where CALC-KEY is a keyword specifying a calculator
  140. model (:38G, :39G, :48G or :49G), ADDRESS is a string containing
  141. a hexadecimal address (5 digits for a ROM address, 6 digits for a
  142. library/flash pointer), NAME-FORMAT is a FORMAT string allowing
  143. the name of the entry to be modified for this particular
  144. calculator, and FLAG-KEYS are keyword symbols specifying certain
  145. flags for this calculator."
  146. (interactive)
  147. (let ((entry-names nil)
  148. (entry-stack-effect nil)
  149. (entry-description nil)
  150. (entry-calc-infos nil)
  151. (entries nil))
  152. (beginning-of-buffer)
  153. (while (not (eobp))
  154. (cond ((eql (char-after) ?*)
  155. ;; A comment line -- ignore it
  156. (forward-line))
  157. ((eql (char-after) ?@)
  158. ;; A directive -- ignore it
  159. (forward-line))
  160. ((eql (char-after) ?\;)
  161. ;; An extended description line
  162. (setq entry-description (cons (rpl-edb-consume-description-line) entry-description)))
  163. ((eql (char-after) ?.)
  164. ;; A keyword line
  165. (cl-destructuring-bind (keyword &rest params) (rpl-edb-consume-keyword-line)
  166. (cond ((eql keyword :AKA)
  167. (dolist (name params)
  168. (push name entry-names)))
  169. ((eql keyword :UserRPL)
  170. (dolist (name params)
  171. (push name entry-names)))
  172. (t
  173. (push (cons keyword params) entry-calc-infos)))))
  174. (t
  175. ;; An identifier/stack-effect line
  176. (when entry-names
  177. (push (list entry-names entry-stack-effect (reverse entry-description) entry-calc-infos) entries))
  178. (cl-destructuring-bind (name stack-effect) (rpl-edb-consume-ident-line)
  179. (cond (name
  180. (setq entry-names (list name))
  181. (setq entry-stack-effect stack-effect))
  182. (t
  183. (setq entry-names nil)
  184. (setq entry-stack-effect nil)))
  185. (setq entry-calc-infos nil)
  186. (setq entry-description nil)))))
  187. (when entry-names
  188. (push (list entry-names entry-stack-effect (reverse entry-description) entry-calc-infos) entries))
  189. (setq rpl-edb-entries (reverse entries))))
  190. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  191. ;;; Functions to create calculator data files
  192. (defun rpl-edb-generate-calculator-data (calculator)
  193. "Generate data for CALCULATOR (a keyword identifying the model).
  194. Return a hash-table whose entries are keyed by entry name and
  195. whose values are lists of the form:
  196. (STACK-EFFECT DESCRIPTION ADDRESS &rest FLAGS).
  197. Assumes `rpl-edb-entries' has been set by calling
  198. `rpl-edb-parse-buffer'."
  199. (cl-assert (keywordp calculator))
  200. (let ((table (make-hash-table :test 'equal)))
  201. (dolist (entry rpl-edb-entries)
  202. (cl-destructuring-bind (names stack-effect description calc-infos) entry
  203. (let ((calc-info (car (cl-member calculator calc-infos
  204. :test (lambda (key info) (equal key (car info)))))))
  205. (when calc-info
  206. (let* ((addr-str (cadr calc-info))
  207. (fmt-str (if (caddr calc-info) (caddr calc-info) "%s"))
  208. (flags (cdddr calc-info))
  209. (stack-str (concat (car stack-effect)
  210. (apply 'concat (mapcar (lambda (s) (concat "\n" s))
  211. (cdr stack-effect)))))
  212. (descrip-str (concat (car description)
  213. (apply 'concat (mapcar (lambda (s) (concat "\n" s))
  214. (cdr description)))))
  215. (data (cons stack-str (cons descrip-str (cons addr-str flags)))))
  216. (dolist (name names)
  217. (puthash (format fmt-str name) data table)))))))
  218. table))
  219. (defun rpl-edb-make-data-filename (calculator)
  220. "Make the SysRPL data filename used for CALCULATOR.
  221. Where CALCULATOR should be a keyword symbol identifying the
  222. calculator model, e.g. :48G, :49G etc."
  223. (cl-assert (keywordp calculator))
  224. (concat "sysrpl-data." (substring (symbol-name calculator) 1) ".el"))
  225. (defun rpl-edb-make-calculator-data-file (calculator)
  226. "Make the appropriate SysRPL data file for CALCULATOR.
  227. The CALCULATOR is identified by keyword: :38G, :39G, :48G
  228. or :49G."
  229. (cl-assert (keywordp calculator))
  230. (rpl-write-data-file (rpl-edb-generate-calculator-data calculator)
  231. (rpl-edb-make-data-filename calculator)))
  232. (defun rpl-edb-make-all-data-files ()
  233. "Create all SysRPL data files.
  234. Assumes the current buffer contains the ``entries.db'' file
  235. created by Carsten Dominik, parsing it if necessary to set the
  236. `rpl-edb-entries' variable, then writing captured data to the
  237. SysRPL data files, one for each calculator type."
  238. (interactive)
  239. (unless rpl-edb-entries
  240. (rpl-edb-parse-buffer))
  241. (dolist (calculator '(:38G :39G :48G :49G))
  242. (rpl-edb-make-calculator-data-file calculator)))
  243. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  244. ;;; Functions to read and query calculator data files
  245. (defvar rpl-edb-data-38g nil
  246. "SysRPL data for the 38G calculator.")
  247. (defvar rpl-edb-data-39g nil
  248. "SysRPL data for the 39G calculator.")
  249. (defvar rpl-edb-data-48g nil
  250. "SysRPL data for the 48G calculator.")
  251. (defvar rpl-edb-data-49g nil
  252. "SysRPL data for the 49G calculator.")
  253. (defun rpl-edb-data (calculator)
  254. "Get SysRPL data for the specified CALCULATOR.
  255. Returns a hash table, keyed by SysRPL word name, whose values each
  256. have the form (STACK-EFFECT DESCRIPTION ADDRESS &rest FLAGS)."
  257. (cl-assert (keywordp calculator))
  258. (cond ((eql calculator :38G)
  259. (unless rpl-edb-data-38g
  260. (setq rpl-edb-data-38g
  261. (rpl-read-data-file (rpl-edb-make-data-filename :38G))))
  262. rpl-edb-data-38g)
  263. ((eql calculator :39G)
  264. (unless rpl-edb-data-39g
  265. (setq rpl-edb-data-39g
  266. (rpl-read-data-file (rpl-edb-make-data-filename :39G))))
  267. rpl-edb-data-39g)
  268. ((eql calculator :48G)
  269. (unless rpl-edb-data-48g
  270. (setq rpl-edb-data-48g
  271. (rpl-read-data-file (rpl-edb-make-data-filename :48G))))
  272. rpl-edb-data-48g)
  273. ((eql calculator :49G)
  274. (unless rpl-edb-data-49g
  275. (setq rpl-edb-data-49g
  276. (rpl-read-data-file (rpl-edb-make-data-filename :49G))))
  277. rpl-edb-data-49g)))
  278. (defun rpl-edb-all-names (calculator)
  279. (cl-assert (keywordp calculator))
  280. (let ((names nil))
  281. (maphash (lambda (key val)
  282. (setq names (cons key names)))
  283. (rpl-edb-data calculator))
  284. names))
  285. (defun rpl-edb-get-stack-effect (calculator name)
  286. (car (gethash name (rpl-edb-data calculator))))
  287. (defun rpl-edb-get-description (calculator name)
  288. (cadr (gethash name (rpl-edb-data calculator))))
  289. (defun rpl-edb-get-address (calculator name)
  290. (caddr (gethash name (rpl-edb-data calculator))))
  291. (defun rpl-edb-get-flags (calculator name)
  292. (cadddr (gethash name (rpl-edb-data calculator))))
  293. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  294. ;; End of file
  295. ;;
  296. (provide 'rpl-edb)