Skip to content

Commit

Permalink
feat: Adds Lambda literals
Browse files Browse the repository at this point in the history
  • Loading branch information
RomarQ committed Jan 29, 2022
1 parent 5c6d295 commit bd8e120
Show file tree
Hide file tree
Showing 16 changed files with 734 additions and 70 deletions.
10 changes: 10 additions & 0 deletions src/converter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { MichelsonJSON, MichelsonMicheline } from '../typings';
import Michelson_JSON from './json';

export const michelineOfJSON = (json: MichelsonJSON): MichelsonMicheline => Michelson_JSON.toMicheline(json, '');

const Converter = {
michelineOfJSON,
};

export default Converter;
105 changes: 105 additions & 0 deletions src/converter/json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Prim } from '../core/enums/prim';
import Guards from '../misc/guards';
import { MichelsonJSON, MichelsonJSON_Prim, MichelsonMicheline } from '../typings';

/**
* @description Convert type from JSON to Micheline
* @param {MichelsonJSON_Prim} michelson type
* @returns {MichelsonMicheline}
*/
const toMichelineType = (michelson: MichelsonJSON_Prim): MichelsonMicheline => {
const args = michelson.args?.map((m) => toMicheline(m, '')) || [];
const annot = michelson.annots || [];
if (args.length || annot.length) {
return `(${[michelson.prim, ...annot, ...args].join(' ')})`;
}
return michelson.prim;
};

/**
* @description Convert sequence from JSON to Micheline
* @param {MichelsonJSON[]} michelson sequence
* @param {string} identation
* @returns {MichelsonMicheline}
*/
const toMichelineSeq = (michelson: MichelsonJSON[], identation: string): MichelsonMicheline => {
const innerIdentation = identation + ' '.repeat(2);
return `${identation ? `\n${identation}` : identation}{\n${michelson
.map((m) => toMicheline(m, innerIdentation))
.join(`\n`)}\n${identation}}`;
};

export const toMicheline = (michelson: MichelsonJSON, padding = ''): MichelsonMicheline => {
if (Array.isArray(michelson)) {
return toMichelineSeq(michelson, padding);
}
if (Guards.isInt(michelson)) {
return michelson.int;
}
if (Guards.isString(michelson)) {
return michelson.string;
}
if (Guards.isBytes(michelson)) {
return michelson.bytes;
}
const prim = michelson.prim;
const args = michelson.args || [];
switch (michelson.prim) {
case Prim.storage:
case Prim.parameter:
return `${padding}${prim} ${(args as MichelsonJSON_Prim[]).map(toMichelineType).join('')};`;
case Prim.code:
return `${padding}${prim} ${toMicheline(michelson.args?.[0] || [], padding).trim()};`;
case Prim.unit:
case Prim.nat:
case Prim.int:
case Prim.mutez:
case Prim.timestamp:
case Prim.string:
case Prim.address:
case Prim.bytes:
case Prim.chain_id:
case Prim.bool:
case Prim.bls12_381_fr:
case Prim.bls12_381_g1:
case Prim.bls12_381_g2:
case Prim.key:
case Prim.key_hash:
case Prim.signature:
case Prim.operation:
case Prim.never:
case Prim.list:
case Prim.set:
case Prim.option:
case Prim.pair:
case Prim.or:
case Prim.map:
case Prim.big_map:
case Prim.lambda:
case Prim.ticket:
case Prim.contract:
case Prim.sapling_state:
case Prim.sapling_transaction:
return toMichelineType(michelson);
case Prim.None:
case Prim.True:
case Prim.False:
case Prim.Unit:
return prim;
case Prim.Some:
case Prim.Pair:
case Prim.Left:
case Prim.Right:
return `(${[prim, ...(args?.map((m) => toMicheline(m, padding)) || [])].join(' ')})`;
default:
return `${padding}${[prim, ...(args?.map((m) => toMicheline(m, padding)) || [])].join(' ')};`;
}
};

const Michelson_JSON = {
toMichelineType,
toMichelineSeq,
toMicheline,
};

