PlsExplain (aka jamlang0001)

A small dynamically typed programming language with first-class comments, where every value is explained by a comment.

This language was developed within 48 hours for the first langjam.

Main Repo

Running the Interpreter

Dependencies

The interpreter depends on python3 and lark. Lark can be installed with pip install lark.

REPL

Invoke the interpreter without command line arguments to start the interactive mode

$ ./pls_explain.py
>>> print("hello world")
hello world /*the string "hello world"*/
>>>

To exit press Ctrl-D.

To execute a Program written in PlsExplain, pass the path to the program as the first command line argument.

$ ./pls_explain.py examples/hello_world.pe
Hello World /*the string "Hello World"*/

First-Class Comments

Comments are first-class values in PlsExplain. This means that they are expressions, can be stored in variables, passed as function arguments and be returned by functions.

>>> let comment = /* This is a comment */;
>>> print(comment)
/* This is a comment */ /* a comment */
>>> print(/*another comment*/)
/*another comment*/ /* a comment */

At a first glance comments might seem to be equivalent to strings. The difference is that comments can be used to explain something. In PlsExplain all values have an associated comment that explains the value. To explain a value with a comment, simply write it next to the expression. When called with a single argument, print shows the associated comment.

>>> let x = 40 + 2 /* the number fourtytwo */;
>>> print(x)
42 /* the number fourtytwo */

Explaining comments don't have to be comment-literals:

 >>> let comment = /* this is a comment */
 >>> let x = 42 comment
 >>> print(x)
 42 /* this is a comment */

Trying to explain a value with something that is not a comment obviously results in a type error.

>>> 42 "this is not a comment"
Backtrace (most recent call last):

line 1:
42 "this is not a comment"
^~~~~~~~~~~~~~~~~~~~~~~~~~
type error: type JlString can not be used to explain values

>>> let s = "not a comment either"

If you see this error, it often means that you have forgotten to separate two expressions with an semicolon.

Auto-generated Comments

If a value is not explained by an explicit comment, the interpreter automagically generates a helpful comment.

>>> let x = 4
>>> print(x)
4.0 /* the number 4.0 */
>>> let y = x + 10
>>> print(y)
14.0 /* the sum of the number 4.0 and the number 10.0 */

The explain operator

The comment explaining a value can be retrieved with the ?operator.

>>> print(x?)
/* the number 4.0 */

Manipulating Comments

The auto-generated comment can sometimes be a little bit verbose:

let fact = fn(n) {
     if (n == 0) {
        1
     } else {
        fact(n - 1) * n;
     }
};
print(fact(4));

This program prints:

24.0 /*the product of the product of the product of the product 
of the number 1.0 and the difference of the difference of 
the difference of the number 4.0 and the number 1.0 and the 
number 1.0 and the number 1.0 and the difference of the 
difference of the number 4.0 and the number 1.0 and the number
1.0 and the difference of the number 4.0 and the number 1.0
and the number 4.0*/

Since comments are first-class values we can manipulate them on the fly. This allows us to generate even more helpful comments.

let fact = fn(n) {
     if (n == 0) {
        1
     } else {
        /* lets generate a helpful comment for the return value */;
        /* (Comments can be concatenated with +) */
        let comment = /* the factorial of */ + n?;
        /* explain the return value with the generated comment */
        (fact(n - 1) * n) comment;
     }
};
let h = 4 /*the number of hours i've slept*/;
print(fact(h));

The output of this program is more concise:

24.0 /* the factorial of the number of hours i've slept*/

Meta-Comments and Meta-Meta-Comments and ...

Since comments are first-class values, comments are also explained by comments. Obviously comments explaining a comment are also explained by comments which in turn are explained by comments and so on.

>>> let x = 42 /* comment */ /* meta-comment */ /* meta-meta-comment */
>>> print(x)
42 /* comment */
>>> print(x?)
/* comment */ /* meta-comment */
>>> print(x??)
/* meta-comment */ /* meta-meta-comment */
>>> print(x???)
/* meta-meta-comment */ /*a comment*/  <-- autogenerated meta-meta-meta-comment
>>> print(x????)
/*a comment*/ /*a comment*/
...

Datatypes

Type Description
Comment the most important type
String unicode strings
Number floating point numbers
Bool Trueor False
Unit only the value ()
List Lists of values

Lists

Currently there is no special syntax for lists, instead the builtin functions list,append,put and gethave to be used to create and manipulate Lists.

>>> let l = list(1, 2, 3)
>>> print(l)
[1, 2, 3] /*a list of the number 1 and the number 2 and the number 3*/
>>> append(l, "world")
>>> print(l)
[1, 2, 3, world] /*a list of the number 1 and the number 2 and the number 3 and the string "world"*/
>>> print(get(l, 2))
3 /*the number 3*/
>>> put(l, 0, "hallo")
>>> print(l)
[hallo, 2, 3, world] /*a list of the string "hallo" and the number 2 and the number 3 and the string "world"*/

Syntax

The Syntax is Expression based.

Literals

Type Examples
Comment /* this is a comment */
String "hello" "with\n escape \" chars"
Number 1, -1.0, 42.5
Bool True, False
Unit ()

Grouping

(a + b) * c

Blocks

{ print("hello"); print("world") }

Multiple expressions can be grouped with curly braces. Expressions are separated with semicolons. The semicolon after the last Expression is optional. Blocks evaluate to the value of their last expression.

Functions

Definition
let f = fn (arg) {
    print(arg);
};

Functions can be defined with the keyword fnfollowed by a parenthesized list of parameters and the function body. The braces are optional, when the body is a single expression. All functions are anonymous and first-class.

Calling a function

print("hello")

Nothing special here.

Variables

Declaration

let x = 42

Variables are declared with the keyword let and must be initialized. Variables are lexicaly scoped. Declarations evaluate to the assigned value.

Assignment

x = 100

Assignments evaluate to the assigned value.

Unary Expressions

-a !b

Nothing unusual.

Binary Expressions

a + b

Operators ordered by decreasing precedence:

Operators Examples
? x?
*, -, % x * y, x % 5
+, - x + y
==, <, > x < y
& x & y
` `

The only unusual operator is the explain operator (?). See First Class Comments for more details. The logic operators &and |are not short circuiting.

Control Flow Expressions

If

if (condition) { "true" } else { "false" }

Parenthesis around the condition are mandatory. Braces around single expressions and the else branch are optional. If expressions evaluate to the value of the taken branch.

While

while (condition) { do_something() }

Parenthesis around the condition are mandatory. The Braces cant be omitted, if the body is a single expression. While loops evaluate to the value of the loop body during the last iteration.

Builtin Functions

Function Description
print(args...) Print any number of values. When called with a single argument, the arguments comment is printed as well
input() Read single line and return it as a string.
str(value) Convert value to string.
cmnt(value) Convert value to comment.
num(value) Convert string to number. Returns ()if the conversion fails.
list(args...) Create list containing the arguments.
append(list, value) Append value to list
put(list, index, value) Put value into list
get(list, index) Get value out of list