| #!python |
| """ Thrift IDL parser/compiler |
| |
| This parser uses the Python PLY LALR parser generator to build a parser for the Thrift IDL grammar. |
| |
| If a compiles \"thyc\" file exists for a given source \"thrift\" file it computes a hash of the file and determines |
| if if it is the source of the \"thyc\" file. If so, it simply returns the parse tree previously computed, otherwise it |
| parses the source and generates a new \"thyc\" file (assuming of course the source file contains no errors.) |
| |
| When the parser encounters import statements it searches for corresponding \"thrift\" or \"thyc\" files in paths corresponding to |
| the specified namespace. |
| |
| Author(s): Mark Slee(mclee@facebook.com), Marc Kwiatkowski (marc@facebook.com) |
| |
| $Id: |
| """ |
| |
| import lex |
| import os |
| import pickle |
| import string |
| import yacc |
| |
| class Error(object): |
| |
| def __init__(self, start=0, end=0, message=""): |
| if len(message) == 0: |
| raise Exception, "NO MESSAGE" |
| self.message = message |
| self.start = start |
| self.end = end |
| |
| def __str__(self): |
| return str(self.start)+": error: "+self.message |
| |
| class SyntaxError(Error): |
| def __init__(self, yaccSymbol): |
| if isinstance(yaccSymbol, yacc.YaccSymbol): |
| Error.__init__(self, yaccSymbol.lineno, yaccSymbol.lineno, "syntax error "+str(yaccSymbol.value)) |
| else: |
| Error.__init__(self, 1, 1, "syntax error "+str(yaccSymbol)) |
| |
| class SymanticsError(Error): |
| |
| def __init__(self, definition, message): |
| Error.__init__(self, definition.start, definition.end, message) |
| self.definition = definition |
| |
| def __str__(self): |
| return str(self.start)+": error: "+self.message |
| |
| class ErrorException(Exception): |
| |
| def __init__(self, errors=None): |
| self.errors = errors |
| |
| class Definition(object): |
| """ Abstract thrift IDL definition unit """ |
| |
| def __init__(self, symbols=None, name="", id=None): |
| if symbols: |
| self.lines(symbols) |
| self.name = name |
| self.id = id |
| |
| def validate(self): |
| pass |
| |
| def lines(self, symbols): |
| self.start = symbols.lineno(1) |
| self.end = symbols.lineno(len(symbols) - 1) |
| |
| class Identifier(Definition): |
| """ An Identifier - name and optional integer id """ |
| |
| def __init__(self, symbols, name, id=None): |
| Definition.__init__(self, symbols, name, id) |
| |
| def __str__(self): |
| result = self.name |
| if self.id != 0: |
| result+="="+str(self.id) |
| return result |
| |
| |
| def toCanonicalType(ttype): |
| if isinstance(ttype, TypedefType): |
| return toCanonicalType(ttype.definitionType) |
| else: |
| return ttype |
| |
| def isComparableType(ttype): |
| ttype = toCanonicalType(ttype) |
| return isinstance(ttype, PrimitiveType) or isinstance(ttype, EnumType) |
| |
| class Type(Definition): |
| """ Abstract Type definition """ |
| |
| def __init__(self, symbols, name): |
| Definition.__init__(self, symbols, name) |
| self.name = name |
| |
| def __str__(self): |
| return self.name |
| |
| class TypedefType(Type): |
| |
| def __init__(self, symbols, name, definitionType): |
| Type.__init__(self, symbols, name) |
| self.definitionType = definitionType |
| |
| def __str__(self): |
| return self.name+"<"+str(self.name)+", "+str(self.definitionType)+">" |
| |
| """ Primitive Types """ |
| |
| class PrimitiveType(Type): |
| |
| def __init__(self, name): |
| Type.__init__(self, None, name) |
| |
| STOP_TYPE = PrimitiveType("stop") |
| VOID_TYPE = PrimitiveType("void") |
| BOOL_TYPE = PrimitiveType("bool") |
| STRING_TYPE =PrimitiveType("utf7") |
| UTF7_TYPE = PrimitiveType("utf7") |
| UTF8_TYPE = PrimitiveType("utf8") |
| UTF16_TYPE = PrimitiveType("utf16") |
| BYTE_TYPE = PrimitiveType("u08") |
| I08_TYPE = PrimitiveType("i08") |
| I16_TYPE = PrimitiveType("i16") |
| I32_TYPE = PrimitiveType("i32") |
| I64_TYPE = PrimitiveType("i64") |
| U08_TYPE = PrimitiveType("u08") |
| U16_TYPE = PrimitiveType("u16") |
| U32_TYPE = PrimitiveType("u32") |
| U64_TYPE = PrimitiveType("u64") |
| FLOAT_TYPE = PrimitiveType("float") |
| |
| PRIMITIVE_MAP = { |
| "stop" : STOP_TYPE, |
| "void" : VOID_TYPE, |
| "bool" : BOOL_TYPE, |
| "string": UTF7_TYPE, |
| "utf7": UTF7_TYPE, |
| "utf8": UTF8_TYPE, |
| "utf16": UTF16_TYPE, |
| "byte" : U08_TYPE, |
| "i08": I08_TYPE, |
| "i16": I16_TYPE, |
| "i32": I32_TYPE, |
| "i64": I64_TYPE, |
| "u08": U08_TYPE, |
| "u16": U16_TYPE, |
| "u32": U32_TYPE, |
| "u64": U64_TYPE, |
| "float": FLOAT_TYPE |
| } |
| |
| """ Collection Types """ |
| |
| class CollectionType(Type): |
| |
| def __init__(self, symbols, name): |
| Type.__init__(self, symbols, name) |
| |
| def validate(self): |
| return True |
| |
| class MapType(CollectionType): |
| |
| def __init__(self, symbols, keyType, valueType): |
| CollectionType.__init__(self, symbols, "map<"+keyType.name+","+valueType.name +">") |
| self.keyType = keyType |
| self.valueType = valueType |
| |
| def validate(self): |
| if not isComparableType(self.keyType): |
| raise ErrorException([SymanticsError(self, "key type \""+str(self.keyType)+"\" is not a comparable type.")]) |
| |
| class SetType(CollectionType): |
| |
| def __init__(self, symbols, valueType): |
| CollectionType.__init__(self, symbols, "set<"+valueType.name+">") |
| self.valueType = valueType |
| |
| def validate(self): |
| if not isComparableType(self.valueType): |
| raise ErrorException([SymanticsError(self, "value type \""+str(self.valueType)+"\" is not a comparable type.")]) |
| |
| class ListType(CollectionType): |
| |
| def __init__(self, symbols, valueType): |
| CollectionType.__init__(self, symbols, "list<"+valueType.name+">") |
| self.valueType = valueType |
| |
| class EnumType(Definition): |
| |
| def __init__(self, symbols, name, enumDefs): |
| Definition.__init__(self, symbols, name) |
| self.enumDefs = enumDefs |
| |
| def validate(self): |
| ids = {} |
| names = {} |
| errors = [] |
| |
| for enumDef in self.enumDefs: |
| |
| if enumDef.name in names: |
| errors.append(SymanticsError(enumDef, self.name+"."+str(enumDef.name)+" already defined at line "+str(names[enumDef.name].start))) |
| else: |
| names[enumDef.name] = enumDef |
| |
| if enumDef.id != None: |
| oldEnumDef = ids.get(enumDef.id) |
| if oldEnumDef: |
| errors.append(SymanticsError(enumDef, "enum "+self.name+" \""+str(enumDef.name)+"\" uses constant already assigned to \""+oldEnumDef.name+"\"")) |
| else: |
| ids[enumDef.id] = enumDef |
| |
| if len(errors): |
| raise ErrorException(errors) |
| |
| def assignId(enumDef, currentId, ids): |
| 'Finds the next available id number for an enum definition' |
| |
| id= currentId + 1 |
| |
| while id in ids: |
| id += 1 |
| |
| enumDef.id = id |
| |
| ids[enumDef.id] = enumDef |
| |
| # assign ids for all enum defs with unspecified ids |
| |
| currentId = 0 |
| |
| for enumDef in self.enumDefs: |
| if not enumDef.id: |
| assignId(enumDef, currentId, ids) |
| currentId = enumDef.id |
| |
| def __repr__(self): |
| return str(self) |
| |
| def __str__(self): |
| return self.name+"<"+string.join(map(lambda enumDef: str(enumDef), self.enumDefs), ", ") |
| |
| class EnumDef(Definition): |
| |
| def __init__(self, symbols, name, id=None): |
| Definition.__init__(self, symbols, name, id) |
| |
| def __repr__(self): |
| return str(self) |
| |
| def __str__(self): |
| result = self.name |
| if self.id: |
| result+= ":"+str(self.id) |
| return result |
| |
| |
| class Field(Definition): |
| |
| def __init__(self, symbols, type, identifier): |
| Definition.__init__(self, symbols, identifier.name, identifier.id) |
| self.type = type |
| self.identifier = identifier |
| |
| def __str__(self): |
| return "<"+str(self.type)+", "+str(self.identifier)+">" |
| |
| def validateFieldList(fieldList): |
| |
| errors = [] |
| names = {} |
| ids = {} |
| |
| for field in fieldList: |
| |
| if field.name in names: |
| oldField = names[field.name] |
| errors.append(SymanticsError(field, "field \""+field.name+"\" already defined at "+str(oldField.start))) |
| else: |
| names[field.name] = field |
| |
| if field.id != None: |
| oldField = ids.get(field.id) |
| if oldField: |
| errors.append(SymanticsError(field, "field \""+field.name+"\" uses constant already assigned to \""+oldField.name+"\"")) |
| else: |
| ids[field.id] = field |
| |
| if len(errors): |
| raise ErrorException(errors) |
| |
| def assignId(field, currentId, ids): |
| 'Finds the next available id number for a field' |
| id = currentId - 1 |
| |
| while id in ids: |
| id-= 1 |
| |
| field.id = id |
| |
| ids[field.id] = field |
| |
| return id |
| |
| # assign ids for all fields with unspecified ids |
| |
| currentId = 0 |
| |
| for field in fieldList: |
| if not field.id: |
| currentId = assignId(field, currentId, ids) |
| |
| class StructType(Type): |
| |
| def __init__(self, symbols, name, fieldList): |
| Type.__init__(self, symbols, name) |
| self.fieldList = fieldList |
| |
| def validate(self): |
| validateFieldList(self.fieldList) |
| |
| def __str__(self): |
| return self.name+"<"+string.join(map(lambda a: str(a), self.fieldList), ", ")+">" |
| |
| class Function(Definition): |
| |
| def __init__(self, symbols, name, resultStruct, argsStruct, ): |
| Definition.__init__(self, symbols, name) |
| self.resultStruct = resultStruct |
| self.argsStruct = argsStruct |
| |
| def validate(self): |
| validateFieldList(self.argsStruct.fieldList) |
| |
| def args(self): |
| return self.argsStruct.fieldList |
| |
| def returnType(self): |
| return self.resultStruct.fieldList[0].type |
| |
| def __str__(self): |
| return self.name+"("+string.join(map(lambda a: str(a), self.argsStruct), ", ")+") => "+str(self.resultStruct) |
| |
| class Service(Definition): |
| |
| def __init__(self, symbols, name, functionList): |
| Definition.__init__(self, symbols, name) |
| self.functionList = functionList |
| |
| def validate(self): |
| |
| errors = [] |
| functionNames = {} |
| for function in self.functionList: |
| if function.name in functionNames: |
| oldFunction = functionName[function.name] |
| errors.append(SymanticsError(function, "function "+function.name+" already defined at "+str(oldFunction.start))) |
| |
| if len(errors): |
| raise ErrorException(errors) |
| |
| def __str__(self): |
| return self.name+"("+string.join(map(lambda a: str(a), self.functionList), ", ")+")" |
| |
| class Program(object): |
| |
| def __init__(self, symbols=None, name="", definitions=None, serviceMap=None, typedefMap=None, enumMap=None, structMap=None, collectionMap=None, |
| primitiveMap=None): |
| |
| self.name = name |
| |
| if not definitions: |
| definitions = [] |
| self.definitions = definitions |
| |
| if not serviceMap: |
| serviceMap = {} |
| self.serviceMap = serviceMap |
| |
| if not typedefMap: |
| typedefMap = {} |
| self.typedefMap = typedefMap |
| |
| if not enumMap: |
| enumMap = {} |
| self.enumMap = enumMap |
| |
| if not structMap: |
| structMap = {} |
| self.structMap = structMap |
| |
| if not collectionMap: |
| collectionMap = {} |
| self.collectionMap = collectionMap |
| |
| if not primitiveMap: |
| primitiveMap = PRIMITIVE_MAP |
| self.primitiveMap = primitiveMap |
| |
| def addDefinition(self, definition, definitionMap, definitionTypeName): |
| |
| oldDefinition = definitionMap.get(definition.name) |
| if oldDefinition: |
| raise ErrorException([SymanticsError(definition, definitionTypeName+" "+definition.name+" is already defined at "+str(oldDefinition.start))]) |
| else: |
| definitionMap[definition.name] = definition |
| |
| # keep an ordered list of definitions so that stub/skel generators can determine the original order |
| |
| self.definitions.append(definition) |
| |
| def addStruct(self, struct): |
| self.addDefinition(struct, self.structMap, "struct") |
| |
| def addTypedef(self, typedef): |
| self.addDefinition(typedef, self.typedefMap, "typedef") |
| |
| def addEnum(self, enum): |
| self.addDefinition(enum, self.enumMap, "enum") |
| |
| def addService(self, service): |
| self.addDefinition(service, self.serviceMap, "service") |
| |
| def addCollection(self, collection): |
| if collection.name in self.collectionMap: |
| return self.collectionMap[collection.name] |
| else: |
| self.collectionMap[collection.name] = collection |
| return collection |
| |
| def getType(self, parent, symbol): |
| """ Get the type definition for a symbol""" |
| |
| typeName = None |
| |
| if isinstance(symbol, Type): |
| return symbol |
| elif isinstance(symbol, Field): |
| typeName = symbol.type.name |
| elif isinstance(symbol, Identifier): |
| typeName = symbol.name |
| else: |
| raise ErrorException([SymanticsError(parent, "unknown symbol \""+str(symbol)+"\"")]) |
| |
| for map in (self.primitiveMap, self.collectionMap, self.typedefMap, self.enumMap, self.structMap): |
| if typeName in map: |
| return map[typeName] |
| |
| raise ErrorException([SymanticsError(parent, "\""+typeName+"\" is not defined.")]) |
| |
| def hasType(self, parent, symbol): |
| """ Determine if a type definition exists for the symbol""" |
| |
| return self.getType(parent, symbol) == True |
| |
| def validate(self): |
| |
| errors = [] |
| |
| # Verify that struct fields types, collection key and element types, and typedef defined types exists and replaces |
| # type names with references to the type objects |
| |
| for struct in self.structMap.values(): |
| for field in struct.fieldList: |
| try: |
| field.type = self.getType(struct, field) |
| except ErrorException, e: |
| errors+= e.errors |
| |
| for collection in self.collectionMap.values(): |
| try: |
| if isinstance(collection, MapType): |
| collection.keyType = self.getType(collection, collection.keyType) |
| |
| collection.valueType = self.getType(collection, collection.valueType) |
| |
| collection.validate() |
| |
| except ErrorException, e: |
| errors+= e.errors |
| |
| for typedef in self.typedefMap.values(): |
| try: |
| typedef.definitionType = self.getType(self, typedef.definitionType) |
| |
| except ErrorException, e: |
| errors+= e.errors |
| |
| # Verify that service function result and arg list types exist and replace type name with reference to definition |
| |
| for service in self.serviceMap.values(): |
| |
| for function in service.functionList: |
| |
| for field in function.resultStruct.fieldList: |
| try: |
| field.type = self.getType(function, field) |
| except ErrorException, e: |
| errors+= e.errors |
| |
| for field in function.argsStruct.fieldList: |
| try: |
| field.type = self.getType(function, field) |
| except ErrorException, e: |
| errors+= e.errors |
| |
| if len(errors): |
| raise ErrorException(errors) |
| |
| class Parser(object): |
| |
| reserved = ("BYTE", |
| # "CONST", |
| "DOUBLE", |
| "ENUM", |
| # "EXCEPTION", |
| # "EXTENDS", |
| "I08", |
| "I16", |
| "I32", |
| "I64", |
| "LIST", |
| "MAP", |
| "SERVICE", |
| "SET", |
| # "STATIC", |
| "STRING", |
| "STRUCT", |
| # "SYNCHRONIZED", |
| "TYPEDEF", |
| "U08", |
| "U16", |
| "U32", |
| "U64", |
| "UTF16", |
| "UTF8", |
| "VOID" |
| ) |
| |
| tokens = reserved + ( |
| # Literals (identifier, integer constant, float constant, string constant, char const) |
| 'ID', 'ICONST', 'SCONST', 'FCONST', |
| # Operators default=, optional*, variable... |
| 'ASSIGN', #'OPTIONAL', 'ELLIPSIS', |
| # Delimeters ( ) { } < > , . ; : |
| 'LPAREN', 'RPAREN', |
| 'LBRACE', 'RBRACE', |
| 'LANGLE', 'RANGLE', |
| 'COMMA' #, 'PERIOD', 'SEMI' , 'COLON' |
| ) |
| |
| precendence = () |
| |
| reserved_map = {} |
| |
| for r in reserved: |
| reserved_map[r.lower()] = r |
| |
| def t_ID(self, t): |
| r'[A-Za-z_][\w_]*' |
| t.type = self.reserved_map.get(t.value,"ID") |
| return t |
| |
| # Completely ignored characters |
| t_ignore = ' \t\x0c' |
| |
| # t_OPTIONAL = r'\*' |
| t_ASSIGN = r'=' |
| |
| # Delimeters |
| t_LPAREN = r'\(' |
| t_RPAREN = r'\)' |
| t_LANGLE = r'\<' |
| t_RANGLE = r'\>' |
| t_LBRACE = r'\{' |
| t_RBRACE = r'\}' |
| t_COMMA = r',' |
| # t_PERIOD = r'\.' |
| # t_SEMI = r';' |
| # t_COLON = r':' |
| # t_ELLIPSIS = r'\.\.\.' |
| |
| # Integer literal |
| t_ICONST = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?' |
| |
| # Floating literal |
| t_FCONST = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' |
| |
| # String literal |
| t_SCONST = r'\"([^\\\n]|(\\.))*?\"' |
| |
| # Comments |
| def t_comment(self, t): |
| r'(?:/\*(.|\n)*?\*/)|(?://[^\n]*\n)' |
| t.lineno += t.value.count('\n') |
| |
| def t_error(self, t): |
| print "Illegal character %s" % repr(t.value[0]) |
| t.skip(1) |
| |
| # Newlines |
| def t_newline(self, t): |
| r'\n+' |
| t.lineno += t.value.count("\n") |
| |
| def p_program(self, p): |
| 'program : definitionlist' |
| pass |
| |
| def p_definitionlist_1(self, p): |
| 'definitionlist : definitionlist definition' |
| pass |
| |
| def p_definitionlist_2(self, p): |
| 'definitionlist :' |
| pass |
| |
| def p_definition_1(self, p): |
| 'definition : typedef' |
| self.pdebug("p_definition_1", p) |
| p[0] = p[1] |
| try: |
| self.program.addTypedef(p[0]) |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_definition_2(self, p): |
| 'definition : enum' |
| self.pdebug("p_definition_2", p) |
| p[0] = p[1] |
| try: |
| self.program.addEnum(p[0]) |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_definition_3(self, p): |
| 'definition : struct' |
| self.pdebug("p_definition_3", p) |
| p[0] = p[1] |
| try: |
| self.program.addStruct(p[0]) |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_definition_4(self, p): |
| 'definition : service' |
| self.pdebug("p_definition_4", p) |
| p[0] = p[1] |
| try: |
| self.program.addService(p[0]) |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_typedef(self, p): |
| 'typedef : TYPEDEF definitiontype ID' |
| self.pdebug("p_typedef", p) |
| p[0] = TypedefType(p, p[3], p[2]) |
| try: |
| p[0].validate() |
| |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_enum(self, p): |
| 'enum : ENUM ID LBRACE enumdeflist RBRACE' |
| self.pdebug("p_enum", p) |
| p[0] = EnumType(p, p[2], p[4]) |
| |
| try: |
| p[0].validate() |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_enumdeflist_1(self, p): |
| 'enumdeflist : enumdeflist COMMA enumdef' |
| self.pdebug("p_enumdeflist_1", p) |
| p[0] = p[1] + (p[3],) |
| |
| def p_enumdeflist_2(self, p): |
| 'enumdeflist : enumdef' |
| self.pdebug("p_enumdeflist_2", p) |
| p[0] = (p[1],) |
| |
| def p_enumdef_0(self, p): |
| 'enumdef : ID ASSIGN ICONST' |
| self.pdebug("p_enumdef_0", p) |
| p[0] = EnumDef(p, p[1], int(p[3])) |
| |
| def p_enumdef_1(self, p): |
| 'enumdef : ID' |
| self.pdebug("p_enumdef_1", p) |
| p[0] = EnumDef(p, p[1]) |
| |
| def p_struct(self, p): |
| 'struct : STRUCT ID LBRACE fieldlist RBRACE' |
| self.pdebug("p_struct", p) |
| p[0] = StructType(p, p[2], p[4]) |
| |
| try: |
| p[0].validate() |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_service(self, p): |
| 'service : SERVICE ID LBRACE functionlist RBRACE' |
| self.pdebug("p_service", p) |
| p[0] = Service(p, p[2], p[4]) |
| try: |
| p[0].validate() |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_functionlist_1(self, p): |
| 'functionlist : functionlist function' |
| self.pdebug("p_functionlist_1", p) |
| p[0] = p[1] + (p[2],) |
| |
| def p_functionlist_2(self, p): |
| 'functionlist :' |
| self.pdebug("p_functionlist_2", p) |
| p[0] = () |
| |
| def p_function(self, p): |
| 'function : functiontype functionmodifiers ID LPAREN fieldlist RPAREN' |
| self.pdebug("p_function", p) |
| |
| resultStruct = StructType(p, p[3]+"_result", (Field(p, p[1], Identifier(None, "success", 1)),)) |
| |
| p[0] = Function(p, p[3], resultStruct, StructType(p, p[3]+"_args", p[5])) |
| try: |
| p[0].validate() |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| def p_functionmodifiers(self, p): |
| 'functionmodifiers :' |
| self.pdebug("p_functionmodifiers", p) |
| p[0] = () |
| |
| def p_fieldlist_1(self, p): |
| 'fieldlist : fieldlist COMMA field' |
| self.pdebug("p_fieldlist_1", p) |
| p[0] = p[1] + (p[3],) |
| |
| def p_fieldlist_2(self, p): |
| 'fieldlist : field' |
| self.pdebug("p_fieldlist_2", p) |
| p[0] = (p[1],) |
| |
| def p_fieldlist_3(self, p): |
| 'fieldlist :' |
| self.pdebug("p_fieldlist_3", p) |
| p[0] = () |
| |
| def p_field_1(self, p): |
| 'field : fieldtype ID ASSIGN ICONST' |
| self.pdebug("p_field_1", p) |
| p[0] = Field(p, p[1], Identifier(None, p[2], int(p[4]))) |
| |
| def p_field_2(self, p): |
| 'field : fieldtype ID' |
| self.pdebug("p_field_2", p) |
| p[0] = Field(p, p[1], Identifier(None, p[2])) |
| |
| def p_definitiontype_1(self, p): |
| 'definitiontype : basetype' |
| self.pdebug("p_definitiontype_1", p) |
| p[0] = p[1] |
| |
| def p_definitiontype_2(self, p): |
| 'definitiontype : collectiontype' |
| self.pdebug("p_definitiontype_2", p) |
| p[0] = p[1] |
| |
| def p_functiontype_1(self, p): |
| 'functiontype : fieldtype' |
| self.pdebug("p_functiontype_1", p) |
| p[0] = p[1] |
| |
| def p_functiontype_2(self, p): |
| 'functiontype : VOID' |
| self.pdebug("p_functiontype_2", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_fieldtype_1(self, p): |
| 'fieldtype : ID' |
| self.pdebug("p_fieldtype_1", p) |
| p[0] = Identifier(p, p[1]) |
| |
| def p_fieldtype_2(self, p): |
| 'fieldtype : basetype' |
| self.pdebug("p_fieldtype_2", p) |
| p[0] = p[1] |
| |
| def p_fieldtype_3(self, p): |
| 'fieldtype : collectiontype' |
| self.pdebug("p_fieldtype_3", p) |
| p[0] = p[1] |
| |
| def p_basetype_1(self, p): |
| 'basetype : STRING' |
| self.pdebug("p_basetype_1", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_2(self, p): |
| 'basetype : BYTE' |
| self.pdebug("p_basetype_2", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_3(self, p): |
| 'basetype : I08' |
| self.pdebug("p_basetype_3", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_4(self, p): |
| 'basetype : U08' |
| self.pdebug("p_basetype_4", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_5(self, p): |
| 'basetype : I16' |
| self.pdebug("p_basetype_5", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_6(self, p): |
| 'basetype : U16' |
| self.pdebug("p_basetype_6", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_7(self, p): |
| 'basetype : I32' |
| self.pdebug("p_basetype_7", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_8(self, p): |
| 'basetype : U32' |
| self.pdebug("p_basetype_8", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_9(self, p): |
| 'basetype : I64' |
| self.pdebug("p_basetype_9", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_10(self, p): |
| 'basetype : U64' |
| self.pdebug("p_basetype_10", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_11(self, p): |
| 'basetype : UTF8' |
| self.pdebug("p_basetype_11", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_12(self, p): |
| 'basetype : UTF16' |
| self.pdebug("p_basetype_12", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_basetype_13(self, p): |
| 'basetype : DOUBLE' |
| self.pdebug("p_basetype_13", p) |
| p[0] = self.program.primitiveMap[p[1].lower()] |
| |
| def p_collectiontype_1(self, p): |
| 'collectiontype : maptype' |
| self.pdebug("p_collectiontype_1", p) |
| p[0] = self.program.addCollection(p[1]) |
| |
| def p_collectiontype_2(self, p): |
| 'collectiontype : settype' |
| self.pdebug("p_collectiontype_2", p) |
| p[0] = self.program.addCollection(p[1]) |
| |
| def p_collectiontype_3(self, p): |
| 'collectiontype : listtype' |
| self.pdebug("p_collectiontype_3", p) |
| p[0] = self.program.addCollection(p[1]) |
| |
| def p_maptype(self, p): |
| 'maptype : MAP LANGLE fieldtype COMMA fieldtype RANGLE' |
| self.pdebug("p_maptype", p) |
| p[0] = MapType(p, p[3], p[5]) |
| |
| def p_settype(self, p): |
| 'settype : SET LANGLE fieldtype RANGLE' |
| self.pdebug("p_settype", p) |
| p[0] = SetType(p, p[3]) |
| |
| def p_listtype(self, p): |
| 'listtype : LIST LANGLE fieldtype RANGLE' |
| self.pdebug("p_listtype", p) |
| p[0] = ListType(p, p[3]) |
| |
| def p_error(self, p): |
| # p_error is called with an empty token if eof was encountered unexpectedly. |
| if not p: |
| self.errors.append(SyntaxError("Unexpected end of file")) |
| else: |
| self.errors.append(SyntaxError(p)) |
| |
| def pdebug(self, name, p): |
| if self.debug: |
| print(name+"("+string.join(map(lambda t: "<<"+str(t)+">>", p), ", ")+")") |
| |
| def __init__(self, **kw): |
| self.debug = kw.get('debug', 0) |
| self.names = { } |
| self.program = Program() |
| self.errors = [] |
| |
| try: |
| modname = os.path.split(os.path.splitext(__file__)[0])[1] + "_" + self.__class__.__name__ |
| except: |
| modname = "parser"+"_"+self.__class__.__name__ |
| self.debugfile = modname + ".dbg" |
| self.tabmodule = modname + "_" + "parsetab" |
| #print self.debugfile, self.tabmodule |
| |
| # Build the lexer and parser |
| lex.lex(module=self, debug=self.debug) |
| yacc.yacc(module=self, |
| debug=self.debug, |
| debugfile=self.debugfile, |
| tabmodule=self.tabmodule) |
| |
| def parsestring(self, s, filename=""): |
| yacc.parse(s) |
| |
| if len(self.errors) == 0: |
| try: |
| self.program.validate() |
| except ErrorException, e: |
| self.errors+= e.errors |
| |
| if len(self.errors): |
| for error in self.errors: |
| print(filename+":"+str(error)) |
| |
| def parse(self, filename, doPickle=True): |
| |
| f = file(filename, "r") |
| |
| self.parsestring(f.read(), filename) |
| |
| if len(self.errors) == 0 and doPickle: |
| |
| outf = file(os.path.splitext(filename)[0]+".thyc", "w") |
| |
| pickle.dump(self.program, outf) |