Describish

From Esolang
Jump to navigation Jump to search

Describish is an esoteric programming language created by Pedro Gimeno in 2008.

It is strongly inspired by Gregor Richards' ORK. It was specifically designed so that a CSS descrambler could be written in it in such a way that it could not be distinguished from a clear, readable English description of the algorithm, with an improved readability over such attempts as the C-to-English version and the version filtered through BabelBuster.

There is no interpreter or compiler of the language currently. It's an open question whether creating an interpreter for it will be illegal, or whether when an interpreter or compiler is created, the CSS algorithm description written in Describish will become illegal.

Currently, the algorithm description written in Describish is protected as free speech.

Syntax

Programs in this language are written in plain text. As of yet, there is no encoding specified.

A program is made by writing a sequence of sentences or type definitions. Every sentence or type definition is ended with a period, except a function header which is ended by a colon.

There are variables, types (user defined and predefined), and functions with a limited number of parameters.

Unlike ORK, identifiers for variables, types and functions can't have spaces. Dashes can be used instead. Identifiers are case-insensitive. Every appearance of 'a' as a syntax element can be replaced with 'an', to facilitate writing proper English. Here we will use both indistinctly.

The predefined types are:

  • text: A string class (a sequence of bytes), having:
    • The property 'length' (length of the string).
    • The property 'code' (the numeric code of the first character of the string; this property can also be written to).
  • character: Alias for 'text'.
  • integer: An integer number class having:
    • The property 'sign' (returns either the integer 1, the integer 0 or the integer -1).
    • The property 'negation' (returns -value if it exists; otherwise undefined).
    • The property 'absolute-value'.
  • number: An IEEE-754 double precision floating-point numeric class having:
    • IEEE-754 round-to-nearest-or-even support only.
    • IEEE-754 semantics for addition, subtraction, multiplication, division and square roots.
    • Support for IEEE-754 denormals, infinity and quiet NaNs.
    • The property 'sign' (same as with integer).
    • The property 'negation' (same as with integer).
    • The property 'absolute-value' (same as with integer).
    • The property 'inverse' (calculates 1/value returning a number).
    • The property 'floor' (returns an integer).
    • The property 'ceiling' (returns an integer).
  • array of <type>: This is a special type, creating an array of elements with said type. Only types which resolve to the base types are allowed: text, integer or number. There's currently no support for multidimensional arrays or arrays of complex types.

User type definitions are written like this:

A <new type> {is|points to} a <type>.

The syntax for pointers is not yet decided, so at the moment only the 'is' variant is acceptable. Composite types can be declared like this:

A <new type> {is|points to} a container having:
 <variable>, which is a <type>,
 ...
and that's all a <new type> has.

A syntax in which the last member starts with 'and' and ends with a period, as opposed to having a separate line that closes the declarations, is under consideration.

There's only one predefined function:

  • concatenate, accepting two parameters of type text.

Expressions can be either constant expressions or variable expressions. The syntax for constant expressions is:

  • <sequence of digits> - An integer. Example: 31
  • <sequence of digits>.<sequence of digits> - A number. Example: 3.1
  • "<sequence of characters>" - A text. Example: "this is a text"

Variable expressions can be:

  • <variable>
  • <array> at position <expression>
  • <container>'s <field>
  • <object>'s <property>
  • the [first|second|third|fourth|fifth|sixth] {<type>|operand} [to <function name>]
  • the [last] [<function>] result

Conditions:

  • <expression> is [not] equal to <expression>
  • <expression> is [not] greater than <expression>
  • <expression> is [not] less than <expression>
  • <expression> is [not] empty
  • <expression> is [not] negative
  • <expression> is [not] numeric
  • <expression> is [not] infinite

'empty' tests if a text expression is an empty string. 'numeric' tests if a number expression is not NaN.

The equivalent to C's main() consist only of one function call without arguments. The function to be called at the start is specified using this syntax:

Start to <function>.

Function header:

To <function> [[with|using] a <type> [{{,|and|with|using}... a <type>}...]]:

