def make_varmgr(istoplevel=False): """Return a variable manager instance.""" appfmt = syslx.View(module.apps.get( args.project).attrs)['appfmt'].s appfmt = diagutil.parse_fmt(appfmt) def new_var(var, name): """Outputs a new definition when VarManager makes a new variable.""" #TODO dodgy, should be using context (look at syslseqs) if istoplevel: template = 'state "{}" as X{}{} {{' else: template = ' state "{}" as {}{}' (_, _, name) = name.partition(' : ') app = module.apps[name] write( template, appfmt(appname=name, **diagutil.attr_fmt_vars(app.attrs)).replace( '\n', r'\n'), var, ' <<highlight>>' if name in highlights else '') return diagutil.VarManager(new_var)
def make_varmgr(): """Return a variable manager instance.""" appfmt = syslx.View(module.apps.get( args.project).attrs)['appfmt'].s appfmt = diagutil.parse_fmt(appfmt) def new_var(var, name): """Outputs a new definition when VarManager makes a new variable.""" app = module.apps[name] write( '[{}] as {}{}', appfmt(appname=name_map.get(name, name), **diagutil.attr_fmt_vars(app.attrs)).replace( '\n', r'\n'), var, ' <<highlight>>' if name in highlights else '') return diagutil.VarManager(new_var)
def _make_varmgr(module, appname, write): """Return a variable manager instance.""" def new_var(var, name): """Outputs a new definition when VarManager makes a new variable.""" app = module.apps[name] write('class "{}" as {} << (D,orchid) >> {{', name, var) typespec = module.apps.get(appname).types.get(name) assert typespec.WhichOneof('type') == 'tuple' fields = sorted(typespec.tuple.attr_defs.iteritems(), key=_attr_sort_key) for (fieldname, fieldtype) in fields: which = fieldtype.WhichOneof('type') suffix = '' prefix = '' while which == 'list': fieldtype = fieldtype.list.type which = fieldtype.WhichOneof('type') suffix = '[]' + suffix if which == 'set': fieldtype = fieldtype.set which = fieldtype.WhichOneof('type') prefix = 'set <' suffix = '>' + suffix bold = False if which == 'primitive': typestr = fieldtype.Primitive.Name(fieldtype.primitive).lower() elif which == 'type_ref': typestr = '.'.join(fieldtype.type_ref.ref.path) bold = True else: typestr = '<color red>**{}**</color>'.format(which) typestr = prefix + typestr + suffix if bold: typestr = '**{}**'.format(typestr) write('+ {} : {}', fieldname, typestr) write('}}') return diagutil.VarManager(new_var)
def sequence_diag(module, params, log_integration=None): """Generate a sequence diagram. Params: module: sysl_pb2.Module params: SequenceDiagParams log_call: invoked for each visited call. def(app=sysl_pb2.Application(), stmt=sysl_pb2.Stmt(), patterns={str}) """ blackboxes = params.blackboxes or [] already_visited = collections.defaultdict(int) write = _Writer(params.activations) var_names = [] def new_var(var, appname): """Outputs a new definition when VarManager makes a new variable.""" app = module.apps[appname] has_category = syslx.patterns( app.attrs) & {'human', 'cron', 'db', 'external', 'ui'} assert len(has_category) <= 1 (order, agent) = { 'human': (0, 'actor'), 'ui': (1, 'boundary'), 'cron': (2, 'control'), 'db': (4, 'database'), 'external': (5, 'control'), }.get(''.join(has_category), (3, 'control')) label = params.appfmt(appname=appname, **diagutil.attr_fmt_vars(app.attrs)).replace( u'\n', ur'\n') var_names.append( ((order, int(var[1:])), u'{} "{}" as {}'.format(agent, label, var))) var_name = diagutil.VarManager(new_var) def visit_endpoint(from_app, appname, epname, uptos, sender_patterns, stmt=None, deactivate=None): """Recursively visit an endpoint.""" if from_app: sender = var_name(syslx.fmt_app_name(from_app.name)) else: sender = '[' agent = var_name(appname) app = module.apps.get(appname) endpt = app.endpoints.get(epname) assert endpt def visit_stmts(stmts, deactivate, last_parent_stmt): """Recursively visit a stmt list.""" def block(last_stmt, block_stmts, fmt, *args): """Output a compound block.""" write(fmt, *args) with write.indent(): return visit_stmts(block_stmts, deactivate, last_stmt) def block_with_end(last_stmt, block_stmts, fmt, *args): """Output a compound block, including the 'end' clause.""" payload = block(last_stmt, block_stmts, fmt, *args) write('end') return payload payload = None for (i, stmt) in enumerate(stmts): last_stmt = last_parent_stmt and i == len(stmts) - 1 if stmt.HasField('call'): with write.indent(): payload = visit_endpoint( app, syslx.fmt_app_name(stmt.call.target), stmt.call.endpoint, uptos, app_patterns, stmt, last_stmt and deactivate) elif stmt.HasField('action'): write('{0} -> {0} : {1}', agent, r'\n'.join(textwrap.wrap(stmt.action.action, 40))) elif stmt.HasField('cond'): payload = block_with_end(last_stmt, stmt.cond.stmt, 'opt {}', stmt.cond.test) elif stmt.HasField('loop'): payload = block_with_end( last_stmt, stmt.loop.stmt, 'loop {} {}', stmt.loop.Mode.Name(stmt.loop.mode), stmt.loop.criterion) elif stmt.HasField('loop_n'): payload = block_with_end(last_stmt, stmt.loop_n.stmt, 'loop {} times', stmt.loop_n.count) elif stmt.HasField('foreach'): payload = block_with_end(last_stmt, stmt.foreach.stmt, 'loop for each {}', stmt.foreach.collection) elif stmt.HasField('group'): payload = block_with_end(last_stmt, stmt.group.stmt, 'group {}', stmt.group.title) elif stmt.HasField('alt'): prefix = 'alt' for (j, choice) in enumerate(stmt.alt.choice): last_alt_stmt = last_stmt and j == len( stmt.alt.choice) - 1 payload = block(last_alt_stmt, choice.stmt, '{} {}', prefix, choice.cond) prefix = 'else' write('end') elif stmt.HasField('ret'): write('{}<--{} : {}', sender, agent, stmt.ret.payload) else: raise Exception('No statement!') return payload app_patterns = syslx.patterns(app.attrs) target_patterns = syslx.patterns(endpt.attrs) patterns = target_patterns human = 'human' in app_patterns human_sender = 'human' in sender_patterns cron = 'cron' in sender_patterns needs_int = not (human or human_sender or cron) and sender != agent # pdb.set_trace() label = re.sub(ur'^.*? -> ', u' ⬄ ', r'\n'.join(textwrap.wrap(unicode(epname), 40))) cron = 'cron' in app_patterns if stmt: assert stmt.HasField('call') # pdb.set_trace() patterns = syslx.patterns(stmt.attrs) or patterns label = params.epfmt( epname=label, human='human' if human else '', human_sender='human sender' if human_sender else '', needs_int='needs_int' if needs_int else '', args=', '.join(p.name for p in stmt.call.arg), patterns=', '.join(sorted(patterns)), **diagutil.attr_fmt_vars(stmt.attrs)).replace('\n', r'\n') if not ((human and sender == '[') or cron): ep_patterns = syslx.patterns(endpt.attrs) icon = '<&timer> ' if 'cron' in ep_patterns else '' # pdb.set_trace() write('{}->{} : {}{}', sender, agent, icon, label) if log_integration and stmt: log_integration(app=from_app, stmt=stmt, patterns=patterns) payload = syslalgo.return_payload(endpt.stmt) calling_self = from_app and syslx.fmt_app_name( from_app.name) == appname if not calling_self and not payload and deactivate: deactivate() if len(endpt.stmt): hit_blackbox = False for (upto, comment) in itertools.chain(uptos.iteritems(), already_visited.keys()): # Compare the common prefix of the current endpt and upto. upto_parts = upto.split(' <- ') if [appname, epname][:len(upto_parts)] == upto_parts: hit_blackbox = True if payload: write.activate(agent) if comment is not None: write('note over {}: {}', agent, comment or 'see below') else: if comment is not None: write('note {}: {}', 'left' if sender > agent else 'right', comment or 'see below') if payload: write('{}<--{} : {}', sender, agent, payload) write.deactivate(agent) break if not hit_blackbox: with write.activated(agent, human or cron) as deactivate: visiting = (appname + ' <- ' + epname, None) #'see above') already_visited[visiting] += 1 try: return visit_stmts(endpt.stmt, deactivate, True) finally: already_visited[visiting] -= 1 if not already_visited[visiting]: del already_visited[visiting] with write.uml(): #write('scale max 8192 height') if params.title: write('title {}', params.title) app_eps = [ re.match(r'(.*?)\s*<-\s*(.*?)(?:\s*\[upto\s+(.*)\])*$', endpt).groups() for endpt in params.endpoints ] # Treat each endpoint as a blackbox for the other endpoints. uptos = {appname + ' <- ' + epname for (appname, epname, _) in app_eps} # Global blackboxes blackboxes = {app: comment for (app, comment) in blackboxes} for (appname, epname, upto) in app_eps: write('== {} <- {} ==', appname, epname) bbs = blackboxes.copy() for bbox in ({upto} if upto else set()) | uptos - {appname + ' <- ' + epname}: bbs[bbox] = 'see below' already_visited.clear() visit_endpoint(None, appname, epname, bbs, []) write.deactivate_all() for (_, var) in sorted(var_names): write.head('{}', var) return str(write)