dykstrom / basic-mode

Emacs major mode for editing BASIC code
GNU General Public License v3.0
7 stars 10 forks source link

Support dialects of BASIC #20

Closed hackerb9 closed 1 year ago

hackerb9 commented 1 year ago

As discussed in #18, it would be useful if basic-mode let users pick from a set of predefined BASIC flavors and made it easier for them to create customized modes own.

One solution would be to use define-derived-mode to create new submodes. Here is an example based on the reference card for the TRS-80 Model III's BASIC. It is similar to most other TRS-80 BASICs (like Color Basic and Model 100 BASIC), but has some notable differences. For example, one of the statements allowed is CLOAD?, so in this example I've modified the syntax table so that question mark is part of an identifier instead of punctuation. [Side note: An unintended benefit of this is that programs that use ? as shorthand for PRINT are properly syntax highlighted, as long as the question mark is followed by a space.]

Click to see basic-trs80-mode ```elisp (defun basic-mode-initialize () "Initializations for sub-modes of basic-mode. This is called by basic-mode on startup and by its derived modes after making customizations to font-lock keywords and syntax tables." (setq-local basic-font-lock-keywords (list (list basic-comment-regexp 0 'font-lock-comment-face) (list basic-linenum-regexp 0 'font-lock-constant-face) (list basic-label-regexp 0 'font-lock-constant-face) (list basic-constant-regexp 0 'font-lock-constant-face) (list basic-keyword-regexp 0 'font-lock-keyword-face) (list basic-type-regexp 0 'font-lock-type-face) (list basic-function-regexp 0 'font-lock-function-name-face) (list basic-builtin-regexp 0 'font-lock-builtin-face))) (if basic-syntax-highlighting-require-separator (setq-local font-lock-defaults (list basic-font-lock-keywords nil t)) (setq-local font-lock-defaults (list basic-font-lock-keywords nil t basic-font-lock-syntax))) (unless font-lock-mode (font-lock-mode 1))) (define-derived-mode basic-trs80-mode basic-mode "Basic[TRS-80]" "Programming mode for BASIC for TRS-80 machines. This is just a demo and so only handles TRS-80 Model III. At a minimum, this is missing keywords used in the TRS-80 Model 100 BASIC and TRS-80 Extended Color Computer BASIC. For more details see `basic-mode'." (setq-local basic-function-regexp (regexp-opt '("abs" "asc" "atn" "cdbl" "cint" "chr$" "cos" "csng" "erl" "err" "exp" "fix" "fre" "inkey$" "inp" "int" "left$" "len" "log" "mem" "mid$" "point" "pos" "reset" "right$" "set" "sgn" "sin" "sqr" "str$" "string$" "tan" "time$" "usr" "val" "varptr") 'symbols)) (setq-local basic-builtin-regexp (regexp-opt '("?" "auto" "clear" "cload" "cload?" "cls" "data" "delete" "edit" "input" "input #" "let" "list" "llist" "lprint" "lprint tab" "lprint using" "new" "mod" "not" "or" "out" "peek" "poke" "print" "print tab" "print using" "read" "restore" "system" "troff" "tron") 'symbols)) (setq-local basic-keyword-regexp (regexp-opt '("as" "defdbl" "defint" "defsng" "defstr" "dim" "do" "else" "end" "error" "for" "gosub" "go sub" "goto" "go to" "if" "next" "on" "step" "random" "resume" "return" "then" "to") 'symbols)) (modify-syntax-entry ?? "w " basic-mode-syntax-table) ; Treat ? as part of identifier ("cload?") (modify-syntax-entry ?# "w " basic-mode-syntax-table) ; Treat # as part of identifier ("input #") (basic-mode-initialize)) ```

As you can see, I'm setting some of the variables, like basic-keyword-regexp, that were declared constant via defconst, so I changed those to defvar.

In addition, I removed some of the font-lock initialization from end of the definition of basic-mode and moved it into basic-mode-initialize. I may have moved more than necessary, but it seems to work.

