├── .idea ├── .name ├── vcs.xml ├── modules.xml ├── filament-gui.iml └── workspace.xml ├── .nvmrc ├── src ├── react-app-env.d.ts ├── setupTests.js ├── canvas.js ├── index.css ├── index.js ├── examples │ ├── examples_panel.css │ ├── examples_panel.tsx │ ├── comps.tsx │ ├── views.js │ └── examples.ts ├── symbols │ ├── symbols.css │ └── symbols_panel.tsx ├── App.css ├── App.js └── editor │ ├── view.tsx │ └── editor.ts ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── manifest.json └── index.html ├── research.md ├── tools ├── test.md └── builddocs.js ├── .gitignore ├── experiments ├── raytracer.html ├── genuary5_golf.html ├── genuary6.html ├── genuary4.html ├── genuary5.html └── raytracer.js ├── scratch.md ├── tsconfig.json ├── package.json ├── tests ├── funcalls.js ├── unicodetest.js ├── util.js └── lang.test.js ├── examples.md ├── README.md └── roadmap.md /.idea/.name: -------------------------------------------------------------------------------- 1 | filament-gui -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16.16.0 2 | 3 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshmarinacci/filament-gui/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshmarinacci/filament-gui/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshmarinacci/filament-gui/HEAD/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /research.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | language comparison 4 | 5 | # Hypertalk 6 | 7 | The language built into HyperCard. 8 | 9 | Used a sort of english like language. variables at the end of phrases. ex: 10 | 11 | ``` 12 | put 4*5 into theVar 13 | ``` 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/canvas.js: -------------------------------------------------------------------------------- 1 | import {useEffect, useRef} from 'react' 2 | 3 | export function CanvasView({result}) { 4 | let ref = useRef() 5 | useEffect(() => { 6 | if (ref.current) { 7 | result.cb(ref.current) 8 | } 9 | }) 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /tools/test.md: -------------------------------------------------------------------------------- 1 | # header1 2 | 3 | ## header2 4 | 5 | a paragraph of cool text 6 | is right here 7 | 8 | # header 3 9 | 10 | ```filament 11 | { 12 | data << [42,43,44,45] 13 | chart(data) 14 | } 15 | ``` 16 | 17 | lets mess with the alphabet 18 | 19 | ```filament 20 | chart(dataset('alphabet'), x_label:'letter', y:'syllables') 21 | ``` -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .eslintcache 26 | /output 27 | -------------------------------------------------------------------------------- /experiments/raytracer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App.js'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | // reportWebVitals(); 17 | -------------------------------------------------------------------------------- /scratch.md: -------------------------------------------------------------------------------- 1 | 2 | * timeline of states entering the union. timeline date vs name 3 | * plot public stock history of apple over the last 12 months 4 | 5 | fetch stock listing 6 | convert to dataset with data.schema and data.items. have make dataset from list w/ default schema? 7 | new dataset(items, schema) 8 | draw barchart of 100+ items? or make a line chart. 9 | 10 | 11 | https://query1.finance.yahoo.com/v7/finance/download/AAPL?period1=1579393259&period2=1611015659&interval=1d&events=history&includeAdjustedClose=true 12 | 13 | Jan 21 2020 to Jan 15 2021 -------------------------------------------------------------------------------- /.idea/filament-gui.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /experiments/genuary5_golf.html: -------------------------------------------------------------------------------- 1 | 2 | 20 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "ES2022", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /src/examples/examples_panel.css: -------------------------------------------------------------------------------- 1 | .accordion-group { 2 | border: 1px solid gray; 3 | padding:0; 4 | margin: 0.25rem 0; 5 | } 6 | .accordion-group h3 { 7 | border: 0px solid gray; 8 | padding: 0.25rem; 9 | margin: 0; 10 | } 11 | .accordion-group h3.open { 12 | background-color: aquamarine; 13 | border-bottom-width: 1px; 14 | } 15 | .accordion-group h3.closed { 16 | background-color: white; 17 | } 18 | 19 | .accordion-group h3.open:hover { 20 | background-color: lightgray; 21 | } 22 | .accordion-group h3.closed:hover { 23 | background-color: aquamarine; 24 | } 25 | 26 | 27 | .accordion-group { 28 | display: flex; 29 | flex-direction: column; 30 | } 31 | .accordion-group button { 32 | border: 0px solid red; 33 | background-color: #eeeeee; 34 | margin: 0.25em; 35 | border-radius: 0.5em; 36 | } 37 | .accordion-group button:hover { 38 | background-color: aqua; 39 | } 40 | -------------------------------------------------------------------------------- /src/symbols/symbols.css: -------------------------------------------------------------------------------- 1 | 2 | ul.symbols { 3 | list-style: none; 4 | margin:0; 5 | padding:0; 6 | } 7 | ul.symbols > li { 8 | /*margin-bottom: 2em;*/ 9 | } 10 | ul.parameters { 11 | list-style: none; 12 | } 13 | 14 | .accordion-item { 15 | border: 0px solid blue; 16 | margin-bottom: 0.25rem; 17 | } 18 | .accordion-button { 19 | border: 0px inset #aaa; 20 | border-width: 1px 0 0 0; 21 | background-color: #eeeeee; 22 | padding:0; 23 | margin:0; 24 | user-select: none; 25 | } 26 | .accordion-button:hover { 27 | background-color: #fff; 28 | } 29 | .accordion-item.opened .accordion-content { 30 | display: block; 31 | } 32 | .accordion-item.closed .accordion-content { 33 | display: none; 34 | } 35 | 36 | .accordion-content .parameters { 37 | border: 0px solid black; 38 | background-color: #ffffff; 39 | } 40 | 41 | .accordion-content blockquote.example { 42 | border: 1px solid #f5b981; 43 | background-color: #efe1b8; 44 | padding: 0.25em; 45 | margin:0.25em; 46 | white-space: pre-line; 47 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "browserslist": { 3 | "production": [ 4 | ">0.2%", 5 | "not dead", 6 | "not op_mini all" 7 | ], 8 | "development": [ 9 | "last 1 chrome version", 10 | "last 1 firefox version", 11 | "last 1 safari version" 12 | ] 13 | }, 14 | "dependencies": { 15 | "@testing-library/jest-dom": "^5.16.4", 16 | "@testing-library/react": "^13.3.0", 17 | "@testing-library/user-event": "^13.5.0", 18 | "@types/node": "^15.0.0", 19 | "@types/react": "^18.0.15", 20 | "@types/react-dom": "^18.0.6", 21 | "alphavantage": "^2.2.0", 22 | "codemirror": "^5.59.1", 23 | "date-fns": "^2.16.1", 24 | "filament-lang": "^0.5.2", 25 | "node-fetch": "^2.6.1", 26 | "pureimage": "^0.2.5", 27 | "react": "^18.2.0", 28 | "react-dom": "^18.2.0", 29 | "react-scripts": "5.0.1", 30 | "tinycolor": "^0.0.1", 31 | "tinycolor2": "^1.4.2", 32 | "typescript": "^4.7.4", 33 | "web-vitals": "^0.2.4" 34 | }, 35 | "devDependencies": { 36 | "@types/codemirror": "^5.60.5", 37 | "tape-approximately": "^1.0.0" 38 | }, 39 | "engines": { 40 | "node": ">=15.0.0" 41 | }, 42 | "eslintConfig": { 43 | "extends": [ 44 | "react-app", 45 | "react-app/jest" 46 | ] 47 | }, 48 | "name": "notebook-lang", 49 | "private": true, 50 | "scripts": { 51 | "start": "react-scripts start", 52 | "build": "react-scripts build", 53 | "test": "react-scripts test", 54 | "eject": "react-scripts eject", 55 | "parser": "node tests/langtests_all.js" 56 | }, 57 | "version": "0.2.0" 58 | } 59 | -------------------------------------------------------------------------------- /tests/funcalls.js: -------------------------------------------------------------------------------- 1 | import {tests} from './util.js' 2 | import {drop, length, max, reverse, sort, sum, take} from 'filament-lang' 3 | import {dataset} from '../../filament-lang/src/dataset.js' 4 | 5 | 6 | let scope = {drop, length, max, reverse, sort, sum, take, dataset} 7 | 8 | tests("functions",[ 9 | ['[4,2,42]',[4,2,42]], 10 | ['length([4,2,42])',3], 11 | ['length(data:[4,2,42])',3], 12 | 13 | ['take(data:[4,2,42],count:2)',[4,2]], 14 | ['take(count:2, data:[4,2,42])',[4,2]], 15 | ['take([4,2,42],count:2)',[4,2]], 16 | ['take([4,2,42],2)',[4,2]], 17 | 18 | ['sort(data:[4,2,42])',[2,4,42]], 19 | ['sort([4,2,42])',[2,4,42]], 20 | ['sort(data:[4,2,42], order:"ascending")',[2,4,42]], 21 | ['sort(data:[4,2,42], order:"descending")',[42,4,2]], 22 | ['sort([4,2,42], order:"descending")',[42,4,2]], 23 | ['sort(order:"descending",[4,2,42])',[42,4,2]], 24 | 25 | ['sum([4,2,42])',48], 26 | ['sum(data:[4,2,42])',48], 27 | ['max(data:[4,2,42])',42], 28 | ['reverse(data:[4,2,42])',[42,2,4]], 29 | ['drop(data:[4,2,42],count:1)',[2,42]], 30 | 31 | 32 | ],{ scope }) 33 | 34 | tests("pipelines",[ 35 | ['[4,2,42]',[4,2,42]], 36 | ['length([4,2,42])',3], 37 | ['take([4,2,42],count:2)',[4,2]], 38 | ['take([4,2,42],count:2) >> sort()',[2,4]], 39 | ['take([4,2,42],count:2) >> sort(order:"descending")',[4,2]], 40 | ],{ scope }) 41 | 42 | 43 | 44 | tests('async functions',[ 45 | ['length(dataset("alphabet"))',26], 46 | [`dataset("alphabet") >> length()`,26], 47 | // [`dataset('tallest_buildings') >> take(count:5) >> length()`,[]] 48 | ],{scope}) 49 | -------------------------------------------------------------------------------- /tests/unicodetest.js: -------------------------------------------------------------------------------- 1 | import {scalar, Scope} from '../../filament-lang/src/ast.js' 2 | import { 3 | add, and, convertunit, 4 | divide, equal, 5 | factorial, greaterthan, greaterthanorequal, 6 | is_prime, 7 | lessthan, 8 | lessthanorequal, 9 | mod, 10 | multiply, 11 | negate, not, notequal, or, 12 | power, 13 | subtract 14 | } from '../../filament-lang/src/math.js' 15 | import {drop, get_field, join, length, map, range, reverse, select, sort, sum, take} from '../../filament-lang/src/lists.js' 16 | import {dataset} from '../../filament-lang/src/dataset.js' 17 | import {Parser} from '../../filament-lang/src/parser.js' 18 | import fs from 'fs' 19 | 20 | let g2_source = fs.readFileSync(new URL("../src/lang/filament.ohm", import.meta.url)).toString() 21 | 22 | function eval_unicode(name, tests) { 23 | let scope = new Scope('unicode') 24 | scope.install(add,subtract,multiply,divide, power,mod, negate, factorial, is_prime) 25 | scope.install(lessthan,lessthanorequal,equal,notequal,greaterthanorequal,greaterthan,and,or,not) 26 | scope.install(range,length,take,drop,join,reverse,map, get_field, select,sort,sum) 27 | scope.install(dataset) 28 | scope.install(convertunit) 29 | scope.set_var('pi',scalar(Math.PI)) 30 | let parser = new Parser(scope,g2_source) 31 | tests.map(tcase => { 32 | console.log("eval ast test case",tcase) 33 | let [code,val] = tcase 34 | let match = parser.parse(code) 35 | let ast = parser.semantics(match).unicode() 36 | console.log("unicode: ",ast) 37 | }) 38 | } 39 | 40 | eval_unicode('simple pipeline',[ 41 | ['4*5 >> 7'], 42 | ['x <> y'], 43 | ]) 44 | -------------------------------------------------------------------------------- /src/examples/examples_panel.tsx: -------------------------------------------------------------------------------- 1 | import {Example, ExampleGroup, EXAMPLES} from './examples' 2 | import {useState} from 'react' 3 | import "./examples_panel.css" 4 | 5 | type AccordionViewProps = { 6 | title:string, 7 | children:JSX.Element[] 8 | } 9 | function AccordionView({title, children}:AccordionViewProps) { 10 | const [open, setOpen] = useState(false) 11 | if(!open) children = [] 12 | return
13 |

