git stults online reflex / master

Tree @master (Download .tar.gz) @masterraw · history · blame


Reflex is a language built around free transformations. All the
language builtins are secondary symbols, the charset supports unicode,
and the language constructs are bidirectional so that programs can be
written in any (human) language. The free transformational structure
means that program equivalence can be easliy established ragardless of
the particular language locality of a given code module; this level of
synthetic, textual genericism is one outcome of free transformations.

All language constructs can be literally written in reverse; where the
reversal is not totally obvious, the reversed symbol or syntax is
explicitly provided. However, program flow must be in a single
direction or semantically symmetric for each program block.

## language overview

#### arithmetic and boolean operators

+  // plus
-  // minus
*  // times
/  // divide
%  // mod
|  // or
&  // and
^  // xor
!  // not
>  // greater than
>= // greater than or equal
<  // less than
<= // less that or equal
== // equal
!= // not equal

#### non-arithmetic operators

=       // define
..      // etc
()      // scope
<>      // label, concatenate labeled
==> <== // import export
:       // qualify (as, from)
>> <<   // read/write

#### type specifiers

These are used to prefix or postfix identifiers (strings that do not
include any of the primitive language symbols) to declare their type.

?      // bool
'      // int
.      // float
""     // string
[]     // list
{}     // struct
{name} // custom type (identifier pattern only)
~      // transform type

Here are some examples of declaring each kind of builtin data type; in
this case they are all postfix.

a?                         // bool identifier pattern
!!                         // true literals
??                         // false literal
true = !!                  // bool declaration

a'                         // int identifier pattern
133381                     // int literal
ten = 10                   // int declaration

a. .a a.b                  // float identifier patterns
991.222                    // float literal
pi = 3.14159               // float declaration

a""                        // string identifier pattern
"this is a string literal" // string literal
prompt = "$>"              // string declaration

