Oxoscript turns into NanoPy - more infos

Introduction

From Python was adopted especially the indentation and the basic language constructs, such as if, while, for, def. NanoPy contains many simplifications, so-called “syntactic sugar”, with which it acts like Python code despite the conceptual differences. For example, in NanoPy, each variable would have to be initialized with a type before it could be used. Thanks to the extensions, the initial assignment of a variable value determines its type. You only need to specify it if it cannot be determined automatically. This makes the statement identical to Python code in many cases.

a = 10
b = 3.145
c = true
d = [1,2,3,4]

If the type cannot be determined, it is appended with a colon:

a:long
b:float[5]

This form also exists in Python, but is optional there, since the variables do not have a fixed type. This makes Python more flexible, but has disadvantages, including memory management issues that must be solved with additional concepts (carbage collection, etc.). In NanoPy, memory management is fixed. We thereby significantly reduce memory consumption and also prevent memory fragmentation.

NanoPy knows the following types: byte, int, long, float and bool. The language also knows the class notion, which can be used to create abstract data types. An abstract data type is a variable class that is composed of several elementary types:

class Rectangle:
  left:int
  top:int
  width:int
  height:int

When creating variables, the new class can now also be used.

a:Rectangle

In addition to individual objects, lists of objects can also be created by writing the desired number in square brackets:

liste:Rectangle[10]

The individual elements can then be read/written via the index in square brackets:

liste[1].left = 10
pixels = liste[1].width * liste[1].height

It is also possible to initialize a variable of a class directly. The following example creates an object of the class Rectangle and sets values for left and top.

c = Rectangle(left=10,top=50)

The initialization of multiple values of an object can also be applied to lists:

liste[0](left=10,top=50)

It is important to know that the language has no dynamic memory management. Therefore there is no new, as in other languages, to create an object. All variables are created by static declaration and remain as long as the program, or the function in question, is running. This leads to a very memory optimized and fast execution speed.

As usual in Python, you can also declare loops and conditions in NanoPy. These look very similar. The condition with if, else, or elif even looks identical:

if a>b:
  print("a is greater than b")
elif a==b:
  print("both numbers are equal")
else:
  print("b is greater than a")

The indentation is solved centrally and identically as in Python. You can also see from the example that the colon was adopted to remain compatible.

As has been shown in tests, especially beginners have difficulties with the syntactic concepts of a programming language, whereby Python is much easier than, for example, Java or C/C++. It would be however still simpler, if one thinks of today unfortunately forgotten educational programming languages of the beginning time. In Basic (“Beginner’s All-purpose Symbolic Instruction Code), for example, there is no need for a colon. For function calls without a return value, the parentheses are also unnecessary and could probably be omitted.

This form of input is also possible in NanoPy and can be used in this way:

if a>b
  print "a is greater than b"
elif a==b
  print "both numbers are equal"
else
  print "b is greater than a"

The colon can also be omitted for class, def, for and while. So you can write Python compatible or omit the unnecessary constructs. Both is valid and possible.

There are two different types of loops that can be used, the for and the while loop.

For the for loop, the syntax is slightly different than in Python:

for i in 10:
  print(i)

This instruction counts from 0 to 9, i.e. is passed through ten times. Instead of a number, a variable can also be specified. The number determines the number of passes, which always starts at 0.

The following instruction shows how to traverse a specific range of values:

for i in [5..10]:
  print(i)

The numbers are enclosed, i.e. counting from 5 to 10 (incl.). You can also count down if the second number is smaller than the first.

Lists can also be iterated with loops. Here it must be noted that the list element is a copy of the original entry in each case. If you want to change a list entry, you have to address the entries via the index:

list = [1,2,3,4,5,6,7,8,9,10]
for i in list
  print i

for i in sizeof(list)
  list[i] = list[i] + 1

for i in list
  print i

The corresponding statement in Python is more flexible, but also harder to understand for beginners and therefore not possible in NanoPy:

for i in range(0,10):
  print(i)

Also these statements can be written without colon and brackets:

for i in 10
  print i
for i in [5..10]
  print i

Single instructions can also be executed on one line:

for i in 10 print i
for i in [5..10] print i

The parenthesis in function calls can be omitted, provided that the parentheses after them are not used in functions. The following example leads to a program error

print (1+2)*3

In this case, the entire expression must be bracketed:

print ((1+2)*3)

The following example of a while loop shows how something can be repeated as long as a certain condition is met:

i = 0
while i < 10
  print i
  i=i-1

Strings are mapped in NanoPy as byte arrays, where one byte is used per character. Unicode is currently not possible, because this uses too much memory. If a string is assigned to a variable, a byte array is automatically created (:byte[114]). The maximum length depends on the system and can be queried with sizeof(list). If you want a different size, you can declare it explicitly.

