#!/usr/bin/env python3

# Author: Andrew Jewett (jewett.aij at g mail)
# License: MIT License  (See LICENSE.md)
# Copyright (c) 2013
# All rights reserved.

"""
lttree_check.py

The original template file format supports any variable types or file names.
However if you plan to process template files using lttree.py to create
LAMMPS-readable input/data files, then variables and file names obey certain
naming conventions.  This code attempts to insure these conventions are obeyed
and to make sure that necessary variables are defined.

 -- This code checks static variables (@) and basic LAMMPS syntax --

This program makes an attempt to check that the variables and file names
which appear in an "lttree" file are not mispelled (or miscapitlised).

It also attempts to check that LAMMPS syntax conventions are obeyed.
(It checks that the appropriate type of variable is located in each column).

It also attempts to check that all of the needed coeffs are defined.

 -- This code does NOT check instance variables ($) --

This code does not check to make sure that all references to instance variables
(such as $atom, $bond, $angle, $dihedral, $improper or $mol variables) are valid
This means a user's input script command (like the "group" command) could refer
to an $atom or $mol which was never defined, and this code would not detect it.
(Why: Checking for instance variables requires building the entire instance tree
 and checking references uses up additional memory after that. I do not do this
 because memory is often very scarce after building the instance tree.)
Instead, we could check for these kinds of errors when post-processing of
the files generated by lttree.py or moltemplate.sh.

 -- This is not the pretiest code I've ever written. --

"""


import sys

try:
    from .ttree_lex import RemoveOuterQuotes, HasWildcard, InputError, \
        ErrorLeader, TextBlock, VarRef, TemplateLexer, \
        ExtractCatName, TableFromTemplate
    from .ttree import BasicUISettings, BasicUIParseArgs, EraseTemplateFiles, \
        StackableCommand, PopCommand, PopRightCommand, PopLeftCommand, \
        PushCommand, PushLeftCommand, PushRightCommand, ScopeCommand, \
        WriteVarBindingsFile, StaticObj, InstanceObj, \
        BasicUI, ScopeBegin, ScopeEnd, WriteFileCommand, Render
    from .lttree_styles import data_atoms, data_prefix, data_masses, \
        data_velocities, data_ellipsoids, data_triangles, data_lines, \
        data_pair_coeffs, data_bond_coeffs, data_angle_coeffs, \
        data_dihedral_coeffs, data_improper_coeffs, data_bondbond_coeffs, \
        data_bondangle_coeffs, data_middlebondtorsion_coeffs, \
        data_endbondtorsion_coeffs, data_angletorsion_coeffs, \
        data_angleangletorsion_coeffs, data_bondbond13_coeffs, \
        data_angleangle_coeffs, data_bonds_by_type, data_angles_by_type, \
        data_dihedrals_by_type, data_impropers_by_type, \
        data_bonds, data_bond_list, data_angles, data_dihedrals, data_impropers, \
        data_boundary, data_pbc, data_prefix_no_space, \
        in_init, in_settings, in_prefix, in_middle, in_charges
    from .lttree import LttreeSettings, LttreeParseArgs
    from .ttree_matrix_stack import AffineTransform, MultiAffineStack, \
        LinTransform
except (ImportError, SystemError, ValueError):
    # not installed as a package
    from ttree_lex import *
    from ttree import *
    from lttree_styles import *
    from lttree import *
    from ttree_matrix_stack import *


try:
    from .ttree import StaticObj, WriteFileCommand, DescrToCatLeafPtkns, \
        AssignStaticVarPtrs, FindReplacementVarPairs, ReplaceVars
except (ImportError, SystemError, ValueError):
    # not installed as a package
    from ttree import *
    from lttree import *
    from ttree_lex import *
    from lttree_styles import *

if sys.version < '2.6':
    raise InputError(
        'Error: Alas, you must upgrade to a newer version of python.')


g_program_name = __file__.split('/')[-1]  # = 'lttree_check.py'
g_version_str = '0.81.2'
g_date_str = '2021-5-24'


# g_no_check_msg = \
#  "(If this error message is wrong, and/or you would like to continue anyway,\n"+\
#  "try running moltemplate again using the \"-nocheck\" command-line-argument.)\n"

g_no_check_msg = \
    '(To continue anyway, run moltemplate using the \"-nocheck\" argument.)\n'
g_omit_pair_coeff_checking = False



def CheckCommonVarNames(prefix, descr_str, suffix, srcloc):
    """ Check the name of variables in a lttree-file to confirm
        that they follow the conventions used by lttree.
        Almost any variable/category name is permitted, except for
        names which closely match those reserved by lttree.

    """

    cat_name, cat_ptkns, leaf_ptkns = \
        DescrToCatLeafPtkns(descr_str,
                            srcloc)

    if (cat_name.lower() == 'mol'):
        if (cat_name != 'mol'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Variable category: \"' + cat_name + '\" does not match, yet overlaps\n' +
                             'closely with a reserved lttree variable category.\n'
                             'Perhaps you meant \"mol\"?')

    elif (cat_name.lower() == 'group'):
        if (cat_name != 'group'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Variable category: \"' + cat_name + '\" does not match, yet overlaps\n' +
                             'closely with a reserved lttree variable category.\n'
                             'Perhaps you meant \"group\"?')
    elif (cat_name.lower() == 'fix'):
        if (cat_name != 'fix'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Variable category: \"' + cat_name + '\" does not match, yet overlaps\n' +
                             'closely with a reserved lttree variable category.\n'
                             'Use \"fix\" instead.')
    elif (cat_name.lower() == 'atom'):
        if (cat_name != 'atom'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Illegal lttree variable category: \"' + cat_name + '\"\n' +
                             'Use \"atom\" instead.')
    elif (cat_name.lower() == 'bond'):
        if (cat_name != 'bond'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Variable category: \"' + cat_name + '\" does not match, yet overlaps\n' +
                             'closely with a reserved lttree variable category.\n'
                             'Use \"bond\" instead.')
    elif (cat_name.lower() == 'angle'):
        if (cat_name != 'angle'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Variable category: \"' + cat_name + '\" does not match, yet overlaps\n' +
                             'closely with a reserved lttree variable category.\n'
                             'Use \"angle\" instead.')
    elif (cat_name.lower() == 'dihedral'):
        if (cat_name != 'dihedral'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Variable category: \"' + cat_name + '\" does not match, yet overlaps\n' +
                             'closely with a reserved lttree variable category.\n'
                             'Use \"dihedral\" instead.')
    elif (cat_name.lower() == 'improper'):
        if (cat_name != 'improper'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Variable category: \"' + cat_name + '\" does not match, yet overlaps\n' +
                             'closely with a reserved lttree variable category.\n'
                             'Use \"improper\" instead.')
    else:
        sys.stderr.write('-----------------------------------------------------\n' +
                         'WARNING: in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                         '  Unrecognised template variable category: \"' + cat_name + '\"\n' +
                         '-----------------------------------------------------\n')


