refactor: Extend reflection to all implementations so far
And also enhances the reflected type parsing so that the types of all implementations so far will parse.
This commit is contained in:
parent
d8199341e0
commit
6f44567306
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
name: 'typocomath',
|
name: 'typocomath',
|
||||||
version: '0.0.1',
|
version: '0.0.2',
|
||||||
description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept',
|
description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept',
|
||||||
main: 'index.ts',
|
main: 'index.ts',
|
||||||
scripts: {
|
scripts: {
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import {Complex} from './type.js'
|
import {Complex} from './type.js'
|
||||||
import type {Dependencies, Signature} from '../interfaces/type.js'
|
import type {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
|
import {$reflect} from '../interfaces/type.js'
|
||||||
|
|
||||||
export const isReal =
|
export const isReal =
|
||||||
<T>(dep: Dependencies<'add' | 'equal' | 'isReal', T>):
|
<T>(dep: Dependencies<'add' | 'equal' | 'isReal', T>):
|
||||||
Signature<'isReal', Complex<T>> =>
|
Signature<'isReal', Complex<T>> =>
|
||||||
z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
|
z => dep.isReal(z.re) && dep.equal(z.re, dep.add(z.re, z.im))
|
||||||
|
|
||||||
export const isSquare: Signature<'isSquare', Complex<any>> = z => true // FIXME: not correct for Complex<bigint> once we get there
|
export const isSquare = (): Signature<'isSquare', Complex<unknown>> =>
|
||||||
|
z => true // FIXME: not correct for Complex<bigint> once we get there
|
||||||
|
|
||||||
|
$reflect!([isReal, isSquare])
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import {Complex} from './type.js'
|
import {Complex} from './type.js'
|
||||||
import {Dependencies, Signature} from '../interfaces/type.js'
|
import {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
|
import {$reflect} from '../interfaces/type.js'
|
||||||
|
|
||||||
export const equal =
|
export const equal =
|
||||||
<T>(dep: Dependencies<'equal', T>): Signature<'equal', Complex<T>> =>
|
<T>(dep: Dependencies<'equal', T>): Signature<'equal', Complex<T>> =>
|
||||||
(w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im)
|
(w, z) => dep.equal(w.re, z.re) && dep.equal(w.im, z.im)
|
||||||
|
$reflect!([equal])
|
||||||
|
@ -2,6 +2,7 @@ import {joinTypes, typeOfDependency} from '../core/Dispatcher.js'
|
|||||||
import type {
|
import type {
|
||||||
ZeroType, OneType, NaNType, Dependencies, Signature, Returns
|
ZeroType, OneType, NaNType, Dependencies, Signature, Returns
|
||||||
} from '../interfaces/type.js'
|
} from '../interfaces/type.js'
|
||||||
|
import {$reflect} from '../interfaces/type.js'
|
||||||
|
|
||||||
export type Complex<T> = { re: T; im: T; }
|
export type Complex<T> = { re: T; im: T; }
|
||||||
|
|
||||||
@ -62,3 +63,5 @@ export const nan =
|
|||||||
export const re =
|
export const re =
|
||||||
<T>(dep: Dependencies<'re', T>): Signature<'re', Complex<T>> =>
|
<T>(dep: Dependencies<'re', T>): Signature<'re', Complex<T>> =>
|
||||||
z => dep.re(z.re)
|
z => dep.re(z.re)
|
||||||
|
|
||||||
|
$reflect!([complex, zero, one, nan, re])
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
* for specific types (including their own).
|
* for specific types (including their own).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {parseReflectedType, ImplementationDef} from './parseReflectedType.js'
|
||||||
|
|
||||||
// First helper types and functions for the Dispatcher
|
// First helper types and functions for the Dispatcher
|
||||||
|
|
||||||
type TypeName = string
|
type TypeName = string
|
||||||
@ -39,19 +41,17 @@ type SpecificationsGroup = Record<string, SpecObject>
|
|||||||
export class Dispatcher {
|
export class Dispatcher {
|
||||||
installSpecification(
|
installSpecification(
|
||||||
name: string,
|
name: string,
|
||||||
signature: Signature,
|
defn: ImplementationDef,
|
||||||
returns: TypeName,
|
|
||||||
dependencies: Record<string, Signature>,
|
|
||||||
behavior: Function // possible todo: constrain this type based
|
behavior: Function // possible todo: constrain this type based
|
||||||
// on the signature, return type, and dependencies. Not sure if
|
// on the signature, return type, and dependencies. Not sure if
|
||||||
// that's really possible, though.
|
// that's really possible, though.
|
||||||
) {
|
) {
|
||||||
console.log('Pretending to install', name, signature, '=>', returns)
|
console.log('Pretending to install', name, 'with signatures')
|
||||||
// @ts-ignore
|
for (const signature of defn.fn.signatures) {
|
||||||
if (behavior.reflectedType) {
|
console.log(' ', signature.args, '=>', signature.returns)
|
||||||
// @ts-ignore
|
}
|
||||||
console.log(' Reflected type:', behavior.reflectedType)
|
if (defn.fn.aliasOf) {
|
||||||
// TODO: parse the reflected type
|
console.log(' As an alias of', defn.fn.aliasOf)
|
||||||
}
|
}
|
||||||
//TODO: implement me
|
//TODO: implement me
|
||||||
}
|
}
|
||||||
@ -77,7 +77,8 @@ export class Dispatcher {
|
|||||||
for (const trio of implementations) {
|
for (const trio of implementations) {
|
||||||
const [k, id, imp] = trio
|
const [k, id, imp] = trio
|
||||||
console.log('Handling implementation', id, 'from', k)
|
console.log('Handling implementation', id, 'from', k)
|
||||||
this.installSpecification(id, ['dunno'], 'unsure', {}, imp)
|
this.installSpecification(
|
||||||
|
id, parseReflectedType(id, imp.reflectedType), imp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
export interface FunctionDef {
|
export type FunctionDef {
|
||||||
name: string,
|
name: string,
|
||||||
|
aliasOf?: string,
|
||||||
signatures: Array<{
|
signatures: Array<{
|
||||||
args: Array<{ name: string, type: string }>
|
args: Array<{ name: string, type: string }>
|
||||||
// FIXME: remove undefined in the end
|
returns: string
|
||||||
returns: string | undefined
|
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DependencyDef extends FunctionDef {
|
|
||||||
aliasOf: string | undefined
|
export type ImplementationDef = {
|
||||||
|
fn: FunctionDef,
|
||||||
|
dependencies: Record<string, FunctionDef>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,32 +18,34 @@ export interface DependencyDef extends FunctionDef {
|
|||||||
*
|
*
|
||||||
* '<T>(dep: configDependency & { complex: ((re: number) => Complex<number>) | ((re: number, im: number) => Complex<number>); }) => (a: number) => number | Complex<number>'
|
* '<T>(dep: configDependency & { complex: ((re: number) => Complex<number>) | ((re: number, im: number) => Complex<number>); }) => (a: number) => number | Complex<number>'
|
||||||
*/
|
*/
|
||||||
export function parseReflectedType(name: string, reflectedType: string) : { fn: FunctionDef, dependencies: Record<string, DependencyDef> } {
|
export function parseReflectedType(name: string, reflectedType: string): ImplementationDef {
|
||||||
const [factoryArgs, fnArgsBlock, returns] = split(reflectedType, '=>').map(trim)
|
console.log('For', name, 'parsing', reflectedType)
|
||||||
const args = parseArgs(fnArgsBlock)
|
const [factoryArgs, fnsClause] = split(reflectedType, '=>', 2).map(trim)
|
||||||
|
const fn = parseAlias(name, fnsClause)
|
||||||
|
|
||||||
const factoryArgsInner = findBlockContents(factoryArgs, '(', ')')
|
const factoryArgsInner = findBlockContents(factoryArgs, '(', ')')
|
||||||
const depArg = split(factoryArgsInner.innerText, ':').map(trim)[1]
|
const depArg = split(factoryArgsInner.innerText, ':').map(trim)[1]
|
||||||
const depArgBlocks = split(depArg, '&').map(trim)
|
const depArgBlocks: string[] = depArg ? split(depArg, '&').map(trim) : []
|
||||||
|
|
||||||
const deps = depArgBlocks
|
const deps = depArgBlocks
|
||||||
.filter(depArgBlock => {
|
.filter(depArgBlock => {
|
||||||
if (depArgBlock.startsWith('{')) {
|
if (depArgBlock.startsWith('{') || depArgBlock === 'configDependency') {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
console.error(`ERROR: Cannot parse dependency "${depArgBlock}"`)
|
throw new SyntaxError(`Cannot parse dependency "${depArgBlock}"`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.flatMap(parseDependencies)
|
.flatMap(parseDependencies)
|
||||||
|
|
||||||
const dependencies: Record<string, DependencyDef> = groupBy(deps, 'name')
|
const dependencies: Record<string, FunctionDef> = groupBy(deps, 'name')
|
||||||
|
|
||||||
return {
|
return {fn, dependencies}
|
||||||
fn: { name, signatures: [{args, returns}] },
|
|
||||||
dependencies
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDependencies(deps: string) {
|
function parseDependencies(deps: string): FunctionDef[] {
|
||||||
|
if (deps === 'configDependency') {
|
||||||
|
return [{name: 'config', signatures: [{args: [], returns: 'Config'}]}]
|
||||||
|
}
|
||||||
const inner = findBlockContents(deps, '{', '}').innerText
|
const inner = findBlockContents(deps, '{', '}').innerText
|
||||||
return split(inner, ';')
|
return split(inner, ';')
|
||||||
.map(trim)
|
.map(trim)
|
||||||
@ -50,19 +54,35 @@ export function parseReflectedType(name: string, reflectedType: string) : { fn:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse a dependency like "complex: ((re: number) => Complex<number>) | ((re: number, im: number) => Complex<number>)"
|
// parse a dependency like "complex: ((re: number) => Complex<number>) | ((re: number, im: number) => Complex<number>)"
|
||||||
function parseDependency(dep: string) {
|
function parseDependency(dep: string): FunctionDef {
|
||||||
const [name, def] = split(dep, ':').map(trim)
|
const [name, def] = split(dep, ':').map(trim)
|
||||||
|
|
||||||
const { aliasOf, innerSignature } = parseAliasOf(def)
|
return parseAlias(name, def)
|
||||||
|
}
|
||||||
|
|
||||||
const signatures = split(innerSignature, '|')
|
// Parse a possibly aliased function
|
||||||
|
function parseAlias(name: string, alias: string): FunctionDef {
|
||||||
|
const { aliasOf, innerSignature } = parseAliasOf(alias)
|
||||||
|
|
||||||
|
return { name, signatures: parseSignatures(innerSignature), aliasOf }
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse function signatures like ((re: number) => Complex<number>) | ((re: number, im: number) => Complex<number>)
|
||||||
|
// But also have to succeed on (a: number) => number | Complex<number>
|
||||||
|
// That's why we only split on an alternation bar `|` that's followed by
|
||||||
|
// a parenthesis; that way we avoid splitting a union return type. Note
|
||||||
|
// this is not necessarily foolproof, as there could be a return type that
|
||||||
|
// is a union with a complicated piece that has to be enclosed in parens;
|
||||||
|
// but so far it seems to work in practice.
|
||||||
|
function parseSignatures(sigs: string) {
|
||||||
|
return split(sigs, /[|]\s*(?=[(])/)
|
||||||
.map(trim)
|
.map(trim)
|
||||||
.map(stripParenthesis)
|
.map(stripParenthesis)
|
||||||
.map(signature => {
|
.map(signature => {
|
||||||
const [argsBlock, returns] = split(signature, '=>').map(trim)
|
const [argsBlock, returns] = split(signature, '=>').map(trim)
|
||||||
|
|
||||||
if (!returns) {
|
if (!returns) {
|
||||||
console.warn(`ERROR: failed to find return type in '${signature}'`)
|
throw new SyntaxError(`Failed to find return type in '${signature}'`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -70,8 +90,6 @@ export function parseReflectedType(name: string, reflectedType: string) : { fn:
|
|||||||
returns
|
returns
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return { name, signatures, aliasOf }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse args like "(re: number, im: number)"
|
// parse args like "(re: number, im: number)"
|
||||||
@ -109,7 +127,6 @@ export function parseReflectedType(name: string, reflectedType: string) : { fn:
|
|||||||
? text.substring(1, text.length - 1)
|
? text.substring(1, text.length - 1)
|
||||||
: text
|
: text
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function findBlockContents(text: string, blockStart: string, blockEnd: string, startIndex = 0) : { start: number, end: number, innerText: string } | undefined {
|
function findBlockContents(text: string, blockStart: string, blockEnd: string, startIndex = 0) : { start: number, end: number, innerText: string } | undefined {
|
||||||
let i = startIndex
|
let i = startIndex
|
||||||
@ -143,21 +160,45 @@ function findBlockContents(text: string, blockStart: string, blockEnd: string, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a string, generate a string source for a regexp that will match
|
||||||
|
* exactly the given string.
|
||||||
|
* Uses the fact that the only characters that need to be escaped in
|
||||||
|
* a character class are \, ], and ^
|
||||||
|
*/
|
||||||
|
function regexpQuote(s: string) {
|
||||||
|
const special = '\\]^'
|
||||||
|
let re = ''
|
||||||
|
for (const char of s) {
|
||||||
|
if (special.includes(char)) re += `\\${char}`
|
||||||
|
else re += `[${char}]`
|
||||||
|
}
|
||||||
|
return re
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split a string by a delimiter, but ignore all occurrences of the delimiter
|
* Split a string by a delimiter, but ignore all occurrences of the delimiter
|
||||||
* that are inside bracket pairs <> () [] {}
|
* that are inside bracket pairs <> () [] {}
|
||||||
*/
|
*/
|
||||||
export function split(text: string, delimiter: string) : string[] {
|
export function split(
|
||||||
|
text: string, delimiter: string | RegExp, pieces = 0): string[] {
|
||||||
|
const delim: RegExp = typeof delimiter === 'string'
|
||||||
|
? new RegExp(regexpQuote(delimiter), 'y')
|
||||||
|
: new RegExp(delimiter.source, 'y')
|
||||||
const parts: string[] = []
|
const parts: string[] = []
|
||||||
|
|
||||||
let i = 0
|
let i = 0
|
||||||
|
let n = 1
|
||||||
let start = 0
|
let start = 0
|
||||||
while (i < text.length) {
|
while (i < text.length && (pieces === 0 || n < pieces)) {
|
||||||
i = skipBrackets(text, i)
|
i = skipBrackets(text, i)
|
||||||
|
|
||||||
if (matchSubString(text, delimiter, i)) {
|
delim.lastIndex = i
|
||||||
|
const result = delim.exec(text)
|
||||||
|
if (result) {
|
||||||
parts.push(text.substring(start, i))
|
parts.push(text.substring(start, i))
|
||||||
i += delimiter.length
|
n += 1
|
||||||
|
i += result[0].length
|
||||||
start = i
|
start = i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import {Dependencies, Signature} from '../interfaces/type.js'
|
import {Dependencies, Signature} from '../interfaces/type.js'
|
||||||
|
import {$reflect} from '../interfaces/type.js'
|
||||||
|
|
||||||
export const unequal =
|
export const unequal =
|
||||||
<T>(dep: Dependencies<'equal', T>): Signature<'unequal', T> =>
|
<T>(dep: Dependencies<'equal', T>): Signature<'unequal', T> =>
|
||||||
(x, y) => !dep.equal(x, y)
|
(x, y) => !dep.equal(x, y)
|
||||||
|
$reflect!([unequal])
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
export * from './type.js'
|
export * from './type.js'
|
||||||
export * from './arithmetic.js'
|
export * from './arithmetic.js'
|
||||||
|
export * from './predicate.js'
|
||||||
|
export * from './relational.js'
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import type {Signature} from '../interfaces/type.js'
|
import type {Signature} from '../interfaces/type.js'
|
||||||
|
import {$reflect} from '../interfaces/type.js'
|
||||||
|
|
||||||
export const isReal: Signature<'isReal', number> = (a) => true
|
export const isReal = (): Signature<'isReal', number> => (a) => true
|
||||||
export const isSquare: Signature<'isSquare', number> = (a) => a >= 0
|
export const isSquare = (): Signature<'isSquare', number> => (a) => a >= 0
|
||||||
|
|
||||||
|
$reflect!([isReal, isSquare])
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {configDependency} from '../core/Config.js'
|
import {configDependency} from '../core/Config.js'
|
||||||
import {Signature} from '../interfaces/type.js'
|
import {Signature} from '../interfaces/type.js'
|
||||||
|
import {$reflect} from '../interfaces/type.js'
|
||||||
|
|
||||||
const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16
|
const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16
|
||||||
|
|
||||||
@ -19,3 +20,4 @@ export const equal =
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
$reflect!([equal])
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import type { Signature } from '../interfaces/type.js'
|
import type { Signature } from '../interfaces/type.js'
|
||||||
|
import {$reflect} from '../interfaces/type.js'
|
||||||
|
|
||||||
export const number_type = {
|
export const number_type = {
|
||||||
name: 'number', // just until we have reflection to tell us
|
name: 'number', // just until we have reflection to tell us
|
||||||
@ -20,7 +21,9 @@ declare module "../interfaces/type" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// I don't like the redundancy of repeating 'zero'; any way to eliminate that?
|
// I don't like the redundancy of repeating 'zero'; any way to eliminate that?
|
||||||
export const zero: Signature<'zero', number> = (a) => 0
|
export const zero = (): Signature<'zero', number> => (a) => 0
|
||||||
export const one: Signature<'one', number> = (a) => 1
|
export const one = (): Signature<'one', number> => (a) => 1
|
||||||
export const nan: Signature<'nan', number> = (a) => NaN
|
export const nan = (): Signature<'nan', number> => (a) => NaN
|
||||||
export const re: Signature<'re', number> = (a) => a
|
export const re = (): Signature<'re', number> => (a) => a
|
||||||
|
|
||||||
|
$reflect!([zero, one, nan, re])
|
||||||
|
Loading…
Reference in New Issue
Block a user