Beginner

Operators in BLISS

Learn arithmetic, comparison, bitwise, shift, and assignment operators in BLISS, including keyword operators and the distinctive dot dereference

Operators in BLISS reflect the language’s systems programming heritage and its typeless, expression-oriented design. Because every BLISS construct is an expression that produces a fullword value, operators combine freely to build larger expressions — including the value of an IF or a block.

BLISS makes two design choices that set its operators apart from C-family languages. First, comparisons use keyword operators (GTR, LSS, EQL, …) instead of symbols, leaving > and < available for other purposes (and avoiding ambiguity in the parser). Second, variable names evaluate to addresses, not values, so the unary dot (.) is effectively the most-used operator in any real BLISS program.

This tutorial walks through arithmetic, comparison, bitwise, shift, and assignment operators using a single program. Output is bridged to printf through a small C wrapper, the same pattern introduced in the Hello World tutorial.

The Operator Categories

CategoryOperatorsNotes
Arithmetic+ - * / MODInteger arithmetic on machine words
Comparison (signed)EQL NEQ GTR LSS GEQ LEQYield 1 (true) or 0 (false)
Comparison (unsigned)EQLU NEQU GTRU LSSU GEQU LEQUTreat operands as unsigned
Bitwise logicalAND OR NOT XOR EQVBit-by-bit on the full machine word
Shift^Positive count shifts left; negative shifts right
Assignment=An expression that returns the assigned value
Dereference. (unary prefix)Fetches the contents at an address

A few things worth noting:

  • NOT is bitwise, not boolean — NOT 0 is -1 (all bits set), not 1. BLISS treats a value as true when its low bit is 1, so NOT still inverts truthiness in conditional context.
  • MOD is a keyword like the comparison operators, not a % symbol.
  • There are no compound assignment operators like += or *= in BLISS — write x = .x + 1 instead.

The Program

This single BLISS module exercises each operator category and hands the results to C wrapper routines that print them. Each routine prints one labeled line of output.

Create a file named operators.bli:

MODULE operators =
BEGIN

EXTERNAL ROUTINE
    print_arith,
    print_comp,
    print_logic,
    print_shift,
    print_prec,
    print_assign,
    print_dot;

GLOBAL ROUTINE run_operators : NOVALUE =
BEGIN
    LOCAL a, b, x, y;

    a = 10;
    b = 3;

    ! Arithmetic: + - * / MOD all work on full machine words
    print_arith(.a + .b, .a - .b, .a * .b, .a / .b, .a MOD .b);

    ! Comparison: keyword operators yielding 1 (true) or 0 (false)
    print_comp(.a EQL .b, .a NEQ .b, .a GTR .b, .a LSS .b);

    ! Bitwise logical: AND OR XOR operate on every bit of the word
    print_logic(12 AND 10, 12 OR 10, 12 XOR 10);

    ! Shift: ^ takes a positive count for left shift, negative for right shift
    print_shift(1 ^ 4, 64 ^ -2);

    ! Precedence: * binds tighter than +, parentheses force grouping
    print_prec(2 + 3 * 4, (2 + 3) * 4);

    ! Assignment is itself an expression returning the assigned value
    y = 100;
    x = (y = .y + 5);
    print_assign(.x, .y);

    ! The dot operator: bare name = address, .name = contents
    a = 42;
    b = .a + 1;
    print_dot(.a, .b)
END;

END
ELUDOM

Create a file named wrapper.c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>

/* blissc uppercases every BLISS identifier in the generated object file,
 * so the C names below match the BLISS source after that translation. */
void RUN_OPERATORS(void);

void PRINT_ARITH(long add, long sub, long mul, long quot, long rem) {
    printf("Arithmetic: 10+3=%ld, 10-3=%ld, 10*3=%ld, 10/3=%ld, 10 MOD 3=%ld\n",
           add, sub, mul, quot, rem);
}

void PRINT_COMP(long eq, long ne, long gt, long lt) {
    printf("Comparison: 10 EQL 3=%ld, 10 NEQ 3=%ld, 10 GTR 3=%ld, 10 LSS 3=%ld\n",
           eq, ne, gt, lt);
}

void PRINT_LOGIC(long andv, long orv, long xorv) {
    printf("Bitwise: 12 AND 10=%ld, 12 OR 10=%ld, 12 XOR 10=%ld\n",
           andv, orv, xorv);
}

void PRINT_SHIFT(long lshift, long rshift) {
    printf("Shift: 1 ^ 4 = %ld, 64 ^ -2 = %ld\n", lshift, rshift);
}

