Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add new types and improve type references #3

Merged
merged 3 commits into from
Feb 3, 2022
Merged
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
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