/HU-ATP

Primary LanguagePython

SmickelScript

Crappy programming language made for a school assignment. Heavily influenced by JavaScript/TypeScript. We had to use functional functions, which sometimes suck in Python. So the code might not always be readable.

Try the online compiler!

Features

  • Five builtin types: number, string, bool, array, and void
  • Three builtin functions: print, println, and rand
  • Custom functions with parameters using the func keyword
  • If statements
  • While loops
  • Scoped variables, with the option to update a var in a 'parent' scope
  • Fixed size arrays
  • Comments
  • Basic CLI interface (using python -m smickelscript.cli)

TODO

Some things which could be improved. The language is "Jan-Complete" at the moment, which means that it should be enough to pass the course.

  • [interpreter] Better debugging (trace state changes?)
  • Better error message when a function is missing a return statement (but a return typehint is given), see test_main_number_no_return
  • [compiler] Some data could be saved in .RODATA instead of .DATA
  • [compiler] Optimize mov statements. There are some mov statements which could be removed/optimized.
  • [compiler] Could've used AsmTokens instead of raw assembly strings.
  • [compiler] Add byte arrays (useful for ASCII strings).
  • [compiler] The print function could automatically be translated to either print_str or print_integer.
  • [compiler] With large programs you sometimes get errors where the literal pool is out of range for LDR. More info.

Interpreter Usage

# Install the package
pip install -e .

# Run a script
python -m smickelscript.cli exec -i example/hello_world.sc

# Run a script and pass one argument
python -m smickelscript.cli exec -i example/hello_name.sc Wouter

# Or pass multiple args
python -m smickelscript.cli exec -i example/multi_args.sc Wouter "How are you?"

# Or call a different entry point
python -m smickelscript.cli exec -i example/functions.sc -e sommig 5

Compiler Usage

# Install the package
pip install -e .

# Compile a script to asm. This prints the generated code to stdout.
python -m smickelscript.cli native -i example_native/hello_world.sc

# Compile and run a script on an Arduino (connect via usb)
python -m smickelscript.cli native -i example_native/hello_world.sc --execute

SmickelScript as a Service Usage

# Install the package
pip install -e .
pip install Flask flask-cors waitress

# Start web api (development)
cd smickelscript_web
flask run

# Start web api (production)
cd smickelscript_web
waitress-serve --port=3025 app:app

# Example NGINX config
# server {
#     listen 443 ssl http2;
#     listen [::]:443 ssl http2;

#     server_name saas.cerbus.nl;

#     include snippets/ssl-cerbus.conf;

#     root /opt/HU-ATP/smickelscript_web/public/;

#     location /api/ {
#         proxy_pass http://127.0.0.1:3025/;
#     }
# }

About the language

Numbers

If a negative sign 'touches' a number it will always be interpreted as a negative number. E.g var a = 1 -1 is NOT the same as var a = 1 - 1. The first example is invalid code, and the second is valid code.

Scope

All variables in the stack are readable and writeable by all functions. For example the following snippet is valid code.

func main() {
    var a = 0;
    incr_a();
    // This will print '1'.
    println(a);
}

func incr_a() {
    a = a + 1;
}

Types

All typehints are optional, but when they are given they will be enforced. If the typehints are omitted they will be guessed. Guessing means that it will try to parse it as a number, and if that doesn't work then it must be a string.

Interpreter

The interpreter will print all print and println output to the stdout. It will also print the return value of the entry point function (which may be None).

Compiler

The compiler transforms your smickelscript source code into ARM Cortex-M0 assembly code. The PlatformIO toolkit is used to compile, flash, and monitor the generated code.

The compiler has a few limitations compared to the interperter:

  • A function can have at most one argument. This is not a technical limitation, but it keeps the compiler's code a lot simpler.
  • You can only have 4 local variables in each scope (aka function). This too keeps the compiler's code significantly less complex.
  • Each element of an array is 4 bytes large. This means that you can't call print and println on arrays, you can however use print_int_as_char to print an integer (element of the array) as an ASCII character.
  • The println function is divided into multiple functions. These functions are print_integer, print_str, println_integer, println_str, and print_int_as_char.
  • A bool type variable is translated into an int 0 or 1 by the compiler, this also means that you have to use either print_integer or println_integer to print a bool variable.
  • The compiler doesn't do a lot of 'logic checking', this means that usually the compiler won't stop you from writing stupid code.
  • You can't access variables in a higher stack layer. if and while statements do NOT create a new stack layer.

Tests

There are 7 files with tests.

  • test_lexer tests the basic functionality of the lexer.
  • test_parser tests the basic functionality of the parser.
  • test_interpreter tests the basic functionality of the interpreter.
  • test_must_haves tests the code samples provided by the course.
  • test_extra tests a few edge cases which aren't (yet) supported by the language. These are optional.
  • test_compiler has tests for the compiler.
  • test_run_native tests the compiled code on actual hardware.
# Run a specific test suite.
python -m pytest tests/test_run_native.py -vv

Jan-Completeness

The not so interesting part.

Language features

Requirements

  • Classes with inheritance (at the top in interpreter.py, parser.py and lexer.py)
  • Object printing for all classes using JSON and a custom encoder.
  • Type annotated
  • Uses higher order functions

Turing completeness

We could write a brainfuck interpreter in this language. Because brainfuck is turing complete it also means that this language is turing complete. To write a brainfuck interpreter you only needs arrays, conditions and basic math (addition and subtraction), all these features are supported in this language.

Other functionality

See statement_exec_map and operators_map at line ~391 inside interpreter.py for a list of all the implemented functions, and how they are implemented.