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