def CheckDataFileNames(filename,
                       srcloc,
                       write_command,
                       fnames_found):
    N_data_prefix = len(data_prefix)
    #data_prefix_no_space = data_prefix.rstrip()
    N_data_prefix_no_space = len(data_prefix)

    section_name = filename[N_data_prefix:]

    if ((section_name.lower() == 'atom') or
            (section_name.lower() == 'atoms')):
        if (filename != data_atoms):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_atoms + '\"?')

        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'velocities') or
          (section_name.lower() == 'velocity')):
        if (filename != data_velocities):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_velocities + '\"?')
        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'mass') or
          (section_name.lower() == 'masses')):
        if (filename != data_masses):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_masses + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'ellipsoids') or
          (section_name.lower() == 'ellipsoid') or
          (section_name.lower() == 'elipsoids') or
          (section_name.lower() == 'elipsoid')):
        if (filename != data_ellipsoids):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_ellipsoids + '\"?')
        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'triangle') or
          (section_name.lower() == 'triangles')):
        if (filename != data_triangles):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_triangles + '\"?')
        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'line') or
          (section_name.lower() == 'lines')):
        if (filename != data_lines):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_lines + '\"?')
        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('pair coef') == 0) or
          (section_name.lower().find('pair_coef') == 0) or
          (section_name.lower().find('paircoef') == 0) or
          (section_name.lower().find('pair by type') == 0) or
          (section_name.lower().find('pair bytype') == 0) or
          (section_name.lower().find('pair_by_type') == 0) or
          (section_name.lower().find('pair_bytype') == 0) or
          (section_name.lower().find('pairbytype') == 0)):
        if (filename != data_pair_coeffs):
            err_msg = 'Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +\
                      'Output file name (\"' + filename + '\") does not match,\n' +\
                      'yet overlaps closely with reserved lttree-file name.\n' +\
                      'Perhaps you meant \"' + data_pair_coeffs + '\"?'
            if ((section_name.lower().find('by type') != -1) or
                    (section_name.lower().find('by_type') != -1) or
                    (section_name.lower().find('bytype') != -1)):
                err_msg += '\n    (Note: "pair" parameters are always assigned by type.\n' +\
                           '     There\'s no need to specify \"by type\")'
            raise InputError(err_msg)
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('bond coef') == 0) or
          (section_name.lower().find('bond_coef') == 0) or
          (section_name.lower().find('bondcoef') == 0)):
        if (filename != data_bond_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_bond_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('angle coef') == 0) or
          (section_name.lower().find('angle_coef') == 0) or
          (section_name.lower().find('anglecoef') == 0)):
        if (filename != data_angle_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_angle_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('dihedral coef') == 0) or
          (section_name.lower().find('dihedral_coef') == 0) or
          (section_name.lower().find('dihedralcoef') == 0)):
        if (filename != data_dihedral_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_dihedral_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('improper coef') == 0) or
          (section_name.lower().find('improper_coef') == 0) or
          (section_name.lower().find('impropercoef') == 0)):
        if (filename != data_improper_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_improper_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    # -- class2 data sections --
    elif ((section_name.lower().find('bondbond coef') == 0) or
          (section_name.lower().find('bondbond_coef') == 0) or
          (section_name.lower().find('bondbondcoef') == 0)):
        if (filename != data_bondbond_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_bondbond_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('bondangle coef') == 0) or
          (section_name.lower().find('bondangle_coef') == 0) or
          (section_name.lower().find('bondanglecoef') == 0)):
        if (filename != data_bondangle_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_bondangle_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('middlebondtorsion coef') == 0) or
          (section_name.lower().find('middlebondtorsion_coef') == 0) or
          (section_name.lower().find('middlebondtorsioncoef') == 0) or
          (section_name.lower().find('middlebondtorision coef') == 0) or
          (section_name.lower().find('middlebondtorision_coef') == 0) or
          (section_name.lower().find('middlebondtorisioncoef') == 0)):
        if (filename != data_middlebondtorsion_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_middlebondtorsion_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('endbondtorsion coef') == 0) or
          (section_name.lower().find('endbondtorsion_coef') == 0) or
          (section_name.lower().find('endbondtorsioncoef') == 0) or
          (section_name.lower().find('endbondtorision coef') == 0) or
          (section_name.lower().find('endbondtorision_coef') == 0) or
          (section_name.lower().find('endbondtorisioncoef') == 0)):
        if (filename != data_endbondtorsion_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_endbondtorsion_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('angletorsion coef') == 0) or
          (section_name.lower().find('angletorsion_coef') == 0) or
          (section_name.lower().find('angletorsioncoef') == 0) or
          (section_name.lower().find('angletorision coef') == 0) or
          (section_name.lower().find('angletorision_coef') == 0) or
          (section_name.lower().find('angletorisioncoef') == 0)):
        if (filename != data_angletorsion_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_angletorsion_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('angleangletorsion coef') == 0) or
          (section_name.lower().find('angleangletorsion_coef') == 0) or
          (section_name.lower().find('angleangletorsioncoef') == 0) or
          (section_name.lower().find('angleangletorision coef') == 0) or
          (section_name.lower().find('angleangletorision_coef') == 0) or
          (section_name.lower().find('angleangletorisioncoef') == 0)):
        if (filename != data_angleangletorsion_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_angleangletorsion_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('bondbond13 coef') == 0) or
          (section_name.lower().find('bondbond13_coef') == 0) or
          (section_name.lower().find('bondbond13coef') == 0)):
        if (filename != data_bondbond13_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_bondbond13_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('angleangle coef') == 0) or
          (section_name.lower().find('angleangle_coef') == 0) or
          (section_name.lower().find('angleanglecoef') == 0)):
        if (filename != data_angleangle_coeffs):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_angleangle_coeffs + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'bonds by type') or
          (section_name.lower() == 'bonds bytype') or
          (section_name.lower() == 'bonds_by_type') or
          (section_name.lower() == 'bonds_bytype') or
          (section_name.lower() == 'bondsbytype') or
          (section_name.lower() == 'bond by type') or
          (section_name.lower() == 'bond bytype') or
          (section_name.lower() == 'bond_by_type') or
          (section_name.lower() == 'bond_bytype') or
          (section_name.lower() == 'bondbytype')):
        if (filename != data_bonds_by_type):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_bonds_by_type + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'angles by type') or
          (section_name.lower() == 'angles bytype') or
          (section_name.lower() == 'angles_by_type') or
          (section_name.lower() == 'angles_bytype') or
          (section_name.lower() == 'anglesbytype') or
          (section_name.lower() == 'angle by type') or
          (section_name.lower() == 'angle bytype') or
          (section_name.lower() == 'angle_by_type') or
          (section_name.lower() == 'angle_bytype') or
          (section_name.lower() == 'anglebytype')):
        if (filename != data_angles_by_type):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_angles_by_type + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'dihedrals by type') or
          (section_name.lower() == 'dihedrals bytype') or
          (section_name.lower() == 'dihedrals_by_type') or
          (section_name.lower() == 'dihedrals_bytype') or
          (section_name.lower() == 'dihedralsbytype') or
          (section_name.lower() == 'dihedral by type') or
          (section_name.lower() == 'dihedral bytype') or
          (section_name.lower() == 'dihedral_by_type') or
          (section_name.lower() == 'dihedral_bytype') or
          (section_name.lower() == 'dihedralbytype')):
        if (filename != data_dihedrals_by_type):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_dihedrals_by_type + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'impropers by type') or
          (section_name.lower() == 'impropers bytype') or
          (section_name.lower() == 'impropers_by_type') or
          (section_name.lower() == 'impropers_bytype') or
          (section_name.lower() == 'impropersbytype') or
          (section_name.lower() == 'improper by type') or
          (section_name.lower() == 'improper bytype') or
          (section_name.lower() == 'improper_by_type') or
          (section_name.lower() == 'improper_bytype') or
          (section_name.lower() == 'improperbytype')):
        if (filename != data_impropers_by_type):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_impropers_by_type + '\"?')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'bonds') or
          (section_name.lower() == 'bond')):
        if (filename != data_bonds):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_bonds + '\"?')
        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower().find('bond list') == 0) or
          (section_name.lower().find('bonds list') == 0) or
          (section_name.lower().find('bond_list') == 0) or
          (section_name.lower().find('bonds_list') == 0)):
        if (filename != data_bond_list):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_bonds_by_type + '\"?')
        elif (write_command != 'write'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'angles') or
          (section_name.lower() == 'angle')):
        if (filename != data_angles):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_angles + '\"?')
        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'dihedrals') or
          (section_name.lower() == 'dihedral')):
        if (filename != data_dihedrals):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_dihedrals + '\"?')
        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'impropers') or
          (section_name.lower() == 'improper')):
        if (filename != data_impropers):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_impropers + '\"?')
        elif (write_command == 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write(\"' + filename + '\") instead.\n')

    elif ((section_name.lower() == 'box boundaries') or
          (section_name.lower() == 'box boundary') or
          (section_name.lower() == 'boundaries') or
          (section_name.lower() == 'boundary') or
          (section_name.lower() == 'boundary conditions') or
          (section_name.lower() == 'periodic boundaries') or
          (section_name.lower() == 'periodic boundary conditions') or
          (section_name.lower() == 'periodic_boundaries') or
          (section_name.lower() == 'periodic_boundary_conditions') or
          (section_name.lower() == 'pbc')):
        if ((filename != data_boundary) and
                (filename != data_pbc)):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + data_boundary + '\"?\n'
                             '(Specify periodic boundary conditions this way.)')
        elif (write_command != 'write_once'):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
                             'want to use the ' + write_command + '() command with \"' + filename + '\".\n'
                             'You should probably use write_once(\"' + filename + '\") instead.\n')
        elif (filename == data_pbc):
            sys.stderr.write('WARNING: write_once(\"' + data_pbc + '\") is depreciated.\n'
                             '     Use write_once(\"' + data_boundary + '\") instead.\n')


def CheckCommonFileNames(filename,
                         srcloc,
                         write_command,
                         filenames_found):
    """
    Check the write() or write_once() statements in a
    lttree-file to make sure that the files being written
    follow the conventions used by lttree.
    Almost any file name is permitted, except for file names
    which closely match those reserved by lttree.

    """

    filenames_found.add(filename)

    N_data_prefix = len(data_prefix)
    #data_prefix_no_space = data_prefix.rstrip()
    N_data_prefix_no_space = len(data_prefix_no_space)

    if ((filename[:N_data_prefix].lower() == data_prefix.lower()) and
        (filename[:N_data_prefix] != data_prefix)):
        raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                         'The beginning of output file (\"' +
                         filename + '\")\n'
                         'does not match yet overlaps closely with a reserved lttree-file name prefix.\n'
                         '(\"' + data_prefix + '\").  Perhaps you meant \"' + data_prefix + filename[N_data_prefix:] + '\"?')

    # check did they forget the space?
    if (filename[:N_data_prefix_no_space] == data_prefix_no_space):
        if (filename[:N_data_prefix] == data_prefix):

            CheckDataFileNames(filename,
                               srcloc,
                               write_command,
                               filenames_found)

        else:
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'The beginning of output file (\"' +
                             filename + '\")\n'
                             'does not match yet overlaps closely with a reserved lttree-file name prefix.\n'
                             '(\"' + data_prefix + '\").  Perhaps you meant \"' + data_prefix + filename[N_data_prefix_no_space:] + '\"?')

    elif ((filename.lower() == 'box boundaries') or
          (filename.lower() == 'box boundary') or
          (filename.lower() == 'boundaries') or
          (filename.lower() == 'boundary') or
          (filename.lower() == 'boundary conditions') or
          (filename.lower() == 'periodic boundaries') or
          (filename.lower() == 'periodic boundary conditions') or
          (filename.lower() == 'periodic_boundaries') or
          (filename.lower() == 'periodic_boundary_conditions') or
          (filename.lower() == 'pbc')):
        # In that case (for one thing) they forgot the data_prefix
        raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                         'Output file name (\"' + filename +
                         '\") does not match,\n'
                         'yet overlaps closely with reserved lttree-file name.\n'
                         'Perhaps you meant \"' + data_boundary + '\"?\n'
                         '(Specify periodic boundary conditions this way.)')

    elif ((filename.lower() == 'init') or
          (filename.lower() == 'in init') or
          (filename.lower() == 'ininit') or
          (filename.lower() == 'initialize') or
          (filename.lower() == 'in initialize') or
          (filename.lower() == 'ininitialize')):
        if (filename != in_init):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + in_init + '\"?')
        # elif (write_command != 'write_once'):
        #    raise InputError('Probable typo in '+ErrorLeader(srcloc.infile,srcloc.lineno)+'\n\n'+
        #                     'When using moltemplate.sh to build LAMMPS input files, you probably do not\n'
        #                     'want to use the '+write_command+'() command with \"'+filename+'\".\n'
        #                     'You should probably use write_once(\"'+filename+'\") instead.\n')

    elif ((filename.lower() == 'settings') or
          (filename.lower() == 'in settings') or
          (filename.lower() == 'insettings')):

        if (filename != in_settings):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + in_settings + '\"?')

    elif ((filename.lower() == 'charges') or
          (filename.lower() == 'in charges') or
          (filename.lower() == 'incharges')):

        if (filename != in_charges):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + in_charges + '\"?')

    elif ((filename.lower() == 'set_coords') or
          (filename.lower() == 'set coords') or
          (filename.lower() == 'setcoords') or
          (filename.lower() == 'in set_coords') or
          (filename.lower() == 'in set coords') or
          (filename.lower() == 'in setcoords')):
        if (filename != in_set_coords):
            raise InputError('Probable typo in ' + ErrorLeader(srcloc.infile, srcloc.lineno) + '\n\n' +
                             'Output file name (\"' + filename +
                             '\") does not match,\n'
                             'yet overlaps closely with reserved lttree-file name.\n'
                             'Perhaps you meant \"' + in_set_coords + '\"?')


