;;; basic.el -- BASIC Compiler/Interpreter
;;
;;; Copyright (C) 2008 Kyle W T Sherman
;;
;; Author:   Kyle W T Sherman <kylewsherman at gmail dot com>
;; Created:  2008-03-29
;; Version:  0.1
;; Keywords: basic compiler interpreter mode
;;
;;; Commentary:
;;
;; Provides `basic-mode' function that implements syntax highlighting
;;
;;; Installation:
;;
;; Put `basic.el' where you keep your elisp files and add something like
;; the following to your .emacs file:
;;
;;   (require 'basic)
;;
;;; Usage:
;;
;;
;; Example:
;;
;;   (basic ... ???

;;; Code:

;; cl
(require 'cl)

;;; basic-mode:

;; font lock settings

;; regular expression keyword generator
(defmacro basic-keywords-re (&rest keywords)
  "Compile-time generation of regexp matching any one of KEYWORDS."
  `(eval-when-compile
     (concat "\\b"
             (regexp-opt ',keywords t)
             "\\b")))

(defvar basic-mode-font-lock-keywords nil
  "BASIC keywords used by font-lock.")

;; types
(let ((basic-types (basic-keywords-re
;; "bigint" "binary" "bit" "blob" "char" "character" "date" "datetime" "dec"
;; "decimal" "double" "enum" "float" "float4" "float8" "int" "int1" "int2" "int3"
;; "int4" "int8" "integer" "long" "longblob" "longtext" "mediumblob" "mediumint"
;; "mediumtext" "numeric" "precision" "real" "smallint" "time" "timestamp"
;; "tinyblob" "tinyint" "tinytext" "unsigned" "varbinary" "varchar"
;; "varcharacter" "year" "zerofill"
"dim" "string"
))
      ;; keywords
      (basic-keywords (basic-keywords-re
;; "all" "alter" "and" "as" "asc" "between" "by" "check" "create" "cross"
;; "declare" "default" "delete" "desc" "distinct" "drop" "exists" "for" "from"
;; "grant" "group" "having" "in" "inner" "insert" "into" "is" "join" "left"
;; "like" "not" "null" "on" "option" "or" "order" "outer" "right" "select" "set"
;; "table" "to" "top" "union" "unique" "update" "values" "view" "where" "with"
"goto" "print" "rem"
))
      ;; reserved words
      (basic-reserved-words (basic-keywords-re
;; "add" "after" "analyze" "asensitive" "before" "both" "call" "cascade" "btree"
;; "change" "collate" "column" "condition" "connection" "constraint" "continue"
;; "convert" "cursor" "database" "databases" "delayed" "describe" "deterministic"
;; "distinctrow" "div" "dual" "each" "else" "elseif" "enclosed" "escaped" "exit"
;; "explain" "false" "fetch" "force" "foreign" "fulltext" "goto" "high_priority"
;; "if" "ignore" "index" "infile" "inout" "insensitive" "interval" "iterate"
;; "key" "keys" "kill" "label" "leading" "leave" "leave" "limit" "lines" "load"
;; "lock" "loop" "match" "mod" "modifies" "natural" "optimize" "optionally" "out"
;; "outfile" "primary" "procedure" "purge" "raid0" "read" "reads" "real"
;; "references" "regexp" "release" "rename" "repeat" "replace" "require"
;; "restrict" "return" "revoke" "rlike" "schema" "schemas" "sensitive"
;; "separator" "show" "soname" "spatial" "specific" "sql" "sqlexception"
;; "sqlstate" "sqlwarning" "ssl" "starting" "straight_join" "terminated" "then"
;; "trailing" "trigger" "true" "undo" "unlock" "unsigned" "upgrade" "usage" "use"
;; "using" "varying" "when" "while" "write" "x509" "xor"
"if" "then"
))
      ;; functions
      (basic-builtin-functions (basic-keywords-re
;; "case" "current_date" "current_time" "current_timestamp" "current_user"
;; "day_hour" "day_microsecond" "day_minute" "day_second" "hour_microsecond"
;; "hour_minute" "hour_second" "localtime" "localtimestamp" "low_priority"
;; "minute_microsecond" "minute_second" "no_write_to_binlog" "second_microsecond"
;; "sql_big_result" "sql_calc_found_rows" "sql_small_result" "utc_date"
;; "utc_time" "utc_timestamp" "year_month"
"chr" "int"
)))
  ;; add all keywords to font lock list
  (setq basic-mode-font-lock-keywords
        (list (cons basic-types 'font-lock-type-face)
              (cons basic-keywords 'font-lock-keyword-face)
              (cons basic-reserved-words 'font-lock-function-name-face)
              (cons basic-builtin-functions 'font-lock-builtin-face))))

;; turn on keyword highlighting
(defun basic-highlight-keywords (keywords)
  "Highlight BASIC keywords.
\nSet `font-lock-keywords' using KEYWORDS."
  (interactive)
  (setq font-lock-keywords keywords)
  (font-lock-fontify-buffer))

;; mode map
(defvar basic-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "C-c C-c") 'basic-send-paragraph)
    (define-key map (kbd "C-c C-r") 'basic-send-region)
    (define-key map (kbd "C-c C-s") 'basic-send-string)
    (define-key map (kbd "C-c C-b") 'basic-send-buffer)
    map)
  "Mode map used for `basic-mode'.")

;;;###autoload
(defun basic-mode ()
  "Major mode to edit, compile, and execute BASIC programs."
  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'basic-mode)
  (setq mode-name "BASIC")
  ;; set local key map
  (use-local-map basic-mode-map)
  ;; (when sql-mode-menu
  ;;   (easy-menu-add sql-mode-menu))      ; xemacs
  ;; (set-syntax-table sql-mode-syntax-table)
  ;;(make-local-variable 'font-lock-defaults)
  ;; set font lock
  (make-local-variable 'basic-mode-font-lock-keywords)
  (basic-highlight-keywords basic-mode-font-lock-keywords)
  ;; set comment start
  (make-local-variable 'basic-comment-start)
  (setq comment-start "rem")
  ;; run hooks
  (run-hooks 'basic-mode-hook)
  )

;;; basic compiler

;; token hash
(defvar basic-token-hash
  nil
  "Buffer local variable to hold current compile tokens.")

;; parse line
(defun basic-parse-line
  "Parse current line and return a list of tokens."
  (save-excursion
    ;; parse buffer and convert syntax to lisp code

;; compile and run buffer
(defun basic-run (&optional buffer)
  "Compile and run a BASIC program in BUFFER.
\nBUFFER defaults to `current-buffer'."
  (interactive)
  (unless buffer
    (setq buffer (current-buffer)))
  ;; setup token bag
  (make-local-variable 'basic-token-hash)
  (setq basic-token-hash (make-hash-table))
  ;; traverse buffer and create token tree
  (save-excursion
    (goto-char (point-min))
    (while (and
            (not (eobp))
            (let ((error (basic-parse-line)))
              (if error
                  (error error)
                t)))
      (forward-line 1))
    )
  )

(provide 'basic)

;;; basic.el ends here