;; SYNOPSIS: verilog mode for emacs.
;; AUTHOR: GPL(C) Mohsin Ahmed, http://www.cs.albany.edu/~mosh

(defun mverilog-mode ()
  "verilog mode"
  (interactive)
  (setq major-mode 'mverilog-mode)
  (setq mode-name  "mverilog")
  (modify-syntax-entry ?_ "w"   )    ; Underscore is a word.
  (mosh-map-local-keys
      [?/]                'self-insert-command   ; spoils indentation.
      [?:]                'self-insert-command   ; spoils indentation.
      [return]            'newline               ; spoils indentation.
      [tab]               'tab-to-tab-stop
      [(control down)]    'mverilog-next-func
      [(control up  )]    'mverilog-prev-func
      [kp-space]          'mverilog-match-paren
  )
 (mosh-color-regexp mverilog-keyword-re 'italic)
)

(defvar mverilog-module-re (concat
   "^\\(module\\|primitive\\)[ \t]+\\([A-Za-z_0-9]+\\)?"
  )
  "Regexp matching verilog modules used by mosh-which-func also."
)

(defun mverilog-prev-func ()
  "goto last verilog function"
  (interactive)
  (beginning-of-line)
  (if (re-search-backward mverilog-module-re 0 t)
      (mosh-color-match 'bold 2)
    (goto-char 0))
  (mosh-which-func)
)

(defun mverilog-next-func ()
  "goto next verilog function"
  (interactive)
  (end-of-line)
  (if (re-search-forward mverilog-module-re (point-max) t)
      (mosh-color-match 'bold 2)
    (goto-char (point-max))
  )
  (mosh-which-func)
)

(defvar mverilog-parens (concat
  "\\<\\("
  "begin"         "\\|"
  "end"           "\\|"
  "module"        "\\|"
  "endmodule"     "\\|"
  "table"         "\\|"
  "endtable"      "\\|"
  "case"          "\\|"
  "endcase"       "\\|"
  "primitive"     "\\|"
  "endprimitive"  "\\|"
  "specify"       "\\|"
  "endspecify"
  "\\)\\>"        "\\|"
  "("             "\\|"
  ")"             "\\|"
  "["             "\\|"
  "]"             "\\|"
  "/\\*"          "\\|"
  "\\*/"
))
;       (re-search-backward "(" (point-min))
;       (re-search-backward mverilog-parens (point-min))

(defun mverilog-match-paren (&optional findtok count token)
  "Goto matching verilog begin/end etc.
   Bound to \\[mverilog-match-paren]."
  (interactive)
  (cond
   ((equal findtok "begin")
     (while (> count 0)
       (re-search-backward mverilog-parens (point-min))
       (setq token (match-string 1))
       (cond
          ((comment-shadow nil) nil)
          ((equal token  "*/")           (search-backward "/*"))
          ((equal token  "end")          (setq count (+ count 1)))
          ((equal token  "endtable")     (setq count (+ count 1)))
          ((equal token  "endmodule")    (setq count (+ count 1)))
          ((equal token  "endprimitive") (setq count (+ count 1)))
          ((equal token  "endspecify")   (setq count (+ count 1)))
          ((equal token  "endcase")      (setq count (+ count 1)))
          ((equal token  "begin")        (setq count (- count 1)))
          ((equal token  "module")       (setq count (- count 1)))
          ((equal token  "primitive")    (setq count (- count 1)))
          ((equal token  "table")        (setq count (- count 1)))
          ((equal token  "specify")      (setq count (- count 1)))
          ((equal token  "case")         (setq count (- count 1))))
   ))
   ((equal findtok "end")
    (while (> count 0)
      (re-search-forward mverilog-parens (point-max))
       ;; (message "count=%d at point=%d" count (point)) ;; test
       ;; (mosh-color-match  'highlight)
      (setq token (match-string 1))
      (cond
          ((comment-shadow t) nil)
          ((equal token  "/*")           (search-forward "*/"))
          ((equal token  "begin")        (setq count (+ count 1)))
          ((equal token  "module")       (setq count (+ count 1)))
          ((equal token  "primitive")    (setq count (+ count 1)))
          ((equal token  "table")        (setq count (+ count 1)))
          ((equal token  "specify")      (setq count (+ count 1)))
          ((equal token  "case")         (setq count (+ count 1)))
          ((equal token  "end")          (setq count (- count 1)))
          ((equal token  "endmodule")    (setq count (- count 1)))
          ((equal token  "endprimitive") (setq count (- count 1)))
          ((equal token  "endtable")     (setq count (- count 1)))
          ((equal token  "endspecify")   (setq count (- count 1)))
          ((equal token  "endcase")      (setq count (- count 1))))
    )
    (backward-word 1)
   )
   ((looking-at "\\<\\(begin\\|module\\|primitive\\|case\\|table\\)\\>")
    (end-of-line)
    (mverilog-match-paren "end" 1)
   )
   ((looking-at "\\(end\\)"  )
    (backward-char)
    (mverilog-match-paren "begin"  1)
   )
   ;; Match /* comments */
   ((looking-at "/\\*") (search-forward  "*/") (backward-char 2))
   ((looking-at "\\*/") (search-backward "/*"))
   ;; If nothing switch windows.
   ((looking-at "\\s\(") (forward-list 1) (backward-char 1))
   ((looking-at "\\s\)") (forward-char 1) (backward-list 1))
   ;; Single window?
   ((eq (count-windows) 1) (exchange-point-and-mark))
   ;; go to other window.
   (t (other-window '1))
  )
)

(defun comment-shadow (forward)
  (interactive)
  "If we are in // comment shadow move out of it, return // comment position"
  (let (eol comment-at)
    (save-excursion
      (end-of-line)
      (setq eol (point))
      (beginning-of-line)
      (setq comment-at (search-forward "//" eol 'noerror))
    )
    (if comment-at
     (progn
       (setq comment-at (- comment-at 2))
       (if (> (point) comment-at) ;; inside comment?
           (if forward (forward-line 1) (goto-char comment-at))
         (setq comment-at nil)    ;; outside comment
       )
    ))
    ;; (message "comment-at %s" comment-at) ;; testing
    comment-at     ;; return where we were inside a comment.
))


;; see Verilog LRM.

(defvar mverilog-keyword-re (concat "\\<\\("
 "always"       "\\|" "and"            "\\|" "assign"         "\\|"
 "attribute"    "\\|" "begin"          "\\|" "buf"            "\\|"
 "bufif0"       "\\|" "bufif1"         "\\|" "case"           "\\|"
 "casex"        "\\|" "casez"          "\\|" "cmos"           "\\|"
 "deassign"     "\\|" "default"        "\\|" "defpram"        "\\|"
 "disable"      "\\|" "edge"           "\\|" "else"           "\\|"
 "end"          "\\|" "endattribute"   "\\|" "endcase"        "\\|"
 "endfunction"  "\\|" "endmodule"      "\\|" "endprimitive"   "\\|"
 "endspecify"   "\\|" "endtable"       "\\|" "endtask"        "\\|"
 "event"        "\\|" "for"            "\\|" "force"          "\\|"
 "forever"      "\\|" "fork"           "\\|" "function"       "\\|"
 "highz0"       "\\|" "highz1"         "\\|" "if"             "\\|"
 "initial"      "\\|" "inout"          "\\|" "input"          "\\|"
 "integer"      "\\|" "join"           "\\|" "large"          "\\|"
 "macromodule"  "\\|" "meduim"         "\\|" "module"         "\\|"
 "nand"         "\\|" "negedge"        "\\|" "nmos"           "\\|"
 "nor"          "\\|" "not"            "\\|" "notif0"         "\\|"
 "notif1"       "\\|" "or"             "\\|" "output"         "\\|"
 "parameter"    "\\|" "pmos"           "\\|" "posedge"        "\\|"
 "primitive"    "\\|" "pull0"          "\\|" "pull1"          "\\|"
 "pulldown"     "\\|" "pullup"         "\\|" "rcmos"          "\\|"
 "real"         "\\|" "realtime"       "\\|" "reg"            "\\|"
 "release"      "\\|" "repeat"         "\\|" "rtranif1"       "\\|"
 "scalared"     "\\|" "signed"         "\\|" "small"          "\\|"
 "specify"      "\\|" "specpram"       "\\|" "strength"       "\\|"
 "strong0"      "\\|" "strong1"        "\\|" "supply0"        "\\|"
 "supply1"      "\\|" "table"          "\\|" "task"           "\\|"
 "time"         "\\|" "tran"           "\\|" "tranif0"        "\\|"
 "tranif1"      "\\|" "tri"            "\\|" "tri0"           "\\|"
 "tri1"         "\\|" "triand"         "\\|" "trior"          "\\|"
 "trireg"       "\\|" "unsigned"       "\\|" "vectored"       "\\|"
 "wait"         "\\|" "wand"           "\\|" "weak0"          "\\|"
 "weak1"        "\\|" "while"          "\\|" "wire"           "\\|"
 "wor"          "\\|" "xnor"           "\\|" "xor"
 "\\)\\>"
))

(defvar mverilog-net-re (concat "\\<\\("
 "supply0"    "\\|"   "supply1"   "\\|"  "tri"     "\\|"
 "triand"     "\\|"   "trior"     "\\|"  "trireg"  "\\|"
 "tri0"       "\\|"   "tri1"      "\\|"  "wand"    "\\|"
 "wire"       "\\|"   "wor"
 "\\)\\>"
))

(defvar mverilog-sys-task-func-re (concat "\\<\\("
 "\$bitstoreal"  "\\|"  "\$countdrivers"  "\\|" "\$display"     "\\|"
 "\$fclose"      "\\|"  "\$fdisplay"      "\\|" "\$fmonitor"    "\\|"
 "\$fopen"       "\\|"  "\$fstrobe"       "\\|" "\$fwrite"      "\\|"
 "\$finish"      "\\|"  "\$getpattern"    "\\|" "\$history"     "\\|"
 "\$incsave"     "\\|"  "\$input"         "\\|" "\$itor"        "\\|"
 "\$key"         "\\|"  "\$list"          "\\|" "\$log"         "\\|"
 "\$monitor"     "\\|"  "\$monitoroff"    "\\|" "\$monitoron"   "\\|"
 "\$nokey"
 "\\)\\>"
))

(provide 'mverilog)

;; EOF

