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