dynare/doc/manual/utils/dynare_dom.py

386 lines
13 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (C) 2018-2019 Dynare Team
#
# This file is part of Dynare.
#
# Dynare is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Dynare is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Dynare. If not, see <https://www.gnu.org/licenses/>.
"""
sphinx.domains.dynare
~~~~~~~~~~~~~~~~~~~~~
The Dynare domain.
Loosely based on the JavaScript domain from the Sphinx team and the CoffeScript domain
by Stephen Sugden (available at sphinx-contrib)
"""
import re
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from sphinx import addnodes
from sphinx.domains import Domain, ObjType
from sphinx.locale import _
from sphinx.directives import ObjectDescription
from sphinx.roles import XRefRole
from sphinx.util.nodes import make_refnode
from sphinx.util.docfields import Field, GroupedField, TypedField
############### Dynare Object types #######################
class DynObject(ObjectDescription):
has_arguments = True
display_prefix = None
allow_nesting = False
def handle_signature(self, sig, signode):
sig = sig.strip()
# Some variable/parameter declarations combine spaces and parenthesis
# in a strange way, so they are treated separately
if sig.startswith(('var V','varexo V','varexo_det V','parameters P','model_comparison F')):
member = sig[:sig.index(' ')]
arglist = sig[sig.index(' '):]
# General cases
elif '(' in sig:
member = sig[:sig.index('(')]
arglist = sig[sig.index('('):]
arglist = arglist.strip()
elif ' ' in sig:
member = sig[:sig.index(' ')]
arglist = sig[sig.index(' '):]
else:
member = sig
arglist = None
prefix = self.env.ref_context.get('dynare:object', None)
name = member.strip()
fullname = name
signode['object'] = prefix
signode['fullname'] = fullname
if self.display_prefix:
signode += addnodes.desc_annotation(self.display_prefix, self.display_prefix)
signode += addnodes.desc_name(name, name)
if self.has_arguments:
if not arglist:
signode += addnodes.desc_parameterlist()
else:
signode += addnodes.desc_addname(arglist,arglist)
return fullname, prefix
def add_target_and_index(self, name_obj, sig, signode):
fullname = name_obj[0]
if fullname not in self.state.document.ids:
signode['names'].append(fullname)
signode['ids'].append(fullname)
signode['first'] = not self.names
self.state.document.note_explicit_target(signode)
objects = self.env.domaindata['dynare']['objects']
if fullname in objects:
self.state_machine.reporter.warning(
'duplicate object description of %s, ' % fullname +
'other instance in ' +
self.env.doc2path(objects[fullname][0]),line=self.lineno)
objects[fullname] = (self.env.docname, self.objtype)
indextext = self.get_index_text(fullname,name_obj)
if indextext:
self.indexnode['entries'].append(('single', indextext, fullname,'', None))
def get_index_text(self, objectname, name_obj):
name, obj = name_obj
regexp = re.compile(r'\s*=\s*')
if bool(regexp.search(name)):
aux, name = re.compile(r'\s*=\s*').split(name)
if self.objtype == 'function':
return _('%s (function)') % name
elif self.objtype == 'class':
return _('%s (class)') % name
elif self.objtype == 'datesmethod':
return _('%s (dates method)') % name
elif self.objtype == 'dseriesmethod':
return _('%s (dseries method)') % name
elif self.objtype == 'x13method':
return _('%s (x13 method)') % name
elif self.objtype == 'reportingmethod':
return _('%s (reporting method)') % name
elif self.objtype == 'matcomm':
return _('%s (MATLAB command)') % name
elif self.objtype == 'command':
return _('%s (command)') % name
elif self.objtype == 'block':
return _('%s (block)') % name
elif self.objtype == 'confblock':
name = name[1:-1]
return _('%s (config block)') % name
elif self.objtype == 'macrodir':
name = name[2:]
return _('%s (macro directive)') % name
class DynCallable(DynObject):
has_arguments = True
doc_field_types = [
TypedField('arguments', label=_('Arguments'),
names=('argument', 'arg', 'parameter', 'param'),
typerolename='func', typenames=('paramtype', 'type')),
Field('returnvalue', label=_('Returns'), has_arg=False,
names=('returns', 'return')),
Field('returntype', label=_('Return type'), has_arg=False,
names=('rtype',)),
Field('example', label=_('Example'), has_arg=False,
names=('ex',)),
]
class DynClass(DynObject):
has_arguments = False
display_prefix = 'Dynare class: '
allow_nesting = True
doc_field_types = [
TypedField('members', label=_('Members'),
names=('argument', 'arg', ),
typerolename='func', typenames=('type', )),
Field('example', label=_('Example'), has_arg=False,
names=('ex',)),
]
class DynFunction(DynCallable):
display_prefix = 'Function: '
allow_nesting = True
class DatesMethod(DynCallable):
display_prefix = 'Method: '
allow_nesting = True
class DseriesMethod(DynCallable):
display_prefix = 'Method: '
allow_nesting = True
class X13Method(DynCallable):
display_prefix = 'Method: '
allow_nesting = True
class ReportingMethod(DynCallable):
display_prefix = 'Method: '
allow_nesting = True
class MatComm(DynCallable):
display_prefix = 'MATLAB/Octave command: '
allow_nesting = False
class DynComm(DynCallable):
display_prefix = 'Command: '
allow_nesting = False
class DynBlock(DynCallable):
display_prefix = 'Block: '
allow_nesting = False
class DynConfBlock(DynCallable):
display_prefix = 'Configuration block: '
has_arguments = False
allow_nesting = False
class DynMacroDir(DynCallable):
display_prefix = 'Macro directive: '
allow_nesting = False
class Constructor(DynCallable):
display_prefix = 'Constructor: '
allow_nesting = False
class DynSimpleObject(ObjectDescription):
has_arguments = False
allow_nesting = False
def handle_signature(self, sig, signode):
sig = sig.strip()
member = sig
arglist = None
prefix = self.env.ref_context.get('dynare:object', None)
name = member
fullname = name
signode['object'] = prefix
signode['fullname'] = fullname
if self.display_prefix:
signode += addnodes.desc_annotation(self.display_prefix, self.display_prefix)
signode += addnodes.desc_name(name, name)
return fullname, prefix
def add_target_and_index(self, name_obj, sig, signode):
fullname = name_obj[0]
if fullname not in self.state.document.ids:
signode['names'].append(fullname)
signode['ids'].append(fullname)
signode['first'] = not self.names
self.state.document.note_explicit_target(signode)
objects = self.env.domaindata['dynare']['objects']
if fullname in objects:
self.state_machine.reporter.warning(
'duplicate object description of %s, ' % fullname +
'other instance in ' +
self.env.doc2path(objects[fullname][0]), line=self.lineno)
objects[fullname] = self.env.docname, self.objtype
indextext = self.get_index_text(fullname,name_obj)
if indextext:
self.indexnode['entries'].append(('single', indextext, fullname,'', None))
def get_index_text(self, objectname, name_obj):
name, obj = name_obj
if self.objtype == 'construct':
name, rest = name.split(' ',1)
return _('%s (constructor)') % name
elif self.objtype == 'matvar':
return _('%s (MATLAB variable)') % name
elif self.objtype == 'specvar':
return _('%s (special variable)') % name
elif self.objtype == 'operator':
endsig = name.find(' ')
name = name[0:endsig]
return _('%s (operator)') % name
elif self.objtype == 'constant':
return _('%s (constant)') % name
class MatlabVar(DynSimpleObject):
display_prefix = 'MATLAB/Octave variable: '
allow_nesting = False
class SpecialVar(MatlabVar):
display_prefix = 'Special variable: '
class Operator(MatlabVar):
display_prefix = 'Operator: '
class Constant(MatlabVar):
display_prefix = 'Constant: '
class Option(MatlabVar):
display_prefix = None
############## Cross-referencing ####################
class DynareXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target):
refnode['dynare:object'] = env.ref_context.get('dynare:object')
return title, target
############### Dynare domain #######################
class DynareDomain(Domain):
name = 'dynare'
label = 'Dynare'
object_types = {
'function': ObjType(_('function'), 'func'),
'datesmethod': ObjType(_('method'), 'datmeth'),
'dseriesmethod': ObjType(_('method'), 'dsermeth'),
'x13method': ObjType(_('method'), 'x13meth'),
'reportingmethod': ObjType(_('method'), 'repmeth'),
'matcomm': ObjType(_('matlab command'), 'mcomm'),
'command': ObjType(_('command'), 'comm'),
'class': ObjType(_('class'), 'class'),
'block': ObjType(_('block'), 'bck'),
'confblock': ObjType(_('config block'), 'cbck'),
'macrodir': ObjType(_('macro directive'), 'mdir'),
'construct': ObjType(_('constructor'), 'cstr'),
'matvar': ObjType(_('matlab variable'), 'mvar'),
'specvar': ObjType(_('special variable'), 'svar'),
'operator': ObjType(_('operator'), 'op'),
'constant': ObjType(_('constant'), 'const'),
'option': ObjType(_('option'), 'opt'),
}
directives = {
'function': DynFunction,
'datesmethod': DatesMethod,
'dseriesmethod': DseriesMethod,
'x13method': X13Method,
'reportingmethod': ReportingMethod,
'matcomm': MatComm,
'command': DynComm,
'class': DynClass,
'block': DynBlock,
'confblock': DynConfBlock,
'macrodir': DynMacroDir,
'construct': Constructor,
'matvar': MatlabVar,
'specvar': SpecialVar,
'operator': Operator,
'constant': Constant,
'option': Option,
}
roles = {
'func': DynareXRefRole(),
'datmeth': DynareXRefRole(),
'dsermeth': DynareXRefRole(),
'x13meth': DynareXRefRole(),
'repmeth': DynareXRefRole(),
'mcomm': DynareXRefRole(),
'comm': DynareXRefRole(),
'class': DynareXRefRole(),
'bck': DynareXRefRole(),
'cbck': DynareXRefRole(),
'mdir': DynareXRefRole(),
'cstr': DynareXRefRole(),
'mvar': DynareXRefRole(),
'svar': DynareXRefRole(),
'op': DynareXRefRole(),
'const': DynareXRefRole(),
'opt': DynareXRefRole(),
}
initial_data = {
'objects': {},
}
def clear_doc(self, docname):
for fullname, (fn, _l) in list(self.data['objects'].items()):
if fn == docname:
del self.data['objects'][fullname]
def find_obj(self, env, obj, name, typ, searchorder=0):
objects = self.data['objects']
newname = name # None
return newname, objects.get(newname)
def merge_domaindata(self, docnames, otherdata):
for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames:
self.data['objects'][fullname] = (fn, objtype)
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
objectname = node.get('dynare:object')
searchorder = node.hasattr('refspecific') and 1 or 0
name, obj = self.find_obj(env, objectname, target, typ, searchorder)
if not obj:
return None
return make_refnode(builder, fromdocname, obj[0], name, contnode, name)
def get_objects(self):
for refname, (docname, type) in list(self.data['objects'].items()):
yield refname, refname, type, docname, refname, 1