def parse_common_block(s: str) -> List[str]: """Parse a common block.""" myword = Word(alphanums + "_") inside = OneOrMore(myword + ZeroOrMore("*") + ZeroOrMore(",")) parenthesis = ZeroOrMore(Char("(") + inside + Char(")")) parser = Literal("common") + Char('/') + myword + Char('/') + \ OneOrMore(Group(myword + parenthesis + ZeroOrMore(Char(",")))) return parser.parseString(s).asList()
def task_from_md(md_lines, root=None): st = StepTask() if root is None: root = StepTask.Root else: root = Path(root) WRDs = OneOrMore(WRD) equality = Char('=') + WRDs name_template = 'name' + equality repo_template = 'repo' + equality statement_template = 'statement' + equality checker_template = 'checker' + equality solution_template = 'solution' + equality tests_template = 'tests' + equality score_template = 'score' + Char('=') + Word(nums) for line in md_lines: # print(line) if line == repo_template: repo = repo_template.parseString(line)[2] st.params['repo'] = root / repo elif line == score_template: score = score_template.parseString(line)[2] st.params['score'] = int(score) elif line == statement_template: statement = statement_template.parseString(line)[2] st.params['statement'] = statement elif line == name_template: name = name_template.parseString(line)[2] st.params['name'] = name elif line == checker_template: checker = checker_template.parseString(line)[2] st.params['checker'] = checker elif line == solution_template: solution = solution_template.parseString(line)[2] st.params['solution'] = solution elif line == tests_template: tests = tests_template.parseString(line)[2] st.params['tests'] = tests st.set_attrs() return st
def parse_define( define_line): # type: (str) -> typing.Any[typing.Type[ParserElement]] # Group for parsing literal suffix of a numbers, e.g. 100UL literal_symbol = Group(CaselessLiteral('L') | CaselessLiteral('U')) literal_suffix = OneOrMore(literal_symbol) # Define name name = Word(alphas, alphas + nums + '_') # Define value, either a hex, int or a string hex_value = Combine( Literal('0x') + Word(hexnums) + Optional(literal_suffix).suppress())('hex_value') int_value = Word(nums)('int_value') + ~Char('.') + Optional( literal_suffix)('literal_suffix') str_value = QuotedString('"')('str_value') # Remove optional parenthesis around values value = Optional('(').suppress() + ( hex_value ^ int_value ^ str_value)('value') + Optional(')').suppress() expr = '#define' + Optional(name)('name') + Optional(value) res = expr.parseString(define_line) return res
def parse_task_language(line): """Разбирает строку вида lang=язык_задач, если язык пустой, возвращает None.""" lang_template = 'lang' + Char('=') + Word('_' + alphanums) lang = None if line == lang_template: lang_tokens = lang_template.parseString(line) logger.info(f'lang_tokens={lang_tokens}') lang = lang_tokens[2] return lang
def expr(self) -> ParserElement: NL = LineEnd() LIST_BREAK = NL + Optional(White(" \t")) + NL | StringEnd() IGNORE = BlockQuote(**self.init_kwargs).expr | Panel( **self.init_kwargs).expr | Color(**self.init_kwargs).expr ROW = LineStart() + Combine( Optional(self.nested_token, default="") + ListIndent(self.indent_state, self.tokens) + SkipTo(NL + Char(self.nested_token + self.tokens) | LIST_BREAK, ignore=IGNORE) + Optional(NL), ) return OneOrMore(ROW, stopOn=LIST_BREAK).setParseAction(self.action)
def _create_grammar(self): """ Pyparsing implementation of a where clause grammar based on http://pyparsing.wikispaces.com/file/view/simpleSQL.py The query language is a series of statements separated by AND or OR operators and parentheses can be used to group/provide precedence. A statement is a combination of three strings "<filter> <operator> <value>" or "<filter> <operator> <filter>". A value can be a string, integer or a real(floating) number or a (ISO YYYY-MM-DD) date. An operator must be one of "= != < > >= <= !:" and are translated into django __lte or equivalent suffixes. See self.as_q Example something < 10 AND other >= 2015-01-01 AND (foo < 1 OR bar > 1) """ quoted_string_excluding_quotes = QuotedString( '"', escChar='\\').setParseAction(lambda token: StringValue(token[0])) and_ = Keyword('and', caseless=True) or_ = Keyword('or', caseless=True) binary_op = oneOf('=> =< = < > >= <= : != !:', caseless=True).setResultsName('operator') # define query tokens identifier = Word(alphas, alphanums + '_$-.').setName('identifier') raw_value_chars = alphanums + '_$-+/$%*;?@[]\\^`{}|~.' raw_value = Word(raw_value_chars, raw_value_chars).setName('raw_value') value_string = quoted_string_excluding_quotes | raw_value # Define a where expression where_expression = Forward() binary_operator_statement = (identifier + binary_op + value_string).setParseAction( self._binary_op_to_q) unary_operator_statement = (identifier | (Char('!') + identifier)).setParseAction( self._unary_op_to_q) free_text_statement = quotedString.copy().setParseAction( self._freetext_to_q) operator_statement = binary_operator_statement | free_text_statement | unary_operator_statement where_condition = Group(operator_statement | ('(' + where_expression + ')')) where_expression << where_condition + ZeroOrMore( (and_ | or_) + where_expression) # define the full grammar query_statement = Forward() query_statement << Group(where_expression).setResultsName("where") return query_statement
def expr(self) -> ParserElement: MENTION = Combine( "[" + Optional( SkipTo("|", failOn="]") + Suppress("|"), default="", ) + "~" + Optional(CaselessLiteral("accountid:")) + Word(alphanums + ":-").setResultsName("accountid") + "]", ) return ((StringStart() | Optional(PrecededBy(White(), retreat=1), default=" ")) + MENTION.setParseAction(self.action) + (StringEnd() | Optional(FollowedBy( White() | Char(punctuation, excludeChars="[") | MENTION), default=" ")))
def expr(self) -> ParserElement: NON_ALPHANUMS = Regex(r"\W", flags=re.UNICODE) TOKEN = Suppress(self.TOKEN) IGNORE = White() + TOKEN | self.get_ignore_expr() ELEMENT = Combine( TOKEN + (~White() & ~Char(self.TOKEN)) + SkipTo(TOKEN, ignore=IGNORE, failOn="\n") + TOKEN + FollowedBy(NON_ALPHANUMS | StringEnd()), ) return (StringStart() | PrecededBy(NON_ALPHANUMS, retreat=1)) + Combine( ELEMENT.setParseAction(self.action) + Optional(~ELEMENT, default=" "), )
def parse_lesson_id(line): """ Разбирает строку line вида lesson = число и возвращает это число. """ id_template = 'lesson' + Char('=') + Word(nums) if not line == id_template: error(f'Expect lesson id as lesson=number, now = {line}') lesson = id_template.parseString(line) lesson_id = int(lesson[2]) return lesson_id
def param_substitude(lines, param_dict): mask_par = (CharsNotIn('{{}}')[0, ] + '{{' + WRD_p + '}}' + CharsNotIn('{{}}')[0, ])[1, ] + Char('\n')[0, 1] # mask_par.setParseAction(param_set) mask_par.setParseAction(lambda tokens: param_set(tokens, param_dict)) for line_to, line in enumerate(lines): line = line.rstrip() if not line: continue lines[line_to] = mask_par.transformString(line) return lines
def create_ast(expression_str: str) -> List[Union[str, List]]: """Evaluates the given expression""" expression = Forward() operand = Group(Char(alphas) + ZeroOrMore("'")) | Group( Group("(" + expression + ")") + ZeroOrMore("'")) expression <<= infixNotation( operand, [ (Empty(), 2, opAssoc.LEFT), ("*", 2, opAssoc.LEFT), ("+", 2, opAssoc.LEFT), ], ) return expression.parseString(expression_str, parseAll=True).asList()
# :label - exported/global label # OPCODE .label - .label gets turned into the corresponding address # OPCODE :label+4 - same, but with an offset labelprefix = oneOf(': .') label = Combine(labelprefix + Word(alphanums + "_")) label.setName('label') labeloffset = Combine(labelprefix + Word(':', alphanums + "_-+")) labeloffset.setName('labeloffset') # Bytes can be represented in binary, hex, char, or a number (0-255 or -128-127) # and may include embedded arithmetic # OPCODE 0b00001100 # OPCODE 0x0b # OPCODE 'a' # OPCODE 254-0x0a # OPCODE 'a'&0b00001111 binbyte = Combine(Literal('0b') + Char('01') * 8) binbyte.setName('binbyte') binbyte.setParseAction(lambda t: [int(t[0], 2)]) hexbyte = Combine(Literal('0x') + Char(srange("[0-9a-fA-F]")) * 2) hexbyte.setName('hexbyte') hexbyte.setParseAction(lambda t: [int(t[0], 16)]) chrbyte = QuotedString(quoteChar="'", unquoteResults=True) chrbyte.setName('char') chrbyte.setParseAction(lambda t: [ord(t[0])]) number = Word(nums + '-') number.setName('number') number.setParseAction(lambda t: [int(t[0])]) allbytes = binbyte | hexbyte | chrbyte | number mathtoken = Combine(oneOf('+ - & |') + allbytes) bytemathexpression = Combine(allbytes + OneOrMore(mathtoken)) bytemathexpression.setParseAction(lambda t: [eval(t[0])])
lisp_integer = Word(nums) lisp_integer.setParseAction(lambda s, l, t: Int(t[0])) lisp_float = Combine(Word(nums) + '.' + Word(nums)) lisp_float.setParseAction(lambda s, l, t: float(t[0])) lisp_number = lisp_integer | lisp_float lisp_string = QuotedString(quoteChar='"', escChar='\\', multiline=True) lisp_string.setParseAction(lambda s, l, t: String(t[0])) special = "_-+*/^><=:'" #lisp_symbol = Word(alphas + nums + '_-' + '?!') # any order #lisp_symbol = Combine(Char(alphas) + Word(alphas + nums + '?' + '!')) # starts with alphas lisp_symbol = Combine( Char(alphas + special) + Optional(Word(alphas + nums + special)) + Optional(Char('?!'))) # Ruby style lisp_symbol.setParseAction(lambda s, l, t: Symbol(t[0])) lisp_atom = lisp_symbol | lisp_string | lisp_number lisp_list = nestedExpr(opener='(', closer=')', content=lisp_atom, ignoreExpr=lisp_string) #lisp_list.setParseAction(lambda s,l,t: List(t)) # does not work lisp_expr = lisp_atom | lisp_list
TREE, CATCH, FINALLY, THROWS, PROTECTED, PUBLIC, PRIVATE, ) = map( Keyword, """src scope options tokens fragment id lexer parser grammar tree catch finally throws protected public private """.split()) KEYWORD = MatchFirst(keywords) # Tokens EOL = Suppress(LineEnd()) # $ SGL_PRINTABLE = Char(printables) singleTextString = originalTextFor( ZeroOrMore(~EOL + (White(" \t") | Word(printables)))).leaveWhitespace() XDIGIT = hexnums INT = Word(nums) ESC = BSLASH + (oneOf(list(r'nrtbf\">' + "'")) | ('u' + Word(hexnums, exact=4)) | SGL_PRINTABLE) LITERAL_CHAR = ESC | ~(APOS | BSLASH) + SGL_PRINTABLE CHAR_LITERAL = APOS + LITERAL_CHAR + APOS STRING_LITERAL = APOS + Combine(OneOrMore(LITERAL_CHAR)) + APOS DOUBLE_QUOTE_STRING_LITERAL = '"' + ZeroOrMore(LITERAL_CHAR) + '"' DOUBLE_ANGLE_STRING_LITERAL = '<<' + ZeroOrMore(SGL_PRINTABLE) + '>>' TOKEN_REF = Word(alphas.upper(), alphanums + '_') RULE_REF = Word(alphas.lower(), alphanums + '_') ACTION_ESC = (BSLASH.suppress() + APOS | BSLASH.suppress()
rr_type_set.setName('<rr_type>') # a series of RR types (without a semicolon separator) rr_type_series = OneOrMore( rr_type_set( '' ) # remove this label so we can float result to a bigger, later label )('rr_types') rr_type_series.setName('<rr_type ...>') # a series of RR types (with a semicolon separator) rr_type_list_series = OneOrMore(rr_type_set + semicolon)('rr_type_list') rr_type_list_series.setName('<rr_type; ...;>') # Following is commonly used in association with DNS zone records rr_fqdn_w_absolute = Combine(domain_generic_fqdn + Optional(Literal('.'))) rr_fqdn_w_absolute.setName('<rr_fqdn_with_abs>') # rr_domain_name is uzed in association with DNS zone records # by 'update-policy', a zone-specific option rr_domain_name = Combine(domain_generic_fqdn + Optional(Literal('.'))) rr_domain_name.setName('<rr_domain_name>') # rr_domain_name may be '*.example.net', '*.congress.gov.', or '*' rr_domain_name_or_wildcard = (rr_domain_name | Char(domain_charset_wildcard)) rr_domain_name_or_wildcard.setName('<target_rr_name>') # ( <fqdn> | '.' ) rr_domain_name_or_root = (rr_domain_name | Literal('.')) rr_domain_name_or_root.setName('<rr_domain_or_root>')
'~~', endQuoteChar='~~').setParseAction(wiki_italic_parse_action) def wiki_underline_parse_action(start, length, tokens): return f'<u>{tokens[0]}</u>' underline = QuotedString( '__', endQuoteChar='__').setParseAction(wiki_underline_parse_action) def literal_parse_action(start, length, tokens): return tokens[0] literal = Literal('\\').setParseAction(nil_parse_action) + Char( printables).setParseAction(literal_parse_action) divider = Literal('{divider}').setParseAction( lambda start, length, tokens: '<hr>') wikiMarkup = literal | link | quote | bold | italic | underline | divider def wiki_render(s): return mark_safe( wikiMarkup.transformString(s)) # TODO: yea, this isn't safe... @register.filter def wiki(value): return wiki_render(value)
from pyparsing import (Empty, alphas, alphanums, nums, Literal, Word, Char, ParserElement, Forward, Combine, OneOrMore, Group, Regex, ZeroOrMore, White, CaselessKeyword, Optional) import re ## TERMINALS identifier = Word(alphas + '_', alphanums + '_') lBrace = Literal('{').suppress() rBrace = Literal('}').suppress() eq = Literal('=').suppress() escapedSymbol = Literal('~').suppress() + Char('{}~"\'/') ## GRAMMAR source: ParserElement = Forward() item: ParserElement = Forward() #### ARGS ##### ARGVALUES def Quote(q): q = Literal(q).suppress() quotedStringBit = Combine( OneOrMore(escapedSymbol | (~q + Regex('[^{}]', re.S))))('stringBit').leaveWhitespace() quotedString = q + (OneOrMore( Group(item.leaveWhitespace() | source.leaveWhitespace() | quotedStringBit)) | Empty())('value') + q return quotedString
def assertParseElementFalse(af_parse_element, af_test_data, af_expected_result): """ A nice wrapper routine to ensure that the word 'False' is in the function name. """ assertParseElement(a_parse_element=af_parse_element, a_test_data=af_test_data, a_expected_result=af_expected_result, a_assert_flag=False) addr = Word(alphanums + '_-./:').setResultsName('addr').setName('<addr>') semicolon, lbrack, rbrack = map(Suppress, ';{}') exclamation = Char('!') aml_nesting = Forward() aml_nesting << ( lbrack + ( ZeroOrMore( Group( (exclamation('not') + aml_nesting) | (exclamation('not') + addr + semicolon) | (aml_nesting) | ( addr + semicolon ) # never set a ResultsLabel here, you get duplicate but un-nested 'addr' ) # never set a ResultsLabel here, you get no [] )(None))('aml_nesting') + rbrack + semicolon)(
class ChoiceTree: ''' Class that parses strings representing possible combinations, and returns possible combinations. e.g. "abc[de|fg]" → [ "abcde", "abcfg" ] "I [eat|like] [|hot]dogs" → [ "I eat dogs", "I like dogs", "I eat hotdogs", "I like hotdogs" ] Escape symbol is '~' e.g. "abc~[def~]" → [ "abc[def]" ] Due to reasons, an escaped escape '~~' is not turned into a literal '~', if this is not up to liking, simply .replace('~~', '~') yourself after parsing. Essentially, consider the noncommutative Semiring of (unordered) lists of strings, so that in python notation: list1+list2 == [*list1, *list2] the concatenation of lists and list1*list2 == [a+b for a in list1 for b in list2] the concatenation of each pair of strings. (This ring has as neutral element the list of the empty string, and as zero element the empty list.) We write addition using the "|" symbol, the product is implicit (i.e. a*b == ab), and use [] as parentheses, so that in python notation e.g. "abc" == ["abc"] and "a|b|c" == ["a", "b", "c"] What ChoiceTree does is parse such expressions, and using the distributivity rule ( [a|b]c == ab|ac ) it simplifies the expression to a sum of products. ''' class Text: def __init__(self, text): self.text = text if text == '' else ''.join(text.asList()) self.count = 1 self.reset() __str__ = __repr__ = lambda s: s.text def next(self): self.done = True return self.text def random(self): return self.text def reset(self): self.done = False def current(self): return self.text class Choice: def __init__(self, vals): self.vals = vals.asList() self.count = sum(v.count for v in self.vals) self.reset() __str__ = __repr__ = lambda s: '[{}]'.format('|'.join( [str(v) for v in s.vals])) def next(self): next = self.vals[self.i] out = next.next() if next.done: self.i += 1 if self.i == len(self.vals): self.done = True return out def random(self): # Weighted based on the number of different possible branches each child has. return np.random.choice(self.vals, p=list(v.count / self.count for v in self.vals)).random() def reset(self): self.i = 0 self.done = False [c.reset() for c in self.vals] def current(self): return self.vals[self.i].current() class Group: def __init__(self, vals): self.vals = vals.asList() self.count = functools.reduce(lambda x, y: x * y, (c.count for c in self.vals), 1) self.reset() __str__ = __repr__ = lambda s: ''.join([str(v) for v in s.vals]) def next(self): i = 0 out = '' while True: out += self.vals[i].next() if self.vals[i].done: if i == len(self.vals) - 1: self.done = True break else: self.vals[i].reset() else: break i += 1 i += 1 while i < len(self.vals): out += self.vals[i].current() i += 1 return out def random(self): return ''.join(v.random() for v in self.vals) def reset(self): self.done = False [c.reset() for c in self.vals] def current(self): return ''.join([c.current() for c in self.vals]) escapedSymbol = Char('~').suppress() + Char('[|]') escapedEsc = Literal('~~') soleEsc = Char('~') lbr = Literal('[').suppress() rbr = Literal(']').suppress() div = Literal('|').suppress() _text = Regex( r'[^\[\|\]~]+' ) # any sequence of characters not containing '[', ']', '|' or '~' text = pGroup( OneOrMore(escapedSymbol | escapedEsc | soleEsc | _text)).setParseAction(lambda t: ChoiceTree.Text(t[0])) group = Forward() choice = pGroup(lbr + group + ZeroOrMore(div + group) + rbr).setParseAction(lambda t: ChoiceTree.Choice(t[0])) empty = Empty().setParseAction(lambda t: ChoiceTree.Text('')) group <<= pGroup(OneOrMore(text | choice) | empty).setParseAction( lambda t: ChoiceTree.Group(t[0])).leaveWhitespace() def __init__(self, text, parse_flags=False, add_brackets=False, leave_escapes=False): self.flag_random = False if parse_flags: if text[:3] == '[?]': text = text[3:] self.flag_random = True if add_brackets: text = '[' + text + ']' self.root: ChoiceTree.Group = ChoiceTree.group.parseString(text)[0] self.count = self.root.count def __iter__(self): if self.flag_random: yield self.random() return while not self.root.done: yield self.root.next() self.root.reset() def random(self): return self.root.random()
def task_from_md(md_lines, params): st = StepTask(params.get('task_lang')) if 'task_root' in params: root = Path(params['task_root']) else: root = StepTask.Root # TODO вынести в parse.py и избавиться от перечислений (свернуть код?) и разрешить русские буквы в пути к файлам WRDs = OneOrMore(alphanums) equality = Char('=') + WRDs name_template = 'name' + equality repo_template = 'repo' + equality statement_template = 'statement' + equality checker_template = 'checker' + equality solution_template = 'solution' + equality tests_template = 'tests' + equality header_template = 'header' + equality footer_template = 'footer' + equality score_template = 'score' + Char('=') + Word(nums) visible_tst_num_template = 'visible_tst_num' + Char('=') + Word(nums) for line in md_lines: # print(line) if line == repo_template: repo = repo_template.parseString(line)[2] st.params['repo'] = root / repo elif line == score_template: score = score_template.parseString(line)[2] st.params['score'] = int(score) elif line == visible_tst_num_template: visible_tst_num = visible_tst_num_template.parseString(line)[2] st.params['visible_tst_num'] = int(visible_tst_num) elif line == statement_template: statement = statement_template.parseString(line)[2] st.params['statement'] = statement elif line == name_template: name = name_template.parseString(line)[2] st.params['name'] = name elif line == checker_template: checker = checker_template.parseString(line)[2] st.params['checker'] = checker elif line == solution_template: solution = solution_template.parseString(line)[2] st.params['solution'] = solution elif line == tests_template: tests = tests_template.parseString(line)[2] st.params['tests'] = tests elif line == header_template: header = header_template.parseString(line)[2] st.params['header'] = header elif line == footer_template: footer = footer_template.parseString(line)[2] st.params['footer'] = footer st.set_attrs() return st
""" import os import os.path import errno import sys import argparse from pprint import PrettyPrinter from pyparsing import Literal, CaselessLiteral, \ ParseException, ParseSyntaxException, \ Word, alphanums, Group, Optional, nums, Combine, Char, \ cppStyleComment, pythonStyleComment, OneOrMore, \ Suppress, ungroup unix_pipe_support = False period = Literal('.') exclamation = Char('!') exclamation.setName('not') lbrack, rbrack, semicolon, slash = map(Suppress, '{};/') dquote = Literal('"').setName("'\"'") squote = Literal("'").setName('"\'"') isc_boolean = ( CaselessLiteral('yes') | CaselessLiteral('no') | Literal('1') | Literal('0') | CaselessLiteral('True') | CaselessLiteral('False') ) isc_boolean.setName('<boolean>') # alphanums_series = Group(Word(alphanums) + Word(alphanums)) + semicolon
Combine, Group, Optional, Word, ZeroOrMore, alphanums, printables, ) from collections import namedtuple from typing import List, Optional as Opt __all__ = ["parse_cli_string"] d = "-" dash = Char(d) arg_identifier = Combine(dash + Optional(dash)) arg_name = Word(alphanums + d) kwarg = Combine(arg_identifier + arg_name) value_bgn = "".join([p for p in list(printables) if p != d]) value = Combine(Char(value_bgn) + ZeroOrMore(Word(printables))) kwarg_and_value = kwarg + value bool_arg = kwarg varargs_and_values = kwarg + value[2, ...] arg = (kwarg_and_value("kwarg") ^ bool_arg("flag") ^ varargs_and_values("varargs")) cli = ZeroOrMore(Group(arg))
+ '){0,16}' + \ domain_label_regex + '\.' \ + '){0,1}'\ + tld_label_regex domain_fqdn = Regex(domain_fqdn_regex) domain_fqdn.setName('<strict-fqdn>') domain_fqdn.setResultsName('domain_name') # Generic fully-qualified domain name (less stringent) domain_generic_fqdn = Combine( domain_generic_label + ZeroOrMore( Literal('.') + domain_generic_label ) + Optional(Char('.')) ) domain_generic_fqdn.setName('<generic-fqdn>') domain_generic_fqdn.setResultsName('domain_name') quoted_domain_generic_fqdn = ( Combine(squote - domain_generic_fqdn - squote) | Combine(dquote - domain_generic_fqdn - dquote) ) quoted_domain_generic_fqdn.setName('<quoted_domain_name>') quotable_domain_generic_fqdn = ( Combine(squote - domain_generic_fqdn - squote) | Combine(dquote - domain_generic_fqdn - dquote) | domain_generic_fqdn )
def from_aiken(md_lines): st = StepMultipleChoice() WRDs = ZeroOrMore(WRD) opt_template = Char(alphas) + (Char(')') ^ Char('.')) + WRDs ans_template = 'ANSWER:' + OneOrMore(Char(alphas) + Char(',')[0, 1]) class Status(Enum): QUESTION = 0 VARIANT = 1 ANSWER = 3 letter_seq = [] # letter sequence from aiken variant, A, B, C, D, etc md_part = [] status = Status.QUESTION for line in md_lines: # Is it SHUFFLE option? """if line.startswith('SHUFFLE:'): sh = ('SHUFFLE:' + WRD).parseString(line) if sh[1].lower() == 'true': st.preserve_order = False elif sh[1].lower() == 'false': st.preserve_order = True else: logger.warning(f'Unknown value SHUFFLE: [{sh[1]}]') continue""" if line.startswith('SHUFFLE:'): st.preserve_order = not bool_check( 'SHUFFLE', line ) # единсвенная проблема в том, что при неправильном написании true или false будет автоматом ставиться true continue # variant begin by A) or A. if line == opt_template: opt = opt_template.parseString(line) letter = opt[0] txt = ' '.join(opt[2:]) if status == Status.QUESTION: # first answer begin, question end status = Status.VARIANT st.text = html(md_part) elif status == Status.VARIANT: # next variant, commit previous variant st.add_option(md_part) md_part = [txt] letter_seq.append(letter) elif line == ans_template and status == Status.VARIANT: # end of question st.add_option(md_part) ans = ans_template.parseString(line) ans_str = " ".join(ans[1:]) logger.debug(f'group1 = {ans_str}') letters = [s.strip() for s in ans_str.split(',')] logger.debug(f'letters={letters}') st.is_multiple_choice = len(letters) > 1 for letter in letters: ind = letter_seq.index(letter) st.options[ind]['is_correct'] = True return st else: # continue a question or answer md_part.append(line)
question in AIKEN format """ import logging from pyparsing import Char, Word, CharsNotIn, ZeroOrMore, nums, alphas, alphanums, printables, srange logger = logging.getLogger('deploy_scripts') # TODO нужны ли kir_letter вместе с srange(['а-я_']) + srange(['А-Я_']) ? kir_letter = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ_' WRD = Word(printables + kir_letter + srange(['а-я_']) + srange(['А-Я_'])) WRD_p = Word(alphanums + kir_letter + srange(['а-я_']) + srange(['А-Я_'])) WRDs = ZeroOrMore(WRD) sharp = Char('#') not_sh = CharsNotIn('#') def error(message="Error"): raise ValueError(message) def bool_check(param_name, line): """ Разбирает line по формату 'param_name:true|false', возвращает True или False (case insensitive), в случае ошибки возвращает False :param param_name: ключ :param line: разбираемая строка :return: True или False в разбираемой строке """ # Todo подумать, надо ли в слушае ошибки False или лучше exception
isc_boolean = (CaselessLiteral('yes') | CaselessLiteral('no') | Literal('1') | Literal('0') | CaselessLiteral('True') | CaselessLiteral('False')) isc_boolean.setName('<boolean>') charset_key_id_base = alphanums + '_-' charset_key_id_dquotable = charset_key_id_base + "'" charset_key_id_squotable = charset_key_id_base + '"' key_id_base = Word(charset_key_id_base, max=62) key_id_dquotable = Combine( Char('"') + Word(charset_key_id_dquotable, max=64) + Char('"')) key_id_squotable = Combine( Char("'") + Word(charset_key_id_squotable, max=64) + Char("'")) key_id = (key_id_dquotable ^ key_id_squotable ^ key_id_base)('key_id') key_id.setName('<key_id>') charset_keysecret_base = alphanums + '+/=' charset_keysecret_dquotable = charset_keysecret_base + "'" charset_keysecret_squotable = charset_keysecret_base + '"' keysecret_base = Word(charset_keysecret_base, max=32767) keysecret_dquotable = Combine( Char('"') + Word(charset_keysecret_dquotable, max=32765) + Char('"')) keysecret_squotable = Combine( Char("'") + Word(charset_keysecret_squotable, max=32765) + Char("'"))