def __init__(self, ctx): self.ctx = ctx scripts = set("postrotate prerotate firstaction lastaction preremove".split()) Stanza = Forward() Spaces = Many(WSChar) Bare = String(set(string.printable) - (set(string.whitespace) | set("#{}'\""))) Num = Number & (WSChar | LineEnd) Comment = OneLineComment("#").map(lambda x: None) ScriptStart = WS >> PosMarker(Choice([Literal(s) for s in scripts])) << WS ScriptEnd = Literal("endscript") Line = (WS >> AnyChar.until(EOL) << WS).map(lambda x: "".join(x)) Lines = Line.until(ScriptEnd | EOF).map(lambda x: "\n".join(x)) Script = ScriptStart + Lines << Opt(ScriptEnd) Script = Script.map(lambda x: [x[0], [x[1]], None]) BeginBlock = WS >> LeftCurly << WS EndBlock = WS >> RightCurly First = PosMarker((Bare | QuotedString)) << Spaces Attr = Spaces >> (Num | Bare | QuotedString) << Spaces Rest = Many(Attr) Block = BeginBlock >> Many(Stanza).map(skip_none).map(self.to_entries) << EndBlock Stmt = WS >> (Script | (First + Rest + Opt(Block))) << WS Stanza <= WS >> (Stmt | Comment) << WS Doc = Many(Stanza).map(skip_none).map(self.to_entries) self.Top = Doc << EOF
def __init__(self, ctx): self.ctx = ctx Complex = Forward() Comment = (WS >> OneLineComment("#")).map(lambda x: None) Name = String(string.ascii_letters + "_/") Num = Number & (WSChar | LineEnd) StartName = WS >> PosMarker(StartTagName(Letters)) << WS EndName = WS >> EndTagName(Letters, ignore_case=True) << WS Cont = Char("\\") + EOL AttrStart = Many(WSChar) AttrEnd = (Many(WSChar) + Cont) | Many(WSChar) OpAttr = (Literal("!=") | Literal("<=") | Literal(">=") | InSet("<>")) & WSChar BareAttr = String( set(string.printable) - (set(string.whitespace) | set("<>'\""))) Attr = AttrStart >> (Num | QuotedString | OpAttr | BareAttr) << AttrEnd Attrs = Many(Attr) StartTag = (WS + LT) >> (StartName + Attrs) << (GT + WS) EndTag = (WS + LT + FS) >> EndName << (GT + WS) Simple = WS >> (Lift(self.to_directive) * PosMarker(Name) * Attrs) << WS Stanza = Simple | Complex | Comment | Many(WSChar | EOL, lower=1).map(lambda x: None) Complex <= (Lift(self.to_section) * StartTag * Many(Stanza).map(skip_none)) << EndTag Doc = Many(Stanza).map(skip_none) self.Top = Doc + EOF
def parse_doc(content, ctx): def to_directive(x): name, rest = x rest = [rest] if rest is not None else [] return Directive(name=name.value.strip(), attrs=rest, lineno=name.lineno, src=ctx) def to_section(name, rest): return Section(name=name.value.strip(), children=rest, lineno=name.lineno, src=ctx) def apply_defaults(cfg): if "DEFAULT" not in cfg: return cfg defaults = cfg["DEFAULT"] not_defaults = cfg[~eq("DEFAULT")] for c in not_defaults: for d in defaults.grandchildren: if d.name not in c: c.children.append(d) cfg.children = list(not_defaults) return cfg header_chars = (set(string.printable) - set(string.whitespace) - set("[]")) | set(" ") sep_chars = set("=:") key_chars = header_chars - sep_chars value_chars = set(string.printable) - set("\n\r") Yes = Literal("yes", True, ignore_case=True) No = Literal("no", False, ignore_case=True) Tru = Literal("true", True, ignore_case=True) Fals = Literal("false", False, ignore_case=True) Boolean = ((Yes | No | Tru | Fals) & (WSChar | LineEnd)) % "Boolean" LeftEnd = (WS + LeftBracket + WS) RightEnd = (WS + RightBracket + WS) Header = ( LeftEnd >> PosMarker(String(header_chars)) << RightEnd) % "Header" Key = WS >> PosMarker(String(key_chars)) << WS Sep = InSet(sep_chars, "Sep") Value = WS >> (Boolean | HangingString(value_chars)) KVPair = WithIndent(Key + Opt(Sep >> Value)) % "KVPair" Comment = (WS >> (OneLineComment("#") | OneLineComment(";")).map(lambda x: None)) Line = Comment | KVPair.map(to_directive) Sect = Lift(to_section) * Header * Many(Line).map(skip_none) Doc = Many(Comment | Sect).map(skip_none) Top = Doc + EOF res = Entry(children=Top(content)[0], src=ctx) return apply_defaults(res)
def parse_doc(f, ctx=None, line_end="\n"): def to_entry(name, rest): if isinstance(rest, list): return Section(name=name.value, children=rest, lineno=name.lineno, src=ctx) return Directive(name=name.value, attrs=[rest], lineno=name.lineno, src=ctx) Sep = InSet(":=") Stmt = Forward() Num = Number & (WSChar | LineEnd) NULL = Literal("none", value=None) Comment = (WS >> OneLineComment("#").map(lambda x: None)) BeginBlock = (WS >> LeftCurly << WS) EndBlock = (WS >> RightCurly << WS) Bare = String( set(string.printable) - (set(string.whitespace) | set("#{}'\""))) Name = WS >> PosMarker( String(string.ascii_letters + "_" + string.digits)) << WS Value = WS >> (Num | NULL | QuotedString | Bare) << WS Block = BeginBlock >> Many(Stmt).map(skip_none) << EndBlock Stanza = (Lift(to_entry) * Name * (Block | (Sep >> Value))) | Comment Stmt <= WS >> Stanza << WS Doc = Many(Stmt).map(skip_none) Top = Doc + EOF return Entry(children=Top(f)[0], src=ctx)
def parse_doc(self, content): try: def to_directive(x): name, rest = x rest = [rest] if rest is not None else [] return Directive(name=name.value.strip(), attrs=rest, lineno=name.lineno, src=self) def to_section(name, rest): return Section(name=name.value.strip(), children=rest, lineno=name.lineno, src=self) def apply_defaults(cfg): if "DEFAULT" not in cfg: return cfg defaults = cfg["DEFAULT"] not_defaults = cfg[~eq("DEFAULT")] for c in not_defaults: for d in defaults.grandchildren: if d.name not in c: c.children.append(d) cfg.children = list(not_defaults) return cfg def make_bytes(number, char_multiple): if char_multiple.lower() == 'k': return number * 2**10 if char_multiple.lower() == 'm': return number * 2**20 if char_multiple.lower() == 'g': return number * 2**30 content = "\n".join(content) header_chars = (set(string.printable) - set(string.whitespace) - set("[]")) | set(" ") sep_chars = set("=") key_chars = header_chars - sep_chars value_chars = set(string.printable) - set("\n\r") On = Literal("on", True, ignore_case=True) Off = Literal("off", False, ignore_case=True) Tru = Literal("true", True, ignore_case=True) Fals = Literal("false", False, ignore_case=True) Boolean = ((On | Off | Tru | Fals) & (WSChar | LineEnd)) % "Boolean" Num = Number & (WSChar | LineEnd) QuoStr = QuotedString & (WSChar | LineEnd) # Handle php.ini shorthand notation for memory limits: 1G, 8M, 50K # https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes MemNum = (Lift(make_bytes) * Number * (Char('K') | Char('M') | Char('G'))) & (WSChar | LineEnd) LeftEnd = (WS + LeftBracket + WS) RightEnd = (WS + RightBracket + WS) Header = (LeftEnd >> PosMarker(String(header_chars)) << RightEnd) % "Header" Key = WS >> PosMarker(String(key_chars)) << WS Sep = InSet(sep_chars, "Sep") Value = WS >> (Boolean | MemNum | Num | QuoStr | HangingString(value_chars)) KVPair = WithIndent(Key + Opt(Sep >> Value)) % "KVPair" Comment = (WS >> (OneLineComment(";")).map(lambda x: None)) Line = Comment | KVPair.map(to_directive) Sect = Lift(to_section) * Header * Many(Line).map(skip_none) Doc = Many(Comment | Sect).map(skip_none) Top = Doc << WS << EOF res = Entry(children=Top(content), src=self) return apply_defaults(res) except SkipException: raise except: raise ParseException(ParseException("Could not parse content: '{0}'". format(content)))
def loads(data): return Entry(children=Top(data)) def load(f): return loads(f.read()) def to_entry(name, rest): if isinstance(rest, list): return Section(name=name.value, children=rest, lineno=name.lineno) return Directive(name=name.value, attrs=[rest], lineno=name.lineno) Sep = InSet(":=") Stmt = Forward() Num = Number & (WSChar | LineEnd) NULL = Literal("none", value=None) Comment = (WS >> OneLineComment("#").map(lambda x: None)) BeginBlock = (WS >> LeftCurly << WS) EndBlock = (WS >> RightCurly << WS) Bare = String(set(string.printable) - (set(string.whitespace) | set("#{}'\""))) Name = WS >> PosMarker( String(string.ascii_letters + "_" + string.digits)) << WS Value = WS >> (Num | NULL | QuotedString | Bare) << WS Block = BeginBlock >> Many(Stmt).map(skip_none) << EndBlock Stanza = (Lift(to_entry) * Name * (Block | (Sep >> Value))) | Comment Stmt <= WS >> Stanza << WS Doc = Many(Stmt).map(skip_none) Top = Doc << EOF
def test_literal_value(): p = Literal("true", value=True) assert p("true") is True
def test_literal(): p = Literal("123") assert p("123") == "123"
def test_literal_value_ignore_case(): p = Literal("true", value=True, ignore_case=True) assert p("TRUE") is True
def test_literal_ignore_case(): p = Literal("true", ignore_case=True) assert p("TrUe") == "TrUe"
def test_forward(): true = Forward() true <= Literal("True", value=True) assert true("True")
json_parser handles primitive json parsing. It doesn't handle unicode or numbers in scientific notation. """ from insights.parsr import (Colon, Comma, EOF, Forward, Literal, LeftBracket, LeftCurly, Number, RightBracket, RightCurly, QuotedString, WS) def loads(data): return Top(data)[0] def load(f): return loads(f.read()) JsonArray = Forward() JsonObject = Forward() TRUE = Literal("true", value=True) FALSE = Literal("false", value=False) NULL = Literal("null", value=None) SimpleValue = (Number | QuotedString | JsonObject | JsonArray | TRUE | FALSE | NULL) JsonValue = (WS >> SimpleValue << WS) Key = (QuotedString << Colon) KVPairs = (((WS >> Key) + JsonValue).sep_by(Comma)) JsonArray <= (LeftBracket >> JsonValue.sep_by(Comma) << RightBracket) JsonObject <= (LeftCurly >> KVPairs.map(lambda res: dict( (k, v) for (k, v) in res)) << RightCurly) Top = JsonValue + EOF
for n in [name.value] + attrs: ret.append(Section(name=n, children=body, lineno=name.lineno)) else: attrs = [attrs] if not isinstance(attrs, list) else attrs ret.append( Directive(name=name.value, attrs=attrs, lineno=name.lineno)) return ret scripts = set("postrotate prerotate firstaction lastaction preremove".split()) Stanza = Forward() Spaces = Many(WSChar) Bare = String(set(string.printable) - (set(string.whitespace) | set("#{}'\""))) Num = Number & (WSChar | LineEnd) Comment = OneLineComment("#").map(lambda x: None) ScriptStart = WS >> PosMarker(Choice([Literal(s) for s in scripts])) << WS ScriptEnd = Literal("endscript") Line = (WS >> AnyChar.until(EOL) << WS).map(lambda x: "".join(x)) Lines = Line.until(ScriptEnd).map(lambda x: "\n".join(x)) Script = ScriptStart + Lines << ScriptEnd Script = Script.map(lambda x: [x[0], x[1], None]) BeginBlock = WS >> LeftCurly << WS EndBlock = WS >> RightCurly First = PosMarker((Bare | QuotedString)) << Spaces Attr = Spaces >> (Num | Bare | QuotedString) << Spaces Rest = Many(Attr) Block = BeginBlock >> Many(Stanza).map(skip_none).map(to_entries) << EndBlock Stmt = WS >> (Script | (First + Rest + Opt(Block))) << WS Stanza <= WS >> (Stmt | Comment) << WS Doc = Many(Stanza).map(skip_none).map(to_entries) Top = Doc + EOF