summaryrefslogtreecommitdiffstats
path: root/ipc/ipdl/ipdl/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/ipdl/ipdl/parser.py')
-rw-r--r--ipc/ipdl/ipdl/parser.py807
1 files changed, 807 insertions, 0 deletions
diff --git a/ipc/ipdl/ipdl/parser.py b/ipc/ipdl/ipdl/parser.py
new file mode 100644
index 000000000..38c46dc73
--- /dev/null
+++ b/ipc/ipdl/ipdl/parser.py
@@ -0,0 +1,807 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os, sys
+from ply import lex, yacc
+
+from ipdl.ast import *
+
+def _getcallerpath():
+ '''Return the absolute path of the file containing the code that
+**CALLED** this function.'''
+ return os.path.abspath(sys._getframe(1).f_code.co_filename)
+
+##-----------------------------------------------------------------------------
+
+class ParseError(Exception):
+ def __init__(self, loc, fmt, *args):
+ self.loc = loc
+ self.error = ('%s%s: error: %s'% (
+ Parser.includeStackString(), loc, fmt)) % args
+ def __str__(self):
+ return self.error
+
+def _safeLinenoValue(t):
+ lineno, value = 0, '???'
+ if hasattr(t, 'lineno'): lineno = t.lineno
+ if hasattr(t, 'value'): value = t.value
+ return lineno, value
+
+def _error(loc, fmt, *args):
+ raise ParseError(loc, fmt, *args)
+
+class Parser:
+ # when we reach an |include [protocol] foo;| statement, we need to
+ # save the current parser state and create a new one. this "stack" is
+ # where that state is saved
+ #
+ # there is one Parser per file
+ current = None
+ parseStack = [ ]
+ parsed = { }
+
+ def __init__(self, type, name, debug=0):
+ assert type and name
+ self.type = type
+ self.debug = debug
+ self.filename = None
+ self.includedirs = None
+ self.loc = None # not always up to date
+ self.lexer = None
+ self.parser = None
+ self.tu = TranslationUnit(type, name)
+ self.direction = None
+ self.errout = None
+
+ def parse(self, input, filename, includedirs, errout):
+ assert os.path.isabs(filename)
+
+ if filename in Parser.parsed:
+ return Parser.parsed[filename].tu
+
+ self.lexer = lex.lex(debug=self.debug,
+ optimize=not self.debug,
+ lextab="ipdl_lextab")
+ self.parser = yacc.yacc(debug=self.debug,
+ optimize=not self.debug,
+ tabmodule="ipdl_yacctab")
+ self.filename = filename
+ self.includedirs = includedirs
+ self.tu.filename = filename
+ self.errout = errout
+
+ Parser.parsed[filename] = self
+ Parser.parseStack.append(Parser.current)
+ Parser.current = self
+
+ try:
+ ast = self.parser.parse(input=input, lexer=self.lexer,
+ debug=self.debug)
+ except ParseError, p:
+ print >>errout, p
+ return None
+
+ Parser.current = Parser.parseStack.pop()
+ return ast
+
+ def resolveIncludePath(self, filepath):
+ '''Return the absolute path from which the possibly partial
+|filepath| should be read, or |None| if |filepath| cannot be located.'''
+ for incdir in self.includedirs +[ '' ]:
+ realpath = os.path.join(incdir, filepath)
+ if os.path.isfile(realpath):
+ return os.path.abspath(realpath)
+ return None
+
+ # returns a GCC-style string representation of the include stack.
+ # e.g.,
+ # in file included from 'foo.ipdl', line 120:
+ # in file included from 'bar.ipd', line 12:
+ # which can be printed above a proper error message or warning
+ @staticmethod
+ def includeStackString():
+ s = ''
+ for parse in Parser.parseStack[1:]:
+ s += " in file included from `%s', line %d:\n"% (
+ parse.loc.filename, parse.loc.lineno)
+ return s
+
+def locFromTok(p, num):
+ return Loc(Parser.current.filename, p.lineno(num))
+
+
+##-----------------------------------------------------------------------------
+
+reserved = set((
+ 'answer',
+ 'as',
+ 'async',
+ 'both',
+ 'bridges',
+ 'call',
+ 'child',
+ 'class',
+ 'compress',
+ 'compressall',
+ '__delete__',
+ 'delete', # reserve 'delete' to prevent its use
+ 'from',
+ 'goto',
+ 'include',
+ 'intr',
+ 'manager',
+ 'manages',
+ 'namespace',
+ 'nested',
+ 'nullable',
+ 'opens',
+ 'or',
+ 'parent',
+ 'prio',
+ 'protocol',
+ 'recv',
+ 'returns',
+ 'send',
+ 'spawns',
+ 'start',
+ 'state',
+ 'struct',
+ 'sync',
+ 'union',
+ 'upto',
+ 'using',
+ 'verify'))
+tokens = [
+ 'COLONCOLON', 'ID', 'STRING',
+] + [ r.upper() for r in reserved ]
+
+t_COLONCOLON = '::'
+
+literals = '(){}[]<>;:,~'
+t_ignore = ' \f\t\v'
+
+def t_linecomment(t):
+ r'//[^\n]*'
+
+def t_multilinecomment(t):
+ r'/\*(\n|.)*?\*/'
+ t.lexer.lineno += t.value.count('\n')
+
+def t_NL(t):
+ r'(?:\r\n|\n|\n)+'
+ t.lexer.lineno += len(t.value)
+
+def t_ID(t):
+ r'[a-zA-Z_][a-zA-Z0-9_]*'
+ if t.value in reserved:
+ t.type = t.value.upper()
+ return t
+
+def t_STRING(t):
+ r'"[^"\n]*"'
+ t.value = t.value[1:-1]
+ return t
+
+def t_error(t):
+ _error(Loc(Parser.current.filename, t.lineno),
+ 'lexically invalid characters `%s', t.value)
+
+##-----------------------------------------------------------------------------
+
+def p_TranslationUnit(p):
+ """TranslationUnit : Preamble NamespacedStuff"""
+ tu = Parser.current.tu
+ tu.loc = Loc(tu.filename)
+ for stmt in p[1]:
+ if isinstance(stmt, CxxInclude):
+ tu.addCxxInclude(stmt)
+ elif isinstance(stmt, Include):
+ tu.addInclude(stmt)
+ elif isinstance(stmt, UsingStmt):
+ tu.addUsingStmt(stmt)
+ else:
+ assert 0
+
+ for thing in p[2]:
+ if isinstance(thing, StructDecl):
+ tu.addStructDecl(thing)
+ elif isinstance(thing, UnionDecl):
+ tu.addUnionDecl(thing)
+ elif isinstance(thing, Protocol):
+ if tu.protocol is not None:
+ _error(thing.loc, "only one protocol definition per file")
+ tu.protocol = thing
+ else:
+ assert(0)
+
+ # The "canonical" namespace of the tu, what it's considered to be
+ # in for the purposes of C++: |#include "foo/bar/TU.h"|
+ if tu.protocol:
+ assert tu.filetype == 'protocol'
+ tu.namespaces = tu.protocol.namespaces
+ tu.name = tu.protocol.name
+ else:
+ assert tu.filetype == 'header'
+ # There's not really a canonical "thing" in headers. So
+ # somewhat arbitrarily use the namespace of the last
+ # interesting thing that was declared.
+ for thing in reversed(tu.structsAndUnions):
+ tu.namespaces = thing.namespaces
+ break
+
+ p[0] = tu
+
+##--------------------
+## Preamble
+def p_Preamble(p):
+ """Preamble : Preamble PreambleStmt ';'
+ |"""
+ if 1 == len(p):
+ p[0] = [ ]
+ else:
+ p[1].append(p[2])
+ p[0] = p[1]
+
+def p_PreambleStmt(p):
+ """PreambleStmt : CxxIncludeStmt
+ | IncludeStmt
+ | UsingStmt"""
+ p[0] = p[1]
+
+def p_CxxIncludeStmt(p):
+ """CxxIncludeStmt : INCLUDE STRING"""
+ p[0] = CxxInclude(locFromTok(p, 1), p[2])
+
+def p_IncludeStmt(p):
+ """IncludeStmt : INCLUDE PROTOCOL ID
+ | INCLUDE ID"""
+ loc = locFromTok(p, 1)
+
+ Parser.current.loc = loc
+ if 4 == len(p):
+ id = p[3]
+ type = 'protocol'
+ else:
+ id = p[2]
+ type = 'header'
+ inc = Include(loc, type, id)
+
+ path = Parser.current.resolveIncludePath(inc.file)
+ if path is None:
+ raise ParseError(loc, "can't locate include file `%s'"% (
+ inc.file))
+
+ inc.tu = Parser(type, id).parse(open(path).read(), path, Parser.current.includedirs, Parser.current.errout)
+ p[0] = inc
+
+def p_UsingStmt(p):
+ """UsingStmt : USING CxxType FROM STRING
+ | USING CLASS CxxType FROM STRING
+ | USING STRUCT CxxType FROM STRING"""
+ if 6 == len(p):
+ header = p[5]
+ elif 5 == len(p):
+ header = p[4]
+ else:
+ header = None
+ if 6 == len(p):
+ kind = p[2]
+ else:
+ kind = None
+ if 6 == len(p):
+ cxxtype = p[3]
+ else:
+ cxxtype = p[2]
+ p[0] = UsingStmt(locFromTok(p, 1), cxxtype, header, kind)
+
+##--------------------
+## Namespaced stuff
+def p_NamespacedStuff(p):
+ """NamespacedStuff : NamespacedStuff NamespaceThing
+ | NamespaceThing"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[1].extend(p[2])
+ p[0] = p[1]
+
+def p_NamespaceThing(p):
+ """NamespaceThing : NAMESPACE ID '{' NamespacedStuff '}'
+ | StructDecl
+ | UnionDecl
+ | ProtocolDefn"""
+ if 2 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ for thing in p[4]:
+ thing.addOuterNamespace(Namespace(locFromTok(p, 1), p[2]))
+ p[0] = p[4]
+
+def p_StructDecl(p):
+ """StructDecl : STRUCT ID '{' StructFields '}' ';'
+ | STRUCT ID '{' '}' ';'"""
+ if 7 == len(p):
+ p[0] = StructDecl(locFromTok(p, 1), p[2], p[4])
+ else:
+ p[0] = StructDecl(locFromTok(p, 1), p[2], [ ])
+
+def p_StructFields(p):
+ """StructFields : StructFields StructField ';'
+ | StructField ';'"""
+ if 3 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[2])
+ p[0] = p[1]
+
+def p_StructField(p):
+ """StructField : Type ID"""
+ p[0] = StructField(locFromTok(p, 1), p[1], p[2])
+
+def p_UnionDecl(p):
+ """UnionDecl : UNION ID '{' ComponentTypes '}' ';'"""
+ p[0] = UnionDecl(locFromTok(p, 1), p[2], p[4])
+
+def p_ComponentTypes(p):
+ """ComponentTypes : ComponentTypes Type ';'
+ | Type ';'"""
+ if 3 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[2])
+ p[0] = p[1]
+
+def p_ProtocolDefn(p):
+ """ProtocolDefn : OptionalProtocolSendSemanticsQual PROTOCOL ID '{' ProtocolBody '}' ';'"""
+ protocol = p[5]
+ protocol.loc = locFromTok(p, 2)
+ protocol.name = p[3]
+ protocol.nestedRange = p[1][0]
+ protocol.sendSemantics = p[1][1]
+ p[0] = protocol
+
+ if Parser.current.type == 'header':
+ _error(protocol.loc, 'can\'t define a protocol in a header. Do it in a protocol spec instead.')
+
+
+def p_ProtocolBody(p):
+ """ProtocolBody : SpawnsStmtsOpt"""
+ p[0] = p[1]
+
+##--------------------
+## spawns/bridges/opens stmts
+
+def p_SpawnsStmtsOpt(p):
+ """SpawnsStmtsOpt : SpawnsStmt SpawnsStmtsOpt
+ | BridgesStmtsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].spawnsStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_SpawnsStmt(p):
+ """SpawnsStmt : PARENT SPAWNS ID AsOpt ';'
+ | CHILD SPAWNS ID AsOpt ';'"""
+ p[0] = SpawnsStmt(locFromTok(p, 1), p[1], p[3], p[4])
+
+def p_AsOpt(p):
+ """AsOpt : AS PARENT
+ | AS CHILD
+ | """
+ if 3 == len(p):
+ p[0] = p[2]
+ else:
+ p[0] = 'child'
+
+def p_BridgesStmtsOpt(p):
+ """BridgesStmtsOpt : BridgesStmt BridgesStmtsOpt
+ | OpensStmtsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].bridgesStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_BridgesStmt(p):
+ """BridgesStmt : BRIDGES ID ',' ID ';'"""
+ p[0] = BridgesStmt(locFromTok(p, 1), p[2], p[4])
+
+def p_OpensStmtsOpt(p):
+ """OpensStmtsOpt : OpensStmt OpensStmtsOpt
+ | ManagersStmtOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].opensStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_OpensStmt(p):
+ """OpensStmt : PARENT OPENS ID ';'
+ | CHILD OPENS ID ';'"""
+ p[0] = OpensStmt(locFromTok(p, 1), p[1], p[3])
+
+##--------------------
+## manager/manages stmts
+
+def p_ManagersStmtOpt(p):
+ """ManagersStmtOpt : ManagersStmt ManagesStmtsOpt
+ | ManagesStmtsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].managers = p[1]
+ p[0] = p[2]
+
+def p_ManagersStmt(p):
+ """ManagersStmt : MANAGER ManagerList ';'"""
+ if 1 == len(p):
+ p[0] = [ ]
+ else:
+ p[0] = p[2]
+
+def p_ManagerList(p):
+ """ManagerList : ID
+ | ManagerList OR ID"""
+ if 2 == len(p):
+ p[0] = [ Manager(locFromTok(p, 1), p[1]) ]
+ else:
+ p[1].append(Manager(locFromTok(p, 3), p[3]))
+ p[0] = p[1]
+
+def p_ManagesStmtsOpt(p):
+ """ManagesStmtsOpt : ManagesStmt ManagesStmtsOpt
+ | MessageDeclsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].managesStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_ManagesStmt(p):
+ """ManagesStmt : MANAGES ID ';'"""
+ p[0] = ManagesStmt(locFromTok(p, 1), p[2])
+
+
+##--------------------
+## Message decls
+
+def p_MessageDeclsOpt(p):
+ """MessageDeclsOpt : MessageDeclThing MessageDeclsOpt
+ | TransitionStmtsOpt"""
+ if 2 == len(p):
+ p[0] = p[1]
+ else:
+ p[2].messageDecls.insert(0, p[1])
+ p[0] = p[2]
+
+def p_MessageDeclThing(p):
+ """MessageDeclThing : MessageDirectionLabel ':' MessageDecl ';'
+ | MessageDecl ';'"""
+ if 3 == len(p):
+ p[0] = p[1]
+ else:
+ p[0] = p[3]
+
+def p_MessageDirectionLabel(p):
+ """MessageDirectionLabel : PARENT
+ | CHILD
+ | BOTH"""
+ if p[1] == 'parent':
+ Parser.current.direction = IN
+ elif p[1] == 'child':
+ Parser.current.direction = OUT
+ elif p[1] == 'both':
+ Parser.current.direction = INOUT
+ else:
+ assert 0
+
+def p_MessageDecl(p):
+ """MessageDecl : SendSemanticsQual MessageBody"""
+ msg = p[2]
+ msg.nested = p[1][0]
+ msg.prio = p[1][1]
+ msg.sendSemantics = p[1][2]
+
+ if Parser.current.direction is None:
+ _error(msg.loc, 'missing message direction')
+ msg.direction = Parser.current.direction
+
+ p[0] = msg
+
+def p_MessageBody(p):
+ """MessageBody : MessageId MessageInParams MessageOutParams OptionalMessageModifiers"""
+ # FIXME/cjones: need better loc info: use one of the quals
+ loc, name = p[1]
+ msg = MessageDecl(loc)
+ msg.name = name
+ msg.addInParams(p[2])
+ msg.addOutParams(p[3])
+ msg.addModifiers(p[4])
+
+ p[0] = msg
+
+def p_MessageId(p):
+ """MessageId : ID
+ | __DELETE__
+ | DELETE
+ | '~' ID"""
+ loc = locFromTok(p, 1)
+ if 3 == len(p):
+ _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])
+ elif 'delete' == p[1]:
+ _error(loc, "`delete' is a reserved identifier")
+ p[0] = [ loc, p[1] ]
+
+def p_MessageInParams(p):
+ """MessageInParams : '(' ParamList ')'"""
+ p[0] = p[2]
+
+def p_MessageOutParams(p):
+ """MessageOutParams : RETURNS '(' ParamList ')'
+ | """
+ if 1 == len(p):
+ p[0] = [ ]
+ else:
+ p[0] = p[3]
+
+def p_OptionalMessageModifiers(p):
+ """OptionalMessageModifiers : OptionalMessageModifiers MessageModifier
+ | MessageModifier
+ | """
+ if 1 == len(p):
+ p[0] = [ ]
+ elif 2 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[2])
+ p[0] = p[1]
+
+def p_MessageModifier(p):
+ """ MessageModifier : MessageVerify
+ | MessageCompress """
+ p[0] = p[1]
+
+def p_MessageVerify(p):
+ """MessageVerify : VERIFY"""
+ p[0] = p[1]
+
+def p_MessageCompress(p):
+ """MessageCompress : COMPRESS
+ | COMPRESSALL"""
+ p[0] = p[1]
+
+##--------------------
+## State machine
+
+def p_TransitionStmtsOpt(p):
+ """TransitionStmtsOpt : TransitionStmt TransitionStmtsOpt
+ |"""
+ if 1 == len(p):
+ # we fill in |loc| in the Protocol rule
+ p[0] = Protocol(None)
+ else:
+ p[2].transitionStmts.insert(0, p[1])
+ p[0] = p[2]
+
+def p_TransitionStmt(p):
+ """TransitionStmt : OptionalStart STATE State ':' Transitions"""
+ p[3].start = p[1]
+ p[0] = TransitionStmt(locFromTok(p, 2), p[3], p[5])
+
+def p_OptionalStart(p):
+ """OptionalStart : START
+ | """
+ p[0] = (len(p) == 2) # True iff 'start' specified
+
+def p_Transitions(p):
+ """Transitions : Transitions Transition
+ | Transition"""
+ if 3 == len(p):
+ p[1].append(p[2])
+ p[0] = p[1]
+ else:
+ p[0] = [ p[1] ]
+
+def p_Transition(p):
+ """Transition : Trigger ID GOTO StateList ';'
+ | Trigger __DELETE__ ';'
+ | Trigger DELETE ';'"""
+ if 'delete' == p[2]:
+ _error(locFromTok(p, 1), "`delete' is a reserved identifier")
+
+ loc, trigger = p[1]
+ if 6 == len(p):
+ nextstates = p[4]
+ else:
+ nextstates = [ State.DEAD ]
+ p[0] = Transition(loc, trigger, p[2], nextstates)
+
+def p_Trigger(p):
+ """Trigger : SEND
+ | RECV
+ | CALL
+ | ANSWER"""
+ p[0] = [ locFromTok(p, 1), Transition.nameToTrigger(p[1]) ]
+
+def p_StateList(p):
+ """StateList : StateList OR State
+ | State"""
+ if 2 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[3])
+ p[0] = p[1]
+
+def p_State(p):
+ """State : ID"""
+ p[0] = State(locFromTok(p, 1), p[1])
+
+##--------------------
+## Minor stuff
+def p_Nested(p):
+ """Nested : ID"""
+ kinds = {'not': 1,
+ 'inside_sync': 2,
+ 'inside_cpow': 3}
+ if p[1] not in kinds:
+ _error(locFromTok(p, 1), "Expected not, inside_sync, or inside_cpow for nested()")
+
+ p[0] = { 'nested': kinds[p[1]] }
+
+def p_Priority(p):
+ """Priority : ID"""
+ kinds = {'normal': 1,
+ 'high': 2}
+ if p[1] not in kinds:
+ _error(locFromTok(p, 1), "Expected normal or high for prio()")
+
+ p[0] = { 'prio': kinds[p[1]] }
+
+def p_SendQualifier(p):
+ """SendQualifier : NESTED '(' Nested ')'
+ | PRIO '(' Priority ')'"""
+ p[0] = p[3]
+
+def p_SendQualifierList(p):
+ """SendQualifierList : SendQualifier SendQualifierList
+ | """
+ if len(p) > 1:
+ p[0] = p[1]
+ p[0].update(p[2])
+ else:
+ p[0] = {}
+
+def p_SendSemanticsQual(p):
+ """SendSemanticsQual : SendQualifierList ASYNC
+ | SendQualifierList SYNC
+ | INTR"""
+ quals = {}
+ if len(p) == 3:
+ quals = p[1]
+ mtype = p[2]
+ else:
+ mtype = 'intr'
+
+ if mtype == 'async': mtype = ASYNC
+ elif mtype == 'sync': mtype = SYNC
+ elif mtype == 'intr': mtype = INTR
+ else: assert 0
+
+ p[0] = [ quals.get('nested', NOT_NESTED), quals.get('prio', NORMAL_PRIORITY), mtype ]
+
+def p_OptionalProtocolSendSemanticsQual(p):
+ """OptionalProtocolSendSemanticsQual : ProtocolSendSemanticsQual
+ | """
+ if 2 == len(p): p[0] = p[1]
+ else: p[0] = [ (NOT_NESTED, NOT_NESTED), ASYNC ]
+
+def p_ProtocolSendSemanticsQual(p):
+ """ProtocolSendSemanticsQual : ASYNC
+ | SYNC
+ | NESTED '(' UPTO Nested ')' ASYNC
+ | NESTED '(' UPTO Nested ')' SYNC
+ | INTR"""
+ if p[1] == 'nested':
+ mtype = p[6]
+ nested = (NOT_NESTED, p[4])
+ else:
+ mtype = p[1]
+ nested = (NOT_NESTED, NOT_NESTED)
+
+ if mtype == 'async': mtype = ASYNC
+ elif mtype == 'sync': mtype = SYNC
+ elif mtype == 'intr': mtype = INTR
+ else: assert 0
+
+ p[0] = [ nested, mtype ]
+
+def p_ParamList(p):
+ """ParamList : ParamList ',' Param
+ | Param
+ | """
+ if 1 == len(p):
+ p[0] = [ ]
+ elif 2 == len(p):
+ p[0] = [ p[1] ]
+ else:
+ p[1].append(p[3])
+ p[0] = p[1]
+
+def p_Param(p):
+ """Param : Type ID"""
+ p[0] = Param(locFromTok(p, 1), p[1], p[2])
+
+def p_Type(p):
+ """Type : MaybeNullable BasicType"""
+ # only actor types are nullable; we check this in the type checker
+ p[2].nullable = p[1]
+ p[0] = p[2]
+
+def p_BasicType(p):
+ """BasicType : ScalarType
+ | ScalarType '[' ']'"""
+ if 4 == len(p):
+ p[1].array = 1
+ p[0] = p[1]
+
+def p_ScalarType(p):
+ """ScalarType : ActorType
+ | CxxID""" # ID == CxxType; we forbid qnames here,
+ # in favor of the |using| declaration
+ if isinstance(p[1], TypeSpec):
+ p[0] = p[1]
+ else:
+ loc, id = p[1]
+ p[0] = TypeSpec(loc, QualifiedId(loc, id))
+
+def p_ActorType(p):
+ """ActorType : ID ':' State"""
+ loc = locFromTok(p, 1)
+ p[0] = TypeSpec(loc, QualifiedId(loc, p[1]), state=p[3])
+
+def p_MaybeNullable(p):
+ """MaybeNullable : NULLABLE
+ | """
+ p[0] = (2 == len(p))
+
+##--------------------
+## C++ stuff
+def p_CxxType(p):
+ """CxxType : QualifiedID
+ | CxxID"""
+ if isinstance(p[1], QualifiedId):
+ p[0] = TypeSpec(p[1].loc, p[1])
+ else:
+ loc, id = p[1]
+ p[0] = TypeSpec(loc, QualifiedId(loc, id))
+
+def p_QualifiedID(p):
+ """QualifiedID : QualifiedID COLONCOLON CxxID
+ | CxxID COLONCOLON CxxID"""
+ if isinstance(p[1], QualifiedId):
+ loc, id = p[3]
+ p[1].qualify(id)
+ p[0] = p[1]
+ else:
+ loc1, id1 = p[1]
+ _, id2 = p[3]
+ p[0] = QualifiedId(loc1, id2, [ id1 ])
+
+def p_CxxID(p):
+ """CxxID : ID
+ | CxxTemplateInst"""
+ if isinstance(p[1], tuple):
+ p[0] = p[1]
+ else:
+ p[0] = (locFromTok(p, 1), str(p[1]))
+
+def p_CxxTemplateInst(p):
+ """CxxTemplateInst : ID '<' ID '>'"""
+ p[0] = (locFromTok(p, 1), str(p[1]) +'<'+ str(p[3]) +'>')
+
+def p_error(t):
+ lineno, value = _safeLinenoValue(t)
+ _error(Loc(Parser.current.filename, lineno),
+ "bad syntax near `%s'", value)