Last active
December 14, 2016 14:13
-
-
Save runiq/49a42a237299581455bbcfc128eb7231 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
tags = { 'otag': re.escape('{{'), 'ctag': re.escape('}}') } | |
# Taken from anki.template.template.Template.compile_regexps() | |
# section = r"%(otag)s[#^]([^}]*)%(ctag)s((?s).+?)%(otag)s/\1%(ctag)s" % tags | |
# tag = r"%(otag)s([=&!>{]?)((?![#^/]).+?)\1?%(ctag)s+" | |
sectionAndTag = r"""%(otag)s # Section regex first | |
([#^]) # Separates section from tag (#1) | |
([^}]*) # Section tag name (#2) | |
%(ctag)s | |
((?s).+?) # Section contents (#3) | |
%(otag)s | |
/\2 # Closing tag with section name | |
%(ctag)s | |
| # Tag regex now | |
%(otag)s | |
([=&!>{]?) # Modifiers according to Anki (#4) | |
((?![#^/]).+?)\4? # Tag name (must not be preceded by [#^/]) (#5) | |
%(ctag)s | |
+""" | |
both_re = re.compile(sectionAndTag % tags, re.VERBOSE) | |
def myReqForTemplate(self, m, flds, t): | |
""" | |
Creates the cached code object for every template in a model. | |
m: Model (dict) | |
flds: Field names (list) | |
t: Template (dict) | |
""" | |
# Maps field names -> list indices in field string because | |
# I don't want to deal with string handling in eval() | |
fMap = {f: i for (i, f) in enumerate(flds)} | |
# t['qfmt'] is the front template of the Anki card (string) | |
codetxt = _parseSec(fMap, t['qfmt']) | |
code = compile(codetxt, '<string>', 'eval') | |
# This will be saved in m['req'] by another function | |
return codetxt, code | |
def _parseSec(fMap, cardQuestionTemplate): | |
""" | |
Builds the expression to be evaluated later. Recursive, | |
therefore expensive. | |
There are three kinds of values in the card question template that are | |
of interest to us. This function maps them to their corresponding code: | |
{{fld}} -> flds[idx] | |
{{#fld}}...{{/fld}} -> flds[idx] and any([...]) | |
{{^fld}}....{{/fld}} -> not flds[idx] and any([...]) | |
The expression is therefore a string in this form (example): | |
"any([flds[0], flds[2], flds[1] and any([flds[5], flds[6]]), flds[3]])". | |
"flds" is a list containing strings, so this expression checks if | |
all of the strings are empty or not. "flds" is not known at the | |
construction time of this expression, only on evaluation (because it | |
contains the field values of the card that is being checked). | |
""" | |
codetxt = [] | |
tags = set() | |
for match in both_re.finditer(cardQuestionTemplate): | |
# See sectionAndTag regex for match group indices | |
# #2: field name for {{#fld}} or {{^fld}} | |
fld = match.group(2) | |
if fld: | |
# #1: '#' or '^' | |
pred = '' if match.group(1) == '#' else 'not ' | |
try: | |
# #3: Section content (the '...' in '{{#fld}}...{{/fld}}' | |
codetxt.append(pred + 'flds[' + str(fMap[fld]) + '] and any([' + _parseSec(fMap, match.group(3)) + '])') | |
except KeyError: | |
# Invalid field names evaluate to False | |
codetxt.append('False') | |
else: | |
# #5: field name for {{fld}} | |
fld = match.group(5) | |
try: | |
codetxt.append('flds[' + str(fMap[fld]) + ']') | |
except KeyError: | |
# Invalid field names evaluate to False | |
codetxt.append('False') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment