PRINT columns, line width and WIDTH¶
Real MBASIC 5.21 counts the column of each character it sends to the screen or
to a file. PRINT uses the count for TAB, for the comma zones and for the line
wrap. POS(0) shows the count. The interpreter and the JavaScript output
(--compile-js) follow the same rules. Issues 20 and 23 are the bug reports.
The rules come from measurements of the real interpreter under cpmemu. The
test programs and the real output are in
tests/regression/integration/fixtures/print_columns/. The test is
tests/regression/integration/test_print_columns.py.
Rules for the screen¶
The default width is 80. WIDTH n changes the width. The range is 1 to 255.
The column is 0-based inside the code. POS(0) shows the column plus 1.
- A line wraps late. A line that is exactly full stays as it is until a printable character arrives. A newline then does not leave an empty line.
- An item (a string, or a number with its spaces) that does not fit in the rest of the line goes whole to the next line. A number counts its leading and trailing space. The printer splits an item that is longer than the width at the width. If such an item starts in the middle of a line, it starts on the next line first.
TAB(n)goes to column n (1-based). If n is more than the width, subtract the width until n fits (TAB(90)at width 80 isTAB(10)). If the cursor is already past the target column, a new line starts first. If the cursor is at the target column, nothing moves.TAB(0)isTAB(1).SPC(n)prints n spaces. The count is n modulo the width, for exampleSPC(256)at width 40 prints 16 spaces. A negative count is 0. The spaces wrap one at a time.- A comma goes to the next multiple of 14. A zone that does not fit
completely in the line is not used. A comma in the last zone starts a new
line. At width 80 the last zone starts at column 56, so a comma at column
56 or more starts a new line. When the width is less than 14, no zone
fits and there is no last-zone rule: the comma pads to the next multiple
of 14 and the spaces wrap, so
PRINT 1,2at width 3 prints blank lines. - After INPUT and LINE INPUT the column is 0, because the user typed Enter.
With
INPUT ;(a semicolon after INPUT) the cursor stays after the answer, and the column grows by the length of the answer. WIDTH 255is an infinite width on the screen. Nothing wraps, and an item is never moved to the next line. The column stops at 255 and does not go higher:POS(0)is then 0 (256 as a byte).TAB(n)andSPC(n)use n modulo 256, soTAB(300)goes to column 44. A comma at column 255 or more pads to the next multiple of 14. A comma at a lower column in the last zone still starts a new line. A file with width 255 does wrap.- A tab character moves to the next multiple of 8. A carriage return sets the column to 0. A backspace moves left. The other control characters do not move the cursor.
Rules for files¶
A file has a width of 255 and these rules are different:
- An item is split at the width. It does not go whole to the next line.
- A comma pads to the next multiple of 14 and wraps. There is no last-zone rule.
- TAB and SPC follow the screen rules.
Where the code is¶
src/print_column.pyhas the rules (column, wrap, TAB, comma).Interpreter.execute_printlays out each item from the real column. It evaluates the items one at a time, soPOS(0)sees the items before it.ColumnTrackerinsrc/iohandler/base.pykeeps the cursor column of each output handler. It wrapsoutput,input,input_line,clear_screenandlocateof every subclass, so the CLI, curses, Tk, web and capturing handlers all track the column. A handler that does not use these methods to draw text must updatecolumnitself. The handler haswidthtoo.Interpreter._screen_line_endedsets the column to 0 when the program receives a keyboard line, and when an interpreter starts. The UIs read INPUT answers, so the interpreter does not callinput()of the handler.- A file keeps its column in
file_info['col']. - The JavaScript runtime has
_col,_width,_wrap_textand_print_list(JS_PRINT_RUNTIMEinsrc/codegen_js_backend.py). A PRINT item that reads POS is a function, so the runtime evaluates it when its turn comes.
Differences that remain¶
- The curses, Tk and web UIs do not check the column before they show an error message. The message can follow the text of a PRINT that ended with a semicolon on the same line. The CLI starts a new line first, as real MBASIC does. The "Division by zero" message that does not stop the program follows the text on the same line, as in real MBASIC.
- The interpreter writes a newline as one
\n. Real MBASIC writes CR LF and an LF alone does not reset the column.PRINT "ab";CHR$(10);"c"gives POS 4 in real MBASIC and 2 here. - A tab character in a string goes to the terminal unchanged. Real MBASIC sends spaces. The column is the same.
- In a file, real MBASIC counts one column less after a wrap at column 255. This affects TAB and POS-like layout in lines longer than 255 characters.
- Real MBASIC accepts
WIDTH 0. This implementation gives an error. - The prompt of INPUT does not wrap at the width.
WIDTH LPRINT nandWIDTH #n, ware not parsed, and LPRINT does not exist.WIDTH n, devicesets the width and then gives Syntax error, as the real binary does.- The CLS statement does not clear the screen, so it does not touch the column.
- PRINT USING does not wrap. It updates the column of the screen or file.
Real MBASIC wraps the output of PRINT USING at the width, and the
whole-item rule applies to each field. At width 30,
PRINT USING "#####.##";1.5;2.5;3.5;4.5;5.5;6.5breaks the line inside the fifth field in real MBASIC. Here the output is one line of 48 characters. A string field wider than the rest of the line also keeps its trailing spaces on the wrapped line in real MBASIC. - When an error stops a PRINT in the middle, the items before the error appear, as in real MBASIC. Real MBASIC then starts a new line before it prints the error message. The error messages of this implementation do not check the column, so the message follows the text on the same line.