% -------------------------------------------------------------------------- % the SUBSTANCES package % % A Chemical Database % % -------------------------------------------------------------------------- % Clemens Niederberger % Web: https://bitbucket.org/cgnieder/substances/ % E-Mail: contact@mychemistry.eu % -------------------------------------------------------------------------- % Copyright 2012 Clemens Niederberger % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Clemens Niederberger. % -------------------------------------------------------------------------- % The substances package consists of the files % - substances.sty, substances-default.def, substances-examples.sub, % substances_en.tex, substances_en.pdf, README % -------------------------------------------------------------------------- % If you have any ideas, questions, suggestions or bugs to report, please % feel free to contact me. % -------------------------------------------------------------------------- \RequirePackage{ expl3 , xparse , l3keys2e , xtemplate , chemmacros } \ProvidesExplPackage {substances} {2012/07/22} {0.1} {A Chemical Database} % ---------------------------------------------------------------------------- % variables: \prop_new:N \l_substances_properties_pre_prop \prop_new:N \l_substances_properties_post_prop \seq_new:N \l_substances_required_seq \seq_new:N \l_substances_chemicals_seq \clist_new:N \l_substances_chemicals_clist \bool_new:N \l_substances_strict_bool \bool_new:N \l_substances_index_entry_bool \bool_new:N \l_substances_imakeidx_bool \bool_new:N \l_substances_multind_bool \bool_new:N \l_substances_splitidx_bool \tl_new:N \l_substances_style_tl \tl_set:Nn \l_substances_style_tl { default } \AtBeginDocument { \@ifpackageloaded { imakeidx } { \bool_set_true:N \l_substances_imakeidx_bool } {} \@ifpackageloaded { splitidx } { \bool_set_true:N \l_substances_splitidx_bool } {} \@ifpackageloaded { multind } { \bool_set_true:N \l_substances_multind_bool } {} } % ---------------------------------------------------------------------------- % messages: \cs_new:Npn \substances_msg:nnn #1#2#3 { \bool_if:NTF \l_substances_strict_bool { \msg_error:nnxx { substances } { #1 } { #2 } { #3 } } { \msg_warning:nnxx { substances } { #1 } { #2 } { #3 } } } \msg_new:nnnn { substances } { property-missing } { Property~`#2'~not~defined~for~substance~`#1'~\msg_line_context:. } { You~called~property~`#2'~for~substance~`#1'.~It~seems~though~that~you~ haven't~defined~it,~yet. } \msg_new:nnnn { substances } { field-missing } { Property~`#2'~is~not~defined~\msg_line_context:. } { You~called~property~`#2'~for~substance~`#1'.~This~property~has~not~been~ declared,~though.~Perhaps~you've~mispelled? } \msg_new:nnnn { substances } { dadabase-missing } { I~can't~find~the~database~file~`#1'~\msg_line_context:. } { You~requested~the~database~file~`#1',~but~apparently~it~is~missing.~Maybe~ you've~mispelled? } \msg_new:nnnn { substances } { required-field } { The~field~`#1'~is~missing~for~substance~`#2'~\msg_line_context:. } { You~declared~the~the~substance~`#2'~but~forgot~to~add~the~required~ property~`#1'. } \msg_new:nnnn { substances } { style-missing } { I~can't~find~the~file~`substances_#1.def'. } { You~specified~the~style~`#1'~which~means~you~want~me~to~load~the~file~ `substances_#1.def'~but~I~can't~find~it.~Perhaps~you've~mispelled?~ Anyway~I'm~loading~the~`default'~style~instead. } % ---------------------------------------------------------------------------- % options \keys_define:nn { substances / options } { strict .bool_set:N = \l_substances_strict_bool , draft .code:n = \bool_set_true:N \l_substances_strict_bool , final .code:n = \bool_set_false:N \l_substances_strict_bool , index .bool_set:N = \l_substances_index_entry_bool , style .tl_set:N = \l_substances_style_tl } \ProcessKeysOptions { substances / options } % ---------------------------------------------------------------------------- % define new property fields: \cs_new:Npn \substances_define_property_field:nnnn #1#2#3#4 { \IfBooleanT { #1 } { \seq_put_right:Nn \l_substances_required_seq { #2 } } \prop_if_exist:cF { l_substances_#2_prop } { \prop_new:c { l_substances_#2_prop } } \IfNoValueTF { #3 } { \prop_put:Nnn \l_substances_properties_pre_prop { #2 } { } } { \tl_if_blank:nTF { #3 } { \prop_put:Nnn \l_substances_properties_pre_prop { #2 } { } } { \prop_put:Nnn \l_substances_properties_pre_prop { #2 } { #3 } } } \IfNoValueTF { #4 } { \prop_put:Nnn \l_substances_properties_post_prop { #2 } { } } { \prop_put:Nnn \l_substances_properties_post_prop { #2 } { #4 } } } \NewDocumentCommand \DeclareSubstanceProperty { smoo } { \substances_define_property_field:nnnn { #1 } { #2 } { #3 } { #4 } } \@onlypreamble\DeclareSubstanceProperty % ---------------------------------------------------------------------------- % declare new substance entry: \cs_new:Npn \substances_declare_substance:nn #1#2 { \seq_put_right:Nn \l_substances_chemicals_seq { #1 } \clist_put_right:Nn \l_substances_chemicals_clist { #1 } \prop_map_inline:Nn \l_substances_properties_pre_prop { \tl_set:Nn \l_tmpa_tl { ##2 } \prop_get:NnN \l_substances_properties_post_prop { ##1 } \l_tmpb_tl \substances_add_property:nnVV { #1 } { ##1 } \l_tmpa_tl \l_tmpb_tl } \keys_set:nn { substances / #1 } { #2 } \seq_map_inline:Nn \l_substances_required_seq { \group_begin: \bool_set_true:N \l_substances_strict_bool \prop_if_in:cnF { l_substances_##1_prop } { #1 } { \substances_msg:nnn { required-field } { ##1 } { #1 } } \group_end: } \prop_if_in:NnTF \l_substances_sort_prop { #1 } { \substances_remove_braces:xN { \prop_get:Nn \l_substances_sort_prop { #1 } } \l_tmpa_tl } { \prop_get:NnN \l_substances_properties_pre_prop { name } \l_tmpa_tl \substances_remove_str_x:VnnN \l_tmpa_tl { #1 } { name } \l_tmpa_tl } \prop_put:NnV \l_substances_sort_prop { #1 } \l_tmpa_tl \prop_if_in:NnT \l_substances_alt_prop { #1 } { \prop_if_in:NnTF \l_substances_altsort_prop { #1 } { \substances_remove_braces:xN { \prop_get:Nn \l_substances_altsort_prop { #1 } } \l_tmpa_tl } { \prop_get:NnN \l_substances_properties_pre_prop { alt } \l_tmpa_tl \substances_remove_str_x:VnnN \l_tmpa_tl { #1 } { alt } \l_tmpa_tl } \prop_put:NnV \l_substances_altsort_prop { #1 } \l_tmpa_tl } \bool_new:c { g_substances_#1_alt_index_entry_bool } } \cs_new:Npn \substances_add_property:nnnn #1#2#3#4 { \keys_define:nn { substances / #1 } { #2 .code:n = \prop_put:cnn { l_substances_#2_prop } { #1 } { #3{##1}#4 } } } \cs_generate_variant:Nn \substances_add_property:nnnn { nnVV } \NewDocumentCommand \DeclareSubstance { mm } { \substances_declare_substance:nn { #1 } { #2 } } \@onlypreamble\DeclareSubstance % ---------------------------------------------------------------------------- % recover values: \cs_new:Npn \substances_get_property:nn #1#2 { \IfSubstanceFieldTF { #2 } { \IfSubstancePropertyTF { #1 } { #2 } { \prop_get:cn { l_substances_#2_prop } { #1 } } { {??} \substances_msg:nnn { property-missing } { #1 } { #2 } } } { {??} \substances_msg:nnn { field-missing } { #1 } { #2 } } } % ---------------------------------------------------------------------------- % some commands for creating user macros or whatever: \NewDocumentCommand \GetSubstanceProperty { mm } { \substances_get_property:nn { #1 } { #2 } } \DeclareExpandableDocumentCommand \RetrieveSubstanceProperty { mm } { \substances_get_property:nn { #1 } { #2 } } \DeclareExpandableDocumentCommand \IfSubstanceFieldTF { mmm } { \cs_if_exist:cTF { l_substances_#1_prop } { #2 } { #3 } } \DeclareExpandableDocumentCommand \IfSubstanceFieldT { mm } { \cs_if_exist:cT { l_substances_#1_prop } { #2 } } \DeclareExpandableDocumentCommand \IfSubstanceFieldF { mm } { \cs_if_exist:cF { l_substances_#1_prop } { #2 } } \DeclareExpandableDocumentCommand \IfSubstanceExistTF { mmm } { \seq_if_in:NnTF \l_substances_chemicals_seq { #1 } { #2 } { #3 } } \DeclareExpandableDocumentCommand \IfSubstanceExistT { mm } { \seq_if_in:NnT \l_substances_chemicals_seq { #1 } { #2 } } \DeclareExpandableDocumentCommand \IfSubstanceExistF { mm } { \seq_if_in:NnF \l_substances_chemicals_seq { #1 } { #2 } } \DeclareExpandableDocumentCommand \IfSubstancePropertyTF { mmmm } { \cs_if_exist:cTF { l_substances_#2_prop } { \prop_if_in:cnTF { l_substances_#2_prop } { #1 } { #3 } { #4 } } { #4 } } \DeclareExpandableDocumentCommand \IfSubstancePropertyT { mmm } { \cs_if_exist:cT { l_substances_#2_prop } { \prop_if_in:cnT { l_substances_#2_prop } { #1 } { #3 } } } \DeclareExpandableDocumentCommand \IfSubstancePropertyF { mmm } { \cs_if_exist:cTF { l_substances_#2_prop } { \prop_if_in:cnF { l_substances_#2_prop } { #1 } { #3 } } { #3 } } \DeclareExpandableDocumentCommand \ForAllSubstancesDo {} { \seq_map_inline:Nn \l_substances_chemicals_seq } \AtBeginDocument { \tl_new:N \AllSubstancesSequence \seq_map_inline:Nn \l_substances_chemicals_seq { \tl_put_right:Nn \AllSubstancesSequence { {#1} } } \cs_new_eq:NN \AllSubstancesClist \l_substances_chemicals_clist } % ---------------------------------------------------------------------------- % user command: \NewDocumentCommand \chem { soomo } { \IfNoValueF { #2 } { #2 } \IfNoValueTF { #5 } { \IfBooleanTF { #1 } { \IfSubstancePropertyTF { #4 } { alt } { \RetrieveSubstanceProperty { #4 } { alt } } { \RetrieveSubstanceProperty { #4 } { name } } } { \RetrieveSubstanceProperty { #4 } { name } } } { \substances_get_property:nn { #4 } { #5 } } \IfNoValueF { #3 } { #3 } \bool_if:NT \l_substances_index_entry_bool { \substances_index_entry:n { #4 } } } % ---------------------------------------------------------------------------- % index command to add an entry to the chemicals list if the option % `index=true' is used: \cs_new:Npn \substances_index:nn #1#2 { \bool_if:NTF \l_substances_imakeidx_bool { \index [ #1 ] { #2 } } { \bool_if:NTF \l_substances_splitidx_bool { \sindex [ #1 ] { #2 } } { \bool_if:NTF \l_substances_multind_bool { \index { #1 } { #2 } } { \index { #2 } } } } } \cs_generate_variant:Nn \substances_index:nn { no,nx } \cs_new:Npn \substances_index_entry:n #1 { \UseInstance { substances-index } { default } { #1 } } \cs_new:Npn \substances_remove_braces:xN #1#2 { \exp_last_unbraced:NNx \tl_set:Nn #2 { #1 } } \cs_generate_variant:Nn \exp_last_unbraced:NNo { NNx } \cs_new:Npn \substances_remove_str_x:nnnN #1#2#3#4 { \tl_set:Nx #4 { \prop_get:cn { l_substances_#3_prop } { #2 } } \tl_remove_all:Nn #4 { #1 } \substances_remove_braces:xN { #4 } #4 } \cs_generate_variant:Nn \substances_remove_str_x:nnnN { V } % let's use xtemplate's features for possible customization later: \DeclareObjectType { substances-index } { 1 } \DeclareTemplateInterface { substances-index } { standard } { 1 } { alternative-entry : boolean = true , alternative-name : boolean = true } \DeclareTemplateCode { substances-index } { standard } { 1 } { alternative-entry = \l_substances_index_alternative_entry_bool , alternative-name = \l_substances_index_alternative_name_bool , } { \AssignTemplateKeys \bool_if:nTF { \l_substances_index_alternative_name_bool && \prop_if_in_p:Nn \l_substances_alt_prop { #1 } } { \substances_index:nx { \c_job_name_tl -chem } { \SubstanceIndexNameAltEntry { \prop_get:Nn \l_substances_sort_prop { #1 } } { \GetSubstanceProperty { #1 } { name } } { \GetSubstanceProperty { #1 } { alt } } } } { \substances_index:nx { \c_job_name_tl -chem } { \SubstanceIndexNameEntry { \prop_get:Nn \l_substances_sort_prop { #1 } } { \GetSubstanceProperty { #1 } { name } } } } \bool_if:nT { \l_substances_index_alternative_entry_bool && \l_substances_index_alternative_name_bool && \prop_if_in_p:Nn \l_substances_alt_prop { #1 } } { \bool_if:cF { g_substances_#1_alt_index_entry_bool } { \substances_index:nx { \c_job_name_tl -chem } { \SubstanceIndexAltEntry { \prop_get:Nn \l_substances_altsort_prop { #1 } } { \GetSubstanceProperty { #1 } { name } } { \GetSubstanceProperty { #1 } { alt } } } \bool_gset_true:c { g_substances_#1_alt_index_entry_bool } } } } \DeclareInstance { substances-index } { default } { standard } { } \cs_new:Npn \SubstanceIndexNameEntry #1#2 { #1@#2 } \cs_new:Npn \SubstanceIndexNameAltEntry #1#2#3 { #1@#2~(#3) } \cs_new:Npn \SubstanceIndexAltEntry #1#2#3 { #1@#3|see{#2} } % ---------------------------------------------------------------------------- % define some default fields: \cs_new_nopar:Npn \@CAS #1-#2-#3 { \iupac{#1\-#2\-#3} } \NewDocumentCommand \CAS { m } { \@CAS #1 } \DeclareSubstanceProperty* { name } [\iupac] \DeclareSubstanceProperty { sort } \DeclareSubstanceProperty { alt } [\iupac] \DeclareSubstanceProperty { altsort } \DeclareSubstanceProperty { CAS } [\CAS] \DeclareSubstanceProperty { PubChem } % ---------------------------------------------------------------------------- % load style file \file_if_exist:nTF { substances- \l_substances_style_tl .def } { \file_input:n { substances- \l_substances_style_tl .def } } { \substances_msg:nnn { style-missing } { \l_substances_style_tl } { } \file_input:n { substances-default.def } } \NewDocumentCommand \LoadSubstances { m } { \file_if_exist:nTF { #1.sub } { \file_input:n { #1.sub } } { \substances_msg:nnn { database-missing } { #1.sub } { } } } \@onlypreamble\LoadSubstances \tex_endinput:D % HISTORY: 2012/07/22 v0.1 - first release to CTAN