def CheckSyntaxCheap(lex):
    """ Parse() builds a static tree of StaticObjs by parsing text file.
    -The "lex" argument is afile or input stream which has been converted
     to a "TemplateLexer" object (similar to the python's built-in shlex lexer).
    """

    fnames_found = set([])
    prematurely_read_token = None

    while True:

        if prematurely_read_token == None:
            command = lex.get_token()
        else:
            command = prematurely_read_token
        prematurely_read_token = None

        #print('Parse(): token = \"'+command+'\", '+lex.error_leader())

        if command == lex.eof:
            #print('Parse(): EOF encountered\n')
            break

        if ((command == 'write') or (command == 'write_once')):
            open_paren = lex.get_token()

            #print('Parse():     open_paren=\"'+open_paren+'\"')

            if open_paren == '{':
                # ..then the user neglected to specify the "filename" file-name
                # argument.  In that case, supply the default, ''.
                # (which is shorthand for the standard out in this case)
                open_curly = open_paren[0]
                open_paren = ''
                close_paren = ''
                filename = ''
                srcloc = lex.GetSrcLoc()
            else:
                filename = lex.get_token()
                if filename == ')':
                    filename == ''
                    close_paren = ')'
                else:
                    close_paren = lex.get_token()
                open_curly = lex.get_token()
                srcloc = lex.GetSrcLoc()

            if ((open_curly != '{') or
                    ((open_paren == '') and (close_paren != '')) or
                    ((open_paren == '(') and (close_paren != ')'))):
                raise InputError('Error: in ' + lex.error_leader() + '\n\n'
                                 'Syntax error at beginning of ' + command + ' command.')

            filename = RemoveOuterQuotes(filename, lex.quotes)
            # The previous line is similar to:
            #filename = filename.strip(lex.quotes)

            CheckCommonFileNames(filename, lex.GetSrcLoc(),
                                 command, fnames_found)

            tmpl_contents = lex.ReadTemplate()
            StaticObj.CleanupReadTemplate(tmpl_contents, lex)

            for entry in tmpl_contents:
                if (type(entry) is VarRef):

                    CheckCommonVarNames(entry.prefix,
                                        entry.descr_str,
                                        entry.suffix,
                                        entry.srcloc)

    # if (data_velocities not in fnames_found):
    #    sys.stderr.write('-------------------------------------------------\n'
    #                     'WARNING: \"'+data_velocities+'\" file not found\n'
    #                     '-------------------------------------------------\n')
    # if (data_pair_coeffs not in fnames_found):
    #    sys.stderr.write('-------------------------------------------------\n'
    #                     'WARNING: \"'+data_pair_coeffs+'\" file not found\n'
    #                     '-------------------------------------------------\n')
    if (data_atoms not in fnames_found):
        sys.stderr.write('WARNING: \"' + data_atoms + '\" file not found\n')
    if (data_masses not in fnames_found):
        sys.stderr.write('WARNING: \"' + data_masses + '\" file not found\n'
                         '         (This is not necessarily a problem.  There are other ways to specify\n'
                         '          the mass, and there are other ways to integrate the equations of\n'
                         '          motion. ATB users, for example, can ignore this message.)\n')

    # if (data_bonds not in fnames_found):
    #    sys.stderr.write('--------------------------------------------------\n'
    #                     'WARNING: \"'+data_bonds+'\" file not found\n'
    #                     '--------------------------------------------------\n')
    # if (data_angles not in fnames_found):
    #    sys.stderr.write('--------------------------------------------------\n'
    #                     'WARNING: \"'+data_angles+'\" file not found\n'
    #                     '--------------------------------------------------\n')
    # if (data_dihedrals not in fnames_found):
    #    sys.stderr.write('--------------------------------------------------\n'
    #                     'WARNING: \"'+data_dihedrals+'\" file not found\n'
    #                     '--------------------------------------------------\n')
    # if (data_impropers not in fnames_found):
    #    sys.stderr.write('--------------------------------------------------\n'
    #                     'WARNING: \"'+data_impropers+'\" file not found\n'
    #                     '--------------------------------------------------\n')
    # if (data_bond_coeffs not in fnames_found):
    #    sys.stderr.write('--------------------------------------------------\n'
    #                     'WARNING: \"'+data_bond_coeffs+'\" file not found\n'
    #                     '--------------------------------------------------\n')
    # if (data_angle_coeffs not in fnames_found):
    #    sys.stderr.write('--------------------------------------------------\n'
    #                     'WARNING: \"'+data_angle_coeffs+'\" file not found\n'
    #                     '--------------------------------------------------\n')
    # if (data_dihedral_coeffs not in fnames_found):
    #    sys.stderr.write('--------------------------------------------------\n'
    #                     'WARNING: \"'+data_dihedral_coeffs+'\" file not found\n'
    #                     '--------------------------------------------------\n')
    # if (data_improper_coeffs not in fnames_found):
    #    sys.stderr.write('--------------------------------------------------\n'
    #                     'WARNING: \"'+data_imrpoper_coeffs+'\" file not found\n'
    #                     '--------------------------------------------------\n')
    if (in_init not in fnames_found):
        sys.stderr.write('WARNING: \"' + in_init + '\" file not found\n')

    if (in_settings not in fnames_found):
        sys.stderr.write('WARNING: \"' + in_settings + '\" file not found\n')




def IsAtomRef(entry):
    """
    Is entry a template of the form:  @atom:b or @{atom:b}
    """
    return ((isinstance(entry, VarRef)) and
            (entry.prefix in ('@', '@{')) and
            (entry.nptr.cat_name == 'atom'))


def IsAtomRefRange(entry):
    """
    Is entry a template of the form:  @{atom:b1}*@{atom:b3}   ?
    """
    is_range = (hasattr(entry, "__getitem__") and  #<-- does entry support []?
                (len(entry) == 3) and
                IsAtomRef(entry[0]) and
                IsAtomRef(entry[2]) and
                ((isinstance(entry[1], TextBlock) and
                  (entry[1].text == '*'))))
    global g_omit_pair_coeff_checking
    if is_range:
        g_omit_pair_coeff_checking = True
    return is_range



