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.
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 |
|---|
![]() |
Please, be aware that CBD and its tools are by no mean finished products and are sort of expected to misbehave.
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.
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 withlabel{and closed with an}(alone on a line). The opening label works like any regular label and the closing one will be replaced withGOTO {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'(REMis 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.bad10 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 thecontentwill 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, usingDEFINE [var] [poke 100,[10]], a subsequent[var]30will be replaced bypoke 100,30;[var]alone will be replaced bypoke 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 insideREM,DATAor""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.bad10 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 fromZZtoAAand single letters and letter+number are never used. Each long name is assigned to a short name independent of type, so ifvariable1becomesXXso willvariable1$becomeXX$
To use these variables they must be declared, there are two ways to do that:
-- On adeclareline, 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 adeclarecommand):declare variable:vawill assignVAtovariable. 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 adeclareline.
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 ondeclarelines 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 onREMs 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.bad10 IF ZZ$="cake" AND DK=3 THEN ZY$="belly full":ZX=10
Optional, with
cocobadig.py test.bad -vs20 ' 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 withfunc .functionName(arg1, arg2, etc)and must end with areturnalone on a line.
The arguments can have default values as infunc .function(arg$="teste")and thereturncan have return variables likereturn ret1, ret2, etc. Thereturnand 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 likeret1, ret2 = .functionName(args). They can be separated by:as usual and can also come after aTHENorELSE: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 firstreturnalone 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 anIF THEN ELSEthat can be passed on to thereturn.
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 agosubto 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 likeA$=A$. The same way, an empty argument location with a default variable equated to itself will not be converted.
Different from a normal function,funcdefinitions 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 insideREM,DATAor""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.bad10 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 likeIF 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 theirIFs. If they are not alone and are not part of aDATA,REMorQUOTEthey 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 aREM(there is no need to break aREMline 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.bad10 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.
RegularREMs 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.bad10 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
#namewherenamecan be any combination of letters numbers and_and they can be kept by simply adding akeep #name1 #name2... on a line before them.keepcan 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.bad10 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 ofcode.badexactly where theincludewas 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.bad10 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
-1and0respectively and their variables can be treated as true booleans onifs and withnotoperators.~variable = true ~condition = false if condition then variable = not variable
cocobadig.py test.bad10 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 -uo10 PX=PX+1:PY = PY - 1 20 LO=LO+20:DI = DI - 10
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
.basor.ascextension 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 usinga(ASCII) ort(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:-lzDefault: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 aREMline (0) or their names can be removed, leaving only a blankREM(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:remat 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:-slDefault: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 torems.
ini:keep_blank_lines = [true,false]arg:-blDefault:false -
Label gap
Add a blank line before or after a label. (has no effect if the line labels were removed)
bBefore,aafter,babefore 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.
bBefore,aafter,babefore 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 (default0). This can be changed to 1 space throughout (1) or the original spacing used can be kept usingk:
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:-uoDefault: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
REMformat
SometimesREMs are created by CBD; they are applied as'(options) but they can be told to use the regular form (optionrem) :
ini:new_rem_format = [s,rem]arg:-nr <s,rem>Default:s -
Convert all
REMs
All pre existingREMs (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:-crDefault: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
?toPRINT
?asPRINTare left alone on the conversion, they can be told to becomePRINTwith:
ini:convert_interr_to_print = [true,false]arg:-cpDefault:false -
Strip adjacent
THEN/ELSEorGOTOs
CoCo Basic doesn't need bothTHENorELSEandGOTOif they are adjacent. The converted code can be told to stripTHEN(optiont),GOTO(optiong, default) or they can all be left alone (optionk)
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 onREMlines 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 is5.
ini:long_var_summary = [#]arg:-vs [#]Default:5 -
Verbose
Set the level of feedback given by the program.
0show nothing,1errors,2errors and warnings,3errors, warnings and steps and4errors, 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:-nrDefault: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:-frbDefault:false -
Use the
.inifile
Tells if the.inifile 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
.inifile
Rewites the.inifile in case its is missing.
arg:-iniDefault:false -
Help
Help is available using:
cocobadig.py -h
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.
