Skip to content

Commit 30bdc9b

Browse files
committed
preprocessor: support importing registered package
1 parent 07f4001 commit 30bdc9b

File tree

9 files changed

+365
-202
lines changed

9 files changed

+365
-202
lines changed

packages/hedgehog-core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@babel/preset-env": "^7.10.4",
2424
"@babel/preset-typescript": "^7.10.4",
2525
"@babel/standalone": "^7.10.5",
26+
"@tensorflow/tfjs": "^2.7.0",
2627
"babel-template": "^6.26.0",
2728
"gpu.js": "^2.9.5",
2829
"mathjs": "^7.1.0",

packages/hedgehog-core/src/runtime/prelude.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,33 @@ import { Sym } from '../lib/symbolic';
88
import { Chol, QR, LU } from '../lib/algebra';
99
import { OutputItem } from '../output/output-item';
1010
import { rawInputsToTex } from '../utilites/process-raw-inputs';
11+
12+
/*
13+
Third party libraries
14+
*/
15+
16+
// synchrounous fetch
1117
const fetch = require('sync-fetch');
1218

19+
// gpu.js
1320
import { GPU } from 'gpu.js';
1421

22+
// math.js
1523
import * as mathjs from 'mathjs';
1624

17-
export { Sym, Mat, Scalar, _Mat, nerdamer, GPU, mathjs };
25+
// ml.js
26+
27+
// tensorflow.js
28+
import * as tf from '@tensorflow/tfjs';
29+
30+
// opencv.js
31+
32+
// d3.js
33+
34+
//tvm.js
35+
36+
37+
export { Sym, Mat, Scalar, _Mat, nerdamer, GPU, mathjs, tf, Chol };
1838

1939
/**
2040
* wrapper of constructing a Mat object
@@ -253,6 +273,18 @@ export function draw(data: any, layout?: any) {
253273

254274
// plot2D is a wrapper for draw() function for scatter plot on 2D only
255275
export function plot2D(x_: any, y_: any) {
276+
if (x_ instanceof Mat && y_ instanceof Mat) {
277+
draw([
278+
{
279+
x: x_.toArray(),
280+
y: y_.toArray(),
281+
type: 'scatter',
282+
mode: 'markers',
283+
marker: { color: 'blue', size: '2' },
284+
},
285+
]);
286+
return;
287+
}
256288
draw([
257289
{
258290
x: x_,
@@ -265,6 +297,18 @@ export function plot2D(x_: any, y_: any) {
265297
}
266298

267299
export function plot2DLine(x_: any, y_: any) {
300+
if (x_ instanceof Mat && y_ instanceof Mat) {
301+
draw([
302+
{
303+
x: x_.toArray(),
304+
y: y_.toArray(),
305+
type: 'scatter',
306+
mode: 'lines+markers',
307+
marker: { color: 'blue', size: '4' },
308+
},
309+
]);
310+
return;
311+
}
268312
draw([
269313
{
270314
x: x_,
@@ -278,6 +322,23 @@ export function plot2DLine(x_: any, y_: any) {
278322

279323
// plot3D is a wrapper for draw() function for scatter plot on 3D only
280324
export function plot3D(x_: any, y_: any, z_: any) {
325+
if (x_ instanceof Mat && y_ instanceof Mat && z_ instanceof Mat){
326+
draw(
327+
[
328+
{
329+
x: x_.toArray(),
330+
y: y_.toArray(),
331+
z: z_.toArray(),
332+
mode: 'markers',
333+
marker: { color: 'blue', size: 2 },
334+
opacity: 0.5,
335+
type: 'scatter3d',
336+
},
337+
],
338+
{}
339+
);
340+
return;
341+
}
281342
draw(
282343
[
283344
{
@@ -295,6 +356,22 @@ export function plot3D(x_: any, y_: any, z_: any) {
295356
}
296357

297358
export function plot3DMesh(x_: any, y_: any, z_: any) {
359+
if (x_ instanceof Mat && y_ instanceof Mat && z_ instanceof Mat){
360+
draw(
361+
[
362+
{
363+
x: x_.toArray(),
364+
y: y_.toArray(),
365+
z: z_.toArray(),
366+
mode: 'markers',
367+
marker: { color: 'blue', size: 2 },
368+
opacity: 0.5,
369+
type: 'mesh3d',
370+
},
371+
],
372+
{}
373+
);
374+
}
298375
draw(
299376
[
300377
{

packages/hedgehog-core/src/transpiler/preprocessor.ts

Lines changed: 110 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,92 @@
1+
/*
2+
About preprocessor, please check the document at
3+
https://github.com/Hedgehog-Computing/Hedgehog-Package-Manager
14
2-
async function fetchLibrary(lib_url: string) {
3-
let raw_string_return = fetch(lib_url, { method: 'get' })
4-
.then(function (body) {
5-
return body.text();
6-
})
7-
.then(function (data) {
8-
return data;
9-
});
10-
11-
return raw_string_return;
12-
}
13-
5+
Hedgehog Lab supports three types of "*import":
6+
a. *import YOUR_FULL_URL, for example *import http://website.com/mylib/myfunction.hhs
7+
b. *import Package_Name: Function_1, Function_2, Function_3 ... the package name must be registered at https://raw.githubusercontent.com/Hedgehog-Computing/Hedgehog-Package-Manager/main/hedgehog-packages.json
8+
c. let my_Function_A = *import MY_PACKAGE: Function_A
9+
*/
1410

1511
async function preprocessor(source: string): Promise<string> {
1612
console.log('The source code after preprocessing');
17-
let result = await preprocessDFS(source , 'root');
13+
let result = await preprocessDFS(source, 'root');
1814
console.log(result);
1915
console.log('End of the source code after preprocessing');
2016
return result;
2117
}
2218

2319
/*
20+
Fetch the full registered package list from
21+
https://github.com/Hedgehog-Computing/Hedgehog-Package-Manager
22+
at
23+
https://raw.githubusercontent.com/Hedgehog-Computing/Hedgehog-Package-Manager/main/hedgehog-packages.json
2424
25-
Function "preprocess_dependencies" is the core function that handle the dependencies of the source code
26-
by feteching the strings of libraries and adding at the top of the source code.
27-
28-
For example, the input string is --
29-
30-
*import http://a.com/my_function.js
31-
let matrixA = mat([[1,2,3],[4,5,6]])
32-
let result = my_function(matrixA)
33-
print(result)
34-
35-
36-
--
25+
Input: Package Name. For example, "Hedgehog-Standard-Library" or "std"
26+
Output: The root location of the package. For example, "https://raw.githubusercontent.com/Hedgehog-Computing/Hedgehog-Standard-Library/main/"
27+
*/
3728

38-
And the library in http://a.com/my_function.js is --
39-
function my_function(matrixA: Mat):Mat
40-
{
41-
return matrixA * matrixA.T();
29+
function getPackageLocation(packageName: string, theFullListInJson: string): string {
30+
console.log("Package name: " + packageName + " , full list in json: " + theFullListInJson);
31+
let jsonObj = JSON.parse(theFullListInJson); let result: string[] = [];
32+
for (let element of jsonObj) {
33+
if (element["name"] === packageName || element["alias"] === packageName) { return element["location"]; }
34+
}
35+
throw "Cannot find the package with name: " + packageName;
4236
}
4337

44-
--
45-
46-
Then the output of preprocess function should be:
47-
48-
49-
function my_function(matrixA: Mat):Mat
50-
{
51-
return matrixA * matrixA.T();
38+
/*
39+
Parse the registered package (the type 1 of import macro: *import PACKAGE_NAME: LIB_NAME_LIST)
40+
Input: the second part of current line splitted by "*import". For example, current line is "*import std:magic, cholesky",
41+
then the input will be "std:magic, cholesky"; if current line is "let myMagic = *import std:magic", then the input should
42+
be "std:magic, cholesky"
43+
Output: A list of string, each string represents the corresponding hhs source file
44+
*/
45+
async function parseRegisterdPackage(secondPart: string): Promise<Array<string>> {
46+
let returnListOfFunctions: string[] = [];
47+
let theFullListInJson = await fetch("https://raw.githubusercontent.com/Hedgehog-Computing/Hedgehog-Package-Manager/main/hedgehog-packages.json", { method: 'get' }).then(body => body.text());
48+
let splittedResult = secondPart.split(':');
49+
if (splittedResult.length != 2) throw "Invalid importing library: " + secondPart;
50+
51+
//get the right package name and HHS list string
52+
let packageName = splittedResult[0]
53+
let importedHHSListString = splittedResult[1];
54+
55+
//get package location
56+
let packageLocation = getPackageLocation(packageName.replace(/\s/g, ''), theFullListInJson);
57+
58+
//get the package json file: package_location + hedgehog-package.json
59+
let packageJsonFile = packageLocation + "hedgehog-package.json";
60+
61+
console.log("Package Json file: " + packageJsonFile);
62+
//get the hedgehog-pacakge.json, then get the list of "includes" libraries
63+
let thePackageJsonString = await fetch(packageJsonFile, { method: 'get' }).then(body => body.text());
64+
console.log("Package Json string: " + thePackageJsonString);
65+
let thePackageJsonObj = JSON.parse(thePackageJsonString);
66+
if ("includes" in thePackageJsonObj) {
67+
let hhsCompleteList = thePackageJsonObj["includes"];
68+
let setHHSCompleteList = new Set(hhsCompleteList);
69+
let importedItemList = importedHHSListString.split(',');
70+
for (let eachItem of importedItemList) {
71+
let eachItemWithoutSpace = eachItem.replace(/\s/g, '');
72+
if (setHHSCompleteList.has(eachItemWithoutSpace)) {
73+
let currentHHSLocation = packageLocation + eachItemWithoutSpace + ".hhs";
74+
let currentItemSourceCode = await fetch(currentHHSLocation, { method: 'get' }).then(body => body.text());
75+
returnListOfFunctions.push(currentItemSourceCode);
76+
}
77+
}
78+
}
79+
else { throw "Cannot find \"includes\" key in the hedgehog-package.json configuration file! Please add a key with name \"includes\" with a complete list of exported libraries. Exception at " + secondPart}
80+
return returnListOfFunctions;
5281
}
53-
let matrixA = mat([[1,2,3],[4,5,6]])
54-
let result = my_function(matrixA)
55-
print(result)
56-
57-
58-
or the input string is
59-
60-
func = *import http://a.com/my_function.js
61-
let matrixA = mat([[1,2,3],[4,5,6]])
62-
let result = my_function(matrixA)
63-
print(result)
64-
65-
and the preprocessed string will be
6682

67-
func = function my_function(matrixA: Mat):Mat
68-
{
69-
return matrixA * matrixA.T();
83+
// A helper function to check if a string contains URL or not. Reference: https://regexr.com/3e6m0
84+
function containsURL(code: string): boolean {
85+
let expression = /(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g;
86+
let regex = new RegExp(expression);
87+
if (code.match(regex)) return true;
88+
return false;
7089
}
71-
let matrixA = mat([[1,2,3],[4,5,6]])
72-
let result = func(matrixA)
73-
print(result)
74-
75-
---
76-
77-
78-
Syntax: each third-party dependency line MUST start with keyword "import" following with the string of URL
79-
80-
For example:
81-
82-
*import http://a.com/b.js
83-
84-
85-
Todo:
86-
Also there is an official repo that contains all stable and official libraries at https://github.com/lidangzzz/hedgehog-lib/blob/master/stable/
87-
To use an official stable libraries in stable folder, the user should add the dependency in this way (for example, a demo function at
88-
https://raw.githubusercontent.com/lidangzzz/hedgehog-lib/master/stable/demo_function.js, the user should add the dependency by adding
89-
90-
using demo_function
91-
92-
at the beginning of the script, which is the same as
93-
94-
import https://github.com/lidangzzz/hedgehog-lib/blob/master/stable/demo_function.js
95-
96-
Also the preprocessor provides a way to allow user to manage the stable libraries with another repo or standalone server or mirror
97-
instead of the "hedgehog-lib" repo on github by defining
98-
99-
STABLE_CODE_BASE = 'http://myserver.com/my-code-base/'
100-
101-
so that user can import a stable function from their owm mirror server http://myserver.com/my-code-base/demo_function.js by using
102-
103-
using demo_function
104-
105-
*/
106-
107-
10890

10991
// code is the string of code, and strCurrentCallStack is the full call stack
11092
async function preprocessDFS(code: string, strCurrentCallStack: string): Promise<string> {
@@ -115,40 +97,54 @@ async function preprocessDFS(code: string, strCurrentCallStack: string): Promise
11597
let returnCode = '';
11698

11799
//3. process each line of code
118-
try{
119-
for (let i=0;i<vecSplittedString.length; i++){
100+
try {
101+
for (let i = 0; i < vecSplittedString.length; i++) {
120102
returnCode += '\n';
121103
//3.1 if current line of code doesn't contain "*import ", just append it to returnCode
122-
if (!vecSplittedString[i].includes("*import ")){ returnCode += '\n' + vecSplittedString[i]; }
104+
if (!vecSplittedString[i].includes("*import ")) { returnCode += '\n' + vecSplittedString[i]; }
123105
//3.2 otherwise, split the string by "*import ", keep the first part (if it exists), then download
124-
// and fetch the second part recursively (which should be and must be a valid URL)
106+
// and fetch the second part recursively (which should be and must be a valid URL or a registered package)
125107
else {
126108
let currentString = vecSplittedString[i];
127109
let splittedResult = currentString.split("*import ");
128-
if (splittedResult.length<2) {
129-
throw "Invalid current line of code for preprocessing: \n"
130-
+ "\nCall stack: \n" + strCurrentCallStack
131-
+ "\nCurrent line: "+ currentString + "\n";
110+
if (splittedResult.length < 2) {
111+
throw "Invalid current line of code for preprocessing: \n"
112+
+ "\nCall stack: \n" + strCurrentCallStack
113+
+ "\nCurrent line: " + currentString + "\n";
132114
}
133115
//3.2.1 add the first part
134116
returnCode += splittedResult[0];
135-
console.log(splittedResult[0] + " is added to source code" )
136-
//3.2.2 download the library from URL
137-
let libraryFromUrl = await fetch(splittedResult[1], { method: 'get' })
138-
.then(function (body) {
139-
let real_library = body.text();
140-
return real_library;
141-
});
142-
143-
//3.2.3 get the current file information (get "FunctionABC.js" from URL string http://mywebsite/FunctionABC.js)
144-
let splittedURLResult = splittedResult[1].split('/');
145-
let strCallStack = strCurrentCallStack + " -> " + splittedURLResult[splittedResult.length-1];
146-
147-
//3.2.4 process the big chunk of code
148-
let currentResult = await preprocessDFS(libraryFromUrl, strCallStack);
117+
//3.2.2 Is it imported from URL or from a registered package?
118+
if (containsURL(splittedResult[1])){
119+
//3.2.2.1 download the library from URL
120+
let libraryFromUrl = await fetch(splittedResult[1], { method: 'get' })
121+
.then(function (body) {
122+
let real_library = body.text();
123+
return real_library;
124+
});
125+
126+
//3.2.3 get the current file information (get "FunctionABC.js" from URL string http://mywebsite/FunctionABC.js)
127+
let splittedURLResult = splittedResult[1].split('/');
128+
let strCallStack = strCurrentCallStack + " -> " + splittedURLResult[splittedResult.length - 1];
129+
130+
//3.2.4 process the big chunk of code
131+
let currentResult = await preprocessDFS(libraryFromUrl, strCallStack);
132+
133+
//3.2.5 append it to the end of returnCode
134+
returnCode += currentResult + "\n";
135+
}
149136

150-
//3.2.5 append it to the end of returnCode
151-
returnCode += currentResult + "\n";
137+
else{
138+
// otherwise, try to split with colon and comma and fetch the registered packages
139+
let result = await parseRegisterdPackage(splittedResult[1]);
140+
let combined_result = "";
141+
result.forEach(element => {
142+
combined_result += element + "\n"
143+
returnCode+= combined_result + '\n';
144+
});
145+
}
146+
147+
152148
}
153149
}
154150
}

packages/hedgehog-lab/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@material-ui/icons": "^4.9.1",
99
"@material-ui/lab": "^4.0.0-alpha.56",
1010
"@monaco-editor/react": "^3.4.2",
11+
"@tensorflow/tfjs": "^2.7.0",
1112
"clsx": "^1.1.1",
1213
"comlink": "^4.3.0",
1314
"ky": "^0.23.0",

0 commit comments

Comments
 (0)