{"id":32955325,"url":"https://github.com/richpl/PyBasic","last_synced_at":"2025-11-17T16:02:15.638Z","repository":{"id":40543493,"uuid":"156002218","full_name":"richpl/PyBasic","owner":"richpl","description":"Simple interactive BASIC interpreter written in Python","archived":false,"fork":false,"pushed_at":"2025-11-08T16:26:36.000Z","size":426,"stargazers_count":213,"open_issues_count":2,"forks_count":56,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-11-08T18:14:53.632Z","etag":null,"topics":["basic","basic-programming-language","interpreter","programming-language","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/richpl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-11-03T16:33:46.000Z","updated_at":"2025-11-08T16:26:39.000Z","dependencies_parsed_at":"2024-10-25T18:19:47.984Z","dependency_job_id":"8686d42a-3b46-4fff-a0b3-e27153bdd987","html_url":"https://github.com/richpl/PyBasic","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/richpl/PyBasic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richpl%2FPyBasic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richpl%2FPyBasic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richpl%2FPyBasic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richpl%2FPyBasic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/richpl","download_url":"https://codeload.github.com/richpl/PyBasic/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richpl%2FPyBasic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":284911789,"owners_count":27083425,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-17T02:00:06.431Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["basic","basic-programming-language","interpreter","programming-language","python"],"created_at":"2025-11-12T22:00:43.627Z","updated_at":"2025-11-17T16:02:15.631Z","avatar_url":"https://github.com/richpl.png","language":"Python","readme":"# A BASIC Interpreter - Program like it's 1979!\r\n\r\n## Introduction\r\n\r\nA simple interactive BASIC interpreter written in Python 3. It is based heavily on material in the excellent book *Writing Interpreters\r\nand Compilers for the Raspberry Pi Using Python* by Anthony J. Dos Reis. However, I have had to adapt the Python interpreter presented\r\nin the book, both to work with the BASIC programming language and to produce an interactive command line interface. The interpreter\r\ntherefore adopts the key techniques for interpreter and compiler writing, the use of a lexical analysis stage followed by a recursive descent parser\r\nwhich implements the context free grammar representing the target programming language.\r\n\r\nThe interpreter is a homage to the home computers of the early 1980s, and when executed, presents an interactive prompt ('\u003e')\r\ntypical of such a home computer. Commands to run, list, save and load BASIC programs can be entered at the prompt as well as\r\nprogram statements themselves.\r\n\r\nThe BASIC dialect that has been implemented is slightly simplified, and naturally avoids machine specific instructions,\r\nsuch as those concerned with sound and graphics for example.\r\n\r\nThere is reasonably comprehensive error checking. Syntax errors will be picked up and reported on by the\r\nlexical analyser as statements are entered. Runtime errors will highlight the cause and the line number of\r\nthe offending statement.\r\n\r\nThe interpreter can be invoked as follows:\r\n\r\n```\r\n$ python interpreter.py\r\n```\r\n\r\nAlthough this started of as a personal project, it has been enhanced considerably by some other Github users. You can see them in the list of contributors! It's very much a group endeavour now.\r\n\r\n## Operators\r\n\r\nA limited range of arithmetic expressions are provided. Addition and subtraction have the lowest precedence,\r\nbut this can be changed with parentheses.\r\n\r\n* **+** - Addition\r\n* **-** - Subtraction\r\n* **\\*** - Multiplication\r\n* **/** - Division\r\n* **MOD** (or **%**) - Modulo\r\n\r\n```\r\n\u003e 10 PRINT 2 * 3\r\n\u003e 20 PRINT 20 / 10\r\n\u003e 30 PRINT 10 + 10\r\n\u003e 40 PRINT 10 - 10\r\n\u003e 50 PRINT 15 MOD 10\r\n\u003e RUN\r\n6\r\n2.0\r\n20\r\n0\r\n5\r\n\u003e\r\n```\r\n\r\nAdditional numerical operations may be performed using numeric functions (see below).\r\n\r\nNot also that '+' does extra duty as a string concatenation operator, while '*' can be used to repeat strings.\r\n\r\n## Commands\r\n\r\nPrograms may be listed using the **LIST** command:\r\n\r\n```\r\n\u003e LIST\r\n10 LET I = 10\r\n20 PRINT I\r\n\u003e\r\n```\r\n\r\nThe list command can take arguments to refine the line selection listed\r\n\r\n`LIST 50` Lists only line 50.\r\n\r\n`LIST 50-100` Lists lines 50 through 100 inclusive.\r\n\r\n`LIST 50 100` Also Lists lines 50 through 100 inclusive, almost any delimiter\r\nworks here.\r\n\r\n`LIST -100` Lists from the start of the program through line 100 inclusive.\r\n\r\n`LIST 50-` Lists from line 50 to the end of the program.\r\n\r\nA program is executed using the **RUN** command:\r\n\r\n```\r\n\u003e RUN\r\n10\r\n\u003e\r\n```\r\n\r\nA program may be saved to disk using the **SAVE** command. Note that the full path must be specified within double quotes:\r\n\r\n```\r\n\u003e SAVE \"C:\\path\\to\\my\\file\"\r\nProgram written to file\r\n\u003e\r\n```\r\n\r\nThe program may be re-loaded from disk using the **LOAD** command, again specifying the full path using double quotes:\r\n\r\n```\r\n\u003e LOAD \"C:\\path\\to\\my\\file\"\r\nProgram read from file\r\n\u003e\r\n```\r\nWhen loading or saving, the .bas extension is assumed if not provided.  If you are loading a simple name (alpha/numbers only) and in the working dir, quotes can be omitted:\r\n\r\n```\r\n\u003e LOAD regression\r\n```\r\nwill load regression.bas from the current working directory.\r\n\r\nIndividual program statements may be deleted by entering their line number only:\r\n\r\n```\r\n\u003e 10 PRINT \"Hello\"\r\n\u003e 20 PRINT \"Goodbye\"\r\n\u003e LIST\r\n10 PRINT \"Hello\"\r\n20 PRINT \"Goodbye\"\r\n\u003e 10\r\n\u003e LIST\r\n20 PRINT \"Goodbye\"\r\n\u003e\r\n```\r\nThe program may be erased entirely from memory using the **NEW** command:\r\n\r\n```\r\n\u003e 10 LET I = 10\r\n\u003e LIST\r\n10 LET I = 10\r\n\u003e NEW\r\n\u003e LIST\r\n\u003e\r\n```\r\n\r\nFinally, it is possible to terminate the interpreter by issuing the **EXIT** command:\r\n\r\n```\r\n\u003e EXIT\r\nc:\\\r\n```\r\n\r\nOn occasion, it might be necessary to force termination of a program and return to the\r\ninterpreter, for example, because it is caught in an infinite loop. This can be achieved by\r\nusing Ctrl-C to force the program to stop:\r\n\r\n```\r\n\u003e 10 PRINT \"Hello\"\r\n\u003e 20 GOTO 10\r\n\u003e RUN\r\n\"Hello\"\r\n\"Hello\"\r\n\"Hello\"\r\n...\r\n...\r\n\u003cCtrl-C\u003e\r\nProgram terminated\r\n\u003e LIST\r\n10 PRINT \"Hello\"\r\n20 GOTO 10\r\n\u003e\r\n```\r\n\r\n## Programming language constructs\r\n\r\n### Statement structure\r\n\r\nAs per usual in old school BASIC, all program statements must be prefixed with a line number which indicates the order in which the\r\nstatements may be executed. There is no renumber command to allow all line numbers to be modified. A statement may be modified or\r\nreplaced by re-entering a statement with the same line number:\r\n\r\n```\r\n\u003e 10 LET I = 10\r\n\u003e LIST\r\n10 LET I = 10\r\n\u003e 10 LET I = 200\r\n\u003e LIST\r\n10 LET I = 200\r\n\u003e\r\n```\r\n\r\nMultiple statements may appear on one line separated by a colon:\r\n```\r\n\u003e 10 LET X = 10: PRINT X\r\n```\r\n\r\n*NOTE*: Currently inline loops are NOT supported\r\n```\r\n10 FOR I = 1 to 10: PRINT I: NEXT\r\n```\r\nwill need to be decomposed to individual lines.\r\n\r\n### Variables\r\n\r\nVariable types follow the typical BASIC convention. *Simple* variables may contain either strings\r\nor numbers (the latter may be integers or floating point numbers). Likewise *array* variables may contain arrays\r\nof either strings or numbers, but they cannot be mixed in the same array.\r\n\r\nNote that all keywords and variable names are case insensitive (and will be converted to upper case internally by the lexical analyser).\r\nString literals will retain their case however. There is no inherent limit on the length of variable names or string literals,\r\nthis will be dictated by the limitations of Python. The range of numeric values is also dependent upon the underlying\r\nPython implementation.\r\n\r\nNote that variable names may only consist of alphanumeric characters and underscores. However, they\r\nmust all begin with an alphabetic character. For example:\r\n\r\n* *MY_VAR*\r\n* *MY_VAR6$*\r\n* *VAR77(0, 0)*\r\n\r\nare all valid variable names, whereas:\r\n\r\n* *5_VAR*\r\n* *_VAR$*\r\n* *66$*\r\n\r\nare all invalid.\r\n\r\nNumeric variables have no suffix, whereas string variables are always suffixed by '$'. Note that 'I' and 'I$' are\r\nconsidered to be separate variables. Note that string literals must always be enclosed within double quotes (not single quotes).\r\nUsing no quotes will result in a syntax error. \r\n\r\nArray variables are defined using the **DIM** statement, which explicitly lists how\r\nmany dimensions the array has, and the sizes of those dimensions:\r\n\r\n```\r\n\u003e REM DEFINE A THREE DIMENSIONAL NUMERIC ARRAY\r\n\u003e 10 DIM A(3, 3, 3)\r\n```\r\n\r\nNote that the index of each dimension always starts at *zero*, but for\r\ncompatibility with some basic dialects the bounds of each dimension will be\r\nexpanded by one to enable element access including the len. So in the above example, \r\nvalid index values for array *A* will be *0, 1*, *2* or *3*\r\nfor each dimension. Arrays may have a maximum of three dimensions. Numeric arrays will\r\nbe initialised with each element set to zero, while string arrays will be initialised\r\nwith each element set to the empty string \"\".\r\n\r\nAs for simple variables, a string array has its name suffixed by a '$' character, while a numeric array does not carry\r\na suffix. An attempt to assign a string value to a numeric array or vice versa will generate an error.\r\n\r\nArray variables\r\nwith the same name but different *dimensionality* are treated as the same. For example,\r\nusing a **DIM** statement to define *I(5)* and then a second **DIM** statement to define *I(5, 5)* will\r\nresult in the second definition (the two dimensional array) overwriting the first definition (the one dimensional array).\r\n\r\nArray values may be used within any expression, such as in a **PRINT** statement for string values, or in any numerical\r\nexpression for number values. However, you must be specific about which array element you are referencing, using the\r\ncorrect number of in-range indexes. If that particular array value has not yet been assigned, then an error\r\nmessage will be printed.\r\n\r\n```\r\n\u003e 10 DIM MYARRAY(2, 2, 2)\r\n\u003e 20 LET MYARRAY(0, 1, 0) = 56\r\n\u003e 30 PRINT MYARRAY(0, 1, 0)\r\n\u003e RUN\r\n56\r\n\u003e 30 PRINT MYARRAY(0, 0, 0)\r\n\u003e RUN\r\nEmpty array value returned in line 30\r\n\u003e\r\n```\r\n\r\nSince **all PyBasic variables have a global scope** and string memory is managed by the python interpreter, there is no garbage collection in PyBasic.  \r\n\r\n### Program constants\r\n\r\nConstants may be defined through the use of the **DATA** statement. They may consist of numeric or string values\r\nand are declared in a comma separated list:\r\n\r\n```\r\n\u003e 10 DATA 56, \"Hello\", 78\r\n```\r\n\r\nThese values can then later be assigned to variables using the **READ** statement. Note that the type of the value\r\n(string or numeric) must match the type of the variable, otherwise an error message will be triggered. Therefore,\r\nattention should be paid to the relative ordering of constants and variables. Once the constants on a **DATA**\r\nstatement are used by a **READ** statement, the next **READ**\r\nstatement will move to the **DATA** statement with the next higher line number, if there are no more **DATA**\r\nstatements before the end of the program an error will be displayed. This is to ensure that the program is not left\r\nin a state where a variable has not been assigned a value, but nevertheless an attempt to use that variable is\r\nmade later on in the program.\r\n\r\nNormally each **DATA** statement is consumed sequently by **READ** statements however, the **RESTORE** statement can\r\nbe used to override this order and set the line number of the **DATA** statement that will be used by the next \r\n**READ** statement. If the *line-number* used in a **RESTORE** statement does not refer to a **DATA** statement an\r\nerror will be displayed.\r\n\r\nThe constants defined in the **DATA** statement may be consumed using several **READ** statements or several **DATA**\r\nstatements may be consumed by a single **READ** statement.:\r\n\r\n```\r\n\u003e 10 DATA 56, \"Hello\", 78\r\n\u003e 20 READ FIRSTNUM, S$\r\n\u003e 30 PRINT FIRSTNUM, \" \", S$\r\n\u003e 40 READ SECONDNUM\r\n\u003e 50 PRINT SECONDNUM\r\n\u003e 60 DATA \"Another \"\r\n\u003e 70 DATA \"Line \"\r\n\u003e 80 DATA \"of \"\r\n\u003e 90 DATA \"Data\"\r\n\u003e 100 RESTORE 10\r\n\u003e 110 READ FIRSTNUM, S$, SECONDNUM, A$, B$, C$, D$\r\n\u003e 120 PRINT S$,\" \",A$,B$,C$,D$\r\n\u003e RUN\r\n56 Hello\r\n78\r\nHello Another Line of Data\r\n\u003e\r\n```\r\n\r\nIt is a limitation of this BASIC dialect that it is not possible to assign constants directly to array variables\r\nwithin a **READ** statement, only simple variables.\r\n\r\n### Comments\r\n\r\nThe **REM** statement is used to indicate a comment, and occupies an entire statement. It has no effect on execution:\r\n\r\n```\r\n\u003e 10 REM THIS IS A COMMENT\r\n```\r\n\r\n### Stopping a program\r\n\r\nThe **STOP** statement may be used to cease program execution. The command **END** has the same effect.\r\n\r\n```\r\n\u003e 10 PRINT \"one\"\r\n\u003e 20 STOP\r\n\u003e 30 PRINT \"two\"\r\n\u003e RUN\r\none\r\n\u003e\r\n```\r\n\r\nA program will automatically cease execution when it reaches the final statement, so a **STOP** may not be necessary. However\r\na **STOP** *will* be required if subroutines have been defined at the end of the program, otherwise execution will continue\r\nthrough to those subroutines without a corresponding subroutine call. This will cause an error when the **RETURN**\r\nstatement is processed and the interpreter attempts to return control back to the caller.\r\n\r\n### Assignment\r\n\r\nAssignment may be made to numeric simple variables (which can contain either integers or floating point numbers) and string simple variables\r\n(string variables are distinguished by their dollar suffix). The interpreter will enforce this division between the two types:\r\n\r\n```\r\n\u003e 10 LET I = 10\r\n\u003e 20 LET I$ = \"Hello\"\r\n```\r\n\r\nThe **LET** keyword is also optional:\r\n\r\n```\r\n\u003e 10 I = 10\r\n```\r\n\r\nArray variables may also have values assigned to them. The indexes can be derived from numeric\r\nexpressions:\r\n\r\n```\r\n\u003e 10 DIM NUMS(3, 3)\r\n\u003e 20 DIM STRS$(3, 3)\r\n\u003e 30 LET INDEX = 0\r\n\u003e 40 LET NUMS(INDEX, INDEX) = 55\r\n\u003e 50 LET STRS$(INDEX, INDEX) = \"hello\"\r\n```\r\n\r\nAttempts to assign the wrong type (number or string) to a numeric or string array, attempts to assign a value to an array by specifying the wrong number\r\nof dimensions, and attempts to assign to an array using an out of range index, will all result in an error.\r\n\r\n### Printing to standard output\r\n\r\nThe **PRINT** statement is used to print to the screen (or to a file, see File I/O below):\r\n\r\n```\r\n\u003e 10 PRINT 2 * 4\r\n\u003e RUN\r\n8\r\n\u003e 10 PRINT \"Hello\"\r\n\u003e RUN\r\nHello\r\n\u003e\r\n```\r\n\r\nMultiple items may be printed by providing a semicolon separated list. The items in the list will be printed immediately after one\r\nanother, so spaces must be inserted if these are required:\r\n\r\n```\r\n\u003e 10 PRINT 345; \" hello \"; 678\r\n\u003e RUN\r\n345 hello 678\r\n\u003e\r\n```\r\n\r\nA blank line may be printed by using the **PRINT** statement without arguments:\r\n\r\n```\r\n\u003e 10 PRINT \"Here is a blank line:\"\r\n\u003e 20 PRINT\r\n\u003e 30 PRINT \"There it was\"\r\n\u003e RUN\r\nHere is a blank line:\r\n\r\nThere it was\r\n\u003e\r\n```\r\n\r\nA print statement terminated by a semicolon will not include a CR/LF.\r\n\r\n### Unconditional branching\r\n\r\nLike it or loath it, the **GOTO** statement is an integral part of BASIC, and is used to transfer control to the statement with the specified line number:\r\n\r\n```\r\n\u003e 10 PRINT \"Hello\"\r\n\u003e 20 GOTO 10\r\n\u003e RUN\r\nHello\r\nHello\r\nHello\r\n...\r\n```\r\n\r\n### Subroutine calls\r\n\r\nThe **GOSUB** statement is used to generate a subroutine call. Control is passed back to the program at the\r\nnext statement after the call by a **RETURN** statement at the end of the subroutine:\r\n\r\n```\r\n\u003e 10 GOSUB 100\r\n\u003e 20 PRINT \"This happens after the subroutine\"\r\n\u003e 30 STOP\r\n\u003e 100 REM HERE IS THE SUBROUTINE\r\n\u003e 110 PRINT \"This happens in the subroutine\"\r\n\u003e 120 RETURN\r\n\u003e RUN\r\nThis happens in the subroutine\r\nThis happens after the subroutine\r\n\u003e\r\n```\r\n\r\nNote that without use of the **STOP** statement, execution will run past the last statement\r\nof the main program (line 30) and will re-execute the subroutine again (at line 100).\r\n\r\nSubroutines may be nested, that is, a subroutine call may be made within another subroutine.\r\n\r\nA subroutine may also be called using the **ON-GOSUB** statement (see Conditional branching\r\nbelow).\r\n\r\nFurther note that **RETURN** passes control back to the next line numbered statement after the call. Subsequent statements after\r\nthe call in the same line will not be executed. For example:\r\n\r\n```\r\n\u003e 10 PRINT \"Print this\":GOSUB 100:PRINT \"This won't be printed\"\r\n\u003e 20 STOP\r\n\u003e 100 PRINT \"Print this too\"\r\n\u003e 110 RETURN\r\n\u003e RUN\r\nPrint this\r\nPrint this too\r\n\u003e\r\n```\r\n\r\n### Loops\r\n\r\nBounded loops are achieved through the use of **FOR-NEXT** statements. The loop is controlled by a numeric\r\nloop variable that is incremented or decremented from a start value to an end value. The loop terminates when\r\nthe loop variable reaches the end value. The loop variable must also be specified in the **NEXT**\r\nstatement at the end of the loop.\r\n\r\n```\r\n\u003e 10 FOR I = 1 TO 3\r\n\u003e 20 PRINT \"hello\"\r\n\u003e 30 NEXT I\r\n\u003e RUN\r\nhello\r\nhello\r\nhello\r\n\u003e\r\n```\r\n\r\nLoops may be nested within one another.\r\n\r\nThe **STEP** statement allows the loop variable to be incremented or decremented by\r\na specified amount. For example, to count down from 5 in steps of -1:\r\n\r\n```\r\n\u003e 10 FOR I = 5 TO 1 STEP -1\r\n\u003e 20 PRINT I\r\n\u003e 30 NEXT I\r\n\u003e RUN\r\n5\r\n4\r\n3\r\n2\r\n1\r\n\u003e\r\n```\r\n\r\nNote that the start value, end value and step value need not be integers, but can be floating\r\npoint numbers as well. If the loop variable was previously assigned in the program, its value will\r\nbe replaced by the start value, it will not be evaluated.\r\n\r\nAfter the completion of the loop, the loop variable value will be the end value + step value (unless\r\nthe loop is exited using a **GOTO** statement).\r\n\r\n### Conditionals\r\n\r\nConditionals are implemented using the **IF-THEN-ELSE** statement. The expression is evaluated and the appropriate \r\nstatements executed depending upon the result of the evaluation. If a positive integer is supplied as \r\nthe **THEN** or the **ELSE** statement, a branch will be performed to the indicated line number.\r\n\r\nNote that the **ELSE** clause is optional and may be omitted. In this case, the **THEN** branch is taken if the\r\nexpression evaluates to true, otherwise the next statement is executed.\r\n\r\n**Conditional branching example:**\r\n\r\n```\r\n\u003e 10 REM PRINT THE GREATEST NUMBER\r\n\u003e 20 LET I = 10\r\n\u003e 30 LET J = 20\r\n\u003e 40 IF I \u003e J THEN 50 ELSE 70\r\n\u003e 50 PRINT I\r\n\u003e 60 GOTO 80\r\n\u003e 70 PRINT J\r\n\u003e 80 REM FINISHED\r\n\u003e RUN\r\n20\r\n\u003e\r\n```\r\n\r\nThe following code segment is equivalent to the segment above:\r\n\r\n```\r\n\u003e 10 REM PRINT THE GREATEST NUMBER\r\n\u003e 20 LET I = 10\r\n\u003e 30 LET J = 20\r\n\u003e 40 IF I \u003e J THEN PRINT I ELSE PRINT J\r\n\u003e 80 REM FINISHED\r\n\u003e RUN\r\n20\r\n\u003e\r\n```\r\n\r\nA **THEN** or **ELSE** can be supplied multiple statements if they are separated by a colon \"**:**\".\r\n\r\n```\r\n\u003e 10 REM PRINT THE GREATEST NUMBER\r\n\u003e 20 LET I = 10\r\n\u003e 30 LET J = 20\r\n\u003e 40 IF I \u003e J THEN LET L = I:PRINT I ELSE LET L = J:PRINT J\r\n\u003e 50 PRINT L\r\n\u003e 80 REM FINISHED\r\n\u003e RUN\r\n20\r\n20\r\n\u003e\r\n```\r\n\r\nNote that should an **IF-THEN-ELSE** stmt be used in a **THEN** code block or multiple **IF-THEN-ELSE** statements\r\nare used in either a single **THEN** or **ELSE** code block, the block grouping is ambiguous and logical processing\r\nmay not function as expected. There is no ambiguity when single **IF-THEN-ELSE** statements are placed within **ELSE**\r\nblocks.\r\n\r\nAmbiguous:\r\n```\r\n\u003e 100 IF I \u003e J THEN IF J \u003e= 100 THEN PRINT \"I \u003e 100\" else PRINT \"Not clear which **IF** this belongs to\"\r\n```\r\n\r\nNot Ambiguous:\r\n```\r\n\u003e 100 IF I \u003c J THEN PRINT \"I is less than J\" ELSE IF J \u003e 100 THEN PRINT \"I \u003e 100\" ELSE PRINT \"J \u003c= 100\"\r\n```\r\n\r\nAllowable relational operators are:\r\n\r\n* '=' (equal, note that in BASIC the same operator is used for assignment)\r\n* '\u003c' (less than)\r\n* '\u003e' (greater than)\r\n* '\u003c=' (less than or equal)\r\n* '\u003e=' (greater than or equal)\r\n* '\u003c\u003e' / '!=' (not equal)\r\n\r\nThe logical operators **AND** and **OR** are also provided to allow you to join two or more expressions. The **NOT** operator can also be given before an expression.\r\n\r\n*=* and *\u003c\u003e* can also be considered logical operators. However, unlike **AND** or **OR** they can't be used to join more than two expressions.\r\n\r\n| Inputs |       | *AND* | *OR*  | *=*   | *\u003c\u003e* / *!=* |\r\n|--------|-------|-------|-------|-------|-------------|\r\n| FALSE  | FALSE | FALSE | FALSE | TRUE  | FALSE       |\r\n| TRUE   | FALSE | FALSE | TRUE  | FALSE | TRUE        |\r\n| TRUE   | TRUE  | TRUE  | TRUE  | TRUE  | FALSE       |\r\n\r\n| Input | NOT   |\r\n|-------|-------|\r\n| TRUE  | FALSE |\r\n| FALSE | TRUE  |\r\n\r\nExample:\r\n\r\n```\r\n\u003e 10 a = 10\r\n\u003e 20 b = 20\r\n\u003e 30 IF NOT a \u003e b AND b = 20 OR a \u003e= 5 THEN 60\r\n\u003e 40 PRINT \"Test failed!\"\r\n\u003e 50 STOP\r\n\u003e 60 PRINT \"Test passed!\"\r\n\u003e RUN\r\nTest passed!\r\n```\r\n\r\nExpressions can be inside brackets to change the order of evaluation. Compare the output when line 30 is changed:\r\n\r\n```\r\n\u003e 30 IF NOT a \u003e b AND (b = 20 OR a \u003e= 5) THEN 60\r\n\u003e RUN\r\nTest failed!\r\n```\r\n\r\n### ON GOTO, ON GOSUB\r\n\r\nThe **ON GOTO|GOSUB** *expr* *line1,line2,...* statement will call a subroutine or branch to a line number in the list of line numbers corresponding to the ordinal\r\nvalue of the evaluated *expr*. The first line number corresponds with an *expr* value of 1.  *expr* must evaluate to an integer value.\r\n If *expr* evaluates to less than 1 or greater than the number of provided line numbers execution continues on the next \r\nstatement without making a subroutine call or branch:\r\n\r\n```\r\n\u003e 20 LET J = 2\r\n\u003e 30 ON J GOSUB 100,200,300\r\n\u003e 40 STOP\r\n\u003e 100 REM THE 1ST SUBROUTINE\r\n\u003e 110 PRINT \"J is ONE\"\r\n\u003e 120 RETURN\r\n\u003e 200 REM THE 2ND SUBROUTINE\r\n\u003e 210 PRINT \"J is TWO\"\r\n\u003e 220 RETURN\r\n\u003e 300 REM THE 3RD SUBROUTINE\r\n\u003e 310 PRINT \"J is THREE\"\r\n\u003e 320 RETURN\r\n\u003e RUN\r\nJ is TWO\r\n\u003e\r\n```\r\n\r\nIt is also possible to call a subroutine depending upon the result of a conditional expression using the **IFF** function (see Ternary Functions below). In\r\nthe example below, if the expression evaluates to true, **IFF** returns a 1 and the subroutine is called, otherwise **IFF** returns a 0 and execution\r\ncontinues to the next statement without making the call:\r\n\r\n```\r\n\u003e 10 LET I = 10\r\n\u003e 20 LET J = 5\r\n\u003e 30 ON IFF (I \u003e J, 1, 0) GOSUB 100\r\n\u003e 40 STOP\r\n\u003e 100 REM THE SUBROUTINE\r\n\u003e 110 PRINT \"I is greater than J\"\r\n\u003e 120 RETURN\r\n\u003e RUN\r\nI is greater than J\r\n\u003e\r\n```\r\n\r\n### Ternary Functions\r\n\r\nAs an alternative to branching, Ternary functions are provided.\r\n\r\n* **IFF**(x, y, z) - Evaluates *x* and returns *y* if true, otherwise returns *z*. *y* and *z* are expected to be numeric.\r\n* **IF$**(x, y$, z$) - As above, but *y$* and *z$* are expected to be strings.\r\n\r\n```\r\n\u003e 10 LET I = 10\r\n\u003e 20 LET J = 5\r\n\u003e 30 PRINT IF$(I \u003e J, \"I is greater than J\", \"I is not greater than J\")\r\n\u003e 40 K = IFF(I \u003e J, 20, 30)\r\n\u003e 50 PRINT K\r\n\u003e RUN\r\nI is greater than J\r\n20\r\n```\r\n\r\n### User input\r\n\r\nThe **INPUT** statement is used to solicit input from the user (or read input from a file, see File I/O below):\r\n\r\n```\r\n\u003e 10 INPUT A\r\n\u003e 20 PRINT A\r\n\u003e RUN\r\n? 22\r\n22\r\n\u003e\r\n```\r\n\r\nThe default input prompt of '? ' may be changed by inserting a prompt string, which must be terminated\r\nby a semicolon, thus:\r\n\r\n```\r\n\u003e 10 INPUT \"Input a number - \"; A\r\n\u003e 20 PRINT A\r\n\u003e RUN\r\nInput a number - 22\r\n22\r\n\u003e\r\n```\r\n\r\nMultiple items may be input by supplying a comma separated list. Input variables will be assigned\r\nto as many input values as supplied at run time. If there are more input values supplied than input\r\nvariables, excess commas will be left in place. Conversely, if not enough input values are\r\nsupplied, an error message will be printed and the user will be asked to re-input the values again.\r\n\r\nFurther, numeric input values must be valid numbers (integers or floating point).\r\n\r\n```\r\n\u003e 10 INPUT \"Num, Str, Num: \": A, B$, C\r\n\u003e 20 PRINT A, B$, C\r\n\u003e RUN\r\nNum, Str, Num: 22, hello!, 33\r\n22 hello!33\r\n\u003e\r\n```\r\n\r\nA mismatch between the input value and input variable type will trigger an error, and the user will be asked\r\nto re-input the values again.\r\n\r\nIt is a limitation of this BASIC dialect that it is not possible to assign constants directly to array variables\r\nwithin an **INPUT** statement, only simple variables.\r\n\r\n### File Input/Output\r\n\r\nData can be read from or written to files using the **OPEN**, **FSEEK**, **INPUT**, **PRINT** and **CLOSE** statements.\r\n\r\nWhen a file is opened using the syntax **OPEN** \"*filename*\" **FOR INPUT|OUTPUT|APPEND AS** *#filenum* [**ELSE** *linenum*] a\r\nfile number (*#filenum*) is assigned to the file, which if specified as the first argument of an **INPUT** or **PRINT**\r\nstatement, will direct the input or output to the file. \r\n\r\nIf there is an error opening a file and the optional **ELSE** option has been specified, program control\r\nwill branch to the specified line number, if the **ELSE** has not been provided an error message will be displayed.\r\n\r\nIf a file is opened for **OUTPUT** which does not exist, the file will be created, if the file does exist, its contents will\r\nbe erased and any new **PRINT** output will replace it. If a file is opened for **APPEND** an error will occur if the file\r\ndoesn't exist (or the **ELSE** branch will occur if specified). If the file does exist, any **PRINT** statements will add to the end\r\nof the file.\r\n\r\nIf an input prompt is specified on an **INPUT** statement being used for file I/O (i.e. *#filenum* is specified) an error\r\nwill be displayed.\r\n\r\nThe **FSEEK** *#filenum*,*filepos* statement will position the file pointer for the next **INPUT** statement.\r\n\r\nThe **CLOSE** *#filenum* statement will close the file.\r\n\r\n```\r\n\u003e 10 OPEN \"FILE.TXT\" FOR OUTPUT AS #1\r\n\u003e 20 PRINT #1,\"0123456789Hello World!\"\r\n\u003e 30 CLOSE #1\r\n\u003e 40 OPEN \"FILE.TXT\" FOR INPUT AS #2\r\n\u003e 50 FSEEK #2,10\r\n\u003e 60 INPUT #2,A$\r\n\u003e 70 PRINT A$\r\n\u003e RUN\r\nHello World!\r\n\u003e\r\n```\r\n\r\n### Numeric functions\r\n\r\nSelected numeric functions are provided, and may be used with any numeric expression. For example,\r\nthe square root function, **SQR**, can be applied expressions consisting of both literals and variables:\r\n\r\n```\r\n\u003e 10 LET I = 6\r\n\u003e 20 PRINT SQR(I - 2)\r\n\u003e RUN\r\n2.0\r\n\u003e\r\n```\r\n\r\nAllowable numeric functions are:\r\n\r\n* **ABS**(x) - Calculates the absolute value of *x*\r\n\r\n* **ATN**(x) - Calculates the arctangent of *x*\r\n\r\n* **COS**(x) - Calculates the cosine of *x*, where *x* is an angle in radians\r\n\r\n* **EXP**(x) - Calculates the exponential of *x*, *e^x* where *e*=2.718281828\r\n\r\n* **INT**(x) - Rounds down numbers to the lowest whole integer less than or equal to *x*\r\n\r\n* **LOG**(x) - Calculates the natural logarithm of *x*\r\n\r\n* **MAX**(x, y[, z]...) - Returns the highest value from a list of expressions\r\n\r\n* **MIN**(x, y[, z]...) - Returns the lowest value from a list of expressions\r\n\r\n```\r\n\u003e 10 PRINT MAX(-2, 0, 1.5, 4)\r\n\u003e 20 PRINT MIN(-2, 0, 1.5, 4)\r\n\u003e RUN\r\n\u003e 4\r\n\u003e -2\r\n```\r\n\r\n* **PI** - Returns the value of pi.\r\n\r\n* **POW**(x, y) - Calculates *x* to the power *y*\r\n\r\n* **RND**(mode) - Psuedorandom number generator. The behavior is different depending on the value passed. If the value is positive, the result will be a new random value between 0 and 1 (including 0 but not 1). If the value is negative, it will be rounded down to the nearest integer and used to reseed the random number generator. Pseudorandom sequences can be repeated by reseeding with the same number.Generates a pseudo random number N, where *0 \u003c= N \u003c 1*. Can be\r\nreset using the **RANDOMIZE** instruction with an optional seed value: e.g.\r\n\r\n```\r\n\u003e 10 RANDOMIZE 100\r\n\u003e 20 PRINT RND(1)\r\n\u003e RUN\r\n0.1456692551041303\r\n\u003e\r\n```\r\n\r\nRandom integers can be generated by combining **RND** and **INT**: e.g.\r\n\r\n```\r\n\u003e 10 PRINT INT(RND(1) * 6) + 1\r\n\u003e RUN\r\n3\r\n\u003e RUN\r\n6\r\n\u003e\r\n```\r\n\r\nSeeds may not produce the same result on another platform.\r\n\r\n* **RNDINT**(*lo*, *hi*) - Generates a pseudo random integer N, where *lo* \u003c= N \u003c= *hi*. Uses the same seed as above.\r\n\r\n* **ROUND**(x) - Rounds number to the nearest integer.\r\n\r\n* **SIN**(x) - Calculates the sine of *x*, where *x* is an angle in radians\r\n\r\n* **SQR**(x) - Calculates the square root of *x*\r\n\r\n* **TAN**(x) - Calculates the tangent of *x*, where *x* is an angle in radians\r\n\r\n### String functions\r\n\r\nSome functions are provided to help you manipulate strings. Functions that return a string\r\nhave a '$' suffix like string variables.\r\n\r\n**NOTE** For compatibility with older basic dialetcs, all string indexes are 1 based.\r\n\r\nThe functions are:\r\n\r\n* **ASC**(x$) - Returns the character code for *x$*. *x$* is expected to be a single character.\r\nNote that despite the name, this function can return codes outside the ASCII range.\r\n\r\n* **CHR$**(x) - Returns the character specified by character code *x*.\r\n\r\n* **INSTR**(x$, y$[, start[, end]]) - Returns position of *y$* inside *x$*, optionally start searching\r\nat position *start* and end at *end*. Returns 0 if no match found.\r\n\r\n* **LEN**(x$) - Returns the length of *x$*.\r\n\r\n* **LOWER$**(x$) - Returns a lower-case version of *x$*.\r\n\r\n* **MID$**(x$, y[, z]) - Returns part of *x$* starting at position *y*.  If z is provided, that number of characters is returned, if omitted the entire rest of the string is returned\r\n\r\n* **LEFT$**(x$, y) - Returns the left most y characters from string x$. If y * exceeds the length of x$, the entire string will be returned.\r\n\r\n* **RIGHT$**(x$, y) - Returns the right most y characters from string x$. If y * exceeds the length of x$, the entire string will be returned.\r\n\r\n* **STR$**(x) - Returns a string representation of numeric value *x*.\r\n\r\n* **UPPER$**(x$) - Returns an upper-case version of *x$*\r\n\r\n* **VAL**(x$) - Attempts to convert *x$* to a numeric value. If *x$* is not numeric, returns 0.\r\n\r\n* **TAB**(x) -  When included in a **PRINT** statement *print-list*, specifies the position *x* on the line where the next text will be printed. If the specified position *x* is less than the current print position a newline is printed and the print location is set to the specified column. If the **TAB** function is used anywhere other than on a **PRINT** statement, it will return a string containing *x* spaces with no CR/LF\r\n\r\nExamples for **ASC**, **CHR$** and **STR$**\r\n```\r\n\u003e 10 I = 65\r\n\u003e 20 J$ = CHR$(I) + \" - \" + STR$(I)\r\n\u003e 30 PRINT J$\r\n\u003e 40 PRINT ASC(\"Z\")\r\nRUN\r\nA - 65\r\n90\r\n```\r\n\r\nStrings may also be concatenated using the '+' operator:\r\n\r\n```\r\n\u003e 10 PRINT \"Hello\" + \" there\"\r\n\u003e RUN\r\nHello there\r\n```\r\nStrings may be repeated using the '*' operator:\r\n\r\n```\r\n\u003e 10 PRINT \"Hello \" * 5\r\n\u003e RUN\r\nHello Hello Hello Hello Hello\r\n```\r\n\r\n## Example programs\r\n\r\nA number of example BASIC programs have been supplied in the repository, in the examples directory:\r\n\r\n* *regression.bas* - A program to exercise the key programming language constructs\r\nin such a way as to allow verification that the interpreter is functioning correctly.\r\n\r\n* *factorial.bas* - A simple BASIC program to take a number, *N*, as input from the user and\r\ncalculate the corresponding factorial *N!*.\r\n\r\n* *rock_scissors_paper.bas* - A BASIC implementation of the rock-paper-scissors game.\r\n\r\n* *PyBStartrek.bas* - A port of the 1971 Star Trek text based strategy game.\r\n\r\n* *adventure.bas* - A port of the original 1976 text based adventure game, originally developed for the PDP-10 by Will Crowther,\r\nand expanded by Don Woods.\r\n\r\n* *bagels.bas* - A guessing game, which made its first appearance in the book 'BASIC Computer Games' in 1978.\r\n\r\n* *eliza.bas* - A port of the early chatbot, posing as a therapist, originally created by Joseph Weizenbaum in 1964. This BASIC version can trace its lineage back to an implementation originally developed by Jeff Shrager in 1973.\r\n\r\n* *oregon.bas* - A port (of a port by the looks of it) of The Oregon Trail. This is a text based adventure game, originally developed by Don Rawitsch, Bill Heinemann, and Paul Dillenberger in 1971. This could still be a bit buggy, the listing I found wasn't the greatest. \r\n\r\n* *life.bas* - An implementation of Conway's Game of Life. This version is a port of the BASIC program which appeared in 'BASIC Computer Games' in 1978.\r\n\r\n* *Pneuma.bas* - A brief 21st century take on the venerable text adventure.\r\n\r\n## Informal grammar definition\r\n\r\n**ABS**(*numerical-expression*) - Calculates the absolute value of the result of *numerical-expression*\r\n\r\n**ASC**(*string-expression*) - Returns the character code of the result of *string-expression*.\r\n\r\n**ATN**(*numerical-expression*) - Calculates the arctangent value of the result of *numerical-expression*\r\n\r\n**CHR$**(*numerical-expression*) - Returns the character specified by character code of the result of *numerical-expression*.\r\n\r\n**CLOSE** *#filenum* - Closes an open file\r\n\r\n**COS**(*numerical-expression*) - Calculates the cosine value of the result of *numerical-expression*\r\n\r\n**DATA**(*expression-list*) - Defines a list of string or numerical values\r\n\r\n**DIM** *array-variable*(*dimensions*) - Defines a new array variable\r\n\r\n**EXIT** - Exits the interpreter\r\n\r\n**EXP**(*numerical-expression*) - Calculates the exponential value of the result of *numerical-expression*\r\n\r\n**FOR** *loop-variable* = *start-value* **TO** *end-value* [**STEP** *increment*] - Bounded loop\r\n\r\n**FSEEK** *#filenum*,*filepos* - Positions the file input pointer to the specified location within the open file, the next **INPUT** *#filenum*\r\nwill read starting at file position *filepos*\r\n\r\n**GOSUB** *line-number* - Subroutine call\r\n\r\n**GOTO** *line-number* - Unconditional branch\r\n\r\n**IF** *expression* **THEN** *line-number*|*basic-statement(s)* [**ELSE** *line-number*|*basic-statement(s)*] - Conditional\r\n\r\n**IFF**(*expression*, *numeric-expression*, *numeric-expression*) - Evaluates *expression* and returns the value of the result of the first *numeric-expression* if true, or the second if false.\r\n\r\n**IF$**(*expression*, *string-expression*, *string-expression*) - Evaluates *expression* and returns the value of the result of the first *string-expression* if true, or the second if false.\r\n\r\n**INPUT** [*#filenum*,|*input-prompt*;] *simple-variable-list* - Processes user or file input presented as a comma separated list\r\n\r\n**INSTR**(*hackstack-string-expression*, *needle-string-expression*[, *start-numeric-expression*[, *end-numeric-expression*]]) - Returns position of first *needle-string-expression* inside first *hackstack-string-expression*, optionally start searching at position given by *start-numeric-expression* and optionally ending at position given by *end-numeric-expression*. Returns -1 if no match found.\r\n\r\n**LEFT$**(*string-expression*, *char-count*) - Takes the result of *string-expression* and returns the left-most *char-count* characters.  If *char-count* exceeds string length the entire string is returned.\r\n\r\n**LEN**(*string-expression*) - Returns the length of the result of *string-expression*\r\n\r\n[**LET**] *variable* = *numeric-expression* | *string-expression* - Assigns a value to a simple variable or array variable\r\n\r\n**LIST** - Lists the program\r\n\r\n**LOAD** *filename* - Loads a program from disk\r\n\r\n**LOWER$**(*string-expression*) - Returns a lower-case version of the result of *string-expression*.\r\n\r\n**LOG**(*numerical-expression*) - Calculates the natural logarithm value of the result of *numerical-expression*\r\n\r\n**NEW** - Clears the program from memory\r\n\r\n**NEXT** *loop-variable* - See **FOR** statement\r\n\r\n**MAX**(*expression-list*) - Returns the highest value in *expression-list*\r\n\r\n**MID$**(*string-expression*, *start-position*[, *end-position*]) - Takes the result of *string-expression* and returns part of it, starting at position *start-position*, and ending at *end-position*. *end-position* can\r\nbe omitted to get the rest of the string.  If *start-position* or *end-position* are negative, the position is counted backwards from the end of the string.\r\n\r\n**MIN**(*expression-list*) - Returns the lowest value in *expression-list*\r\n\r\n**ON** *expression* **GOSUB|GOTO** *line-number1,line-number2,...* - Conditional subroutine call|branch - Program flow will be transferred either through a **GOSUB** subroutine call or a **GOTO** branch to the line number in the list of line numbers corresponding to the ordinal value of the evaluated *expr*. The first line number corresponds with an *expr* value of 1.  *expr* must evaluate to an integer value.\r\n\r\n**OPEN** \"*filename*\" **FOR INPUT|OUTPUT|APPEND AS** *#filenum* [**ELSE** *linenum*] - Opens the specified file. Program control is transferred to *linenum* if an error occurs otherwise continues\r\non the next line.\r\n\r\n**PI** - Returns the value of pi\r\n\r\n**POW**(*base*, *exponent*) - Calculates the result of raising the base to the power of the exponent\r\n\r\n**PRINT** [*#filenum*,]*print-list* - Prints a semicolon separated list of literals or variables to the screen or to a file.  Included CR/LF by default, but this can be suppressed by ending the statement with a semicolon.\r\n\r\n**RANDOMIZE** [*numeric-expression*] - Resets random number generator to an unpredictable sequence. With\r\noptional seed (*numeric expression*), the sequence is predictable.\r\n\r\n**READ** *simple-variable-list* - Reads a set of constants into the list of variables.\r\n\r\n**REM** *comment* - Internal program documentation\r\n\r\n**RETURN** - Return from a subroutine\r\n\r\n**RESTORE** *line-number* - sets the line number that the next **READ** will start loading constants from. *line-number* must refer to a **DATA** statement\r\n\r\n**RIGHT$**(*string-expression*, *char-count*) - Takes the result of *string-expression* and returns the right-most *char-count* characters. If *char-count* exceeds string length, the entire string is returned.\r\n\r\n**RND**(*mode*) - For mode values \u003e= 0 generates a pseudo random number N, where 0 \u003c= N \u003c 1.  For values \u003c 0 reseeds the PRNG\r\n\r\n**RNDINT**(*lo-numerical-expression*, *hi-numerical-expression*) - Generates a pseudo random integer N, where *lo-numerical-expression* \u003c= N \u003c= *hi-numerical-expression*\r\n\r\n**ROUND**(*numerical-expression*) - Rounds *numerical-expression* to the nearest integer\r\n\r\n**RUN** - Runs the program\r\n\r\n**SAVE** *filename* - Saves a program to disk\r\n\r\n**SIN**(*numerical-expression*) - Calculates the sine value of the result of *numerical-expression*\r\n\r\n**SQR**(*numerical-expression*) - Calculates the square root of the expression\r\n\r\n**STOP** - Terminates a program\r\n\r\n**STR$**(*numerical-expression*) - Returns a string representation of the result of *numerical-expression*\r\n\r\n**TAN**(*numerical-expression*) - Calculates the tangent value of the result of *numerical-expression*\r\n\r\n**UPPER$**(*string-expression*) - Returns an upper-case version of the result of *string-expression*\r\n\r\n**VAL**(*string-expression*) - Attempts to convert the result of *string-expression* to a numeric value. If it is not numeric, returns 0.\r\n\r\n## Architecture\r\n\r\nThe interpreter is implemented using the following Python classes:\r\n\r\n* basictoken.py - This implements the tokens that are produced by the lexical analyser. The class mostly defines token categories\r\nand provides a simple token pretty printing method.\r\n\r\n* lexer.py - This class implements the lexical analyser. Lexical analysis is performed on one statement at a time, as each statement is\r\nentered into the interpreter.\r\n\r\n* basicparser.py - This class implements a parser for individual BASIC statements. This is somewhat inefficient in that statements,\r\nfor example those in a loop, must be re-parsed every time they are executed. However, such a model allows us to develop an\r\ninteractive interpreter where statements can be gradually added to the program between runs.\r\nSince the parser is oriented to the processing of individual statements, it uses a\r\nsignalling mechanism (using FlowSignal objects) to its caller indicate when program level actions are required, such as recording the return address\r\nfollowing a subroutine jump. However, the parser does maintain a symbol table (implemented as a dictionary) in order to record\r\nthe value of variables as they are assigned.\r\n\r\n* program.py - This class implements an actual basic program, which is represented as a dictionary. Dictionary keys are\r\nstatement line numbers and the corresponding value is the list of tokens that make up the statement with that line number.\r\nStatements are executed by calling the parser to parse one statement at a time. This class\r\nmaintains a program counter, an indication of which line number should be executed next. The program counter is incremented to the next line\r\nnumber in sequence, unless executed a statement has resulted in a branch. The parser indicates this by signalling to the program object that\r\ncalls it using a FlowSignal object.\r\n\r\n* interpreter.py - This class provides the interface to the user. It allows the user to both input program statements and to execute\r\nthe resulting program. It also allows the user to run commands, for example to save and load programs, or to list them.\r\n\r\n* flowsignal.py - Implements a FlowSignal object that allows the parser to signal a change in control flow. For example, as\r\nthe result of a jump defined in the statement just parsed (GOTO, conditional branch evaluation), a loop decision,\r\na subroutine call, or program termination. This paradigm of using the parser to simply parse individual statements, the Program\r\nobject to make control flow decisions and to track execution, and a signalling mechanism to allow the parser to signal\r\ncontrol flow changes to the Program object, is used consistently throughout the implementation.\r\n\r\n## Open issues\r\n\r\n* It is not possible to renumber a program. This would require considerable extra functionality.\r\n* Negative values are printed with a space (e.g. '- 5') in program listings because of tokenization. This does not affect functionality.\r\n* User input values cannot be directly assigned to array variables in an **INPUT** or **READ** statement\r\n* Strings representing numbers (e.g. \"10\") can actually be assigned to numeric variables in **INPUT** and **READ** statements without an\r\nerror, Python will silently convert them to integers.\r\n\r\n## License\r\n\r\nPyBasic is made available under the GNU General Public License, version 3.0 or later (GPL-3.0-or-later).\r\n","funding_links":[],"categories":["Dialects"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frichpl%2FPyBasic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frichpl%2FPyBasic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frichpl%2FPyBasic/lists"}