-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfpm.js
More file actions
116 lines (104 loc) · 2.97 KB
/
fpm.js
File metadata and controls
116 lines (104 loc) · 2.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* Find the datatype of an input.
* @param {*} a - Any datatype
* @returns {String} - The datatype of a as a string.
*/
const getDataType = (a) => {
if (a === undefined) {
return 'Undefined';
}
if (a === null) {
return 'Null';
}
return (a).name || ((a).constructor && (a).constructor.name);
}
function formattingError() {
throw new Error(`There is a formatting error somewhere.
Your definition should look something like this:
m.add = ({ a: Number, b: Number }) => {
return a + a;
};
or even:
m.add = ({ a: Number, b: Number }) => a + a;
`);
}
/**
* Creates an object of methods
* @param {String} name - The name of our function
*/
const fpm = (name) => {
if (!name || name === '' || typeof name !== 'string') {
throw new Error('You must specify a function name');
}
// Store patterns as object
const patterns = {};
// Initial
const ctx = this;
/**
* Split stringified arguments to array of argument names and types
* @param {String} str - Stringified arguments
* @returns {Array}
*/
const convertToArgs = (str) =>
str.split(',')
.map((arg) => {
const pair = arg.trim().split('=');
if (pair.length < 2) {
if (pair.length === 1) {
throw new TypeError(`Argument ${pair[0]} required an input type`);
}
throw new TypeError(`Invalid argument setup`);
}
return pair.map((item, k) => item.trim());
});
const mergeArgTypes = (...args) => {
if (!args || args.length === 0) {
return '_';
}
return args.map((arg) => arg[1]).join(',');
};
/**
* Get the arguments and argument types from stringified function
* @param {*} str - Stringified function definition
* @returns {Array|Error}
*/
const getArgs = (str) => {
const regExp = /\(([^)]+)\)/;
const matches = regExp.exec(str);
if (matches.length === 0) {
formattingError();
}
// The first one will be a string of an object.
return convertToArgs(matches[0].substring(1, matches[0].length-1));
}
/*
Could search for number of arguments first
Then try for type matches. May be faster...
*/
const attemptMatch = (...args) => {
const key = args.map((arg) => getDataType(arg)).join(',');
if (!patterns[key]) {
// throw or silent fail?
throw new Error('Unable to find a function pattern match');
}
return patterns[key](...args);
}
// Define the function within this context
ctx[name] = () => {};
const o = {};
Object.defineProperty(o, 'add', {
set(fn) {
const fnStr = fn.toString();
// Extract arguments from function string
const args = getArgs(fnStr);
// Convert function argument types to a comma separated string
const key = mergeArgTypes(...args);
// patterns[key] = new Function('a', 'b', 'return a + b');
patterns[key] = fn;
// May need to use Function.caller to maintain caller context
ctx[name] = attemptMatch;
},
});
return o;
};
module.exports = fpm;