It must precede every function definition. It is ended by another function header, a type definition, the 'Start to' statement, or the end of program.

In the first part, if either 'with' or 'using' are used, they are ignored. In the second part, there's no difference in semantics with either 'using', 'with', 'and' or ','.

Conditional statement:

If <condition> then <statement>.

To declare a local variable:

Say <variable> is a <type>.

Assignment:

Set <variable expression> to <expression>.

Comments are enclosed in square brackets. An alternative syntax using parentheses instead of square brackets is being considered.

Input has two possible forms:

Read an <input type> into <var-expression>.
Read one <text type> into <var-expression>.

The first form accepts integers, numbers or text. It is not fully defined at the moment. The second form reads one character at a time, setting the destination to an empty string in case of end of file.

Output has two variants as well:

Output an <output type> from <expression>.
Output one <text type> from <expression>.

Loop start:

Repetition will start here.

Loop end:

Repeat again.

Every loop start must be matched by a loop end and vice versa. The loop end can be part of an if statement; the loop start can't. Nesting loops is possible, but discouraged for readability reasons: creating a separate function containing the inner loop is preferred when possible.

Math statments:

Add <expression> to <variable expression>.
Subtract <expression> from <variable expression>.
Multiply <variable expression> by <expression>.
Divide <variable expression> by <expression>.
Put the remainder of dividing <expression> by <expression> into <variable expression>.

Function call:

Now <function name> [[with|using] <function-arg> [{{,|and|with|using}... <function-arg>}...]].

Return from function, returning a value:

Return <expression>.

Return from function without returning a value:

Return.

The returned value can be used with the expression "the [last] [<function>] result", which acts as a constant of the type returned by the last function called.

Examples

The CSS descrambler algorithm description follows (it is at the same time a program written in language Describish), based on a mathematical description by Charles M. Hannum:

[This is a formal explanation of how to decrypt sectors encrypted
 with a CSS cipher. At the same time it is a program written in
 the Describish programming language.]

[Definitions:]
A key is an array of integer.
A sector is an array of integer.


[Main procedure. Reads a key, sets it up and performs the main
  read-sector / decrypt / write-sector process until EOF is found.]
To readkey-read-decrypt-and-write:
Say n is an integer.
Say character-read is a character.
Say decryption-key is a key.

Set n to 0.
Repetition will start here.
Read one character into character-read.
Set decryption-key at position n to character-read's code.
Add 1 to n.
If n is not equal to 5 then repeat again.

Repetition will start here.
Now read-decrypt-and-write using decryption-key.
If the result is not equal to 0 then repeat again.


[The following procedure describes how to process each
 sector, reading it, performing the decryption and
 writing it back.]
To read-decrypt-and-write with a key:
Say n is an integer.
Say sector-data is a sector.
Say character-read is a character.
Say character-to-write is a character.

Set n to 0.
Repetition will start here.
Read one character into character-read.
[Exit on EOF:]
If character-read is empty then return 0.
Set sector-data at position n to character-read's code.
Add 1 to n.
If n is not equal to 2048 then repeat again.

Now decrypt sector-data using the key.

Set n to 0.
Repetition will start here.
Set character-to-write's code to sector-data at position n.
Output one character from character-to-write.
Add 1 to n.
If n is not equal to 2048 then repeat again.

Return 1.


[Main decryption procedure.]
To decrypt a sector with a key:
Say sector-specific-key is a key.
Say n is an integer.
Say previous-LFSR-0 is an integer.
Say LFSR-0 is an integer.
Say LFSR-0-helper is an integer.
Say previous-LFSR-1 is an integer.
Say LFSR-1 is an integer.
Say LFSR-1-helper is an integer.
Say previous-LFSR-combination is an integer.
Say LFSR-combination is an integer.
Say temp is an integer.

[Duplicate the key prior to setting up the sector-specific-key using the sector.]
Set n to 0.
Repetition will start here.
Set sector-specific-key at position n to the key at position n.
Add 1 to n.
If n is not equal to 5 then repeat again.

