# Simple interpreter An interpreter of a simple artificial programming language written in Python 3. Here you'll find a brief description of the language and the implementation. ## Simple language This software is an interpreter of a relatively simple programming language designed by me. ### Data types Simple language supports integer numbers, floating-point numbers and the Boolean data type. Literal integer numbers are comprised of a sequence of digits. Please note that negative integer number literals are not supported just yet. Floating-point number literals follow a bit more complicated format, with scientific notation support and stuff. At the moment negative floating-point numbers literals are not supported either. The Boolean data type has two literal values: `True` and `False`. ### Variables Named memory locations are called variables. Numbers can be stored in memory by assigning them to variables using the assignment operator `:=`. A variable can be used anywhere a number can be used. x := 1; answer_to_everything := 42; y := x; ### Arithmetic expressions Numbers can be computed from complex arithmetic expressions. Simple language supports addition, subtraction, multiplication, and division with grouping using parentheses. An arithmetic expression can be used anywhere a number can be used. microseconds := 1000000 * seconds; ### Input/output Simple language provides basic output facilities using the `print` statement. print 60 * 3.14 / 180; print days_per_year * 24; Only `print`ing numbers is supported at the moment. ### Control flow Simple language supports conditional control flow using the `if` operator. When executed, the `if` operator executes its body statement if it's condition evaluates to the true Boolean value. never_printed := 0; if (False) print never_printed; Boolean values can be computed from complex logical expressions. Simple language supports conjunction (`&&`) and disjunction (`||`) of logical expressions, as well as comparing them using the equality (`==`) and inequality (`!=`) operators. Arguably not the most useful feature at the moment, but I am working on it. A logical expression can be used anywhere a Boolean value can be used. if (True && False == False) days := 366; Please note that the conditional operators `&&` and `||` have the same precedence right now. ### Compound statement A compound statement (or a block) is a sequence of statements grouped together inside a pair of curly braces (`{` and `}`). When executed, a block executes its statements sequentially. A block can be used anywhere a statement can be used. if (True) { days := 366; hours := days * 24; minutes := hours * 60; seconds := minutes * 60; } ### Language grammar The language grammar written in the Extended Backus‒Naur Form (EBNF) (as described in [the corresponding Wikipedia article] (https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form)) is as follows: program = { statement } ; statement = empty_statement | block | assignment | print_statement | if_statement ; empty_statement = ";" ; block = "{" , { statement } , "}" ; assignment = identifier , ":=" , arithmetic_expression , ";" ; print_statement = "print" , arithmetic_expression , ";" ; arithmetic_expression = arithmetic_term , { "+" , arithmetic_term | "-" , arithmetic_term } ; arithmetic_term = arithmetic_factor , { "*" , arithmetic_factor | "/" , arithmetic_factor } ; arithmetic_factor = identifier | number | "(" , arithmetic_expression , ")" ; number = integer_number | floating_point_number ; integer_number = digit , { digit } ; digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; (* Valid floating-point number literals: "1e1", "1e+1", "1e-1", ".1", "1.". Invalid floating-point number literals: "1e", ".e1". *) floating_point_number = integer_number , exponent | integer_number , "." , { digit } , [ exponent ] ; | { digit } , "." , integer_number , [ exponent ] ; exponent = ( "e" | "E" ) , [ "+" | "-" ] , integer_number ; identifier = ( LETTER | "_" ) , { LETTER | digit | "_" } ; (* LETTER is a symbol Python considers a letter under the current locale like "a", "b", "c", "X", "Y", "Z", etc. *) if_statement = "if" , "(" , logical_expression , ")" , statement ; logical_expression = logical_term , { "&&" , logical_term | "||" , logical_term } ; logical_term = logical_factor , [ "==" , logical_factor | "!=" , logical_factor ] ; logical_factor = "True" | "False" | "(" , logical_expression , ")" ; ## Interpreter design This implementation follows the conventional interpreter design principles (more or less). ### Lexer The *lexer* represents the contents of a source file as a sequence of *tokens*. Token examples include identifiers (like `x` and `foo`), literal values (either numeric like `42` and `3.14` or Boolean like `True`), parentheses (`(` and `)`), arithmetic operation signs (`+`, `*`), etc. The lexer is implemented in `src/lexer.py`. ### Parser The *parser* builds a program tree according to the rules described in the language grammar. Each tree node processes its children accordingly. For example, a node representing addition of two numbers must have exactly two children. When executed, this node evaluates its children and adds the two values. + / \ / \ 1 2 Each of the children in the example above might in turn be represented by a complex subtree. For example the right-side operand of the addition might be a result of multiplicating two numbers. + / \ / \ / \ 1 * / \ / \ 2 3 The `if` statement also has two children (its condition and body), but executes its body only after making sure the condition evaluates to the true Boolean value. if / \ ------- ------- / \ && := / \ / \ / \ / \ / \ days 366 / \ True False The parser is implemented in `src/parser.py`. ## Usage To use this software, you need to be able to run Python 3 scripts. To execute a script written in Simple language, pass the path to the script to `src/parser.py`. You can also pass the path to a script to `src/lexer.py` to examine the tokens the script gets separated into. ## License This project, including all of the files and their contents, is licensed under terms of the MIT License. See [LICENSE.txt] for details. [LICENSE.txt]: LICENSE.txt