summaryrefslogtreecommitdiff
path: root/src/parse.lisp
blob: 37415c8d3b88faa124878851e24ebf705cf2741e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
(in-package #:parse)

(defparameter reg-loc
  '(("zr" . 0) ("lk" . 1)
    ("sp" . 2) ("cc" . 3)
    ("vl" . 4) ("fp" . 5))
  "A symbol table mapping register aliases to identifiers. If you want to add
a new alias, do it here.")

(defparameter label-loc '()
  "A symbol table mapping label names to line indices.")

(define-condition parser-error (error)
  ((message :initarg :message
            :initform nil
            :reader message))
  (:report (lambda (condition stream)
             (format stream "~A" (message condition))))
  (:documentation "Dedicated error for an invalid parse."))

(defun to-register (tokens)
  "Attempts to parse a register from the start of TOKENS. If it is badly formed,
throws a PARSER-ERROR."
  (or (and (equal (car tokens) 'LEX::DOLLAR)
           (cadr tokens)
           (let ((r (cadr tokens)))
             (cond ((stringp r) (cdr (assoc r reg-loc :test #'string=)))
                   ((numberp r) (and (<= 0 r 23) r)))))
      (error 'parser-error
             :message
             (format nil "PARSE failed--Expected register, got ~a.~%"
                     (subseq tokens 0 (min 2 (length tokens)))))))

(defun tokens->ast (program)
  "Given PROGRAM, which is a list of lists of symbols,
filters out the labels and parses."
  ;; TODO add directives
  (let ((program (remove nil (mapcar #'extract-label program)))
        (i 0))
    (mapcar (lambda (l) (extract-instruction l i)) program)))

(let ((i 0))
  (defun extract-label (line)
    "Given a series of tokens LINE, determines if LINE is
in the form STRING {colon}. If it is, then it is treated as a
label, and pushed onto the stack with the line index.

Note that this function is intended to be called using mapcar,
so that labels can be added to a map and otherwise removed from
processing."
    (if (and (equal 2 (length line))
	     (stringp (car line))
	     (equal 'lex::colon (cadr line)))
	(progn (push (cons (read-from-string (car line)) i) label-loc) nil)
	(progn (incf i) line))))

(defun extract-instruction (line i)
  "Given instruction LINE, determines the expected type format and passes
LINE and the index I to the the respective function."
  (let* ((mnemonic (intern (string-upcase (car line)) :util))
         ;; TODO add pseudo-ops (i.e., nop, leave, ret...)
         ;; should probably be their own extract function
         (type-fn (cond
                    ((member mnemonic util:type-r) #'extract-r-type)
                    ((member mnemonic util:type-i) #'extract-i-type)
                    ((member mnemonic util:type-j) #'extract-j-type))))
    (if type-fn
        (funcall type-fn line i)
        (error 'parser-error
               :message
               (format nil "PARSE failed--~a is not a known keyword.~%" mnemonic)))))

(defun extract-r-type (line i)
  (let ((mnemonic (intern (string-upcase (car line)) :util)))
    (defun die ()
      (error 'parser-error
             :message
             (format nil "PARSE failed---Incorrect number of operands for ~a" mnemonic)))
    (defun eat-registers (registers-so-far lst)
      (if (not (null lst))
          (eat-registers (cons (to-register lst) registers-so-far)
                         (cddr lst))
          (reverse registers-so-far)))
    (let* ((registers (eat-registers '() (cdr line)))
           ;; handle special cases
           (registers (cond ((member mnemonic '(util::CMP util::CEV))
                             (if (= 2 (length registers))
                                 (cons 0 registers)
                                 (die)))
                            ((member mnemonic '(util::NOT))
                             (if (= 2 (length registers))
                                 (append registers (list 0))
                                 (die)))
                            (t (if (= 3 (length registers)) registers (die))))))
      (list :op mnemonic :d (car registers) :s1 (cadr registers) :s2 (caddr registers)))))

(defun extract-i-type (line i)
  line)

(defun extract-j-type (line i)
  line)