export type Type = PrimitiveType | ArrayType | TupleType | BytesType ; export class ArrayType { constructor ( public elementType , public bound ? ) { } toString ( ) { return `${ this . elementType }[${ this . bound ?? '' }]` ; } } export class BytesType { constructor ( public bound ? ) { } static fromString ( str ) { const bound = str === 'byte' ? 1 : Number . parseInt ( str . substring ( 5 ) , 10 ) || undefined ; return new BytesType ( bound ) ; } toString ( ) { return `bytes${ this . bound ?? '' }` ; } } export class TupleType { constructor ( public elementType ? ) { } toString ( ) { return `(${ this . elementType }, ${ this . elementType })` ; } } export enum PrimitiveType { INT = 'int' , BOOL = 'bool' , STRING = 'string' , PUBKEY = 'pubkey' , SIG = 'sig' , DATASIG = 'datasig' , ANY = 'any' } const ExplicitlyCastableTo = { [ PrimitiveType . INT ] : [ PrimitiveType . INT , PrimitiveType . BOOL , ] , [ PrimitiveType . BOOL ] : [ PrimitiveType . BOOL , PrimitiveType . INT ] , [ PrimitiveType . STRING ] : [ PrimitiveType . STRING ] , [ PrimitiveType . PUBKEY ] : [ PrimitiveType . PUBKEY ] , [ PrimitiveType . SIG ] : [ PrimitiveType . SIG , PrimitiveType . DATASIG ] , [ PrimitiveType . DATASIG ] : [ PrimitiveType . DATASIG ] , [ PrimitiveType . ANY ] : [ ] , } ; export function explicitlyCastable ( from ? , to ? ) { if ( ! from || ! to ) return false ; if ( from instanceof TupleType || to instanceof TupleType ) return false ; if ( from instanceof ArrayType && to instanceof ArrayType ) { return explicitlyCastable ( from . elementType , to . elementType ) ; } if ( from instanceof ArrayType || to instanceof ArrayType ) return false ; if ( to instanceof BytesType ) { if ( from === PrimitiveType . BOOL ) return false ; if ( from === PrimitiveType . INT ) return true ; if ( ! to . bound ) return true ; if ( from instanceof BytesType ) { if ( ! from . bound ) return true ; return from . bound === to . bound ; } return false ; } if ( from instanceof BytesType ) { if ( to === PrimitiveType . INT ) return ! from . bound || from . bound <= 4 ; if ( to === PrimitiveType . BOOL ) return false ; if ( to === PrimitiveType . STRING ) return false ; if ( to === PrimitiveType . PUBKEY ) return true ; if ( to === PrimitiveType . SIG ) return true ; if ( to === PrimitiveType . DATASIG ) return true ; return true ; } return ExplicitlyCastableTo [ from ] . includes ( to ) ; } export function implicitlyCastable ( actual ? , expected ? ) { if ( ! actual || ! expected ) return false ; if ( actual instanceof TupleType || expected instanceof TupleType ) return false ; if ( actual instanceof ArrayType && expected instanceof ArrayType ) { return implicitlyCastable ( actual . elementType , expected . elementType ) ; } if ( actual instanceof ArrayType || expected instanceof ArrayType ) return false ; if ( expected === PrimitiveType . ANY ) return true ; if ( expected instanceof BytesType ) { if ( actual === PrimitiveType . BOOL ) return false ; if ( actual === PrimitiveType . INT ) return false ; if ( actual === PrimitiveType . STRING ) return false ; if ( ! expected . bound ) return true ; if ( actual instanceof BytesType ) { if ( ! actual . bound ) return false ; return actual . bound === expected . bound ; } return false ; } return actual === expected ; } export function resultingType ( left ? , right ? ) { if ( implicitlyCastable ( left , right ) ) return right ; if ( implicitlyCastable ( right , left ) ) return left ; if ( left instanceof BytesType && right instanceof BytesType ) { return new BytesType ( ) ; } return undefined ; } export function arrayType ( types ) { if ( types . length === 0 ) return undefined ; let resType = types [ 0 ] ; types . forEach ( ( t ) => { resType = resultingType ( resType , t ) as Type ; } ) ; return resType ; } export function implicitlyCastableSignature ( actual , expected ) { if ( actual . length !== expected . length ) return false ; return expected . every ( ( t , i ) => implicitlyCastable ( actual [ i ] , t ) ) ; } export function parseType ( str ) { if ( str . startsWith ( 'byte' ) ) return BytesType . fromString ( str ) ; return PrimitiveType [ str . toUpperCase ( ) as keyof typeof PrimitiveType ] ; } export function isPrimitive ( type ) { return ! ! PrimitiveType [ type . toString ( ) . toUpperCase ( ) as keyof typeof PrimitiveType ] ; }