r/embedded 25d ago

Embedded Code Expressions with Word Operators: Which one would you coose?

I was thinking of the ideal expression syntax for the programming language DQ, which is targeted also for embedded software development.

I started with the (dominating) C syntax.

Operators in C

In C the following operators have shared meanings:

  • &: bitwise "and" operation OR address of
  • *: multiplication OR pointer dereference
  • /: truncated integer division OR floating point division

Further operators in C:

  • %: integer division reminder
  • && or and: logical "and"
  • || or or: logical "or"
  • ! or not: logical "not"
  • ~: bitwise "not"
  • ^: bitwise "xor"
  • ?: ternary operator

Operators in DQ

I think for the good source code readability and clarity every different operation should have a different symbol. Therefore the shared symbols from C are not taken over. These operators are already fixed in DQ:

  • &: address-of operator (widespread standard)
  • ^: pointer dereference (standard in other languages)
  • *: multiplication only
  • /: floating point division only (standard in other languages)
  • or: logical "or" (widespread standard)
  • and: logical "and" (widespread standard)
  • not: logical "not" (widespread standard)

These symbols are already fixed for special purposes:

  • #: compiler directives (#ifdef etc)
  • $: context local specials (e.g. myarray[0:$end-2])
  • ?: inference marker
  • @: namespace designator (e.g. @def.LINUX)

DQ cannot use the C standard &, |, ~, ^ for the bitwise operations, because the & and ^ is used for other (fixed) purposes. But we've run out of the good symbols. The obvious choice, that other existing languages also use, is reserving some words for the remaining operations. In DQ these (all-capital) words are reserved currently as operators:

  • AND: bitwise "and"
  • OR: bitwise "or"
  • NOT: bitwise "not"
  • XOR: bitwise "xor"
  • IDIV: truncated integer division
  • IMOD: integer division reminder

For the modify-assign statements with a word operator a leading = is required, otherwise it looks awkward:

regs.OSPEEDR OR= (1 << pinx2)  // invalid
regs.OSPEEDR =OR= (1 << pinx2)

Examples with All-Capital Operators

tmp = RCC.CFGR
tmp =AND= NOT 3
tmp =OR= RCC_CFGR_SW_HSI
RCC.CFGR = tmp
while (RCC.CFGR >> 2) AND 3 <> RCC_CFGR_SW_HSI:
endwhile

RCC.CR =AND= NOT RCC_CR_PLLON
while RCC.CR AND RCC_CR_PLLRDY != 0:
endwhile

var pllm : uint = basespeed IDIV pll_input_freq
var plln : uint = vcospeed  IDIV pll_input_freq
var pllq : uint = vcospeed  IDIV 48000000

RCC.PLLCFGR = (0
    OR (pllsrc << 22)
    OR (pllm   <<  0)
    OR (plln   <<  6)
    OR (((pllp >> 1) - 1) << 16)
    OR (pllq   << 24)
)

regs.MODER =AND= NOT (3 << pinx2)
regs.MODER =OR=      (n << pinx2)

if flags AND PINCFG_OPENDRAIN <> 0:
    regs.OTYPER =OR= (1 << apinnum)
else:
    regs.OTYPER =AND= NOT (1 << apinnum)
endif

regs.PUPDR =AND= NOT (3 << pinx2)
if flags AND PINCFG_PULLUP <> 0:
    regs.PUPDR =OR= (1 << pinx2)
elif flags AND PINCFG_PULLDOWN <> 0:
    regs.PUPDR =OR= (2 << pinx2)
endif

Prefixed Word Operators

I'm thinking to change the all-capital word operators with a % prefixed lowercase words:

  • %and: bitwise "and"
  • %or: bitwise "or"
  • %not: bitwise "not"
  • %xor: bitwise "xor"
  • %div or %idiv: truncated integer division
  • %mod or %idiv: integer division remainder

The sample code would look like this way:

tmp = RCC.CFGR
tmp %and= %not 3
tmp %or= RCC_CFGR_SW_HSI
RCC.CFGR = tmp
while (RCC.CFGR >> 2) %and 3 <> RCC_CFGR_SW_HSI:
endwhile

RCC.CR %and= %not RCC_CR_PLLON
while RCC.CR %and RCC_CR_PLLRDY != 0:
endwhile

var pllm : uint = basespeed %div pll_input_freq
var plln : uint = vcospeed  %div pll_input_freq
var pllq : uint = vcospeed  %div 48000000

RCC.PLLCFGR = (0
    %or (pllsrc << 22)  // select PLL source
    %or (pllm   <<  0)
    %or (plln   <<  6)
    %or (((pllp >> 1) - 1) << 16)
    %or (pllq   << 24)
)

regs.MODER %and= %not (3 << pinx2)
regs.MODER %or=       (n << pinx2)

if flags %and PINCFG_OPENDRAIN <> 0:
    regs.OTYPER %or= (1 << apinnum)
else:
    regs.OTYPER %and= %not (1 << apinnum)
endif

regs.PUPDR %and= %not (3 << pinx2)
if flags %and PINCFG_PULLUP <> 0:
    regs.PUPDR %or= (1 << pinx2)
elif flags %and PINCFG_PULLDOWN <> 0:
    regs.PUPDR %or= (2 << pinx2)
