Skip to content

Commit

Permalink
feat: refactor result pattern to better fit typescript compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
Eikix committed Sep 26, 2023
1 parent 9d3f44e commit f432045
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 176 deletions.
17 changes: 12 additions & 5 deletions src/memory/memory.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { test, expect, describe } from 'bun:test';
import { Memory, UnknownAddressError, WriteOnceError } from './memory';
import { Relocatable, SegmentError } from 'primitives/relocatable';
import {
MaybeRelocatable,
Relocatable,
SegmentError,
} from 'primitives/relocatable';
import { Felt } from 'primitives/felt';
import { UnsignedInteger } from 'primitives/uint';
import { Err, Ok, VMError } from 'result-pattern/result';

describe('Memory', () => {
describe('get', () => {
test('should return error if address is not written to', () => {
const memory = new Memory();
const address = new Relocatable(0, 0);
const result = memory.get(address);
const result = memory.get(address) as Err<VMError>;
expect(result.unwrapErr()).toEqual(UnknownAddressError);
});

Expand All @@ -19,7 +24,9 @@ describe('Memory', () => {
const address = new Relocatable(0, 0);
const value = new Felt(10n);
memory.insert(address, value);
expect(memory.get(address).unwrap()).toEqual(value);
expect((memory.get(address) as Ok<MaybeRelocatable>).unwrap()).toEqual(
value
);
});
});

Expand All @@ -28,7 +35,7 @@ describe('Memory', () => {
const memory = new Memory();
const address = new Relocatable(1, 0);
const value = new Felt(10n);
const result = memory.insert(address, value);
const result = memory.insert(address, value) as Err<VMError>;
expect(result.unwrapErr()).toEqual(SegmentError);
});

Expand All @@ -38,7 +45,7 @@ describe('Memory', () => {
const address = new Relocatable(0, 0);
const value = new Felt(10n);
memory.insert(address, value);
const err = memory.insert(address, value);
const err = memory.insert(address, value) as Err<VMError>;
expect(err.unwrapErr()).toEqual(WriteOnceError);
});
});
Expand Down
12 changes: 6 additions & 6 deletions src/memory/memory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Result, VMError } from '../result-pattern/result';
import { Ok, Err, VMError, Result } from 'result-pattern/result';
import {
MaybeRelocatable,
Relocatable,
Expand Down Expand Up @@ -29,23 +29,23 @@ export class Memory {

insert(address: Relocatable, value: MaybeRelocatable): Result<true, VMError> {
if (address.getSegmentIndex() >= this.numSegments) {
return Result.error(SegmentError);
return new Err(SegmentError);
}

if (this.data.get(address) !== undefined) {
return Result.error(WriteOnceError);
return new Err(WriteOnceError);
}

this.data.set(address, value);
return Result.ok(true);
return new Ok(true);
}

get(address: Relocatable): Result<MaybeRelocatable, VMError> {
const value = this.data.get(address);
if (value === undefined) {
return Result.error(UnknownAddressError);
return new Err(UnknownAddressError);
}
return Result.ok(value);
return new Ok(value);
}
}

Expand Down
9 changes: 6 additions & 3 deletions src/primitives/felt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ describe('Felt', () => {
describe('conversions', () => {
test('should convert correctly a felt to a number if inner is below Javascript max safe integer', () => {
const felt = new Felt(10n);
expect(felt.toNumber().unwrap()).toEqual(10);
const result = felt.toUint();
expect(result.isOk() && result.unwrap()).toEqual(10);
});
test('should convert correctly a felt to its string representation', () => {
const felt = new Felt(10n);
Expand All @@ -35,8 +36,10 @@ describe('Felt', () => {
});
test('should fail number conversion when felt inner > JS max number', () => {
const felt = new Felt(BigInt(Number.MAX_SAFE_INTEGER + 1));
const result = felt.toNumber();
expect(result.unwrapErr().message).toEqual(ConversionError.message);
const result = felt.toUint();
expect(result.isErr() && result.unwrapErr().message).toEqual(
ConversionError.message
);
});
});

Expand Down
15 changes: 9 additions & 6 deletions src/primitives/felt.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Result, VMError } from 'result-pattern/result';
import { Result, Err, Ok, VMError } from 'result-pattern/result';
import { Uint, UnsignedInteger } from './uint';

export class FeltError extends Error {}

export const ConversionError = {
message:
'FeltError: cannot convert to Felt to Number, as underlying bigint > Number.MAX_SAFE_INTEGER',
'FeltError: cannot convert to Felt to Number, as underlying bigint > Number.MAX_SAFE_INTEGER or < 0',
};

export class Felt {
Expand Down Expand Up @@ -42,14 +43,16 @@ export class Felt {
return this.inner.toString();
}

toNumber(): Result<number, VMError> {
toUint(): Result<Uint, VMError> {
const num = Number(this.inner);

// The value of the largest integer n such that n and n + 1 are both exactly representable as a Number value.
// The value of Number.MAX_SAFE_INTEGER is 9007199254740991, i.e. 2^53 − 1.
if (num > Number.MAX_SAFE_INTEGER) {
return Result.error(ConversionError);
if (num > Number.MAX_SAFE_INTEGER || num < 0) {
return new Err(ConversionError);
}
return Result.ok(num);

return new Ok(UnsignedInteger.toUint(num));
}

toHexString(): string {
Expand Down
25 changes: 14 additions & 11 deletions src/primitives/relocatable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ForbiddenOperation,
} from './relocatable';
import { UnsignedInteger } from './uint';
import { Err, Ok, VMError } from 'result-pattern/result';

describe('Relocatable', () => {
describe('constructor', () => {
Expand All @@ -21,52 +22,52 @@ describe('Relocatable', () => {
test('should subtract two relocatables properly within the same segment', () => {
const a = new Relocatable(1, 10);
const b = new Relocatable(1, 3);
const result = a.sub(b).unwrap();
const result = (a.sub(b) as Ok<Relocatable>).unwrap();
expect(result.getOffset()).toEqual(7);
expect(result.getSegmentIndex()).toEqual(1);
});

test('should return error OffsetUnderflow when offset goes below zero', () => {
const a = new Relocatable(1, 2);
const b = new Relocatable(1, 3);
const result = a.sub(b).unwrapErr();
const result = (a.sub(b) as Err<VMError>).unwrapErr();
expect(result).toEqual(OffsetUnderflow);
});

test('should return error SegmentError when segments are different', () => {
const a = new Relocatable(1, 10);
const b = new Relocatable(2, 5);
const result = a.sub(b).unwrapErr();
const result = (a.sub(b) as Err<VMError>).unwrapErr();
expect(result).toEqual(SegmentError);
});

test('should subtract a Felt from a Relocatable', () => {
const relocatable = new Relocatable(0, 10);
const felt = new Felt(5n);
const result = relocatable.sub(felt).unwrap();
const result = (relocatable.sub(felt) as Ok<Relocatable>).unwrap();
expect(result.getOffset()).toEqual(5);
expect(result.getSegmentIndex()).toEqual(0);
});

test('should return OffsetUnderflow when subtracting a larger Felt', () => {
const relocatable = new Relocatable(0, 5);
const felt = new Felt(10n);
const result = relocatable.sub(felt).unwrapErr();
const result = (relocatable.sub(felt) as Err<VMError>).unwrapErr();
expect(result).toEqual(OffsetUnderflow);
});

test('should subtract a Relocatable', () => {
const a = new Relocatable(0, 10);
const b = new Relocatable(0, 5);
const result = a.sub(b).unwrap();
const result = (a.sub(b) as Ok<Relocatable>).unwrap();
expect(result.getOffset()).toEqual(5);
expect(result.getSegmentIndex()).toEqual(0);
});

test('should subtract a Felt', () => {
const a = new Relocatable(0, 10);
const b = new Felt(5n);
const result = a.sub(b).unwrap();
const result = (a.sub(b) as Ok<Relocatable>).unwrap();
expect(result.getOffset()).toEqual(5);
expect(result.getSegmentIndex()).toEqual(0);
});
Expand All @@ -76,27 +77,29 @@ describe('Relocatable', () => {
test('should add a Felt', () => {
const relocatable = new Relocatable(0, 5);
const felt = new Felt(5n);
const result = relocatable.add(felt).unwrap();
const result = (relocatable.add(felt) as Ok<Relocatable>).unwrap();
expect(result.getOffset()).toEqual(10);
expect(result.getSegmentIndex()).toEqual(0);
});

test('should return error ForbiddenOperation when adding an incompatible MaybeRelocatable', () => {
const a = new Relocatable(0, 10);
const b = new Relocatable(0, 5);
const result = a.add(b).unwrapErr();
const result = (a.add(b) as Err<VMError>).unwrapErr();
expect(result).toEqual(ForbiddenOperation);
});
test('should add a Felt to a Relocatable', () => {
const relocatable = new Relocatable(0, 5);
const felt = new Felt(5n);
const result = relocatable.add(felt).unwrap();
const result = (relocatable.add(felt) as Ok<Relocatable>).unwrap();
expect(result.getOffset()).toEqual(10);
expect(result.getSegmentIndex()).toEqual(0);
});
test('should add a positive number correctly to a relocatable', () => {
const relocatable = new Relocatable(0, 5);
const result = relocatable.add(UnsignedInteger.toUint(5)).unwrap();
const result = (
relocatable.add(UnsignedInteger.toUint(5)) as Ok<Relocatable>
).unwrap();
expect(result.getOffset()).toEqual(10);
expect(result.getSegmentIndex()).toEqual(0);
});
Expand Down
41 changes: 23 additions & 18 deletions src/primitives/relocatable.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Result, VMError } from 'result-pattern/result';
import { Result, Ok, Err, VMError } from 'result-pattern/result';
import { ConversionError, Felt } from './felt';
import { Uint, UnsignedInteger } from './uint';

Expand Down Expand Up @@ -43,56 +43,61 @@ export class Relocatable {

add(other: MaybeRelocatable | Uint): Result<Relocatable, VMError> {
if (other instanceof Felt) {
const num = other.toNumber();
const num = other.toUint();
if (num.isErr()) {
return Result.error(ConversionError);
return new Err(ConversionError);
}
if (this.getOffset() + num.unwrap() > Number.MAX_SAFE_INTEGER) {
return Result.error(OffsetOverflow);
return new Err(OffsetOverflow);
}
return Result.ok(
return new Ok(
new Relocatable(this.getSegmentIndex(), this.getOffset() + num.unwrap())
);
}

if (other instanceof Relocatable) {
return Result.error(ForbiddenOperation);
return new Err(ForbiddenOperation);
}

return Result.ok(
return new Ok(
new Relocatable(this.getSegmentIndex(), this.getOffset() + other)
);
}

sub(other: MaybeRelocatable | Uint): Result<Relocatable, VMError> {
if (other instanceof Felt) {
const delta = other.toNumber().unwrapOrUndefined();
if (delta === undefined) {
return Result.error(ConversionError);
const delta = other.toUint();

if (delta.isErr()) {
return delta;
}
if (this.getOffset() < delta) {
return Result.error(OffsetUnderflow);

if (this.getOffset() < delta.unwrap()) {
return new Err(OffsetUnderflow);
}
return Result.ok(
new Relocatable(this.getSegmentIndex(), this.getOffset() - delta)
return new Ok(
new Relocatable(
this.getSegmentIndex(),
this.getOffset() - delta.unwrap()
)
);
}

if (other instanceof Relocatable) {
if (this.offset < other.offset) {
return Result.error(OffsetUnderflow);
return new Err(OffsetUnderflow);
}

if (this.segmentIndex !== other.segmentIndex) {
return Result.error(SegmentError);
return new Err(SegmentError);
}

return Result.ok(
return new Ok(
new Relocatable(this.segmentIndex, this.offset - other.offset)
);
}

return Result.ok(
return new Ok(
new Relocatable(this.getSegmentIndex(), this.getOffset() - other)
);
}
Expand Down
Loading

0 comments on commit f432045

Please sign in to comment.