1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5import os, sys 6from ply import lex, yacc 7 8from ipdl.ast import * 9 10def _getcallerpath(): 11 '''Return the absolute path of the file containing the code that 12**CALLED** this function.''' 13 return os.path.abspath(sys._getframe(1).f_code.co_filename) 14 15##----------------------------------------------------------------------------- 16 17class ParseError(Exception): 18 def __init__(self, loc, fmt, *args): 19 self.loc = loc 20 self.error = ('%s%s: error: %s'% ( 21 Parser.includeStackString(), loc, fmt)) % args 22 def __str__(self): 23 return self.error 24 25def _safeLinenoValue(t): 26 lineno, value = 0, '???' 27 if hasattr(t, 'lineno'): lineno = t.lineno 28 if hasattr(t, 'value'): value = t.value 29 return lineno, value 30 31def _error(loc, fmt, *args): 32 raise ParseError(loc, fmt, *args) 33 34class Parser: 35 # when we reach an |include [protocol] foo;| statement, we need to 36 # save the current parser state and create a new one. this "stack" is 37 # where that state is saved 38 # 39 # there is one Parser per file 40 current = None 41 parseStack = [ ] 42 parsed = { } 43 44 def __init__(self, type, name, debug=0): 45 assert type and name 46 self.type = type 47 self.debug = debug 48 self.filename = None 49 self.includedirs = None 50 self.loc = None # not always up to date 51 self.lexer = None 52 self.parser = None 53 self.tu = TranslationUnit(type, name) 54 self.direction = None 55 self.errout = None 56 57 def parse(self, input, filename, includedirs, errout): 58 assert os.path.isabs(filename) 59 60 if filename in Parser.parsed: 61 return Parser.parsed[filename].tu 62 63 self.lexer = lex.lex(debug=self.debug, 64 optimize=not self.debug, 65 lextab="ipdl_lextab") 66 self.parser = yacc.yacc(debug=self.debug, 67 optimize=not self.debug, 68 tabmodule="ipdl_yacctab") 69 self.filename = filename 70 self.includedirs = includedirs 71 self.tu.filename = filename 72 self.errout = errout 73 74 Parser.parsed[filename] = self 75 Parser.parseStack.append(Parser.current) 76 Parser.current = self 77 78 try: 79 ast = self.parser.parse(input=input, lexer=self.lexer, 80 debug=self.debug) 81 except ParseError, p: 82 print >>errout, p 83 return None 84 85 Parser.current = Parser.parseStack.pop() 86 return ast 87 88 def resolveIncludePath(self, filepath): 89 '''Return the absolute path from which the possibly partial 90|filepath| should be read, or |None| if |filepath| cannot be located.''' 91 for incdir in self.includedirs +[ '' ]: 92 realpath = os.path.join(incdir, filepath) 93 if os.path.isfile(realpath): 94 return os.path.abspath(realpath) 95 return None 96 97 # returns a GCC-style string representation of the include stack. 98 # e.g., 99 # in file included from 'foo.ipdl', line 120: 100 # in file included from 'bar.ipd', line 12: 101 # which can be printed above a proper error message or warning 102 @staticmethod 103 def includeStackString(): 104 s = '' 105 for parse in Parser.parseStack[1:]: 106 s += " in file included from `%s', line %d:\n"% ( 107 parse.loc.filename, parse.loc.lineno) 108 return s 109 110def locFromTok(p, num): 111 return Loc(Parser.current.filename, p.lineno(num)) 112 113 114##----------------------------------------------------------------------------- 115 116reserved = set(( 117 'answer', 118 'as', 119 'async', 120 'both', 121 'bridges', 122 'call', 123 'child', 124 'class', 125 'compress', 126 'compressall', 127 '__delete__', 128 'delete', # reserve 'delete' to prevent its use 129 'from', 130 'goto', 131 'include', 132 'intr', 133 'manager', 134 'manages', 135 'namespace', 136 'nested', 137 'nullable', 138 'opens', 139 'or', 140 'parent', 141 'prio', 142 'protocol', 143 'recv', 144 'returns', 145 'send', 146 'spawns', 147 'start', 148 'state', 149 'struct', 150 'sync', 151 'union', 152 'upto', 153 'using', 154 'verify')) 155tokens = [ 156 'COLONCOLON', 'ID', 'STRING', 157] + [ r.upper() for r in reserved ] 158 159t_COLONCOLON = '::' 160 161literals = '(){}[]<>;:,~' 162t_ignore = ' \f\t\v' 163 164def t_linecomment(t): 165 r'//[^\n]*' 166 167def t_multilinecomment(t): 168 r'/\*(\n|.)*?\*/' 169 t.lexer.lineno += t.value.count('\n') 170 171def t_NL(t): 172 r'(?:\r\n|\n|\n)+' 173 t.lexer.lineno += len(t.value) 174 175def t_ID(t): 176 r'[a-zA-Z_][a-zA-Z0-9_]*' 177 if t.value in reserved: 178 t.type = t.value.upper() 179 return t 180 181def t_STRING(t): 182 r'"[^"\n]*"' 183 t.value = t.value[1:-1] 184 return t 185 186def t_error(t): 187 _error(Loc(Parser.current.filename, t.lineno), 188 'lexically invalid characters `%s', t.value) 189 190##----------------------------------------------------------------------------- 191 192def p_TranslationUnit(p): 193 """TranslationUnit : Preamble NamespacedStuff""" 194 tu = Parser.current.tu 195 tu.loc = Loc(tu.filename) 196 for stmt in p[1]: 197 if isinstance(stmt, CxxInclude): 198 tu.addCxxInclude(stmt) 199 elif isinstance(stmt, Include): 200 tu.addInclude(stmt) 201 elif isinstance(stmt, UsingStmt): 202 tu.addUsingStmt(stmt) 203 else: 204 assert 0 205 206 for thing in p[2]: 207 if isinstance(thing, StructDecl): 208 tu.addStructDecl(thing) 209 elif isinstance(thing, UnionDecl): 210 tu.addUnionDecl(thing) 211 elif isinstance(thing, Protocol): 212 if tu.protocol is not None: 213 _error(thing.loc, "only one protocol definition per file") 214 tu.protocol = thing 215 else: 216 assert(0) 217 218 # The "canonical" namespace of the tu, what it's considered to be 219 # in for the purposes of C++: |#include "foo/bar/TU.h"| 220 if tu.protocol: 221 assert tu.filetype == 'protocol' 222 tu.namespaces = tu.protocol.namespaces 223 tu.name = tu.protocol.name 224 else: 225 assert tu.filetype == 'header' 226 # There's not really a canonical "thing" in headers. So 227 # somewhat arbitrarily use the namespace of the last 228 # interesting thing that was declared. 229 for thing in reversed(tu.structsAndUnions): 230 tu.namespaces = thing.namespaces 231 break 232 233 p[0] = tu 234 235##-------------------- 236## Preamble 237def p_Preamble(p): 238 """Preamble : Preamble PreambleStmt ';' 239 |""" 240 if 1 == len(p): 241 p[0] = [ ] 242 else: 243 p[1].append(p[2]) 244 p[0] = p[1] 245 246def p_PreambleStmt(p): 247 """PreambleStmt : CxxIncludeStmt 248 | IncludeStmt 249 | UsingStmt""" 250 p[0] = p[1] 251 252def p_CxxIncludeStmt(p): 253 """CxxIncludeStmt : INCLUDE STRING""" 254 p[0] = CxxInclude(locFromTok(p, 1), p[2]) 255 256def p_IncludeStmt(p): 257 """IncludeStmt : INCLUDE PROTOCOL ID 258 | INCLUDE ID""" 259 loc = locFromTok(p, 1) 260 261 Parser.current.loc = loc 262 if 4 == len(p): 263 id = p[3] 264 type = 'protocol' 265 else: 266 id = p[2] 267 type = 'header' 268 inc = Include(loc, type, id) 269 270 path = Parser.current.resolveIncludePath(inc.file) 271 if path is None: 272 raise ParseError(loc, "can't locate include file `%s'"% ( 273 inc.file)) 274 275 inc.tu = Parser(type, id).parse(open(path).read(), path, Parser.current.includedirs, Parser.current.errout) 276 p[0] = inc 277 278def p_UsingStmt(p): 279 """UsingStmt : USING CxxType FROM STRING 280 | USING CLASS CxxType FROM STRING 281 | USING STRUCT CxxType FROM STRING""" 282 if 6 == len(p): 283 header = p[5] 284 elif 5 == len(p): 285 header = p[4] 286 else: 287 header = None 288 if 6 == len(p): 289 kind = p[2] 290 else: 291 kind = None 292 if 6 == len(p): 293 cxxtype = p[3] 294 else: 295 cxxtype = p[2] 296 p[0] = UsingStmt(locFromTok(p, 1), cxxtype, header, kind) 297 298##-------------------- 299## Namespaced stuff 300def p_NamespacedStuff(p): 301 """NamespacedStuff : NamespacedStuff NamespaceThing 302 | NamespaceThing""" 303 if 2 == len(p): 304 p[0] = p[1] 305 else: 306 p[1].extend(p[2]) 307 p[0] = p[1] 308 309def p_NamespaceThing(p): 310 """NamespaceThing : NAMESPACE ID '{' NamespacedStuff '}' 311 | StructDecl 312 | UnionDecl 313 | ProtocolDefn""" 314 if 2 == len(p): 315 p[0] = [ p[1] ] 316 else: 317 for thing in p[4]: 318 thing.addOuterNamespace(Namespace(locFromTok(p, 1), p[2])) 319 p[0] = p[4] 320 321def p_StructDecl(p): 322 """StructDecl : STRUCT ID '{' StructFields '}' ';' 323 | STRUCT ID '{' '}' ';'""" 324 if 7 == len(p): 325 p[0] = StructDecl(locFromTok(p, 1), p[2], p[4]) 326 else: 327 p[0] = StructDecl(locFromTok(p, 1), p[2], [ ]) 328 329def p_StructFields(p): 330 """StructFields : StructFields StructField ';' 331 | StructField ';'""" 332 if 3 == len(p): 333 p[0] = [ p[1] ] 334 else: 335 p[1].append(p[2]) 336 p[0] = p[1] 337 338def p_StructField(p): 339 """StructField : Type ID""" 340 p[0] = StructField(locFromTok(p, 1), p[1], p[2]) 341 342def p_UnionDecl(p): 343 """UnionDecl : UNION ID '{' ComponentTypes '}' ';'""" 344 p[0] = UnionDecl(locFromTok(p, 1), p[2], p[4]) 345 346def p_ComponentTypes(p): 347 """ComponentTypes : ComponentTypes Type ';' 348 | Type ';'""" 349 if 3 == len(p): 350 p[0] = [ p[1] ] 351 else: 352 p[1].append(p[2]) 353 p[0] = p[1] 354 355def p_ProtocolDefn(p): 356 """ProtocolDefn : OptionalProtocolSendSemanticsQual PROTOCOL ID '{' ProtocolBody '}' ';'""" 357 protocol = p[5] 358 protocol.loc = locFromTok(p, 2) 359 protocol.name = p[3] 360 protocol.nestedRange = p[1][0] 361 protocol.sendSemantics = p[1][1] 362 p[0] = protocol 363 364 if Parser.current.type == 'header': 365 _error(protocol.loc, 'can\'t define a protocol in a header. Do it in a protocol spec instead.') 366 367 368def p_ProtocolBody(p): 369 """ProtocolBody : SpawnsStmtsOpt""" 370 p[0] = p[1] 371 372##-------------------- 373## spawns/bridges/opens stmts 374 375def p_SpawnsStmtsOpt(p): 376 """SpawnsStmtsOpt : SpawnsStmt SpawnsStmtsOpt 377 | BridgesStmtsOpt""" 378 if 2 == len(p): 379 p[0] = p[1] 380 else: 381 p[2].spawnsStmts.insert(0, p[1]) 382 p[0] = p[2] 383 384def p_SpawnsStmt(p): 385 """SpawnsStmt : PARENT SPAWNS ID AsOpt ';' 386 | CHILD SPAWNS ID AsOpt ';'""" 387 p[0] = SpawnsStmt(locFromTok(p, 1), p[1], p[3], p[4]) 388 389def p_AsOpt(p): 390 """AsOpt : AS PARENT 391 | AS CHILD 392 | """ 393 if 3 == len(p): 394 p[0] = p[2] 395 else: 396 p[0] = 'child' 397 398def p_BridgesStmtsOpt(p): 399 """BridgesStmtsOpt : BridgesStmt BridgesStmtsOpt 400 | OpensStmtsOpt""" 401 if 2 == len(p): 402 p[0] = p[1] 403 else: 404 p[2].bridgesStmts.insert(0, p[1]) 405 p[0] = p[2] 406 407def p_BridgesStmt(p): 408 """BridgesStmt : BRIDGES ID ',' ID ';'""" 409 p[0] = BridgesStmt(locFromTok(p, 1), p[2], p[4]) 410 411def p_OpensStmtsOpt(p): 412 """OpensStmtsOpt : OpensStmt OpensStmtsOpt 413 | ManagersStmtOpt""" 414 if 2 == len(p): 415 p[0] = p[1] 416 else: 417 p[2].opensStmts.insert(0, p[1]) 418 p[0] = p[2] 419 420def p_OpensStmt(p): 421 """OpensStmt : PARENT OPENS ID ';' 422 | CHILD OPENS ID ';'""" 423 p[0] = OpensStmt(locFromTok(p, 1), p[1], p[3]) 424 425##-------------------- 426## manager/manages stmts 427 428def p_ManagersStmtOpt(p): 429 """ManagersStmtOpt : ManagersStmt ManagesStmtsOpt 430 | ManagesStmtsOpt""" 431 if 2 == len(p): 432 p[0] = p[1] 433 else: 434 p[2].managers = p[1] 435 p[0] = p[2] 436 437def p_ManagersStmt(p): 438 """ManagersStmt : MANAGER ManagerList ';'""" 439 if 1 == len(p): 440 p[0] = [ ] 441 else: 442 p[0] = p[2] 443 444def p_ManagerList(p): 445 """ManagerList : ID 446 | ManagerList OR ID""" 447 if 2 == len(p): 448 p[0] = [ Manager(locFromTok(p, 1), p[1]) ] 449 else: 450 p[1].append(Manager(locFromTok(p, 3), p[3])) 451 p[0] = p[1] 452 453def p_ManagesStmtsOpt(p): 454 """ManagesStmtsOpt : ManagesStmt ManagesStmtsOpt 455 | MessageDeclsOpt""" 456 if 2 == len(p): 457 p[0] = p[1] 458 else: 459 p[2].managesStmts.insert(0, p[1]) 460 p[0] = p[2] 461 462def p_ManagesStmt(p): 463 """ManagesStmt : MANAGES ID ';'""" 464 p[0] = ManagesStmt(locFromTok(p, 1), p[2]) 465 466 467##-------------------- 468## Message decls 469 470def p_MessageDeclsOpt(p): 471 """MessageDeclsOpt : MessageDeclThing MessageDeclsOpt 472 | TransitionStmtsOpt""" 473 if 2 == len(p): 474 p[0] = p[1] 475 else: 476 p[2].messageDecls.insert(0, p[1]) 477 p[0] = p[2] 478 479def p_MessageDeclThing(p): 480 """MessageDeclThing : MessageDirectionLabel ':' MessageDecl ';' 481 | MessageDecl ';'""" 482 if 3 == len(p): 483 p[0] = p[1] 484 else: 485 p[0] = p[3] 486 487def p_MessageDirectionLabel(p): 488 """MessageDirectionLabel : PARENT 489 | CHILD 490 | BOTH""" 491 if p[1] == 'parent': 492 Parser.current.direction = IN 493 elif p[1] == 'child': 494 Parser.current.direction = OUT 495 elif p[1] == 'both': 496 Parser.current.direction = INOUT 497 else: 498 assert 0 499 500def p_MessageDecl(p): 501 """MessageDecl : SendSemanticsQual MessageBody""" 502 msg = p[2] 503 msg.nested = p[1][0] 504 msg.prio = p[1][1] 505 msg.sendSemantics = p[1][2] 506 507 if Parser.current.direction is None: 508 _error(msg.loc, 'missing message direction') 509 msg.direction = Parser.current.direction 510 511 p[0] = msg 512 513def p_MessageBody(p): 514 """MessageBody : MessageId MessageInParams MessageOutParams OptionalMessageModifiers""" 515 # FIXME/cjones: need better loc info: use one of the quals 516 loc, name = p[1] 517 msg = MessageDecl(loc) 518 msg.name = name 519 msg.addInParams(p[2]) 520 msg.addOutParams(p[3]) 521 msg.addModifiers(p[4]) 522 523 p[0] = msg 524 525def p_MessageId(p): 526 """MessageId : ID 527 | __DELETE__ 528 | DELETE 529 | '~' ID""" 530 loc = locFromTok(p, 1) 531 if 3 == len(p): 532 _error(loc, "sorry, `%s()' destructor syntax is a relic from a bygone era. Declare `__delete__()' in the `%s' protocol instead", p[1]+p[2], p[2]) 533 elif 'delete' == p[1]: 534 _error(loc, "`delete' is a reserved identifier") 535 p[0] = [ loc, p[1] ] 536 537def p_MessageInParams(p): 538 """MessageInParams : '(' ParamList ')'""" 539 p[0] = p[2] 540 541def p_MessageOutParams(p): 542 """MessageOutParams : RETURNS '(' ParamList ')' 543 | """ 544 if 1 == len(p): 545 p[0] = [ ] 546 else: 547 p[0] = p[3] 548 549def p_OptionalMessageModifiers(p): 550 """OptionalMessageModifiers : OptionalMessageModifiers MessageModifier 551 | MessageModifier 552 | """ 553 if 1 == len(p): 554 p[0] = [ ] 555 elif 2 == len(p): 556 p[0] = [ p[1] ] 557 else: 558 p[1].append(p[2]) 559 p[0] = p[1] 560 561def p_MessageModifier(p): 562 """ MessageModifier : MessageVerify 563 | MessageCompress """ 564 p[0] = p[1] 565 566def p_MessageVerify(p): 567 """MessageVerify : VERIFY""" 568 p[0] = p[1] 569 570def p_MessageCompress(p): 571 """MessageCompress : COMPRESS 572 | COMPRESSALL""" 573 p[0] = p[1] 574 575##-------------------- 576## State machine 577 578def p_TransitionStmtsOpt(p): 579 """TransitionStmtsOpt : TransitionStmt TransitionStmtsOpt 580 |""" 581 if 1 == len(p): 582 # we fill in |loc| in the Protocol rule 583 p[0] = Protocol(None) 584 else: 585 p[2].transitionStmts.insert(0, p[1]) 586 p[0] = p[2] 587 588def p_TransitionStmt(p): 589 """TransitionStmt : OptionalStart STATE State ':' Transitions""" 590 p[3].start = p[1] 591 p[0] = TransitionStmt(locFromTok(p, 2), p[3], p[5]) 592 593def p_OptionalStart(p): 594 """OptionalStart : START 595 | """ 596 p[0] = (len(p) == 2) # True iff 'start' specified 597 598def p_Transitions(p): 599 """Transitions : Transitions Transition 600 | Transition""" 601 if 3 == len(p): 602 p[1].append(p[2]) 603 p[0] = p[1] 604 else: 605 p[0] = [ p[1] ] 606 607def p_Transition(p): 608 """Transition : Trigger ID GOTO StateList ';' 609 | Trigger __DELETE__ ';' 610 | Trigger DELETE ';'""" 611 if 'delete' == p[2]: 612 _error(locFromTok(p, 1), "`delete' is a reserved identifier") 613 614 loc, trigger = p[1] 615 if 6 == len(p): 616 nextstates = p[4] 617 else: 618 nextstates = [ State.DEAD ] 619 p[0] = Transition(loc, trigger, p[2], nextstates) 620 621def p_Trigger(p): 622 """Trigger : SEND 623 | RECV 624 | CALL 625 | ANSWER""" 626 p[0] = [ locFromTok(p, 1), Transition.nameToTrigger(p[1]) ] 627 628def p_StateList(p): 629 """StateList : StateList OR State 630 | State""" 631 if 2 == len(p): 632 p[0] = [ p[1] ] 633 else: 634 p[1].append(p[3]) 635 p[0] = p[1] 636 637def p_State(p): 638 """State : ID""" 639 p[0] = State(locFromTok(p, 1), p[1]) 640 641##-------------------- 642## Minor stuff 643def p_Nested(p): 644 """Nested : ID""" 645 kinds = {'not': 1, 646 'inside_sync': 2, 647 'inside_cpow': 3} 648 if p[1] not in kinds: 649 _error(locFromTok(p, 1), "Expected not, inside_sync, or inside_cpow for nested()") 650 651 p[0] = { 'nested': kinds[p[1]] } 652 653def p_Priority(p): 654 """Priority : ID""" 655 kinds = {'normal': 1, 656 'high': 2} 657 if p[1] not in kinds: 658 _error(locFromTok(p, 1), "Expected normal or high for prio()") 659 660 p[0] = { 'prio': kinds[p[1]] } 661 662def p_SendQualifier(p): 663 """SendQualifier : NESTED '(' Nested ')' 664 | PRIO '(' Priority ')'""" 665 p[0] = p[3] 666 667def p_SendQualifierList(p): 668 """SendQualifierList : SendQualifier SendQualifierList 669 | """ 670 if len(p) > 1: 671 p[0] = p[1] 672 p[0].update(p[2]) 673 else: 674 p[0] = {} 675 676def p_SendSemanticsQual(p): 677 """SendSemanticsQual : SendQualifierList ASYNC 678 | SendQualifierList SYNC 679 | INTR""" 680 quals = {} 681 if len(p) == 3: 682 quals = p[1] 683 mtype = p[2] 684 else: 685 mtype = 'intr' 686 687 if mtype == 'async': mtype = ASYNC 688 elif mtype == 'sync': mtype = SYNC 689 elif mtype == 'intr': mtype = INTR 690 else: assert 0 691 692 p[0] = [ quals.get('nested', NOT_NESTED), quals.get('prio', NORMAL_PRIORITY), mtype ] 693 694def p_OptionalProtocolSendSemanticsQual(p): 695 """OptionalProtocolSendSemanticsQual : ProtocolSendSemanticsQual 696 | """ 697 if 2 == len(p): p[0] = p[1] 698 else: p[0] = [ (NOT_NESTED, NOT_NESTED), ASYNC ] 699 700def p_ProtocolSendSemanticsQual(p): 701 """ProtocolSendSemanticsQual : ASYNC 702 | SYNC 703 | NESTED '(' UPTO Nested ')' ASYNC 704 | NESTED '(' UPTO Nested ')' SYNC 705 | INTR""" 706 if p[1] == 'nested': 707 mtype = p[6] 708 nested = (NOT_NESTED, p[4]) 709 else: 710 mtype = p[1] 711 nested = (NOT_NESTED, NOT_NESTED) 712 713 if mtype == 'async': mtype = ASYNC 714 elif mtype == 'sync': mtype = SYNC 715 elif mtype == 'intr': mtype = INTR 716 else: assert 0 717 718 p[0] = [ nested, mtype ] 719 720def p_ParamList(p): 721 """ParamList : ParamList ',' Param 722 | Param 723 | """ 724 if 1 == len(p): 725 p[0] = [ ] 726 elif 2 == len(p): 727 p[0] = [ p[1] ] 728 else: 729 p[1].append(p[3]) 730 p[0] = p[1] 731 732def p_Param(p): 733 """Param : Type ID""" 734 p[0] = Param(locFromTok(p, 1), p[1], p[2]) 735 736def p_Type(p): 737 """Type : MaybeNullable BasicType""" 738 # only actor types are nullable; we check this in the type checker 739 p[2].nullable = p[1] 740 p[0] = p[2] 741 742def p_BasicType(p): 743 """BasicType : ScalarType 744 | ScalarType '[' ']'""" 745 if 4 == len(p): 746 p[1].array = 1 747 p[0] = p[1] 748 749def p_ScalarType(p): 750 """ScalarType : ActorType 751 | CxxID""" # ID == CxxType; we forbid qnames here, 752 # in favor of the |using| declaration 753 if isinstance(p[1], TypeSpec): 754 p[0] = p[1] 755 else: 756 loc, id = p[1] 757 p[0] = TypeSpec(loc, QualifiedId(loc, id)) 758 759def p_ActorType(p): 760 """ActorType : ID ':' State""" 761 loc = locFromTok(p, 1) 762 p[0] = TypeSpec(loc, QualifiedId(loc, p[1]), state=p[3]) 763 764def p_MaybeNullable(p): 765 """MaybeNullable : NULLABLE 766 | """ 767 p[0] = (2 == len(p)) 768 769##-------------------- 770## C++ stuff 771def p_CxxType(p): 772 """CxxType : QualifiedID 773 | CxxID""" 774 if isinstance(p[1], QualifiedId): 775 p[0] = TypeSpec(p[1].loc, p[1]) 776 else: 777 loc, id = p[1] 778 p[0] = TypeSpec(loc, QualifiedId(loc, id)) 779 780def p_QualifiedID(p): 781 """QualifiedID : QualifiedID COLONCOLON CxxID 782 | CxxID COLONCOLON CxxID""" 783 if isinstance(p[1], QualifiedId): 784 loc, id = p[3] 785 p[1].qualify(id) 786 p[0] = p[1] 787 else: 788 loc1, id1 = p[1] 789 _, id2 = p[3] 790 p[0] = QualifiedId(loc1, id2, [ id1 ]) 791 792def p_CxxID(p): 793 """CxxID : ID 794 | CxxTemplateInst""" 795 if isinstance(p[1], tuple): 796 p[0] = p[1] 797 else: 798 p[0] = (locFromTok(p, 1), str(p[1])) 799 800def p_CxxTemplateInst(p): 801 """CxxTemplateInst : ID '<' ID '>'""" 802 p[0] = (locFromTok(p, 1), str(p[1]) +'<'+ str(p[3]) +'>') 803 804def p_error(t): 805 lineno, value = _safeLinenoValue(t) 806 _error(Loc(Parser.current.filename, lineno), 807 "bad syntax near `%s'", value) 808