#!/usr/bin/python3
_D_='SteeringFirmware::correctCourse'
_Dz='rocket_in_motion'
_Dy='rocket_ready'
_Dx='correct_course'
_Dw='fire_booster'
_Dv='steering_requirement_1'
_Du='booster_requirement_1'
_Dt='steering_firmware'
_Ds='test/macro.py'
_Dr='test/relations_test.sbdl'
_Dq='--custom-directive'
_Dp='template-fill'
_Do='openfmea-csv'
_Dn='elem_type'
_Dm='skinparam linetype ortho\n'
_Dl='{} .{}.{} {}{}\n'
_Dk='\n{}{} "{}" as {} {}{}'
_Dj='Not a file: {}'
_Di='class_type_name'
_Dh='[^a-zA-Z0-9.:_ ]+|(?<=.)\\.$'
_Dg='verification'
_Df='validation'
_De='uncertainty'
_Dd='transient'
_Dc='temperature'
_Db='subsystem'
_Da='structure'
_DZ='stability'
_DY='specification'
_DX='simulation'
_DW='resistance'
_DV='resolution'
_DU='reliability'
_DT='redundancy'
_DS='radiation'
_DR='quality assurance'
_DQ='proportional-integral-derivative'
_DP='performance'
_DO='parameter'
_DN='optimization'
_DM='operating system'
_DL='mechanism'
_DK='measurement'
_DJ='manufacturing'
_DI='management'
_DH='maintenance'
_DG='iteration'
_DF='integration'
_DE='instrumentation'
_DD='inspection'
_DC='implementation'
_DB='identification'
_DA='high voltage'
_D9='heat transfer'
_D8='frequency'
_D7='failure mode and effects analysis'
_D6='evaluation'
_D5='electromagnetic'
_D4='electrical'
_D3='efficiency'
_D2='dimension'
_D1='development'
_D0='degrees of freedom'
_C_='data acquisition'
_Cz='correction'
_Cy='coordinate'
_Cx='controller'
_Cw='configuration'
_Cv='configure'
_Cu='coefficient'
_Ct='center of mass'
_Cs='center of gravity'
_Cr='calculation'
_Cq='auxiliary'
_Cp='architecture'
_Co='approximate'
_Cn='aerodynamics'
_Cm='acceleration'
_Cl='_SPEC_BACKUP'
_Ck='Attributes'
_Cj='declaration'
_Ci='arguments'
_Ch='skinparam NoteBackgroundColor LightSteelBlue\nskinparam ParticipantBackgroundColor LightGrey\nskinparam backgroundColor transparent'
_Cg='multipartite'
_Cf='sbdl_unique_identifier_gen_alphabet'
_Ce='sbdl_unique_identifier_gen_length'
_Cd='profile_output'
_Cc='fmea_cause_extension'
_Cb='openfmea_complex_aspect'
_Ca='openfmea_mode_based_import_relations'
_CZ='openfmea_bidirectional_import_relations'
_CY='openfmea_include_in_template'
_CX='openfmea_fmeadev_url'
_CW='openfmea_content_based_keys'
_CV='openfmea_include_cme_unlinked'
_CU='openfmea_include_cme_orphans'
_CT='openfmea_expand_compoundfields'
_CS='remote_procedure_call_api_endpoint'
_CR='remote_procedure_call_api_key'
_CQ='remote_procedure_call_enable'
_CP='remote_resouce_fetch_enable'
_CO='sbdl_disable_pretty_print'
_CN='sbdl_disable_pretty_print_environ'
_CM='manual_command'
_CL='add_description_bsci_property'
_CK='elision_placeholder'
_CJ='reference_maximum_length'
_CI='usecase_diagram_style'
_CH='state_diagram_descriptions'
_CG='state_diagram_style'
_CF='uml_text_block_wrap_element'
_CE='process_diagram_descriptions'
_CD='function_diagram_aspect'
_CC='function_diagram_descriptions'
_CB='aspect_diagram_descriptions'
_CA='aspect_diagram_element'
_C9='aspect_diagram_aspect'
_C8='requirement_diagram_legend'
_C7='requirement_diagram_style'
_C6='requirement_diagram_title_node_always'
_C5='requirement_diagram_toplevel_depth'
_C4='requirement_diagram_aspect_groups'
_C3='network_diagram_dpi'
_C2='network_diagram_layout'
_C1='network_diagram_labels'
_C0='network_diagram_descriptions'
_B_='network_diagram_height'
_Bz='network_diagram_width'
_By='parser_allow_load_config'
_Bx='parser_allow_custom_directive_import'
_Bw='parser_custom_attributes'
_Bv='parser_allow_preparse_directives'
_Bu='plantuml_remote_render'
_Bt='plantuml_generate_footer'
_Bs='plantuml_output_type'
_Br='plantuml_limit_size'
_Bq='plantuml_command'
_Bp='wsl_auto_correct_paths'
_Bo='sbdl_allow_package_installation'
_Bn='sbdl_custom_sigil_extraction'
_Bm='sbdl_reference_prefix_strip'
_Bl='sbdl_caching_suffix'
_Bk='sbdl_caching_enabled'
_Bj='sbdl_warn_new_config'
_Bi='sbdl_update_package'
_Bh='sbdl_hash_check'
_Bg='sbdl_hash_length'
_Bf='sbdl_ast_pre_hooks'
_Be='sbdl_attribute_passthrough'
_Bd='sbdl_file_extension'
_Bc='sbdl_element_color_map'
_Bb='sbdl_grammar_write_statement_separator'
_Ba='sbdl_grammar_custom_comment'
_BZ='sbdl_grammar_custom_noop'
_BY='sbdl_grammar_custom_end'
_BX='sbdl_grammar_custom_begin'
_BW='sbdl_grammar_custom_suffix'
_BV='sbdl_grammar_custom_prefix'
_BU='tracemalloc'
_BT='rocket_launch'
_BS='launch_protocol'
_BR='system_requirement_1'
_BQ='rocket_steering'
_BP='rocket_system'
_BO='test/function_test.sbdl'
_BN='--custom-mode'
_BM='--mode'
_BL='from:yaml-tree'
_BK='usecase-diagram'
_BJ='state-diagram'
_BI='function-process-diagram'
_BH='function-diagram'
_BG='network-diagram'
_BF='requirement-diagram'
_BE='element-diagram'
_BD='aspect-diagram'
_BC='from:openfmea'
_BB='openfmea-portfolio'
_BA='yaml-tree'
_B9='compile'
_B8='format'
_B7='<<{}>>'
_B6='config'
_B5='directives'
_B4='State'
_B3='Dynamic'
_B2='Design'
_B1='Testing'
_B0='relations'
_A_='relation'
_Az='Tokens'
_Ay='input'
_Ax='output'
_Aw='actor'
_Av='identifier'
_Au='reference'
_At='^\\s*{}\\s*{}.*?{}\\s*$'
_As='category'
_Ar='**LOCKED_CONFIG_KEYS'
_Aq='linux'
_Ap='openfmea_compoundfield_separator'
_Ao='usecase_diagram_descriptions'
_An='function_diagram_style'
_Am='aspect_diagram_style'
_Al='network_diagram_hide_types'
_Ak='network_diagram_node_size'
_Aj='parser_allow_global_directive_definition'
_Ai='parser_allow_directive_definition'
_Ah='plantuml_remote_render_uri'
_Ag='plantuml_ortho_line_routing'
_Af='plantuml_layout_tweaks'
_Ae='plantuml_raw_output'
_Ad='prolog_trace_file'
_Ac='sbdl_output_meta_elements'
_Ab='sbdl_enable_embedded_functions'
_Aa='sbdl_caching_dependent_files'
_AZ='sbdl_show_stack_trace'
_AY='sbdl_output_reference'
_AX='sbdl_grammar_use_formal'
_AW='sbdl_grammar_write_attribute_separator'
_AV='test/example.cpp'
_AU='test/example.sbdl'
_AT='from:csv-matrix'
_AS='from:json-tree'
_AR='json-tree'
_AQ='csv-matrix'
_AP='openfmea'
_AO='{} ({})'
_AN='OpenFMEA'
_AM='left'
_AL='right'
_AK='__main__'
_AJ='__parser_hints__'
_AI='Types'
_AH='Statements'
_AG='occurrence_post'
_AF='detectability_post'
_AE='customtype'
_AD='function'
_AC='parser_allow_directives'
_AB='prolog_result_output'
_AA='sbdl_import_allows_recurse'
_A9='sbdl_sort_output_by_identifier'
_A8='sbdl_sort_output_by_type'
_A7='ignore'
_A6='action'
_A5='@startuml\n{style}\n\n{header}\n{content}\n@enduml\n'
_A4='title "{}"\n'
_A3='system'
_A2='Unknown'
_A1='properties'
_A0='occurrence'
_z='severity'
_y='detectability'
_x='interface'
_w='profile_snapshots'
_v='profile_output_mem'
_u='process_diagram_swimlanes'
_t='parser_imported_files'
_s='sbdl_grammar_native_freeform'
_r='id'
_q='fmea'
_p='Architecture'
_o='prolog_enabled'
_n='sbdl_ast_post_hooks'
_m='meta-data'
_l='action_detection'
_k='detection'
_j='\n\n'
_i='control'
_h='categories'
_g='['
_f='aspect'
_e='::'
_d='uml_text_block_wrap'
_c='sbdl'
_b='types'
_a='utf-8'
_Z='\\'
_Y='universal_diagram_style'
_X='effect'
_W='requirement'
_V='_'
_U='parser_global_directives'
_T='sbdl_output_pretty_format'
_S='FMEA'
_R='*'
_Q='cause'
_P='.'
_O='description'
_N='  '
_M='type'
_L=','
_K='test/test.sbdl'
_J='mode'
_I=' '
_H='\\n'
_G='-m'
_F=', '
_E='-o'
_D='\n'
_C=False
_B=None
_A=True
import argparse,base64,datetime,copy,csv,functools,getpass,hashlib,importlib,io,json,os,pathlib,pickle,platform,re,shlex,string,subprocess,sys,tarfile,tempfile,textwrap,threading,time,traceback,types,unittest,urllib.request,urllib.parse,uuid,warnings,zlib,zipfile
warnings.filterwarnings(_A7,category=DeprecationWarning)
__NAME=_c
__VERSION='1.22.5'
__VERSION_DEV='deb3e54'
__VERSION_DSL='26.4'
__VERSION_REST_API='1.1.0'
__AUTHOR='contact@sbdl.dev'
__URL='https://sbdl.dev'
__LOGO=''
__HELP_TEXT=''
_CONFIG_DATA={_BV:'_sbdl\\s?\\(',_BW:'\\)$',_BX:'_sbdl_block_begin',_BY:'_sbdl_block_end',_BZ:'^\\.\\.\\.$',_Ba:_B,_AW:_A,_Bb:_C,_s:_A,_AX:_C,_Bc:{},_T:_C,_AY:_A,_Bd:'.sbdl',_Be:_R,_Bf:[],_n:[],_Bg:5,_Bh:_A,_Bi:'sbdl-package.tar.gz',_AZ:_C,_A8:_C,_A9:_C,_Bj:_A,_AA:_A,_Bk:_C,_Bl:'.sbdlc',_Aa:{},_Bm:'',_Ab:_A,_Bn:_B,_Bo:_A,_Ac:_C,_Bp:_A,_o:_A,_AB:_C,_Ad:_B,_Ae:_C,_Bq:'plantuml',_Br:65536,_Bs:'png',_Bt:_A,_Af:_C,_Ag:_C,_Bu:_C,_Ah:'https://www.plantuml.com/plantuml',_AC:_A,_Bv:_A,_Ai:_A,_Aj:_A,_U:{},_Bw:_A,_Bx:_A,_By:_A,_t:{},_Bz:38,_B_:22,_C0:_C,_C1:_A,_Ak:20000,_C2:_Cg,_C3:200,_Al:'',_C4:_C,_C5:2,_C6:_C,_Y:"!pragma layout newlayouter\nskinparam dpi 200\nskinparam backgroundColor transparent\n'skinparam monochrome reverse\n\n",_C7:'\n<style>\n  node {\n      HorizontalAlignment center\n      MaximumWidth 150\n  }\n  wbsDiagram {\n    Linecolor black\n    arrow {\n      LineColor black\n    }\n    .toplevel {\n    }\n    .source {\n      LineStyle 2\n      RoundCorner 10\n    }\n    .aspect {\n      LineStyle 8.0;3.0\n      LineColor lightgray\n      BackgroundColor lightgray\n      LineThickness 1.0\n      RoundCorner 0\n      Shadowing 0.0\n    }\n  }\n</style>',_C8:_B,_Am:'hide circle',_C9:'frame',_CA:'class',_CB:_C,_An:_Ch,_CC:_C,_CD:'participant','process_diagram_style':_Ch,_CE:_C,_u:_C,_d:15,_CF:40,_CG:'',_CH:_C,_CI:'',_Ao:_C,_CJ:0,_CK:'[...]',_CL:_C,_CM:'less -R'if os.name=='posix'else'',_CN:'SBDL_NO_PRETTY',_CO:_C,_CP:_A,_CQ:_C,_CR:'1879da28-0fc2-4f1d-8ff1-407f4c070c44',_CS:'{base}/{endp}'.format(base=__URL,endp='rest.api'),_Ap:'; ',_CT:'',_CU:_A,_CV:_A,_CW:_C,_CX:'https://fmea.dev',_CY:_A,_CZ:_C,_Ca:_C,_Cb:_C,_Cc:_V,_Cd:_B,_v:_B,_w:{},_Ce:8,_Cf:'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'}
def name():return __NAME
def version():return __VERSION
def version_dev():return __VERSION_DEV
def version_dsl():return __VERSION_DSL
def version_rest_api():return __VERSION_REST_API
def author():return __AUTHOR
def url():return __URL
def allow_pretty_print():return not get_config_value(_CN)in os.environ and not get_config_value(_CO,number=_A)
def logo():return __LOGO if allow_pretty_print()else''
def help_text():return __HELP_TEXT
def current_os():
	if sys.platform.startswith('win'):return'windows'
	elif sys.platform.startswith(_Aq):return _Aq
	elif sys.platform.startswith('darwin'):return'macos'
	else:return
def get_config_value(key,raise_on_missing=_A,default=_B,number=_B):
	global _CONFIG_DATA;config_data=_CONFIG_DATA;result=default
	if key in config_data:
		result=config_data[key];env_var_key=f"sbdl_{key}".upper();env_var_value=os.getenv(env_var_key)
		if env_var_value is not _B:result=env_var_value
	else:
		message='Missing configuration key requested: {}'.format(key)
		if raise_on_missing:raise Exception(message)
	if number is not _B and result is not _B:
		if number:float_val=float(result);int_val=int(result);result=int_val if int_val==float_val else float_val
		else:result=str(result)
	return result
def config_key_locked(key):lock_key=_Ar;return key in get_config_value(lock_key,raise_on_missing=_C,default={})
def lock_config_key(key):
	lock_key=_Ar
	if not get_config_value(lock_key,raise_on_missing=_C):set_config_value(lock_key,{},warn_on_new=_C)
	add_to_config_value(lock_key,{key:_A})
def set_config_value(key,data,warn_on_new=_A):
	if not config_key_locked(key):
		if not key in _CONFIG_DATA and get_config_value(_Bj,number=_A)and warn_on_new:f_print('WARNING: adding new key ({}) to config registry'.format(key),warning=_A)
		_CONFIG_DATA[key]=data
	else:debug_out(f'Trying to set locked config key:: "{key}"')
def use_config_data(new_config_data,skip_keys=_B):
	global _CONFIG_DATA;lock_key=_Ar;skip_keys_l=list(get_config_value(lock_key,raise_on_missing=_C,default={}).keys())
	if skip_keys:skip_keys_l.extend(skip_keys)
	for skip_key in skip_keys_l:
		if skip_key in new_config_data:new_config_data.pop(skip_key)
	_CONFIG_DATA={**_CONFIG_DATA,**new_config_data}
def load_config(config_path,skip_keys=_B):
	debug_out('Reading config: {}'.format(config_path))
	with open_input_file(config_path)as configfile:use_config_data(json.load(configfile),skip_keys)
def add_to_config_value(key,data):
	if not config_key_locked(key):
		if isinstance(_CONFIG_DATA[key],dict)and isinstance(data,dict):_CONFIG_DATA[key]|=data
		elif isinstance(_CONFIG_DATA[key],list):
			if isinstance(data,list):_CONFIG_DATA[key].extend(data)
			else:_CONFIG_DATA[key].append(data)
		else:raise Exception('Cannot add to config value that is not a list or dict')
	else:debug_out(f'Trying to set locked config key:: "{key}"')
def init():global _CONFIG_DATA_DEFAULT;_CONFIG_DATA_DEFAULT=copy.deepcopy(_CONFIG_DATA)
def reset():
	for new_type_name in set(SBDL_Semantics.custom_types.keys()):del SBDL_Semantics.custom_types[new_type_name];del SBDL_Semantics.type_properties[new_type_name];del SBDL_Semantics.type_links[new_type_name]
	global _CONFIG_DATA;_CONFIG_DATA=copy.deepcopy(_CONFIG_DATA_DEFAULT)
def print_null(*content,end=_B,error=_C,verbose=_A,do_debug=_C,debug=_C,do_warning=_A,warning=_C,opts=_B,prefix=_B):0
def safe_stdout_endcode(in_string):
	result=in_string
	if isinstance(result,str):result=result.encode(sys.stdout.encoding,errors=_A7).decode(sys.stdout.encoding)
	return result
def get_date_string():return datetime.datetime.fromtimestamp(time.time(),datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%S')
def get_user_string():
	result='UNKNOWN-USER'
	try:result=getpass.getuser()
	except Exception as _:pass
	return result
def exec_external(command_list):debug_out('EXECUTE_EXTERNAL: '+str(command_list));exec_result=subprocess.run(command_list,capture_output=_A,text=_A,check=_C);return exec_result.returncode,exec_result.stdout,exec_result.stderr
def lazy_import(package_name,custom_message=_B,install_missing=_A):
	imported_package=_B;error_msg=_B
	try:imported_package=importlib.import_module(package_name)
	except Exception as _:
		error_msg='ERROR: trying to import package "{}" -- is it installed the Python environment?'.format(package_name)
		if custom_message:error_msg+=_I+custom_message
	if error_msg:
		if install_missing and get_config_value(_Bo,number=_A):
			f_print(f'ERROR: package "{package_name}" is missing.',error=_A);do_install=input('Attempt to install automatically via pip? Y/[N]\n').strip().lower()
			if do_install=='y':
				ret,stdout,stderr=install_pip(package_name,force=_A)
				if ret!=0:f_print(stdout,error=_A);f_print(stderr,error=_A)
				else:f_print(f'Successfully installed "{package_name}"')
			return lazy_import(package_name,custom_message,install_missing=_C)
		else:raise Exception(error_msg)
	return imported_package
def fix_wsl_path(path):
	result=path
	if get_config_value(_Bp,number=_A):
		if result.startswith(_Z)and not result.startswith('\\\\'):result='\\\\wsl$'+result;result=str(pathlib.Path(result))
	return result
def rest_api_call(function_name,arguments_dict,print_l=print_null):
	D='result';C='api_version';B='errors';A='api_name';API_END_POINT=get_config_value(_CS);result_data={}
	def create_rest_object(command,arguments_dict):return{A:name(),C:version_rest_api(),'api_key':get_config_value(_CR),_AD:command,_Ci:{**arguments_dict}}
	def report_errors(response_struct):
		print_l('REST_API_CALL:: {} <-- {}'.format(API_END_POINT,response_struct),debug=_A)
		if B in response_struct and isinstance(response_struct[B],list)and len(response_struct[B])>0:print_l('REST_API_CALL:: Response errors: \n  {}'.format(_F.join(response_struct[B])),error=_A)
	if get_config_value(_CQ,number=_A):
		rest_object=create_rest_object(function_name,arguments_dict);print_l('REST_API_CALL:: {} --> {}'.format(API_END_POINT,rest_object),debug=_A);request_point=urllib.request.Request(API_END_POINT);request_point.add_header('Content-Type','application/json; charset=utf-8');request_data=json.dumps(rest_object).encode(_a);request_point.add_header('Content-Length',len(request_data))
		try:rest_response=urllib.request.urlopen(request_point,request_data)
		except(urllib.error.HTTPError,urllib.error.URLError)as e:print_l('REST_API_CALL:: Error: {}'.format(e),error=_A);rest_response=e
		if rest_response.status!=200:
			print_l('REST_API_CALL:: Response status bad: {} ({})'.format(rest_response.status,API_END_POINT),error=_A)
			try:report_errors(json.load(rest_response))
			except Exception as _:pass
			raise Exception('Remote Procedure Call failed')
		else:
			response_struct=json.load(rest_response);print_l('REST_API_CALL:: Response data: {}'.format(response_struct),debug=_A)
			if A in response_struct and C in response_struct and B in response_struct and D in response_struct:
				if response_struct[C]!=version_rest_api():print_l('REST_API_CALL:: REST API Versions differ: {}(remote) != {}(local)'.format(response_struct[C],version_rest_api()),debug=_A)
				if response_struct[A]!=rest_object[A]:print_l('REST_API_CALL:: REST API Names differ: {}(remote) != {}(local)'.format(response_struct[A],rest_object[A]),error=_A)
				error_num=len(response_struct[B])
				if error_num>0:report_errors(response_struct);raise Exception('Remote Procedure Call response reported {} errors'.format(error_num))
				else:result_data=response_struct[D]
			else:raise Exception('JSON response structure appears invalid')
	else:raise Exception('Remote Procedure Calls are disabled')
	return result_data
def rest_api_call_ping(print_l):print_l(rest_api_call('ping',{'date':get_date_string()},print_l=print_l));return 0
def lsp_get_compiler_diagnostics(file_path,text,_,__):
	errors=[];non_errors=[];sbdl_elements={};file_text_lines=[line if line.strip()!=''else _D for line in text.splitlines()]
	def intercept_file_opener(file_opener_path):
		if file_opener_path==file_path:return list_file_object(file_text_lines)
		else:return open_input_file(file_opener_path)
	def intercept_f_print(*content,end=_B,error=_C,verbose=_A,do_debug=_C,debug=_C,do_warning=_A,warning=_C,opts=_B,prefix=_B):
		flat_content=_F.join(content)
		if error:errors.append(flat_content)
		else:non_errors.append(flat_content)
	def list_from_error(compiler_error):
		error_split=compiler_error.split(_e);line_number=0;start_column_number=0;end_column_number=0;error_message=''
		if len(error_split)>=2:
			reference_split=error_split[0].rsplit(':',1)
			if len(reference_split)==2:
				try:
					error_file_path=str(reference_split[0].strip())
					if error_file_path==file_path:line_number=int(reference_split[1].strip())-1;end_column_number=len(file_text_lines[line_number])-1
				except ValueError:pass
			error_message='; '.join(error_split[1:]).strip()
		else:error_message=compiler_error
		return[line_number,start_column_number,end_column_number,error_message]
	reset();set_config_value(_AA,0);set_config_value(_Ac,1);parser_elements,reading_errors=aggregate_all_parser_elements_from_files([file_path],file_opener=intercept_file_opener,do_recurse=_C,do_hidden=_C,print_l=intercept_f_print);sbdl_ast=SBDL_AST(parser_elements);parsing_errors=sbdl_ast.check_parsing(print_l=intercept_f_print);sbdl_elements,element_errors=sbdl_ast.sbdl_elements(print_l=intercept_f_print);valid,model_errors=sbdl_ast.validate_sbdl_elements(sbdl_elements,print_l=intercept_f_print);total_errors=reading_errors+parsing_errors+element_errors+model_errors+valid;return sbdl_elements,[list_from_error(e)for e in errors],total_errors
def lsp_get_symbol_map_at(uri,line,element_cache,__):
	symbol_map={}
	for(_,element)in element_cache.items():
		file_uri=element.definition().parser_element().source();line_num=element.definition().parser_element().index()
		if uri.strip()==file_uri.strip()and line_num==line+1:
			scope_map=element.get_parser_hint(SBDL_Parser.Statements.scope)
			if scope_map:symbol_map|=scope_map
	return symbol_map
def lsp_get_symbol(_,line,col,text,__,___):
	file_content=text.splitlines();symbol_name=_B;symbol_seps=[SBDL_Parser.Attributes.separator,SBDL_Parser.Tokens.general_separator]
	def is_symbol_separator(string_in,position):
		result=string_in[position].isspace()
		if not result:
			for symbol_sep in symbol_seps:
				if string_in[position:position+len(symbol_sep)]==symbol_sep:result=_A;break
		return result
	def get_word_at(string_in,position):
		word=''
		while position<len(string_in):
			if is_symbol_separator(string_in,position):break
			else:word+=string_in[position]
			position+=1
		return word
	def get_word_at_mid(string_in,position):
		search_position=0;current_word=_B;current_word_pos=_B;update_word=_A
		while search_position<len(string_in):
			if update_word:current_word=get_word_at(string_in,search_position);current_word_pos=search_position;update_word=_C
			if is_symbol_separator(string_in,search_position):update_word=_A
			if search_position==position:break
			search_position+=1
		return current_word,current_word_pos
	if line<len(file_content):symbol_name,_=get_word_at_mid(file_content[line],col)
	return symbol_name
def lsp_get_elem_at(uri,line,_,__,element_cache,___):
	result_element=_B;line_number=line+1
	for(_,element)in element_cache.items():
		elem_p=element.definition().parser_element()
		if elem_p.source()==uri:
			elem_start_line=elem_p.index();elem_end_line=elem_start_line
			if elem_p.indexes():elem_end_line=elem_p.indexes()[-1][1]
			if line_number>=elem_start_line and line_number<=elem_end_line:result_element=element
	return result_element
def lsp_get_definition_location(uri,line,col,text,element_cache,print_l):
	definition_location=_B
	def import_path_extract(import_symbol):
		import_path=_B;import_symbol=import_symbol.strip()
		if SBDL_Parser.is_string_directive(import_symbol):
			open_s=SBDL_Parser.Tokens.macro_delimeters[0];close_s=SBDL_Parser.Tokens.macro_delimeters[1]
			if import_symbol.startswith(open_s)and import_symbol.endswith(close_s):
				import_symbol=import_symbol[len(open_s):-len(close_s)]
				if import_symbol.startswith(SBDL_Parser.Tokens.macro_variable):
					import_symbol=import_symbol[len(SBDL_Parser.Tokens.macro_variable):];import_symbols=import_symbol.split(SBDL_Parser.Tokens.macro_index_separator,1)
					if len(import_symbols)==2 and import_symbols[0]==SBDL_Parser.Macros.import_sbdl:import_path=str(pathlib.Path(uri).parent/pathlib.Path(import_symbols[1]))
		else:
			element=lsp_get_elem_at(uri,line,col,text,element_cache,print_l)
			if element and element.is_a(SBDL_Parser.Tokens.depends):import_symbol_clean=import_symbol.removeprefix(SBDL_Parser.Tokens.declaration_attribute_delimeter[0]).removesuffix(SBDL_Parser.Tokens.declaration_attribute_delimeter[1]).removeprefix(SBDL_Parser.Tokens.raw_content_delimiters[0]).removesuffix(SBDL_Parser.Tokens.raw_content_delimiters[1]);import_path=str(pathlib.Path(uri).parent/pathlib.Path(import_symbol_clean))
		return import_path
	symbol_name=lsp_get_symbol(uri,line,col,text,element_cache,print_l);symbol_map=lsp_get_symbol_map_at(uri,line,element_cache,print_l)
	if symbol_name in symbol_map:symbol_name=symbol_map[symbol_name]
	if symbol_name:
		if(import_path:=import_path_extract(symbol_name)):definition_location=[import_path,0,symbol_name]
		else:
			symbol_name,_=SBDL_Parser.identifier_stereotype(symbol_name)
			if symbol_name in element_cache:
				file_uri=element_cache[symbol_name].definition().parser_element().source()
				try:result_line=int(element_cache[symbol_name].definition().parser_element().index())-1;definition_location=[file_uri,result_line,symbol_name]
				except ValueError:pass
	return definition_location
def lsp_get_reference_locations(uri,line,col,text,element_cache,print_l):
	definition_locations=[];symbol_name=lsp_get_symbol(uri,line,col,text,element_cache,print_l);symbol_map=lsp_get_symbol_map_at(uri,line,element_cache,print_l)
	if symbol_name in symbol_map:symbol_name=symbol_map[symbol_name]
	if symbol_name:
		symbol_name,_=SBDL_Parser.identifier_stereotype(symbol_name)
		for(_,element)in element_cache.items():
			if symbol_name in[x.identifier()for x in element.relations()]+[x.identifier()for x in element.children()]+[x.identifier()for x in element.parents()]:
				file_uri=element.definition().parser_element().source()
				try:result_line=int(element.definition().parser_element().index())-1;definition_locations.append([file_uri,result_line,symbol_name])
				except ValueError:pass
	return definition_locations
def lsp_element_info(element_identifier,elements,_):
	result=''
	if element_identifier in elements:element=elements[element_identifier];result=f"""
**{element.identifier()}** [*type:* **{"->".join(element.types())}**]

*Description*: {element.description()}

*Properties*: {_F.join(element.properties())}

*Relations*: {_F.join([x.identifier()for x in element.relations()])}

*Location*: {element.reference()}

"""
	elif element_identifier in SBDL_Semantics.type_information:result=f"""
*Type*: **{element_identifier}**

*Category*: **{SBDL_Semantics.type_information[element_identifier][_As]}**

*Description*: {SBDL_Semantics.type_information[element_identifier][_O]}
"""
	elif element_identifier in SBDL_Semantics.property_descriptions:result=f"\n*Property*: **{element_identifier}**\n\n*Description*: {SBDL_Semantics.property_descriptions[element_identifier]}\n"
	elif element_identifier in SBDL_Semantics.keyword_information:result=f"\n*Keyword*: **{element_identifier}**\n\n*Description*: {SBDL_Semantics.keyword_information[element_identifier]}\n"
	return result
def lsp_start(_,__,___,arguments,print_l):
	_=lazy_import('pygls','LSP Mode requires PyGLS to be installed');from pygls.lsp.server import LanguageServer;from lsprotocol.types import InitializeResult,ServerCapabilities,TextDocumentSyncKind,DidOpenTextDocumentParams,DidChangeTextDocumentParams,Diagnostic,DiagnosticSeverity,Range,Position,PublishDiagnosticsParams,Location,Hover,MarkupContent,MarkupKind;element_cache={}
	class SBDL_LSP(LanguageServer):
		def __init__(self,*args,**kwargs):super().__init__(*args,**kwargs);self.diag_timers={}
	sbdl_lsp=SBDL_LSP(name(),version())
	def uri_to_file_path(uri_arg):
		path=pathlib.Path(urllib.parse.unquote(urllib.parse.urlparse(uri_arg).path))
		if sys.platform.startswith('win')and str(path).startswith('/'):path=pathlib.Path(str(path)[1:])
		result_path=str(path)
		if sys.platform.startswith('win'):result_path=result_path.removeprefix(_Z)
		return result_path
	def file_path_to_uri(path):return pathlib.Path(path).resolve().as_uri()
	@sbdl_lsp.feature('initialize')
	def initialize(*_,**__):return InitializeResult(capabilities=ServerCapabilities(text_document_sync=TextDocumentSyncKind.Full))
	def publish_compiler_diagnostics(ls,uri):
		document=ls.workspace.get_text_document(uri);element_data,compiler_diagnostics,_=lsp_get_compiler_diagnostics(uri_to_file_path(uri),document.source,arguments,print_l);element_cache[uri]=element_data;diagnostics=[]
		for compiler_diagnostic in compiler_diagnostics:diagnostics.append(Diagnostic(range=Range(start=Position(compiler_diagnostic[0],compiler_diagnostic[1]),end=Position(compiler_diagnostic[0],compiler_diagnostic[2])),message=compiler_diagnostic[3],severity=DiagnosticSeverity.Error,source=_c))
		ls.text_document_publish_diagnostics(PublishDiagnosticsParams(uri=uri,diagnostics=diagnostics))
	def schedule_diagnostics(ls,uri):
		if uri in ls.diag_timers:ls.diag_timers[uri].cancel()
		timer=threading.Timer(1.5,lambda:ls.thread()(publish_compiler_diagnostics)(ls,uri));ls.diag_timers[uri]=timer;timer.start()
	@sbdl_lsp.feature('textDocument/didOpen')
	def did_open(ls,params):ls.thread()(publish_compiler_diagnostics)(ls,params.text_document.uri)
	@sbdl_lsp.feature('textDocument/didChange')
	def did_change(ls,params):schedule_diagnostics(ls,params.text_document.uri)
	@sbdl_lsp.feature('textDocument/didSave')
	def did_save(ls,params):schedule_diagnostics(ls,params.text_document.uri)
	@sbdl_lsp.feature('textDocument/didClose')
	def did_close(ls,params):
		uri=params.text_document.uri
		if uri in ls.diag_timers:ls.diag_timers[uri].cancel();del ls.diag_timers[uri]
		element_cache.pop(uri,_B)
	@sbdl_lsp.feature('textDocument/definition')
	def definition(ls,params):
		uri=params.text_document.uri
		if uri not in element_cache:return
		symbol=lsp_get_definition_location(uri_to_file_path(uri),params.position.line,params.position.character,ls.workspace.get_text_document(uri).source,element_cache[uri],print_l)
		if not symbol:return
		return Location(uri=file_path_to_uri(symbol[0]),range=Range(start=Position(symbol[1],0),end=Position(symbol[1],999999)))
	@sbdl_lsp.feature('textDocument/references')
	def references(ls,params):
		uri=params.text_document.uri
		if uri not in element_cache:return
		reference_list=lsp_get_reference_locations(uri_to_file_path(uri),params.position.line,params.position.character,ls.workspace.get_text_document(uri).source,element_cache[uri],print_l);locations=[]
		for ref in reference_list:locations.append(Location(uri=file_path_to_uri(ref[0]),range=Range(start=Position(ref[1],0),end=Position(ref[1],999999))))
		return locations
	@sbdl_lsp.feature('textDocument/hover')
	def hover(ls,params):
		uri=params.text_document.uri
		if uri not in element_cache:return
		symbol=lsp_get_symbol(uri_to_file_path(uri),params.position.line,params.position.character,ls.workspace.get_text_document(uri).source,element_cache[uri],print_l)
		if not symbol:return
		symbol,_=SBDL_Parser.identifier_stereotype(symbol);symbol_map=lsp_get_symbol_map_at(uri_to_file_path(uri),params.position.line,element_cache[uri],print_l)
		if symbol in symbol_map:symbol=symbol_map[symbol]
		return Hover(contents=MarkupContent(kind=MarkupKind.Markdown,value=lsp_element_info(symbol,element_cache[uri],print_l)))
	sbdl_lsp.start_io();return 0
class SBDL_Parser:
	class Statements:declaration=_Cj;depends='depends';using='using';scope='scope';operator='operator';customtype=_AE
	class Types:aspect=_f;requirement=_W;mode='fmea:mode';effect='fmea:effect';cause='fmea:cause';current_control='fmea:control';current_detection='fmea:detection';action_control='fmea:action-control';action_detection='fmea:action-detection';test='test';definition='definition';realisation='realisation';function=_AD;event='event';state='state';transition='transition';usecase='usecase';interface=_x;trace='trace';group='group'
	class Tokens:declaration='is';declaration_group_delimeters=['{','}'];declaration_attribute_assign=' {} '.format(declaration);declaration_attribute_delimeter=['"','"'];general_separator=';';raw_content_delimiters=['[[[',']]]'];declaration_attribute_separator=general_separator;statement_separator=general_separator;hashbang='#!sbdl';hashbang_formal='#!sbdl-f';prefix='@sbdl\\s';prefix_block_start='@sbdl\\-begin';prefix_block_end='@sbdl\\-end';comment='#';using='using';depends='depends';scope='scope';customtype=_AE;line_continuation=_Z;replacement_string_default='';replacement_map={_L:'，',';':'；'};declaration_token_rule='\\s+{}\\s+'.format(declaration);declaration_rule=f"^.+?{declaration_token_rule}[^\\\\{declaration_group_delimeters[0]}]+{declaration_group_delimeters[0]}.*?{declaration_group_delimeters[1]}\\s*$";using_rule=_At.format(using,declaration_group_delimeters[0],declaration_group_delimeters[1]);depends_rule=_At.format(depends,declaration_group_delimeters[0],declaration_group_delimeters[1]);scope_rule=_At.format(scope,declaration_group_delimeters[0],declaration_group_delimeters[1]);customtype_rule='^\\s*{}\\s+{}'.format(customtype,declaration_rule.replace('^','',1));macro_delimeters=[_g,']'];macro_variable='@';macro_defer='%';macro_rule='\\{}{}(?:[^\\]\\\\]|\\\\.)*\\{}'.format(macro_delimeters[0],macro_variable,macro_delimeters[1]);macro_index_separator=':';whitespace_list=list(string.whitespace);escape=_Z;stereotype_separator='^';link_operator='||';source_link_operator='~|';operator_rule='\\s*({}|{})\\s*'.format(_Z+_Z.join(link_operator),_Z+_Z.join(source_link_operator));inherit='inherit';compose='compose';hash_separator='~';scope_prefix=_e;stdio_name='-';nested_elements_depth_pop='nest_elems_end';customtype_union=_L;copy_element='copy of'
	class Attributes:description=_O;detectability=_y;detectability_post=_AF;severity=_z;occurrence=_A0;occurrence_post=_AG;separator=_L;parent='parent';child='child';related='related';pragma='pragma';remark='remark';reference=_Au;identifier=_Av;type=_M;types=_b;stereotype='stereotype';actor=_Aw;tag='tag';description_bsci='description_bsci';precondition='precondition';postcondition='postcondition';invariant='invariant';output=_Ax;input=_Ay;custom_prefix='custom:';condition='condition';condition_alternative='alternative';custom_required_attr='property:required';custom_optional_attr='property:optional';custom_optional_link_types='relation:optional';custom_required_link_types='relation:required';custom_validator='validator';custom_required_attr_old='required_property';custom_optional_attr_old='optional_property';custom_link_types_old='relation_type';color='color';control_only='control_only';return_control='return_control';raw_content='raw_content';loop_name='loop';parallel_name='parallel';port_name='port'
	class Macros:self_element='SELF';self_reference='SELF_ID';self_reference_attr='SELF_PROP';abort='ABORT';message='MESSAGE';msg='MSG';date='DATE';user='USER';add='ADD';sub='SUB';equal='EQUAL';concat='CONCAT';instli='INSTLI';showall='SHOW_ALL';rmcom='RMCOM';mkid='MKID';dfp='DFP';dsl_version='DSL_VERSION';compiler_version='COMPILER_VERSION';requiredsl='REQUIRE_DSL_VERSION';requirecompiler='REQUIRE_COMPILER_VERSION';requiredsl_exact='REQUIRE_DSL_VERSION_EXACT';path='PATH';file_name='FILE';directory='DIR';context='CONTEXT';curline='=LINE';preline='-LINE';sucline='+LINE';line='LINE';import_sbdl='IMPORT';import_directive='IMPORT_DIRECTIVE';load_config='LOAD_CONFIG';git_commit_hash='GIT_COMMIT_HASH';define='DEFINE';define_append='DEFINE_APPEND';define_from_func='DEFUNC';define_from_index='DEFIND';define_from_file='DEFINEF';define_from_file_hash='DEFINEFH';expand_define='EXPAND';parsed_elements='PARSED_ELEMENTS';generated_elements='GENERATED_ELEMENTS';synthetic_element='SYNTHETIC_ELEMENT';cross_reference_available='CROSS_REFS_AVAILABLE';extend_cause='EXTEND_CAUSE';cpp_class='CPPCLASS';py_class='PYCLASS';c_func='CFUNC';py_func='PYFUNC';prolog_startup='PL_START';prolog_assert='PL_ASSERT';prolog_assert_n='PL_ASSERT!';prolog_cmd='PL_COMMAND';prolog_trace_file='PL_TRACE_FILE';prolog_result_output='PL_RESULT_OUTPUT';id_from_prop='ID_FROM_PROP'
	@classmethod
	def get_lexical_spec(self):language_spec={};language_spec[_AH]={k:v for(k,v)in vars(self.Statements).items()if not k.startswith(_V)};language_spec[_AI]={k:v for(k,v)in vars(self.Types).items()if not k.startswith(_V)};language_spec[_Az]={k:v for(k,v)in vars(self.Tokens).items()if not k.startswith(_V)};language_spec[_Ck]={k:v for(k,v)in vars(self.Tokens).items()if not k.startswith(_V)};language_spec['Macros']={k:v for(k,v)in vars(self.Macros).items()if not k.startswith(_V)};return language_spec
	@classmethod
	def set_lexical_spec(self,lexical_spec):
		if not hasattr(self,_Cl):self._SPEC_BACKUP=self.get_lexical_spec()
		section_map={_AH:self.Statements,_AI:self.Types,_Az:self.Tokens,_Ck:self.Attributes,'Macros':self.Macros}
		for(section,section_data)in lexical_spec.items():
			if not section in section_map:raise Exception(f"Invalid lexical section name: {section}")
			else:
				section_class=section_map[section]
				for(attribute,value)in section_data.items():
					if section in[_AH,_AI]:
						for type_link in SBDL_Semantics.type_links:
							if type_link==getattr(section_class,attribute):SBDL_Semantics.type_links[value]=SBDL_Semantics.type_links.pop(type_link);break
					setattr(section_class,attribute,value)
	@classmethod
	def unset_lexical_spec(self):
		if hasattr(self,_Cl):self.set_lexical_spec(self._SPEC_BACKUP)
	@classmethod
	def set_formal_spec(self):set_config_value(_AX,_A);set_config_value(_AW,_C);formal_decl='≜';formal_delim=['⟦','⟧'];custom_type='τ';self.set_lexical_spec({_Az:{'hashbang':self.Tokens.hashbang_formal,_Cj:formal_decl,'declaration_group_delimeters':formal_delim,'declaration_attribute_assign':' {} '.format(formal_decl),'declaration_attribute_delimeter':['«','»'],'declaration_token_rule':self.Tokens.declaration_token_rule.replace(self.Tokens.declaration,formal_decl),'declaration_rule':self.Tokens.declaration_rule.replace(self.Tokens.declaration_group_delimeters[0],formal_delim[0]).replace(self.Tokens.declaration_group_delimeters[1],formal_delim[1]).replace(self.Tokens.declaration,formal_decl),'using_rule':self.Tokens.using_rule.replace(self.Tokens.declaration_group_delimeters[0],formal_delim[0]).replace(self.Tokens.declaration_group_delimeters[1],formal_delim[1]),'scope_rule':self.Tokens.scope_rule.replace(self.Tokens.declaration_group_delimeters[0],formal_delim[0]).replace(self.Tokens.declaration_group_delimeters[1],formal_delim[1]),'customtype_rule':self.Tokens.customtype_rule.replace(self.Tokens.declaration_group_delimeters[0],formal_delim[0]).replace(self.Tokens.declaration_group_delimeters[1],formal_delim[1]).replace(self.Tokens.declaration,formal_decl).replace(self.Tokens.customtype,custom_type),'stereotype_separator':'⦂',_AE:custom_type},_AH:{_AE:'τ'}})
	class Parser_Hints:
		@classmethod
		def class_type_name(cls):0
		def set_parser_hint(self,key,value):
			parser_hints=getattr(self,_AJ,_B)
			if parser_hints is _B:parser_hints={}
			parser_hints[key]=value;setattr(self,_AJ,parser_hints)
		def get_parser_hint(self,key):
			parser_hints=getattr(self,_AJ,_B)
			if parser_hints is not _B:return parser_hints.get(key)
		def copy_parser_hints(self,target):
			parser_hints=getattr(self,_AJ,_B)
			if parser_hints is not _B:
				for(hint_key,hint_value)in parser_hints.items():target.set_parser_hint(hint_key,hint_value)
	class Parser_Element(Parser_Hints):
		def __init__(self,content,source,indexes,context=''):
			add_profile_mem_counter('Allocated Parser Elements');self.__content=content;self.__source=source;self.__index=indexes;self.__context=context
			if isinstance(self.__index,(list,tuple)):self.__index=[x for x in self.__index if isinstance(x,(list,tuple))and x and x[0]<len(content)]
		def content(self):return self.__content
		def content_line_layout(self):return self.line_layout_content_indexes(self.content(),self.indexes())
		def content_sanitized(self):return self.content().strip()
		def source(self):return self.__source
		def index(self):
			result=self.__index
			if isinstance(self.__index,(list,tuple)):
				if len(self.__index)>0:result=self.index_at(len(self.content())-len(self.content().lstrip()))
				else:result=0
			return result
		def indexes(self):
			result=self.__index
			if isinstance(self.__index,(list,tuple)):result=self.__index.copy()
			else:result=0,self.__index
			return result
		def indexes_at(self,pos):
			result=[]
			if isinstance(self.__index,(list,tuple)):
				for ind in self.__index:
					if pos<=ind[0]:result.append(ind)
			return result
		def index_at(self,pos):
			result=self.__index
			if isinstance(self.__index,(list,tuple)):
				if len(self.__index)>0:
					result=self.__index[0][1]-1
					for ind in self.__index:
						if pos>=ind[0]:result=ind[1]
				else:result=0
			return result
		def reference(self):return self.shorten_reference('{}:{}'.format(self.source(),self.index()))
		def context(self):return self.__context
		def copy(self):return SBDL_Parser.Parser_Element(self.content(),self.source(),self.indexes(),self.context())
		@classmethod
		def shorten_reference(self,reference):
			reference_result=reference.removeprefix(get_config_value(_Bm));max_length=get_config_value(_CJ,number=_A)
			if max_length!=_B and max_length>0:reference_result=(reference_result[::-1][:max_length]+(reference_result[::-1][max_length:]and get_config_value(_CK)[::-1]))[::-1]
			return reference_result
		@classmethod
		def line_layout_content_indexes(self,content,indexes):
			line_layout_string='';line_numbers_indexes=indexes;start_pos=0
			def append_line_output(number,content):nonlocal line_layout_string;line_layout_string+='{}: {}\n'.format(str(number).zfill(3),content)
			for lbp in range(len(line_numbers_indexes)):
				line_break=line_numbers_indexes[lbp]
				if lbp>0:append_line_output(line_break[1]-1,content[start_pos:line_break[0]])
				start_pos=line_break[0]
			append_line_output(line_numbers_indexes[-1][1],content[start_pos:line_numbers_indexes[-1][0]]);return line_layout_string
	@classmethod
	def builtin_directive_dict(self):
		D='!PROLOG;';C='SBDL_PROLOG_COMMANDS';B='SBDL_PROLOG_ASSERTS';A='SBDL_PROLOG_ENV'
		def abort_directive(_,*inp):f_print('ABORT: {}'.format(''.join(inp)),error=_A);sys.exit(1)
		def message_directive(_,*inp):f_print('{}'.format(''.join(inp)));return''
		def msg_directive(macros,*inp):content=''.join(inp);message_directive(macros,content);return content
		def date_directive(_):return get_date_string()
		def user_directive(_):return get_user_string()
		def add_directive(_,*args):
			result=0
			for val in args:result+=int(val)
			return str(result)
		def sub_directive(_,*args):
			result=_B
			for val in args:
				if result==_B:result=int(val)
				else:result-=int(val)
			return str(result)
		def equal_directive(_,*args):
			first_val=_B
			for val in args:
				if first_val==_B:first_val=val
				elif val!=first_val:raise self.MacroReplaceException('{} is not equal to {}'.format(val,first_val),first_pass_can_ignore=_C)
			return''
		def index_string_list_directive(_,*args):input_str=_I.join(args[:-1]).split();return str(input_str[int(args[-1])])
		def remove_comment_prefixes_directive(_,*inp):
			result=''.join(inp).strip();common_comment_prefixes=['//','/*',_R,'#']
			for prefix in common_comment_prefixes:
				if result.startswith(prefix):result=result[len(prefix):].lstrip()
			return result
		def mk_id_directive(macros,*inp):result=''.join(inp).strip();return self.sanitize_identifier(remove_comment_prefixes_directive(macros,result))
		def concat_directive(_,*inp):return''.join(inp)
		def show_all_directives(macros):return message_directive(macros,'DEFINITIONS AVAILABLE:\n'+json.dumps([x for x in macros.keys()],indent=2))
		def description_from_precursor_directive(macros):return self.Attributes.description+self.Tokens.declaration_attribute_assign+self.Tokens.declaration_attribute_delimeter[0]+remove_comment_prefixes_directive(macros,macros['-LINE'])+self.Tokens.declaration_attribute_delimeter[1]
		def get_version_numbers_dsl(inp):params=_P.join(inp).split(_P);paramsg=version_dsl().split(_P);return int(params[0]),int(params[1]),int(paramsg[0]),int(paramsg[1]),params
		def get_version_numbers_compiler(inp):params=_P.join(inp).split(_P);paramsg=version().split(_P);return int(params[0]),int(params[1]),int(params[2]),int(paramsg[0]),int(paramsg[1]),int(paramsg[2]),params
		def compiler_version_directive(_,__):return version()
		def language_version_directive(_,__):return version_dsl()
		def require_language_version_directive(macros,*inp):
			p1,p2,pg1,pg2,params=get_version_numbers_dsl(inp)
			if pg1<p1 or pg1==p1 and pg2<p2:abort_directive(macros,'Newer version of {} language required: {} (required) vs {} (current version)'.format(name(),_P.join(params),version_dsl()))
			return''
		def require_compiler_version_directive(macros,*inp):
			p1,p2,p3,pg1,pg2,pg3,params=get_version_numbers_compiler(inp)
			if pg1<p1 or pg1==p1 and pg2<p2 or pg1==p1 and pg2==p2 and pg3<p3:abort_directive(macros,'Newer version of {} compiler required: {} (required) vs {} (current version)'.format(name(),_P.join(params),version()))
			return''
		def require_language_version_directive_exact(macros,*inp):
			p1,p2,pg1,pg2,params=get_version_numbers_dsl(inp)
			if pg1!=p1 or pg2!=p2:abort_directive(macros,'Specific version of {} language required: {} (required) vs {} (current version)'.format(name(),_P.join(params),version_dsl()))
			return''
		def expand_define(macros,*inp):
			if len(inp)!=1:raise Exception('Definition expansion requires 1 arguments (definition name)')
			return SBDL_Parser.sanitize(macros[inp[0]].replace(_D,_H))
		def extend_deeper_cause(macros,*inp):
			def get_highest_severity_from_modeid(mode_id):
				highest_severity=0;worst_severity=10
				for effect_id_ in macros[mode_id][SBDL_Parser.Types.effect]:
					effect_id=effect_id_[SBDL_Parser.Attributes.identifier]
					if SBDL_Parser.Attributes.severity in macros[effect_id]:
						prop_sev=int(macros[effect_id][SBDL_Parser.Attributes.severity])
						if prop_sev>highest_severity:highest_severity=prop_sev
					else:highest_severity=worst_severity
				return highest_severity if highest_severity>0 else worst_severity
			if SBDL_Parser.Macros.cross_reference_available in macros:
				extend_symbol=get_config_value(_Cc)
				if len(inp)<1:raise Exception('No element identifier provided')
				this_elem=macros[macros[SBDL_Parser.Macros.self_reference]];this_aspect_links=[]
				if SBDL_Parser.Types.aspect in this_elem:
					for anaspect in this_elem[SBDL_Parser.Types.aspect]:this_aspect_links.append(SBDL_Element.Element_Link(anaspect[SBDL_Parser.Attributes.identifier],SBDL_Parser.Types.aspect))
				identifier=inp[0];new_identifier=identifier+extend_symbol;cause_to_extend=macros[identifier]
				if cause_to_extend[SBDL_Parser.Attributes.type]!=SBDL_Parser.Types.cause:raise Exception('{} is not a failure cause'.format(identifier))
				if len(inp)<2:raise Exception('No detectability for {} is provided'.format(identifier))
				detectability_value=inp[1];requirement_ids=_B
				if len(inp)>2:requirement_ids=inp[2:]
				new_mode_elem=SBDL_Element_Synthetic(new_identifier,cause_to_extend[SBDL_Parser.Attributes.description],SBDL_Parser.Types.mode,detectability_value);new_mode_parent_link=SBDL_Element_Synthetic.Element_Link(identifier,SBDL_Parser.Attributes.parent);new_mode_elem.add_parent(new_mode_parent_link)
				if requirement_ids:
					for requirement_id in requirement_ids:new_mode_requirement_link=SBDL_Element_Synthetic.Element_Link(str(requirement_id),SBDL_Parser.Types.requirement);new_mode_elem.add_link(new_mode_requirement_link)
				for aspect_link in this_aspect_links:new_mode_elem.add_link(aspect_link)
				for modeid_to_extend_ in cause_to_extend[SBDL_Parser.Types.mode]:
					modeid_to_extend=modeid_to_extend_[SBDL_Parser.Attributes.identifier];new_effect_identifier=modeid_to_extend+extend_symbol;mode_to_extend=macros[modeid_to_extend];new_effect_elem=SBDL_Element_Synthetic(new_effect_identifier,mode_to_extend[SBDL_Parser.Attributes.description],SBDL_Parser.Types.effect,get_highest_severity_from_modeid(modeid_to_extend));new_effect_elem.add_parent(SBDL_Element_Synthetic.Element_Link(modeid_to_extend,SBDL_Parser.Attributes.parent))
					for aspect_link in this_aspect_links:new_effect_elem.add_link(aspect_link)
					macros[SBDL_Parser.Macros.generated_elements][new_effect_elem.identifier()]=new_effect_elem.native_element();new_mode_elem.add_link(SBDL_Element.Element_Link(new_effect_elem.identifier(),new_effect_elem.type()))
				macros[SBDL_Parser.Macros.generated_elements][new_mode_elem.identifier()]=new_mode_elem.native_element();return new_mode_elem.identifier()
		def prolog_startup(_,*inp):
			E="Prolog Error!\nOffending Assertion [{}:{}]: {}\n\t'{}'\nError Message:\n\t{}";D='ERROR: attempting to interact with non-existent Prolog environment';result_val=''
			if not get_config_value(_o,number=_A):f_print('PROLOG environment requested but Prolog support is disabled!',warning=_A,opts=['REVERSE']);return result_val
			def prolog_element_to_fact(element):
				prolog_separator=_F
				def prolog_attr(key,value,quote=_A):
					if quote:base_str="{key}('{value}')"
					else:base_str='{key}({value})'
					return base_str.format(key=key,value=value)
				def pstr(str_i):
					result=str_i;pl_exclude=[_R,'-',':'];pl_repl=''
					if str_i!=_B:
						for pl_ex in pl_exclude:result=result.replace(pl_ex,pl_repl)
					return result
				attr_list=[];etype=pstr(element.type());stereot=pstr(element.stereotype());identifier=pstr(element.identifier());properties=element.properties();links=element.links();parents=element.parents();children=element.children();related=element.related();attr_list.append(prolog_attr(self.Attributes.identifier,identifier));attr_list.append(prolog_attr(self.Attributes.stereotype,stereot));inter_prop=[]
				for prop in properties:inter_prop.append(prolog_attr(pstr(prop),pstr(element.get_property(prop))))
				attr_list.append(prolog_attr(_A1,_g+prolog_separator.join(inter_prop)+']',quote=_C))
				def handle_link_list(link_list,link_type):
					inter_list=[]
					for link in link_list:inter_list.append(prolog_attr(_A_,prolog_attr(self.Attributes.type,pstr(link.type()))+prolog_separator+prolog_attr(self.Attributes.identifier,pstr(link.identifier()))+prolog_separator+prolog_attr(self.Attributes.stereotype,pstr(link.stereotype())),quote=_C))
					attr_list.append(prolog_attr(link_type,_g+prolog_separator.join(inter_list)+']',quote=_C))
				handle_link_list(links,_B0);handle_link_list(parents,'parents');handle_link_list(children,'children');handle_link_list(related,'related');return prolog_attr(etype,prolog_separator.join(attr_list),quote=_C)
			def prolog_trace(prolog_py_string):
				global SBDL_PROLOG_TRACE
				if SBDL_PROLOG_TRACE:SBDL_PROLOG_TRACE.write('{}.\n'.format(prolog_py_string))
			def prolog_assert(fact_string,print_method):
				global SBDL_PROLOG_ENV;result=[];prolog_trace(fact_string)
				if A in globals():result=SBDL_PROLOG_ENV.assertz(fact_string)
				elif get_config_value(_o,number=_A):print_method(D,error=_A,opts=['RED'])
				return result
			def prolog_query(query_string,print_method):
				global SBDL_PROLOG_ENV;result=[];prolog_trace(query_string)
				if A in globals():result=list(SBDL_PROLOG_ENV.query(query_string))
				elif get_config_value(_o,number=_A):print_method(D,error=_A,opts=['RED'])
				return result
			def prolog_ast_to_facts(sbdl_ast,_):
				elements,_=sbdl_ast.elements();prolog_facts=[]
				for element in elements:prolog_facts.append(prolog_element_to_fact(elements[element]))
				prolog_facts.sort();return prolog_facts
			def prolog_ast_to_facts_assert(sbdl_ast,print_method):
				facts=prolog_ast_to_facts(sbdl_ast,print_method)
				for fact in facts:prolog_assert(fact,print_method)
				return 0
			def execute_prolog_asserts(_,print_method):
				global SBDL_PROLOG_ASSERTS;error_count=0;result=[]
				if B in globals():
					for prolog_assert in SBDL_PROLOG_ASSERTS:
						assert_positive=prolog_assert[2]
						try:result=prolog_query(prolog_assert[1],print_method)
						except Exception as e:error_count+=1;print_method(E.format(prolog_assert[3],prolog_assert[4],prolog_assert[0],prolog_assert[1],str(e)),error=_A)
						if assert_positive and len(result)==0 or not assert_positive and len(result)>0:print_method("Prolog Assert Failure [{}:{}]: {}\n\t'{}'".format(prolog_assert[3],prolog_assert[4],prolog_assert[0],prolog_assert[1]),error=_A);error_count+=1
						print_method('PrologAssert::{}: {}'.format(prolog_assert[0],'PASS'if error_count==0 else'FAIL'),debug=not get_config_value(_AB,number=_A),error=get_config_value(_AB,number=_A))
				return error_count
			def execute_prolog_commands(_,print_method):
				global SBDL_PROLOG_COMMANDS;error_count=0
				if C in globals():
					for prolog_command in SBDL_PROLOG_COMMANDS:
						try:prolog_assert(prolog_command[1],print_method)
						except Exception as e:error_count+=1;print_method(E.format(prolog_command[2],prolog_command[3],prolog_command[0],prolog_command[1],str(e)),error=_A)
				return error_count
			def prolog_teardown(_,__):
				global SBDL_PROLOG_TRACE
				if SBDL_PROLOG_TRACE:SBDL_PROLOG_TRACE.close()
				return 0
			add_to_config_value(_n,prolog_ast_to_facts_assert);add_to_config_value(_n,execute_prolog_asserts);add_to_config_value(_n,execute_prolog_commands);add_to_config_value(_n,prolog_teardown)
			if not A in globals():
				global SBDL_PROLOG_ENV;global SBDL_PROLOG_TRACE
				if len(inp)!=0:raise Exception('Prolog Startup does not accept arguments')
				try:from pyswip import Prolog;SBDL_PROLOG_ENV=Prolog()
				except Exception as _:raise self.MacroReplaceException('ERROR creating Prolog environment. Do you have pyswip and swi-prolog installed?',first_pass_can_ignore=_C)
				trace_file_path=get_config_value(_Ad)
				if trace_file_path:SBDL_PROLOG_TRACE=open_output_file(trace_file_path,is_text=_A,append=_C)
				else:SBDL_PROLOG_TRACE=_B
			return result_val
		def prolog_assert(macros,*inp,posneg):
			result_val=''
			if len(inp)<2:raise self.MacroReplaceException('Prolog assertion requires a least two arguments',first_pass_can_ignore=_C)
			global SBDL_PROLOG_ASSERTS
			if not A in globals()and get_config_value(_o,number=_A):f_print('Prolog assert specified but Prolog environment not started!',error=_A);result_val=D
			if not B in globals():SBDL_PROLOG_ASSERTS=[]
			SBDL_PROLOG_ASSERTS.append([inp[0],_L.join(inp[1:]),posneg,macros[self.Macros.path],macros[self.Macros.line]]);return result_val
		def prolog_assert_pos(macros,*inp):return prolog_assert(macros,*inp,posneg=_A)
		def prolog_assert_neg(macros,*inp):return prolog_assert(macros,*inp,posneg=_C)
		def prolog_cmd(macros,*inp):
			result_val=''
			if len(inp)<2:raise self.MacroReplaceException('Prolog command requires a least two arguments',first_pass_can_ignore=_C)
			global SBDL_PROLOG_COMMANDS
			if not A in globals()and get_config_value(_o,number=_A):f_print('Prolog command specified but Prolog environment not started!',error=_A);result_val=D
			if not C in globals():SBDL_PROLOG_COMMANDS=[]
			SBDL_PROLOG_COMMANDS.append([inp[0],_L.join(inp[1:]),macros[self.Macros.path],macros[self.Macros.line]]);return result_val
		def prolog_result_output(_,*__):set_config_value(_AB,_A);return''
		def prolog_trace_file(_,*inp):
			if len(inp)<1:raise self.MacroReplaceException('Prolog tracefile requires a file path argument',first_pass_can_ignore=_C)
			set_config_value(_Ad,inp[0]);return''
		def import_directive(macros,*inp):
			param_path=''.join(inp);import_path=os.path.join(macros[SBDL_Parser.Macros.path].replace(macros[SBDL_Parser.Macros.file_name],''),param_path)
			if get_config_value(_Bx,number=_A):
				import_errors=import_custom_directive_file(import_path,f_print)
				if import_errors:raise self.MacroReplaceException(f'ERROR importing custom directive file: "{import_path}"',first_pass_can_ignore=_C)
			else:raise self.MacroReplaceException('ERROR import of custom directives is disabled',first_pass_can_ignore=_C)
			return''
		def load_config_directive(macros,*inp):
			param_path=''.join(inp);config_path=os.path.join(macros[SBDL_Parser.Macros.path].replace(macros[SBDL_Parser.Macros.file_name],''),param_path)
			if get_config_value(_By,number=_A):
				try:load_config(config_path,skip_keys=[_U])
				except Exception as e:raise self.MacroReplaceException(f"ERROR loading config: {e}",first_pass_can_ignore=_C)
			else:raise self.MacroReplaceException('ERROR loading of config is disabled',first_pass_can_ignore=_C)
			return''
		def git_commit_hash(macros,*inp):
			param_path=''.join(inp);config_path=os.path.join(macros[SBDL_Parser.Macros.path].replace(macros[SBDL_Parser.Macros.file_name],''),param_path)
			try:repo_root=pathlib.Path(config_path).resolve();hash_value=subprocess.check_output(['git','-C',str(repo_root),'rev-parse','--short','HEAD'],text=_A).strip()
			except Exception as e:hash_value='GIT_HASH_UNAVAILABLE';f_print(f"{macros[SBDL_Parser.Macros.file_name]}:{macros[SBDL_Parser.Macros.line]}:: WARNING cannot retrieve Git commit hash: {e}",warning=_A)
			return hash_value
		def identifier_from_property(macros,*inp):
			A='SBDL_PROPERTY_VALUE_CACHE_MEMO'
			if not SBDL_Parser.Macros.cross_reference_available in macros:return
			identifier=''
			if len(inp)<2:raise self.MacroReplaceException('ERROR: property name and value arguments required',first_pass_can_ignore=_C)
			property_name=inp[0];property_value=inp[1]
			def gen_property_cache(input_defs,prop_name):
				prop_cache_gen={}
				for(def_key,def_val)in input_defs.items():
					if isinstance(def_val,dict):
						if prop_name in def_val:
							prop_val=def_val[prop_name]
							if not prop_val in prop_cache_gen:prop_cache_gen[prop_val]=[]
							prop_cache_gen[prop_val].append(def_key)
				return prop_cache_gen
			global_directives=get_config_value(_U)
			if not A in global_directives:global_directives[A]={}
			property_cache=global_directives[A]
			if not property_name in property_cache and SBDL_Parser.Macros.cross_reference_available in macros:property_cache[property_name]=gen_property_cache(macros,property_name)
			if property_name in property_cache and property_value in property_cache[property_name]:identifier=self.Attributes.separator.join(property_cache[property_name][property_value])
			return identifier
		return{self.Macros.abort:abort_directive,self.Macros.message:message_directive,self.Macros.msg:msg_directive,self.Macros.date:date_directive,self.Macros.user:user_directive,self.Macros.add:add_directive,self.Macros.sub:sub_directive,self.Macros.equal:equal_directive,self.Macros.concat:concat_directive,self.Macros.instli:index_string_list_directive,self.Macros.showall:show_all_directives,self.Macros.rmcom:remove_comment_prefixes_directive,self.Macros.mkid:mk_id_directive,self.Macros.dfp:description_from_precursor_directive,self.Macros.dsl_version:language_version_directive,self.Macros.compiler_version:compiler_version_directive,self.Macros.requiredsl:require_language_version_directive,self.Macros.requirecompiler:require_compiler_version_directive,self.Macros.requiredsl_exact:require_language_version_directive_exact,self.Macros.extend_cause:extend_deeper_cause,self.Macros.expand_define:expand_define,self.Macros.prolog_startup:prolog_startup,self.Macros.prolog_assert:prolog_assert_pos,self.Macros.prolog_assert_n:prolog_assert_neg,self.Macros.prolog_cmd:prolog_cmd,self.Macros.prolog_result_output:prolog_result_output,self.Macros.prolog_trace_file:prolog_trace_file,self.Macros.import_directive:import_directive,self.Macros.load_config:load_config_directive,self.Macros.git_commit_hash:git_commit_hash,self.Macros.id_from_prop:identifier_from_property}
	class MacroReplaceException(Exception):
		def __init__(self,error_message,first_pass_can_ignore=_A):super().__init__(error_message);self.can_ignore_in_first_pass=first_pass_can_ignore
		def first_pass_can_ignore(self):return self.can_ignore_in_first_pass
	@classmethod
	def is_string_directive(self,input_str):
		is_directive=_C
		if re.match(self.Tokens.macro_rule,input_str):is_directive=_A
		return is_directive
	@classmethod
	def replace_directives(self,input_str,macro_definitions,throw_error,print_l):
		B='C_DEF REPLACE: {}';A='Compiler definition requires a least two arguments (define name, filename)';result=input_str;errors=0;macro_param_sep=_L
		def define_directive_base(macros,append,*args):
			if len(args)<2:raise self.MacroReplaceException('Compiler definition requires a least two arguments (k, v)',first_pass_can_ignore=_C)
			else:
				flat_text=_I.join(args[1:])
				if append and args[0]in macros:flat_text=macros[args[0]]+flat_text
				macros[args[0]]=flat_text
			return''
		def define_directive(macros,*args):return define_directive_base(macros,_C,*args)
		def define_directive_append(macros,*args):return define_directive_base(macros,_A,*args)
		def define_directive_from_function(macros,*args):macros[args[0]]=macros[args[1]](macros,*args[2:]);return''
		def define_directive_from_index(macros,*args):macros[args[0]]=macros[args[1]][args[2]];return''
		def define_directive_from_file(macros,*args):
			if len(args)<2:raise self.MacroReplaceException(A,first_pass_can_ignore=_C)
			define_name=args[0];filepath=os.path.join(macros['DIR'],*args[1:]);success,contents=plain_text_input_handler(filepath);contents=''.join(contents);macros[define_name]=contents;return''
		def define_directive_from_file_hash(macros,*args):
			if len(args)<2:raise self.MacroReplaceException(A,first_pass_can_ignore=_C)
			def hash_file(filepath):
				hasher=hashlib.sha256()
				try:
					with open(filepath,'rb')as f:
						while(chunk:=f.read(4096)):hasher.update(chunk)
				except Exception as e:raise self.MacroReplaceException(str(e),first_pass_can_ignore=_C)
				return hasher.hexdigest()[:8]
			define_name=args[0];filepath=os.path.join(macros['DIR'],*args[1:]);macros[define_name]=hash_file(filepath);return''
		def split_term_id(input_str):
			result=[input_str];escape=SBDL_Parser.Tokens.escape;splitc=SBDL_Parser.Tokens.macro_index_separator;pos=0
			while pos<len(input_str):
				if input_str[pos:pos+len(escape)]==escape:pos+=len(escape)
				elif input_str[pos:pos+len(splitc)]==splitc:result=[input_str[:pos].replace(escape,''),input_str[pos+len(splitc):]];break
				pos+=1
			return result
		def term(match):
			result_l=match[len(SBDL_Parser.Tokens.macro_delimeters[0])+len(SBDL_Parser.Tokens.macro_variable):-len(SBDL_Parser.Tokens.macro_delimeters[1])];result_l=split_term_id(result_l);index=_B
			if len(result_l)>1:index=SBDL_Parser.Tokens.macro_index_separator.join(result_l[1:]);result_l=SBDL_Parser.Tokens.macro_index_separator.join(result_l[0:1])
			else:result_l=''.join(result_l)
			return result_l,index
		def fudge_recurse(possible_directive):nonlocal errors;fudge_value=SBDL_Parser.Tokens.macro_delimeters[0]+possible_directive+SBDL_Parser.Tokens.macro_delimeters[1];fudged_value,errors_l=self.replace_directives(fudge_value,macro_definitions,throw_error,print_l);errors+=errors_l;return fudged_value if fudged_value!=fudge_value else possible_directive
		def smart_flatten_macro_definition(macro_content):
			resultant_content=macro_content
			if isinstance(macro_content,str):resultant_content=macro_content
			elif isinstance(macro_content,list):resultant_content=SBDL_Parser.Attributes.separator.join([smart_flatten_macro_definition(x)for x in macro_content])
			elif isinstance(macro_content,dict):
				if SBDL_Parser.Attributes.identifier in macro_content:
					m_identifier=macro_content[SBDL_Parser.Attributes.identifier];resultant_content=m_identifier
					if SBDL_Parser.Attributes.stereotype in macro_content:m_stereotype=macro_content[SBDL_Parser.Attributes.stereotype];resultant_content=f"{m_identifier}{SBDL_Parser.Tokens.stereotype_separator}{m_stereotype}"
					else:resultant_content=m_identifier
			return resultant_content
		if get_config_value(_AC,number=_A)and result!=_B and isinstance(result,str):
			if get_config_value(_Ai,number=_A):macro_definitions[SBDL_Parser.Macros.define]=define_directive;macro_definitions[SBDL_Parser.Macros.define_append]=define_directive_append;macro_definitions[SBDL_Parser.Macros.define_from_func]=define_directive_from_function;macro_definitions[SBDL_Parser.Macros.define_from_index]=define_directive_from_index;macro_definitions[SBDL_Parser.Macros.define_from_file]=define_directive_from_file;macro_definitions[SBDL_Parser.Macros.define_from_file_hash]=define_directive_from_file_hash
			else:macro_definitions.pop(SBDL_Parser.Macros.define,_B);macro_definitions.pop(SBDL_Parser.Macros.define_append,_B);macro_definitions.pop(SBDL_Parser.Macros.define_from_func,_B);macro_definitions.pop(SBDL_Parser.Macros.define_from_index,_B);macro_definitions.pop(SBDL_Parser.Macros.define_from_file,_B);macro_definitions.pop(SBDL_Parser.Macros.define_from_file_hash,_B)
			matches=re.findall(self.Tokens.macro_rule,result)
			for amatch in matches:
				print_l('DEFINITION FOUND  : {}'.format(amatch),debug=_A);macro,index=term(amatch);print_l('DEFINITION NAME   : {}'.format(macro),debug=_A);print_l('DEFINITION INDEX  : {}'.format(index),debug=_A);macro=fudge_recurse(macro)
				if macro in macro_definitions:
					print_l('DEF. ENTRY FOUND  : {}'.format(macro),debug=_A);replace_content=_B
					if callable(macro_definitions[macro]):
						params=[x.replace('\\{}'.format(macro_param_sep),macro_param_sep)for x in re.split('(?<!\\\\){}'.format(macro_param_sep),index)]if index!=_B else[]
						for p in range(0,len(params)):params[p]=fudge_recurse(params[p])
						try:replace_content=str(macro_definitions[macro](macro_definitions,*params))
						except SBDL_Parser.MacroReplaceException as macro_excep:errors+=1;raise macro_excep
						except Exception as e:
							errors+=1;can_ignore=_A
							if throw_error:raise self.MacroReplaceException('Error calling compiler function. Were the correct number of arguments provided? [{}]'.format(e),first_pass_can_ignore=can_ignore)
					elif index==_B:replace_content=macro_definitions[macro]
					else:
						index_fudge=fudge_recurse(index)
						def replace_cross_ref(rmacro,rindex):
							nonlocal errors;replace_result=_B
							if isinstance(macro_definitions[rmacro],dict)and rindex in macro_definitions[rmacro]:replace_result=macro_definitions[rmacro][rindex]
							else:
								print_l(B.format('[Index Not Found]'),debug=_A);errors+=1
								if throw_error:raise self.MacroReplaceException('Attempted replacement for non-existent definition index: "{}[{}]"'.format(rmacro,rindex))
							replace_result=smart_flatten_macro_definition(replace_result)
							if SBDL_Parser.Tokens.macro_variable in replace_result:replace_result,errors=self.replace_directives(replace_result,macro_definitions,throw_error,print_l)
							return replace_result
						replace_content=replace_cross_ref(macro,index_fudge)
					print_l('C_DEF REPLACE: "{}" --> "{}"'.format(input_str,replace_content),debug=_A)
					if replace_content!=_B and replace_content!=str(_B)and errors==0:result=result.replace(amatch,str(smart_flatten_macro_definition(replace_content)))
				else:
					print_l(B.format('[Definition Not Found]'),debug=_A);errors+=1
					if throw_error:raise self.MacroReplaceException('Attempted replacement for non-existent definition: "{}"'.format(macro),first_pass_can_ignore=_A)
		return result,errors
	@classmethod
	def parse_kvp_string(self,kvp_string,allow_nested_declarations,parser_element=_B,parser_offset=0):
		kvp_set={};pos=0;error=_C;error_message='';error_position=0;parser_elements=[]
		def read_until(string_list):
			nonlocal pos;var_str=''if pos<len(kvp_string)else _B
			def check_for_string():
				nonlocal pos;result=_C
				for stringval in string_list:
					if pos<len(kvp_string)and stringval==kvp_string[pos:pos+len(stringval)]:result=_A;pos+=len(stringval)
				return result
			while pos<len(kvp_string):
				if pos+1<len(kvp_string)and kvp_string[pos]==self.Tokens.escape and kvp_string[pos+1]in[self.Tokens.declaration_attribute_delimeter[0],self.Tokens.declaration_attribute_delimeter[1]]:var_str+=str(kvp_string[pos+1]);pos+=2
				elif check_for_string():return var_str
				else:var_str+=str(kvp_string[pos]);pos+=1
			return var_str
		def has_white_space(input_str):
			haswhitespace=_C
			for wsp in self.Tokens.whitespace_list:
				if len(input_str.split(wsp))>1:haswhitespace=_A
			return haswhitespace
		def read_operator():
			nonlocal pos;operator_result=_B
			def check_op(operator_string):
				nonlocal pos;in_raw=kvp_string[pos:];in_string=in_raw.lstrip()
				if in_string.startswith(operator_string):return operator_string,len(operator_string)+len(in_raw)-len(in_string)
			for op in[SBDL_Parser.Tokens.source_link_operator,SBDL_Parser.Tokens.link_operator]:
				if(op_res:=check_op(op)):operator_result=op_res[0];pos+=op_res[1]
			return operator_result
		def read_var():
			nonlocal pos;raw_content_start=self.Tokens.raw_content_delimiters[0];variable_name=_B
			if kvp_string.lstrip()[pos:pos+len(raw_content_start)]==raw_content_start:return self.Attributes.raw_content
			else:
				variable_name=read_until([self.Tokens.declaration_attribute_assign])
				if variable_name!=_B:
					variable_name=variable_name.strip()
					if has_white_space(variable_name):variable_name=_B
			return variable_name
		def read_string():nonlocal pos;pos+=1;return read_until([self.Tokens.declaration_attribute_delimeter[1]])
		def read_raw_content():nonlocal pos;raw_content_start=self.Tokens.raw_content_delimiters[0];raw_content_end=self.Tokens.raw_content_delimiters[1];pos+=len(raw_content_start);return read_until([raw_content_end])
		def read_identifier():
			nonlocal pos
			def do_read_identifier():return read_until(self.Tokens.whitespace_list+[self.Tokens.declaration_attribute_separator])
			identifier_content=do_read_identifier()
			while _A:
				if identifier_content is not _B and len(identifier_content)>1 and self.Attributes.separator in[identifier_content[-1],peek_next_nonwhtsp(len(self.Attributes.separator))]:
					new_identifier_content=do_read_identifier()
					if new_identifier_content is not _B:identifier_content+=new_identifier_content.strip()
					else:break
				else:break
			if identifier_content!=_B:identifier_content=identifier_content.strip()
			return identifier_content
		def read_value():
			nonlocal pos;raw_content_start=self.Tokens.raw_content_delimiters[0]
			while pos<len(kvp_string):
				if not kvp_string[pos]in self.Tokens.whitespace_list:
					if kvp_string[pos]==self.Tokens.declaration_attribute_delimeter[0]:return read_string()
					elif kvp_string[pos:pos+len(raw_content_start)]==raw_content_start:return read_raw_content()
					else:return read_identifier()
				else:pos+=1
		def gobble_separator():
			nonlocal pos;gobbled=re.match('\\s?{}\\s?'.format(self.Tokens.declaration_attribute_separator),kvp_string[pos:])
			if gobbled:pos+=len(gobbled[0])
		def peek_next_nonwhtsp(peek_length):return kvp_string[pos:].strip()[:peek_length]
		def peek_group_opener():
			is_group_opener=_C;group_opener=SBDL_Parser.Tokens.declaration_group_delimeters[0]
			if peek_next_nonwhtsp(len(group_opener))==group_opener:is_group_opener=_A
			return is_group_opener
		def get_group(start_pos):
			nonlocal pos;resultant_group='';search_pos=start_pos;end_group_pos=search_pos;opened_count=0;closed_count=0;group_opener=SBDL_Parser.Tokens.declaration_group_delimeters[0];group_closer=SBDL_Parser.Tokens.declaration_group_delimeters[1];group_len=len(group_opener)if len(group_opener)>=len(group_closer)else len(group_closer)
			while search_pos<len(kvp_string):
				group_char=kvp_string[search_pos:][:group_len]
				if kvp_string[search_pos]==self.Tokens.escape:search_pos+=1
				elif group_char==group_opener:opened_count+=1
				elif group_char==group_closer:closed_count+=1
				if opened_count>0 and opened_count==closed_count:end_group_pos=search_pos+len(group_closer);break
				search_pos+=1
			resultant_group=kvp_string[start_pos:end_group_pos];pos=end_group_pos;return resultant_group
		def do_error(msg):nonlocal error,error_message,error_position;error=_A;error_message=msg;error_position=pos
		def read_kvp():
			nonlocal pos;start_read_pos=pos;kvp=_B
			def make_parser(content_str,count_element):
				nonlocal parser_elements;new_parser_elem=self.Parser_Element(content=content_str,source=parser_element.source(),indexes=[[x[0]-index_pos-parser_offset,x[1]]for x in parser_element.indexes_at(index_pos+parser_offset)])
				if count_element:parser_elements.append(new_parser_elem)
				return new_parser_elem
			if(operator:=read_operator()):index_pos=start_read_pos;kvp=f"{operator}_{pos}",make_parser(operator,_C)
			elif(var_name:=read_var()):
				var_value=read_value()
				if var_value!=_B:
					if peek_group_opener():
						if not allow_nested_declarations:do_error('Unexpected nested declaration "{}" @ attribute column offset [{}]'.format(kvp_string[pos:10],pos+1))
						index_pos=start_read_pos;kvp=var_name,make_parser(get_group(start_read_pos),_A)
					else:kvp=var_name,var_value
				else:do_error('Invalid value assignment "{}" @ attribute column offset [{}:{}]'.format(kvp_string[start_read_pos+1:pos+1],start_read_pos+1,pos+1))
			elif len(kvp_string[start_read_pos:pos].strip())>0:do_error('Invalid identifier assignment "{}" @ attribute column offset [{}:{}]'.format(kvp_string[start_read_pos:pos],start_read_pos,pos))
			gobble_separator();return kvp
		new_kvp=read_kvp()
		while new_kvp!=_B and not error:
			if not new_kvp[0]in kvp_set:kvp_set[new_kvp[0]]=new_kvp[1]
			else:error=_A;error_message+='"{}" attribute already defined'.format(new_kvp[0])
			new_kvp=read_kvp()
		if parser_elements:parser_elements[-1].set_parser_hint(SBDL_Parser.Tokens.nested_elements_depth_pop,len(parser_elements))
		return kvp_set,error,error_message,error_position
	@classmethod
	def parse_declaration(self,declaration_element,syntactic_parent=_B):
		identifier='';declaration_type='';kvp_dictionary={};data_dictionary={};nested_declarations=[];d_position=declaration_element.index();error=_A;error_message='';sanitized_content=SBDL_Parser.sanitize_escaped(declaration_element.content_sanitized());assign_split=re.split(self.Tokens.declaration_token_rule,sanitized_content,maxsplit=1)
		if len(assign_split)==2:
			identifier=assign_split[0].strip();group_delimeters=self.Tokens.declaration_group_delimeters;type_split=re.split('\\{}'.format(group_delimeters[0]),assign_split[1],maxsplit=1)
			if len(type_split)==2 and len(type_split[1])>=len(group_delimeters[1]):
				if type_split[1][len(type_split[1])-len(group_delimeters[1]):]==group_delimeters[1]:
					declaration_kvp=type_split[1][:-len(group_delimeters[1])];declaration_type=type_split[0].strip();decl_cont=declaration_element.content();decl_nws=re.sub('\\s+',_I,decl_cont);kvp_nws=re.sub('\\s+',_I,declaration_kvp);kvp_offset=decl_nws.find(kvp_nws)+(len(decl_cont)-len(decl_nws));kvp_dictionary,d_error,d_message,d_position=self.parse_kvp_string(SBDL_Parser.sanitize_escaped(declaration_kvp,reverse=_A),allow_nested_declarations=_A,parser_element=declaration_element,parser_offset=kvp_offset);error_message+=d_message
					if not d_error:error=_C
				else:error_message+='Expected closing delimeter'
			else:error_message+='Expected group delimeters'
		elif re.match(self.Tokens.operator_rule,sanitized_content):return[self.parse_operator(declaration_element)]
		else:error_message+='Expected operator token'
		for d_entry in kvp_dictionary:
			if not isinstance(kvp_dictionary[d_entry],self.Parser_Element):data_dictionary[d_entry]=kvp_dictionary[d_entry]
		resultant_declaration=SBDL_Declaration_Statement(declaration_element,identifier,declaration_type,data_dictionary,error=error,error_message='Declaration statement: '+error_message,syntactic_parent=syntactic_parent,error_pos=d_position);declaration_element.copy_parser_hints(resultant_declaration)
		for d_entry in kvp_dictionary:
			if isinstance(kvp_dictionary[d_entry],self.Parser_Element):nested_declarations+=self.parse_declaration(kvp_dictionary[d_entry],syntactic_parent=resultant_declaration)
		return[resultant_declaration]+nested_declarations
	@classmethod
	def parse_customtype(self,customtype_element):prototype_elem=self.parse_declaration(self.Parser_Element(customtype_element.content().replace(self.Tokens.customtype,'',1),customtype_element.source(),customtype_element.index()))[0];customtype_elem=SBDL_Customtype_Statement(customtype_element,prototype_elem.identifier(),prototype_elem.data_type(),prototype_elem.data());return customtype_elem
	@classmethod
	def parse_generic_statement(self,gen_element,token_str,statement_class):
		A='Expected {} delimeter';data_dictionary={};error=_A;error_message=f"{token_str} statement: ";base_string=gen_element.content_sanitized();d_position=_B
		if base_string.startswith(token_str):
			base_string=base_string[len(token_str):].strip()
			if base_string.startswith(SBDL_Parser.Tokens.declaration_group_delimeters[0]):
				base_string=base_string[len(SBDL_Parser.Tokens.declaration_group_delimeters[0]):].strip()
				if base_string.endswith(SBDL_Parser.Tokens.declaration_group_delimeters[1]):
					base_string=base_string[:-len(SBDL_Parser.Tokens.declaration_group_delimeters[1])].strip();data_dictionary,d_error,d_message,d_position=self.parse_kvp_string(base_string,allow_nested_declarations=_C);error_message+=d_message
					if not d_error:error=_C
				else:error_message+=A.format(SBDL_Parser.Tokens.declaration_group_delimeters[1])
			else:error_message+=A.format(SBDL_Parser.Tokens.declaration_group_delimeters[0])
		else:error_message+='Expected {} keyboard'.format(token_str)
		return statement_class(gen_element,token_str,data_dictionary=data_dictionary,error=error,error_message=error_message,error_pos=d_position)
	@classmethod
	def parse_using_statement(self,using_element):return self.parse_generic_statement(using_element,SBDL_Parser.Tokens.using,SBDL_Using_Statement)
	@classmethod
	def parse_depends_statement(self,depends_element):return self.parse_generic_statement(depends_element,SBDL_Parser.Tokens.depends,SBDL_Depends_Statement)
	@classmethod
	def parse_scope_statement(self,scope_element):return self.parse_generic_statement(scope_element,SBDL_Parser.Tokens.scope,SBDL_Scope_Statement)
	@classmethod
	def parse_operator(self,operator_element):return SBDL_Operator(operator_element,SBDL_Parser.Statements.operator)
	@classmethod
	def identifier_stereotype(self,identifier_string):
		identifier=identifier_string;stereotype=_B;identifier_stereotype_split=identifier_string.split(self.Tokens.stereotype_separator)
		if len(identifier_stereotype_split)>1:stereotype=identifier_stereotype_split[-1];identifier=self.Tokens.stereotype_separator.join(identifier_stereotype_split[:-1])
		return identifier,stereotype
	@classmethod
	def identifier_stereotype_join(self,identifier,stereotype):
		mergedid=identifier
		if stereotype:mergedid+=self.Tokens.stereotype_separator+stereotype
		return mergedid
	@classmethod
	def identifier_hash(self,identifier_string):
		identifier=identifier_string;hashi=_B;identifier_hash_split=identifier_string.split(self.Tokens.hash_separator)
		if len(identifier_hash_split)>1:hashi=identifier_hash_split[-1];identifier=self.Tokens.hash_separator.join(identifier_hash_split[:-1])
		return identifier,hashi
	@classmethod
	def identifier_hash_join(self,identifier,hashi):
		identifier_l=identifier
		if hashi:identifier_l+=self.Tokens.hash_separator+hashi
		return identifier_l
	@classmethod
	def sanitize_identifier(self,identifier_string,guard_exception=_C):
		result=identifier_string;replacement_character=self.Tokens.replacement_string_default;illegal_characters=[self.Attributes.separator,_I,'\t',self.Tokens.declaration_attribute_separator,*self.Tokens.whitespace_list,self.Tokens.statement_separator]
		for illegal_character in illegal_characters:result=result.replace(illegal_character,self.Tokens.replacement_map[illegal_character]if illegal_character in self.Tokens.replacement_map else replacement_character)
		if guard_exception and result!=identifier_string:raise Exception('Identifier contains illegal characters: "{}"'.format(identifier_string))
		return result
	@classmethod
	def sanitize(self,unclean_string):
		def replace_unescapechar(inputc,char_to_replace):
			represult=inputc;repcount=0
			for repmatch in re.finditer(char_to_replace,represult):
				pos=repmatch.start()+repcount
				if pos==0 or pos>0 and represult[pos-1]!=SBDL_Parser.Tokens.escape:represult=represult[:pos]+SBDL_Parser.Tokens.escape+represult[pos:];repcount+=1
			return represult
		replace_list=[SBDL_Parser.Tokens.comment,*SBDL_Parser.Tokens.declaration_attribute_delimeter,*SBDL_Parser.Tokens.declaration_group_delimeters];result=unclean_string
		for char_to_rep in replace_list:result=replace_unescapechar(result,char_to_rep)
		return result
	@classmethod
	def sanitize_escaped(self,unclean_string,reverse=_C):
		escape_list=[[SBDL_Parser.Tokens.escape+SBDL_Parser.Tokens.declaration_group_delimeters[0],'##1'],[SBDL_Parser.Tokens.escape+SBDL_Parser.Tokens.declaration_group_delimeters[1],'##2']];result=unclean_string;incr=-1 if reverse else 1
		for escape_pair in escape_list:result=result.replace(*escape_pair[::incr])
		return result
	@classmethod
	def remove_escape_chars(self,escaped_string):
		result=escaped_string
		for char in[SBDL_Parser.Tokens.comment,*SBDL_Parser.Tokens.declaration_attribute_delimeter,*SBDL_Parser.Tokens.declaration_group_delimeters]:result=result.replace(SBDL_Parser.Tokens.escape+char,char)
		return result
	@classmethod
	def attribute_string(self,statement_object):
		indent_str='';newline_str='';spacing_str=_I
		if get_config_value(_T,number=_A):indent_str='    ';newline_str=(self.Tokens.line_continuation if not get_config_value(_s,number=_A)else'')+_D;spacing_str=''
		def needs_delim(attr_str):
			result=_C
			for wtsp in self.Tokens.whitespace_list:
				if len(attr_str.split(wtsp))>1:result=_A
			return result
		attributes=spacing_str
		for attr in sorted(statement_object.data()):
			content=self.sanitize(str(statement_object.data()[attr]))
			if len(content)>0:
				delim_open=SBDL_Parser.Tokens.declaration_attribute_delimeter[0];delim_close=SBDL_Parser.Tokens.declaration_attribute_delimeter[1]
				if not needs_delim(content):delim_open=delim_close=''
				attributes+='{indent}{attrname}{assign}{delimiter_open}{attrcontent}{delimiter_close}{separator}{newline}{spacing}'.format(attrname=attr,assign=SBDL_Parser.Tokens.declaration_attribute_assign,delimiter_open=delim_open,delimiter_close=delim_close,attrcontent=content,separator=SBDL_Parser.Tokens.declaration_attribute_separator if get_config_value(_AW,number=_A)else'',indent=indent_str,newline=newline_str,spacing=spacing_str)
		return attributes
	@classmethod
	def declaration_string(self,declaration_object):
		newline_str=''
		if get_config_value(_T,number=_A):newline_str=(self.Tokens.line_continuation if not get_config_value(_s,number=_A)else'')+_D
		return'{ident} {assignkw} {typename} {opencont}{newline}{attrset}{closecont}'.format(ident=declaration_object.identifier(),assignkw=SBDL_Parser.Tokens.declaration,typename=declaration_object.data_type(),opencont=newline_str+SBDL_Parser.Tokens.declaration_group_delimeters[0],attrset=self.attribute_string(declaration_object),closecont=SBDL_Parser.Tokens.declaration_group_delimeters[1],newline=newline_str)
	@classmethod
	def generic_elem_string(self,gen_object):
		newline_str=''
		if get_config_value(_T,number=_A):newline_str=(self.Tokens.line_continuation if not get_config_value(_s,number=_A)else'')+_D
		return'{kw} {opencont}{newline}{attrset}{closecont}'.format(kw=gen_object.data_type(),opencont=SBDL_Parser.Tokens.declaration_group_delimeters[0],attrset=self.attribute_string(gen_object),closecont=SBDL_Parser.Tokens.declaration_group_delimeters[1],newline=newline_str)
	@classmethod
	def depends_string(self,depends_object):return self.generic_elem_string(depends_object)
	@classmethod
	def using_string(self,using_object):return self.generic_elem_string(using_object)
	@classmethod
	def scope_string(self,scope_object):return self.generic_elem_string(scope_object)
	@classmethod
	def get_statements(self,input_string,include_trailing=_A):
		statements=[];statement_markers=self.Tokens.declaration_group_delimeters;marker_length=len(statement_markers[0])if statement_markers[0]>=statement_markers[1]else len(statement_markers[1]);accumulator='';open_close_count=[0,0];search_pos=0
		while search_pos<len(input_string):
			character=input_string[search_pos:search_pos+marker_length]
			if input_string[search_pos]==self.Tokens.escape:
				if search_pos+2<len(input_string):accumulator+=character;accumulator+=input_string[search_pos+1];search_pos+=2
				continue
			elif character==statement_markers[0]:open_close_count[0]+=1
			elif character==statement_markers[1]:open_close_count[1]+=1
			accumulator+=character
			if open_close_count[1]>0 and open_close_count[0]==open_close_count[1]:open_close_count[0]=0;open_close_count[1]=0;statements.append(accumulator);accumulator=''
			elif character==self.Tokens.statement_separator and accumulator.lstrip()==self.Tokens.statement_separator and len(statements)>0:statements[-1]+=accumulator;accumulator=''
			elif accumulator.strip()in[self.Tokens.link_operator,self.Tokens.source_link_operator]:statements.append(accumulator);accumulator=''
			search_pos+=1
		if len(accumulator)>0 and include_trailing:statements.append(accumulator)
		return statements
	@classmethod
	def parse_statements(self,parser_element):
		result=[];blanks=0;sbdl_statement_string=SBDL_Parser.sanitize_escaped(parser_element.content());statements=self.get_statements(sbdl_statement_string);residual_sbdl_statement_string=sbdl_statement_string
		for statement_index in range(len(statements)):
			statement=statements[statement_index]
			if not statement.isspace():residual_sbdl_statement_string=residual_sbdl_statement_string.replace(str(statement),'',1)
		if len(residual_sbdl_statement_string)>0:statements.append(residual_sbdl_statement_string)
		statement_pos=0
		for statement in statements:
			statement_pos_p=statement_pos;statement_pos+=len(statement)
			def rule_match(rule,statementl):return re.match(rule,statementl)
			def new_parser_elem(statementl,statement_pos_pl):sanitized=SBDL_Parser.sanitize_escaped(statementl,reverse=_A);indexes=[(x[0]-statement_pos_pl,x[1])for x in parser_element.indexes_at(statement_pos_pl)];ctx=parser_element.context();src=parser_element.source();new_elem=self.Parser_Element(sanitized,src,indexes,ctx);return new_elem
			def add_specific_error():
				maybe_error=result[-1]
				if maybe_error.error():maybe_error.extend_error_message(' [error begins ~ line {}]'.format(maybe_error.parser_element().index_at(result[-1].error_position())))
			if statement.endswith(self.Tokens.statement_separator):statement=statement[0:-len(self.Tokens.statement_separator)]
			if rule_match(self.Tokens.customtype_rule,statement):result.append(self.parse_customtype(new_parser_elem(statement,statement_pos_p)))
			elif rule_match(self.Tokens.declaration_rule,statement):result.extend(self.parse_declaration(new_parser_elem(statement,statement_pos_p)));add_specific_error()
			elif rule_match(self.Tokens.depends_rule,statement):result.append(self.parse_depends_statement(new_parser_elem(statement,statement_pos_p)));add_specific_error()
			elif rule_match(self.Tokens.using_rule,statement):result.append(self.parse_using_statement(new_parser_elem(statement,statement_pos_p)));add_specific_error()
			elif rule_match(self.Tokens.scope_rule,statement):result.append(self.parse_scope_statement(new_parser_elem(statement,statement_pos_p)));add_specific_error()
			elif len(statement.strip())==0:blanks+=1
			elif rule_match(self.Tokens.operator_rule,statement):result.append(self.parse_operator(new_parser_elem(statement,statement_pos_p)))
			else:result.append(SBDL_Statement(new_parser_elem(statement,statement_pos_p),_A2,{},_A,'Statement does not match any recognised rule'))
		if len(result)+blanks<len(statements):result.append(SBDL_Statement(parser_element,_A2,{},_A,'Unknown error (not all statements parsed)'))
		return result
	@classmethod
	def parse_elements(self,parser_element):return self.parse_statements(parser_element)
	@classmethod
	def mk_type_info_struct(self,cat,desc):return{_As:cat,_O:desc}
class SBDL_Semantics:
	type_links={SBDL_Parser.Types.mode:{SBDL_Parser.Types.requirement:_A,SBDL_Parser.Types.effect:_A,SBDL_Parser.Types.current_control:_A,SBDL_Parser.Types.current_detection:_A,SBDL_Parser.Types.test:_A,SBDL_Parser.Types.action_control:_A,SBDL_Parser.Types.action_detection:_A},SBDL_Parser.Types.cause:{SBDL_Parser.Types.requirement:_A,SBDL_Parser.Types.mode:_A,SBDL_Parser.Types.current_control:_A,SBDL_Parser.Types.current_detection:_A,SBDL_Parser.Types.test:_A,SBDL_Parser.Types.action_control:_A,SBDL_Parser.Types.action_detection:_A,SBDL_Parser.Types.event:_A},SBDL_Parser.Types.effect:{SBDL_Parser.Types.requirement:_A},SBDL_Parser.Types.aspect:dict.fromkeys((v for(k,v)in vars(SBDL_Parser.Types).items()if not k.startswith('__')),_A),SBDL_Parser.Statements.using:dict.fromkeys((v for(k,v)in vars(SBDL_Parser.Types).items()if not k.startswith('__')),_A),SBDL_Parser.Statements.scope:dict.fromkeys((v for(k,v)in vars(SBDL_Parser.Types).items()if not k.startswith('__')),_A),SBDL_Parser.Statements.customtype:dict.fromkeys((v for(k,v)in vars(SBDL_Parser.Types).items()if not k.startswith('__')),_A),SBDL_Parser.Types.trace:{SBDL_Parser.Types.cause:_A,SBDL_Parser.Types.mode:_A,SBDL_Parser.Types.effect:_A,SBDL_Parser.Types.current_control:_A,SBDL_Parser.Types.function:_A,SBDL_Parser.Types.transition:_A,SBDL_Parser.Types.event:_A,SBDL_Parser.Types.state:_A,SBDL_Parser.Types.interface:_A},SBDL_Parser.Types.test:{SBDL_Parser.Types.requirement:_A,SBDL_Parser.Types.definition:_A,SBDL_Parser.Types.realisation:_A,SBDL_Parser.Types.current_detection:_A},SBDL_Parser.Types.definition:{SBDL_Parser.Types.requirement:_A,SBDL_Parser.Types.function:_A,SBDL_Parser.Types.state:_A,SBDL_Parser.Types.usecase:_A,SBDL_Parser.Types.interface:_A,SBDL_Parser.Types.current_control:_A,SBDL_Parser.Types.current_detection:_A,SBDL_Parser.Types.action_control:_A,SBDL_Parser.Types.action_detection:_A},SBDL_Parser.Types.realisation:{SBDL_Parser.Types.requirement:_A,SBDL_Parser.Types.definition:_A,SBDL_Parser.Types.function:_A,SBDL_Parser.Types.state:_A,SBDL_Parser.Types.interface:_A},SBDL_Parser.Types.function:{SBDL_Parser.Types.event:_A,SBDL_Parser.Types.function:_A,SBDL_Parser.Types.usecase:_A,SBDL_Parser.Types.interface:_A,SBDL_Parser.Types.requirement:_A},SBDL_Parser.Types.transition:{SBDL_Parser.Types.state:_A,SBDL_Parser.Types.event:_A},SBDL_Parser.Types.usecase:{SBDL_Parser.Types.requirement:_A},SBDL_Parser.Types.interface:{SBDL_Parser.Types.requirement:_A,SBDL_Parser.Types.interface:_A},SBDL_Parser.Types.requirement:{SBDL_Parser.Types.current_control:_A}};global_properties={SBDL_Parser.Attributes.description:_A,SBDL_Parser.Attributes.remark:_A,SBDL_Parser.Attributes.reference:_C,SBDL_Parser.Attributes.tag:_A,SBDL_Parser.Attributes.pragma:_C};type_properties={SBDL_Parser.Types.aspect:{SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.related,SBDL_Parser.Attributes.color},SBDL_Parser.Types.requirement:{SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.related,SBDL_Parser.Attributes.color},SBDL_Parser.Types.mode:{SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.detectability,SBDL_Parser.Attributes.detectability_post},SBDL_Parser.Types.effect:{SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.severity},SBDL_Parser.Types.cause:{SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.occurrence,SBDL_Parser.Attributes.occurrence_post},SBDL_Parser.Types.function:{SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.precondition,SBDL_Parser.Attributes.postcondition,SBDL_Parser.Attributes.invariant,SBDL_Parser.Attributes.input,SBDL_Parser.Attributes.output,SBDL_Parser.Attributes.color},SBDL_Parser.Types.state:{SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.color},SBDL_Parser.Types.usecase:{SBDL_Parser.Attributes.actor},SBDL_Parser.Types.event:{SBDL_Parser.Attributes.precondition,SBDL_Parser.Attributes.postcondition,SBDL_Parser.Attributes.invariant,SBDL_Parser.Attributes.output,SBDL_Parser.Attributes.condition,SBDL_Parser.Attributes.condition_alternative,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.control_only,SBDL_Parser.Attributes.return_control,SBDL_Parser.Attributes.color},SBDL_Parser.Types.interface:{SBDL_Parser.Attributes.parent},SBDL_Parser.Types.definition:{SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.related},SBDL_Parser.Types.realisation:{SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.related}};keyword_information={SBDL_Parser.Tokens.declaration:'Defines the entity on the left to be the immutable content of the expression on the right',SBDL_Parser.Tokens.using:'Defines element properties to be used for all subsequent elements in the compilation unit, or until reset with the same statement',SBDL_Parser.Tokens.depends:'Content in other units on which the currurent unit depends',SBDL_Parser.Tokens.scope:'Defines property prefixes to be used for all subsequent elements in the compilation unit, or until reset with the same statement',SBDL_Parser.Tokens.customtype:'Defines a custom element type which extends one or more types from based model or other custom types',SBDL_Parser.Tokens.copy_element:"Defines that the element is based on a copy of another element's definition"};type_information={SBDL_Parser.Types.aspect:SBDL_Parser.mk_type_info_struct(_p,'Unit of system decomposition. May associate with a variety of perspectives: logical, functional ...'),SBDL_Parser.Types.requirement:SBDL_Parser.mk_type_info_struct(_p,'Requirement (or acceptance criteria) definition. Describes an expected behaviour at the associated level of abstraction.'),SBDL_Parser.Types.mode:SBDL_Parser.mk_type_info_struct(_S,'Failure Mode. Describes the way in which a failure may occur, from the perspective of a requirement.'),SBDL_Parser.Types.effect:SBDL_Parser.mk_type_info_struct(_S,'Failure Effect defintion. Describes the consequence of a failure mode.'),SBDL_Parser.Types.cause:SBDL_Parser.mk_type_info_struct(_S,'Failure Cause. An underlying technical mechanism, scenario or sequence of events that may result in a failure mode.'),SBDL_Parser.Types.current_control:SBDL_Parser.mk_type_info_struct(_S,'Existing controls which are present to prevent a failure cause either from occurring or leading to its associated failure mode.'),SBDL_Parser.Types.current_detection:SBDL_Parser.mk_type_info_struct(_S,'Existing detections (tests) which are present to measure (before release) the occurence of a failure mode.'),SBDL_Parser.Types.action_control:SBDL_Parser.mk_type_info_struct(_S,'Necessary steps that remain to be taken in order to prevent the occurance of a failure cause or mode.'),SBDL_Parser.Types.action_detection:SBDL_Parser.mk_type_info_struct(_S,'Necessary steps that remain to be taken in order to increase the detectability of a failure mode.'),SBDL_Parser.Types.test:SBDL_Parser.mk_type_info_struct(_B1,'Instance of a test for a particular part of a design.'),SBDL_Parser.Types.definition:SBDL_Parser.mk_type_info_struct(_B2,'Prescriptive definition of a particular aspect of design.'),SBDL_Parser.Types.realisation:SBDL_Parser.mk_type_info_struct(_B2,'Final realisation of a particular aspect of prescriptive design.'),SBDL_Parser.Types.function:SBDL_Parser.mk_type_info_struct(_B3,'Definition of a function.'),SBDL_Parser.Types.event:SBDL_Parser.mk_type_info_struct(_B3,'Definition of a dynamic event. May be a step within a broader function or cause a transition between states. Events may be composed as trees, with an event also entailing all of its children. Decisions can be expressed with the condition property; unmet conditions entail the alternative (property) children instead of default children.'),SBDL_Parser.Types.state:SBDL_Parser.mk_type_info_struct(_B4,'Definition of a state.'),SBDL_Parser.Types.transition:SBDL_Parser.mk_type_info_struct(_B4,'Definition of a transition between states. Takes an ordered pair of states (from,to).'),SBDL_Parser.Types.usecase:SBDL_Parser.mk_type_info_struct(_p,'Definition of a usecase within a particular abstraction of a the system.'),SBDL_Parser.Types.interface:SBDL_Parser.mk_type_info_struct(_p,'Definition of an interface exposing behaviour externally from the given abstraction.'),SBDL_Parser.Types.trace:SBDL_Parser.mk_type_info_struct(_B1,'A dyanmic occurance of element instance (for example, an event or failure cause). Intended to be embedded within log files. Can be used to build and validate dynamic behaviour against the statically defined behaviour model.'),SBDL_Parser.Types.group:SBDL_Parser.mk_type_info_struct(_p,'Syntactic group of model elements. Used only to structure model representation and facilitate filtering; has no semantic implications for the model itself. May parent (contain) any other element type.')};category_descriptions={_p:'Architectural elements are used for specifying system decomposition. Such decompositions may be according to different views/schemes. for example: logical/structural, functional ...',_S:'Failure Mode and Effect Analysis (FMEA) elements are used to describe risks present in a system, and the controls and actions mitigating them.',_B2:'Design elements elaborate on the prescription and description of design decisions and their technical concepts.',_B1:'Testing elements describe the implementation and coverage of tests (including dynamic traces).',_B3:'Dynamic elements describe and structure dynamic system behaviours, such as functions and events.',_B4:'State elements capture the stateful behaviour of the system by offering a stateful view of dynamic elements.'};property_descriptions={SBDL_Parser.Attributes.description:'[string] Descriptive body of text for the given element.',SBDL_Parser.Attributes.detectability:'[number] Numerical rating on the interval [1(best)..10(worst)] indicating quality of a failure detection.',SBDL_Parser.Attributes.detectability_post:'[number] Numerical rating on the interval [1(best)..10(worst)] indicating quality of a failure detection AFTER realisation of an improvement action.',SBDL_Parser.Attributes.severity:'[number] Numerical rating on the interval [1(least)..10(most)] indicating the severity of a failure effect.',SBDL_Parser.Attributes.occurrence:'[number] Numerical rating on the interval [1(infrequent)..10(always)] indicating the probability of a failure cause occurring.',SBDL_Parser.Attributes.occurrence_post:'[number] Numerical rating on the interval [1(infrequent)..10(always)] indicating the probability of a failure cause occurring AFTER realisation of an improvement action.',SBDL_Parser.Attributes.parent:'[identifier] Hierarchical parent of the given element.',SBDL_Parser.Attributes.child:'[identifier] Hierarchical child of the given element.',SBDL_Parser.Attributes.related:'[identifier] Indicates a related element.',SBDL_Parser.Attributes.remark:'[string] General remark or more extensive information.',SBDL_Parser.Attributes.actor:'[string] Indicates the name of an actor affiliated with the given element.',SBDL_Parser.Attributes.tag:'[string] Comma separated list of tags which indicate some property of the given element.',SBDL_Parser.Attributes.precondition:'[string] Precondition for correct behaviour of the given element.',SBDL_Parser.Attributes.postcondition:"[string] Condition resulting from the given element's behaviour.",SBDL_Parser.Attributes.invariant:'[string] Conditions unaffected by the behaviour of the given element.',SBDL_Parser.Attributes.output:'[string] Output produced by the given element.',SBDL_Parser.Attributes.input:'[string] Input consumed by the given element.',SBDL_Parser.Attributes.condition:'[string] Element is conditional on the specified binary decision.',SBDL_Parser.Attributes.condition_alternative:'[identifier] Specifies an alternative for an unmet condition.',SBDL_Parser.Attributes.color:'[string] Color associated with element.',SBDL_Parser.Attributes.control_only:'[*] Presence indicates control flow only',SBDL_Parser.Attributes.return_control:'[*] Presence indicates immediate return of control flow'};directive_descriptons={SBDL_Parser.Macros.self_element:'[-> SBDL_Element] Element object for the current element (for internal cross-referencing)',SBDL_Parser.Macros.self_reference:'[-> string] Identifier of the current element (when embedded within an element property)',SBDL_Parser.Macros.self_reference_attr:"[-> string] Identifier of the current element's property (when embedded within an element property)",SBDL_Parser.Macros.abort:'[string ->] Abort compilation with error',SBDL_Parser.Macros.message:'[string ->] Show a message (on stdout) during compilation',SBDL_Parser.Macros.msg:'[string -> string] Show a message (on stdout) during compilation and replace occurence with the message inline',SBDL_Parser.Macros.date:"[-> string] Today's date",SBDL_Parser.Macros.add:'[int, ... -> int] Sum of arguments',SBDL_Parser.Macros.sub:'[int, ... -> int] Subtraction of subsequent arguments from the first argument',SBDL_Parser.Macros.equal:'[val, val ->] Raise a compiler error if two value arguments are not equal',SBDL_Parser.Macros.concat:'[string, ... -> string] Concatentate string arguments',SBDL_Parser.Macros.instli:'[string, ... -> string] Index a list of string terms separated by whitespaces',SBDL_Parser.Macros.showall:'[->] Show a message (on stdout) displaying all defined macros',SBDL_Parser.Macros.rmcom:'[string -> string] Remove comments from a string',SBDL_Parser.Macros.mkid:'[string -> string] Make a given string a valid SBDL identifier',SBDL_Parser.Macros.dfp:'[string -> string] Return a string defining a description as the previous line',SBDL_Parser.Macros.dsl_version:'[->string] DSL version string',SBDL_Parser.Macros.compiler_version:'[->] Compiler version string',SBDL_Parser.Macros.requiredsl:'[string ->] Raise a compiler error if the DSL version is not at least equal to the argument',SBDL_Parser.Macros.requiredsl_exact:'[string ->] Raise a compiler error if the DSL version is not exactly equal to the argument',SBDL_Parser.Macros.requirecompiler:'[string ->] Raise a compiler error if the current compiler version is not at least equal to the argument',SBDL_Parser.Macros.path:'[-> string] Path of the current file',SBDL_Parser.Macros.file_name:'[-> string] Name of the current file',SBDL_Parser.Macros.directory:'[-> string] Name of the current directory',SBDL_Parser.Macros.context:'[-> string] Context string (embedded statement)',SBDL_Parser.Macros.curline:'[-> string] Current line (embedded statement)',SBDL_Parser.Macros.preline:'[-> string] Previous line (embedded statement)',SBDL_Parser.Macros.sucline:'[-> string] Next line (embedded statement)',SBDL_Parser.Macros.line:'[-> string] Current line number',SBDL_Parser.Macros.import_sbdl:'[string ->] Import contents of another SBDL file',SBDL_Parser.Macros.import_directive:'[string ->] Import a custom directives file',SBDL_Parser.Macros.load_config:'[string ->] Load a compiler configuration file',SBDL_Parser.Macros.git_commit_hash:"[string -> string] Return the current (or stated path) Git repository's short commit hash",SBDL_Parser.Macros.id_from_prop:'[string, string -> string] Takes a property name and value. Returns the identifier list of elements with matching properties (which may be an empty string)',SBDL_Parser.Macros.define:'[string, string->] Create a key,value pair compiler definition',SBDL_Parser.Macros.define_append:'[string, string ->] Append to a named definition',SBDL_Parser.Macros.define_from_func:'[string, string, string ->] Create a definition from the result of a named compiler function application',SBDL_Parser.Macros.define_from_index:'[string, string, string ->] Create a definition from the result of indexing another name definition',SBDL_Parser.Macros.define_from_file:'[string, string ->] Create a definition from the contents of a named file',SBDL_Parser.Macros.define_from_file_hash:'[string, string ->] Create a definition from the hash of the contents of a named file',SBDL_Parser.Macros.expand_define:"[string -> string] 'Expand' and sanitize a named definition",SBDL_Parser.Macros.parsed_elements:'[->] Set of parsed elements (useable only programatically)',SBDL_Parser.Macros.generated_elements:'[->] Set of output elements generated by other directives (useable only programmatically)',SBDL_Parser.Macros.synthetic_element:'[->] Synthetic object class reference, used to generate elements dynamically (usable only programmatically)',SBDL_Parser.Macros.cross_reference_available:'[-> bool] Indicates whether cross references are available',SBDL_Parser.Macros.extend_cause:'[string, int -> string] In a failure cause to indicate it extends a name higher-level failure cause, args: [higher-cause, detectability] return [failure-mode-identifier]',SBDL_Parser.Macros.cpp_class:'[-> string] Most recently defined C++ Class (embedded statement)',SBDL_Parser.Macros.py_class:'[-> string] Most recently defined Python Class (embedded statement)',SBDL_Parser.Macros.c_func:'[-> string] Most recently defined C/C++ function (embedded statement)',SBDL_Parser.Macros.py_func:'[-> string] Most recently defined Python function (embedded statement)',SBDL_Parser.Macros.prolog_startup:'[->] Startup the Prolog environment (required at least once to use Prolog directives; indempotent)',SBDL_Parser.Macros.prolog_assert:'[string ->] Assert the truth of a Prolog query (throw a compiler error if not)',SBDL_Parser.Macros.prolog_assert_n:'[string ->] Assert a Prolog query as false  (throw a compiler error if not)',SBDL_Parser.Macros.prolog_cmd:'[string ->] Issue a Prolog statement',SBDL_Parser.Macros.prolog_trace_file:'[string->] Write all generated Prolog facts, assertions and commands to a named file (for external use in Prolog)',SBDL_Parser.Macros.prolog_result_output:'[->] Output the result of Prolog assertions to stdout'};custom_types={}
	@classmethod
	def get_valid_link_type_for_type_name(self,type_name):
		valid_type_links=[];skip_types=[SBDL_Parser.Statements.using,SBDL_Parser.Statements.depends,SBDL_Parser.Statements.scope,_c]
		if type_name in self.type_links:valid_type_links.extend([x for x in self.type_links[type_name].keys()if isinstance(x,str)])
		for other_type_name in self.type_links:
			if type_name in self.type_links[other_type_name]:valid_type_links.append(other_type_name)
		valid_type_links=[x for x in valid_type_links if not x in skip_types];return valid_type_links
	@classmethod
	def get_language_information_struct(self):
		lang_info_struct={_h:{}};lang_info_struct[_b]=self.type_information;lang_type_struct=lang_info_struct[_b];categories=lang_info_struct[_h];skip_properties=[SBDL_Parser.Attributes.pragma,SBDL_Parser.Attributes.reference]
		for type_name in lang_type_struct:
			lang_type_struct[type_name][_B0]=self.get_valid_link_type_for_type_name(type_name);valid_properties=list(self.global_properties.keys())
			if type_name in self.type_properties:valid_properties.extend(list(self.type_properties[type_name]))
			lang_type_struct[type_name][_A1]=[p for p in valid_properties if not p in skip_properties];category=lang_type_struct[type_name][_As]
			if not category in categories:categories[category]={_O:self.category_descriptions[category],_b:{}}
			categories[category][_b][type_name]=_A
		lang_info_struct[_A1]=self.property_descriptions;lang_info_struct[_B5]=dict.fromkeys([x for x in vars(SBDL_Parser.Macros).values()if isinstance(x,str)and x!=_c and x!=_AK],'No Description')
		for directive_description in self.directive_descriptons:lang_info_struct[_B5][directive_description]=self.directive_descriptons[directive_description]
		lang_info_struct['CLI']=help_text();return lang_info_struct
	@classmethod
	def type_color(self,type_name):
		E='#FFFF00';D='#FF6347';C='#FF69B4';B='#9370DB';A='#1E90FF';color_list=['#FFC0CB','#FFA07A','#FFD700','#32CD32','#00FFFF',A,B,C,D,E,'#008000','#00FF7F','#00CED1','#00BFFF',A,B,C,D,'#FF4500',E,'#9ACD32','#ADFF2F','#7FFFD4','#AFEEEE','#ADD8E6','#87CEEB','#8A2BE2','#FF00FF','#FF1493'];color_index_map=get_config_value(_Bc)
		if not type_name in color_index_map:
			new_color='#FFFFFF'
			if len(color_index_map.keys())<len(color_list):new_color=color_list[len(color_index_map)]
			color_index_map[type_name]=new_color
		return color_index_map[type_name]
	@classmethod
	def calc_bsci(self,statement_text,language):
		bsci_value=.0;sentence_tokens=[_P];clausal_tokens=[_L,';','(',_g];conjunction_tokens=['for','and','nor','but','or','yet','so','either','neither','both','whether','rather','after','as long as','as soon as','before','now','since','till','until','when','whenever','while','within'];exclude_length_threshold=3;processed_statement_text=_I.join([x for x in statement_text.split()if len(x)>exclude_length_threshold or x in conjunction_tokens+clausal_tokens]);sentences=[processed_statement_text]
		for sentence_token in sentence_tokens:
			new_sentences=[]
			for sentence in sentences:new_sentences.extend([x for x in sentence.split(sentence_token)if len(x)>0])
			sentences=new_sentences
		number_of_sentences=len(sentences);unique_words_dict={};conjunctions_count=0;clause_count=1;words_count=0
		for word in _I.join(sentences).split():
			word_p=word.lower()
			for clausal_token in clausal_tokens:
				if clausal_token in word_p:clause_count+=1;word_p=word_p.replace(clausal_token,'')
			for conjunction_token in conjunction_tokens:
				if conjunction_token in word_p:conjunctions_count+=1;word_p=word_p.replace(conjunction_token,'')
			if len(word_p)>exclude_length_threshold:words_count+=1;unique_words_dict[word_p]=_A
		bsci_value=len(unique_words_dict)*(conjunctions_count+clause_count);return bsci_value
	@classmethod
	def get_identifier_from_string(self,input_string,aggressive_length=20,min_word_len=3,separator='',stop_words=_B,stop_chars=_B,abbreviations=_B,right_to_left=_A):
		def excess_length(word_list):return len(''.join(word_list))-aggressive_length
		if stop_words is _B:stop_words={}
		if stop_chars is _B:stop_chars={}
		if abbreviations is _B:abbreviations={}
		text_words=[x.lower()if not x.isupper()else x for x in input_string.split()]
		if right_to_left:text_words.reverse()
		def transform_word_if_necessary(transform_function):
			nonlocal text_words
			for word_pos in range(len(text_words)):
				if excess_length(text_words)<=0:break
				current_word=text_words[word_pos]
				if len(current_word)>min_word_len:text_words[word_pos]=transform_function(current_word)
		transform_word_if_necessary(lambda x:x if x not in stop_words else'');transform_word_if_necessary(lambda x:abbreviations[x]if x in abbreviations else x)
		def remove_stop_chars(word):
			if len(word)>1:
				for stop_char in stop_chars:word=word[0]+word[1:].replace(stop_char,'')
			return word
		transform_word_if_necessary(remove_stop_chars)
		if right_to_left:text_words.reverse()
		text_words=[x.capitalize()if not x.isupper()else x for x in text_words];return SBDL_Parser.sanitize_identifier(separator.join(text_words))
	@classmethod
	def get_identifier_from_string_eng_standard_v2(self,input_string,aggressive_length=20,min_word_len=3,separator='',right_to_left=_A):stop_words={'the','and','of','to','in','a','on','for','with','at','by','an','shall','should','must','will','can','may','might','could','be','have','use','then'};stop_chars={'a','e','i','o','u'};abbreviations={_Cm:'accel','actuator':'act',_Cn:'aero','analysis':'anal',_Co:'approx',_Cp:'arch','assembly':'assy',_Cq:'aux','battery':'batt',_Cr:'calc',_Cs:'cg',_Ct:'cm',_Cu:'coeff',_Cv:_B6,_Cw:_B6,_i:'ctrl',_Cx:'cntrlr',_Cy:'coord',_Cz:'corr','current':'curr',_C_:'daq','decibel':'db',_D0:'dof','define':'def','design':'des',_D1:'dev','diameter':'dia',_D2:'dim','dynamic':'dyn',_D3:'eff',_D4:'elec',_D5:'em','error':'err','estimate':'est',_D6:'eval','execute':'exec',_D7:_q,'fatigue':'fat','feature':'feat','feedback':'fb',_D8:'freq',_AD:'func','gradient':'grad','hardware':'hw',_D9:'ht',_DA:'hv',_Av:_r,_DB:_r,_DC:'impl',_Ay:'in',_DD:'insp',_DE:'instr',_DF:'intg',_x:'intf',_DG:'iter','load':'ld','logic':'log',_DH:'maint','manage':'mg',_DI:'mgmt',_DJ:'mfg','material':'matl','maximum':'max',_DK:'meas',_DL:'mech','minimum':'min','model':'mdl','momentum':'mom','nominal':'nom',_DM:'os',_DN:'opt',_Ax:'out',_DO:'param','perform':'perf',_DP:'perf','physics':'phys','position':'pos','power':'pwr','pressure':'pres','process':'proc',_DQ:'pid',_DR:'qa',_DS:'rad',_DT:'redund',_Au:'ref',_DU:'rel',_W:'req',_DV:'res',_DW:'resist','safety':'sfty','sensor':'sens',_DX:'sim','software':'sw',_DY:'spec',_DZ:'stab','static':'stat',_Da:'struc',_Db:'subsys',_A3:'sys',_Dc:'temp','tension':'tens','testing':'test','thermal':'therm','torque':'torq',_Dd:'trans',_De:'uncert',_Df:'val',_Dg:'verif','voltage':'volt','weight':'wt','target':'targ'};return self.get_identifier_from_string(re.sub(_Dh,'',input_string),aggressive_length,min_word_len,separator,stop_words=stop_words,stop_chars=stop_chars,abbreviations=abbreviations,right_to_left=right_to_left)
	@classmethod
	def get_identifier_from_string_eng_standard_v1(self,input_string,aggressive_length=20,min_word_len=3,separator='',right_to_left=_A):stop_words={'the','and','of','to','in','a','on','for','with','at','by','an',_A3,'shall','should','must','will','can','may','might','could','be','have','use','perform','execute','define',_Cv,'manage','then'};stop_chars={'a','e','i','o','u'};abbreviations={_Cm:'accel','actuator':'act',_Cn:'aero','analysis':'anal',_Co:'approx',_Cp:'arch','assembly':'assy',_Cq:'aux','battery':'batt',_Cr:'calc',_Cs:'cg',_Ct:'cm',_Cu:'coeff',_Cw:_B6,_i:'ctrl',_Cx:'cntrlr',_Cy:'coord',_Cz:'corr','current':'curr',_C_:'daq','decibel':'db',_D0:'dof','design':'des',_D1:'dev','diameter':'dia',_D2:'dim','dynamic':'dyn',_D3:'eff',_D4:'elec',_D5:'em','error':'err','estimate':'est',_D6:'eval',_D7:_q,'fatigue':'fat','feature':'feat','feedback':'fb',_D8:'freq',_AD:'func','gradient':'grad','hardware':'hw',_D9:'ht',_DA:'hv',_Av:_r,_DB:_r,_DC:'impl',_Ay:'in',_DD:'insp',_DE:'instr',_DF:'intg',_x:'intf',_DG:'iter','load':'ld','logic':'log',_DH:'maint',_DI:'mgmt',_DJ:'mfg','material':'matl','maximum':'max',_DK:'meas',_DL:'mech','minimum':'min','model':'mdl','momentum':'mom','nominal':'nom',_DM:'os',_DN:'opt',_Ax:'out',_DO:'param',_DP:'perf','physics':'phys','position':'pos','power':'pwr','pressure':'pres','process':'proc',_DQ:'pid',_DR:'qa',_DS:'rad',_DT:'redund',_Au:'ref',_DU:'rel',_W:'req',_DV:'res',_DW:'resist','safety':'sfty','sensor':'sens',_DX:'sim','software':'sw',_DY:'spec',_DZ:'stab','static':'stat',_Da:'struc',_Db:'subsys',_A3:'sys',_Dc:'temp','tension':'tens','testing':'test','thermal':'therm','torque':'torq',_Dd:'trans',_De:'uncert',_Df:'val',_Dg:'verif','voltage':'volt','weight':'wt','target':'targ'};return self.get_identifier_from_string(re.sub(_Dh,'',input_string),aggressive_length,min_word_len,separator,stop_words=stop_words,stop_chars=stop_chars,abbreviations=abbreviations,right_to_left=right_to_left)
	@classmethod
	def get_identifier_from_string_eng_standard(self,input_string,aggressive_length=20,min_word_len=3,separator='',right_to_left=_A):return self.get_identifier_from_string_eng_standard_v1(input_string=input_string,aggressive_length=aggressive_length,min_word_len=min_word_len,separator=separator,right_to_left=right_to_left)
	@classmethod
	def shorten_string_on_word_boundary(self,input_string,shorten_length):return _I.join(input_string.split()[-len(input_string[-shorten_length:].split()):])
	@classmethod
	def get_unique_id_from_string(self,input_string):
		digest=hashlib.sha256(input_string.encode('utf‑8')).digest();alphabet=get_config_value(_Cf);length=get_config_value(_Ce,number=_A);num=int.from_bytes(digest,'big');base=len(alphabet);smart_key=[]
		while len(smart_key)<length:num,idx=divmod(num,base);smart_key.append(alphabet[idx])
		return''.join(smart_key)
	@classmethod
	def create_custom_embedded_function(self,function_content,argument_list):
		if not get_config_value(_Ab,number=_A):raise Exception('Embedded function definition is disabled (but requested)')
		embedded_function=_B;embedded_hasbang='#!python'
		if function_content.startswith(embedded_hasbang):
			try:
				function_content=function_content.removeprefix(embedded_hasbang).strip().replace(_H,_D);func_def=f"def _custom_function({_L.join(argument_list)}):\n"
				for line in function_content.splitlines():func_def+='    '+line+_D
				local_namespace={};exec(func_def,{},local_namespace);embedded_function=local_namespace['_custom_function']
			except Exception as e:raise Exception(f"Custom function definition error: {e}")from e
		else:raise Exception(f"Custom function does not start with expected prefix ({embedded_hasbang}):\n{function_content}")
		return embedded_function
	@classmethod
	def create_customtype_from_prototype(self,prototype):
		def create_customtype_element_class(new_type_name,base_type_classes,req_attrs,base_attrs,base_links,parent_links,child_links,related_links,custom_validator_function):
			class SBDL_Custom_Element_Class(*base_type_classes):
				@classmethod
				def class_type_name(self):return new_type_name
				def __init__(self,declaration_statement):
					self._set_type(new_type_name);self._set_required_attributes(list(req_attrs.keys()),extend=_A);super().__init__(declaration_statement);new_properties=base_attrs.copy();new_properties.update(self._properties);new_properties={x:str(y).replace(SBDL_Parser.Tokens.macro_delimeters[0]+SBDL_Parser.Tokens.macro_defer,SBDL_Parser.Tokens.macro_delimeters[0]+SBDL_Parser.Tokens.macro_variable)for(x,y)in new_properties.items()};self._properties=new_properties
					for base_link in base_links:self._gather_links_from_attr_type_source(base_link,base_links,self._links)
					for base_link in parent_links:self._gather_links_from_attr_type_source(base_link,parent_links,self._parents)
					for base_link in child_links:self._gather_links_from_attr_type_source(base_link,child_links,self._children)
					for base_link in related_links:self._gather_links_from_attr_type_source(base_link,related_links,self._related)
					self._custom_validator=custom_validator_function
				def validate_self(self,elements=_B):
					is_valid,error=super().validate_self(elements);req_rel_count={};req_rel_class={}
					for(type_link,type_link_value)in SBDL_Semantics.type_links[self.class_type_name()].items():
						if type(type_link_value)is int:req_rel_count[type_link]=0;req_rel_class[type_link]=self.declaration_type_to_class(type_link)
					for link in self.links():
						for req_rel_type in req_rel_count:
							if link.is_a(req_rel_class[req_rel_type]):req_rel_count[req_rel_type]+=1
					for(req_rel_type,count_val)in req_rel_count.items():
						if count_val==0:is_valid=_C;error+=f"Missing required relation: {req_rel_type}; "
					if self._custom_validator is not _B:
						custom_validator_result=self._custom_validator(self,elements)
						if isinstance(custom_validator_result,bool)and custom_validator_result is _A:0
						else:
							is_valid=_C
							if isinstance(custom_validator_result,str):error+=custom_validator_result+'; '
					return is_valid,error
			return SBDL_Custom_Element_Class
		base_type_names=[t.strip()for t in prototype.data_type().split(SBDL_Parser.Tokens.customtype_union)];new_type_name=prototype.identifier();base_type_classes=[];new_type_class=_B;builtin_types=vars(SBDL_Parser.Types).values();base_attrs={};req_attrs={};base_links={};parent_links={};child_links={};related_links={};custom_validator=_B
		for base_type_name in base_type_names:
			base_type_class=_B
			if base_type_name in builtin_types:base_type_class=SBDL_Element.declaration_type_to_class(base_type_name)
			elif base_type_name in self.custom_types:base_type_class=self.custom_types[base_type_name][1]
			else:raise Exception('Invalid base type: "{}"'.format(base_type_name))
			base_type_classes.append(base_type_class)
			if new_type_name in builtin_types or new_type_name in self.custom_types:
				if new_type_name in self.custom_types and prototype.parser_element().reference()in self.custom_types[new_type_name]:0
				else:raise Exception('Redefinition of existing type: "{}"'.format(new_type_name))
			self.custom_types[new_type_name]=[prototype.parser_element().reference(),_B]
			if base_type_name in self.type_links:self.type_links[new_type_name]=self.type_links[base_type_name].copy()
			else:self.type_links[new_type_name]={}
			extra_links={}
			for link_type in self.type_links:
				for target_link_type in self.type_links[link_type]:
					if target_link_type==base_type_name:extra_links[link_type]=_A
			self.type_links[new_type_name].update(extra_links);new_type_properties=set()
			if base_type_name in self.type_properties:new_type_properties=set(self.type_properties[base_type_name])
			self.type_properties[new_type_name]=new_type_properties
		for attr in prototype.data():
			if attr in[SBDL_Parser.Attributes.custom_required_attr,SBDL_Parser.Attributes.custom_required_attr_old]:
				for req_attr in prototype.data()[attr].split(SBDL_Parser.Attributes.separator):req_attrs[req_attr]=_A;self.type_properties[new_type_name].add(req_attr)
			elif attr in[SBDL_Parser.Attributes.custom_optional_attr,SBDL_Parser.Attributes.custom_optional_attr_old]:
				for opt_attr in prototype.data()[attr].split(SBDL_Parser.Attributes.separator):self.type_properties[new_type_name].add(opt_attr)
			elif attr in[SBDL_Parser.Attributes.custom_link_types_old,SBDL_Parser.Attributes.custom_optional_link_types,SBDL_Parser.Attributes.custom_required_link_types]:
				for new_rel in prototype.data()[attr].split(SBDL_Parser.Attributes.separator):
					if not(new_rel in builtin_types or new_rel in self.custom_types):raise Exception(f'Unrecognised type link specificied: "{new_rel}"')
					elif attr==SBDL_Parser.Attributes.custom_required_link_types:self.type_links[new_type_name][new_rel]=1
					else:self.type_links[new_type_name][new_rel]=_A
			elif attr in dict.fromkeys(vars(SBDL_Parser.Types).values(),_A)or attr in SBDL_Semantics.custom_types:base_links[attr]=prototype.data()[attr]
			elif attr==SBDL_Parser.Attributes.custom_validator:
				if get_config_value(_Ab,number=_A):custom_validator=self.create_custom_embedded_function(prototype.data()[attr],['self','elements'])
				else:f_print(f"Embedded functions are disabled; custom type validator ignored for: {new_type_name}",warning=_A)
			elif attr==SBDL_Parser.Attributes.parent:parent_links[attr]=prototype.data()[attr]
			elif attr==SBDL_Parser.Attributes.child:child_links[attr]=prototype.data()[attr]
			elif attr==SBDL_Parser.Attributes.related:related_links[attr]=prototype.data()[attr]
			else:self.type_properties[new_type_name].add(attr);base_attrs[attr]=prototype.data()[attr]
		new_type_class=create_customtype_element_class(new_type_name,base_type_classes,req_attrs,base_attrs,base_links,parent_links,child_links,related_links,custom_validator);new_type_class.Base_Prototype=prototype.copy();self.custom_types[new_type_name][1]=new_type_class
class SBDL_Parsed_Object(SBDL_Parser.Parser_Hints):
	def throw_error(self,err=_A2):raise Exception('Abstract parser object called: "{}" on {}'.format(err,self))
	def parser_element(self):self.throw_error('parser_element()')
	def error(self):self.throw_error('error()')
	def error_message(self):self.throw_error('error_message()')
	def string(self):self.throw_error('string()')
	def type(self):self.throw_error('type()')
	def copy(self):self.throw_error('copy()')
	def syntactic_parent(self):self.throw_error('parent()')
class SBDL_Statement(SBDL_Parsed_Object):
	def __init__(self,parser_element,statement_type_string,data_dictionary,error=_C,error_message='',syntactic_parent=_B,error_pos=0):self.__parser_element=parser_element;self.__statement_type=str(statement_type_string);self.__data_dictionary=dict(data_dictionary);self.__error=bool(error);self.__error_pos=error_pos;self.__error_message=error_message;self.__syntactic_parent=syntactic_parent
	def parser_element(self):return self.__parser_element.copy()
	def type(self):return self.__statement_type
	def data_type(self):0
	def data(self):
		result=self.__data_dictionary.copy()
		if self.syntactic_parent():
			if SBDL_Parser.Attributes.parent in result:result[SBDL_Parser.Attributes.parent]+=SBDL_Parser.Attributes.separator+self.syntactic_parent().identifier()
			else:result[SBDL_Parser.Attributes.parent]=self.syntactic_parent().identifier()
		return result
	def error(self):return self.__error
	def error_position(self):return self.__error_pos
	def error_message(self):return self.__error_message
	def extend_error_message(self,extension):self.__error_message+=extension
	def copy(self):return SBDL_Statement(self.parser_element(),self.type(),self.data(),self.error(),self.error_message(),self.syntactic_parent())
	def inherit_data(self,parent_data):self.__data_dictionary={**parent_data,**self.__data_dictionary}
	def apply_scope(self,scope_data):
		scope_map={}
		for(scope_item,scope_value)in scope_data.items():
			if scope_item in self.__data_dictionary:new_data_value=scope_value+SBDL_Parser.Tokens.scope_prefix+self.__data_dictionary[scope_item];scope_map[self.__data_dictionary[scope_item]]=new_data_value;self.__data_dictionary[scope_item]=new_data_value
		return scope_map
	def identifier(self):return'{}_{}'.format(self.__statement_type,self.__parser_element.reference())
	def syntactic_parent(self):return self.__syntactic_parent
class SBDL_Operator(SBDL_Statement):
	def __init__(self,parser_element,statement_type_string,data_dictionary=_B,error=_C,error_message='',error_pos=0):
		if data_dictionary==_B:data_dictionary={}
		super().__init__(parser_element,statement_type_string,data_dictionary,error,error_message,error_pos=error_pos)
	def data_type(self):return SBDL_Parser.Statements.operator
	def string(self):return self.parser_element.content()
	def copy(self):return SBDL_Operator(self.parser_element(),self.data_type(),self.data(),self.error(),self.error_message())
class SBDL_Using_Statement(SBDL_Statement):
	def __init__(self,parser_element,statement_type_string,data_dictionary,error=_C,error_message='',error_pos=0):super().__init__(parser_element,statement_type_string,data_dictionary,error,error_message,error_pos=error_pos)
	def data_type(self):return SBDL_Parser.Statements.using
	def string(self):return SBDL_Parser.using_string(self)
	def copy(self):return SBDL_Using_Statement(self.parser_element(),self.data_type(),self.data(),self.error(),self.error_message())
	def identifier(self):return str(uuid.uuid4())
class SBDL_Depends_Statement(SBDL_Statement):
	def __init__(self,parser_element,statement_type_string,data_dictionary,error=_C,error_message='',error_pos=0):super().__init__(parser_element,statement_type_string,data_dictionary,error,error_message,error_pos=error_pos)
	def data_type(self):return SBDL_Parser.Statements.depends
	def string(self):return SBDL_Parser.depends_string(self)
	def copy(self):return SBDL_Depends_Statement(self.parser_element(),self.data_type(),self.data(),self.error(),self.error_message())
	def identifier(self):return str(uuid.uuid4())
class SBDL_Scope_Statement(SBDL_Statement):
	def __init__(self,parser_element,statement_type_string,data_dictionary,error=_C,error_message='',error_pos=0):super().__init__(parser_element,statement_type_string,data_dictionary,error,error_message,error_pos=error_pos)
	def data_type(self):return SBDL_Parser.Statements.scope
	def string(self):return SBDL_Parser.scope_string(self)
	def copy(self):return SBDL_Scope_Statement(self.parser_element(),self.data_type(),self.data(),self.error(),self.error_message())
	def identifier(self):return str(uuid.uuid4())
class SBDL_Declaration_Statement(SBDL_Statement):
	def __init__(self,parser_element,identifier,declaration_type,data_dictionary,error=_C,error_message='',syntactic_parent=_B,error_pos=0):
		error_found=error;error_content=error_message
		try:SBDL_Parser.sanitize_identifier(identifier,_A)
		except Exception as e:error_found=_A;error_content='{}; {}'.format(error_content,str(e))
		self.__identifier=SBDL_Parser.sanitize_identifier(identifier);self.__declaration_type=declaration_type;super().__init__(parser_element,SBDL_Parser.Statements.declaration,data_dictionary,error_found,error_content,syntactic_parent,error_pos=error_pos)
	def data_type(self):return self.__declaration_type
	def identifier(self):return self.__identifier
	def apply_scope(self,scope_data):
		scope_map={}
		for(scope_item,scope_value)in scope_data.items():
			if scope_item==SBDL_Parser.Attributes.identifier:new_identifier=scope_value+SBDL_Parser.Tokens.scope_prefix+self.__identifier;scope_map[self.__identifier]=new_identifier;self.__identifier=new_identifier
		scope_map|=super().apply_scope(scope_data);return scope_map
	def string(self):return SBDL_Parser.declaration_string(self)
	def copy(self):copy_element=SBDL_Declaration_Statement(self.parser_element(),self.identifier(),self.data_type(),self.data(),self.error(),self.error_message(),self.syntactic_parent());copy_element.__dict__.update(self.__dict__);return copy_element
class SBDL_Customtype_Statement(SBDL_Declaration_Statement):
	def string(self):return SBDL_Parser.Tokens.customtype+SBDL_Parser.Tokens.whitespace_list[0]+super().string()
	def copy(self):return SBDL_Customtype_Statement(self.parser_element(),self.identifier(),self.data_type(),self.data(),self.error(),self.error_message(),self.syntactic_parent())
class SBDL_Element(SBDL_Parser.Parser_Hints):
	@classmethod
	def class_type_name(self):0
	class Element_Link:
		def __init__(self,identifier,type_name,do_sanitize=_A,identifier_is_origin=_B,stereotype_explicit=_B,target_stereotype=_B,hash_explicit=_B):
			identifier_l,stereotype=SBDL_Parser.identifier_stereotype(identifier);identifier_l,hashl=SBDL_Parser.identifier_hash(identifier_l);stereotype=stereotype_explicit if stereotype_explicit else stereotype;hashl=hash_explicit if hash_explicit else hashl
			if do_sanitize:SBDL_Parser.sanitize_identifier(identifier_l,_A)
			self.__link_name=identifier_l;self.__link_stereotype=stereotype;self.__type_name=type_name;self.__identifier_is_origin=identifier_is_origin;self.__target_stereotype=target_stereotype;self.__hash=hashl
		def validate_self(self,_):
			valid=_A;error=''
			if self.stereotype()and SBDL_Parser.Tokens.hash_separator in str(self.stereotype()):valid=_C;error='Stereotype contains hash reference: "{}" (hash should precede stereotype). '.format(self.stereotype())
			return valid,error
		def type(self):return self.__type_name
		def types(self):
			result=[self.type()]
			try:result=SBDL_Element.declaration_type_to_class(self.type()).types()
			except Exception as _:pass
			return result
		def identifier(self,with_stereotype=_C,with_hash=_C):identifier=self.__link_name;identifier=SBDL_Parser.identifier_hash_join(identifier,self.__hash)if with_hash else identifier;identifier=SBDL_Parser.identifier_stereotype_join(identifier,self.stereotype()if with_stereotype else _B);return identifier
		def stereotype(self,target=_C):return self.__link_stereotype if not target else self.__target_stereotype
		def hash(self):return self.__hash
		def identifier_is_origin(self):return self.__identifier_is_origin
		def copy(self):return SBDL_Element.Element_Link(self.identifier(with_stereotype=_A,with_hash=_A),self.type(),identifier_is_origin=self.identifier_is_origin())
		def is_a(self,target):
			result=_C
			try:
				if target.type()in self.types():result=_A
			except Exception as _:pass
			if not result:
				try:
					if self.type()==target:result=_A
				except Exception as _:pass
			if not result:
				if isinstance(target,type)and hasattr(target,_Di):
					if target.class_type_name()==self.type()or target.class_type_name()in self.types():result=_A
			return result
	Element_Relation=Element_Link
	def is_a(self,target):
		result=_C
		try:
			if isinstance(self,target):result=_A
		except Exception as _:pass
		if not result:
			try:
				if target.type()in self.types():result=_A
			except Exception as _:pass
		if not result:
			try:
				if self.type()==target:result=_A
			except Exception as _:pass
		if not result:
			if isinstance(target,type)and hasattr(target,_Di):
				if target.class_type_name()in self.types():result=_A
		return result
	def __init__(self,declaration_statement):
		add_profile_mem_counter('Allocated SBDL Elements');self._set_type(self.class_type_name(),override=_C);self._set_required_attributes([],override=_C)
		for required_attribute in self._required_attributes:
			if not required_attribute in declaration_statement.data():raise Exception('Property "{}" not specified (required)'.format(required_attribute))
		self._definition=declaration_statement;self._identifier,self._stereotype=SBDL_Parser.identifier_stereotype(declaration_statement.identifier());self._is_meta=_C;self._links=[];self._parents=[];self._children=[];self._related=[];self._properties={}
		for type_name in list(vars(SBDL_Parser.Types).values())+list(SBDL_Semantics.custom_types.keys()):self._gather_links_from_attr_type(type_name,self._links)
		if self.type()in SBDL_Semantics.type_properties:
			for aprop in SBDL_Semantics.type_properties[self.type()]:self._gather_properties_from_attr_type(aprop)
		for aprop in SBDL_Semantics.global_properties:self._gather_properties_from_attr_type(aprop)
		if get_config_value(_Bw,number=_A):
			for aprop in[x for x in declaration_statement.data().keys()if x.startswith(SBDL_Parser.Attributes.custom_prefix)]:self._gather_properties_from_attr_type(aprop)
		self.__check_raise_attrs()
		if not SBDL_Parser.Attributes.reference in self.properties():self.set_property(SBDL_Parser.Attributes.reference,self._definition.parser_element().reference())
		else:self.set_property(SBDL_Parser.Attributes.reference,self.reference())
	def _gather_links_from_attr_type(self,attr_type,target_list):self._gather_links_from_attr_type_source(attr_type,self._definition.data(),target_list)
	def _gather_links_from_attr_type_source(self,attr_type,source_list,target_list):
		if attr_type in source_list:
			self._accept_attr(attr_type);attr_string=source_list[attr_type];links=[];macro_depth=0;last_split=0
			for pos in range(len(attr_string)):
				current_char=attr_string[pos]
				if current_char==SBDL_Parser.Tokens.macro_delimeters[0]:macro_depth+=1
				elif current_char==SBDL_Parser.Tokens.macro_delimeters[1]:macro_depth-=1
				if current_char==SBDL_Parser.Attributes.separator and macro_depth<1:links.append(attr_string[last_split:pos]);last_split=pos+1
				elif pos==len(attr_string)-1:links.append(attr_string[last_split:])
			target_list.extend([self.Element_Link(x,attr_type,not SBDL_Parser.is_string_directive(x))for x in links]);self._remove_duplicate_link_objects(target_list)
	def _gather_properties_from_attr_type(self,attr_type):
		special_properties=[SBDL_Parser.Attributes.parent,SBDL_Parser.Attributes.child,SBDL_Parser.Attributes.related]
		if attr_type in self._definition.data():
			self._accept_attr(attr_type)
			if not attr_type in special_properties:self._properties[attr_type]=self._definition.data()[attr_type]
	def _remove_duplicate_link_objects(self,links_object):
		link_type_set={};result_list=[]
		for link in links_object:
			if not link.type()in link_type_set:link_type_set[link.type()]={}
			if not link.identifier()in link_type_set[link.type()]:link_type_set[link.type()][link.identifier()]=_A;result_list.append(link)
		del links_object[:];links_object.extend(result_list)
	def _remove_duplicate_links(self):self._remove_duplicate_link_objects(self._links)
	def _remove_duplicate_parents(self):self._remove_duplicate_link_objects(self._parents)
	def _remove_duplicate_children(self):self._remove_duplicate_link_objects(self._children)
	def _remove_duplicate_related(self):self._remove_duplicate_link_objects(self._related)
	def _set_type(self,typename,override=_C):
		if override:self._type=typename
		else:
			try:self._type
			except AttributeError:self._type=typename
	def _set_required_attributes(self,attributes,override=_C,extend=_C):
		if override:self._required_attributes=attributes
		else:
			try:
				self._required_attributes
				if extend:self._required_attributes.extend(attributes)
			except AttributeError:self._required_attributes=attributes
	def _accept_attr(self,attr):
		try:self._con_attr
		except AttributeError:self._con_attr={}
		finally:self._con_attr[attr]=_A
	def __check_raise_attrs(self):
		unrec_attrs=[];con_attr={}
		if hasattr(self,'_con_attr'):con_attr=self._con_attr
		for attr in self._definition.data():
			if not attr in con_attr:unrec_attrs.append(attr)
		if len(unrec_attrs)>0:raise Exception('Unrecognised attributes: {}'.format(_F.join(unrec_attrs)))
	def identifier(self,with_stereotype=_C):return SBDL_Parser.identifier_stereotype_join(self._identifier,self.stereotype()if with_stereotype else _B)
	def stereotype(self):return self._stereotype
	def description(self):
		description_content=''
		if SBDL_Parser.Attributes.description in self.properties():description_content=self.get_property(SBDL_Parser.Attributes.description)
		return SBDL_Parser.remove_escape_chars(description_content)if description_content!=_B else _B
	def replace_directives_from_dict(self,input_dict,throw_error=_A,print_l=print_null):
		input_dict[SBDL_Parser.Macros.self_reference]=self.identifier();input_dict[SBDL_Parser.Macros.self_element]=input_dict[self.identifier()]
		def do_replace(inp,attr_name):input_dict[SBDL_Parser.Macros.self_reference_attr]=attr_name;return SBDL_Parser.replace_directives(inp,input_dict,throw_error,print_l)[0]
		def gen_replaced_links(input_links,attr_name=_B):
			result=[]
			for ilink in input_links:
				link_identifier=ilink.identifier(_A,_A);link_identifier=do_replace(link_identifier,attr_name if attr_name!=_B else ilink.type()).split(SBDL_Parser.Attributes.separator)
				for linkid in link_identifier:result.append(self.Element_Link(linkid,ilink.type()))
			return result
		self._links=gen_replaced_links(self._links);self._parents=gen_replaced_links(self._parents,SBDL_Parser.Attributes.parent);self._children=gen_replaced_links(self._children,SBDL_Parser.Attributes.child);self._related=gen_replaced_links(self._related,SBDL_Parser.Attributes.related)
		for prop in self._properties:self._properties[prop]=do_replace(self._properties[prop],prop)
	def _generate_data_dictionary(self,extra_links=_B,flatten_references=_A):
		result={}
		if extra_links==_B:extra_links=[]
		targ_links=[self.links(),self.parents(),self.children(),self.related(),extra_links];pragma_val=self.pragma();no_dups={}
		def make_link_obj(linkid,sttyp):
			linkobj={SBDL_Parser.Attributes.identifier:linkid}
			if sttyp:linkobj[SBDL_Parser.Attributes.stereotype]=sttyp
			return linkobj
		def add_to_res(ltype,lid,lstereo,flatten_l):
			if not ltype in result:result[ltype]=str(lid)if flatten_l else[make_link_obj(lid,lstereo)];no_dups[ltype]={}
			elif not lid in no_dups[ltype]:result[ltype]+=SBDL_Parser.Attributes.separator+str(lid)if flatten_l else[make_link_obj(lid,lstereo)]
			no_dups[ltype][lid]=_A
		for targ_links_list in targ_links:
			for alink in targ_links_list:add_to_res(alink.type(),alink.identifier(with_stereotype=flatten_references,with_hash=_C),alink.stereotype(),flatten_references)
		if pragma_val!=_B:add_to_res(SBDL_Parser.Attributes.pragma,pragma_val,_B,flatten_references)
		for aproperty in self._properties:add_to_res(aproperty,self._properties[aproperty],_B,_A)
		return result
	def get_as_dictionary(self,extra_links=_B,flatten_references=_A):
		if extra_links==_B:extra_links=[]
		return self._generate_data_dictionary(extra_links,flatten_references)
	def definition(self):return SBDL_Declaration_Statement(self._definition.parser_element(),self.identifier(with_stereotype=_A),self.type(),self._generate_data_dictionary())
	def reference(self):
		reference=self._definition.parser_element().reference()
		if SBDL_Parser.Attributes.reference in self.properties():reference=SBDL_Parser.Parser_Element.shorten_reference(self.get_property(SBDL_Parser.Attributes.reference))
		return reference
	def type(self):return self._type
	@classmethod
	def types(self):
		types_list=[]
		for type_class in self.__mro__:
			if type_class==object:break
			class_type_name=type_class.class_type_name()
			if class_type_name:types_list.append(class_type_name)
		return types_list
	def links(self):return self._links.copy()
	def relations(self):return self.links()
	def parents(self):return self._parents.copy()
	def children(self):return self._children.copy()
	def related(self):return self._related.copy()
	def properties(self):return self._properties.keys()
	def get_property(self,key):return self._properties[key]
	def delete_property(self,key):
		if key in self._properties:del self._properties[key]
	def set_property(self,key,value):
		if value==_B:self.delete_property(key)
		else:self._properties[key]=value
	@classmethod
	def declaration_type_to_class(self,type_name,raise_on_error=_A):
		declaration_to_element_map={SBDL_Parser.Types.aspect:SBDL_Aspect,SBDL_Parser.Types.requirement:SBDL_Requirement,SBDL_Parser.Types.mode:SBDL_Failure_Mode,SBDL_Parser.Types.effect:SBDL_Failure_Effect,SBDL_Parser.Types.cause:SBDL_Failure_Cause,SBDL_Parser.Types.current_control:SBDL_Failure_Current_Control,SBDL_Parser.Types.current_detection:SBDL_Failure_Current_Detection,SBDL_Parser.Types.action_control:SBDL_Failure_Action_Control,SBDL_Parser.Types.action_detection:SBDL_Failure_Action_Detection,SBDL_Parser.Types.test:SBDL_Test_Definition,SBDL_Parser.Types.definition:SBDL_Definition,SBDL_Parser.Types.realisation:SBDL_Realisation,SBDL_Parser.Types.function:SBDL_Function,SBDL_Parser.Types.event:SBDL_Event,SBDL_Parser.Types.state:SBDL_State,SBDL_Parser.Types.transition:SBDL_Transition,SBDL_Parser.Types.usecase:SBDL_UseCase,SBDL_Parser.Types.interface:SBDL_Interface,SBDL_Parser.Types.trace:SBDL_Trace,SBDL_Parser.Types.group:SBDL_Group};resultant_type_class=_B
		if type_name in declaration_to_element_map:resultant_type_class=declaration_to_element_map[type_name]
		elif type_name in SBDL_Semantics.custom_types and len(SBDL_Semantics.custom_types[type_name])>1:resultant_type_class=SBDL_Semantics.custom_types[type_name][1]
		elif raise_on_error:raise Exception('"{}" is not a valid declaration type'.format(type_name))
		return resultant_type_class
	@classmethod
	def declaration_to_element(self,declaration_statement):return self.declaration_type_to_class(declaration_statement.data_type())(declaration_statement)
	@classmethod
	def statement_to_element(self,statement):
		result=_B
		if isinstance(statement,SBDL_Customtype_Statement):result=SBDL_CustomType(statement)
		elif statement.type()==SBDL_Parser.Statements.declaration:result=self.declaration_to_element(statement)
		elif statement.type()==SBDL_Parser.Statements.depends:result=SBDL_Depends(statement)
		elif statement.type()==SBDL_Parser.Statements.using:result=SBDL_Using(statement)
		elif statement.type()==SBDL_Parser.Statements.scope:result=SBDL_Scope(statement)
		else:raise Exception('"{}" is not a valid statement type'.format(statement.type()))
		return result
	@classmethod
	def __validate_link_types(self,typea,typeb):
		def links_between(type1,type2):
			result=_C
			if type1 in SBDL_Semantics.type_links:
				if type2 in SBDL_Semantics.type_links[type1]:result=_A
			return result
		return links_between(typea,typeb)or links_between(typeb,typea)
	def validate_link_type(self,link):return self.__validate_link_types(self.type(),link.type())
	def validate_parent_link_type(self,parent_link):return len(list(set(self.types())&set(parent_link.types())))>0 or parent_link.is_a(SBDL_Group)
	def validate_child_link_type(self,child_link):return self.validate_parent_link_type(child_link)
	def validate_relation_extended(self,_,__):is_valid=_A;error='';return is_valid,error
	def validate_self(self,elements=_B):
		is_valid=_A;error=''
		for link in self.links()+self.parents()+self.children()+self.related():
			link_valid,link_error=link.validate_self(elements)
			if not link_valid:is_valid=_C;error+=link_error
			if get_config_value(_Bh,number=_A):
				if link.hash():
					if link.identifier()in elements:
						link_hash=link.hash();target_hash=elements[link.identifier()].hash()
						if link_hash!=target_hash:is_valid=_C;error+='Hash mismatch for target "{}". Specified: "{}", computed: "{}". '.format(link.identifier(),link_hash,target_hash)
		return is_valid,error
	@classmethod
	def get_ids_string(self,link_list,elem_type=_B):
		result=_B
		if elem_type==_B:result=SBDL_Parser.Attributes.separator.join([x.identifier()for x in link_list])
		else:result=SBDL_Parser.Attributes.separator.join([x.identifier()for x in link_list if x.is_a(elem_type)])
		return result
	def prune_missing_references(self,element_set):
		def prune_list(reference_list):
			for r in range(0,len(reference_list)):
				if not reference_list[r].identifier()in element_set:del reference_list[r];prune_list(reference_list);break
		prune_list(self._links);prune_list(self._parents);prune_list(self._children);prune_list(self._related)
	def is_meta(self):return self._is_meta
	def pragma(self):return self._definition.data()[SBDL_Parser.Attributes.pragma]if self._definition!=_B and SBDL_Parser.Attributes.pragma in self._definition.data()else _B
	def hash(self):return hashlib.sha256(re.sub('\\s+',_I,self.definition().parser_element().content().strip()).encode()).hexdigest()[:get_config_value(_Bg,number=_A)]
	@classmethod
	def apply_operator(self,operator_element,element_stack):
		link_info_key='implicit_link_source';operator_value=operator_element.parser_element().content().strip()
		if operator_value in[SBDL_Parser.Tokens.link_operator,SBDL_Parser.Tokens.source_link_operator]:
			if len(element_stack)>1:
				source_obj=element_stack[-1];target_obj=element_stack[-2]
				if operator_value==SBDL_Parser.Tokens.source_link_operator:
					for targ_cand in element_stack[::-1][1:]:
						if not link_info_key in targ_cand[1]:target_obj=targ_cand;break
				if self.__validate_link_types(source_obj[0].type(),target_obj[0].type()):source_obj[0]._links.append(self.Element_Link(target_obj[0].identifier(),target_obj[0].type()));source_obj[0]._remove_duplicate_links();source_obj[1][link_info_key]=_A
				else:raise Exception(f"Operand types cannot be linked ({source_obj[0].identifier()}:{source_obj[0].type()} to {target_obj[0].identifier()}:{target_obj[0].type()})")
			else:raise Exception('Operator requires two operands')
		else:raise Exception('UNIMPLEMENTED OPERATOR')
class SBDL_Element_Rating(SBDL_Element):
	def __init__(self,declaration_statement):self._rating=_B;self._rating_post=_B;super().__init__(declaration_statement)
	def _set_rating_from_data_dictionary(self,attribute,optional=_C,post=_C):
		if attribute in self._definition.data():
			rating_data=self._definition.data()[attribute]
			if not post:self._rating=rating_data
			else:self._rating_post=rating_data
		elif not optional:raise Exception('{} not found'.format(attribute))
	class Ratings:ratings_map={SBDL_Parser.Types.mode:SBDL_Parser.Attributes.detectability,SBDL_Parser.Types.effect:SBDL_Parser.Attributes.severity,SBDL_Parser.Types.cause:SBDL_Parser.Attributes.occurrence};ratings_post_map={SBDL_Parser.Types.mode:SBDL_Parser.Attributes.detectability_post,SBDL_Parser.Types.cause:SBDL_Parser.Attributes.occurrence_post}
	def rating(self,post=_C):
		rating_value=self._rating
		if post and self._rating_post is not _B:rating_value=self._rating_post
		return rating_value
	def rating_name(self,post=_C):
		result=_B
		if not post:
			if self.types()[-1]in self.Ratings.ratings_map:result=self.Ratings.ratings_map[self.types()[-1]]
		elif self.types()[-1]in self.Ratings.ratings_post_map:result=self.Ratings.ratings_post_map[self.types()[-1]]
		return result
	def _generate_data_dictionary(self,extra_links=_B,flatten_references=_A):
		if extra_links==_B:extra_links=[]
		result=super()._generate_data_dictionary(extra_links,flatten_references)
		def handle_rating(do_post):
			rating_attr=self.rating_name(post=do_post)
			if rating_attr!=_B and self.rating(post=do_post)!=_B:result[rating_attr]=self.rating(post=do_post)
		handle_rating(_C);handle_rating(_A);return result
	def replace_directives_from_dict(self,input_dict,throw_error=_A,print_l=print_null):
		super().replace_directives_from_dict(input_dict,throw_error,print_l)
		if self._rating!=_B:self._rating=SBDL_Parser.replace_directives(self._rating,input_dict,throw_error,print_l)[0]
		if self._rating_post!=_B:self._rating_post=SBDL_Parser.replace_directives(self._rating_post,input_dict,throw_error,print_l)[0]
class SBDL_Element_Synthetic(SBDL_Element_Rating):
	def __init__(self,identifier,description,type,rating=0,stereotype_explicit=_B,rating_post=_B,source='Synthetic',indexes=_B):
		if indexes==_B:indexes=[]
		SBDL_Parser.sanitize_identifier(identifier,_A);self._identifier,self._stereotype=SBDL_Parser.identifier_stereotype(identifier)
		if stereotype_explicit:self._stereotype=stereotype_explicit
		self._type=type;self._rating=rating;self._rating_post=rating_post if rating_post!=_B else rating;self._links=[];self._parents=[];self._children=[];self._related=[];self._properties={};self._source=source;self._indexes=indexes;self._is_meta=_C;self._definition=_B;self.set_property(SBDL_Parser.Attributes.description,description)
	@classmethod
	def from_element(self,sbdl_element):
		rating=_B;rating_post=_B
		if isinstance(sbdl_element,SBDL_Element_Rating):rating=sbdl_element.rating();rating_post=sbdl_element.rating(post=_A)
		new_element=SBDL_Element_Synthetic(sbdl_element.identifier(),sbdl_element.description(),sbdl_element.type(),rating,sbdl_element.stereotype(),rating_post);new_element.set_links(sbdl_element.links());new_element.set_parents(sbdl_element.parents());new_element.set_children(sbdl_element.children());new_element.set_related(sbdl_element.related())
		for(prop,val)in{prop:sbdl_element.get_property(prop)for prop in sbdl_element.properties()}.items():new_element.set_property(prop,val)
		return new_element
	def copy(self):return self.from_element(self)
	def class_type_name(self):return self.type()
	def types(self):
		types_list=[self.type()];matching_base_class=SBDL_Element.declaration_type_to_class(self.type(),raise_on_error=_C)
		if matching_base_class is not _B:types_list=matching_base_class.types()
		return types_list
	def definition(self):pp=get_config_value(_T,number=_A);set_config_value(_T,_C);self._definition=SBDL_Declaration_Statement(SBDL_Parser.Parser_Element('',self._source,self._indexes),self.identifier(),self.type(),self._generate_data_dictionary());self._definition=SBDL_Declaration_Statement(SBDL_Parser.Parser_Element(super().definition().string(),self._source,self._indexes),self.identifier(),self.type(),self._generate_data_dictionary());set_config_value(_T,pp);return super().definition()
	def set_identifier(self,identifier):self._identifier=identifier
	def set_type(self,type_name):self._type=type_name
	def set_rating(self,rating,post=_C):
		if post:self._rating_post=rating
		else:self._rating=rating
	def add_link(self,elem_link):self._links.append(elem_link);self._remove_duplicate_links()
	def add_relation(self,elem_link):return self.add_link(elem_link)
	def set_links(self,links):self._links=links;self._remove_duplicate_links()
	def set_relations(self,links):return self.set_links(links)
	def add_parent(self,parent_link):self._parents.append(parent_link);self._remove_duplicate_parents()
	def set_parents(self,parents):self._parents=parents;self._remove_duplicate_parents()
	def add_related(self,related_link):self._related.append(related_link);self._remove_duplicate_related()
	def set_related(self,related):self._related=related;self._remove_duplicate_related()
	def add_child(self,child_link):self._children.append(child_link);self._remove_duplicate_children()
	def set_children(self,children):self._children=children;self._remove_duplicate_children()
	def set_meta(self,is_meta):self._is_meta=is_meta
	def native_element(self):return self.declaration_to_element(self.definition())
	def validate_parent_link_type(self,parent_link):return _A
	def validate_child_link_type(self,child_link):return _A
class SBDL_Depends(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Statements.depends
	def __init__(self,depends_statement):
		self._set_required_attributes([])
		for attr in depends_statement.data():self._accept_attr(attr)
		super().__init__(depends_statement)
		for attr in depends_statement.data():self._gather_properties_from_attr_type(attr)
	def definition(self):return SBDL_Depends_Statement(self._definition.parser_element(),self.type(),self._generate_data_dictionary())
class SBDL_Using(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Statements.using
class SBDL_Scope(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Statements.scope
class SBDL_CustomType(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Statements.customtype
	def __init__(self,customtype_statement):
		self.__original=customtype_statement.copy();self._set_required_attributes([])
		for attr in customtype_statement.data():self._accept_attr(attr)
		super().__init__(customtype_statement)
	def definition(self):return SBDL_Customtype_Statement(self._definition.parser_element(),self.identifier(with_stereotype=_A),self.__original.data_type(),self.__original.data().copy()|self._generate_data_dictionary())
class SBDL_Aspect(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.aspect
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children);self._gather_links_from_attr_type(SBDL_Parser.Attributes.related,self._related)
	def validate_link_stereotype(self,target):self_stereo=self.stereotype();target_stereo=target.stereotype(_A);return self_stereo==target_stereo or self_stereo==_B or target_stereo==_B
	def validate_parent_link_type(self,parent_link):return super().validate_parent_link_type(parent_link)and self.validate_link_stereotype(parent_link)or parent_link.is_a(SBDL_Group)
	def validate_child_link_type(self,child_link):
		result=super().validate_child_link_type(child_link)and self.validate_link_stereotype(child_link)
		if not result:result=child_link.is_a(SBDL_Interface)
		return result
class SBDL_Definition(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.definition
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children);self._gather_links_from_attr_type(SBDL_Parser.Attributes.related,self._related)
class SBDL_Realisation(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.realisation
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children);self._gather_links_from_attr_type(SBDL_Parser.Attributes.related,self._related)
class SBDL_Requirement(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.requirement
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children);self._gather_links_from_attr_type(SBDL_Parser.Attributes.related,self._related)
class SBDL_Failure_Mode(SBDL_Element_Rating):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.mode
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._set_rating_from_data_dictionary(self.Ratings.ratings_map[self.types()[-1]],optional=_A);self._set_rating_from_data_dictionary(self.Ratings.ratings_post_map[self.types()[-1]],optional=_A,post=_A);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children)
	def validate_parent_link_type(self,parent_link):return parent_link.is_a(SBDL_Parser.Types.cause)or parent_link.is_a(SBDL_Group)
	def validate_child_link_type(self,child_link):return child_link.is_a(SBDL_Parser.Types.effect)
	@classmethod
	def validate_fmea_relation_extended(self,source,link_obj,elements):
		is_valid=_A;error=''
		def find_parent_relation(ids1,ids2):
			found=_C
			for anid1 in ids1:
				anid1parents=[x.identifier()for x in elements[anid1].parents()]
				for anid2 in ids2:
					if anid2 in anid1parents:found=_A;break
			return found
		this_aspects=[x.identifier()for x in source.links()if x.is_a(SBDL_Aspect)];targ_aspects=[x.identifier()for x in elements[link_obj.identifier()].links()if x.is_a(SBDL_Aspect)]
		if link_obj.is_a(SBDL_Parser.Attributes.parent):is_valid=find_parent_relation(this_aspects,targ_aspects)
		elif link_obj.is_a(SBDL_Parser.Attributes.child):is_valid=find_parent_relation(targ_aspects,this_aspects)
		if not is_valid:error='Hierachical (parent/child) FMEA elements must be affiliated to similarly nested aspect elements'
		return is_valid,error
	def validate_relation_extended(self,link_obj,elements):return SBDL_Failure_Mode.validate_fmea_relation_extended(self,link_obj,elements)
class SBDL_Failure_Effect(SBDL_Element_Rating):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.effect
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._set_rating_from_data_dictionary(self.Ratings.ratings_map[self.types()[-1]],optional=_A);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents)
	def validate_parent_link_type(self,parent_link):return parent_link.is_a(SBDL_Parser.Types.mode)or parent_link.is_a(SBDL_Group)
	def validate_relation_extended(self,link_obj,elements):return SBDL_Failure_Mode.validate_fmea_relation_extended(self,link_obj,elements)
class SBDL_Failure_Cause(SBDL_Element_Rating):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.cause
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._set_rating_from_data_dictionary(self.Ratings.ratings_map[SBDL_Parser.Types.cause],optional=_A);self._set_rating_from_data_dictionary(self.Ratings.ratings_post_map[SBDL_Parser.Types.cause],optional=_A,post=_A);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children)
	def validate_child_link_type(self,child_link):return child_link.is_a(SBDL_Parser.Types.mode)
	def validate_relation_extended(self,link_obj,elements):return SBDL_Failure_Mode.validate_fmea_relation_extended(self,link_obj,elements)
class SBDL_Failure_Current_Control(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.current_control
class SBDL_Failure_Current_Detection(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.current_detection
class SBDL_Test_Definition(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.test
class SBDL_Failure_Action_Control(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.action_control
class SBDL_Failure_Action_Detection(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.action_detection
class SBDL_Event(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.event
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._condition_alternative=[];self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children);self._gather_links_from_attr_type(SBDL_Parser.Attributes.condition_alternative,self._condition_alternative)
	def validate_child_link_type(self,child_link):return child_link.is_a(self)
	def condition_alternative(self):return self._condition_alternative.copy()
	def validate_self(self,elements=_B):
		is_valid,error=super().validate_self(elements)
		for alternative in self.condition_alternative():
			if elements!=_B:
				if not alternative.identifier()in elements or not elements[alternative.identifier()].is_a(self):is_valid=_C;error+='Condition alternative ({}) must be a defined event. '.format(alternative.identifier())
		if self.stereotype()in[SBDL_Parser.Attributes.condition,SBDL_Parser.Attributes.loop_name]:
			if not SBDL_Parser.Attributes.condition in self.properties():is_valid=_C;error+=f'Stereotype of "{self.stereotype()}" requires a "{SBDL_Parser.Attributes.condition}" property'
		return is_valid,error
class SBDL_Function(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.function
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children)
class SBDL_State(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.state
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents);self._gather_links_from_attr_type(SBDL_Parser.Attributes.child,self._children)
class SBDL_Transition(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.transition
	def validate_self(self,elements=_B):
		is_valid,error=super().validate_self(elements);state_links=[x for x in self.links()if x.is_a(SBDL_State)]
		if len(state_links)!=2:is_valid=_C;error='{} must reference one ordered pair of states. '.format(SBDL_Parser.Types.transition)
		return is_valid,error
class SBDL_UseCase(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.usecase
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._gather_properties_from_attr_type(SBDL_Parser.Attributes.actor)
class SBDL_Interface(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.interface
	def __init__(self,declaration_statement):super().__init__(declaration_statement);self._gather_links_from_attr_type(SBDL_Parser.Attributes.parent,self._parents)
	def validate_parent_link_type(self,parent_link):return parent_link.is_a(SBDL_Aspect)or parent_link.is_a(SBDL_Group)
class SBDL_Trace(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.trace
	def __init__(self,declaration_statement):self._set_required_attributes([]);super().__init__(declaration_statement)
class SBDL_Group(SBDL_Element):
	@classmethod
	def class_type_name(self):return SBDL_Parser.Types.group
	def __init__(self,declaration_statement):self._set_required_attributes([]);super().__init__(declaration_statement)
	def validate_child_link_type(self,child_link):return _A
class SBDL_AST:
	def __init__(self,parser_elements):
		self.__parsed_objects=[];self.__cached_elements=_B;self.__cached_elements_hash=_B;self.clear_filter_element_set()
		for parser_element in parser_elements:self.__parsed_objects.extend(SBDL_Parser.parse_elements(parser_element))
	@classmethod
	def __parser_error(self,print_l,reference,message,content,extra=''):
		if len(extra)>0:extra='\n    '+extra
		print_l('{}:: Parsing error: {}\n    {}{}'.format(reference,message,content,extra),error=_A);print_l('Exception Stack trace:\n--------\n{}--------'.format(traceback.format_exc()),debug=_A)
	def ast(self):return self.__parsed_objects.copy()
	def force_parsed_objects(self,parsed_objects):self.__parsed_objects=parsed_objects
	def check_parsing(self,print_l=print_null):
		errors=0
		for parsed_object in self.__parsed_objects:
			if parsed_object.error():errors+=1;self.__parser_error(print_l,parsed_object.parser_element().reference(),parsed_object.error_message(),parsed_object.parser_element().content())
		return errors
	def sbdl_elements(self,print_l=print_null,prune_missing_references=_C,base_elements=_B):return self.elements(print_l,prune_missing_references,base_elements)
	def elements(self,print_l=print_null,prune_missing_references=_C,base_elements=_B):
		A='Element redefinition ({})'
		if base_elements is _B:base_elements={}
		def calc_cache_hash():return hash((id(self.__parsed_objects),self.hash_filter_element_set()))
		if self.__cached_elements is not _B and calc_cache_hash()==self.__cached_elements_hash:return self.__cached_elements
		sbdl_elements={};sbdl_elements_union=base_elements|sbdl_elements;sbdl_e_stack=[];inherit_data={};scope_data={};error_count=0;source=_B;pending_op=_B
		def handle_using(in_data,out_data):
			for data_key in in_data:
				if len(str(in_data[data_key]))==0:
					if data_key in out_data:del out_data[data_key]
				else:out_data[data_key]=in_data[data_key]
		def handle_depends(depends_elem):
			nonlocal error_count,sbdl_elements;depend_elem=SBDL_Element.statement_to_element(depends_elem)
			def add_depend_elem_to_result():
				if get_config_value(_Ac,number=_A):sbdl_elements[depend_elem.identifier()]=depend_elem
			if SBDL_Parser.Attributes.reference in depends_elem.data():print_l(f"{SBDL_Parser.Tokens.depends.capitalize()}: element already parsed: {depends_elem.string()}",debug=_A);add_depend_elem_to_result();return
			for(import_name,import_path)in depends_elem.data().items():
				import_path=str(pathlib.Path(depends_elem.parser_element().source()).parent/import_path);print_l(f'{SBDL_Parser.Tokens.depends.capitalize()}: "{import_name}" In: "{import_path}" ',debug=_A);abs_import_path=pathlib.Path(import_path).resolve();prior_input_files=get_config_value(_t)
				if abs_import_path in prior_input_files:print_l(f"{SBDL_Parser.Tokens.depends.capitalize()}: path already handled: {abs_import_path}",debug=_A);continue
				read_elements,import_errors=aggregate_all_parser_elements_from_files([import_path],do_recurse=get_config_value(_AA,number=_A),do_hidden=_C,print_l=print_l,read_input_set=prior_input_files);add_to_config_value(_t,{abs_import_path:_A})
				if not import_errors:
					import_elements,import_errors=SBDL_AST(read_elements).elements(print_l=print_l,prune_missing_references=prune_missing_references,base_elements=sbdl_elements);error_count+=import_errors
					if not import_errors:
						add_depend_elem_to_result()
						for(import_identifier,import_elem)in import_elements.items():
							if import_identifier in sbdl_elements:error_count+=1;self.__parser_error(print_l,depends_elem.parser_element().reference(),A.format(import_identifier),depends_elem.parser_element().content())
							else:sbdl_elements[import_identifier]=import_elem
		for parsed_object_source in self.__parsed_objects:
			parsed_object=parsed_object_source.copy();sbdl_e_stack_pop=parsed_object.get_parser_hint(SBDL_Parser.Tokens.nested_elements_depth_pop);source_l=parsed_object.parser_element().source()
			if source_l!=source:print_l('BOU: {}'.format(parsed_object.parser_element().reference()),debug=_A);source=source_l;inherit_data.clear();scope_data.clear()
			if isinstance(parsed_object,SBDL_Statement):
				if isinstance(parsed_object,SBDL_Depends_Statement):print_l('DEPENDS: {}'.format(parsed_object.data()),debug=_A);handle_depends(parsed_object)
				elif isinstance(parsed_object,SBDL_Using_Statement):print_l('USING+=: {}'.format(parsed_object.data()),debug=_A);handle_using(parsed_object.data(),inherit_data)
				elif isinstance(parsed_object,SBDL_Scope_Statement):print_l('SCOPE+=: {}'.format(parsed_object.data()),debug=_A);handle_using(parsed_object.data(),scope_data)
				elif isinstance(parsed_object,SBDL_Customtype_Statement):
					print_l('TYPE: {} {} {}'.format(parsed_object.identifier(),parsed_object.data_type(),parsed_object.data()),debug=_A)
					try:
						SBDL_Semantics.create_customtype_from_prototype(parsed_object);customtype_statement=SBDL_Element.statement_to_element(parsed_object)
						if not customtype_statement.identifier()in sbdl_elements_union:sbdl_elements[customtype_statement.identifier()]=customtype_statement
						else:error_count+=1;self.__parser_error(print_l,parsed_object.parser_element().reference(),'Customtype redefines element identifier',parsed_object.parser_element().content())
					except Exception as e:error_count+=1;self.__parser_error(print_l,parsed_object.parser_element().reference(),str(e),parsed_object.parser_element().content())
				elif isinstance(parsed_object,SBDL_Declaration_Statement):
					print_l('DECL: {} {} {}'.format(parsed_object.identifier(),parsed_object.data_type(),parsed_object.data()),debug=_A);inherit_data_l=inherit_data.copy()
					if parsed_object.data_type()in inherit_data_l:del inherit_data_l[parsed_object.data_type()]
					elif parsed_object.data_type().startswith(SBDL_Parser.Tokens.copy_element):
						print_l('COPY: {} {} {}'.format(parsed_object.identifier(),parsed_object.data_type(),parsed_object.data()),debug=_A);copy_from_identifier=parsed_object.data_type().removeprefix(SBDL_Parser.Tokens.copy_element).strip()
						if copy_from_identifier in sbdl_elements_union:copy_target=sbdl_elements_union[copy_from_identifier];copy_merged_data=copy_target.definition().data();copy_merged_data.pop(SBDL_Parser.Attributes.reference,_B);copy_merged_data|=parsed_object.data();copy_merged_data[f"{SBDL_Parser.Attributes.custom_prefix}copy-source"]=copy_target.identifier();new_copy_object=SBDL_Declaration_Statement(parsed_object.parser_element(),parsed_object.identifier(),copy_target.type(),copy_merged_data,parsed_object.error(),parsed_object.error_message(),parsed_object.syntactic_parent());new_copy_object.inherit_data(parsed_object.data());parsed_object=new_copy_object
						else:error_count+=1;self.__parser_error(print_l,parsed_object.parser_element().reference(),f"Unrecognised copy-from identifier ({copy_from_identifier})",parsed_object.parser_element().content())
					parsed_object.inherit_data(inherit_data_l);scope_map=parsed_object.apply_scope(scope_data.copy())
					try:
						statement_element=SBDL_Element.statement_to_element(parsed_object);statement_element.set_parser_hint(SBDL_Parser.Statements.scope,scope_map)
						if not statement_element.identifier()in sbdl_elements_union:
							if get_config_value(_CL,number=_A)and statement_element.description():statement_element.set_property(SBDL_Parser.Attributes.description_bsci,SBDL_Semantics.calc_bsci(statement_element.description(),'english'))
							sbdl_elements[statement_element.identifier()]=statement_element;sbdl_e_stack.append((statement_element,{}))
						else:error_count+=1;self.__parser_error(print_l,parsed_object.parser_element().reference(),A.format(statement_element.identifier()),parsed_object.parser_element().content())
					except Exception as e:error_count+=1;self.__parser_error(print_l,parsed_object.parser_element().reference(),str(e),parsed_object.parser_element().content())
					if pending_op and len(sbdl_elements)>1:
						try:sbdl_e_stack[-1][0].apply_operator(pending_op,sbdl_e_stack)
						except Exception as e:error_count+=1;self.__parser_error(print_l,pending_op.parser_element().reference(),str(e),pending_op.parser_element().content())
						pending_op=_B
				elif isinstance(parsed_object,SBDL_Operator):
					if pending_op:error_count+=1;self.__parser_error(print_l,pending_op.parser_element().reference(),'Double operator',pending_op.parser_element().content())
					pending_op=parsed_object
				else:error_count+=1;self.__parser_error(print_l,parsed_object.parser_element().reference(),'Unrecognised statement ({})'.format(parsed_object.type()),parsed_object.parser_element().content())
			else:self.__parser_error(print_l,parsed_object.parser_element().reference(),'Internal parser error: Unrecognised parsed object (type: {})'.format(parsed_object.type()),parsed_object.parser_element().content());error_count+=1
			if sbdl_e_stack_pop:sbdl_e_stack[-sbdl_e_stack_pop:]=[]
			sbdl_elements_union=base_elements|sbdl_elements
		if pending_op!=_B:self.__parser_error(print_l,parsed_object.parser_element().reference(),'Dangling operator: missing operand?',pending_op.parser_element().content());error_count+=1
		if prune_missing_references:
			for element_id in sbdl_elements:sbdl_elements[element_id].prune_missing_references(sbdl_elements_union)
		if get_config_value(_AC,number=_A):
			crossref_dict,crossref_errors=self.get_full_ast_as_dict(sbdl_elements_union);crossref_dict.update(SBDL_Parser.builtin_directive_dict())
			if get_config_value(_Ai,number=_A)and get_config_value(_Aj,number=_A):crossref_dict.update(get_config_value(_U))
			error_count+=crossref_errors;crossref_dict[SBDL_Parser.Macros.cross_reference_available]=_A;crossref_dict[SBDL_Parser.Macros.generated_elements]={};crossref_dict[SBDL_Parser.Macros.parsed_elements]=sbdl_elements;crossref_dict[SBDL_Parser.Macros.synthetic_element]=SBDL_Element_Synthetic
			for element_id in sbdl_elements:
				try:sbdl_elements[element_id].replace_directives_from_dict(crossref_dict,_A,print_l)
				except Exception as e:self.__parser_error(print_l,sbdl_elements[element_id].reference(),'Cross-referencing error: {}'.format(str(e)),sbdl_elements[element_id].definition().parser_element().content());error_count+=1
			generated_elements=crossref_dict[SBDL_Parser.Macros.generated_elements]
			for new_elem_id in generated_elements:
				if isinstance(generated_elements[new_elem_id],SBDL_Element):sbdl_elements[new_elem_id]=generated_elements[new_elem_id]
		for filter_f in self.__element_filters:sbdl_elements=self.filter_element_set(sbdl_elements,filter_f)
		if len(self.__element_filters)>0 and prune_missing_references:
			for element_id in sbdl_elements:sbdl_elements[element_id].prune_missing_references(sbdl_elements)
		self.__cached_elements=sbdl_elements,error_count;self.__cached_elements_hash=calc_cache_hash();return sbdl_elements,error_count
	@classmethod
	def get_all_parent_children_for_element(self,element,elements,parents_children):
		parents=element.parents()if parents_children else element.children();parents_i=[p.identifier()for p in parents];self_id=element.identifier()
		for(_,ev)in elements.items():
			eid=ev.identifier();ech=ev.children()if parents_children else ev.parents()
			if eid!=self_id and not eid in parents_i:
				for child in ech:
					if child.identifier()==self_id:parents.append(SBDL_Element.Element_Link(eid,SBDL_Parser.Attributes.parent if parents_children else SBDL_Parser.Attributes.child,identifier_is_origin=_A,stereotype_explicit=child.stereotype(),hash_explicit=child.hash()))
		return parents
	@classmethod
	def get_all_parents_for_element(self,element,elements):return self.get_all_parent_children_for_element(element,elements,_A)
	@classmethod
	def get_all_children_for_element(self,element,elements):return self.get_all_parent_children_for_element(element,elements,_C)
	@classmethod
	def get_all_links_for_element(self,element,elements):
		links=element.links();links_i=[l.identifier()for l in links];self_id=element.identifier()
		for elem in elements:
			elem_id=elements[elem].identifier();elem_links=elements[elem].links()
			if elem_id!=self_id and not elem_id in links_i:
				for link in elem_links:
					if link.identifier()==self_id:links.append(SBDL_Element.Element_Link(elem_id,elements[elem].type(),identifier_is_origin=_A,stereotype_explicit=link.stereotype(),hash_explicit=link.hash()))
		return links
	get_all_relations_for_element=get_all_links_for_element
	@classmethod
	def get_all_parents_links_children_for_element(self,element,elements):return self.get_all_parents_for_element(element,elements)+self.get_all_children_for_element(element,elements)+self.get_all_links_for_element(element,elements)
	@classmethod
	def get_top_level_element_set(self,elements,child_elements=_B,depth=0):
		if child_elements==_B:child_elements={}
		top_level_elements={}
		for element in elements:
			if depth>0:child_elements[element]=_A
			elem_children=self.get_all_children_for_element(elements[element],elements);self.get_top_level_element_set({x.identifier():elements[x.identifier()]for x in elem_children if x.identifier()in elements},child_elements,depth+1)
		for element in elements:
			if not element in child_elements:top_level_elements[element]=_A
		return top_level_elements
	@classmethod
	def validate_sbdl_elements(self,sbdl_elements,print_l=print_null):return self.validate_elements(sbdl_elements,print_l)
	@classmethod
	def validate_elements(self,sbdl_elements,print_l=print_null):
		result=_A;errors=0
		def validate_existance(this_element,link):
			nonlocal error,errors;errors_l=0
			if not link.identifier()in sbdl_elements:error+='Undefined element referenced: {}; '.format(link.identifier());errors_l+=1
			elif link.identifier()==this_element.identifier():error+='Self-referencing link: {}; '.format(link.identifier());errors_l+=1
			errors+=errors_l;return errors==0
		def generic_element_link_check(element_object):
			nonlocal errors;warn_if_not_in={SBDL_Parser.Types.cause:[SBDL_Parser.Types.mode],SBDL_Parser.Types.mode:[SBDL_Parser.Types.cause,SBDL_Parser.Types.effect,SBDL_Parser.Types.requirement],SBDL_Parser.Types.effect:[SBDL_Parser.Types.mode]};element_type=element_object.type();element_link_types=[x.type()for x in self.get_all_links_for_element(element_object,sbdl_elements)];element_p_types=[x.type()for x in self.get_all_parents_for_element(element_object,sbdl_elements)];element_c_types=[x.type()for x in self.get_all_children_for_element(element_object,sbdl_elements)];did_warn=_C
			if element_type in warn_if_not_in:
				for should_have_type in warn_if_not_in[element_type]:
					if not should_have_type in element_link_types:print_l("{}:: WARNING: {} '{}' does not link to a {}".format(element_object.reference(),element_type,element_object.identifier(),should_have_type),warning=_A);did_warn=_A
			if not did_warn and len(element_link_types)==0 and len(element_p_types)==0 and len(element_c_types)==0 and element_type!=SBDL_Parser.Tokens.customtype:print_l("{}:: WARNING: {} '{}' does not link to any elements".format(element_object.reference(),element_type,element_object.identifier()),warning=_A)
		for sbdl_element in sbdl_elements:
			error='';this_element=sbdl_elements[sbdl_element];generic_element_link_check(this_element)
			def individual_element_check(element_object):
				nonlocal error,errors;elem_valid,elem_error=element_object.validate_self(sbdl_elements)
				if not elem_valid:errors+=1;error+=elem_error
			def ext_valid_link(ext_valid_link,elem_l):
				nonlocal error,errors;extend_valid,error_ext=elem_l.validate_relation_extended(ext_valid_link,sbdl_elements)
				if not extend_valid:errors+=1;error+=error_ext
			individual_element_check(this_element)
			for parent_link in this_element.parents():
				print_l('Validating parent link from {} to {}'.format(this_element.identifier(),parent_link.identifier()),debug=_A)
				if validate_existance(this_element,parent_link):
					if not this_element.validate_parent_link_type(SBDL_Element.Element_Link(parent_link.identifier(),sbdl_elements[parent_link.identifier()].type(),target_stereotype=sbdl_elements[parent_link.identifier()].stereotype())):error+='Invalid types for parent linking: {} ({}) to {} ({}); '.format(this_element.identifier(with_stereotype=_A),this_element.type(),sbdl_elements[parent_link.identifier()].identifier(with_stereotype=_A),sbdl_elements[parent_link.identifier()].type());errors+=1
					ext_valid_link(parent_link,this_element)
			for child_link in this_element.children():
				print_l('Validating child link from {} to {}'.format(this_element.identifier(),child_link.identifier()),debug=_A)
				if validate_existance(this_element,child_link):
					if not this_element.validate_child_link_type(SBDL_Element.Element_Link(child_link.identifier(),sbdl_elements[child_link.identifier()].type(),target_stereotype=sbdl_elements[child_link.identifier()].stereotype())):error+='Invalid types for child linking: {} ({}) to {} ({}); '.format(this_element.identifier(with_stereotype=_A),this_element.type(),sbdl_elements[child_link.identifier()].identifier(with_stereotype=_A),sbdl_elements[child_link.identifier()].type());errors+=1
					ext_valid_link(child_link,this_element)
			for related_link in this_element.related():print_l('Validating related link from {} to {}'.format(this_element.identifier(),related_link.identifier()),debug=_A);validate_existance(this_element,related_link);ext_valid_link(related_link,this_element)
			for link in this_element.links():
				print_l('Validating link from {} to {}'.format(this_element.identifier(),link.identifier()),debug=_A)
				if validate_existance(this_element,link):
					if not sbdl_elements[link.identifier()].is_a(link):error+='Linked element type does not match expected type: {} ({} not of type {}); '.format(link.identifier(),sbdl_elements[link.identifier()].type(),link.type());errors+=1
					elif not this_element.validate_link_type(SBDL_Element.Element_Link(link.identifier(),sbdl_elements[link.identifier()].type(),target_stereotype=sbdl_elements[link.identifier()].stereotype())):error+='Invalid types for linking: {} ({}) to {} ({}); '.format(this_element.identifier(),this_element.type(),sbdl_elements[link.identifier()].identifier(),sbdl_elements[link.identifier()].type());errors+=1
					ext_valid_link(link,this_element)
			if isinstance(sbdl_elements[sbdl_element],SBDL_Element_Rating):
				if sbdl_elements[sbdl_element].rating()!=_B:
					try:_=int(sbdl_elements[sbdl_element].rating())
					except Exception as _:error+='Invalid rating value: {}; '.format(sbdl_elements[sbdl_element].rating());errors+=1
			if len(error)>0:full_links_info='(full element ref. list: {})'.format(sbdl_elements[sbdl_element].get_ids_string(sbdl_elements[sbdl_element].links()+sbdl_elements[sbdl_element].parents()+sbdl_elements[sbdl_element].children()+sbdl_elements[sbdl_element].related()));self.__parser_error(print_l,this_element.reference(),error,this_element.definition().parser_element().content(),full_links_info)
		return result if errors==0 else _C,errors
	@classmethod
	def get_full_ast_as_dict(self,elements,flatten_references=_C):
		errors=0;result={}
		def get_full_dictionary_for_element_id(element_id):elem=elements[element_id];return elem.get_as_dictionary(self.get_all_links_for_element(elem,elements)+self.get_all_parents_for_element(elem,elements)+self.get_all_children_for_element(elem,elements),flatten_references=flatten_references)
		for element_id in elements:
			result[element_id]={SBDL_Parser.Attributes.type:elements[element_id].type(),SBDL_Parser.Attributes.types:elements[element_id].types()if not flatten_references else'{} '.format(SBDL_Parser.Attributes.separator).join(elements[element_id].types()),**get_full_dictionary_for_element_id(element_id)}
			if elements[element_id].stereotype():result[element_id][SBDL_Parser.Attributes.stereotype]=elements[element_id].stereotype()
		return result,errors
	def fully_connected_cme_graph(self,_=print_null):
		elements,errors=self.elements(print_null);start_node_id='start_node';end_node_id='end_node';placeholder=_B;empty_cause_node_id='empty_cause_element';empty_mode_node_id='empty_mode_element';empty_effect_node_id='empty_effect_element';elements[start_node_id]=SBDL_Element_Synthetic(start_node_id,start_node_id,start_node_id,_B);elements[end_node_id]=SBDL_Element_Synthetic(end_node_id,end_node_id,end_node_id,_B);elements[empty_cause_node_id]=SBDL_Element_Synthetic(empty_cause_node_id,placeholder,SBDL_Parser.Types.cause,_B);elements[empty_mode_node_id]=SBDL_Element_Synthetic(empty_mode_node_id,placeholder,SBDL_Parser.Types.mode,_B);elements[empty_effect_node_id]=SBDL_Element_Synthetic(empty_effect_node_id,placeholder,SBDL_Parser.Types.effect,_B);elements[empty_cause_node_id].add_link(SBDL_Element.Element_Link(empty_mode_node_id,SBDL_Parser.Types.mode));elements[empty_mode_node_id].add_link(SBDL_Element.Element_Link(empty_effect_node_id,SBDL_Parser.Types.effect));elements[start_node_id].set_meta(_A);elements[end_node_id].set_meta(_A);elements[empty_cause_node_id].set_meta(_A);elements[empty_mode_node_id].set_meta(_A);elements[empty_effect_node_id].set_meta(_A)
		def make_fully_connected(element):
			nonlocal elements;links=SBDL_AST.get_all_links_for_element(element,elements)
			def has_connection_of_type(type_name):
				for link in links:
					if link.is_a(type_name):return _A
				return _C
			this_elem_link=SBDL_Element.Element_Link(element.identifier(),element.type())
			if element.is_a(SBDL_Failure_Cause):
				elements[start_node_id].add_link(this_elem_link)
				if not has_connection_of_type(SBDL_Parser.Types.mode):elements[empty_mode_node_id].add_link(this_elem_link)
			elif element.is_a(SBDL_Failure_Mode):
				if not has_connection_of_type(SBDL_Parser.Types.cause):elements[empty_cause_node_id].add_link(this_elem_link)
				if not has_connection_of_type(SBDL_Parser.Types.effect):elements[empty_effect_node_id].add_link(this_elem_link)
			if element.is_a(SBDL_Failure_Effect):
				elements[end_node_id].add_link(this_elem_link)
				if not has_connection_of_type(SBDL_Parser.Types.mode):elements[empty_mode_node_id].add_link(this_elem_link)
		for elem in elements:make_fully_connected(elements[elem])
		return elements[start_node_id],elements[end_node_id],elements,placeholder,errors
	@classmethod
	def filter_element_set(self,element_set,filter_pred_f):
		result_set={}
		for element in element_set:
			element_obj=element_set[element]
			if filter_pred_f(element_obj,element_set):result_set[element_obj.identifier()]=element_obj
		return result_set
	def add_filter_element_set(self,filter_function):self.__element_filters.append(filter_function)
	def clear_filter_element_set(self):self.__element_filters=[]
	def hash_filter_element_set(self):return tuple(id(f)for f in self.__element_filters)
	def filter_memo_function_elements_operation(self,element,elements,filter_id,search_set,memo_set,object_link_function,depth=0,depth_bound=_B,filter_id_re=_B):
		element_identifier=element.identifier()
		if search_set is _B:search_set=set()
		if memo_set is _B:memo_set={}
		def do_memo_set(v):memo_set[element_identifier]=v;return v
		search_set.add(element_identifier)
		if filter_id_re is _B:filter_id_re=re.compile(filter_id)
		element_links=object_link_function(element,elements)
		if element_identifier in memo_set:return memo_set[element_identifier]
		elif filter_id_re.match(element_identifier):return do_memo_set(_A)
		for link in element_links:
			if filter_id_re.match(link.identifier()):return do_memo_set(_A)
		if depth_bound is _B or depth<depth_bound:
			for link in element_links:
				link_identifier=link.identifier()
				if not link_identifier in search_set and link_identifier in elements:
					if self.filter_memo_function_elements_operation(elements[link_identifier],elements,filter_id,search_set,memo_set,object_link_function,depth=depth+1,depth_bound=depth_bound,filter_id_re=filter_id_re):return do_memo_set(_A)
		return do_memo_set(_C)
	def add_filter_element_set_linked(self,filter_id,depth_bound):self.add_filter_element_set(functools.partial(self.filter_memo_function_elements_operation,search_set=_B,memo_set=_B,object_link_function=self.get_all_links_for_element,filter_id=filter_id,depth_bound=depth_bound))
	def add_filter_element_set_connected(self,filter_id,depth_bound):self.add_filter_element_set(functools.partial(self.filter_memo_function_elements_operation,search_set=_B,memo_set=_B,object_link_function=self.get_all_parents_links_children_for_element,filter_id=filter_id,depth_bound=depth_bound))
	def add_filter_element_set_parents(self,filter_id,depth_bound):self.add_filter_element_set(functools.partial(self.filter_memo_function_elements_operation,search_set=_B,memo_set=_B,object_link_function=self.get_all_children_for_element,filter_id=filter_id,depth_bound=depth_bound))
	def add_filter_element_set_children(self,filter_id,depth_bound):self.add_filter_element_set(functools.partial(self.filter_memo_function_elements_operation,search_set=_B,memo_set=_B,object_link_function=self.get_all_parents_for_element,filter_id=filter_id,depth_bound=depth_bound))
	def add_filter_element_set_type(self,filter_id):self.add_filter_element_set(lambda x,y:_A if re.match(filter_id,x.type())else _C)
	def add_filter_element_set_id(self,filter_id):self.add_filter_element_set(lambda x,y:_A if re.match(filter_id,x.identifier())else _C)
	def add_filter_element_set_property(self,property_name,property_value):
		if not(isinstance(property_name,str)and len(property_name)==0):self.add_filter_element_set(lambda x,y:_A if property_name in x.properties()and re.match(property_value,x.get_property(property_name))else _C)
	def process_trace_elements(self,parser_element_list,print_l=print_null):
		model_elements,errors=self.elements(print_l,prune_missing_references=_A);system_state={};occurrence_counters={}
		def trace_error(message,element_ref):print_l('[TRACE] {}:: {}\n    {}'.format(element_ref.reference(),message,element_ref.definition().parser_element().content()),error=_A)
		def count_occurrence(trace_element):
			if not trace_element.identifier()in occurrence_counters:occurrence_counters[trace_element.identifier()]=0
			occurrence_counters[trace_element.identifier()]+=1
		def handle_trace_occurrence(trace_element):
			nonlocal system_state;count_occurrence(trace_element);trace_entries=[l.identifier()for l in trace_element.links()]
			for trace_entry_id in trace_entries:count_occurrence(model_elements[trace_entry_id])
		for parser_element in parser_element_list:
			if errors>0:break
			for parsed_element in SBDL_Parser.parse_elements(parser_element):
				if errors>0:break
				element=SBDL_Element.statement_to_element(parsed_element)
				if not element.is_a(SBDL_Trace):errors+=1;trace_error('"{}" (type: {}) is not a trace element'.format(element.identifier(),element.type()),element);break
				elif not element.identifier()in model_elements:errors+=1;trace_error('"{}" is not defined in model'.format(element.identifier()),element);break
				else:handle_trace_occurrence(element)
		return occurrence_counters,errors
def is_input_file(path):
	result=_C
	if path==SBDL_Parser.Tokens.stdio_name or os.path.isfile(make_remote_file_local(path)):result=_A
	return result
class base_file_object:
	def __iter__(self):return self
	def __next__(self):
		next_data=self.readline()
		if len(next_data)==0:raise StopIteration
		return next_data
	def __enter__(self):return self
	def __exit__(self,a,b,c):0
	def close(self):0
	def readlines(self,hint=-1):
		result=[];hint_count=0
		while hint==-1 or hint_count<hint:
			line=self.readline()
			if line=='':break
			result.append(line);hint_count+=len(line)
		return result
	def read(self,size=-1):return''.join(self.readlines(size))
	def readline(self,_=-1):return''
	def peekline(self,_=-1):return''
class stdio_clone(base_file_object):
	def read(self,size=-1):return sys.stdin.read(size)
	def write(self,data):
		if isinstance(data,str):sys.stdout.write(safe_stdout_endcode(data))
		else:sys.stdout.buffer.write(data)
	def readline(self,size=-1):return sys.stdin.readline(size)
class list_file_object(base_file_object):
	def __init__(self,list_of_lines):self.list_of_lines=list_of_lines;self.line_counter=0
	def readline(self,size=-1):
		result='';readsize=_B
		if size>=0:readsize=size
		if self.line_counter<len(self.list_of_lines):result=self.list_of_lines[self.line_counter][:readsize];self.line_counter+=1
		return result
	def peekline(self,size=-1):counter_pre=self.line_counter;result=self.readline(size);self.line_counter=counter_pre;return result
def plain_text_input_handler(file_reference):
	text_file_object=list_file_object([])
	try:
		with open(file_reference,'rt',encoding='utf-8-sig')as input_file:text_file_object=list_file_object(input_file.readlines())
	except FileNotFoundError as notfound:raise notfound
	except Exception as _:debug_out('Ignoring: {}'.format(file_reference))
	return _A,text_file_object
def stdio_input_handler(file_reference):return file_reference==SBDL_Parser.Tokens.stdio_name,stdio_clone()
def ms_character_fix(input_string):return input_string.replace('“','"').replace('”','"')
def msword_input_handler(file_reference):
	result=_C;handler=_B;_,extension=os.path.splitext(file_reference)
	def fixup_characters_in_place(input_list):return[ms_character_fix(x)for x in input_list]
	def remove_empty_lines(input_list):return[x for x in input_list if len(x)>0]
	if extension=='.doc'or extension=='.docx':
		try:import docx2txt;handler=list_file_object(remove_empty_lines(fixup_characters_in_place(docx2txt.process(file_reference).split(_D))));result=_A
		except Exception as e:f_print('Trying to parse what looks like an MS Word file but docx2txt package cannot be imported correctly:\n  {}'.format(str(e)),do_warning=_A,warning=_A)
	return result,handler
def msexcel_input_handler(file_reference):
	result=_C;handler=_B;_,extension=os.path.splitext(file_reference)
	if extension=='.xls'or extension=='.xlsx':
		try:
			import openpyxl;wb=openpyxl.load_workbook(file_reference,data_only=_A);ws=wb.active;content=[]
			for col_cells in ws.iter_cols():
				header=col_cells[0].value;content.append(str(header))
				for cell in col_cells[1:]:content.append(ms_character_fix(str(cell.value)))
			handler=list_file_object(content);result=_A
		except Exception as e:f_print('Trying to parse what looks like an MS Excel file but openpyxl package cannot be imported correctly:\n  {}'.format(str(e)),do_warning=_A,warning=_A)
	return result,handler
def make_remote_file_local(file_reference):
	local_file_reference=file_reference;parsed_file_reference=urllib.parse.urlparse(local_file_reference);error_string=_B
	if parsed_file_reference.scheme in['http','https','ftp','file://']and get_config_value(_CP,number=_A):
		try:local_file_reference,_=urllib.request.urlretrieve(local_file_reference)
		except Exception as e:error_string=str(e)+_g+file_reference+']'
		if error_string:raise Exception(error_string)
	return local_file_reference
def open_input_file(file_reference):
	potential_handlers=[msword_input_handler,msexcel_input_handler,stdio_input_handler,plain_text_input_handler];selected_handler=_B
	for potential_handler in potential_handlers:
		can_handle,handler_object=potential_handler(make_remote_file_local(file_reference))
		if can_handle:selected_handler=handler_object;break
	return selected_handler
def open_output_file(file_reference,is_text=_A,append=_C):return open(file_reference,('a'if append else'w')+('t'if is_text else'b'),encoding=_a if is_text else _B)if file_reference!=SBDL_Parser.Tokens.stdio_name else stdio_clone()
def context_directive_sniffer_class(matching_context,_,__):
	class_string=_B;parts=matching_context.split()
	for part in range(0,len(parts)):
		if parts[part]=='class'and part+1<len(parts):class_string=parts[part+1].replace(':','')
	return class_string
def context_directive_sniffer_cpp_function(matching_context,_,__):
	func_match=_B;context_split=matching_context.split()
	if len(context_split)>1:
		if context_split[0][-1]!=';':func_match=context_split[1].split('(')[0]
	return func_match
def context_directive_sniffer_py_function(matching_context,file_path,file_line):return context_directive_sniffer_cpp_function(matching_context,file_path,file_line)
def context_directive_sniffer_file_type(file_path):_,file_extension=os.path.splitext(file_path);file_extension=file_extension[1:];result=file_extension.upper();return result
def context_directive_sniffer(context_lines,file_path,file_line,_,all_entries_result=_C):
	B='^(PY)$';A='.*class.*';result={};macro_types={SBDL_Parser.Macros.cpp_class:['^(CPP|CXX|H)$',A,context_directive_sniffer_class],SBDL_Parser.Macros.py_class:[B,A,context_directive_sniffer_class],SBDL_Parser.Macros.c_func:['^(CPP|CXX|C)$','\\s*\\S+?\\s+\\S+\\s*\\(.*?\\)\\s*{.*',context_directive_sniffer_cpp_function],SBDL_Parser.Macros.py_func:[B,'def\\s+\\S+\\s*\\(.*\\)\\s*:',context_directive_sniffer_py_function]};file_type=context_directive_sniffer_file_type(file_path)
	for macro_type_id in macro_types:
		macro_type=macro_types[macro_type_id]
		if all_entries_result:result[macro_type_id]=_B
		if re.match(macro_type[0],file_type):
			def handle_match(macro_match,m_type_id,m_type):
				if macro_match:
					extract_result=macro_match.group()if m_type[1]==_B else m_type[2](macro_match.group(),file_path,file_line)
					if extract_result!=_B:result[m_type_id]=extract_result
				return macro_match
			found_match=_C
			for context_line in context_lines:
				if handle_match(re.search(macro_type[1],context_line),macro_type_id,macro_type):found_match=_A
			if not found_match:handle_match(re.search(macro_type[1],_D.join(context_lines)),macro_type_id,macro_type)
	return result
def get_parser_elements_from_filepath(filepath,elements_list,print_l=print_null,file_opener=open_input_file,read_input_set=_B):
	if read_input_set==_B:read_input_set={}
	errors=0;line_count=0;line_buffer='';line_buffer_markers=[];add_to_config_value(_Aa,{filepath:_A})
	def handle_error(content,excep=''):nonlocal errors;print_l('{}:{}:: General reading error: {}\n    {}'.format(filepath,line_count,str(excep),content.strip()),error=_A);errors+=1
	prev_non_sbdl_non_empty_lines=[''];next_non_sbdl_non_empty_lines=[''];non_sbdl_line_prefix=''
	def push_context(con):
		if len(prev_non_sbdl_non_empty_lines)>3:prev_non_sbdl_non_empty_lines.pop(0)
		prev_non_sbdl_non_empty_lines.append(con)
	local_directives={}
	if get_config_value(_Aj,number=_A):local_directives=get_config_value(_U)
	local_directives.update(SBDL_Parser.builtin_directive_dict())
	def update_per_file_directives(explicit_updates=_B):
		updates=explicit_updates
		if updates is _B:updates={SBDL_Parser.Macros.path:filepath,SBDL_Parser.Macros.file_name:file_name,SBDL_Parser.Macros.directory:dir_name}
		local_directives.update(updates);return updates
	file_name=os.path.basename(filepath);file_ext=os.path.splitext(filepath)[1:][0];dir_name=filepath.replace(file_name,'');abs_path=os.path.abspath(filepath);native_sbdl_file=_A if get_config_value(_Bd)==file_ext else _C;update_per_file_directives()
	def clear_context_directives(target_dict):
		context_mac=context_directive_sniffer([],'','',{},all_entries_result=_A)
		for entry_id in context_mac:target_dict.pop(entry_id,'')
	clear_context_directives(local_directives)
	def import_directive(_,*inp):
		nonlocal errors;param_path=''.join(inp);import_path=os.path.join(local_directives[SBDL_Parser.Macros.path].replace(local_directives[SBDL_Parser.Macros.file_name],''),param_path);file_directive_cache={SBDL_Parser.Macros.import_sbdl:local_directives[SBDL_Parser.Macros.import_sbdl],SBDL_Parser.Macros.path:local_directives[SBDL_Parser.Macros.path],SBDL_Parser.Macros.file_name:local_directives[SBDL_Parser.Macros.file_name],SBDL_Parser.Macros.directory:local_directives[SBDL_Parser.Macros.directory]}
		try:
			print_l('Import: {}'.format(import_path),debug=_A);add_to_config_value(_t,{pathlib.Path(import_path).resolve():_A});read_elements,import_errors=aggregate_all_parser_elements_from_files([import_path],do_recurse=get_config_value(_AA,number=_A),do_hidden=_C,print_l=print_l,file_opener=file_opener,read_input_set=read_input_set);elements_list.extend(read_elements);errors+=import_errors
			if import_errors:raise Exception('(see prior error)')
		except Exception as e:errors+=1;handle_error(str(e),'Error during import of "{}"'.format(param_path))
		update_per_file_directives(file_directive_cache);return''if errors==0 else''.join(inp)
	local_directives[SBDL_Parser.Macros.import_sbdl]=import_directive
	def strip_comments(line):
		result=line;comment_markers=[SBDL_Parser.Tokens.comment,get_config_value(_Ba)]
		for comment_marker in comment_markers:
			if comment_marker!=_B:
				comment_marker_match=re.search(comment_marker,line)
				if comment_marker_match:
					comment_marker_match_pos=comment_marker_match.start()
					if comment_marker_match_pos==0 or line[comment_marker_match_pos-1]!=SBDL_Parser.Tokens.escape:result=re.split(comment_marker,line,maxsplit=1)[0];break
		return result
	def is_raw_state(line):
		if not hasattr(is_raw_state,'raw_state'):is_raw_state.raw_state=_C
		raw_delimit=SBDL_Parser.Tokens.raw_content_delimiters;full_raw=is_raw_state.raw_state;last_opening=line.rfind(raw_delimit[0]);last_closing=line.rfind(raw_delimit[1])
		if last_opening>last_closing:is_raw_state.raw_state=_A
		elif last_closing>last_opening:is_raw_state.raw_state=_C;full_raw=_C
		return is_raw_state.raw_state,full_raw
	def append_line(line_l,eof=_C):
		def is_line_continuation(line_content):
			result=_C;line_content_1=line_content;cont_mark=SBDL_Parser.Tokens.line_continuation;end_str=line_content_1[-len(cont_mark):]
			if end_str==cont_mark:line_content_1=line_content_1[:-len(cont_mark)];result=_A
			elif native_sbdl_file and get_config_value(_s,number=_A):result=_A
			return result,line_content_1
		def line_buffer_markers_update():line_buffer_markers.append([len(line_buffer),line_count])
		def apply_directives(input_line):
			result_line=input_line
			def previous_line():
				pre_line=prev_non_sbdl_non_empty_lines[-1].strip()
				if len(prev_non_sbdl_non_empty_lines)>1:
					if pre_line==non_sbdl_line_prefix.strip():pre_line=prev_non_sbdl_non_empty_lines[-2].strip()
				return pre_line
			local_directives.update({SBDL_Parser.Macros.context:prev_non_sbdl_non_empty_lines[-1],SBDL_Parser.Macros.curline:non_sbdl_line_prefix,SBDL_Parser.Macros.preline:previous_line(),SBDL_Parser.Macros.sucline:next_non_sbdl_non_empty_lines[0],SBDL_Parser.Macros.line:str(line_count)})
			if get_config_value(_Bv,number=_A):
				try:result_line=SBDL_Parser.replace_directives(result_line,local_directives,throw_error=_A,print_l=print_l)[0].strip()
				except SBDL_Parser.MacroReplaceException as macro_excep:
					if not macro_excep.first_pass_can_ignore():raise macro_excep
					else:0
			return result_line
		nonlocal line_count,line_buffer,line_buffer_markers;is_raw,_=is_raw_state(line_l);line=line_l
		if not is_raw:line=strip_comments(line);line=line.strip();line=apply_directives(line)
		is_cont,cont_line=is_line_continuation(line)or is_raw
		if is_cont and not eof:
			line_buffer_markers_update();line_buffer+=cont_line+_I
			if is_raw:line_buffer+=_H
		else:
			line_buffer_markers_update();parse_line=line_buffer+line
			if len(parse_line)>0:elements_list.append(SBDL_Parser.Parser_Element(parse_line,os.path.normpath(filepath),line_buffer_markers,context=prev_non_sbdl_non_empty_lines[-1]))
			line_buffer='';line_buffer_markers=[]
	def append_line_EOF():append_line('',eof=_A)
	def detect_block_start_end(line):
		nonlocal native_sbdl_file;result=_C
		if re.search(SBDL_Parser.Tokens.prefix_block_start,line)or re.search(get_config_value(_BX),line):native_sbdl_file=_A;result=_A
		elif re.search(SBDL_Parser.Tokens.prefix_block_end,line)or re.search(get_config_value(_BY),line):native_sbdl_file=_C;result=_A
		return result
	def extract_sbdl_line_non_native(line):
		nonlocal non_sbdl_line_prefix;result='';custom_prefix=get_config_value(_BV);custom_suffix=get_config_value(_BW);custom_noop=get_config_value(_BZ);custom_sigil=get_config_value(_Bn);sigils=custom_sigil if isinstance(custom_sigil,(list,tuple,dict))else[custom_sigil]
		if re.search(SBDL_Parser.Tokens.prefix,line):non_sbdl_line_prefix,result=re.split(SBDL_Parser.Tokens.prefix,line,maxsplit=1)
		elif re.search(custom_prefix,line):
			non_sbdl_line_prefix,result=re.split(custom_prefix,line,maxsplit=1)
			if re.search(custom_suffix,result):result=re.split(custom_suffix,result,maxsplit=1)[0]
		else:
			for sigil in sigils:
				if sigil and(matched_sigil:=re.search(sigil,line)):non_sbdl_line_prefix,custom_content=re.split(sigil,line,maxsplit=1);sigil_content=matched_sigil.group(0);custom_type=sigils[sigil]if isinstance(sigils,dict)else SBDL_Parser.Types.definition;simulated_element=SBDL_Element_Synthetic(identifier=sigil_content,description=custom_content.lstrip(),type=custom_type,rating=_B,stereotype_explicit='CustomSigil',rating_post=_B,source=filepath,indexes=[(0,line_count)]);result=simulated_element.definition().string();break
		if re.match(custom_noop,result):result=''
		return non_sbdl_line_prefix.strip(),result.strip()
	def handle_line(line):
		nonlocal native_sbdl_file;_,full_raw_line=is_raw_state(line);line_content=line.strip()if not full_raw_line else line.strip('\n\r');print_l('READ: {}:{}'.format(filepath,line_count),debug=_A)
		if line_count==1 and line_content==SBDL_Parser.Tokens.hashbang:native_sbdl_file=_A
		if line_count==1 and line_content==SBDL_Parser.Tokens.hashbang_formal:native_sbdl_file=_A;SBDL_Parser.set_formal_spec()
		else:
			content_to_append=line_content
			if not native_sbdl_file:
				_,content_to_append=extract_sbdl_line_non_native(line_content)
				if len(content_to_append)==0 and len(line_content)>0:push_context(line)
			append_line(content_to_append)
		local_directives.update(context_directive_sniffer(prev_non_sbdl_non_empty_lines,filepath,line_count,local_directives))
	def handle_file():
		nonlocal next_non_sbdl_non_empty_lines,line_count
		with file_opener(filepath)as fmfile:
			while _A:
				line=fmfile.readline();next_line=fmfile.peekline().strip();_,next_line_content=extract_sbdl_line_non_native(next_line)
				if len(next_line_content)>0:next_line=''
				next_non_sbdl_non_empty_lines=[next_line]
				if len(line)>0:
					line_count+=1
					try:
						if not detect_block_start_end(line):handle_line(line)
					except Exception as e:handle_error(line,e)
				else:append_line_EOF();break
	if not abs_path in read_input_set:read_input_set[abs_path]=_A;handle_file()
	else:print_l('Skipping already parsed file: "{}"'.format(abs_path),debug=_A)
	print_l('C_DEF LOCALS : {}'.format(local_directives),debug=_A);return errors
def aggregate_all_parser_elements_from_files(input_list,do_recurse,do_hidden,print_l=print_null,file_opener=open_input_file,read_input_set=_B):
	aggregated_elements=[];errors=0
	if read_input_set is _B:read_input_set={}
	def is_hidden(filename):return _A if os.path.basename(filename)[:1]==_P else _C
	for afile in input_list:
		if os.path.isdir(afile):
			if do_recurse:print_l('Recursing: {}'.format(afile));elements_l,errors_l=aggregate_all_parser_elements_from_files([os.path.join(afile,x)for x in os.listdir(afile)],do_recurse,do_hidden,print_l,file_opener,read_input_set);errors+=errors_l;aggregated_elements.extend(elements_l)
			else:print_l('Skipping directory: {}'.format(afile))
		elif is_input_file(afile):
			if not is_hidden(afile)or do_hidden:
				print_l('Read  : {}'.format(afile),debug=_A)
				try:parser_elements=[];errors_l=get_parser_elements_from_filepath(afile,parser_elements,print_l,file_opener,read_input_set);errors+=errors_l;aggregated_elements.extend(parser_elements)
				except Exception as e:errors+=1;print_l('Error reading: {}\n  {}'.format(afile,str(e)),error=_A)
			else:print_l('Skipping hidden file: {}'.format(afile))
		else:errors+=1;print_l(_Dj.format(afile),error=_A)
	return aggregated_elements,errors
def data_tree_node_to_element(identifier,data_node):
	if not isinstance(data_node,dict):raise Exception('Trying to extract tree information from not dict object')
	default_val='EMPTY_VAL##@@'
	def e_k_source(source,key,default=default_val):
		if key in source:return source.pop(key)
		elif default==default_val:raise Exception('Trying to extract non-existent key: "{}" from: {}'.format(key,data_node))
		else:return default
	def e_k(key,default=default_val):return e_k_source(data_node,key,default)
	new_tree_elem=SBDL_Element_Synthetic(identifier=identifier,description=e_k(SBDL_Parser.Attributes.description,''),type=e_k(SBDL_Parser.Attributes.type),rating=0,stereotype_explicit=e_k(SBDL_Parser.Attributes.stereotype,_B),rating_post=0,source='TreeNode',indexes=[])
	for key in data_node:
		data_entry=data_node[key]
		if isinstance(data_entry,str):new_tree_elem.set_property(key,data_entry)
		elif isinstance(data_entry,list)and key!=SBDL_Parser.Attributes.types:
			for link_entry in data_entry:
				if not SBDL_Parser.Attributes.identifier in link_entry:raise Exception('Missing "{}" in link definition: {}'.format(SBDL_Parser.Attributes.identifier,link_entry))
				link_st=e_k_source(link_entry,SBDL_Parser.Attributes.stereotype,_B);new_link=SBDL_Element.Element_Link(link_entry[SBDL_Parser.Attributes.identifier],key,stereotype_explicit=link_st)
				if key==SBDL_Parser.Attributes.child:new_tree_elem.add_parent(new_link)
				elif key==SBDL_Parser.Attributes.parent:new_tree_elem.add_child(new_link)
				else:new_tree_elem.add_link(new_link)
	return new_tree_elem
def aggregate_all_parser_elements_from_struct_func(input_list,do_recurse,_,__,file_opener,struct_loader):
	aggregated_elements=[];errors=0
	if do_recurse:raise Exception('Cannot recurse for tree imports (recurse option specified)')
	for input_file_path in input_list:
		with file_opener(input_file_path)as input_file:tree_data=struct_loader(input_file);aggregated_elements.extend([SBDL_Parser.Parser_Element(data_tree_node_to_element(x,tree_data[x]).definition().string(),input_file_path,'TreeImport')for x in tree_data])
	return aggregated_elements,errors
def aggregate_all_parser_elements_from_json(input_list,do_recurse,do_hidden,print_l=print_null,file_opener=open_input_file):return aggregate_all_parser_elements_from_struct_func(input_list,do_recurse,do_hidden,print_l,file_opener,json.load)
def aggregate_all_parser_elements_from_yaml(input_list,do_recurse,do_hidden,print_l=print_null,file_opener=open_input_file):yaml=lazy_import('yaml');return aggregate_all_parser_elements_from_struct_func(input_list,do_recurse,do_hidden,print_l,file_opener,yaml.safe_load)
def aggregate_all_parser_elements_from_csv(input_list,do_recurse,do_hidden,print_l=print_null,file_opener=open_input_file):
	def fix_up_cell_types(row_dict):
		new_dict={}
		for key in row_dict:
			if row_dict[key]==_B or len(str(row_dict[key]).strip())==0:0
			elif key in vars(SBDL_Parser.Types).values()or key in SBDL_Semantics.custom_types or key==SBDL_Parser.Attributes.parent or key==SBDL_Parser.Attributes.child or key==SBDL_Parser.Attributes.types:
				link_targets=str(row_dict[key]).split(SBDL_Parser.Attributes.separator);link_list=[]
				for target in link_targets:link_list.append({SBDL_Parser.Attributes.identifier:target})
				new_dict[key]=link_list
			else:new_dict[key]=row_dict[key]
		return new_dict
	def csv_nodes_extractor(input_file):
		reader=csv.DictReader(input_file);tree_data={}
		for row in reader:
			row_dict=dict(row)
			if not SBDL_Parser.Attributes.identifier in row_dict:raise Exception('Missing "{}" in row: {}'.format(SBDL_Parser.Attributes.identifier,row_dict))
			else:identifier=row_dict.pop(SBDL_Parser.Attributes.identifier);tree_data[identifier]=fix_up_cell_types(row_dict)
		return tree_data
	return aggregate_all_parser_elements_from_struct_func(input_list,do_recurse,do_hidden,print_l,file_opener,csv_nodes_extractor)
def plantuml_writer(file_opener,output_file,content,_):
	content_to_output=content
	if not get_config_value(_Ae,number=_A):
		if not get_config_value(_Bu):
			try:
				plantuml_env=os.environ.copy();plantuml_env.pop('LD_LIBRARY_PATH',_B);plantuml_env['PLANTUML_LIMIT_SIZE']=str(get_config_value(_Br));plantuml_command=get_config_value(_Bq)
				if isinstance(plantuml_command,str):plantuml_command=shlex.split(plantuml_command)
				plantuml_command_full=plantuml_command+['-t',get_config_value(_Bs),'-p'];debug_out('EXECUTE_PLANTUML: '+str(plantuml_command_full));exec_result=subprocess.run(plantuml_command_full,input=content.encode(),stdout=subprocess.PIPE,stderr=subprocess.STDOUT,env=plantuml_env,check=_C);content_to_output=exec_result.stdout
			except Exception as e:raise Exception(f"Error while executing PlantUML: {e}")
		else:content_to_output=plantuml_writer_remote(content)
	with file_opener(output_file,is_text=get_config_value(_Ae,number=_A))as plantuml_file:plantuml_file.write(content_to_output)
def plantuml_writer_remote(plantuml_text,fmt='png'):
	try:
		render_uri=get_config_value(_Ah);debug_out(f"REMOTE PlantUML rendering at: {render_uri}")
		def _encode(text):
			alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_';data=zlib.compress(text.encode(_a))[2:-4];result=[]
			for i in range(0,len(data),3):b=list(data[i:i+3]);b+=[0]*(3-len(b));result+=[alphabet[b[0]>>2&63],alphabet[(b[0]&3)<<4|b[1]>>4&15],alphabet[(b[1]&15)<<2|b[2]>>6&3],alphabet[b[2]&63]]
			return''.join(result)
		puml_url_base=get_config_value(_Ah).rstrip('/');encoded_text=_encode(plantuml_text);puml_url=f"{puml_url_base}/{fmt}/{encoded_text}";req=urllib.request.Request(puml_url,headers={'User-Agent':'Mozilla/5.0'})
		with urllib.request.urlopen(req)as response:
			if response.status!=200:raise RuntimeError(f"PlantUML server returned HTTP {response.status} for URL: {puml_url_base}")
			return response.read()
	except Exception as e:raise RuntimeError(f'PlantUML remote render exception: "{e}" for URL: {puml_url_base}')from e
def plantuml_footer_generator():
	footer_content=''
	if get_config_value(_Bt,number=_A):footer_content+='footer \\nGenerated by {generator} on {date}\\n\\n\n'.format(generator='{} version {}'.format(__NAME,__VERSION),date=get_date_string())
	return footer_content
def plantuml_identifier(in_string):
	default_replace='';replace_list=['/',_P,'-',_e,_L,_I,'\t',_D];result=in_string
	for replace_str in replace_list:result=result.replace(replace_str,default_replace)
	if result=='':result=default_replace
	return result
def write_requirement_graph_output(sbdl_ast,output_file,file_opener,arguments,print_l):
	B='rtype';A='rid';elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A);requirements={k:v for(k,v)in elements.items()if v.is_a(SBDL_Requirement)};title_sanitized=SBDL_Parser.sanitize_identifier(arguments.title);root_requirement=SBDL_Element_Synthetic(title_sanitized,'',SBDL_Parser.Types.requirement,_B);root_requirement.add_link(SBDL_Element.Element_Link(title_sanitized,SBDL_Parser.Types.aspect));top_level_requirements=[]
	for(rid,rev)in requirements.items():
		if len(SBDL_AST.get_all_parents_for_element(rev,requirements))==0:top_level_requirements.append({A:rid,B:rev.type()})
	if len(top_level_requirements)==1 and not get_config_value(_C6):root_requirement=requirements[top_level_requirements[0][A]]
	else:
		for top_level_requirement in top_level_requirements:root_requirement.add_child(SBDL_Element.Element_Link(top_level_requirement[A],top_level_requirement[B]))
	def line_wrap(content,columns=1000):return _H.join(textwrap.wrap(content,columns))
	def sanitize_desc(desc):return line_wrap(desc)
	def output_requirement_tree(r_element,depth=1,path=_B,lr=_C):
		if path==_B:path=[]
		if r_element.identifier()in path:print_l('WARNING: cyclic dependency in requirements graph: {}'.format(path),warning=_A);return''
		prefix=_R*depth
		if SBDL_Parser.Attributes.color in r_element.properties():
			color=r_element.get_property(SBDL_Parser.Attributes.color)
			if color!=_B and len(color)>0:prefix+='[#{}]'.format(color)
		if depth>2:
			if lr:prefix+='>'
			else:prefix+='<'
		rtype=''
		if depth==1:rtype='<<source>>'
		elif depth<=get_config_value(_C5):rtype='<<toplevel>>'
		else:rtype='<<regular>>'
		occurrences=len(SBDL_AST.get_all_parents_for_element(r_element,requirements));output='{prefix} <i>{aspect} </i>\\n<b>{rid} </b>\\n{rdesc}{occ} {rtype}\n'.format(prefix=prefix,aspect=line_wrap(r_element.get_ids_string(sbdl_ast.get_all_links_for_element(r_element,elements),SBDL_Aspect)),rid=r_element.identifier(),rdesc=sanitize_desc(r_element.description()),occ='\\n<i>[{} occurrences]</i>'.format(occurrences)if occurrences>1 else'',rtype=rtype);children=SBDL_AST.get_all_children_for_element(r_element,requirements);children.sort(key=lambda x:requirements[x.identifier()].get_ids_string(sbdl_ast.get_all_links_for_element(requirements[x.identifier()],elements),SBDL_Aspect));last_child_aspect=_B;prefix=_R*(depth+1);aspect_groups=get_config_value(_C4)
		for child in children:
			child_aspect=requirements[child.identifier()].get_ids_string(sbdl_ast.get_all_links_for_element(requirements[child.identifier()],elements),SBDL_Aspect)
			if aspect_groups and child_aspect!=last_child_aspect:last_child_aspect=child_aspect;output+='{prefix} <i>{aspect} </i> <<aspect>>\n'.format(prefix=prefix,aspect=child_aspect)
			lr=not lr;output+=output_requirement_tree(requirements[child.identifier()],depth+2 if aspect_groups else depth+1,path+[r_element.identifier()],lr)
		return output
	def plantuml_requirement_diagram_wrap(content):
		header=plantuml_footer_generator();legend_text='';legend=get_config_value(_C8)
		if legend!=_B:legend_text='legend\n{legend_content}\nendlegend\n'.format(legend_content=legend)
		plant_uml_prepost='@startwbs\n{style}\n\n{header}\n{content}\n{legend}@endwbs\n';return plant_uml_prepost.format(style=get_config_value(_Y)+get_config_value(_C7),header=header,content=content,legend=legend_text)
	plantuml_writer(file_opener,output_file,plantuml_requirement_diagram_wrap(output_requirement_tree(root_requirement)),print_l);return errors
def write_decomposition_graph_output(sbdl_ast,output_file,file_opener,arguments,print_l):
	elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A)
	def plantuml_decomposition_diagram():
		header=plantuml_footer_generator();title=_A4.format(arguments.title)if arguments.title!=''else'';plant_uml_prepost=_A5;content='';notes='';note_count=0;interface_count=0;relations='';descriptions=get_config_value(_CB,number=_A);interface_mem={};handled_elems=[];indentation_prefix=_N
		def puml_id(elem_id):return plantuml_identifier(elem_id)
		def note_rel_pos(posindex):positions={0:'top',1:'bottom',2:_AL,3:_AM};return positions[posindex%4]
		def interface_rel_pos(posindex):positions={0:'up',1:'down',2:_AL,3:_AM};return positions[posindex%4]if get_config_value(_Af,number=_A)else''
		def handle_elem(elem_id,pref='',parent_id=_B):
			nonlocal content,relations,notes,note_count,interface_mem,interface_count
			def add_interface_line(interface,type_name,pref_l=''):
				nonlocal content
				if not interface.identifier()in interface_mem:interface_mem[interface.identifier()]=[x for x in elements[interface.identifier()].links()if x.is_a(SBDL_Interface)];content+='\n{}{} "{}{}" as {}'.format(pref+pref_l,type_name,puml_id(interface.identifier()),_H+_H.join(textwrap.wrap(elements[interface.identifier()].description(),get_config_value(_d,number=_A)))if descriptions else'',puml_id(interface.identifier()))
			if not elem_id in handled_elems and elements[elem_id].is_a(SBDL_Aspect):
				handled_elems.append(elem_id);interfaces=[link for link in SBDL_AST.get_all_links_for_element(elements[elem_id],elements)if link.is_a(SBDL_Interface)];ports=[child for child in SBDL_AST.get_all_children_for_element(elements[elem_id],elements)if elements[child.identifier()].is_a(SBDL_Interface)]
				for interface in interfaces:
					interface_elem=elements[interface.identifier()]
					if interface_elem.stereotype()==SBDL_Parser.Attributes.port_name:ports.append(interface)
					else:add_interface_line(interface,_x);stereotype=interface.stereotype();relations+='[{}] -{}- {}{}\n'.format(puml_id(elem_id),interface_rel_pos(interface_count),puml_id(interface.identifier()),''if not stereotype else': '+stereotype);interface_count+=1
				aspect_stereotype=elements[elem_id].stereotype()
				if not aspect_stereotype:aspect_stereotype=elements[elem_id].type()
				else:aspect_stereotype=elements[elem_id].type()+_e+aspect_stereotype
				aspect_stereotype=_B7.format(aspect_stereotype);content+=_Dk.format(pref,get_config_value(_C9),elem_id,puml_id(elem_id),aspect_stereotype,''if not SBDL_Parser.Attributes.color in elements[elem_id].properties()else' #'+elements[elem_id].get_property(SBDL_Parser.Attributes.color));children=SBDL_AST.get_all_children_for_element(elements[elem_id],elements)
				if len(children)>0 or len(interfaces):
					content+=' { '
					for port in ports:port_type='port'if len(interface_mem)%2==0 else'portout';add_interface_line(port,port_type,indentation_prefix)
					for child in children:handle_elem(child.identifier(),pref+indentation_prefix,parent_id=elem_id)
					content+='\n{}}}\n'.format(pref)
				notes+='note {} of {}\n**{}**\n{}{}\nend note\n'.format(note_rel_pos(note_count),puml_id(elem_id),puml_id(elem_id),_D.join(textwrap.wrap(elements[elem_id].description(),get_config_value(_d,number=_A))),_D+elements[elem_id].reference()if arguments.source else'');note_count+=1;related=elements[elem_id].related();related.extend([link for link in elements[elem_id].links()if link.is_a(SBDL_Aspect)])
				for relation in related:
					if parent_id!=relation.identifier():
						relation_type=relation.stereotype();relation_type=relation_type if relation_type else'';link_text=': '+relation_type if relation_type else'';relation_end='>'
						if relation_type.startswith(SBDL_Parser.Tokens.inherit):relation_end='|>'
						elif elements[relation.identifier()].is_a(SBDL_Interface):relation_end='('
						elif relation_type.startswith(SBDL_Parser.Tokens.compose):relation_end=_R
						relations+=_Dl.format(puml_id(elem_id),interface_rel_pos(interface_count),relation_end,puml_id(relation.identifier()),link_text);interface_count+=1
			else:0
		elements_ordered=list(elements.keys());elements_ordered.sort(key=lambda key:len(SBDL_AST.get_all_parents_for_element(elements[key],elements)))
		for elem in elements_ordered:handle_elem(elem)
		for interface_id in interface_mem:
			for int_link in interface_mem[interface_id]:relations+='{} .{}. {}\n'.format(puml_id(interface_id),interface_rel_pos(interface_count),puml_id(int_link.identifier()));interface_count+=1
		return plant_uml_prepost.format(style=(_Dm if get_config_value(_Ag,number=_A)else'')+get_config_value(_Y)+get_config_value(_Am),header=header+title,content=content+_D+relations+_D+(_D+notes if descriptions or arguments.source else''))
	plantuml_writer(file_opener,output_file,plantuml_decomposition_diagram(),print_l);return errors
def write_element_graph_output(sbdl_ast,output_file,file_opener,arguments,print_l):
	elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A)
	def plantuml_decomposition_diagram():
		header=plantuml_footer_generator();title=_A4.format(arguments.title)if arguments.title!=''else'';plant_uml_prepost=_A5;content='';relations='';interface_mem={};handled_elems=[]
		def puml_id(elem_id):A='ascii';return plantuml_identifier(elem_id).replace('(','').replace(')','').replace('[]','').replace(']','').replace(':',_V).encode(A,_A7).decode(A)
		def interface_rel_pos(posindex):positions={0:'up',1:'down',2:_AL,3:_AM};return positions[posindex%4]if get_config_value(_Af,number=_A)else''
		def detailed_content(elem_id):
			props=[];links={};result='\t{}: **{}**\n'.format(SBDL_Parser.Attributes.type,elements[elem_id].type());elem=elements[elem_id]
			for prop in elem.properties():props.append('\t{}:\n\t  {}'.format(prop,textwrap.shorten(elem.get_property(prop),width=get_config_value(_CF,number=_A),placeholder='...')))
			for link in elem.links():
				if not link.type()in links:links[link.type()]=[]
				links[link.type()].append(link.identifier())
			if len(links)>0:
				result+='\t== relations ==\n'
				for link in links:
					result+='\t.. {}..\n'.format(link)
					for lid in links[link]:result+='\t{}\n'.format(lid)
			if len(props)>0:result+='\t== properties ==\n'+'\n\t..\n'.join(props)
			return result
		def handle_elem(elem_id,pref='',_=_B):
			nonlocal content;nonlocal relations;nonlocal interface_mem;interface_count=0
			if not elem_id in handled_elems:
				handled_elems.append(elem_id);interfaces=[link for link in SBDL_AST.get_all_links_for_element(elements[elem_id],elements)if link.is_a(SBDL_Parser.Types.interface)]
				for interface in interfaces:
					if not interface.identifier()in interface_mem:interface_mem[interface.identifier()]=_A;content+='\n{}{} "**{}**\\n{}" as {}'.format(pref,_x,interface.identifier(),'',puml_id(interface.identifier()))
					relations+='{} -{}- {}\n'.format(puml_id(elem_id),interface_rel_pos(interface_count),puml_id(interface.identifier()));interface_count+=1
				aspect_stereotype=elements[elem_id].stereotype()
				if not aspect_stereotype:aspect_stereotype=elements[elem_id].type()
				else:aspect_stereotype=elements[elem_id].type()+_e+aspect_stereotype
				aspect_stereotype=_B7.format(aspect_stereotype);content+=_Dk.format(pref,get_config_value(_CA),elem_id,puml_id(elem_id),aspect_stereotype,''if not SBDL_Parser.Attributes.color in elements[elem_id].properties()else' #'+elements[elem_id].get_property(SBDL_Parser.Attributes.color));content+='{{\n{}\n}}\n'.format(detailed_content(elem_id));related=elements[elem_id].related();related.extend([link for link in elements[elem_id].links()])
				for relation in related:
					relation_type=relation.stereotype();relation_type=relation_type if relation_type else'';link_text=': '+relation_type if relation_type else'';relation_end='>'
					if relation_type.startswith(SBDL_Parser.Tokens.inherit):relation_end='|>'
					elif elements[relation.identifier()].is_a(SBDL_Parser.Types.interface):relation_end='('
					elif relation_type.startswith(SBDL_Parser.Tokens.compose):relation_end=_R
					relations+=_Dl.format(puml_id(elem_id),interface_rel_pos(interface_count),relation_end,puml_id(relation.identifier()),link_text)
			else:0
		elements_ordered=list(elements.keys())
		for elem in elements_ordered:handle_elem(elem)
		return plant_uml_prepost.format(style=(_Dm if get_config_value(_Ag,number=_A)else'')+get_config_value(_Y)+get_config_value(_Am),header=header+title,content=content+_D+relations+_D+(_D+''))
	plantuml_writer(file_opener,output_file,plantuml_decomposition_diagram(),print_l);return errors
def write_function_graph_output(sbdl_ast,output_file,file_opener,arguments,print_l):
	elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A);show_descriptions=get_config_value(_CC,number=_A);event_count=0;entities={}
	def puml_id(elem_id):return plantuml_identifier(elem_id)
	def plantuml_function_diagram():
		header=plantuml_footer_generator();title=_A4.format(arguments.title)if arguments.title!=''else'';plant_uml_prepost=_A5;content='';empty_aspect_replace=_A2
		def elem_colour_prefix(elem):
			colour=''
			if SBDL_Parser.Attributes.color in elem.properties():colour=f" #{elem.get_property(SBDL_Parser.Attributes.color)} "
			return colour
		def handle_func(func_id,depth=1):
			A='end\n';nonlocal content;func_element=elements[func_id];children=SBDL_AST.get_all_children_for_element(func_element,elements);func_links=SBDL_AST.get_all_links_for_element(func_element,elements);func_aspect=_F.join([x.identifier()for x in func_links if x.is_a(SBDL_Aspect)])
			if func_aspect=='':func_aspect=empty_aspect_replace
			prev_aspect=func_aspect;is_first_edge=_A
			def define_aspect(aspect_name):
				nonlocal content
				if not aspect_name in entities:
					entities[aspect_name]=_A;aspect_stereotype=elements[aspect_name].stereotype()if aspect_name in elements else _B
					if not aspect_stereotype:aspect_stereotype=elements[aspect_name].type()
					else:aspect_stereotype=elements[aspect_name].type()+_e+aspect_stereotype
					aspect_stereotype=_B7.format(aspect_stereotype);aspect_color=''
					if aspect_name in elements and SBDL_Parser.Attributes.color in elements[aspect_name].properties():aspect_color=f" #{elements[aspect_name].get_property(SBDL_Parser.Attributes.color)}"
					content+='{}{} "{}{}" as {} {}{}\n'.format(_N*depth,get_config_value(_CD),aspect_name,_H+elements[aspect_name].reference()if arguments.source else'',puml_id(aspect_name),aspect_stereotype,aspect_color)
			content+='group {}<<{}>> {}{} #red\n'.format(elem_colour_prefix(func_element),f"{func_element.type()}::{func_element.stereotype()}"if func_element.stereotype()else func_element.type(),puml_id(func_element.identifier()),_H+func_element.reference()if arguments.source else'')
			if show_descriptions:content+='{}note across: {}\n'.format(_N*depth,func_element.description())
			for child in children:
				if child.identifier()in elements:handle_func(child.identifier(),depth=depth+1)
			output_desc=''
			def handle_event(event_elem):
				nonlocal prev_aspect,is_first_edge,content,output_desc,event_count;event_count+=1;event_links=SBDL_AST.get_all_links_for_element(event_elem,elements);return_control=SBDL_Parser.Attributes.return_control in event_elem.properties();event_aspect=_F.join([x.identifier()for x in event_links if x.is_a(SBDL_Aspect)]);define_aspect(prev_aspect)
				if event_aspect==''or event_aspect==_B:event_aspect=prev_aspect
				else:define_aspect(event_aspect)
				if is_first_edge:
					if SBDL_Parser.Attributes.input in func_element.properties():content+='{}[-> {}:**{}**\\n{}\n'.format(_N*depth,puml_id(func_aspect),SBDL_Parser.Attributes.input,func_element.get_property(SBDL_Parser.Attributes.input))
					content+='{}{} o--> {}:**{}**\n'.format(_N*depth,puml_id(func_aspect),puml_id(event_aspect),puml_id(func_element.identifier()));content+='{}activate {}\n'.format(_N*depth,puml_id(func_aspect));is_first_edge=_C
				else:content+='{}{} --> {}:{}\n'.format(_N*depth,puml_id(prev_aspect),puml_id(event_aspect),output_desc)
				condition=_B;stereotype=event_elem.stereotype();stereotype_spec=stereotype in[SBDL_Parser.Attributes.loop_name,SBDL_Parser.Attributes.condition,SBDL_Parser.Attributes.parallel_name]
				if stereotype_spec or SBDL_Parser.Attributes.condition in event_elem.properties():condition=event_elem.get_property(SBDL_Parser.Attributes.condition)if SBDL_Parser.Attributes.condition in event_elem.properties()else'';content+='group {}{} [{}]\n'.format(elem_colour_prefix(event_elem),SBDL_Parser.Attributes.condition if not stereotype else stereotype,condition)
				if not SBDL_Parser.Attributes.control_only in event_elem.properties():ev_color=' #'+event_elem.get_property(SBDL_Parser.Attributes.color)if SBDL_Parser.Attributes.color in event_elem.properties()else'';content+='{}hnote over "{}"{}: **{}**{}{}\n'.format(_N*depth,puml_id(event_aspect),ev_color,event_elem.identifier(),'\\n{}'.format(_H.join(textwrap.wrap(event_elem.description(),get_config_value(_d,number=_A))))if show_descriptions else'',_H+event_elem.reference()if arguments.source else'')
				if not return_control:prev_aspect=event_aspect
				if event_aspect=='':event_aspect=empty_aspect_replace
				if SBDL_Parser.Attributes.output in event_elem.properties():output_desc=event_elem.get_property(SBDL_Parser.Attributes.output)
				else:output_desc=''
				for event_id in[x.identifier()for x in SBDL_AST.get_all_children_for_element(event_elem,elements)if x.identifier()in elements and elements[x.identifier()].is_a(SBDL_Event)]:handle_event(elements[event_id])
				if condition or stereotype_spec:
					if len(event_elem.condition_alternative())>0:
						content+='else not ({})\n'.format(condition)
						for event_id in[x.identifier()for x in event_elem.condition_alternative()if x.identifier()in elements and elements[x.identifier()].is_a(SBDL_Event)]:handle_event(elements[event_id])
					content+=A
			for event_id in[x.identifier()for x in func_links if x.is_a(SBDL_Event)and x.identifier()in elements]:handle_event(elements[event_id])
			if not is_first_edge:
				content+='{}{} -->o {}:{}\n'.format(_N*depth,puml_id(prev_aspect),puml_id(func_aspect),output_desc);content+='{}deactivate {}\n'.format(_N*depth,puml_id(func_aspect))
				if SBDL_Parser.Attributes.output in func_element.properties():content+='{}[<- {}:**{}**\\n{}\n'.format(_N*depth,puml_id(func_aspect),SBDL_Parser.Attributes.output,func_element.get_property(SBDL_Parser.Attributes.output))
			content+=A
		top_level_elems=SBDL_AST.get_top_level_element_set(elements)
		for func in[elements[x].identifier()for x in top_level_elems if elements[x].is_a(SBDL_Function)]:handle_func(func)
		if event_count>0:content='autonumber "[000]"\n'+content
		return plant_uml_prepost.format(style=get_config_value(_Y)+get_config_value(_An),header=header+title,content=content)
	plantuml_writer(file_opener,output_file,plantuml_function_diagram(),print_l);return errors
def write_process_graph_output(sbdl_ast,output_file,file_opener,_,print_l):
	errors=0;elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A);show_descriptions=get_config_value(_CE,number=_A);first_event=_A;g_process_elem=_B;note_stagger=_C
	def elem_colour_prefix(elem):
		colour=''
		if SBDL_Parser.Attributes.color in elem.properties():colour=f"#{elem.get_property(SBDL_Parser.Attributes.color)} "
		return colour
	def recurse_event_contents(event_elem,current_aspect=_B,indent=2):
		nonlocal first_event,note_stagger;indent_str=_I*indent;event_cont='';ev_aspects=[x for x in SBDL_AST.get_all_relations_for_element(event_elem,elements)if x.is_a(SBDL_Aspect)];ev_aspect=ev_aspects[0].identifier()if len(ev_aspects)>0 else _I;control_only=SBDL_Parser.Attributes.control_only in event_elem.properties();ev_links=[x.identifier()for x in SBDL_AST.get_all_children_for_element(event_elem,elements)if x.identifier()in elements and elements[x.identifier()].is_a(SBDL_Event)];is_loop=SBDL_Parser.Attributes.loop_name in event_elem.stereotype()if event_elem.stereotype()else _C;is_parallel=SBDL_Parser.Attributes.parallel_name in event_elem.stereotype()if event_elem.stereotype()else _C;condition=''
		if SBDL_Parser.Attributes.condition in event_elem.properties():condition=event_elem.get_property(SBDL_Parser.Attributes.condition)
		def emit_note():
			nonlocal event_cont,note_stagger
			if show_descriptions and event_elem.description():event_cont+=f"{indent_str}note "+(_AL if note_stagger else _AM)+'\n{indent_str}{event_elem.description()}\n{indent_str}end note\n';note_stagger=not note_stagger
		if ev_aspect!=current_aspect and get_config_value(_u,number=_A):
			event_cont+=f"{indent_str}"
			if ev_aspect in elements:
				colour=elem_colour_prefix(elements[ev_aspect])
				if colour:event_cont+=f"|{colour.strip()}"
			event_cont+=f"|{ev_aspect}|\n"
		if first_event:
			event_cont+=f"{indent_str}start\n";first_event=_C
			if get_config_value(_u,number=_A):event_cont+=f"{indent_str}{elem_colour_prefix(g_process_elem)}:{g_process_elem.identifier()}; <<procedure>>\n";emit_note()
		if is_loop:suffix=_D if control_only else'';event_cont+=f"{indent_str}repeat{suffix}"
		if not control_only:prefix=elem_colour_prefix(event_elem)if not is_loop else'';event_cont+=f"{indent_str}{prefix}:{event_elem.identifier()}; <<task>>\n";emit_note()
		if ev_links and condition and not is_loop:event_cont+=f"{indent_str}if ({condition}) then (condition met)\n"
		child_count=0
		for event_id in ev_links:
			if is_parallel:suffix=' again'if child_count else'';event_cont+=f"{indent_str}fork{suffix}\n"
			event_cont+=recurse_event_contents(elements[event_id],ev_aspect,indent=indent+2);child_count+=1
		if is_parallel:event_cont+=f"{indent_str}endfork\n"
		if ev_links and SBDL_Parser.Attributes.condition_alternative in event_elem.properties():
			alternative=event_elem.get_property(SBDL_Parser.Attributes.condition_alternative)
			if alternative in elements:event_cont+=f"{indent_str}else (alternative)\n";event_cont+=recurse_event_contents(elements[alternative],ev_aspect,indent=indent+2)
		if ev_links and condition and not is_loop:event_cont+=f"{indent_str}endif\n"
		if is_loop:event_cont+=f"{indent_str}repeat while({condition}) is (condition met)\n"
		return event_cont
	def handle_process_element(process_elem):
		nonlocal g_process_elem,first_event;first_event=_A;g_process_elem=process_elem;process_cont=''
		if not get_config_value(_u,number=_A):
			process_cont+=f'group {elem_colour_prefix(process_elem)}"**'+(f"{process_elem.type()}::{process_elem.stereotype()}"if process_elem.stereotype()else process_elem.type())+f'** {process_elem.identifier()}" {{\n';description=process_elem.description()
			if show_descriptions and description:process_cont+=f"note\n{description}\nend note\n"
		process_links=SBDL_AST.get_all_links_for_element(process_elem,elements)
		for event_id in[x.identifier()for x in process_links if x.is_a(SBDL_Event)and x.identifier()in elements]:process_cont+=recurse_event_contents(elements[event_id])
		process_cont+='  stop\n'
		if not get_config_value(_u,number=_A):process_cont+='}\n'
		return process_cont
	def plantuml_process_diagram():
		process_dia_cont='';process_dia_cont+='@startuml\n';process_dia_cont+=get_config_value(_Y)+get_config_value(_An)+_j
		for process_elem in[elements[f]for f in elements if elements[f].is_a(SBDL_Function)]:process_dia_cont+=handle_process_element(process_elem)
		process_dia_cont+='\n@enduml\n';return process_dia_cont
	plantuml_writer(file_opener,output_file,plantuml_process_diagram(),print_l);return errors
def write_state_graph_output(sbdl_ast,output_file,file_opener,arguments,print_l):
	elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A);show_descriptions=get_config_value(_CH,number=_A)
	def plantuml_state_diagram():
		header=plantuml_footer_generator();title=_A4.format(arguments.title)if arguments.title!=''else'';plant_uml_prepost=_A5;statedefs='';statedefs_dict={};transitions_visited={};content='';relations=''
		def handle_elem(elem_id,depth=0):
			if elem_id in elements:
				element=elements[elem_id];nonlocal statedefs_dict,content,relations;elem_children=SBDL_AST.get_all_children_for_element(element,elements);elem_aspect=_F.join([x.identifier()for x in sbdl_ast.get_all_links_for_element(element,elements)if x.is_a(SBDL_Aspect)])
				if not elem_aspect in statedefs_dict:statedefs_dict[elem_aspect]=''
				statedefs_dict[elem_aspect]+='{}state {} {{\n'.format(_N*depth,elem_id)
				for child in elem_children:handle_elem(child.identifier(),depth+1)
				statedefs_dict[elem_aspect]+='{}}}\n'.format(_N*depth);content+='state {}{}: {}{}\n'.format(elem_id,''if not SBDL_Parser.Attributes.color in element.properties()else' #'+element.get_property(SBDL_Parser.Attributes.color),_H.join(textwrap.wrap(element.description(),get_config_value(_d,number=_A)))if show_descriptions else'',_H+element.reference()if arguments.source else'');transitions=[x for x in SBDL_AST.get_all_links_for_element(element,elements)if x.is_a(SBDL_Transition)]
				for transition_link in transitions:
					if transition_link.identifier()in elements and not transition_link.identifier()in transitions_visited:transition=elements[transition_link.identifier()];states=[x.identifier()for x in transition.links()if x.is_a(SBDL_State)];event_ids=[x.identifier()for x in transition.links()if x.is_a(SBDL_Event)];event_text=_H.join(event_ids);relations+='{} --> {} : **{}**{}\n'.format(states[0],states[1],event_text,_H+transition.reference()if arguments.source else'');transitions_visited[transition_link.identifier()]=_A
		top_level_elems=SBDL_AST.get_top_level_element_set(elements)
		for elem_id in top_level_elems:
			if elements[elem_id].is_a(SBDL_State):handle_elem(elem_id)
		for statedef in statedefs_dict:statedefs+=statedefs_dict[statedef]+_D
		return plant_uml_prepost.format(style=get_config_value(_Y)+get_config_value(_CG),header=header+title,content=statedefs+content+_D+relations)
	plantuml_writer(file_opener,output_file,plantuml_state_diagram(),print_l);return errors
def write_usecase_graph_output(sbdl_ast,output_file,file_opener,arguments,print_l):
	elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A)
	def puml_id(input_id):return plantuml_identifier(input_id)
	def plantuml_usecase_diagram():
		header=plantuml_footer_generator();title=_A4.format(arguments.title)if arguments.title!=''else'';plant_uml_prepost=_A5;aspects={};content=''
		for usecase in[elements[elem]for elem in elements if elements[elem].is_a(SBDL_UseCase)]:
			actor=usecase.get_property(_Aw)if _Aw in usecase.properties()else'User';content+='actor "{}" as {}\n'.format(actor,puml_id(actor))
			for function in[elements[link.identifier()]for link in sbdl_ast.get_all_links_for_element(usecase,elements)if link.identifier()in elements and elements[link.identifier()].is_a(SBDL_Function)]:
				aspect=_F.join([link.identifier()for link in sbdl_ast.get_all_links_for_element(function,elements)if link.is_a(SBDL_Aspect)])
				if not aspect in aspects:aspects[aspect]=[]
				aspects[aspect].append([actor,usecase.identifier(),usecase.description(),function.identifier(),function.description(),usecase.reference(),function.reference()])
		relations=''
		for aspect in aspects:
			content+='\nframe "{}" {{\n'.format(puml_id(aspect))
			for usecase_tuple in aspects[aspect]:content+='  usecase "{}" as {}\n'.format('**{}**\\n{}{}'.format(usecase_tuple[3],_H.join(textwrap.wrap(usecase_tuple[4],get_config_value(_d,number=_A))),_H+usecase_tuple[6]if arguments.source else'')if get_config_value(_Ao,number=_A)else usecase_tuple[3],puml_id(usecase_tuple[3]));relations+='\n{} --> {}: {}{}\n'.format(puml_id(usecase_tuple[0]),puml_id(usecase_tuple[3]),'**{}**\\n{}'.format(usecase_tuple[1],_H.join(textwrap.wrap(usecase_tuple[2],get_config_value(_d,number=_A))))if get_config_value(_Ao,number=_A)else usecase_tuple[1],_H+usecase_tuple[5]if arguments.source else'')
			content+='}\n'
		content+=relations;return plant_uml_prepost.format(style=get_config_value(_Y)+get_config_value(_CI),header=header+title,content=content)
	plantuml_writer(file_opener,output_file,plantuml_usecase_diagram(),print_l);return errors
def write_network_graph_output(sbdl_ast,output_file,_,arguments,print_l):
	try:import networkx as nx,matplotlib.pyplot as plt
	except Exception as _:print_l('Could not import Python packages necessary for network graph rendering: networkx, matplotlib',error=_A);return 1
	elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A);sbdl_graph=nx.MultiDiGraph();skip_types={x for x in get_config_value(_Al,number=_C).split(_L)};node_name_sep=_e;node_text_wrap_width=25
	def node_name(elem_id):
		etype=elements[elem_id].type();stereotype=elements[elem_id].stereotype()
		if stereotype:stereotype=f"\n<<{etype}∷{stereotype}>>"
		else:stereotype=f"\n<<{etype}>>"
		node_text=_D.join(textwrap.wrap('{}{}{}'.format(elem_id,node_name_sep,elements[elem_id].type()),width=node_text_wrap_width));node_text+=stereotype
		if get_config_value(_C0,number=_A):node_text+=_j+_D.join(textwrap.wrap(elements[elem_id].description(),width=node_text_wrap_width))
		if arguments.source:node_text+=_j+_D.join(textwrap.wrap(elements[elem_id].reference(),width=node_text_wrap_width))
		return node_text
	def elem_id_f(node_name):return node_name_sep.join(node_name.replace(_D,'').split(node_name_sep)[:-1])
	for elem_id in elements:
		if elements[elem_id].type()not in skip_types:sbdl_graph.add_node(node_name(elem_id),elem_type=elements[elem_id].type())
	for elem_id in elements:
		if elements[elem_id].type()not in skip_types:
			elem_links=elements[elem_id].links()+elements[elem_id].parents()+elements[elem_id].children()
			for elem_link in elem_links:
				if not elem_link.type()in skip_types and elem_link.identifier()in elements:sbdl_graph.add_edge(node_name(elem_id),node_name(elem_link.identifier()))
	colour_list=[]
	for node in sbdl_graph:colour_list.append(SBDL_Semantics.type_color(elements[elem_id_f(node)].type()))
	def mpl(graphin):return nx.multipartite_layout(graphin,subset_key=_Dn)
	def dot(graphin):return nx.nx_agraph.graphviz_layout(graphin,prog='dot',args='-Goverlap=false -Goverlap_scaling=1000000 -Gscale={} -Gsep=20 -Gdefaultdist=100000'.format(get_config_value(_Ak,number=_A)))
	def spring(graphin):return nx.spring_layout(graphin,k=1)
	layouts={_Cg:mpl,'graphviz':dot,'spring':spring,'spiral':nx.spiral_layout,'random':nx.random_layout,'planar':nx.planar_layout};options={'font_size':10,'node_size':get_config_value(_Ak,number=_A),'edgecolors':'gray','edge_color':'darkgray','linewidths':1,'with_labels':get_config_value(_C1,number=_A),'width':2,'node_color':colour_list,'node_shape':'s','pos':layouts[get_config_value(_C2,number=_C)](sbdl_graph)};plt.figure(figsize=(get_config_value(_Bz,number=_A),get_config_value(_B_,number=_A)));nx.draw_networkx(sbdl_graph,**options,alpha=1.);ax=plt.gca();ax.margins(.0);plt.axis('off');plt.tight_layout();plt.plot();plt.savefig(output_file,dpi=get_config_value(_C3,number=_A));return errors
def write_sigma_graph_output(sbdl_ast,output_file,_,arguments,print_l):
	nx=lazy_import('networkx');Sigma=lazy_import('ipysigma').Sigma;elements,errors=sbdl_ast.elements(print_l,prune_missing_references=_A);sbdl_graph=nx.MultiDiGraph();skip_types={x for x in get_config_value(_Al,number=_C).split(_L)}
	for elem_id in elements:
		if elements[elem_id].type()not in skip_types:elem=elements[elem_id];sbdl_graph.add_node(elem_id,elem_type=elem.type(),**{key:elem.get_property(key)for key in elem.properties()})
	for elem_id in elements:
		if elements[elem_id].type()not in skip_types:
			elem_links=elements[elem_id].links()+elements[elem_id].parents()+elements[elem_id].children()
			for elem_link in elem_links:
				if elem_link.type()not in skip_types and elem_link.identifier()in elements:sbdl_graph.add_edge(elem_id,elem_link.identifier(),relation=elem_link.type())
	Sigma.write_html(sbdl_graph,output_file,fullscreen=_A,node_color=_Dn,node_color_palette={elements[elem_id].type():SBDL_Semantics.type_color(elements[elem_id].type())for elem_id in elements},edge_label=_A_,node_label_size=sbdl_graph.degree,default_edge_type='arrow',node_size=sbdl_graph.degree,node_size_range=(3,30),start_layout=360,hide_info_panel=_C,background_color='#002b36',node_label_color='#93a1a1',node_pictogram_color='#eee8d5',node_halo_color='#268bd2',edge_color=_A_,edge_color_palette={elements[elem_id].type():SBDL_Semantics.type_color(elements[elem_id].type())for elem_id in elements})
	with open(output_file,'r',encoding=_a)as f:sigma_html=f.read()
	sigma_html=sigma_html.replace('<title>IPyWidget export</title>',f"<title>{arguments.title}</title>")
	with open(output_file,'w',encoding=_a)as f:f.write(sigma_html)
	return errors
class OpenFMEA:
	@classmethod
	def generate_sbdl_table_from_ast(self,sbdl_ast,id_in_body=_C,source_in_body=_C,print_l=print_null,multi_field_separator=_B):
		A='key';complex_aspect=get_config_value(_Cb,number=_A)
		if multi_field_separator is _B:multi_field_separator=get_config_value(_Ap)
		id_counter=0;sbdl_row={A:_B,_f:_B,_W:_B,_J:_B,_X:_B,_Q:_B,_i:_B,_k:_B,_z:_B,_A0:_B,_AG:_B,_y:_B,_AF:_B,_A6:_B,_l:_B}
		def is_row_empty(row):
			for column in row:
				if row[column]!=_B:return _C
			return _A
		start_node,end_node,elements,_,errors=sbdl_ast.fully_connected_cme_graph()
		if errors>0:print_l('Generate Table WARNING: {} errors in CME graph'.format(errors),warning=_A)
		def element_text(elem):
			id_content='';body_content=str(elem.description());ref_content=''
			if id_in_body:id_content='[{}] '.format(elem.identifier())
			if source_in_body and elem.definition()!=_B:ref_content=' [{}]'.format(elem.reference())
			return id_content+body_content+ref_content
		def generate_table(element,row=_B,path=_B,add_single=_C):
			if path==_B:path={}
			print_l('ELEM:',element.identifier(),'(',path.keys(),')',debug=_A);rows=[]
			def do_clear():nonlocal row,path;print_l('CLEAR',debug=_A);row=sbdl_row.copy();path={element.identifier():_A}
			def recurse_links(type_class,append=_C):
				if not add_single or append:
					for link in SBDL_AST.get_all_links_for_element(element,elements):
						if elements[link.identifier()].is_a(type_class):
							if element.identifier()==start_node.identifier():do_clear()
							print_l('RECURSE:',element.identifier(),link.identifier(),debug=_A);rows.extend(generate_table(elements[link.identifier()],row if append else row.copy(),path.copy()))
			def assign_column_value(column,value):
				if not column in row or row[column]==_B:row[column]=value
				elif type(row[column])is list:
					if not value in row[column]:row[column].append(value)
				elif row[column]!=value:row[column]=[row[column],value]
			def assign_column(column,extra_content=''):assign_column_value(column,element_text(element)+extra_content)
			def add_row():
				def flatten_row(lrow):
					new_row=lrow.copy()
					for attr in new_row:
						if type(new_row[attr])is list:new_row[attr]=multi_field_separator.join(new_row[attr])
					return new_row
				def add_row_local(lrow):
					nonlocal id_counter
					if not is_row_empty(lrow):lrow[A]='{}'.format(str(id_counter).zfill(3));id_counter+=1;rows.append(lrow)
				def expand_rows_by_compound_attribute(in_rows,attr_name):
					result_rows=[]
					for in_row in in_rows:
						if attr_name in in_row:
							attrs_split=in_row[attr_name]if type(in_row[attr_name])is list else[in_row[attr_name]]
							for an_attr in attrs_split:new_row=in_row.copy();new_row[attr_name]=an_attr;result_rows.append(new_row)
						else:result_rows=[in_row]
					return result_rows
				for elem_id in path:elements[elem_id].visited=_A
				rows_to_add=[row];expand_attrs=get_config_value(_CT)
				if len(expand_attrs)>0:
					for exp_attr in expand_attrs.split(_L):rows_to_add=expand_rows_by_compound_attribute(rows_to_add,exp_attr)
				for add_row in rows_to_add:add_row_local(flatten_row(add_row))
			def is_fully_meta():
				result=_A
				for step in path:
					if not elements[step].is_meta():result=_C;break
				return result
			def is_partly_meta():clean_path=path.copy();clean_path.pop(start_node.identifier());clean_path.pop(end_node.identifier());return any([elements[x].is_meta()for x in clean_path])
			def get_compounded_linked_events():
				event_links=[x for x in element.links()if x.is_a(SBDL_Parser.Types.event)];result=[]
				for link in event_links:
					if link.identifier()in elements:result.append(element_text(elements[link.identifier()]))
				return'({})'.format(_F.join(result))if len(result)>0 else''
			if not element.identifier()in path:
				path[element.identifier()]=_A;custom_prop_prefix=SBDL_Parser.Attributes.custom_prefix+get_config_value(_Be)
				for aprop in element.properties():
					if aprop.startswith(custom_prop_prefix):passthrough_attribute_key=aprop.replace(custom_prop_prefix,'',1);passthrough_attribute_value=element.get_property(aprop);assign_column_value(passthrough_attribute_key,passthrough_attribute_value)
				if element.identifier()==start_node.identifier():recurse_links(SBDL_Failure_Cause)
				elif element.identifier()==end_node.identifier():
					print_l('ADD',element.identifier(),debug=_A)
					if not is_fully_meta()and(get_config_value(_CV,number=_A)or not is_partly_meta()):add_row()
				elif element.is_a(SBDL_Failure_Cause):
					events=get_compounded_linked_events();assign_column(_Q,_I+events if len(events)>0 else'');assign_column_value(_A0,element.rating());assign_column_value(_AG,element.rating(post=_A))
					if not element.is_meta():
						if complex_aspect:recurse_links(SBDL_Aspect,append=_A)
						recurse_links(SBDL_Failure_Current_Control,append=_A);recurse_links(SBDL_Failure_Current_Detection,append=_A);recurse_links(SBDL_Test_Definition,append=_A);recurse_links(SBDL_Failure_Action_Control,append=_A);recurse_links(SBDL_Failure_Action_Detection,append=_A)
					recurse_links(SBDL_Failure_Mode)
				elif element.is_a(SBDL_Failure_Mode):
					assign_column(_J);assign_column_value(_y,element.rating());assign_column_value(_AF,element.rating(post=_A))
					if not element.is_meta():
						if complex_aspect:recurse_links(SBDL_Aspect,append=_A)
						recurse_links(SBDL_Requirement,append=_A);recurse_links(SBDL_Failure_Current_Control,append=_A);recurse_links(SBDL_Failure_Current_Detection,append=_A);recurse_links(SBDL_Test_Definition,append=_A);recurse_links(SBDL_Failure_Action_Control,append=_A);recurse_links(SBDL_Failure_Action_Detection,append=_A)
					recurse_links(SBDL_Failure_Effect)
				elif element.is_a(SBDL_Failure_Effect):
					assign_column(_X);assign_column_value(_z,element.rating())
					if not element.is_meta()and complex_aspect:recurse_links(SBDL_Aspect,append=_A)
					recurse_links(end_node.type())
				elif element.is_a(SBDL_Aspect):element.visited=_A;assign_column(_f)
				elif element.is_a(SBDL_Requirement):element.visited=_A;assign_column(_W);recurse_links(SBDL_Aspect,append=_A)
				elif element.is_a(SBDL_Failure_Current_Control):element.visited=_A;assign_column(_i)
				elif element.is_a(SBDL_Failure_Current_Detection)or element.is_a(SBDL_Test_Definition):element.visited=_A;assign_column(_k)
				elif element.is_a(SBDL_Failure_Action_Control):element.visited=_A;assign_column(_A6)
				elif element.is_a(SBDL_Failure_Action_Detection):element.visited=_A;assign_column(_l)
				else:print_l('Unrecognised item in CME graph',element.identifier(),debug=_A)
			else:print_l('BACKEDGE',debug=_A)
			if add_single:print_l('ADD-SINGLE',element.identifier(),debug=_A);add_row()
			return rows
		def get_orphans(elements_l):
			rows=[]
			if get_config_value(_CU,number=_A):
				for el in elements_l:
					if not'visited'in vars(elements[el])and not elements[el].is_a(SBDL_Event)and not elements[el].is_meta():print_l('ORPHAN',elements[el].identifier(),debug=_A);rows.extend(generate_table(elements[el],row=sbdl_row.copy(),add_single=_A))
			return rows
		def apply_smart_keys(input_rows):
			if get_config_value(_CW,number=_A):
				for row in input_rows:
					join_props=[_W,_Q,_J,_X];join_string=''
					for join_prop in join_props:
						if join_prop in row and row[join_prop]:join_string+=row[join_prop]
					if len(join_string)>0:row[A]=SBDL_Semantics.get_unique_id_from_string(join_string)
		def check_row_keys(input_rows):
			keys_found={}
			for row in input_rows:
				if A in row:
					key_value=row[A]
					if key_value in keys_found:print_l(f"Warning: duplicate keys detected in OpenFMEA output: {key_value}",error=_A)
					else:keys_found[key_value]=_A
		resultant_entries=generate_table(start_node)+get_orphans(elements);apply_smart_keys(resultant_entries);check_row_keys(resultant_entries);return resultant_entries,errors
	@classmethod
	def struct_from_table(self,title,sbdl_table,open_fmea_type=_S):
		result_struct={_B8:_AN,'version':'1.0',_M:open_fmea_type,_m:{'title':title,'application':name(),'application_version':version(),'cmd_line':str(sys.argv),'date':get_date_string()},_q:sbdl_table,'layout':{}};result_struct[_m]['user']=get_user_string()
		try:result_struct[_m][_A3]=platform.uname()
		except Exception as _:result_struct[_m][_A3]='UNKNOWN'
		return result_struct
	@classmethod
	def read_file(self,file_path):
		open_sbdl_data=_B
		with open_input_file(file_path)as open_sbdl_file:open_sbdl_data=self.read_struct_from_file(open_sbdl_file)
		return open_sbdl_data
	@classmethod
	def write_struct_to_file(self,open_sbdl_struct,open_sbdl_file,pretty=_A):json.dump(open_sbdl_struct,open_sbdl_file,indent=2 if pretty else _B)
	@classmethod
	def read_struct_from_file(self,open_sbdl_file):
		try:open_sbdl_content=json.load(open_sbdl_file)
		except json.JSONDecodeError as e:raise Exception(f"Not an OpenFMEA file (invalid JSON?): {str(e)}")from e
		if not(_B8 in open_sbdl_content and open_sbdl_content[_B8]==_AN):raise Exception('Not an OpenFMEA file')
		if not(_M in open_sbdl_content and open_sbdl_content[_M]in[_S,'Analyzer']):raise Exception('Not an FMEA file')
		return open_sbdl_content
	@classmethod
	def replace_table_entries_in_place(self,table_data,from_value,to_value,number_value=10):
		for row in range(len(table_data)):
			for column in table_data[row]:
				if table_data[row][column]==from_value:
					target_value=to_value
					if column in{_z,_A0,_y}:target_value=number_value
					table_data[row][column]=target_value
		return table_data
	@classmethod
	def write_opensbdl_output(self,sbdl_ast,output_file,file_opener,arguments,print_l):
		sbdl_table,errors=OpenFMEA.generate_sbdl_table_from_ast(sbdl_ast,id_in_body=arguments.identifier,source_in_body=arguments.source,print_l=print_l)
		with file_opener(output_file)as open_sbdl_file:OpenFMEA.write_struct_to_file(OpenFMEA.struct_from_table(arguments.title,self.replace_table_entries_in_place(sbdl_table,_B,'')),open_sbdl_file)
		return errors
	@classmethod
	def get_quickshare_output(self,sbdl_ast,arguments,print_l):mem_file=io.StringIO();sbdl_table,errors=OpenFMEA.generate_sbdl_table_from_ast(sbdl_ast,id_in_body=arguments.identifier,source_in_body=arguments.source,print_l=print_l);OpenFMEA.write_struct_to_file(OpenFMEA.struct_from_table(arguments.title,self.replace_table_entries_in_place(sbdl_table,_B,'')),mem_file,pretty=_C);mem_file.seek(0);compressed_openfmea=base64.b64encode(zlib.compress(mem_file.read().encode(_a))).decode(_a);mem_file.close();output_uri='{baseurl}/analyzer/?QUICK_SHARE#>>{content}<<'.format(baseurl=get_config_value(_CX),content=compressed_openfmea);return errors,output_uri
	@classmethod
	def write_quickshare_output(self,sbdl_ast,output_file,file_opener,arguments,print_l):
		errors,output_uri=self.get_quickshare_output(sbdl_ast,arguments,print_l)
		with file_opener(output_file)as quickshare_file:quickshare_file.write(output_uri)
		return errors
	@classmethod
	def write_opensbdl_portfolio_output(self,sbdl_ast,output_file,file_opener,arguments,print_l):
		sbdl_table,errors=OpenFMEA.generate_sbdl_table_from_ast(sbdl_ast,id_in_body=arguments.identifier,source_in_body=arguments.source,print_l=print_l);sbdl_table=self.replace_table_entries_in_place(sbdl_table,_B,'');sbdl_elements,errors_e=sbdl_ast.elements(print_l,prune_missing_references=_A);errors+=errors_e;top_level_name=arguments.title;generic_fmea_name='General';content_section_name=_S;fmea_content_name=_q;portfolio_struct={}
		def add_entry_to_path(path_arr,entry):
			struct_entry=portfolio_struct
			for path_entry in path_arr:
				if not path_entry in struct_entry:struct_entry[path_entry]=[]if path_entry==fmea_content_name else{}
				struct_entry=struct_entry[path_entry]
			struct_entry.append(entry)
		def get_entry_path(entry):
			def trace_aspect_path(aspect,path=_B):
				if path==_B:path=[]
				if aspect in sbdl_elements:
					path=[aspect,*path];parent_aspects=sbdl_elements[aspect].parents()
					if len(parent_aspects)==1:path=trace_aspect_path(parent_aspects[0].identifier(),path)
				return path
			aspect_name=self.get_id_from_string(entry[_f]);entry_path=trace_aspect_path(aspect_name)
			if len(entry_path)==0:entry_path.append(generic_fmea_name)
			return[*entry_path,'{} {}'.format(content_section_name,aspect_name),fmea_content_name]
		for anentry in sbdl_table:add_entry_to_path([top_level_name,*get_entry_path(anentry)],anentry)
		with file_opener(output_file)as open_sbdl_portfolio_file:OpenFMEA.write_struct_to_file(OpenFMEA.struct_from_table(arguments.title,portfolio_struct,open_fmea_type='Portfolio'),open_sbdl_portfolio_file)
		return errors
	@classmethod
	def get_id_from_string(self,string_content,raw=_C):
		string_content=string_content.lstrip();result=_B
		if string_content and len(string_content)>2 and string_content[0]==_g:
			level=1;i=1
			while i<len(string_content)and level>0:
				if string_content[i]==_g:level+=1
				elif string_content[i]==']':level-=1
				i+=1
			if level==0:
				result=string_content[:i]
				if not raw:content=result[1:-1];result=SBDL_Parser.Tokens.replacement_string_default.join(content.split());result=SBDL_Parser.sanitize_identifier(result)
		return result
	@classmethod
	def remove_id_from_description(self,description):
		result=description;id_string=self.get_id_from_string(description,_A)
		if id_string!=_B:result=result.replace(id_string,'')
		return result
	@classmethod
	def add_id_to_string(self,identifier,string_content):return'[{}] {}'.format(identifier,string_content)
	@classmethod
	def sbdl_elements_from_table(self,sbdl_table,_):return self.elements_from_table(sbdl_table,_)
	@classmethod
	def elements_from_table(self,sbdl_table,_):
		multi_field_separator=get_config_value(_Ap);elems_to_consider={_f:SBDL_Parser.Types.aspect,_W:SBDL_Parser.Types.requirement,_J:SBDL_Parser.Types.mode,_X:SBDL_Parser.Types.effect,_Q:SBDL_Parser.Types.cause,_i:SBDL_Parser.Types.current_control,_k:SBDL_Parser.Types.current_detection,_A6:SBDL_Parser.Types.action_control,_l:SBDL_Parser.Types.action_detection};elem_rating={_X:_z,_Q:_A0,_J:_y};elem_rating_post={_Q:_AG,_J:_AF};elem_link={_Q:[_J,_i,_A6,_k,_l],_J:[_f,_X,_k,_W,_l,_Q],_W:[_f,_J],_X:[_J],_i:[_Q],_k:[_J],_A6:[_Q],_l:[_J]};elem_present_every_row={_k,_l};replace_str=SBDL_Parser.Tokens.replacement_string_default;elements={};id_lookup={}
		if not get_config_value(_CZ,number=_A):
			if get_config_value(_Ca,number=_A):elem_link[_X].remove(_J);elem_link[_Q].remove(_J)
			else:elem_link[_J].remove(_Q);elem_link[_X].remove(_J)
		def get_id_from_attr(attr_type,attr):
			A='$COUNTER$';result=self.get_id_from_string(attr)
			if result!=_B:0
			elif attr_type in id_lookup and attr in id_lookup[attr_type]:result=id_lookup[attr_type][attr]
			else:
				if not attr_type in id_lookup:id_lookup[attr_type]={};id_lookup[attr_type][A]=1
				result=id_lookup[attr_type][attr]=replace_str.join((elems_to_consider[attr_type]+replace_str+str(id_lookup[attr_type][A]).zfill(3)).split());id_lookup[attr_type][A]+=1
			return result
		def split_multi_field(multi_data):return[x.strip()for x in str(multi_data).split(multi_field_separator)]
		def present_on_relevant_rows(source_content,source_column,target_content,target_column):
			result=_A
			if target_column in elem_present_every_row:
				for row in sbdl_table:
					source_content_full=split_multi_field(row[source_column])
					for source_content_l in source_content_full:
						if self.get_id_from_string(source_content_l)==source_content:
							target_content_full=[self.get_id_from_string(x)for x in split_multi_field(row[target_column])]
							if not target_content in target_content_full:result=_C
			return result
		def relation_present_for_elem(elem,relation):return relation.identifier()in[x.identifier()for x in elem.relations()]
		def get_relation_type_for_elem(elem,rel_type):return[x for x in elem.relations()if x.is_a(rel_type)]
		def prune_cause_mode_related_elements(element_domain):
			for(_,elem)in element_domain.items():
				if elem.is_a(SBDL_Failure_Cause):
					rels=elem.relations();mode_rels=get_relation_type_for_elem(elem,SBDL_Failure_Mode);det_rels=get_relation_type_for_elem(elem,SBDL_Failure_Current_Detection)+get_relation_type_for_elem(elem,SBDL_Failure_Action_Detection)
					for det_rel in det_rels:
						mode_linked_det=_A
						for mode_rel in mode_rels:
							if not relation_present_for_elem(element_domain[mode_rel.identifier()],det_rel):mode_linked_det=_C
						if mode_linked_det:rels=[x for x in rels if x.identifier()!=det_rel.identifier()]
					elem.set_relations(rels)
		def handle_elem(row_data,attr):
			attr_data_full=split_multi_field(row_data[attr])
			for attr_data in attr_data_full:
				attr_data_no_id=self.remove_id_from_description(attr_data).strip()
				if len(attr_data)>0:
					if attr in elems_to_consider:
						id_ref=get_id_from_attr(attr,attr_data);elem=_B
						if id_ref in elements:elem=elements[id_ref]
						else:elem=SBDL_Element_Synthetic(id_ref,attr_data_no_id,elems_to_consider[attr],row_data[elem_rating[attr]]if attr in elem_rating and elem_rating[attr]in row_data else _B,rating_post=row_data[elem_rating_post[attr]]if attr in elem_rating_post and elem_rating_post[attr]in row_data else _B);elements[id_ref]=elem
						if attr in elem_link:
							for link_type in elem_link[attr]:
								target_content_full=split_multi_field(row_data[link_type])
								for target_content in target_content_full:
									if target_content!=_B and len(target_content)>0 and present_on_relevant_rows(id_ref,attr,get_id_from_attr(link_type,target_content),link_type):elements[id_ref].add_link(SBDL_Element.Element_Link(get_id_from_attr(link_type,target_content),elems_to_consider[link_type]))
		for row in sbdl_table:
			row_data=row.copy()
			for attr in row_data:handle_elem(row_data,attr)
		prune_cause_mode_related_elements(elements);return list(elements.values())
	@classmethod
	def sbdl_elements_from_general(self,general_entries,fmea_elements,_):return self.elements_from_general(general_entries,fmea_elements,_)
	@classmethod
	def elements_from_general(self,general_entries,fmea_elements,_):
		D='edges';C='type_definition';B='count';A='type_name';general_elements=[];fmea_elements_map={elem.identifier():elem for elem in fmea_elements};supported_general_types={'fault':{A:'fmea:fault',C:f"customtype fmea:fault is fmea:cause {{ relation_type is fmea:fault,{SBDL_Parser.Types.requirement},{SBDL_Parser.Types.cause} }}",B:0}}
		def entry_identifier(gen_ent):
			identifier=self.get_id_from_string(gen_ent[_O])
			if identifier is _B:identifier=gen_ent[_r]
			return identifier
		for general_element in general_entries:
			gen_elem_obj=general_entries[general_element]
			if _r in gen_elem_obj and _O in gen_elem_obj and _M in gen_elem_obj:
				if gen_elem_obj[_M]in supported_general_types:
					if supported_general_types[gen_elem_obj[_M]][B]==0:supported_general_types[gen_elem_obj[_M]][B]+=1;new_customtype_statement=SBDL_Parser.parse_customtype(SBDL_Parser.Parser_Element(supported_general_types[gen_elem_obj[_M]][C],_AN,0));new_customtype_def=SBDL_CustomType(new_customtype_statement);general_elements.append(new_customtype_def)
					new_gen_elem=SBDL_Element_Synthetic(entry_identifier(gen_elem_obj),self.remove_id_from_description(gen_elem_obj[_O]),supported_general_types[gen_elem_obj[_M]][A],_B)
					if D in gen_elem_obj:
						for edge in gen_elem_obj[D]:
							explicit_identifier=self.get_id_from_string(edge);matching_identifier=_B;matching_type=_B
							for other_entry in general_entries.values():
								if _O in other_entry and other_entry[_O]==edge:matching_identifier=entry_identifier(other_entry);matching_type=supported_general_types[other_entry[_M]][A]if other_entry[_M]in supported_general_types else other_entry[_M]
							if matching_identifier is _B:
								if explicit_identifier is not _B:
									if explicit_identifier in fmea_elements_map:matching_identifier=explicit_identifier;matching_type=fmea_elements_map[matching_identifier].type()
								elif isinstance(edge,str)and len(edge)>0:
									for fmea_element in fmea_elements:
										if fmea_element.description()==edge:matching_identifier=fmea_element.identifier();matching_type=fmea_element.type()
							if matching_identifier and matching_type:new_gen_elem.add_link(SBDL_Element.Element_Link(matching_identifier,matching_type))
					general_elements.append(new_gen_elem)
				else:debug_out(f"Unsupported general element type: {gen_elem_obj[_M]}")
			else:debug_out(f"Malformed general element type: {gen_elem_obj}")
		return general_elements
	@classmethod
	def sbdl_statements_from_openfmea(self,openfmea_data,source_name):return self.statements_from_openfmea(openfmea_data,source_name)
	@classmethod
	def statements_from_openfmea(self,openfmea_data,source_name):
		A='general';elements=[]
		if _q in openfmea_data:elements.extend(self.elements_from_table(openfmea_data[_q],source_name))
		else:debug_out(f"No FMEA data in {source_name}")
		if _m in openfmea_data and A in openfmea_data[_m]:elements.extend(self.elements_from_general(openfmea_data[_m][A],elements,source_name))
		else:debug_out(f"No general elements in {source_name}")
		return[SBDL_Parser.Parser_Element(x.definition().string(),source_name,_AN)for x in elements]
	@classmethod
	def aggregate_all_parser_elements_from_files(self,source_files,recurse,hidden,print_l):
		if hidden or recurse:raise Exception('Cannot recurse for OpenFMEA input files')
		aggregated_elements=[];errors=0
		for input_file in source_files:
			print_l('Reading: {}'.format(input_file))
			if is_input_file(input_file):open_sbdl_data=self.read_file(input_file);aggregated_elements.extend(self.statements_from_openfmea(open_sbdl_data,input_file))
			else:errors+=1;print_l(_Dj.format(input_file),error=_A)
		set_config_value(_A8,_A);set_config_value(_A9,_A);return aggregated_elements,errors
	@classmethod
	def aggregate_all_parser_elements_from_quickshare(self,source_files,recurse,hidden,_):
		if hidden or recurse:raise Exception('Cannot recurse for OpenFMEA-Quickshare input files')
		aggregated_elements=[];errors=0
		for input_file in source_files:
			with open_input_file(input_file)as open_quickshare_file:
				quickshare_content=open_quickshare_file.read();match=re.search('>>(.*?)<<',quickshare_content)
				if match:openfmea_file_content=zlib.decompress(base64.b64decode(match.group(1)));open_sbdl_data=self.read_struct_from_file(io.StringIO(openfmea_file_content.decode()));aggregated_elements.extend(self.statements_from_openfmea(open_sbdl_data,input_file))
		set_config_value(_A8,_A);set_config_value(_A9,_A);return aggregated_elements,errors
def get_csv_writer(file_object):return csv.writer(file_object,delimiter=_L,quotechar='"',quoting=csv.QUOTE_MINIMAL,lineterminator=_D)
def write_csv_output(sbdl_ast,output_file,file_opener,arguments,print_l):
	with file_opener(output_file)as csv_file:
		table,errors=OpenFMEA.generate_sbdl_table_from_ast(sbdl_ast,id_in_body=arguments.identifier,source_in_body=arguments.source,print_l=print_l);header=[]
		if len(table)>0:
			csv_writer=get_csv_writer(csv_file)
			for column in table[0]:header.append(column)
			csv_writer.writerow(header)
			for row in table:
				output_row=[]
				for column in header:
					if column in row:output_row.append(row[column])
					else:output_row.append('')
				csv_writer.writerow(output_row)
	return errors
def write_aggregated_output(sbdl_ast,output_file,file_opener,__,print_l):
	print_l('Writing aggregate output: {}'.format(output_file),debug=_A);new_line_str=_j if get_config_value(_T,number=_A)else _D
	with file_opener(output_file)as fmfile:
		sbdl_element_set,errors=sbdl_ast.elements(print_l=print_l);sbdl_elements=[sbdl_element_set[x]for x in sbdl_element_set];sort_by_type=get_config_value(_A8,number=_A);sort_by_iden=get_config_value(_A9,number=_A)
		def gen_sort_key(elem):
			key=[]
			if sort_by_type:key.append(elem.type())
			if sort_by_iden:key.append(elem.identifier())
			return tuple(key)
		if sort_by_type or sort_by_iden:sbdl_elements.sort(key=gen_sort_key)
		fmfile.write(SBDL_Parser.Tokens.hashbang+_D)
		for element in sbdl_elements:
			if not get_config_value(_AY,number=_A):element.delete_property(SBDL_Parser.Attributes.reference)
			print_l('Writing element: {}'.format(element.identifier()),debug=_A);fmfile.write('{}{} {}'.format(element.definition().string(),SBDL_Parser.Tokens.statement_separator if get_config_value(_Bb,number=_A)else'',new_line_str))
	return errors
def write_aggregated_output_f(sbdl_ast,output_file,file_opener,__,print_l):set_config_value(_T,_A);set_config_value(_AY,_C);SBDL_Parser.set_formal_spec();return write_aggregated_output(sbdl_ast,output_file,file_opener,__,print_l)
def write_rpc_output(sbdl_ast,output_file,file_opener,arguments,print_l):
	A='sbdl_statements';print_l('Remote processing to output [{}]: {}'.format(arguments.rpc,output_file),debug=_A);sbdl_elements,errors=sbdl_ast.elements();sbdl_statement_strings=[sbdl_elements[e].definition().string()for e in sbdl_elements]
	if arguments.rpc==_B:errors+=1;print_l('RPC parameter required',error=_A)
	else:
		rpc_result=rest_api_call(arguments.rpc,{_Ci:str(sys.argv),A:sbdl_statement_strings},print_l)
		with file_opener(output_file)as outfile:
			if isinstance(rpc_result,dict):
				if A in rpc_result and isinstance(rpc_result[A],list):outfile.write(_D.join(rpc_result[A]))
			else:outfile.write(rpc_result)
	return errors
def query_output(sbdl_ast,_,__,___,____):
	elements,errors=sbdl_ast.elements()
	for element_id in elements:element=elements[element_id];f_print('   Element ID: {}'.format(element.identifier()),opts=['BOLD'],prefix='');f_print('         Type: {}'.format(element.type()),opts=['BLUE'],prefix='');f_print('   Full Types: {}'.format(_F.join(element.types())),opts=['BLUE'],prefix='');f_print('  Description: {}'.format(element.description()),opts=['CYAN'],prefix='');f_print('        Links: {}'.format(_F.join([_AO.format(x.identifier(),x.type())for x in sbdl_ast.get_all_links_for_element(elements[element_id],elements)])),opts=[],prefix='');f_print('      Parents: {}'.format(_F.join([_AO.format(x.identifier(),x.type())for x in sbdl_ast.get_all_parents_for_element(elements[element_id],elements)])),opts=[],prefix='');f_print('     Children: {}'.format(_F.join([_AO.format(x.identifier(),x.type())for x in sbdl_ast.get_all_children_for_element(elements[element_id],elements)])),opts=[],prefix='');f_print('    Reference: {}'.format(element.reference()),opts=[],prefix='');f_print('   Properties: {}'.format(_F.join(['{}<-"{}"'.format(x,element.get_property(x))for x in element.properties()if x!=SBDL_Parser.Attributes.reference])),opts=[],prefix='');f_print('         Hash: {}'.format(element.hash()),prefix='')
	return errors
def write_matrixcsv_output(sbdl_ast,output_file,file_opener,__,___):
	elements_in,errors=sbdl_ast.elements();elements,errors_dict=sbdl_ast.get_full_ast_as_dict(elements_in,flatten_references=_A);errors+=errors_dict;column_pos={SBDL_Parser.Attributes.identifier:0,SBDL_Parser.Attributes.type:1,SBDL_Parser.Attributes.stereotype:2,SBDL_Parser.Attributes.description:3,SBDL_Parser.Attributes.reference:4}
	for element_id in elements:
		for akey in elements[element_id].keys():
			if not akey in column_pos:column_pos[akey]=len(column_pos.keys())
	with file_opener(output_file)as csvfile:
		csv_writer=get_csv_writer(csvfile);header=[_B]*len(column_pos)
		for acol in column_pos:header[column_pos[acol]]=acol
		csv_writer.writerow(header)
		for element_id in elements:
			output_row=['']*len(column_pos);output_row[column_pos[SBDL_Parser.Attributes.identifier]]=element_id
			for akey in elements[element_id]:output_row[column_pos[akey]]=elements[element_id][akey]
			csv_writer.writerow(output_row)
	return errors
def write_matrixjson_output(sbdl_ast,output_file,file_opener,__,___):
	elements,errors=sbdl_ast.elements();full_dict,errors_dict=sbdl_ast.get_full_ast_as_dict(elements);errors+=errors_dict
	with file_opener(output_file)as jsonfile:json.dump(full_dict,jsonfile,indent=2);jsonfile.write(_D)
	return errors
def write_yaml_tree_output(sbdl_ast,output_file,file_opener,__,___):
	yaml=lazy_import('yaml');elements,errors=sbdl_ast.elements();full_dict,errors_dict=sbdl_ast.get_full_ast_as_dict(elements);errors+=errors_dict
	with file_opener(output_file)as yamlfile:yaml.dump(full_dict,yamlfile,default_flow_style=_C,indent=2);yamlfile.write(_D)
	return errors
def fiddle_officex_file(office_file_path,output_file_path,fiddle_function):
	errors=0
	def zip_directory(dir_path,output_path):
		with zipfile.ZipFile(output_path,'w',zipfile.ZIP_DEFLATED)as zipf:
			for(root,_,files)in os.walk(dir_path):
				for file in files:file_path=os.path.join(root,file);arcname=os.path.relpath(file_path,start=dir_path);zipf.write(file_path,arcname)
	with tempfile.TemporaryDirectory()as tmp_dir:
		tmp_dir_path=pathlib.Path(tmp_dir)
		with zipfile.ZipFile(office_file_path,'r')as zf:zf.extractall(tmp_dir_path)
		errors+=fiddle_function(tmp_dir);zip_directory(tmp_dir_path,output_file_path)
	return errors
def apply_template_to_officex_files(officex_dir,sbdl_ast,print_l):
	def find_templatable_files(find_path,pattern):return[p for p in pathlib.Path(find_path).rglob(pattern)if p.is_file()]
	for templatable_file in find_templatable_files(officex_dir,'*.xml'):debug_out(f"Templating OfficeX file: {templatable_file}");write_template_fill(sbdl_ast,templatable_file,_B,arguments=types.SimpleNamespace(**{'template':str(templatable_file)}),print_l=print_l)
	return 0
def template_docx_file(template_dict,infile,outfile):warnings.filterwarnings(_A7,category=UserWarning,module='docxcompose');docxtpl=lazy_import('docxtpl');word_template=docxtpl.DocxTemplate(infile);word_template.render(template_dict);word_template.save(outfile)
def write_template_fill(sbdl_ast,output_file,file_opener,arguments,print_l):
	elements,errors=sbdl_ast.elements();full_dict,errors_dict=sbdl_ast.get_full_ast_as_dict(elements);errors+=errors_dict;template_context={_c:full_dict}
	if get_config_value(_CY,number=_A):openfmea,errors_fmea=OpenFMEA.generate_sbdl_table_from_ast(sbdl_ast,id_in_body=arguments.identifier,source_in_body=arguments.source,print_l=print_l);errors+=errors_fmea;template_context[_AP]=openfmea
	if arguments.template==_B:print_l('Template file not specified',error=_A);errors+=1
	else:
		file_ext=os.path.splitext(arguments.template)[1]
		if file_ext in['.docx']:debug_out('Word Docx template detected');template_docx_file(template_context,arguments.template,output_file)
		elif file_ext in['.xlsx','.xlsm']:debug_out('Generic Office-X template detected');errors+=fiddle_officex_file(arguments.template,output_file,functools.partial(apply_template_to_officex_files,sbdl_ast=sbdl_ast,print_l=print_l))
		else:
			debug_out('Standard plaintext template detected');import jinja2
			with open_input_file(arguments.template)as templateinput:template_content=templateinput.read()
			with file_opener(output_file)as templateoutput:
				try:templateoutput.write(jinja2.Environment().from_string(template_content).render(**template_context))
				except jinja2.TemplateSyntaxError as error:errors+=1;print_l('{}:{}:: {}'.format(arguments.template,error.lineno,error.message),error=_A)
				except jinja2.UndefinedError as error:errors+=1;print_l('{}:: {} (this can be due to using a non-existent index)'.format(arguments.template,error.message),error=_A)
				except jinja2.TemplateRuntimeError as error:errors+=1;print_l('{}:: {}'.format(arguments.template,error.message),error=_A)
	return errors
def write_type_information(_,output_file,file_opener,____,_____):
	all_types=[x for x in vars(SBDL_Parser.Types).values()if isinstance(x,str)]+list(SBDL_Semantics.custom_types.keys());skip_types=[_c,_AK]
	with file_opener(output_file)as output_file:
		output_file.write(_AI);output_file.write(_D);output_file.write('=====');output_file.write(_j)
		for type_name in all_types:
			if not type_name in skip_types:
				type_ancestors=SBDL_Element.declaration_type_to_class(type_name).types();output_file.write(type_name);output_file.write(_D);output_file.write('-'*len(type_name));output_file.write(_D);output_file.write('   Description: ')
				if type_name in SBDL_Semantics.type_information:output_file.write(SBDL_Semantics.type_information[type_name][_O])
				else:output_file.write('N/A')
				output_file.write(_D);output_file.write('     Relations: ');output_file.write(_F.join(SBDL_Semantics.get_valid_link_type_for_type_name(type_name)));output_file.write(_D);output_file.write('    Properties: ');output_file.write(_F.join(SBDL_Semantics.global_properties.keys()))
				if type_name in SBDL_Semantics.type_properties:output_file.write(_F);output_file.write(_F.join(SBDL_Semantics.type_properties[type_name]))
				output_file.write(_D);output_file.write('    Derivation: ');output_file.write('->'.join(type_ancestors))
				if len(type_ancestors)==1:output_file.write(' [BASE TYPE]')
				output_file.write(_j)
		output_file.write('Properties');output_file.write(_D);output_file.write('==========');output_file.write(_j)
		for(prop,prop_desc)in SBDL_Semantics.property_descriptions.items():output_file.write(f"{prop}: {prop_desc}\n")
	return 0
def affinity_graph():
	A='\n    + ';pre_post_content='@startuml\nskinparam linetype ortho\nskinparam backgroundColor transparent\nskinparam dpi 300\nhide circle\n\nframe "SBDL Type Affinity" #transparent {{\n\n{content}\n\n}}\n@enduml\n';graph_output='';skip_types=[SBDL_Parser.Tokens.using,SBDL_Parser.Tokens.scope,SBDL_Parser.Tokens.customtype,SBDL_Parser.Types.group,SBDL_Parser.Types.current_control,SBDL_Parser.Types.current_detection,SBDL_Parser.Types.action_control,SBDL_Parser.Types.action_detection,_c,_AK];emph=[SBDL_Parser.Attributes.description]
	def puml_id(input_str):return input_str.replace(_I,_V).replace('-',_V)
	lang_info_struct=SBDL_Semantics.get_language_information_struct()
	for category in lang_info_struct[_h]:
		graph_output+='frame "{}" {{\n'.format(category)
		for e_type in lang_info_struct[_h][category][_b]:
			if type(e_type)==type('')and e_type not in skip_types:graph_output+='  entity "**{}**" as {} {} {{{}\n    .. Generic ..\n{}\n  }}\n'.format(e_type,puml_id(e_type),SBDL_Semantics.type_color(e_type),A+A.join([x for x in SBDL_Semantics.type_properties[e_type]])if e_type in SBDL_Semantics.type_properties else'',_D.join(['    + '+x for x in SBDL_Semantics.global_properties if SBDL_Semantics.global_properties[x]]))
		graph_output+='}\n'
	for source in SBDL_Semantics.type_links:
		for target in SBDL_Semantics.type_links[source]:
			if type(source)==type('')and type(target)==type('')and source not in skip_types and target not in skip_types and source!=target:graph_output+='\n"{}" -[#gray]- "{}"'.format(puml_id(source),puml_id(target))
	for attr in emph:graph_output=graph_output.replace(attr,'//{}//'.format(attr))
	return pre_post_content.format(content=graph_output)
def list_imports_from_files(_,output_file,file_opener,__,___):
	with file_opener(output_file)as output:
		for imported_file in sorted(get_config_value(_t).keys()):output.write(f"{imported_file}\n")
	return 0
def install_package(package_name,package_url,install_target,print_l):
	errors=0
	try:
		print_l(f"Installing {package_name}...");bin_target=os.path.expanduser(install_target);print_l(f"Fetching {package_name} package...");plantuml_dl_file=make_remote_file_local(package_url);print_l(f'Installing {package_name} package to "{install_target}"...')
		with tarfile.open(plantuml_dl_file,'r:bz2')as tar:
			for afile in tar.getnames():print_l('Install: '+str(afile))
			tar.extractall(path=bin_target)
		print_l(f'{package_name} installed in "{install_target}".')
	except Exception as e:print_l(str(e),error=_A);errors+=1
	return errors
def install_pip(package_name,force=_C):ret,stdout,stderr=exec_external([sys.executable,_G,'pip','install',*(['--trusted-host',f"{urllib.parse.urlparse(__URL).netloc}"]if force else[]),'--upgrade',package_name,*(['--break-system-packages']if force else[])]);return ret,stdout,stderr
def update_self(print_l,force=_C):
	print_l(f"Updating {__NAME}...");ret,stdout,stderr=install_pip(urllib.parse.urljoin(url(),get_config_value(_Bi)),force=force);print_l(stdout)
	if ret!=0:print_l('Error during update:',error=_A);print_l(stderr,error=_A)
	return ret
def initialise_dependencies(print_l):
	errors=0;print_l('Installing dependencies...');install_target='~/.local/bin'
	if current_os()==_Aq:errors+=install_package('PlantUML',urllib.parse.urljoin(__URL,'deps/plantuml-headless-jre-linux-x64.tar.bz2'),install_target,print_l)
	else:print_l(f"ERROR: No automatic PlantUML dependency file available for {current_os()}",error=_A);errors+=1
	print_l(f"Installation complete (errors: {errors}).");return errors
def get_cache_file_path(input_file):
	cache_file_path=_B
	if input_file!=SBDL_Parser.Tokens.stdio_name:cache_file_path=f"{input_file}{get_config_value(_Bl)}"
	return cache_file_path
def check_ast_cache(do_caching,source_files,print_l):
	valid_cache=_C;cache_file=_B
	if do_caching:
		if len(source_files)==1:
			input_file=source_files[0];cache_file=get_cache_file_path(input_file)
			if cache_file is not _B:
				if os.path.exists(cache_file):
					if os.path.getmtime(cache_file)>os.path.getmtime(input_file):valid_cache=_A
					else:print_l(f"Cache dirty for: {input_file}")
				if valid_cache:
					for dep_file in get_config_value(_Aa):
						if os.path.exists(dep_file):
							if os.path.getmtime(cache_file)<os.path.getmtime(dep_file):valid_cache=_C;print_l(f"Cache dependency dirty: {dep_file}");break
			else:print_l(f"Not creating cache for unsuitable path: {input_file}")
		else:print_l(f"Not creating cache due to multiple ({len(source_files)}) input files")
	else:debug_out('Caching disabled')
	return valid_cache,cache_file
def store_ast_cache(ast_objects,cache_file_path,print_l):
	if cache_file_path is not _B:
		print_l(f"Creating new cache: {cache_file_path}")
		try:
			with open(cache_file_path,'wb')as f:pickle.dump(ast_objects,f)
		except Exception as e:print_l(f"Caching storage error: {str(e)} [{cache_file_path}] ",error=_A)
	else:print_l('Skipping cache file creation')
def load_ast_cache(parser_elements,do_caching,source_files,print_l):
	sbdl_ast_object=_B
	def gen_ast():nonlocal sbdl_ast_object;sbdl_ast_object=SBDL_AST(parser_elements)
	valid_cache,cache_file=check_ast_cache(do_caching,source_files,print_l)
	if do_caching:
		if valid_cache:
			print_l(f"Loading cache for: {_F.join(source_files)}");sbdl_ast_object=SBDL_AST([])
			try:
				with open(cache_file,'rb')as f:unpickled_object=pickle.load(f);sbdl_ast_object.force_parsed_objects(unpickled_object)
			except Exception as e:print_l(f"Caching error: {str(e)} - regenerating",error=_A);gen_ast();store_ast_cache(sbdl_ast_object.ast(),cache_file,print_l)
		elif len(source_files)==1:gen_ast();store_ast_cache(sbdl_ast_object.ast(),cache_file,print_l)
	if sbdl_ast_object is _B:gen_ast()
	return sbdl_ast_object
def import_custom_directive_file(file_path,print_l):
	errors=0
	try:loader=importlib.machinery.SourceFileLoader(os.path.basename(file_path),file_path);spec=importlib.util.spec_from_loader(os.path.basename(file_path),loader);macromodule=importlib.util.module_from_spec(spec);loader.exec_module(macromodule);add_to_config_value(_U,macromodule.DIRECTIVES)
	except Exception as e:print_l('Error during custom directive import for: {}\n  {}'.format(file_path,e),error=_A);errors+=1
	return errors
def import_custom_modes_file(file_path,print_l):
	errors=0;imported_modes={}
	try:loader=importlib.machinery.SourceFileLoader(os.path.basename(file_path),file_path);spec=importlib.util.spec_from_loader(os.path.basename(file_path),loader);modesmodule=importlib.util.module_from_spec(spec);loader.exec_module(modesmodule);imported_modes.update(modesmodule.MODES)
	except Exception as e:print_l('Error during custom mode import for: {}\n  {}'.format(file_path,e),error=_A);errors+=1
	return errors,imported_modes
def f_print(*content,end=_B,error=_C,verbose=_A,do_debug=_C,debug=_C,do_warning=_A,warning=_C,opts=_B,prefix=_B):
	A='RESET';end_l=_D;type_opts={'RED':'\x1b[1;31m','BLUE':'\x1b[1;34m','CYAN':'\x1b[1;36m','GREEN':'\x1b[0;32m',A:'\x1b[0;0m','BOLD':'\x1b[;1m','REVERSE':'\x1b[;7m'};file_l=sys.stdout
	if end!=_B:end_l=end
	if error or warning:file_l=sys.stderr
	def print_b(contentl,end):print(safe_stdout_endcode(contentl),end=end,file=file_l,flush=_A)
	if opts!=_B and allow_pretty_print():
		for opt in opts:
			if opt in type_opts:print_b(type_opts[opt],end='')
	if(verbose or error)and not debug and not warning or do_debug or warning and do_warning:print_b('{}{}{}'.format(__NAME if prefix==_B else prefix,':: 'if prefix==_B else'',content[0]if len(content)==1 else content),end=end_l)
	if opts!=_B and allow_pretty_print():print_b(type_opts[A],end='')
debug_out=functools.partial(f_print,debug=_A)
def main(arguments):
	C='fromopenfmea';B='fromopenfmea-quickshare';A='from:openfmea-quickshare'
	if get_config_value(_AX,number=_A):SBDL_Parser.set_formal_spec()
	abort_on_errors=not arguments.skip_errors;total_errors=0;print_l=functools.partial(f_print,verbose=arguments.verbose,do_debug=arguments.debug,do_warning=_A if arguments.warning=='all'else _C);start_time_point=time.time();step_time_point=start_time_point
	def check_step(step,error_count,content='',ignorable=_A):
		nonlocal total_errors,step_time_point;total_errors+=error_count
		if error_count>0:
			if abort_on_errors or not ignorable:print_l('Aborting due to {} errors during: {} {}'.format(error_count,step,content),error=_A);sys.exit(1)
			else:print_l('Encountered {} errors during: {} {}'.format(error_count,step,content),error=_A)
		else:print_l(f"{step} done [{time.time()-step_time_point:.3f}s]")
		step_time_point=time.time()
	operations={_B9:write_aggregated_output,'compile-f':write_aggregated_output_f,'aggregate':write_aggregated_output,'query':query_output,_AQ:write_matrixcsv_output,'matrixcsv':write_matrixcsv_output,_AR:write_matrixjson_output,'matrixjson':write_matrixjson_output,_BA:write_yaml_tree_output,_Do:write_csv_output,'fmeacsv':write_csv_output,_AP:OpenFMEA.write_opensbdl_output,'openfmea-quickshare':OpenFMEA.write_quickshare_output,_BB:OpenFMEA.write_opensbdl_portfolio_output,A:write_aggregated_output,_BC:write_aggregated_output,B:write_aggregated_output,C:write_aggregated_output,_BD:write_decomposition_graph_output,_BE:write_element_graph_output,_BF:write_requirement_graph_output,_BG:write_network_graph_output,'sigma-graph':write_sigma_graph_output,_BH:write_function_graph_output,_BI:write_process_graph_output,_BJ:write_state_graph_output,_BK:write_usecase_graph_output,_Dp:write_template_fill,'rpc':write_rpc_output,_AS:write_aggregated_output,_BL:write_aggregated_output,_AT:write_aggregated_output,'type-info':write_type_information,'lsp':lsp_start,'list-import':list_imports_from_files};macro_errors=0
	for amacropath in arguments.custom_directive:macro_errors+=import_custom_directive_file(amacropath,print_l)
	check_step('Custom directive import',macro_errors);mode_errors=0
	for amodepath in arguments.custom_mode:
		mode_error,new_modes=import_custom_modes_file(amodepath,print_l);mode_errors+=mode_error
		if mode_error==0:
			for new_mode in new_modes:
				def ext_mode_wrapper(a,b,c,d,e,func=new_modes[new_mode]):return func(a,b,c,d,e,_CONFIG_DATA)
				operations[new_mode]=ext_mode_wrapper
	check_step('Custom mode import',mode_errors)
	if arguments.already_processed:print_l('Input should be treated as already-processed');arguments.skip_validation=_A;set_config_value(_AC,_C)
	if not arguments.mode in operations:print_l('Mode "{}" is not in available modes'.format(arguments.mode),error=_A);check_step('Mode selection',1,ignorable=_C)
	if len(arguments.source_files)==0:print_l('WARNING: no source files specified',error=_A)
	aggregate_methods={_BC:OpenFMEA.aggregate_all_parser_elements_from_files,A:OpenFMEA.aggregate_all_parser_elements_from_quickshare,C:OpenFMEA.aggregate_all_parser_elements_from_files,B:OpenFMEA.aggregate_all_parser_elements_from_quickshare,_AS:aggregate_all_parser_elements_from_json,_BL:aggregate_all_parser_elements_from_yaml,_AT:aggregate_all_parser_elements_from_csv};aggregation_method=aggregate_all_parser_elements_from_files if not arguments.mode in aggregate_methods else aggregate_methods[arguments.mode];parser_elements,read_errors=aggregation_method([fix_wsl_path(x)for x in arguments.source_files],arguments.recurse,arguments.hidden,print_l);check_step('File reading',read_errors);sbdl_ast=load_ast_cache(parser_elements,get_config_value(_Bk,number=_A),arguments.source_files,print_l);check_step('Parsing',sbdl_ast.check_parsing(print_l))
	for pre_hook in get_config_value(_Bf):check_step('Pre Hook: {}'.format(pre_hook.__name__),pre_hook(sbdl_ast,print_l))
	sbdl_elements,statement_errors=sbdl_ast.elements(print_l);check_step('Element instantiation',statement_errors)
	if arguments.filter_related!=_B:sbdl_ast.add_filter_element_set_connected(arguments.filter_related,arguments.filter_depth)
	if arguments.filter_related_x!=_B:sbdl_ast.add_filter_element_set_linked(arguments.filter_related_x,arguments.filter_depth)
	if arguments.filter_parents!=_B:sbdl_ast.add_filter_element_set_parents(arguments.filter_parents,arguments.filter_depth)
	if arguments.filter_children!=_B:sbdl_ast.add_filter_element_set_children(arguments.filter_children,arguments.filter_depth)
	if arguments.filter_group!=_B:sbdl_ast.add_filter_element_set_children(arguments.filter_group,arguments.filter_depth);sbdl_ast.add_filter_element_set_id('(?!{})'.format(arguments.filter_group))
	if arguments.filter_type!=_B:sbdl_ast.add_filter_element_set_type(arguments.filter_type)
	if arguments.filter_identifier!=_B:sbdl_ast.add_filter_element_set_id(arguments.filter_identifier)
	if arguments.filter_property!=_B:sbdl_ast.add_filter_element_set_property(*arguments.filter_property)
	if not arguments.skip_validation:_,ast_errors=sbdl_ast.validate_elements(sbdl_elements,print_l);check_step('Validation',ast_errors)
	else:check_step('Validation (skipped)',0)
	if arguments.trace:trace_elements,trace_errors=aggregate_all_parser_elements_from_files(arguments.trace,arguments.recurse,arguments.hidden,print_l);_,trace_process_errors=sbdl_ast.process_trace_elements(trace_elements,print_l);trace_errors+=trace_process_errors;check_step('Tracing',trace_errors)
	for post_hook in get_config_value(_n):check_step('Post Hook: {}'.format(post_hook.__name__),post_hook(sbdl_ast,print_l))
	print_l('Mode: {}'.format(arguments.mode));print_l('Writing: {}'.format(arguments.output));check_step('Output writing',operations[arguments.mode](sbdl_ast,arguments.output,open_output_file,arguments,print_l));print_l('C_DEF GLOBALS: {}'.format(get_config_value(_U)),debug=_A);print_l(f"All operations complete [{time.time()-start_time_point:.3f}s]");add_profile_mem_snapshot('main_compilation_process');return total_errors if not arguments.skip_errors else 0
def handle_arguments(args_l):F='append';E='config_file';D='normal';C='output_file';B='element_identifier';A='store_true';operating_modes={_B9:'Parse all specified input files, gather SBDL elements, validate model, apply filters, write SBDL-formatted output','query':'Compile inputs, then pretty print the results (after filtering)',_AQ:'Compile inputs, then write a CSV-formatted representation of the SBDL elements to the output',_AR:'Compile inputs, then write a JSON-formatted representation of the SBDL elements to the output',_BA:'Compile inputs, then write a YAML-formatted representation of the SBDL elements to the output',_AT:'Read SBDL-schema CSV-matrix inputs and write SBDL-formatted output',_AS:'Read SBDL-schema JSON-tree inputs and write SBDL-formatted output',_BL:'Read SBDL-schema YAML-tree inputs and write SBDL-formatted output',_AP:'Compile inputs, then write the FMEA-related content to an OpenFMEA-formatted ouput',_BB:'Compile inputs, then write the FMEA-related content to an OpenFMEA Portfolio-formatted ouput, organised by aspect hierarchy',_BC:'Read OpenFMEA-formatted input and write SBDL-formatted output',_Do:'Compile inputs, then write the FMEA-related content to a CSV-formatted ouput',_BG:'Compile inputs, then write a PNG-formatted output, visually representing the network of SBDL elements',_BF:'Compile inputs, then write a SysML-style requirements diagram to rendering-backend-formatted output',_BD:'Compile inputs, then write a SysML-style block diagram to rendering-backend-formatted output (simplified, aspects only)',_BE:'Compile inputs, then write a SysML-style block diagram to rendering-backend-formatted output (detailed, with properties and relations)',_BH:'Compile inputs, then write a SysML-style sequence diagram to rendering-backend-formatted output',_BI:'Compile inputs, then write a SysML-style activity diagram to rendering-backend-formatted output',_BJ:'Compile inputs, then write a SysML-style state diagram to rendering-backend-formatted output',_BK:'Compile inputs, then write a SysML-style use-case diagram to rendering-backend-formatted output',_Dp:"Compile inputs, then provide an object, 'sbdl', in a Jinja parsing environment and apply it to the specified template file",'rpc':'Compile inputs, then transmit to the RPC server for processing by the specified RPC (see --rpc)'};parser=argparse.ArgumentParser(description='{}\n\n{} Version {} (DSL Version {}). System Behaviour Description Language (SBDL) compiler.\nWWW: {}. Author: {}.'.format(logo(),name().upper(),version(),version_dsl(),url(),author()),epilog='e.g. "'+os.path.basename(sys.argv[0])+' <file 1> <file 2> <file n>"\n\n---------------\nOperating Modes\n---------------\n{}'.format(_D.join(['{}: {}'.format(x.rjust(25),operating_modes[x])for x in operating_modes])),formatter_class=argparse.RawDescriptionHelpFormatter);parser._positionals.title='Base Arguments';parser._optionals.title='Optional Arguments';parser.add_argument('source_files',help='List of files to compile ["-" implies stdin]',nargs=_R);parser.add_argument(_G,_BM,metavar='operating_mode',help='Specify the mode of operation',default=_B9);parser.add_argument(_E,'--output',metavar=C,help='Specify the name of the output file',default=SBDL_Parser.Tokens.stdio_name);parser.add_argument('--version',help='Print the current version',action=A,default=_C);parser.add_argument('--debug',help=argparse.SUPPRESS,action=A,default=_C);parser.add_argument('-W','-w','--warning',help='Set warning level',choices=[D,'all'],default=D);parser.add_argument('--hidden',help='Include hidden files when recursing',action=A,default=_C);parser.add_argument('-i','--identifier',help='Include element identifiers in applicable output formats',action=A,default=_C);parser.add_argument('-s','--source',help='Include source reference in applicable output formats',action=A,default=_C);parser.add_argument('-r','--recurse',help='Recurse on directories specified in the input list',action=A,default=_C);parser.add_argument('--skip-errors',help='Do not stop for errors (emit warning instead)',action=A,default=_C);parser.add_argument('--skip-validation',help='Do not validate model elements and relations',action=A,default=_C);parser.add_argument('--already-processed',help='Disables certain processing actions (implies --skip-validation). Useful when input has already been processed',action=A,default=_C);parser.add_argument('--title',help='Provide a default title for certain output formats',default='');parser.add_argument('-v','--verbose',help='Enable verbose output during execution',action=A,default=_C);parser.add_argument('--dumpaffinitygraph',metavar=C,help=argparse.SUPPRESS,action=_B);parser.add_argument('--dumplanginfo',metavar=C,help=argparse.SUPPRESS,action=_B);parser.add_argument('--run_tests','--run-tests',action=A,help=argparse.SUPPRESS);parser.add_argument('--rest-api-ping',action=A,help=argparse.SUPPRESS);parser.add_argument('--manual',help='Show extensive {} manual page'.format(name().upper()),action=A);parser.add_argument('--dump-config','--dumpconfig',metavar=E,help='Dump the internal configuration to a named JSON file',action=_B);parser.add_argument('--load-config','--loadconfig',metavar=E,help='Load the internal configuration from a named JSON file',default=_B);parser.add_argument('--list-config','--listconfig',help='List internal configuration options',action=A,default=_C);parser.add_argument('--set-config','--setconfig',metavar=('config_option','config_value'),help='Set a named configuration option',action=F,nargs=2);parser.add_argument('-D','--define',metavar=('name','value'),help='Specify a named global definition',action=F,nargs=2);parser.add_argument('--trace',metavar='trace_files',help='Provide a trace file to be processed',default=_B,nargs=_R);parser.add_argument('--template',metavar='template_file',help="Specify a template file for the 'template-fill' mode",default=_B);parser.add_argument('-fr','--filter-related',metavar=B,help='Filter everything but those elements with a direct or indirect relation to the specified element identifier (regex)  [INCLUDES: parents/children]',default=_B);parser.add_argument('-frx','--filter-related-x',metavar=B,help='Filter everything but those elements with a direct or indirect connection to the specified element identifier (regex) [EXCLUDES: parents/children]',default=_B);parser.add_argument('-fch','--filter-children',metavar=B,help='Filter everything but those elements which are children of the specified element identifier (regex)',default=_B);parser.add_argument('-fpa','--filter-parents',metavar=B,help='Filter everything but those elements which are parental ancestors of the specified element identifier (regex)',default=_B);parser.add_argument('-fd','--filter-depth',metavar='filter_depth',help='Maximum depth for filters which pursue links (natural number)',type=int,default=_B);parser.add_argument('-ft','--filter-type',metavar='element_type',help='Filter everything but those elements which are of the specified element type (regex)',default=_B);parser.add_argument('-fi','--filter-identifier',metavar=B,help='Filter everything but those elements whose identifiers match the specified string (regex)',default=_B);parser.add_argument('-fpr','--filter-property',metavar=('property_name','property_value'),help='Filter everything but those elements possessing a named property matching the specified string (regex)',default=_B,nargs=2);parser.add_argument('-fg','--filter-group',metavar='group_identifier',help='Shortcut filter for everything but those elements which are children of the specified group identifier (regex) -- excludes the group element itself',default=_B);parser.add_argument(_Dq,metavar='compiler_definitions',help='Specify a file path defining custom compiler directives',default=[],nargs=_R);parser.add_argument(_BN,'--custom_mode',metavar='mode_definitions',help='Specify a file path containing custom compiler modes',default=[],nargs=_R);parser.add_argument('--rpc',help='Remote Procedure Call to be used by RPC-based modes',default=_B);parser.add_argument('--update',help=argparse.SUPPRESS,action=A,default=_C);parser.add_argument('--update-force',help=argparse.SUPPRESS,action=A,default=_C);parser.add_argument('--install-deps',help=argparse.SUPPRESS,action=A,default=_C);help_tmp=io.StringIO();parser.print_help(file=help_tmp);help_tmp.seek(0);global __HELP_TEXT;__HELP_TEXT=help_tmp.read();return parser.parse_args(args_l)
def show_manual(_):
	B='----------------------\n';A='===========================================================\n';manual_text=A;manual_text+='                      {} Manual\n'.format(name().upper());manual_text+=A;manual_text+=help_text()+_D;manual_text+=B;manual_text+='Model Type Information\n';manual_text+=B
	def add_manual(strcon=''):nonlocal manual_text;manual_text+=strcon+_D
	def add_manual_header(strhead,double=_C,headchar='-'):
		hrline=headchar*len(strhead);add_manual()
		if double:add_manual(hrline)
		add_manual(strhead);add_manual(hrline)
	sbdl_info_struct=SBDL_Semantics.get_language_information_struct();sbdl_properties=sbdl_info_struct[_A1];sbdl_types=sbdl_info_struct[_b];sbdl_directives=sbdl_info_struct[_B5]
	for category in sbdl_info_struct[_h]:
		add_manual_header(category,headchar='=');add_manual(sbdl_info_struct[_h][category][_O])
		for elem_type in sbdl_info_struct[_h][category][_b]:
			add_manual_header(_AO.format(elem_type,category));add_manual('Description:\n  {}'.format(sbdl_types[elem_type][_O]));add_manual();add_manual('Relations:\n  {}'.format(_L.join(sbdl_types[elem_type][_B0])));add_manual();add_manual('Properties :')
			for elem_prop in sbdl_types[elem_type][_A1]:
				if elem_prop in sbdl_properties:add_manual('  {:<15}: {}'.format(elem_prop,sbdl_properties[elem_prop]))
	add_manual('\n-------------------');add_manual('Compiler Directives');add_manual('-------------------\n')
	for directive in sbdl_directives:add_manual('  {}: {}'.format(directive,sbdl_directives[directive]))
	try:
		manual_command=get_config_value(_CM)
		if isinstance(manual_command,str)and len(manual_command)==0:f_print(_D+manual_text,prefix=_B)
		else:
			if not isinstance(manual_command,list):manual_command=shlex.split(manual_command)
			subprocess.run(manual_command,input=manual_text.encode(),check=_A)
	except:raise Exception('Error calling system process to display manual')
	return 0
class LanguageLevelTests(unittest.TestCase):
	TEST_OUTPUT_FILE='test.out';TEST_OUTPUT_FILE2='test2.out';TEST_DIAGRAM_OUTPUT_FILE='test.png'
	def setUp(self):reset()
	def testGeneral(self):main(handle_arguments([_K,_E,self.TEST_OUTPUT_FILE]))
	def testGeneralLarge(self):main(handle_arguments(['test/elevator.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testNested(self):main(handle_arguments(['test/nested_test.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testFunction(self):main(handle_arguments([_BO,_E,self.TEST_OUTPUT_FILE]))
	def testCrossRefs(self):main(handle_arguments(['test/deferred_directive.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testRelations(self):main(handle_arguments([_Dr,_E,self.TEST_OUTPUT_FILE]))
	def testLinks(self):main(handle_arguments(['test/link_test.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testCustomTypeParent(self):main(handle_arguments(['test/customtype_parent_test.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testCustomType(self):main(handle_arguments(['test/customtype_test.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testLanguageExample(self):main(handle_arguments([_AU,_AV,_E,self.TEST_OUTPUT_FILE]))
	def testFMEAHierarchical(self):main(handle_arguments(['test/fmea_hierarchical.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testEmbeddedExcel(self):main(handle_arguments(['test/excel_test.xlsx',_E,self.TEST_OUTPUT_FILE]))
	def testEmbeddedWord(self):main(handle_arguments(['test/word_test.docx',_E,self.TEST_OUTPUT_FILE]))
	def testEmbeddedBlock(self):main(handle_arguments(['test/sbdl_block_test.md',_E,self.TEST_OUTPUT_FILE]))
	def testRawContent(self):main(handle_arguments(['test/raw_test.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testScoping(self):main(handle_arguments(['test/file_scope_test.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testEscaping(self):main(handle_arguments(['test/escaped_directive_test.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testCustomDirectiveImport(self):main(handle_arguments(['test/directive_imp_test.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testProlog(self):main(handle_arguments(['test/test_prolog.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testCustomDirective(self):main(handle_arguments([_K,_Dq,_Ds,_E,self.TEST_OUTPUT_FILE]))
	def testCustomModes(self):main(handle_arguments([_K,_BM,'test_mode',_BN,_Ds,_E,self.TEST_OUTPUT_FILE]))
	def testIdFromProp(self):main(handle_arguments(['test/id_from_prop.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testDirectiveChain(self):main(handle_arguments(['test/macro_chain.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testMarkDownRefs(self):main(handle_arguments(['test/test.md',_E,self.TEST_OUTPUT_FILE]))
	def testNestedOperators(self):main(handle_arguments(['test/test_linking_syntax_scope.sbdl',_E,self.TEST_OUTPUT_FILE]))
	def testGeneralAspectDiagram(self):main(handle_arguments([_K,_E,self.TEST_DIAGRAM_OUTPUT_FILE,_G,_BD]))
	def testGeneralElementDiagram(self):main(handle_arguments([_K,_E,self.TEST_DIAGRAM_OUTPUT_FILE,_G,_BE]))
	def testGeneralFunctionDiagram(self):main(handle_arguments([_BO,_E,self.TEST_DIAGRAM_OUTPUT_FILE,_G,_BH]))
	def testGeneralProcessDiagram(self):main(handle_arguments([_BO,_E,self.TEST_DIAGRAM_OUTPUT_FILE,_G,_BI]))
	def testGeneralStateDiagram(self):main(handle_arguments([_K,_E,self.TEST_DIAGRAM_OUTPUT_FILE,_G,_BJ]))
	def testGeneralRequirementDiagram(self):main(handle_arguments([_K,_E,self.TEST_DIAGRAM_OUTPUT_FILE,_G,_BF]))
	def testGeneralUsercaseDiagram(self):main(handle_arguments([_K,_E,self.TEST_DIAGRAM_OUTPUT_FILE,_G,_BK]))
	def testGeneralNetworkDiagram(self):main(handle_arguments([_K,_E,self.TEST_DIAGRAM_OUTPUT_FILE,_G,_BG]))
	def testGeneralJSON(self):main(handle_arguments([_K,_E,self.TEST_OUTPUT_FILE,_G,_AR]))
	def testGeneralYAML(self):main(handle_arguments([_K,_E,self.TEST_OUTPUT_FILE,_G,_BA]))
	def testGeneralCSV(self):main(handle_arguments([_K,_E,self.TEST_OUTPUT_FILE,_G,_AQ]))
	def testGeneralOpenFMEA(self):main(handle_arguments([_K,_E,self.TEST_OUTPUT_FILE,_G,_AP]))
	def testGeneralOpenFMEAPortfolio(self):main(handle_arguments([_K,_E,self.TEST_OUTPUT_FILE,_G,_BB]))
	def testGeneralFMEACSV(self):main(handle_arguments([_K,_E,self.TEST_OUTPUT_FILE,_G,'fmeacsv']))
	def testFromJSON(self):main(handle_arguments([_AU,_AV,_E,self.TEST_OUTPUT_FILE,_G,_AR]));reset();main(handle_arguments([self.TEST_OUTPUT_FILE,_E,self.TEST_OUTPUT_FILE2,_G,_AS]))
	def testFromCSV(self):main(handle_arguments([_AU,_AV,_E,self.TEST_OUTPUT_FILE,_G,_AQ]));reset();main(handle_arguments([self.TEST_OUTPUT_FILE,_E,self.TEST_OUTPUT_FILE2,_G,_AT]))
	def testAPIBasic(self):
		ret_code,_,stderr=exec_external(['python','test/external_use_test.py'])
		if ret_code!=0:f_print(stderr,error=_A);raise Exception('API Test failed')
	def testSimpleSyntheticOutput(self):main(handle_arguments([_BM,'write-test',_BN,'extensions/element_write.py',_E,self.TEST_OUTPUT_FILE,_Dr]))
class LanguageSemanticTests(unittest.TestCase):
	def setUp(self):self.parser_elements,errors=aggregate_all_parser_elements_from_files([_AU,_AV],_C,_C,print_null,open_input_file);self.ast=SBDL_AST(self.parser_elements);self.sbdl_elements,statement_errors=self.ast.elements(print_null)
	def test_general_elements_check(self):0
	def test_general_attributes_check(self):
		for elem in self.sbdl_elements:0
	def test_geneal_relations_check(self):
		for elem in self.sbdl_elements:0
	def test_existence_type_semantic_check(self):
		expected_elem_type={_BP:SBDL_Aspect,'rocket_booster':SBDL_Aspect,_BQ:SBDL_Aspect,_Dt:SBDL_Aspect,_BR:SBDL_Requirement,_Du:SBDL_Requirement,_Dv:SBDL_Requirement,_BS:SBDL_Function,_Dw:SBDL_Event,_Dx:SBDL_Event,_Dy:SBDL_State,_Dz:SBDL_State,_BT:SBDL_Transition,_D_:SBDL_Event}
		for elem in expected_elem_type:0
	def test_existence_semantic_relation(self):
		relations_between={_Dt:_BQ,_BR:_BP,_Du:_BR,_Dv:_BQ,_BS:_BP,_Dw:_BS,_Dx:_D_,_Dy:_BT,_Dz:_BT}
		def relation_exists(elemidA,elemidB):return elemidB in[*[x.identifier()for x in SBDL_AST.get_all_children_for_element(self.sbdl_elements[elemidA],self.sbdl_elements)],*[x.identifier()for x in SBDL_AST.get_all_parents_for_element(self.sbdl_elements[elemidA],self.sbdl_elements)],*[x.identifier()for x in SBDL_AST.get_all_links_for_element(self.sbdl_elements[elemidA],self.sbdl_elements)]]
		for elem in relations_between:0
def run_tests(_):errors=0;test_results=unittest.main(argv=[name()],exit=_C,verbosity=2).result;errors=errors+len(test_results.failures)+len(test_results.errors);return errors
def handle_pre_exec_args(ARGS):
	global debug_out;debug_out=functools.partial(debug_out,do_debug=ARGS.debug)
	if ARGS.debug:set_config_value(_AZ,_A);ARGS.verbose=_A
	if ARGS.load_config!=_B:f_print('Loading JSON config from {}'.format(ARGS.load_config),verbose=ARGS.verbose);load_config(ARGS.load_config)
	if ARGS.set_config!=_B:
		for config_param_pair in ARGS.set_config:set_config_value(config_param_pair[0],config_param_pair[1]);lock_config_key(config_param_pair[0])
	if ARGS.define!=_B:
		if isinstance(_CONFIG_DATA[_U],dict):
			for macro_def_pair in ARGS.define:_CONFIG_DATA[_U][macro_def_pair[0]]=macro_def_pair[1]
	if ARGS.list_config:
		def ftype(object_ref):return str(type(object_ref)).replace('class ','').replace('<','').replace('>','').replace("'",'')
		for configoption in _CONFIG_DATA.keys():f_print('{:<40} [{:<8}]: "{}"'.format(configoption,ftype(_CONFIG_DATA[configoption]),str(get_config_value(configoption))[:30].replace(_D,_H)),prefix='')
		sys.exit(0)
	if ARGS.dump_config!=_B:
		f_print('Dumping JSON config to {} and exiting'.format(ARGS.dump_config),verbose=ARGS.verbose)
		with open_output_file(ARGS.dump_config)as configfile:json.dump(_CONFIG_DATA,configfile,indent=4)
		sys.exit(0)
	if ARGS.dumpaffinitygraph!=_B:
		f_print('Dumping Affinity Graph to {} and exiting'.format(ARGS.dump_config),verbose=ARGS.verbose)
		with open_output_file(ARGS.dumpaffinitygraph)as affinityfile:affinityfile.write(affinity_graph())
		sys.exit(0)
	if ARGS.dumplanginfo!=_B:
		f_print('Dumping Languge Information JSON to {} and exiting'.format(ARGS.dumplanginfo),verbose=ARGS.verbose)
		with open_output_file(ARGS.dumplanginfo)as langinfofile:json.dump(SBDL_Semantics.get_language_information_struct(),langinfofile,indent=2)
		sys.exit(0)
	if ARGS.run_tests:sys.exit(run_tests(ARGS))
	if ARGS.rest_api_ping:sys.exit(rest_api_call_ping(f_print))
	if ARGS.manual:sys.exit(show_manual(ARGS))
	if ARGS.update or ARGS.update_force:sys.exit(update_self(f_print,ARGS.update_force))
	if ARGS.install_deps:sys.exit(initialise_dependencies(f_print))
	if ARGS.version:
		if ARGS.verbose:
			version_output=f"{version()} (SBDL Compiler), {version_dsl()} (SBDL Language)"
			if ARGS.debug:version_output+=f", {version_dev()} (Development Hash)"
			f_print(version_output,prefix='')
		else:f_print(version(),prefix='')
		sys.exit(0)
def whoopsie_handler(whoopsie):
	A='---------------------------------';w_print=functools.partial(f_print,error=_A);p_print=functools.partial(w_print,opts=['RED']);w_print('             RUHROH!             ',opts=['BOLD']);p_print('          _ ._  _ , _ ._');p_print("        (_ ' ( `  )_  .__)");p_print('      ( (  (    )   `)  ) _)');p_print('     (__ (_   (_ . _) _) ,__)');p_print("         `~~`\\ ' . /`~~`");p_print('              ;   ;');p_print('              /   \\\\');p_print('_____________/_ __ \\_____________');w_print(A);w_print('>>>> Internal Compiler Error <<<<');w_print(A);w_print('       Version Information       ');w_print(A);w_print('COMPILER: {}'.format(__VERSION));w_print('     DSL: {}'.format(__VERSION_DSL));w_print(A);w_print('          Command Line           ');w_print(A);w_print(str(sys.argv));w_print(A);w_print('          Error Content          ');w_print(A)
	try:w_print('"{}"'.format(str(whoopsie)))
	except Exception as _:w_print(_A2)
	if isinstance(whoopsie,Exception)and get_config_value(_AZ,number=_A):
		w_print(A);w_print('           Stack Trace           ');w_print(A)
		try:w_print(traceback.format_exc(),end='',prefix='')
		except Exception as _:w_print('Could not print stack trace')
	w_print(A);sys.exit(99)
def init_profiling():
	global main,main_original;profile_output=get_config_value(_Cd);profile_output_mem=get_config_value(_v)
	def wrap_main(ARGS):
		f_print(f"Profiling performance to: {profile_output}",warning=_A,do_warning=_A);cProfile=lazy_import('cProfile')
		def profile_main():main_original(ARGS)
		cProfile.runctx('profile_main()',globals(),locals(),profile_output)
	if profile_output is not _B:main_original=main;main=wrap_main
	if profile_output_mem is not _B:f_print(f"Profiling memory to: {profile_output_mem}",warning=_A,do_warning=_A);lazy_import(_BU).start()
def add_profile_mem_snapshot(snapshot_name):
	if get_config_value(_v)is not _B:add_to_config_value(_w,{snapshot_name:lazy_import(_BU,'Tracemalloc not available').take_snapshot()})
def add_profile_mem_counter(counter_name,counter_incrememnt=1):
	if get_config_value(_v)is not _B:
		snapshots=get_config_value(_w)
		if not counter_name in snapshots:snapshots[counter_name]=0
		snapshots[counter_name]+=counter_incrememnt
def deinit_profiling():
	A='----------------\n';profile_output_mem=get_config_value(_v)
	if profile_output_mem is not _B:
		tracemalloc=lazy_import(_BU);current,peak=tracemalloc.get_traced_memory();tracemalloc.stop()
		with open(profile_output_mem,'w',encoding=_a)as profile_output_file:
			profile_output_file.write(A);profile_output_file.write(' General Stats\n');profile_output_file.write(A);profile_output_file.write(f"Memory in use at termination: {current/1024**2:.2f} MB\n");profile_output_file.write(f"Peak memory during execution: {peak/1024**2:.2f} MB\n")
			for(snapshot_name,snapshot)in get_config_value(_w).items():
				if not isinstance(snapshot,tracemalloc.Snapshot):profile_output_file.write(f"{snapshot_name}: {str(snapshot)}\n")
			for(snapshot_name,snapshot)in get_config_value(_w).items():
				if isinstance(snapshot,tracemalloc.Snapshot):
					profile_output_file.write('\n----------------\n');profile_output_file.write(f" {snapshot_name}\n");profile_output_file.write(A);top_stats=snapshot.statistics('lineno');profile_output_file.write(f"Top 20 memory allocation in {snapshot_name}:\n")
					for stat in range(0,19):
						if stat<len(top_stats):profile_output_file.write(f"  {stat+1:02d}: {str(top_stats[stat])}\n")
def run_main():
	init_data()
	try:ARGS=handle_arguments(sys.argv[1:]);handle_pre_exec_args(ARGS);init_profiling();main_result=main(ARGS);deinit_profiling();sys.exit(main_result)
	except Exception as whoopsie:whoopsie_handler(whoopsie)
def init_data():global __LOGO;__LOGO='\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;0m \x1b[0m\x1b[38;2;1;1;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;2;3m \x1b[0m\x1b[38;2;3;3;4m \x1b[0m\x1b[38;2;3;2;4m \x1b[0m\x1b[38;2;2;2;4m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;0;0m \x1b[0m\x1b[38;2;4;3;1m \x1b[0m\x1b[38;2;2;1;1m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;1;1;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;2;3m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;5;6;8m \x1b[0m\x1b[38;2;3;5;12m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;3;1;2m \x1b[0m\x1b[38;2;3;1;1m \x1b[0m\x1b[38;2;8;5;2m \x1b[0m\x1b[38;2;5;4;2m \x1b[0m\x1b[38;2;1;0;1m \x1b[0m\x1b[38;2;2;0;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;1;2m \x1b[0m\x1b[38;2;3;4;8m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;43;51;72m:\x1b[0m\x1b[38;2;72;80;120m-\x1b[0m\x1b[38;2;68;72;113m:\x1b[0m\x1b[38;2;53;57;90m:\x1b[0m\x1b[38;2;21;27;44m.\x1b[0m\x1b[38;2;0;2;5m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;2;3m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;3;5;9m \x1b[0m\x1b[38;2;10;16;36m \x1b[0m\x1b[38;2;7;6;12m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;9;5;0m \x1b[0m\x1b[38;2;5;2;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;1;0m \x1b[0m\x1b[38;2;2;2;3m \x1b[0m\x1b[38;2;4;7;11m \x1b[0m\x1b[38;2;13;21;37m \x1b[0m\x1b[38;2;6;9;19m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;68;74;84m:\x1b[0m\x1b[38;2;196;198;238m#\x1b[0m\x1b[38;2;214;188;246m#\x1b[0m\x1b[38;2;216;173;244m#\x1b[0m\x1b[38;2;201;156;236m#\x1b[0m\x1b[38;2;185;149;233m*\x1b[0m\x1b[38;2;143;129;203m+\x1b[0m\x1b[38;2;76;77;123m-\x1b[0m\x1b[38;2;21;27;45m.\x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;3m \x1b[0m\x1b[38;2;0;1;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;4;3;3m \x1b[0m\x1b[38;2;2;1;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;3;2;3m \x1b[0m\x1b[38;2;1;1;0m \x1b[0m\x1b[38;2;16;7;18m \x1b[0m\x1b[38;2;71;21;55m.\x1b[0m\x1b[38;2;31;6;19m \x1b[0m\x1b[38;2;6;13;24m \x1b[0m\x1b[38;2;4;11;21m \x1b[0m\x1b[38;2;30;12;30m \x1b[0m\x1b[38;2;58;8;37m.\x1b[0m\x1b[38;2;6;2;5m \x1b[0m\x1b[38;2;2;2;3m \x1b[0m\x1b[38;2;1;2;3m \x1b[0m\x1b[38;2;1;2;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;2;2m \x1b[0m\x1b[38;2;1;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;1;2;3m \x1b[0m\x1b[38;2;2;2;4m \x1b[0m\x1b[38;2;1;1;3m \x1b[0m\x1b[38;2;1;0;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;3m \x1b[0m\x1b[38;2;2;2;3m \x1b[0m\x1b[38;2;0;1;1m \x1b[0m\n    \x1b[38;2;4;4;4m \x1b[0m\x1b[38;2;177;184;198m#\x1b[0m\x1b[38;2;232;223;243m%\x1b[0m\x1b[38;2;237;198;232m%\x1b[0m\x1b[38;2;241;180;228m%\x1b[0m\x1b[38;2;241;171;232m#\x1b[0m\x1b[38;2;236;169;242m#\x1b[0m\x1b[38;2;227;175;251m#\x1b[0m\x1b[38;2;202;167;247m#\x1b[0m\x1b[38;2;162;148;229m*\x1b[0m\x1b[38;2;96;106;173m=\x1b[0m\x1b[38;2;38;55;91m:\x1b[0m\x1b[38;2;5;12;17m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;1;2m \x1b[0m\x1b[38;2;3;3;3m \x1b[0m\x1b[38;2;5;4;6m \x1b[0m\x1b[38;2;11;14;28m \x1b[0m\x1b[38;2;3;4;10m \x1b[0m\x1b[38;2;2;1;3m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;0;2m \x1b[0m\x1b[38;2;54;28;58m.\x1b[0m\x1b[38;2;132;56;117m-\x1b[0m\x1b[38;2;42;60;88m:\x1b[0m\x1b[38;2;9;81;104m:\x1b[0m\x1b[38;2;10;56;82m.\x1b[0m\x1b[38;2;38;41;81m.\x1b[0m\x1b[38;2;92;23;73m.\x1b[0m\x1b[38;2;16;3;16m \x1b[0m\x1b[38;2;1;0;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;8;11;22m \x1b[0m\x1b[38;2;14;19;38m \x1b[0m\x1b[38;2;5;6;10m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;1;1m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;1;1;3m \x1b[0m\x1b[38;2;0;1;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;1;0m \x1b[0m\x1b[38;2;0;2;3m \x1b[0m\x1b[38;2;1;3;4m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;2;2;2m \x1b[0m\x1b[38;2;136;142;149m+\x1b[0m\x1b[38;2;225;224;239m%\x1b[0m\x1b[38;2;235;212;239m%\x1b[0m\x1b[38;2;235;190;235m%\x1b[0m\x1b[38;2;238;179;240m%\x1b[0m\x1b[38;2;218;164;238m#\x1b[0m\x1b[38;2;207;163;238m#\x1b[0m\x1b[38;2;203;172;241m#\x1b[0m\x1b[38;2;202;179;249m#\x1b[0m\x1b[38;2;182;171;247m#\x1b[0m\x1b[38;2;137;152;234m*\x1b[0m\x1b[38;2;81;119;201m=\x1b[0m\x1b[38;2;34;65;115m:\x1b[0m\x1b[38;2;9;24;41m \x1b[0m\x1b[38;2;0;1;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;6;5;8m \x1b[0m\x1b[38;2;20;24;31m \x1b[0m\x1b[38;2;0;2;0m \x1b[0m\x1b[38;2;0;8;3m \x1b[0m\x1b[38;2;5;29;48m \x1b[0m\x1b[38;2;10;49;83m.\x1b[0m\x1b[38;2;11;123;139m-\x1b[0m\x1b[38;2;15;194;189m+\x1b[0m\x1b[38;2;15;208;179m+\x1b[0m\x1b[38;2;11;152;118m=\x1b[0m\x1b[38;2;1;98;70m:\x1b[0m\x1b[38;2;0;66;50m.\x1b[0m\x1b[38;2;10;37;42m.\x1b[0m\x1b[38;2;7;28;55m \x1b[0m\x1b[38;2;0;15;22m \x1b[0m\x1b[38;2;0;1;0m \x1b[0m\x1b[38;2;6;9;10m \x1b[0m\x1b[38;2;7;7;11m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;1;1m \x1b[0m\x1b[38;2;0;1;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;5;9;10m \x1b[0m\x1b[38;2;14;22;34m \x1b[0m\x1b[38;2;33;43;68m.\x1b[0m\x1b[38;2;46;58;98m:\x1b[0m\x1b[38;2;60;69;125m:\x1b[0m\x1b[38;2;74;82;153m-\x1b[0m\x1b[38;2;84;92;172m-\x1b[0m\x1b[38;2;96;97;180m=\x1b[0m\x1b[38;2;103;99;189m=\x1b[0m\x1b[38;2;101;96;187m=\x1b[0m\x1b[38;2;96;95;182m=\x1b[0m\x1b[38;2;88;90;167m-\x1b[0m\x1b[38;2;68;73;125m-\x1b[0m\x1b[38;2;18;20;31m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;34;35;36m.\x1b[0m\x1b[38;2;214;218;226m%\x1b[0m\x1b[38;2;226;218;237m%\x1b[0m\x1b[38;2;227;200;237m%\x1b[0m\x1b[38;2;223;180;238m#\x1b[0m\x1b[38;2;212;164;239m#\x1b[0m\x1b[38;2;196;154;238m*\x1b[0m\x1b[38;2;185;155;236m*\x1b[0m\x1b[38;2;179;165;235m*\x1b[0m\x1b[38;2;172;178;240m#\x1b[0m\x1b[38;2;162;185;243m#\x1b[0m\x1b[38;2;143;174;245m*\x1b[0m\x1b[38;2;104;147;230m+\x1b[0m\x1b[38;2;67;122;208m=\x1b[0m\x1b[38;2;38;85;150m-\x1b[0m\x1b[38;2;17;43;75m.\x1b[0m\x1b[38;2;6;10;13m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;11;12m \x1b[0m\x1b[38;2;24;27;58m.\x1b[0m\x1b[38;2;26;9;47m \x1b[0m\x1b[38;2;54;57;110m:\x1b[0m\x1b[38;2;59;87;156m-\x1b[0m\x1b[38;2;16;94;142m:\x1b[0m\x1b[38;2;16;216;233m*\x1b[0m\x1b[38;2;20;231;226m*\x1b[0m\x1b[38;2;13;235;213m*\x1b[0m\x1b[38;2;10;221;179m*\x1b[0m\x1b[38;2;16;174;126m=\x1b[0m\x1b[38;2;7;122;77m-\x1b[0m\x1b[38;2;5;85;73m:\x1b[0m\x1b[38;2;40;54;116m:\x1b[0m\x1b[38;2;47;60;130m:\x1b[0m\x1b[38;2;36;11;45m \x1b[0m\x1b[38;2;25;17;45m \x1b[0m\x1b[38;2;6;10;19m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;0m \x1b[0m\x1b[38;2;7;17;26m \x1b[0m\x1b[38;2;19;38;65m.\x1b[0m\x1b[38;2;36;63;111m:\x1b[0m\x1b[38;2;61;88;160m-\x1b[0m\x1b[38;2;83;107;203m=\x1b[0m\x1b[38;2;103;119;227m=\x1b[0m\x1b[38;2;112;117;228m+\x1b[0m\x1b[38;2;120;117;236m+\x1b[0m\x1b[38;2;127;119;243m+\x1b[0m\x1b[38;2;132;121;239m+\x1b[0m\x1b[38;2;145;126;243m+\x1b[0m\x1b[38;2;152;129;241m+\x1b[0m\x1b[38;2;150;130;232m+\x1b[0m\x1b[38;2;144;132;227m+\x1b[0m\x1b[38;2;144;144;234m*\x1b[0m\x1b[38;2;143;155;245m*\x1b[0m\x1b[38;2;135;154;231m*\x1b[0m\x1b[38;2;34;39;56m.\x1b[0m\n    \x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;147;149;152m+\x1b[0m\x1b[38;2;228;231;240m@\x1b[0m\x1b[38;2;219;208;236m%\x1b[0m\x1b[38;2;209;181;232m#\x1b[0m\x1b[38;2;205;168;234m#\x1b[0m\x1b[38;2;198;162;240m#\x1b[0m\x1b[38;2;188;157;241m*\x1b[0m\x1b[38;2;167;147;234m*\x1b[0m\x1b[38;2;155;154;234m*\x1b[0m\x1b[38;2;147;175;238m*\x1b[0m\x1b[38;2;134;191;245m#\x1b[0m\x1b[38;2;118;191;250m*\x1b[0m\x1b[38;2;89;168;243m*\x1b[0m\x1b[38;2;68;139;235m+\x1b[0m\x1b[38;2;51;110;212m=\x1b[0m\x1b[38;2;35;76;161m:\x1b[0m\x1b[38;2;24;55;110m:\x1b[0m\x1b[38;2;101;51;166m-\x1b[0m\x1b[38;2;181;65;185m=\x1b[0m\x1b[38;2;217;62;178m=\x1b[0m\x1b[38;2;231;22;169m-\x1b[0m\x1b[38;2;234;11;165m-\x1b[0m\x1b[38;2;195;27;174m-\x1b[0m\x1b[38;2;71;110;204m=\x1b[0m\x1b[38;2;7;216;237m*\x1b[0m\x1b[38;2;12;251;251m#\x1b[0m\x1b[38;2;8;253;230m*\x1b[0m\x1b[38;2;6;188;135m+\x1b[0m\x1b[38;2;24;120;131m-\x1b[0m\x1b[38;2;158;45;185m-\x1b[0m\x1b[38;2;235;10;155m-\x1b[0m\x1b[38;2;238;5;135m-\x1b[0m\x1b[38;2;225;8;128m-\x1b[0m\x1b[38;2;196;10;126m-\x1b[0m\x1b[38;2;122;12;105m:\x1b[0m\x1b[38;2;34;8;47m \x1b[0m\x1b[38;2;0;2;3m \x1b[0m\x1b[38;2;7;17;36m \x1b[0m\x1b[38;2;14;44;87m.\x1b[0m\x1b[38;2;31;77;149m:\x1b[0m\x1b[38;2;50;102;194m-\x1b[0m\x1b[38;2;66;116;219m=\x1b[0m\x1b[38;2;68;112;221m=\x1b[0m\x1b[38;2;76;109;227m=\x1b[0m\x1b[38;2;96;110;229m=\x1b[0m\x1b[38;2;115;117;231m+\x1b[0m\x1b[38;2;124;118;234m+\x1b[0m\x1b[38;2;128;119;236m+\x1b[0m\x1b[38;2;131;120;231m+\x1b[0m\x1b[38;2;143;125;226m+\x1b[0m\x1b[38;2;151;130;231m+\x1b[0m\x1b[38;2;153;136;230m*\x1b[0m\x1b[38;2;151;142;230m*\x1b[0m\x1b[38;2;140;147;228m*\x1b[0m\x1b[38;2;133;158;235m*\x1b[0m\x1b[38;2;134;167;232m*\x1b[0m\x1b[38;2;42;53;68m:\x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;4;3;3m \x1b[0m\x1b[38;2;163;166;172m*\x1b[0m\x1b[38;2;220;222;240m%\x1b[0m\x1b[38;2;212;198;234m%\x1b[0m\x1b[38;2;209;182;237m#\x1b[0m\x1b[38;2;203;169;239m#\x1b[0m\x1b[38;2;192;157;239m*\x1b[0m\x1b[38;2;174;146;235m*\x1b[0m\x1b[38;2;152;139;232m*\x1b[0m\x1b[38;2;139;141;234m+\x1b[0m\x1b[38;2;131;157;232m*\x1b[0m\x1b[38;2;103;167;232m*\x1b[0m\x1b[38;2;88;183;248m*\x1b[0m\x1b[38;2;61;165;238m+\x1b[0m\x1b[38;2;35;120;211m=\x1b[0m\x1b[38;2;26;88;191m-\x1b[0m\x1b[38;2;92;69;202m-\x1b[0m\x1b[38;2;228;94;192m+\x1b[0m\x1b[38;2;255;131;190m*\x1b[0m\x1b[38;2;253;94;184m+\x1b[0m\x1b[38;2;253;19;162m=\x1b[0m\x1b[38;2;249;2;151m-\x1b[0m\x1b[38;2;255;0;155m-\x1b[0m\x1b[38;2;195;25;196m-\x1b[0m\x1b[38;2;21;130;179m=\x1b[0m\x1b[38;2;10;180;196m+\x1b[0m\x1b[38;2;9;195;200m+\x1b[0m\x1b[38;2;2;132;117m-\x1b[0m\x1b[38;2;123;75;203m=\x1b[0m\x1b[38;2;255;0;169m-\x1b[0m\x1b[38;2;254;2;127m-\x1b[0m\x1b[38;2;254;4;119m-\x1b[0m\x1b[38;2;251;4;115m-\x1b[0m\x1b[38;2;249;3;111m-\x1b[0m\x1b[38;2;227;5;109m-\x1b[0m\x1b[38;2;148;8;104m:\x1b[0m\x1b[38;2;25;24;77m.\x1b[0m\x1b[38;2;12;39;114m.\x1b[0m\x1b[38;2;26;66;168m:\x1b[0m\x1b[38;2;26;78;185m:\x1b[0m\x1b[38;2;39;99;202m-\x1b[0m\x1b[38;2;52;106;211m=\x1b[0m\x1b[38;2;63;103;209m=\x1b[0m\x1b[38;2;87;106;213m=\x1b[0m\x1b[38;2;108;112;219m=\x1b[0m\x1b[38;2;125;121;230m+\x1b[0m\x1b[38;2;134;126;234m+\x1b[0m\x1b[38;2;135;122;227m+\x1b[0m\x1b[38;2;141;122;225m+\x1b[0m\x1b[38;2;152;130;229m+\x1b[0m\x1b[38;2;157;141;231m*\x1b[0m\x1b[38;2;153;148;228m*\x1b[0m\x1b[38;2;143;153;229m*\x1b[0m\x1b[38;2;133;159;234m*\x1b[0m\x1b[38;2;103;133;190m+\x1b[0m\x1b[38;2;46;61;81m:\x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;2;2m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;2;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;48;48;48m.\x1b[0m\x1b[38;2;205;216;227m%\x1b[0m\x1b[38;2;217;225;248m%\x1b[0m\x1b[38;2;203;197;236m%\x1b[0m\x1b[38;2;202;183;238m#\x1b[0m\x1b[38;2;200;171;240m#\x1b[0m\x1b[38;2;188;154;238m*\x1b[0m\x1b[38;2;165;136;232m*\x1b[0m\x1b[38;2;142;127;228m+\x1b[0m\x1b[38;2;129;129;232m+\x1b[0m\x1b[38;2;108;125;227m+\x1b[0m\x1b[38;2;94;137;226m+\x1b[0m\x1b[38;2;76;144;226m+\x1b[0m\x1b[38;2;47;129;217m=\x1b[0m\x1b[38;2;36;112;204m-\x1b[0m\x1b[38;2;157;55;188m-\x1b[0m\x1b[38;2;253;123;188m*\x1b[0m\x1b[38;2;251;121;194m*\x1b[0m\x1b[38;2;248;58;182m+\x1b[0m\x1b[38;2;243;3;166m-\x1b[0m\x1b[38;2;246;3;166m-\x1b[0m\x1b[38;2;255;1;177m-\x1b[0m\x1b[38;2;190;23;196m-\x1b[0m\x1b[38;2;23;125;173m-\x1b[0m\x1b[38;2;8;179;197m+\x1b[0m\x1b[38;2;8;179;174m=\x1b[0m\x1b[38;2;3;144;121m-\x1b[0m\x1b[38;2;136;84;208m=\x1b[0m\x1b[38;2;253;0;168m-\x1b[0m\x1b[38;2;248;3;133m-\x1b[0m\x1b[38;2;249;5;121m-\x1b[0m\x1b[38;2;246;3;114m-\x1b[0m\x1b[38;2;242;5;117m-\x1b[0m\x1b[38;2;222;1;103m-\x1b[0m\x1b[38;2;194;0;98m:\x1b[0m\x1b[38;2;77;20;98m.\x1b[0m\x1b[38;2;2;13;64m \x1b[0m\x1b[38;2;8;25;95m.\x1b[0m\x1b[38;2;26;59;145m:\x1b[0m\x1b[38;2;42;77;178m-\x1b[0m\x1b[38;2;64;88;196m-\x1b[0m\x1b[38;2;91;99;208m=\x1b[0m\x1b[38;2;109;108;218m=\x1b[0m\x1b[38;2;114;107;217m=\x1b[0m\x1b[38;2;124;112;221m+\x1b[0m\x1b[38;2;141;125;229m+\x1b[0m\x1b[38;2;141;123;221m+\x1b[0m\x1b[38;2;148;129;221m+\x1b[0m\x1b[38;2;160;148;230m*\x1b[0m\x1b[38;2;155;154;228m*\x1b[0m\x1b[38;2;141;154;225m*\x1b[0m\x1b[38;2;137;176;238m*\x1b[0m\x1b[38;2;103;139;178m+\x1b[0m\x1b[38;2;4;5;4m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;2;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;90;96;99m-\x1b[0m\x1b[38;2;198;213;228m%\x1b[0m\x1b[38;2;202;219;242m%\x1b[0m\x1b[38;2;200;209;242m%\x1b[0m\x1b[38;2;198;195;239m#\x1b[0m\x1b[38;2;195;177;237m#\x1b[0m\x1b[38;2;187;159;238m*\x1b[0m\x1b[38;2;165;141;236m*\x1b[0m\x1b[38;2;148;134;238m+\x1b[0m\x1b[38;2;122;127;230m+\x1b[0m\x1b[38;2;88;114;224m=\x1b[0m\x1b[38;2;64;109;216m=\x1b[0m\x1b[38;2;38;98;201m-\x1b[0m\x1b[38;2;44;115;210m=\x1b[0m\x1b[38;2;141;104;223m+\x1b[0m\x1b[38;2;205;96;207m+\x1b[0m\x1b[38;2;220;73;206m+\x1b[0m\x1b[38;2;218;26;211m=\x1b[0m\x1b[38;2;208;7;210m-\x1b[0m\x1b[38;2;205;10;207m-\x1b[0m\x1b[38;2;166;17;187m-\x1b[0m\x1b[38;2;50;70;158m:\x1b[0m\x1b[38;2;3;174;204m=\x1b[0m\x1b[38;2;5;234;234m*\x1b[0m\x1b[38;2;4;235;207m*\x1b[0m\x1b[38;2;3;199;144m+\x1b[0m\x1b[38;2;29;137;149m=\x1b[0m\x1b[38;2;149;46;189m-\x1b[0m\x1b[38;2;210;11;178m-\x1b[0m\x1b[38;2;218;4;162m-\x1b[0m\x1b[38;2;219;3;151m-\x1b[0m\x1b[38;2;212;3;137m-\x1b[0m\x1b[38;2;199;0;125m:\x1b[0m\x1b[38;2;165;5;122m:\x1b[0m\x1b[38;2;62;23;94m.\x1b[0m\x1b[38;2;4;11;46m \x1b[0m\x1b[38;2;10;15;67m \x1b[0m\x1b[38;2;33;45;122m.\x1b[0m\x1b[38;2;69;82;185m-\x1b[0m\x1b[38;2;97;106;217m=\x1b[0m\x1b[38;2;112;115;224m=\x1b[0m\x1b[38;2;116;112;223m=\x1b[0m\x1b[38;2;136;122;229m+\x1b[0m\x1b[38;2;151;130;228m+\x1b[0m\x1b[38;2;164;144;233m*\x1b[0m\x1b[38;2;164;150;231m*\x1b[0m\x1b[38;2;161;156;230m*\x1b[0m\x1b[38;2;153;160;229m*\x1b[0m\x1b[38;2;139;164;232m*\x1b[0m\x1b[38;2;132;177;238m*\x1b[0m\x1b[38;2;90;127;163m=\x1b[0m\x1b[38;2;19;26;31m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;3;4m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;27;27;27m.\x1b[0m\x1b[38;2;111;120;127m=\x1b[0m\x1b[38;2;186;205;220m#\x1b[0m\x1b[38;2;202;220;239m%\x1b[0m\x1b[38;2;198;208;236m%\x1b[0m\x1b[38;2;193;192;235m#\x1b[0m\x1b[38;2;185;176;242m#\x1b[0m\x1b[38;2;163;152;237m*\x1b[0m\x1b[38;2;142;141;234m+\x1b[0m\x1b[38;2;107;126;229m+\x1b[0m\x1b[38;2;71;119;224m=\x1b[0m\x1b[38;2;46;112;215m=\x1b[0m\x1b[38;2;32;94;185m-\x1b[0m\x1b[38;2;28;71;115m:\x1b[0m\x1b[38;2;58;103;180m-\x1b[0m\x1b[38;2;65;83;197m-\x1b[0m\x1b[38;2;73;59;163m:\x1b[0m\x1b[38;2;72;53;143m:\x1b[0m\x1b[38;2;59;96;175m-\x1b[0m\x1b[38;2;29;161;193m=\x1b[0m\x1b[38;2;6;198;215m+\x1b[0m\x1b[38;2;7;227;240m*\x1b[0m\x1b[38;2;4;209;206m+\x1b[0m\x1b[38;2;3;207;180m+\x1b[0m\x1b[38;2;4;208;137m+\x1b[0m\x1b[38;2;1;189;96m=\x1b[0m\x1b[38;2;10;157;92m=\x1b[0m\x1b[38;2;43;84;110m:\x1b[0m\x1b[38;2;74;47;136m:\x1b[0m\x1b[38;2;88;36;138m:\x1b[0m\x1b[38;2;94;39;148m:\x1b[0m\x1b[38;2;89;44;141m:\x1b[0m\x1b[38;2;54;35;95m.\x1b[0m\x1b[38;2;14;19;41m \x1b[0m\x1b[38;2;8;10;51m \x1b[0m\x1b[38;2;12;21;82m \x1b[0m\x1b[38;2;33;48;143m:\x1b[0m\x1b[38;2;70;82;200m-\x1b[0m\x1b[38;2;112;114;230m=\x1b[0m\x1b[38;2;127;124;232m+\x1b[0m\x1b[38;2;149;142;238m*\x1b[0m\x1b[38;2;156;150;239m*\x1b[0m\x1b[38;2;163;156;236m*\x1b[0m\x1b[38;2;169;166;236m*\x1b[0m\x1b[38;2;159;166;233m*\x1b[0m\x1b[38;2;141;166;233m*\x1b[0m\x1b[38;2;128;173;237m*\x1b[0m\x1b[38;2;124;185;238m*\x1b[0m\x1b[38;2;71;105;125m-\x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;2;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;16;16;17m \x1b[0m\x1b[38;2;75;80;84m-\x1b[0m\x1b[38;2;126;137;145m+\x1b[0m\x1b[38;2;173;188;202m#\x1b[0m\x1b[38;2;189;206;227m%\x1b[0m\x1b[38;2;178;197;230m#\x1b[0m\x1b[38;2;175;200;244m#\x1b[0m\x1b[38;2;152;193;251m#\x1b[0m\x1b[38;2;97;167;239m*\x1b[0m\x1b[38;2;49;114;176m=\x1b[0m\x1b[38;2;17;29;47m.\x1b[0m\x1b[38;2;108;116;118m=\x1b[0m\x1b[38;2;129;212;225m#\x1b[0m\x1b[38;2;32;153;227m=\x1b[0m\x1b[38;2;24;81;156m:\x1b[0m\x1b[38;2;11;46;73m.\x1b[0m\x1b[38;2;33;161;183m=\x1b[0m\x1b[38;2;63;255;255m#\x1b[0m\x1b[38;2;27;254;255m#\x1b[0m\x1b[38;2;2;241;245m*\x1b[0m\x1b[38;2;3;241;235m*\x1b[0m\x1b[38;2;4;246;219m*\x1b[0m\x1b[38;2;5;244;150m*\x1b[0m\x1b[38;2;6;227;97m+\x1b[0m\x1b[38;2;8;179;53m=\x1b[0m\x1b[38;2;4;97;35m:\x1b[0m\x1b[38;2;13;50;75m.\x1b[0m\x1b[38;2;18;92;114m:\x1b[0m\x1b[38;2;15;127;190m-\x1b[0m\x1b[38;2;5;54;140m.\x1b[0m\x1b[38;2;7;48;123m.\x1b[0m\x1b[38;2;14;35;85m.\x1b[0m\x1b[38;2;9;16;52m \x1b[0m\x1b[38;2;27;49;128m:\x1b[0m\x1b[38;2;66;101;210m=\x1b[0m\x1b[38;2;105;134;241m+\x1b[0m\x1b[38;2;123;145;239m+\x1b[0m\x1b[38;2;144;167;247m*\x1b[0m\x1b[38;2;156;184;249m#\x1b[0m\x1b[38;2;158;194;250m#\x1b[0m\x1b[38;2;143;187;246m#\x1b[0m\x1b[38;2;139;188;246m#\x1b[0m\x1b[38;2;132;186;244m*\x1b[0m\x1b[38;2;114;174;228m*\x1b[0m\x1b[38;2;81;126;156m=\x1b[0m\x1b[38;2;29;44;51m.\x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;1;2;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;2;3;3m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;5;5;5m \x1b[0m\x1b[38;2;27;30;31m.\x1b[0m\x1b[38;2;52;57;59m:\x1b[0m\x1b[38;2;65;74;79m:\x1b[0m\x1b[38;2;63;74;80m:\x1b[0m\x1b[38;2;39;46;51m.\x1b[0m\x1b[38;2;3;1;0m \x1b[0m\x1b[38;2;75;105;110m-\x1b[0m\x1b[38;2;193;252;255m@\x1b[0m\x1b[38;2;118;245;250m%\x1b[0m\x1b[38;2;20;179;251m+\x1b[0m\x1b[38;2;24;109;220m-\x1b[0m\x1b[38;2;29;86;160m-\x1b[0m\x1b[38;2;39;158;187m=\x1b[0m\x1b[38;2;67;251;252m#\x1b[0m\x1b[38;2;29;250;253m#\x1b[0m\x1b[38;2;4;245;248m*\x1b[0m\x1b[38;2;2;241;233m*\x1b[0m\x1b[38;2;7;245;220m*\x1b[0m\x1b[38;2;4;242;153m*\x1b[0m\x1b[38;2;3;215;91m+\x1b[0m\x1b[38;2;9;166;47m=\x1b[0m\x1b[38;2;6;93;27m:\x1b[0m\x1b[38;2;29;76;110m:\x1b[0m\x1b[38;2;39;149;194m=\x1b[0m\x1b[38;2;15;155;242m=\x1b[0m\x1b[38;2;11;60;173m:\x1b[0m\x1b[38;2;10;52;154m:\x1b[0m\x1b[38;2;16;60;161m:\x1b[0m\x1b[38;2;17;34;77m.\x1b[0m\x1b[38;2;14;21;43m \x1b[0m\x1b[38;2;37;55;92m:\x1b[0m\x1b[38;2;87;120;158m=\x1b[0m\x1b[38;2;111;148;180m+\x1b[0m\x1b[38;2;117;163;192m*\x1b[0m\x1b[38;2;112;163;196m+\x1b[0m\x1b[38;2;107;163;198m+\x1b[0m\x1b[38;2;85;142;176m+\x1b[0m\x1b[38;2;64;110;139m-\x1b[0m\x1b[38;2;51;82;99m:\x1b[0m\x1b[38;2;16;27;30m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;2;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;3;3;3m \x1b[0m\x1b[38;2;2;3;3m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;2m \x1b[0m\x1b[38;2;0;0;9m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;7;5;15m \x1b[0m\x1b[38;2;79;167;187m+\x1b[0m\x1b[38;2;84;236;248m#\x1b[0m\x1b[38;2;36;210;246m*\x1b[0m\x1b[38;2;5;126;231m=\x1b[0m\x1b[38;2;8;80;187m:\x1b[0m\x1b[38;2;20;84;145m:\x1b[0m\x1b[38;2;36;170;194m+\x1b[0m\x1b[38;2;59;255;255m#\x1b[0m\x1b[38;2;20;248;252m#\x1b[0m\x1b[38;2;1;248;252m*\x1b[0m\x1b[38;2;2;241;229m*\x1b[0m\x1b[38;2;2;242;210m*\x1b[0m\x1b[38;2;2;241;153m*\x1b[0m\x1b[38;2;3;211;90m+\x1b[0m\x1b[38;2;5;159;42m-\x1b[0m\x1b[38;2;4;97;28m:\x1b[0m\x1b[38;2;19;74;97m:\x1b[0m\x1b[38;2;25;121;167m-\x1b[0m\x1b[38;2;6;146;237m=\x1b[0m\x1b[38;2;5;55;169m:\x1b[0m\x1b[38;2;3;42;143m.\x1b[0m\x1b[38;2;13;49;159m:\x1b[0m\x1b[38;2;23;35;98m.\x1b[0m\x1b[38;2;6;3;9m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;4;8;15m \x1b[0m\x1b[38;2;9;19;38m \x1b[0m\x1b[38;2;9;9;18m \x1b[0m\x1b[38;2;10;8;8m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;1;2;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;1;1m \x1b[0m\x1b[38;2;2;1;1m \x1b[0m\x1b[38;2;3;4;5m \x1b[0m\x1b[38;2;4;5;8m \x1b[0m\x1b[38;2;4;6;8m \x1b[0m\x1b[38;2;1;3;1m \x1b[0m\x1b[38;2;60;49;91m:\x1b[0m\x1b[38;2;97;79;194m-\x1b[0m\x1b[38;2;91;126;229m+\x1b[0m\x1b[38;2;71;199;249m*\x1b[0m\x1b[38;2;34;168;247m+\x1b[0m\x1b[38;2;19;110;229m-\x1b[0m\x1b[38;2;9;61;157m:\x1b[0m\x1b[38;2;8;46;89m.\x1b[0m\x1b[38;2;24;173;204m+\x1b[0m\x1b[38;2;26;250;250m#\x1b[0m\x1b[38;2;12;244;249m*\x1b[0m\x1b[38;2;7;247;247m*\x1b[0m\x1b[38;2;5;249;233m*\x1b[0m\x1b[38;2;2;238;204m*\x1b[0m\x1b[38;2;3;243;167m*\x1b[0m\x1b[38;2;4;217;104m+\x1b[0m\x1b[38;2;2;149;37m-\x1b[0m\x1b[38;2;6;96;30m:\x1b[0m\x1b[38;2;8;45;63m.\x1b[0m\x1b[38;2;9;54;98m.\x1b[0m\x1b[38;2;19;118;216m-\x1b[0m\x1b[38;2;16;62;182m:\x1b[0m\x1b[38;2;23;60;171m:\x1b[0m\x1b[38;2;31;55;147m:\x1b[0m\x1b[38;2;12;18;46m \x1b[0m\x1b[38;2;14;7;40m \x1b[0m\x1b[38;2;49;32;103m.\x1b[0m\x1b[38;2;15;11;27m \x1b[0m\x1b[38;2;0;3;5m \x1b[0m\x1b[38;2;10;10;16m \x1b[0m\x1b[38;2;20;15;20m \x1b[0m\x1b[38;2;6;3;5m \x1b[0m\x1b[38;2;3;3;4m \x1b[0m\x1b[38;2;2;3;4m \x1b[0m\x1b[38;2;2;3;4m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;88;62;100m:\x1b[0m\x1b[38;2;170;116;227m+\x1b[0m\x1b[38;2;138;105;241m+\x1b[0m\x1b[38;2;111;104;238m=\x1b[0m\x1b[38;2;99;121;236m+\x1b[0m\x1b[38;2;81;131;234m+\x1b[0m\x1b[38;2;50;103;216m-\x1b[0m\x1b[38;2;41;81;177m-\x1b[0m\x1b[38;2;32;56;110m:\x1b[0m\x1b[38;2;13;134;166m-\x1b[0m\x1b[38;2;8;228;255m*\x1b[0m\x1b[38;2;1;202;227m+\x1b[0m\x1b[38;2;4;223;222m*\x1b[0m\x1b[38;2;3;239;208m*\x1b[0m\x1b[38;2;6;243;205m*\x1b[0m\x1b[38;2;2;251;204m*\x1b[0m\x1b[38;2;0;237;162m*\x1b[0m\x1b[38;2;1;174;91m=\x1b[0m\x1b[38;2;6;102;56m:\x1b[0m\x1b[38;2;7;30;43m \x1b[0m\x1b[38;2;20;29;68m.\x1b[0m\x1b[38;2;35;67;161m:\x1b[0m\x1b[38;2;30;49;142m:\x1b[0m\x1b[38;2;36;38;108m.\x1b[0m\x1b[38;2;35;24;64m.\x1b[0m\x1b[38;2;47;21;75m.\x1b[0m\x1b[38;2;83;38;136m:\x1b[0m\x1b[38;2;105;50;177m-\x1b[0m\x1b[38;2;108;61;166m-\x1b[0m\x1b[38;2;6;3;6m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;2;2m \x1b[0m\x1b[38;2;2;2;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;3;2;3m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;84;55;86m:\x1b[0m\x1b[38;2;224;127;227m*\x1b[0m\x1b[38;2;211;114;236m*\x1b[0m\x1b[38;2;172;113;235m+\x1b[0m\x1b[38;2;144;110;233m+\x1b[0m\x1b[38;2;131;117;238m+\x1b[0m\x1b[38;2;109;106;238m=\x1b[0m\x1b[38;2;102;109;249m=\x1b[0m\x1b[38;2;55;67;124m:\x1b[0m\x1b[38;2;7;49;75m.\x1b[0m\x1b[38;2;52;162;197m+\x1b[0m\x1b[38;2;39;160;194m+\x1b[0m\x1b[38;2;18;173;179m=\x1b[0m\x1b[38;2;5;191;182m+\x1b[0m\x1b[38;2;0;192;178m+\x1b[0m\x1b[38;2;5;236;213m*\x1b[0m\x1b[38;2;28;243;210m*\x1b[0m\x1b[38;2;51;203;175m*\x1b[0m\x1b[38;2;27;92;97m:\x1b[0m\x1b[38;2;4;10;30m \x1b[0m\x1b[38;2;22;33;69m.\x1b[0m\x1b[38;2;91;94;210m=\x1b[0m\x1b[38;2;92;63;198m-\x1b[0m\x1b[38;2;103;58;183m-\x1b[0m\x1b[38;2;137;65;192m=\x1b[0m\x1b[38;2;167;70;202m=\x1b[0m\x1b[38;2;182;67;195m=\x1b[0m\x1b[38;2;189;73;197m=\x1b[0m\x1b[38;2;151;71;171m=\x1b[0m\x1b[38;2;6;3;7m \x1b[0m\x1b[38;2;0;0;1m \x1b[0m\x1b[38;2;1;0;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;0;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;101;62;94m-\x1b[0m\x1b[38;2;224;126;209m*\x1b[0m\x1b[38;2;230;118;222m*\x1b[0m\x1b[38;2;227;117;230m*\x1b[0m\x1b[38;2;221;117;236m*\x1b[0m\x1b[38;2;198;109;223m+\x1b[0m\x1b[38;2;136;78;164m=\x1b[0m\x1b[38;2;16;16;25m \x1b[0m\x1b[38;2;7;5;15m \x1b[0m\x1b[38;2;48;39;37m.\x1b[0m\x1b[38;2;101;101;80m-\x1b[0m\x1b[38;2;144;149;89m+\x1b[0m\x1b[38;2;84;130;117m=\x1b[0m\x1b[38;2;39;116;136m-\x1b[0m\x1b[38;2;104;153;105m+\x1b[0m\x1b[38;2;144;146;78m+\x1b[0m\x1b[38;2;86;76;60m-\x1b[0m\x1b[38;2;13;7;8m \x1b[0m\x1b[38;2;6;4;10m \x1b[0m\x1b[38;2;7;2;4m \x1b[0m\x1b[38;2;69;42;70m:\x1b[0m\x1b[38;2;185;102;208m+\x1b[0m\x1b[38;2;214;98;222m+\x1b[0m\x1b[38;2;226;90;215m+\x1b[0m\x1b[38;2;237;84;207m+\x1b[0m\x1b[38;2;234;85;200m+\x1b[0m\x1b[38;2;169;71;152m=\x1b[0m\x1b[38;2;40;21;39m.\x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;0;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;1;1;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;15;13;14m \x1b[0m\x1b[38;2;61;36;56m.\x1b[0m\x1b[38;2;97;54;88m:\x1b[0m\x1b[38;2;102;54;93m:\x1b[0m\x1b[38;2;64;33;57m.\x1b[0m\x1b[38;2;8;4;3m \x1b[0m\x1b[38;2;5;6;8m \x1b[0m\x1b[38;2;18;19;26m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;6;2;1m \x1b[0m\x1b[38;2;20;10;4m \x1b[0m\x1b[38;2;22;13;10m \x1b[0m\x1b[38;2;14;2;0m \x1b[0m\x1b[38;2;1;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;1;1m \x1b[0m\x1b[38;2;9;9;11m \x1b[0m\x1b[38;2;3;2;5m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;16;9;12m \x1b[0m\x1b[38;2;73;38;66m:\x1b[0m\x1b[38;2;104;50;94m:\x1b[0m\x1b[38;2;100;47;88m:\x1b[0m\x1b[38;2;58;29;51m.\x1b[0m\x1b[38;2;0;2;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    \x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;1;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;1;0m \x1b[0m\x1b[38;2;4;2;0m \x1b[0m\x1b[38;2;4;4;4m \x1b[0m\x1b[38;2;2;2;1m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;1;0m \x1b[0m\x1b[38;2;2;2;2m \x1b[0m\x1b[38;2;1;1;1m \x1b[0m\x1b[38;2;3;3;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;2;1;2m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\x1b[38;2;0;0;0m \x1b[0m\n    '
init()
if __name__==_AK:run_main()