def substitute(s, objlist=(), globals={}, locals={}):
    if not locals:
        locals = getlocals(3)
    backslash = False
    dollar = False
    nparen = 0
    name = ''
    evalstr = ''
    squote = False
    dquote = False
    out = ''
    for tmp in s:
        if backslash:
            out += tmp
            backslash = False
            continue
        if dollar and nparen == 0:
            if tmp == '_' or ('a' <= tmp <= 'z') or ('A' <= tmp <= 'Z'):
                name += tmp
                continue
            if '0' <= tmp <= '9' and name != '':
                name += tmp
                continue
            if tmp == '(' and name == '':
                nparen = 1
                evalstr = ''
                continue
            out += substitutename(name, objlist, globals, locals)
            dollar = False
        if tmp == '"' and not squote:
            dquote = not dquote
        elif tmp == "'" and not dquote:
            squote = not squote
        if not dquote and not squote:
            if nparen > 0:
                if tmp == '(':
                    nparen += 1
                elif tmp == ')':
                    nparen -= 1
                    if nparen == 0:
                        out += substituteexpr(evalstr, globals, locals)
                        dollar = False
                evalstr += tmp
                continue
            if tmp == '$':
                dollar = True
                name = ''
                continue
        if nparen == 0:
            out += tmp
        else:
            evalstr += tmp
        if tmp == '\\':
            backslash = True
    if dollar:
        out += substitutename(name, objlist, globals, locals)
    else:
        if nparen > 0:
            out += '$(' + evalstr
    return out