[Home] [MBL Reference] [SMPL Reference]

Simple Math Programming Language (SMPL)

2023-11-09

WARNING: THE DOCUMENTATION IS NOT UP TO DATE. WE WILL PROVIDE AN UPDATE SOON.

This document describes the Simple Math Programming Language (SMPL).

SMPL is a math-oriented programming language that can be interpreted in the browser. Its primary use is for the mathe:buddy app.

SMPL is (mostly) an imperative, typed language. Its syntax is basically a subset of JavaScript, but extends with intrinsic mathematical data types (e.g. terms, sets and matrices) and operator overloading for these types.

The language definition of SMPL is independent of concrete implementations. A reference implementation can be found here. Visit our online playground.

SMPL is heavily used by the mathe:buddy language.

History Notes

Many concepts (and also parts of the source code) are taken from the Simple E-Learning Language SELL. Compared to SELL, SMPL is Turing Complete, but lacks an interactive e-learning environment.

First Example

The following example program creates two \(3 \times 3\)-matrices \(A\) and \(B\), with random (integral) entries in range [-5,5] without zero. Both matrices are numerically unequal. The product of \(A\) and \(B\) is finally assigned to variable \(C\).

% multiplication of two 3x3-matrices
let A/B = randZ<3,3>(-5,5)
let C = A * B

The example demonstrates some of the key features of SMPL:

The following Python program is similar to the two lines of SMPL code above (but not semantically equivalent, since it generates zero-elements in some cases).

import numpy
A = numpy.round(numpy.random.rand(3,3)*10) - 5
while True:
  B = numpy.round(numpy.random.rand(3,3)*10) - 5
  if not numpy.array_equal(A,B):
    break
C = numpy.matmul(A, B)

Programs

An SMPl program is a sequence of statements \(p=(s_0 ~s_1 \dots)\). Each statement ends by a semicolon (;). Declarations and assignments are executed statement wise, i.e. \(s_{i+1}\) is executed after statement \(s_i\).

Example:

let x = 3
let y = sin(x)

The example programs executes lines one and two in given order. Each line is evaluated, before the next line is executed. Indeed, we also could write all statements in one line, since the semicolon separates statements.

Comments

Comments provide the opportunity co write notes in natural language that is not executed. Comments are introduced by the percentage sign (%) and are valid until the next line break.

Example:

% this is a comment line
let a = 5     % assign integer constant 5 to variable a
%let b = 7

The same listing can be denoted as follows without comments:

let a = 5

Declarations

Declarations are initiated with keyword let, followed by an identifier and finally assigned expression by =. The expression in the right-hand side is mandatory to derive the data type. Data types are described in detail in the next section.

Example:

let x = 5, z = 9.1011
let y = 7
let u = rand(5)
let v = zeros<2,3>()
let a:b = randZ<3,3>(-2, 2)
let c/d/e = rand<3,3>(-2, 2)

Expressions

An assignment has the form X = Y;. First, the right-hand side Y is evaluated and then assigned to the variable X on the left-hand side.

Variables are named by identifiers, consisting of one ore more characters. The first character must be a lowercase or uppercase letter or underscore, i.e. a..z or A..Z or _. Starting from the second character, also numbers 0..9 are allowed additionally. Keywords and function names of the standard function library (see appendix) are not allowed. Examples: x, y0, A, mat_0.

The right-hand side of an assignment consists of a unary constant (e.g. 1337 or 3.14 or -42) or a function call (e.g. sin(x)) or a variable (e.g. x) or an expression (e.g. a + 4).

An expression is denoted in infix notation: The operator is denoted between two operands in case of a binary operation or the operator is denoted before the operand in case of a unary operation.

The following list of operators is implemented in SMPL. The list is ordered by increasing precedence. Explicit parentheses can break the default precedence (e.g. \(a * (b+c)\)).

