diff --git a/src/bindings/python/hammer.py b/src/bindings/python/hammer.py index e7e082298e7f99ecd8de4c8b07c756bea4d7a70e..a25dd933f46775f4c4537b31ec556aa8970b3f6a 100644 --- a/src/bindings/python/hammer.py +++ b/src/bindings/python/hammer.py @@ -4,7 +4,8 @@ import sys _ffi = FFI() -## Types +# {{{ Types + _ffi.cdef("typedef struct HAllocator_ HAllocator;") _ffi.cdef("typedef struct HArena_ HArena;") _ffi.cdef("typedef int bool;") @@ -110,10 +111,12 @@ typedef struct HBenchmarkResults_ { } HBenchmarkResults; """) -## Arena functions +# }}} +# {{{ Arena functions _ffi.cdef("void* h_arena_malloc(HArena *arena, size_t count);") _ffi.cdef("void h_arena_free(HArena *arena, void* ptr);") - +# }}} +# {{{ cdefs ## The following section was generated by ## $ perl ../desugar-header.pl <../../hammer.h |sed -e 's/.*/_ffi.cdef("&")/' _ffi.cdef("HParseResult* h_parse(const HParser* parser, const uint8_t* input, size_t length);") @@ -216,7 +219,7 @@ _lib = _ffi.verify("#include <hammer/hammer.h>", libraries=['hammer']) _lib.TT_PYTHON = _lib.TT_USER # TODO: Use the token type allocator from #45 - +# }}} class _DynamicScopeHolder(threading.local): """A dynamically-scoped holder of python objects, which may or may not otherwise appear in the object graph. Intended for use with CFFI """ @@ -263,8 +266,8 @@ def _toHParsedToken(arena, pyobj): hpt = _ffi.cast("HParsedToken*", _lib.h_arena_malloc(_ffi.sizeof(parseResult.arena, "HParsedToken"))) hpt.token_type = _lib.TT_PYTHON hpt.user = cobj - hpt.bit_offset = 127; - hpt.index = 0; + hpt.bit_offset = chr(127) + hpt.index = 0 return hpt def _fromParseResult(cobj): @@ -311,6 +314,11 @@ class Parser(object): else: return None + def __mul__(self, count): + return repeat_n(self, count) + + + class IndirectParser(Parser): def bind(self, inner): _lib.h_bind_indirect(self._parser, inner._parser) @@ -326,12 +334,20 @@ def token(token): return Parser(_lib.h_token(token, len(token)), ()) def ch(char): - return token(char) + """Returns either a token or an int, depending on the type of the + argument""" + if isinstance(char, int): + return Parser(_lib.h_ch(char), ()) + else: + return token(char) def ch_range(chr1, chr2): if not isinstance(chr1, str) or not isinstance(chr2, str): raise TypeError("ch_range can't handle unicode") - return Parser(_lib.h_ch_range(chr1, chr2), ()) + def my_action(pr): + # print "In action: ", pr + return pr + return action(Parser(_lib.h_ch_range(ord(chr1), ord(chr2)), ()), my_action) def int_range(parser, i1, i2): if type(parser) != BitsParser: @@ -436,3 +452,36 @@ def run_test(): ch('3')), ch(',')) return p_test.parse("1,2,3") + +# {{{ Automatic parser construction... python specific + +# TODO: Implement Parsable metaclass, which requires the existence of +# a "parse" method. + +# This is expected to be extended by user code. As a general rule, +# only provide auto-parsers for your own types. +AUTO_PARSERS = { + str: token, + unicode: token, +} + +def _auto_seq(lst): + return sequence(*(auto_1(p, default_method=_auto_choice) + for p in lst)) + +def _auto_choice(lst): + return choice(*(auto_1(p, default_method=_auto_seq) + for p in lst)) + +def auto_1(arg, default_method=_auto_choice): + if isinstance(arg, Parser): + return arg + elif type(arg) in AUTO_PARSERS: + return AUTO_PARSERS[type(arg)](arg) + else: + return default_method(arg) + +def auto(*args): + return auto_1(args, default_method=_auto_choice) + +# }}}