Skip to content

farique1/CoCo-Basic-Dignified

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

⛔️ DEPRECATED

This repo is part of a series of tools that were integrated into the Basic Dignified Suite. The newer versions are available there. This version is out of date and will no longer be supported here.

This repo now exists for archival purpose only.


CoCo Basic Dignified

Beta Version

CoCo Basic Dignified is a 'dialect' of CoCo Basic using modern coding style and standards that can be composed on any text editor and converted to the traditional CoCo Basic to be executed.

Dignified Basic can be written without line numbers, can be indented using TABs or spaces and have broken lines, can use variables with long names, have macros defined and external files included, has a kind of function construct, can use line toggles, true and false statements, compound arithmetic operators and more.

There is a syntax highlight, build system and comment setting available for Sublime Text 3 working with the CoCo Basic and CoCo Basic Dignified rules to help improve the overall experience.

CoCo Basic Dignified
CoCo Badig in action

Please, be aware that CBD and its tools are by no mean finished products and are sort of expected to misbehave.

The Program

Standard behaviour

CoCo Basic Dignified reads a text file containing the Dignified code and write back traditional CoCo Basic in ASCII and/or tokenized format.
The Dignified code uses a .bad extension, the ASCII is given .asc and the tokenized is .bas.

Toolshed's decb must be present for tokenized output.

Unlike the traditional CoCo Basic, instructions, functions and variables must be separated by spaces from alphanumeric characters. The CoCo Syntax Highlight will reflect this and there are several settings to conform the spacing when the conversion is made.
Dignified code should always end with a blank line.

Features and usage

Run with: cocobadig.py <source> [destination] args...