Operator Description
|| Logical Or (binary)
&& Logical And (binary)
==,!= Equal, Unequal (binary)
<, <=,>,>= Less than, Less or equal, Greater than, Greater or equal (binary)
+, - Addition, Subtraction (binary)
*, / Multiplication, Division (binary)
^ Potency (binary)
++, -- Postfix Incrementation, Decrementation (unary)
! logical not (unary)

Not all operators can be applied to each data type. For example, a && b is only valid, if operands a and b are boolean.

Base data types are evaluated at compile-time. The compiler reports an error, if types do not match for a operator.

Dimensions are evaluated at runtime. For example, a RuntimeError is thrown if two matrices with a different number of rows are added.

Comparing non-integer numbers with == and != applies the following numerical compare: a == b is implemented as \(|a-b|\leq\epsilon\) and a != b is implemented as \(|a-b|>\epsilon\). (Note: \(\epsilon\) is statically set to \(10^{-9}\). It will be configurable in future SMPL revisions.)

Some examples for expressions (the examples assumes, that variables y, u, w, A, B, C have been declared before usage):

let x = 1.23 * sin(y) + exp(u + 2*w)
let C = A * transpose(B)
let d = det(C)

The set of implemented functions is listed in the appendix.

Data Types

SMPL supports the following data types:

Conditions

Conditional code is executed only, if a conditional is true. The if-statement has the form if C { S0 } elif C1 { S1 } ... else { Sn }, with a sequences of statements S0, S1 etc. Sequence S0 is executed, if the boolean condition S0 is true. Sequence S1 is executed, if the boolean condition S0 is false and the boolean condition S1 is true. In case that all conditions Ci are false, then sequence Sn is executed.

The elif parts and else part are optional.

Example

let s = 0
let x = rand(-5,5)
if x > 0 {
  s = 1
}
elif x < 0 {
  s = -1
}
else {
  x = 0
}

Loops

Example:

while x > 0 {
  % body
}

Example:

do {
  % body
} while x > 0

Example:

let f = 1
for k from 1 to 4 {
  f = f * k
}

Appendix: Built-in constants

The following list describes all built-in constants. We use the notation :T after each variable to indicate its data type.

Appendix: Built-in functions

The following list describes all built-in functions. We use the notation :T1|T2|... to list valid data types for each parameter and the return value. For example abs(x:INT|REAL|COMPLEX):REAL denotes function abs with one parameter named x that can be an integer a real value or complex value. The function returns a real value.

Some function also require dimensions. These are embedded into <...>.

Notation: We write data type VOID for functions that do not return any value.

Appendix: Grammar

The following formal grammar (denoted in EBNF) is currently implemented.

<GRAMMAR>
   program =
     { (statement | "\n") };
   statement =
       declareOrAssign
     | ifCond
     | whileLoop
     | forLoop
     | figure;
   declareOrAssign =
     [ "let" ]                 # "let" -> declaration
     ID ["[" TERM "]"]         # variable ID, optional: vector-index
     ( {":" ID} | {"/" ID} )   # additional variable ID(s), '/' := distinct
     ["(" ID { "," ID } ")"]   # function parameters
     "=" TERM                  # assign right-hand side
     [">>>" ID ">>>" TERM [">>>" TERM]]     # TESTS: expected: type, value,
     (";"|"\n");                            #                     stringified
   ifCond =
     "if" TERM block { "elif" TERM block } [ "else" block ];
   whileLoop =
     "while" TERM block;
   forLoop =
     "for" ID "from" TERM "to" TERM block;
   block =
     "{" { statement } "}";
   figure =
     "figure" "{" { figureStatement } "}";
   figureStatement =
       ("x_axis"|"y_axis") "(" num "," num "," STR ")"
     | "function" "(" ID ")"
     | "circle" "(" num "," num "," num ")";
   num =
       ["-"] INT
     | ["-"] REAL;
</GRAMMAR>

Author: Andreas Schwenk, TH Köln