Skip to content

Commit

Permalink
Merge pull request #3 from RomarQ/rq@types
Browse files Browse the repository at this point in the history
feat: Add new types and improve type references
  • Loading branch information
RomarQ committed Feb 3, 2022
2 parents 54646c5 + b873e4a commit 257d287
Show file tree
Hide file tree
Showing 14 changed files with 2,085 additions and 99 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const SmartML = require('@tezwell/smartts-sdk/smartml');
const contract = new Contract()
.setStorage(Nat(0))
.addEntrypoint(
new EntryPoint('ep1').inputType(TNat).code((arg) => [
new EntryPoint('ep1').inputType(TNat()).code((arg) => [
// Define a variable named "some_address"
DefineVar('some_address', Address('tz1')),
// Require sender to be equal to variable "some_address", otherwise fail with "Not Admin!"
Expand Down
6 changes: 3 additions & 3 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ export class Contract {
return `
(
template_id (static_id 0 ${this.line})
storage ${this.#storage.toString()}
storage_type (${this.#storage_type ? this.#storage_type.toString() : '()'})
storage ${this.#storage}
storage_type (${this.#storage_type || '()'})
messages (${this.#entries.join(' ')})
flags (${this.#options.flags.map((flag) => flag.toString())})
flags (${this.#options.flags.join(' ')})
privates ()
views ()
entry_points_layout ()
Expand Down
27 changes: 14 additions & 13 deletions src/core/literal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import {
TBool,
TList,
TTimestamp,
TChainID,
TChain_id,
TInt,
TBytes,
TOption,
TUnknown,
TUnit,
TRecord,
TMap,
TBigMap,
TBig_map,
} from '../type';
import { Prim } from '../enums/prim';
import { IExpression, IExpressionKind } from '../../typings/expression';
Expand Down Expand Up @@ -130,7 +130,7 @@ class MapLiteral implements ILiteral {
if (prim === Prim.map) {
this.type = TMap(keyType, valueType);
} else {
this.type = TBigMap(keyType, valueType);
this.type = TBig_map(keyType, valueType);
}
}

Expand All @@ -147,25 +147,26 @@ class MapLiteral implements ILiteral {
}
}

export const Unit = (line = new LineInfo()) => new Literal(Prim.unit, undefined, TUnit, line);
export const Unit = (line = new LineInfo()) => new Literal(Prim.unit, undefined, TUnit(), line);

export const Nat = (value: number, line = new LineInfo()) => new Literal(Prim.nat, value, TNat, line);
export const Int = (value: number, line = new LineInfo()) => new Literal(Prim.int, value, TInt, line);
export const Mutez = (value: number, line = new LineInfo()) => new Literal(Prim.mutez, value, TMutez, line);
export const Nat = (value: number, line = new LineInfo()) => new Literal(Prim.nat, value, TNat(), line);
export const Int = (value: number, line = new LineInfo()) => new Literal(Prim.int, value, TInt(), line);
export const Mutez = (value: number, line = new LineInfo()) => new Literal(Prim.mutez, value, TMutez(), line);

export const String = (value: string, line = new LineInfo()) => new Literal(Prim.string, `"${value}"`, TString, line);
export const String = (value: string, line = new LineInfo()) => new Literal(Prim.string, `"${value}"`, TString(), line);

export const Bool = (value: boolean, line = new LineInfo()) =>
new Literal(Prim.bool, capitalizeBoolean(value), TBool, line);
new Literal(Prim.bool, capitalizeBoolean(value), TBool(), line);

export const Address = (address: string, line = new LineInfo()) => new Literal(Prim.address, address, TAddress, line);
export const Address = (address: string, line = new LineInfo()) => new Literal(Prim.address, address, TAddress(), line);

export const Timestamp = (timestamp: number, line = new LineInfo()) =>
new Literal(Prim.timestamp, timestamp, TTimestamp, line);
new Literal(Prim.timestamp, timestamp, TTimestamp(), line);

export const ChainID = (chainID: string, line = new LineInfo()) => new Literal('chain_id_cst', chainID, TChainID, line);
export const ChainID = (chainID: string, line = new LineInfo()) =>
new Literal('chain_id_cst', chainID, TChain_id(), line);

export const Bytes = (bytes: string, line = new LineInfo()) => new Literal(Prim.bytes, bytes, TBytes, line);
export const Bytes = (bytes: string, line = new LineInfo()) => new Literal(Prim.bytes, bytes, TBytes(), line);

// Containers
export const List = (items: IExpressionKind[], innerType: IType, line = new LineInfo()) =>
Expand Down
108 changes: 63 additions & 45 deletions src/core/type/index.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,40 @@
import { composeRightCombLayout, parenthesis } from '../../misc/utils';
import { ILayout } from '../../typings/literal';
import { IType } from '../../typings/type';
import { Layout } from '../enums/layout';
import { Prim, SmartPyAtom } from '../enums/prim';

/**
* @description All Type classes must extend this class. It identifies Type classes.
*/
export class BaseType implements IType {
_isType = true as const;
}

export class SimpleType extends BaseType {
constructor(public name: string) {
super();
}
export class SimpleType implements IType {
constructor(public name: string) {}

toString() {
return `"${this.name}"`;
}
}

export class ContainerType extends BaseType {
constructor(public type: string, public innerTypes: IType[]) {
super();
}
export class ContainerType implements IType {
constructor(public type: string, public innerTypes: IType[]) {}

toString() {
return `(${this.type} ${this.innerTypes.map((t) => t.toString()).join(' ')})`;
return `(${this.type} ${this.innerTypes.join(' ')})`;
}
}

export class Type_Record extends BaseType {
layout: ILayout | Layout;
export class Type_Record implements IType {
private layout: ILayout | Layout;
constructor(public fields: Record<string, IType>, layout?: ILayout | Layout) {
super();
this.layout = layout || Object.keys(fields);
this.layout = layout || composeRightCombLayout(Object.keys(fields));
}

private translateLayout = (layout: ILayout | Layout): string => {
const normalizeTuple = (elements: ILayout): string =>
elements.reduce<string>((pv, cv) => {
if (Array.isArray(cv)) {
return `(${[pv, normalizeTuple(cv)].join(' ')})`;
}
const field = `("${cv}")`;
return !!pv ? `(${pv} ${field})` : field;
}, '');
const normalizeTuple = (layout: string | ILayout): string => {
if (typeof layout === 'string') {
return `("${layout}")`;
}
const [left, right] = layout;
return parenthesis([normalizeTuple(left), normalizeTuple(right)].join(' '));
};

if (Array.isArray(layout)) {
return layout.length ? `(Some ${normalizeTuple(layout)})` : 'None';
}
Expand All @@ -65,24 +54,36 @@ export const TUnknown: IType = {
toString: () => '(unknown 0)',
};
// Singleton types
export const TUnit = new SimpleType(Prim.unit);
export const TNat = new SimpleType(Prim.nat);
export const TInt = new SimpleType(Prim.int);
export const TMutez = new SimpleType(Prim.mutez);
export const TString = new SimpleType(Prim.string);
export const TBool = new SimpleType(Prim.bool);
export const TAddress = new SimpleType(Prim.address);
export const TTimestamp = new SimpleType(Prim.timestamp);
export const TChainID = new SimpleType(Prim.chain_id);
export const TBytes = new SimpleType(Prim.bytes);
export const TUnit = () => new SimpleType(Prim.unit);
export const TNat = () => new SimpleType(Prim.nat);
export const TInt = () => new SimpleType(Prim.int);
export const TMutez = () => new SimpleType(Prim.mutez);
export const TString = () => new SimpleType(Prim.string);
export const TBool = () => new SimpleType(Prim.bool);
export const TAddress = () => new SimpleType(Prim.address);
export const TTimestamp = () => new SimpleType(Prim.timestamp);
export const TChain_id = () => new SimpleType(Prim.chain_id);
export const TBytes = () => new SimpleType(Prim.bytes);
export const TBls12_381_fr = () => new SimpleType(Prim.bls12_381_fr);
export const TBls12_381_g1 = () => new SimpleType(Prim.bls12_381_g1);
export const TBls12_381_g2 = () => new SimpleType(Prim.bls12_381_g2);
export const TKey = () => new SimpleType(Prim.key);
export const TKey_hash = () => new SimpleType(Prim.key_hash);
export const TSignature = () => new SimpleType(Prim.signature);
export const TOperation = () => new SimpleType(Prim.operation);
export const TNever = () => new SimpleType(Prim.never);
// Container types
export const TList = (innerType: IType) => new ContainerType(Prim.list, [innerType]);
export const TSet = (innerType: IType) => new ContainerType(Prim.set, [innerType]);
export const TOption = (innerType: IType) => new ContainerType(Prim.option, [innerType]);
export const TRecord = (fields: Record<string, IType>, layout?: ILayout | Layout) => new Type_Record(fields, layout);
export const TMap = (keyType: IType, valueType: IType) => new ContainerType(Prim.map, [keyType, valueType]);
export const TBigMap = (keyType: IType, valueType: IType) =>
export const TBig_map = (keyType: IType, valueType: IType) =>
new ContainerType(SmartPyAtom.bigmap, [keyType, valueType]);
export const TTuple = (...types: IType[]) => new ContainerType(SmartPyAtom.tuple, types);
export const TPair = (left: IType, right: IType) => new ContainerType(SmartPyAtom.tuple, [left, right]);
export const TLambda = (left: IType, right: IType) => new ContainerType(Prim.lambda, [left, right]);
export const TContract = (innerType: IType) => new ContainerType(Prim.contract, [innerType]);
// Artificial Types
export const TRecord = (fields: Record<string, IType>, layout?: ILayout | Layout) => new Type_Record(fields, layout);

const Types = {
TUnknown,
Expand All @@ -93,16 +94,33 @@ const Types = {
TMutez,
TString,
TBool,
TBytes,
TAddress,
TTimestamp,
TChainID,
TChain_id,
TBls12_381_fr,
TBls12_381_g1,
TBls12_381_g2,
TKey,
TKey_hash,
TSignature,
TOperation,
TNever,
// Container types
TList,
TSet,
TOption,
TRecord,
TPair,
TMap,
TBigMap,
TTuple,
TBig_map,
TLambda,
// TTicket,
TContract,
// TSapling_state,
// TSapling_transaction,
// Artificial Types
TRecord,
// TVariant,
};

export default Types;
17 changes: 17 additions & 0 deletions src/misc/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { ILayout } from '../typings/literal';
import { IToString } from '../typings/shared';

export const parenthesis = (str: string) => `(${str})`;
export const capitalizeBoolean = (bool: boolean): string => (bool ? 'True' : 'False');

export class LineInfo implements IToString {
Expand All @@ -21,8 +23,23 @@ export class LineInfo implements IToString {
}
}

/**
* @description Build right aligned nested binary pairs
* @see https://tezos.gitlab.io/active/michelson.html#operations-on-pairs-and-right-combs
* @param fields A sequence of strings
* @returns {ILayout}
*/
export const composeRightCombLayout = (fields: string[]): ILayout => {
if (fields.length > 2) {
return [fields[0], composeRightCombLayout(fields.slice(1))];
}
return fields;
};

const Utils = {
capitalizeBoolean,
composeRightCombLayout,
parenthesis,
};

export default Utils;
6 changes: 3 additions & 3 deletions src/typings/type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { IToString } from './shared';

export type IType = IToString;
export interface IType {
toString: () => string;
}
16 changes: 8 additions & 8 deletions tests/Compilation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('Compile Contracts', () => {
.setStorage(Nat(1))
.addEntrypoint(
new EntryPoint('ep1')
.inputType(TNat)
.inputType(TNat())
.code((arg) => [
DefineVar('A', Nat(1)),
Require(Equal(GetLocal('A'), arg), String('Error Message')),
Expand All @@ -34,11 +34,11 @@ describe('Compile Contracts', () => {

it('Simple 2', () => {
const contract = new Contract()
.setStorage(List([Nat(1)], TNat))
.setStorage(List([Nat(1)], TNat()))
.addEntrypoint(
new EntryPoint('ep1')
.config({ lazy: false })
.inputType(TBool)
.inputType(TBool())
.code((arg) => [
DefineVar('A', Bool(false)),
Require(Equal(GetLocal('A'), arg), String('Error Message')),
Expand All @@ -56,10 +56,10 @@ describe('Compile Contracts', () => {

it('Simple 3', () => {
const contract = new Contract()
.setStorageType(TList(TNat))
.setStorage(List([], TNat))
.setStorageType(TList(TNat()))
.setStorage(List([], TNat()))
.addEntrypoint(
new EntryPoint('ep1').inputType(TList(TNat)).code((arg) => [
new EntryPoint('ep1').inputType(TList(TNat())).code((arg) => [
// Define a variable named "some_address"
DefineVar('some_address', Address('tz1')),
// Require sender to be equal to variable "some_address", otherwise fail with "Not Admin!"
Expand Down Expand Up @@ -88,7 +88,7 @@ describe('Compile Contracts', () => {
verifyMichelsonOutput(contract);
});
it('Storage (None)', () => {
const contract = new Contract().setStorageType(TOption(TNat)).setStorage(None()).toString();
const contract = new Contract().setStorageType(TOption(TNat())).setStorage(None()).toString();

expect(contract).toMatchSnapshot();
verifyMichelsonOutput(contract);
Expand All @@ -99,7 +99,7 @@ describe('Compile Contracts', () => {
.setStorage(
Record({
testField1: Nat(1),
testField2: List([String('Hello World')], TString),
testField2: List([String('Hello World')], TString()),
}),
)
.toString();
Expand Down
2 changes: 1 addition & 1 deletion tests/distributables/commonjs.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const SmartML = require('@tezwell/smartts-sdk/smartml');
const contract = new Contract()
.setStorage(Nat(0))
.addEntrypoint(
new EntryPoint('ep1').inputType(TNat).code((arg) => [
new EntryPoint('ep1').inputType(TNat()).code((arg) => [
// Define a variable named "some_address"
DefineVar('some_address', Address('tz1')),
// Require sender to be equal to variable "some_address", otherwise fail with "Not Admin!"
Expand Down
2 changes: 1 addition & 1 deletion tests/distributables/esm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import SmartML from '@tezwell/smartts-sdk/smartml';
const contract = new Contract()
.setStorage(Nat(0))
.addEntrypoint(
new EntryPoint('ep1').inputType(TNat).code((arg) => [
new EntryPoint('ep1').inputType(TNat()).code((arg) => [
// Define a variable named "some_address"
DefineVar('some_address', Address('tz1')),
// Require sender to be equal to variable "some_address", otherwise fail with "Not Admin!"
Expand Down
2 changes: 1 addition & 1 deletion tests/statements/foreach.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('Test (ForEach) statement', () => {
.setStorage(Nat(1))
.addEntrypoint(
new EntryPoint('ep1')
.inputType(TList(TNat))
.inputType(TList(TNat()))
.code((arg) => [
ForEachOf(arg).Do((i) => [SetValue(ContractStorage(), Add(ContractStorage(), i))]),
]),
Expand Down
Loading

0 comments on commit 257d287

Please sign in to comment.