def CheckSyntaxStatic(context_node,
                      root_node,
                      atom_column_names,
                      allow_wildcards,
                      data_pair_coeffs_defined,
                      data_bond_coeffs_defined,
                      data_angle_coeffs_defined,
                      data_dihedral_coeffs_defined,
                      data_improper_coeffs_defined,
                      in_pair_coeffs_defined,
                      in_bond_coeffs_defined,
                      in_angle_coeffs_defined,
                      in_dihedral_coeffs_defined,
                      in_improper_coeffs_defined,
                      search_instance_commands):

    if search_instance_commands:
        assert(isinstance(context_node, StaticObj))
        commands = context_node.instance_commands
    else:
        # Note: Leaf nodes contain no commands, so skip them
        if (not hasattr(context_node, 'commands')):
            return
        # Otherwise process their commands
        commands = context_node.commands

    for command in commands:
        if isinstance(command, WriteFileCommand):

            filename = command.filename

            if filename == None:  # (The "create_var" command causes this)
                pass
            elif ((filename.find(in_prefix)==0)or #if filename begins with "In "
                  (filename.find(in_middle)!= -1)): #if filename contains '.in.'
                CheckInFileSyntax(command.tmpl_list,
                                  root_node,
                                  allow_wildcards,
                                  in_pair_coeffs_defined,
                                  in_bond_coeffs_defined,
                                  in_angle_coeffs_defined,
                                  in_dihedral_coeffs_defined,
                                  in_improper_coeffs_defined)

            elif filename == 'Data Atoms':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):

                    assert(hasattr(table[i], '__len__'))
                    if len(table[i]) == 0:
                        pass  # skip blank lines
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                            (table[i][0].text == '#')):
                        pass  # skip comment lines
                    else:
                        syntax_err = False

                        if len(table[i]) < len(atom_column_names):
                            syntax_err = True
                        else:
                            syntax_err = False
                            for j in range(0, len(atom_column_names)):
                                if ((atom_column_names[j].lower() == 'atom-id') and
                                    (not ((j < len(table[i])) and
                                          isinstance(table[i][j], VarRef) and
                                          (table[i][j].prefix in ('$', '${')) and
                                          (ExtractCatName(table[i][j].descr_str) == 'atom')))):
                                    syntax_err = True
                                elif ((atom_column_names[j].lower() == 'molecule-id') and
                                      (not ((j < len(table[i])) and
                                            isinstance(table[i][j], VarRef) and
                                            (table[i][j].prefix in ('$', '${')) and
                                            (ExtractCatName(table[i][j].descr_str) == 'mol')))):
                                    syntax_err = True
                                elif ((atom_column_names[j].lower() == 'atom-type') and
                                      (not ((j < len(table[i])) and
                                            (isinstance(table[i][j], VarRef)) and
                                            (table[i][j].prefix in ('@', '@{')) and
                                            (table[i][j].nptr.cat_name == 'atom') and
                                            (table[i][j].nptr.cat_node == root_node)))):
                                    syntax_err = True

                        if syntax_err:
                            correct_rows_list = [s for s in atom_column_names]
                            for j in range(0, len(correct_rows_list)):
                                if correct_rows_list[j].lower() == 'atom-id':
                                    correct_rows_list[j] = '$atom:id'
                                elif correct_rows_list[j].lower() == 'atom-type':
                                    correct_rows_list[j] = '@atom:type'
                                elif correct_rows_list[j].lower() == 'molecule-id':
                                    correct_rows_list[j] = '$mol:id'
                                correct_rows_msg = ' '.join(correct_rows_list)
                            raise InputError('----------------------------------------------------\n' +
                                             '     Syntax error near ' +
                                             ErrorLeader(table[i][0].srcloc.infile,
                                                         table[i][0].srcloc.lineno) + '\n'
                                             '     Invalid "Data Atoms" syntax.\n' +
                                             'Each line of the \"Data Atoms\" section should have this format:\n\n'
                                             '  ' + correct_rows_msg + '\n\n'
                                             'You may have forgotten to specify the LAMMPS atom_style.\n' +
                                             '(You can do this running moltemplate with the -atom-style _style_ argument.)\n' +
                                             '----------------------------------------------------\n' +
                                             g_no_check_msg)

            elif filename == 'Data Bonds':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    syntax_err = False
                    assert(hasattr(table[i], '__len__'))
                    if len(table[i]) > 0:
                        if ((len(table[i]) > 1) and
                                isinstance(table[i][0], TextBlock) and
                                (len(table[i][0].text) > 0) and
                                (table[i][0].text == '#')):
                            pass
                        else:
                            if len(table[i]) < 4:
                                syntax_err = True
                            table_entry = table[i][0]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'bond'))):
                                syntax_err = True
                            if len(table[i]) > 1:
                                table_entry = table[i][1]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('@', '@{')) and
                                     (table_entry.nptr.cat_name == 'bond') and
                                     (table_entry.nptr.cat_node == root_node))):
                                syntax_err = True
                            if len(table[i]) > 2:
                                table_entry = table[i][2]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 3:
                                table_entry = table[i][3]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True

                            if syntax_err:
                                raise InputError('----------------------------------------------------\n' +
                                                 '     Syntax error near ' +
                                                 ErrorLeader(table[i][0].srcloc.infile,
                                                             table[i][0].srcloc.lineno) + '\n'
                                                 '     Incorrect "Data Bonds" syntax.\n' +
                                                 'Each line of the \"Data Bonds\" section should have this format:\n\n'
                                                 '  $bond:id @bond:type $atom:id1 $atom:id2\n' +
                                                 '----------------------------------------------------\n' +
                                                 g_no_check_msg)

            elif filename == 'Data Bond List':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    syntax_err = False
                    assert(hasattr(table[i], '__len__'))
                    if len(table[i]) > 0:
                        if ((len(table[i]) > 1) and
                                isinstance(table[i][0], TextBlock) and
                                (len(table[i][0].text) > 0) and
                                (table[i][0].text == '#')):
                            pass
                        else:
                            if len(table[i]) < 3:
                                syntax_err = True
                            table_entry = table[i][0]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'bond'))):
                                syntax_err = True
                            if len(table[i]) > 1:
                                table_entry = table[i][1]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 2:
                                table_entry = table[i][2]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True

                            if syntax_err:
                                raise InputError('----------------------------------------------------\n' +
                                                 '     Syntax error near ' +
                                                 ErrorLeader(table[i][0].srcloc.infile,
                                                             table[i][0].srcloc.lineno) + '\n'
                                                 '     Incorrect "Data Bond List" syntax.\n' +
                                                 'Each lines in this section should have this format:\n\n'
                                                 '  $bond:id $atom:id1 $atom:id2\n' +
                                                 '----------------------------------------------------\n' +
                                                 g_no_check_msg)

            elif filename == 'Data Angles':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    syntax_err = False
                    assert(hasattr(table[i], '__len__'))
                    if len(table[i]) > 0:
                        if ((len(table[i]) > 1) and
                                isinstance(table[i][0], TextBlock) and
                                (len(table[i][0].text) > 0) and
                                (table[i][0].text == '#')):
                            pass
                        else:
                            if len(table[i]) < 5:
                                syntax_err = True
                            table_entry = table[i][0]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'angle'))):
                                syntax_err = True
                            if len(table[i]) > 1:
                                table_entry = table[i][1]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('@', '@{')) and
                                     (table_entry.nptr.cat_name == 'angle') and
                                     (table_entry.nptr.cat_node == root_node))):
                                syntax_err = True
                            if len(table[i]) > 2:
                                table_entry = table[i][2]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 3:
                                table_entry = table[i][3]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 4:
                                table_entry = table[i][4]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True

                            if syntax_err:
                                raise InputError('----------------------------------------------------\n' +
                                                 '     Syntax error near ' +
                                                 ErrorLeader(table[i][0].srcloc.infile,
                                                             table[i][0].srcloc.lineno) + '\n'
                                                 '     Incorrect "Data Angles" syntax.\n' +
                                                 'Each line of the \"Data Angles\" section should have this format:\n\n'
                                                 '  $angle:id @angle:type $atom:id1 $atom:id2 $atom:id3\n' +
                                                 '----------------------------------------------------\n\n' +
                                                 g_no_check_msg)

            elif filename == 'Data Dihedrals':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    syntax_err = False
                    assert(hasattr(table[i], '__len__'))
                    if len(table[i]) > 0:
                        if ((len(table[i]) > 1) and
                                isinstance(table[i][0], TextBlock) and
                                (len(table[i][0].text) > 0) and
                                (table[i][0].text == '#')):
                            pass
                        else:
                            if len(table[i]) < 6:
                                syntax_err = True
                            table_entry = table[i][0]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'dihedral'))):
                                syntax_err = True
                            if len(table[i]) > 1:
                                table_entry = table[i][1]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('@', '@{')) and
                                     (table_entry.nptr.cat_name == 'dihedral') and
                                     (table_entry.nptr.cat_node == root_node))):
                                syntax_err = True
                            if len(table[i]) > 2:
                                table_entry = table[i][2]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 3:
                                table_entry = table[i][3]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 4:
                                table_entry = table[i][4]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 5:
                                table_entry = table[i][5]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True

                            if syntax_err:
                                raise InputError('----------------------------------------------------\n' +
                                                 '     Syntax error near ' +
                                                 ErrorLeader(table[i][0].srcloc.infile,
                                                             table[i][0].srcloc.lineno) + '\n'
                                                 '     Incorrect "Data Dihedrals" syntax.\n' +
                                                 'Each line of the \"Data Dihedrals\" section should have this format:\n\n'
                                                 '  $dihedral:id @dihedral:type $atom:id1 $atom:id2 $atom:id3 $atom:id4\n' +
                                                 '----------------------------------------------------\n' +
                                                 g_no_check_msg)

            elif filename == 'Data Impropers':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    syntax_err = False
                    assert(hasattr(table[i], '__len__'))
                    if len(table[i]) > 0:
                        if ((len(table[i]) > 1) and
                                isinstance(table[i][0], TextBlock) and
                                (len(table[i][0].text) > 0) and
                                (table[i][0].text == '#')):
                            pass
                        else:
                            if len(table[i]) < 6:
                                syntax_err = True
                            table_entry = table[i][0]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'improper'))):
                                syntax_err = True
                            if len(table[i]) > 1:
                                table_entry = table[i][1]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('@', '@{')) and
                                     (table_entry.nptr.cat_name == 'improper') and
                                     (table_entry.nptr.cat_node == root_node))):
                                syntax_err = True
                            if len(table[i]) > 2:
                                table_entry = table[i][2]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 3:
                                table_entry = table[i][3]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 4:
                                table_entry = table[i][4]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True
                            if len(table[i]) > 5:
                                table_entry = table[i][5]
                            if (not ((isinstance(table_entry, VarRef)) and
                                     (table_entry.prefix in ('$', '${')) and
                                     (ExtractCatName(table_entry.descr_str) == 'atom'))):
                                syntax_err = True

                            if syntax_err:
                                raise InputError('----------------------------------------------------\n' +
                                                 '     Syntax error near ' +
                                                 ErrorLeader(table[i][0].srcloc.infile,
                                                             table[i][0].srcloc.lineno) + '\n'
                                                 '     Incorrect "Data Impropers" syntax.\n' +
                                                 'Each line of the \"Data Impropers\" section should have this format:\n\n'
                                                 '  $improper:id @improper:type $atom:id1 $atom:id2 $atom:id3 $atom:id4\n' +
                                                 '----------------------------------------------------\n' +
                                                 g_no_check_msg)

            # A simple wildcard is the character "*" on its own.
            # These are okay.
            # A "compound" wildcard expression is something like
            # 5*7               or
            # 5*                or
            #  *7               or
            # @{bond:A}*@bond:B or
            # @{bond:A}*        or
            # *@bond:B
            # LAMMPS allows this but in moltemplate this causes
            # unintended side-effects.  Check for these now.
            if filename in set(['Data Bond Coeffs',
                                'Data Angle Coeffs',
                                'Data Dihedral Coeffs',
                                'Data Improper Coeffs',
                                'Data Pair Coeffs']):
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    assert(hasattr(table[i], '__len__'))
                    if len(table[i]) > 0:
                        if (isinstance(table[i][0], TextBlock) and
                                table[i][0].text == '*'):
                            if filename == 'Data Bond Coeffs':
                                data_bond_coeffs_defined.add('*')
                            elif filename == 'Data Angle Coeffs':
                                data_angle_coeffs_defined.add('*')
                            elif filename == 'Data Dihedral Coeffs':
                                data_dihedral_coeffs_defined.add('*')
                            elif filename == 'Data Improper Coeffs':
                                data_improper_coeffs_defined.add('*')
                            elif filename == 'Data Pair Coeffs':
                                data_pair_coeffs_defined.add(('*', '*'))
                        else:
                            compound_wildcard = False
                            if (len(table[i]) > 1):
                                if hasattr(table[i][0], '__len__'):
                                    ltmpl = table[i][0]
                                else:
                                    ltmpl = [table[i][0]]
                                for entry in ltmpl:
                                    if (isinstance(entry, TextBlock) and
                                            ('*' in entry.text)):
                                        compound_wildcard = True
                                    elif (isinstance(entry, VarRef) and
                                          ('*' in entry.descr_str)):
                                        compound_wildcard = True
                            if compound_wildcard and (not allow_wildcards):
                                raise InputError('--- Paranoid checking: ---\n'
                                                 '    Possible error near ' +
                                                 ErrorLeader(entry.srcloc.infile,
                                                             entry.srcloc.lineno) + '\n'
                                                 'Wildcard symbols (\"*\", \"?\") are forbiddin within \"' + filename + '\".\n'
                                                 'It is safer to specify the parameters for each type explicitly.\n'
                                                 'To get past this error message, run moltemplate.sh with the \"-allow-wildcards\"\n'
                                                 'argument.\n'
                                                 '(Special case: If the \"*\" character is part of a LAMMPS-style \"*\" range\n'
                                                 ' (eg \"@{atom:C1}*@{atom:C3}\", \"@{bond:B2}*@{bond:B5}\") then MAKE SURE that\n'
                                                 ' the in-betwen types are assigned to connsecutively increasing integer values\n'
                                                 ' by defining them in that order.  If that fails, use the \"-a\" argument\n'
                                                 ' to assign these types to integers manually.)\n')

            if filename == 'Data Bond Coeffs':
                # Commenting the next line out.  We did this already:
                # table = TableFromTemplate(command.tmpl_list,
                #                          [[' ','\t','\r'], '\n'],
                #                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (table[i][0].text == '*')):
                        pass  # we dealt with this case earlier
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not (isinstance(table[i][0], VarRef) and
                               (table[i][0].prefix in ('@', '@{')) and
                               (table[i][0].nptr.cat_name == 'bond') and
                               (table[i][0].nptr.cat_node == root_node))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect "Data Bond Coeffs" syntax.\n'
                                         '  Each line of the \"Data Bond Coeffs\" section\n'
                                         '  should have the following syntax:\n\n' +
                                         '     @bond:type list-of-parameters...\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)
                    else:
                        data_bond_coeffs_defined.add(table[i][0].binding)

            elif filename == 'Data Angle Coeffs':
                # Commenting the next line out.  We did this already:
                # table = TableFromTemplate(command.tmpl_list,
                #                          [[' ','\t','\r'], '\n'],
                #                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (table[i][0].text == '*')):
                        pass  # we dealt with this case earlier
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not (isinstance(table[i][0], VarRef) and
                               (table[i][0].prefix in ('@', '@{')) and
                               (table[i][0].nptr.cat_name == 'angle') and
                               (table[i][0].nptr.cat_node == root_node))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect "Data Angle Coeffs" syntax.\n'
                                         '  Each line of the \"Data Angle Coeffs\" section\n'
                                         '  should have the following syntax:\n\n' +
                                         '     @angle:type list-of-parameters...\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)
                    else:
                        data_angle_coeffs_defined.add(table[i][0].binding)

            elif filename == 'Data Dihedral Coeffs':
                # Commenting the next line out.  We did this already:
                # table = TableFromTemplate(command.tmpl_list,
                #                          [[' ','\t','\r'], '\n'],
                #                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (table[i][0].text == '*')):
                        pass  # we dealt with this case earlier
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not (isinstance(table[i][0], VarRef) and
                               (table[i][0].prefix in ('@', '@{')) and
                               (table[i][0].nptr.cat_name == 'dihedral') and
                               (table[i][0].nptr.cat_node == root_node))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect "Data Dihedral Coeffs" syntax.\n'
                                         '  Each line of the \"Data Dihedral Coeffs\" section\n'
                                         '  should have the following syntax:\n\n' +
                                         '     @dihedral:type list-of-parameters...\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)
                    else:
                        data_dihedral_coeffs_defined.add(table[i][0].binding)

            elif filename == 'Data Improper Coeffs':
                # Commenting the next line out.  We did this already:
                # table = TableFromTemplate(command.tmpl_list,
                #                          [[' ','\t','\r'], '\n'],
                #                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (table[i][0].text == '*')):
                        pass  # we dealt with this case earlier
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not (isinstance(table[i][0], VarRef) and
                               (table[i][0].prefix in ('@', '@{')) and
                               (table[i][0].nptr.cat_name == 'improper') and
                               (table[i][0].nptr.cat_node == root_node))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect "Data Improper Coeffs" syntax.\n'
                                         '  Each line of the \"Data Improper Coeffs\" section\n'
                                         '  should have the following syntax:\n\n' +
                                         '     @improper:type list-of-parameters...\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)
                    else:
                        data_improper_coeffs_defined.add(table[i][0].binding)

            elif filename == 'Data Pair Coeffs':
                # Commenting the next line out.  We did this already:
                # table = TableFromTemplate(command.tmpl_list,
                #                          [[' ','\t','\r'], '\n'],
                #                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 0) and
                          isinstance(table[i][0], TextBlock) and
                          (table[i][0].text == '*')):
                        pass  # we dealt with this case earlier
                    elif ((len(table[i]) > 0) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not ((len(table[i]) > 0) and
                               (IsAtomRef(table[i][0]) or
                                IsAtomRefRange(table[i][0])))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect "Data Pair Coeffs" syntax.\n'
                                         '  Each line of the \"Data Pair Coeffs\" section\n'
                                         '  should have the following syntax:\n\n' +
                                         '     @atom:type list-of-parameters...\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)
                    else:
                        if g_omit_pair_coeff_checking:
                            sys.stderr.write('--------------------------------------------------------------\n'
                                             'WARNING: UNABLE TO CHECK THE "PairCoeff" SECTION.\n'
                                         '         (This happens if you use @{atom:a}*@{atom:b} syntax.)\n')
                        else:
                            data_pair_coeffs_defined.add((table[i][0].binding,
                                                          table[i][0].binding))

            elif filename == 'Data Bonds By Type':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not ((# Ignore lines beginning with '#'
                                isinstance(table[i][0],TextBlock) and
                                (str(table[i][0])[:2] == '"#')) or
                               (len(table[i]) >= 3) and
                               isinstance(table[i][0], VarRef) and
                               (table[i][0].prefix in ('@', '@{')) and
                               (table[i][0].nptr.cat_name == 'bond') and
                               (table[i][0].nptr.cat_node == root_node))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect \"Data Bonds By Type\" syntax.\n'
                                         '  Each line of the \"Data Bonds By Type\" section should begin with an\n'
                                         '  @bond:type variable followed by 2 atom types.\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)

            elif filename == 'Data Angles By Type':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not ((# Ignore lines beginning with '#'
                                isinstance(table[i][0],TextBlock) and
                                (str(table[i][0])[:2] == '"#')) or
                               (len(table[i]) >= 4) and
                               isinstance(table[i][0], VarRef) and
                               (table[i][0].prefix in ('@', '@{')) and
                               (table[i][0].nptr.cat_name == 'angle') and
                               (table[i][0].nptr.cat_node == root_node))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect \"Data Angles By Type\" syntax.\n'
                                         '  Each line of the \"Data Angles By Type\" section should begin with an\n'
                                         '  @angle:type variable followed by 3 atom types (and 2 optional bond types).\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)

            elif filename == 'Data Dihedrals By Type':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not ((# Ignore lines beginning with '#'
                                isinstance(table[i][0],TextBlock) and
                                (str(table[i][0])[:2] == '"#')) or
                               (len(table[i]) >= 5) and
                               isinstance(table[i][0], VarRef) and
                               (table[i][0].prefix in ('@', '@{')) and
                               (table[i][0].nptr.cat_name == 'dihedral') and
                               (table[i][0].nptr.cat_node == root_node))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect \"Data Dihedrals By Type\" syntax.\n'
                                         '  Each line of the \"Data Dihedrals By Type\" section should begin with a\n\n'
                                         '  @dihedral:type variable followed by 4 atom types (and 3 optional bond types).\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)

            elif filename == 'Data Impropers By Type':
                table = TableFromTemplate(command.tmpl_list,
                                          [[' ', '\t', '\r'], '\n'],
                                          [True, False])
                for i in range(0, len(table)):
                    if len(table[i]) == 0:
                        pass
                    elif ((len(table[i]) > 1) and
                          isinstance(table[i][0], TextBlock) and
                          (len(table[i][0].text) > 0) and
                          (table[i][0].text == '#')):
                        # Ignore comment lines (postprocessing removes them)
                        pass
                    elif (not ((# Ignore lines beginning with '#'
                                isinstance(table[i][0],TextBlock) and
                                (str(table[i][0])[:2] == '"#')) or
                               (len(table[i]) >= 5) and
                               isinstance(table[i][0], VarRef) and
                               (table[i][0].prefix in ('@', '@{')) and
                               (table[i][0].nptr.cat_name == 'improper') and
                               (table[i][0].nptr.cat_node == root_node))):
                        raise InputError('----------------------------------------------------\n' +
                                         '     Syntax error near ' +
                                         ErrorLeader(table[i][0].srcloc.infile,
                                                     table[i][0].srcloc.lineno) + '\n'
                                         '     Incorrect \"Data Impropers By Type\" syntax.\n'
                                         '  Each line of the \"Data Impropers By Type\" section should begin with an\n\n'
                                         '  @improper:type variable followed by 4 atom types (and 3 optional bond types).\n' +
                                         '----------------------------------------------------\n' +
                                         g_no_check_msg)

    # Recursively invoke AssignVarPtrs() on all (non-leaf) child nodes:
    for child in context_node.children.values():
        CheckSyntaxStatic(child,
                          root_node,
                          atom_column_names,
                          allow_wildcards,
                          data_pair_coeffs_defined,
                          data_bond_coeffs_defined,
                          data_angle_coeffs_defined,
                          data_dihedral_coeffs_defined,
                          data_improper_coeffs_defined,
                          in_pair_coeffs_defined,
                          in_bond_coeffs_defined,
                          in_angle_coeffs_defined,
                          in_dihedral_coeffs_defined,
                          in_improper_coeffs_defined,
                          search_instance_commands)


def CheckInFileSyntax(tmpl_list,
                      root_node,
                      allow_wildcards,
                      pair_coeffs_defined,
                      bond_coeffs_defined,
                      angle_coeffs_defined,
                      dihedral_coeffs_defined,
                      improper_coeffs_defined):

    table = TableFromTemplate(tmpl_list,
                              [[' ', '\t', '\r'], '\n'],
                              [True, False])

    for i in range(0, len(table)):
        assert(hasattr(table[i], '__len__'))

        if len(table[i]) > 0:
            if ((isinstance(table[i][0], TextBlock)) and
                (table[i][0].text in set(['bond_coeff',
                                          'angle_coeff',
                                          'dihedral_coeff',
                                          'improper_coeff']))):
                if len(table[i]) > 1:  # if not deal with error later
                    if (isinstance(table[i][1], TextBlock) and
                            table[i][1].text == '*'):
                        if table[i][0].text == 'bond_coeff':
                            bond_coeffs_defined.add('*')
                        elif table[i][0].text == 'angle_coeff':
                            angle_coeffs_defined.add('*')
                        elif table[i][0].text == 'dihedral_coeff':
                            dihedral_coeffs_defined.add('*')
                        elif table[i][0].text == 'improper_coeff':
                            improper_coeffs_defined.add('*')
                    else:
                        compound_wildcard = False
                        if (len(table[i]) > 1):
                            if hasattr(table[i][1], '__len__'):
                                ltmpl = table[i][1]
                            else:
                                ltmpl = [table[i][1]]
                            for entry in ltmpl:
                                if (isinstance(entry, TextBlock) and
                                        ('*' in entry.text)):
                                    compound_wildcard = True
                                elif (isinstance(entry, VarRef) and
                                      ('*' in entry.descr_str)):
                                    compound_wildcard = True
                        if compound_wildcard and (not allow_wildcards):
                            raise InputError('---- Paranoid checking: ---\n'
                                             '     Possible error near ' +
                                             ErrorLeader(entry.srcloc.infile,
                                                         entry.srcloc.lineno) + '\n'
                                             'Wildcard symbols (\"*\", \"?\") are forbidden within a \"' +
                                             table[i][0].text + '\".\n'
                                             'command. It is safer to specify the parameters for each bond type explicitly.\n'
                                             'To get past this error message, run moltemplate.sh with the \"-allow-wildcards\"\n'
                                             'argument.\n'
                                             '(Special case: If the \"*\" character is part of a LAMMPS-style \"*\" range\n'
                                             ' (eg \"@{atom:C1}*@{atom:C3}\", \"@{bond:B2}*@{bond:B5}\") then MAKE SURE that\n'
                                             ' the in-betwen types are assigned to connsecutively increasing integer values\n'
                                             ' by defining them in that order.  If that fails, use the \"-a\" argument\n'
                                             ' to assign these types to integers manually.)\n')

            if ((isinstance(table[i][0], TextBlock)) and
                ((table[i][0].text.lower() == 'bondcoeff') or
                 (table[i][0].text.lower() == 'bond_coeff'))):
                if table[i][0].text != 'bond_coeff':
                    raise InputError('----------------------------------------------------\n' +
                                     '     Spelling error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Use \"bond_coeff\", not \"' + table[i][0].text + '\"\n' +
                                     '----------------------------------------------------\n' +
                                     g_no_check_msg)

                if ((len(table[i]) > 1) and
                        isinstance(table[i][1], TextBlock) and
                        (table[i][1].text == '*')):
                    pass  # we dealt with this case earlier
                elif (not ((len(table[i]) > 1) and
                           (isinstance(table[i][1], VarRef)) and
                           (table[i][1].prefix in ('@', '@{')) and
                           (table[i][1].nptr.cat_name == 'bond') and
                           (table[i][1].nptr.cat_node == root_node))):
                    raise InputError('----------------------------------------------------\n' +
                                     '     Syntax error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Invalid \"bond_coeff\" command.\n\n' +
                                     '  Each \"bond_coeff\" command should have the following syntax:\n\n' +
                                     '    bond_coeff @bond:type [optional style] list-of-parameters...\n' +
                                     '----------------------------------------------------\n\n' +
                                     g_no_check_msg)
                else:
                    bond_coeffs_defined.add(table[i][1].binding)

            if ((isinstance(table[i][0], TextBlock)) and
                ((table[i][0].text.lower() == 'anglecoeff') or
                 (table[i][0].text.lower() == 'angle_coeff'))):
                if table[i][0].text != 'angle_coeff':
                    raise InputError('----------------------------------------------------\n' +
                                     '     Spelling error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Use \"angle_coeff\", not \"' + table[i][0].text + '\"\n' +
                                     '----------------------------------------------------\n' +
                                     g_no_check_msg)

                if ((len(table[i]) > 1) and
                        isinstance(table[i][1], TextBlock) and
                        (table[i][1].text == '*')):
                    pass  # we dealt with this case earlier
                elif (not ((len(table[i]) > 1) and
                           (isinstance(table[i][1], VarRef)) and
                           (table[i][1].prefix in ('@', '@{')) and
                           (table[i][1].nptr.cat_name == 'angle') and
                           (table[i][1].nptr.cat_node == root_node))):
                    raise InputError('----------------------------------------------------\n' +
                                     '     Syntax error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Invalid \"angle_coeff\" command.\n\n' +
                                     '  Each \"angle_coeff\" command should have the following syntax:\n\n' +
                                     '    angle_coeff @angle:type [optional style] list-of-parameters...\n' +
                                     '----------------------------------------------------\n\n' +
                                     g_no_check_msg)
                else:
                    angle_coeffs_defined.add(table[i][1].binding)

            if ((isinstance(table[i][0], TextBlock)) and
                ((table[i][0].text.lower() == 'dihedralcoeff') or
                 (table[i][0].text.lower() == 'dihedral_coeff'))):
                if table[i][0].text != 'dihedral_coeff':
                    raise InputError('----------------------------------------------------\n' +
                                     '     Spelling error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Use \"dihedral_coeff\", not \"' + table[i][0].text + '\"\n' +
                                     '----------------------------------------------------\n' +
                                     g_no_check_msg)

                if ((len(table[i]) > 1) and
                        isinstance(table[i][1], TextBlock) and
                        (table[i][1].text == '*')):
                    pass  # we dealt with this case earlier
                elif (not ((len(table[i]) > 1) and
                           (isinstance(table[i][1], VarRef)) and
                           (table[i][1].prefix in ('@', '@{')) and
                           (table[i][1].nptr.cat_name == 'dihedral') and
                           (table[i][1].nptr.cat_node == root_node))):
                    raise InputError('----------------------------------------------------\n' +
                                     '     Syntax error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Invalid \"dihedral_coeff\" command.\n\n' +
                                     '  Each \"dihedral_coeff\" command should have the following syntax:\n\n' +
                                     '    dihedral_coeff @dihedral:type [optional style] list-of-parameters...\n' +
                                     '----------------------------------------------------\n\n' +
                                     g_no_check_msg)
                else:
                    dihedral_coeffs_defined.add(table[i][1].binding)

            if ((isinstance(table[i][0], TextBlock)) and
                ((table[i][0].text.lower() == 'impropercoeff') or
                 (table[i][0].text.lower() == 'improper_coeff'))):
                if table[i][0].text != 'improper_coeff':
                    raise InputError('----------------------------------------------------\n' +
                                     '     Spelling error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Use \"improper_coeff\", not \"' + table[i][0].text + '\"\n' +
                                     '----------------------------------------------------\n' +
                                     g_no_check_msg)

                if ((len(table[i]) > 1) and
                        isinstance(table[i][1], TextBlock) and
                        (table[i][1].text == '*')):
                    pass  # we dealt with this case earlier
                elif (not ((len(table[i]) > 1) and
                           (isinstance(table[i][1], VarRef)) and
                           (table[i][1].prefix in ('@', '@{')) and
                           (table[i][1].nptr.cat_name == 'improper') and
                           (table[i][1].nptr.cat_node == root_node))):
                    raise InputError('----------------------------------------------------\n' +
                                     '     Syntax error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Invalid \"improper_coeff\" command.\n\n' +
                                     '  Each \"improper_coeff\" command should have the following syntax:\n\n' +
                                     '    improper_coeff @improper:type [optional style] list-of-parameters...\n' +
                                     '----------------------------------------------------\n\n' +
                                     g_no_check_msg)
                else:
                    improper_coeffs_defined.add(table[i][1].binding)

            elif ((isinstance(table[i][0], TextBlock)) and
                  ((table[i][0].text.lower() == 'paircoeff') or
                   (table[i][0].text.lower() == 'pair_coeff'))):
                if table[i][0].text != 'pair_coeff':
                    raise InputError('----------------------------------------------------\n' +
                                     '     Spelling error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Use \"pair_coeff\", not \"' + table[i][0].text + '\"\n' +
                                     '----------------------------------------------------\n' +
                                     g_no_check_msg)

                if len(table[i]) > 2:  # if not, deal with error later
                    if ((isinstance(table[i][1], TextBlock) and
                         (table[i][1].text == '*')) and         #'* *' <--BUG?
                        (isinstance(table[i][1], TextBlock) and #      <--BUG?
                         (table[i][1].text == '*'))):           #      <--BUG?
                        pair_coeffs_defined.add(('*', '*'))
                    else:
                        compound_wildcard = False
                        assert(len(table[i]) > 1)

                        if hasattr(table[i][1], '__len__'):
                            ltmpl = table[i][1]
                        else:
                            ltmpl = [table[i][1]]
                        for entry in ltmpl:
                            if (isinstance(entry, TextBlock) and
                                    ('*' in entry.text)):
                                compound_wildcard = True
                            elif (isinstance(entry, VarRef) and
                                  ('*' in entry.descr_str)):
                                compound_wildcard = True

                        if hasattr(table[i][2], '__len__'):
                            ltmpl = table[i][2]
                        else:
                            ltmpl = [table[i][2]]
                        for entry in ltmpl:
                            if (isinstance(entry, TextBlock) and
                                    ('*' in entry.text)):
                                compound_wildcard = True
                            elif (isinstance(entry, VarRef) and
                                  ('*' in entry.descr_str)):
                                compound_wildcard = True

                        if compound_wildcard and (not allow_wildcards):
                            raise InputError('---- Paranoid checking: ---\n'
                                             '     Possible error near ' +
                                             ErrorLeader(entry.srcloc.infile,
                                                         entry.srcloc.lineno) + '\n'
                                             'Wildcard symbols (\"*\", \"?\") are forbidden within \"pair_coeff\" commands.\n'
                                             'It is safer to specify the parameters for each bond type explicitly.\n'
                                             'To get past this error message, run moltemplate.sh with the \"-allow-wildcards\"\n'
                                             'argument.\n'
                                             '(Special case: If the \"*\" character is part of a LAMMPS-style \"*\" range\n'
                                             ' (eg \"@{atom:C1}*@{atom:C3}\", \"@{bond:B2}*@{bond:B5}\") then MAKE SURE that\n'
                                             ' the in-betwen types are assigned to connsecutively increasing integer values\n'
                                             ' by defining them in that order.  If that fails, use the \"-a\" argument\n'
                                             ' to assign these types to integers manually.)\n')

                if ((len(table[i]) > 2) and
                    ((isinstance(table[i][1], TextBlock) and
                      (table[i][1].text == '*')) or
                     (isinstance(table[i][2], TextBlock) and
                      (table[i][2].text == '*')))):
                    pass  # we dealt with this case earlier

                elif (not ((len(table[i]) > 2) and
                           (IsAtomRef(table[i][1]) or
                            IsAtomRefRange(table[i][1]))
                           and
                           (IsAtomRef(table[i][2]) or
                            IsAtomRefRange(table[i][2])))):

                    raise InputError('----------------------------------------------------\n' +
                                     '     Syntax error near ' +
                                     ErrorLeader(table[i][0].srcloc.infile,
                                                 table[i][0].srcloc.lineno) + '\n'
                                     '     Invalid \"pair_coeff\" command.\n\n' +
                                     '  Each \"pair_coeff\" command should have the following syntax:\n\n' +
                                     '    pair_coeff @atom:typeI @atom:typeJ [optional style] list-of-parameters...\n' +
                                     '----------------------------------------------------\n\n' +
                                     g_no_check_msg)
                else:
                    if g_omit_pair_coeff_checking:
                        sys.stderr.write('--------------------------------------------------------------\n'
                                         'WARNING: UNABLE TO CHECK "pair_coeff" COMMANDS.\n'
                                         '         (This happens if you use @{atom:a}*@{atom:b} syntax.)\n')
                        
                    else:
                        pair_coeffs_defined.add(
                            (table[i][1].binding, table[i][2].binding))


def LttreeCheckParseArgs(argv, settings, main=False, show_warnings=True):

    LttreeParseArgs(argv, settings, False, show_warnings)

    if main:
        # Instantiate the lexer we will be using.
        #  (The lexer's __init__() function requires an openned file.
        #   Assuming __name__ == "__main__", then the name of that file should
        #   be the last remaining (unprocessed) argument in the argument list.)
        if len(argv) == 1:
            raise InputError('Error: This program requires at least one argument\n'
                             '       the name of a file containing ttree template commands\n')
        settings.allow_wildcards = True
        i = 1
        while i < len(argv):
            if argv[i].lower() in ('-allow-wildcards', '-allowwildcards'):
                settings.allow_wildcards = True
                del argv[i:i+1]
            elif argv[i].lower() in ('-forbid-wildcards', '-forbidwildcards'):
                settings.allow_wildcards = False
                del argv[i:i+1]
            else:
                i += 1
            #(perhaps later I'll add some additional argumets)

        # The only argument left should be the system.lt file we want to read:
        if len(argv) == 2:
            try:
                # Parse text from the file named argv[1]
                settings.lex.infile = argv[1]  
                settings.lex.instream = open(argv[1], 'r')
            except IOError:
                sys.stderr.write('Error: unable to open file\n'
                                 '       \"' + argv[1] + '\"\n'
                                 '       for reading.\n')
                sys.exit(1)
        else:
            # if there are more than 2 remaining arguments,
            problem_args = ['\"' + arg + '\"' for arg in argv[1:]]
            raise InputError('Syntax Error(' + g_program_name + '):\n\n'
                             '       Unrecognized argument.\n'
                             '         (That or there is some other problem with the argument list.)\n'
                             '       The problem begins with these arguments:\n'
                             '         ' + (' '.join(problem_args)) + '\n\n'
                             '       (The actual problem may be earlier in the argument list.\n'
                             '       If these arguments are source files, then keep in mind\n'
                             '       that this program can not parse multiple source files.)\n'
                             '       Check the syntax of the entire argument list.\n')

    return




#######  control flow begins here: #######
def main():
    sys.stderr.write(g_program_name + ' v' +
                     g_version_str + ' ' + g_date_str + '\n')

    try:
        # Parse the argument list and instantiate the lexer we will be using:
        g_omit_pair_coeff_checking = False

        settings = LttreeSettings()
        LttreeCheckParseArgs([arg for arg in sys.argv], #(deep copy of sys.argv)
                             settings,
                             main=True,
                             show_warnings=True)

        # Invoke syntax checker pass:
        # This first check only checks for very simple mistakes
        # (mispelled versions of standard files or variable names).
        CheckSyntaxCheap(settings.lex)
        settings.lex.instream.close()
        # Now read the file again.
        # This time parse it using StaticObj.ReadTemplate().
        # (This will allow us to check for deeper problems.)
        # Parse text from the file named argv[1]
        #  Note: Assigning settings.lex directly does not work
        #        because it does not set the include path:
        #  settings.lex = TemplateLexer(open(settings.lex.infile, 'r'),
        #                               settings.lex.infile)  <--DONT USE!
        # Instead reinitialize settings.lex the same way we did earlier:
        # by using LttreeCheckParseArgs():
        settings = LttreeSettings()
        LttreeCheckParseArgs([arg for arg in sys.argv], #(deep copy of sys.argv)
                             settings,
                             main=True,
                             show_warnings=False)

        static_tree_root = StaticObj('', None) # The root of the static tree
                                               # has name '' (equivalent to '/')
        # has name '' (equivalent to '/')
        sys.stderr.write(g_program_name +
                         ':    parsing the class definitions...')
        static_tree_root.Parse(settings.lex)

        sys.stderr.write(' done\n' + g_program_name +
                         ':    looking up classes...')
        static_tree_root.LookupStaticRefs()
        sys.stderr.write(' done\n' + g_program_name +
                         ':    looking up @variables...')
        AssignStaticVarPtrs(static_tree_root,
                            search_instance_commands=False)
        replace_var_pairs = {}
        FindReplacementVarPairs(static_tree_root, replace_var_pairs)
        ReplaceVars(static_tree_root, replace_var_pairs,
                    search_instance_commands=False)
        AssignStaticVarPtrs(static_tree_root,
                            search_instance_commands=True)
        ReplaceVars(static_tree_root, replace_var_pairs,
                    search_instance_commands=True)
        sys.stderr.write(' done\n')
        #sys.stderr.write(' done\n\nclass_def_tree = ' + str(static_tree_root) + '\n\n')

        data_pair_coeffs_defined = set([])
        data_bond_coeffs_defined = set([])
        data_angle_coeffs_defined = set([])
        data_dihedral_coeffs_defined = set([])
        data_improper_coeffs_defined = set([])
        in_pair_coeffs_defined = set([])
        in_bond_coeffs_defined = set([])
        in_angle_coeffs_defined = set([])
        in_dihedral_coeffs_defined = set([])
        in_improper_coeffs_defined = set([])

        # Now check the static syntax
        #  Here we check the contents of the the "write_once()" commands:
        CheckSyntaxStatic(static_tree_root,
                          static_tree_root,
                          settings.column_names,
                          settings.allow_wildcards,
                          data_pair_coeffs_defined,
                          data_bond_coeffs_defined,
                          data_angle_coeffs_defined,
                          data_dihedral_coeffs_defined,
                          data_improper_coeffs_defined,
                          in_pair_coeffs_defined,
                          in_bond_coeffs_defined,
                          in_angle_coeffs_defined,
                          in_dihedral_coeffs_defined,
                          in_improper_coeffs_defined,
                          search_instance_commands=False)
        #  Here we check the contents of the the "write()" commands:
        CheckSyntaxStatic(static_tree_root,
                          static_tree_root,
                          settings.column_names,
                          settings.allow_wildcards,
                          data_pair_coeffs_defined,
                          data_bond_coeffs_defined,
                          data_angle_coeffs_defined,
                          data_dihedral_coeffs_defined,
                          data_improper_coeffs_defined,
                          in_pair_coeffs_defined,
                          in_bond_coeffs_defined,
                          in_angle_coeffs_defined,
                          in_dihedral_coeffs_defined,
                          in_improper_coeffs_defined,
                          search_instance_commands=True)

        if 'bond' in static_tree_root.categories:

            if ((len(data_bond_coeffs_defined) > 0) and
                    (len(in_bond_coeffs_defined) > 0)):
                raise InputError('---------------------------------------------------------------------\n' +
                                 '     Syntax error: You can EITHER use \"bond_coeff\" commands\n' +
                                 '                    OR you can have a \"Data Bond Coeffs\" section.\n' +
                                 '     LAMMPS will not allow both (...as of late 2012)\n' +
                                 '---------------------------------------------------------------------\n' +
                                 g_no_check_msg)
                #'     If this is no longer true, to override this error message you must\n'+
                #'     disable error checking by running moltemplate with the -nocheck option.\n')
            if len(data_bond_coeffs_defined) > 0:
                bond_coeffs_defined = data_bond_coeffs_defined
            else:
                bond_coeffs_defined = in_bond_coeffs_defined

            bond_types_have_wildcards = False
            bond_bindings = static_tree_root.categories['bond'].bindings
            for nd, bond_binding in bond_bindings.items():
                if not nd.IsDeleted():
                    has_wildcard = HasWildcard(bond_binding.full_name)
                    if has_wildcard:
                        bond_types_have_wildcards = True
            for nd, bond_binding in bond_bindings.items():
                if not nd.IsDeleted():
                    #has_wildcard = HasWildcard(bond_binding.full_name)
                    if ((not (bond_binding in bond_coeffs_defined)) and
                        #(not has_wildcard) and
                        (not bond_types_have_wildcards) and
                        (not ('*' in bond_coeffs_defined))):
                        raise InputError('---------------------------------------------------------------------\n' +
                                         '     Syntax error: Missing bond coeff.\n\n' +
                                         '  No coeffs for the \"' + bond_binding.full_name + '\" bond type have been\n' +
                                         'defined, but a reference to that bond type was discovered\n' +
                                         'near ' + ErrorLeader(bond_binding.refs[0].srcloc.infile,
                                                               bond_binding.refs[0].srcloc.lineno) + '.   Check this file and also check\n'
                                         'your \"bond_coeff\" commands or your \"Data Bond Coeffs" section.\n'
                                         '---------------------------------------------------------------------\n' +
                                         g_no_check_msg)

        if 'angle' in static_tree_root.categories:

            if ((len(data_angle_coeffs_defined) > 0) and
                (len(in_angle_coeffs_defined) > 0)):
                raise InputError('---------------------------------------------------------------------\n' +
                                 '     Syntax error: You can EITHER use \"angle_coeff\" commands\n' +
                                 '                    OR you can have a \"Data Angle Coeffs\" section.\n' +
                                 '     LAMMPS will not allow both (...as of late 2012)\n' +
                                 '---------------------------------------------------------------------\n' +
                                 g_no_check_msg)
                #'     If this is no longer true, to override this error message you must\n'+
                #'     disable error checking by running moltemplate with the -nocheck option.\n')
            if len(data_angle_coeffs_defined) > 0:
                angle_coeffs_defined = data_angle_coeffs_defined
            else:
                angle_coeffs_defined = in_angle_coeffs_defined

            angle_types_have_wildcards = False
            angle_bindings = static_tree_root.categories['angle'].bindings
            for nd, angle_binding in angle_bindings.items():
                if not nd.IsDeleted():
                    has_wildcard = HasWildcard(angle_binding.full_name)
                    if has_wildcard:
                        angle_types_have_wildcards = True
            for nd, angle_binding in angle_bindings.items():
                if not nd.IsDeleted():
                    #has_wildcard = HasWildcard(angle_binding.full_name)
                    if ((not (angle_binding in angle_coeffs_defined)) and
                        #(not has_wildcard)) and
                        (not angle_types_have_wildcards) and
                        (not ('*' in angle_coeffs_defined))):
                        raise InputError('---------------------------------------------------------------------\n' +
                                         '     Syntax error: Missing angle coeff.\n\n' +
                                         '  No coeffs for the \"' + angle_binding.full_name + '\" angle type have been\n' +
                                         'defined, but a reference to that angle type was discovered\n' +
                                         'near ' + ErrorLeader(angle_binding.refs[0].srcloc.infile,
                                                               angle_binding.refs[0].srcloc.lineno) + '.   Check this file and also check\n'
                                         'your \"angle_coeff\" commands or your \"Data Angle Coeffs" section.\n' +
                                         '---------------------------------------------------------------------\n' +
                                         g_no_check_msg)

        if 'dihedral' in static_tree_root.categories:
            #sys.stderr.write('dihedral_bindings = '+str(dihedral_bindings)+'\n')

            if ((len(data_dihedral_coeffs_defined) > 0) and
                (len(in_dihedral_coeffs_defined) > 0)):
                raise InputError('---------------------------------------------------------------------\n' +
                                 '     Syntax error: You can EITHER use \"dihedral_coeff\" commands\n' +
                                 '                    OR you can have a \"Data Dihedral Coeffs\" section.\n' +
                                 '     LAMMPS will not allow both (...as of late 2012)\n' +
                                 '---------------------------------------------------------------------\n' +
                                 g_no_check_msg)
                #'     If this is no longer true, to override this error message you must\n'+
                #'     disable error checking by running moltemplate with the -nocheck option.\n')
            if len(data_dihedral_coeffs_defined) > 0:
                dihedral_coeffs_defined = data_dihedral_coeffs_defined
            else:
                dihedral_coeffs_defined = in_dihedral_coeffs_defined

            dihedral_types_have_wildcards = False
            dihedral_bindings = static_tree_root.categories[
                'dihedral'].bindings
            for nd, dihedral_binding in dihedral_bindings.items():
                if not nd.IsDeleted():
                    has_wildcard = HasWildcard(dihedral_binding.full_name)
                    if has_wildcard:
                        dihedral_types_have_wildcards = True
            for nd, dihedral_binding in dihedral_bindings.items():
                if not nd.IsDeleted():
                    #has_wildcard = HasWildcard(dihedral_binding.full_name)
                    if ((not (dihedral_binding in dihedral_coeffs_defined)) and
                        #(not has_wildcard) and
                        (not dihedral_types_have_wildcards) and
                        (not ('*' in dihedral_coeffs_defined))):
                        raise InputError('---------------------------------------------------------------------\n' +
                                         '     Syntax error: Missing dihedral coeff.\n\n' +
                                         '  No coeffs for the \"' + dihedral_binding.full_name + '\" dihedral type have been\n' +
                                         'defined, but a reference to that dihedral type was discovered\n' +
                                         'near ' + ErrorLeader(dihedral_binding.refs[0].srcloc.infile,
                                                               dihedral_binding.refs[0].srcloc.lineno) + '.   Check this file and also check\n'
                                         'your \"dihedral_coeff\" commands or your \"Data Dihedral Coeffs" section.\n' +
                                         '---------------------------------------------------------------------\n' +
                                         g_no_check_msg)

        if 'improper' in static_tree_root.categories:

            if ((len(data_improper_coeffs_defined) > 0) and
                    (len(in_improper_coeffs_defined) > 0)):
                raise InputError('---------------------------------------------------------------------\n' +
                                 '     Syntax error: You can EITHER use \"improper_coeff\" commands\n' +
                                 '                    OR you can have a \"Data Improper Coeffs\" section.\n' +
                                 '     LAMMPS will not allow both (...as of late 2012)\n' +
                                 '---------------------------------------------------------------------\n' +
                                 g_no_check_msg)
                #'     If this is no longer true, to override this error message you must\n'+
                #'     disable error checking by running moltemplate with the -nocheck option.\n')
            if len(data_improper_coeffs_defined) > 0:
                improper_coeffs_defined = data_improper_coeffs_defined
            else:
                improper_coeffs_defined = in_improper_coeffs_defined

            improper_types_have_wildcards = False
            improper_bindings = static_tree_root.categories[
                'improper'].bindings
            for nd, improper_binding in improper_bindings.items():
                if not nd.IsDeleted():
                    has_wildcard = HasWildcard(improper_binding.full_name)
                    if has_wildcard:
                        improper_types_have_wildcards = True
            for nd, improper_binding in improper_bindings.items():
                if not nd.IsDeleted():
                    #has_wildcard = HasWildcard(improper_binding.full_name)
                    if ((not (improper_binding in improper_coeffs_defined)) and
                        #(not has_wildcard) and
                        (not improper_types_have_wildcards) and
                        (not ('*' in improper_coeffs_defined))):
                        raise InputError('---------------------------------------------------------------------\n' +
                                         '     Syntax error: Missing improper coeff.\n\n' +
                                         '  No coeffs for the \"' + improper_binding.full_name + '\" improper type have been\n' +
                                         'defined, but a reference to that improper type was discovered\n' +
                                         'near ' + ErrorLeader(improper_binding.refs[0].srcloc.infile,
                                                               improper_binding.refs[0].srcloc.lineno) + '.   Check this file and also check\n'
                                         'your \"improper_coeff\" commands or your \"Data Improper Coeffs" section.\n' +
                                         '---------------------------------------------------------------------\n' +
                                         g_no_check_msg)

        if 'atom' in static_tree_root.categories:

            if ((len(data_pair_coeffs_defined) > 0) and
                (len(in_pair_coeffs_defined) > 0)):
                raise InputError('---------------------------------------------------------------------\n' +
                                 '     Syntax error: You can EITHER use \"pair_coeff\" commands\n' +
                                 '                    OR you can have a \"Data Pair Coeffs\" section.\n' +
                                 '     LAMMPS will not allow both (...as of late 2012)\n' +
                                 '---------------------------------------------------------------------\n' +
                                 g_no_check_msg)
                #'     If this is no longer true, to override this error message you must\n'+
                #'     disable error checking by running moltemplate with the -nocheck option.\n')

            if len(data_pair_coeffs_defined) > 0:
                pair_coeffs_defined = data_pair_coeffs_defined
            else:
                pair_coeffs_defined = in_pair_coeffs_defined

            atom_types_have_wildcards = False
            atom_bindings = static_tree_root.categories['atom'].bindings
            for nd, atom_binding in atom_bindings.items():
                if not nd.IsDeleted():
                    has_wildcard = HasWildcard(atom_binding.full_name)
                    if has_wildcard:
                        atom_types_have_wildcards = True
            for nd, atom_binding in atom_bindings.items():
                if not nd.IsDeleted():
                    #has_wildcard = HasWildcard(atom_binding.full_name)
                    if ((not ((atom_binding, atom_binding)
                              in
                              pair_coeffs_defined)) and
                        #(not has_wildcard) and
                        (not atom_types_have_wildcards) and
                        (not (('*', '*') in pair_coeffs_defined)) and
                        (not (atom_binding.nptr.cat_name,
                              atom_binding.nptr.cat_node,
                              atom_binding.nptr.leaf_node)
                         in replace_var_pairs) and
                        (not g_omit_pair_coeff_checking)):

                        raise InputError('---------------------------------------------------------------------\n' +
                                         '     Syntax error: Missing pair coeff.\n\n' +
                                         '  No pair coeffs for the \"' + atom_binding.full_name + '\" atom type have been\n' +
                                         'defined, but a reference to that atom type was discovered\n' +
                                         'near ' + ErrorLeader(atom_binding.refs[0].srcloc.infile,
                                                               atom_binding.refs[0].srcloc.lineno) + '.   Check this file and\n'
                                         'also check your \"pair_coeff\" commands or your \"Data Pair Coeffs" section.\n\n' +
                                         g_no_check_msg)
        # else:
        #    raise InputError('Error: No atom types (@atom) have been defined.\n')

        sys.stderr.write(g_program_name + ': -- No errors detected. --\n')
        exit(0)

    except (ValueError, InputError) as err:
        sys.stderr.write('\n' + str(err) + '\n')
        sys.exit(1)

    return


if __name__ == '__main__':
    main()
