|  | # | 
|  | # simple parser for Thrift. | 
|  | # | 
|  |  | 
|  | # Note: the scanner module is designed to allow this wildcard import | 
|  | from scanner import * | 
|  |  | 
|  |  | 
|  | def parse(contents): | 
|  |  | 
|  | scanner = Scanner(contents) | 
|  | program = Program() | 
|  |  | 
|  | while True: | 
|  |  | 
|  | t = scanner.get() | 
|  | if t is None: | 
|  | return program | 
|  |  | 
|  | ### delta: we don't enforce HeaderList followed by DefinitionList | 
|  | ### delta: deprecated namespaces are not parsed | 
|  |  | 
|  | if t == ID_INCLUDE: | 
|  | inc = scanner.value_of(TYPE_LIT) | 
|  | program.add_include(inc) | 
|  | elif t == ID_NAMESPACE: | 
|  | lang = scanner.value_of(TYPE_ID) | 
|  | ns = scanner.value_of(TYPE_ID) | 
|  | program.add_namespace(lang, ns) | 
|  | elif t == ID_CPP_INCLUDE: | 
|  | inc = scanner.value_of(TYPE_LIT) | 
|  | program.add_cpp_include(inc) | 
|  | elif t == ID_PHP_NAMESPACE: | 
|  | ns = scanner.value_of(TYPE_ID) | 
|  | program.set_php_namespace(ns) | 
|  | elif t == ID_XSD_NAMESPACE: | 
|  | ns = scanner.value_of(TYPE_LIT) | 
|  | program.set_xsd_namespace(ns) | 
|  | elif t == ID_CONST: | 
|  | doc = scanner.doc | 
|  | ft = parse_field_type(scanner, True) | 
|  | ident = scanner.value_of(TYPE_ID) | 
|  | scanner.eat_expected(SYM_EQ) | 
|  | value = parse_const_value(scanner) | 
|  | scanner.eat_commasemi() | 
|  | program.add_const(ident, ft, value, doc) | 
|  | elif t == ID_TYPEDEF: | 
|  | doc = scanner.doc | 
|  | ft = parse_field_type(scanner, False) | 
|  | ident = scanner.value_of(TYPE_ID) | 
|  | program.add_typedef(ident, ft, doc) | 
|  | elif t == ID_ENUM: | 
|  | enum_doc = scanner.doc | 
|  | enum_ident = scanner.value_of(TYPE_ID) | 
|  | scanner.eat_expected(SYM_LBRACE) | 
|  | values = [ ] | 
|  | while True: | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == SYM_RBRACE: | 
|  | break | 
|  | if t.ttype != TYPE_ID: | 
|  | raise ExpectedType(TYPE_ID, t.ttype, scanner.lineno) | 
|  | doc = scanner.doc | 
|  | ident = t.tvalue | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == SYM_EQ: | 
|  | value = scanner.value_of(TYPE_INT) | 
|  | else: | 
|  | scanner.pushback(t) | 
|  | value = None | 
|  | scanner.eat_commasemi() | 
|  | values.append(EnumValue(ident, value, doc)) | 
|  | program.add_enum(enum_ident, values, enum_doc) | 
|  | elif t == ID_SENUM: | 
|  | doc = scanner.doc | 
|  | ident = scanner.value_of(TYPE_ID) | 
|  | scanner.eat_expected(SYM_LBRACE) | 
|  | values = [ ] | 
|  | while True: | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == SYM_RBRACE: | 
|  | break | 
|  | if t.ttype != TYPE_LIT: | 
|  | raise ExpectedType(TYPE_LIT, t.ttype, scanner.lineno) | 
|  | scanner.eat_commasemi() | 
|  | values.append(t.tvalue) | 
|  | program.add_senum(ident, values, doc) | 
|  | elif t == ID_STRUCT: | 
|  | doc = scanner.doc | 
|  | ident = scanner.value_of(TYPE_ID) | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == ID_XSD_ALL: | 
|  | xsd_all = True | 
|  | else: | 
|  | xsd_all = False | 
|  | scanner.pushback(t) | 
|  | fields = parse_field_list(scanner, SYM_LBRACE, SYM_RBRACE) | 
|  | annotations = parse_annotations(scanner) | 
|  | program.add_struct(ident, fields, annotations, doc) | 
|  | elif t == ID_EXCEPTION: | 
|  | doc = scanner.doc | 
|  | ident = scanner.value_of(TYPE_ID) | 
|  | fields = parse_field_list(scanner, SYM_LBRACE, SYM_RBRACE) | 
|  | program.add_exception(ident, fields, doc) | 
|  | elif t == ID_SERVICE: | 
|  | svc_doc = scanner.doc | 
|  | svc_ident = scanner.value_of(TYPE_ID) | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == ID_EXTENDS: | 
|  | extends = t.tvalue | 
|  | t = scanner.get(eof_allowed=False) | 
|  | else: | 
|  | extends = None | 
|  | if t != SYM_LBRACE: | 
|  | raise ExpectedError(SYM_LBRACE, t, scanner.lineno) | 
|  | functions = [ ] | 
|  | while True: | 
|  | t = scanner.get(eof_allowed=False) | 
|  | doc = scanner.doc | 
|  | if t == SYM_RBRACE: | 
|  | break | 
|  | if t == ID_ASYNC: | 
|  | async = True | 
|  | t = scanner.get(eof_allowed=False) | 
|  | else: | 
|  | async = False | 
|  | if t == ID_VOID: | 
|  | ft = FieldType(ident=ID_VOID) | 
|  | else: | 
|  | scanner.pushback(t) | 
|  | ft = parse_field_type(scanner, True) | 
|  | ident = scanner.value_of(TYPE_ID) | 
|  | params = parse_field_list(scanner, SYM_LPAREN, SYM_RPAREN) | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == ID_THROWS: | 
|  | throws = parse_field_list(scanner, SYM_LPAREN, SYM_RPAREN) | 
|  | else: | 
|  | throws = None | 
|  | scanner.pushback(t) | 
|  | scanner.eat_commasemi() | 
|  | functions.append(Function(ident, async, ft, params, throws, doc)) | 
|  | program.add_service(svc_ident, extends, functions, svc_doc) | 
|  | else: | 
|  | raise IncorrectSyntax(scanner.lineno) | 
|  |  | 
|  |  | 
|  | def parse_field_type(scanner, ident_allowed): | 
|  | ident = scanner.get_type(TYPE_ID) | 
|  | if ident in BASE_TYPES: | 
|  | return FieldType(ident=ident) | 
|  |  | 
|  | cpp_type = None | 
|  |  | 
|  | if ident == ID_MAP: | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == ID_CPP_TYPE: | 
|  | cpp_type = scanner.value_of(TYPE_LITERAL) | 
|  | t = scanner.get() | 
|  | if t != SYM_LT: | 
|  | raise ExpectedError(SYM_LT, t, scanner.lineno) | 
|  | map_from = parse_field_type(scanner, True) | 
|  | scanner.eat_expected(SYM_COMMA) | 
|  | map_to = parse_field_type(scanner, True) | 
|  | scanner.eat_expected(SYM_GT) | 
|  | return FieldType(cpp_type=cpp_type, map_from=map_from, map_to=map_to, | 
|  | annotations=parse_annotations(scanner)) | 
|  |  | 
|  | if ident == ID_SET: | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == ID_CPP_TYPE: | 
|  | cpp_type = scanner.value_of(TYPE_LITERAL) | 
|  | t = scanner.get() | 
|  | if t != SYM_LT: | 
|  | raise ExpectedError(SYM_LT, t, scanner.lineno) | 
|  | set_of = parse_field_type(scanner, True) | 
|  | scanner.eat_expected(SYM_GT) | 
|  | return FieldType(cpp_type=cpp_type, set_of=set_of, | 
|  | annotations=parse_annotations(scanner)) | 
|  |  | 
|  | if ident == ID_LIST: | 
|  | scanner.eat_expected(SYM_LT) | 
|  | list_of = parse_field_type(scanner, True) | 
|  | scanner.eat_expected(SYM_GT) | 
|  | t = scanner.get() | 
|  | if t == ID_CPP_TYPE: | 
|  | cpp_type = scanner.value_of(TYPE_LITERAL) | 
|  | elif t is not None: | 
|  | scanner.pushback(t) | 
|  | return FieldType(cpp_type=cpp_type, list_of=list_of, | 
|  | annotations=parse_annotations(scanner)) | 
|  |  | 
|  | # random identifiers are allowed for FieldType, but not DefinitionType | 
|  | if ident_allowed: | 
|  | return FieldType(ident=ident) | 
|  |  | 
|  | raise IncorrectSyntax(scanner.lineno) | 
|  |  | 
|  |  | 
|  | def parse_const_value(scanner): | 
|  | value = scanner.get(eof_allowed=False) | 
|  | if value.ttype in [TYPE_INT, TYPE_HEX, TYPE_DUB, TYPE_LIT, TYPE_ID]: | 
|  | return ConstValue(ConstValue.CTYPE_BASE, value) | 
|  |  | 
|  | if value == SYM_LBRKT: | 
|  | values = [ ] | 
|  | while True: | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == SYM_RBRKT: | 
|  | return ConstValue(ConstValue.CTYPE_LIST, values) | 
|  | scanner.pushback(t) | 
|  | scanner.eat_commasemi() | 
|  | values.append(parse_const_value(scanner)) | 
|  |  | 
|  | if value == SYM_LBRACE: | 
|  | values = [ ] | 
|  | while True: | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == SYM_RBRACE: | 
|  | return ConstValue(ConstValue.CTYPE_MAP, values) | 
|  | scanner.pushback(t) | 
|  | key = parse_const_value(scanner) | 
|  | scanner.eat_expected(SYM_COLON) | 
|  | value = parse_const_value(scanner) | 
|  | scanner.eat_commasemi() | 
|  | values.append(KeyValuePair(key, value)) | 
|  |  | 
|  | raise IncorrectSyntax(scanner.lineno) | 
|  |  | 
|  |  | 
|  | def parse_field_list(scanner, start, end): | 
|  | scanner.eat_expected(start) | 
|  |  | 
|  | fields = [ ] | 
|  | while True: | 
|  | t = scanner.get(eof_allowed=False) | 
|  | if t == end: | 
|  | return fields | 
|  | doc = scanner.doc | 
|  | if t.ttype == TYPE_INT: | 
|  | field_id = t.tvalue | 
|  | scanner.eat_expected(SYM_COLON) | 
|  | t = scanner.get(eof_allowed=False) | 
|  | else: | 
|  | field_id = None | 
|  | if t == ID_REQUIRED or t == ID_OPTIONAL: | 
|  | ### delta: we don't warn when this occurs in an arglist | 
|  | requiredness = t | 
|  | else: | 
|  | requiredness = None | 
|  | scanner.pushback(t) | 
|  | ft = parse_field_type(scanner, True) | 
|  | ident = scanner.value_of(TYPE_ID) | 
|  | t = scanner.get() | 
|  | if t == SYM_EQ: | 
|  | value = parse_const_value(scanner) | 
|  | t = scanner.get() | 
|  | else: | 
|  | value = None | 
|  | if t == ID_XSD_OPTIONAL: | 
|  | xsd_optional = True | 
|  | t = scanner.get() | 
|  | else: | 
|  | xsd_optional = False | 
|  | if t == ID_XSD_NILLABLE: | 
|  | xsd_nillable = True | 
|  | t = scanner.get() | 
|  | else: | 
|  | xsd_nillable = False | 
|  | if t == ID_XSD_ATTRS: | 
|  | xsd_attrs = parse_field_list(scanner, SYM_LBRACE, SYM_RBRACE) | 
|  | else: | 
|  | xsd_attrs = None | 
|  | if t is not None: | 
|  | scanner.pushback(t) | 
|  | scanner.eat_commasemi() | 
|  | fields.append(Field(ident, ft, doc, field_id, requiredness, value, | 
|  | xsd_optional, xsd_nillable, xsd_attrs)) | 
|  |  | 
|  |  | 
|  | def parse_annotations(scanner): | 
|  | t = scanner.get() | 
|  | if t is None: | 
|  | return None | 
|  | if t != SYM_LPAREN: | 
|  | scanner.pushback(t) | 
|  | return None | 
|  | annotations = [ ] | 
|  | while True: | 
|  | ident = scanner.value_of(TYPE_ID) | 
|  | scanner.eat_expected(SYM_EQ) | 
|  | value = scanner.value_of(TYPE_LIT) | 
|  | annotations.append(KeyValuePair(ident, value)) | 
|  |  | 
|  | scanner.eat_commasemi() | 
|  | t = scanner.get() | 
|  | if t == SYM_RPAREN: | 
|  | return annotations | 
|  | scanner.pushback(t) | 
|  |  | 
|  |  | 
|  | class Program(object): | 
|  | def __init__(self): | 
|  | self.includes = [ ] | 
|  | self.namespaces = [ ] | 
|  | self.cpp_includes = [ ] | 
|  | self.php_namespace = None | 
|  | self.xsd_namespace = None | 
|  | self.consts = [ ] | 
|  | self.typedefs = [ ] | 
|  | self.enums = [ ] | 
|  | self.structs = [ ] | 
|  | self.exceptions = [ ] | 
|  | self.services = [ ] | 
|  |  | 
|  | def add_include(self, include): | 
|  | self.includes.append(include) | 
|  |  | 
|  | def add_namespace(self, lang, namespace): | 
|  | self.namespaces.append(Namespace(lang, namespace)) | 
|  |  | 
|  | def add_cpp_include(self, include): | 
|  | self.cpp_includes.append(include) | 
|  |  | 
|  | def set_php_namespace(self, namespace): | 
|  | self.php_namespace = namespace | 
|  |  | 
|  | def set_xsd_namespace(self, namespace): | 
|  | self.xsd_namespace = namespace | 
|  |  | 
|  | def add_const(self, ident, field_type, value, doc): | 
|  | self.consts.append(ConstDef(ident, field_type, value, doc)) | 
|  |  | 
|  | def add_typedef(self, ident, field_type, doc): | 
|  | self.typedefs.append(Typedef(ident, field_type, doc)) | 
|  |  | 
|  | def add_enum(self, ident, value, doc): | 
|  | self.enums.append(Enum(ident, value, doc)) | 
|  |  | 
|  | def add_senum(self, ident, values, doc): | 
|  | self.typedefs.append(Typedef(ident, FieldType(values=values), doc)) | 
|  |  | 
|  | def add_struct(self, ident, fields, annotations, doc): | 
|  | self.structs.append(Struct(ident, fields, annotations, doc)) | 
|  |  | 
|  | def add_exception(self, ident, fields, doc): | 
|  | self.exceptions.append(Exception(ident, fields, doc)) | 
|  |  | 
|  | def add_service(self, ident, extends, functions, doc): | 
|  | self.services.append(Service(ident, extends, functions, doc)) | 
|  |  | 
|  |  | 
|  | class Service(object): | 
|  | def __init__(self, ident, extends, functions, doc): | 
|  | self.ident = ident | 
|  | self.extends = extends | 
|  | self.functions = functions | 
|  | self.doc = doc | 
|  |  | 
|  |  | 
|  | class Function(object): | 
|  | def __init__(self, ident, async, field_type, params, throws, doc): | 
|  | self.ident = ident | 
|  | self.async = async | 
|  | self.field_type = field_type | 
|  | self.params = params | 
|  | self.throws = throws | 
|  | self.doc = doc | 
|  |  | 
|  |  | 
|  | class Enum(object): | 
|  | def __init__(self, ident, values, doc): | 
|  | self.ident = ident | 
|  | self.values = values | 
|  | self.doc = doc | 
|  |  | 
|  | for i in range(1, len(values)): | 
|  | if values[i].value is None: | 
|  | ### keep as integer? | 
|  | values[i].value = str(int(values[i - 1].value) + 1) | 
|  |  | 
|  |  | 
|  | class EnumValue(object): | 
|  | def __init__(self, ident, value, doc): | 
|  | self.ident = ident | 
|  | self.value = value | 
|  | self.doc = doc | 
|  |  | 
|  |  | 
|  | class Field(object): | 
|  | def __init__(self, ident, field_type, doc, field_id, requiredness, value, | 
|  | xsd_optional, xsd_nillable, xsd_attrs): | 
|  | assert value is None or isinstance(value, ConstValue) | 
|  |  | 
|  | self.ident = ident | 
|  | self.field_type = field_type | 
|  | self.doc = doc | 
|  | self.field_id = field_id | 
|  | self.requiredness = requiredness | 
|  | self.value = value | 
|  | self.xsd_optional = xsd_optional | 
|  | self.xsd_nillable = xsd_nillable | 
|  | self.xsd_attrs = xsd_attrs | 
|  |  | 
|  |  | 
|  | class FieldType(object): | 
|  | def __init__(self, ident=None, cpp_type=None, map_from=None, map_to=None, | 
|  | set_of=None, list_of=None, annotations=None, values=None): | 
|  | if map_from is not None: | 
|  | self.ident = ID_MAP | 
|  | elif set_of is not None: | 
|  | self.ident = ID_SET | 
|  | elif list_of is not None: | 
|  | self.ident = ID_LIST | 
|  | elif values is not None: | 
|  | self.ident = ID_STRING | 
|  | else: | 
|  | assert ident is not None | 
|  | self.ident = ident | 
|  | self.cpp_type = cpp_type | 
|  | self.map_from = map_from | 
|  | self.map_to = map_to | 
|  | self.set_of = set_of | 
|  | self.list_of = list_of | 
|  | self.annotations = annotations | 
|  | self.values = values | 
|  |  | 
|  |  | 
|  | class KeyValuePair(object): | 
|  | def __init__(self, key, value): | 
|  | self.key = key | 
|  | self.value = value | 
|  |  | 
|  |  | 
|  | class ConstDef(object): | 
|  | def __init__(self, ident, field_type, value, doc): | 
|  | assert isinstance(value, ConstValue) | 
|  |  | 
|  | self.ident = ident | 
|  | self.field_type = field_type | 
|  | self.value = value | 
|  | self.doc = doc | 
|  |  | 
|  |  | 
|  | class ConstValue(object): | 
|  | CTYPE_BASE = 'base' | 
|  | CTYPE_LIST = 'list' | 
|  | CTYPE_MAP = 'map' | 
|  |  | 
|  | def __init__(self, ctype, value): | 
|  | self.ctype = ctype | 
|  | self.value = value | 
|  |  | 
|  |  | 
|  | class Typedef(object): | 
|  | def __init__(self, ident, field_type, doc): | 
|  | self.ident = ident | 
|  | self.field_type = field_type | 
|  | self.doc = doc | 
|  |  | 
|  |  | 
|  | class Struct(object): | 
|  | def __init__(self, ident, fields, annotations, doc): | 
|  | self.ident = ident | 
|  | self.fields = fields | 
|  | self.annotations = annotations | 
|  | self.doc = doc | 
|  |  | 
|  |  | 
|  | class Exception(object): | 
|  | def __init__(self, ident, fields, doc): | 
|  | self.ident = ident | 
|  | self.fields = fields | 
|  | self.doc = doc | 
|  |  | 
|  |  | 
|  | class Namespace(object): | 
|  | def __init__(self, lang, namespace): | 
|  | self.lang = lang | 
|  | self.namespace = namespace | 
|  |  | 
|  |  | 
|  | BASE_TYPES = [ | 
|  | ID_STRING, | 
|  | ID_BINARY, | 
|  | ID_SLIST, | 
|  | ID_BOOL, | 
|  | ID_BYTE, | 
|  | ID_I16, | 
|  | ID_I32, | 
|  | ID_I64, | 
|  | ID_DOUBLE, | 
|  | ] | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | import sys | 
|  | parse(open(sys.argv[1]).read()) |