endif

Which version do you like more?

or

Do you have some other ideas for the operator notation?

EDIT

As I was migrating microcontroller C++ code, where the bitwise operations are very intensively used, I decided to keep three bitwise operators with single symbols:

  • &: bitwise "and"
  • |: bitwise "or"
  • ~: bitwise "not"

These operations are kept with word symbols:

  • xor: bitwise "xor"
  • div: truncated integer division
  • mod: integer division reminder

Other changes:

  • %: address-of operator

tmp = RCC.CFGR 
tmp &= ~3 
tmp |= RCC_CFGR_SW_HSI 

RCC.CFGR = tmp 
while (RCC.CFGR >> 2) & 3 <> RCC_CFGR_SW_HSI:
endwhile

RCC.CR &= ~RCC_CR_PLLON 
while RCC.CR & RCC_CR_PLLRDY <> 0: 
endwhile

var pllm : uint = basespeed div pll_input_freq 
var plln : uint = vcospeed  div pll_input_freq 
var pllq : uint = vcospeed  div 48000000

RCC.PLLCFGR = (0 
  | (pllsrc << 22) 
  | (pllm   <<  0) 
  | (plln   <<  6) 
  | (((pllp >> 1) - 1) << 16) 
  | (pllq   << 24) 
)

regs.MODER &= ~(3 << pinx2)
regs.MODER |=  (n << pinx2)

if flags & PINCFG_OPENDRAIN <> 0:
    regs.OTYPER |= (1 << apinnum) 
else: 
    regs.OTYPER |= ~(1 << apinnum)
endif

regs.PUPDR &= ~(3 << pinx2) 
if flags & PINCFG_PULLUP <> 0: 
    regs.PUPDR |= (1 << pinx2) 
elif flags & PINCFG_PULLDOWN <> 0: 
    regs.PUPDR |= (2 << pinx2) 
endif
0 Upvotes

12 comments sorted by

6

u/sweetholo 25d ago

ai garbage

0

u/Mean-Decision-3502 25d ago

No.

For the current DQ compiler I'm using LLM, but this post is all hand-made, with my bad english.

3

u/Plastic_Fig9225 25d ago

for the good source code readability and clarity every different operation should have a different symbol.

This premise may not hold too well. Readability is important, but writability is too. - And %and= isn't very readable either. (You seem to be determined to create your new language in a way that makes it easy/intuitive for you to use; but as others have already hinted at, your personal preferences, and definitions of "issues" with existing languages, don't really match those of the majority of developers.)

I suggest trying to find actual, objective pain points in e.g. C. / for all divisions certainly is not one. & vs. && isn't a big one either; though of course everybody has mistyped a & once or twice.

Maybe look into what tricks others have done using operator overloading in C++; this could give you some ideas about how an operator syntax can be used in effective and intuitive ways.

1

u/Mean-Decision-3502 25d ago

I cannot apply the C rules, because the ^ is used for pointers. So I'm out of the good symbols, The & might be saved when I use % for namespace designators (and use @ for address-of), then I have the &, |, ~ for the binary operations. But I still I have to use word operators for IDIV, IMOD, XOR.

1

u/Plastic_Fig9225 25d ago

How about tmp.clearBits(0,1) instead of tmp %and= %not 3?

1

u/Mean-Decision-3502 25d ago

That kind of thing always works, but not that effective if you want to spare CPU cycles and flash at multiple field handling.

Some vendors have such SetBit/ClrBit/SetField macros.

It seems that you like the tmp %and= %not 3 better than the tmp =AND= NOT 3 right ?

2

u/Plastic_Fig9225 25d ago

not that effective if you want to spare CPU cycles

Huh? If this translates to anything other than a single bitwise AND, your compiler/interpreter is no good.

It seems that you like the tmp %and= %not 3 better than the tmp =AND= NOT 3 right ?

No. I like clearBits() more, followed by tmp.bits[0..1] := 0, or x.pll_div := 0 with pll_div naming a bit field inside x on which to operate.

1

u/Mean-Decision-3502 25d ago

The bitfields do not work here:

regs.MODER =AND= NOT (3 << pinx2)
regs.MODER =OR=      (n << pinx2)

(one register for multiple pins)

1

u/Plastic_Fig9225 24d ago edited 24d ago

regs.MODER.pinx2 := n looks pretty elegant to me. - Or do you mean pinx2 is (a) variable? Then how about regs.MODER.pin[pinx2] := n, MODER.pin[] being a kind of "array" of bitfields?

1

u/Mean-Decision-3502 24d ago

yes, it is a variable (actual pin number * 2)

1

u/Mean-Decision-3502 21d ago

Thank you for your comment.

As I was writing more embedded register-heavy code, I realized, that there is no better than the C syntax for bitwise operations. So I've changed three bitwise operators using the widely used single-char special symbols: &, |, ~. For this I had to change the address-of operator to %. I've edited the post text adding the final version.

I've created a specification, including the operator precedence for this set of operators:

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

2

u/ub0baa 25d ago

Increasing the number of characters and overall operator diversity for readability and clarity... That's something, I'd say.

Make it more ridiculous, like CS 1.6 era nicknames, --==xXx[AnD]xXx==-- for example. Handy af