├── favicon ├── favicon.ico ├── favicon-16x16.png ├── favicon-32x32.png ├── mstile-70x70.png ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── apple-touch-icon.png ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── browserconfig.xml ├── manifest.json └── safari-pinned-tab.svg ├── fira ├── eot │ ├── FiraSans-Two.eot │ ├── FiraMono-Bold.eot │ ├── FiraSans-Bold.eot │ ├── FiraSans-Book.eot │ ├── FiraSans-Eight.eot │ ├── FiraSans-Four.eot │ ├── FiraSans-Hair.eot │ ├── FiraSans-Heavy.eot │ ├── FiraSans-Light.eot │ ├── FiraSans-Thin.eot │ ├── FiraSans-Ultra.eot │ ├── FiraMono-Medium.eot │ ├── FiraMono-Regular.eot │ ├── FiraSans-Italic.eot │ ├── FiraSans-Medium.eot │ ├── FiraSans-Regular.eot │ ├── FiraSans-SemiBold.eot │ ├── FiraSans-BoldItalic.eot │ ├── FiraSans-BookItalic.eot │ ├── FiraSans-ExtraBold.eot │ ├── FiraSans-ExtraLight.eot │ ├── FiraSans-FourItalic.eot │ ├── FiraSans-HairItalic.eot │ ├── FiraSans-ThinItalic.eot │ ├── FiraSans-TwoItalic.eot │ ├── FiraSans-UltraLight.eot │ ├── FiraSans-EightItalic.eot │ ├── FiraSans-HeavyItalic.eot │ ├── FiraSans-LightItalic.eot │ ├── FiraSans-MediumItalic.eot │ ├── FiraSans-UltraItalic.eot │ ├── FiraSans-ExtraBoldItalic.eot │ ├── FiraSans-SemiBoldItalic.eot │ ├── FiraSans-ExtraLightItalic.eot │ └── FiraSans-UltraLightItalic.eot ├── ttf │ ├── FiraSans-Two.ttf │ ├── FiraMono-Bold.ttf │ ├── FiraSans-Bold.ttf │ ├── FiraSans-Book.ttf │ ├── FiraSans-Eight.ttf │ ├── FiraSans-Four.ttf │ ├── FiraSans-Hair.ttf │ ├── FiraSans-Heavy.ttf │ ├── FiraSans-Light.ttf │ ├── FiraSans-Thin.ttf │ ├── FiraSans-Ultra.ttf │ ├── FiraMono-Medium.ttf │ ├── FiraMono-Regular.ttf │ ├── FiraSans-Italic.ttf │ ├── FiraSans-Medium.ttf │ ├── FiraSans-Regular.ttf │ ├── FiraSans-SemiBold.ttf │ ├── FiraSans-BoldItalic.ttf │ ├── FiraSans-BookItalic.ttf │ ├── FiraSans-ExtraBold.ttf │ ├── FiraSans-ExtraLight.ttf │ ├── FiraSans-FourItalic.ttf │ ├── FiraSans-HairItalic.ttf │ ├── FiraSans-ThinItalic.ttf │ ├── FiraSans-TwoItalic.ttf │ ├── FiraSans-UltraLight.ttf │ ├── FiraSans-EightItalic.ttf │ ├── FiraSans-HeavyItalic.ttf │ ├── FiraSans-LightItalic.ttf │ ├── FiraSans-MediumItalic.ttf │ ├── FiraSans-UltraItalic.ttf │ ├── FiraSans-ExtraBoldItalic.ttf │ ├── FiraSans-SemiBoldItalic.ttf │ ├── FiraSans-ExtraLightItalic.ttf │ └── FiraSans-UltraLightItalic.ttf ├── woff │ ├── FiraSans-Two.woff │ ├── FiraMono-Bold.woff │ ├── FiraMono-Medium.woff │ ├── FiraSans-Bold.woff │ ├── FiraSans-Book.woff │ ├── FiraSans-Eight.woff │ ├── FiraSans-Four.woff │ ├── FiraSans-Hair.woff │ ├── FiraSans-Heavy.woff │ ├── FiraSans-Italic.woff │ ├── FiraSans-Light.woff │ ├── FiraSans-Medium.woff │ ├── FiraSans-Thin.woff │ ├── FiraSans-Ultra.woff │ ├── FiraMono-Regular.woff │ ├── FiraSans-Regular.woff │ ├── FiraSans-SemiBold.woff │ ├── FiraSans-BoldItalic.woff │ ├── FiraSans-BookItalic.woff │ ├── FiraSans-EightItalic.woff │ ├── FiraSans-ExtraBold.woff │ ├── FiraSans-ExtraLight.woff │ ├── FiraSans-FourItalic.woff │ ├── FiraSans-HairItalic.woff │ ├── FiraSans-HeavyItalic.woff │ ├── FiraSans-LightItalic.woff │ ├── FiraSans-ThinItalic.woff │ ├── FiraSans-TwoItalic.woff │ ├── FiraSans-UltraItalic.woff │ ├── FiraSans-UltraLight.woff │ ├── FiraSans-MediumItalic.woff │ ├── FiraSans-ExtraBoldItalic.woff │ ├── FiraSans-ExtraLightItalic.woff │ ├── FiraSans-SemiBoldItalic.woff │ └── FiraSans-UltraLightItalic.woff ├── woff2 │ ├── FiraMono-Bold.woff2 │ ├── FiraSans-Bold.woff2 │ ├── FiraSans-Book.woff2 │ ├── FiraSans-Four.woff2 │ ├── FiraSans-Hair.woff2 │ ├── FiraSans-Thin.woff2 │ ├── FiraSans-Two.woff2 │ ├── FiraMono-Medium.woff2 │ ├── FiraSans-Eight.woff2 │ ├── FiraSans-Heavy.woff2 │ ├── FiraSans-Italic.woff2 │ ├── FiraSans-Light.woff2 │ ├── FiraSans-Medium.woff2 │ ├── FiraSans-Ultra.woff2 │ ├── FiraMono-Regular.woff2 │ ├── FiraSans-ExtraBold.woff2 │ ├── FiraSans-Regular.woff2 │ ├── FiraSans-SemiBold.woff2 │ ├── FiraSans-TwoItalic.woff2 │ ├── FiraSans-BoldItalic.woff2 │ ├── FiraSans-BookItalic.woff2 │ ├── FiraSans-EightItalic.woff2 │ ├── FiraSans-ExtraLight.woff2 │ ├── FiraSans-FourItalic.woff2 │ ├── FiraSans-HairItalic.woff2 │ ├── FiraSans-HeavyItalic.woff2 │ ├── FiraSans-LightItalic.woff2 │ ├── FiraSans-ThinItalic.woff2 │ ├── FiraSans-UltraItalic.woff2 │ ├── FiraSans-UltraLight.woff2 │ ├── FiraSans-MediumItalic.woff2 │ ├── FiraSans-SemiBoldItalic.woff2 │ ├── FiraSans-ExtraBoldItalic.woff2 │ ├── FiraSans-ExtraLightItalic.woff2 │ └── FiraSans-UltraLightItalic.woff2 ├── LICENSE └── fira.css ├── setup.js ├── lang ├── BlockClosure.js ├── Method.js ├── SourceLoc.js ├── Interpreter.js ├── Obj.js ├── builtins.js ├── Class.js ├── syntaxHighlight.js ├── instructions.js ├── grammar.js ├── asts.js ├── parse.js ├── prelude.js └── activations.js ├── seymour.css ├── syntaxHighlight.css ├── README.md ├── lib.js ├── viz ├── macroViz.css ├── Env.js ├── MicroVizEvents.js ├── macroViz.js ├── EventRecorder.js ├── microViz.css ├── events.js └── microViz.js ├── LICENSE ├── pathmatcher.js ├── style.css ├── notes ├── index.js ├── index.html ├── highlighting.css ├── seymour.js ├── 3rdparty └── codemirror.css └── highlighting.js /favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/favicon.ico -------------------------------------------------------------------------------- /favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /favicon/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/mstile-70x70.png -------------------------------------------------------------------------------- /fira/eot/FiraSans-Two.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Two.eot -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Two.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Two.ttf -------------------------------------------------------------------------------- /favicon/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/mstile-144x144.png -------------------------------------------------------------------------------- /favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /favicon/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/mstile-310x150.png -------------------------------------------------------------------------------- /favicon/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/mstile-310x310.png -------------------------------------------------------------------------------- /fira/eot/FiraMono-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraMono-Bold.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Bold.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Book.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Book.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Eight.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Eight.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Four.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Four.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Hair.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Hair.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Heavy.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Heavy.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Light.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Thin.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Ultra.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Ultra.eot -------------------------------------------------------------------------------- /fira/ttf/FiraMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraMono-Bold.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Bold.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Book.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Book.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Eight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Eight.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Four.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Four.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Hair.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Hair.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Heavy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Heavy.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Light.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Thin.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Ultra.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Ultra.ttf -------------------------------------------------------------------------------- /fira/woff/FiraSans-Two.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Two.woff -------------------------------------------------------------------------------- /favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /fira/eot/FiraMono-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraMono-Medium.eot -------------------------------------------------------------------------------- /fira/eot/FiraMono-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraMono-Regular.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Italic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Medium.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-Regular.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-SemiBold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-SemiBold.eot -------------------------------------------------------------------------------- /fira/ttf/FiraMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraMono-Medium.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraMono-Regular.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Italic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Medium.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-SemiBold.ttf -------------------------------------------------------------------------------- /fira/woff/FiraMono-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraMono-Bold.woff -------------------------------------------------------------------------------- /fira/woff/FiraMono-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraMono-Medium.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Bold.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Book.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Book.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Eight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Eight.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Four.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Four.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Hair.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Hair.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Heavy.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Heavy.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Italic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Light.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Medium.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Thin.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Ultra.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Ultra.woff -------------------------------------------------------------------------------- /fira/woff2/FiraMono-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraMono-Bold.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Bold.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Book.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Book.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Four.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Four.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Hair.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Hair.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Thin.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Two.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Two.woff2 -------------------------------------------------------------------------------- /fira/eot/FiraSans-BoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-BoldItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-BookItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-BookItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-ExtraBold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-ExtraBold.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-ExtraLight.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-ExtraLight.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-FourItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-FourItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-HairItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-HairItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-ThinItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-ThinItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-TwoItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-TwoItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-UltraLight.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-UltraLight.eot -------------------------------------------------------------------------------- /fira/ttf/FiraSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-BoldItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-BookItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-BookItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-ExtraBold.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-ExtraLight.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-FourItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-FourItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-HairItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-HairItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-ThinItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-TwoItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-TwoItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-UltraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-UltraLight.ttf -------------------------------------------------------------------------------- /fira/woff/FiraMono-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraMono-Regular.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-Regular.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-SemiBold.woff -------------------------------------------------------------------------------- /fira/woff2/FiraMono-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraMono-Medium.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Eight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Eight.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Heavy.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Heavy.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Italic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Light.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Medium.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Ultra.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Ultra.woff2 -------------------------------------------------------------------------------- /favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /fira/eot/FiraSans-EightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-EightItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-HeavyItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-HeavyItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-LightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-LightItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-MediumItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-MediumItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-UltraItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-UltraItalic.eot -------------------------------------------------------------------------------- /fira/ttf/FiraSans-EightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-EightItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-HeavyItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-HeavyItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-LightItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-MediumItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-UltraItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-UltraItalic.ttf -------------------------------------------------------------------------------- /fira/woff/FiraSans-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-BoldItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-BookItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-BookItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-EightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-EightItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-ExtraBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-ExtraBold.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-ExtraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-ExtraLight.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-FourItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-FourItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-HairItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-HairItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-HeavyItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-HeavyItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-LightItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-ThinItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-ThinItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-TwoItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-TwoItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-UltraItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-UltraItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-UltraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-UltraLight.woff -------------------------------------------------------------------------------- /fira/woff2/FiraMono-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraMono-Regular.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-ExtraBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-ExtraBold.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-Regular.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-SemiBold.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-TwoItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-TwoItalic.woff2 -------------------------------------------------------------------------------- /fira/eot/FiraSans-ExtraBoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-ExtraBoldItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-SemiBoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-SemiBoldItalic.eot -------------------------------------------------------------------------------- /fira/ttf/FiraSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /fira/woff/FiraSans-MediumItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-MediumItalic.woff -------------------------------------------------------------------------------- /fira/woff2/FiraSans-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-BoldItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-BookItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-BookItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-EightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-EightItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-ExtraLight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-ExtraLight.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-FourItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-FourItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-HairItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-HairItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-HeavyItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-HeavyItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-LightItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-ThinItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-ThinItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-UltraItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-UltraItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-UltraLight.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-UltraLight.woff2 -------------------------------------------------------------------------------- /fira/eot/FiraSans-ExtraLightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-ExtraLightItalic.eot -------------------------------------------------------------------------------- /fira/eot/FiraSans-UltraLightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/eot/FiraSans-UltraLightItalic.eot -------------------------------------------------------------------------------- /fira/ttf/FiraSans-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /fira/ttf/FiraSans-UltraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/ttf/FiraSans-UltraLightItalic.ttf -------------------------------------------------------------------------------- /fira/woff/FiraSans-ExtraBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-ExtraBoldItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-ExtraLightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-ExtraLightItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-SemiBoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-SemiBoldItalic.woff -------------------------------------------------------------------------------- /fira/woff/FiraSans-UltraLightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff/FiraSans-UltraLightItalic.woff -------------------------------------------------------------------------------- /fira/woff2/FiraSans-MediumItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-MediumItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-SemiBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-SemiBoldItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-ExtraBoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-ExtraBoldItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-ExtraLightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-ExtraLightItalic.woff2 -------------------------------------------------------------------------------- /fira/woff2/FiraSans-UltraLightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harc/seymour/HEAD/fira/woff2/FiraSans-UltraLightItalic.woff2 -------------------------------------------------------------------------------- /setup.js: -------------------------------------------------------------------------------- 1 | // APPLICATION STATE. THIS STUFF IS GLOBAL 2 | 3 | window.microViz = new MicroViz(microVizContainer); 4 | window.editor = microViz.editor; 5 | editor.setOption('lineNumbers', true); 6 | 7 | window.macroViz = new MacroViz(macroVizContainer); 8 | 9 | window.pathMatchers = null; -------------------------------------------------------------------------------- /lang/BlockClosure.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class BlockClosure extends Obj { 4 | constructor(sourceLoc, formals, code, parent) { 5 | super(null); 6 | this.sourceLoc = sourceLoc; 7 | this.formals = formals; 8 | this.code = code; 9 | this.parent = parent; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #2b5797 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lang/Method.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Method { 4 | constructor(sourceLoc, _class, selector, className, formals, code) { 5 | this.sourceLoc = sourceLoc; 6 | this.class = _class; 7 | this.selector = selector; 8 | this.className = className; 9 | this.formals = formals; 10 | this.code = code; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /seymour.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | font-family: inherit; 3 | } 4 | 5 | parseError { 6 | display: block; 7 | white-space: pre-wrap; 8 | color: maroon; 9 | background: yellow; 10 | padding: 0 4px; 11 | padding: 4px; 12 | opacity: 1; 13 | max-height: 100pt; 14 | 15 | -webkit-user-select: none; 16 | -moz-user-select: none; 17 | user-select: none; 18 | } -------------------------------------------------------------------------------- /favicon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "icons": [ 4 | { 5 | "src": "favicon/android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "favicon/android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#ffffff", 16 | "background_color": "#ffffff", 17 | "display": "standalone" 18 | } -------------------------------------------------------------------------------- /syntaxHighlight.css: -------------------------------------------------------------------------------- 1 | .sh_keyword { 2 | color: #5F136F; 3 | } 4 | 5 | .sh_number, 6 | .sh_string, 7 | .sh_true, 8 | .sh_false, 9 | .sh_null { 10 | color: #2F19AB; 11 | } 12 | 13 | .sh_varName, 14 | .sh_className { 15 | color: #333; 16 | } 17 | 18 | .sh_className { 19 | font-weight: bold; 20 | } 21 | 22 | .sh_binSelector { 23 | color: #0B1010; 24 | } 25 | 26 | .sh_selector { 27 | color: #2F1988; 28 | } 29 | 30 | .sh_instVarName { 31 | color: maroon; 32 | } 33 | 34 | .sh_comment { 35 | color: #638b61; 36 | } 37 | -------------------------------------------------------------------------------- /lang/SourceLoc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class SourceLoc { 4 | constructor(startPos, endPos, startLineNumber, endLineNumber) { 5 | this.startPos = startPos; 6 | this.endPos = endPos; 7 | this.startLineNumber = startLineNumber; 8 | this.endLineNumber = endLineNumber; 9 | } 10 | 11 | equals(sourceLoc) { 12 | return this.startPos === sourceLoc.startPos && this.endPos === sourceLoc.endPos; 13 | } 14 | 15 | contains(sourceLoc) { 16 | return this.startPos <= sourceLoc.startPos && sourceLoc.endPos <= this.endPos; 17 | } 18 | 19 | strictlyContains(sourceLoc) { 20 | return this.contains(sourceLoc) && !this.equals(sourceLoc); 21 | } 22 | 23 | containsIdx(pos) { 24 | return this.startPos <= pos && pos < this.endPos; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Seymour 2 | ======= 3 | 4 | Seymour is a live programming environment that visualizes program execution as the user types. It features a micro visualization that shows details of the program’s execution, and a macro visualization that puts the micro visualization in context, letting the user focus on different parts of the program execution. These visualizations come together to give the user a helpful live programming experience. 5 | 6 | We are excited about the prospect of using this environment in the classroom and learning from student feedback. In doing so, we hope to make Seymour a valuable learning aid for students, and ultimately, create a better user experience for all programmers. 7 | 8 | For more details, see our LIVE '17 paper: 9 | https://harc.github.io/seymour-live2017/ 10 | 11 | -- Alex and Saketh 12 | -------------------------------------------------------------------------------- /lib.js: -------------------------------------------------------------------------------- 1 | function range(from, to) { 2 | const ans = []; 3 | for (let x = from; x <= to; x++) { 4 | ans.push(x); 5 | } 6 | return ans; 7 | } 8 | 9 | function d(elementType, attributes, ...children) { 10 | const node = document.createElement(elementType); 11 | if (attributes == null && children.length === 0) { 12 | return node; 13 | } 14 | 15 | Object.keys(attributes).forEach(name => node.setAttribute(name, attributes[name])); 16 | for (let child of children) { 17 | node.appendChild(typeof child === 'string' ? document.createTextNode(child) : child); 18 | } 19 | return node; 20 | } 21 | 22 | function spaces(n) { 23 | let str = ''; 24 | while (n-- > 0) { 25 | str += ' '; 26 | } 27 | return str; 28 | } 29 | 30 | function flatten(arrs) { 31 | return [].concat.apply([], arrs); 32 | } 33 | -------------------------------------------------------------------------------- /lang/Interpreter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Interpreter { 4 | constructor(sourceLoc, code, R) { 5 | this.R = R; 6 | this.global = new TopLevelActivation(sourceLoc, code, R); 7 | this.currentActivation = this.global; 8 | } 9 | 10 | step() { 11 | this.currentActivation = this.currentActivation.step(); 12 | return this.currentActivation === null; 13 | } 14 | 15 | runForMillis(timeLimit) { 16 | const t0 = performance.now(); 17 | while (true) { 18 | let done; 19 | // try { 20 | done = this.step(); 21 | // } catch(e) { 22 | // console.error('system error', e); 23 | // return true; 24 | // } 25 | if (done) { 26 | return true; 27 | } 28 | if (performance.now() - t0 >= timeLimit) { 29 | return false; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lang/Obj.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Obj { 4 | constructor(_class) { 5 | this.id = Obj.nextId++; 6 | this.class = _class; 7 | this.instVars = Object.create(null); 8 | if (this.class) { 9 | this.class.instVarNames.forEach(name => this.instVars[name] = null); 10 | } 11 | } 12 | 13 | lookup(selector) { 14 | return this.class.getMethod(selector); 15 | } 16 | 17 | setInstVar(name, value) { 18 | this.assertIsInstVarName(name); 19 | this.instVars[name] = value; 20 | } 21 | 22 | getInstVar(name) { 23 | this.assertIsInstVarName(name); 24 | return this.instVars[name]; 25 | } 26 | 27 | assertIsInstVarName(name) { 28 | if (!Object.prototype.hasOwnProperty.call(this.instVars, name)) { 29 | throw new Error(name + ' is not an instance variable'); 30 | } 31 | } 32 | 33 | toString() { 34 | return this.class.name + '@' + this.id; 35 | } 36 | } 37 | 38 | Obj.nextId = 0; 39 | -------------------------------------------------------------------------------- /viz/macroViz.css: -------------------------------------------------------------------------------- 1 | .macroViz { 2 | white-space: nowrap; 3 | padding-bottom: 5px; 4 | } 5 | 6 | .macroViz macroViz { 7 | display: block; 8 | } 9 | 10 | .macroViz macroVizNode { 11 | display: inline-block; 12 | white-space: nowrap; 13 | vertical-align: top; 14 | } 15 | 16 | .macroViz macroVizNode.collapsed > label { 17 | padding: 0.5px; 18 | box-shadow: 1px 1px 0px hsla(0, 0%, 0%, 0.2); 19 | } 20 | 21 | .macroViz macroVizNode > label { 22 | display: block; 23 | color: #555; 24 | box-shadow: 1px 1px 0px hsl(0, 0%, 0%); 25 | padding: 4px; 26 | background: hsl(0, 0%, 90%); 27 | margin: 0; 28 | margin-left: 1px; 29 | margin-right: 1px; 30 | } 31 | 32 | .macroViz macroVizNode.error > label { 33 | background: HSL(352, 52%, 59%); 34 | } 35 | 36 | .macroViz macroVizNode[isFocusable="false"] > label { 37 | display: none; 38 | background: hsl(0, 0%, 97%); 39 | } 40 | 41 | .macroViz macroVizNode > label > text { 42 | display: none; 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 HARC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lang/builtins.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | TopLevelActivation.prototype.installBuiltins = function() { 4 | const declClass = (name, superClass, instVarNames) => { 5 | const _class = new Class(name, superClass, instVarNames); 6 | this.declVar(null, name, _class); 7 | return _class; 8 | } 9 | 10 | const _Object = declClass('Object', null, []); 11 | 12 | const Block = declClass('Block', _Object, []); 13 | { 14 | let loop, callCond, callBody; 15 | Block.declMethod(null, 'while_do:', new Ident(null, 'Block'), [new Ident(null, 'body')], 16 | new IPush(0, 17 | new IDeclVar('activationPathToken', null, 18 | loop = new IPushThis( 19 | new IPrim( 20 | function() { callCond.operands[3] = this.varValues.activationPathToken++; }, 21 | null, 22 | callCond = new ISend('call', 0, null, undefined, 23 | new ICond( 24 | new IPushFromVar('body', 25 | new IPrim( 26 | function() { callBody.operands[3] = this.varValues.activationPathToken++; }, 27 | null, 28 | callBody = new ISend('call', 0, null, undefined, null))), 29 | new IPush(null, new INonLocalReturn(null))))))))); 30 | callBody.operands[4] = loop; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /lang/Class.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Class extends Obj { 4 | constructor(name, superClass = null, instVarNames = new Set()) { 5 | super(null); 6 | this.name = name; 7 | this.superClass = superClass; 8 | if (superClass === null) { 9 | this.instVarNames = new Set(); 10 | this.methods = Object.create(null); 11 | } else { 12 | this.instVarNames = new Set(superClass.instVarNames); 13 | this.methods = Object.create(superClass.methods); 14 | } 15 | instVarNames.forEach(name => { 16 | if (this.instVarNames.has(name)) { 17 | throw new Error('duplicate instance variable ' + name + ' in class ' + this.name); 18 | } 19 | this.instVarNames.add(name); 20 | }); 21 | } 22 | 23 | makeNewInstance() { 24 | return new Obj(this); 25 | } 26 | 27 | declMethod(sourceLoc, selector, className, formals, code) { 28 | if (Object.prototype.hasOwnProperty.call(this.methods, selector)) { 29 | throw new Error('duplicate declaration of method ' + selector + ' in class ' + this.name); 30 | } 31 | this.methods[selector] = new Method(sourceLoc, this, selector, className, formals, code); 32 | } 33 | 34 | getMethod(selector) { 35 | const m = this.methods[selector]; 36 | if (m === undefined) { 37 | throw new Error('class ' + this.name + ' does not understand ' + selector); 38 | } 39 | return m; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pathmatcher.js: -------------------------------------------------------------------------------- 1 | class PathMatcher { 2 | constructor(path, env = null) { 3 | this.path = path; 4 | this.idx = env ? path.length : 0; 5 | this.envAtIdx = env; 6 | } 7 | 8 | reset(globalEnv) { 9 | this.idx = 0; 10 | this.envAtIdx = globalEnv; 11 | } 12 | 13 | get env() { 14 | return this.idx === this.path.length ? this.envAtIdx : null; 15 | } 16 | 17 | processEvent(child, parent) { 18 | console.assert(this.idx < this.path.length); 19 | if (this.envAtIdx === parent.activationEnv && 20 | child.activationPathToken === this.path[this.idx]) { 21 | this.idx++; 22 | this.envAtIdx = child.activationEnv; 23 | } 24 | } 25 | } 26 | 27 | function getPathMatchers(activationEnv) { 28 | const pathMatchers = []; 29 | let env = activationEnv; 30 | while (env) { 31 | pathMatchers.unshift(getPathMatcher(env)); 32 | env = env.parentEnv; 33 | } 34 | return pathMatchers; 35 | } 36 | 37 | function getPathMatcher(activationEnv) { 38 | return new PathMatcher(getPath(activationEnv), activationEnv); 39 | } 40 | 41 | function getPath(activationEnv) { 42 | const path = []; 43 | while (true) { 44 | const callerEnv = activationEnv.callerEnv; 45 | if (callerEnv) { 46 | path.push(activationEnv.programOrSendEvent.activationPathToken); 47 | activationEnv = callerEnv.programOrSendEvent.activationEnv; 48 | } else { 49 | break; 50 | } 51 | } 52 | path.reverse(); 53 | return path; 54 | } -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @import url("fira/fira.css"); 2 | 3 | html { 4 | font-size: 13px; 5 | line-height: 1.4rem; 6 | } 7 | 8 | body { 9 | margin: 0; 10 | font-family: "Fira Sans"; 11 | font-size: inherit; 12 | } 13 | 14 | #topHalf { 15 | height: 50vh; 16 | width: 100vw; 17 | overflow-y: auto; 18 | border-bottom: 1px solid #ddd; 19 | font-family: "Fira Mono"; 20 | font-feature-settings: "liga" 0; 21 | } 22 | 23 | .CodeMirror { 24 | font-family: inherit; 25 | } 26 | 27 | #microVizContainer { 28 | padding: 0; 29 | width: 100vw; 30 | } 31 | 32 | #macroVizScroller { 33 | overflow: auto; 34 | height: 100%; 35 | } 36 | 37 | #bottomHalf { 38 | height: 50vh; 39 | position: relative; 40 | overflow: hidden; 41 | } 42 | 43 | #legend { 44 | position: absolute; 45 | bottom: 0px; 46 | left: 0px; 47 | border: 1px solid black; 48 | } 49 | 50 | parseError { 51 | display: block; 52 | white-space: pre-wrap; 53 | color: maroon; 54 | background: yellow; 55 | padding: 0 4px; 56 | padding: 4px; 57 | opacity: 1; 58 | max-height: 100pt; 59 | 60 | -webkit-user-select: none; 61 | -moz-user-select: none; 62 | user-select: none; 63 | } 64 | 65 | /* errors */ 66 | 67 | #errorDiv { 68 | color: hsl(0, 50%, 70%); 69 | padding: 5px; 70 | border-bottom: 1px solid #ddd; 71 | background: hsl(0, 0%, 95%); 72 | cursor: row-resize; 73 | } 74 | 75 | /*#errorDiv:empty { 76 | padding: 0px !important; 77 | }*/ 78 | 79 | /** DEMO CHANGES **/ 80 | 81 | #legend { 82 | display: none; 83 | } 84 | 85 | .macroViz macroVizNode.collapsed > label { 86 | display: none; 87 | } -------------------------------------------------------------------------------- /favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lang/syntaxHighlight.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const syntaxHighlight = (function() { 4 | 5 | const semantics = seymourGrammar.createSemantics(); 6 | let doc; 7 | 8 | function syntaxHighlight(cm, matcher) { 9 | doc = cm.doc; 10 | doc.getAllMarks().forEach(mark => mark.clear()); 11 | const matchResult = matcher.match('tokens'); 12 | semantics(matchResult).syntaxHighlight(); 13 | } 14 | 15 | function mark(startIdx, endIdx, className) { 16 | const startPos = doc.posFromIndex(startIdx); 17 | const endPos = doc.posFromIndex(endIdx); 18 | const marke = doc.markText(startPos, endPos, { className: 'sh_' + className }); 19 | } 20 | 21 | semantics.addOperation('syntaxHighlight()', { 22 | 23 | tokens(children) { 24 | children.syntaxHighlight(); 25 | }, 26 | 27 | token(children) { 28 | if (this.numChildren !== 1) { 29 | throw new Error('token cst nodes should only have one child'); 30 | } 31 | this.child(0).syntaxHighlight(); 32 | }, 33 | 34 | valueToken(t) { 35 | return t.syntaxHighlight(); 36 | }, 37 | 38 | instVarAccess(_dot, _spaces, name) { 39 | mark(name.source.startIdx, name.source.endIdx, 'instVarName'); 40 | }, 41 | 42 | javaStyleSelector(_dot, _spaces1, selector, _spaces2, _open) { 43 | mark(selector.source.startIdx, selector.source.endIdx, 'selector'); 44 | }, 45 | 46 | kwSelectorPrefix(prefix, _spaces, _receiverToken) { 47 | mark(prefix.source.startIdx, prefix.source.endIdx, 'selector'); 48 | }, 49 | 50 | kwSelectorPart(selector, colon) { 51 | mark(selector.source.startIdx, colon.source.endIdx, 'selector'); 52 | }, 53 | 54 | _nonterminal(children) { 55 | if (this.ctorName !== 'any') { 56 | mark(this.source.startIdx, this.source.endIdx, this.ctorName); 57 | } 58 | } 59 | 60 | }); 61 | 62 | return syntaxHighlight; 63 | 64 | })(); 65 | -------------------------------------------------------------------------------- /notes: -------------------------------------------------------------------------------- 1 | Evaluator 2 | * globalActivation : Activation 3 | * currentActivation : Activation 4 | + step() : Boolean 5 | + run() : Void 6 | 7 | Activation 8 | * args : Array 9 | * vars : Map 10 | * parent : Activation 11 | * caller : Activation 12 | * stack : Array 13 | * nextInstruction : Instruction 14 | + methodActivation : MethodActivation 15 | + step() : Activation 16 | + iPush(value : Object) : Activation 17 | + iDrop() : Activation 18 | + iNonLocalReturn(value : Object) : Activation 19 | + iReturn(value : Object) : Activation 20 | + declVar(name : String, value : Object) : Void 21 | + setVar(name : String, value : Object) : Void 22 | + getVar(name : String) : Object 23 | + getDeclActivation(name : String) : Activation 24 | + hasVar(name : String) : Boolean 25 | + classOf(obj : Object) : Class 26 | 27 | MethodActivation <: Activation 28 | * method : Method 29 | * receiver : Object 30 | 31 | BlockActivation <: Activation 32 | * block : Block 33 | + return(value : Object) : Activation 34 | 35 | Method 36 | * formals : Array 37 | * code : Instruction 38 | 39 | Block <: Object 40 | * formals : Array 41 | * code : Instruction 42 | * parent : Activation 43 | 44 | Instruction 45 | + eval(a: Activation) : Activation 46 | // just call the equivalent method in Activation 47 | 48 | PushValue <: Instruction 49 | * next : Instruction 50 | * value : Object 51 | 52 | PushVar <: Instruction 53 | * next : Instruction 54 | * name : String 55 | 56 | PopIntoVar <: Instruction 57 | * next : Instruction 58 | * name : String 59 | 60 | DeclVar <: Instruction 61 | * next : Instruction 62 | * name : String 63 | 64 | Drop <: Instruction 65 | * next : Instruction 66 | 67 | Call <: Instruction 68 | * next : Instruction 69 | * selector : String 70 | * numArgs : Number 71 | 72 | ReturnFromBlock <: Instruction 73 | * next : Instruction 74 | 75 | Cond <: Instruction 76 | * nextIfTrue : Instruction 77 | * nextIfFalse : Instruction 78 | 79 | ------------------------- 80 | 81 | Class 82 | * superClass : Class 83 | * methods : Map 84 | * instVarNames : Set 85 | + newInstance() : Instance 86 | + lookup(selector : String) : Method 87 | 88 | PushThis <: Instruction 89 | * next : Instruction 90 | 91 | PushInstVar <: Instruction 92 | * next : Instruction 93 | * name : String 94 | 95 | PopIntoInstVar <: Instruction 96 | * next : Instruction 97 | * name : String 98 | 99 | MakeNew <: Instruction 100 | * next : Instruction 101 | 102 | ReturnFromMethod <: Instruction 103 | * next : Instruction 104 | -------------------------------------------------------------------------------- /viz/Env.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Env { 4 | constructor(sourceLoc, parentEnv, callerEnv, programOrSendEvent) { 5 | this.id = Env.nextEnvId++; 6 | this.sourceLoc = sourceLoc; 7 | this.parentEnv = parentEnv; 8 | this.callerEnv = callerEnv; 9 | this.programOrSendEvent = programOrSendEvent; 10 | this.currentSendEvent = null; 11 | this.microVizEvents = new MicroVizEvents(programOrSendEvent, true, sourceLoc); 12 | this.programOrSendEventToMicroVizEvents = new Map([[programOrSendEvent, this.microVizEvents]]); 13 | } 14 | 15 | receive(event) { 16 | let env = this; 17 | let visitedDeclEnv = false; 18 | while (env) { 19 | env.maybeAdd(event); 20 | if (event instanceof VarAssignmentEvent && 21 | env === event.declEnv) { 22 | visitedDeclEnv = true; 23 | event.visitedDeclEnv = true; 24 | } 25 | env = env.callerEnv; 26 | } 27 | } 28 | 29 | maybeAdd(event) { 30 | const programOrSendEvent = this.targetProgramOrSendEventFor(event); 31 | if (!programOrSendEvent) { 32 | return; 33 | } 34 | const microVizEvents = this.programOrSendEventToMicroVizEvents.get(programOrSendEvent); 35 | if (event instanceof SendEvent && event.sourceLoc !== null) { 36 | const newMicroVizEvents = new MicroVizEvents(event, false, event.sourceLoc); 37 | this.programOrSendEventToMicroVizEvents.set(event, newMicroVizEvents); 38 | microVizEvents.add(newMicroVizEvents); 39 | } else if (!(event instanceof SendEvent)) { 40 | microVizEvents.add(event); 41 | } 42 | } 43 | 44 | targetProgramOrSendEventFor(event) { 45 | if (event.env === this) { 46 | return this.programOrSendEvent; 47 | } 48 | 49 | let env = event.env; 50 | while (env !== this) { 51 | const sendEvent = env.programOrSendEvent; 52 | if (this.programOrSendEventToMicroVizEvents.has(sendEvent)) { 53 | if (this.shouldOnlyShowWhenLocal(event) && event.sourceLoc !== null) { 54 | return sendEvent.sourceLoc.strictlyContains(event.env.sourceLoc) ? sendEvent : null; 55 | } else { 56 | return sendEvent; 57 | } 58 | } else { 59 | env = env.callerEnv; 60 | } 61 | } 62 | return null; 63 | } 64 | 65 | shouldOnlyShowWhenLocal(event) { 66 | return event instanceof SendEvent || 67 | event instanceof ReturnEvent || 68 | event instanceof VarDeclEvent || 69 | event instanceof ShowEvent || 70 | event instanceof VarAssignmentEvent && event.visitedDeclEnv; 71 | } 72 | 73 | shouldBubbleUp(event) { 74 | if (event instanceof VarAssignmentEvent) { 75 | return this !== event.declEnv; 76 | } else { 77 | return true; 78 | } 79 | } 80 | } 81 | 82 | Env.nextEnvId = 0; 83 | 84 | class Scope extends Env { 85 | constructor(sourceLoc, parentEnv, callerEnv, programOrSendEvent) { 86 | super(sourceLoc, parentEnv, callerEnv, programOrSendEvent); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // TODO: factor out some of this UI logic 4 | 5 | const DEBUG = false; 6 | console.debug = function(...args) { 7 | if (DEBUG) { 8 | console.log(...args); 9 | } 10 | }; 11 | 12 | // ----- 13 | 14 | const S = new Seymour(microVizContainer, macroVizContainer); 15 | 16 | S.addListener('codeChanged', code => { 17 | clearError(); 18 | localStorage.setItem('seymour-program', S.editor.getValue()); 19 | }); 20 | 21 | S.addListener('run', (_, __) => toggleRunning(true)); 22 | 23 | S.addListener('error', e => displayError(e.toString())); 24 | 25 | S.addListener('done', (_, __) => toggleRunning(false)); 26 | 27 | 28 | let running = false; 29 | function toggleRunning(optFlag = null) { 30 | if (optFlag !== null) { 31 | running = optFlag; 32 | } else { 33 | running = !running; 34 | } 35 | workingIndicator.style.color = running ? 'red' : 'black'; 36 | } 37 | 38 | // ERRORS 39 | 40 | function clearError() { 41 | errorDiv.innerHTML = ''; 42 | } 43 | 44 | function displayError(message) { 45 | errorDiv.innerHTML = ''; 46 | errorDiv.innerText = message; 47 | } 48 | 49 | 50 | // Local storage keys 51 | const LS_KEY_PREFIX = 'app17392_'; 52 | const LS_editorsShareOfUsableHeight = LS_KEY_PREFIX + 'editorsShareOfUsableHeight'; 53 | 54 | // RESIZE LOGIC 55 | let editorsShareOfUsableHeight = localStorage.getItem(LS_editorsShareOfUsableHeight) || 0.5; 56 | 57 | fixHeights(); 58 | 59 | $(errorDiv).mousedown(e => { 60 | $(errorDiv).data('lastY', e.clientY); 61 | $('body').mousemove(bodyMouseMoveHandler); 62 | $('body').mouseup(bodyMouseUpHandler); 63 | 64 | function bodyMouseUpHandler(e) { 65 | $(errorDiv).data('lastY', null); 66 | $('body').off('mousemove', bodyMouseMoveHandler); 67 | $('body').off('mouseup', bodyMouseUpHandler); 68 | } 69 | function bodyMouseMoveHandler(e) { 70 | const lastY = $(errorDiv).data('lastY'); 71 | if (!lastY) { 72 | return; 73 | } 74 | e.preventDefault(); 75 | const delta = e.clientY - lastY; 76 | const newEditorHeight = $(topHalf).outerHeight() + delta; 77 | const usableHeight = $(window).innerHeight() - $(errorDiv).outerHeight(true); 78 | editorsShareOfUsableHeight = newEditorHeight / usableHeight; 79 | localStorage.setItem(LS_editorsShareOfUsableHeight, editorsShareOfUsableHeight); 80 | $(errorDiv).data('lastY', e.clientY); 81 | fixHeights(); 82 | } 83 | }); 84 | 85 | function fixHeights() { 86 | const usableHeight = $(window).innerHeight() - $(errorDiv).outerHeight(true); 87 | $(topHalf).outerHeight(usableHeight * editorsShareOfUsableHeight); 88 | $(bottomHalf).outerHeight(usableHeight - $(topHalf).outerHeight()); 89 | } 90 | 91 | 92 | 93 | const sampleProgram = `"hello world".println(); 94 | 95 | (3 + 4).println(); 96 | 97 | var sum = 0; 98 | for 1 to: 10 do: {x | 99 | sum = sum + x; 100 | }; 101 | sum.println();`; 102 | S.editor.setValue(localStorage.getItem('seymour-program') || sampleProgram); 103 | -------------------------------------------------------------------------------- /viz/MicroVizEvents.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class MicroVizEvents extends CheckedEmitter { 4 | constructor(programOrSendEvent, isImplementation, sourceLoc) { 5 | super(); 6 | this.registerEvent('addEventGroup', 'eventGroup'); 7 | 8 | this.programOrSendEvent = programOrSendEvent; 9 | this.isImplementation = isImplementation; 10 | this.sourceLoc = sourceLoc; 11 | this.eventGroups = []; 12 | } 13 | 14 | get lastEventGroup() { 15 | return this.eventGroups[this.eventGroups.length - 1]; 16 | } 17 | 18 | add(event) { 19 | if (!this.sourceLoc) { 20 | return; 21 | } 22 | 23 | // TODO: make sure that `contains` is the right thing -- it used to be strictlyContains, 24 | // but that was causing problems for programs that were a single variable declaration, e.g., 25 | // `var x = 2;` 26 | const eventIsLocal = event.sourceLoc && this.sourceLoc.contains(event.sourceLoc); 27 | if (eventIsLocal && this.lastEventGroup instanceof LocalEventGroup) { 28 | const lastEvent = this.lastEventGroup.lastEvent; 29 | if (event.sourceLoc.startPos >= lastEvent.sourceLoc.endPos || // Toby's rule 30 | event.sourceLoc.strictlyContains(lastEvent.sourceLoc) || // Inside-out rule 31 | event.sourceLoc.equals(lastEvent.sourceLoc) && 32 | event.constructor !== lastEvent.constructor && 33 | (event instanceof ReturnEvent || event instanceof ShowEvent)) { 34 | // no-op 35 | } else { 36 | const eventGroup = new LocalEventGroup(); 37 | this.emit('addEventGroup', eventGroup); 38 | this.eventGroups.push(eventGroup); 39 | } 40 | } else if (eventIsLocal && !(this.lastEventGroup instanceof LocalEventGroup)) { 41 | const eventGroup = new LocalEventGroup(); 42 | this.emit('addEventGroup', eventGroup); 43 | this.eventGroups.push(eventGroup); 44 | } else if (!eventIsLocal && !(this.lastEventGroup instanceof RemoteEventGroup)) { 45 | const eventGroup = new RemoteEventGroup(); 46 | this.emit('addEventGroup', eventGroup); 47 | this.eventGroups.push(eventGroup); 48 | } 49 | this.lastEventGroup.add(event); 50 | } 51 | } 52 | 53 | class AbstractEventGroup extends CheckedEmitter { 54 | constructor(events) { 55 | super(); 56 | this.registerEvent('addEvent', 'event'); 57 | 58 | this.events = events; 59 | } 60 | 61 | add(event) { 62 | throw new Error('abstract method!'); 63 | } 64 | 65 | get lastEvent() { 66 | return this.events[this.events.length - 1]; 67 | } 68 | } 69 | 70 | class LocalEventGroup extends AbstractEventGroup { 71 | constructor(...events) { 72 | super(events); 73 | } 74 | 75 | add(event) { 76 | this.emit('addEvent', event); 77 | this.events.push(event); 78 | } 79 | } 80 | 81 | class RemoteEventGroup extends AbstractEventGroup { 82 | constructor(...events) { 83 | super(events); 84 | this.registerEvent('removeEvent', 'event'); 85 | } 86 | 87 | add(event) { 88 | for (let idx = 0; idx < this.events.length; idx++) { 89 | if (event.subsumes(this.events[idx])) { 90 | this.emit('removeEvent', this.events[idx]); 91 | this.events.splice(idx, 1); 92 | break; 93 | } 94 | } 95 | 96 | this.emit('addEvent', event); 97 | this.events.push(event); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Seymour! 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
63 |
64 |
65 |
66 |
67 |
    68 |
  • focused
  • 69 |
  • definition
  • 70 |
  • call
  • 71 | 72 |