a[]                        // list identifier pattern
[1,2,3,4]                  // list literal
xy = [1, 2]                // list declaration
xy = [                     // multiline list declaration

a{}                        // struct identifier pattern
{ 10, 10. }                // struct literal 
thing = { a', b. }         // struct declaration
thing = {                  // multiline struct declaration
thing.a                    // access struct's a element
b{thing}                   // struct of type thing identifier pattern

a~                         // transform identifier pattern
                           // no such thing as a transform literal per se
transform = a -> -a        // transform declaration

#### free transformations

Transformations are reflex's functions. They take generic expressions,
which includes space-delimited identifiers and literals. By default,
transformations in reflex are "free", meaning that they are direct,
textual substituations. They are "free" in the sense that they are
free of any semantic value. A semantic value, with respect to language
constructs, can be declared using the above type
specifiers. Transformations are declared like

name = expression -> logic

The name must not contain any of the language primitives as described
above. All transformations act on expressions, which includes lists of
parameters and basically every language construct. Parantheses can be
used to scope applications of transformations to expressions.

The logic of a transformation can either be declared via many single lines
(which create cases; they do not have memory), or by having a multiline block
of code, which can either be free form, or switch form.

Type identifiers exempt tokens from literal matching in a
transformation. So `a + b` will literally match those three characters
interspersed with any amount of whitespace, whereas `a. + b.` will
match a floating point number referred to as `a`, then a `+`, then a
floating point number referred to as `b`.

// takes in the literal text foo and replaces it with bar
foo2bar = foo -> bar
bar = a -> b
(foo2bar foo) a  // b

// changes + to -
free_negate = + -> -
free_negate 1 + 2. / (100. + 20.)  // -> 1 - 2. / (100. - 20.) -> 0.975

// switches two integers passed in as a whitespace delimited expression
switch = a' b' -> b a
switch 10 1  // 1 10

// a string transformation defined using many single lines
response = a"" -> "hello " a "!"
response = "goodbye" -> "goodbye!"
response "tom"     // "hello tom!"
response "goodbye" // "goodbye!"

// one defined using freeform multiline
multiline = x' y' ->
    dist = sqrt x * x + y * y
    -> 1. / dist

// one defined using multiline switch
switch_multiline = a' ->
    a < 0 -> !!
          -> ??

The last line of a multiline transformation is what is returned from the
transformation (the one with the `->`). With the multiline switch, the case that
evaluates to true returns the associated value.

A special case of the transform declaration syntax can be used to declare a
program main. In this special case, the transform identifier is simply omitted
before the `=`. The program file, imported as a package, can then itself be used
as a transform (see importing and exporting, below). As such, there can be only
one main in a program file, and only one main in any set of compiled programs
(so how it normally works).

#### pattern matching

Supposed we want to split off the first three digits of an integer. We
would like to be able to do `abc'` to define an int identifier named
`abc`, but we would also like to be able to split a single coherant
typed token into units in the transformation expression -- so `abc'`
would somehow allow `c` to be the first digit, `b` the second, etc. We
do this using the `..` (etc) operator.

bottom_three =' -> bar
bottom_three 12345  // 345

middle_four = ->
middle_four 1234.5678  // 34.56

The etc operator can also be used within the transformation logic to
refer to the unnamed piece of the expression pattern; ie a splat.

rstrip = ..a" ->
    a == "\n" -> rstrip ..
              -> ..a

The last example uses recursion and the `..` operator to strip whitespace off
the right side of a string until there is no more. If we use more than one of
`..` in a transformation expression pattern, to refer to them within the
transformation logic, we must use the `<>` operator. This allows us to affix a
label to a generic element in a pattern, and, within the scope that labeled
patterns are defined, to recombine the elements into a data element abstract
concatenation. We can do this with any generic pattern symbol. This is more
rigid and verbose; for completeness we'll use it on all the pattern elements in
an example.

shift_and_xor = ..<al>a' ..<bl>b' -> al ^ bl

Cases where there is only a single generic item in the pattern, scoped pattern
concatenation is not required, and there is special sugar around `..`; using
the recursive strip function from above, we show how to explicitly concatenate
labeled patterns using `<>`:

rstrip = ..<left>a" ->
    a == "\n" -> rstrip left
              -> <left a>

The etc operator can also be used to reference the children of any ast node in
the pattern, including all input. For example:

= .. -> /* program */

is a generic main taking any set of arguments.

drop_left = .. + b -> b

simply ignores everything to the right side of an addition expression,
including any nested expressions using parentheses, etc. To simply pass through
all arguments to a transformation, we could do the following:

pass_through = .. -> ..

#### structs

Data structures can be used to bundle together primitive data elements into new
types. This can be done via a type declaration:

mytype = { a' b. }

This defines a new type called `mytype` containing an int and a float. The
elements of a user-defined struct can be accesssed via the `.` operator, like

#### reading and writing

Reading and writing is done using the `>>` operator; pointing toward an
identifier is a read into that identifier; pointing away from an identifier is
a write from that identifier.

"hello world" >>  // write "hello world" to stdout
input <<          // read next line from stdin

#### importing and exporting code

Because of the language's multi-locale bent, modules and identifiers
may export themselves under an alias; they may also be imported under
an alias. Identifiers in particular must be explicitly exported to be
available via importing the module they are defined in, even if they
are not aliased. Identifiers may be exported into a "virtual" module
that does not correspond to single program file. A module of code is
either a program file that has not been aliased, or several program
files that have been aliased to the same name. In this case there must
be no duplicated public identifiers between the modules.

// inside foo.rx we export this module as bar
bar ==>

// export a transform (as itself)
plus_ten = a' -> a + 10 ==>

// export a transform as something else
plus_eleven a' -> a + 11 ==> p11

// export transform as something else in a different module
plus_twelve a' -> a + 12 ==> p12 : stupid_math

// inside another file

// import module foo under its export name bar
==> bar

// import module stupid_math as math
==> stupid_math : math

// access a transform from within an imported module
bar:plus_ten 1
math:plus_twelve 1

#### reflexive equivalence

In all the previous examples we've written from left to right, but
reflex can also be equivalently written from right to left.

z <- a = sdrawkcab
backwards a -> z
backwards == sdrawkcab  // true

#### locale equivalence

Reflex supports unicode characters in identifiers. Using this we can
define cross-locale equivalences. The simplest is a literal
translation using a free tranformation.

猫2foo =  -> foo
狗2bar =  -> bar