summaryrefslogtreecommitdiffstats
path: root/lib/parse
diff options
context:
space:
mode:
Diffstat (limited to 'lib/parse')
-rw-r--r--lib/parse/is_exercise.js17
-rw-r--r--lib/parse/is_quiz.js87
-rw-r--r--lib/parse/lex.js56
3 files changed, 107 insertions, 53 deletions
diff --git a/lib/parse/is_exercise.js b/lib/parse/is_exercise.js
new file mode 100644
index 0000000..74ed753
--- /dev/null
+++ b/lib/parse/is_exercise.js
@@ -0,0 +1,17 @@
+var _ = require('lodash');
+
+function isExercise(nodes) {
+ var codeType = { type: 'code' };
+
+ // Number of code nodes in section
+ var len = _.filter(nodes, codeType).length;
+
+ return (
+ // Got 3 or 4 code blocks
+ (len === 3 || len === 4) &&
+ // Ensure all nodes are at the end
+ _.all(_.last(nodes, len), codeType)
+ );
+}
+
+module.exports = isExercise;
diff --git a/lib/parse/is_quiz.js b/lib/parse/is_quiz.js
new file mode 100644
index 0000000..3322ff0
--- /dev/null
+++ b/lib/parse/is_quiz.js
@@ -0,0 +1,87 @@
+var _ = require('lodash');
+
+function isQuizNode(node) {
+ return (/^[(\[][ x][)\]]/).test(node.text || node);
+}
+
+function isTableQuestion(nodes) {
+ var block = questionBlock(nodes);
+ return (
+ block.length === 1 &&
+ block[0].type === 'table' &&
+ _.all(block[0].cells[0].slice(1), isQuizNode)
+ );
+}
+
+function isListQuestion(nodes) {
+ var block = questionBlock(nodes);
+ // Counter of when we go in and out of lists
+ var inlist = 0;
+ // Number of lists we found
+ var lists = 0;
+ // Elements found outside a list
+ var outsiders = 0;
+ // Ensure that we have nothing except lists
+ _.each(block, function(node) {
+ if(node.type === 'list_start') {
+ inlist++;
+ } else if(node.type === 'list_end') {
+ inlist--;
+ lists++;
+ } else if(inlist === 0) {
+ // Found non list_start or list_end whilst outside a list
+ outsiders++;
+ }
+ });
+ return lists > 0 && outsiders === 0;
+}
+
+function isQuestion(nodes) {
+ return isListQuestion(nodes) || isTableQuestion(nodes);
+}
+
+// Remove (optional) paragraph header node and blockquote
+function questionBlock(nodes) {
+ return nodes.slice(
+ nodes[0].type === 'paragraph' ? 1 : 0,
+ _.findIndex(nodes, { type: 'blockquote_start' })
+ );
+}
+
+function splitQuestions(nodes) {
+ // Represents nodes in current question
+ var buffer = [];
+ return _.reduce(nodes, function(accu, node) {
+ // Add node to buffer
+ buffer.push(node);
+
+ // Flush buffer once we hit the end of a question
+ if(node.type === 'blockquote_end') {
+ accu.push(buffer);
+ // Clear buffer
+ buffer = [];
+ }
+
+ return accu;
+ }, []);
+}
+
+function isQuiz(nodes) {
+ // Extract potential questions
+ var questions = splitQuestions(
+ // Skip quiz title if there
+ nodes.slice(
+ (nodes[0] && nodes[0].type) === 'paragraph' ? 1 : 0
+ )
+ );
+
+ // Nothing that looks like questions
+ if(questions.length === 0) {
+ return false;
+ }
+
+ // Ensure all questions are correctly structured
+ return _.all(questions, isQuestion);
+}
+
+module.exports = isQuiz;
diff --git a/lib/parse/lex.js b/lib/parse/lex.js
index d9f9fd9..3391acf 100644
--- a/lib/parse/lex.js
+++ b/lib/parse/lex.js
@@ -1,6 +1,9 @@
var _ = require('lodash');
var kramed = require('kramed');
+var isExercise = require('./is_exercise');
+var isQuiz = require('./is_quiz');
+
// Split a page up into sections (lesson, exercises, ...)
function splitSections(nodes) {
var section = [];
@@ -17,59 +20,6 @@ function splitSections(nodes) {
}, []).concat([section]); // Add remaining nodes
}
-function isQuizNode(node) {
- return (/^[(\[][ x][)\]]/).test(node.text || node);
-}
-
-function isExercise(nodes) {
- var codeType = { type: 'code' };
-
- // Number of code nodes in section
- var len = _.filter(nodes, codeType).length;
-
- return (
- // Got 3 or 4 code blocks
- (len === 3 || len === 4) &&
- // Ensure all nodes are at the end
- _.all(_.last(nodes, len), codeType)
- );
-}
-
-function isQuiz(nodes) {
- if (nodes.length < 3) {
- return false;
- }
-
- // Support having a first paragraph block
- // before our series of questions
- var quizNodes = nodes.slice(nodes[0].type === 'paragraph' ? 1 : 0);
-
- // No questions
- if (!_.some(quizNodes, { type: 'blockquote_start' })) {
- return false;
- }
-
- // Check if section has list of questions
- // or table of questions
- var listIdx = _.findIndex(quizNodes, { type: 'list_item_start' });
- var tableIdx = _.findIndex(quizNodes, { type: 'table' });
-
- if(
- // List of questions
- listIdx !== -1 && isQuizNode(quizNodes[listIdx + 1]) ||
-
- // Table of questions
- (
- tableIdx !== -1 &&
- _.every(quizNodes[tableIdx].cells[0].slice(1), isQuizNode)
- )
- ) {
- return true;
- }
-
- return false;
-}
-
// What is the type of this section
function sectionType(nodes, idx) {
if(isExercise(nodes)) {