Skip to content

Commit

Permalink
Merge branch 'main' into sampling-output-take-3
Browse files Browse the repository at this point in the history
  • Loading branch information
magland committed Jun 14, 2024
2 parents 6f6a7e2 + 25a41e5 commit fd21058
Show file tree
Hide file tree
Showing 14 changed files with 25,964 additions and 284 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: test stan-playground

on:
pull_request:
branches: ['main']
workflow_dispatch:

permissions:
contents: read

concurrency:
group: 'tests'
cancel-in-progress: true

jobs:
frontend-tests:
name: yarn tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'yarn'
cache-dependency-path: gui/yarn.lock
- name: Install dependencies
run: cd gui; yarn
- name: Test
run: cd gui; yarn test
1 change: 0 additions & 1 deletion gui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/stan-playground-logo.png" />
<!-- <link rel="stylesheet" href="/src/styles.css" /> -->
<script src="https://github.com/stan-dev/stanc3/releases/download/nightly/stanc.js"></script>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 4 additions & 0 deletions gui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
"tinystan": "^0.0.2"
},
"devDependencies": {
"@testing-library/dom": "^10.1.0",
"@testing-library/react": "^16.0.0",
"@types/node": "^20.12.11",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
Expand All @@ -45,10 +47,12 @@
"@typescript-eslint/parser": "^6.0.0",
"@vitejs/plugin-react": "^4.0.3",
"@vitest/coverage-v8": "^1.6.0",
"@vitest/web-worker": "^1.6.0",
"eslint": "^8.45.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"jsdom": "^24.1.0",
"typescript": "^5.0.2",
"vite": "^5.2.12",
"vitest": "^1.6.0"
Expand Down
53 changes: 13 additions & 40 deletions gui/src/app/FileEditor/StanCompileResultWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,33 @@
import { Done } from "@mui/icons-material";
import { FunctionComponent, useEffect, useState } from "react";
import runStanc from "./runStanc";
import { FunctionComponent } from "react";
import { StancErrors } from "../Stanc/Types";

type Props = {
width: number
height: number
mainStanText: string | undefined
onValidityChanged?: (valid: boolean) => void
stancErrors: StancErrors,
}

type CompiledModel = {
errors?: string[]
warnings?: string[]
result: string
}

const StanCompileResultWindow: FunctionComponent<Props> = ({width, height, mainStanText, onValidityChanged}) => {
const [model, setModel] = useState<CompiledModel | undefined>(undefined)
useEffect(() => {
setModel(undefined)
if (mainStanText === undefined) return
;(async () => {
const m = await runStanc('main.stan', mainStanText, ["auto-format", "max-line-length=78"])
setModel(m)
})()
}, [mainStanText])

useEffect(() => {
if (!model) {
onValidityChanged && onValidityChanged(false)
return
}
onValidityChanged && onValidityChanged(model.errors === undefined)
}, [model, onValidityChanged])
const StanCompileResultWindow: FunctionComponent<Props> = ({ width, height, stancErrors }) => {

if (!model) return <div />
if ((model.errors) && (model.errors.length > 0)) {
if ((stancErrors.errors) && (stancErrors.errors.length > 0)) {
return (
<div style={{width, height, color: 'red', padding: 0, overflow: 'auto'}}>
<div style={{ width, height, color: 'red', padding: 0, overflow: 'auto' }}>
<h3>Errors</h3>
{model.errors.map((error, i) => <div key={i} style={{font: 'courier', fontSize: 13}}><pre>{error}</pre></div>)}
{stancErrors.errors.slice(1).map((error, i) => <div key={i} style={{ font: 'courier', fontSize: 13 }}><pre>{error}</pre></div>)}
</div>
)
}
if ((model.warnings) && (model.warnings.length > 0)) {
if ((stancErrors.warnings) && (stancErrors.warnings.length > 0)) {
return (
<div style={{width, height, color: 'blue', padding: 0, overflow: 'auto'}}>
<div style={{ width, height, color: 'blue', padding: 0, overflow: 'auto' }}>
<h3>Warnings</h3>
{model.warnings.map((warning, i) => <div key={i} style={{font: 'courier', fontSize: 13}}><pre>{warning}</pre></div>)}
{stancErrors.warnings.map((warning, i) => <div key={i} style={{ font: 'courier', fontSize: 13 }}><pre>{warning}</pre></div>)}
</div>
)
}
if (model.result === mainStanText) {
return (<div style={{color: 'green'}}><Done /> canonical format</div>)
}
return (<div style={{color: 'green'}}><Done /></div>)

return (<div style={{ color: 'green' }}><Done /></div>)
}

export default StanCompileResultWindow
export default StanCompileResultWindow
25 changes: 10 additions & 15 deletions gui/src/app/FileEditor/StanFileEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Splitter } from '@fi-sci/splitter';
import { AutoFixHigh, Settings, } from "@mui/icons-material";
import { FunctionComponent, useCallback, useEffect, useMemo, useState } from "react";
import StanCompileResultWindow from "./StanCompileResultWindow";
import useStanc from "../Stanc/useStanc";
import TextEditor, { ToolbarItem } from "./TextEditor";
import runStanc from "./runStanc";
import compileStanProgram from '../compileStanProgram/compileStanProgram';

type Props = {
Expand All @@ -22,16 +22,12 @@ type Props = {
type CompileStatus = 'preparing' | 'compiling' | 'compiled' | 'failed' | ''

const StanFileEditor: FunctionComponent<Props> = ({ fileName, fileContent, onSaveContent, editedFileContent, setEditedFileContent, readOnly, width, height, setCompiledUrl }) => {
const [validSyntax, setValidSyntax] = useState<boolean>(false)
const handleAutoFormat = useCallback(() => {
if (editedFileContent === undefined) return
; (async () => {
const model = await runStanc('main.stan', editedFileContent, ["auto-format", "max-line-length=78"])
if (model.result) {
setEditedFileContent(model.result)
}
})()
}, [editedFileContent, setEditedFileContent])

const { stancErrors, requestFormat } = useStanc("main.stan", editedFileContent, setEditedFileContent);

const validSyntax = useMemo(() => {
return stancErrors.errors === undefined
}, [stancErrors]);

const [compileStatus, setCompileStatus] = useState<CompileStatus>('')
const [theStanFileContentThasHasBeenCompiled, setTheStanFileContentThasHasBeenCompiled] = useState<string>('')
Expand Down Expand Up @@ -104,7 +100,7 @@ const StanFileEditor: FunctionComponent<Props> = ({ fileName, fileContent, onSav
icon: <AutoFixHigh />,
tooltip: 'Auto format this stan file',
label: 'auto format',
onClick: handleAutoFormat,
onClick: requestFormat,
color: 'darkblue'
})
}
Expand Down Expand Up @@ -132,7 +128,7 @@ const StanFileEditor: FunctionComponent<Props> = ({ fileName, fileContent, onSav
}

return ret
}, [editedFileContent, fileContent, handleAutoFormat, handleCompile, compileStatus, compileMessage, validSyntax, readOnly])
}, [editedFileContent, fileContent, requestFormat, handleCompile, compileStatus, compileMessage, validSyntax, readOnly])

const isCompiling = compileStatus === 'compiling'

Expand Down Expand Up @@ -162,8 +158,7 @@ const StanFileEditor: FunctionComponent<Props> = ({ fileName, fileContent, onSav
editedFileContent ? <StanCompileResultWindow
width={0}
height={0}
mainStanText={editedFileContent}
onValidityChanged={valid => setValidSyntax(valid)}
stancErrors={stancErrors}
/> : (
<div style={{ padding: 20 }}>Select an example from the left panel</div>
)
Expand Down
14 changes: 0 additions & 14 deletions gui/src/app/FileEditor/runStanc.ts

This file was deleted.

18 changes: 9 additions & 9 deletions gui/src/app/StanSampler/StanModelWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ const progressPrintCallback = (msg: string) => {
return;
}
const report = parseProgress(msg);
postMessage({ purpose: Replies.Progress, report })
self.postMessage({ purpose: Replies.Progress, report })
}


let model: StanModel;

onmessage = function (e) {
self.onmessage = (e) => {
const purpose: Requests = e.data.purpose;

switch (purpose) {
Expand All @@ -63,35 +63,35 @@ onmessage = function (e) {
m => {
model = m;
console.log("Web Worker loaded Stan model built from version " + m.stanVersion());
postMessage({ purpose: Replies.ModelLoaded });
self.postMessage({ purpose: Replies.ModelLoaded });
});
break;
}
case Requests.Sample: {
if (!model) {
postMessage({ purpose: Replies.StanReturn, error: "Model not loaded yet!" })
self.postMessage({ purpose: Replies.StanReturn, error: "Model not loaded yet!" })
return;
}
try {
const { paramNames, draws } = model.sample(e.data.sampleConfig);
// TODO? use an ArrayBuffer so we can transfer without serialization cost
postMessage({ purpose: Replies.StanReturn, draws, paramNames, error: null });
self.postMessage({ purpose: Replies.StanReturn, draws, paramNames, error: null });
} catch (e: any) {
postMessage({ purpose: Replies.StanReturn, error: e.toString() })
self.postMessage({ purpose: Replies.StanReturn, error: e.toString() })
}
break;
}
case Requests.Pathfinder: {
if (!model) {
postMessage({ purpose: Replies.StanReturn, error: "Model not loaded yet!" })
self.postMessage({ purpose: Replies.StanReturn, error: "Model not loaded yet!" })
return;
}
try {
const { draws, paramNames } = model.pathfinder(e.data.pathfinderConfig);
// TODO? use an ArrayBuffer so we can transfer without serialization cost
postMessage({ purpose: Replies.StanReturn, draws, paramNames, error: null });
self.postMessage({ purpose: Replies.StanReturn, draws, paramNames, error: null });
} catch (e: any) {
postMessage({ purpose: Replies.StanReturn, error: e.toString() })
self.postMessage({ purpose: Replies.StanReturn, error: e.toString() })
}
break;
}
Expand Down
25 changes: 25 additions & 0 deletions gui/src/app/Stanc/Types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export type StancErrors = {
errors?: string[];
warnings?: string[];
};

type StancReturn = { result?: string } & StancErrors;

export type StancFunction = (
name: string,
code: string,
args: string[],
) => StancReturn;

export type StancReplyMessage = { fatal: string } | StancReturn;

export enum StancWorkerRequests {
FormatStanCode = "format",
CheckSyntax = "check",
}

export type StancRequestMessage = {
purpose: StancWorkerRequests;
name: string;
code: string;
};
Loading

0 comments on commit fd21058

Please sign in to comment.