[null,{"project":"session","version":"1.0.0","links":"[]","unit":"struct","info":"# TODO\n\n* Add a function, say `Session.toQuiz : Type.list (SESSION) --> Html.Code.DOCUMENT` that returns an interactive questionnaire.\n\n
Session.toQuiz : Type.list (SESSION) --> Html.Code.DOCUMENT
that returns an interactive questionnaire. Data.Moral.type(SESSION)\n ↗ \\\n / \\\nfromFlatCode / \\ Data.Moral.goodValue\n / \\\n / ↘ runnerCode\n FLAT_CODE ←⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ SESSION ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯→ code : EcmaScript.CODE\n toFlatCode | /\n | /\n | /\n run | / code(session)\n | /\n | /\n ↓ ↙\n SESSION\n |\n |\n bugChecker |\n |\n ↓ bugSummary\n SESSION ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯→ Type.nullify(Type.MARKDOWN)\n |\n |\n | allMarkdownToHtml\n | toHtml\n |\n ↓\n Data.HTML
\nSESSION
syntaxSESSION | \n::= | \nType.list (Type.or (SESSION_STEP) (DOC_STEP)) | \n
SESSION_STEP
syntaxEach SESSION_STEP
is a record with the following properties:
id | \nEcmaScript.ID | \n
input | \nEcmaScript.CODE | \n
output | \nANYTHING | \n
__output__ | \nANYTHING | \n
error | \nSTRING | \n
__error__ | \nSTRING | \n
__bug__ | \nType.nullify(Data.Moral.type(STRING)) | \n
Note, that
\ninput
is a mandatory property, the others are all optional.output
or error
; and also at most one of the two properties __output__
or __error__
.DOC_STEP
syntaxdoc | \nType.MARKDOWN | \n
__doc__ | \nData.HTML | \n
id
, input
, output
, error
and doc
are user-defined properties.__output__
, __error__
, __bug__
and __doc__
are machine-generated properties.__output__
is the real output, __error__
is the real error, output
is the predicted output and error
is the predicted error.Consider the advise to only use identifiers with a single initial _
as variables in sessions.
Note, that our symbol ◀
for input, ▶
for output and ❌
for error are similar to the ones of Firebug. The prompt >
of Node.js is a kind of input symbol, took, but goes into the opposite direction.
Note, that our (named) input statement ["in", x, expr]
does not translate in node to
> x = expr\n> var x = expr // alteratively
\nbut to
\n> (x = expr, x)\n> (var x = expr, x) // alternatively
\nThe former is a statement and evaluates to undefined
, the latter is an expression and evaluates to the value of x
.
Each session is list of session steps and doc steps, and each of these steps is a string record. So, generally speaking, each session is a String Record List, i.e. each SESSION
value is a Type.list(Type.record(STRING))
value.
In the flatcode.tofu
we already defined Flat Code, which is a more convenient form to edit String Record Lists. A String Record List
[ { a_1 : "AAA111",\n a_2 : "AAA222",\n a_3 : "AAA333" },\n { b_1 : "BBB111",\n b_2 : "BBB222" },\n { c_1 : "C1",\n c_2 : "CC22",\n c_3 : "CCC333" } ]
\ncan equivalently be represented by the FlatCode string
\n'---\\na_1\\n AAA111\\na_2\\n AAA222\\na_3\\n AAA333\\n---\\nb_1\\n BBB111\\nb_2\\n BBB222\\n---\\nc_1\\n C1\\nc_2\\n CC22\\nc_3\\n CCC333\\n---\\n'
\nwhich is displayed as
\n╭─────────╮\n│---↵ │\n│a_1↵ │\n│ AAA111↵│\n│a_2↵ │\n│ AAA222↵│\n│a_3↵ │\n│ AAA333↵│\n│---↵ │\n│b_1↵ │\n│ BBB111↵│\n│b_2↵ │\n│ BBB222↵│\n│---↵ │\n│c_1↵ │\n│ C1↵ │\n│c_2↵ │\n│ CC22↵ │\n│c_3↵ │\n│ CCC333↵│\n│---↵ │\n│ │\n╰─────────╯
\nIf strRecL
is the given String Record List, the according Flat Code can be generated by
FlatCode.fromStringRecordList (strRecL)
\nThe rules for this conversion are:
\neach key-value pair key: str
is written on a line with key
and str
, indented by two spaces on the next line
╭───────╮\n│key↵ │\n│ str1↵│\n╰───────╯
the String Record List [r_1,...,r_n]
itself is then displayed as a horizontal sequence of blocks, separated by horizontal Markdown lines, i.e. lines starting with three or more dashes ---
.
╭──────╮\n│---↵ │\n│r_1↵ │\n│---↵ │\n│r_2↵ │\n│---↵ │\n│r_3↵ │\n│---↵ │\n╰──────╯
The conversions are done by
\n fromFlatCode Data.Moral.goodValue\n FLAT_CODE ----------------> Data.Moral.type(Session) -------------------> SESSION\n\n\n toFlatCode\n SESSION -----------------------------------------------------> FLAT_CODE
\nSymbol | \nUnicode/JS | \nHTML | \n
---|---|---|
| | \n"\\u007c" | \n| | \n
⎯ | \n"\\u23af" | \n⎯ | \n
← | \n"\\u2190" | \n← | \n
↑ | \n"\\u2191" | \n↑ | \n
→ | \n"\\u2192" | \n→ | \n
↓ | \n"\\u2193" | \n↓ | \n
↖ | \n"\\u2196" | \n↖ | \n
↗ | \n"\\u2197" | \n↗ | \n
↘ | \n"\\u2198" | \n↘ | \n
↙ | \n"\\u2199" | \n↙ | \n
The Session
structure provides data types and functions that allow to conveniently display, run, test and document entire JavaScript sessions.
This provides a testing system for JavaScript programs and a more expressive and comprehensible alternative to assertion testing in say Node.js or Dart.
\nSome JavaScript engines provide an interactive Read-Eval-Print-Loop feature, where the user can input expressions and statements and the machine immediately evaluates and prints the results. Examples of these interactive dialog systems are the REPL of Node.js or the console of Firebug or in Chrome. An actual example of such a dialog is what we call a session.
\nA typical example of a session is this
\n........
\n","$markdown_caption$":"global constant extensible Session : STRUCT","$html_caption$":"global constant extensible Session : STRUCT"},["SESSION_STEP",{"unit":"type","name":"Session.SESSION_STEP","value":"Type.synonym ([\n 'Session.SESSION_STEP',\n Type.modelPlus (Rec.create ({ input : EcmaScript.CODE }))\n (Rec.create ({ id : EcmaScript.ID,\n output : ANYTHING,\n error : STRING, // only either `output` or `error` must occur\n __output__ : ANYTHING,\n __error__ : STRING, // only either `__output__` or `__error__` must occur\n __bug__ : Type.nullify(Data.Moral.type(STRING)) }))\n])","info":"....","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"type","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.SESSION_STEP","$value$":"Type.synonym ([\n 'Session.SESSION_STEP',\n Type.modelPlus (Rec.create ({ input : EcmaScript.CODE }))\n (Rec.create ({ id : EcmaScript.ID,\n output : ANYTHING,\n error : STRING, // only either `output` or `error` must occur\n __output__ : ANYTHING,\n __error__ : STRING, // only either `__output__` or `__error__` must occur\n __bug__ : Type.nullify(Data.Moral.type(STRING)) }))\n])","$info$":"....
\n","$markdown_caption$":"global constant extensible Session.SESSION_STEP : TYPE","$html_caption$":"global constant extensible Session.SESSION_STEP : TYPE"}],["DOC_STEP",{"unit":"type","name":"Session.DOC_STEP","value":"Type.synonym ([\n 'Session.DOC_STEP',\n Type.modelPlus (Rec.create ({ doc : Type.MARKDOWN }))\n (Rec.create ({ __doc__ : Data.HTML }))\n])","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"type","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.DOC_STEP","$value$":"Type.synonym ([\n 'Session.DOC_STEP',\n Type.modelPlus (Rec.create ({ doc : Type.MARKDOWN }))\n (Rec.create ({ __doc__ : Data.HTML }))\n])","$markdown_caption$":"global constant extensible Session.DOC_STEP : TYPE","$html_caption$":"global constant extensible Session.DOC_STEP : TYPE"}],["SESSION",{"unit":"type","name":"Session.SESSION","value":"Type.synonym ([\n 'Session.SESSION',\n Type.list (Type.or (SESSION_STEP) (DOC_STEP))\n])","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"type","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.SESSION","$value$":"Type.synonym ([\n 'Session.SESSION',\n Type.list (Type.or (SESSION_STEP) (DOC_STEP))\n])","$markdown_caption$":"global constant extensible Session.SESSION : TYPE","$html_caption$":"global constant extensible Session.SESSION : TYPE"}],["defaultId",{"unit":"value","name":"Session.defaultId","type":"EcmaScript.ID","value":"'_'","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.defaultId","$value$":"'_'","$markdown_caption$":"global constant extensible Session.defaultId : EcmaScript.ID","$html_caption$":"global constant extensible Session.defaultId : EcmaScript.ID"}],["fromFlatCode",{"unit":"function","name":"Session.fromFlatCode","type":"Type.function ([FLAT_CODE, Data.Moral.type(SESSION)])","value":"function (flatCode) {\n var result = FlatCode.parseStringRecordList (flatCode); // `result` is of type `Data.Moral.type(Type.list(Type.record(STRING)))`\n if (Data.Moral.isBad (result)) {\n return Data.Moral.bad (\n \"`Session.fromFlatCode(flatCode)` error, when trying to convert `flatCode` into a string record list:\\n\" +\n Data.Moral.badValue (result)\n );\n } else {\n var recL = Data.Moral.goodValue (result); // `recL` is here of type `Type.list (Type.record (STRING))`\n // change all `output` and `__output__` properties from type `STRING` to type `ANYTHING`\n for (var i = 0; i < recL.length; i++) {\n if ('output' in recL[i]) {\n try {\n var x = Val.read (recL[i]['output']);\n recL[i]['output'] = x;\n } catch (e) {\n return Data.Moral.bad ( \"`Session.fromFlatCode(flatCode)` error, the `output` value `\"\n + recL[i]['output'] + \"` in the step `\" + i + \"` does not parse to a value.\" )\n }\n }\n if ('__output__' in recL[i]) {\n try {\n var x = Val.read (recL[i]['__output__']);\n recL[i]['__output__'] = x;\n } catch (e) {\n return Data.Moral.bad ( \"`Session.fromFlatCode(flatCode)` error, the `__output__` value `\"\n + recL[i]['__output__'] + \"` in the step `\" + i + \"` does not parse to a value.\" )\n }\n }\n }\n // verify that `recL` is a session\n var c = SESSION.complain (recL);\n if (c) {\n return Data.Moral.bad ( \"`Session.fromFlatCode(flatCode)` error.\\n\"\n + \"`flatCode` does convert into a string record list, which is not a proper `SESSION`, however:\\n\"\n + c );\n } else {\n return Data.Moral.good (recL);\n }\n }\n}","info":"`Session.fromFlatCode (flatCode)` returns either of the following two forms:\n\n* `{good: session}`, if `flatCode` does convert into a proper `SESSION` value (namely `session`), or\n\n* `{bad: message}`, if it does not, where `message` is a string giving an explanation.","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"function","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.fromFlatCode","$value$":"function (flatCode) {\n var result = FlatCode.parseStringRecordList (flatCode); // `result` is of type `Data.Moral.type(Type.list(Type.record(STRING)))`\n if (Data.Moral.isBad (result)) {\n return Data.Moral.bad (\n \"`Session.fromFlatCode(flatCode)` error, when trying to convert `flatCode` into a string record list:\\n\" +\n Data.Moral.badValue (result)\n );\n } else {\n var recL = Data.Moral.goodValue (result); // `recL` is here of type `Type.list (Type.record (STRING))`\n // change all `output` and `__output__` properties from type `STRING` to type `ANYTHING`\n for (var i = 0; i < recL.length; i++) {\n if ('output' in recL[i]) {\n try {\n var x = Val.read (recL[i]['output']);\n recL[i]['output'] = x;\n } catch (e) {\n return Data.Moral.bad ( \"`Session.fromFlatCode(flatCode)` error, the `output` value `\"\n + recL[i]['output'] + \"` in the step `\" + i + \"` does not parse to a value.\" )\n }\n }\n if ('__output__' in recL[i]) {\n try {\n var x = Val.read (recL[i]['__output__']);\n recL[i]['__output__'] = x;\n } catch (e) {\n return Data.Moral.bad ( \"`Session.fromFlatCode(flatCode)` error, the `__output__` value `\"\n + recL[i]['__output__'] + \"` in the step `\" + i + \"` does not parse to a value.\" )\n }\n }\n }\n // verify that `recL` is a session\n var c = SESSION.complain (recL);\n if (c) {\n return Data.Moral.bad ( \"`Session.fromFlatCode(flatCode)` error.\\n\"\n + \"`flatCode` does convert into a string record list, which is not a proper `SESSION`, however:\\n\"\n + c );\n } else {\n return Data.Moral.good (recL);\n }\n }\n}","$info$":"Session.fromFlatCode (flatCode)
returns either of the following two forms:
{good: session}
, if flatCode
does convert into a proper SESSION
value (namely session
), or
{bad: message}
, if it does not, where message
is a string giving an explanation.
....
\n","$markdown_caption$":"global constant extensible Session.toFlatCode : Type.lambda ([SESSION, FLAT_CODE])","$html_caption$":"global constant extensible Session.toFlatCode : Type.lambda ([SESSION, FLAT_CODE])"}],["run",{"unit":"value","name":"Session.run","type":"$prod1 ([SESSION, SESSION])","value":"function (session) {\n session = Val.clone (session);\n for (var i = 0; i < session.length; i++) {\n if ('input' in session[i]) {\n var step = session[i];\n var id = ('id' in step ? step.id : defaultId);\n try {\n eval (\"var \" + id + \" = \" + step.input + \";\");\n var __output__ = eval (id);\n step.__output__ = __output__;\n } catch (e) {\n step.__error__ = e.toString();\n }\n }\n }\n return session;\n}","info":"`Session.run(session)` evaluates the given `session` in the current environment and returns (a clone of) the same `session` again, but with boosted session steps.\n\nFor example,\n\n > Session.run ([{input: '12 * 23'}])\n { input: '12 * 23', __output__: '276' }\n","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.run","$value$":"function (session) {\n session = Val.clone (session);\n for (var i = 0; i < session.length; i++) {\n if ('input' in session[i]) {\n var step = session[i];\n var id = ('id' in step ? step.id : defaultId);\n try {\n eval (\"var \" + id + \" = \" + step.input + \";\");\n var __output__ = eval (id);\n step.__output__ = __output__;\n } catch (e) {\n step.__error__ = e.toString();\n }\n }\n }\n return session;\n}","$info$":"Session.run(session)
evaluates the given session
in the current environment and returns (a clone of) the same session
again, but with boosted session steps.
For example,
\n> Session.run ([{input: '12 * 23'}])\n{ input: '12 * 23', __output__: '276' }
\n","$markdown_caption$":"global constant extensible Session.run : $prod1 ([SESSION, SESSION])","$html_caption$":"global constant extensible Session.run : $prod1 ([SESSION, SESSION])"}],["runnerCode",{"unit":"function","name":"Session.runnerCode","type":"Type.lambda ([SESSION, $literal ($prod0 (SESSION))])","value":"function (session) {\n var code = \"var __session__ =\" + \"\\n\"\n + Str.indent(2)(Literal.block (session)) + \";\\n\";\n for (var i = 0; i < session.length; i++) {\n if ('input' in session[i]) {\n var step = session[i];\n var id = ('id' in step ? step.id : defaultId);\n code += \"try {\" + \"\\n\"\n + \" var \" + id + \" =\" + \"\\n\"\n + Str.indent(4)(step.input) + \";\" + \"\\n\"\n + \" __session__[\" + i + \"].__output__ = \" + id + \";\" + \"\\n\"\n + \"} catch (e) {\" + \"\\n\"\n + \" __session__[\" + i + \"].__error__ = e.toString();\" + \"\\n\"\n + \"}\" + \"\\n\";\n }\n }\n return ( \"(function () {\\n\"\n + Str.indent(2)(code)\n + \" return __session__;\\n\"\n + \"}())\" );\n}","info":"`Session.runner(session)` takes a `session` and returns the code of a self-calling closure function.\nWhen this code is evaluated, it returns the given `session`, but boosted by additional `__output__` or `__error__` properties for each session step.\n\nFor example, let `session1` be the `SESSION` value given by\n\n [ { doc: 'A simple session example' },\n { input: '12 + 34' },\n { id: '_x',\n input: '123' },\n { id: '_y',\n input: '234' },\n { input: '_x + _y' } ]\n\n\nthen `Session.runnerCode(session1)` returns\n\n ╭────────────────────────────────────────────╮\n │(function () {↵ │\n │ var __session__ =↵ │\n │ [ { doc : \"A simple session example\" },↵│\n │ { input : \"12 + 34\" },↵ │\n │ { id : \"_x\",↵ │\n │ input : \"123\" },↵ │\n │ { id : \"_y\",↵ │\n │ input : \"234\" },↵ │\n │ { input : \"_x + _y\" } ];↵ │\n │ try {↵ │\n │ var _ =↵ │\n │ 12 + 34;↵ │\n │ __session__[1].__output__ = _;↵ │\n │ } catch (e) {↵ │\n │ __session__[1].__error__ = e;↵ │\n │ }↵ │\n │ try {↵ │\n │ var _x =↵ │\n │ 123;↵ │\n │ __session__[2].__output__ = _x;↵ │\n │ } catch (e) {↵ │\n │ __session__[2].__error__ = e;↵ │\n │ }↵ │\n │ try {↵ │\n │ var _y =↵ │\n │ 234;↵ │\n │ __session__[3].__output__ = _y;↵ │\n │ } catch (e) {↵ │\n │ __session__[3].__error__ = e;↵ │\n │ }↵ │\n │ try {↵ │\n │ var _ =↵ │\n │ _x + _y;↵ │\n │ __session__[4].__output__ = _;↵ │\n │ } catch (e) {↵ │\n │ __session__[4].__error__ = e;↵ │\n │ }↵ │\n │ return __session__;↵ │\n │}()) │\n ╰────────────────────────────────────────────╯\n\nand when we run this code, e.g. by calling `eval(Session.runnerCode(session1))`, the returned `SESSION` value is this:\n\n [ { doc: 'A simple session example' },\n { input: '12 + 34',\n __output__: 46 },\n { id: '_x',\n input: '123',\n __output__: 123 },\n { id: '_y',\n input: '234',\n __output__: 234 },\n { input: '_x + _y',\n __output__: 357 } ]\n","comment":"Update the `type` of this unit!!!!","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"function","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.runnerCode","$value$":"function (session) {\n var code = \"var __session__ =\" + \"\\n\"\n + Str.indent(2)(Literal.block (session)) + \";\\n\";\n for (var i = 0; i < session.length; i++) {\n if ('input' in session[i]) {\n var step = session[i];\n var id = ('id' in step ? step.id : defaultId);\n code += \"try {\" + \"\\n\"\n + \" var \" + id + \" =\" + \"\\n\"\n + Str.indent(4)(step.input) + \";\" + \"\\n\"\n + \" __session__[\" + i + \"].__output__ = \" + id + \";\" + \"\\n\"\n + \"} catch (e) {\" + \"\\n\"\n + \" __session__[\" + i + \"].__error__ = e.toString();\" + \"\\n\"\n + \"}\" + \"\\n\";\n }\n }\n return ( \"(function () {\\n\"\n + Str.indent(2)(code)\n + \" return __session__;\\n\"\n + \"}())\" );\n}","$info$":"Session.runner(session)
takes a session
and returns the code of a self-calling closure function. When this code is evaluated, it returns the given session
, but boosted by additional __output__
or __error__
properties for each session step.
For example, let session1
be the SESSION
value given by
[ { doc: 'A simple session example' },\n { input: '12 + 34' },\n { id: '_x',\n input: '123' },\n { id: '_y',\n input: '234' },\n { input: '_x + _y' } ]
\nthen Session.runnerCode(session1)
returns
╭────────────────────────────────────────────╮\n│(function () {↵ │\n│ var __session__ =↵ │\n│ [ { doc : "A simple session example" },↵│\n│ { input : "12 + 34" },↵ │\n│ { id : "_x",↵ │\n│ input : "123" },↵ │\n│ { id : "_y",↵ │\n│ input : "234" },↵ │\n│ { input : "_x + _y" } ];↵ │\n│ try {↵ │\n│ var _ =↵ │\n│ 12 + 34;↵ │\n│ __session__[1].__output__ = _;↵ │\n│ } catch (e) {↵ │\n│ __session__[1].__error__ = e;↵ │\n│ }↵ │\n│ try {↵ │\n│ var _x =↵ │\n│ 123;↵ │\n│ __session__[2].__output__ = _x;↵ │\n│ } catch (e) {↵ │\n│ __session__[2].__error__ = e;↵ │\n│ }↵ │\n│ try {↵ │\n│ var _y =↵ │\n│ 234;↵ │\n│ __session__[3].__output__ = _y;↵ │\n│ } catch (e) {↵ │\n│ __session__[3].__error__ = e;↵ │\n│ }↵ │\n│ try {↵ │\n│ var _ =↵ │\n│ _x + _y;↵ │\n│ __session__[4].__output__ = _;↵ │\n│ } catch (e) {↵ │\n│ __session__[4].__error__ = e;↵ │\n│ }↵ │\n│ return __session__;↵ │\n│}()) │\n╰────────────────────────────────────────────╯
\nand when we run this code, e.g. by calling eval(Session.runnerCode(session1))
, the returned SESSION
value is this:
[ { doc: 'A simple session example' },\n { input: '12 + 34',\n __output__: 46 },\n { id: '_x',\n input: '123',\n __output__: 123 },\n { id: '_y',\n input: '234',\n __output__: 234 },\n { input: '_x + _y',\n __output__: 357 } ]
\n","$comment$":"Update the type
of this unit!!!!
Session.bugChecker(session)
checks for every session step and adds a __bug__
property, accordingly.
Note, that __bug__
is of type Type.nullify(Data.Moral.type(STRING))
, which means that we have three distinct kind of values
{bad: message}
, is a malicious bug, saying that there is something fundamentally wrong. This happens when
__output__
and the real __output__
are different values.null
, indicating that there is no bug in the given session step. This is the case in one of the following situations
output
and real __output__
are equal.error
and real __error__
, and they have the same string message.output
but a real __output__
which as a literal string is equal to the input
.{good: message}
, is a benign bug or warning, saying that the session step is insufficiently verified, either because the real value/error is not determined, yet, or because the predicted value/error is still missing. This value occurs when
__error__
and the predicted error
have different message strings.output
, but no real __output__
value, yet.__output__
which is different from the input
, but there is no predicted __output__
value.error
, but no real __error__
thrown, yet.__error__
is thrown here, but no error
is predicted.output
or error
, and no real __output__
or __error__
, at all.Each of these three __bug__
values is represented by a symbol (generated by Session.toHtml(session)
):
Symbol | \n__bug__ value | \nstanding for | \n
---|---|---|
✔ | \nnull | \nO.k., step is verified | \n
??? | \n{good: message} | \nWarning, insufficient information | \n
!!! | \n{bad: message} | \nBug!, wrong prediction | \n
Session.conflictSummary(sessionL)
takes a session
and returns a (rather short) string summarizing all the session steps containing __bug__
properties.
Session.allMarkdownToHtml(session)
returns (a clone of) session
, where each doc
property with a Type.MARKDOWN
value is replaced by a __doc__
property with a Data.HTML
value.
Session.allMarkdownToHtml
requires the Markdown.toHtml
function from the utils
tofu.
The default light gray background and light black color as the basic style for sessions in HTML.
\n","$markdown_caption$":"global constant extensible Session.Css.defaultStyle : $Css","$html_caption$":"global constant extensible Session.Css.defaultStyle : $Css"}],["userStyle",{"unit":"value","name":"Session.Css.userStyle","type":"$Css","value":"'color:#666666;background-color:#FFFFFF;font-family:monospace;padding:1px'","info":"The default colors for user input in sessions: white background and gray font color.\nThe font is `monospace`.","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.Css.userStyle","$value$":"'color:#666666;background-color:#FFFFFF;font-family:monospace;padding:1px'","$info$":"The default colors for user input in sessions: white background and gray font color. The font is monospace
.
The default colors for machine output in sessions: little more gray than the white background of user input. The font is monospace
.
The black triangle pointing to the left ◀ is the input symbol for sessions in HTML.
\n","$markdown_caption$":"global constant extensible Session.Symbols.inputSymbol : Data.HTML","$html_caption$":"global constant extensible Session.Symbols.inputSymbol : Data.HTML"}],["outputSymbol",{"unit":"value","name":"Session.Symbols.outputSymbol","type":"Data.HTML","value":"\"▶\"","info":"The black triangle pointing to the right ▶ is the output symbol for sessions in HTML.","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.Symbols.outputSymbol","$value$":"\"▶\"","$info$":"The black triangle pointing to the right ▶ is the output symbol for sessions in HTML.
\n","$markdown_caption$":"global constant extensible Session.Symbols.outputSymbol : Data.HTML","$html_caption$":"global constant extensible Session.Symbols.outputSymbol : Data.HTML"}],["errorSymbol",{"unit":"value","name":"Session.Symbols.errorSymbol","type":"Data.HTML","value":"\"❌\"","info":"The cross ❌ is the error symbol for sessions in HTML.","$unit_flags$":{"kind_of_value_unit":"static_value","type_of_value_unit":"any","scope":"global","modifiability":"constant","extensibility":"extensible","dialect":"es6","augmentation":"typed_augmentation","origin":"value_code","kind_of_unit":"value"},"$name$":"Session.Symbols.errorSymbol","$value$":"\"❌\"","$info$":"The cross ❌ is the error symbol for sessions in HTML.
\n","$markdown_caption$":"global constant extensible Session.Symbols.errorSymbol : Data.HTML","$html_caption$":"global constant extensible Session.Symbols.errorSymbol : Data.HTML"}]],["toHtml",{"unit":"function","name":"Session.toHtml","type":"Type.lambda ([SESSION, Data.HTML])","value":"function (session) {\n\n var id = defaultId; // updated in each call of `oneStepToHtml(step)`\n\n function userCode (code) {\n return '' + Html.entityify (code) + '';\n }\n function machineCode (code) {\n return '' + Html.entityify (code) + '';\n }\n\n function bugSymbol (bug) { // `bug` is of type `Type.nullify(Data.Moral.type(STRING))`\n if (bug === null)\n { return '✔'; }\n else if (Data.Moral.isGood (bug))\n { return '???'; }\n else if (Data.Moral.isBad (bug))\n { return '!!!'; }\n }\n\n function threeCellRow (leftCell, middleCell, rightCell) {\n return ( 'Session.toHtml(session)
returns a colored HTML version of the given session
.
Note, that HTML is not generated from Markdown by this function. If the given Markdown properties (comment
and doc
) have no HTML counterparts (__comment__
and __doc__
, respectively), the Markdown versions are displayed in the returned HTML. If you need to make sure that all Markdown is converted to HTML, run a prior Session.allMarkdownToHtml(session)
.
The symbols
\n---------------------------------------------------------------------------- --------------------- ------------------------------\n<span style="color:green">✔</span> checked and no bug `__bug__ === null`\n<span style="color:yellow;font-weight:bold" title="message">???</span> unchecked `__bug__ === {good: message}`\n<span style="color:red;font-weight:bold" title="message">!!!</span> checked and bug `__bug__ === {bad: message}`\n---------------------------------------------------------------------------- --------------------- ------------------------------
\n","$comment$":"Correct the table (Markdown/Pandoc/HTML) in the info
!!!
In the JavaScript community, assertion testing has become an established paradigm in verifying the correctness of code. There is a Commonjs specification, which is implemented e.g. by the Assert module of Nodejs. Dart has an assert
statement built into its language.
The basic idea is that you insert an assertion about some value or state in the program and that the JavaScript machine evaluates it. In case the assertion turns out to be true, nothing happens and the machine continues executing. But if the assertion is incorrect, the execution is interrupted and an error message is issued.
\nFor example, in Dart, we can insert a line
\nassert (x != null);
\nand an AssertionError
is throws if the variable x
is null
at this point. Otherwise, the statement is silently ignored.
In a Nodjs REPL session, we can load the Assert
module
> var Assert = require('assert')
\nand have this dialog:
\n> Assert (123 === 123)\nundefined\n> Assert (123 === 234)\nAssertionError: false == true .....
\ni.e. the first input Assert (123 === 123)
has no effect (undefined
) because it is correct. But the second input Assert (123 === 234)
throws an error.
We introduce session testing as a more expressive and powerful testing method, especially for functional programming. It does not only evaluate a single statement, but whole "sessions", i.e. dialogs with interactive JavaScript machines like the Nodejs REPL or the JavaScript console of a browser, say Firefox.
\nSuppose you want to test the output of a user-defined function called square
in a Nodejs REPL session. square(x)
is supposed to return the square value x * x
of its input. At each loop or step in such a session, Node offers you the prompt >
that awaits your input and then prints the result. So, if square
is implemented properly, this would be an example session:
> square(2)\n4\n> square(9)\n81\n> square(-3)\n9\n> square(0)\n0
\nYou could also make ..............................................
\n