73 |
74 |
75 |
76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /viz/macroViz.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class MacroViz extends CheckedEmitter { 4 | constructor(container, eventRecorder = null) { 5 | super(); 6 | this.registerEvent('click', 'event', 'child', 'childView'); 7 | this.registerEvent('mouseover', 'event', 'child', 'childView'); 8 | this.registerEvent('mouseout', 'event', 'child', 'childView'); 9 | 10 | this.macroViz = this; 11 | 12 | this.container = container; 13 | this.container.classList.add('macroViz'); 14 | 15 | if (eventRecorder !== null) { 16 | this.setEventRecorder(eventRecorder); 17 | } 18 | } 19 | 20 | setEventRecorder(eventRecorder) { 21 | this.eventRecorder = eventRecorder; 22 | this.container.innerHTML = ''; 23 | this.nodeViews = new Map(); 24 | 25 | this.eventRecorder.addListener('addChild', (child, parent) => 26 | this.addChild(child, parent)); 27 | this.eventRecorder.addListener('addRoot', (root) => 28 | this.addRoot(root)); 29 | } 30 | 31 | get events() { 32 | if (this.nodeViews) { 33 | return Array.from(this.nodeViews.keys()); 34 | } else { 35 | return []; 36 | } 37 | } 38 | 39 | getNodeView(event) { 40 | return this.nodeViews.get(event); 41 | } 42 | 43 | addRoot(root) { 44 | this.topLevelNode = new NodeView(this, root); 45 | this.container.appendChild(this.topLevelNode.DOM); 46 | } 47 | 48 | addChild(child, parent) { 49 | if (child instanceof SendEvent || child instanceof ProgramEvent) { 50 | this.nodeViews.get(parent).addChild(child); 51 | } else if (child instanceof ErrorEvent) { 52 | const parentView = 53 | this.nodeViews.get(parent) || this.nodeViews.get(parent.env.programOrSendEvent); 54 | parentView.error(); 55 | } 56 | } 57 | 58 | setView(child, childView) { 59 | this.nodeViews.set(child, childView); 60 | } 61 | 62 | onClick(event, child) { this.emit('click', event, child, this.nodeViews.get(event)); 63 | console.log('event', child); } 64 | onMouseover(event, child) { this.emit('mouseover', event, child, this.nodeViews.get(event)); } 65 | onMouseout(event, child) { this.emit('mouseout', event, child, this.nodeViews.get(event)); } 66 | } 67 | 68 | class NodeView { 69 | constructor(parent, event) { 70 | this.parent = parent; 71 | this.macroViz = parent.macroViz; 72 | this.event = event; 73 | 74 | this.macroViz.setView(this.event, this); 75 | this.render(); 76 | } 77 | 78 | render() { 79 | this.children = d('children', {}); 80 | this.label = d('label', {}, this.event.selector || 'program'); 81 | this.DOM = d('macroVizNode', {isFocusable: !!this.event.activationEnv.sourceLoc}, 82 | this.label, this.children); 83 | 84 | this.label._event = this.event; 85 | this.label.onclick = (event) => this.macroViz.onClick(event, this.event); 86 | this.label.onmouseover = (event) => this.macroViz.onMouseover(event, this.event); 87 | this.label.onmouseout = (event) => this.macroViz.onMouseout(event, this.event); 88 | 89 | if (/*!this.event.activationEnv.sourceLoc || */!this.event.sourceLoc) { 90 | this.collapse(); 91 | } 92 | 93 | this.event.children.forEach(child => { 94 | this.addChild(child); 95 | }); 96 | } 97 | 98 | addChild(child) { 99 | const childView = new NodeView(this, child); 100 | this.children.appendChild(childView.DOM); 101 | } 102 | 103 | collapse(collapse = true) { 104 | this.DOM.classList.toggle('collapsed', collapse); 105 | } 106 | 107 | error() { 108 | this.DOM.classList.add('error'); 109 | if (this.parent && this.parent.error != null) { 110 | this.parent.error(); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lang/instructions.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Instruction { 4 | constructor(...operands) { 5 | this.operands = operands; 6 | } 7 | 8 | eval(activation) { 9 | console.debug('instruction:', this.constructor.name, ...this.operands); 10 | return activation[this.constructor.name].apply(activation, this.operands); 11 | } 12 | } 13 | 14 | class IPrim extends Instruction { 15 | constructor(primFn, sourceLoc, nextInstruction) { 16 | super(primFn, sourceLoc, nextInstruction); 17 | } 18 | } 19 | 20 | class IPush extends Instruction { 21 | constructor(value, nextInstruction) { 22 | super(value, nextInstruction); 23 | } 24 | } 25 | 26 | class IPushThis extends Instruction { 27 | constructor(nextInstruction) { 28 | super(nextInstruction); 29 | } 30 | } 31 | 32 | class IPushFromVar extends Instruction { 33 | constructor(name, nextInstruction) { 34 | super(name, nextInstruction); 35 | } 36 | } 37 | 38 | class IPushFromInstVar extends Instruction { 39 | constructor(name, nextInstruction) { 40 | super(name, nextInstruction); 41 | } 42 | } 43 | 44 | class IPopIntoVar extends Instruction { 45 | constructor(name, sourceLoc, nextInstruction) { 46 | super(name, sourceLoc, nextInstruction); 47 | } 48 | } 49 | 50 | class IPopIntoInstVar extends Instruction { 51 | constructor(name, sourceLoc, nextInstruction) { 52 | super(name, sourceLoc, nextInstruction); 53 | } 54 | } 55 | 56 | class IDeclVar extends Instruction { 57 | constructor(name, sourceLoc, nextInstruction) { 58 | super(name, sourceLoc, nextInstruction); 59 | } 60 | } 61 | 62 | class IDup extends Instruction { 63 | constructor(nextInstruction) { 64 | super(nextInstruction); 65 | } 66 | } 67 | 68 | class IDrop extends Instruction { 69 | constructor(nextInstruction) { 70 | super(nextInstruction); 71 | } 72 | } 73 | 74 | class ICond extends Instruction { 75 | constructor(nextInstructionIfTrue, nextInstructionIfFalse) { 76 | super(nextInstructionIfTrue, nextInstructionIfFalse); 77 | } 78 | } 79 | 80 | class IBlock extends Instruction { 81 | constructor(sourceLoc, formals, code, nextInstruction) { 82 | super(sourceLoc, formals, code, nextInstruction); 83 | } 84 | } 85 | 86 | class IDeclClass extends Instruction { 87 | constructor(name, instVarNames, nextInstruction) { 88 | super(name, instVarNames, nextInstruction); 89 | } 90 | } 91 | 92 | class IDeclMethod extends Instruction { 93 | constructor(sourceLoc, selector, className, formals, code, nextInstruction) { 94 | super(sourceLoc, selector, className, formals, code, nextInstruction); 95 | } 96 | } 97 | 98 | class INew extends Instruction { 99 | constructor(nextInstruction) { 100 | super(nextInstruction); 101 | } 102 | } 103 | 104 | class IArray extends Instruction { 105 | constructor(size, nextInstruction) { 106 | super(size, nextInstruction); 107 | } 108 | } 109 | 110 | class ISend extends Instruction { 111 | constructor(selector, numArgs, sourceLoc, activationPathToken, nextInstruction) { 112 | super(selector, numArgs, sourceLoc, activationPathToken, nextInstruction); 113 | } 114 | } 115 | 116 | class ISuperSend extends Instruction { 117 | constructor(selector, numArgs, sourceLoc, activationPathToken, nextInstruction) { 118 | super(selector, numArgs, sourceLoc, activationPathToken, nextInstruction); 119 | } 120 | } 121 | 122 | class INonLocalReturn extends Instruction { 123 | constructor(sourceLoc) { 124 | super(sourceLoc); 125 | } 126 | } 127 | 128 | class ILocalReturn extends Instruction { 129 | constructor(sourceLoc) { 130 | super(sourceLoc); 131 | } 132 | } 133 | 134 | class IDone extends Instruction { 135 | constructor() { 136 | super(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /viz/EventRecorder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class EventRecorder extends CheckedEmitter { 4 | constructor(newEventHandler) { 5 | super(); 6 | this.registerEvent('addChild', 'child', 'parent'); 7 | this.registerEvent('addRoot', 'root'); 8 | this.currentProgramOrSendEvent = null; 9 | } 10 | 11 | program(sourceLoc) { 12 | const event = new ProgramEvent(sourceLoc); 13 | this.currentProgramOrSendEvent = event; 14 | const env = this.mkEnv(sourceLoc, null); 15 | return env; 16 | } 17 | 18 | send(sourceLoc, env, recv, selector, args, activationPathToken) { 19 | const event = new SendEvent(sourceLoc, env, recv, selector, args, activationPathToken); 20 | env.currentSendEvent = event; 21 | this.currentProgramOrSendEvent = event; 22 | // this event is only sent to event handler after it gets an activation environment (see below) 23 | } 24 | 25 | mkEnv(newEnvSourceLoc, parentEnv) { 26 | const programOrSendEvent = this.currentProgramOrSendEvent; 27 | const callerEnv = programOrSendEvent.env; 28 | const newEnv = new Env(newEnvSourceLoc, parentEnv, callerEnv, programOrSendEvent); 29 | if ((programOrSendEvent instanceof SendEvent || programOrSendEvent instanceof ProgramEvent) && 30 | !programOrSendEvent.activationEnv) { 31 | programOrSendEvent.activationEnv = newEnv; 32 | const parentEvent = programOrSendEvent.env ? programOrSendEvent.env.programOrSendEvent : null; 33 | if (parentEvent) { 34 | parentEvent.children.push(programOrSendEvent); 35 | programOrSendEvent.env.receive(programOrSendEvent); 36 | this.emit('addChild', programOrSendEvent, parentEvent); 37 | } else { 38 | this.emit('addRoot', programOrSendEvent); 39 | } 40 | } 41 | return newEnv; 42 | } 43 | 44 | receive(env, returnValue) { 45 | env.currentSendEvent.returnValue = returnValue; 46 | this.currentProgramOrSendEvent = env.programOrSendEvent; 47 | return returnValue; 48 | } 49 | 50 | 51 | enterScope(sourceLoc, env) { 52 | this.send(sourceLoc, env, null, 'enterNewScope', []); 53 | return this.mkEnv(sourceLoc); 54 | } 55 | 56 | leaveScope(env) { 57 | this.receive(env, null); 58 | } 59 | 60 | 61 | _emit(event) { 62 | this.currentProgramOrSendEvent.children.push(event); 63 | event.env.receive(event); 64 | this.emit('addChild', event, this.currentProgramOrSendEvent); 65 | } 66 | 67 | show(sourceLoc, env, string, alt) { 68 | if (typeof string !== 'string') { 69 | string = alt; 70 | } 71 | const event = new ShowEvent(sourceLoc, env, string); 72 | this._emit(event); 73 | } 74 | 75 | error(sourceLoc, env, errorString) { 76 | const event = new ErrorEvent(sourceLoc, env, errorString); 77 | this._emit(event); 78 | } 79 | 80 | localReturn(sourceLoc, env, value) { 81 | const event = new LocalReturnEvent(sourceLoc, env, value); 82 | this._emit(event); 83 | return value; 84 | } 85 | 86 | nonLocalReturn(sourceLoc, env, value) { 87 | const event = new NonLocalReturnEvent(sourceLoc, env, value); 88 | this._emit(event); 89 | return value; 90 | } 91 | 92 | declVar(sourceLoc, env, name, value) { 93 | const event = new VarDeclEvent(sourceLoc, env, name, value); 94 | this._emit(event); 95 | return value; 96 | } 97 | 98 | assignVar(sourceLoc, env, declEnv, name, value) { 99 | const event = new VarAssignmentEvent(sourceLoc, env, declEnv, name, value); 100 | this._emit(event); 101 | return value; 102 | } 103 | 104 | assignInstVar(sourceLoc, env, obj, name, value) { 105 | const event = new InstVarAssignmentEvent(sourceLoc, env, obj, name, value); 106 | this._emit(event); 107 | return value; 108 | } 109 | 110 | instantiate(sourceLoc, env, _class, args, newInstance) { 111 | const event = new InstantiationEvent(sourceLoc, env, _class, args, newInstance); 112 | this._emit(event); 113 | return newInstance; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /highlighting.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --hover-color : hsl(0, 0%, 80%); 3 | --focus-color : hsl(219, 90%, 65%); 4 | /* TODO: tune these colors */ 5 | --def-color : hsl(219, 72%, 90%); 6 | --ref-color : hsl(219, 80%, 85%); 7 | --ref-specific-color: hsl(219, 80%, 75%); 8 | 9 | --widget-background-color: hsl(217, 96%, 95%); 10 | 11 | --color-a: hsl(28, 100%, 53%); 12 | --color-b: hsl(121, 57%, 40%); 13 | --color-c: hsl(359, 69%, 50%); 14 | --color-d: hsl(271, 39%, 57%); 15 | --color-e: hsl(10, 29%, 42%); 16 | --color-f: hsl(319, 66%, 68%); 17 | --color-g: hsl(60, 68%, 44%); 18 | --color-h: hsl(185, 77%, 46%); 19 | } 20 | 21 | body { 22 | background: var(--test); 23 | } 24 | 25 | temp { 26 | 27 | color: hsl(57, 90%, 70%); /* old highlight hue */ 28 | } 29 | 30 | /* macroViz */ 31 | 32 | /* TODO: consider keeping cursorOnDecl, cursorOnSend */ 33 | 34 | macroVizNode[isFocusable="true"] > label:hover { 35 | background: var(--hover-color); 36 | } 37 | 38 | macroVizNode.highlight-focus > label { 39 | background-color: var(--focus-color) !important; 40 | } 41 | 42 | macroVizNode.highlight-def > label { 43 | background-color: var(--def-color) !important; 44 | } 45 | 46 | macroVizNode.highlight-ref > label { 47 | background-color: var(--ref-color) !important; 48 | } 49 | 50 | macroVizNode.highlight-ref-specific > label { 51 | background-color: var(--ref-specific-color) !important; 52 | } 53 | 54 | macroVizNode.highlight-ref-1 > label { 55 | background-color: var(--color-a) !important; 56 | } 57 | 58 | macroVizNode.highlight-ref-2 > label { 59 | background-color: var(--color-b) !important; 60 | } 61 | 62 | macroVizNode.highlight-ref-3 > label { 63 | background-color: var(--color-c) !important; 64 | } 65 | 66 | macroVizNode.highlight-ref-4 > label { 67 | background-color: var(--color-d) !important; 68 | } 69 | 70 | macroVizNode.highlight-ref-5 > label { 71 | background-color: var(--color-e) !important; 72 | } 73 | 74 | macroVizNode.highlight-ref-6 > label { 75 | background-color: var(--color-f) !important; 76 | } 77 | 78 | macroVizNode.highlight-ref-7 > label { 79 | background-color: var(--color-g) !important; 80 | } 81 | 82 | macroVizNode.highlight-ref-8 > label { 83 | background-color: var(--color-h) !important; 84 | } 85 | 86 | 87 | /* TODO: ref-colors */ 88 | 89 | /* microViz */ 90 | 91 | 92 | /* code */ 93 | 94 | .CodeMirror *[class*=' highlight-'] { 95 | box-shadow: 1px 1px 0px 0px #aaa; 96 | } 97 | 98 | .CodeMirror .highlight-def { 99 | background-color: var(--def-color) !important; 100 | } 101 | 102 | .CodeMirror .highlight-ref { 103 | background-color: var(--ref-color) !important; 104 | } 105 | 106 | resultWidget { 107 | display: block; 108 | z-index: 2000; 109 | border-radius: 2pt; 110 | padding: 0 4pt; 111 | margin-top: 4px; 112 | background: var(--widget-background-color); 113 | box-shadow: 1px 1px 4px hsl(0, 0%, 30%); 114 | } 115 | 116 | focusWidget { 117 | display: block; 118 | z-index: 2000; 119 | border-radius: 2pt; 120 | padding: 2pt 4pt; 121 | margin-top: 0px; 122 | background: var(--widget-background-color); 123 | box-shadow: 1px 1px 4px hsl(0, 0%, 30%); 124 | } 125 | 126 | focusWidget > call { 127 | padding: 0 2pt; 128 | } 129 | 130 | focusWidget > call.index-0 { color: var(--color-a); } 131 | focusWidget > call.index-1 { color: var(--color-b); } 132 | focusWidget > call.index-2 { color: var(--color-c); } 133 | focusWidget > call.index-3 { color: var(--color-d); } 134 | focusWidget > call.index-4 { color: var(--color-e); } 135 | focusWidget > call.index-5 { color: var(--color-f); } 136 | focusWidget > call.index-6 { color: var(--color-g); } 137 | focusWidget > call.index-7 { color: var(--color-h); } 138 | 139 | 140 | /* other */ 141 | 142 | #legend { 143 | list-style-type: none; 144 | padding: 10px; 145 | margin: 0px; 146 | margin-bottom: 10px; 147 | background-color: hsl(0, 0%, 100%); 148 | } 149 | 150 | #legend > .definition > .swatch { 151 | color: var(--def-color) ; 152 | } 153 | #legend > .call > .swatch { 154 | color: var(--ref-color) ; 155 | } 156 | #legend > .focused > .swatch { 157 | color: var(--focus-color) ; 158 | } 159 | -------------------------------------------------------------------------------- /lang/grammar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const seymourGrammar = ohm.grammar(` 4 | 5 | Seymour { 6 | 7 | Program 8 | = (ClassDecl | MethodDecl | Stmt)* 9 | 10 | Stmt 11 | = var varName "=" Exp ";" -- varDecl 12 | | varName "=" Exp ";" -- varAssign 13 | | this "." varName "=" Exp ";" -- instVarAssign 14 | | return Exp ";" -- return 15 | | Exp ";" -- exp 16 | | "@{" (~"}@" any)* "}@" -- js 17 | 18 | ClassDecl 19 | = class className (extends className)? (with NonemptyListOf)? ";" 20 | 21 | MethodDecl 22 | = def className "." varName "(" ListOf ")" MethodBody -- java 23 | | def className "(" ListOf ")" MethodBody -- call 24 | | def varName className (kwSelectorPart varName)+ MethodBody -- prefixKeyword 25 | | def className (kwSelectorPart varName)+ MethodBody -- keyword 26 | | def className binSelector varName MethodBody -- binary 27 | 28 | MethodBody 29 | = "=" Exp ";" -- exp 30 | | "{" Stmt* "}" -- stmt 31 | 32 | Exp 33 | = KWSendExp 34 | 35 | KWSendExp 36 | = varName EqExp (kwSelectorPart EqExp)+ -- prefixKeyword 37 | | EqExp (kwSelectorPart EqExp)+ -- keyword 38 | | super (kwSelectorPart EqExp)+ -- super 39 | | EqExp 40 | 41 | EqExp 42 | = RelExp ("==" | "!=") RelExp -- eq 43 | | RelExp 44 | 45 | RelExp 46 | = AddExp ("<=" | "<" | ">=" | ">") AddExp -- rel 47 | | AddExp 48 | 49 | AddExp 50 | = AddExp ("+" | "-") MulExp -- add 51 | | MulExp 52 | 53 | MulExp 54 | = MulExp ("*" | "/" | "%") DotExp -- mul 55 | | DotExp 56 | 57 | DotExp 58 | = DotExp "(" Actuals ")" -- call 59 | | DotExp "." varName "(" Actuals ")" -- send 60 | | super "." varName "(" Actuals ")" -- superSend 61 | | this "." varName ~"(" -- instVarAccess 62 | | UnExp 63 | 64 | UnExp 65 | = "-" PriExp -- neg 66 | | PriExp 67 | 68 | PriExp 69 | = "(" Exp ")" -- paren 70 | | "{" BlockArgNames? Stmt* Exp? "}" -- block 71 | | "[" ListOf "]" -- array 72 | | new UnExp "(" Actuals ")" -- new 73 | | string -- str 74 | | varName -- var 75 | | className -- class 76 | | number -- number 77 | | this -- this 78 | | true -- true 79 | | false -- false 80 | | null -- null 81 | 82 | BlockArgNames 83 | = ListOf "|" 84 | 85 | Actuals 86 | = ListOf 87 | 88 | // Lexical rules 89 | 90 | varName (a variable name) 91 | = ~keyword lower alnum* 92 | 93 | className (a class name) 94 | = upper alnum* 95 | 96 | kwSelectorPart 97 | = varName ":" 98 | 99 | string (a string) 100 | = "\\"" (~"\\"" ~"\\n" any)* "\\"" 101 | 102 | number (a number) 103 | = digit* "." digit+ -- fract 104 | | digit+ -- whole 105 | 106 | binSelector (a binary selector) 107 | = "==" | "!=" | "<=" | "<" | ">=" | ">" | "+" | "-" | "*" | "/" | "%" | "@" 108 | 109 | class = "class" ~alnum 110 | def = "def" ~alnum 111 | extends = "extends" ~alnum 112 | false = "false" ~alnum 113 | new = "new" ~alnum 114 | with = "with" ~alnum 115 | owner = "owner" ~alnum 116 | null = "null" ~alnum 117 | return = "return" ~alnum 118 | super = "super" ~alnum 119 | this = "this" ~alnum 120 | true = "true" ~alnum 121 | var = "var" ~alnum 122 | 123 | keyword 124 | = class | def | extends | false | new | with | owner | null | return | super | this | true | var 125 | 126 | space 127 | += comment 128 | 129 | comment 130 | = "/*" (~"*/" any)* "*/" -- multiLine 131 | | "//" (~"\\n" any)* -- singleLine 132 | 133 | // Lexical rules for syntax highlighting (admittedly hacky!) 134 | 135 | tokens 136 | = token* 137 | 138 | token 139 | = comment | kwSelectorPrefix | kwSelectorPart | valueToken | keyword | instVarAccess 140 | | javaStyleSelector | binSelector | any 141 | 142 | valueToken 143 | = this | null | true | false | varName | className | string | number 144 | 145 | instVarAccess 146 | = "." spaces varName ~(spaces "(") 147 | 148 | javaStyleSelector 149 | = "." spaces varName &(spaces "(") 150 | 151 | kwSelectorPrefix 152 | = varName &(space+ expStart) 153 | 154 | expStart 155 | = valueToken | "(" | "{" 156 | 157 | } 158 | 159 | `); 160 | -------------------------------------------------------------------------------- /fira/LICENSE: -------------------------------------------------------------------------------- 1 | Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. 2 | with Reserved Font Name < Fira >, 3 | 4 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 5 | This license is copied below, and is also available with a FAQ at: 6 | http://scripts.sil.org/OFL 7 | 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font creation 16 | efforts of academic and linguistic communities, and to provide a free and 17 | open framework in which fonts may be shared and improved in partnership 18 | with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply 27 | to any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software components as 38 | distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, deleting, 41 | or substituting -- in part or in whole -- any of the components of the 42 | Original Version, by changing formats or by porting the Font Software to a 43 | new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 51 | redistribute, and sell modified and unmodified copies of the Font 52 | Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, 55 | in Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the corresponding 66 | Copyright Holder. This restriction only applies to the primary font name as 67 | presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created 79 | using the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /seymour.js: -------------------------------------------------------------------------------- 1 | class Seymour extends CheckedEmitter { 2 | constructor(microVizContainer, macroVizContainer = null, 3 | enableMicroViz = true, enableHighlighting = true) { 4 | super(); 5 | 6 | this.microVizContainer = microVizContainer || null; 7 | this.macroVizContainer = macroVizContainer || null; 8 | 9 | this.registerEvent('codeChanged', 'code'); 10 | this.registerEvent('run', 'ast', 'code'); 11 | this.registerEvent('error', 'e'); 12 | this.registerEvent('done', 'ast', 'code'); 13 | 14 | // setup 15 | this.microViz = new MicroViz(this.microVizContainer, enableMicroViz); 16 | this.editor = this.microViz.editor; 17 | this.editor.setOption('lineNumbers', true); 18 | 19 | if (this.macroVizContainer) { 20 | this.macroViz = new MacroViz(this.macroVizContainer); 21 | } 22 | 23 | this.pathMatchers = null; 24 | 25 | // highlighting 26 | this.highlighting = new Highlighting(this, enableHighlighting); 27 | 28 | // tie the knot 29 | this.interpreter = null; 30 | this.R = null; 31 | this.timeoutId = null; 32 | this.running = false; 33 | 34 | this.m = seymourGrammar.matcher(); 35 | 36 | this.parseErrorWidget = null; 37 | 38 | this.editor.on('beforeChange', (cmInstance, changeObj) => { 39 | var insertedText = changeObj.text.join('\n'); 40 | var fromIdx = this.editor.indexFromPos(changeObj.from); 41 | var toIdx = this.editor.indexFromPos(changeObj.to); 42 | this.m.replaceInputRange(fromIdx, toIdx, insertedText); 43 | }); 44 | 45 | this.editor.on('changes', (cmInstance, changes) => { 46 | this.handleChanges(cmInstance, changes); 47 | }); 48 | } 49 | 50 | run(ast, code = null) { 51 | if (code !== null) { 52 | this.emit('codeChanged', code); 53 | if (this.timeoutId !== null) { 54 | clearTimeout(this.timeoutId); 55 | } 56 | Obj.nextId = 0; 57 | this.R = new EventRecorder(); 58 | if (this.macroVizContainer) { 59 | this.macroViz.setEventRecorder(this.R); 60 | } 61 | this.interpreter = new Interpreter(ast.sourceLoc, code, this.R); 62 | if (this.pathMatchers === null) { 63 | this.pathMatchers = getPathMatchers(this.interpreter.global.env); 64 | } 65 | this.pathMatchers.forEach(pathMatcher => pathMatcher.reset(this.interpreter.global.env)); 66 | 67 | this.R.addListener('addChild', (child, parent) => { 68 | this.pathMatchers.forEach(pathMatcher => { 69 | if (pathMatcher.env) { 70 | // nothing to do 71 | return; 72 | } 73 | pathMatcher.processEvent(child, parent); 74 | if (pathMatcher.env) { 75 | this.highlighting.focusPath(pathMatcher); 76 | } 77 | }); 78 | }); 79 | 80 | this.highlighting.clearFocus(); 81 | this.highlighting.focusPath(this.pathMatchers[0]); 82 | } 83 | 84 | let done; 85 | this.emit('run', ast, code); 86 | try { 87 | done = this.interpreter.runForMillis(30); 88 | } catch (e) { 89 | const activationEnv = this.R.currentProgramOrSendEvent.activationEnv; 90 | this.R.error( 91 | activationEnv ? null : this.R.currentProgramOrSendEvent.sourceLoc, 92 | activationEnv || this.R.currentProgramOrSendEvent.env, e.toString()); 93 | this.emit('error', e); 94 | done = true; 95 | } 96 | 97 | if (done) { 98 | this.emit('done', ast, code); 99 | this.timeoutId = null; 100 | console.log('(done)'); 101 | } else { 102 | this.timeoutId = setTimeout(() => this.run(), 0); 103 | } 104 | } 105 | 106 | handleChanges(cmInstance, changes) { 107 | if (this.parseErrorWidget) { 108 | this.editor.removeLineWidget(this.parseErrorWidget); 109 | this.parseErrorWidget = undefined; 110 | } 111 | 112 | syntaxHighlight(this.editor, this.m); 113 | if (this.changesTimeout) { 114 | clearTimeout(this.changesTimeout); 115 | } 116 | 117 | const r = this.m.match(); 118 | if (r.succeeded()) { 119 | // console.clear(); 120 | const ast = parse(r); 121 | console.debug('ast', ast); 122 | const code = preludeAST.toInstruction(ast.toInstruction(new IDone())); 123 | console.debug('code', code); 124 | this.changesTimeout = setTimeout(() => { 125 | this.run(ast, code); 126 | this.changesTimeout = null; 127 | }, 200); 128 | } else { 129 | const expected = r.getExpectedText(); 130 | const pos = this.editor.doc.posFromIndex(r.getRightmostFailurePosition()); 131 | const error = document.createElement('parseError'); 132 | error.innerText = spaces(pos.ch) + '^\nExpected: ' + expected; 133 | this.parseErrorWidget = this.editor.addLineWidget(pos.line, error); 134 | this.changesTimeout = setTimeout(() => { 135 | $(error).slideDown().queue(() => { 136 | if (this.parseErrorWidget) { 137 | this.parseErrorWidget.changed(); 138 | } 139 | }); 140 | this.changesTimeout = null; 141 | }, 2000); 142 | $(error).hide(); 143 | this.parseErrorWidget.changed(); 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /viz/microViz.css: -------------------------------------------------------------------------------- 1 | .microViz .CodeMirror { 2 | line-height: 1.4rem; 3 | display: inline-block; 4 | min-width: 350px; 5 | height: auto; 6 | border-right: 1px dashed hsl(0, 0%, 80%); 7 | } 8 | 9 | .microViz .CodeMirror-scroll { 10 | min-height: 100%; 11 | height: auto; 12 | overflow-y: hidden; 13 | overflow-x: auto; 14 | } 15 | 16 | .microViz .CodeMirror-code > div:nth-child(even){ 17 | background: hsl(0, 0%, 98%); 18 | } 19 | /*-------------------------------*/ 20 | 21 | .microViz { 22 | line-height: inherit; 23 | display: flex; 24 | overflow: auto; 25 | padding: 10px 0; 26 | position: relative; 27 | } 28 | 29 | .microViz microVizBackground { 30 | position: absolute; 31 | top: 0px; 32 | left: 0px; 33 | width: 100%; 34 | z-index: -1; 35 | } 36 | 37 | .microViz line { 38 | display: block; 39 | line-height: inherit; 40 | min-height: 1.4rem; 41 | min-width: 2px; 42 | } 43 | 44 | .microViz line:nth-child(even) { 45 | background: hsl(0, 0%, 98%); 46 | } 47 | 48 | .microViz microVizHolder { 49 | display: inline-block; 50 | flex: 1; 51 | overflow-x: auto; 52 | overflow-y: hidden; 53 | } 54 | 55 | .microViz microVizDiv { 56 | display: inline-block; 57 | white-space: nowrap; 58 | line-height: 1.4rem; 59 | padding: 4px 0; /* same as codemirror-lines */ 60 | } 61 | 62 | .microViz microVizBackground { 63 | padding: 4px 0; /* same as codemirror-lines */ 64 | } 65 | 66 | /* layout stuff */ 67 | /* top-level send, whose sourceloc is always the entire program */ 68 | .microViz microVizDiv > send { 69 | display: inline-block; 70 | } 71 | 72 | .microViz microVizDiv spacer { 73 | height: 1.4rem; 74 | } 75 | 76 | .microViz spacer:after { 77 | content: " "; 78 | } 79 | 80 | .microViz microVizDiv event[eventtype="ErrorEvent"] { 81 | color: HSL(352, 52%, 59%); 82 | } 83 | 84 | .microViz microVizDiv event, 85 | .microViz microVizDiv localEventGroup, 86 | .microViz microVizDiv remoteEventGroup, 87 | .microViz microVizDiv spacer, 88 | .microViz microVizDiv wrapper { 89 | display: inline-block; 90 | } 91 | 92 | .microViz microVizDiv send { 93 | display: inline-flex; 94 | } 95 | 96 | .microViz microVizDiv localEventGroup, 97 | .microViz microVizDiv remoteEventGroup, 98 | .microViz #microVizDiv wrapper { 99 | white-space: nowrap; 100 | } 101 | 102 | .microViz microVizDiv localEventGroup > *, 103 | .microViz microVizDiv remoteEventGroup > *, 104 | .microViz microVizDiv wrapper > * { 105 | vertical-align: top; 106 | } 107 | 108 | 109 | 110 | /*---*/ 111 | 112 | .microViz microVizDiv send:not([isImplementation="true"]):not([empty="true"]) { 113 | border-radius: 4px; 114 | background: rgba(100, 149, 237, .05); /*hsla(0, 0%, 0%, 0.05);*/ 115 | box-shadow: 1px 1px 2px #ddd; 116 | margin: 0 4pt; 117 | } 118 | 119 | .microViz microVizDiv send[empty="true"] > remoteEventGroup > emptySendDot { 120 | color: hsl(219, 79%, 66%); 121 | } 122 | 123 | .microViz microVizDiv event { 124 | margin: 0 5px; 125 | height: 1.4rem; 126 | } 127 | 128 | .microViz microVizDiv send { 129 | margin: 0; 130 | } 131 | 132 | .microViz microvizDiv > send { 133 | background: none !important; 134 | margin: 0 0 !important; 135 | box-shadow: none !important; 136 | } 137 | 138 | .microViz microVizDiv send[isImplementation="true"] { 139 | border-radius: 4px; 140 | background: hsla(60, 56%, 91%, 0.35); 141 | box-shadow: 1px 1px 2px #ddd; 142 | margin: 0 4pt; 143 | } 144 | 145 | .microViz microVizDiv send[empty="true"].firstInLine { 146 | margin-left: 10px; 147 | } 148 | 149 | .microViz microVizDiv send[empty="true"].lastInLine { 150 | margin-right: 10px; 151 | } 152 | 153 | .microViz microVizDiv send[empty="true"] + event:not(.firstInLine) { 154 | margin-left: 0; 155 | } 156 | 157 | .microViz microVizDiv event + send[empty="true"]:not(.firstInLine) { 158 | margin-left: -0.3rem; 159 | } 160 | 161 | .microViz microVizDiv localEventGroup:nth-child(1n+2), 162 | .microViz microVizDiv remoteEventGroup:nth-child(1n+2) { 163 | border-left: 1px dashed #aaa; 164 | } 165 | 166 | .microViz microVizDiv event.remote { 167 | opacity: 0.5; 168 | pointer-events: none; 169 | } 170 | 171 | .microViz microVizDiv event.remote::before { 172 | content: '('; 173 | } 174 | 175 | .microViz microVizDiv event.remote::after { 176 | content: ')'; 177 | } 178 | 179 | /* only debugging stuff past this point */ 180 | 181 | /* 182 | .microViz microVizDiv localEventGroup, 183 | .microViz microVizDiv remoteEventGroup, 184 | .microViz microVizDiv wrapper { 185 | border: 1px solid cornflowerblue; 186 | border-radius: 4px; 187 | padding: 1px; 188 | } 189 | 190 | .microViz microVizDiv wrapper { 191 | border-color: red; 192 | } 193 | 194 | .microViz microVizDiv send { 195 | border: 1px solid red; 196 | border-radius: 4px; 197 | padding: 1px; 198 | } 199 | 200 | .microViz microVizDiv spacer::after { 201 | content: '(spacer)'; 202 | color: hsl(0, 0%, 70%); 203 | } 204 | */ 205 | 206 | /* ------------------------------------ */ 207 | 208 | .microViz microVizDiv > model { 209 | display: block; 210 | } 211 | 212 | .microViz microVizDiv > model send, 213 | .microViz microVizDiv > model localEventGroup, 214 | .microViz microVizDiv > model remoteEventGroup { 215 | display: block; 216 | vertical-align: top; 217 | margin: 4px; 218 | } 219 | 220 | .microViz microVizDiv > model send, 221 | .microViz microVizDiv > model event { 222 | display: inline-block; 223 | /*float: left;*/ 224 | /*clear: left;*/ 225 | } 226 | 227 | .microViz microVizDiv > model remoteEventGroup { 228 | opacity: 0.6; 229 | } 230 | 231 | .microViz microVizDiv > model localEventGroup, 232 | .microViz microVizDiv > model remoteEventGroup { 233 | display: inline-block; 234 | border: 1px solid cornflowerblue; 235 | border-radius: 4px; 236 | margin-left: 8pt; 237 | } 238 | 239 | .microViz microVizDiv > model event { 240 | background: #efefef; 241 | border-radius: 4px; 242 | padding: 4px; 243 | margin: 2px; 244 | } 245 | 246 | .microViz microVizDiv > model send > label { 247 | display: block; 248 | margin: 2px; 249 | } 250 | 251 | 252 | 253 | /*.microViz event:not(.lastInLine):not(:last-child):after, 254 | .microViz send[empty="true"]:not(.lastInLine):not([isImplementation=true]):not(:last-child):after { 255 | content: ', '; 256 | color: hsl(0, 0%, 70%); 257 | white-space: pre; 258 | }*/ -------------------------------------------------------------------------------- /viz/events.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Event { 4 | constructor(sourceLoc, env) { 5 | this.id = Event.nextEventId++; 6 | this.sourceLoc = sourceLoc; 7 | this.env = env; 8 | this.children = []; 9 | } 10 | 11 | toMicroVizString() { 12 | throw new Error('abstract method!'); 13 | } 14 | 15 | _valueString(v) { 16 | if (typeof v === 'function') { 17 | return '{function}'; 18 | } else if (v === undefined) { 19 | return 'undefined'; 20 | } else if (v === Infinity) { 21 | return '∞'; 22 | } else if (v === -Infinity) { 23 | return '-∞'; 24 | } else if (v instanceof Obj) { 25 | return v.id < Event.objectIdEmojis.length ? Event.objectIdEmojis[v.id] : '#' + v.id; 26 | } else { 27 | return JSON.stringify(v); 28 | } 29 | } 30 | 31 | subsumes(that) { 32 | return false; 33 | } 34 | } 35 | 36 | Event.nextEventId = 0; 37 | 38 | Event.objectIdEmojis = [ 39 | '🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐨', '🐯', '🦁', '🐮', '🐷', '🐸', '🐵', '🐔', '🐧', 40 | '🐦', '🐤', '🦆', '🦅', '🦉', '🦇', '🐺', '🐗', '🐴', '🦄', '🐝', '🐛', '🦋', '🐌', '🐞', '🐜', '🕷', 41 | '🐢', '🐍', '🦎', '🦂', '🦀', '🦑', '🐙', '🦐', '🐠', '🐟', '🐡', '🐬', '🦈', '🐳', '🐋', '🐊', '🐆', 42 | '🐅', '🐃', '🐂', '🐄', '🦌', '🐪', '🐫', '🐘', '🦏', '🦍', '🐎', '🐖', '🐐', '🐏', '🐑', '🐕', '🐩', 43 | '🐈', '🐓', '🦃', '🕊', '🐇', '🐁', '🐀', '🐿', '🐉', '🐲', '🍏', '🍎', '🍐', '🍊', '🍋', '🍌', '🍉', 44 | '🍇', '🍓', '🍈', '🍒', '🍑', '🍍', '🥝', '🥑', '🍅', '🍆', '🥒', '🥕', '🌽', '🌶', '🥔', '🍠', '🌰', 45 | '🥜', '🍯', '🥐', '🍞', '🥖', '🧀', '🥚', '🍳', '🥓', '🥞', '🍤', '🍗', '🍖', '🍕', '🌭', '🍔', '🍟', 46 | '🥙', '🌮', '🌯', '🍝', '🍜', '🍲', '🍣', '🍱', '🍦', '🍧', '🍨', '🍰', '🎂', '🍮', '🍭', '🍬', '🍫', 47 | '🍿', '🍩', '🍪', '🥛', '🍼', '☕️', '🍵', '🍶', '🍺', '🍷', '🥃', '🍸', '🍹', '⚽️', '🏀', '🏈', '⚾️', 48 | '🎾', '🏐', '🏉', '🎱', '🏓', '🏸', '🏒', '⛸', '🏄', '🎸', '🎷', '🏠', '🏰', '😀', '😱', '👦🏻', '👨🏾']; 49 | 50 | class ProgramEvent extends Event { 51 | constructor(sourceLoc) { 52 | super(sourceLoc, null); 53 | // also: activationEnv 54 | } 55 | } 56 | 57 | class SendEvent extends Event { 58 | constructor(sourceLoc, env, recv, selector, args, activationPathToken) { 59 | super(sourceLoc, env); 60 | this.recv = recv; 61 | this.selector = selector; 62 | this.args = args; 63 | this.activationPathToken = activationPathToken; 64 | // also: activationEnv, returnValue 65 | } 66 | 67 | toDetailString() { 68 | let s = 69 | 'receiver: ' + this._valueString(this.recv) + '\n' + 70 | 'selector: ' + this.selector + '\n' + 71 | 'arguments: [' + this.args.map(x => this._valueString(x)).join(', ') + ']\n'; 72 | if (this.hasOwnProperty('returnValue')) { 73 | s += '⇒ ' + this._valueString(this.returnValue); 74 | } 75 | return s; 76 | } 77 | 78 | isInlineBlockCall() { 79 | const isBlockCall = this.recv instanceof BlockClosure && this.selector === 'call'; 80 | if (!isBlockCall) return false; 81 | 82 | let someParentContains = false; 83 | let env = this.env; 84 | while (env) { 85 | const send = env.programOrSendEvent; 86 | someParentContains = someParentContains || 87 | (send.sourceLoc && send.sourceLoc.contains(this.activationEnv.sourceLoc)); 88 | env = env.callerEnv; 89 | } 90 | return isBlockCall && someParentContains; 91 | } 92 | } 93 | 94 | class VarDeclEvent extends Event { 95 | constructor(sourceLoc, env, name, value) { 96 | super(sourceLoc, env); 97 | this.name = name; 98 | this.value = value; 99 | } 100 | 101 | toMicroVizString() { 102 | return this.name + ' = ' + this._valueString(this.value); 103 | } 104 | } 105 | 106 | class VarAssignmentEvent extends Event { 107 | constructor(sourceLoc, env, declEnv, name, value) { 108 | super(sourceLoc, env); 109 | this.declEnv = declEnv; 110 | this.name = name; 111 | this.value = value; 112 | } 113 | 114 | subsumes(that) { 115 | return (that instanceof VarAssignmentEvent || that instanceof VarDeclEvent) && 116 | this.name === that.name && this.declEnv === that.declEnv; 117 | } 118 | 119 | toMicroVizString() { 120 | return this.name + ' = ' + this._valueString(this.value); 121 | } 122 | } 123 | 124 | class InstVarAssignmentEvent extends Event { 125 | constructor(sourceLoc, env, obj, name, value) { 126 | super(sourceLoc, env); 127 | this.obj = obj; 128 | this.name = name; 129 | this.value = value; 130 | } 131 | 132 | subsumes(that) { 133 | return that instanceof InstVarAssignmentEvent && 134 | this.receiver === that.receiver && this.name === that.name; 135 | } 136 | 137 | toMicroVizString() { 138 | return this._valueString(this.obj) + '.' + this.name + ' = ' + this._valueString(this.value); 139 | } 140 | } 141 | 142 | class InstantiationEvent extends Event { 143 | // TODO: how should we handle the call to init? 144 | constructor(sourceLoc, env, _class, args, newInstance) { 145 | super(sourceLoc, env); 146 | this.class = _class; 147 | this.args = args; 148 | this.newInstance = newInstance; 149 | } 150 | 151 | toMicroVizString() { 152 | return 'new ' + this._valueString(this.class) + ' -> ' + this._valueString(this.newInstance); 153 | } 154 | } 155 | 156 | class ReturnEvent extends Event { 157 | constructor(sourceLoc, env, value) { 158 | super(sourceLoc, env); 159 | this.value = value; 160 | } 161 | 162 | toMicroVizString() { 163 | throw new Error('abstract method'); 164 | } 165 | } 166 | 167 | class LocalReturnEvent extends ReturnEvent { 168 | toMicroVizString() { 169 | return '→ ' + this._valueString(this.value); 170 | } 171 | } 172 | 173 | class NonLocalReturnEvent extends ReturnEvent { 174 | toMicroVizString() { 175 | return 'return ' + this._valueString(this.value); 176 | } 177 | } 178 | 179 | class ShowEvent extends Event { 180 | constructor(sourceLoc, env, string) { 181 | super(sourceLoc, env); 182 | this.string = string; 183 | } 184 | 185 | toMicroVizString() { return this.string; } 186 | } 187 | 188 | class ErrorEvent extends Event { 189 | constructor(sourceLoc, env, errorString) { 190 | super(sourceLoc, env); 191 | this.errorString = errorString; 192 | } 193 | 194 | toMicroVizString() { return '▨'; } 195 | } 196 | -------------------------------------------------------------------------------- /lang/asts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class AST { 4 | constructor(sourceLoc) { 5 | this.sourceLoc = sourceLoc; 6 | } 7 | 8 | toInstruction(next) { 9 | throw new Error('abstract method'); 10 | } 11 | } 12 | 13 | class JSPrim extends AST { 14 | constructor(sourceLoc, code) { 15 | super(sourceLoc); 16 | this.code = code; 17 | try { 18 | this.primFn = eval('(function() {' + code + '})'); 19 | } catch (e) { 20 | console.error('bad JS prim code', code); 21 | throw e; 22 | } 23 | } 24 | 25 | toInstruction(next) { 26 | return new IPrim(this.primFn, this.sourceLoc, next); 27 | } 28 | } 29 | 30 | class Ident extends AST { 31 | constructor(sourceLoc, name) { 32 | super(sourceLoc); 33 | this.name = name; 34 | } 35 | 36 | toInstruction(next) { 37 | throw new Error('not supported'); 38 | } 39 | } 40 | 41 | class Seq extends AST { 42 | constructor(sourceLoc, asts) { 43 | super(sourceLoc); 44 | this.asts = asts; 45 | } 46 | 47 | toInstruction(next) { 48 | return this.asts.reduceRight((rest, ast) => ast.toInstruction(rest), next); 49 | } 50 | } 51 | 52 | class VarDecl extends AST { 53 | constructor(sourceLoc, name, expr) { 54 | super(sourceLoc); 55 | this.name = name; 56 | this.expr = expr; 57 | } 58 | 59 | toInstruction(next) { 60 | return this.expr.toInstruction(new IDeclVar(this.name.name, this.sourceLoc, next)); 61 | } 62 | } 63 | 64 | class VarAssign extends AST { 65 | constructor(sourceLoc, name, expr) { 66 | super(sourceLoc); 67 | this.name = name; 68 | this.expr = expr; 69 | } 70 | 71 | toInstruction(next) { 72 | return this.expr.toInstruction(new IPopIntoVar(this.name.name, this.sourceLoc, next)); 73 | } 74 | } 75 | 76 | class InstVarAssign extends AST { 77 | constructor(sourceLoc, name, expr) { 78 | super(sourceLoc); 79 | this.name = name; 80 | this.expr = expr; 81 | } 82 | 83 | toInstruction(next) { 84 | return this.expr.toInstruction(new IPopIntoInstVar(this.name.name, this.sourceLoc, next)); 85 | } 86 | } 87 | 88 | class NonLocalReturn extends AST { 89 | constructor(sourceLoc, value) { 90 | super(sourceLoc); 91 | this.value = value; 92 | } 93 | 94 | toInstruction(next) { 95 | return this.value.toInstruction(new INonLocalReturn(this.sourceLoc, next)); 96 | } 97 | } 98 | 99 | class ExpStmt extends AST { 100 | constructor(sourceLoc, exp) { 101 | super(sourceLoc); 102 | this.exp = exp; 103 | } 104 | 105 | toInstruction(next) { 106 | return this.exp.toInstruction(new IDrop(next)); 107 | } 108 | } 109 | 110 | class ClassDecl extends AST { 111 | constructor(sourceLoc, name, superClassName, instVarNames) { 112 | super(sourceLoc); 113 | this.name = name; 114 | this.superClassName = superClassName; 115 | this.instVarNames = instVarNames; 116 | } 117 | 118 | toInstruction(next) { 119 | return new IPushFromVar(this.superClassName.name, 120 | new IDeclClass(this.name.name, this.instVarNames.map(ident => ident.name), next)); 121 | } 122 | } 123 | 124 | class MethodDecl extends AST { 125 | constructor(sourceLoc, className, selectorParts, formals, body) { 126 | super(sourceLoc); 127 | this.className = className; 128 | this.selectorParts = selectorParts; 129 | this.formals = formals; 130 | this.body = body; 131 | } 132 | 133 | toInstruction(next) { 134 | return new IPushFromVar(this.className.name, 135 | new IDeclMethod( 136 | this.sourceLoc, 137 | this.selectorParts.map(ident => ident.name).join(''), 138 | this.className, 139 | this.formals, 140 | this.body.toInstruction(new IPush(null, new INonLocalReturn(null))), 141 | next)); 142 | } 143 | } 144 | 145 | class Send extends AST { 146 | constructor(sourceLoc, recv, selectorParts, args, activationPathToken) { 147 | super(sourceLoc); 148 | this.recv = recv; 149 | this.selectorParts = selectorParts; 150 | this.args = args; 151 | this.activationPathToken = activationPathToken; 152 | } 153 | 154 | toInstruction(next) { 155 | const selector = this.selectorParts.map(ident => ident.name).join(''); 156 | return this.recv.toInstruction( 157 | this.args.reduceRight((rest, arg) => arg.toInstruction(rest), 158 | new ISend(selector, this.args.length, this.sourceLoc, this.activationPathToken, next))); 159 | } 160 | } 161 | 162 | class SuperSend extends AST { 163 | constructor(sourceLoc, selectorParts, args, activationPathToken) { 164 | super(sourceLoc); 165 | this.selectorParts = selectorParts; 166 | this.args = args; 167 | this.activationPathToken = activationPathToken; 168 | } 169 | 170 | toInstruction(next) { 171 | const selector = this.selectorParts.map(ident => ident.name).join(''); 172 | return this.args.reduceRight( 173 | (rest, arg) => arg.toInstruction(rest), 174 | new ISuperSend(selector, this.args.length, this.sourceLoc, this.activationPathToken, next)); 175 | } 176 | } 177 | 178 | class Var extends AST { 179 | constructor(sourceLoc, name) { 180 | super(sourceLoc); 181 | this.name = name; 182 | } 183 | 184 | toInstruction(next) { 185 | return new IPushFromVar(this.name, next); 186 | } 187 | } 188 | 189 | class InstVar extends AST { 190 | constructor(sourceLoc, name) { 191 | super(sourceLoc); 192 | this.name = name; 193 | } 194 | 195 | toInstruction(next) { 196 | return new IPushFromInstVar(this.name.name, next); 197 | } 198 | } 199 | 200 | class LocalReturn extends AST { 201 | constructor(sourceLoc, value) { 202 | super(sourceLoc); 203 | this.value = value; 204 | } 205 | 206 | toInstruction(next) { 207 | return this.value.toInstruction(new ILocalReturn(this.sourceLoc, next)); 208 | } 209 | } 210 | 211 | class Block extends AST { 212 | constructor(sourceLoc, formals, bodyExpr) { 213 | super(sourceLoc); 214 | this.formals = formals; 215 | this.bodyExpr = bodyExpr; 216 | } 217 | 218 | toInstruction(next) { 219 | return new IBlock( 220 | this.sourceLoc, 221 | this.formals, 222 | this.bodyExpr.toInstruction(new ILocalReturn(null)), 223 | next); 224 | } 225 | } 226 | 227 | class ArrayLit extends AST { 228 | constructor(sourceLoc, es) { 229 | super(sourceLoc); 230 | this.es = es; 231 | } 232 | 233 | toInstruction(next) { 234 | return this.es.reduceRight( 235 | (rest, e) => e.toInstruction(rest), 236 | new IArray(this.es.length, next)); 237 | } 238 | } 239 | 240 | class New extends AST { 241 | constructor(sourceLoc, _class, args, activationPathToken) { 242 | super(sourceLoc); 243 | this.class = _class; 244 | this.args = args; 245 | this.activationPathToken = activationPathToken; 246 | } 247 | 248 | toInstruction(next) { 249 | return this.class.toInstruction( 250 | new INew( 251 | new IDup( 252 | this.args.reduceRight( 253 | (rest, arg) => arg.toInstruction(rest), 254 | new ISend( 255 | 'init', 256 | this.args.length, 257 | this.sourceLoc, 258 | this.activationPathToken, 259 | new IDrop(next)))))); 260 | } 261 | } 262 | 263 | class Lit extends AST { 264 | constructor(sourceLoc, value) { 265 | super(sourceLoc); 266 | this.value = value; 267 | } 268 | 269 | toInstruction(next) { 270 | return new IPush(this.value, next); 271 | } 272 | } 273 | 274 | class This extends AST { 275 | constructor(sourceLoc) { 276 | super(sourceLoc); 277 | } 278 | 279 | toInstruction(next) { 280 | return new IPushThis(next); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /fira/fira.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: 'Fira Sans'; 3 | src: url('eot/FiraSans-Hair.eot'); 4 | src: local('Fira Sans Hair'), 5 | url('eot/FiraSans-Hair.eot') format('embedded-opentype'), 6 | url('woff/FiraSans-Hair.woff') format('woff'), 7 | url('ttf/FiraSans-Hair.ttf') format('truetype'); 8 | font-weight: 100; 9 | font-style: normal; 10 | } 11 | 12 | @font-face{ 13 | font-family: 'Fira Sans'; 14 | src: url('eot/FiraSans-HairItalic.eot'); 15 | src: local('Fira Sans Hair Italic'), 16 | url('eot/FiraSans-HairItalic.eot') format('embedded-opentype'), 17 | url('woff/FiraSans-HairItalic.woff') format('woff'), 18 | url('ttf/FiraSans-HairItalic.ttf') format('truetype'); 19 | font-weight: 100; 20 | font-style: italic; 21 | } 22 | 23 | @font-face{ 24 | font-family: 'Fira Sans'; 25 | src: url('eot/FiraSans-UltraLight.eot'); 26 | src: local('Fira Sans UltraLight'), 27 | url('eot/FiraSans-UltraLight.eot') format('embedded-opentype'), 28 | url('woff/FiraSans-UltraLight.woff') format('woff'), 29 | url('ttf/FiraSans-UltraLight.ttf') format('truetype'); 30 | font-weight: 200; 31 | font-style: normal; 32 | } 33 | 34 | @font-face{ 35 | font-family: 'Fira Sans'; 36 | src: url('eot/FiraSans-UltraLightItalic.eot'); 37 | src: local('Fira Sans UltraLight Italic'), 38 | url('eot/FiraSans-UltraLightItalic.eot') format('embedded-opentype'), 39 | url('woff/FiraSans-UltraLightItalic.woff') format('woff'), 40 | url('ttf/FiraSans-UltraLightItalic.ttf') format('truetype'); 41 | font-weight: 200; 42 | font-style: italic; 43 | } 44 | 45 | @font-face{ 46 | font-family: 'Fira Sans'; 47 | src: url('eot/FiraSans-Light.eot'); 48 | src: local('Fira Sans Light'), 49 | url('eot/FiraSans-Light.eot') format('embedded-opentype'), 50 | url('woff/FiraSans-Light.woff') format('woff'), 51 | url('ttf/FiraSans-Light.ttf') format('truetype'); 52 | font-weight: 300; 53 | font-style: normal; 54 | } 55 | 56 | @font-face{ 57 | font-family: 'Fira Sans'; 58 | src: url('eot/FiraSans-LightItalic.eot'); 59 | src: local('Fira Sans Light Italic'), 60 | url('eot/FiraSans-LightItalic.eot') format('embedded-opentype'), 61 | url('woff/FiraSans-LightItalic.woff') format('woff'), 62 | url('ttf/FiraSans-LightItalic.ttf') format('truetype'); 63 | font-weight: 300; 64 | font-style: italic; 65 | } 66 | 67 | @font-face{ 68 | font-family: 'Fira Sans'; 69 | src: url('eot/FiraSans-Regular.eot'); 70 | src: local('Fira Sans Regular'), 71 | url('eot/FiraSans-Regular.eot') format('embedded-opentype'), 72 | url('woff/FiraSans-Regular.woff') format('woff'), 73 | url('ttf/FiraSans-Regular.ttf') format('truetype'); 74 | font-weight: 400; 75 | font-style: normal; 76 | } 77 | 78 | @font-face{ 79 | font-family: 'Fira Sans'; 80 | src: url('eot/FiraSans-Italic.eot'); 81 | src: local('Fira Sans Regular Italic'), 82 | url('eot/FiraSans-Italic.eot') format('embedded-opentype'), 83 | url('woff/FiraSans-Italic.woff') format('woff'), 84 | url('ttf/FiraSans-Italic.ttf') format('truetype'); 85 | font-weight: 400; 86 | font-style: italic; 87 | } 88 | 89 | @font-face{ 90 | font-family: 'Fira Sans'; 91 | src: url('eot/FiraSans-Medium.eot'); 92 | src: local('Fira Sans Medium'), 93 | url('eot/FiraSans-Medium.eot') format('embedded-opentype'), 94 | url('woff/FiraSans-Medium.woff') format('woff'), 95 | url('ttf/FiraSans-Medium.ttf') format('truetype'); 96 | font-weight: 500; 97 | font-style: normal; 98 | } 99 | 100 | @font-face{ 101 | font-family: 'Fira Sans'; 102 | src: url('eot/FiraSans-MediumItalic.eot'); 103 | src: local('Fira Sans Medium Italic'), 104 | url('eot/FiraSans-MediumItalic.eot') format('embedded-opentype'), 105 | url('woff/FiraSans-MediumItalic.woff') format('woff'), 106 | url('ttf/FiraSans-MediumItalic.ttf') format('truetype'); 107 | font-weight: 500; 108 | font-style: italic; 109 | } 110 | 111 | @font-face{ 112 | font-family: 'Fira Sans'; 113 | src: url('eot/FiraSans-SemiBold.eot'); 114 | src: local('Fira Sans SemiBold'), 115 | url('eot/FiraSans-SemiBold.eot') format('embedded-opentype'), 116 | url('woff/FiraSans-SemiBold.woff') format('woff'), 117 | url('ttf/FiraSans-SemiBold.ttf') format('truetype'); 118 | font-weight: 600; 119 | font-style: normal; 120 | } 121 | 122 | @font-face{ 123 | font-family: 'Fira Sans'; 124 | src: url('eot/FiraSans-SemiBoldItalic.eot'); 125 | src: local('Fira Sans SemiBold Italic'), 126 | url('eot/FiraSans-SemiBoldItalic.eot') format('embedded-opentype'), 127 | url('woff/FiraSans-SemiBoldItalic.woff') format('woff'), 128 | url('ttf/FiraSans-SemiBoldItalic.ttf') format('truetype'); 129 | font-weight: 600; 130 | font-style: italic; 131 | } 132 | 133 | @font-face{ 134 | font-family: 'Fira Sans'; 135 | src: url('eot/FiraSans-Bold.eot'); 136 | src: local('Fira Sans Bold'), 137 | url('eot/FiraSans-Bold.eot') format('embedded-opentype'), 138 | url('woff/FiraSans-Bold.woff') format('woff'), 139 | url('ttf/FiraSans-Bold.ttf') format('truetype'); 140 | font-weight: 700; 141 | font-style: normal; 142 | } 143 | 144 | @font-face{ 145 | font-family: 'Fira Sans'; 146 | src: url('eot/FiraSans-BoldItalic.eot'); 147 | src: local('Fira Sans Bold Italic'), 148 | url('eot/FiraSans-BoldItalic.eot') format('embedded-opentype'), 149 | url('woff/FiraSans-BoldItalic.woff') format('woff'), 150 | url('ttf/FiraSans-BoldItalic.ttf') format('truetype'); 151 | font-weight: 700; 152 | font-style: italic; 153 | } 154 | 155 | @font-face{ 156 | font-family: 'Fira Sans'; 157 | src: url('eot/FiraSans-ExtraBold.eot'); 158 | src: local('Fira Sans ExtraBold'), 159 | url('eot/FiraSans-ExtraBold.eot') format('embedded-opentype'), 160 | url('woff/FiraSans-ExtraBold.woff') format('woff'), 161 | url('ttf/FiraSans-ExtraBold.ttf') format('truetype'); 162 | font-weight: 800; 163 | font-style: normal; 164 | } 165 | 166 | @font-face{ 167 | font-family: 'Fira Sans'; 168 | src: url('eot/FiraSans-ExtraBoldItalic.eot'); 169 | src: local('Fira Sans ExtraBold Italic'), 170 | url('eot/FiraSans-ExtraBoldItalic.eot') format('embedded-opentype'), 171 | url('woff/FiraSans-ExtraBoldItalic.woff') format('woff'), 172 | url('ttf/FiraSans-ExtraBoldItalic.ttf') format('truetype'); 173 | font-weight: 800; 174 | font-style: italic; 175 | } 176 | 177 | @font-face{ 178 | font-family: 'Fira Sans'; 179 | src: url('eot/FiraSans-Heavy.eot'); 180 | src: local('Fira Sans Heavy'), 181 | url('eot/FiraSans-Heavy.eot') format('embedded-opentype'), 182 | url('woff/FiraSans-Heavy.woff') format('woff'), 183 | url('ttf/FiraSans-Heavy.ttf') format('truetype'); 184 | font-weight: 900; 185 | font-style: normal; 186 | } 187 | 188 | @font-face{ 189 | font-family: 'Fira Sans'; 190 | src: url('eot/FiraSans-HeavyItalic.eot'); 191 | src: local('Fira Sans Heavy Italic'), 192 | url('eot/FiraSans-HeavyItalic.eot') format('embedded-opentype'), 193 | url('woff/FiraSans-HeavyItalic.woff') format('woff'), 194 | url('ttf/FiraSans-HeavyItalic.ttf') format('truetype'); 195 | font-weight: 900; 196 | font-style: italic; 197 | } 198 | 199 | 200 | @font-face{ 201 | font-family: 'Fira Mono'; 202 | src: url('eot/FiraMono-Regular.eot'); 203 | src: local('Fira Mono'), 204 | url('eot/FiraMono-Regular.eot') format('embedded-opentype'), 205 | url('woff/FiraMono-Regular.woff') format('woff'), 206 | url('ttf/FiraMono-Regular.ttf') format('truetype'); 207 | font-weight: 400; 208 | font-style: normal; 209 | } 210 | 211 | @font-face{ 212 | font-family: 'Fira Mono'; 213 | src: url('eot/FiraMono-Bold.eot'); 214 | src: local('Fira Mono Bold'), 215 | url('eot/FiraMono-Bold.eot') format('embedded-opentype'), 216 | url('woff/FiraMono-Bold.woff') format('woff'), 217 | url('ttf/FiraMono-Bold.ttf') format('truetype'); 218 | font-weight: 600; 219 | font-style: normal; 220 | } 221 | 222 | -------------------------------------------------------------------------------- /3rdparty/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | } 9 | 10 | /* PADDING */ 11 | 12 | .CodeMirror-lines { 13 | padding: 4px 0; /* Vertical padding around content */ 14 | } 15 | .CodeMirror pre { 16 | padding: 0 4px; /* Horizontal padding of content */ 17 | } 18 | 19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 20 | background-color: white; /* The little square between H and V scrollbars */ 21 | } 22 | 23 | /* GUTTER */ 24 | 25 | .CodeMirror-gutters { 26 | border-right: 1px solid #ddd; 27 | background-color: #f7f7f7; 28 | white-space: nowrap; 29 | } 30 | .CodeMirror-linenumbers {} 31 | .CodeMirror-linenumber { 32 | padding: 0 3px 0 5px; 33 | min-width: 20px; 34 | text-align: right; 35 | color: #999; 36 | white-space: nowrap; 37 | } 38 | 39 | .CodeMirror-guttermarker { color: black; } 40 | .CodeMirror-guttermarker-subtle { color: #999; } 41 | 42 | /* CURSOR */ 43 | 44 | .CodeMirror-cursor { 45 | border-left: 1px solid black; 46 | border-right: none; 47 | width: 0; 48 | } 49 | /* Shown when moving in bi-directional text */ 50 | .CodeMirror div.CodeMirror-secondarycursor { 51 | border-left: 1px solid silver; 52 | } 53 | .cm-fat-cursor .CodeMirror-cursor { 54 | width: auto; 55 | border: 0 !important; 56 | background: #7e7; 57 | } 58 | .cm-fat-cursor div.CodeMirror-cursors { 59 | z-index: 1; 60 | } 61 | 62 | .cm-animate-fat-cursor { 63 | width: auto; 64 | border: 0; 65 | -webkit-animation: blink 1.06s steps(1) infinite; 66 | -moz-animation: blink 1.06s steps(1) infinite; 67 | animation: blink 1.06s steps(1) infinite; 68 | background-color: #7e7; 69 | } 70 | @-moz-keyframes blink { 71 | 0% {} 72 | 50% { background-color: transparent; } 73 | 100% {} 74 | } 75 | @-webkit-keyframes blink { 76 | 0% {} 77 | 50% { background-color: transparent; } 78 | 100% {} 79 | } 80 | @keyframes blink { 81 | 0% {} 82 | 50% { background-color: transparent; } 83 | 100% {} 84 | } 85 | 86 | /* Can style cursor different in overwrite (non-insert) mode */ 87 | .CodeMirror-overwrite .CodeMirror-cursor {} 88 | 89 | .cm-tab { display: inline-block; text-decoration: inherit; } 90 | 91 | .CodeMirror-rulers { 92 | position: absolute; 93 | left: 0; right: 0; top: -50px; bottom: -20px; 94 | overflow: hidden; 95 | } 96 | .CodeMirror-ruler { 97 | border-left: 1px solid #ccc; 98 | top: 0; bottom: 0; 99 | position: absolute; 100 | } 101 | 102 | /* DEFAULT THEME */ 103 | 104 | .cm-s-default .cm-header {color: blue;} 105 | .cm-s-default .cm-quote {color: #090;} 106 | .cm-negative {color: #d44;} 107 | .cm-positive {color: #292;} 108 | .cm-header, .cm-strong {font-weight: bold;} 109 | .cm-em {font-style: italic;} 110 | .cm-link {text-decoration: underline;} 111 | .cm-strikethrough {text-decoration: line-through;} 112 | 113 | .cm-s-default .cm-keyword {color: #708;} 114 | .cm-s-default .cm-atom {color: #219;} 115 | .cm-s-default .cm-number {color: #164;} 116 | .cm-s-default .cm-def {color: #00f;} 117 | .cm-s-default .cm-variable, 118 | .cm-s-default .cm-punctuation, 119 | .cm-s-default .cm-property, 120 | .cm-s-default .cm-operator {} 121 | .cm-s-default .cm-variable-2 {color: #05a;} 122 | .cm-s-default .cm-variable-3 {color: #085;} 123 | .cm-s-default .cm-comment {color: #a50;} 124 | .cm-s-default .cm-string {color: #a11;} 125 | .cm-s-default .cm-string-2 {color: #f50;} 126 | .cm-s-default .cm-meta {color: #555;} 127 | .cm-s-default .cm-qualifier {color: #555;} 128 | .cm-s-default .cm-builtin {color: #30a;} 129 | .cm-s-default .cm-bracket {color: #997;} 130 | .cm-s-default .cm-tag {color: #170;} 131 | .cm-s-default .cm-attribute {color: #00c;} 132 | .cm-s-default .cm-hr {color: #999;} 133 | .cm-s-default .cm-link {color: #00c;} 134 | 135 | .cm-s-default .cm-error {color: #f00;} 136 | .cm-invalidchar {color: #f00;} 137 | 138 | .CodeMirror-composing { border-bottom: 2px solid; } 139 | 140 | /* Default styles for common addons */ 141 | 142 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 143 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 144 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 145 | .CodeMirror-activeline-background {background: #e8f2ff;} 146 | 147 | /* STOP */ 148 | 149 | /* The rest of this file contains styles related to the mechanics of 150 | the editor. You probably shouldn't touch them. */ 151 | 152 | .CodeMirror { 153 | position: relative; 154 | overflow: hidden; 155 | background: white; 156 | } 157 | 158 | .CodeMirror-scroll { 159 | overflow: scroll !important; /* Things will break if this is overridden */ 160 | /* 30px is the magic margin used to hide the element's real scrollbars */ 161 | /* See overflow: hidden in .CodeMirror */ 162 | margin-bottom: -30px; margin-right: -30px; 163 | padding-bottom: 30px; 164 | height: 100%; 165 | outline: none; /* Prevent dragging from highlighting the element */ 166 | position: relative; 167 | } 168 | .CodeMirror-sizer { 169 | position: relative; 170 | border-right: 30px solid transparent; 171 | } 172 | 173 | /* The fake, visible scrollbars. Used to force redraw during scrolling 174 | before actual scrolling happens, thus preventing shaking and 175 | flickering artifacts. */ 176 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 177 | position: absolute; 178 | z-index: 6; 179 | display: none; 180 | } 181 | .CodeMirror-vscrollbar { 182 | right: 0; top: 0; 183 | overflow-x: hidden; 184 | overflow-y: scroll; 185 | } 186 | .CodeMirror-hscrollbar { 187 | bottom: 0; left: 0; 188 | overflow-y: hidden; 189 | overflow-x: scroll; 190 | } 191 | .CodeMirror-scrollbar-filler { 192 | right: 0; bottom: 0; 193 | } 194 | .CodeMirror-gutter-filler { 195 | left: 0; bottom: 0; 196 | } 197 | 198 | .CodeMirror-gutters { 199 | position: absolute; left: 0; top: 0; 200 | min-height: 100%; 201 | z-index: 3; 202 | } 203 | .CodeMirror-gutter { 204 | white-space: normal; 205 | height: 100%; 206 | display: inline-block; 207 | vertical-align: top; 208 | margin-bottom: -30px; 209 | } 210 | .CodeMirror-gutter-wrapper { 211 | position: absolute; 212 | z-index: 4; 213 | background: none !important; 214 | border: none !important; 215 | } 216 | .CodeMirror-gutter-background { 217 | position: absolute; 218 | top: 0; bottom: 0; 219 | z-index: 4; 220 | } 221 | .CodeMirror-gutter-elt { 222 | position: absolute; 223 | cursor: default; 224 | z-index: 4; 225 | } 226 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 227 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 228 | 229 | .CodeMirror-lines { 230 | cursor: text; 231 | min-height: 1px; /* prevents collapsing before first draw */ 232 | } 233 | .CodeMirror pre { 234 | /* Reset some styles that the rest of the page might have set */ 235 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 236 | border-width: 0; 237 | background: transparent; 238 | font-family: inherit; 239 | font-size: inherit; 240 | margin: 0; 241 | white-space: pre; 242 | word-wrap: normal; 243 | line-height: inherit; 244 | color: inherit; 245 | z-index: 2; 246 | position: relative; 247 | overflow: visible; 248 | -webkit-tap-highlight-color: transparent; 249 | -webkit-font-variant-ligatures: contextual; 250 | font-variant-ligatures: contextual; 251 | } 252 | .CodeMirror-wrap pre { 253 | word-wrap: break-word; 254 | white-space: pre-wrap; 255 | word-break: normal; 256 | } 257 | 258 | .CodeMirror-linebackground { 259 | position: absolute; 260 | left: 0; right: 0; top: 0; bottom: 0; 261 | z-index: 0; 262 | } 263 | 264 | .CodeMirror-linewidget { 265 | position: relative; 266 | z-index: 2; 267 | overflow: auto; 268 | } 269 | 270 | .CodeMirror-widget {} 271 | 272 | .CodeMirror-rtl pre { direction: rtl; } 273 | 274 | .CodeMirror-code { 275 | outline: none; 276 | } 277 | 278 | /* Force content-box sizing for the elements where we expect it */ 279 | .CodeMirror-scroll, 280 | .CodeMirror-sizer, 281 | .CodeMirror-gutter, 282 | .CodeMirror-gutters, 283 | .CodeMirror-linenumber { 284 | -moz-box-sizing: content-box; 285 | box-sizing: content-box; 286 | } 287 | 288 | .CodeMirror-measure { 289 | position: absolute; 290 | width: 100%; 291 | height: 0; 292 | overflow: hidden; 293 | visibility: hidden; 294 | } 295 | 296 | .CodeMirror-cursor { 297 | position: absolute; 298 | pointer-events: none; 299 | } 300 | .CodeMirror-measure pre { position: static; } 301 | 302 | div.CodeMirror-cursors { 303 | visibility: hidden; 304 | position: relative; 305 | z-index: 3; 306 | } 307 | div.CodeMirror-dragcursors { 308 | visibility: visible; 309 | } 310 | 311 | .CodeMirror-focused div.CodeMirror-cursors { 312 | visibility: visible; 313 | } 314 | 315 | .CodeMirror-selected { background: #d9d9d9; } 316 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 317 | .CodeMirror-crosshair { cursor: crosshair; } 318 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 319 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 320 | 321 | .cm-searching { 322 | background: #ffa; 323 | background: rgba(255, 255, 0, .4); 324 | } 325 | 326 | /* Used to force a border model for a node */ 327 | .cm-force-border { padding-right: .1px; } 328 | 329 | @media print { 330 | /* Hide the cursor when printing */ 331 | .CodeMirror div.CodeMirror-cursors { 332 | visibility: hidden; 333 | } 334 | } 335 | 336 | /* See issue #2901 */ 337 | .cm-tab-wrap-hack:after { content: ''; } 338 | 339 | /* Help users use markselection to safely style text background */ 340 | span.CodeMirror-selectedtext { background: none; } 341 | -------------------------------------------------------------------------------- /lang/parse.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const parse = (function() { 4 | 5 | const semantics = seymourGrammar.createSemantics(); 6 | 7 | let programSource; 8 | let lineNumbers; 9 | 10 | semantics.addOperation('toAST()', { 11 | 12 | Program(declsAndStmts) { 13 | lineNumbers = []; 14 | let currLineNumber = 1; 15 | for (let pos = 0; pos < programSource.length; pos++) { 16 | lineNumbers[pos] = currLineNumber; 17 | if (programSource[pos] === '\n') { 18 | currLineNumber++; 19 | } 20 | } 21 | lineNumbers[programSource.length] = currLineNumber; 22 | return new Seq( 23 | new SourceLoc(0, programSource.length, 1, currLineNumber), 24 | declsAndStmts.toAST()); 25 | }, 26 | 27 | Stmt_varDecl(_var, x, _eq, e, _sc) { 28 | return new VarDecl(this.sourceLoc(), x.toIdent(), e.toAST()); 29 | }, 30 | 31 | Stmt_varAssign(x, _eq, e, _sc) { 32 | return new VarAssign(this.sourceLoc(), x.toIdent(), e.toAST()); 33 | }, 34 | 35 | Stmt_instVarAssign(_this, _dot, x, _eq, e, _sc) { 36 | return new InstVarAssign(this.sourceLoc(), x.toIdent(), e.toAST()); 37 | }, 38 | 39 | Stmt_return(_return, e, _sc) { 40 | return new NonLocalReturn(this.sourceLoc(), e.toAST()); 41 | }, 42 | 43 | Stmt_exp(e, _sc) { 44 | return new ExpStmt(this.sourceLoc(), e.toAST()); 45 | }, 46 | 47 | Stmt_js(_oc, _codeChars, _cp) { 48 | var code = this.sourceString.substring(2, this.sourceString.length - 2); 49 | return new JSPrim(this.sourceLoc(), code); 50 | }, 51 | 52 | ClassDecl(_class, C, _optExtends, optS, _optWith, optXs, _sc) { 53 | return new ClassDecl( 54 | this.sourceLoc(), 55 | C.toIdent(), 56 | optS.toIdent()[0] || new Ident(null, 'Object'), 57 | optXs.toIdent()[0] || []); 58 | }, 59 | 60 | MethodDecl_java(_def, C, _dot, m, _op, xs, _cp, b) { 61 | return new MethodDecl(this.sourceLoc(), C.toIdent(), [m.toIdent()], xs.toIdent(), b.toAST()); 62 | }, 63 | 64 | MethodDecl_prefixKeyword(_def, pref, C, selParts, xs, b) { 65 | return new MethodDecl( 66 | this.sourceLoc(), 67 | C.toIdent(), 68 | [pref.toIdent(), new Ident(null, '_')].concat(selParts.toIdent()), 69 | xs.toIdent(), 70 | b.toAST()); 71 | }, 72 | 73 | MethodDecl_keyword(_def, C, selParts, xs, b) { 74 | return new MethodDecl( 75 | this.sourceLoc(), 76 | C.toIdent(), 77 | selParts.toIdent(), 78 | xs.toIdent(), 79 | b.toAST()); 80 | }, 81 | 82 | MethodDecl_binary(_def, C, m, x, b) { 83 | return new MethodDecl(this.sourceLoc(), C.toIdent(), [m.toIdent()], [x.toIdent()], b.toAST()); 84 | }, 85 | 86 | MethodDecl_call(_def, C, _op, xs, _cp, b) { 87 | return new MethodDecl( 88 | this.sourceLoc(), 89 | C.toIdent(), 90 | [new Ident(_op.sourceLoc(), 'call')], 91 | xs.toIdent(), 92 | b.toAST()); 93 | }, 94 | 95 | MethodBody_exp(_eq, e, _sc) { 96 | return new NonLocalReturn(this.sourceLoc(), e.toAST()); 97 | }, 98 | 99 | MethodBody_stmt(_oc, ss, _cc) { 100 | return new Seq(this.sourceLoc(), ss.toAST()); 101 | }, 102 | 103 | KWSendExp_prefixKeyword(pref, e, selParts, es) { 104 | return new Send( 105 | this.sourceLoc(), 106 | e.toAST(), 107 | [pref.toIdent(), new Ident(null, '_')].concat(selParts.toIdent()), 108 | es.toAST(), 109 | pref.toActivationPathToken()); 110 | }, 111 | 112 | KWSendExp_keyword(e, selParts, es) { 113 | return new Send( 114 | this.sourceLoc(), 115 | e.toAST(), 116 | selParts.toIdent(), 117 | es.toAST(), 118 | selParts.toActivationPathToken()[0]); 119 | }, 120 | 121 | KWSendExp_super(_super, selParts, es) { 122 | return new SuperSend( 123 | this.sourceLoc(), 124 | selParts.toIdent(), 125 | es.toAST(), 126 | _super.toActivationPathToken()); 127 | }, 128 | 129 | EqExp_eq(x, op, y) { 130 | return new Send( 131 | this.sourceLoc(), x.toAST(), 132 | [op.toIdent()], 133 | [y.toAST()], 134 | op.toActivationPathToken()); 135 | }, 136 | 137 | RelExp_rel(x, op, y) { 138 | return new Send( 139 | this.sourceLoc(), 140 | x.toAST(), 141 | [op.toIdent()], 142 | [y.toAST()], 143 | op.toActivationPathToken()); 144 | }, 145 | 146 | AddExp_add(x, op, y) { 147 | return new Send( 148 | this.sourceLoc(), 149 | x.toAST(), 150 | [op.toIdent()], 151 | [y.toAST()], 152 | op.toActivationPathToken()); 153 | }, 154 | 155 | MulExp_mul(x, op, y) { 156 | return new Send( 157 | this.sourceLoc(), 158 | x.toAST(), 159 | [op.toIdent()], 160 | [y.toAST()], 161 | op.toActivationPathToken()); 162 | }, 163 | 164 | DotExp_call(b, op, es, _cp) { 165 | return new Send( 166 | this.sourceLoc(), 167 | b.toAST(), 168 | [new Ident(null, 'call')], 169 | es.toAST(), 170 | op.toActivationPathToken()); 171 | }, 172 | 173 | DotExp_send(e, dot, m, _op, es, _cp) { 174 | return new Send( 175 | this.sourceLoc(), 176 | e.toAST(), 177 | [m.toIdent()], 178 | es.toAST(), 179 | dot.toActivationPathToken()); 180 | }, 181 | 182 | DotExp_superSend(_super, dot, m, _op, es, _cp) { 183 | return new SuperSend( 184 | this.sourceLoc(), 185 | [m.toIdent()], 186 | es.toAST(), 187 | dot.toActivationPathToken()); 188 | }, 189 | 190 | DotExp_instVarAccess(_this, _dot, x) { 191 | return new InstVar(this.sourceLoc(), x.toIdent()); 192 | }, 193 | 194 | UnExp_neg(minus, x) { 195 | return new Send( 196 | this.sourceLoc(), 197 | new Lit(null, 0), 198 | [minus.toIdent()], 199 | [x.toAST()], 200 | minus.toActivationPathToken()); 201 | }, 202 | 203 | PriExp_paren(_op, e, _cp) { 204 | return e.toAST(); 205 | }, 206 | 207 | PriExp_block(_oc, optXs, ss, optE, _cc) { 208 | const xs = optXs.toIdent()[0] || []; 209 | const body = ss.toAST(); 210 | const e = optE.toAST()[0]; 211 | const returnExp = e || new Lit(null, null); 212 | body.push(new LocalReturn(returnExp.sourceLoc, returnExp)); 213 | return new Block(this.sourceLoc(), xs, new Seq(null, body)); 214 | }, 215 | 216 | PriExp_array(_os, es, _cs) { 217 | return new ArrayLit(this.sourceLoc(), es.toAST()); 218 | }, 219 | 220 | PriExp_new(_new, C, _op, es, _cp) { 221 | return new New(this.sourceLoc(), C.toAST(), es.toAST(), _new.toActivationPathToken()); 222 | }, 223 | 224 | PriExp_str(s) { 225 | return new Lit(this.sourceLoc(), s.stringValue()); 226 | }, 227 | 228 | PriExp_var(x) { 229 | return new Var(this.sourceLoc(), x.sourceString); 230 | }, 231 | 232 | PriExp_class(C) { 233 | return new Var(this.sourceLoc(), C.sourceString); 234 | }, 235 | 236 | PriExp_number(_) { 237 | return new Lit(this.sourceLoc(), parseFloat(this.sourceString)); 238 | }, 239 | 240 | PriExp_this(_) { 241 | return new This(this.sourceLoc()); 242 | }, 243 | 244 | PriExp_true(_) { 245 | return new Lit(this.sourceLoc(), true); 246 | }, 247 | 248 | PriExp_false(_) { 249 | return new Lit(this.sourceLoc(), false); 250 | }, 251 | 252 | PriExp_null(_) { 253 | return new Lit(this.sourceLoc(), null); 254 | }, 255 | 256 | NonemptyListOf(x, _seps, xs) { 257 | return [x.toAST()].concat(xs.toAST()); 258 | }, 259 | 260 | EmptyListOf() { 261 | return []; 262 | } 263 | 264 | }); 265 | 266 | semantics.addOperation('toIdent()', { 267 | 268 | _nonterminal(children) { 269 | return new Ident(this.sourceLoc(), this.sourceString); 270 | }, 271 | 272 | _terminal() { 273 | return new Ident(this.sourceLoc(), this.sourceString); 274 | }, 275 | 276 | BlockArgNames(xs, _bar) { 277 | return xs.toIdent(); 278 | }, 279 | 280 | ListOf(x) { 281 | return x.toIdent(); 282 | }, 283 | 284 | NonemptyListOf(x, _seps, xs) { 285 | return [x.toIdent()].concat(xs.toIdent()); 286 | }, 287 | 288 | EmptyListOf() { 289 | return []; 290 | } 291 | 292 | }); 293 | 294 | semantics.addOperation('toActivationPathToken()', { 295 | 296 | _nonterminal(children) { 297 | return this._node; 298 | }, 299 | 300 | _terminal() { 301 | return this._node; 302 | } 303 | 304 | }); 305 | 306 | semantics.addOperation('sourceLoc()', { 307 | 308 | _nonterminal(children) { 309 | return createSourceLoc(this.source.startIdx, this.source.endIdx); 310 | }, 311 | 312 | _terminal() { 313 | return createSourceLoc(this.source.startIdx, this.source.endIdx); 314 | }, 315 | 316 | ListOf(x) { 317 | return x.sourceLoc(); 318 | }, 319 | 320 | NonemptyListOf(x, _seps, xs) { 321 | return [x.sourceLoc()].concat(xs.sourceLoc()); 322 | }, 323 | 324 | EmptyListOf() { 325 | return []; 326 | } 327 | 328 | }); 329 | 330 | semantics.addOperation('stringValue()', { 331 | 332 | string(_oq, cs, _cq) { 333 | const chars = []; 334 | let idx = 0; 335 | cs = cs.sourceString; 336 | while (idx < cs.length) { 337 | let c = cs[idx++]; 338 | if (c === '\\' && idx < cs.length) { 339 | c = cs[idx++]; 340 | switch (c) { 341 | case 'n': c = '\n'; break; 342 | case 't': c = '\t'; break; 343 | default: idx--; 344 | } 345 | } 346 | chars.push(c); 347 | } 348 | return chars.join(''); 349 | } 350 | 351 | }); 352 | 353 | let includeSourceLocs; 354 | 355 | function createSourceLoc(startPos, endPos) { 356 | return includeSourceLocs ? 357 | new SourceLoc(startPos, endPos, lineNumbers[startPos], lineNumbers[endPos]) : 358 | null; 359 | } 360 | 361 | function parse(matchResult, optIncludeSourceLocs) { 362 | includeSourceLocs = arguments.length === 1 ? true : !!optIncludeSourceLocs; 363 | if (matchResult.succeeded()) { 364 | programSource = matchResult.input; 365 | return semantics(matchResult).toAST(); 366 | } else { 367 | throw new Error(matchResult.message); 368 | } 369 | } 370 | 371 | return parse; 372 | })(); 373 | -------------------------------------------------------------------------------- /lang/prelude.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const preludeAST = (function() { 4 | 5 | const source = ` 6 | 7 | def Object.init() {} 8 | def Object.getClass() { 9 | var ans = null; 10 | @{ this.setVar('ans', this.classOf(this.receiver)); }@ 11 | return ans; 12 | } 13 | def Object == that { 14 | var ans = null; 15 | @{ this.setVar('ans', this.receiver === this.getVar('that')); }@ 16 | return ans; 17 | } 18 | def Object != that { 19 | var ans = null; 20 | @{ this.setVar('ans', this.receiver !== this.getVar('that')); }@ 21 | return ans; 22 | } 23 | def Object.log() { 24 | @{ console.log('>>', this.receiver); }@ 25 | return null; 26 | } 27 | def Object.println() = this.toString().log(); 28 | def Object.show() { 29 | var str = this.toDebugString(); 30 | var defaultStr = this.toIdString(); 31 | @{ this.R.show( 32 | this.env.programOrSendEvent.sourceLoc, 33 | this.env.programOrSendEvent.env, 34 | this.getVar('str'), 35 | this.getVar('defaultStr') 36 | ); }@ 37 | return this; 38 | } 39 | def Object.toIdString() { 40 | var ans = null; 41 | @{ 42 | const thisObj = this.receiver; 43 | this.setVar('ans', thisObj instanceof Obj ? '#' + thisObj.id : '' + thisObj); 44 | }@ 45 | return ans; 46 | } 47 | def Object.toString() = this.toIdString(); 48 | def Object.toDebugString() = this.toIdString(); 49 | 50 | def Block.call() { 51 | // The implementation of this method is in Activation.ISend -- see activations.js 52 | @{ throw new Error('Block\\'s call method should never be called directly'); }@ 53 | } 54 | 55 | class Class; 56 | def Class.name() { 57 | var ans = null; 58 | @{ this.setVar('ans', this.receiver.name); }@ 59 | return ans; 60 | } 61 | def Class.toString() = "class " + this.name(); 62 | def Class.toDebugString() = "class " + this.name(); 63 | 64 | class Null; 65 | def Null.toString() = "null"; 66 | 67 | class Comparable; 68 | def Comparable < that { 69 | var ans = null; 70 | @{ this.setVar('ans', this.receiver < this.getVar('that')); }@ 71 | return ans; 72 | } 73 | def Comparable <= that { 74 | var ans = null; 75 | @{ this.setVar('ans', this.receiver <= this.getVar('that')); }@ 76 | return ans; 77 | } 78 | def Comparable > that { 79 | var ans = null; 80 | @{ this.setVar('ans', this.receiver > this.getVar('that')); }@ 81 | return ans; 82 | } 83 | def Comparable >= that { 84 | var ans = null; 85 | @{ this.setVar('ans', this.receiver >= this.getVar('that')); }@ 86 | return ans; 87 | } 88 | 89 | class String extends Comparable; 90 | def String.size() { 91 | var ans = null; 92 | @{ this.setVar('ans', this.receiver.length); }@ 93 | return ans; 94 | } 95 | def String.get(idx) { 96 | if (1 <= idx and: {idx <= this.size()}) then: { 97 | var ans = null; 98 | @{ this.setVar('ans', this.receiver[this.getVar('idx') - 1]); }@ 99 | return ans; 100 | } else: { 101 | new IndexOutOfBounds(this, idx).throw(); 102 | }; 103 | } 104 | def String + that { 105 | var ans = that.toString(); 106 | @{ this.setVar('ans', this.receiver + this.getVar('ans')); }@ 107 | return ans; 108 | } 109 | def String.toString() = this; 110 | def String.toIdString() { 111 | var ans = null; 112 | @{ this.setVar('ans', JSON.stringify(this.receiver)); }@ 113 | return ans; 114 | } 115 | 116 | class Number extends Comparable; 117 | def Number + that { 118 | var ans = null; 119 | @{ this.setVar('ans', this.receiver + this.getVar('that')); }@ 120 | return ans; 121 | } 122 | def Number - that { 123 | var ans = null; 124 | @{ this.setVar('ans', this.receiver - this.getVar('that')); }@ 125 | return ans; 126 | } 127 | def Number * that { 128 | var ans = null; 129 | @{ this.setVar('ans', this.receiver * this.getVar('that')); }@ 130 | return ans; 131 | } 132 | def Number / that { 133 | var ans = null; 134 | @{ this.setVar('ans', this.receiver / this.getVar('that')); }@ 135 | return ans; 136 | } 137 | def Number % that { 138 | var ans = null; 139 | @{ this.setVar('ans', this.receiver % this.getVar('that')); }@ 140 | return ans; 141 | } 142 | def Number.sqrt() { 143 | var ans = null; 144 | @{ this.setVar('ans', Math.sqrt(this.receiver)); }@ 145 | return ans; 146 | } 147 | def Number.floor() { 148 | var ans = null; 149 | @{ this.setVar('ans', Math.floor(this.receiver)); }@ 150 | return ans; 151 | } 152 | def Number.ceil() { 153 | var ans = null; 154 | @{ this.setVar('ans', Math.ceil(this.receiver)); }@ 155 | return ans; 156 | } 157 | def Number.min(other) { 158 | var ans = null; 159 | @{ this.setVar('ans', Math.min(this.receiver, this.getVar('other'))); }@ 160 | return ans; 161 | } 162 | def Number.max(other) { 163 | var ans = null; 164 | @{ this.setVar('ans', Math.max(this.receiver, this.getVar('other'))); }@ 165 | return ans; 166 | } 167 | 168 | class Boolean; 169 | 170 | class True extends Boolean; 171 | def True.not() = false; 172 | def True and: block = block(); 173 | def True or: block = true; 174 | def if True then: tb = tb(); 175 | def if True then: tb else: fb = tb(); 176 | 177 | class False extends Boolean; 178 | def False.not() = true; 179 | def False and: block = false; 180 | def False or: block = block(); 181 | def if False then: tb = null; 182 | def if False then: tb else: fb = fb(); 183 | 184 | def for Number to: end do: body = for this to: end by: 1 do: body; 185 | def for Number to: end by: step do: body { 186 | var idx = this; 187 | while {idx <= end} do: { 188 | body(idx); 189 | idx = idx + 1; 190 | }; 191 | return null; 192 | } 193 | 194 | class Array with size; 195 | def Array.init(size) { 196 | if (size.getClass() != Number or: {size < 0}) then: { 197 | new InvalidArgument(this, "init", "size", size).throw(); 198 | }; 199 | this.size = size; 200 | } 201 | def Array.size() = this.size; 202 | def Array.get(idx) { 203 | if (idx.getClass() != Number or: {idx < 1 or: {idx > this.size}}) then: { 204 | new IndexOutOfBounds(this, idx).throw(); 205 | }; 206 | var ans = null; 207 | @{ 208 | let value = this.receiver.instVars[this.getVar('idx')]; 209 | this.setVar('ans', value === undefined ? null : value); 210 | }@ 211 | return ans; 212 | } 213 | def Array.set(idx, value) { 214 | if (idx.getClass() != Number or: {idx < 1 or: {idx > this.size}}) then: { 215 | new IndexOutOfBounds(this, idx).throw(); 216 | }; 217 | @{ 218 | this.receiver.instVars[this.getVar('idx')] = this.getVar('value'); 219 | this.R.assignInstVar(null, this.env, this.receiver, this.getVar('idx'), this.getVar('value')); 220 | }@ 221 | } 222 | def forEach Array do: fn { 223 | for 1 to: this.size do: {idx | 224 | fn(this.get(idx), idx); 225 | }; 226 | } 227 | def Array.map(fn) { 228 | var ans = new Array(this.size); 229 | forEach this do: {x, idx | 230 | var value = fn(x); 231 | ans.set(idx, value); 232 | }; 233 | return ans; 234 | } 235 | def Array.filter(fn) { 236 | var predVals = new Array(this.size()); 237 | var numItems = 0; 238 | forEach this do: {x, i| 239 | predVals.set(i, fn(x)); 240 | if predVals.get(i) then: { numItems = numItems + 1; }; 241 | }; 242 | 243 | var ans = new Array(numItems); 244 | var idx = 1; 245 | forEach this do: {x, i| 246 | if predVals.get(i) then: { 247 | ans.set(idx, x); 248 | idx = idx + 1; 249 | }; 250 | }; 251 | return ans; 252 | } 253 | def Array.reduce(fn, z) { 254 | var acc = z; 255 | forEach this do: {x | 256 | acc = fn(acc, x); 257 | }; 258 | return acc; 259 | } 260 | def Array.toString() { 261 | var first = true; 262 | var meat = ""; 263 | forEach this do: {x | 264 | if first then: { 265 | first = false; 266 | } else: { 267 | meat = meat + ", "; 268 | }; 269 | meat = meat + x.toString(); 270 | }; 271 | return "[" + meat + "]"; 272 | } 273 | 274 | class Dictionary; 275 | def Dictionary.init(kvPairs) { 276 | forEach kvPairs do: {pair | 277 | var key = pair.get(1); 278 | var val = pair.get(2); 279 | @{ 280 | this.receiver.instVars[this.getVar('key')] = this.getVar('val'); 281 | this.R.assignInstVar(null, this.env, this.receiver, this.getVar('key'), this.getVar('val')); 282 | }@ 283 | }; 284 | } 285 | def Dictionary.has(key) { 286 | var ans = null; 287 | @{ this.setVar('ans', this.getVar('key') in this.receiver.instVars); }@ 288 | return ans; 289 | } 290 | def Dictionary.get(key) { 291 | var ans = null; 292 | @{ this.setVar('ans', this.receiver.instVars[this.getVar('key')]); }@ 293 | return ans; 294 | } 295 | def Dictionary.set(key, value) { 296 | var ans = this.get(key); 297 | @{ 298 | this.receiver.instVars[this.getVar('key')] = this.getVar('value'); 299 | this.R.assignInstVar(null, this.env, this.receiver, this.getVar('key'), this.getVar('value')); 300 | }@ 301 | return ans; 302 | } 303 | 304 | def Number to: end { 305 | var size = end - this + 1; 306 | var arr = new Array(size); 307 | for 1 to: size do: {idx | 308 | var num = this + idx - 1; 309 | arr.set(idx, num); 310 | }; 311 | return arr; 312 | } 313 | 314 | // Exceptions 315 | 316 | def Object.throw() { 317 | @{ this.throw(this.receiver); }@ 318 | } 319 | 320 | def try Block on: exceptionClass do: catchBlock { 321 | "TODO".throw(); 322 | } 323 | 324 | def try Block catch: catchBlock { 325 | return try this on: Object do: catchBlock; 326 | } 327 | 328 | class Exception; 329 | def Exception.toString() { 330 | return "[" + this.getClass().name() + " exception]"; 331 | } 332 | 333 | class IndexOutOfBounds extends Exception with collection, index; 334 | def IndexOutOfBounds.init(collection, index) { 335 | this.collection = collection; 336 | this.index = index; 337 | } 338 | 339 | class InvalidArgument extends Exception with receiver, methodName, argName, argValue; 340 | def InvalidArgument.init(receiver, methodName, argName, argValue) { 341 | this.receiver = receiver; 342 | this.methodName = methodName; 343 | this.argName = argName; 344 | this.argValue = argValue; 345 | } 346 | 347 | `; 348 | 349 | try { 350 | const matchResult = seymourGrammar.match(source); 351 | return parse(matchResult, false); 352 | } catch (e) { 353 | console.error('Parse error in prelude -- details below'); 354 | console.error(e); 355 | throw e; 356 | } 357 | })(); -------------------------------------------------------------------------------- /lang/activations.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Activation { 4 | constructor(args, formals, parent, caller, sourceLoc, code) { 5 | if (caller) { 6 | this.R = caller.R; 7 | this.env = this.R.mkEnv(sourceLoc, parent.env, caller.env); 8 | } 9 | this.args = args; 10 | this.varDeclActivations = Object.create(parent === null ? null : parent.varDeclActivations); 11 | this.varValues = Object.create(null); 12 | if (args.length < formals.length) { 13 | throw new Error('not enough arguments'); 14 | } 15 | this.formals = formals; 16 | this.parent = parent; 17 | this.caller = caller; 18 | this.sourceLoc = sourceLoc; 19 | this.stack = []; 20 | this.nextInstruction = code; 21 | } 22 | 23 | declareFormals() { 24 | this.formals.forEach( 25 | (formal, idx) => this.declVar(formal.sourceLoc, formal.name, this.args[idx])); 26 | } 27 | 28 | step() { 29 | return this.nextInstruction.eval(this); 30 | } 31 | 32 | get receiver() { 33 | throw new Error('subclass responsibility'); 34 | } 35 | 36 | get topLevelActivation() { 37 | throw new Error('subclass responsibility'); 38 | } 39 | 40 | get methodActivation() { 41 | throw new Error('subclass responsibility'); 42 | } 43 | 44 | declVar(sourceLoc, name, value) { 45 | if (this.varDeclActivations[name] === this) { 46 | throw new Error('duplicate declaration for ' + name); 47 | } 48 | this.varDeclActivations[name] = this; 49 | this.varValues[name] = value; 50 | if (sourceLoc) { 51 | this.R.declVar(sourceLoc, this.env, name, value); 52 | } 53 | } 54 | 55 | setVar(name, value) { 56 | const declActivation = this.varDeclActivations[name]; 57 | if (!declActivation) { 58 | throw new Error('undeclared variable ' + name); 59 | } else { 60 | declActivation.varValues[name] = value; 61 | } 62 | } 63 | 64 | classOf(value) { 65 | if (value instanceof BlockClosure) { 66 | return this.topLevelActivation.varValues.Block; 67 | } else if (value instanceof Class) { 68 | return this.topLevelActivation.varValues.Class; 69 | } else if (value instanceof Obj) { 70 | return value.class; 71 | } else if (typeof value === 'number') { 72 | return this.topLevelActivation.varValues.Number; 73 | } else if (typeof value === 'string') { 74 | return this.topLevelActivation.varValues.String; 75 | } else if (value === null) { 76 | return this.topLevelActivation.varValues.Null; 77 | } else if (value === true) { 78 | return this.topLevelActivation.varValues.True; 79 | } else if (value === false) { 80 | return this.topLevelActivation.varValues.False; 81 | } else { 82 | console.error(value); 83 | throw new Error('not sure what is the class of this object'); 84 | } 85 | } 86 | 87 | getVar(name) { 88 | const declActivation = this.varDeclActivations[name]; 89 | if (!declActivation) { 90 | throw new Error('undeclared variable ' + name); 91 | } else { 92 | return declActivation.varValues[name]; 93 | } 94 | } 95 | 96 | IPrim(primFn, sourceLoc, nextInstruction) { 97 | primFn.call(this); 98 | this.nextInstruction = nextInstruction; 99 | return this; 100 | } 101 | 102 | IPush(value, nextInstruction) { 103 | this.stack.push(value); 104 | this.nextInstruction = nextInstruction; 105 | return this; 106 | } 107 | 108 | IPushThis(nextInstruction) { 109 | return this.IPush(this.receiver, nextInstruction); 110 | } 111 | 112 | IPushFromVar(name, nextInstruction) { 113 | this.stack.push(this.getVar(name)); 114 | this.nextInstruction = nextInstruction; 115 | return this; 116 | } 117 | 118 | IPushFromInstVar(name, nextInstruction) { 119 | const value = this.receiver.getInstVar(name); 120 | this.stack.push(value); 121 | this.nextInstruction = nextInstruction; 122 | return this; 123 | } 124 | 125 | IPopIntoVar(name, sourceLoc, nextInstruction) { 126 | this.assertStackContainsAtLeastThisManyElements(1); 127 | const value = this.stack.pop(); 128 | this.setVar(name, value); 129 | if (sourceLoc) { 130 | this.R.assignVar(sourceLoc, this.env, this.varDeclActivations[name].env, name, value); 131 | } 132 | this.nextInstruction = nextInstruction; 133 | return this; 134 | } 135 | 136 | IPopIntoInstVar(name, sourceLoc, nextInstruction) { 137 | this.assertStackContainsAtLeastThisManyElements(1); 138 | const value = this.stack.pop(); 139 | this.receiver.setInstVar(name, value); 140 | if (sourceLoc) { 141 | this.R.assignInstVar(sourceLoc, this.env, this.receiver, name, value); 142 | } 143 | this.nextInstruction = nextInstruction; 144 | return this; 145 | } 146 | 147 | IDeclVar(name, sourceLoc, nextInstruction) { 148 | this.assertStackContainsAtLeastThisManyElements(1); 149 | const value = this.stack.pop(); 150 | this.declVar(sourceLoc, name, value); 151 | this.nextInstruction = nextInstruction; 152 | return this; 153 | } 154 | 155 | IDup(nextInstruction) { 156 | this.assertStackContainsAtLeastThisManyElements(1); 157 | const value = this.stack[this.stack.length - 1]; 158 | this.stack.push(value); 159 | console.debug('duplicated', value); 160 | this.nextInstruction = nextInstruction; 161 | return this; 162 | } 163 | 164 | IDrop(nextInstruction) { 165 | this.assertStackContainsAtLeastThisManyElements(1); 166 | const value = this.stack.pop(); 167 | console.debug('dropped', value); 168 | this.nextInstruction = nextInstruction; 169 | return this; 170 | } 171 | 172 | ICond(nextInstructionIfTrue, nextInstructionIfFalse) { 173 | this.assertStackContainsAtLeastThisManyElements(1); 174 | const value = this.stack.pop(); 175 | this.nextInstruction = value ? nextInstructionIfTrue : nextInstructionIfFalse; 176 | return this; 177 | } 178 | 179 | IBlock(sourceLoc, formals, code, nextInstruction) { 180 | this.stack.push(new BlockClosure(sourceLoc, formals, code, this)); 181 | this.nextInstruction = nextInstruction; 182 | return this; 183 | } 184 | 185 | IDeclClass(name, instVarNames, nextInstruction) { 186 | throw new Error('class declarations are only allowed in the top-level activation'); 187 | } 188 | 189 | IDeclMethod(sourceLoc, selector, className, formals, code, nextInstruction) { 190 | throw new Error('method declarations are only allowed in the top-level activation'); 191 | } 192 | 193 | INew(nextInstruction) { 194 | this.assertStackContainsAtLeastThisManyElements(1); 195 | const _class = this.stack.pop(); 196 | if (!(_class instanceof Class)) { 197 | throw new Error('not a class'); 198 | } 199 | const instance = _class.makeNewInstance(); 200 | this.stack.push(instance); 201 | console.debug('made a new instance of', _class, instance); 202 | this.nextInstruction = nextInstruction; 203 | return this; 204 | } 205 | 206 | IArray(size, nextInstruction) { 207 | this.assertStackContainsAtLeastThisManyElements(size); 208 | const arr = this.getVar('Array').makeNewInstance(); 209 | arr.setInstVar('size', size); 210 | for (let idx = size; idx >= 1; idx--) { 211 | const value = this.stack.pop(); 212 | arr.instVars[idx] = value; 213 | } 214 | this.stack.push(arr); 215 | this.nextInstruction = nextInstruction; 216 | return this; 217 | } 218 | 219 | ISend(selector, numArgs, sourceLoc, activationPathToken, nextInstruction) { 220 | this.assertStackContainsAtLeastThisManyElements(numArgs + 1); 221 | 222 | const args = []; 223 | for (let idx = 0; idx < numArgs; idx++) { 224 | args.unshift(this.stack.pop()); 225 | } 226 | const receiver = this.stack.pop(); 227 | console.debug('send:', receiver, '.', selector, '(', ...args, ')', sourceLoc, activationPathToken); 228 | 229 | this.R.send(sourceLoc, this.env, receiver, selector, args, activationPathToken); 230 | 231 | if (receiver instanceof BlockClosure && selector === 'call') { 232 | this.nextInstruction = nextInstruction; 233 | return new BlockActivation(receiver, args, this); 234 | } else { 235 | const receiversClass = this.classOf(receiver); 236 | const method = receiversClass.getMethod(selector); 237 | this.nextInstruction = nextInstruction; 238 | return new MethodActivation(method, receiver, args, this.topLevelActivation, this); 239 | } 240 | } 241 | 242 | ISuperSend(selector, numArgs, sourceLoc, activationPathToken, nextInstruction) { 243 | const methodActivation = this.methodActivation; 244 | if (!methodActivation) { 245 | throw new Error('super-sends are only allowed inside a method'); 246 | } 247 | this.assertStackContainsAtLeastThisManyElements(numArgs); 248 | const args = []; 249 | for (let idx = 0; idx < numArgs; idx++) { 250 | args.unshift(this.stack.pop()); 251 | } 252 | console.debug('super send:', this.receiver, '.', selector, '(', ...args, ')', sourceLoc, activationPathToken); 253 | 254 | this.R.send(sourceLoc, this.env, receiver, selector, args, activationPathToken); 255 | 256 | const _class = methodActivation.method.class.superClass; 257 | const method = _class.getMethod(selector); 258 | this.nextInstruction = nextInstruction; 259 | return new MethodActivation(method, this.receiver, args, this.topLevelActivation, this); 260 | } 261 | 262 | ILocalReturn(sourceLoc) { 263 | throw new Error('can only return from a block activation'); 264 | } 265 | 266 | INonLocalReturn(sourceLoc) { 267 | this.assertStackContainsAtLeastThisManyElements(1); 268 | const value = this.stack.pop(); 269 | console.debug('(non-local) returning', value, sourceLoc); 270 | const methodActivation = this.methodActivation; 271 | let activation = this; 272 | while (activation !== null) { 273 | if (activation === methodActivation) { 274 | const caller = methodActivation.caller; 275 | caller.stack.push(value); 276 | this.nextInstruction = null; 277 | if (sourceLoc) { 278 | this.R.nonLocalReturn(sourceLoc, this.env, value); 279 | } 280 | caller.R.receive(caller.env, value); 281 | return caller; 282 | } 283 | activation = activation.caller; 284 | } 285 | throw new Error('cannot return from a method activation that is no longer on the stack'); 286 | } 287 | 288 | throw(e) { 289 | // * unwind stack until try _ on: E do: C 290 | // w/ classOf(E) <: E is found 291 | // * call C(e) 292 | // * but what's the sourceLoc of the call? etc. 293 | debugger; 294 | } 295 | 296 | IDone() { 297 | throw new Error('`done` instruction is only valid in top-level activation'); 298 | } 299 | 300 | assertStackContainsAtLeastThisManyElements(n) { 301 | if (this.stack.length < n) { 302 | throw new Error('stack should contain at least ' + n + ' elements'); 303 | } 304 | } 305 | } 306 | 307 | class TopLevelActivation extends Activation { 308 | constructor(sourceLoc, code, R) { 309 | super([], [], null, null, sourceLoc, code); 310 | this.R = R; 311 | this.env = R.program(sourceLoc); 312 | this.installBuiltins(); 313 | } 314 | 315 | hasSourceLoc() { 316 | return !!this.sourceLoc; 317 | } 318 | 319 | get receiver() { 320 | throw new Error('cannot use `this` in top-level activation'); 321 | } 322 | 323 | get topLevelActivation() { 324 | return this; 325 | } 326 | 327 | get methodActivation() { 328 | return null; 329 | } 330 | 331 | IDeclClass(name, instVarNames, nextInstruction) { 332 | this.assertStackContainsAtLeastThisManyElements(1); 333 | const superClass = this.stack.pop(); 334 | const _class = new Class(name, superClass, instVarNames); 335 | if (!(_class instanceof Class)) { 336 | throw new Error('not a class!'); 337 | } 338 | this.declVar(null, name, _class); 339 | this.nextInstruction = nextInstruction; 340 | return this; 341 | } 342 | 343 | IDeclMethod(sourceLoc, selector, className, formals, code, nextInstruction) { 344 | this.assertStackContainsAtLeastThisManyElements(1); 345 | const _class = this.stack.pop(); 346 | if (!(_class instanceof Class)) { 347 | throw new Error('not a class!'); 348 | } 349 | _class.declMethod(sourceLoc, selector, className, formals, code); 350 | this.nextInstruction = nextInstruction; 351 | return this; 352 | } 353 | 354 | INonLocalReturn(sourceLoc) { 355 | throw new Error('cannot return from top-level activation'); 356 | } 357 | 358 | IDone() { 359 | if (this.stack.length > 0) { 360 | throw new Error('expected stack to be empty'); 361 | } 362 | return null; 363 | } 364 | } 365 | 366 | class MethodActivation extends Activation { 367 | constructor(method, receiver, args, parent, caller) { 368 | super(args, method.formals, parent, caller, method.sourceLoc, method.code); 369 | this.method = method; 370 | this._receiver = receiver; 371 | this.declareFormals(); 372 | } 373 | 374 | declareFormals() { 375 | this.declVar(this.method.className.sourceLoc, 'this', this.receiver); 376 | super.declareFormals(); 377 | } 378 | 379 | hasSourceLoc() { 380 | return !!this.method.sourceLoc; 381 | } 382 | 383 | get receiver() { 384 | return this._receiver; 385 | } 386 | 387 | get topLevelActivation() { 388 | return this.parent; 389 | } 390 | 391 | get methodActivation() { 392 | return this; 393 | } 394 | } 395 | 396 | class BlockActivation extends Activation { 397 | constructor(closure, args, caller) { 398 | super(args, closure.formals, closure.parent, caller, closure.sourceLoc, closure.code); 399 | this.blockClosure = closure; 400 | this.declareFormals(); 401 | } 402 | 403 | hasSourceLoc() { 404 | return !!this.blockClosure.sourceLoc; 405 | } 406 | 407 | get receiver() { 408 | return this.methodActivation.receiver; 409 | } 410 | 411 | get topLevelActivation() { 412 | return this.methodActivation ? 413 | this.methodActivation.topLevelActivation : 414 | this.caller.topLevelActivation; 415 | } 416 | 417 | get methodActivation() { 418 | return this.parent.methodActivation; 419 | } 420 | 421 | ILocalReturn(sourceLoc) { 422 | this.assertStackContainsAtLeastThisManyElements(1); 423 | const value = this.stack.pop(); 424 | console.debug('returning', value, sourceLoc); 425 | if (sourceLoc) { 426 | this.R.localReturn(sourceLoc, this.env, value); 427 | } 428 | this.caller.R.receive(this.caller.env, value); 429 | this.caller.stack.push(value); 430 | this.nextInstruction = null; 431 | return this.caller; 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /highlighting.js: -------------------------------------------------------------------------------- 1 | const NUM_COLORS = 8; 2 | const COLOR_LETTERS = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; 3 | 4 | // interactions in each section should be able to trigger all effects for 5 | // themselves and the other sections 6 | // these are rows in the act-on/acted-on matrix 7 | 8 | class Highlighting { 9 | constructor(globalState, enableHighlighting) { 10 | this.globalState = globalState; 11 | this.enableHighlighting = enableHighlighting; 12 | 13 | this.registerListenersMacroViz(); 14 | this.registerListenersMicroViz(); 15 | this.registerListenersCode(); 16 | 17 | // UI state 18 | 19 | this.resultWidget = null; 20 | 21 | this.focusWidget = null; 22 | this.selectableCalls = null; 23 | this.focusSourceLoc = null; 24 | 25 | // low level effect state 26 | 27 | // macroViz 28 | this.highlightedEvents = {}; 29 | 30 | // code 31 | this.markers = {}; 32 | 33 | this.partials(); 34 | } 35 | 36 | partials() { 37 | this.macroVizHighlightHover = 38 | _.partial(Highlighting.prototype.macroVizHighlight, _, 'hover', ''); 39 | this.macroVizHighlightFocus = 40 | _.partial(Highlighting.prototype.macroVizHighlight, _, 'focus', ''); 41 | this.macroVizHighlightDef = 42 | _.partial(Highlighting.prototype.macroVizHighlight, _, 'def', ''); 43 | this.macroVizHighlightRef = 44 | _.partial(Highlighting.prototype.macroVizHighlight, _, 'ref', _); 45 | this.macroVizHighlightRefSpecific = 46 | _.partial(Highlighting.prototype.macroVizHighlight, _, 'ref-specific'); 47 | 48 | this.macroVizClearAllFocus = 49 | _.partial(Highlighting.prototype.macroVizClearAll, 'focus', false); 50 | this.macroVizClearAllHover = 51 | _.partial(Highlighting.prototype.macroVizClearAll, 'hover', false); 52 | this.macroVizClearAllDef = 53 | _.partial(Highlighting.prototype.macroVizClearAll, 'def', false); 54 | this.macroVizClearAllRef = 55 | _.partial(Highlighting.prototype.macroVizClearAll, 'ref', false); 56 | this.macroVizClearAllRefColors = 57 | _.partial(Highlighting.prototype.macroVizClearAll, 'ref', true); 58 | 59 | // code partials 60 | 61 | this.codeHighlightDef = 62 | _.partial(Highlighting.prototype.codeHighlight, _, 'def'); 63 | this.codeHighlightRef = 64 | _.partial(Highlighting.prototype.codeHighlight, _, 'ref'); 65 | 66 | this.codeClearDef = 67 | _.partial(Highlighting.prototype.codeClear, 'def'); 68 | this.codeClearRef = 69 | _.partial(Highlighting.prototype.codeClear, 'ref'); 70 | } 71 | 72 | get macroViz() { return this.globalState.macroViz; } 73 | get microViz() { return this.globalState.microViz; } 74 | get editor() { return this.microViz.editor; } 75 | 76 | // INTERACTIONS 77 | // ============ 78 | 79 | registerListenersMacroViz() { 80 | if (!this.macroViz) { 81 | return; 82 | } 83 | 84 | this.macroViz.addListener('click', (__, event, _) => { 85 | //if (!event.isInlineBlockCall()) { 86 | this.focusLexicalStack(event); 87 | //} 88 | }); 89 | 90 | if (this.enableHighlighting) { 91 | this.macroViz.addListener('mouseover', (__, event, _) => { 92 | this.macroVizClearAllHover(); 93 | this.macroVizHighlightHover(event); 94 | 95 | this.codeClearDef(); 96 | this.codeHighlightDef(event.activationEnv.sourceLoc); 97 | 98 | this.codeClearRef(); 99 | this.codeHighlightRef(event.sourceLoc); 100 | }); 101 | } 102 | 103 | this.macroViz.addListener('mouseout', (__, event, _) => { 104 | this.macroVizClearAllHover(); 105 | this.codeClearDef(); 106 | this.codeClearRef(); 107 | }); 108 | } 109 | 110 | registerListenersMicroViz() { 111 | this.microViz.addListener('click', (DOMEvent, event, view) => { 112 | if (DOMEvent.getModifierState('Meta') && 113 | event instanceof SendEvent && 114 | !view.isImplementation) { 115 | this.focusLexicalStack(event); 116 | } 117 | }); 118 | 119 | if (this.enableHighlighting) { 120 | this.microViz.addListener('mouseover', (_, event, view) => { 121 | if (event instanceof SendEvent && !view.isImplementation) { 122 | 123 | this.codeClearDef(); 124 | this.codeHighlightDef(event.activationEnv.sourceLoc); 125 | 126 | this.codeClearRef(); 127 | this.codeHighlightRef(event.sourceLoc); 128 | this.codeClearResultWidget(); 129 | this.codeAddResultWidget(event); 130 | this.macroVizClearAllRef(); 131 | if (this.macroViz) { 132 | this.macroVizHighlightRef(event); 133 | } 134 | 135 | // TODO: may want to highlight more things, but this is enough for now 136 | } 137 | }); 138 | } 139 | 140 | this.microViz.addListener('mouseout', (_, event, view) => { 141 | this.codeClearDef(); 142 | 143 | this.macroVizClearAllRef(); 144 | this.codeClearRef(); 145 | this.codeClearResultWidget(); 146 | }); 147 | } 148 | 149 | registerListenersCode() { 150 | if (!this.enableHighlighting) { 151 | return; 152 | } 153 | // TODO: this is gonna need some serious color work to be sensible 154 | this.editor.getWrapperElement().onmousemove = e => { 155 | const highlightCode = e.getModifierState('Meta'); 156 | const pos = this.editor.coordsChar({left: e.pageX, top: e.pageY}); 157 | 158 | const event = this.mostSpecificEventContaining(pos); 159 | const defSourceLoc = event && event.activationEnv && event.activationEnv.sourceLoc; 160 | 161 | this.macroVizClearAllDef(); 162 | if (this.macroViz) { 163 | this.macroVizHighlightDefsAt(pos); 164 | } 165 | this.codeClearDef(); 166 | if (highlightCode && defSourceLoc) { 167 | this.codeHighlightDef(defSourceLoc); 168 | } 169 | 170 | this.macroVizClearAllRefColors(); 171 | let selectableCalls = null; 172 | if (this.macroViz) { 173 | selectableCalls = this.macroVizHighlightRefsAt( 174 | pos, event, highlightCode && defSourceLoc); 175 | } 176 | this.codeClearRef(); 177 | if (highlightCode && event) { 178 | this.codeHighlightRef(event.sourceLoc); 179 | } 180 | 181 | this.codeClearFocusWidget(event && event.sourceLoc) 182 | if (highlightCode && event && event.activationEnv.sourceLoc) { 183 | this.codeAddFocusWidget(event.sourceLoc, pos, selectableCalls); 184 | } 185 | 186 | } 187 | // TODO: hover + click - macro, micro focus 188 | 189 | this.editor.on('cursorActivity', _ => { 190 | const pos = this.editor.doc.getCursor('head'); 191 | 192 | this.macroVizClearAllDef(); 193 | if (this.macroViz) { 194 | this.macroVizHighlightDefsAt(pos); 195 | } 196 | 197 | this.macroVizClearAllRefColors(); 198 | if (this.macroViz) { 199 | this.macroVizHighlightRefsAt(pos); 200 | } 201 | }); 202 | 203 | this.editor.getWrapperElement().onmouseout = _ => { 204 | this.macroVizClearAllDef(); 205 | this.codeClearDef(); 206 | 207 | this.macroVizClearAllRef(); 208 | this.codeClearRef(); 209 | }; 210 | } 211 | 212 | // HIGHER LEVEL EFFECTS AND UTILS 213 | // ============================== 214 | 215 | focusLexicalStack(event) { 216 | this.globalState.pathMatchers = getPathMatchers(event.activationEnv); 217 | this.clearFocus(); 218 | 219 | this.globalState.pathMatchers.forEach(path => { this.focusPath(path) }); 220 | } 221 | 222 | clearFocus() { 223 | this.macroVizClearAllFocus(); 224 | this.globalState.microViz.setPaths(this.globalState.pathMatchers); 225 | } 226 | 227 | focusPath(path) { 228 | if (this.macroViz) { 229 | this.macroVizHighlightFocus(path.env.programOrSendEvent); 230 | } 231 | this.microVizFocus(path); 232 | } 233 | 234 | mostSpecificEventContaining(pos) { 235 | const idx = this.editor.doc.indexFromPos(pos); 236 | let theEvent = null; 237 | if (!this.macroViz) { 238 | return null; 239 | } 240 | this.macroViz.events.forEach(event => { 241 | if (!(event instanceof SendEvent)) return; 242 | 243 | if (event.sourceLoc && event.sourceLoc.containsIdx(idx)) { 244 | if (theEvent === null) { 245 | theEvent = event; 246 | } else if (theEvent.sourceLoc.strictlyContains(event.sourceLoc)) { 247 | theEvent = event; 248 | } 249 | } 250 | }); 251 | return theEvent; 252 | } 253 | 254 | macroVizHighlightDefsAt(pos) { 255 | const idx = this.editor.doc.indexFromPos(pos); 256 | 257 | this.macroViz.events.forEach(event => { 258 | if (!(event instanceof SendEvent)) return; 259 | if (event.activationEnv.sourceLoc && 260 | event.activationEnv.sourceLoc.containsIdx(idx)) { 261 | this.macroVizHighlightDef(event); 262 | } 263 | }); 264 | } 265 | 266 | macroVizHighlightRefsAt(pos, mostSpecificEvent = null, colorDifferentCalls = false) { 267 | const idx = this.editor.doc.indexFromPos(pos); 268 | 269 | let numSeen = 0; 270 | const selectableCalls = []; 271 | this.macroViz.events.forEach(event => { 272 | if (!(event instanceof SendEvent)) return; 273 | if (!event.sourceLoc) return; 274 | 275 | if (mostSpecificEvent && 276 | event.sourceLoc.equals(mostSpecificEvent.sourceLoc)) { 277 | if (colorDifferentCalls && numSeen < NUM_COLORS) { 278 | this.macroVizHighlight(event, `ref-${numSeen + 1}`); 279 | selectableCalls.push(event); 280 | numSeen++; 281 | } else { 282 | this.macroVizHighlightRefSpecific(event); 283 | } 284 | } else if (event.sourceLoc.containsIdx(idx)) { 285 | this.macroVizHighlightRef(event); 286 | } 287 | }); 288 | 289 | return selectableCalls; 290 | } 291 | 292 | // UI utils 293 | // ======== 294 | 295 | codeAddResultWidget(event, sourceLoc = event.sourceLoc, value = null) { 296 | if (value === null) { 297 | value = event.hasOwnProperty('returnValue') ? 298 | event._valueString(event.returnValue) : '?'; 299 | } 300 | if (!sourceLoc) { 301 | return; 302 | } 303 | 304 | const startPos = this.editor.doc.posFromIndex(sourceLoc.startPos); 305 | const endPos = this.editor.doc.posFromIndex(sourceLoc.endPos); 306 | 307 | const widget = d('resultWidget', {}, '⇒ ' + value); 308 | this.editor.addWidget({line: endPos.line, ch: startPos.ch}, widget); 309 | this.resultWidget = widget; 310 | 311 | return widget; 312 | } 313 | 314 | codeClearResultWidget() { 315 | if (this.resultWidget) { 316 | $(this.resultWidget).remove(); 317 | } 318 | } 319 | 320 | codeAddFocusWidget(sourceLoc, mousePos, calls) { 321 | if (!sourceLoc) { 322 | return; 323 | } 324 | 325 | if (this.focusSourceLoc && sourceLoc.equals(this.focusSourceLoc)) { 326 | return; 327 | } 328 | 329 | const startPos = this.editor.doc.posFromIndex(sourceLoc.startPos); 330 | const endPos = this.editor.doc.posFromIndex(sourceLoc.endPos); 331 | 332 | const widget = this.renderFocusWidget(calls); 333 | this.editor.addWidget({line: endPos.line, ch: mousePos.ch}, widget); 334 | this.focusWidget = widget; 335 | this.selectableCalls = calls; 336 | this.focusSourceLoc = sourceLoc; 337 | 338 | return widget; 339 | } 340 | 341 | renderFocusWidget(calls) { 342 | const ans = d('focusWidget', {}, 343 | ...calls.map((call, idx) => d('call', {class: `index-${idx}`}, '●'))); 344 | 345 | ans.onmousemove = e => { 346 | calls.forEach((c, i) => { 347 | this.macroVizClear(c, `ref-${i+1}`) 348 | this.macroVizHighlight(c, `ref-${i+1}`) 349 | }); 350 | e.stopPropagation() 351 | }; 352 | 353 | ans.onmouseout = e => e.stopPropagation(); 354 | 355 | calls.forEach((call, idx) => { 356 | const callDOM = ans.children[idx]; 357 | callDOM.onmouseover = e => { 358 | calls.forEach((c, i) => { 359 | this.macroVizClear(c, `ref-${i+1}`) 360 | }); 361 | this.macroVizHighlight(call, `ref-${idx+1}`); 362 | }; 363 | 364 | callDOM.onmousemove = e => e.stopPropagation(); 365 | 366 | callDOM.onclick = e => this.focusLexicalStack(call); 367 | }); 368 | return ans; 369 | } 370 | 371 | codeClearFocusWidget(sourceLoc) { 372 | if (this.focusWidget && 373 | (sourceLoc === null || !sourceLoc.equals(this.focusSourceLoc))) { 374 | $(this.focusWidget).remove(); 375 | this.focusWidget = null; 376 | this.selectableCalls = null; 377 | this.focusSourceLoc = null; 378 | } 379 | } 380 | 381 | // LOW-LEVEL EFFECTS 382 | // ================= 383 | 384 | // macroViz 385 | // -------- 386 | 387 | macroVizClearAll(highlightType = null, isPrefix = false) { 388 | if (!isPrefix) { 389 | const events = highlightType === null ? 390 | this.macroViz.events : this.highlightedEvents[highlightType] || []; 391 | events.forEach(event => this.macroVizClear(event, highlightType, false)); 392 | 393 | if (highlightType) { 394 | this.highlightedEvents[highlightType] = []; 395 | } 396 | } else { 397 | Object.keys(this.highlightedEvents) 398 | .forEach(type => { 399 | if (type.startsWith(highlightType)) { 400 | this.macroVizClearAll(type, false); 401 | } 402 | }); 403 | } 404 | } 405 | 406 | macroVizClear(event, highlightType = null, isPrefix = false) { 407 | const view = this.macroViz.getNodeView(event); 408 | if (!view) return; 409 | if (highlightType !== null && !isPrefix) { 410 | view.DOM.classList.remove(`highlight-${highlightType}`); 411 | } else { 412 | const prefix = highlightType === null ? '' : highlightType; 413 | const classList = view.DOM.classList; 414 | for (let i = classList.length - 1; i >= 0; i--) { 415 | const cls = classList[i]; 416 | if (cls.startsWith(`highlight-${prefix}`)) { 417 | classList.remove(cls) 418 | } 419 | } 420 | } 421 | } 422 | 423 | macroVizHighlight(event, highlightType) { 424 | const view = this.macroViz.getNodeView(event); 425 | if (!view) return; 426 | view.DOM.classList.add(`highlight-${highlightType}`); 427 | 428 | if (!this.highlightedEvents.hasOwnProperty(highlightType)) { 429 | this.highlightedEvents[highlightType] = []; 430 | } 431 | this.highlightedEvents[highlightType].push(event); 432 | } 433 | 434 | // microViz 435 | // -------- 436 | 437 | microVizFocus(path) { 438 | this.microViz.addImplementation(path.env.microVizEvents); 439 | } 440 | 441 | // code 442 | // ---- 443 | 444 | codeHighlight(sourceLoc, highlightType) { 445 | if (!sourceLoc) { 446 | return; 447 | } 448 | const startPos = this.editor.doc.posFromIndex(sourceLoc.startPos); 449 | const endPos = this.editor.doc.posFromIndex(sourceLoc.endPos); 450 | this.markers[highlightType] = this.editor.doc.markText(startPos, endPos, { 451 | className: 'highlight-' + highlightType, 452 | clearOnEnter: true 453 | }); 454 | return this.markers[highlightType]; 455 | } 456 | 457 | codeClear(highlightType) { 458 | if (this.markers[highlightType] != null) { 459 | this.markers[highlightType].clear(); 460 | this.markers[highlightType] = null; 461 | } 462 | } 463 | 464 | // TODO: partials 465 | } 466 | -------------------------------------------------------------------------------- /viz/microViz.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // toplevel class. persists across focuses. manages codemirror 4 | class MicroViz extends CheckedEmitter { 5 | constructor(container, enableMicroViz) { 6 | super(); 7 | this.registerEvent('click', 'DOMEvent', 'event', 'eventView'); 8 | this.registerEvent('mouseover', 'DOMEvent', 'event', 'eventView'); 9 | this.registerEvent('mouseout', 'DOMEvent', 'event', 'eventView'); 10 | 11 | this.microViz = this; 12 | this.enableMicroViz = enableMicroViz; 13 | 14 | this.container = container; 15 | this.editor = CodeMirror(container); 16 | this.widgetForLine = {}; 17 | 18 | this.render(); 19 | } 20 | 21 | render() { 22 | this.microVizParent = d('microVizDiv', {}); 23 | this.background = d('microVizBackground', {}); 24 | this.microVizHolder = d('microVizHolder', {}, 25 | this.background, 26 | this.microVizParent); 27 | this.container.appendChild(this.microVizHolder); 28 | this.container.classList.add('microViz'); 29 | } 30 | 31 | // compositing and paths 32 | 33 | get currentPath() { return this.paths[this.currentPathIdx]; } 34 | 35 | setPaths(paths) { 36 | if (!this.enableMicroViz) { 37 | return; 38 | } 39 | 40 | this.eventViews = new Map(); 41 | this.implementations = new Map(); 42 | 43 | this.paths = paths; 44 | this.currentPathIdx = 0; 45 | 46 | Object.keys(this.widgetForLine) 47 | .forEach(line => { 48 | this.widgetForLine[line].clear(); 49 | }); 50 | this.widgetForLine = {}; 51 | this.microVizParent.innerHTML = ''; 52 | 53 | this.clearBackground(); 54 | this.setupBackground(); 55 | } 56 | 57 | setImplementation(sendView) { 58 | this.implementations.set(this.currentPath, sendView); 59 | this.currentPathIdx++; 60 | } 61 | 62 | setEventView(event, view) { 63 | this.eventViews.set(event, view); 64 | } 65 | 66 | addImplementation(microVizEvents) { 67 | if (!this.enableMicroViz) { 68 | return; 69 | } 70 | 71 | const implMicroVizEvents = microVizEvents; 72 | 73 | const parentPath = this.paths[this.currentPathIdx - 1]; 74 | const parentView = this.implementations.get(parentPath) || null; 75 | 76 | if (parentView) { 77 | const implView = new SendView(parentView, implMicroVizEvents); 78 | parentView.addImplementation(implView); 79 | } else { 80 | const implView = new SendView(this, implMicroVizEvents); 81 | this.microVizParent.appendChild(implView.DOM); 82 | this.tagLines(implView.startLine, implView.endLine); 83 | this.fixHeightsFor(implView); 84 | } 85 | } 86 | 87 | // background/printer paper 88 | 89 | clearBackground() { 90 | while (this.background.children.length > 0) { 91 | this.background.removeChild(this.background.firstChild); 92 | } 93 | } 94 | 95 | setupBackground() { 96 | range(1, this.editor.getValue().split('\n').length) 97 | .forEach(line => { 98 | const lineDiv = d('line', {startLine: line, endLine: line}); 99 | this.background.appendChild(lineDiv); 100 | }); 101 | } 102 | 103 | tagLines(startLine, endLine) { 104 | const realEndLine = this.editor.getValue().split('\n').length; 105 | range(startLine, realEndLine) 106 | .forEach(lineNumber => { 107 | const cmLineNumber = lineNumber - 1; 108 | this.editor.removeLineClass(cmLineNumber, 'text'); 109 | }) 110 | range(startLine, endLine) 111 | .forEach(lineNumber => { 112 | const cmLineNumber = lineNumber - 1; 113 | this.editor.addLineClass(cmLineNumber, 'text', `line${lineNumber}`); 114 | }); 115 | } 116 | 117 | // line spacing logic 118 | 119 | fixHeight(lineNumber) { 120 | const $ = (el, query) => el.querySelector(query); 121 | const $$ = (el, query) => [].slice.call(el.querySelectorAll(query)); 122 | 123 | const cmLineNumber = lineNumber - 1; 124 | 125 | const line = $(this.container, `.CodeMirror .line${lineNumber}.CodeMirror-line`); 126 | const itemsOnLine = $$(this.container, '*[endLine="' + lineNumber + '"]').concat(line); 127 | 128 | itemsOnLine.forEach(item => item.style.paddingBottom = '0px'); 129 | 130 | const bottoms = new WeakMap(); 131 | itemsOnLine.forEach(element => bottoms.set(element, element.getBoundingClientRect().bottom)); 132 | const bottom = itemsOnLine 133 | .map(item => bottoms.get(item)) 134 | .reduce((x, y) => Math.max(x, y), bottoms.get(line)); 135 | 136 | this.spaceLine(cmLineNumber, line, bottoms.get(line), bottom); 137 | 138 | const bgline = $(this.container, 'line[startLine="' + lineNumber + '"]'); 139 | this.inflate(bgline, bottoms.get(bgline), bottom); 140 | 141 | const spacers = $$(this.container, 'spacer[endLine="' + lineNumber + '"]'); 142 | spacers.forEach(spacer => this.inflate(spacer, bottoms.get(spacer), bottom)); 143 | 144 | const localEvents = $$(this.container, 'event[endLine="' + lineNumber + '"]:not(.remote)'); 145 | localEvents.forEach(event => this.inflate(event, bottoms.get(event), bottom)); 146 | 147 | const remoteEventGroups = $$(this.container, 'remoteEventGroup[endLine="' + lineNumber + '"]'); 148 | remoteEventGroups.forEach(remoteEventGroup => 149 | this.inflate(remoteEventGroup, bottoms.get(remoteEventGroup), bottom)); 150 | } 151 | 152 | spaceLine(cmLineNumber, line, lineBottom, bottomY) { 153 | const paddingBottom = bottomY - lineBottom; 154 | if (this.widgetForLine.hasOwnProperty(cmLineNumber)) { 155 | this.widgetForLine[cmLineNumber].node.style.height = paddingBottom; 156 | this.widgetForLine[cmLineNumber].changed(); 157 | } else { 158 | this.widgetForLine[cmLineNumber] = this.editor.addLineWidget( 159 | cmLineNumber, this.spacer(paddingBottom) 160 | ); 161 | } 162 | } 163 | 164 | inflate(element, elementBottom, bottomY) { 165 | const currentPaddingBottom = parseInt(element.style.paddingBottom) || 0; 166 | const newPaddingBottom = bottomY - elementBottom; 167 | if (newPaddingBottom - currentPaddingBottom > 1 || 168 | newPaddingBottom - currentPaddingBottom < -1) { 169 | element.style.paddingBottom = newPaddingBottom + 'px'; 170 | } 171 | } 172 | 173 | spacer(height) { 174 | return d('div', { 175 | style: `height: ${height}px` 176 | }); 177 | } 178 | 179 | fixHeightsFor(item) { 180 | item.extent.forEach(line => this.fixHeight(line)); 181 | } 182 | 183 | // events 184 | 185 | onClick(DOMEvent, event) { 186 | this.emit('click', DOMEvent, event, this.eventViews.get(event)); 187 | } 188 | 189 | onMouseover(DOMEvent, event) { 190 | this.emit('mouseover', DOMEvent, event, this.eventViews.get(event)); 191 | } 192 | 193 | onMouseout(DOMEvent, event) { 194 | this.emit('mouseout', DOMEvent, event, this.eventViews.get(event)); 195 | } 196 | 197 | } 198 | 199 | 200 | class AbstractView { 201 | constructor(parent, sourceLoc, classes) { 202 | this.parent = parent; 203 | this.microViz = parent.microViz; 204 | this.sourceLoc = sourceLoc; 205 | this.classes = classes; 206 | this.attributes = { 207 | startLine: this.sourceLoc.startLineNumber, 208 | endLine: this.sourceLoc.endLineNumber, 209 | class: this.classes 210 | }; 211 | } 212 | 213 | get classList() { 214 | return this.DOM.classList; 215 | } 216 | 217 | render() { 218 | this.DOM._model = this.model; 219 | } 220 | 221 | get startLine() { return this.attributes.startLine; } 222 | get endLine() { return this.attributes.endLine; } 223 | get extent() { return range(this.startLine, this.endLine); } 224 | } 225 | 226 | class SendView extends AbstractView { 227 | constructor(parent, microVizEvents, sourceLoc=microVizEvents.sourceLoc, classes='') { 228 | super(parent, sourceLoc, classes); 229 | this.microVizEvents = microVizEvents; 230 | this.attributes.isImplementation = this.isImplementation; 231 | this.model = microVizEvents; 232 | this.numGroups = 0; 233 | this.eventGroups = []; 234 | 235 | if (this.isImplementation) { 236 | // parent needs to know about this view before it renders 237 | this.microViz.setImplementation(this); 238 | } 239 | 240 | if (!this.isImplementation) { 241 | this.attributes.title = this.microVizEvents.programOrSendEvent.toDetailString(); 242 | } 243 | 244 | this.render(); 245 | 246 | this.microVizEvents.addListener('addEventGroup', eventGroup => 247 | this.addEventGroup(eventGroup)); 248 | 249 | if (!this.isImplementation) { 250 | this.microViz.setEventView(this.microVizEvents.programOrSendEvent, this); 251 | this.DOM.onclick = (e) => { 252 | this.microViz.onClick(e, this.microVizEvents.programOrSendEvent); 253 | e.stopPropagation(); 254 | }; 255 | this.DOM.onmouseover = (e) => { 256 | this.microViz.onMouseover(e, this.microVizEvents.programOrSendEvent); 257 | e.stopPropagation(); 258 | }; 259 | this.DOM.onmouseout = (e) => { 260 | this.microViz.onMouseout(e, this.microVizEvents.programOrSendEvent); 261 | e.stopPropagation(); 262 | }; 263 | } 264 | } 265 | 266 | get isImplementation() { return this.microVizEvents.isImplementation; } 267 | 268 | render() { 269 | this.DOM = d('send', this.attributes); 270 | if (!this.isImplementation) { 271 | this.addEmptySendGroup(); 272 | } 273 | this.microVizEvents.eventGroups.forEach(group => this.addEventGroup(group)); 274 | super.render(); 275 | } 276 | 277 | addEmptySendGroup() { 278 | this.DOM.setAttribute('empty', true); 279 | this.DOM.appendChild( 280 | d('remoteEventGroup', {class: 'empty', startLine: this.startLine, endLine: this.endLine}, 281 | d('emptySendDot', {}, '▪')) 282 | ); 283 | } 284 | 285 | addEventGroup(eventGroup) { 286 | if (this.numGroups === 0 && !this.isImplementation) { 287 | this.DOM.removeChild(this.DOM.firstChild); 288 | this.DOM.removeAttribute('empty'); 289 | } 290 | 291 | this.numGroups++; 292 | if (this.numGroups > 1) { 293 | this.DOM.setAttribute('loopy', true); 294 | } 295 | 296 | let eventGroupView; 297 | if (eventGroup instanceof LocalEventGroup) { 298 | eventGroupView = new LocalEventGroupView(this, eventGroup, this.sourceLoc); 299 | } else if (eventGroup instanceof RemoteEventGroup) { 300 | eventGroupView = new RemoteEventGroupView(this, eventGroup, this.sourceLoc); 301 | } else { 302 | throw new Error('groups must be local or remote'); 303 | } 304 | 305 | this.DOM.appendChild(eventGroupView.DOM); 306 | } 307 | 308 | addImplementation(implView) { 309 | if (!this.isImplementation) { 310 | throw new Error('tried to add an implementation to a non-implementation'); 311 | } 312 | 313 | console.assert(this.numGroups === 1, 'implementation must have 1 event group'); 314 | this.eventGroups[0].addImplementation(implView); 315 | } 316 | } 317 | 318 | class LocalEventGroupView extends AbstractView { 319 | constructor(parent, localEventGroup, sourceLoc=localEventGroup.sourceLoc, classes='') { 320 | super(parent, sourceLoc, classes); 321 | this.localEventGroup = localEventGroup; 322 | this.model = localEventGroup; 323 | this.lastPopulatedLineNumber = sourceLoc.startLineNumber - 1; 324 | this.lastEventNode = null; 325 | this.children = {}; 326 | this.spacers = {}; 327 | 328 | this.extent.forEach(line => this.children[line] = []); 329 | this.extent.forEach(line => this.spacers[line] = null); 330 | 331 | // parent needs to know about this view before it renders 332 | this.parent.eventGroups.push(this); 333 | 334 | this.render(); 335 | 336 | this.localEventGroup.addListener('addEvent', event => { 337 | this.addEvent(event); 338 | }); 339 | } 340 | 341 | render() { 342 | this.DOM = d('localEventGroup', this.attributes); 343 | this.addSpacers(); 344 | this.localEventGroup.events.forEach(event => this.addEvent(event)); 345 | super.render(); 346 | } 347 | 348 | // MAIN ADDEVENT LOGIC 349 | 350 | addEvent(event) { 351 | if (event.sourceLoc.startLineNumber > this.lastPopulatedLineNumber) { 352 | this.addFirstInLine(event); 353 | } else if (event.sourceLoc.startLineNumber === this.lastChild.startLine) { 354 | this.addOverlappingAtTop(event); 355 | } else if (event.sourceLoc.startLineNumber > this.lastChild.startLine) { 356 | this.addOverlappingPushDown(event); 357 | } else if (event.sourceLoc.startLineNumber < this.lastChild.startLine) { 358 | this.addOverlappingInsideOut(event); 359 | } else { 360 | throw new Error('impossible!'); 361 | } 362 | } 363 | 364 | addFirstInLine(event) { 365 | this.lastEventNode = this.mkEventView(event, event.sourceLoc, 'firstInLine lastInLine'); 366 | this.lastPopulatedLineNumber = event.sourceLoc.endLineNumber; 367 | 368 | const referenceDOM = 369 | this.spacers[this.lastEventNode.endLine].DOM.nextSibling || null; 370 | this.lastEventNode.extent.forEach(line => this.removeSpacer(line)); 371 | this.addChild(this.lastEventNode, referenceDOM); 372 | } 373 | 374 | addOverlappingAtTop(event) { 375 | if (this.lastEventNode !== null) { 376 | this.lastEventNode.classList.remove('lastInLine'); 377 | } 378 | this.lastEventNode = this.mkEventView(event, event.sourceLoc); 379 | range(this.lastPopulatedLineNumber + 1, event.sourceLoc.endLineNumber) 380 | .forEach(line => this.removeSpacer(line)); 381 | this.lastPopulatedLineNumber = 382 | Math.max(this.lastPopulatedLineNumber, event.sourceLoc.endLineNumber); 383 | 384 | const childrenOnLine = this.children[event.sourceLoc.startLineNumber]; 385 | const referenceDOM = childrenOnLine[childrenOnLine.length - 1].DOM.nextSibling; 386 | this.addChild(this.lastEventNode, referenceDOM); 387 | } 388 | 389 | addOverlappingPushDown(event) { 390 | if (this.lastEventNode !== null) { 391 | this.lastEventNode.classList.remove('lastInLine'); 392 | } 393 | // TODO: factor out creation 394 | this.lastEventNode = this.mkEventView(event, event.sourceLoc, 'pushDown'); 395 | this.lastPopulatedLineNumber = 396 | Math.max(this.lastPopulatedLineNumber, event.sourceLoc.endLineNumber); 397 | 398 | range(this.lastPopulatedLineNumber + 1, event.endLine) 399 | .forEach(line => this.removeSpacer(line)); 400 | const childrenOnLine = this.children[event.sourceLoc.startLineNumber]; 401 | const referenceDOM = childrenOnLine[childrenOnLine.length - 1].DOM.nextSibling; 402 | 403 | const spacers = 404 | range(this.lastChild.startLine, event.sourceLoc.startLineNumber - 1). 405 | map(lineNumber => new Spacer(this, lineNumber)); 406 | if (spacers.length > 0) { this.lastEventNode.classList.add('firstInLine'); } 407 | this.addChild(new Wrapper(this, ...spacers, this.lastEventNode), referenceDOM); 408 | } 409 | 410 | addOverlappingInsideOut(event) { 411 | if (this.lastEventNode !== null) { 412 | this.lastEventNode.classList.remove('lastInLine'); 413 | } 414 | const nodesToWrap = flatten( 415 | range(event.sourceLoc.startLineNumber, event.sourceLoc.endLineNumber) 416 | .map(line => this.children[line].concat(this.spacers[line])) 417 | ).filter(node => node !== null); 418 | const referenceDOM = nodesToWrap[nodesToWrap.length-1].DOM.nextSibling || null; 419 | nodesToWrap.forEach(node => this.removeChildOrSpacer(node)); 420 | const wrapper = new Wrapper(this, ...nodesToWrap); 421 | this.addChild(wrapper, referenceDOM); 422 | this.addOverlappingPushDown(event); 423 | } 424 | 425 | addImplementation(implView) { 426 | // pick out nodes on implview's extent 427 | const nodesToWrap = flatten(this.extent 428 | .map(line => this.children[line].concat(this.spacers[line]))) 429 | .filter(node => node !== null) 430 | .filter(child => 431 | !(child.startLine < implView.startLine && child.endLine < implView.startLine) && 432 | !(child.startLine > implView.endLine && child.endLine > implView.endLine)); 433 | const referenceDOM = nodesToWrap[nodesToWrap.length-1].DOM.nextSibling || null; 434 | 435 | // remove those nodes 436 | nodesToWrap.forEach(node => this.removeChildOrSpacer(node)); 437 | 438 | const parentWrapper = new Wrapper(this, ...nodesToWrap); 439 | if (implView.startLine > 1 && nodesToWrap.every(n => n instanceof Spacer)) { 440 | parentWrapper.classList.add('firstInLine'); 441 | } 442 | this.addChildWithoutTracking(parentWrapper, referenceDOM); 443 | 444 | const startLine = nodesToWrap[0].startLine; 445 | const childNodes = []; 446 | range(startLine, implView.startLine - 1) 447 | .forEach(lineNumber => childNodes.push(new Spacer(this, lineNumber))); 448 | if (childNodes.length > 0) { 449 | implView.classList.add('firstInLine'); 450 | } 451 | childNodes.push(implView); 452 | const childWrapper = new Wrapper(this, ...childNodes); 453 | childWrapper.classList.remove('firstInLine'); 454 | this.addChildWithoutTracking(childWrapper, referenceDOM); 455 | } 456 | 457 | // UTILS 458 | 459 | mkEventView(event, sourceLoc, classes = '') { 460 | if (event instanceof MicroVizEvents) { 461 | return new SendView(this, event, sourceLoc, classes + ' lastInLine'); 462 | } else { 463 | return new EventView(this, event, sourceLoc, classes + ' lastInLine'); 464 | } 465 | } 466 | 467 | get numChildren() { return flatten(Object.values(this.children)).length; } 468 | 469 | addChild(view, referenceDOM) { 470 | if (view.classList.contains('firstInLine') && 471 | view.startLine > this.startLine) { 472 | this.DOM.insertBefore(d('br'), referenceDOM); 473 | } 474 | 475 | if (this.lastChild) { 476 | range(this.lastChild.endLine, view.startLine) 477 | .forEach(line => this.microViz.fixHeight(line)); 478 | } 479 | 480 | this.children[view.startLine].push(view); 481 | this.lastChild = view; 482 | this.DOM.insertBefore(view.DOM, referenceDOM); 483 | this.microViz.fixHeightsFor(view); 484 | } 485 | 486 | addChildWithoutTracking(view, referenceDOM) { 487 | if (view.classList.contains('firstInLine') && 488 | view.startLine > this.startLine) { 489 | this.DOM.insertBefore(d('br'), referenceDOM); 490 | } 491 | 492 | if (this.lastChild) { 493 | range(this.lastChild.endLine, view.startLine) 494 | .forEach(line => this.microViz.fixHeight(line)); 495 | } 496 | this.DOM.insertBefore(view.DOM, referenceDOM); 497 | this.microViz.fixHeightsFor(view); 498 | } 499 | 500 | removeChildOrSpacer(view) { 501 | if (view instanceof Spacer) { 502 | this.removeSpacer(view.lineNumber); 503 | } else { 504 | const childrenOnLine = this.children[view.startLine]; 505 | const viewIdx = childrenOnLine.indexOf(view); 506 | this.children[view.startLine] = childrenOnLine.splice(viewIdx, 1); 507 | 508 | if (view.classList.contains('firstInLine') && 509 | view.DOM.previousSibling) { 510 | this.DOM.removeChild(view.DOM.previousSibling); 511 | } 512 | this.DOM.removeChild(view.DOM); 513 | } 514 | } 515 | 516 | addSpacers() { 517 | this.extent 518 | .forEach(line => { 519 | const spacer = new Spacer(this, line); 520 | this.spacers[line] = spacer; 521 | if (line > this.startLine) { 522 | this.DOM.appendChild(d('br')); 523 | } 524 | this.DOM.appendChild(spacer.DOM); 525 | // this.microViz.fixHeightsFor(spacer); 526 | }) 527 | } 528 | 529 | removeSpacer(line) { 530 | if (this.spacers[line]) { 531 | if (line > this.startLine) { 532 | this.DOM.removeChild(this.spacers[line].DOM.previousSibling); 533 | } 534 | this.DOM.removeChild(this.spacers[line].DOM); 535 | this.spacers[line] = null; 536 | } 537 | } 538 | } 539 | 540 | class RemoteEventGroupView extends AbstractView { 541 | constructor(parent, remoteEventGroup, sourceLoc=remoteEventGroup.sourceLoc, classes='') { 542 | super(parent, sourceLoc, classes); 543 | this.remoteEventGroup = remoteEventGroup; 544 | this.model = remoteEventGroup; 545 | this.eventViews = new Map(); 546 | this.numShownEvents = 0; 547 | 548 | // parent needs to know about this view before it renders 549 | this.parent.eventGroups.push(this); 550 | 551 | this.render(); 552 | 553 | this.remoteEventGroup.addListener('addEvent', event => this.addEvent(event)); 554 | this.remoteEventGroup.addListener('removeEvent', event => this.removeEvent(event)); 555 | } 556 | 557 | render() { 558 | this.DOM = d('remoteEventGroup', this.attributes); 559 | this.remoteEventGroup.events.forEach(event => this.addEvent(event)); 560 | super.render(); 561 | } 562 | 563 | addEvent(event) { 564 | const eventView = new EventView(this, event, this.sourceLoc, 'remote firstInLine lastInLine'); 565 | this.eventViews.set(event, eventView); 566 | if (this.numShownEvents > 0) { 567 | this.DOM.appendChild(d('br')); 568 | } 569 | this.DOM.appendChild(eventView.DOM); 570 | this.microViz.fixHeightsFor(this); 571 | 572 | this.numShownEvents++; 573 | } 574 | 575 | removeEvent(event) { 576 | const eventView = this.eventViews.get(event); 577 | if (eventView.DOM.previousSibling && eventView.DOM.previousSibling.nodeName === 'BR') { 578 | this.DOM.removeChild(eventView.DOM.previousSibling); 579 | } 580 | // if we're getting rid of the first event in the group, 581 | // make sure the group doesn't start with a
tag 582 | if (!(eventView.DOM.previousSibling) && eventView.DOM.nextSibling) { 583 | this.DOM.removeChild(eventView.DOM.nextSibling); 584 | } 585 | this.DOM.removeChild(eventView.DOM); 586 | this.numShownEvents--; 587 | } 588 | } 589 | 590 | class Spacer extends AbstractView { 591 | constructor(parent, lineNumber) { 592 | super(parent, { 593 | startLineNumber: lineNumber, 594 | endLineNumber: lineNumber 595 | }, ''); 596 | this.lineNumber = lineNumber; 597 | this.render(); 598 | } 599 | 600 | render() { 601 | this.DOM = d('spacer', this.attributes); 602 | } 603 | } 604 | 605 | class Wrapper extends AbstractView { 606 | constructor(parent, ...views) { 607 | let classes = []; 608 | 609 | let firstInLine = false; 610 | for (let view of views) { 611 | if (view instanceof Spacer) { continue; } 612 | if (view.classList.contains('firstInLine')) { 613 | firstInLine = true; 614 | } 615 | break; 616 | } 617 | 618 | if (firstInLine) { 619 | classes.push('firstInLine'); 620 | } 621 | // if (views[views.length - 1].classList.contains('lastInLine')) { 622 | // classes.push('lastInLine'); 623 | // } 624 | 625 | super(parent, { 626 | startLineNumber: views[0].startLine, 627 | endLineNumber: views[views.length - 1].endLine 628 | }, classes.join(' ')); 629 | this.views = views; 630 | this.render(); 631 | } 632 | 633 | render() { 634 | this.DOM = d('wrapper', this.attributes, 635 | ...flatten(this.views.map((v, idx) => { 636 | if ((v.classList.contains('firstInLine') || v instanceof Spacer) && idx > 0) { 637 | return [d('br'), v.DOM]; 638 | } else { 639 | return [v.DOM]; 640 | } 641 | }))); 642 | } 643 | } 644 | 645 | class EventView extends AbstractView { 646 | constructor(parent, event, sourceLoc=event.sourceLoc, classes='') { 647 | super(parent, sourceLoc, classes); 648 | this.event = event; 649 | this.model = event; 650 | 651 | this.attributes.eventType = event.constructor.name; 652 | 653 | this.render(); 654 | 655 | this.microViz.setEventView(this.event, this); 656 | this.DOM.onclick = (e) => { 657 | this.microViz.onClick(e, this.event); 658 | e.stopPropagation(); 659 | }; 660 | this.DOM.onmouseover = (e) => { 661 | this.microViz.onMouseover(e, this.event); 662 | e.stopPropagation(); 663 | }; 664 | this.DOM.onmouseout = (e) => { 665 | this.microViz.onMouseout(e, this.event); 666 | e.stopPropagation(); 667 | }; 668 | } 669 | 670 | render() { 671 | this.DOM = d('event', this.attributes, this.event.toMicroVizString()); 672 | super.render(); 673 | } 674 | } 675 | --------------------------------------------------------------------------------