From e9961bceea48518ec0992d1102c124a6f313873c Mon Sep 17 00:00:00 2001 From: ningOTI Date: Sat, 26 May 2018 16:37:04 +0800 Subject: [PATCH] =?UTF-8?q?Add=20Source=20=C2=A72=20(#35),=20missing=20sym?= =?UTF-8?q?bols=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add button for playground at Navbar No functionality. * Make first NavbarGroup's align left explicit * Change playground icon * Add routes to Playground * Update test snapshots * Fix bad test for Playground component Oops * Add react-ace for IDE * Try passing props into playground * Load a preset "Hello World" onto the ace editor. Changes: - Changed `Playground` component to a statefull one (extends `React.Component) - add property `playgroundCode` to IApplicationState (bad practice, change to a slice of the state instead) - add interface `IPlaygroundProps` to denote the properties passed to Playground * Remove unused onChange() * Add stored state for playground editor text Once again, this is a WIP. Text typed into the playground editor will stay in the playground (or rather, appear again) after leaving and coming back. Changes: - added of action, actionCreator (updatePlaygroundCode) - added a reducer under application's `reducer` function Possible problems: - Location of reducer - Location of playgroundCode stored (as in previous commit) * Change initialValue prop to editorValue * Made playground actions a separate file * Modify actions and dispatch call * Change redux import statement * Add IPlaygroundState to IState in index reducer * Add playground reducer * Remove playgroundCode from IApplicationState * Fix type errors Made a compromise by including an IPlaygroundState as part of IApplicationProps * Re-disable console.logs (tslint) * Fix missing imports for react-ace mode, theme * Add playground CSS * Add JSDoc style comments for playground * Fix some tests * Fix semantics of mapStateToProps for ApplicationContainer * Do trivial formatting * Add interpreter toolchain as 'slang' This is a copy over of the current state of `./frontend/src/toolchain` at source-academy/source-academy2 67d9184. It includes only source chapter 1, a new specification (see [PR 6 at the source-academy/source-academy2](https://github.com/source-academy/source-academy2/pull/6) repository). * Add react-saga and react types to yarn The react types were added as a result of the `yarn add` command. * Add saga to middleware list * Segregate actionTypes from actions * Fix reducer actionType import * Remove actionType import from action/index * Fix fatal bug with defineVariable calls * Fix missing imports Some imports of slang are already provided as additional dependencies to other modules, e.g. acorn is already a dependency of webpack * Add tests for slang * Remove unused type definitions * Lint typescript files of slang * Add interruption actionType * Add sagas WIP For ny's reference * Add more interpreter actions * Change to older version of saga * Add notification file * Change es target to es2016 This was the target in sourceacademy-2 as well. Without this, there was a problem with casting an `IterableIterator` to `yield*`, that expected an array. * Revert "Remove unused type definitions" This reverts commit e18f2ae3e1bd86380cc12285652ac204d01e6f65. "Oops" - @ningyuansg * Fix some slang errors * Make 16/05 code review changes Changes not made: - Change of `Playground` component to an `SFC` * Fix test snapshot (name of onChange for ReactAce) Due to 7ac707c. * Revert Playground as SFC, make Editor container * Make Playground stateless This is done by abstracting out the ace editor into its own component , `Editor`, and its own container to handle redux calls, `EditorContainer`. * Add test and fix export type Export of `Editor` changed to a default export, like the other components. A test has been added for the `Editor` component. Snapshot changes pending. * Fix type errors according to tslint * Change object methods to use es6 shorthand * Fix more linting errors for interpreter.ts * Disable `no-empty` rule. Rationale: A no-op function is much easier to see as `() => {}` as compared to `() => undefined` or even `() =>`. In addition, this syntax is used by parts of the interpreter as well. * Add `max-classes-per-file` exception for interpreter files Splitting the exports would cause futher refactoring in other parts of the interpreter codebase * Fix linting errors in parser, rules/braces... * Fix tslint errors in `parser` Exception was added for multiple class exports. * Format interpreter.ts * Fix tslint errors in rules/* * Fix tslint errors for rttc.ts * Fix tslint errors for node.ts * Fix tslint errors for types.ts * Fix stdlib/list, stdlib/misc and scheudler tslint An additional change is the use of a line disable in `parser.ts` instead of an entire file disable. * Fix `saga` not being run after store creation * Fix Editor test (tslint errors) 1. Missing comma in object props 2. Syntax error and missing object comprehension in creating Editor * Add properties context, output to IPlaygroundState 1. context is of type slang.Context. This holds the 'environment' for the slang interpreter 2. Output is a string array that will hold the stdout/stderr (display, error calls) of a program run in the editor. For repl programs, it will also hold the return value of the program. Each element of an array is a historical output. * Add button to call on handleEvalEditor This change allows the newly added button to call upon the handleEvalEditor callback, whose reducer is not defined (yet) * Add mapDispatchToProps for EditorContainer Note that the intersection type is used with `Pick` in order to define `DispatchProps`, this might not be ideal. Input on this issue would be appreciated. * Fix 'ace is undefined' error * Add Output SFC * Add output tests, did yarn format * Add 'run' button to EditorContainer * Change Editor snapshot to include button * Wire saga interpreter success to output SFC * Fix wrong file extension '.tsx' -> '.ts' 'Oops' - ny * Move state definitions into their own file Fixes #7 * Add type for interpreter outputs In addition, tslint rule `interface-over-type-literal` was disabled, due to the need for many types (that would not be extended) * Fix #11, using bindActionCreators * Add flexboxgrid dependency * Line Editor and Output up side to side * Refactor and modularise playground as IDE * Change Output to Repl This is to allow for the incoming repl, which will produce outputs as well. * Implement multiple Outputs in Repl (history) * Fix class playground not taking up all of parent * Make use of slang's toString to format values Previously, undefined would print as '' (empty string). * Make flexbox tags relative to current component * Make IPlaygroundState properties readonly Fixes #14 * Add ReplInput (not wired to saga) * Replace TextArea with AceEditor Also change theme to dark background so we can debug better. * Move IDE buton to a separate Control component This is contained within the IDE component * Add property replValue to IPlaygroundState replValue, like editorValue, will store the contents of repl. It is used to allow the contents of the repl to persist through route changes. It is also the target for evaluation. * Make repl read from state.playground.replValue * Add evalRepl * Update Control snapshot with eval button * Add action UPDATE_REPL_VALUE * Implement persistent repl content across routes * Did yarn format * Add action clear repl Also removed unnecessarily verbose typings * Make eval button clear the replValue * Fix repl context resetting every eval TODO: Editor run needs to update playground context * Add keyboard shortcut for REPL * Fix formatting for repl shortcut * Errors are loosely represented as string 'error' * Fix yarn format git hooks passing for unformatted Previously, if the staged files are not formatted according to prettier, running the pre-commit hook would change the file formatting, and then return an exit code 0, allowing the commit to go through. However, the changes made by prettier are not staged. Now, the pre-commit hook will fail, so that only prettier-passed files can be committed. * Add ParseError in slang * Improve error messages to specify type of error * Improve CSS for repl output * Fix many error messages not printing on new lines Thank you stackoverflow * Fix editor not changing playground.context * Change ParseError to a function, parseError * Simplify REPL Output Component creation Turns out there's a 2-arity `map` that provides index * Rename playground css to ide * Change Playground to IDE in ide.css No visible change in effect. Regardless, this will probably be refactored once SCSS is introduced * Change "ClEAR_REPL" to "CLEAR_REPL_INPUT" This is to differenciate between the clearing of repl input and output * Add button and actions to clear REPL output - Button added - Action and reducer added * Make the clear button wipe the REPL context * Revert "Make clear button wipe the REPL context" I realised that source academy does not clear the context, and that clearing only the output is in line with most command-line applications and REPLs. This reverts commit 4bd3c443ae0b6fc45db5bae830254ca3037ac1fa. * Remove duplicate EditorContainer Was received from merging master into the branch * Fix button row in playground overflowing to right Would cause tiny bit of empty space on the right of the page, slightly outside the viewport. * Make run button clearReplOutput, remove default * Output code typed in ReplInput as Output * Use array method concat, reorder definitions Readability changes. * No verify commit, tests fails * Fix failing tests Okay, so slang's display needs access to the store in order to call dispatch, so that the display can be output'd in realtime. In the previous commit, this was achieved by importing the instantiated store from src/index.tsx into src/slang/stdlib/misc.ts. This works when the full server is running, but fails with the tests, because mockContext calls createContext, which imports stdlib/misc, which tries to import store. But because it's just a test, and store doesn't exist, the test fails. * Make run button interrupt execution * Make eval interrupt execution, remove notifs Next, change display so that if playground.output array is empty, initialise. * Fix display not working after a clear * Fix Eval button crashing site when Output[] empty This is because the Output SFC tries to access an undefined output.consoleLogs. output.consoleLogs is undefined after an Eval button click, because EVAL_INTERPRETER_(SUCCESS|ERRORS) will try to access the last element of playground.outputs, but the last element after playground.outputs is always a CodeOutput for Eval button presses; and CodeOutput doesn't have a consoleLogs property, so EVAL_INTERPRETER_(SUCCESS|ERRORS) copies over undefined as the new ResultOutput or ErrorOutput's consoleLog property. * Refactor display interactions with react/redux * Add Math obj, symbols NaN and Infinity Math: resolves #20, Symbols: resolves #16 * Add Source §2 (#35) * Rename context.week -> context.chapter * Add list library TODO: Arrow functions do not work with map * Make context chapter 2 by default for dev * Fix arrow funcs not working with map/build_list... * Revert default libraries back to chap 1 * Add sourceChapter property to playground state Playground defaults to most recent source chapter. * Add component for selecting source chapters Not yet wired. * Add action for chapter select Not yet wired up. * Add chapter select * Add section sign, notification on chapter change * Add list.equal --- src/actions/actionTypes.ts | 2 + src/actions/playground.ts | 13 +++ src/components/IDE/Control.tsx | 54 ++++++++---- src/components/IDE/__tests__/Control.tsx | 1 + .../__tests__/__snapshots__/Control.tsx.snap | 14 ++- src/containers/IDE/ControlContainer.ts | 4 +- src/mocks/context.ts | 4 +- src/reducers/playground.ts | 8 +- src/reducers/states.ts | 7 +- src/sagas/index.ts | 16 +++- .../__tests__/__snapshots__/index.ts.snap | 86 +++++++++++++++++++ src/slang/createContext.ts | 53 ++++++------ src/slang/interop.ts | 4 +- src/slang/parser.ts | 6 +- src/slang/syntaxTypes.ts | 46 +++++----- src/slang/types.ts | 2 +- 16 files changed, 235 insertions(+), 85 deletions(-) diff --git a/src/actions/actionTypes.ts b/src/actions/actionTypes.ts index 536d1fdee9..735b1a65c0 100644 --- a/src/actions/actionTypes.ts +++ b/src/actions/actionTypes.ts @@ -13,6 +13,8 @@ export const CLEAR_REPL_INPUT = 'CLEAR_REPL_INPUT' export const CLEAR_REPL_OUTPUT = 'CLEAR_REPL_OUTPUT' export const CLEAR_CONTEXT = 'CLEAR_CONTEXT' export const SEND_REPL_INPUT_TO_OUTPUT = 'SEND_REPL_INPUT_TO_OUTPUT' +export const CHAPTER_SELECT = 'CHAPTER_SELECT' +export const CHANGE_CHAPTER = 'CHANGE_CHAPTER' /** Interpreter */ export const HANDLE_CONSOLE_LOG = 'HANDLE_CONSOLE_LOG' diff --git a/src/actions/playground.ts b/src/actions/playground.ts index c900fe6437..39b772f22c 100644 --- a/src/actions/playground.ts +++ b/src/actions/playground.ts @@ -1,3 +1,4 @@ +import { ChangeEvent } from 'react' import { ActionCreator } from 'redux' import * as actionTypes from './actionTypes' @@ -19,6 +20,18 @@ export const sendReplInputToOutput: ActionCreator = (newOut } }) +export const chapterSelect: ActionCreator = ( + e: ChangeEvent +) => ({ + type: actionTypes.CHAPTER_SELECT, + payload: e.currentTarget.value +}) + +export const changeChapter: ActionCreator = (newChapter: number) => ({ + type: actionTypes.CHANGE_CHAPTER, + payload: newChapter +}) + export const evalEditor = () => ({ type: actionTypes.EVAL_EDITOR }) diff --git a/src/components/IDE/Control.tsx b/src/components/IDE/Control.tsx index ee54aa5014..543773cb8b 100644 --- a/src/components/IDE/Control.tsx +++ b/src/components/IDE/Control.tsx @@ -1,6 +1,7 @@ +import { Button, IconName, Intent } from '@blueprintjs/core' import * as React from 'react' -import { Button, IconName, Intent } from '@blueprintjs/core' +import { sourceChapters } from '../../reducers/states' /** * @property handleEvalEditor - A callback function for evaluation @@ -11,27 +12,41 @@ export interface IControlProps { handleEvalEditor: () => void handleEvalRepl: () => void handleClearReplOutput: () => void + handleChapterSelect: (e: React.ChangeEvent) => void handleInterruptEval: () => void } +const genericButton = ( + label: string, + icon: IconName, + handleClick = () => {}, + intent = Intent.NONE, + notMinimal = false +) => ( + +) + +const chapterSelect = (handleSelect = (e: React.ChangeEvent) => {}) => ( +
+ +
+) + class Control extends React.Component { public render() { - const genericButton = ( - label: string, - icon: IconName, - handleClick = () => {}, - intent = Intent.NONE, - notMinimal = false - ) => ( - - ) const runButton = this.props.isRunning ? null : genericButton('Run', 'play', this.props.handleEvalEditor) @@ -48,8 +63,9 @@ class Control extends React.Component {
-
{evalButton}
-
{clearButton}
+ {chapterSelect(this.props.handleChapterSelect)} +
{evalButton}
+
{clearButton}
diff --git a/src/components/IDE/__tests__/Control.tsx b/src/components/IDE/__tests__/Control.tsx index 941a3e1239..d74ee3a909 100644 --- a/src/components/IDE/__tests__/Control.tsx +++ b/src/components/IDE/__tests__/Control.tsx @@ -10,6 +10,7 @@ test('Control renders correctly', () => { handleEvalEditor: () => {}, handleEvalRepl: () => {}, handleClearReplOutput: () => {}, + handleChapterSelect: (e: React.ChangeEvent) => {}, handleInterruptEval: () => {} } const app = diff --git a/src/components/IDE/__tests__/__snapshots__/Control.tsx.snap b/src/components/IDE/__tests__/__snapshots__/Control.tsx.snap index 162ba7418a..1dc888996b 100644 --- a/src/components/IDE/__tests__/__snapshots__/Control.tsx.snap +++ b/src/components/IDE/__tests__/__snapshots__/Control.tsx.snap @@ -9,12 +9,22 @@ exports[`Control renders correctly 1`] = `
-
+
+ +
+
Eval
-
+
Clear diff --git a/src/containers/IDE/ControlContainer.ts b/src/containers/IDE/ControlContainer.ts index 0c52a2baa6..5c683699ed 100644 --- a/src/containers/IDE/ControlContainer.ts +++ b/src/containers/IDE/ControlContainer.ts @@ -2,7 +2,7 @@ import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux' import { bindActionCreators, Dispatch } from 'redux' import { handleInterruptExecution } from '../../actions/interpreter' -import { clearReplOutput, evalEditor, evalRepl } from '../../actions/playground' +import { chapterSelect, clearReplOutput, evalEditor, evalRepl } from '../../actions/playground' import Control, { IControlProps } from '../../components/IDE/Control' import { IState } from '../../reducers/states' @@ -11,6 +11,7 @@ type StateProps = Pick type DispatchProps = Pick & Pick & Pick & + Pick & Pick /** No-op mapStateToProps */ @@ -27,6 +28,7 @@ const mapDispatchToProps: MapDispatchToProps = (dispatch: Dis handleEvalEditor: evalEditor, handleEvalRepl: evalRepl, handleClearReplOutput: clearReplOutput, + handleChapterSelect: chapterSelect, handleInterruptEval: handleInterruptExecution }, dispatch diff --git a/src/mocks/context.ts b/src/mocks/context.ts index 12ecd4c6f7..ded2dfc7df 100644 --- a/src/mocks/context.ts +++ b/src/mocks/context.ts @@ -1,6 +1,6 @@ import { createContext } from '../slang' import { Context } from '../slang/types' -export function mockContext(): Context { - return createContext() +export function mockContext(chapter = 1): Context { + return createContext(chapter) } diff --git a/src/reducers/playground.ts b/src/reducers/playground.ts index 2a00539a20..f7f449af63 100644 --- a/src/reducers/playground.ts +++ b/src/reducers/playground.ts @@ -1,5 +1,6 @@ import { Reducer } from 'redux' import { + CHANGE_CHAPTER, CLEAR_CONTEXT, CLEAR_REPL_INPUT, CLEAR_REPL_OUTPUT, @@ -44,7 +45,12 @@ export const reducer: Reducer = (state = defaultPlayground, ac case CLEAR_CONTEXT: return { ...state, - context: createContext() + context: createContext(state.sourceChapter) + } + case CHANGE_CHAPTER: + return { + ...state, + sourceChapter: action.payload } case HANDLE_CONSOLE_LOG: /* Possible cases: diff --git a/src/reducers/states.ts b/src/reducers/states.ts index 696621780f..e548185339 100644 --- a/src/reducers/states.ts +++ b/src/reducers/states.ts @@ -11,7 +11,11 @@ export interface IApplicationState { readonly environment: ApplicationEnvironment } +export const sourceChapters = [1, 2] +const latestSourceChapter = sourceChapters.slice(-1)[0] + export interface IPlaygroundState { + readonly sourceChapter: number readonly editorValue: string readonly replValue: string readonly context: Context @@ -88,9 +92,10 @@ export const defaultApplication: IApplicationState = { } export const defaultPlayground: IPlaygroundState = { + sourceChapter: latestSourceChapter, editorValue: '', replValue: '', - context: createContext(), + context: createContext(latestSourceChapter), output: [], isRunning: false } diff --git a/src/sagas/index.ts b/src/sagas/index.ts index 6b5fe39e69..10c4727f61 100644 --- a/src/sagas/index.ts +++ b/src/sagas/index.ts @@ -6,7 +6,7 @@ import { Context, interrupt, runInContext } from '../slang' import * as actions from '../actions' import * as actionTypes from '../actions/actionTypes' -import { showWarningMessage } from '../notification' +import { showSuccessMessage, showWarningMessage } from '../notification' function* evalCode(code: string, context: Context) { const { result, interrupted } = yield race({ @@ -44,6 +44,20 @@ function* interpreterSaga(): SagaIterator { yield put(actions.sendReplInputToOutput(code)) yield* evalCode(code, context) }) + + yield takeEvery(actionTypes.CHAPTER_SELECT, function*(action) { + const newChapter = parseInt((action as actionTypes.IAction).payload, 10) + const oldChapter = yield select((state: IState) => state.playground.sourceChapter) + if (newChapter !== oldChapter) { + yield put(actions.changeChapter(newChapter)) + yield put(actions.handleInterruptExecution()) + yield put(actions.clearContext()) + yield put(actions.clearReplOutput()) + yield call(showSuccessMessage, `Switched to Source \xa7${newChapter}`) + } else { + yield undefined + } + }) } function* mainSaga() { diff --git a/src/slang/__tests__/__snapshots__/index.ts.snap b/src/slang/__tests__/__snapshots__/index.ts.snap index c18adc1898..85933c03d2 100644 --- a/src/slang/__tests__/__snapshots__/index.ts.snap +++ b/src/slang/__tests__/__snapshots__/index.ts.snap @@ -6,10 +6,53 @@ Object { "value": ArrowClosure { "frame": Object { "environment": Object { + "Infinity": Infinity, + "NaN": NaN, "display": [Function], "error": [Function], + "math_E": 2.718281828459045, + "math_LN10": 2.302585092994046, + "math_LN2": 0.6931471805599453, + "math_LOG10E": 0.4342944819032518, + "math_LOG2E": 1.4426950408889634, "math_PI": 3.141592653589793, + "math_SQRT1_2": 0.7071067811865476, + "math_SQRT2": 1.4142135623730951, + "math_abs": [Function], + "math_acos": [Function], + "math_acosh": [Function], + "math_asin": [Function], + "math_asinh": [Function], + "math_atan": [Function], + "math_atan2": [Function], + "math_atanh": [Function], + "math_cbrt": [Function], + "math_ceil": [Function], + "math_clz32": [Function], + "math_cos": [Function], + "math_cosh": [Function], + "math_exp": [Function], + "math_expm1": [Function], + "math_floor": [Function], + "math_fround": [Function], + "math_hypot": [Function], + "math_imul": [Function], + "math_log": [Function], + "math_log10": [Function], + "math_log1p": [Function], + "math_log2": [Function], + "math_max": [Function], + "math_min": [Function], + "math_pow": [Function], + "math_random": [Function], + "math_round": [Function], + "math_sign": [Function], + "math_sin": [Function], + "math_sinh": [Function], "math_sqrt": [Function], + "math_tan": [Function], + "math_tanh": [Function], + "math_trunc": [Function], "parse_int": [Function], "prompt": [Function], "runtime": [Function], @@ -66,10 +109,53 @@ exports[`Arrow function definition returns itself 2`] = ` ArrowClosure { "frame": Object { "environment": Object { + "Infinity": Infinity, + "NaN": NaN, "display": [Function], "error": [Function], + "math_E": 2.718281828459045, + "math_LN10": 2.302585092994046, + "math_LN2": 0.6931471805599453, + "math_LOG10E": 0.4342944819032518, + "math_LOG2E": 1.4426950408889634, "math_PI": 3.141592653589793, + "math_SQRT1_2": 0.7071067811865476, + "math_SQRT2": 1.4142135623730951, + "math_abs": [Function], + "math_acos": [Function], + "math_acosh": [Function], + "math_asin": [Function], + "math_asinh": [Function], + "math_atan": [Function], + "math_atan2": [Function], + "math_atanh": [Function], + "math_cbrt": [Function], + "math_ceil": [Function], + "math_clz32": [Function], + "math_cos": [Function], + "math_cosh": [Function], + "math_exp": [Function], + "math_expm1": [Function], + "math_floor": [Function], + "math_fround": [Function], + "math_hypot": [Function], + "math_imul": [Function], + "math_log": [Function], + "math_log10": [Function], + "math_log1p": [Function], + "math_log2": [Function], + "math_max": [Function], + "math_min": [Function], + "math_pow": [Function], + "math_random": [Function], + "math_round": [Function], + "math_sign": [Function], + "math_sin": [Function], + "math_sinh": [Function], "math_sqrt": [Function], + "math_tan": [Function], + "math_tanh": [Function], + "math_trunc": [Function], "parse_int": [Function], "prompt": [Function], "runtime": [Function], diff --git a/src/slang/createContext.ts b/src/slang/createContext.ts index 45921567b0..d958b515a9 100644 --- a/src/slang/createContext.ts +++ b/src/slang/createContext.ts @@ -17,8 +17,8 @@ const createEmptyRuntime = () => ({ nodes: [] }) -export const createEmptyContext = (week: number): Context => ({ - week, +export const createEmptyContext = (chapter: number): Context => ({ + chapter, errors: [], cfg: createEmptyCFG(), runtime: createEmptyRuntime() @@ -56,24 +56,15 @@ export const importExternals = (context: Context, externals: string[]) => { export const importBuiltins = (context: Context) => { ensureGlobalEnvironmentExist(context) - if (context.week >= 3) { - defineSymbol(context, 'math_PI', Math.PI) - defineSymbol(context, 'math_sqrt', Math.sqrt) + if (context.chapter >= 1) { defineSymbol(context, 'runtime', misc.runtime) defineSymbol(context, 'display', misc.display) defineSymbol(context, 'error', misc.error_message) defineSymbol(context, 'prompt', prompt) defineSymbol(context, 'parse_int', misc.parse_int) defineSymbol(context, 'undefined', undefined) - } - - if (context.week >= 4) { - defineSymbol(context, 'math_log', Math.log) - defineSymbol(context, 'math_exp', Math.exp) - defineSymbol(context, 'alert', alert) - defineSymbol(context, 'math_floor', Math.floor) - defineSymbol(context, 'timed', misc.timed) - + defineSymbol(context, 'NaN', NaN) + defineSymbol(context, 'Infinity', Infinity) // Define all Math libraries const objs = Object.getOwnPropertyNames(Math) for (const i in objs) { @@ -88,14 +79,15 @@ export const importBuiltins = (context: Context) => { } } - if (context.week >= 5) { - defineSymbol(context, 'list', list.list) + if (context.chapter >= 2) { + // List library defineSymbol(context, 'pair', list.pair) defineSymbol(context, 'is_pair', list.is_pair) - defineSymbol(context, 'is_list', list.is_list) - defineSymbol(context, 'is_empty_list', list.is_empty_list) defineSymbol(context, 'head', list.head) defineSymbol(context, 'tail', list.tail) + defineSymbol(context, 'is_empty_list', list.is_empty_list) + defineSymbol(context, 'is_list', list.is_list) + defineSymbol(context, 'list', list.list) defineSymbol(context, 'length', list.length) defineSymbol(context, 'map', list.map) defineSymbol(context, 'build_list', list.build_list) @@ -106,12 +98,20 @@ export const importBuiltins = (context: Context) => { defineSymbol(context, 'member', list.member) defineSymbol(context, 'remove', list.remove) defineSymbol(context, 'remove_all', list.remove_all) - defineSymbol(context, 'equal', list.equal) - defineSymbol(context, 'assoc', list.assoc) defineSymbol(context, 'filter', list.filter) defineSymbol(context, 'enum_list', list.enum_list) defineSymbol(context, 'list_ref', list.list_ref) defineSymbol(context, 'accumulate', list.accumulate) + defineSymbol(context, 'equal', list.equal) + } + + if (context.chapter >= Infinity) { + // previously week 4 + defineSymbol(context, 'alert', alert) + defineSymbol(context, 'math_floor', Math.floor) + defineSymbol(context, 'timed', misc.timed) + // previously week 5 + defineSymbol(context, 'assoc', list.assoc) if (window.hasOwnProperty('ListVisualizer')) { defineSymbol(context, 'draw', (window as any).ListVisualizer.draw) } else { @@ -119,22 +119,19 @@ export const importBuiltins = (context: Context) => { throw new Error('List visualizer is not enabled') }) } - } - if (context.week >= 6) { + // previously week 6 defineSymbol(context, 'is_number', misc.is_number) - } - if (context.week >= 8) { + // previously week 8 defineSymbol(context, 'undefined', undefined) defineSymbol(context, 'set_head', list.set_head) defineSymbol(context, 'set_tail', list.set_tail) - } - if (context.week >= 9) { + // previously week 9 defineSymbol(context, 'array_length', misc.array_length) } } -const createContext = (week = 3, externals = []) => { - const context = createEmptyContext(week) +const createContext = (chapter = 1, externals = []) => { + const context = createEmptyContext(chapter) importBuiltins(context) importExternals(context, externals) diff --git a/src/slang/interop.ts b/src/slang/interop.ts index 082b8a6985..45030dbd24 100644 --- a/src/slang/interop.ts +++ b/src/slang/interop.ts @@ -1,7 +1,7 @@ import { generate } from 'astring' import { MAX_LIST_DISPLAY_LENGTH } from './constants' import { apply } from './interpreter' -import { Closure, Context, Value } from './types' +import { ArrowClosure, Closure, Context, Value } from './types' export const closureToJS = (value: Value, context: Context, klass: string) => { function DummyClass(this: Value) { @@ -30,7 +30,7 @@ export const closureToJS = (value: Value, context: Context, klass: string) => { } export const toJS = (value: Value, context: Context, klass?: string) => { - if (value instanceof Closure) { + if (value instanceof Closure || value instanceof ArrowClosure) { return value.fun } else { return value diff --git a/src/slang/parser.ts b/src/slang/parser.ts index e975b8c564..dcfa02e1e0 100644 --- a/src/slang/parser.ts +++ b/src/slang/parser.ts @@ -10,7 +10,7 @@ import { Context, ErrorSeverity, ErrorType, SourceError } from './types' // tslint:disable-next-line:interface-name export interface ParserOptions { - week: number + chapter: number } export class DisallowedConstructError implements SourceError { @@ -133,7 +133,7 @@ for (const type of Object.keys(syntaxTypes)) { usages: [] } context.cfg.edges[id] = [] - if (syntaxTypes[node.type] > context.week) { + if (syntaxTypes[node.type] > context.chapter) { context.errors.push(new DisallowedConstructError(node)) } } @@ -167,7 +167,7 @@ rules.forEach(rule => { const keys = Object.keys(rule.checkers) keys.forEach(key => { walkers[key] = compose(walkers[key], (node, context) => { - if (typeof rule.disableOn !== 'undefined' && context.week >= rule.disableOn) { + if (typeof rule.disableOn !== 'undefined' && context.chapter >= rule.disableOn) { return } const checker = rule.checkers[key] diff --git a/src/slang/syntaxTypes.ts b/src/slang/syntaxTypes.ts index 0efe756b42..2ebbfb2c54 100644 --- a/src/slang/syntaxTypes.ts +++ b/src/slang/syntaxTypes.ts @@ -1,42 +1,40 @@ const syntaxTypes: { [nodeName: string]: number } = { - // Week 3 - Program: 3, - ExpressionStatement: 3, - IfStatement: 3, - FunctionDeclaration: 3, - VariableDeclaration: 3, - ReturnStatement: 3, - CallExpression: 3, - UnaryExpression: 3, - BinaryExpression: 3, - LogicalExpression: 3, - ConditionalExpression: 3, - FunctionExpression: 3, - ArrowFunctionExpression: 3, - Identifier: 3, - Literal: 3, + // Chapter 1 + Program: 1, + ExpressionStatement: 1, + IfStatement: 1, + FunctionDeclaration: 1, + VariableDeclaration: 1, + ReturnStatement: 1, + CallExpression: 1, + UnaryExpression: 1, + BinaryExpression: 1, + LogicalExpression: 1, + ConditionalExpression: 1, + FunctionExpression: 1, + ArrowFunctionExpression: 1, + Identifier: 1, + Literal: 1, + + // Chapter 2 + ArrayExpression: 2, // Week 5 EmptyStatement: 5, - ArrayExpression: 5, - - // Week 8 + // preivously Week 8 AssignmentExpression: 8, WhileStatement: 8, - - // Week 9 + // previously Week 9 ForStatement: 9, BreakStatement: 9, ContinueStatement: 9, MemberExpression: 9, - - // Week 10 + // previously Week 10 ThisExpression: 10, ObjectExpression: 10, Property: 10, UpdateExpression: 10, NewExpression: 10, - // Disallowed Forever SwitchStatement: Infinity, DebuggerStatement: Infinity, diff --git a/src/slang/types.ts b/src/slang/types.ts index 6a9c186597..338f44f985 100644 --- a/src/slang/types.ts +++ b/src/slang/types.ts @@ -93,7 +93,7 @@ export interface TypeError extends SourceError { export interface Context { /** The source version used */ - week: number + chapter: number /** All the errors gathered */ errors: SourceError[]