diff --git a/common/static/js/capa/symbolic_mathjax_preprocessor.js b/common/static/js/capa/symbolic_mathjax_preprocessor.js
new file mode 100644
index 0000000000..19104553dc
--- /dev/null
+++ b/common/static/js/capa/symbolic_mathjax_preprocessor.js
@@ -0,0 +1,22 @@
+window.SymbolicMathjaxPreprocessor = function () {
+ this.fn = function (eqn) {
+ // flags and config
+ var superscriptsOn = true;
+
+ if (superscriptsOn) {
+ // find instances of "__" and make them superscripts ("^") and tag them
+ // as such. Specifcally replace instances of "__X" or "__{XYZ}" with
+ // "^{CHAR$1}", marking superscripts as different from powers
+
+ // a zero width space--this is an invisible character that no one would
+ // use, that gets passed through MathJax and to the server
+ var c = "\u200b";
+ eqn = eqn.replace(/__(?:([^\{])|\{([^\}]+)\})/g, '^{' + c + '$1$2}');
+
+ // NOTE: MathJax supports '\class{name}{mathcode}' but not for asciimath
+ // input, which is too bad. This would be preferable to the char tag
+ }
+
+ return eqn;
+ };
+};
diff --git a/doc/public/course_data_formats/symbolic_response.rst b/doc/public/course_data_formats/symbolic_response.rst
new file mode 100644
index 0000000000..773821766e
--- /dev/null
+++ b/doc/public/course_data_formats/symbolic_response.rst
@@ -0,0 +1,26 @@
+#################
+Symbolic Response
+#################
+
+This document plans to document features that the current symbolic response
+supports. In general it allows the input and validation of math expressions,
+up to commutativity and some identities.
+
+
+********
+Features
+********
+
+This is a partial list of features, to be revised as we go along:
+ * sub and superscripts: an expression following the ``^`` character
+ indicates exponentiation. To use superscripts in variables, the syntax
+ is ``b_x__d`` for the variable ``b`` with subscript ``x`` and super
+ ``d``.
+
+ An example of a problem::
+
+
+
+
+
+ It's a bit of a pain to enter that.
diff --git a/lms/lib/symmath/formula.py b/lms/lib/symmath/formula.py
index 7c4ea084d6..914a65d1b0 100644
--- a/lms/lib/symmath/formula.py
+++ b/lms/lib/symmath/formula.py
@@ -248,14 +248,21 @@ class formula(object):
fix_hat(xml)
def flatten_pmathml(xml):
- '''
- Give the text version of PMathML elements
+ ''' Give the text version of certain PMathML elements
+
+ Sometimes MathML will be given with each letter separated (it
+ doesn't know if its implicit multiplication or what). From an xml
+ node, find the (text only) variable name it represents. So it takes
+
+ m
+ a
+ x
+
+ and returns 'max', for easier use later on.
'''
tag = gettag(xml)
if tag == 'mn': return xml.text
elif tag == 'mi': return xml.text
- # elif tag == 'msub': return '_'.join([flatten_pmathml(y) for y in xml])
- # elif tag == 'msup': return '^'.join([flatten_pmathml(y) for y in xml])
elif tag == 'mrow': return ''.join([flatten_pmathml(y) for y in xml])
raise Exception, '[flatten_pmathml] unknown tag %s' % tag
@@ -263,23 +270,63 @@ class formula(object):
# they have the character \u200b in the superscript
# replace them with a__b so snuggle doesn't get confused
def fix_superscripts(xml):
+ ''' Look for and replace sup elements with 'X__Y' or 'X_Y__Z'
+
+ In the javascript, variables with '__X' in them had an invisible
+ character inserted into the sup (to distinguish from powers)
+ E.g. normal:
+
+ a
+ b
+ c
+
+ to be interpreted '(a_b)^c' (nothing done by this method)
+
+ And modified:
+
+ b
+ x
+
+
+ d
+
+
+ to be interpreted 'a_b__c'
+
+ also:
+
+ x
+
+
+ B
+
+
+ to be 'x__B'
+ '''
for k in xml:
tag = gettag(k)
- # match node to a superscript
+ # match things like the last example--
+ # the second item in msub is an mrow with the first
+ # character equal to \u200b
if (tag == 'msup' and
len(k) == 2 and gettag(k[1]) == 'mrow' and
gettag(k[1][0]) == 'mo' and k[1][0].text == u'\u200b'): # whew
+ # replace the msup with 'X__Y'
k[1].remove(k[1][0])
newk = etree.Element('mi')
newk.text = '%s__%s' % (flatten_pmathml(k[0]), flatten_pmathml(k[1]))
xml.replace(k, newk)
+ # match things like the middle example-
+ # the third item in msubsup is an mrow with the first
+ # character equal to \u200b
if (tag == 'msubsup' and
len(k) == 3 and gettag(k[2]) == 'mrow' and
gettag(k[2][0]) == 'mo' and k[2][0].text == u'\u200b'): # whew
+ # replace the msubsup with 'X_Y__Z'
k[2].remove(k[2][0])
newk = etree.Element('mi')
newk.text = '%s_%s__%s' % (flatten_pmathml(k[0]), flatten_pmathml(k[1]), flatten_pmathml(k[2]))
@@ -316,7 +363,7 @@ class formula(object):
try:
xml = self.preprocess_pmathml(self.expr)
except Exception, err:
- # print 'Err %s while preprocessing; expr=%s' % (err, self.expr)
+ log.warning('Err %s while preprocessing; expr=%s' % (err, self.expr))
return "Error! Cannot process pmathml"
pmathml = etree.tostring(xml, pretty_print=True)
self.the_pmathml = pmathml