From now on, when showing code, usually the first excerpt is Dignified, followed by the program call and the traditional Basic output. For the sake of clarity only the arguments relevant to the current topic will be shown on the program call.

  • Directing the code flow is done with labels.
    Labels are created using curly brackets {like_this} and can be used alone on a line to receive the code flow or on a branching (jump) instruction to direct the flow to the corresponding line label. They can only have letters, numbers and underscore and they cannot be only numbers. {@} points to its own line (abraço, Giovanni!).
    A special kind of label can be used to create a concise closed loop. It is opened with label{ and closed with an } (alone on a line). The opening label works like any regular label and the closing one will be replaced with GOTO {label}. Loop labels can be nested.
    Labels marking a section are called Line Labels and labels on jump instructions (GOTO, GOSUB, etc) are called Branching Labels. Closed loop labels are calle Loop Labels.
    A comment can be used after a line label using a ' (REM is not supported) and it will stay after the conversion as an addition to the label or alone on the line, depending on the label conversion argument used.
    Labels not following the naming convention, duplicated line labels, labels branching to inexistent line labels and loop labels not closed will generate an error and stop the conversion. Labels with illegal characters are higlighted when using the CoCo Syntax Highlight.

     {hello_loop}  
     print "press A to toggle"  
     if inkey$ <> "A" then goto {@}  
     hello{  
     	print "hello world"  
     	if inkey$ = "A" then goto {hello_loop}  
     }  

    cocobadig.py test.bad

     10 PRINT "press A to toggle"  
     20 IF INKEY$<>"A" THEN 20  
     30 PRINT "hello world"  
     40 IF INKEY$="A" THEN 10  
     50 GOTO 30  
  • Defines are used to create aliases on the code that are replaced when the conversion is made. They are created with define [name] [content] where the content will replace the [name]. There can be as many as needed and there can be several on the same line, separated by commas: define [name1] [content1], [name2] [content2], [name3] [content3].
    Duplicated defines will give an error and stop the conversion.
    A [] inside a define content is a define variable and will be substituted by an argument touching the closing define bracket. If there is content inside the variable bracket, the text will be used as default in case no argument is found.
    For instance, using DEFINE [var] [poke 100,[10]], a subsequent [var]30 will be replaced by poke 100,30; [var] alone will be replaced by poke 100,10.
    Variables passed to defines must be touching the closing bracket and be followed by an space, indicating the end of the variable content. A define that takes a variable but does not want one must also be followed by an space after the closing bracket, indicating no variable is given.
    Defines can be used as variables for other defines but they cannot be the same and a define with a variable as a variable for a define is a very finicky situation and should be used cautiously.

    [...] constructs inside REM, DATA or "" will be identified as DEFINEs.

     define [ifa] [if a$ = ], [enter] [chr$(13)]  
     define [pause][if inkey$<>[" "] goto {@}]  
    
     [ifa]"2" then print "dois"  
     [ifa]"4" then print "quatro"  
     [pause][enter]  

    cocobadig.py test.bad

     10 IF A$="2" THEN PRINT "dois"  
     20 IF A$="4" THEN PRINT "quatro"  
     30 IF INKEY$<>CHR$(13) GOTO 30  
  • Long name variables can be used on the Dignified code. They can only have letters, numbers and underscore and cannot be only numbers or have less than 3 characters. As is the case with the instructions, they must be separated by empty spaces from other alphanumeric characters or commands (the non alpha characters ~, $ on the variables can touch other commands).
    When converted they are replaced by an associated standard two letter variables. They are assigned on a descending order from ZZ to AA and single letters and letter+number are never used. Each long name is assigned to a short name independent of type, so if variable1 becomes XX so will variable1$ become XX$
    To use these variables they must be declared, there are two ways to do that:
    -- On a declare line, separated by commas: declare variable1, variable2, variable3.
    -- In place, preceded by an ~: ~long_var = 3.
    An explicit assignment between a long and a short name can be forced by using a : when declaring a variable (only on a declare command): declare variable:va will assign VA to variable. Forcing declaration of the same variable to different short or long names will cause an error.
    As variables are assigned independent of type, explicit type character ($) cannot be used on a declare line.
    When a long name variable is declared, a new two letter variable is assigned to it, redeclaring the variable will maintain the previous assignment. A warning will be given for repeated declarations of the same variable. Variables declared on declare lines have precedence over ~ declarations.
    After it is assigned, a long name variable can be used without the ~ but a text without the ~ that has not been previously declared as a variable will be taken as a normal Basic instruction and will probably give a syntax error.
    Reserved CoCo Basic commands should not be used as variables names as they will be assigned and converted.
    Traditional one and two letters variables can be used normally alongside long names ones, just be aware that the letters at the end of the alphabet are being used up and they may clash with the hard coded ones.
    The conversion (and the CoCo Syntax Highlight) will catch illegal variables when declaring but it is not always perfect on ~ so keep an eye on them.
    A summary of the long and short name associations can be generated on REMs at the end of the converted code.

     declare food, drink:dk  
     if food$ = "cake" and drink = 3 then _  
     	~result$ = "belly full":  
     	~sleep = 10  
     endif  

    cocobadig.py test.bad

     10 IF ZZ$="cake" AND DK=3 THEN ZY$="belly full":ZX=10  

    Optional, with cocobadig.py test.bad -vs

     20 ' ZZ-food, ZY-result, ZX-sleep, DK-drink  
  • Proto-functions can be used to emulate the use of modern function definition and calls.
    They are defined with func .functionName(arg1, arg2, etc) and must end with a return alone on a line.
    The arguments can have default values as in func .function(arg$="teste") and the return can have return variables like return ret1, ret2, etc. The return and can also have a : before it or at the end of the line above to join them on the conversion.
    The functions are called with .functionName(arg1, arg2, etc) and can be assigned to variables like ret1, ret2 = .functionName(args). They can be separated by : as usual and can also come after a THEN or ELSE: if a=1 then .doStuff() else .dontDoStuff().
    The number of arguments and return variables must be the same and explicitly given except for an argument with a default value, in this case the default will be used if a empty space is passed on that position. To use a function call with empty argument positions and preserve the variable value, the default must be the variable name, eg: func .applyColor(color1=color1, color2=color2) can be called with .applyColor(10,), .applyColor(,20) or even .applyColor(,) and the color not specified will be maintained.
    Proto-functions cannot have multiple conditional returns (only the first return alone on a line will be parsed for variables and will signal the end of the function definition) but the conditional value can be established a priori in a variable on an IF THEN ELSE that can be passed on to the return.
    Obviously there are no local variables on the CoCo Basic (which can limit the usefulness of the proto-functions) but this can be simulated by using unique named variables inside the functions. Proto-functions can also be useful to apply different results to different variables at different points in the code.
    When converting to traditional Basic, function definitions are essentially labels so they cannot have the same name as one of them. A function call is a gosub to that label with the variables assigned before and after it accordingly.
    If the arguments or return variable are the same between function calls or definitions they will not be equated on the conversion to avoid unnecessary repetition like A$=A$. The same way, an empty argument location with a default variable equated to itself will not be converted.
    Different from a normal function, func definitions will not deviate the code flow from itself so they must be placed at a point unreachable by the normal code flow.
    Everything on the same line as the function definition will be converted to a comment. ## comments will be ignored as usual.
    Upon conversion, function definitions and calls will follow the label configurations.

    Some known limitations:
    Function recursion (called inside itself) is not recommended.
    Proto-function definitions and calls cannot have the _ line separator.
    .xxx() constructs inside REM, DATA or "" will be identified as function calls.

     ch$ = .getUpper("a")  
     print ch$  
     end  
     func .getUpper(up$)  
     	ch = asc(up$) - 32  
     return chr$(ch)  

    cocobadig.py test.bad

     10 UP$="a":GOSUB 40:CH$=CHR$(CH)  
     20 PRINT CH$  
     30 END  
     40 ' {getUpper}  
     50 CH=ASC(UP$)-32  
     60 RETURN  
  • A single traditional CoCo Basic line can span several lines on Dignified code using : or _ breaks. They are then joined when converting to form a single line.
    Colons : can be used at the end of a line, to join the next one, or at the beginning of a line, to join it to the previous one, and are retained when joined. Their function is the same as on the traditional CoCo Basic, separating different instructions.
    Underscores _ can only be used at the end of a line and they are deleted when the line is joined. They are useful to join broken instructions like IF THEN ELSE, long quotes or anything that must form a single command on the converted code.
    endifs can be used (not obligatory, just Python it) but are for cosmetical or organisational purpose only. They must be alone on their lines and are removed upon conversion without any validation regarding their IFs. If they are not alone and are not part of a DATA, REM or QUOTE they will generate a warning but will not be deleted. endifs that are part of any of the previous commands but are alone on a line due to a line break will be deleted.
    Numbers at the start of a line will be removed, generate a warning. Numbers at the beginning of a line after an underscore break _ will be preserved but numbers at the beginning of a line after a colon : break will be removed even if it is part of a REM (there is no need to break a REM line with an : anyway)
    All of the warning situations above are highlighted with the CoCo Syntax Highlight.

     if a$ = "C" then _  
     	for f = 1 to 10:  
     		print f:  
     	next  
     	:print "done"  
     	:end  
     endif  

    cocobadig.py test.bad

     10 IF A$="C" THEN FOR F=1 TO 10:PRINT F:NEXT:PRINT "done":END  
  • The Dignified code can use exclusive comments ## that are stripped during the conversion.
    Regular REMs are kept. A tenacious bug (called My Own Regex Incompetency) prevents the ## from being removed if there are any " after it. (this is so ## are not removed inside quotes.)
    Block comments can also be used, they can be '' or ###, the text inside the first one will be kept while the text inside the later will be removed. They will toggle the block if they are alone on a line, open it if at the start or close when at the end of a line. Regular ## lines inside a '' block will be removed.

     ## this will not be converted  
     rem this will  
     ' this also will  
     ###  
     This will go  
     ###  
     ''  
     And this stays  
     ''  

    cocobadig.py test.bad

     10 REM this will  
     20 ' this also will  
     30 ' And this stay  
  • Parts of the code can be tagged with line toggles to be removed on demand when converted. They have the format #name where name can be any combination of letters numbers and _ and they can be kept by simply adding a keep #name1 #name2... on a line before them. keep can take none, one or more toggles on the same line, separated by spaces.
    Toggles are used at the start of a line (#1 print "Hello") and also can be alone on a line to denote the start and end of a whole section to be removed, just like block comments. They can be useful to debug different code snippets without having to comment and uncomment them every time. Block toggles can be nested but cannot be interleaved. If they are not closed a warning will be given.

     keep #2  
     #1 print "this will not be converted"  
     #2 print "this will"  
     print "this also will"  
     #3  
     print "This will go"  
     print "And so will this"  
     #3  

    cocobadig.py test.bad

     10 PRINT "this will"  
     20 PRINT "this also will"  
  • An external piece of Dignified code can be inserted anywhere using the include command.
    include "code.bad" will insert the contents of code.bad exactly where the include was and can even have its lines joined with the main code by using : or _ on any of the files.

     print "This is the main file."  
     '  
     include "help.bad"  
     '  
     print "This is the main file again."  

    cocobadig.py test.bad

     10 PRINT "This is the main file."  
     20 '  
     30 PRINT "This is a helper code."  
     40 PRINT "Saved on another file."  
     50 '  
     60 PRINT "This is the main file again."  
  • True and false statements can be used with numeric variables, they will be converted to -1 and 0 respectively and their variables can be treated as true booleans on ifs and with not operators.

     ~variable = true  
     ~condition = false  
     if condition then variable = not variable  

    cocobadig.py test.bad

     10 ZZ=-1  
     20 ZY=0  
     30 IF ZY THEN ZZ=NOT ZZ  
  • Shorthand and compound arithmetic operators (++, --, +=, -=, *=, /=, ^=) can be used and will be converted to normal CoCo Basic operations.
    If the unpack operator (-uo) argument is used, the conversion will try to preserve the spaces used with the operators.

     PX++ :PY --  
     LO+=20 :DI -= 10  

    cocobadig.py test.bad -uo

     10 PX=PX+1:PY = PY - 1  
     20 LO=LO+20:DI = DI - 10  

Configurable arguments

Arguments can be passed on the code itself, on CoCoBadig.ini or through the command line with each method having a priority higher than the one before.

Files

  • Source file
    The Dignified code file to be read.
    ini: source_file = [source] arg: <source>

  • Destination file
    The traditional CoCo Basic code file to be saved.
    ini: destin_file = [destination] args: [destination]

    If no name is given, the destination file will be the source with a .bas or .asc extension accordingly.

  • Output Format (tonekized format is only available if decb is present)
    The format of the converted output.
    ini: output_format = [t,a,b] arg: -of <t,a,b> Default: b
    Both formats are exported by default (b). A single format can be forced by using a (ASCII) or t (tokenized).

    The ASCII format is always exported and will only be deleted if the tokenized is successfully saved.

  • decb Path
    The path to the decb file.
    ini: decb_filepath = []

Numbering

  • Starting line number
    The number of the first line.
    ini: line_start = [#] arg: -ls [#] Default: 10

  • Line step value
    The line number increment amount.
    ini: line_step = [#] arg: -lp [#] Default: 10

  • Add leading zeros
    Line numbers can be padded with zeroes.
    ini: leading_zeros = [true,false] arg: -lz Default: false

{do_stuff}  
	print "Say something"  
	do$ = "Do something"  
goto {do_stuff}  

cocobadig.py test.bad -ls 5 -lp 5

5 ' {do_stuff}  
10 PRINT "Say something"  
15 DO$="Do something"  
20 GOTO 5  

cocobadig.py test.bad -ls 1 -lp 50 -lz

001 ' {do_stuff}  
051 PRINT "Say something"  
101 DO$="Do something"  
151 GOTO 1  

Labels
Labels are removed from the code as default but there are some options to left them for debugging or readability purposes.

  • Label conversions
    Line labels can be left on the code on a REM line (0) or their names can be removed, leaving only a blank REM (and any comments they had) on its place (1). The default (2) removes the line altogether. If the lines are kept, branching labels points to them. If they are removed, the code flow is directed to the line mediately after where the label was.
    ini: handle_label_lines = [0,1,2] arg: -ll <0,1,2> Default: 2

  • Show branching labels
    Add a :rem at the end of lines with branching instructions showing the name of the labels used, making it easier to visualize the flow on the converted code.
    ini: show_branches_labels = [true,false] arg: -sl Default: false

{print_result}  
	print "Result"  
if r$ = "Result" goto {print_result} else goto {@}  

cocobadig.py test.bad -ll 2

10 PRINT "Result"  
20 IF R$="Result" GOTO 10 ELSE GOTO 20  

cocobadig.py test.bad -ll 1

10 '  
20 PRINT "Result"  
30 IF R$="Result" GOTO 10 ELSE GOTO 30  

cocobadig.py test.bad -ll 0 -sl

10 ' {print_result}  
20 PRINT "Result"  
30 IF R$="Result" GOTO 10 ELSE GOTO 30:' {print_result} {SELF}  

Blank lines
Blank lines are stripped from the source by default but they can also be left on the converted code.
Blank lines after Dignified commands (define, declare,...) are always removed.
Extra lines can also be added close to labels for clarity and organization.

  • Keep blank lines
    Do not erase blank lines on the converted code, convert them to rems.
    ini: keep_blank_lines = [true,false] arg: -bl Default: false

  • Label gap
    Add a blank line before or after a label. (has no effect if the line labels were removed)
    b Before, a after, ba before and after.
    ini: label_gap = [b,a,ba] arg: -lg <b,a,ba> Default: none

{print_result}  
	PRINT "Result"  
  
	GOTO {print_result}  

cocobadig.py test.bad -bl -lg a

10 ' {print_result}  
20 '  
30 PRINT "Result"  
40 '  
50 GOTO 10  

Spacing
Spacing and indentation are automatically removed from the converted code.
This behaviour can be configured, however, as follows.

  • Spaces around :
    Add an space before or after the instruction separating character :. Has no effect if keeping original spacing.
    b Before, a after, ba before and after.
    ini: colon_spaces = [b,a,ba] arg: -cs <b,a,ba> Default: none

  • Amount of general spacing
    The conversion automatically strips all blanks (spaces and TABs) from the code (default 0). This can be changed to 1 space throughout (1) or the original spacing used can be kept using k:
    ini: general_spaces = [0,1,k] arg: -gs <0,1,k> Default: 0

  • Unpack operators
    All spaces surrounding mathematical operators and punctuation (+-=<>*/^\.,;) are stripped by default. They can be kept as general spaces using:
    ini: unpack_operators = [true,false] arg: -uo Default: false

for f = 10  to 7  :read a:print a  : next  
for f = 0   to 9  :read a:print a  : next  
for f = 100 to 300:read a:print a+1: next  

cocobadig.py test.bad -gs 0 -cs b

10 FORF=10TO7 :READA :PRINTA :NEXT  
20 FORF=0TO9 :READA :PRINTA :NEXT  
30 FORF=100TO300 :READA :PRINTA+1 :NEXT  

Indentation
CBD, by default, also strips all indentation used on the Dignified code. This can be controlled with.

  • Keep indentation
    Maintain the original indentations. TABs are converted to a specified number of spaces (2 by default).
    ini: keep_indent = [#] arg: -ki [#] Default: 2
for f = 1 to 200  
	print "Beware"  
	 if f = 100 then print "Middle point reached!"  
next  

cocobadig.py test.bad -ki 3

10 FOR F=1 TO 200  
20    PRINT "Beware"  
30     IF F=100 THEN PRINT "Middle point reached!"  
40 NEXT  

Comments
The conversion uses REMs on certain occasions to keep some original formatting; they can be redefined as such:

  • New REM format
    Sometimes REMs are created by CBD; they are applied as ' (option s) but they can be told to use the regular form (option rem) :
    ini: new_rem_format = [s,rem] arg: -nr <s,rem> Default: s

  • Convert all REMs
    All pre existing REMs (not added by CBD) can be changed to maintain coherence along the converted code. The conversion will conform to the condition above.
    ini: convert_rem_formats = [true,false] arg: -cr Default: false

{start_tutorial}  
	print "What tutorial?"  
	' Nevermid  

cocobadig.py test.bad -nr rem -cr

10 REM {start_tutorial}  
20 PRINT "What tutorial?"  
30 REM Nevermid  

General conversions

  • Convert ? to PRINT
    ? as PRINT are left alone on the conversion, they can be told to become PRINT with:
    ini: convert_interr_to_print = [true,false] arg: -cp Default: false

  • Strip adjacent THEN/ELSE or GOTOs
    CoCo Basic doesn't need both THEN or ELSE and GOTO if they are adjacent. The converted code can be told to strip THEN (option t), GOTO (option g, default) or they can all be left alone (option k)
    ini: strip_then_goto = [t,g,k] arg: -tg <t,g,k> Default: g

{at_last}  
	? "What?"  
	if me$ = "Huh?" then goto {at_last} else goto {i_agree}  
	?:? "Not here."  
{i_agree}  
	? "That is (almost) all folks."  

cocobadig.py test.bad -nc -cp -tg t

10 ' {at_last}  
20 print "What?"  
30 if me$="Huh?" then 10 else 50  
40 print:print "Not here."  
50 ' {i_agree}  
60 print "That is (almost) all folks."  

Misc

  • Long variable summary
    A summary of all the long name variables used on the code along with their associated classic short names can be generated on REM lines at the end of the converted code and the amount shown per line can be set. If used without number argument the amount shown per line is 5.
    ini: long_var_summary = [#] arg: -vs [#] Default: 5

  • Verbose
    Set the level of feedback given by the program.
    0 show nothing, 1 errors, 2 errors and warnings, 3 errors, warnings and steps and 4 errors, warnings, steps and details.
    ini: verbose_level = [#] arg: -vb <#> Default: 3

  • Supress info comments
    Do not add information about CoCo Basic Dignified at the top of the converted code.
    ini: rem_header = [true,false] arg: -nr Default: false

  • Running from build system
    On build systems, like the one in Sublime, errrors and warning messages demand the file name to be on the offending log line. To declutter the log on a run from a console window this information is omitted unless told so.
    arg: -frb Default: false

  • Use the .ini file
    Tells if the .ini file settings should be used or not, allowing it to be disabled without being moved or deleted.
    ini: use_ini_file = [true,false] Default: true

  • Write the .ini file
    Rewites the .ini file in case its is missing.
    arg: -ini Default: false

  • Help
    Help is available using:
    cocobadig.py -h

Technicalities and warnings

Yes, they are one and the same

CoCo Basic Dignfied was made on a Mac OS with Python 2.7.10 and eventually ported to Python 3.8.1.
This was the first Python program I made; it has been through a lot of iterations but it is still a hot mess of ideas, experiments and alternative (bad) coding.
Use with care and caution.

About

⛔️ DEPRECATED <Current version at https://github.com/farique1/basic-dignified>

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages