@ucod-io/parser
v5.1.7
Published
A parser designed for the UCOD project, capable of ingeniously transforming TypeScript and JSON into a UCOD node structures.
Downloads
30
Readme
UCOD Parser
Description
This parser is designed specifically for UCOD nodes and flows. It efficiently interprets UCOD files, extracting nodes and flows to produce a corresponding JSON representation.
Usage
import { parseNode } from 'mod.ts';
/**
* For node use
* import { parseNode } from '@ucod-io/parser'
*/
const parsedNode = parseNode(`
export const $$name = "Addition";
export default (a: number, b: number): number => a + b;
`);
console.log(parsedNode);
{
"name": "Addition",
"description": "",
"state": [],
"config": [],
"ports": {
"flow": { "in": false, "out": [] },
"data": {
"in": [
{ "name": "a", "type": "number", "optional": false },
{ "name": "b", "type": "number", "optional": false }
],
"out": [{ "name": "number", "type": "number" }]
}
}
}
API
Functions
parseNode
parseNode
takes the code of a node as a string and returns a JSON representation of the node.
import { parseNode } from '@ucod-io/parser';
const additionCode = `
export const $$name = "Addition";
export default (a: number, b: number): number => a + b;
`;
const parsedNode = parseNode(additionCode);
parseFlow
parseFlow
takes a json representation of the flow and returns a parsed flow JSON.
import { parseFlow } from '@ucod-io/parser';
const flow = {
name: 'additionFlow',
version: '1.0.0',
nodes: [
{
id: 'core.math.addition',
code: addition.code,
},
{
id: 'core.math.randomNumber',
code: randomNumber.code,
},
],
connections: [
['core.math.randomNumber', 'core.math.addition'],
['core.math.randomNumber', 'core.math.addition'],
],
};
const parsedFlow = parseFlow(flow);
parseAst
To allow the usage of the parser in the browser, the parser can be used with an AST instead of a string. The AST is generated by @swc/wasm-web.
import { parseAst } from '@ucod-io/parser';
import { useEffect, useState } from 'react';
import initSwc, { parseSync } from '@swc/wasm-web';
export default function App() {
const [initialized, setInitialized] = useState(false);
useEffect(() => {
async function importAndRunSwcOnMount() {
await initSwc();
setInitialized(true);
}
importAndRunSwcOnMount();
}, []);
function parse() {
if (!initialized) {
return;
}
const additionCode = `
export const $$name = "Addition";
export default (a: number, b: number): number => a + b;
`;
const result = parseSync(additionCode, {
syntax: 'typescript',
target: 'esnext',
comments: true,
});
const ast = result.body;
const parsedNode = parseAst(ast);
console.log(parsedNode);
}
return (
<div className='App'>
<button onClick={parse}>Parse</button>
</div>
);
}
parseAst
accepts the body of the AST as a parameter. The AST body is the result of the parseSync
function of
@swc/wasm-web. For more information about the SWC project, please visit the
SWC website.
Interfaces
ParsedNode
interface ParsedNode {
name: string;
description: string;
state: ParsedNodeProperty[];
config: ParsedNodeProperty[];
ports: {
flow: {
in: boolean;
out: string[];
};
data: {
in: ParsedNodePort[];
out: ParsedNodePort[];
};
};
}
| Field | Type | Description | optional | default value | example |
| ----------- | -------------------- | --------------------------------- | -------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| name | string | The name of the node | false | - | 'Addition' |
| description | string | The description of the node | true | '' | 'This node adds two numbers' |
| state | ParsedNodeProperty[] | The state properties of the node | true | [] | [{name: 'result', type: 'number', optional: false}]
|
| config | ParsedNodeProperty[] | The config properties of the node | true | [] | [{name: 'result', type: 'number', optional: false}]
|
| ports | ParsedNodePorts | The ports of the node | false | - | {flow: {in: false, out: []}, data: {in: [{name: 'a', type: 'number', optional: false}], out: [{name: 'result', type: 'number'}]}}
|
Examples
Addition
export const $$name = 'Addition';
export const $$description = 'This node adds two numbers';
export default (a: number, b: number): number => a + b;
Http Request
import axios from 'axios';
interface Config {
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
url: string;
headers?: Record<string, string>;
}
export const $$name = 'Http Request';
export const $$description = 'Make an HTTP request';
export const $$exec = true;
export const $$config: Config = {
method: 'GET',
url: 'https://example.com',
headers: {
'Content-Type': 'application/json',
},
};
export const $$state = {
data: null,
error: null,
};
export default async (url?: string, method?: string, [err, suc]: Array<Function>): {
data: any;
} => {
const config: Config = {
method: method || $$config.method,
url: url || $$config.url,
headers: $$config.headers,
};
try {
const response = await axios(config);
$$state.data = response.data;
suc(response.data);
return response.data;
} catch (error) {
$$state.error = error;
err(error);
return error;
}
};
Importent Notes
Node Name
The Node name is required and must be exported and defined with the $$name
variable. And it must be a string.
example
export const $$name = 'Addition';
Node Description
The Node description is optional but if you want to add a description to your node you have to export and define it with
the $$description
variable. And it must be a string. example:
export const $$description = 'This node adds two numbers';
Node State and Config
The Node state/config is optional but if you want to add a state/config to your node you have to export and define it
with the $$state
or/and $$config
variable(s).
Typing
You can leave the typing of the state/config to the parser. It will automatically detect the type and mark them all as required.
export const $$state = {
data: null,
error: null,
};
export const $$config = {
method: 'GET',
url: 'https://example.com',
};
Inline Typing
You can also define the type of the state/config using typescript inline typing
export const $$state: {
data: any;
error: any;
} = {
data: null,
error: null,
};
export const $$config: {
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
url: string;
} = {
method: 'GET',
url: 'https://example.com',
};
Interface Typing
You can also define the type of the state using typescript interfaces.
interface State {
data: any;
error: any;
}
interface Config {
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
url: string;
}
export const $$state: State = {
data: null,
error: null,
};
export const $$config: Config = {
method: 'GET',
url: 'https://example.com',
};
Output Ports
Function Output
If you want to add an output to your node you have to explicitly define the return type of the default function.
export default (a: number, b: number): number => a + b;
you can also define the output type using typescript interfaces.
interface Output {
data: any;
error: any;
}
export default (a: number, b: number): Output => {
return {
data: a + b,
error: null,
};
};
Or you can define the output type using typescript inline typing.
export default (a: number, b: number): {
data: any;
error: any;
} => {
return {
data: a + b,
error: null,
};
};
Callback functions
Callback functions are functions that are passed as parameters to the default function. The type of the callback functions should be:
Array<Function>
Array<UCallback>
Function[]
UCallback[]
The callback functions should be called in the default function as the last parameter.
export default (a: number, b: number, [err, suc]: Array<Function>) => {};
export default (a: number, b: number, [err, suc]: Array<UCallback>) => {};
export default (a: number, b: number, [err, suc]: Function[]) => {};
export default (a: number, b: number, [err, suc]: UCallback[]) => {};
Tests
To run the UCOD-Parser execute this command
make test
Benchmarks
To run the UCOD-Parser benchmarks execute this command
make bench
Results
| Benchmark | Time (avg) | iter/s | (min..max) | p75 | p99 | p995 | | ----------- | -------------- | -------- | ---------------------- | -------- | --------- | --------- | | Addition V1 | 105.44 ms/iter | 9.5 | (98.09 ms … 117.86 ms) | 109 ms | 117.86 ms | 117.86 ms | | Addition V2 | 101.83 µs/iter | 9,820.7 | (85.96 µs … 2.59 ms) | 98.32 µs | 190.21 µs | 208.66 µs | | Addition V5 | 97.24 µs/iter | 10,284.2 | (88.09 µs … 730.42 µs) | 97.43 µs | 161.98 µs | 177.62 µs |
summary Addition V5 1.05x faster than Addition V2 1084.36x faster than Addition V1
| Benchmark | Time (avg) | iter/s | (min..max) | p75 | p99 | p995 | | ---------- | -------------- | ------- | ----------------------- | --------- | --------- | --------- | | Request V1 | 108.84 ms/iter | 9.2 | (101.15 ms … 126.58 ms) | 113.82 ms | 126.58 ms | 126.58 ms | | Request V2 | 163.29 µs/iter | 6,124.0 | (148.55 µs … 2.27 ms) | 162.29 µs | 239.24 µs | 273.15 µs | | Request V5 | 125.18 µs/iter | 7,988.8 | (115.45 µs … 714.9 µs) | 123.63 µs | 195.98 µs | 223.82 µs |
summary Request V5 1.3x faster than Request V2 869.52x faster than Request V1