setOpen(!open)}>{title}

14 | {children} 15 |
16 | } 17 | 18 | 19 | 20 | type ExamplesPanelProps = { 21 | onSetDoc:(e:any)=>{} 22 | } 23 | export function ExamplesPanel({onSetDoc}:ExamplesPanelProps) { 24 | function make_example_button(ex:Example, i:number):JSX.Element { 25 | return 38 | 39 | } 40 | return
41 | 42 |

Examples

43 | {EXAMPLES.map((group:ExampleGroup,i) => { 44 | if(group.type === 'group') { 45 | let items = group.content.map(make_example_button) 46 | return 47 | } else { 48 | return
nothing
49 | } 50 | })} 51 |
52 | } 53 | -------------------------------------------------------------------------------- /examples.md: -------------------------------------------------------------------------------- 1 | # examples 2 | 3 | * What's the shortest possibl 4 | e raytracer using vector math. 5 | * Loop over every pixel 6 | * generate primary ray 7 | * intersect with list of objects 8 | * find normal at closest intersection 9 | * calculate shading using lights. 10 | * project secondary rays and recurse 11 | 12 | 13 | ```javascript 14 | class Color { 15 | constructor(red,green,blue) { 16 | this.red = red, 17 | this.blue = blue 18 | this.green = green 19 | } 20 | } 21 | class Sphere { 22 | center = new Point() 23 | radius = 1 24 | color = new Color(0,255,0) 25 | intersect(ray) { 26 | let l = this.center.sub(ray.origin) 27 | let adj2 = l.dot(ray.direction) 28 | let d2 = l.dot(l).subtract(adj2*adj2) 29 | if(d2 < this.radius*this.radius) { 30 | return true 31 | } else { 32 | return false 33 | } 34 | } 35 | } 36 | 37 | const scene = { 38 | width: 800, 39 | height: 600, 40 | fov: 90.0 41 | sphere: new Sphere( 42 | new Point(0,0,-5), 43 | 1.0, 44 | new Color(0.4,1.0,0.4) 45 | ) 46 | } 47 | 48 | class Ray { 49 | origin: new Point(), 50 | direction: new Vec3() 51 | } 52 | 53 | let canvas = new Image(100,100) 54 | 55 | function create_prime(x,y,scene) { 56 | let canvas_x = (x+0.5)/scene.width * 2.0 - 1.0 57 | let canvas_y = 1.0 - (y+0.5)/scene.height * 2.0 58 | return new Ray( 59 | new Point(0,0,0), 60 | new Vec3(canvas_x,canvas_y,-1).normalize(), 61 | ) 62 | } 63 | 64 | for let i=0; i{}, 7 | children:JSX.Element, 8 | } 9 | 10 | export const CollapsablePanel = ({children, direction, onToggle, open}:CollapsablePanelProps) => { 11 | const label = () => { 12 | if(direction === 'left') return open?"<":">" 13 | if(direction === 'right') return open?">":"<" 14 | return "|" 15 | } 16 | return
17 | 18 | {open?children:""} 19 |
20 | } 21 | 22 | 23 | type VBoxProps = { 24 | grow:boolean, 25 | fill:boolean, 26 | classes:any, 27 | children:JSX.Element 28 | } 29 | export const VBox = ({children, classes={}, grow = false, fill=false}:VBoxProps) => { 30 | let style:CSSProperties = { 31 | display: 'flex', 32 | flexDirection: 'column' 33 | } 34 | let clsses:any = { 35 | grow: grow, 36 | fill:fill, 37 | } 38 | Object.keys(classes).forEach(key => clsses[key] = classes[key]) 39 | let clssstr = "vbox " + Object.keys(clsses).filter(k => clsses[k]).join(" ") 40 | return
{children}
41 | } 42 | 43 | type HBoxProps = { 44 | grow:boolean, 45 | fill:boolean, 46 | classes:any, 47 | children:JSX.Element 48 | } 49 | 50 | export const HBox = ({children, grow=false, fill = false}:HBoxProps) => { 51 | let style:CSSProperties = { 52 | display: 'flex', 53 | flexDirection: 'row' 54 | } 55 | let clsses:any = { 56 | fill: fill, 57 | grow: grow, 58 | } 59 | let clssstr = "hbox " + Object.keys(clsses).filter(k => clsses[k]).join(" ") 60 | return
{children}
61 | } 62 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: white; 3 | color: #333; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 5 | } 6 | * { 7 | font-size: 100%; 8 | } 9 | 10 | .fill { 11 | width: 100%; 12 | } 13 | .grow { 14 | flex: 1.0; 15 | } 16 | 17 | textarea { 18 | font-size: 150%; 19 | line-height: 150%; 20 | padding: 0.5em; 21 | min-height: 10em; 22 | } 23 | canvas { 24 | border:1px solid black; 25 | width: 100%; 26 | height: auto; 27 | justify-self: center; 28 | align-self: center; 29 | } 30 | 31 | .docs > div { 32 | border: 0px solid red; 33 | max-width: 20em; 34 | margin: 0.25em; 35 | } 36 | 37 | 38 | .list-result { 39 | display: flex; 40 | flex-direction: row; 41 | flex-wrap: wrap; 42 | } 43 | 44 | .table-wrapper { 45 | max-height: 200px; 46 | overflow: auto; 47 | } 48 | .table-result { 49 | border: 1px solid #333333; 50 | border-collapse: collapse; 51 | width: 100%; 52 | } 53 | .table-result th { 54 | background-color: aliceblue; 55 | position: sticky; 56 | top:0; 57 | } 58 | .table-result td, .table-result th { 59 | border: 1px solid #333333; 60 | } 61 | 62 | .CodeMirror { 63 | border: 1px solid #eee; 64 | height: auto !important; 65 | } 66 | article { 67 | /*border: 1px solid red;*/ 68 | } 69 | 70 | .grow { 71 | flex: 1.0; 72 | } 73 | 74 | .hbox { 75 | 76 | } 77 | 78 | .vbox { 79 | } 80 | .hbox { 81 | 82 | } 83 | 84 | .vbox { 85 | } 86 | 87 | 88 | 89 | main { 90 | border: 0px solid red; 91 | /*display: grid;*/ 92 | /*grid-template-columns: 20rem 1fr 20rem;*/ 93 | grid-gap: 2rem; 94 | grid-template-rows: 2rem 1fr 2rem; 95 | max-height: 99vh; 96 | } 97 | header { 98 | grid-row: 1/2; 99 | grid-column: 1/4; 100 | /*font-size: 2rem;*/ 101 | margin: 0 20px; 102 | /*font-weight: bold;*/ 103 | } 104 | .collapse { 105 | border: 0px solid blue; 106 | background-color: #f0f0f0; 107 | margin: 0 20px; 108 | padding: 0.5rem; 109 | grid-row: 2/4; 110 | overflow: scroll; 111 | display: flex; 112 | flex-direction: column; 113 | align-items: stretch; 114 | } 115 | .collapse.left > button { 116 | align-self: end; 117 | } 118 | .collapse.right > button { 119 | align-self: start; 120 | } 121 | .entries { 122 | border: 0px solid green; 123 | grid-row: 2/4; 124 | overflow: scroll; 125 | } 126 | 127 | header a { 128 | padding: 0 1rem; 129 | } 130 | 131 | 132 | article { 133 | border: 1px dashed #daa; 134 | border-width: 1px 0 1px 0; 135 | background-color: #f9f9f9; 136 | padding: 0.5rem; 137 | margin: 0.5rem; 138 | } 139 | -------------------------------------------------------------------------------- /src/symbols/symbols_panel.tsx: -------------------------------------------------------------------------------- 1 | 2 | // @ts-ignore 3 | import API_RAW from "filament-lang/src/api.json.js" 4 | import {useState} from 'react' 5 | import "./symbols.css" 6 | 7 | type FSymbolAPI = { 8 | "charts":FSymbol[], 9 | } 10 | 11 | type FSymbol = { 12 | name:string, 13 | params:object, 14 | summary:string, 15 | examples:string[] 16 | } 17 | const API:FSymbolAPI = JSON.parse(API_RAW) 18 | type ConstantProps = { 19 | symbol:FSymbol 20 | } 21 | function Constant({symbol}:ConstantProps) { 22 | return
  • const {symbol.name}
  • 23 | } 24 | 25 | function search_doc(name:string):FSymbol|null { 26 | for(let group of Object.values(API)) { 27 | for( let doc of group) { 28 | if(doc.name === name) { 29 | return doc 30 | } 31 | } 32 | } 33 | return null 34 | } 35 | 36 | type FunctionSymbolProps = { 37 | symbol:FSymbol 38 | } 39 | function FunctionSymbol({symbol}:FunctionSymbolProps) { 40 | let [open, setOpen] = useState(false) 41 | 42 | let rendered_doc:JSX.Element 43 | let doc = search_doc(symbol.name) 44 | if(doc) { 45 | // console.log("matched a doc",doc) 46 | let parms = Object.entries(doc.params).map(([name, val]) => { 47 | return
  • {name}:{val}
  • 48 | }) 49 | rendered_doc =
    50 |

    function {doc.summary}

    51 |
      {parms}
    52 | {doc.examples.map(ex => { 53 | return
    {ex.toString().trim()}
    54 | })} 55 |
    56 | } else { 57 | let parms = Object.entries(symbol.params).map(([name, val]) => { 58 | return
  • {name}
  • 59 | }) 60 | rendered_doc =
    61 | fun  62 | {symbol.name}( 63 |

    {symbol.summary}

    64 |
      {parms}
    65 | ) 66 |
    67 | } 68 | return
  • 69 |

    setOpen(!open)}>{symbol.name}

    70 | {rendered_doc} 71 |
  • 72 | } 73 | 74 | type SymbolsPanelProps = { 75 | scope:any, 76 | } 77 | export function SymbolsPanel({scope}:SymbolsPanelProps) { 78 | let names:string[] = scope.names().slice() 79 | names.sort() 80 | return
      81 | {names.map((name) => { 82 | let fun = scope.funs[name] 83 | if(fun.type === 'scalar') return 84 | return 85 | })} 86 |
    87 | } 88 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import {useState} from 'react' 2 | import './App.css' 3 | import "codemirror/addon/hint/show-hint.css" 4 | import "codemirror/lib/codemirror.css" 5 | import {View} from './editor/view.tsx' 6 | import {ExamplesPanel} from './examples/examples_panel.tsx' 7 | import {make_standard_scope} from 'filament-lang' 8 | import {SymbolsPanel} from './symbols/symbols_panel.tsx' 9 | import {CollapsablePanel} from './examples/comps' 10 | 11 | const realdoc = [ 12 | { 13 | type:'filament', 14 | title:'arithmetic', 15 | input:"4+5+3", 16 | output:null, 17 | }, 18 | { 19 | type:'filament', 20 | title:'range', 21 | input:"range(10)", 22 | output:null, 23 | } 24 | ] 25 | 26 | 27 | function update_doc(doc, entry, code) { 28 | console.log("updating",doc,entry,code) 29 | } 30 | let scope = make_standard_scope() 31 | 32 | function MainView({children, setDoc}) { 33 | const [left_open,set_left_open] = useState(true) 34 | const [right_open,set_right_open] = useState(true) 35 | 36 | let cols = `${left_open?'20rem':'5rem'} 1fr ${right_open?'20rem':'5rem'}`; 37 | let style = { 38 | 'display':'grid', 39 | 'gridTemplateColumns': cols 40 | } 41 | 42 | return
    43 | set_left_open(!left_open)}> 44 | setDoc(d)}/> 45 | 46 | {children} 47 | set_right_open(!right_open)}> 48 | 49 | 50 |
    51 | } 52 | 53 | function App() { 54 | const [doc, setDoc] = useState(realdoc) 55 | let entries = doc.map((entry,i) => update_doc(doc,entry,code)} 58 | scope={scope}/>) 59 | const add_entry = () => { 60 | let new_doc = doc.slice() 61 | new_doc.push({ 62 | type:'filament', 63 | title:'title', 64 | input:'', 65 | output:null 66 | }) 67 | setDoc(new_doc) 68 | } 69 | return 70 |
    71 | Filament: 72 | tutorial 73 | intro 74 | spec 75 | api 76 |
    77 |
    {entries} 78 | 79 |
    80 |
    81 | } 82 | 83 | export default App 84 | -------------------------------------------------------------------------------- /src/editor/view.tsx: -------------------------------------------------------------------------------- 1 | import {useEffect, useRef, useState} from 'react' 2 | // import {default as grammar_url} from 'filament-lang/src/filament.ohm.js' 3 | import {eval_code, setup_parser} from 'filament-lang' 4 | import * as codemirror from 'codemirror' 5 | import {EditorFromTextArea} from 'codemirror' 6 | import "codemirror/addon/mode/simple.js" 7 | import "codemirror/addon/hint/show-hint.js" 8 | import "codemirror/addon/hint/show-hint.css" 9 | import {ResultArea} from '../examples/views' 10 | import {synonyms} from './editor' 11 | 12 | type Entry = { 13 | title: string; 14 | input: string; 15 | type:string, 16 | output:any, 17 | } 18 | 19 | type ScopeType = { 20 | 21 | } 22 | 23 | type EdUpdate = ((instance:codemirror.Editor) => void) 24 | 25 | type OnChangeCB = ((code:string) => void) 26 | 27 | 28 | type ViewProps = { 29 | entry:Entry, 30 | onChange:OnChangeCB 31 | scope:ScopeType, 32 | } 33 | 34 | export function View({entry, onChange, scope}:ViewProps) { 35 | const ref = useRef(null) 36 | const [editor, setEditor] = useState(null) 37 | const [result, setResult] = useState(null) 38 | const [title, setTitle] = useState("") 39 | const onEval = async (code:string):Promise => { 40 | try { 41 | // let grammar = await fetch(grammar_url).then(r => r.text()) 42 | // console.log("got the grammar", grammar) 43 | await setup_parser() 44 | code = "{" + code + "}" 45 | let d = await eval_code(code,scope) 46 | console.log("result is ", d) 47 | setResult(d) 48 | // console.log("done") 49 | } catch (e:any) { 50 | console.log("an error happened", e) 51 | setResult(e) 52 | } 53 | } 54 | useEffect(() => { 55 | if (ref.current && editor === null) { 56 | const trigger_update:EdUpdate = () => onEval(ed.getValue()) 57 | let ed:EditorFromTextArea = codemirror.fromTextArea(ref.current, { 58 | value: 'some cool text', 59 | lineNumbers: true, 60 | viewportMargin: Infinity, 61 | mode:'filament', 62 | // @ts-ignore 63 | hintOptions: {hint: synonyms, completeSingle: false, scope:scope}, 64 | lineWrapping: true, 65 | matchBrackets: true, 66 | autoCloseBrackets: true, 67 | extraKeys: { 68 | "Ctrl-Space": "autocomplete", 69 | "Ctrl-Enter": trigger_update, 70 | }, 71 | }) 72 | setEditor(ed) 73 | ed.setValue(entry.input) 74 | setTitle(entry.title) 75 | ed.on('changes', () => onChange(ed.getValue())) 76 | } 77 | if (ref.current && editor !== null) { 78 | editor.setValue(entry.input) 79 | setTitle(entry.title) 80 | setResult(entry.output) 81 | } 82 | }, [entry]) 83 | 84 | return
    85 |

    {title}

    86 |