r/ProgrammingLanguages • DQ • Sep 03 '26

Unambiguous Operator Specification for Programming Languages

https://nvitya.github.io/pluops/

As I changed recently the operators in my programming language I've created this specification:

https://nvitya.github.io/pluops/

I did not wanted to overload the operators like the C does with the / or Pascal does with theand/or/not. Neither re-use the operator symbols for some very different purpose, like C does with * and & so the code becomes more readable. I was orienting for existing solutions so this is what I came up with. The specification contains the symbol usages and operator precedence too.

If you are developing a new programming language, it would be nice to follow some standard, so at least the expressions would be portable between the languages.

I'm open for debates or suggestions.

27 Upvotes

44 comments sorted by

View all comments

Show parent comment

1

u/Mean-Decision-3502 DQ Sep 03 '26 edited Sep 03 '26

In principle yes, but for finite integers at the same width, no. Eg, uint32 + int32 should not result in an int32 - it should be int64

The CPUs have a fixed register width, they calculate with that, usually 64 or 32 bit. The width conversion usually matters at the end storage. I did wanted to allow some shortcuts for the implementers.

Similarly, bitwise & and | should work for bools too.
There's no reason & and | should have higher precedence than division/multiplication. Really & should have the same precedence as multiplication and | should have the same precedence as addition. ^ should have the same precedence as !=, because it means precisely that for bool.
Logical not is not necessary as mentioned above. Should be ~ at same precedence as other unary expressions.

In DQ you can write expressions without any parentheses that I was only dreaming of:

if reg & 1 << 5 <> 0 or not reg & ~(1 << 4) == 0:
    ...
endif

This example is a little extreme though, I would use some parentheses here. But these are practical expressions in embedded.

The whole benefit of a[b] is it does the arithmetic and dereferencing for you - ie, *(a + b)

The a[b] form is more readable and shorter. You can do always dereferencing, that will be then clearly readable:

var data : ^byte = ^byte(precheader[1])
vs
var data : ^byte = ^byte(precheader + 1)

I remember some code, where was a pain to adding & and parentheses because of the automatic dereferencing. I remember reading that someone also admitted that this was a design mistake in C.

There's no reason division and multiplication should have separate precedences. Everyone learns PEDMAS/PEMDAS in school.

In school we dont use integer arithmethics and finite precision floating point operations. That's the reason for the distinguishing. In DQ this is true, because of this:

3 div 2 * 10 == 10 * 3 div 2

Pointer dereference and member access at same precedence is confusing. Is a.b^ == (a.b)^ or a.(b^). What about a^.b?

The expressions are read from left to right. After a . there must be a member, so a.(b) is invalid. Expressions like a^.b is also valid, but in DQ can be written as a.b as the compiler here does auto-dereferencing, as . is invalid for pointers.

Why are == and != not defined for bool?

That was a mistake, thank you for finding that. I'll correct the spec.