void PRINT_PREC(long no_paren, long with_paren) {
    printf("Precedence: 2 + 3 * 4 = %ld, (2 + 3) * 4 = %ld\n",
           no_paren, with_paren);
}

void PRINT_ASSIGN(long x, long y) {
    printf("Assignment as expression: x = %ld, y = %ld\n", x, y);
}

void PRINT_DOT(long a_val, long b_val) {
    printf("Dot operator: .a = %ld, .b = .a + 1 = %ld\n", a_val, b_val);
}

int main(void) {
    RUN_OPERATORS();
    return 0;
}

Walking Through the Operators

Arithmetic

+ - * / behave as expected on machine words, and integer division truncates toward zero. MOD is a keyword, so it sits between operands like any other binary operator: .a MOD .b. There is no exponentiation operator in core BLISS.

Comparison and the Truth Convention

Each comparison yields exactly 1 for true and 0 for false. In conditional context, however, BLISS does not insist on those exact values: any expression with a 1 in the low bit is treated as true. That is why NOT continues to invert truthiness even though it is a bitwise operator.

Unsigned variants (EQLU, GTRU, …) exist because BLISS is typeless — the compiler cannot guess whether .x was meant as signed or unsigned, so the operator carries the interpretation.

Bitwise Logic

AND, OR, XOR, EQV, and NOT operate bit-by-bit across the entire machine word. For 12 AND 10:

12 = ...0000 1100
10 = ...0000 1010
AND  ...0000 1000  = 8

The Shift Operator

The single shift operator ^ reads as “shift by.” A positive right-hand side shifts left; a negative right-hand side shifts right. 1 ^ 4 shifts 1 left by four positions to produce 16. 64 ^ -2 shifts 64 right by two positions, also producing 16.

Precedence and Associativity

BLISS precedence runs roughly: dot (highest) → unary minus → * / MOD+ - → shift → comparison → NOTANDOR XOR EQV → assignment (lowest). Parentheses override grouping just as in C.

Assignment as an Expression

Since every BLISS construct returns a value, so does =. The statement x = (y = .y + 5) evaluates .y + 5, stores it into y, and the assignment expression yields the new value, which is then stored into x. Both end up with 105.

The Dot, Revisited

Without the dot, .a + 1 would attempt to add 1 to the address of a — a meaningful operation in BLISS, but rarely what you want for arithmetic. Reading b = .a + 1 aloud as “store into b the contents of a plus one” matches what the compiler does.

Running with Docker

1
2
3
4
5
6
# Pull the BLISS image (custom blissc compiler)
docker pull codearchaeology/bliss:latest

# Compile, link, and run the operators example
docker run --rm -v $(pwd):/app -w /app codearchaeology/bliss:latest \
    sh -c 'blissc -o operators.o operators.bli && gcc -o operators wrapper.c operators.o && ./operators'

The two-step build mirrors the Hello World tutorial: blissc lowers BLISS to a native object file through LLVM, then gcc links it with the C wrapper containing main and the I/O bridge routines.

Expected Output

Arithmetic: 10+3=13, 10-3=7, 10*3=30, 10/3=3, 10 MOD 3=1
Comparison: 10 EQL 3=0, 10 NEQ 3=1, 10 GTR 3=1, 10 LSS 3=0
Bitwise: 12 AND 10=8, 12 OR 10=14, 12 XOR 10=6
Shift: 1 ^ 4 = 16, 64 ^ -2 = 16
Precedence: 2 + 3 * 4 = 14, (2 + 3) * 4 = 20
Assignment as expression: x = 105, y = 105
Dot operator: .a = 42, .b = .a + 1 = 43

Key Concepts

  • Comparisons are keyword operatorsEQL, NEQ, GTR, LSS, GEQ, LEQ for signed values, plus EQLU/GTRU/… for unsigned interpretation.
  • Logical operators are bitwiseAND, OR, XOR, EQV, and NOT operate on every bit; BLISS uses the low bit as its truth indicator in conditional context.
  • One shift operator handles both directionsvalue ^ count shifts left for positive count and right for negative count.
  • No compound assignment — write x = .x + 1; there is no += or ++ in BLISS.
  • Assignment is an expressionx = (y = expr) works because every construct, including =, produces a value.
  • The dot operator participates in expressions.a + .b reads contents of two variables and adds them, while a + b would add their addresses.
  • MOD is a keyword, not a symbol — there is no % operator in BLISS.
  • Parentheses cost nothing — when in doubt about precedence, group explicitly; the optimizing compiler removes any redundancy.

Running Today

All examples can be run using Docker:

docker pull codearchaeology/bliss:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining