remove semi-colons
This commit is contained in:
16
package.json
16
package.json
@ -27,18 +27,31 @@
|
|||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject",
|
||||||
|
"fmt": "prettier --write ./src/**.{ts,tsx} && prettier --write ./src/**/*.{ts,tsx}"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"transformIgnorePatterns": [
|
"transformIgnorePatterns": [
|
||||||
"node_modules/(?!(three|allotment)/)"
|
"node_modules/(?!(three|allotment)/)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"prettier": {
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
"react-app",
|
"react-app",
|
||||||
"react-app/jest"
|
"react-app/jest"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
]
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
@ -56,6 +69,7 @@
|
|||||||
"@types/three": "^0.146.0",
|
"@types/three": "^0.146.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"postcss": "^8.4.19",
|
"postcss": "^8.4.19",
|
||||||
|
"prettier": "^2.8.0",
|
||||||
"tailwindcss": "^3.2.4"
|
"tailwindcss": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import React from "react";
|
import React from 'react'
|
||||||
import { render, screen } from "@testing-library/react";
|
import { render, screen } from '@testing-library/react'
|
||||||
import App from "./App";
|
import App from './App'
|
||||||
|
|
||||||
let listener: ((rect: any) => void) | undefined = undefined;
|
let listener: ((rect: any) => void) | undefined = undefined
|
||||||
(global as any).ResizeObserver = class ResizeObserver {
|
;(global as any).ResizeObserver = class ResizeObserver {
|
||||||
constructor(ls: ((rect: any) => void) | undefined) {
|
constructor(ls: ((rect: any) => void) | undefined) {
|
||||||
listener = ls;
|
listener = ls
|
||||||
}
|
}
|
||||||
observe() {}
|
observe() {}
|
||||||
unobserve() {}
|
unobserve() {}
|
||||||
disconnect() {}
|
disconnect() {}
|
||||||
};
|
}
|
||||||
|
|
||||||
test("renders learn react link", () => {
|
test('renders learn react link', () => {
|
||||||
render(<App />);
|
render(<App />)
|
||||||
const linkElement = screen.getByText(/viewer/i);
|
const linkElement = screen.getByText(/viewer/i)
|
||||||
expect(linkElement).toBeInTheDocument();
|
expect(linkElement).toBeInTheDocument()
|
||||||
});
|
})
|
||||||
|
111
src/App.tsx
111
src/App.tsx
@ -1,20 +1,20 @@
|
|||||||
import { useRef, useState, useEffect } from "react";
|
import { useRef, useState, useEffect } from 'react'
|
||||||
import { Canvas } from "@react-three/fiber";
|
import { Canvas } from '@react-three/fiber'
|
||||||
import { Allotment } from "allotment";
|
import { Allotment } from 'allotment'
|
||||||
import { OrbitControls, OrthographicCamera } from "@react-three/drei";
|
import { OrbitControls, OrthographicCamera } from '@react-three/drei'
|
||||||
import { lexer } from "./lang/tokeniser";
|
import { lexer } from './lang/tokeniser'
|
||||||
import { abstractSyntaxTree } from "./lang/abstractSyntaxTree";
|
import { abstractSyntaxTree } from './lang/abstractSyntaxTree'
|
||||||
import { executor } from "./lang/executor";
|
import { executor } from './lang/executor'
|
||||||
import { BufferGeometry } from "three";
|
import { BufferGeometry } from 'three'
|
||||||
import CodeMirror from "@uiw/react-codemirror";
|
import CodeMirror from '@uiw/react-codemirror'
|
||||||
import { javascript } from "@codemirror/lang-javascript";
|
import { javascript } from '@codemirror/lang-javascript'
|
||||||
import { ViewUpdate } from "@codemirror/view";
|
import { ViewUpdate } from '@codemirror/view'
|
||||||
import {
|
import {
|
||||||
lineHighlightField,
|
lineHighlightField,
|
||||||
addLineHighlight,
|
addLineHighlight,
|
||||||
} from "./editor/highlightextension";
|
} from './editor/highlightextension'
|
||||||
import { useStore } from "./useStore";
|
import { useStore } from './useStore'
|
||||||
import { isOverlapping } from "./lib/utils";
|
import { isOverlapping } from './lib/utils'
|
||||||
|
|
||||||
const _code = `sketch mySketch {
|
const _code = `sketch mySketch {
|
||||||
path myPath = lineTo(0,1)
|
path myPath = lineTo(0,1)
|
||||||
@ -22,13 +22,13 @@ const _code = `sketch mySketch {
|
|||||||
path rightPath = lineTo(1,0)
|
path rightPath = lineTo(1,0)
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
show(mySketch)`;
|
show(mySketch)`
|
||||||
|
|
||||||
const OrrthographicCamera = OrthographicCamera as any;
|
const OrrthographicCamera = OrthographicCamera as any
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const cam = useRef();
|
const cam = useRef()
|
||||||
const [code, setCode] = useState(_code);
|
const [code, setCode] = useState(_code)
|
||||||
const { editorView, setEditorView, setSelectionRange, selectionRange } =
|
const { editorView, setEditorView, setSelectionRange, selectionRange } =
|
||||||
useStore(
|
useStore(
|
||||||
({ editorView, setEditorView, setSelectionRange, selectionRange }) => ({
|
({ editorView, setEditorView, setSelectionRange, selectionRange }) => ({
|
||||||
@ -37,31 +37,32 @@ function App() {
|
|||||||
setSelectionRange,
|
setSelectionRange,
|
||||||
selectionRange,
|
selectionRange,
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
||||||
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
const onChange = (value: string, viewUpdate: ViewUpdate) => {
|
||||||
setCode(value);
|
setCode(value)
|
||||||
if (editorView) {
|
if (editorView) {
|
||||||
editorView?.dispatch({ effects: addLineHighlight.of([0, 0]) });
|
editorView?.dispatch({ effects: addLineHighlight.of([0, 0]) })
|
||||||
}
|
}
|
||||||
}; //, []);
|
} //, []);
|
||||||
const onUpdate = (viewUpdate: ViewUpdate) => {
|
const onUpdate = (viewUpdate: ViewUpdate) => {
|
||||||
if (!editorView) {
|
if (!editorView) {
|
||||||
setEditorView(viewUpdate.view);
|
setEditorView(viewUpdate.view)
|
||||||
}
|
}
|
||||||
const range = viewUpdate.state.selection.ranges[0];
|
const range = viewUpdate.state.selection.ranges[0]
|
||||||
const isNoChange = range.from === selectionRange[0] && range.to === selectionRange[1]
|
const isNoChange =
|
||||||
|
range.from === selectionRange[0] && range.to === selectionRange[1]
|
||||||
if (isNoChange) return
|
if (isNoChange) return
|
||||||
setSelectionRange([range.from, range.to]);
|
setSelectionRange([range.from, range.to])
|
||||||
};
|
}
|
||||||
const [geoArray, setGeoArray] = useState<
|
const [geoArray, setGeoArray] = useState<
|
||||||
{ geo: BufferGeometry; sourceRange: [number, number] }[]
|
{ geo: BufferGeometry; sourceRange: [number, number] }[]
|
||||||
>([]);
|
>([])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const tokens = lexer(code);
|
const tokens = lexer(code)
|
||||||
const ast = abstractSyntaxTree(tokens);
|
const ast = abstractSyntaxTree(tokens)
|
||||||
const programMemory = executor(ast);
|
const programMemory = executor(ast)
|
||||||
const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] =
|
const geos: { geo: BufferGeometry; sourceRange: [number, number] }[] =
|
||||||
programMemory.root.mySketch
|
programMemory.root.mySketch
|
||||||
.map(
|
.map(
|
||||||
@ -69,17 +70,17 @@ function App() {
|
|||||||
geo,
|
geo,
|
||||||
sourceRange,
|
sourceRange,
|
||||||
}: {
|
}: {
|
||||||
geo: BufferGeometry;
|
geo: BufferGeometry
|
||||||
sourceRange: [number, number];
|
sourceRange: [number, number]
|
||||||
}) => ({ geo, sourceRange })
|
}) => ({ geo, sourceRange })
|
||||||
)
|
)
|
||||||
.filter((a: any) => !!a.geo);
|
.filter((a: any) => !!a.geo)
|
||||||
setGeoArray(geos);
|
setGeoArray(geos)
|
||||||
console.log(programMemory);
|
console.log(programMemory)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e)
|
||||||
}
|
}
|
||||||
}, [code]);
|
}, [code])
|
||||||
return (
|
return (
|
||||||
<div className="h-screen">
|
<div className="h-screen">
|
||||||
<Allotment>
|
<Allotment>
|
||||||
@ -129,49 +130,49 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</Allotment>
|
</Allotment>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App
|
||||||
|
|
||||||
function Line({
|
function Line({
|
||||||
geo,
|
geo,
|
||||||
sourceRange,
|
sourceRange,
|
||||||
}: {
|
}: {
|
||||||
geo: BufferGeometry;
|
geo: BufferGeometry
|
||||||
sourceRange: [number, number];
|
sourceRange: [number, number]
|
||||||
}) {
|
}) {
|
||||||
const { setHighlightRange, selectionRange } = useStore(
|
const { setHighlightRange, selectionRange } = useStore(
|
||||||
({ setHighlightRange, selectionRange }) => ({
|
({ setHighlightRange, selectionRange }) => ({
|
||||||
setHighlightRange,
|
setHighlightRange,
|
||||||
selectionRange,
|
selectionRange,
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
// This reference will give us direct access to the mesh
|
// This reference will give us direct access to the mesh
|
||||||
const ref = useRef<BufferGeometry | undefined>() as any;
|
const ref = useRef<BufferGeometry | undefined>() as any
|
||||||
const [hovered, setHover] = useState(false);
|
const [hovered, setHover] = useState(false)
|
||||||
const [editorCursor, setEditorCursor] = useState(false);
|
const [editorCursor, setEditorCursor] = useState(false)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const shouldHighlight = isOverlapping(sourceRange, selectionRange);
|
const shouldHighlight = isOverlapping(sourceRange, selectionRange)
|
||||||
setEditorCursor(shouldHighlight);
|
setEditorCursor(shouldHighlight)
|
||||||
}, [selectionRange, sourceRange]);
|
}, [selectionRange, sourceRange])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<mesh
|
<mesh
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onPointerOver={(event) => {
|
onPointerOver={(event) => {
|
||||||
setHover(true);
|
setHover(true)
|
||||||
setHighlightRange(sourceRange);
|
setHighlightRange(sourceRange)
|
||||||
}}
|
}}
|
||||||
onPointerOut={(event) => {
|
onPointerOut={(event) => {
|
||||||
setHover(false);
|
setHover(false)
|
||||||
setHighlightRange([0, 0]);
|
setHighlightRange([0, 0])
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<primitive object={geo} />
|
<primitive object={geo} />
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={hovered ? "hotpink" : editorCursor ? "skyblue" : "orange"}
|
color={hovered ? 'hotpink' : editorCursor ? 'skyblue' : 'orange'}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
import { StateField, StateEffect } from "@codemirror/state";
|
import { StateField, StateEffect } from '@codemirror/state'
|
||||||
import { EditorView, Decoration } from "@codemirror/view";
|
import { EditorView, Decoration } from '@codemirror/view'
|
||||||
|
|
||||||
export { EditorView }
|
export { EditorView }
|
||||||
|
|
||||||
export const addLineHighlight = StateEffect.define<[number, number]>();
|
export const addLineHighlight = StateEffect.define<[number, number]>()
|
||||||
|
|
||||||
export const lineHighlightField = StateField.define({
|
export const lineHighlightField = StateField.define({
|
||||||
create() {
|
create() {
|
||||||
return Decoration.none;
|
return Decoration.none
|
||||||
},
|
},
|
||||||
update(lines, tr) {
|
update(lines, tr) {
|
||||||
lines = lines.map(tr.changes);
|
lines = lines.map(tr.changes)
|
||||||
const deco = []
|
const deco = []
|
||||||
for (let e of tr.effects) {
|
for (let e of tr.effects) {
|
||||||
if (e.is(addLineHighlight)) {
|
if (e.is(addLineHighlight)) {
|
||||||
lines = Decoration.none;
|
lines = Decoration.none
|
||||||
const [from, to] = e.value
|
const [from, to] = e.value
|
||||||
if (!(from === to && from === 0)) {
|
if (!(from === to && from === 0)) {
|
||||||
lines = lines.update({ add: [matchDeco.range(from, to)] });
|
lines = lines.update({ add: [matchDeco.range(from, to)] })
|
||||||
deco.push(matchDeco.range(from, to))
|
deco.push(matchDeco.range(from, to))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lines;
|
return lines
|
||||||
},
|
},
|
||||||
provide: (f) => EditorView.decorations.from(f),
|
provide: (f) => EditorView.decorations.from(f),
|
||||||
});
|
})
|
||||||
|
|
||||||
const matchDeco = Decoration.mark({class: "bg-yellow-200"})
|
const matchDeco = Decoration.mark({ class: 'bg-yellow-200' })
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client'
|
||||||
import './index.css';
|
import './index.css'
|
||||||
import App from './App';
|
import App from './App'
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals'
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
|
||||||
document.getElementById('root') as HTMLElement
|
|
||||||
);
|
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
)
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||||
reportWebVitals();
|
reportWebVitals()
|
||||||
|
@ -1,244 +1,244 @@
|
|||||||
import { abstractSyntaxTree, findClosingBrace } from "./abstractSyntaxTree";
|
import { abstractSyntaxTree, findClosingBrace } from './abstractSyntaxTree'
|
||||||
import { lexer } from "./tokeniser";
|
import { lexer } from './tokeniser'
|
||||||
|
|
||||||
describe("findClosingBrace", () => {
|
describe('findClosingBrace', () => {
|
||||||
test("finds the closing brace", () => {
|
test('finds the closing brace', () => {
|
||||||
const basic = "( hey )";
|
const basic = '( hey )'
|
||||||
expect(findClosingBrace(lexer(basic), 0)).toBe(4);
|
expect(findClosingBrace(lexer(basic), 0)).toBe(4)
|
||||||
|
|
||||||
const handlesNonZeroIndex =
|
const handlesNonZeroIndex =
|
||||||
"(indexForBracketToRightOfThisIsTwo(shouldBeFour)AndNotThisSix)";
|
'(indexForBracketToRightOfThisIsTwo(shouldBeFour)AndNotThisSix)'
|
||||||
expect(findClosingBrace(lexer(handlesNonZeroIndex), 2)).toBe(4);
|
expect(findClosingBrace(lexer(handlesNonZeroIndex), 2)).toBe(4)
|
||||||
expect(findClosingBrace(lexer(handlesNonZeroIndex), 0)).toBe(6);
|
expect(findClosingBrace(lexer(handlesNonZeroIndex), 0)).toBe(6)
|
||||||
|
|
||||||
const handlesNested =
|
const handlesNested =
|
||||||
"{a{b{c(}d]}eathou athoeu tah u} thatOneToTheLeftIsLast }";
|
'{a{b{c(}d]}eathou athoeu tah u} thatOneToTheLeftIsLast }'
|
||||||
expect(findClosingBrace(lexer(handlesNested), 0)).toBe(18);
|
expect(findClosingBrace(lexer(handlesNested), 0)).toBe(18)
|
||||||
|
|
||||||
// throws when not started on a brace
|
// throws when not started on a brace
|
||||||
expect(() => findClosingBrace(lexer(handlesNested), 1)).toThrow();
|
expect(() => findClosingBrace(lexer(handlesNested), 1)).toThrow()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe("testing AST", () => {
|
describe('testing AST', () => {
|
||||||
test("test 5 + 6", () => {
|
test('test 5 + 6', () => {
|
||||||
const tokens = lexer("5 +6");
|
const tokens = lexer('5 +6')
|
||||||
const result = abstractSyntaxTree(tokens);
|
const result = abstractSyntaxTree(tokens)
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
type: "Program",
|
type: 'Program',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 4,
|
end: 4,
|
||||||
body: [
|
body: [
|
||||||
{
|
{
|
||||||
type: "ExpressionStatement",
|
type: 'ExpressionStatement',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 4,
|
end: 4,
|
||||||
expression: {
|
expression: {
|
||||||
type: "BinaryExpression",
|
type: 'BinaryExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 4,
|
end: 4,
|
||||||
left: {
|
left: {
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 1,
|
end: 1,
|
||||||
value: 5,
|
value: 5,
|
||||||
raw: "5",
|
raw: '5',
|
||||||
},
|
},
|
||||||
operator: "+",
|
operator: '+',
|
||||||
right: {
|
right: {
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 3,
|
start: 3,
|
||||||
end: 4,
|
end: 4,
|
||||||
value: 6,
|
value: 6,
|
||||||
raw: "6",
|
raw: '6',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
test("test const myVar = 5", () => {
|
test('test const myVar = 5', () => {
|
||||||
const tokens = lexer("const myVar = 5");
|
const tokens = lexer('const myVar = 5')
|
||||||
const { body } = abstractSyntaxTree(tokens);
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 15,
|
end: 15,
|
||||||
kind: "const",
|
kind: 'const',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 6,
|
start: 6,
|
||||||
end: 15,
|
end: 15,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 6,
|
start: 6,
|
||||||
end: 11,
|
end: 11,
|
||||||
name: "myVar",
|
name: 'myVar',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 14,
|
start: 14,
|
||||||
end: 15,
|
end: 15,
|
||||||
value: 5,
|
value: 5,
|
||||||
raw: "5",
|
raw: '5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
test("test multi-line", () => {
|
test('test multi-line', () => {
|
||||||
const code = `const myVar = 5
|
const code = `const myVar = 5
|
||||||
const newVar = myVar + 1
|
const newVar = myVar + 1
|
||||||
`;
|
`
|
||||||
const tokens = lexer(code);
|
const tokens = lexer(code)
|
||||||
const { body } = abstractSyntaxTree(tokens);
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 15,
|
end: 15,
|
||||||
kind: "const",
|
kind: 'const',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 6,
|
start: 6,
|
||||||
end: 15,
|
end: 15,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 6,
|
start: 6,
|
||||||
end: 11,
|
end: 11,
|
||||||
name: "myVar",
|
name: 'myVar',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 14,
|
start: 14,
|
||||||
end: 15,
|
end: 15,
|
||||||
value: 5,
|
value: 5,
|
||||||
raw: "5",
|
raw: '5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 16,
|
start: 16,
|
||||||
end: 40,
|
end: 40,
|
||||||
kind: "const",
|
kind: 'const',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 22,
|
start: 22,
|
||||||
end: 40,
|
end: 40,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 22,
|
start: 22,
|
||||||
end: 28,
|
end: 28,
|
||||||
name: "newVar",
|
name: 'newVar',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "BinaryExpression",
|
type: 'BinaryExpression',
|
||||||
start: 31,
|
start: 31,
|
||||||
end: 40,
|
end: 40,
|
||||||
left: {
|
left: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 31,
|
start: 31,
|
||||||
end: 36,
|
end: 36,
|
||||||
name: "myVar",
|
name: 'myVar',
|
||||||
},
|
},
|
||||||
operator: "+",
|
operator: '+',
|
||||||
right: {
|
right: {
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 39,
|
start: 39,
|
||||||
end: 40,
|
end: 40,
|
||||||
value: 1,
|
value: 1,
|
||||||
raw: "1",
|
raw: '1',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
test('test using std function "log"', () => {
|
test('test using std function "log"', () => {
|
||||||
const code = `log(5, "hello", aIdentifier)`;
|
const code = `log(5, "hello", aIdentifier)`
|
||||||
const tokens = lexer(code);
|
const tokens = lexer(code)
|
||||||
const { body } = abstractSyntaxTree(tokens);
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: "ExpressionStatement",
|
type: 'ExpressionStatement',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 28,
|
end: 28,
|
||||||
expression: {
|
expression: {
|
||||||
type: "CallExpression",
|
type: 'CallExpression',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 28,
|
end: 28,
|
||||||
callee: {
|
callee: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 3,
|
end: 3,
|
||||||
name: "log",
|
name: 'log',
|
||||||
},
|
},
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 4,
|
start: 4,
|
||||||
end: 5,
|
end: 5,
|
||||||
value: 5,
|
value: 5,
|
||||||
raw: "5",
|
raw: '5',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 7,
|
start: 7,
|
||||||
end: 14,
|
end: 14,
|
||||||
value: "hello",
|
value: 'hello',
|
||||||
raw: '"hello"',
|
raw: '"hello"',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 16,
|
start: 16,
|
||||||
end: 27,
|
end: 27,
|
||||||
name: "aIdentifier",
|
name: 'aIdentifier',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
optional: false,
|
optional: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe("testing function declaration", () => {
|
describe('testing function declaration', () => {
|
||||||
test("fn funcN = () => {}", () => {
|
test('fn funcN = () => {}', () => {
|
||||||
const tokens = lexer("fn funcN = () => {}");
|
const tokens = lexer('fn funcN = () => {}')
|
||||||
const { body } = abstractSyntaxTree(tokens);
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 19,
|
end: 19,
|
||||||
kind: "fn",
|
kind: 'fn',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 3,
|
start: 3,
|
||||||
end: 19,
|
end: 19,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 3,
|
start: 3,
|
||||||
end: 8,
|
end: 8,
|
||||||
name: "funcN",
|
name: 'funcN',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "FunctionExpression",
|
type: 'FunctionExpression',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 19,
|
end: 19,
|
||||||
id: null,
|
id: null,
|
||||||
params: [],
|
params: [],
|
||||||
body: {
|
body: {
|
||||||
type: "BlockStatement",
|
type: 'BlockStatement',
|
||||||
start: 17,
|
start: 17,
|
||||||
end: 19,
|
end: 19,
|
||||||
body: [],
|
body: [],
|
||||||
@ -247,74 +247,74 @@ describe("testing function declaration", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
test("fn funcN = (a, b) => {return a + b}", () => {
|
test('fn funcN = (a, b) => {return a + b}', () => {
|
||||||
const tokens = lexer(
|
const tokens = lexer(
|
||||||
["fn funcN = (a, b) => {", " return a + b", "}"].join("\n")
|
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
|
||||||
);
|
)
|
||||||
const { body } = abstractSyntaxTree(tokens);
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 39,
|
end: 39,
|
||||||
kind: "fn",
|
kind: 'fn',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 3,
|
start: 3,
|
||||||
end: 39,
|
end: 39,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 3,
|
start: 3,
|
||||||
end: 8,
|
end: 8,
|
||||||
name: "funcN",
|
name: 'funcN',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "FunctionExpression",
|
type: 'FunctionExpression',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 39,
|
end: 39,
|
||||||
id: null,
|
id: null,
|
||||||
params: [
|
params: [
|
||||||
{
|
{
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 12,
|
start: 12,
|
||||||
end: 13,
|
end: 13,
|
||||||
name: "a",
|
name: 'a',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 16,
|
end: 16,
|
||||||
name: "b",
|
name: 'b',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
body: {
|
body: {
|
||||||
type: "BlockStatement",
|
type: 'BlockStatement',
|
||||||
start: 21,
|
start: 21,
|
||||||
end: 39,
|
end: 39,
|
||||||
body: [
|
body: [
|
||||||
{
|
{
|
||||||
type: "ReturnStatement",
|
type: 'ReturnStatement',
|
||||||
start: 25,
|
start: 25,
|
||||||
end: 37,
|
end: 37,
|
||||||
argument: {
|
argument: {
|
||||||
type: "BinaryExpression",
|
type: 'BinaryExpression',
|
||||||
start: 32,
|
start: 32,
|
||||||
end: 37,
|
end: 37,
|
||||||
left: {
|
left: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 32,
|
start: 32,
|
||||||
end: 33,
|
end: 33,
|
||||||
name: "a",
|
name: 'a',
|
||||||
},
|
},
|
||||||
operator: "+",
|
operator: '+',
|
||||||
right: {
|
right: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 36,
|
start: 36,
|
||||||
end: 37,
|
end: 37,
|
||||||
name: "b",
|
name: 'b',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -324,75 +324,75 @@ describe("testing function declaration", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
test("call expression assignment", () => {
|
test('call expression assignment', () => {
|
||||||
const tokens = lexer(
|
const tokens = lexer(
|
||||||
`fn funcN = (a, b) => { return a + b }
|
`fn funcN = (a, b) => { return a + b }
|
||||||
const myVar = funcN(1, 2)`
|
const myVar = funcN(1, 2)`
|
||||||
);
|
)
|
||||||
const { body } = abstractSyntaxTree(tokens);
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 37,
|
end: 37,
|
||||||
kind: "fn",
|
kind: 'fn',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 3,
|
start: 3,
|
||||||
end: 37,
|
end: 37,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 3,
|
start: 3,
|
||||||
end: 8,
|
end: 8,
|
||||||
name: "funcN",
|
name: 'funcN',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "FunctionExpression",
|
type: 'FunctionExpression',
|
||||||
start: 11,
|
start: 11,
|
||||||
end: 37,
|
end: 37,
|
||||||
id: null,
|
id: null,
|
||||||
params: [
|
params: [
|
||||||
{
|
{
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 12,
|
start: 12,
|
||||||
end: 13,
|
end: 13,
|
||||||
name: "a",
|
name: 'a',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 15,
|
start: 15,
|
||||||
end: 16,
|
end: 16,
|
||||||
name: "b",
|
name: 'b',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
body: {
|
body: {
|
||||||
type: "BlockStatement",
|
type: 'BlockStatement',
|
||||||
start: 21,
|
start: 21,
|
||||||
end: 37,
|
end: 37,
|
||||||
body: [
|
body: [
|
||||||
{
|
{
|
||||||
type: "ReturnStatement",
|
type: 'ReturnStatement',
|
||||||
start: 23,
|
start: 23,
|
||||||
end: 35,
|
end: 35,
|
||||||
argument: {
|
argument: {
|
||||||
type: "BinaryExpression",
|
type: 'BinaryExpression',
|
||||||
start: 30,
|
start: 30,
|
||||||
end: 35,
|
end: 35,
|
||||||
left: {
|
left: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 30,
|
start: 30,
|
||||||
end: 31,
|
end: 31,
|
||||||
name: "a",
|
name: 'a',
|
||||||
},
|
},
|
||||||
operator: "+",
|
operator: '+',
|
||||||
right: {
|
right: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 34,
|
start: 34,
|
||||||
end: 35,
|
end: 35,
|
||||||
name: "b",
|
name: 'b',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -403,45 +403,45 @@ const myVar = funcN(1, 2)`
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 38,
|
start: 38,
|
||||||
end: 63,
|
end: 63,
|
||||||
kind: "const",
|
kind: 'const',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 44,
|
start: 44,
|
||||||
end: 63,
|
end: 63,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 44,
|
start: 44,
|
||||||
end: 49,
|
end: 49,
|
||||||
name: "myVar",
|
name: 'myVar',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "CallExpression",
|
type: 'CallExpression',
|
||||||
start: 52,
|
start: 52,
|
||||||
end: 63,
|
end: 63,
|
||||||
callee: {
|
callee: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 52,
|
start: 52,
|
||||||
end: 57,
|
end: 57,
|
||||||
name: "funcN",
|
name: 'funcN',
|
||||||
},
|
},
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 58,
|
start: 58,
|
||||||
end: 59,
|
end: 59,
|
||||||
value: 1,
|
value: 1,
|
||||||
raw: "1",
|
raw: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 61,
|
start: 61,
|
||||||
end: 62,
|
end: 62,
|
||||||
value: 2,
|
value: 2,
|
||||||
raw: "2",
|
raw: '2',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -449,87 +449,87 @@ const myVar = funcN(1, 2)`
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe("structures specific to this lang", () => {
|
describe('structures specific to this lang', () => {
|
||||||
test("sketch", () => {
|
test('sketch', () => {
|
||||||
let code = `sketch mySketch {
|
let code = `sketch mySketch {
|
||||||
path myPath = lineTo(0,1)
|
path myPath = lineTo(0,1)
|
||||||
lineTo(1,1)
|
lineTo(1,1)
|
||||||
path rightPath = lineTo(1,0)
|
path rightPath = lineTo(1,0)
|
||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
`;
|
`
|
||||||
const tokens = lexer(code);
|
const tokens = lexer(code)
|
||||||
const { body } = abstractSyntaxTree(tokens);
|
const { body } = abstractSyntaxTree(tokens)
|
||||||
expect(body).toEqual([
|
expect(body).toEqual([
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 102,
|
end: 102,
|
||||||
kind: "sketch",
|
kind: 'sketch',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 7,
|
start: 7,
|
||||||
end: 102,
|
end: 102,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 7,
|
start: 7,
|
||||||
end: 15,
|
end: 15,
|
||||||
name: "mySketch",
|
name: 'mySketch',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "SketchExpression",
|
type: 'SketchExpression',
|
||||||
start: 16,
|
start: 16,
|
||||||
end: 102,
|
end: 102,
|
||||||
body: {
|
body: {
|
||||||
type: "BlockStatement",
|
type: 'BlockStatement',
|
||||||
start: 16,
|
start: 16,
|
||||||
end: 102,
|
end: 102,
|
||||||
body: [
|
body: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 20,
|
start: 20,
|
||||||
end: 45,
|
end: 45,
|
||||||
kind: "path",
|
kind: 'path',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 25,
|
start: 25,
|
||||||
end: 45,
|
end: 45,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 25,
|
start: 25,
|
||||||
end: 31,
|
end: 31,
|
||||||
name: "myPath",
|
name: 'myPath',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "CallExpression",
|
type: 'CallExpression',
|
||||||
start: 34,
|
start: 34,
|
||||||
end: 45,
|
end: 45,
|
||||||
callee: {
|
callee: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 34,
|
start: 34,
|
||||||
end: 40,
|
end: 40,
|
||||||
name: "lineTo",
|
name: 'lineTo',
|
||||||
},
|
},
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 41,
|
start: 41,
|
||||||
end: 42,
|
end: 42,
|
||||||
value: 0,
|
value: 0,
|
||||||
raw: "0",
|
raw: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 43,
|
start: 43,
|
||||||
end: 44,
|
end: 44,
|
||||||
value: 1,
|
value: 1,
|
||||||
raw: "1",
|
raw: '1',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -538,78 +538,78 @@ describe("structures specific to this lang", () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "ExpressionStatement",
|
type: 'ExpressionStatement',
|
||||||
start: 48,
|
start: 48,
|
||||||
end: 59,
|
end: 59,
|
||||||
expression: {
|
expression: {
|
||||||
type: "CallExpression",
|
type: 'CallExpression',
|
||||||
start: 48,
|
start: 48,
|
||||||
end: 59,
|
end: 59,
|
||||||
callee: {
|
callee: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 48,
|
start: 48,
|
||||||
end: 54,
|
end: 54,
|
||||||
name: "lineTo",
|
name: 'lineTo',
|
||||||
},
|
},
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 55,
|
start: 55,
|
||||||
end: 56,
|
end: 56,
|
||||||
value: 1,
|
value: 1,
|
||||||
raw: "1",
|
raw: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 57,
|
start: 57,
|
||||||
end: 58,
|
end: 58,
|
||||||
value: 1,
|
value: 1,
|
||||||
raw: "1",
|
raw: '1',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
optional: false,
|
optional: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "VariableDeclaration",
|
type: 'VariableDeclaration',
|
||||||
start: 62,
|
start: 62,
|
||||||
end: 90,
|
end: 90,
|
||||||
kind: "path",
|
kind: 'path',
|
||||||
declarations: [
|
declarations: [
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: 'VariableDeclarator',
|
||||||
start: 67,
|
start: 67,
|
||||||
end: 90,
|
end: 90,
|
||||||
id: {
|
id: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 67,
|
start: 67,
|
||||||
end: 76,
|
end: 76,
|
||||||
name: "rightPath",
|
name: 'rightPath',
|
||||||
},
|
},
|
||||||
init: {
|
init: {
|
||||||
type: "CallExpression",
|
type: 'CallExpression',
|
||||||
start: 79,
|
start: 79,
|
||||||
end: 90,
|
end: 90,
|
||||||
callee: {
|
callee: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 79,
|
start: 79,
|
||||||
end: 85,
|
end: 85,
|
||||||
name: "lineTo",
|
name: 'lineTo',
|
||||||
},
|
},
|
||||||
arguments: [
|
arguments: [
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 86,
|
start: 86,
|
||||||
end: 87,
|
end: 87,
|
||||||
value: 1,
|
value: 1,
|
||||||
raw: "1",
|
raw: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "Literal",
|
type: 'Literal',
|
||||||
start: 88,
|
start: 88,
|
||||||
end: 89,
|
end: 89,
|
||||||
value: 0,
|
value: 0,
|
||||||
raw: "0",
|
raw: '0',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -618,18 +618,18 @@ describe("structures specific to this lang", () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "ExpressionStatement",
|
type: 'ExpressionStatement',
|
||||||
start: 93,
|
start: 93,
|
||||||
end: 100,
|
end: 100,
|
||||||
expression: {
|
expression: {
|
||||||
type: "CallExpression",
|
type: 'CallExpression',
|
||||||
start: 93,
|
start: 93,
|
||||||
end: 100,
|
end: 100,
|
||||||
callee: {
|
callee: {
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 93,
|
start: 93,
|
||||||
end: 98,
|
end: 98,
|
||||||
name: "close",
|
name: 'close',
|
||||||
},
|
},
|
||||||
arguments: [],
|
arguments: [],
|
||||||
optional: false,
|
optional: false,
|
||||||
@ -641,6 +641,6 @@ describe("structures specific to this lang", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,40 +1,42 @@
|
|||||||
import { BoxGeometry, SphereGeometry, BufferGeometry } from 'three'
|
import { BoxGeometry, SphereGeometry, BufferGeometry } from 'three'
|
||||||
import {mergeBufferGeometries} from 'three/examples/jsm/utils/BufferGeometryUtils'
|
import { mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils'
|
||||||
|
|
||||||
export function lineGeo({
|
export function lineGeo({
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
}: {
|
}: {
|
||||||
from: [number, number, number];
|
from: [number, number, number]
|
||||||
to: [number, number, number];
|
to: [number, number, number]
|
||||||
}): BufferGeometry {
|
}): BufferGeometry {
|
||||||
const sq = (a: number): number => a * a;
|
const sq = (a: number): number => a * a
|
||||||
const center = [
|
const center = [
|
||||||
(from[0] + to[0]) / 2,
|
(from[0] + to[0]) / 2,
|
||||||
(from[1] + to[1]) / 2,
|
(from[1] + to[1]) / 2,
|
||||||
(from[2] + to[2]) / 2,
|
(from[2] + to[2]) / 2,
|
||||||
];
|
]
|
||||||
const Hypotenuse3d = Math.sqrt(
|
const Hypotenuse3d = Math.sqrt(
|
||||||
sq(from[0] - to[0]) + sq(from[1] - to[1]) + sq(from[2] - to[2])
|
sq(from[0] - to[0]) + sq(from[1] - to[1]) + sq(from[2] - to[2])
|
||||||
);
|
)
|
||||||
const ang1 = Math.atan2(from[2] - to[2], from[0] - to[0]);
|
const ang1 = Math.atan2(from[2] - to[2], from[0] - to[0])
|
||||||
const Hypotenuse2d = Math.sqrt(sq(from[0] - to[0]) + sq(from[2] - to[2]));
|
const Hypotenuse2d = Math.sqrt(sq(from[0] - to[0]) + sq(from[2] - to[2]))
|
||||||
const ang2 = Math.abs(Math.atan((to[1] - from[1])/ Hypotenuse2d))*Math.sign(to[1] - from[1])*(Math.sign(to[0] - from[0])||1);
|
const ang2 =
|
||||||
|
Math.abs(Math.atan((to[1] - from[1]) / Hypotenuse2d)) *
|
||||||
|
Math.sign(to[1] - from[1]) *
|
||||||
|
(Math.sign(to[0] - from[0]) || 1)
|
||||||
|
|
||||||
// create BoxGeometry with size [Hypotenuse3d, 0.1, 0.1] centered at center, with rotation of [0, ang1, ang2]
|
// create BoxGeometry with size [Hypotenuse3d, 0.1, 0.1] centered at center, with rotation of [0, ang1, ang2]
|
||||||
const lineBody = new BoxGeometry(Hypotenuse3d, 0.1, 0.1);
|
const lineBody = new BoxGeometry(Hypotenuse3d, 0.1, 0.1)
|
||||||
lineBody.rotateY(ang1)
|
lineBody.rotateY(ang1)
|
||||||
lineBody.rotateZ(ang2)
|
lineBody.rotateZ(ang2)
|
||||||
lineBody.translate(center[0], center[1], center[2]);
|
lineBody.translate(center[0], center[1], center[2])
|
||||||
|
|
||||||
// create line end balls with SphereGeometry at `to` and `from` with radius of 0.15
|
// create line end balls with SphereGeometry at `to` and `from` with radius of 0.15
|
||||||
const lineEnd1 = new SphereGeometry(0.15);
|
const lineEnd1 = new SphereGeometry(0.15)
|
||||||
lineEnd1.translate(to[0], to[1], to[2]);
|
lineEnd1.translate(to[0], to[1], to[2])
|
||||||
// const lineEnd2 = new SphereGeometry(0.15);
|
// const lineEnd2 = new SphereGeometry(0.15);
|
||||||
// lineEnd2.translate(from[0], from[1], from[2])
|
// lineEnd2.translate(from[0], from[1], from[2])
|
||||||
|
|
||||||
// group all three geometries
|
// group all three geometries
|
||||||
return mergeBufferGeometries([lineBody, lineEnd1]);
|
return mergeBufferGeometries([lineBody, lineEnd1])
|
||||||
// return mergeBufferGeometries([lineBody, lineEnd1, lineEnd2]);
|
// return mergeBufferGeometries([lineBody, lineEnd1, lineEnd2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,58 +1,58 @@
|
|||||||
import fs from "node:fs";
|
import fs from 'node:fs'
|
||||||
|
|
||||||
import { abstractSyntaxTree } from "./abstractSyntaxTree";
|
import { abstractSyntaxTree } from './abstractSyntaxTree'
|
||||||
import { lexer } from "./tokeniser";
|
import { lexer } from './tokeniser'
|
||||||
import { executor, ProgramMemory } from "./executor";
|
import { executor, ProgramMemory } from './executor'
|
||||||
|
|
||||||
describe("test", () => {
|
describe('test', () => {
|
||||||
it("test assigning two variables, the second summing with the first", () => {
|
it('test assigning two variables, the second summing with the first', () => {
|
||||||
const code = `const myVar = 5
|
const code = `const myVar = 5
|
||||||
const newVar = myVar + 1`;
|
const newVar = myVar + 1`
|
||||||
const { root } = exe(code);
|
const { root } = exe(code)
|
||||||
expect(root.myVar).toBe(5);
|
expect(root.myVar).toBe(5)
|
||||||
expect(root.newVar).toBe(6);
|
expect(root.newVar).toBe(6)
|
||||||
});
|
})
|
||||||
it("test assigning a var with a string", () => {
|
it('test assigning a var with a string', () => {
|
||||||
const code = `const myVar = "a str"`;
|
const code = `const myVar = "a str"`
|
||||||
const { root } = exe(code);
|
const { root } = exe(code)
|
||||||
expect(root.myVar).toBe("a str");
|
expect(root.myVar).toBe('a str')
|
||||||
});
|
})
|
||||||
it("test assigning a var by cont concatenating two strings string", () => {
|
it('test assigning a var by cont concatenating two strings string', () => {
|
||||||
const code = fs.readFileSync(
|
const code = fs.readFileSync(
|
||||||
"./src/lang/testExamples/variableDeclaration.cado",
|
'./src/lang/testExamples/variableDeclaration.cado',
|
||||||
"utf-8"
|
'utf-8'
|
||||||
);
|
)
|
||||||
const { root } = exe(code);
|
const { root } = exe(code)
|
||||||
expect(root.myVar).toBe("a str another str");
|
expect(root.myVar).toBe('a str another str')
|
||||||
});
|
})
|
||||||
it("test with function call", () => {
|
it('test with function call', () => {
|
||||||
const code = `
|
const code = `
|
||||||
const myVar = "hello"
|
const myVar = "hello"
|
||||||
log(5, myVar)`;
|
log(5, myVar)`
|
||||||
const programMemoryOverride = {
|
const programMemoryOverride = {
|
||||||
log: jest.fn(),
|
log: jest.fn(),
|
||||||
};
|
}
|
||||||
const { root } = executor(abstractSyntaxTree(lexer(code)), {
|
const { root } = executor(abstractSyntaxTree(lexer(code)), {
|
||||||
root: programMemoryOverride,
|
root: programMemoryOverride,
|
||||||
_sketch: [],
|
_sketch: [],
|
||||||
});
|
})
|
||||||
expect(root.myVar).toBe("hello");
|
expect(root.myVar).toBe('hello')
|
||||||
expect(programMemoryOverride.log).toHaveBeenCalledWith(5, "hello");
|
expect(programMemoryOverride.log).toHaveBeenCalledWith(5, 'hello')
|
||||||
});
|
})
|
||||||
it("fn funcN = () => {}", () => {
|
it('fn funcN = () => {}', () => {
|
||||||
const { root } = exe(
|
const { root } = exe(
|
||||||
[
|
[
|
||||||
"fn funcN = (a, b) => {",
|
'fn funcN = (a, b) => {',
|
||||||
" return a + b",
|
' return a + b',
|
||||||
"}",
|
'}',
|
||||||
"const theVar = 60",
|
'const theVar = 60',
|
||||||
"const magicNum = funcN(9, theVar)",
|
'const magicNum = funcN(9, theVar)',
|
||||||
].join("\n")
|
].join('\n')
|
||||||
);
|
)
|
||||||
expect(root.theVar).toBe(60);
|
expect(root.theVar).toBe(60)
|
||||||
expect(root.magicNum).toBe(69);
|
expect(root.magicNum).toBe(69)
|
||||||
});
|
})
|
||||||
it("sketch declaration", () => {
|
it('sketch declaration', () => {
|
||||||
let code = `sketch mySketch {
|
let code = `sketch mySketch {
|
||||||
path myPath = lineTo(0,1)
|
path myPath = lineTo(0,1)
|
||||||
lineTo(1,1)
|
lineTo(1,1)
|
||||||
@ -60,33 +60,33 @@ log(5, myVar)`;
|
|||||||
close()
|
close()
|
||||||
}
|
}
|
||||||
show(mySketch)
|
show(mySketch)
|
||||||
`;
|
`
|
||||||
const { root, return: _return } = exe(code);
|
const { root, return: _return } = exe(code)
|
||||||
expect(
|
expect(
|
||||||
root.mySketch.map(({ previousPath, geo, ...rest }: any) => rest)
|
root.mySketch.map(({ previousPath, geo, ...rest }: any) => rest)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{ type: "base", from: [0, 0] },
|
{ type: 'base', from: [0, 0] },
|
||||||
{ type: "toPoint", to: [0, 1], sourceRange: [25, 45], name: "myPath" },
|
{ type: 'toPoint', to: [0, 1], sourceRange: [25, 45], name: 'myPath' },
|
||||||
{ type: "toPoint", to: [1, 1], sourceRange: [48, 59] },
|
{ type: 'toPoint', to: [1, 1], sourceRange: [48, 59] },
|
||||||
{ type: "toPoint", to: [1, 0], sourceRange: [67, 90], name: "rightPath" },
|
{ type: 'toPoint', to: [1, 0], sourceRange: [67, 90], name: 'rightPath' },
|
||||||
{
|
{
|
||||||
type: "close",
|
type: 'close',
|
||||||
firstPath: { type: "base", from: [0, 0] },
|
firstPath: { type: 'base', from: [0, 0] },
|
||||||
sourceRange: [93, 100],
|
sourceRange: [93, 100],
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
expect(root.mySketch[0]).toEqual(root.mySketch[4].firstPath);
|
expect(root.mySketch[0]).toEqual(root.mySketch[4].firstPath)
|
||||||
// hmm not sure what handle the "show" function
|
// hmm not sure what handle the "show" function
|
||||||
expect(_return).toEqual([
|
expect(_return).toEqual([
|
||||||
{
|
{
|
||||||
type: "Identifier",
|
type: 'Identifier',
|
||||||
start: 108,
|
start: 108,
|
||||||
end: 116,
|
end: 116,
|
||||||
name: "mySketch",
|
name: 'mySketch',
|
||||||
},
|
},
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ function exe(
|
|||||||
code: string,
|
code: string,
|
||||||
programMemory: ProgramMemory = { root: {}, _sketch: [] }
|
programMemory: ProgramMemory = { root: {}, _sketch: [] }
|
||||||
) {
|
) {
|
||||||
const tokens = lexer(code);
|
const tokens = lexer(code)
|
||||||
const ast = abstractSyntaxTree(tokens);
|
const ast = abstractSyntaxTree(tokens)
|
||||||
return executor(ast, programMemory);
|
return executor(ast, programMemory)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { Program, BinaryPart, BinaryExpression } from "./abstractSyntaxTree";
|
import { Program, BinaryPart, BinaryExpression } from './abstractSyntaxTree'
|
||||||
import {Path, sketchFns } from "./sketch";
|
import { Path, sketchFns } from './sketch'
|
||||||
|
|
||||||
export interface ProgramMemory {
|
export interface ProgramMemory {
|
||||||
root: { [key: string]: any };
|
root: { [key: string]: any }
|
||||||
return?: any;
|
return?: any
|
||||||
_sketch: Path[];
|
_sketch: Path[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const executor = (
|
export const executor = (
|
||||||
node: Program,
|
node: Program,
|
||||||
programMemory: ProgramMemory = { root: {}, _sketch: [] },
|
programMemory: ProgramMemory = { root: {}, _sketch: [] },
|
||||||
options: { bodyType: "root" | "sketch" | "block" } = { bodyType: "root" }
|
options: { bodyType: 'root' | 'sketch' | 'block' } = { bodyType: 'root' }
|
||||||
): any => {
|
): any => {
|
||||||
const _programMemory: ProgramMemory = {
|
const _programMemory: ProgramMemory = {
|
||||||
root: {
|
root: {
|
||||||
@ -18,33 +18,33 @@ export const executor = (
|
|||||||
},
|
},
|
||||||
_sketch: [],
|
_sketch: [],
|
||||||
return: programMemory.return,
|
return: programMemory.return,
|
||||||
};
|
}
|
||||||
const { body } = node;
|
const { body } = node
|
||||||
body.forEach((statement) => {
|
body.forEach((statement) => {
|
||||||
if (statement.type === "VariableDeclaration") {
|
if (statement.type === 'VariableDeclaration') {
|
||||||
statement.declarations.forEach((declaration) => {
|
statement.declarations.forEach((declaration) => {
|
||||||
const variableName = declaration.id.name;
|
const variableName = declaration.id.name
|
||||||
if (declaration.init.type === "Literal") {
|
if (declaration.init.type === 'Literal') {
|
||||||
_programMemory.root[variableName] = declaration.init.value;
|
_programMemory.root[variableName] = declaration.init.value
|
||||||
} else if (declaration.init.type === "BinaryExpression") {
|
} else if (declaration.init.type === 'BinaryExpression') {
|
||||||
_programMemory.root[variableName] = getBinaryExpressionResult(
|
_programMemory.root[variableName] = getBinaryExpressionResult(
|
||||||
declaration.init,
|
declaration.init,
|
||||||
_programMemory
|
_programMemory
|
||||||
);
|
)
|
||||||
} else if (declaration.init.type === "SketchExpression") {
|
} else if (declaration.init.type === 'SketchExpression') {
|
||||||
const sketchInit = declaration.init;
|
const sketchInit = declaration.init
|
||||||
const fnMemory: ProgramMemory = {
|
const fnMemory: ProgramMemory = {
|
||||||
root: {
|
root: {
|
||||||
..._programMemory.root,
|
..._programMemory.root,
|
||||||
},
|
},
|
||||||
_sketch: [],
|
_sketch: [],
|
||||||
};
|
}
|
||||||
const { _sketch } = executor(sketchInit.body, fnMemory, {
|
const { _sketch } = executor(sketchInit.body, fnMemory, {
|
||||||
bodyType: "sketch",
|
bodyType: 'sketch',
|
||||||
});
|
})
|
||||||
_programMemory.root[variableName] = _sketch;
|
_programMemory.root[variableName] = _sketch
|
||||||
} else if (declaration.init.type === "FunctionExpression") {
|
} else if (declaration.init.type === 'FunctionExpression') {
|
||||||
const fnInit = declaration.init;
|
const fnInit = declaration.init
|
||||||
|
|
||||||
_programMemory.root[declaration.id.name] = (...args: any[]) => {
|
_programMemory.root[declaration.id.name] = (...args: any[]) => {
|
||||||
const fnMemory: ProgramMemory = {
|
const fnMemory: ProgramMemory = {
|
||||||
@ -52,106 +52,108 @@ export const executor = (
|
|||||||
..._programMemory.root,
|
..._programMemory.root,
|
||||||
},
|
},
|
||||||
_sketch: [],
|
_sketch: [],
|
||||||
};
|
}
|
||||||
if (args.length > fnInit.params.length) {
|
if (args.length > fnInit.params.length) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Too many arguments passed to function ${declaration.id.name}`
|
`Too many arguments passed to function ${declaration.id.name}`
|
||||||
);
|
)
|
||||||
} else if (args.length < fnInit.params.length) {
|
} else if (args.length < fnInit.params.length) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Too few arguments passed to function ${declaration.id.name}`
|
`Too few arguments passed to function ${declaration.id.name}`
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
fnInit.params.forEach((param, index) => {
|
fnInit.params.forEach((param, index) => {
|
||||||
fnMemory.root[param.name] = args[index];
|
fnMemory.root[param.name] = args[index]
|
||||||
});
|
})
|
||||||
return executor(fnInit.body, fnMemory, {bodyType: 'block'}).return;
|
return executor(fnInit.body, fnMemory, { bodyType: 'block' }).return
|
||||||
};
|
|
||||||
} else if (declaration.init.type === "CallExpression") {
|
|
||||||
const fnName = declaration.init.callee.name;
|
|
||||||
const fnArgs = declaration.init.arguments.map((arg) => {
|
|
||||||
if (arg.type === "Literal") {
|
|
||||||
return arg.value;
|
|
||||||
} else if (arg.type === "Identifier") {
|
|
||||||
return _programMemory.root[arg.name];
|
|
||||||
}
|
}
|
||||||
});
|
} else if (declaration.init.type === 'CallExpression') {
|
||||||
if ("lineTo" === fnName || "close" === fnName) {
|
const fnName = declaration.init.callee.name
|
||||||
if (options.bodyType !== "sketch") {
|
const fnArgs = declaration.init.arguments.map((arg) => {
|
||||||
|
if (arg.type === 'Literal') {
|
||||||
|
return arg.value
|
||||||
|
} else if (arg.type === 'Identifier') {
|
||||||
|
return _programMemory.root[arg.name]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if ('lineTo' === fnName || 'close' === fnName) {
|
||||||
|
if (options.bodyType !== 'sketch') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Cannot call ${fnName} outside of a sketch declaration`
|
`Cannot call ${fnName} outside of a sketch declaration`
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
const result = sketchFns[fnName](
|
const result = sketchFns[fnName](
|
||||||
_programMemory,
|
_programMemory,
|
||||||
variableName,
|
variableName,
|
||||||
[declaration.start, declaration.end],
|
[declaration.start, declaration.end],
|
||||||
...fnArgs
|
...fnArgs
|
||||||
);
|
)
|
||||||
_programMemory._sketch = result.programMemory._sketch;
|
_programMemory._sketch = result.programMemory._sketch
|
||||||
_programMemory.root[variableName] = result.currentPath;
|
_programMemory.root[variableName] = result.currentPath
|
||||||
} else {
|
} else {
|
||||||
_programMemory.root[variableName] = _programMemory.root[fnName](
|
_programMemory.root[variableName] = _programMemory.root[fnName](
|
||||||
...fnArgs
|
...fnArgs
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} else if (statement.type === "ExpressionStatement") {
|
} else if (statement.type === 'ExpressionStatement') {
|
||||||
const expression = statement.expression;
|
const expression = statement.expression
|
||||||
if (expression.type === "CallExpression") {
|
if (expression.type === 'CallExpression') {
|
||||||
const functionName = expression.callee.name;
|
const functionName = expression.callee.name
|
||||||
const args = expression.arguments.map((arg) => {
|
const args = expression.arguments.map((arg) => {
|
||||||
if (arg.type === "Literal") {
|
if (arg.type === 'Literal') {
|
||||||
return arg.value;
|
return arg.value
|
||||||
} else if (arg.type === "Identifier") {
|
} else if (arg.type === 'Identifier') {
|
||||||
return _programMemory.root[arg.name];
|
return _programMemory.root[arg.name]
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
if ("lineTo" === functionName || "close" === functionName) {
|
if ('lineTo' === functionName || 'close' === functionName) {
|
||||||
if (options.bodyType !== "sketch") {
|
if (options.bodyType !== 'sketch') {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Cannot call ${functionName} outside of a sketch declaration`
|
`Cannot call ${functionName} outside of a sketch declaration`
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
const result = sketchFns[functionName](_programMemory, "", [statement.start, statement.end], ...args);
|
const result = sketchFns[functionName](
|
||||||
_programMemory._sketch = [...result.programMemory._sketch];
|
_programMemory,
|
||||||
} else if("show" === functionName) {
|
'',
|
||||||
if (options.bodyType !== "root") {
|
[statement.start, statement.end],
|
||||||
throw new Error(
|
...args
|
||||||
`Cannot call ${functionName} outside of a root`
|
)
|
||||||
);
|
_programMemory._sketch = [...result.programMemory._sketch]
|
||||||
|
} else if ('show' === functionName) {
|
||||||
|
if (options.bodyType !== 'root') {
|
||||||
|
throw new Error(`Cannot call ${functionName} outside of a root`)
|
||||||
}
|
}
|
||||||
_programMemory.return = expression.arguments;
|
_programMemory.return = expression.arguments
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
_programMemory.root[functionName](...args);
|
_programMemory.root[functionName](...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (statement.type === "ReturnStatement") {
|
} else if (statement.type === 'ReturnStatement') {
|
||||||
if (statement.argument.type === "BinaryExpression") {
|
if (statement.argument.type === 'BinaryExpression') {
|
||||||
_programMemory.return = getBinaryExpressionResult(
|
_programMemory.return = getBinaryExpressionResult(
|
||||||
statement.argument,
|
statement.argument,
|
||||||
_programMemory
|
_programMemory
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return _programMemory;
|
return _programMemory
|
||||||
};
|
}
|
||||||
|
|
||||||
function getBinaryExpressionResult(
|
function getBinaryExpressionResult(
|
||||||
expression: BinaryExpression,
|
expression: BinaryExpression,
|
||||||
programMemory: ProgramMemory
|
programMemory: ProgramMemory
|
||||||
) {
|
) {
|
||||||
const getVal = (part: BinaryPart) => {
|
const getVal = (part: BinaryPart) => {
|
||||||
if (part.type === "Literal") {
|
if (part.type === 'Literal') {
|
||||||
return part.value;
|
return part.value
|
||||||
} else if (part.type === "Identifier") {
|
} else if (part.type === 'Identifier') {
|
||||||
return programMemory.root[part.name];
|
return programMemory.root[part.name]
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
const left = getVal(expression.left);
|
const left = getVal(expression.left)
|
||||||
const right = getVal(expression.right);
|
const right = getVal(expression.right)
|
||||||
return left + right;
|
return left + right
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ProgramMemory } from "./executor";
|
import { ProgramMemory } from './executor'
|
||||||
import { lineGeo } from "./engine";
|
import { lineGeo } from './engine'
|
||||||
import { BufferGeometry } from 'three'
|
import { BufferGeometry } from 'three'
|
||||||
|
|
||||||
type Coords2d = [number, number]
|
type Coords2d = [number, number]
|
||||||
@ -7,75 +7,75 @@ type SourceRange = [number, number]
|
|||||||
|
|
||||||
export type Path =
|
export type Path =
|
||||||
| {
|
| {
|
||||||
type: "points";
|
type: 'points'
|
||||||
name?: string;
|
name?: string
|
||||||
from: Coords2d;
|
from: Coords2d
|
||||||
to: Coords2d;
|
to: Coords2d
|
||||||
geo: BufferGeometry;
|
geo: BufferGeometry
|
||||||
sourceRange: SourceRange;
|
sourceRange: SourceRange
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "horizontalLineTo";
|
type: 'horizontalLineTo'
|
||||||
name?: string;
|
name?: string
|
||||||
x: number;
|
x: number
|
||||||
previousPath: Path;
|
previousPath: Path
|
||||||
geo: BufferGeometry;
|
geo: BufferGeometry
|
||||||
sourceRange: SourceRange;
|
sourceRange: SourceRange
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "verticalLineTo";
|
type: 'verticalLineTo'
|
||||||
name?: string;
|
name?: string
|
||||||
y: number;
|
y: number
|
||||||
previousPath: Path;
|
previousPath: Path
|
||||||
geo: BufferGeometry;
|
geo: BufferGeometry
|
||||||
sourceRange: SourceRange;
|
sourceRange: SourceRange
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "toPoint";
|
type: 'toPoint'
|
||||||
name?: string;
|
name?: string
|
||||||
to: Coords2d;
|
to: Coords2d
|
||||||
previousPath: Path;
|
previousPath: Path
|
||||||
geo: BufferGeometry;
|
geo: BufferGeometry
|
||||||
sourceRange: SourceRange;
|
sourceRange: SourceRange
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "close";
|
type: 'close'
|
||||||
name?: string;
|
name?: string
|
||||||
firstPath: Path;
|
firstPath: Path
|
||||||
previousPath: Path;
|
previousPath: Path
|
||||||
geo: BufferGeometry;
|
geo: BufferGeometry
|
||||||
sourceRange: SourceRange;
|
sourceRange: SourceRange
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "base";
|
type: 'base'
|
||||||
from: Coords2d;
|
from: Coords2d
|
||||||
};
|
}
|
||||||
|
|
||||||
function addBasePath(programMemory: ProgramMemory) {
|
function addBasePath(programMemory: ProgramMemory) {
|
||||||
const base: Path = {
|
const base: Path = {
|
||||||
type: "base",
|
type: 'base',
|
||||||
from: [0, 0],
|
from: [0, 0],
|
||||||
};
|
}
|
||||||
if (programMemory._sketch?.length === 0) {
|
if (programMemory._sketch?.length === 0) {
|
||||||
return {
|
return {
|
||||||
...programMemory,
|
...programMemory,
|
||||||
_sketch: [base],
|
_sketch: [base],
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return programMemory;
|
}
|
||||||
|
return programMemory
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PathReturn {
|
interface PathReturn {
|
||||||
programMemory: ProgramMemory;
|
programMemory: ProgramMemory
|
||||||
currentPath: Path;
|
currentPath: Path
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCoordsFromPaths(paths: Path[], index = 0): Coords2d {
|
function getCoordsFromPaths(paths: Path[], index = 0): Coords2d {
|
||||||
const currentPath = paths[index]
|
const currentPath = paths[index]
|
||||||
if(!currentPath) {
|
if (!currentPath) {
|
||||||
return [0, 0]
|
return [0, 0]
|
||||||
}
|
}
|
||||||
if(currentPath.type === 'points' || currentPath.type === 'toPoint') {
|
if (currentPath.type === 'points' || currentPath.type === 'toPoint') {
|
||||||
return currentPath.to
|
return currentPath.to
|
||||||
} else if (currentPath.type === 'base') {
|
} else if (currentPath.type === 'base') {
|
||||||
return currentPath.from
|
return currentPath.from
|
||||||
@ -86,33 +86,38 @@ function getCoordsFromPaths(paths: Path[], index = 0): Coords2d {
|
|||||||
const pathBefore = getCoordsFromPaths(paths, index - 1)
|
const pathBefore = getCoordsFromPaths(paths, index - 1)
|
||||||
return [pathBefore[0], currentPath.y]
|
return [pathBefore[0], currentPath.y]
|
||||||
}
|
}
|
||||||
return [0,0]
|
return [0, 0]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sketchFns = {
|
export const sketchFns = {
|
||||||
close: (programMemory: ProgramMemory, name: string = "", sourceRange: SourceRange): PathReturn => {
|
close: (
|
||||||
|
programMemory: ProgramMemory,
|
||||||
|
name: string = '',
|
||||||
|
sourceRange: SourceRange
|
||||||
|
): PathReturn => {
|
||||||
const lastPath = programMemory?._sketch?.[
|
const lastPath = programMemory?._sketch?.[
|
||||||
programMemory?._sketch.length - 1
|
programMemory?._sketch.length - 1
|
||||||
] as Path;
|
] as Path
|
||||||
|
|
||||||
let from = getCoordsFromPaths(programMemory?._sketch, programMemory?._sketch.length - 1)
|
let from = getCoordsFromPaths(
|
||||||
const firstPath = programMemory?._sketch?.[0] as Path;
|
programMemory?._sketch,
|
||||||
if (lastPath?.type === "base") {
|
programMemory?._sketch.length - 1
|
||||||
throw new Error("Cannot close a base path");
|
)
|
||||||
|
const firstPath = programMemory?._sketch?.[0] as Path
|
||||||
|
if (lastPath?.type === 'base') {
|
||||||
|
throw new Error('Cannot close a base path')
|
||||||
}
|
}
|
||||||
let to = getCoordsFromPaths(programMemory?._sketch, 0)
|
let to = getCoordsFromPaths(programMemory?._sketch, 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const newPath: Path = {
|
const newPath: Path = {
|
||||||
type: "close",
|
type: 'close',
|
||||||
firstPath,
|
firstPath,
|
||||||
previousPath: lastPath,
|
previousPath: lastPath,
|
||||||
geo: lineGeo({from: [...from, 0], to: [...to, 0]}),
|
geo: lineGeo({ from: [...from, 0], to: [...to, 0] }),
|
||||||
sourceRange
|
sourceRange,
|
||||||
};
|
}
|
||||||
if (name) {
|
if (name) {
|
||||||
newPath.name = name;
|
newPath.name = name
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
programMemory: {
|
programMemory: {
|
||||||
@ -120,31 +125,34 @@ export const sketchFns = {
|
|||||||
_sketch: [...(programMemory?._sketch || []), newPath],
|
_sketch: [...(programMemory?._sketch || []), newPath],
|
||||||
},
|
},
|
||||||
currentPath: newPath,
|
currentPath: newPath,
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
lineTo: (
|
lineTo: (
|
||||||
programMemory: ProgramMemory,
|
programMemory: ProgramMemory,
|
||||||
name: string = "",
|
name: string = '',
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
): PathReturn => {
|
): PathReturn => {
|
||||||
const _programMemory = addBasePath(programMemory);
|
const _programMemory = addBasePath(programMemory)
|
||||||
const [x, y] = args;
|
const [x, y] = args
|
||||||
if (!_programMemory._sketch) {
|
if (!_programMemory._sketch) {
|
||||||
throw new Error("No sketch to draw on");
|
throw new Error('No sketch to draw on')
|
||||||
}
|
}
|
||||||
const lastPath: Path =
|
const lastPath: Path =
|
||||||
_programMemory._sketch[_programMemory._sketch.length - 1];
|
_programMemory._sketch[_programMemory._sketch.length - 1]
|
||||||
let from = getCoordsFromPaths(programMemory?._sketch, programMemory?._sketch.length - 1)
|
let from = getCoordsFromPaths(
|
||||||
|
programMemory?._sketch,
|
||||||
|
programMemory?._sketch.length - 1
|
||||||
|
)
|
||||||
const currentPath: Path = {
|
const currentPath: Path = {
|
||||||
type: "toPoint",
|
type: 'toPoint',
|
||||||
to: [x, y],
|
to: [x, y],
|
||||||
previousPath: lastPath,
|
previousPath: lastPath,
|
||||||
geo: lineGeo({from: [...from, 0], to: [x, y, 0]}),
|
geo: lineGeo({ from: [...from, 0], to: [x, y, 0] }),
|
||||||
sourceRange
|
sourceRange,
|
||||||
};
|
}
|
||||||
if (name) {
|
if (name) {
|
||||||
currentPath.name = name;
|
currentPath.name = name
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
programMemory: {
|
programMemory: {
|
||||||
@ -152,6 +160,6 @@ export const sketchFns = {
|
|||||||
_sketch: [...(_programMemory._sketch || []), currentPath],
|
_sketch: [...(_programMemory._sketch || []), currentPath],
|
||||||
},
|
},
|
||||||
currentPath,
|
currentPath,
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
@ -10,148 +10,148 @@ import {
|
|||||||
isWord,
|
isWord,
|
||||||
isComma,
|
isComma,
|
||||||
lexer,
|
lexer,
|
||||||
} from "./tokeniser";
|
} from './tokeniser'
|
||||||
|
|
||||||
describe("testing helpers", () => {
|
describe('testing helpers', () => {
|
||||||
it("test is number", () => {
|
it('test is number', () => {
|
||||||
expect(isNumber("1")).toBe(true);
|
expect(isNumber('1')).toBe(true)
|
||||||
expect(isNumber("5?")).toBe(true);
|
expect(isNumber('5?')).toBe(true)
|
||||||
expect(isNumber("5 + 6")).toBe(true);
|
expect(isNumber('5 + 6')).toBe(true)
|
||||||
expect(isNumber("5 + a")).toBe(true);
|
expect(isNumber('5 + a')).toBe(true)
|
||||||
|
|
||||||
expect(isNumber("a")).toBe(false);
|
expect(isNumber('a')).toBe(false)
|
||||||
expect(isNumber("?")).toBe(false);
|
expect(isNumber('?')).toBe(false)
|
||||||
expect(isNumber("?5")).toBe(false);
|
expect(isNumber('?5')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is whitespace", () => {
|
it('test is whitespace', () => {
|
||||||
expect(isWhitespace(" ")).toBe(true);
|
expect(isWhitespace(' ')).toBe(true)
|
||||||
expect(isWhitespace(" ")).toBe(true);
|
expect(isWhitespace(' ')).toBe(true)
|
||||||
expect(isWhitespace(" a")).toBe(true);
|
expect(isWhitespace(' a')).toBe(true)
|
||||||
expect(isWhitespace("a ")).toBe(true);
|
expect(isWhitespace('a ')).toBe(true)
|
||||||
|
|
||||||
expect(isWhitespace("a")).toBe(false);
|
expect(isWhitespace('a')).toBe(false)
|
||||||
expect(isWhitespace("?")).toBe(false);
|
expect(isWhitespace('?')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is word", () => {
|
it('test is word', () => {
|
||||||
expect(isWord("a")).toBe(true);
|
expect(isWord('a')).toBe(true)
|
||||||
expect(isWord("a ")).toBe(true);
|
expect(isWord('a ')).toBe(true)
|
||||||
expect(isWord("a5")).toBe(true);
|
expect(isWord('a5')).toBe(true)
|
||||||
expect(isWord("a5a")).toBe(true);
|
expect(isWord('a5a')).toBe(true)
|
||||||
|
|
||||||
expect(isWord("5")).toBe(false);
|
expect(isWord('5')).toBe(false)
|
||||||
expect(isWord("5a")).toBe(false);
|
expect(isWord('5a')).toBe(false)
|
||||||
expect(isWord("5a5")).toBe(false);
|
expect(isWord('5a5')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is string", () => {
|
it('test is string', () => {
|
||||||
expect(isString('""')).toBe(true);
|
expect(isString('""')).toBe(true)
|
||||||
expect(isString('"a"')).toBe(true);
|
expect(isString('"a"')).toBe(true)
|
||||||
expect(isString('"a" ')).toBe(true);
|
expect(isString('"a" ')).toBe(true)
|
||||||
expect(isString('"a"5')).toBe(true);
|
expect(isString('"a"5')).toBe(true)
|
||||||
expect(isString("'a'5")).toBe(true);
|
expect(isString("'a'5")).toBe(true)
|
||||||
expect(isString('"with escaped \\" backslash"')).toBe(true);
|
expect(isString('"with escaped \\" backslash"')).toBe(true)
|
||||||
|
|
||||||
expect(isString('"')).toBe(false);
|
expect(isString('"')).toBe(false)
|
||||||
expect(isString('"a')).toBe(false);
|
expect(isString('"a')).toBe(false)
|
||||||
expect(isString('a"')).toBe(false);
|
expect(isString('a"')).toBe(false)
|
||||||
expect(isString(' "a"')).toBe(false);
|
expect(isString(' "a"')).toBe(false)
|
||||||
expect(isString('5"a"')).toBe(false);
|
expect(isString('5"a"')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is operator", () => {
|
it('test is operator', () => {
|
||||||
expect(isOperator("+")).toBe(true);
|
expect(isOperator('+')).toBe(true)
|
||||||
expect(isOperator("+ ")).toBe(true);
|
expect(isOperator('+ ')).toBe(true)
|
||||||
expect(isOperator("-")).toBe(true);
|
expect(isOperator('-')).toBe(true)
|
||||||
expect(isOperator("<=")).toBe(true);
|
expect(isOperator('<=')).toBe(true)
|
||||||
expect(isOperator("<= ")).toBe(true);
|
expect(isOperator('<= ')).toBe(true)
|
||||||
expect(isOperator(">=")).toBe(true);
|
expect(isOperator('>=')).toBe(true)
|
||||||
expect(isOperator(">= ")).toBe(true);
|
expect(isOperator('>= ')).toBe(true)
|
||||||
expect(isOperator("> ")).toBe(true);
|
expect(isOperator('> ')).toBe(true)
|
||||||
expect(isOperator("< ")).toBe(true);
|
expect(isOperator('< ')).toBe(true)
|
||||||
expect(isOperator("| ")).toBe(true);
|
expect(isOperator('| ')).toBe(true)
|
||||||
expect(isOperator("|> ")).toBe(true);
|
expect(isOperator('|> ')).toBe(true)
|
||||||
expect(isOperator("^ ")).toBe(true);
|
expect(isOperator('^ ')).toBe(true)
|
||||||
expect(isOperator("% ")).toBe(true);
|
expect(isOperator('% ')).toBe(true)
|
||||||
expect(isOperator("+* ")).toBe(true);
|
expect(isOperator('+* ')).toBe(true)
|
||||||
|
|
||||||
expect(isOperator("5 + 5")).toBe(false);
|
expect(isOperator('5 + 5')).toBe(false)
|
||||||
expect(isOperator("a")).toBe(false);
|
expect(isOperator('a')).toBe(false)
|
||||||
expect(isOperator("a+")).toBe(false);
|
expect(isOperator('a+')).toBe(false)
|
||||||
expect(isOperator("a+5")).toBe(false);
|
expect(isOperator('a+5')).toBe(false)
|
||||||
expect(isOperator("5a+5")).toBe(false);
|
expect(isOperator('5a+5')).toBe(false)
|
||||||
expect(isOperator(", newVar")).toBe(false);
|
expect(isOperator(', newVar')).toBe(false)
|
||||||
expect(isOperator(",")).toBe(false);
|
expect(isOperator(',')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is paran start", () => {
|
it('test is paran start', () => {
|
||||||
expect(isParanStart("(")).toBe(true);
|
expect(isParanStart('(')).toBe(true)
|
||||||
expect(isParanStart("( ")).toBe(true);
|
expect(isParanStart('( ')).toBe(true)
|
||||||
expect(isParanStart("(5")).toBe(true);
|
expect(isParanStart('(5')).toBe(true)
|
||||||
expect(isParanStart("(5 ")).toBe(true);
|
expect(isParanStart('(5 ')).toBe(true)
|
||||||
expect(isParanStart("(5 + 5")).toBe(true);
|
expect(isParanStart('(5 + 5')).toBe(true)
|
||||||
expect(isParanStart("(5 + 5)")).toBe(true);
|
expect(isParanStart('(5 + 5)')).toBe(true)
|
||||||
expect(isParanStart("(5 + 5) ")).toBe(true);
|
expect(isParanStart('(5 + 5) ')).toBe(true)
|
||||||
|
|
||||||
expect(isParanStart("5")).toBe(false);
|
expect(isParanStart('5')).toBe(false)
|
||||||
expect(isParanStart("5 + 5")).toBe(false);
|
expect(isParanStart('5 + 5')).toBe(false)
|
||||||
expect(isParanStart("5( + 5)")).toBe(false);
|
expect(isParanStart('5( + 5)')).toBe(false)
|
||||||
expect(isParanStart(" ( + 5)")).toBe(false);
|
expect(isParanStart(' ( + 5)')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is paran end", () => {
|
it('test is paran end', () => {
|
||||||
expect(isParanEnd(")")).toBe(true);
|
expect(isParanEnd(')')).toBe(true)
|
||||||
expect(isParanEnd(") ")).toBe(true);
|
expect(isParanEnd(') ')).toBe(true)
|
||||||
expect(isParanEnd(")5")).toBe(true);
|
expect(isParanEnd(')5')).toBe(true)
|
||||||
expect(isParanEnd(")5 ")).toBe(true);
|
expect(isParanEnd(')5 ')).toBe(true)
|
||||||
|
|
||||||
expect(isParanEnd("5")).toBe(false);
|
expect(isParanEnd('5')).toBe(false)
|
||||||
expect(isParanEnd("5 + 5")).toBe(false);
|
expect(isParanEnd('5 + 5')).toBe(false)
|
||||||
expect(isParanEnd("5) + 5")).toBe(false);
|
expect(isParanEnd('5) + 5')).toBe(false)
|
||||||
expect(isParanEnd(" ) + 5")).toBe(false);
|
expect(isParanEnd(' ) + 5')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is block start", () => {
|
it('test is block start', () => {
|
||||||
expect(isBlockStart("{")).toBe(true);
|
expect(isBlockStart('{')).toBe(true)
|
||||||
expect(isBlockStart("{ ")).toBe(true);
|
expect(isBlockStart('{ ')).toBe(true)
|
||||||
expect(isBlockStart("{5")).toBe(true);
|
expect(isBlockStart('{5')).toBe(true)
|
||||||
expect(isBlockStart("{a")).toBe(true);
|
expect(isBlockStart('{a')).toBe(true)
|
||||||
expect(isBlockStart("{5 ")).toBe(true);
|
expect(isBlockStart('{5 ')).toBe(true)
|
||||||
|
|
||||||
expect(isBlockStart("5")).toBe(false);
|
expect(isBlockStart('5')).toBe(false)
|
||||||
expect(isBlockStart("5 + 5")).toBe(false);
|
expect(isBlockStart('5 + 5')).toBe(false)
|
||||||
expect(isBlockStart("5{ + 5")).toBe(false);
|
expect(isBlockStart('5{ + 5')).toBe(false)
|
||||||
expect(isBlockStart("a{ + 5")).toBe(false);
|
expect(isBlockStart('a{ + 5')).toBe(false)
|
||||||
expect(isBlockStart(" { + 5")).toBe(false);
|
expect(isBlockStart(' { + 5')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is block end", () => {
|
it('test is block end', () => {
|
||||||
expect(isBlockEnd("}")).toBe(true);
|
expect(isBlockEnd('}')).toBe(true)
|
||||||
expect(isBlockEnd("} ")).toBe(true);
|
expect(isBlockEnd('} ')).toBe(true)
|
||||||
expect(isBlockEnd("}5")).toBe(true);
|
expect(isBlockEnd('}5')).toBe(true)
|
||||||
expect(isBlockEnd("}5 ")).toBe(true);
|
expect(isBlockEnd('}5 ')).toBe(true)
|
||||||
|
|
||||||
expect(isBlockEnd("5")).toBe(false);
|
expect(isBlockEnd('5')).toBe(false)
|
||||||
expect(isBlockEnd("5 + 5")).toBe(false);
|
expect(isBlockEnd('5 + 5')).toBe(false)
|
||||||
expect(isBlockEnd("5} + 5")).toBe(false);
|
expect(isBlockEnd('5} + 5')).toBe(false)
|
||||||
expect(isBlockEnd(" } + 5")).toBe(false);
|
expect(isBlockEnd(' } + 5')).toBe(false)
|
||||||
});
|
})
|
||||||
it("test is comma", () => {
|
it('test is comma', () => {
|
||||||
expect(isComma(",")).toBe(true);
|
expect(isComma(',')).toBe(true)
|
||||||
expect(isComma(", ")).toBe(true);
|
expect(isComma(', ')).toBe(true)
|
||||||
expect(isComma(",5")).toBe(true);
|
expect(isComma(',5')).toBe(true)
|
||||||
expect(isComma(",5 ")).toBe(true);
|
expect(isComma(',5 ')).toBe(true)
|
||||||
|
|
||||||
expect(isComma("5")).toBe(false);
|
expect(isComma('5')).toBe(false)
|
||||||
expect(isComma("5 + 5")).toBe(false);
|
expect(isComma('5 + 5')).toBe(false)
|
||||||
expect(isComma("5, + 5")).toBe(false);
|
expect(isComma('5, + 5')).toBe(false)
|
||||||
expect(isComma(" , + 5")).toBe(false);
|
expect(isComma(' , + 5')).toBe(false)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
describe("testing lexer", () => {
|
describe('testing lexer', () => {
|
||||||
it("test lexer", () => {
|
it('test lexer', () => {
|
||||||
expect(stringSummaryLexer("1 + 2")).toEqual([
|
expect(stringSummaryLexer('1 + 2')).toEqual([
|
||||||
"number '1' from 0 to 1",
|
"number '1' from 0 to 1",
|
||||||
"whitespace ' ' from 1 to 3",
|
"whitespace ' ' from 1 to 3",
|
||||||
"operator '+' from 3 to 4",
|
"operator '+' from 3 to 4",
|
||||||
"whitespace ' ' from 4 to 5",
|
"whitespace ' ' from 4 to 5",
|
||||||
"number '2' from 5 to 6",
|
"number '2' from 5 to 6",
|
||||||
]);
|
])
|
||||||
expect(stringSummaryLexer("54 + 22500 + 6")).toEqual([
|
expect(stringSummaryLexer('54 + 22500 + 6')).toEqual([
|
||||||
"number '54' from 0 to 2",
|
"number '54' from 0 to 2",
|
||||||
"whitespace ' ' from 2 to 3",
|
"whitespace ' ' from 2 to 3",
|
||||||
"operator '+' from 3 to 4",
|
"operator '+' from 3 to 4",
|
||||||
@ -161,8 +161,8 @@ describe("testing lexer", () => {
|
|||||||
"operator '+' from 11 to 12",
|
"operator '+' from 11 to 12",
|
||||||
"whitespace ' ' from 12 to 13",
|
"whitespace ' ' from 12 to 13",
|
||||||
"number '6' from 13 to 14",
|
"number '6' from 13 to 14",
|
||||||
]);
|
])
|
||||||
expect(stringSummaryLexer("a + bo + t5 - 6")).toEqual([
|
expect(stringSummaryLexer('a + bo + t5 - 6')).toEqual([
|
||||||
"word 'a' from 0 to 1",
|
"word 'a' from 0 to 1",
|
||||||
"whitespace ' ' from 1 to 2",
|
"whitespace ' ' from 1 to 2",
|
||||||
"operator '+' from 2 to 3",
|
"operator '+' from 2 to 3",
|
||||||
@ -176,33 +176,33 @@ describe("testing lexer", () => {
|
|||||||
"operator '-' from 12 to 13",
|
"operator '-' from 12 to 13",
|
||||||
"whitespace ' ' from 13 to 14",
|
"whitespace ' ' from 13 to 14",
|
||||||
"number '6' from 14 to 15",
|
"number '6' from 14 to 15",
|
||||||
]);
|
])
|
||||||
expect(stringSummaryLexer('a + "a str" - 6')).toEqual([
|
expect(stringSummaryLexer('a + "a str" - 6')).toEqual([
|
||||||
"word 'a' from 0 to 1",
|
"word 'a' from 0 to 1",
|
||||||
"whitespace ' ' from 1 to 2",
|
"whitespace ' ' from 1 to 2",
|
||||||
"operator '+' from 2 to 3",
|
"operator '+' from 2 to 3",
|
||||||
"whitespace ' ' from 3 to 4",
|
"whitespace ' ' from 3 to 4",
|
||||||
"string '\"a str\"' from 4 to 11",
|
'string \'"a str"\' from 4 to 11',
|
||||||
"whitespace ' ' from 11 to 12",
|
"whitespace ' ' from 11 to 12",
|
||||||
"operator '-' from 12 to 13",
|
"operator '-' from 12 to 13",
|
||||||
"whitespace ' ' from 13 to 14",
|
"whitespace ' ' from 13 to 14",
|
||||||
"number '6' from 14 to 15",
|
"number '6' from 14 to 15",
|
||||||
]);
|
])
|
||||||
expect(stringSummaryLexer("a + 'str'")).toEqual([
|
expect(stringSummaryLexer("a + 'str'")).toEqual([
|
||||||
"word 'a' from 0 to 1",
|
"word 'a' from 0 to 1",
|
||||||
"whitespace ' ' from 1 to 2",
|
"whitespace ' ' from 1 to 2",
|
||||||
"operator '+' from 2 to 3",
|
"operator '+' from 2 to 3",
|
||||||
"whitespace ' ' from 3 to 4",
|
"whitespace ' ' from 3 to 4",
|
||||||
"string ''str'' from 4 to 9",
|
"string ''str'' from 4 to 9",
|
||||||
]);
|
])
|
||||||
expect(stringSummaryLexer("a +'str'")).toEqual([
|
expect(stringSummaryLexer("a +'str'")).toEqual([
|
||||||
"word 'a' from 0 to 1",
|
"word 'a' from 0 to 1",
|
||||||
"whitespace ' ' from 1 to 2",
|
"whitespace ' ' from 1 to 2",
|
||||||
"operator '+' from 2 to 3",
|
"operator '+' from 2 to 3",
|
||||||
"string ''str'' from 3 to 8",
|
"string ''str'' from 3 to 8",
|
||||||
]);
|
])
|
||||||
|
|
||||||
expect(stringSummaryLexer("a + (sick)")).toEqual([
|
expect(stringSummaryLexer('a + (sick)')).toEqual([
|
||||||
"word 'a' from 0 to 1",
|
"word 'a' from 0 to 1",
|
||||||
"whitespace ' ' from 1 to 2",
|
"whitespace ' ' from 1 to 2",
|
||||||
"operator '+' from 2 to 3",
|
"operator '+' from 2 to 3",
|
||||||
@ -210,9 +210,9 @@ describe("testing lexer", () => {
|
|||||||
"brace '(' from 4 to 5",
|
"brace '(' from 4 to 5",
|
||||||
"word 'sick' from 5 to 9",
|
"word 'sick' from 5 to 9",
|
||||||
"brace ')' from 9 to 10",
|
"brace ')' from 9 to 10",
|
||||||
]);
|
])
|
||||||
|
|
||||||
expect(stringSummaryLexer("a + { sick}")).toEqual([
|
expect(stringSummaryLexer('a + { sick}')).toEqual([
|
||||||
"word 'a' from 0 to 1",
|
"word 'a' from 0 to 1",
|
||||||
"whitespace ' ' from 1 to 2",
|
"whitespace ' ' from 1 to 2",
|
||||||
"operator '+' from 2 to 3",
|
"operator '+' from 2 to 3",
|
||||||
@ -221,14 +221,14 @@ describe("testing lexer", () => {
|
|||||||
"whitespace ' ' from 5 to 6",
|
"whitespace ' ' from 5 to 6",
|
||||||
"word 'sick' from 6 to 10",
|
"word 'sick' from 6 to 10",
|
||||||
"brace '}' from 10 to 11",
|
"brace '}' from 10 to 11",
|
||||||
]);
|
])
|
||||||
|
|
||||||
expect(stringSummaryLexer("log('hi')")).toEqual([
|
expect(stringSummaryLexer("log('hi')")).toEqual([
|
||||||
"word 'log' from 0 to 3",
|
"word 'log' from 0 to 3",
|
||||||
"brace '(' from 3 to 4",
|
"brace '(' from 3 to 4",
|
||||||
"string ''hi'' from 4 to 8",
|
"string ''hi'' from 4 to 8",
|
||||||
"brace ')' from 8 to 9",
|
"brace ')' from 8 to 9",
|
||||||
]);
|
])
|
||||||
expect(stringSummaryLexer("log('hi', 'hello')")).toEqual([
|
expect(stringSummaryLexer("log('hi', 'hello')")).toEqual([
|
||||||
"word 'log' from 0 to 3",
|
"word 'log' from 0 to 3",
|
||||||
"brace '(' from 3 to 4",
|
"brace '(' from 3 to 4",
|
||||||
@ -237,8 +237,8 @@ describe("testing lexer", () => {
|
|||||||
"whitespace ' ' from 9 to 10",
|
"whitespace ' ' from 9 to 10",
|
||||||
"string ''hello'' from 10 to 17",
|
"string ''hello'' from 10 to 17",
|
||||||
"brace ')' from 17 to 18",
|
"brace ')' from 17 to 18",
|
||||||
]);
|
])
|
||||||
expect(stringSummaryLexer("fn funcName = (param1, param2) => {}")).toEqual([
|
expect(stringSummaryLexer('fn funcName = (param1, param2) => {}')).toEqual([
|
||||||
"word 'fn' from 0 to 2",
|
"word 'fn' from 0 to 2",
|
||||||
"whitespace ' ' from 2 to 3",
|
"whitespace ' ' from 2 to 3",
|
||||||
"word 'funcName' from 3 to 11",
|
"word 'funcName' from 3 to 11",
|
||||||
@ -256,16 +256,16 @@ describe("testing lexer", () => {
|
|||||||
"whitespace ' ' from 33 to 34",
|
"whitespace ' ' from 33 to 34",
|
||||||
"brace '{' from 34 to 35",
|
"brace '{' from 34 to 35",
|
||||||
"brace '}' from 35 to 36",
|
"brace '}' from 35 to 36",
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
|
||||||
const stringSummaryLexer = (input: string) =>
|
const stringSummaryLexer = (input: string) =>
|
||||||
lexer(input).map(
|
lexer(input).map(
|
||||||
({ type, value, start, end }) =>
|
({ type, value, start, end }) =>
|
||||||
`${type.padEnd(12, " ")} ${`'${value}'`.padEnd(10, " ")} from ${String(
|
`${type.padEnd(12, ' ')} ${`'${value}'`.padEnd(10, ' ')} from ${String(
|
||||||
start
|
start
|
||||||
).padEnd(3, " ")} to ${end}`
|
).padEnd(3, ' ')} to ${end}`
|
||||||
);
|
)
|
||||||
|
@ -1,44 +1,55 @@
|
|||||||
const NUMBER = /^[0-9]+/;
|
const NUMBER = /^[0-9]+/
|
||||||
const WHITESPACE = /\s+/;
|
const WHITESPACE = /\s+/
|
||||||
const WORD = /^[a-zA-Z_][a-zA-Z0-9_]*/;
|
const WORD = /^[a-zA-Z_][a-zA-Z0-9_]*/
|
||||||
// regex that captures everything between two non escaped quotes and the quotes aren't captured in the match
|
// regex that captures everything between two non escaped quotes and the quotes aren't captured in the match
|
||||||
const STRING = /^(["'])(?:(?=(\\?))\2.)*?\1/;
|
const STRING = /^(["'])(?:(?=(\\?))\2.)*?\1/
|
||||||
// verbose regex for finding operators, multiple character operators need to be first
|
// verbose regex for finding operators, multiple character operators need to be first
|
||||||
const OPERATOR = /^(>=|<=|==|=>|!=|\*|\+|-|\/|%|=|<|>|\||\^)/;
|
const OPERATOR = /^(>=|<=|==|=>|!=|\*|\+|-|\/|%|=|<|>|\||\^)/
|
||||||
|
|
||||||
const BLOCK_START = /^\{/;
|
const BLOCK_START = /^\{/
|
||||||
const BLOCK_END = /^\}/;
|
const BLOCK_END = /^\}/
|
||||||
const PARAN_START = /^\(/;
|
const PARAN_START = /^\(/
|
||||||
const PARAN_END = /^\)/;
|
const PARAN_END = /^\)/
|
||||||
const COMMA = /^,/;
|
const COMMA = /^,/
|
||||||
|
|
||||||
export const isNumber = (character: string) => NUMBER.test(character);
|
export const isNumber = (character: string) => NUMBER.test(character)
|
||||||
export const isWhitespace = (character: string) => WHITESPACE.test(character);
|
export const isWhitespace = (character: string) => WHITESPACE.test(character)
|
||||||
export const isWord = (character: string) => WORD.test(character);
|
export const isWord = (character: string) => WORD.test(character)
|
||||||
export const isString = (character: string) => STRING.test(character);
|
export const isString = (character: string) => STRING.test(character)
|
||||||
export const isOperator = (character: string) => OPERATOR.test(character);
|
export const isOperator = (character: string) => OPERATOR.test(character)
|
||||||
export const isBlockStart = (character: string) => BLOCK_START.test(character);
|
export const isBlockStart = (character: string) => BLOCK_START.test(character)
|
||||||
export const isBlockEnd = (character: string) => BLOCK_END.test(character);
|
export const isBlockEnd = (character: string) => BLOCK_END.test(character)
|
||||||
export const isParanStart = (character: string) => PARAN_START.test(character);
|
export const isParanStart = (character: string) => PARAN_START.test(character)
|
||||||
export const isParanEnd = (character: string) => PARAN_END.test(character);
|
export const isParanEnd = (character: string) => PARAN_END.test(character)
|
||||||
export const isComma = (character: string) => COMMA.test(character);
|
export const isComma = (character: string) => COMMA.test(character)
|
||||||
|
|
||||||
function matchFirst(str: string, regex: RegExp) {
|
function matchFirst(str: string, regex: RegExp) {
|
||||||
const theMatch = str.match(regex);
|
const theMatch = str.match(regex)
|
||||||
if (!theMatch) {
|
if (!theMatch) {
|
||||||
throw new Error("Should always be a match:" + str);
|
throw new Error('Should always be a match:' + str)
|
||||||
}
|
}
|
||||||
return theMatch[0];
|
return theMatch[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Token {
|
export interface Token {
|
||||||
type: "number" | "word" | "operator" | "string" | "brace" | "whitespace" | "comma";
|
type:
|
||||||
value: string;
|
| 'number'
|
||||||
start: number;
|
| 'word'
|
||||||
end: number;
|
| 'operator'
|
||||||
|
| 'string'
|
||||||
|
| 'brace'
|
||||||
|
| 'whitespace'
|
||||||
|
| 'comma'
|
||||||
|
value: string
|
||||||
|
start: number
|
||||||
|
end: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeToken = (type: Token["type"], value: string, start: number): Token => ({
|
const makeToken = (
|
||||||
|
type: Token['type'],
|
||||||
|
value: string,
|
||||||
|
start: number
|
||||||
|
): Token => ({
|
||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
start,
|
start,
|
||||||
@ -46,39 +57,43 @@ const makeToken = (type: Token["type"], value: string, start: number): Token =>
|
|||||||
})
|
})
|
||||||
|
|
||||||
const returnTokenAtIndex = (str: string, startIndex: number): Token | null => {
|
const returnTokenAtIndex = (str: string, startIndex: number): Token | null => {
|
||||||
const strFromIndex = str.slice(startIndex);
|
const strFromIndex = str.slice(startIndex)
|
||||||
if (isOperator(strFromIndex)) {
|
if (isOperator(strFromIndex)) {
|
||||||
return makeToken("operator", matchFirst(strFromIndex, OPERATOR), startIndex);
|
return makeToken('operator', matchFirst(strFromIndex, OPERATOR), startIndex)
|
||||||
}
|
}
|
||||||
if (isString(strFromIndex)) {
|
if (isString(strFromIndex)) {
|
||||||
return makeToken("string", matchFirst(strFromIndex, STRING), startIndex);
|
return makeToken('string', matchFirst(strFromIndex, STRING), startIndex)
|
||||||
}
|
}
|
||||||
if (isParanEnd(strFromIndex)) {
|
if (isParanEnd(strFromIndex)) {
|
||||||
return makeToken("brace", matchFirst(strFromIndex, PARAN_END), startIndex);
|
return makeToken('brace', matchFirst(strFromIndex, PARAN_END), startIndex)
|
||||||
}
|
}
|
||||||
if (isParanStart(strFromIndex)) {
|
if (isParanStart(strFromIndex)) {
|
||||||
return makeToken("brace", matchFirst(strFromIndex, PARAN_START), startIndex);
|
return makeToken('brace', matchFirst(strFromIndex, PARAN_START), startIndex)
|
||||||
}
|
}
|
||||||
if (isBlockStart(strFromIndex)) {
|
if (isBlockStart(strFromIndex)) {
|
||||||
return makeToken("brace", matchFirst(strFromIndex, BLOCK_START), startIndex);
|
return makeToken('brace', matchFirst(strFromIndex, BLOCK_START), startIndex)
|
||||||
}
|
}
|
||||||
if (isBlockEnd(strFromIndex)) {
|
if (isBlockEnd(strFromIndex)) {
|
||||||
return makeToken("brace", matchFirst(strFromIndex, BLOCK_END), startIndex);
|
return makeToken('brace', matchFirst(strFromIndex, BLOCK_END), startIndex)
|
||||||
}
|
}
|
||||||
if (isComma(strFromIndex)) {
|
if (isComma(strFromIndex)) {
|
||||||
return makeToken("comma", matchFirst(strFromIndex, COMMA), startIndex);
|
return makeToken('comma', matchFirst(strFromIndex, COMMA), startIndex)
|
||||||
}
|
}
|
||||||
if (isNumber(strFromIndex)) {
|
if (isNumber(strFromIndex)) {
|
||||||
return makeToken("number", matchFirst(strFromIndex, NUMBER), startIndex);
|
return makeToken('number', matchFirst(strFromIndex, NUMBER), startIndex)
|
||||||
}
|
}
|
||||||
if (isWord(strFromIndex)) {
|
if (isWord(strFromIndex)) {
|
||||||
return makeToken("word", matchFirst(strFromIndex, WORD), startIndex);
|
return makeToken('word', matchFirst(strFromIndex, WORD), startIndex)
|
||||||
}
|
}
|
||||||
if (isWhitespace(strFromIndex)) {
|
if (isWhitespace(strFromIndex)) {
|
||||||
return makeToken("whitespace", matchFirst(strFromIndex, WHITESPACE), startIndex);
|
return makeToken(
|
||||||
|
'whitespace',
|
||||||
|
matchFirst(strFromIndex, WHITESPACE),
|
||||||
|
startIndex
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return null;
|
return null
|
||||||
};
|
}
|
||||||
|
|
||||||
export const lexer = (str: string): Token[] => {
|
export const lexer = (str: string): Token[] => {
|
||||||
const recursivelyTokenise = (
|
const recursivelyTokenise = (
|
||||||
@ -87,14 +102,14 @@ export const lexer = (str: string): Token[] => {
|
|||||||
previousTokens: Token[] = []
|
previousTokens: Token[] = []
|
||||||
): Token[] => {
|
): Token[] => {
|
||||||
if (currentIndex >= str.length) {
|
if (currentIndex >= str.length) {
|
||||||
return previousTokens;
|
return previousTokens
|
||||||
}
|
}
|
||||||
const token = returnTokenAtIndex(str, currentIndex);
|
const token = returnTokenAtIndex(str, currentIndex)
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return recursivelyTokenise(str, currentIndex + 1, previousTokens);
|
return recursivelyTokenise(str, currentIndex + 1, previousTokens)
|
||||||
}
|
}
|
||||||
const nextIndex = currentIndex + token.value.length;
|
const nextIndex = currentIndex + token.value.length
|
||||||
return recursivelyTokenise(str, nextIndex, [...previousTokens, token]);
|
return recursivelyTokenise(str, nextIndex, [...previousTokens, token])
|
||||||
};
|
}
|
||||||
return recursivelyTokenise(str);
|
return recursivelyTokenise(str)
|
||||||
};
|
}
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import { isOverlapping } from "./utils";
|
import { isOverlapping } from './utils'
|
||||||
import { Range } from "../useStore";
|
import { Range } from '../useStore'
|
||||||
|
|
||||||
describe("testing isOverlapping", () => {
|
describe('testing isOverlapping', () => {
|
||||||
testBothOrders([0, 5], [3, 10]);
|
testBothOrders([0, 5], [3, 10])
|
||||||
testBothOrders([0, 5], [3, 4]);
|
testBothOrders([0, 5], [3, 4])
|
||||||
testBothOrders([0, 5], [5, 10]);
|
testBothOrders([0, 5], [5, 10])
|
||||||
testBothOrders([0, 5], [6, 10], false);
|
testBothOrders([0, 5], [6, 10], false)
|
||||||
testBothOrders([0, 5], [-1, 1]);
|
testBothOrders([0, 5], [-1, 1])
|
||||||
testBothOrders([0, 5], [-1, 0]);
|
testBothOrders([0, 5], [-1, 0])
|
||||||
testBothOrders([0, 5], [-2, -1], false);
|
testBothOrders([0, 5], [-2, -1], false)
|
||||||
});
|
})
|
||||||
|
|
||||||
function testBothOrders(a: Range, b: Range, result = true) {
|
function testBothOrders(a: Range, b: Range, result = true) {
|
||||||
it(`test is overlapping ${a} ${b}`, () => {
|
it(`test is overlapping ${a} ${b}`, () => {
|
||||||
expect(isOverlapping(a, b)).toBe(result);
|
expect(isOverlapping(a, b)).toBe(result)
|
||||||
expect(isOverlapping(b, a)).toBe(result);
|
expect(isOverlapping(b, a)).toBe(result)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { Range } from '../useStore'
|
import { Range } from '../useStore'
|
||||||
|
|
||||||
export const isOverlapping = (a: Range, b: Range) => {
|
export const isOverlapping = (a: Range, b: Range) => {
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { ReportHandler } from 'web-vitals';
|
import { ReportHandler } from 'web-vitals'
|
||||||
|
|
||||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
getCLS(onPerfEntry);
|
getCLS(onPerfEntry)
|
||||||
getFID(onPerfEntry);
|
getFID(onPerfEntry)
|
||||||
getFCP(onPerfEntry);
|
getFCP(onPerfEntry)
|
||||||
getLCP(onPerfEntry);
|
getLCP(onPerfEntry)
|
||||||
getTTFB(onPerfEntry);
|
getTTFB(onPerfEntry)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default reportWebVitals;
|
export default reportWebVitals
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
// allows you to do things like:
|
// allows you to do things like:
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom'
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
import create from 'zustand'
|
import create from 'zustand'
|
||||||
import {addLineHighlight, EditorView} from './editor/highlightextension'
|
import { addLineHighlight, EditorView } from './editor/highlightextension'
|
||||||
|
|
||||||
export type Range = [number, number]
|
export type Range = [number, number]
|
||||||
|
|
||||||
interface StoreState {
|
interface StoreState {
|
||||||
editorView: EditorView | null,
|
editorView: EditorView | null
|
||||||
setEditorView: (editorView: EditorView) => void,
|
setEditorView: (editorView: EditorView) => void
|
||||||
highlightRange: [number, number],
|
highlightRange: [number, number]
|
||||||
setHighlightRange: (range: Range) => void,
|
setHighlightRange: (range: Range) => void
|
||||||
selectionRange: [number, number],
|
selectionRange: [number, number]
|
||||||
setSelectionRange: (range: Range) => void,
|
setSelectionRange: (range: Range) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStore = create<StoreState>()((set, get) => ({
|
export const useStore = create<StoreState>()((set, get) => ({
|
||||||
editorView: null,
|
editorView: null,
|
||||||
setEditorView: (editorView) => {
|
setEditorView: (editorView) => {
|
||||||
set({editorView})
|
set({ editorView })
|
||||||
},
|
},
|
||||||
highlightRange: [0, 0],
|
highlightRange: [0, 0],
|
||||||
setHighlightRange: (highlightRange) => {
|
setHighlightRange: (highlightRange) => {
|
||||||
set({ highlightRange })
|
set({ highlightRange })
|
||||||
const editorView = get().editorView
|
const editorView = get().editorView
|
||||||
if (editorView) {
|
if (editorView) {
|
||||||
editorView.dispatch({ effects: addLineHighlight.of(highlightRange) });
|
editorView.dispatch({ effects: addLineHighlight.of(highlightRange) })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectionRange: [0, 0],
|
selectionRange: [0, 0],
|
||||||
|
@ -8239,6 +8239,11 @@ prelude-ls@~1.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||||
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
||||||
|
|
||||||
|
prettier@^2.8.0:
|
||||||
|
version "2.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9"
|
||||||
|
integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==
|
||||||
|
|
||||||
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
|
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
|
||||||
version "5.6.0"
|
version "5.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
|
||||||
|
Reference in New Issue
Block a user