why collect garbage when you could not
Find a file
2022-09-22 22:52:41 -04:00
complexpr aaa more ambiguity fixed 2022-09-22 22:34:23 -04:00
complexpr-bin Improved bin/repl functionality 2022-09-22 17:36:20 -04:00
examples new range syntax 2022-09-20 23:38:58 -04:00
.gitignore clippy moment 2022-09-11 13:02:18 -04:00
Cargo.lock update Cargo.lock 2022-09-22 17:41:34 -04:00
Cargo.toml project reorganization, general improvements 2022-09-16 23:06:49 -04:00
LICENSE refactoring, reorganization 2022-09-17 11:17:22 -04:00
README.md begin README documentation 2022-09-22 22:52:41 -04:00

complexpr

complexpr is a programming language, for a sufficiently inclusive definition of the term. It can be used to accomplish many tasks that the average programmer would wish to accomplish, including:

  • Printing "Hello, world!" (and over 3 other strings!)
  • Computing factorials
  • Finding prime numbers
  • Simulating arbitrary Turing machines
  • And ∞ more!

To install complexpr, simply clone this repository, cd complexpr-bin, and cargo install --path .. You can then use the complexpr command to start a REPL, or pass a filename as an argument to read from a file.

Primitive types

complexpr features a wide variety of primitive types, suitable for all your programming needs.

Type Description Example values
Nil Represents nothing (like null or None) nil
Int 64-bit signed integer 35, -1003
Float 64-bit floating-point number 10.3, 0.0061
Complex Complex number, represented as a pair of 64-bit floats 1.5 - 2i
Rational Rational number, represented as a pair of 64-bit integers 3//5, -11//25
Bool Boolean type, either true or false true, false
Char A single Unicode character 'A', '\n'
List A heterogeneous list [1, 2.0, "3"]
Map A heterogeneous hashmap {1: "one", "2": 2}
Func A function max, fn(x,y) (x^y)

Complex numbers can be written as the sum of a real component and imaginary component, where the imaginary component is suffixed with an i. For example, 5i, -1.3+3i, and 0.5-0.2i are all valid complex numbers.

Rational numbers are defined using the rational division operator //, described below.

Lists

Escape characters

Character and string literals can use the following escape codes to represent characters:

Escape Character
\\ Backslash \
\' Single quote '
\" Double quote "
\0 Null byte (0x00)
\t Tab (0x09)
\n Newline (0x0A)
\r Carriage return (0x0D)
\e Escape (0x1B)
\x## Arbitrary character (one byte codepoint)
\u{##…} Arbitrary character (any size codepoint)

Strings can extend across multiple lines, and will include the newlines by default. To prevent the newlines from being included, escape them using a \ at the end of the line.

Arithmetic operators

Data is rather useless if there is no way to operate it. As such, complexpr also includes several operators, the most basic of these being arithmetic operators.

Operator Description Example usage
+ Addition, concatenation of strings/chars/lists 1 + 3.0, "ab" + 'c'
- Subtraction 3 - 2
* Multiplication, repeating a list n times 5 * 7.3, [0] * 10
/ Division 22.3 / 5.1
% Modulo (remainder after division) 27 % 4
^ Exponentiation 5^2, 0.2^(-3)
// Rational division (results in a rational type) 5//3, -1//10

All of these operators (except //) follow the following rule when applied to numeric operands of differing types: the type of the result is the more generic of the two operand types. In order of increasing genericness, Int, Rational, Float, Complex. As an example, the type of 5.0 + 3//2 is Float, since floats are more generic than rationals

Unlike the other operators, // is only valid for integer and rational arguments, and always results in a rational value.

+ can also be used for concatenating chars and strings to each other. The result of this is always a string. + can also be used to concatenate lists. * can be used for repeating a list or string a certain number of times, for example [0,1,2] * 3 = [0,1,2,0,1,2,0,1,2].

^ has the highest precedence and is right-associative (so, 2^3^4 = 2^(3^4)). *, /, %, and // have the next highest precedence, followed by + and -.

Comparison operators

In addition to operating on data, it is also desirable to compare it to other data. complexpr has the standard set of comparison operators plus a fairly uncommon one.

Operator Description Example usage (all true)
== Equality 5 == 5.0
!= Inequality 3//5 != 0.2
> Greater than 10 > -10
< Less than 3 < 4.0
>= Greater than or equal to 5//12 >= 0.01
<= Less than or equal to 0.03 <= 3.03
<=> Spaceship (see below) (4 <=> 5) == -1

Equality and inequality will always succeed, regardless of the types of the arguments. Numeric types which have equal values will compare as equal, even if the types are different.

Comparison operators can be applied to real numbers (not Complex), chars, strings, and lists. Characters are compared by their codepoints, and strings and lists are compared lexicographically.

The spaceship operator results in 0 if the arguments are equal, 1 if the left-hand side is greater, or -1 if the right-hand side is greater.

Variables, assignment, and scope

Variables are declared using the keyword let. For example, let number = 12; declares a variable named number with the value of 12. The variable does not need to be assigned to right away, in which case it defaults to nil (for example: let number;). let can only be used in statements (we'll get to the difference between statements and expressions later).

Use a single equals sign (=) to reassign a previously declared variable. As with many other programming languages, you can also use compound assignment operators to modify a variable's value. These include +=, -=, *=, /=, %=, ^=, and //=, and do exactly what you would expect. These cannot be used in let statements, for obvious reasons.

complexpr uses lexical scoping, so a variable is only valid in the block it was declared in, as well as all inner blocks. When assigning to a variable, if multiple outer scopes contain a declaration for the variable, the innermost one is modified.

Control flow

complexpr offers three procedural-style methods of control flow: if statements, while loops, and for-each loops. If statements use the keywords if for the first condition, elif for all subsequent conditions, and else for the else clause. Conditions do not need to be parenthesized. The bodies of an if statement should be enclosed in braces (currently this is not required if the body is one statement long, but this may change in the future).

While loops are syntactically very similar, using the keyword while and also not requiring parentheses for the condition.

For-each loops use the keyword for, followed by the loop variable, a colon, and then the collection to iterate over (see below for information regarding iterators).

Examples:

let value = 5;
if value > 3 {
    while value > 4 {
        value -= 0.3;
    }
} elif value == 2 {
    for n: [1,3,5,4] {
        value += n;
    }
} else {
    value -= 1;
}

Functions

todo!();