Now set-up sector-specific-key with the sector.

Set LFSR-0 to sector-specific-key at position 0.
Multiply LFSR-0 by 512.
Add 256 to LFSR-0.
Add sector-specific-key at position 1 to LFSR-0.

Set temp to sector-specific-key at position 2.
Divide temp by 32.
Put the remainder of dividing temp by 8 into LFSR-1.
Multiply LFSR-1 by 131072.
Add 2097152 to LFSR-1.
Put the remainder of dividing sector-specific-key at position 2 by 32 into temp.
Multiply temp by 65536.
Add temp to LFSR-1.
Set temp to sector-specific-key at position 3.
Multiply temp by 256.
Add temp to LFSR-1.
Add sector-specific-key at position 4 to LFSR-1.

Set LFSR-combination to 0.

[Main decoding loop.]
Repetition will start here.

Set previous-LFSR-0 to LFSR-0.
Set previous-LFSR-1 to LFSR-1.
Set previous-LFSR-combination to LFSR-combination.

Set temp to previous-LFSR-0.
Divide temp by 16384.
Now xor temp and previous-LFSR-0.
Set LFSR-0-helper to the result.

Set LFSR-0 to previous-LFSR-0.
Divide LFSR-0 by 256.
Set temp to LFSR-0-helper.
Multiply temp by 512.
Now xor temp and LFSR-0.
Set LFSR-0 to the result.
Multiply temp by 8.
Now xor temp and LFSR-0.
Set LFSR-0 to the result.
Put the remainder of dividing LFSR-0-helper by 4 into temp.
Multiply temp by 32768.
Now xor temp and LFSR-0.
Put the remainder of dividing the result by 131072 into LFSR-0.

Set temp to previous-LFSR-1.
Divide temp by 8.
Now xor temp and previous-LFSR-1.
Set LFSR-1-helper to the result.
Divide temp by 2.
Now xor temp and LFSR-1-helper.
Set LFSR-1-helper to the result.
Divide temp by 256.
Now xor temp and LFSR-1-helper.
Set LFSR-1-helper to the result.

Put the remainder of dividing LFSR-1-helper by 256 into LFSR-1.
Multiply LFSR-1 by 131072.
Set temp to LFSR-1-helper.
Divide temp by 256.
Now xor temp and LFSR-1.
Put the remainder of dividing the result by 33554432 into LFSR-1.

Set temp to LFSR-0.
Divide temp by 512.
Put the remainder of dividing temp by 256 into temp.
Set LFSR-combination to 255.
Subtract temp from LFSR-combination.
Set temp to LFSR-1.
Divide temp by 131072.
Add temp to LFSR-combination.
Set temp to previous-LFSR-combination.
Divide temp by 256.
Add temp to LFSR-combination.
Put the remainder of dividing LFSR-combination by 256 into LFSR-combination.

Now PAL-mangle the sector at position n.
Set temp to the result.
Now xor temp with LFSR-combination.
Set the sector at position n to the result.

Add 1 to n.
If n is not equal to 2048 then repeat again.


[Decryption key set-up procedure]
To set-up a key with a sector:
Say n is an integer.
Say n-plus-84 is an integer.
Set n to 0.
Set n-plus-84 to 84.

Repetition will start here.
Now xor the key at position n with the sector at position n-plus-84.
Now bit-reverse the result.
Set the key at position n to the bit-reverse result.
Add 1 to n.
Add 1 to n-plus-84.
If n is not equal to 5 then repeat again.


[Bit-reversal operation]
To bit-reverse an integer:

Say result is an integer.
Say processing-number is an integer.
Say bits is an integer.
Say last-bit is an integer.

Set processing-number to the integer to bit-reverse.
Set bits-processed to 0.

Repetition will start here.

Put the remainder of dividing processing-number by 2 into last-bit.
Multiply result by 2.
Add last-bit to result.
Divide processing-number by 2.
Add 1 to bits-processed.
If bits-processed is not equal to 8 then repeat again.

Return result.

