Let's make a programming language, kinda
The Challenge
We need a tiny language to express combinations of permissions
// Basic expressions
has_permissions('profile:read')
// Basic combinations
has_permissions('transactions:read' || 'financials:manage')
// Compound expressions, with parentheses
has_permissions(('transactions:read' && 'expenses:read') || 'financials:manage')
How to get lost in too much theoretical mumbo-jumbo and forget that you have a day job and still need to pay the bills
DSLs
a.k.a
(Domain Specific Language)
So, like, where do you start?
Expressions
class UnaryExpr {
value = undefined;
constructor(value) {
this.value = value;
}
or(other) {
return new BinaryExpr(this, other, '||');
}
and(other) {
return new BinaryExpr(this, other, '&&');
}
toString() {
return `${this.value}`;
}
}
Unary Expressions
class BinaryExpr {
left = undefined;
right = undefined;
op = undefined;
constructor(left, right, op) {
this.left = left;
this.right = right;
if (op !== '||' && op !== '&&') {
throw Error(`invalid operator value: ${op}`);
}
this.op = op;
}
or(other) {
return new BinaryExpr(this, other, '||')
}
and(other) {
return new BinaryExpr(this, other, '&&')
}
toString() {
return `(${this.left} ${this.op} ${this.right})`;
}
}
Binary Expressions
const expr = new BinaryExpr('left', 'right', '||');
const e2 = expr.and(new BinaryExpr('lhs', 'rhs', '&&'));
console.log(e2.toString());
> (left && (lhs && rhs))
Put 'em together
class Permission extends UnaryExpr {}
const trxRead = new Permission('transactions:read');
const trxWrite = new Permission('transactions:write');
const finManage = new Permission('financials:manage');
const perms = trxRead.and(trxWrite).or(finManage);
console.log((perms.toString()));
> (transactions:read || financials:manage)
const perms = trxRead.and(trxWrite).or(finManage);
console.log((perms.toString()));
> ((transactions:read && transactions:write) || financials:manage)
CanIDoThatPlzThankYou
"Abstract" Syntax Tree
We don't need no stinkin' AST!
Wait, actually, we do need one
Oh, we already have one! kinda
Just don't look too closely...
The "Interpreter"
class Interpreter {
constructor(permissions) {
this.permissions = permissions || [];
}
exec(expr) {
if (expr instanceof lang.UnaryExpr) {
return this.permissions.includes(expr.value);
}
if (expr instanceof lang.BinaryExpr) {
switch (expr.op) {
case '&&':
return this.exec(expr.left) && this.exec(expr.right);
case '||':
return this.exec(expr.left) || this.exec(expr.right);
default:
throw Error(`RuntimeError: invalid operator ${expr.op}`);
}
}
throw Error(`RuntimeError: unsupported expression type ${expr}`);
}
}
Run It
const lang = require('./minilang');
const runtime = require('./runtime');
const trxRead = new lang.Permission('transactions:read');
const trxWrite = new lang.Permission('transactions:read');
const finManage = new lang.Permission('financials:manage');
const userPerms = ['transactions:read', 'expenses:read'];
const int = new runtime.Interpreter(userPerms);
const trxReadAndWrite = trxRead.and(trxWrite);
console.log(`User can read/write transactions: ${int.exec(trxReadAndWrite)}`);
> User can read/write transactions: true
console.log(`User can manage financials: ${int.exec(finManage)}`);
> User can manage financials: false
int.permissions = ['financials:manage'];
console.log(`User can manage financials: ${int.exec(perms)}`);
> User can manage financials: true
Highly Recommended
FIN
Programming languages aren't scary, right? Right??
By signupskm
Programming languages aren't scary, right? Right??
- 52