basic-mode ```elisp (define-derived-mode basic-mode prog-mode "Basic" "Major mode for editing BASIC code." :group 'basic (add-hook 'xref-backend-functions #'basic-xref-backend nil t) (setq-local indent-line-function 'basic-indent-line) (setq-local comment-start "'") (setq-local syntax-propertize-function (syntax-propertize-rules ("\\(\\_\\)" (1 "<")))) (basic-mode-initialize)) ```

Further questions:

Trivia: According to the Small BASIC FAQ, there are over 230 different BASIC dialects.

hackerb9 commented 1 year ago

PS A nice Rosetta Stone of various early BASICs can be found here: BASIC Program Conversion by Bill Crider. It covers the IBM PC & PC jr, C64, Apple //e, Apple ][+, TRS-80 Models III and IV, and TRS-80 Color Computers. It goes over every reserved word and what it means on the various platforms.

dykstrom commented 1 year ago

Further questions:

  • What is the difference between basic-builtin-regexp and basic-keyword-regexp? It seemed like keywords were mostly flow control (IF, GOTO, CALL) and data declarations (DIM, DEFINT), while builtins were everything else (POKE, PRINT), but I couldn't quite tell. For example, why are "AS" and "RANDOMIZE" keywords? Why isn't "PEEK" a function?

The division of reserved words into categories is loosely based on the QuickBasic 4.5 documentation, which for example states that PEEK is a statement. Aside from that, I made some arbitrary choices that are very likely to be incorrect.

To make basic-mode more similar to other programming modes, I think built-in functions (e.g. ABS, CHR$) should be highlighted in builtin-face, statements (e.g. IF, DIM, RANDOMIZE) should be highlighted in keyword-face, and function/subroutine definitions should be highlighted in function-name-face. Whether PEEK (and possibly others) are built-in functions or statements will be up to the derived mode to decide.

hackerb9 commented 1 year ago

Whether PEEK (and possibly others) are built-in functions or statements will be up to the derived mode to decide.

I agree. If the documentation for a particular language specifies one way or another, the derived mode should try to match that expectation of the programmer.