[XOR operation]
To xor an integer with an integer:
Say operand-1 is an integer.
Say operand-2 is an integer.
Say bit-1 is an integer.
Say bit-2 is an integer.
Say end-of-loop is an integer.
Say running-bit is an integer.
Say accumulated-result is an integer.
Set operand-1 to the first integer to xor.
Set operand-2 to the second integer to xor.
Set running-bit to 1.
Set accumulated-result to 0.

Repetition will start here.
Put the remainder of dividing operand-1 by 2 into bit-1.
Put the remainder of dividing operand-2 by 2 into bit-2.
If bit-1 is not equal to bit-2 then add running-bit to accumulated-result.
Divide operand-1 by 2.
Divide operand-2 by 2.
Multiply running-bit by 2.
Set end-of-loop to 1.
If operand-1 is not equal to 0 then set end-of-loop to 0.
If operand-2 is not equal to 0 then set end-of-loop to 0.
If end-of-loop is equal to 0 then repeat again.
Return accumulated-result.

To PAL-mangle an integer:
Say a is an integer.
Say b is an integer.
Say c is an integer.
Say d is an integer.
Say e is an integer.
Say f is an integer.
Say g is an integer.
Say h is an integer.
Say A2 is an integer.
Say B2 is an integer.
Say C2 is an integer.
Say D2 is an integer.
Say E2 is an integer.
Say F2 is an integer.
Say G2 is an integer.
Say H2 is an integer.
Say parameter is an integer.
Say result is an integer.

Set parameter to the integer to PAL-mangle.
Put the reminder of dividing parameter by 2 into a.
Divide parameter by 2.
Put the reminder of dividing parameter by 2 into b.
Divide parameter by 2.
Put the reminder of dividing parameter by 2 into c.
Divide parameter by 2.
Put the reminder of dividing parameter by 2 into d.
Divide parameter by 2.
Put the reminder of dividing parameter by 2 into e.
Divide parameter by 2.
Put the reminder of dividing parameter by 2 into f.
Divide parameter by 2.
Put the reminder of dividing parameter by 2 into g.
Divide parameter by 2.
Set h to parameter.

Set A2 to a.
Multiply A2 by b.
Add d to A2.
Add 1 to A2.
Put the remainder of dividing A2 by 2 into A2.
Set B2 to e.
Multiply B2 by f.
Add g to B2.
Add 1 to B2.
Put the remainder of dividing B2 by 2 into B2.
Set C2 to A2.
Multiply C2 by B2.
Add f to C2.
Add 1 to C2.
Put the remainder of dividing C2 by 2 into C2.
Set D2 to A2.
Multiply D2 by B2.
Add b to D2.
Add 1 to D2.
Put the remainder of dividing D2 by 2 into D2.
Set E2 to a.
Multiply E2 by b.
Add c to E2.
Add 1 to E2.
Put the remainder of dividing E2 by 2 into E2.
Set F2 to e.
Add f to F2.
If F2 is less than 2 then add 1 to F2.
Add h to F2.
Put the remainder of dividing F2 by 2 into F2.
Set G2 to E2.
Multiply G2 by F2.
Add a to G2.
Add 1 to G2.
Put the remainder of dividing G2 by 2 into G2.
Set H2 to E2.
Add F2 to H2.
If H2 is less than 2 then add 1 to H2.
Add e to H2.
Put the remainder of dividing H2 by 2 into H2.

Set result to H2.
Multiply result by 2.
Add G2 to result.
Multiply result by 2.
Add F2 to result.
Multiply result by 2.
Add E2 to result.
Multiply result by 2.
Add D2 to result.
Multiply result by 2.
Add C2 to result.
Multiply result by 2.
Add B2 to result.
Multiply result by 2.
Add A2 to result.
Return result.


Start to readkey-read-decrypt-and-write.

Computational class

Arrays are unlimited in size. Array indices, on the other hand, are limited by the maximum integer range. The maximum size of integer is not defined, but if arbitrary integers are allowed it should be trivial to write an universal Turing machine with unlimited tape in Describish, thus proving its Turing completeness.

See also