Pyli/Tutorial & Overview

From Jonathan Gardner's Tech Wiki
Jump to: navigation, search

Pyli is:

  • A program. You can run the program and feed it input and get back output.
  • A parser. It parses pyli programs into data structures.
  • An interpreter. It runs pyli programs.

Syntax & Evaluator

Comments

Comments start with '#' and run to the end of the line, like Python and Perl.

Numbers

Basic numbers are integers and floats.

12345
-12345
12345.123345


Sign (+ or -) is optional and appears before the base prefix.

Base prefix of 0b means the rest is in binary. 0o is octal, 0d is decimal (default) and 0x is hexadecimal.

Type suffix can be 'i' for integer or 'f' for float. You can't combine suffixes and prefixes. That is, prefixed notation is always an integer. The type is implied to be an integer unless an exponential form is used.

Exponential form (1.5e-7) is OK, and you can have a type suffix of 'i' if you want it as an integer.

Integers do not overflow and can be used to represent arbitrarily large numbers.

Floats are whatever the native float type is based on the processor, so some precision is lost.

Strings

Literal strings are called text, and are stored in unicode. They are denoted with quotes (single or double), and allow Python-like escape sequences.

"foo"
'foo'

These evaluates to the text 'foo'.

The other type of string---bytes---are distinct from text. They store bytes of information.

Vectors

Data may be grouped into vectors. The items are separated with whitespac (tabs, newlines, spaces).

[1 2 3]

This evaluates to a vector of three items, 1, 2 and 3.

Variable Names

Variable names are simply written out without any special decorations. Variable names can start with anything but something that looks like a number, including '+5' or '-5'.

Variable names evaluate to the value that they represent by looking up the variable. (See variable lookup below.)

Function Calls

When evaluating a vector (that is, what [1 2 3] gives back, not [1 2 3] itself), the first item is invoked with args of the rest of the items. That is, in the following expression

(foo 1 2 3)

the function that 'foo' refers to is invoked with parameters (1 2 3).

Quote

You can use the special form 'quote' to return something unevaluated. For instance:

(quote 1)

will evaluate to something that will evaluate to 1.

There are several shortcuts for quote:

[1 2 3]

This creates a vector with 1, 2, and 3, all of which were evaluated. (Seem familiar?)

&(1 2 3)

This creates a vector with 1, 2, and 3, as of yet unevaluated.

&(1 $2 3)

This creates a vector with 1, 2 and 3. 1 and 3 are not yet evaluated, although 2 was.

Inline

Sometimes you have a value that is a list and you'd like to splice it into the current list. This is done with the inline special form.

(foo 1 2 (inline bar) 3)

If bar is the vector ("a" "b" "c"), then this evaluates to whatever (foo 1 2 "a" "b" "c" 3) would evaluate to.

Conclusion

The above is the complete syntax of Pyli. It is also a description of how the lexer, parser, and evaluator work.

Variable Lookup

There are two namespaces: The lexical and the dynamic. Variable lookups always occur in the lexical namespace by default, unless explicitly specified to be done in the dynamic namespace.

What is the difference between the two? Well, the lexical environment is the environment of the code at the time it was created. That is, all you have to do is look at the code before the object to see what it is.

The dynamic environment, on the other hand, is the environment at the time the function is invoked. The dynamic environment typically handles configuration and signal handlers. These are things you'd normally have to pass along to everyone.

Functions

Functions are created in a number of ways. The easiest way is using the function 'fn'.

(fn params body...)

The params is a vector that lists the name of the variables arguments should match with. The last variable may be (rest name) to match whatever is left.

The body is invokved and executed when the function is called in the dynamic environment of the caller and the lexical environment of the surrounding code extended by the arguments passed in.

Note that both the params and body will be evaluated as you call 'fn'. You should quote them appropriately. For example:

(fn &(a b c) &(+ a b c 5))

Note that the params ((a b c)) are quoted with the shortcut &. Note that the body is also quoted likewise. If it were not so, when you run this expression it would try to lookup a, b, and c, calling a with the parameters of b and c in order to evaluate the params, as well as attempting to call + with a, b, c, and 5 to find the body.

There are various functions to make declaring functions easier.

  • (named-fn name params body...): Give the function a name for debugging purposes.
  • (defn name params body...): Define a named function and put it in name in the topmost lexical scope.

Idioms

  • Functions with no arguments are usually expressed as the expression they would run as, or as a progn.
  • Functions with one argument may look like (fn &(it) ...).

Math

  • +: Takes 0 or more arguments and sums them. With 0 arguments, returns 0. With text, bytes, and vectors, it concatenates them together.
  • *: Takes 0 or more arguments and multiplies them. With text, bytes, and vectors, it duplicates them. With 0 arguments returns 1.
  • -: Takes 1 or more arguments and subtracts them. With 1 argument, returns the negative.
  • /: Takes 1 or more arguments and divides them. With 1 arguments, returns the inverse. For all integers, this is 0.

... etc ...

All the useful math functions are here.

Text Functions

You can do everything you'd want to do with text in a language like Python or Perl, including regular expressions.

Bytes Functions

Similar to Text Functions, but less text-oriented and more byte-oriented.

Vector Functions

Whatever you'd like to do with vectors. Vectors are, by nature, mutable, although they can be "frozen".

Dict Functions

Idem.

File Functions

...

Code Flow

if

If has several friends that are all based on it: cond, case, when, unless.

label

Label allows you to restart or return from a block of code.

loop, while, until, do-times, for...

These are all built on label. For is almost exactly like Python's for.

Conditions (Exceptions, warnings, and logging)

  • (signal condition): Signals a condition. This will call *signal-handler*, which is in the dynamic scope. If you signal a condition from the signal-handler, it will bubble up to the next highest level.

Conditions can be errors or warnings or just debug events. The main signal handler only worries about errors.

  • (on-condition handler body...): Runs body with signal handler specified in handler.
  • (on-error handler body...): Idem, but the signal handler only handlers errors.
  • (if-more value-name iterator when-more when-done): Tries to grab the next item from the iterator. If there is more, it puts it in value-name and runs when-more. If not, it runs when-done.

Threads

The pyli system is built so that it uses microthreads.