What was confusing to me was how I ought to use the font-lock-builtin-face when I was trying to create a new derived mode. [Update: Please see issue #21 for more about this.]

The BASIC manuals I've been looking at don't use the term "builtin" to distinguish any of the keywords. Instead, they refer to Statements, Functions, and Operators. What are "builtins" in BASIC? It would seem like every BASIC statement is builtin, so the only reason to distinguish builtins from keywords would be to highlight some other difference in the language, such as flow control, which does get rather tangled in BASIC. By the way, the Emacs manual does not help as it defines font-lock-builtin-face as being for "builtin functions". C like languages seem to use that face for preprocessor directives, which seems like the opposite of a "builtin", but adds credence to the idea that the face is available to be used for other purposes if a language lacks the notion of builtin functions. That Rosetta Stone I mentioned does break down the BASIC reserved words into different categories, although I am not sure they are helpful for syntax highlighting or not.
hackerb9 commented 1 year ago

Just for reference, here are the categories into which Bill Crider's “BASIC Programming Conversion” puts the reserved words from several dialects of BASIC. Actually, "tags" would be a better description, as each word can fit in multiple boxes.

1. INPUT 1. Human: Keyboard, Joystick, Paddle, Light Pen 1. CMD 1. GET 1. GET# 1. IN# 1. INKEY$ 1. INPUT 1. INPUT LINE 1. INPUT# 1. INPUT$ 1. JOYSTK 1. KEY 1. KEY$ 1. LINE INPUT 1. LINE INPUT# 1. PDL 1. PEEK 1. PEN 1. POKE (for input?) 1. PR# 1. READ 1. READ# 1. STICK 1. STRIG 1. Storage: Cassette, Sequential/Random file access, Disk 1. APPEND 1. AUDIO 1. B-A: 1. B-F: 1. B-P: 1. B-R: 1. B-W: 1. BACKUP 1. BLOAD 1. BLOCK-ALLOCATE: 1. BLOCK-EXECUTE: 1. BLOCK-FREE: 1. BLOCK-READ: 1. BLOCK-WRITE: 1. BSAVE 1. BUFFER-POINTER 1. CATALOG 1. CHAIN 1. CHDIR 1. CLOAD 1. CLOADM 1. CLOSE 1. CMD 1. COPY 1. CSAVE 1. CSAVEM 1. DCLOSE 1. DELETE 1. DIR 1. DIRECTORY 1. DLOAD 1. DLOADM 1. DOPEN 1. DRIVE 1. DSAVE 1. DSKI$ 1. DSKINI 1. DSKO$ 1. EOF 1. ERASE 1. FIELD 1. FILE 1. FILES 1. FORMAT 1. FRE 1. FREE 1. GET 1. GET# 1. IN# 1. INP 1. INPUT 1. INPUT # 1. INPUT$ 1. KILL 1. LINE INPUT 1. LINE INPUT# 1. LOAD 1. LOADM 1. LOF 1. LPOS 1. M-E: 1. M-R: 1. M-W: 1. MEMORY-EXECUTE: 1. MEMORY-READ: 1. MEMORY-WRITE: 1. MERGE 1. MKDIR 1. MOTOR 1. NAME 1. NEW 1. OPEN 1. OUT 1. OUTPUT 1. POINTER 1. PR # 1. PRINT 1. PRINT # 1. PRINT #USING 1. PRINT USING 1. PUT 1. PUT# 1. READ 1. READ# 1. RECALL 1. RECORD 1. RESTORE 1. RMDIR 1. RUN 1. SAVE 1. SAVEM 1. SCRATCH 1. SHLOAD 1. SKIPF 1. ST 1. STATUS 1. STORE 1. UNLOAD 1. WRITE 1. WRITE# 1. External Input: Display, Serial/Modem, Printer, A/D Converter 1. COM 1. CMD 1. CSRLIN 1. IN# 1. IN 1. INP 1. LPOS 1. PEEK 1. POINT 1. POSN 1. PPOINT 1. ST 1. STATUS 1. WAIT 1. SCREEN 1. PROCESSING 1. Program Flow 1. AUTO 1. BUFFER-POINTER 1. CHAIN 1. CHDIR 1. CMD 1. DATA 1. DELETE 1. ELSE 1. END 1. ERASE 1. ERL 1. ERR 1. ERROR 1. ERRS$ 1. FOR 1. GOSUB 1. GOTO 1. IF 1. IF-THEN 1. IF-THEN-ELSE 1. KILL 1. LIST 1. LLIST 1. M-E 1. ON ERR 1. POINTER 1. REM 1. REN 1. RENAME 1. RENUM 1. RENUMBER 1. RESTORE 1. RESUME 1. MEMORY-EXECUTE 1. RESUME NEXT 1. RETURN 1. RMDIR 1. MKDIR 1. NEXT 1. NOTRACE 1. ON ERR GOTO 1. ON ERROR GOTO 1. RUN 1. SCRATCH 1. SHELL 1. SKIPF 1. SPEED= 1. STOP 1. THEN 1. TO 1. TRACE OFF 1. TRACE ON 1. TRACE 1. TROFF 1. TRON 1. WAIT 1. WEND 1. WHILE 1. Functions 1. ABS 1. ASC 1. ATN 1. CDBL 1. CHR$ 1. CINT 1. COS 1. CSRLIN 1. CVD 1. CVI 1. CVN 1. CVS 1. EOF 1. ERL 1. ERR 1. ERRS$ 1. EXP 1. FIX 1. FRE 1. HEXS 1. INKEY$ 1. INP 1. INPUT$ 1. JOYSTK 1. LEFT$ 1. LOC 1. LOF 1. LOG 1. LPOS 1. M-R: 1. MEM 1. MEMORY-READ: 1. MID$ 1. MKD$ 1. MKI$ 1. MKN$ 1. MKS$ 1. OCT$ 1. PDL 1. PEEK 1. PEN 1. POINT 1. POS 1. PPOINT 1. RIGHT$ 1. RND 1. ROW 1. SCREEN 1. SGN 1. SQR 1. STRIG 1. TIME$ 1. SIN 1. SPACE$ 1. STATUS 1. STICK 1. STRING$ 1. TANVAL 1. VARPTR 1. SPC( 1. STR$ 1. TI$ 1. VARPTR$ 1. Operators 1. ^ Exponentiation 1. \- Negation 1. \* Multiplication 1. / Floating-Point Division 1. \ Integer Division 1. MOD Modulo Arithmetic 1. \+ Addition 1. \- Subtraction 1. = Equality 1. < Less Than 1. > Greater Than 1. NOT 1. AND 1. OR 1. XOR 1. EQV 1. IMP 1. Data Types: Constants, Variables, Strings, Numerics, Arrays 1. CDBL 1. CHR$ 1. CINT 1. COMMON 1. CONCAT 1. CSNG 1. CVD 1. CVI 1. CVN 1. CVS 1. DATA 1. DEFDBL 1. DEFFN 1. DEFINT 1. DEFSNG 1. DEFSTR 1. DIM 1. FIX 1. HEX$ 1. INT 1. LET 1. MKD$ 1. MKI$ 1. MKN$ 1. MKS$ 1. OCT$ 1. OPTION BASE 1. STR$ 1. STRING$ 1. SWAP 1. VAL 1. VARPTR 1. VARPTR$ 1. Machine & Memory Interface 1. B-A: 1. B-F: 1. B-P: 1. B-R: 1. B-W: 1. BLOAD 1. BLOCK-ALLOCATE: 1. BLOCK-EXECUTE: 1. BLOCK-FREE: 1. BLOCK-READ: 1. BLOCK-WRITE: 1. BSAVE 1. BUFFER-POINTER: 1. CALL 1. DEF SEG 1. FRE 1. HIMEM: 1. INP 1. LOADM 1. LOMEM: 1. M-E: 1. M-R: 1. M-W: 1. MEM 1. MEMORY-EXECUTE: 1. MEMORY-READ: 1. MEMORY-WRITE: 1. OUT 1. PCLEAR 1. PCLS 1. PCOPY 1. PEEK 1. PMODE 1. POKE 1. POP 1. PR # 1. SAVEM 1. SHLOAD 1. SPEED= 1. STATUS 1. SYS 1. SYSTEM 1. UNLOAD 1. USER 1. USR 1. VARPTR 1. VARPTR$ 1. OUTPUT 1. Display output 1. CIRCLE 1. CLS 1. CMD 1. COLOR 1. COLOR= 1. CSRLIN 1. DRAW 1. DRAWTO 1. FLASH 1. GET 1. GR 1. HCOLOR 1. HCOLOR= 1. HGR 1. HGR2 1. HLIN 1. HLIN-AT 1. HPLOT 1. HTAB 1. INVERSE 1. KEY 1. LINE 1. LOCATE 1. NORMAL 1. PAGE 1. PAINT 1. PALETTE 1. PALETTE USING 1. PCLEAR 1. PCLS 1. PCOPY 1. PEEK 1. PEN 1. PLOT 1. PMAP 1. PMODE 1. POINT 1. POINTER 1. POKE 1. PPOINT 1. PR # 1. PRESET 1. PRINT 1. PRINT # 1. PRINT #USING 1. PRINT @ 1. PRINT AT 1. PRINT USING 1. PSET 1. PUT 1. RESET 1. ROT= 1. SCALE 1. SCALE= 1. SCREEN 1. SCRN( 1. SET 1. SHLOAD 1. VIEW 1. VLIN 1. VLIN-AT 1. VPOS 1. VTAB 1. WIDTH 1. WINDOW 1. WRITE 1. WRITE # 1. XDRAW 1. XPLOT 1. Sound 1. AUDIO 1. BEEP 1. MOTOR 1. NOISE 1. PEEK 1. PLAY 1. POKE 1. SOUND 1. Printer 1. CMD 1. LLIST 1. LPOS 1. LPRINT 1. LPRINT USING 1. POSN 1. PR # 1. PRINT 1. PRINT @ 1. PRINT # 1. PRINT #USING 1. PRINT USING 1. ST 1. STATUS 1. TAB 1. TAB( 1. WRITE 1. WRITE# 1. Storage output 1. APPEND 1. B-A: 1. B-F: 1. B-P: 1. B-R: 1. B-W: 1. BACKUP 1. BLOCK-READ: 1. BLOCK-WRITE: 1. BLOCK-ALLOCATE: 1. BLOCK-EXECUTE: 1. BLOCK-FREE: 1. BUFFER-POINTER 1. BSAVE 1. CATALOG 1. CHDIR 1. CLOSE 1. DSKI$ 1. DSKO$ 1. PRINT USING 1. PUT 1. CMD 1. COLLECT 1. COPY 1. CSAVE 1. CSAVEM 1. DCLOSE 1. DELETE 1. DIR 1. DIRECTORY 1. DOPEN 1. DRIVE 1. DSAVE 1. DSKINI 1. ERASE 1. FIELD 1. FILES 1. KILL 1. LOF 1. MKDIR 1. MOTOR 1. OPEN 1. OUTPUT 1. PR # 1. PRINT 1. PUT # 1. PRINT # 1. PRINT #USING 1. RENAME 1. RMDIR 1. SAVE 1. SAVEM 1. SCRATCH 1. SYSTEM 1. TAB 1. TAB( 1. STORE 1. UNLOAD 1. WRITE 1. WRITE# 1. External Communications 1. AUDIO 1. CMD 1. COM 1. MOTOR 1. OFF 1. ON 1. OUT 1. PEEK 1. POKE 1. PR # 1. WAIT

Crider says that he is covering BASIC for "Apple, IBM PC, IBM PCjr, Commodore 64, TRS-80 Model III, and TRS-80 Color Computer". However, I noticed that his list of all 551 reserved words one should avoid using in identifiers as they are defined in one BASIC or another actually includes more keywords than were listed in the categories above. They appear to be from Atari BASIC (e.g., PTRIG) and Tektronix 4051 Graphic System BASIC Language (e.g., BAPPEN).

```BASIC ABS ACS ACSD ACSG ADR AND APPEND ARCOS ARCSIN ARCTAN ASC ASCII ASN ASND ASNG AT ATAN ATN ATND ATNG AUDIO AUTO AXIS B-A: B-F: B-P: B-R: B-W: BACKUP BAPPEN BASE BEEP BGET BLOAD BLOCK-ALLOCATE: BLOCK-EXECUTE: BLOCK-FREE: BLOCK-READ: BLOCK-WRITE: BUFFER-POINTER BOLD BPUT BREAK BRIGHTNESS BSAVE BUTTON BYE CALL CATALOG CDBL CH CHAIN CHANGE CHAR CHAR$ CHARSIZE CHDIR CHR CHR$ CINT CIRCLE CLEAR CLG CLK$ CLK CLOAD CLOADM CLOCK CLOG CLOSE CLR CLRDOT CLS CMD CO CODE COLLECT COLOR COLOR= COM COMMON CON CONCAT CONSOLE CONT COPY COS COSD COSG COSH COUNT CSAVE CSAVEM CSH CSNG CSRLIN CUR CVD CVI CVN CVS CVT$% CVT$F CVT%$ CVTF$ DASH DAT DATA DATE$ DCLOSE DEBUG DEF DEFDBL DEFFN DEFINT DEF SEG DEFSNG DEFSTR DEFUSR DEG DEGREE DEL DELETE DET DIGITS DIM DIR DIRECTORY DLOAD DLOADM DMS DOPEN DOS DOT DRAW DRAWTO DRIVE DS DSAVE DSKI$ DSKINI DSKO$ DSP EDIT ELSE END ENTER ENVIRON ENVIRON$ EOF EQ EQV ERASE ERDEV ERDEV$ ERL ERR ERRL ERRN ERROR ERRS$ EXAM EXCHANGE EXEC EXIT EXP EXT FDIM FETCH FGET FIELD FIF FILE FILES FILL FIN FIND FINPUT FIX FLASH FLOW FLT FMT FN FNEND FONT FOR FORMAT FOUT FPRINT FPUT FRAC FRE FREE FUNTIL FUZZ GE GET GET# GIN GO GO TO GOODBYE GOSUB GOSUB-OF GOT GOTO GOTO-OF GR GRAD GRAPHICS GT HCOLOR HCOLOR= HEADER HEX$ HGR HGR2 HIMEM: HLIN HLIN-AT HOME HPLOT HSCRN HTAB IF IF-GOT IF-GOTO IF-LET IF-THE IF-THEN IF-THEN-ELSE IMAGE IMP IN# INCH INCHAR INDEX INIT INKEY$ INP INPUT INPUT# INPUT$ INPUT1 INPUTLINE INSTR INT INTER$ INVERSE IOCTL IOCTL$ JOYSTK KEY KEY$ KILL LE LEFT LEFT$ LEN LET LGT LI LIN LINE LINE INPUT# LINEINPUT LINK LINPUT LIS LIST LLIST LN LOAD LOADM LOC LOCATE LOF LOG LOG10 LOGE LOMEM: LPOS LPRINT LPRINTUSING LSET LT M-E: M-R: M-W: MAN MARK MAT * MAT + MAT - MAT = MAT CON MAT IDN MAT INPUT MAT INV MAT PRINT MAT READ MAT TRN MAT ZER MAX MDD MEM MEMORY-EXECUTE: MEMORY-READ: MEMORY-WRITE: MERGE MID MID$ MIN MKD$ MKDIR MKI$ MKN$ MKS$ MOD MONITOR MOTOR MPY MTPACK NAME NE NEW NEX NEXT NOFLOW NOISE NORMAL NOT NOTE NOTRACE NULL NUM NUM$ OCT$ OFF OLD ON ON ERR GOTO ON ERROR GOTO ON-GOSUB ON-GOT ON-GOTO ONERR OPEN OPTION OPTION BASE OR OUT OUTPUT PADDLE PAGE PAINT PALETTE PALETTE USING PAUSE PCLEAR PCLS PCOPY PDL PEEK PEN PI PIN PLAY PLOT PMAP PMODE POINT POINTER POKE POLL POP POS POSITION POSN PPOINT PR# PRECISION PRESET PRI PRINT PRINT #USING PRINT AT PRINT USING PRINT # PRINT @ PSET PTR PTRIG PUT PUT# RAD RADIAN RAN RANDOM RANDOMIZE RBYTE RDRAW REA READ READ# RECALL RECORD REM REMARK REN RENAME RENUM RENUMBER REP REPEAT$ RES RESET RESTORE RESUME RESUME NEXT RET RETURN RIGHT RIGHT$ RMDIR RMOVE RND ROT= ROTATE ROW RSET RU RUN SAVE SAVEM SCALE SCALE= SCR SCRATCH SCREEN SCRN SCRN( SECRET SEG SEG$ SET SETCOLOR SETDOT SGET SGN SHELL SHLOAD SHUT SIN SIND SING SINH SKIPF SLEEP SNH SORT SOUND SPA SPACE SPACE$ SPC SPC( SPEED= SPUT SQR SQRT ST STATUS STE STEP STICK STO STOP STORE STR STR$ STRIG STRING STRING$ STUFF SUB SUBEND SUM SWAP SYS SYSTEM TAB TAB( TAN TAND TANG TANH TAPPEND TEXT THE THEN TI TI$ TIM TIME TIME$ TIMER TLIST TLOAD TNH TO TOP TRACE TRACE OFF TRACE ON TRAP TROFF TRON TSAVE TYP TYPE UNLOAD UNTIL USER USING USR VAL VARPTR VARPTR$ VERIFY VIEW VIEWPORT VLIN VLIN-AT VPOS VTAB WAIT WBYTE WEAVE WEND WHILE WIDTH WINDOW WRITE WRITE# XDRAW XIO XOR XPLOT XRA ```
hackerb9 commented 1 year ago

I have extended the changes I showed here so that derived modes can add and remove words from the parent mode instead of redefining the entire list. As an example, I have derived a TRS-80 Model 100 mode from the TRS-80 mode using just the differences: removing a few keywords and adding others. This is just to demonstrate how it could work. I do not know that there actually needs to be a Model 100 mode that is separate from the general TRS-80 mode.

You can see the changes at https://github.com/hackerb9/basic-mode/tree/dervish

The main difference was postponing the regexp-opt until basic-mode-initialize was called.

Open questions:

  1. Should there actually be specific versions for minor tweaks? It seems like having fewer submodes will make it easier to use basic-mode.el.
  2. Did I set the strings correctly using defvar-local?
  3. Will a submode ever want to override the actual regexp instead of just the set of strings that generates the regexp? That is not currently possible in my changes.
Outdated comment, moved to issue #21 1. My categorization of the different keywords/builtins/functions is haphazard. For example, is TIME$, which is the current time of day, a "function"? That's essentially what it is, although it uses the semantics of a variable for setting/getting. And then there's the TIME command which sets up a certain routine to run at a given time. It seems like that's more "control-flow", and so I put it in the basic-keywords instead of basic-builtins list. But, then again, I'm not even sure that that's what basic-keywords is for. [I just noticed that the [QuickBasic manual](https://archive.org/details/Microsoft_QuickBASIC_4.5_2nd_Edition_Manual/page/n116/mode/1up) refers to `TIME$` as an "intrinsic function", so it sounds like I was maybe right in having it highlighted as a function.]
hackerb9 commented 1 year ago

Here is an example of how one might derive a new mode from a previous one by changing only a few parts. While I still don't know if this will be useful for you, as the developer of basic-mode, I realized that holding off on the regexp-opt is a very good thing for users, particularly anybody who wants to extend basic-mode with their dialect's idiosyncrasies.

(define-derived-mode basic-m100-mode basic-trs80-mode "Basic[M100]"
  "Programming mode for BASIC for the TRS-80 Model 100.
It is a test of inheriting and modifying the TRS-80 mode. 
Derived from `basic-trs80-mode'."

  (mapcar (lambda (x) (delete x basic-functions))
          '("mem" "point" "set" "random" "reset" "usr"))
  (nconc basic-functions
         '("csrlin" "date$" "day$" "eof" "himem"
           "instr" "input$" "maxfiles" "maxram"
           "rnd" "space$" "time$"))

  (mapcar (lambda (x) (delete x basic-builtins))
          '("auto" "delete" "system" "troff" "tron"))
  (nconc basic-builtins 
         '("beep" "cloadm" "close" "csave" "csavem"
           "files" "ipl" "key" "kill" "lcopy" "line"
           "load" "loadm"
           "menu" "merge" "motor" "name" "open"
           "power" "preset" "print @" "pset" "save" "savem"
           "screen" "sound"))

  (nconc basic-keywords
         '("com" "mdm" "on com gosub" "on error goto"
           "on key gosub" "on mdm gosub" "on time$"
           "runm" "time"))

  (basic-mode-initialize))

Of course, for this to work, you'd need the derivative-mode modifications to basic-mode.el found in my dervish tree.


Side note: The Model 100 keywords and builtins should probably get added to the main basic-mode lists instead of being a separate submode as I notice that many of them are also listed in the QuickBasic 4.5 manual.

dykstrom commented 1 year ago

I have now created a PR for this issue. It can be considered as a first draft. There is work left to do. Documentation needs to be updated. And the QB4.5 mode needs to be finished.

The changes build mostly on your examples. I have updated how variables are set because I experienced some problems with nconc.

I added in the TRS modes even though they were examples. If you want them updated or removed, let me know.

I also removed some keywords and types. defbool does not seem to exist other than in my imagination. I also removed the boolean type and true and false constants because they do not exist in classic BASIC dialects. Maybe there is something more to remove from the standard basic-mode?

hackerb9 commented 1 year ago

By the way, since the standard basic-mode is getting stripped down to just the core, I suggest that .BAS files should open up in a submode (basic-generic-mode?) that is widely encompassing. That way people new to basic-mode won't have to do anything to get it to work (usually).

Generic mode could just be an alias for QB45, which seems fairly broad, but I'm imagining it as the union of all the other modes, so that no matter whether a person is typing VARPTR, USR, or PTRIG it'll get fontified. For the most part, the extra syntax highlighting won't hurt as it only happens if those keywords are actually used (e.g., TRUE = -1) .

dykstrom commented 1 year ago

A basic-generic-mode as the default is a good idea. That makes the main basic-mode just a template for sub modes.