Boolexps

Submitted by raevnos on Mon, 2007-06-04 22:53

A boolexp is the key part of a @lock: #1, sex:M*, canpass/1, etc. This page talks about their implementation and describes how to add a new type of key.

Well... a new key of a 'X^Y' sort. Adding new tokens is tricky for a couple of reasons: There just aren't that many possible ASCII characters left, and it's a lot more work than extending ^. (Which really should have been used to mean an exclusive-or to go along with & and |, but too late now. Too bad I didn't think of it back when...).

Implementation

All boolexp-related code is in src/boolexp.c.

Keys are transformed into a parse tree by a hand-written recursive descent parser (The top function is parse_boolexp_E()). This parse tree is then compiled to bytecode. The bytecode itself is usually stored in the chunk manager; the boolexp field of the lock structure holds a chunk reference.

Some locks are checked very frequently, hence the use of bytecode to make evaluating a boolexp as fast as possible.

Adding a new key type

Adding a completely new key type involves extending the parser and "assembler", and the VM function that evaluates the bytecode, and the function that converts bytecode to a string for display, and and and...

Adding a new ^-type key involves several fewer steps (But still quite a few). This is a brief guide; look at the comments and existing code in src/boolexp.c for hints:

  1. Add a new opcode to the bvm_opcode enum.
  2. Add a new entry to the flag_locks array.
  3. Add a case for the new opcode to the switch in eval_boolexp(). The variable r is the result register; set it to 1 if the key succeeded, 0 if it failed. The value of (char*)(bytecode + arg) is the string on the right hand side of the ^.
  4. Add a case for the new opcode to the switch in unparse_boolexp() to convert the bytecode into a string. Use OP_TFLAG as a template.
  5. Add a case for the new opcode to the switch in emit_bytecode(). Just adding it to the same group as OP_TFLAG and letting it fall through is usually all that's needed here.
  6. Add a case for the new opcode to the switch in print_boolexp() to print out a pseudo-assembly instruction. This is used in debugging the boolexp compiler.
  7. Optionally add a case for the new opcode to the switch in check_lock() to carry out @warnings checks.

Future

emit_bytecode() might move from a switch to, on GCC, an optimized jump table. Macros will be used to choose which version to use with only one evaluation function, using the same approach as the ocaml bytecode VM.