a = "This is a test"
print sizeof(a)
b:byte[40]
b = "Hello"

When a numeric value is assigned to a byte array, it is automatically converted to a string:

i = 10
a = "My lucky number is " + i
print a

Functions are declared with def as in Python and can be declared with or without parameters. If no type is specified for the parameters, the type int is assumed.

def helloWorld():
  print("Hello world")

def summe(a,b)->int:
  return a+b

def floatsumme(a:float,b:float)->float:
  return a+b

The parameter and return types are taken from Python. There they are optional, here they are mandatory, except for int parameters.

Parameterless functions without return values can be written in a shortened form. Here are the same examples in the simplified form:

def helloWorld
  print "Hello world"

def summe(a,b)->int
  return a+b

def floatsumme(a:float,b:float)->float
  return a+b

The abbreviated form without parentheses is only permissible if no parameters have to be declared.

Classes can also be nested. Furthermore, it is also possible to add functions to classes.

class Shape:
  position:vector
  radius:int

  def init
    position = vector(x=120,y=120)
    radius = 100

  def draw
    clear
    drawCircle position.x,position.y,radius
    update

myShape:Shape
myShape.init
myShape.draw

In addition to Python, the language also knows constants. Like variables, these are placeholders for values, but their value cannot be changed at runtime. Constants can also be calculated and you can use them everywhere where you can use variables. Additionally they can be used for static declarations, e.g. lists. For declaration we use the statement const:

const WIDTH = 10
const HEIGHT = 20
const AREA = WIDTH * HEIGHT

elements:vector[AREA*2]

Care must be taken when passing values to functions. The language copies all values, i.e. when a list or object is passed to a function, a copy of it is created. This handling is called by value. Other languages allow to pass references to variables (by reference). This is not possible with NanoPy, but can be worked around.

Large objects or lists that are generally valid are simply declared globally. These variables are always visible in functions and do not have to be passed as parameters:

list:byte[200]

def initializeList
  for i in sizeof(list)
    list[i] = 0

initializeList

This saves valuable memory and increases the execution speed of the code, since no large objects have to be copied.

Class variables, i.e. variables defined in a class declaration, are object global, i.e. all functions of the object can also access these variables.

class Rectangle
  pos:vector
  size:int

  def init
    pos.x = 10
    pos.y = 20
    size = 30

r:Rectangle
r.init

In communication, data is often exchanged between processes, coprocessors or other systems. NanoPy provides with a conversion command with which object data can be converted into byte array. The « instruction can be used for this purpose:

The following instruction converts the object v into a byte array buf. Since vector contains two float variables of 4 bytes each, buf is 8 bytes long.

v = vector(x=10,y=20)
buf << v

Conversely, it is also possible to convert from a buffer back into a structure:

buf = [0,0,32,65,0,0,160,65]
v:vector
v << buf
print v.toString()

If lists must be passed functions, the dimensioning does not have to be specified explicitly.

def sumfloats(list:float[])->float:
  result:float

  for v in list:
    result = result + v

  return result

floats:float[5] = [0.1,0.2,0.3,0.4,0.5]

print(sumfloats(floats))

Simplified form (no colons, single-line, non-mandatory brackets omitted):

def sumfloats(list:float[])->float
  result:float
  for v in list result = result + v
  return result

floats:float[5] = [0.1,0.2,0.3,0.4,0.5]

print sumfloats(floats)

The dimension can also be left open when returning functions:

def test(t)->byte[]
  a:byte[20]
  a="byte[20]"

  b:byte[10]
  b="byte[10]"

  if t%2 == 0
    return a
  else
    return b

for i in 10
  print test(i)

The NanoPy language is used for several systems, therefore this article will not go into concrete platform-specific functions. The goal is to make all Oxon products “programmable” with the language, which will be implemented gradually over the next few months. Currently NanoPy can only be used on the educational learning computers of the Oxocard Mini series. The Oxocard Blockly cards, which are mainly programmed with Blockly, contain a simplified pre-release version of NanoPy, which is no longer being developed and is not compatible with the current version.

On the Oxocard Minis, but also on future devices, we provide an event-driven programming model in NanoPy. On the Oxocards you can already use this and experiment with it. NanoPy provides event procedures that are called by the operating system. On the Oxocards these are the two procedures onDraw and onClick. onDraw is called when the screen is to be redrawn. This happens up to 50 times per second, depending on the workload of the system. onClick is called when the user clicks a button. On the IoT devices, we will provide other event procedures that can be used to easily power the devices.

This example turns the screen on/off when you press the left/right button:

turnOn = false

def onDraw
  if turnOn
    background 255,255,255
	else
    background 0,0,0
  update

def onClick
  b = getButtons()
  if b.left
    turnOn = false
  elif b.right
    turnOn = true

Summary