diff --git a/grade.html b/grade.html new file mode 100644 index 0000000..c7f6701 --- /dev/null +++ b/grade.html @@ -0,0 +1,17 @@ + + + + + + flapjs grading + + + + + + +
+ + + + \ No newline at end of file diff --git a/scripts/compute.js b/scripts/compute.js index 3b7b6f3..ba0f693 100644 --- a/scripts/compute.js +++ b/scripts/compute.js @@ -393,7 +393,7 @@ function* run_input_Moore(graph, input, interactive) { * @param {string} input - input string * @param {boolean} interactive - whether to step through and highlight the computation * @returns {Iterable} return a generator that - * if noninteractive, evaluates to the final accept/reject immediately in one step + * if noninteractive, evaluates to the final accept/reject immediately in one step * if interactive, evaluates step by step with highlight */ export function run_input(graph, machine_type, input, interactive=false) { diff --git a/scripts/grade.js b/scripts/grade.js new file mode 100644 index 0000000..414b045 --- /dev/null +++ b/scripts/grade.js @@ -0,0 +1,109 @@ +/** @module grade */ + +import * as compute from './compute.js'; +import * as permalink from './permalink.js'; + +// if not in browser, don't run +if (typeof document !== 'undefined') { + const cur_file_path = window.location.pathname; + if (cur_file_path.includes('grade.html')) { + document.addEventListener('DOMContentLoaded', init); + } +} + +let test_cases = []; +let graph = {}; + +function display_test_cases(test_cases) { + const test_cases_display_area = document.getElementById('display_test_cases'); + while (test_cases_display_area.childElementCount) { // wipe all elements + test_cases_display_area.removeChild(test_cases_display_area.firstChild); + } + for (const [input_string, accepted] of test_cases) { + const test_case_div = document.createElement('div'); + const test_string_span = document.createElement('span'); + const test_result_span = document.createElement('span'); + test_result_span.className = 'result_class'; + test_result_span.id = `${input_string}_result`; + test_string_span.innerText = input_string; + test_result_span.innerText = accepted ? 'accepted' : 'rejected'; + test_case_div.appendChild(test_string_span); + test_case_div.appendChild(test_result_span); + test_cases_display_area.appendChild(test_case_div); + } +} + +/** + * given test cases as plain text, parse the document and store the results in `test_cases` + * @param {string} text - a string consisting of lines of input. every two line specify a test case, with + * the first line being the input string and the second line being accept/reject + */ +function parse_test_cases(text) { + test_cases = []; // clear + const lines = text.trimEnd().split('\n') // if the file ends with a new line, remove it + if (lines.length & 1) { + alert('invalid test file: odd number of lines'); + return; + } + for (let i = 0; i < lines.length; i+=2) { + let accepted; + switch (lines[i+1]) { + case 'accepted': + accepted = true; + break; + case 'rejected': + accepted = false; + break; + default: + alert('invalid test file: keywords other than "accepted" or "rejected"'); + return; + } + test_cases.push([lines[i], accepted]); + } + display_test_cases(test_cases); +} + +function test_all_cases() { + const url_input = document.getElementById('machine_url'); + const url = url_input.value; + const graph_str = url.substring(url.indexOf('#')+1); + let type, graph; + try { + [type, graph] = permalink.deserialize(graph_str); + } catch { + alert('invalid graph') + return; + } + + for (const [input_string, expected] of test_cases) { + // eslint-disable-next-line no-unused-vars + const { value: actual, _ } = compute.run_input(graph, type, input_string).next(); + const color = (expected == actual) ? 'green' : 'red'; + const input_string_result = document.getElementById(`${input_string}_result`); + input_string_result.style.background = color; + } +} + +function upload_test_cases() { + const test_case_uploader = document.getElementById('test_cases'); + const fr = new FileReader(); + fr.onload = () => parse_test_cases(fr.result); + if (test_case_uploader.files.length) { + fr.readAsText(test_case_uploader.files[0]) + } +} + +function init() { + const test_case_uploader = document.getElementById('test_cases'); + test_case_uploader.addEventListener('change', upload_test_cases); + const start_test_btn = document.getElementById('start_test'); + start_test_btn.addEventListener('click', test_all_cases); + const url_input = document.getElementById('machine_url'); + url_input.addEventListener('click', () => { + for (const elem of document.getElementsByClassName('result_class')) { + if (elem.style.background) { + elem.style.background = ''; + } + } + }); +} diff --git a/scripts/index.js b/scripts/index.js index f89494e..ef1cf0a 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -13,11 +13,15 @@ import * as ui_setup from './ui_setup.js'; // if not in browser, don't run if (typeof document !== 'undefined') { - document.addEventListener('DOMContentLoaded', init); - window.addEventListener('resize', () => drawing.draw(graph)); + // get current page + const cur_file_path = window.location.pathname; + if (!cur_file_path.includes('grade.html')) { + document.addEventListener('DOMContentLoaded', init); + window.addEventListener('resize', () => drawing.draw(graph)); + } } -let graph = {}; // global graph +let graph = {}; // global graph, initially empty /** handles double click */ function bind_double_click() { @@ -415,7 +419,7 @@ function init() { bind_dd(); bind_permalink(); bind_mousemove(); - ui_setup.bind_plus_minus(); + ui_setup.bind_plus_minus(``); ui_setup.add_input_bar(); // called so one input bar appears on opening of homepage ui_setup.htmlSetUp(); // initiate eventlisteners for sidenavbar, second sidenavbar, and popup tutorial init_graph(); // leave this last since we want it to override some of the above diff --git a/styles/grade.css b/styles/grade.css new file mode 100644 index 0000000..6ddb935 --- /dev/null +++ b/styles/grade.css @@ -0,0 +1,4 @@ +#display_test_cases > div { + display: flex; + justify-content: space-between; +} diff --git a/test_cases.txt b/test_cases.txt new file mode 100644 index 0000000..9f81c38 --- /dev/null +++ b/test_cases.txt @@ -0,0 +1,10 @@ +101 +rejected +01 +accepted + +accepted +0011 +accepted +00111 +rejected \ No newline at end of file