export default Michelson_JSON;
Empty file added src/converter/micheline.ts
Empty file.
16 changes: 16 additions & 0 deletions src/core/enums/prim.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* @see https://tezos.gitlab.io/alpha/michelson.html#full-grammar
*/
export enum Prim {
// Type
unit = 'unit',
Expand Down Expand Up @@ -42,4 +45,17 @@ export enum Prim {
Elt = 'Elt',
Left = 'Left',
Right = 'Right',
// Root fields
storage = 'storage',
parameter = 'parameter',
code = 'code',
// Instructions
DROP = 'DROP',
CDR = 'CDR',
NIL = 'NIL',
PAIR = 'PAIR',
UNPAIR = 'UNPAIR',
PUSH = 'PUSH',
LAMBDA = 'LAMBDA',
IF = 'IF',
}
108 changes: 60 additions & 48 deletions src/core/literal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Michelson_Type, IRecordVariant } from './type';
import type { MichelsonJSON, MichelsonMicheline, PairsOfKeys, IType, PrimValue } from '../typings';
import type { MichelsonJSON, MichelsonMicheline, PairsOfKeys, IType, PrimValue, IValue } from '../typings';
import {
TBig_map,
TBls12_381_fr,
Expand Down Expand Up @@ -29,12 +29,12 @@ import {
} from './type';
import { Prim } from './enums/prim';
import Utils, { composeRightCombLayout } from '../misc/utils';
import Converter from '../../src/converter';
import { TLambda } from '.';

export type Michelson_LiteralUnion = Michelson_Literal | Michelson_Literal_C1 | Michelson_Map;

export class Michelson_Literal {
export class Michelson_Literal implements IValue {
private prim: PrimValue;
private value: number | string | boolean;
private value: number | string | boolean | Prim;
type: IType;

constructor(prim: PrimValue, type: IType, value?: number | string | boolean) {
Expand All @@ -43,6 +43,8 @@ export class Michelson_Literal {
switch (prim) {
case Prim.Unit:
case Prim.None:
case Prim.False:
case Prim.True:
this.value = prim;
break;
default:
Expand All @@ -56,55 +58,53 @@ export class Michelson_Literal {
toMicheline(): MichelsonMicheline {
switch (this.prim) {
case Prim.None:
case Prim.False:
case Prim.True:
case Prim.Unit:
case Prim.int:
case Prim.bytes:
return `${this.value}`;
case Prim.string:
return `"${this.value}"`;
case Prim.bool:
return Utils.capitalizeBoolean(this.value as boolean);
}

throw new Error(`Cannot produce michelson for literal of type: ${this.prim}`);
}

toJSON(): Record<string, unknown> {
toJSON(): MichelsonJSON {
switch (this.prim) {
case Prim.None:
case Prim.Unit:
case Prim.False:
case Prim.True:
return {
prim: this.value,
prim: this.value as Prim,
};
case Prim.int:
return {
[Prim.int]: `${this.value}`,
};
case Prim.string:
return {
[Prim.string]: this.value,
[Prim.string]: this.value as string,
};
case Prim.bytes:
return {
// Same behaviour as in "tezos-client"
[Prim.bytes]: Utils.compressHexString(`${this.value}`),
};
case Prim.bool:
return {
prim: Utils.capitalizeBoolean(this.value as boolean),
};
}

throw new Error(`Cannot produce michelson JSON for literal of type: ${this.prim}`);
}
}

export class Michelson_Literal_C1 {
#prim: Prim;
#elements: Michelson_LiteralUnion[];
export class Michelson_Literal_C1 implements IValue {
#prim: PrimValue;
#elements: IValue[];
type: IType;

constructor(prim: PrimValue, type: IType, elements: Michelson_LiteralUnion[]) {
constructor(prim: PrimValue, type: IType, elements: IValue[]) {
this.#prim = prim;
this.type = type;
this.#elements = elements;
Expand All @@ -117,7 +117,7 @@ export class Michelson_Literal_C1 {
case Prim.Left:
case Prim.Right:
return `(${this.#prim} ${this.#elements.map((v) => v.toMicheline()).join(' ')})`;
case Prim.list:
case Prim.Elt:
return `{ ${this.#elements.map((v) => v.toMicheline()).join(' ; ')} }`;
}

Expand All @@ -134,24 +134,24 @@ export class Michelson_Literal_C1 {
prim: this.#prim,
args: this.#elements.map((v) => v.toJSON()),
};
case Prim.list:
case Prim.Elt:
return this.#elements.map((v) => v.toJSON());
}

throw new Error(`Cannot produce michelson JSON for literal of type: ${this.#prim}`);
}
}

export class Michelson_Map {
#elements: Michelson_LiteralUnion[][];
export class Michelson_Map implements IValue {
#elements: IValue[][];
type: IType;

constructor(type: Michelson_Type, elements: Michelson_LiteralUnion[][]) {
constructor(type: Michelson_Type, elements: IValue[][]) {
this.type = type;
this.#elements = elements;
}

private buildMichelineElt = (key: Michelson_LiteralUnion, value: Michelson_LiteralUnion) => {
private buildMichelineElt = (key: IValue, value: IValue) => {
return `${Prim.Elt} ${key.toMicheline()} ${value.toMicheline()}`;
};

Expand All @@ -173,11 +173,8 @@ export class Michelson_Map {
* @param layout record layout
* @returns {Michelson_LiteralUnion}
*/
const buildRecord = (
fields: Record<string, Michelson_LiteralUnion>,
layout?: PairsOfKeys<string>,
): Michelson_LiteralUnion => {
const buildBranch = (branch: string | PairsOfKeys<string>): Michelson_LiteralUnion => {
const buildRecord = (fields: Record<string, IValue>, layout?: PairsOfKeys<string>): IValue => {
const buildBranch = (branch: string | PairsOfKeys<string>): IValue => {
if (typeof branch === 'string') {
// Set field annotation
fields[branch].type.setAnnotation(branch);
Expand All @@ -196,7 +193,7 @@ const buildRecord = (
* @param type variant type
* @returns {Michelson_LiteralUnion}
*/
const buildVariant = (target: string, value: Michelson_LiteralUnion, type: IRecordVariant): Michelson_LiteralUnion => {
const buildVariant = (target: string, value: IValue, type: IRecordVariant): IValue => {
const [left, right] = type.layout;
if (left === target) {
return Left(value, type);
Expand All @@ -214,6 +211,23 @@ const buildVariant = (target: string, value: Michelson_LiteralUnion, type: IReco
throw new Error(`Variant (${target}) is invalid.`);
};

const buildLambda = (michelson: MichelsonMicheline | MichelsonJSON, type: IType): IValue => {
if (typeof michelson === 'string') {
return {
toMicheline: () => michelson,
toJSON: () => {
throw new Error('Convertion from Micheline to JSON is not implemented.');
},
type,
};
}
return {
toMicheline: () => Converter.michelineOfJSON(michelson),
toJSON: () => michelson,
type,
};
};

// Singletons
export const Nat = (value: number) => new Michelson_Literal(Prim.int, TNat(), value);
export const Int = (value: number) => new Michelson_Literal(Prim.int, TInt(), value);
Expand All @@ -233,30 +247,28 @@ export const Bls12_381_g2 = (value: string) => new Michelson_Literal(Prim.bytes,
export const Key = (value: string) => new Michelson_Literal(Prim.string, TKey(), value);
export const Key_hash = (value: string) => new Michelson_Literal(Prim.string, TKey_hash(), value);
export const Signature = (value: string) => new Michelson_Literal(Prim.string, TSignature(), value);
export const Bool = (value: boolean) => new Michelson_Literal(Prim.bool, TBool(), value);
export const Bool = (value: boolean) => new Michelson_Literal(value ? Prim.True : Prim.False, TBool());
export const Unit = () => new Michelson_Literal(Prim.Unit, TUnit());
// Containers
export const List = (elements: Michelson_LiteralUnion[], innerType: IType) =>
new Michelson_Literal_C1(Prim.list, TList(innerType), elements);
export const Set = (elements: Michelson_LiteralUnion[], innerType: IType) =>
new Michelson_Literal_C1(Prim.list, TSet(innerType), elements);
export const List = (elements: IValue[], innerType: IType) =>
new Michelson_Literal_C1(Prim.Elt, TList(innerType), elements);
export const Set = (elements: IValue[], innerType: IType) =>
new Michelson_Literal_C1(Prim.Elt, TSet(innerType), elements);
export const None = (innerType: IType) => new Michelson_Literal(Prim.None, TOption(innerType));
export const Some = (element: Michelson_LiteralUnion) =>
new Michelson_Literal_C1(Prim.Some, TOption(element.type), [element]);
export const Pair = (left: Michelson_LiteralUnion, right: Michelson_LiteralUnion) =>
export const Some = (element: IValue) => new Michelson_Literal_C1(Prim.Some, TOption(element.type), [element]);
export const Pair = (left: IValue, right: IValue) =>
new Michelson_Literal_C1(Prim.Pair, TPair(left.type, right.type), [left, right]);
export const Map = (elements: Michelson_LiteralUnion[][], keyType: IType, valueType: IType) =>
export const Map = (elements: IValue[][], keyType: IType, valueType: IType) =>
new Michelson_Map(TMap(keyType, valueType), elements);
export const Big_map = (elements: Michelson_LiteralUnion[][], keyType: IType, valueType: IType) =>
export const Big_map = (elements: IValue[][], keyType: IType, valueType: IType) =>
new Michelson_Map(TBig_map(keyType, valueType), elements);
export const Left = (value: Michelson_LiteralUnion, type: IType) => new Michelson_Literal_C1(Prim.Left, type, [value]);
export const Right = (value: Michelson_LiteralUnion, type: IType) =>
new Michelson_Literal_C1(Prim.Right, type, [value]);
export const Lambda = (code: MichelsonMicheline | MichelsonJSON, inType: IType, outType: IType) =>
buildLambda(code, TLambda(inType, outType));
export const Left = (value: IValue, type: IType) => new Michelson_Literal_C1(Prim.Left, type, [value]);
export const Right = (value: IValue, type: IType) => new Michelson_Literal_C1(Prim.Right, type, [value]);
// Artificial containers
export const Record = (fields: Record<string, Michelson_LiteralUnion>, layout?: PairsOfKeys<string>) =>
buildRecord(fields, layout);
export const Variant = (branch: string, value: Michelson_LiteralUnion, type: IRecordVariant) =>
buildVariant(branch, value, type);
export const Record = (fields: Record<string, IValue>, layout?: PairsOfKeys<string>) => buildRecord(fields, layout);
export const Variant = (branch: string, value: IValue, type: IRecordVariant) => buildVariant(branch, value, type);

const Literals = {
// Singletons
Expand Down Expand Up @@ -284,7 +296,7 @@ const Literals = {
Pair,
Map,
Big_map,
// Lambda,
Lambda,
// Artificial containers
Record,
Variant,
Expand Down
Loading

0 comments on commit bd8e120

Please sign in to comment.