def StmtIfElse(): yield ps.token(TOKEN.IF) yield ps.token(TOKEN.PAR_OPEN) condition = yield Exp yield ps.token(TOKEN.PAR_CLOSE) yield ps.token(TOKEN.CURL_OPEN) if_contents = yield ps.many(Stmt) yield ps.token(TOKEN.CURL_CLOSE) first_cond = AST.CONDBRANCH(expr=condition, stmts=if_contents) elifs = yield ps.many(StmtElif) elses = yield ps.times(StmtElse, 0, 1) return AST.IFELSE(condbranches=[first_cond, *elifs, *elses])
def enum(): yield lexeme(string('enum')) name = yield identifier yield lexeme(string('{')) members = yield many(enum_value) yield lexeme(string('}')) return ProtobufEnum(name, members)
def message(): yield lexeme(string('message')) name = yield identifier yield lexeme(string('{')) fields = yield many(field) yield lexeme(string('}')) return Message(name, fields)
def InfixOpDecl(): side = yield (ps.token(TOKEN.INFIXL) ^ ps.token(TOKEN.INFIXR)) if side.typ is TOKEN.INFIXL: found_kind = FunKind.INFIXL elif side.typ is TOKEN.INFIXR: found_kind = FunKind.INFIXR else: raise Exception("Should never happen") found_fixity = yield ps.token(TOKEN.INT) operator = yield ps.token(TOKEN.OP_IDENTIFIER) yield ps.token(TOKEN.PAR_OPEN) a = yield ps.token(TOKEN.IDENTIFIER) yield ps.token(TOKEN.COMMA) b = yield ps.token(TOKEN.IDENTIFIER) yield ps.token(TOKEN.PAR_CLOSE) typesig = yield ps.times(InfFunTypeSig, 0, 1) typesig = typesig[0] if len(typesig) > 0 else None yield ps.token(TOKEN.CURL_OPEN) decls = yield ps.many(VarDecl) found_stmts = yield ps.many1(Stmt) yield ps.token(TOKEN.CURL_CLOSE) return AST.FUNDECL(kind=found_kind, fixity=found_fixity, id=operator, params=[a, b], type=typesig, vardecls=decls, stmts=found_stmts)
def sdf_atom(): yield P.times(seperator >> num_f, 3) # coordinates atom_symb = yield seperator >> P.many(P.letter()) atom_symb = ''.join(atom_symb) dd = yield seperator >> num_i # mass delta ccc = yield seperator >> num_i # charge delta yield P.times(seperator >> num_i, 4) return SdfAtom(atom_symb, dd, ccc)
def StmtElif(): yield ps.token(TOKEN.ELIF) yield ps.token(TOKEN.PAR_OPEN) condition = yield Exp yield ps.token(TOKEN.PAR_CLOSE) yield ps.token(TOKEN.CURL_OPEN) contents = yield ps.many(Stmt) yield ps.token(TOKEN.CURL_CLOSE) return AST.CONDBRANCH(expr=condition, stmts=contents)
def StmtWhile(): yield ps.token(TOKEN.WHILE) yield ps.token(TOKEN.PAR_OPEN) condition = yield Exp yield ps.token(TOKEN.PAR_CLOSE) yield ps.token(TOKEN.CURL_OPEN) contents = yield ps.many(Stmt) yield ps.token(TOKEN.CURL_CLOSE) return AST.LOOP(init=None, cond=condition, update=None, stmts=contents)
def number(): "Parse an IBIS numerical value." s = yield word( regex( r"[-+]?[0-9]*\.?[0-9]+(([eE][-+]?[0-9]+)|([TknGmpMuf][a-zA-Z]*))?") << many(letter())) m = re.search(r'[^\d]+$', s) if m: ix = m.start() c = s[ix] if c in IBIS_num_suf: res = float(s[:ix] + IBIS_num_suf[c]) else: raise ParseError("IBIS numerical suffix", s[ix:], ix) else: res = float(s) return res
def FunDecl(): fname = yield ps.token(TOKEN.IDENTIFIER) yield ps.token(TOKEN.PAR_OPEN) args = yield ps.times(FArgs, 0, 1) args = args[0] if len(args) > 0 else args yield ps.token(TOKEN.PAR_CLOSE) typesig = yield ps.times(FunTypeSig, 0, 1) typesig = typesig[0] if len(typesig) > 0 else None yield ps.token(TOKEN.CURL_OPEN) decls = yield ps.many(VarDecl) found_stmts = yield ps.many1(Stmt) yield ps.token(TOKEN.CURL_CLOSE) return AST.FUNDECL(kind=FunKind.FUNC, fixity=None, id=fname, params=args, type=typesig, vardecls=decls, stmts=found_stmts)
def PrefixOpDecl(): yield ps.token(TOKEN.PREFIX) operator = yield ps.token(TOKEN.OP_IDENTIFIER) yield ps.token(TOKEN.PAR_OPEN) varname = yield ps.token(TOKEN.IDENTIFIER) yield ps.token(TOKEN.PAR_CLOSE) typesig = yield ps.times(PreFunTypeSig, 0, 1) typesig = typesig[0] if len(typesig) > 0 else None yield ps.token(TOKEN.CURL_OPEN) decls = yield ps.many(VarDecl) found_stmts = yield ps.many1(Stmt) yield ps.token(TOKEN.CURL_CLOSE) return AST.FUNDECL(kind=FunKind.PREFIX, fixity=None, id=operator, params=[varname], type=typesig, vardecls=decls, stmts=found_stmts)
def StmtFor(): yield ps.token(TOKEN.FOR) yield ps.token(TOKEN.PAR_OPEN) initial = yield ps.times(ActStmt, 0, 1) initial = initial[0] if len(initial) > 0 else None yield ps.token(TOKEN.SEMICOLON) condition = yield ps.times(Exp, 0, 1) condition = condition[0] if len(condition) > 0 else None yield ps.token(TOKEN.SEMICOLON) update = yield ps.times(ActStmt, 0, 1) update = update[0] if len(update) > 0 else None yield ps.token(TOKEN.PAR_CLOSE) yield ps.token(TOKEN.CURL_OPEN) contents = yield ps.many(Stmt) yield ps.token(TOKEN.CURL_CLOSE) return AST.LOOP(init=initial, cond=condition, update=update, stmts=contents)
def fix_image_url(url, repo_name): '''Fixes a GitHub image urls. Any links with `github.com` are invalid, because the return *html* content. Image links would have `githubusercontent.com`. For example: - This returns an html: https://github.com/Retrothopter/Niobium-Nanotech/blob/master/Preview.png - This returns a png: https://githubusercontent.com/Retrothopter/Niobium-Nanotech/blob/master/Preview.png Any links that are relative are also invalid. For example: - preview.png - sprites/preview.png - /sprites/preview.png''' # FIXME: this assumes `master` is always the branch we want, while in reality we need the # `default_branch` of the repository, which could also for example be `main` from urllib.parse import urlparse from parsec import optional, string, regex, none_of, many, ParseError glob = ( optional(string('/')) >> string(repo_name) >> string("/blob/master/") >> many(none_of("?")).parsecmap(lambda x: "".join(x))) o = urlparse(url) if o.netloc == "raw.githubusercontent.com": return url try: path = glob.parse(o.path) except ParseError as e: path = None if o.netloc == "github.com" and path: return f"https://raw.githubusercontent.com/{repo_name}/master/{path}" if o.netloc == "": return f"https://raw.githubusercontent.com/{repo_name}/master/{o.path}" return url
def fix_image_url(url, repo_name): '''Fixes a GitHub url, where the url should point to an image. Any links with `github.com` are invalid, because they're html links, while image links would have `githubusercontent.com`, for example: - https://github.com/Retrothopter/Niobium-Nanotech/blob/master/Preview.png; Any links that don't have a domain are relative and as such invalid, for example: - preview.png; - sprites/preview.png; - /sprites/preview.png This is also why a repo name is required. ''' from urllib.parse import urlparse from parsec import optional, string, regex, none_of, many, ParseError glob = ( optional(string('/')) >> string(repo_name) >> string("/blob/master/") >> many(none_of("?")).parsecmap(lambda x: "".join(x))) o = urlparse(url) if o.netloc == "raw.githubusercontent.com": return url try: path = glob.parse(o.path) except ParseError as e: path = None if o.netloc == "github.com" and path: return f"https://raw.githubusercontent.com/{repo_name}/master/{path}" if o.netloc == "": return f"https://raw.githubusercontent.com/{repo_name}/master/{o.path}" # print('[warning] non github url:', url) return url
def quoted(): end_quote = yield quote body = yield many(charseq(end_quote)) yield string(end_quote) raise StopGenerator(''.join(body))
def array(): yield lbrack << many(comment) elements = yield sepBy(value, comma) yield rbrack << many(comment) raise StopGenerator(elements)
"1" => "One" "234" => "Two,Three,Four" "10,000" => "One,Zero,Zero,Zero,Zero" " 4619 " => "Four,Six,One,Nine" ''' examples = ['', '1', '234', '10,000', ' 4619 '] expected = [ '', 'One', 'Two,Three,Four', 'One,Zero,Zero,Zero,Zero', 'Four,Six,One,Nine' ] print(parsec.string("1").parse("111")) whitespace = parsec.regex(r'\s+') commas = parsec.regex(r',+') ignore = parsec.many((whitespace | commas)) lexeme = lambda p: p << ignore zero = lexeme(parsec.string('0')).result('Zero') one = lexeme(parsec.string('1')).result('One') two = lexeme(parsec.string('2')).result('Two') three = lexeme(parsec.string('3')).result('Three') four = lexeme(parsec.string('4')).result('Four') five = lexeme(parsec.string('5')).result('Five') six = lexeme(parsec.string('6')).result('Six') seven = lexeme(parsec.string('7')).result('Seven') eight = lexeme(parsec.string('8')).result('Eight') nine = lexeme(parsec.string('9')).result('Nine') parser = ignore >> parsec.many(
@parsec.Parser def newch_command(text, index): """Parse a new chapter command, which optionally may contain a title.""" is_chapter = newch_parser(text, index) if not is_chapter.status: return is_chapter else: chapter_text = text_parser(text, is_chapter.index) if not chapter_text.status: return parsec.Value.success(is_chapter.index, NewChapter('')) else: return parsec.Value.success( chapter_text.index, NewChapter(chapter_text.value.strip())) tweet_parser = parsec.many( par_command ^ break_command ^ newch_command ^ text_command) def parse(text): """Parse formatting commands from the given text. Parameters ---------- text : str Body of the tweet to be parsed. Returns ------- [Text | NewChapter | NewParagraph | Break] A list of Text, NewChapter, NewParagraph and Break objects representing the sequence of formatting commands in the tweet.
def ActArgs(): a = yield Exp b = yield ps.many(ps.token(TOKEN.COMMA) >> Exp) return [a, *b]
def IdField(): i = yield ps.token(TOKEN.IDENTIFIER) found_fields = yield ps.many(ps.token(TOKEN.ACCESSOR)) return AST.VARREF(id=i, fields=found_fields)
def Exp(): a = yield ConvExp b = yield ps.many(ExpMore) b = flatten(b) return AST.DEFERREDEXPR(contents=[a, *b])
def ImportListStrict(): n = yield ImportName ns = yield ps.many(ps.token(TOKEN.COMMA) >> ImportName) return [n, *ns]
def object_pair(): key = yield quoted | lexeme(regex(r'[a-zA-Z][-_a-zA-Z0-9]*')) yield many(comment) << colon << many(comment) val = yield value raise StopGenerator((key, val))
def parser(): head = yield header yield parsec.many(parsec.string('\n')) samps = yield parsec.many(sample) return head, samps
def StmtElse(): yield ps.token(TOKEN.ELSE) yield ps.token(TOKEN.CURL_OPEN) contents = yield ps.many(Stmt) yield ps.token(TOKEN.CURL_CLOSE) return AST.CONDBRANCH(expr=None, stmts=contents)
def _ldpc_param_table(): params = yield ps.many(_ldpc_param) return dict(params)
def SPL(): found_imports = yield ps.many(ImportDecl) found_decls = yield ps.many(Decl) return AST.SPL(imports=found_imports, decls=found_decls)
def FunType(): a = yield ps.many(Type) yield ps.token(TOKEN.OP_IDENTIFIER, cond=(lambda x: x == "->")) b = yield RetType return AST.FUNTYPE(from_types=a, to_type=b)
def FArgs(): x = yield ps.token(TOKEN.IDENTIFIER) xs = yield ps.many(ps.token(TOKEN.COMMA) >> ps.token(TOKEN.IDENTIFIER)) return [x, *xs]
def json_object(): yield lbrace << many(comment) pairs = yield sepBy(object_pair, comma) yield many(comment) << rbrace raise StopGenerator(dict(pairs))
def header(): items = yield parsec.many(header_field) d = {} for item in items: d.update(item) return d