Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as transform from './transform';
export { code, node, transform, Property, PropertyType, Span };
export { Edge } from './edge';
export { LoopChecker } from './loop-checker';
export { ShiftChecker } from './shift-checker';
export { ISpanAllocatorResult, SpanAllocator } from './span-allocator';
export { Reachability } from './reachability';

Expand Down Expand Up @@ -112,6 +113,34 @@ export class Builder {
return new node.Pause(errorCode, reason);
}

// Shift

/**
* Create a node that packs bits into a field in
* little endian format.
*
* state[field] <<= value;
*
* This node does not provide otherwise as this node must
* consume bytes upon execution.
*/
public lshift(field: string, bits: number): node.Shift {
return new node.Shift(field, bits, true);
}

/**
* Create a node that packs bits into a field in
* big endian format.
*
* state[field] >>= value;
*
* This node does not provide otherwise as this node must
* consume bytes upon execution.
*/
public rshift(field: string, bits: number): node.Shift {
return new node.Shift(field, bits, false);
}

// Span

/**
Expand Down
29 changes: 29 additions & 0 deletions src/node/shift.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Node } from './base';

/**
* This node consumes and stores an integer into a specified
* `field` from the input before forwarding execution.
*/
export class Shift extends Node {
/**
* @param field State's property name
* @param bits number of bits to shift it can be between 1 and 8
* @param lshift if true, bits are shifted to the left.
* otherwise, it goes in the opposite direction.
*/
constructor (public readonly field: string, public readonly bits:number, public readonly lshift: boolean){
const name = (lshift) ? "lshift" : "rshift";
const operation = (bits > 1) ? `_${bits * 8}`: "";
super(`${name}_${field}${operation}`);
if (/^_/.test(field)) {
throw new Error(`Can't use internal field in \`.${name}()\`: "${field}"`);
}
if (bits < 1 || bits > 8){
throw new Error(`bits must be a number between 1 to 8`);
}
}
/** `.otherwise()` is not supported on this type of node as it consumes a bit.
* enabling this defeats the purpose.
*/
public otherwise(): this { throw new Error('Not supported'); }
}
77 changes: 77 additions & 0 deletions src/shift-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { debuglog } from 'node:util';

import { Property } from './property';
import { Node, Shift } from './node';
import { Reachability } from './reachability';


const debug = debuglog('llparse-builder:shift-checker');

/**
* Validates shift nodes to find invalid or missing properties or bit sizes
*/
export class ShiftChecker {
public readonly property_table = new Map<string, Property>();
constructor (properties: readonly Property[]){
properties.forEach((prop) => {this.property_table.set(prop.name, prop)});
}

/**
* validates a single shift node to see if a property exists
* to be fed bits to. It also has the ability of checking if numbers
* may overflow.
*
* @param shift the shift node to check.
*/
private validate_shift(shift: Shift): void{
const property = this.property_table.get(shift.field);
if (!property){
throw new Error(`"${shift.field}" has not been defined for ${shift.name}`);
}
let size;
switch (property.ty){
case 'ptr':
throw new Error(
`${shift.field} cannot be provided to "${shift.name}" because field was defined as a "ptr"`
);
case 'i8':
size = 1;
break;
case 'i16':
size = 2;
break;
case 'i32':
size = 4;
break;
case 'i64':
size = 8;
break;
default:
/* TODO (Vizonex): ensure this is unreachable. */
/* UNREACHABLE */
size = 0;
}
// Ensure bits can't overflow...
if (shift.bits > size){
throw new Error(`${shift.bits} > ${size} and will cause node "${shift.field}" to overflow.`)
}
}

/**
* Run shift checker pass on a graph starting from `root`.
*
* @param root Graph root node
*/
public check(root: Node): void {
const r = new Reachability();
const nodes = r.build(root);

for (const node of nodes){
if (node instanceof Shift){
debug('checking %j', node.name);
this.validate_shift(node);
}
}
}
}