text:
using Crayons
using Terming
const T = Terming
using DisplayStructure
const DS = DisplayStructure

display =
    "######                                      \n" *
    "#     # #  ####  #####  #        ##   #   # \n" *
    "#     # # #      #    # #       #  #   # #  \n" *
    "#     # #  ####  #    # #      #    #   #   \n" *
    "#     # #      # #####  #      ######   #   \n" *
    "#     # # #    # #      #      #    #   #   \n" *
    "######  #  ####  #      ###### #    #   #    "

structure =
    "\t   #####                                                        \n" *
    "\t  #     # ##### #####  #    #  ####  ##### #    # #####  ###### \n" *
    "\t  #         #   #    # #    # #    #   #   #    # #    # #      \n" *
    "\t   #####    #   #    # #    # #        #   #    # #    # #####  \n" *
    "\t        #   #   #####  #    # #        #   #    # #####  #      \n" *
    "\t  #     #   #   #   #  #    # #    #   #   #    # #   #  #      \n" *
    "\t   #####    #   #    #  ####   ####    #    ####  #    # ######  "

function paint()
    h, w = size = (20, 75)
    T.displaysize(h, w)

    area = DS.DisplayArray(DS.Rectangle(h, w))

    logo = DS.DisplayArray(DS.Rectangle(4, 8))
    logo[2, 4] = Char(0x2B24)
    logo[3, 3] = Char(0x2B24)
    logo[3, 5] = Char(0x2B24)

    display_block = DS.DisplayArray(DS.Rectangle(7, 45))
    display_block[1:end, 1:end] = display

    structure_block = DS.DisplayArray(DS.Rectangle(7, 65))
    structure_block[1:end, 1:end] = structure

    T.buffered() do buffer
        T.cshow(false); T.clear()

        T.print(buffer, Crayon(foreground=(93, 173, 226)))
        DS.render(buffer, area, pos=(1, 1))

        T.print(buffer, Crayon(foreground=(149, 88, 178)))
        DS.render(buffer, logo, pos=(3, 64))

        T.print(buffer, Crayon(foreground=(205, 92, 92), bold=true))
        DS.render(buffer, display_block, pos=(3, 5))

        T.print(buffer, Crayon(foreground=(82, 190, 128)))
        DS.render(buffer, structure_block, pos=(12, 5))

        T.print(buffer, Crayon(reset = true))
    end

    readline()
    T.cmove_line_last()
    T.cshow(true)
    return
end

paint()


text:
module DINA

export get_dina, dina_years, aggregate_quantiles, dina_quantile_panel

using CategoricalArrays: cut, recode, levels
using Chain: @chain
using DataDeps: register, DataDep, @datadep_str, unpack
using DataFrames: DataFrames, DataFrame, disallowmissing!,
    groupby, combine, transform!, ByRow
using LinearAlgebra: dot
using ReadableRegex: look_for, one_or_more, DIGIT
using StatFiles: StatFiles, load
using StatsBase: wquantile
using TableOperations
using Statistics: mean

function __init__()
    register(DataDep(
        "USDINA",
        """
        Dataset: Distributional National Accounts
        Authors: Piketty, Saez, Zucan
        Website: http://gabriel-zucman.eu/usdina/

        Use data under terms specified on the website. The details are described in Piketty, Saez & Zucman (2018).
        Please include this citation if you plan to use this database:
            Piketty, T., E. Saez & G. Zucman:
            Distributional National Accounts: Methods and Estimates for the United States
            Quarterly Journal of Economics, 2018, 133 (2): 553-609.
        """,
        "https://eml.berkeley.edu/~saez/PSZ2020Dinafiles.zip",
        "1115e8bd58b5ed073670ee31a3fffd9f968e85b5bc365a81035a3fd679cdcc86",
        post_fetch_method=unpack
    ))
end

function dina_years()
	files_in_data_dir = readdir(@datadep_str("USDINA"))
	dta_files = filter(endswith(".dta"), files_in_data_dir)

	r = look_for(one_or_more(DIGIT))

	map(dta_files) do f
		year_string = match(r, f).match
		parse(Int, year_string)
	end
end

function get_dina(year)
    file = "USDINA/usdina$(year).dta"
    load(@datadep_str(file))
end

function dina_quantile_panel(var, byvar, ngroups, years = dina_years();
                             wgt = :dweght,
                             equalsplit = true)
    mapreduce(vcat, years) do yr
        aggregate_quantiles(var, byvar, ngroups, yr; wgt, by_taxunit = equalsplit)
    end
end

function aggregate_quantiles(var, byvar, ngroups, year; wgt, by_taxunit)
	tbl0 = get_dina(year)

	ids = :id
	grp = [:age]

    cols0 = unique([wgt; var; byvar])
	cols = unique([ids; grp; cols0])

	tbl = TableOperations.select(tbl0, cols...)

	df = DataFrame(tbl) |> disallowmissing!

    filter!(ids => >(0), df)

    if by_taxunit
        transform!(
            groupby(df, :id),
            cols0 .=> sum,
            renamecols = false
        )
    end

    q = 0:1/ngroups:1

    # Cut byvar into groups (e.g. income into deciles)
    groups = cut(df[!,byvar], wquantile(df[!,byvar], df[!,wgt], q), extend=true, labels = format)
    df.group_id = parse.(Int, string.(groups))

    # Aggregate by group
    agg_df = combine(
        groupby(df, [:group_id, :age]),
        ([v, wgt] => ((x,w) -> dot(x,w)./sum(w)) => v for v in var)..., wgt => sum => wgt
        )

    agg_df[!,:year] .= year

    # Other group indentiers
    df.group = recode(groups, (g_id => "group " * g_id for g_id in levels(groups))...)

    if ngroups == 10
        transform!(agg_df,
            :group_id => ByRow(x -> ifelse(x <= 5, "bottom 50", ifelse(x <= 9, "middle 40", "top 10"))) => :three_groups)
    end

	agg_df
end

format(from, to, i; kwargs...) = "$i"

end

ext: jl


text:
"""
closer(ps::ParseState)

A magical function determining whether the parsing of an expression should continue or
stop.
"""
function closer(ps::ParseState)
    kindof(ps.nt) === Tokens.ENDMARKER ||
    (ps.closer.newline && kindof(ps.ws) == NewLineWS && !iscomma(ps.t)) ||
    (ps.closer.semicolon && kindof(ps.ws) == SemiColonWS) ||
    (isoperator(ps.nt) && precedence(ps.nt) <= ps.closer.precedence) ||
    (kindof(ps.nt) === Tokens.WHERE && ps.closer.precedence == LazyAndOp) ||
    (ps.closer.inwhere && kindof(ps.nt) === Tokens.WHERE) ||
    (ps.closer.inwhere && ps.closer.ws && kindof(ps.t) === Tokens.RPAREN && isoperator(ps.nt) && precedence(ps.nt) < DeclarationOp) ||
    (ps.closer.precedence > WhereOp && (
        (kindof(ps.nt) === Tokens.LPAREN && !(ps.t.kind === Tokens.EX_OR)) ||
        kindof(ps.nt) === Tokens.LBRACE ||
        kindof(ps.nt) === Tokens.LSQUARE ||
        (kindof(ps.nt) === Tokens.STRING && isemptyws(ps.ws)) ||
        ((kindof(ps.nt) === Tokens.RPAREN || kindof(ps.nt) === Tokens.RSQUARE) && isidentifier(ps.nt))
    )) ||
    (iscomma(ps.nt) && ps.closer.precedence > 0) ||
    (ps.closer.comma && iscomma(ps.nt)) ||
    (ps.closer.tuple && (iscomma(ps.nt) || isassignment(ps.nt))) ||
    (kindof(ps.nt) === Tokens.FOR && ps.closer.precedence > -1) ||
    (ps.closer.block && kindof(ps.nt) === Tokens.END) ||
    (ps.closer.paren && kindof(ps.nt) === Tokens.RPAREN) ||
    (ps.closer.brace && kindof(ps.nt) === Tokens.RBRACE) ||
    (ps.closer.square && kindof(ps.nt) === Tokens.RSQUARE) ||
    (@static VERSION < v"1.4" ? false : ((ps.closer.insquare || ps.closer.inmacro) && kindof(ps.nt) === Tokens.APPROX && kindof(ps.nws) == EmptyWS)) ||
    kindof(ps.nt) === Tokens.ELSEIF ||
    kindof(ps.nt) === Tokens.ELSE ||
    kindof(ps.nt) === Tokens.CATCH ||
    kindof(ps.nt) === Tokens.FINALLY ||
    (ps.closer.ifop && isoperator(ps.nt) && (precedence(ps.nt) <= 0 || kindof(ps.nt) === Tokens.COLON)) ||
    (ps.closer.range && (kindof(ps.nt) === Tokens.FOR || iscomma(ps.nt) || kindof(ps.nt) === Tokens.IF)) ||
    (ps.closer.ws && !isemptyws(ps.ws) &&
        !iscomma(ps.nt) &&
        !iscomma(ps.t) &&
        !(!ps.closer.inmacro && kindof(ps.nt) === Tokens.FOR) &&
        !(kindof(ps.nt) === Tokens.DO) &&
        !(
            (isbinaryop(ps.nt) && !(ps.closer.wsop && isemptyws(ps.nws) && isunaryop(ps.nt) && precedence(ps.nt) > 7)) ||
            (isunaryop(ps.t) && kindof(ps.ws) == WS && ps.lt.kind !== CSTParser.Tokens.COLON)
        )) ||
    (ps.closer.unary && (kindof(ps.t) in (Tokens.INTEGER, Tokens.FLOAT, Tokens.RPAREN, Tokens.RSQUARE, Tokens.RBRACE) && isidentifier(ps.nt)))
end

"""
    @closer ps rule body

Continues parsing closing on `rule`.
"""
macro closer(ps, opt, body)
    quote
        local tmp1 = getfield($(esc(ps)).closer, $opt)
        setfield!($(esc(ps)).closer, $opt, true)
        out = $(esc(body))
        setfield!($(esc(ps)).closer, $opt, tmp1)
        out
    end
end

"""
    @nocloser ps rule body

Continues parsing not closing on `rule`.
"""
macro nocloser(ps, opt, body)
    quote
        local tmp1 = getfield($(esc(ps)).closer, $opt)
        setfield!($(esc(ps)).closer, $opt, false)
        out = $(esc(body))
        setfield!($(esc(ps)).closer, $opt, tmp1)
        out
    end
end

macro closeparen(ps, body)
    quote
        local tmp1 = $(esc(ps)).closer.paren
        $(esc(ps)).closer.paren = true
        out = $(esc(body))
        $(esc(ps)).closer.paren = tmp1
        out
    end
end

macro closesquare(ps, body)
    quote
        local tmp1 = $(esc(ps)).closer.square
        $(esc(ps)).closer.square = true
        out = $(esc(body))
        $(esc(ps)).closer.square = tmp1
        out
    end
end
macro closebrace(ps, body)
    quote
        local tmp1 = $(esc(ps)).closer.brace
        $(esc(ps)).closer.brace = true
        out = $(esc(body))
        $(esc(ps)).closer.brace = tmp1
        out
    end
end

"""
    @precedence ps prec body

Continues parsing binary operators until it hits a more loosely binding
operator (with precdence lower than `prec`).
"""
macro precedence(ps, prec, body)
    quote
        local tmp1 = $(esc(ps)).closer.precedence
        $(esc(ps)).closer.precedence = $(esc(prec))
        out = $(esc(body))
        $(esc(ps)).closer.precedence = tmp1
        out
    end
end

# Closer_TMP and ancillary functions help reduce code generation
struct Closer_TMP
    newline::Bool
    semicolon::Bool
    inmacro::Bool
    tuple::Bool
    comma::Bool
    insquare::Bool
    range::Bool
    ifop::Bool
    ws::Bool
    wsop::Bool
    unary::Bool
    precedence::Int
end

@noinline function create_tmp(c::Closer)
    Closer_TMP(c.newline,
        c.semicolon,
        c.inmacro,
        c.tuple,
        c.comma,
        c.insquare,
        c.range,
        c.ifop,
        c.ws,
        c.wsop,
        c.unary,
        c.precedence)
end

@noinline function update_from_tmp!(c::Closer, tmp::Closer_TMP)
    c.newline = tmp.newline
    c.semicolon = tmp.semicolon
    c.inmacro = tmp.inmacro
    c.tuple = tmp.tuple
    c.comma = tmp.comma
    c.insquare = tmp.insquare
    c.range = tmp.range
    c.ifop = tmp.ifop
    c.ws = tmp.ws
    c.wsop = tmp.wsop
    c.unary = tmp.unary
    c.precedence = tmp.precedence
end

@noinline function update_to_default!(c::Closer)
    c.newline = true
    c.semicolon = true
    c.inmacro = false
    c.tuple = false
    c.comma = false
    c.insquare = false
    c.range = false
    c.ifop = false
    c.ws = false
    c.wsop = false
    c.unary = false
    c.precedence = -1
end

"""
    @default ps body

Parses the next expression using default closure rules.
"""
macro default(ps, body)
    quote
        TMP = create_tmp($(esc(ps)).closer)
        update_to_default!($(esc(ps)).closer)
        out = $(esc(body))
        update_from_tmp!($(esc(ps)).closer, TMP)
        out
    end
end

isidentifier(x::EXPR) = typof(x) === IDENTIFIER || typof(x) === NONSTDIDENTIFIER

isunarycall(x::EXPR) = typof(x) === UnaryOpCall
isbinarycall(x::EXPR) = typof(x) === BinaryOpCall
iswherecall(x::EXPR) = typof(x) === WhereOpCall
isdeclaration(x::EXPR) = isbinarycall(x) && is_decl(x[2])
isinterpolant(x::EXPR) = isunarycall(x) && is_exor(x[1])
istuple(x::EXPR) = typof(x) === TupleH
is_either_id_op_interp(x::EXPR) = isidentifier(x) || isoperator(x) || isinterpolant(x)
is_splat(x::EXPR) = isunarycall(x) && is_dddot(x[2])

isliteral(x::EXPR) = typof(x) === LITERAL
iskw(x::EXPR) = typof(x) === KEYWORD # TODO: should change to `iskeyword`
ispunctuation(x::EXPR) = typof(x) === PUNCTUATION

isstring(x) = typof(x) === StringH || (isliteral(x) && (kindof(x) === Tokens.STRING || kindof(x) === Tokens.TRIPLE_STRING))
is_integer(x) = isliteral(x) && kindof(x) === Tokens.INTEGER
is_float(x) = isliteral(x) && kindof(x) === Tokens.FLOAT
is_number(x) = isliteral(x) && (kindof(x) === Tokens.INTEGER || kindof(x) === Tokens.FLOAT)
is_nothing(x) = isliteral(x) && kindof(x) === Tokens.NOTHING

isajuxtaposition(ps::ParseState, ret::EXPR) = ((is_number(ret) && (isidentifier(ps.nt) || kindof(ps.nt) === Tokens.LPAREN || kindof(ps.nt) === Tokens.CMD || kindof(ps.nt) === Tokens.STRING || kindof(ps.nt) === Tokens.TRIPLE_STRING)) ||
        ((typof(ret) === UnaryOpCall && is_prime(ret.args[2]) && isidentifier(ps.nt)) ||
        ((kindof(ps.t) === Tokens.RPAREN || kindof(ps.t) === Tokens.RSQUARE) && (isidentifier(ps.nt) || kindof(ps.nt) === Tokens.CMD)) ||
        ((kindof(ps.t) === Tokens.STRING || kindof(ps.t) === Tokens.TRIPLE_STRING) && (kindof(ps.nt) === Tokens.STRING || kindof(ps.nt) === Tokens.TRIPLE_STRING)))) || ((kindof(ps.t) in (Tokens.INTEGER, Tokens.FLOAT) || kindof(ps.t) in (Tokens.RPAREN, Tokens.RSQUARE, Tokens.RBRACE)) && isidentifier(ps.nt))

"""
    has_error(ps::ParseState)
    has_error(x::EXPR)

Determine whether a parsing error occured while processing text with the given
`ParseState`, or exists as a (sub) expression of `x`.
"""
function has_error(x::EXPR)
    return typof(x) == ErrorToken || (x.args !== nothing && any(has_error, x.args))
end
has_error(ps::ParseState) = ps.errored

# When using the FancyDiagnostics package, Meta.parse is the
# same as CSTParser.parse. Manually call the flisp parser here
# to make sure we test what we want, even when people load the
# FancyDiagnostics package.
function flisp_parse(str::AbstractString, pos::Int; greedy::Bool=true, raise::Bool=true)
    if VERSION < v"1.6-DEV"
        bstr = String(str)
        ex, pos = ccall(:jl_parse_string, Any,
                        (Ptr{UInt8}, Csize_t, Int32, Int32),
                        bstr, sizeof(bstr), pos - 1, greedy ? 1 : 0)
    else
        filename = "none"
        rule = greedy ? :statement : :atom
        ex, pos = Core.Compiler.fl_parse(str, filename, pos - 1, rule)
    end
    if raise && isa(ex, Expr) && ex.head === :error
        throw(Meta.ParseError(ex.args[1]))
    end
    if ex === ()
        raise && throw(Meta.ParseError("end of input"))
        ex = Expr(:error, "end of input")
    end
    # pos is zero-based byte offset
    return ex, pos + 1
end

function flisp_parse(str::AbstractString; raise::Bool=true)
    ex, pos = flisp_parse(str, 1, greedy=true, raise=raise)
    if isa(ex, Expr) && ex.head === :error
        return ex
    end
    if !(pos > ncodeunits(str))
        raise && throw(Meta.ParseError("extra token after end of expression"))
        return Expr(:error, "extra token after end of expression")
    end
    return ex
end

function flisp_parse(stream::IO; greedy::Bool=true, raise::Bool=true)
    pos = position(stream)
    ex, Δ = flisp_parse(read(stream, String), 1, greedy=greedy, raise=raise)
    seek(stream, pos + Δ - 1)
    return ex
end

using Base.Meta

function norm_ast(a::Any)
    if isa(a, Expr)
        for (i, arg) in enumerate(a.args)
            a.args[i] = norm_ast(arg)
        end
        if a.head === :line
            return Expr(:line, a.args[1], :none)
        end
        if a.head === :macrocall
            fa = a.args[1]
            if fa === Symbol("@int128_str")
                return Base.parse(Int128, a.args[3])
            elseif fa === Symbol("@uint128_str")
                return Base.parse(UInt128, a.args[3])
            elseif fa === Symbol("@bigint_str")
                return  Base.parse(BigInt, a.args[3])
            elseif fa == Symbol("@big_str")
                s = a.args[3]
                n = tryparse(BigInt, s)
                if !(n === nothing)
                    return (n)
                end
                n = tryparse(BigFloat, s)
                if !(n === nothing)
                    return isnan((n)) ? :NaN : (n)
                end
                return s
            end
        elseif length(a.args) >= 2 && isexpr(a, :call) && a.args[1] == :- && isa(a.args[2], Number)
            return -a.args[2]
        end
        return a
    elseif isa(a, QuoteNode)
        return Expr(:quote, norm_ast(a.value))
    elseif isa(a, AbstractFloat) && isnan(a)
        return :NaN
    end
    return a
end

function flisp_parsefile(str, display=true)
    io = IOBuffer(str)
    failed = false
    x1 = Expr(:file)
    try
        while !eof(io)
            push!(x1.args, flisp_parse(io))
        end
    catch er
        isa(er, InterruptException) && rethrow(er)
        if display
            Base.showerror(stdout, er, catch_backtrace())
            println()
        end
        return x1, true
    end
    if length(x1.args) > 0  && x1.args[end] === nothing
        pop!(x1.args)
    end
    x1 = norm_ast(x1)
    remlineinfo!(x1)
    return x1, false
end

function cst_parsefile(str)
    x, ps = CSTParser.parse(ParseState(str), true)
    sp = check_span(x)
    # remove leading/trailing nothings
    if length(x.args) > 0 && is_nothing(x.args[1])
        popfirst!(x.args)
    end
    if length(x.args) > 0 && is_nothing(x.args[end])
        pop!(x.args)
    end
    x0 = norm_ast(Expr(x))
    x0, has_error(ps), sp
end

function check_file(file, ret, neq)
    str = read(file, String)
    x0, cstfailed, sp = cst_parsefile(str)
    x1, flispfailed = flisp_parsefile(str)

    print("\r                             ")
    if !isempty(sp)
        printstyled(file, color=:blue)
        @show sp
        println()
        push!(ret, (file, :span))
    end
    if cstfailed
        printstyled(file, color=:yellow)
        println()
        push!(ret, (file, :errored))
    elseif !(x0 == x1)
        cumfail = 0
        printstyled(file, color=:green)
        println()
        c0, c1 = CSTParser.compare(x0, x1)
        printstyled(string("    ", c0), bold=true, color=:ligth_red)
        println()
        printstyled(string("    ", c1), bold=true, color=:light_green)
        println()
        push!(ret, (file, :noteq))
    end
end

function check_base(dir=dirname(Base.find_source_file("essentials.jl")), display=false)
    N = 0
    neq = 0
    err = 0
    aerr = 0
    fail = 0
    bfail = 0
    ret = []
    oldstderr = stderr
    redirect_stderr()
    for (rp, d, files) in walkdir(dir)
        for f in files
            file = joinpath(rp, f)
            if endswith(file, ".jl")
                N += 1
                try
                    print("\r", rpad(string(N), 5), rpad(string(round(fail / N * 100, sigdigits=3)), 8), rpad(string(round(err / N * 100, sigdigits=3)), 8), rpad(string(round(neq / N * 100, sigdigits=3)), 8))

                    check_file(file, ret, neq)
                catch er
                    isa(er, InterruptException) && rethrow(er)
                    if display
                        Base.showerror(stdout, er, catch_backtrace())
                        println()
                    end
                    fail += 1
                    printstyled(file, color=:red)
                    println()
                    push!(ret, (file, :failed))
                end
            end
        end
    end
    redirect_stderr(oldstderr)
    if bfail + fail + err + neq > 0
        println("\r$N files")
        printstyled("failed", color=:red)
        println(" : $fail    $(100 * fail / N)%")
        printstyled("errored", color=:yellow)
        println(" : $err     $(100 * err / N)%")
        printstyled("not eq.", color=:green)
        println(" : $neq    $(100 * neq / N)%", "  -  $aerr     $(100 * aerr / N)%")
        printstyled("base failed", color=:magenta)
        println(" : $bfail    $(100 * bfail / N)%")
        println()
    else
        println("\r")
    end
    ret
end

"""
    compare(x,y)

Recursively checks whether two Base.Expr are the same. Returns unequal sub-
expressions.
"""
compare(x, y) = x == y ? true : (x, y)

function compare(x::Expr, y::Expr)
    if x == y
        return true
    else
        if x.head != y.head
            return (x, y)
        end
        if length(x.args) != length(y.args)
            return (x.args, y.args)
        end
        for i = 1:length(x.args)
            t = compare(x.args[i], y.args[i])
            if t != true
                return t
            end
        end
    end
end

"""
check_span(x, neq = [])

Recursively checks whether the span of an expression equals the sum of the span
of its components. Returns a vector of failing expressions.
"""
function check_span(x::EXPR, neq=[])
    (ispunctuation(x) || isidentifier(x) || iskw(x) || isoperator(x) || isliteral(x) || typof(x) == StringH) && return neq

    s = 0
    for a in x.args
        check_span(a, neq)
        s += a.fullspan
    end
    if length(x.args) > 0 && s != x.fullspan
        push!(neq, x)
    end
    neq
end

function speed_test()
    dir = dirname(Base.find_source_file("essentials.jl"))
    println("speed test : ", @timed(for i = 1:5
        parse(read(joinpath(dir, "essentials.jl"), String), true);
        parse(read(joinpath(dir, "abstractarray.jl"), String), true);
    end)[2])
end

"""
    str_value(x)

Attempt to get a string representation of a nodeless expression.
"""
function str_value(x)
    if typof(x) === IDENTIFIER || typof(x) === LITERAL
        return valof(x)
    elseif isidentifier(x)
        valof(x.args[2])
    elseif typof(x) === OPERATOR || typof(x) === MacroName
        return string(Expr(x))
    else
        return ""
    end
end

_unescape_string(s::AbstractString) = sprint(_unescape_string, s, sizehint=lastindex(s))
function _unescape_string(io, s::AbstractString)
    a = Iterators.Stateful(s)
    for c in a
        if !isempty(a) && c == '\\'
            c = popfirst!(a)
            if c == 'x' || c == 'u' || c == 'U'
                n = k = 0
                m = c == 'x' ? 2 :
                    c == 'u' ? 4 : 8
                while (k += 1) <= m && !isempty(a)
                    nc = Base.peek(a)
                    n = '0' <= nc <= '9' ? n << 4 + nc - '0' :
                        'a' <= nc <= 'f' ? n << 4 + nc - 'a' + 10 :
                        'A' <= nc <= 'F' ? n << 4 + nc - 'A' + 10 : break
                    popfirst!(a)
                end
                if k == 1
                    # throw(ArgumentError("invalid $(m == 2 ? "hex (\\x)" :
                    #                         "unicode (\\u)") escape sequence used in $(repr(s))"))
                    # push error to ParseState?
                    n = 0
                end
                if m == 2 # \x escape sequence
                    write(io, UInt8(n))
                else
                    print(io, Char(n))
                end
            elseif '0' <= c <= '7'
                k = 1
                n = c - '0'
                while (k += 1) <= 3 && !isempty(a)
                    c  = Base.peek(a)
                    n = ('0' <= c <= '7') ? n << 3 + c - '0' : break
                    popfirst!(a)
                end
                if n > 255
                    # throw(ArgumentError("octal escape sequence out of range"))
                    # push error to ParseState?
                    n = 255
                end
                write(io, UInt8(n))
            else
                print(io, c == 'a' ? '\a' :
                          c == 'b' ? '\b' :
                          c == 't' ? '\t' :
                          c == 'n' ? '\n' :
                          c == 'v' ? '\v' :
                          c == 'f' ? '\f' :
                          c == 'r' ? '\r' :
                          c == 'e' ? '\e' : c)
            end
        else
            print(io, c)
        end
    end
end

function valid_escaped_seq(s::AbstractString)
    a = Iterators.Stateful(s)
    for c in a
        if !isempty(a) && c == '\\'
            c = popfirst!(a)
            if c == 'x' || c == 'u' || c == 'U'
                n = k = 0
                m = c == 'x' ? 2 :
                    c == 'u' ? 4 : 8
                while (k += 1) <= m && !isempty(a)
                    nc = Base.peek(a)
                    n = '0' <= nc <= '9' ? n << 4 + (nc - '0') :
                        'a' <= nc <= 'f' ? n << 4 + (nc - 'a' + 10) :
                        'A' <= nc <= 'F' ? n << 4 + (nc - 'A' + 10) : break
                    popfirst!(a)
                end
                if k == 1 || n > 0x10ffff
                    u = m == 4 ? 'u' : 'U'
                    return false
                end
            elseif '0' <= c <= '7'
                k = 1
                n = c - '0'
                while (k += 1) <= 3 && !isempty(a)
                    c = Base.peek(a)
                    n = ('0' <= c <= '7') ? n << 3 + c - '0' : break
                    popfirst!(a)
                end
                if n > 255
                    return false
                end
            else
                c == 'a' ||
                c == 'b' ||
                c == 't' ||
                c == 'n' ||
                c == 'v' ||
                c == 'f' ||
                c == 'r' ||
                c == 'e' ||
                c == '\\' ||
                c == '"' ||
                c == '\'' ||
                return false
            end
        end
    end
    return true
end

"""
    is_getfield(x::EXPR)

Is this an expression of the form `a.b`.
"""
is_getfield(x::EXPR) = isbinarycall(x) && length(x) == 3 && kindof(x[2]) === Tokens.DOT

"""
    disallowednumberjuxt(ret::EXPR)

Does this number literal end in a decimal and so cannot precede a paren for
implicit multiplication?
"""
disallowednumberjuxt(ret::EXPR) = is_number(ret) && last(valof(ret)) == '.'

nexttokenstartsdocstring(ps::ParseState) = isidentifier(ps.nt) && val(ps.nt, ps) == "doc" && (kindof(ps.nnt) === Tokens.STRING || kindof(ps.nnt) === Tokens.TRIPLE_STRING)

"""
    is_wrapped_assignment(x::EXPR)

Is `x` an assignment expression, ignoring any surrounding parentheses.
"""
is_wrapped_assignment(x::EXPR) = is_assignment(x) || (isbracketed(x) && is_wrapped_assignment(x.args[2]))

"""
    is_range(x::EXPR)

Is `x` a valid iterator for use in `for` loops or generators?
"""
is_range(x::EXPR) = isbinarycall(x) && (is_eq(x.args[2]) || is_in(x.args[2]) || is_elof(x.args[2]))

"""
    _do_kw_convert(ps::ParseState, a::EXPR)

Should `a` be converted to a keyword-argument expression?
"""
_do_kw_convert(ps::ParseState, a::EXPR) = !ps.closer.brace && is_assignment(a)

"""
    _kw_convert(ps::ParseState, a::EXPR)

Converted an assignment expression to a keyword-argument expression.
"""
_kw_convert(a::EXPR) = EXPR(Kw, EXPR[a.args[1], a.args[2], a.args[3]], a.fullspan, a.span)

"""
    convertsigtotuple(sig::EXPR)

When parsing a function or macro signature, should it be converted to a tuple?
"""
convertsigtotuple(sig::EXPR) = isbracketed(sig) && !(istuple(sig.args[2]) || (typof(sig.args[2]) === Block) || is_splat(sig.args[2]))

"""
    docable(head)

When parsing a block of expressions, can documentation be attached? Prefixed docs at the
top-level are handled within `parse(ps::ParseState, cont = false)`.
"""
docable(head) = head === Begin || head === ModuleH || head === BareModule || head === Quote

should_negate_number_literal(ps::ParseState, op::EXPR) = (is_plus(op) || is_minus(op)) && (kindof(ps.nt) === Tokens.INTEGER || kindof(ps.nt) === Tokens.FLOAT) && isemptyws(ps.ws) && kindof(ps.nnt) != Tokens.CIRCUMFLEX_ACCENT

isbracketed(x::EXPR) = typof(x) === InvisBrackets # Assumption that x has 3 args, doesn't need checking?

unwrapbracket(x::EXPR) = isbracketed(x) ? unwrapbracket(x[2]) : x

isbeginorblock(x::EXPR) = typof(x) === Begin || typof(unwrapbracket(x)) == Block

"""
    can_become_comparison(x::EXPR)

Is `x` a binary comparison call (e.g. `a < b`) that can be extended to include more
arguments?
"""
can_become_comparison(x::EXPR) = isbinarycall(x) && (precedence(x.args[2]) == ComparisonOp || is_issubt(x.args[2]) || is_issupt(x.args[2]))

"""
    can_become_chain(x::EXPR, op::EXPR)

Is `x` a binary call for `+` or `*` that can be extended to include more
arguments?
"""
can_become_chain(x::EXPR, op::EXPR) = isbinarycall(x) && (is_star(op) || is_plus(op)) && kindof(op) == kindof(x.args[2]) && !x.args[2].dot && x.args[2].span > 0

function loop_check(ps, prevpos)
    if position(ps) <= prevpos
        throw(CSTInfiniteLoop("Infinite loop at $ps"))
    else
        position(ps)
    end
end


text:
#==============================================================================#
# AppStream.jl
#
# This file is generated from:
# https://github.com/aws/aws-sdk-js/blob/master/apis/appstream-2016-12-01.normal.json
#==============================================================================#

__precompile__()

module AppStream

using AWSCore

"""
    using AWSSDK.AppStream.associate_fleet
    associate_fleet([::AWSConfig], arguments::Dict)
    associate_fleet([::AWSConfig]; FleetName=, StackName=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "AssociateFleet", arguments::Dict)
    appstream([::AWSConfig], "AssociateFleet", FleetName=, StackName=)

# AssociateFleet Operation

Associates the specified fleet with the specified stack.

# Arguments

## `FleetName = ::String` -- *Required*
The name of the fleet.

## `StackName = ::String` -- *Required*
The name of the stack.

# Returns

`AssociateFleetResult`

# Exceptions

`LimitExceededException`, `InvalidAccountStatusException`, `ResourceNotFoundException`, `ConcurrentModificationException`, `IncompatibleImageException` or `OperationNotPermittedException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/AssociateFleet)
"""
@inline associate_fleet(aws::AWSConfig=default_aws_config(); args...) = associate_fleet(aws, args)

@inline associate_fleet(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "AssociateFleet", args)

@inline associate_fleet(args) = associate_fleet(default_aws_config(), args)

"""
    using AWSSDK.AppStream.copy_image
    copy_image([::AWSConfig], arguments::Dict)
    copy_image([::AWSConfig]; SourceImageName=, DestinationImageName=, DestinationRegion=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "CopyImage", arguments::Dict)
    appstream([::AWSConfig], "CopyImage", SourceImageName=, DestinationImageName=, DestinationRegion=, <keyword arguments>)

# CopyImage Operation

Copies the image within the same region or to a new region within the same AWS account. Note that any tags you added to the image will not be copied.

# Arguments

## `SourceImageName = ::String` -- *Required*
The name of the image to copy.

## `DestinationImageName = ::String` -- *Required*
The name that the image will have when it is copied to the destination.

## `DestinationRegion = ::String` -- *Required*
The destination region to which the image will be copied. This parameter is required, even if you are copying an image within the same region.

## `DestinationImageDescription = ::String`
The description that the image will have when it is copied to the destination.

# Returns

`CopyImageResponse`

# Exceptions

`ResourceAlreadyExistsException`, `ResourceNotFoundException`, `ResourceNotAvailableException`, `LimitExceededException`, `InvalidAccountStatusException` or `IncompatibleImageException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/CopyImage)
"""
@inline copy_image(aws::AWSConfig=default_aws_config(); args...) = copy_image(aws, args)

@inline copy_image(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "CopyImage", args)

@inline copy_image(args) = copy_image(default_aws_config(), args)

"""
    using AWSSDK.AppStream.create_directory_config
    create_directory_config([::AWSConfig], arguments::Dict)
    create_directory_config([::AWSConfig]; DirectoryName=, OrganizationalUnitDistinguishedNames=, ServiceAccountCredentials=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "CreateDirectoryConfig", arguments::Dict)
    appstream([::AWSConfig], "CreateDirectoryConfig", DirectoryName=, OrganizationalUnitDistinguishedNames=, ServiceAccountCredentials=)

# CreateDirectoryConfig Operation

Creates a Directory Config object in AppStream 2.0. This object includes the information required to join streaming instances to an Active Directory domain.

# Arguments

## `DirectoryName = ::String` -- *Required*
The fully qualified name of the directory (for example, corp.example.com).

## `OrganizationalUnitDistinguishedNames = [::String, ...]` -- *Required*
The distinguished names of the organizational units for computer accounts.

## `ServiceAccountCredentials = [ ... ]` -- *Required*
The credentials for the service account used by the streaming instance to connect to the directory.
```
 ServiceAccountCredentials = [
        "AccountName" => <required> ::String,
        "AccountPassword" => <required> ::String
    ]
```

# Returns

`CreateDirectoryConfigResult`

# Exceptions

`ResourceAlreadyExistsException`, `LimitExceededException` or `InvalidAccountStatusException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/CreateDirectoryConfig)
"""
@inline create_directory_config(aws::AWSConfig=default_aws_config(); args...) = create_directory_config(aws, args)

@inline create_directory_config(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "CreateDirectoryConfig", args)

@inline create_directory_config(args) = create_directory_config(default_aws_config(), args)

"""
    using AWSSDK.AppStream.create_fleet
    create_fleet([::AWSConfig], arguments::Dict)
    create_fleet([::AWSConfig]; Name=, InstanceType=, ComputeCapacity=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "CreateFleet", arguments::Dict)
    appstream([::AWSConfig], "CreateFleet", Name=, InstanceType=, ComputeCapacity=, <keyword arguments>)

# CreateFleet Operation

Creates a fleet. A fleet consists of streaming instances that run a specified image.

# Arguments

## `Name = ::String` -- *Required*
A unique name for the fleet.

## `ImageName = ::String`
The name of the image used to create the fleet.

## `ImageArn = ::String`
The ARN of the public, private, or shared image to use.

## `InstanceType = ::String` -- *Required*
The instance type to use when launching fleet instances. The following instance types are available:

*   stream.standard.medium

*   stream.standard.large

*   stream.compute.large

*   stream.compute.xlarge

*   stream.compute.2xlarge

*   stream.compute.4xlarge

*   stream.compute.8xlarge

*   stream.memory.large

*   stream.memory.xlarge

*   stream.memory.2xlarge

*   stream.memory.4xlarge

*   stream.memory.8xlarge

*   stream.graphics-design.large

*   stream.graphics-design.xlarge

*   stream.graphics-design.2xlarge

*   stream.graphics-design.4xlarge

*   stream.graphics-desktop.2xlarge

*   stream.graphics-pro.4xlarge

*   stream.graphics-pro.8xlarge

*   stream.graphics-pro.16xlarge

## `FleetType = "ALWAYS_ON" or "ON_DEMAND"`
The fleet type.

ALWAYS_ON

Provides users with instant-on access to their apps. You are charged for all running instances in your fleet, even if no users are streaming apps.

ON_DEMAND

Provide users with access to applications after they connect, which takes one to two minutes. You are charged for instance streaming when users are connected and a small hourly fee for instances that are not streaming apps.

## `ComputeCapacity = ["DesiredInstances" => <required> ::Int]` -- *Required*
The desired capacity for the fleet.

## `VpcConfig = [ ... ]`
The VPC configuration for the fleet.
```
 VpcConfig = [
        "SubnetIds" =>  [::String, ...],
        "SecurityGroupIds" =>  [::String, ...]
    ]
```

## `MaxUserDurationInSeconds = ::Int`
The maximum time that a streaming session can run, in seconds. Specify a value between 600 and 57600.

## `DisconnectTimeoutInSeconds = ::Int`
The time after disconnection when a session is considered to have ended, in seconds. If a user who was disconnected reconnects within this time interval, the user is connected to their previous session. Specify a value between 60 and 57600.

## `Description = ::String`
The description for display.

## `DisplayName = ::String`
The fleet name for display.

## `EnableDefaultInternetAccess = ::Bool`
Enables or disables default internet access for the fleet.

## `DomainJoinInfo = [ ... ]`
The information needed to join a Microsoft Active Directory domain.
```
 DomainJoinInfo = [
        "DirectoryName" =>  ::String,
        "OrganizationalUnitDistinguishedName" =>  ::String
    ]
```

# Returns

`CreateFleetResult`

# Exceptions

`ResourceAlreadyExistsException`, `ResourceNotAvailableException`, `ResourceNotFoundException`, `LimitExceededException`, `InvalidAccountStatusException`, `InvalidRoleException`, `ConcurrentModificationException`, `InvalidParameterCombinationException`, `IncompatibleImageException` or `OperationNotPermittedException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/CreateFleet)
"""
@inline create_fleet(aws::AWSConfig=default_aws_config(); args...) = create_fleet(aws, args)

@inline create_fleet(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "CreateFleet", args)

@inline create_fleet(args) = create_fleet(default_aws_config(), args)

"""
    using AWSSDK.AppStream.create_image_builder
    create_image_builder([::AWSConfig], arguments::Dict)
    create_image_builder([::AWSConfig]; Name=, InstanceType=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "CreateImageBuilder", arguments::Dict)
    appstream([::AWSConfig], "CreateImageBuilder", Name=, InstanceType=, <keyword arguments>)

# CreateImageBuilder Operation

Creates an image builder. An image builder is a virtual machine that is used to create an image.

The initial state of the builder is `PENDING`. When it is ready, the state is `RUNNING`.

# Arguments

## `Name = ::String` -- *Required*
A unique name for the image builder.

## `ImageName = ::String`
The name of the image used to create the builder.

## `ImageArn = ::String`
The ARN of the public, private, or shared image to use.

## `InstanceType = ::String` -- *Required*
The instance type to use when launching the image builder.

## `Description = ::String`
The description for display.

## `DisplayName = ::String`
The image builder name for display.

## `VpcConfig = [ ... ]`
The VPC configuration for the image builder. You can specify only one subnet.
```
 VpcConfig = [
        "SubnetIds" =>  [::String, ...],
        "SecurityGroupIds" =>  [::String, ...]
    ]
```

## `EnableDefaultInternetAccess = ::Bool`
Enables or disables default internet access for the image builder.

## `DomainJoinInfo = [ ... ]`
The information needed to join a Microsoft Active Directory domain.
```
 DomainJoinInfo = [
        "DirectoryName" =>  ::String,
        "OrganizationalUnitDistinguishedName" =>  ::String
    ]
```

## `AppstreamAgentVersion = ::String`
The version of the AppStream 2.0 agent to use for this image builder. To use the latest version of the AppStream 2.0 agent, specify [LATEST].

# Returns

`CreateImageBuilderResult`

# Exceptions

`LimitExceededException`, `InvalidAccountStatusException`, `ResourceAlreadyExistsException`, `ResourceNotAvailableException`, `ResourceNotFoundException`, `InvalidRoleException`, `ConcurrentModificationException`, `InvalidParameterCombinationException`, `IncompatibleImageException` or `OperationNotPermittedException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/CreateImageBuilder)
"""
@inline create_image_builder(aws::AWSConfig=default_aws_config(); args...) = create_image_builder(aws, args)

@inline create_image_builder(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "CreateImageBuilder", args)

@inline create_image_builder(args) = create_image_builder(default_aws_config(), args)

"""
    using AWSSDK.AppStream.create_image_builder_streaming_url
    create_image_builder_streaming_url([::AWSConfig], arguments::Dict)
    create_image_builder_streaming_url([::AWSConfig]; Name=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "CreateImageBuilderStreamingURL", arguments::Dict)
    appstream([::AWSConfig], "CreateImageBuilderStreamingURL", Name=, <keyword arguments>)

# CreateImageBuilderStreamingURL Operation

Creates a URL to start an image builder streaming session.

# Arguments

## `Name = ::String` -- *Required*
The name of the image builder.

## `Validity = ::Int`
The time that the streaming URL will be valid, in seconds. Specify a value between 1 and 604800 seconds. The default is 3600 seconds.

# Returns

`CreateImageBuilderStreamingURLResult`

# Exceptions

`OperationNotPermittedException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/CreateImageBuilderStreamingURL)
"""
@inline create_image_builder_streaming_url(aws::AWSConfig=default_aws_config(); args...) = create_image_builder_streaming_url(aws, args)

@inline create_image_builder_streaming_url(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "CreateImageBuilderStreamingURL", args)

@inline create_image_builder_streaming_url(args) = create_image_builder_streaming_url(default_aws_config(), args)

"""
    using AWSSDK.AppStream.create_stack
    create_stack([::AWSConfig], arguments::Dict)
    create_stack([::AWSConfig]; Name=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "CreateStack", arguments::Dict)
    appstream([::AWSConfig], "CreateStack", Name=, <keyword arguments>)

# CreateStack Operation

Creates a stack to start streaming applications to users. A stack consists of an associated fleet, user access policies, and storage configurations.

# Arguments

## `Name = ::String` -- *Required*
The name of the stack.

## `Description = ::String`
The description for display.

## `DisplayName = ::String`
The stack name for display.

## `StorageConnectors = [[ ... ], ...]`
The storage connectors to enable.
```
 StorageConnectors = [[
        "ConnectorType" => <required> "HOMEFOLDERS", "GOOGLE_DRIVE" or "ONE_DRIVE",
        "ResourceIdentifier" =>  ::String,
        "Domains" =>  [::String, ...]
    ], ...]
```

## `RedirectURL = ::String`
The URL that users are redirected to after their streaming session ends.

## `FeedbackURL = ::String`
The URL that users are redirected to after they click the Send Feedback link. If no URL is specified, no Send Feedback link is displayed.

## `UserSettings = [[ ... ], ...]`
The actions that are enabled or disabled for users during their streaming sessions. By default, these actions are enabled.
```
 UserSettings = [[
        "Action" => <required> "CLIPBOARD_COPY_FROM_LOCAL_DEVICE", "CLIPBOARD_COPY_TO_LOCAL_DEVICE", "FILE_UPLOAD", "FILE_DOWNLOAD" or "PRINTING_TO_LOCAL_DEVICE",
        "Permission" => <required> "ENABLED" or "DISABLED"
    ], ...]
```

# Returns

`CreateStackResult`

# Exceptions

`LimitExceededException`, `InvalidAccountStatusException`, `ResourceAlreadyExistsException`, `ConcurrentModificationException`, `InvalidRoleException`, `ResourceNotFoundException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/CreateStack)
"""
@inline create_stack(aws::AWSConfig=default_aws_config(); args...) = create_stack(aws, args)

@inline create_stack(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "CreateStack", args)

@inline create_stack(args) = create_stack(default_aws_config(), args)

"""
    using AWSSDK.AppStream.create_streaming_url
    create_streaming_url([::AWSConfig], arguments::Dict)
    create_streaming_url([::AWSConfig]; StackName=, FleetName=, UserId=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "CreateStreamingURL", arguments::Dict)
    appstream([::AWSConfig], "CreateStreamingURL", StackName=, FleetName=, UserId=, <keyword arguments>)

# CreateStreamingURL Operation

Creates a temporary URL to start an AppStream 2.0 streaming session for the specified user. A streaming URL enables application streaming to be tested without user setup.

# Arguments

## `StackName = ::String` -- *Required*
The name of the stack.

## `FleetName = ::String` -- *Required*
The name of the fleet.

## `UserId = ::String` -- *Required*
The ID of the user.

## `ApplicationId = ::String`
The name of the application to launch after the session starts. This is the name that you specified as **Name** in the Image Assistant.

## `Validity = ::Int`
The time that the streaming URL will be valid, in seconds. Specify a value between 1 and 604800 seconds. The default is 60 seconds.

## `SessionContext = ::String`
The session context. For more information, see [Session Context](http://docs.aws.amazon.com/appstream2/latest/developerguide/managing-stacks-fleets.html#managing-stacks-fleets-parameters) in the *Amazon AppStream 2.0 Developer Guide*.

# Returns

`CreateStreamingURLResult`

# Exceptions

`ResourceNotFoundException`, `ResourceNotAvailableException`, `OperationNotPermittedException` or `InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/CreateStreamingURL)
"""
@inline create_streaming_url(aws::AWSConfig=default_aws_config(); args...) = create_streaming_url(aws, args)

@inline create_streaming_url(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "CreateStreamingURL", args)

@inline create_streaming_url(args) = create_streaming_url(default_aws_config(), args)

"""
    using AWSSDK.AppStream.delete_directory_config
    delete_directory_config([::AWSConfig], arguments::Dict)
    delete_directory_config([::AWSConfig]; DirectoryName=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DeleteDirectoryConfig", arguments::Dict)
    appstream([::AWSConfig], "DeleteDirectoryConfig", DirectoryName=)

# DeleteDirectoryConfig Operation

Deletes the specified Directory Config object from AppStream 2.0. This object includes the information required to join streaming instances to an Active Directory domain.

# Arguments

## `DirectoryName = ::String` -- *Required*
The name of the directory configuration.

# Returns

`DeleteDirectoryConfigResult`

# Exceptions

`ResourceInUseException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DeleteDirectoryConfig)
"""
@inline delete_directory_config(aws::AWSConfig=default_aws_config(); args...) = delete_directory_config(aws, args)

@inline delete_directory_config(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DeleteDirectoryConfig", args)

@inline delete_directory_config(args) = delete_directory_config(default_aws_config(), args)

"""
    using AWSSDK.AppStream.delete_fleet
    delete_fleet([::AWSConfig], arguments::Dict)
    delete_fleet([::AWSConfig]; Name=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DeleteFleet", arguments::Dict)
    appstream([::AWSConfig], "DeleteFleet", Name=)

# DeleteFleet Operation

Deletes the specified fleet.

# Arguments

## `Name = ::String` -- *Required*
The name of the fleet.

# Returns

`DeleteFleetResult`

# Exceptions

`ResourceInUseException`, `ResourceNotFoundException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DeleteFleet)
"""
@inline delete_fleet(aws::AWSConfig=default_aws_config(); args...) = delete_fleet(aws, args)

@inline delete_fleet(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DeleteFleet", args)

@inline delete_fleet(args) = delete_fleet(default_aws_config(), args)

"""
    using AWSSDK.AppStream.delete_image
    delete_image([::AWSConfig], arguments::Dict)
    delete_image([::AWSConfig]; Name=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DeleteImage", arguments::Dict)
    appstream([::AWSConfig], "DeleteImage", Name=)

# DeleteImage Operation

Deletes the specified image. You cannot delete an image when it is in use. After you delete an image, you cannot provision new capacity using the image.

# Arguments

## `Name = ::String` -- *Required*
The name of the image.

# Returns

`DeleteImageResult`

# Exceptions

`ResourceInUseException`, `ResourceNotFoundException`, `OperationNotPermittedException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DeleteImage)
"""
@inline delete_image(aws::AWSConfig=default_aws_config(); args...) = delete_image(aws, args)

@inline delete_image(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DeleteImage", args)

@inline delete_image(args) = delete_image(default_aws_config(), args)

"""
    using AWSSDK.AppStream.delete_image_builder
    delete_image_builder([::AWSConfig], arguments::Dict)
    delete_image_builder([::AWSConfig]; Name=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DeleteImageBuilder", arguments::Dict)
    appstream([::AWSConfig], "DeleteImageBuilder", Name=)

# DeleteImageBuilder Operation

Deletes the specified image builder and releases the capacity.

# Arguments

## `Name = ::String` -- *Required*
The name of the image builder.

# Returns

`DeleteImageBuilderResult`

# Exceptions

`ResourceNotFoundException`, `OperationNotPermittedException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DeleteImageBuilder)
"""
@inline delete_image_builder(aws::AWSConfig=default_aws_config(); args...) = delete_image_builder(aws, args)

@inline delete_image_builder(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DeleteImageBuilder", args)

@inline delete_image_builder(args) = delete_image_builder(default_aws_config(), args)

"""
    using AWSSDK.AppStream.delete_image_permissions
    delete_image_permissions([::AWSConfig], arguments::Dict)
    delete_image_permissions([::AWSConfig]; Name=, SharedAccountId=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DeleteImagePermissions", arguments::Dict)
    appstream([::AWSConfig], "DeleteImagePermissions", Name=, SharedAccountId=)

# DeleteImagePermissions Operation

Deletes permissions for the specified private image. After you delete permissions for an image, AWS accounts to which you previously granted these permissions can no longer use the image.

# Arguments

## `Name = ::String` -- *Required*
The name of the private image.

## `SharedAccountId = ::String` -- *Required*
The 12-digit ID of the AWS account for which to delete image permissions.

# Returns

`DeleteImagePermissionsResult`

# Exceptions

`ResourceNotAvailableException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DeleteImagePermissions)
"""
@inline delete_image_permissions(aws::AWSConfig=default_aws_config(); args...) = delete_image_permissions(aws, args)

@inline delete_image_permissions(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DeleteImagePermissions", args)

@inline delete_image_permissions(args) = delete_image_permissions(default_aws_config(), args)

"""
    using AWSSDK.AppStream.delete_stack
    delete_stack([::AWSConfig], arguments::Dict)
    delete_stack([::AWSConfig]; Name=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DeleteStack", arguments::Dict)
    appstream([::AWSConfig], "DeleteStack", Name=)

# DeleteStack Operation

Deletes the specified stack. After the stack is deleted, the application streaming environment provided by the stack is no longer available to users. Also, any reservations made for application streaming sessions for the stack are released.

# Arguments

## `Name = ::String` -- *Required*
The name of the stack.

# Returns

`DeleteStackResult`

# Exceptions

`ResourceInUseException`, `ResourceNotFoundException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DeleteStack)
"""
@inline delete_stack(aws::AWSConfig=default_aws_config(); args...) = delete_stack(aws, args)

@inline delete_stack(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DeleteStack", args)

@inline delete_stack(args) = delete_stack(default_aws_config(), args)

"""
    using AWSSDK.AppStream.describe_directory_configs
    describe_directory_configs([::AWSConfig], arguments::Dict)
    describe_directory_configs([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DescribeDirectoryConfigs", arguments::Dict)
    appstream([::AWSConfig], "DescribeDirectoryConfigs", <keyword arguments>)

# DescribeDirectoryConfigs Operation

Retrieves a list that describes one or more specified Directory Config objects for AppStream 2.0, if the names for these objects are provided. Otherwise, all Directory Config objects in the account are described. These objects include the information required to join streaming instances to an Active Directory domain.

Although the response syntax in this topic includes the account password, this password is not returned in the actual response.

# Arguments

## `DirectoryNames = [::String, ...]`
The directory names.

## `MaxResults = ::Int`
The maximum size of each page of results.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results for this operation. If this value is null, it retrieves the first page.

# Returns

`DescribeDirectoryConfigsResult`

# Exceptions

`ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DescribeDirectoryConfigs)
"""
@inline describe_directory_configs(aws::AWSConfig=default_aws_config(); args...) = describe_directory_configs(aws, args)

@inline describe_directory_configs(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DescribeDirectoryConfigs", args)

@inline describe_directory_configs(args) = describe_directory_configs(default_aws_config(), args)

"""
    using AWSSDK.AppStream.describe_fleets
    describe_fleets([::AWSConfig], arguments::Dict)
    describe_fleets([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DescribeFleets", arguments::Dict)
    appstream([::AWSConfig], "DescribeFleets", <keyword arguments>)

# DescribeFleets Operation

Retrieves a list that describes one or more specified fleets, if the fleet names are provided. Otherwise, all fleets in the account are described.

# Arguments

## `Names = [::String, ...]`
The names of the fleets to describe.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results for this operation. If this value is null, it retrieves the first page.

# Returns

`DescribeFleetsResult`

# Exceptions

`ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DescribeFleets)
"""
@inline describe_fleets(aws::AWSConfig=default_aws_config(); args...) = describe_fleets(aws, args)

@inline describe_fleets(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DescribeFleets", args)

@inline describe_fleets(args) = describe_fleets(default_aws_config(), args)

"""
    using AWSSDK.AppStream.describe_image_builders
    describe_image_builders([::AWSConfig], arguments::Dict)
    describe_image_builders([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DescribeImageBuilders", arguments::Dict)
    appstream([::AWSConfig], "DescribeImageBuilders", <keyword arguments>)

# DescribeImageBuilders Operation

Retrieves a list that describes one or more specified image builders, if the image builder names are provided. Otherwise, all image builders in the account are described.

# Arguments

## `Names = [::String, ...]`
The names of the image builders to describe.

## `MaxResults = ::Int`
The maximum size of each page of results.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results for this operation. If this value is null, it retrieves the first page.

# Returns

`DescribeImageBuildersResult`

# Exceptions

`ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DescribeImageBuilders)
"""
@inline describe_image_builders(aws::AWSConfig=default_aws_config(); args...) = describe_image_builders(aws, args)

@inline describe_image_builders(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DescribeImageBuilders", args)

@inline describe_image_builders(args) = describe_image_builders(default_aws_config(), args)

"""
    using AWSSDK.AppStream.describe_image_permissions
    describe_image_permissions([::AWSConfig], arguments::Dict)
    describe_image_permissions([::AWSConfig]; Name=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DescribeImagePermissions", arguments::Dict)
    appstream([::AWSConfig], "DescribeImagePermissions", Name=, <keyword arguments>)

# DescribeImagePermissions Operation

Retrieves a list that describes the permissions for a private image that you own.

# Arguments

## `Name = ::String` -- *Required*
The name of the private image for which to describe permissions. The image must be one that you own.

## `MaxResults = ::Int`
The maximum size of each results page.

## `SharedAwsAccountIds = [::String, ...]`
The 12-digit ID of one or more AWS accounts with which the image is shared.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results. If this value is empty, only the first page is retrieved.

# Returns

`DescribeImagePermissionsResult`

# Exceptions

`ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DescribeImagePermissions)
"""
@inline describe_image_permissions(aws::AWSConfig=default_aws_config(); args...) = describe_image_permissions(aws, args)

@inline describe_image_permissions(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DescribeImagePermissions", args)

@inline describe_image_permissions(args) = describe_image_permissions(default_aws_config(), args)

"""
    using AWSSDK.AppStream.describe_images
    describe_images([::AWSConfig], arguments::Dict)
    describe_images([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DescribeImages", arguments::Dict)
    appstream([::AWSConfig], "DescribeImages", <keyword arguments>)

# DescribeImages Operation

Retrieves a list that describes one or more specified images, if the image names are provided. Otherwise, all images in the account are described.

# Arguments

## `Names = [::String, ...]`
The names of the images to describe.

## `Arns = [::String, ...]`
The ARNs of the public, private, and shared images to describe.

## `Type = "PUBLIC", "PRIVATE" or "SHARED"`
The type of image (public, private, or shared) to describe.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results. If this value is empty, only the first page is retrieved.

## `MaxResults = ::Int`
The maximum size of each page of results.

# Returns

`DescribeImagesResult`

# Exceptions

`InvalidParameterCombinationException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DescribeImages)
"""
@inline describe_images(aws::AWSConfig=default_aws_config(); args...) = describe_images(aws, args)

@inline describe_images(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DescribeImages", args)

@inline describe_images(args) = describe_images(default_aws_config(), args)

"""
    using AWSSDK.AppStream.describe_sessions
    describe_sessions([::AWSConfig], arguments::Dict)
    describe_sessions([::AWSConfig]; StackName=, FleetName=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DescribeSessions", arguments::Dict)
    appstream([::AWSConfig], "DescribeSessions", StackName=, FleetName=, <keyword arguments>)

# DescribeSessions Operation

Retrieves a list that describes the streaming sessions for a specified stack and fleet. If a user ID is provided for the stack and fleet, only streaming sessions for that user are described. If an authentication type is not provided, the default is to authenticate users using a streaming URL.

# Arguments

## `StackName = ::String` -- *Required*
The name of the stack. This value is case-sensitive.

## `FleetName = ::String` -- *Required*
The name of the fleet. This value is case-sensitive.

## `UserId = ::String`
The user ID.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results for this operation. If this value is null, it retrieves the first page.

## `Limit = ::Int`
The size of each page of results. The default value is 20 and the maximum value is 50.

## `AuthenticationType = "API", "SAML" or "USERPOOL"`
The authentication method. Specify `API` for a user authenticated using a streaming URL or `SAML` for a SAML federated user. The default is to authenticate users using a streaming URL.

# Returns

`DescribeSessionsResult`

# Exceptions

`InvalidParameterCombinationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DescribeSessions)
"""
@inline describe_sessions(aws::AWSConfig=default_aws_config(); args...) = describe_sessions(aws, args)

@inline describe_sessions(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DescribeSessions", args)

@inline describe_sessions(args) = describe_sessions(default_aws_config(), args)

"""
    using AWSSDK.AppStream.describe_stacks
    describe_stacks([::AWSConfig], arguments::Dict)
    describe_stacks([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DescribeStacks", arguments::Dict)
    appstream([::AWSConfig], "DescribeStacks", <keyword arguments>)

# DescribeStacks Operation

Retrieves a list that describes one or more specified stacks, if the stack names are provided. Otherwise, all stacks in the account are described.

# Arguments

## `Names = [::String, ...]`
The names of the stacks to describe.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results for this operation. If this value is null, it retrieves the first page.

# Returns

`DescribeStacksResult`

# Exceptions

`ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DescribeStacks)
"""
@inline describe_stacks(aws::AWSConfig=default_aws_config(); args...) = describe_stacks(aws, args)

@inline describe_stacks(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DescribeStacks", args)

@inline describe_stacks(args) = describe_stacks(default_aws_config(), args)

"""
    using AWSSDK.AppStream.disassociate_fleet
    disassociate_fleet([::AWSConfig], arguments::Dict)
    disassociate_fleet([::AWSConfig]; FleetName=, StackName=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "DisassociateFleet", arguments::Dict)
    appstream([::AWSConfig], "DisassociateFleet", FleetName=, StackName=)

# DisassociateFleet Operation

Disassociates the specified fleet from the specified stack.

# Arguments

## `FleetName = ::String` -- *Required*
The name of the fleet.

## `StackName = ::String` -- *Required*
The name of the stack.

# Returns

`DisassociateFleetResult`

# Exceptions

`ResourceInUseException`, `ResourceNotFoundException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/DisassociateFleet)
"""
@inline disassociate_fleet(aws::AWSConfig=default_aws_config(); args...) = disassociate_fleet(aws, args)

@inline disassociate_fleet(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "DisassociateFleet", args)

@inline disassociate_fleet(args) = disassociate_fleet(default_aws_config(), args)

"""
    using AWSSDK.AppStream.expire_session
    expire_session([::AWSConfig], arguments::Dict)
    expire_session([::AWSConfig]; SessionId=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "ExpireSession", arguments::Dict)
    appstream([::AWSConfig], "ExpireSession", SessionId=)

# ExpireSession Operation

Immediately stops the specified streaming session.

# Arguments

## `SessionId = ::String` -- *Required*
The ID of the streaming session.

# Returns

`ExpireSessionResult`

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/ExpireSession)
"""
@inline expire_session(aws::AWSConfig=default_aws_config(); args...) = expire_session(aws, args)

@inline expire_session(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "ExpireSession", args)

@inline expire_session(args) = expire_session(default_aws_config(), args)

"""
    using AWSSDK.AppStream.list_associated_fleets
    list_associated_fleets([::AWSConfig], arguments::Dict)
    list_associated_fleets([::AWSConfig]; StackName=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "ListAssociatedFleets", arguments::Dict)
    appstream([::AWSConfig], "ListAssociatedFleets", StackName=, <keyword arguments>)

# ListAssociatedFleets Operation

Retrieves the name of the fleet that is associated with the specified stack.

# Arguments

## `StackName = ::String` -- *Required*
The name of the stack.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results for this operation. If this value is null, it retrieves the first page.

# Returns

`ListAssociatedFleetsResult`

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/ListAssociatedFleets)
"""
@inline list_associated_fleets(aws::AWSConfig=default_aws_config(); args...) = list_associated_fleets(aws, args)

@inline list_associated_fleets(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "ListAssociatedFleets", args)

@inline list_associated_fleets(args) = list_associated_fleets(default_aws_config(), args)

"""
    using AWSSDK.AppStream.list_associated_stacks
    list_associated_stacks([::AWSConfig], arguments::Dict)
    list_associated_stacks([::AWSConfig]; FleetName=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "ListAssociatedStacks", arguments::Dict)
    appstream([::AWSConfig], "ListAssociatedStacks", FleetName=, <keyword arguments>)

# ListAssociatedStacks Operation

Retrieves the name of the stack with which the specified fleet is associated.

# Arguments

## `FleetName = ::String` -- *Required*
The name of the fleet.

## `NextToken = ::String`
The pagination token to use to retrieve the next page of results for this operation. If this value is null, it retrieves the first page.

# Returns

`ListAssociatedStacksResult`

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/ListAssociatedStacks)
"""
@inline list_associated_stacks(aws::AWSConfig=default_aws_config(); args...) = list_associated_stacks(aws, args)

@inline list_associated_stacks(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "ListAssociatedStacks", args)

@inline list_associated_stacks(args) = list_associated_stacks(default_aws_config(), args)

"""
    using AWSSDK.AppStream.list_tags_for_resource
    list_tags_for_resource([::AWSConfig], arguments::Dict)
    list_tags_for_resource([::AWSConfig]; ResourceArn=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "ListTagsForResource", arguments::Dict)
    appstream([::AWSConfig], "ListTagsForResource", ResourceArn=)

# ListTagsForResource Operation

Retrieves a list of all tags for the specified AppStream 2.0 resource. You can tag AppStream 2.0 image builders, images, fleets, and stacks.

For more information about tags, see [Tagging Your Resources](http://docs.aws.amazon.com/appstream2/latest/developerguide/tagging-basic.html) in the *Amazon AppStream 2.0 Developer Guide*.

# Arguments

## `ResourceArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the resource.

# Returns

`ListTagsForResourceResponse`

# Exceptions

`ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/ListTagsForResource)
"""
@inline list_tags_for_resource(aws::AWSConfig=default_aws_config(); args...) = list_tags_for_resource(aws, args)

@inline list_tags_for_resource(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "ListTagsForResource", args)

@inline list_tags_for_resource(args) = list_tags_for_resource(default_aws_config(), args)

"""
    using AWSSDK.AppStream.start_fleet
    start_fleet([::AWSConfig], arguments::Dict)
    start_fleet([::AWSConfig]; Name=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "StartFleet", arguments::Dict)
    appstream([::AWSConfig], "StartFleet", Name=)

# StartFleet Operation

Starts the specified fleet.

# Arguments

## `Name = ::String` -- *Required*
The name of the fleet.

# Returns

`StartFleetResult`

# Exceptions

`ResourceNotFoundException`, `OperationNotPermittedException`, `LimitExceededException`, `InvalidAccountStatusException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/StartFleet)
"""
@inline start_fleet(aws::AWSConfig=default_aws_config(); args...) = start_fleet(aws, args)

@inline start_fleet(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "StartFleet", args)

@inline start_fleet(args) = start_fleet(default_aws_config(), args)

"""
    using AWSSDK.AppStream.start_image_builder
    start_image_builder([::AWSConfig], arguments::Dict)
    start_image_builder([::AWSConfig]; Name=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "StartImageBuilder", arguments::Dict)
    appstream([::AWSConfig], "StartImageBuilder", Name=, <keyword arguments>)

# StartImageBuilder Operation

Starts the specified image builder.

# Arguments

## `Name = ::String` -- *Required*
The name of the image builder.

## `AppstreamAgentVersion = ::String`
The version of the AppStream 2.0 agent to use for this image builder. To use the latest version of the AppStream 2.0 agent, specify [LATEST].

# Returns

`StartImageBuilderResult`

# Exceptions

`ResourceNotAvailableException`, `ResourceNotFoundException`, `ConcurrentModificationException`, `InvalidAccountStatusException` or `IncompatibleImageException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/StartImageBuilder)
"""
@inline start_image_builder(aws::AWSConfig=default_aws_config(); args...) = start_image_builder(aws, args)

@inline start_image_builder(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "StartImageBuilder", args)

@inline start_image_builder(args) = start_image_builder(default_aws_config(), args)

"""
    using AWSSDK.AppStream.stop_fleet
    stop_fleet([::AWSConfig], arguments::Dict)
    stop_fleet([::AWSConfig]; Name=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "StopFleet", arguments::Dict)
    appstream([::AWSConfig], "StopFleet", Name=)

# StopFleet Operation

Stops the specified fleet.

# Arguments

## `Name = ::String` -- *Required*
The name of the fleet.

# Returns

`StopFleetResult`

# Exceptions

`ResourceNotFoundException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/StopFleet)
"""
@inline stop_fleet(aws::AWSConfig=default_aws_config(); args...) = stop_fleet(aws, args)

@inline stop_fleet(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "StopFleet", args)

@inline stop_fleet(args) = stop_fleet(default_aws_config(), args)

"""
    using AWSSDK.AppStream.stop_image_builder
    stop_image_builder([::AWSConfig], arguments::Dict)
    stop_image_builder([::AWSConfig]; Name=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "StopImageBuilder", arguments::Dict)
    appstream([::AWSConfig], "StopImageBuilder", Name=)

# StopImageBuilder Operation

Stops the specified image builder.

# Arguments

## `Name = ::String` -- *Required*
The name of the image builder.

# Returns

`StopImageBuilderResult`

# Exceptions

`ResourceNotFoundException`, `OperationNotPermittedException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/StopImageBuilder)
"""
@inline stop_image_builder(aws::AWSConfig=default_aws_config(); args...) = stop_image_builder(aws, args)

@inline stop_image_builder(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "StopImageBuilder", args)

@inline stop_image_builder(args) = stop_image_builder(default_aws_config(), args)

"""
    using AWSSDK.AppStream.tag_resource
    tag_resource([::AWSConfig], arguments::Dict)
    tag_resource([::AWSConfig]; ResourceArn=, Tags=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "TagResource", arguments::Dict)
    appstream([::AWSConfig], "TagResource", ResourceArn=, Tags=)

# TagResource Operation

Adds or overwrites one or more tags for the specified AppStream 2.0 resource. You can tag AppStream 2.0 image builders, images, fleets, and stacks.

Each tag consists of a key and an optional value. If a resource already has a tag with the same key, this operation updates its value.

To list the current tags for your resources, use [ListTagsForResource](@ref). To disassociate tags from your resources, use [UntagResource](@ref).

For more information about tags, see [Tagging Your Resources](http://docs.aws.amazon.com/appstream2/latest/developerguide/tagging-basic.html) in the *Amazon AppStream 2.0 Developer Guide*.

# Arguments

## `ResourceArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the resource.

## `Tags = ::Dict{String,String}` -- *Required*
The tags to associate. A tag is a key-value pair (the value is optional). For example, `Environment=Test`, or, if you do not specify a value, `Environment=`.

If you do not specify a value, we set the value to an empty string.

# Returns

`TagResourceResponse`

# Exceptions

`LimitExceededException`, `InvalidAccountStatusException` or `ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/TagResource)
"""
@inline tag_resource(aws::AWSConfig=default_aws_config(); args...) = tag_resource(aws, args)

@inline tag_resource(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "TagResource", args)

@inline tag_resource(args) = tag_resource(default_aws_config(), args)

"""
    using AWSSDK.AppStream.untag_resource
    untag_resource([::AWSConfig], arguments::Dict)
    untag_resource([::AWSConfig]; ResourceArn=, TagKeys=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "UntagResource", arguments::Dict)
    appstream([::AWSConfig], "UntagResource", ResourceArn=, TagKeys=)

# UntagResource Operation

Disassociates one or more specified tags from the specified AppStream 2.0 resource.

To list the current tags for your resources, use [ListTagsForResource](@ref).

For more information about tags, see [Tagging Your Resources](http://docs.aws.amazon.com/appstream2/latest/developerguide/tagging-basic.html) in the *Amazon AppStream 2.0 Developer Guide*.

# Arguments

## `ResourceArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the resource.

## `TagKeys = [::String, ...]` -- *Required*
The tag keys for the tags to disassociate.

# Returns

`UntagResourceResponse`

# Exceptions

`ResourceNotFoundException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/UntagResource)
"""
@inline untag_resource(aws::AWSConfig=default_aws_config(); args...) = untag_resource(aws, args)

@inline untag_resource(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "UntagResource", args)

@inline untag_resource(args) = untag_resource(default_aws_config(), args)

"""
    using AWSSDK.AppStream.update_directory_config
    update_directory_config([::AWSConfig], arguments::Dict)
    update_directory_config([::AWSConfig]; DirectoryName=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "UpdateDirectoryConfig", arguments::Dict)
    appstream([::AWSConfig], "UpdateDirectoryConfig", DirectoryName=, <keyword arguments>)

# UpdateDirectoryConfig Operation

Updates the specified Directory Config object in AppStream 2.0. This object includes the information required to join streaming instances to an Active Directory domain.

# Arguments

## `DirectoryName = ::String` -- *Required*
The name of the Directory Config object.

## `OrganizationalUnitDistinguishedNames = [::String, ...]`
The distinguished names of the organizational units for computer accounts.

## `ServiceAccountCredentials = [ ... ]`
The credentials for the service account used by the streaming instance to connect to the directory.
```
 ServiceAccountCredentials = [
        "AccountName" => <required> ::String,
        "AccountPassword" => <required> ::String
    ]
```

# Returns

`UpdateDirectoryConfigResult`

# Exceptions

`ResourceInUseException`, `ResourceNotFoundException` or `ConcurrentModificationException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/UpdateDirectoryConfig)
"""
@inline update_directory_config(aws::AWSConfig=default_aws_config(); args...) = update_directory_config(aws, args)

@inline update_directory_config(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "UpdateDirectoryConfig", args)

@inline update_directory_config(args) = update_directory_config(default_aws_config(), args)

"""
    using AWSSDK.AppStream.update_fleet
    update_fleet([::AWSConfig], arguments::Dict)
    update_fleet([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "UpdateFleet", arguments::Dict)
    appstream([::AWSConfig], "UpdateFleet", <keyword arguments>)

# UpdateFleet Operation

Updates the specified fleet.

If the fleet is in the `STOPPED` state, you can update any attribute except the fleet name. If the fleet is in the `RUNNING` state, you can update the `DisplayName` and `ComputeCapacity` attributes. If the fleet is in the `STARTING` or `STOPPING` state, you can't update it.

# Arguments

## `ImageName = ::String`
The name of the image used to create the fleet.

## `ImageArn = ::String`
The ARN of the public, private, or shared image to use.

## `Name = ::String`
A unique name for the fleet.

## `InstanceType = ::String`
The instance type to use when launching fleet instances. The following instance types are available:

*   stream.standard.medium

*   stream.standard.large

*   stream.compute.large

*   stream.compute.xlarge

*   stream.compute.2xlarge

*   stream.compute.4xlarge

*   stream.compute.8xlarge

*   stream.memory.large

*   stream.memory.xlarge

*   stream.memory.2xlarge

*   stream.memory.4xlarge

*   stream.memory.8xlarge

*   stream.graphics-design.large

*   stream.graphics-design.xlarge

*   stream.graphics-design.2xlarge

*   stream.graphics-design.4xlarge

*   stream.graphics-desktop.2xlarge

*   stream.graphics-pro.4xlarge

*   stream.graphics-pro.8xlarge

*   stream.graphics-pro.16xlarge

## `ComputeCapacity = ["DesiredInstances" => <required> ::Int]`
The desired capacity for the fleet.

## `VpcConfig = [ ... ]`
The VPC configuration for the fleet.
```
 VpcConfig = [
        "SubnetIds" =>  [::String, ...],
        "SecurityGroupIds" =>  [::String, ...]
    ]
```

## `MaxUserDurationInSeconds = ::Int`
The maximum time that a streaming session can run, in seconds. Specify a value between 600 and 57600.

## `DisconnectTimeoutInSeconds = ::Int`
The time after disconnection when a session is considered to have ended, in seconds. If a user who was disconnected reconnects within this time interval, the user is connected to their previous session. Specify a value between 60 and 57600.

## `DeleteVpcConfig = ::Bool`
Deletes the VPC association for the specified fleet.

## `Description = ::String`
The description for display.

## `DisplayName = ::String`
The fleet name for display.

## `EnableDefaultInternetAccess = ::Bool`
Enables or disables default internet access for the fleet.

## `DomainJoinInfo = [ ... ]`
The information needed to join a Microsoft Active Directory domain.
```
 DomainJoinInfo = [
        "DirectoryName" =>  ::String,
        "OrganizationalUnitDistinguishedName" =>  ::String
    ]
```

## `AttributesToDelete = ["VPC_CONFIGURATION", "VPC_CONFIGURATION_SECURITY_GROUP_IDS" or "DOMAIN_JOIN_INFO", ...]`
The fleet attributes to delete.

# Returns

`UpdateFleetResult`

# Exceptions

`ResourceInUseException`, `LimitExceededException`, `InvalidAccountStatusException`, `InvalidRoleException`, `ResourceNotFoundException`, `ResourceNotAvailableException`, `InvalidParameterCombinationException`, `ConcurrentModificationException`, `IncompatibleImageException` or `OperationNotPermittedException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/UpdateFleet)
"""
@inline update_fleet(aws::AWSConfig=default_aws_config(); args...) = update_fleet(aws, args)

@inline update_fleet(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "UpdateFleet", args)

@inline update_fleet(args) = update_fleet(default_aws_config(), args)

"""
    using AWSSDK.AppStream.update_image_permissions
    update_image_permissions([::AWSConfig], arguments::Dict)
    update_image_permissions([::AWSConfig]; Name=, SharedAccountId=, ImagePermissions=)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "UpdateImagePermissions", arguments::Dict)
    appstream([::AWSConfig], "UpdateImagePermissions", Name=, SharedAccountId=, ImagePermissions=)

# UpdateImagePermissions Operation

Adds or updates permissions for the specified private image.

# Arguments

## `Name = ::String` -- *Required*
The name of the private image.

## `SharedAccountId = ::String` -- *Required*
The 12-digit ID of the AWS account for which you want add or update image permissions.

## `ImagePermissions = [ ... ]` -- *Required*
The permissions for the image.
```
 ImagePermissions = [
        "allowFleet" =>  ::Bool,
        "allowImageBuilder" =>  ::Bool
    ]
```

# Returns

`UpdateImagePermissionsResult`

# Exceptions

`ResourceNotFoundException`, `ResourceNotAvailableException` or `LimitExceededException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/UpdateImagePermissions)
"""
@inline update_image_permissions(aws::AWSConfig=default_aws_config(); args...) = update_image_permissions(aws, args)

@inline update_image_permissions(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "UpdateImagePermissions", args)

@inline update_image_permissions(args) = update_image_permissions(default_aws_config(), args)

"""
    using AWSSDK.AppStream.update_stack
    update_stack([::AWSConfig], arguments::Dict)
    update_stack([::AWSConfig]; Name=, <keyword arguments>)

    using AWSCore.Services.appstream
    appstream([::AWSConfig], "UpdateStack", arguments::Dict)
    appstream([::AWSConfig], "UpdateStack", Name=, <keyword arguments>)

# UpdateStack Operation

Updates the specified fields for the specified stack.

# Arguments

## `DisplayName = ::String`
The stack name for display.

## `Description = ::String`
The description for display.

## `Name = ::String` -- *Required*
The name of the stack.

## `StorageConnectors = [[ ... ], ...]`
The storage connectors to enable.
```
 StorageConnectors = [[
        "ConnectorType" => <required> "HOMEFOLDERS", "GOOGLE_DRIVE" or "ONE_DRIVE",
        "ResourceIdentifier" =>  ::String,
        "Domains" =>  [::String, ...]
    ], ...]
```

## `DeleteStorageConnectors = ::Bool`
Deletes the storage connectors currently enabled for the stack.

## `RedirectURL = ::String`
The URL that users are redirected to after their streaming session ends.

## `FeedbackURL = ::String`
The URL that users are redirected to after they click the Send Feedback link. If no URL is specified, no Send Feedback link is displayed.

## `AttributesToDelete = ["STORAGE_CONNECTORS", "STORAGE_CONNECTOR_HOMEFOLDERS", "STORAGE_CONNECTOR_GOOGLE_DRIVE", "STORAGE_CONNECTOR_ONE_DRIVE", "REDIRECT_URL", "FEEDBACK_URL", "THEME_NAME" or "USER_SETTINGS", ...]`
The stack attributes to delete.

## `UserSettings = [[ ... ], ...]`
The actions that are enabled or disabled for users during their streaming sessions. By default, these actions are enabled.
```
 UserSettings = [[
        "Action" => <required> "CLIPBOARD_COPY_FROM_LOCAL_DEVICE", "CLIPBOARD_COPY_TO_LOCAL_DEVICE", "FILE_UPLOAD", "FILE_DOWNLOAD" or "PRINTING_TO_LOCAL_DEVICE",
        "Permission" => <required> "ENABLED" or "DISABLED"
    ], ...]
```

# Returns

`UpdateStackResult`

# Exceptions

`ResourceNotFoundException`, `ResourceInUseException`, `InvalidRoleException`, `InvalidParameterCombinationException`, `LimitExceededException`, `InvalidAccountStatusException`, `IncompatibleImageException` or `OperationNotPermittedException`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/appstream-2016-12-01/UpdateStack)
"""
@inline update_stack(aws::AWSConfig=default_aws_config(); args...) = update_stack(aws, args)

@inline update_stack(aws::AWSConfig, args) = AWSCore.Services.appstream(aws, "UpdateStack", args)

@inline update_stack(args) = update_stack(default_aws_config(), args)

end # module AppStream

#==============================================================================#
# End of file
#==============================================================================#


text:
pwd()
# cd("d:\\test\\week1")
# shell mode:
# ; ls (on Linux and mac OS X)
# ; mkdir folder
# ; cd folder

# on Linux and mac OS X:
cmd = `echo Julia is smart`
typeof(cmd) #> Cmd (constructor with 1 method)
run(cmd) #> Julia is smart
run(`date`) #> Sun Oct 12 09:44:50 GMT 2014
cmd = `cat file1.txt`
run(cmd) #> text from file1.txt: This a a test for try / catch / finally
success(cmd) #> true

# interpolation:
file = "file1.txt"
cmd = `cat $file`
run(cmd) #> text from file1.txt: This a a test for try / catch / finally
# cmd = `ls *.*`   # works only on Windows
# run(cmd) #> returns: file1.txt  shell.jl  test.txt
# pipelining:
run(pipeline(`cat $file`,"test.txt")) #> text from file1.txt is written into test.txt
run(pipeline("test.txt",`cat`))
run(pipeline(`echo $("\nhi\nJulia")`,`cat`,`grep -n J`)) #> 3:Julia
run(pipeline(`cat "tosort.txt"`,`sort`)) # returns A B C

println()
println("Output grep command:")
# output:
# run(`grep is $(readdir())`) # returns where "is" is found in text files in current dir
#=
array_product_benchmark.jl:# gc_disable()
callc.jl:# test existence of library:
file1.txt:This a a test for try / catch / finally
performance.jl:foo1(x::Int) = isprime(x) ? x: false
shell.jl:cmd = `echo Julia is smart`
shell.jl:run(cmd) #> Julia is smart
shell.jl:run(cmd) #> text from file1.txt: This a a test for try / catch / finally

shell.jl:run(cmd) #> text from file1.txt: This a a test for try / catch / finally

shell.jl:run(`cat $file` |> "test.txt") #> text from file1.txt is written into te
st.txt
shell.jl:run(`grep is $(readdir())`) # returns where "is" is found in text files
in current dir
shell.jl:# CThis a a test for try / catch / finally
test.txt:This a a test for try / catch / finally
=#
println()

# reading the output of the command:
a = readall(pipeline(`cat "tosort.txt"`,`sort`))
#> a has value "A\r\nB\r\nC\n"

run(`cat "file1.txt"` & `cat "tosort.txt"`)
# B
# A
# CThis a a test for try / catch / finally

# platform variations:
Sys.KERNEL  #> :NT
fun1 = ()
fun2 = ()
Sys.iswindows() ? fun1 : fun2
if Sys.iswindows()
    fun1
else
    fun2
end


text:
"""
Calculate water critical properties
T --- temperature in Kelvin
p --- pressure in bar
ro --- density in mol/l
doi: 10.1063/1.555786
"""
function methanolCritical()
	sub=newCritical()
	sub.T=512.50    #K
	sub.p=80.9464   #bar
	sub.ro= 8.40    #mol/l #322 kg/m^3
	return sub
end

"""
Calculate water properties at triple point
T --- temperature in Kelvin
p --- pressure in bar
vRo --- vapor density in mol/l
lRo --- liquid density in mol/l
doi: 10.1063/1.555786
"""
function methanolTriple()
	sub=newTriple()
	sub.T=175.59
	sub.p=1.835*10.0^-6	#bar
	sub.lRo=28.226	#kg/m^3 to mol/l
	sub.vRo=1.271*10.0^(-7)	#kg/m^3s to mol/l
	return sub
end

"""
calculate methanol properties at saturation line
T --- temperature in Kelvin
p --- pressure in bar
vRo --- vapor density in mol/l
lRo --- liquid density in mol/l
doi: 10.1063/1.555786
"""
function waterSaturate(T)
a=[-10.75284879, 16.758206642, -3.603424623, 4.373231941, -2.381377449, 4.572198698]    #pressure
b=[2.51730906, -2.46669419, 3.06681768, -1.32507680]    #liquid density
c=[10.61966850, -2.55668202, 3.81845421, 4.79556752]    #vapor density
sub=newSaturate()
crit=methanolCritical()

if (T<crit.T)
	v=1.0-T/crit.T
	sub.p=exp(a[1]/v + a[2] + a[3]*v + a[4]*v^2 + a[5]*v^3 + a[6]*(1-v)^1.7)
	sub.lRo=crit.ro*(1+b[1]*(1-v)^0.35 + b[2]*(v-1) + b[3]*(v^2-1) + b[4]*(v^3-1))
	sub.vRo=crit.ro*exp(c[1]*(1 - 1/v) + c[2]*(1-v)^0.35 + c[3]*(1-v) + c[4]*(1-v)^2)
	sub.T=T
else
	sub.p=-1
	sub.lRo=-1
	sub.vRo=-1
	sub.T=-1
end
return sub
end


text:
"""
    mutable struct Node{PARA,F}

    Node Object, which is the building block of the diagram tree. Each node is a collection of CACHED proapgator objects and other child CACHED node objects

# Members
- para::PARA     : user-defined parameters, which will be used to evaluate the factor and the weight of the node (e.g., if the node represents a vertex function, then the parameter may be the momentum basis of the external legs)
- operation::Int : #1: multiply, 2: add, ...
- factor::F      : additional factor of the node
- components::Vector{Vector{Int}}  : Index to the cached propagators stored in certain pools. Each Vector{Int} is for one kind of propagator.
- childNodes::Vector{Int}  : Indices to the cached nodes stored in certain pool. They are the child of the current node in the diagram tree.
- parent::Int : Index to the cached nodes which is the parent of the current node.
"""
struct Node{PARA,F}
    name::Symbol
    para::PARA
    operation::Int #1: multiply, 2: add, ...
    factor::F
    loopidx::Int
    siteidx::Vector{Int}
    children::Vector{Int}
    function Node{P,F}(name::Symbol, para; loopidx = 0, siteidx = [], operator = ADD, children = [], factor = 1.0) where {F,P}
        # @assert typeof(para) == P
        return new{P,F}(name, para, operator, F(factor), loopidx, siteidx, children)
    end
end

function Base.isequal(a::Node{P}, b::Node{P}) where {P}
    # only parent is allowed to be different
    if (isequal(a.para, b.para) == false) || (a.operation != b.operation) || (Set(a.children) != Set(b.children)) || (a.factor ≈ b.factor) == false || a.loopidx != b.loopidx || a.siteidx != b.siteidx
        return false
    else
        return true
    end
end
Base.:(==)(a::Node{P}, b::Node{P}) where {P} = Base.isequal(a, b)

"""
    mutable struct ExpressionTree{V,PARA,F,W}

    Diagram Object represents a set of Feynman diagrams in an experssion tree (forest) structure

# Members
- name::Symbol                     : Name of the tree
- loopBasis::V                     : Tuple of pools of cached basis  in a format of (BasisPool1, BasisPool2, ...)
- node::CachedPool{Node{PARA,F},W} : Pool of the nodes in the diagram tree
- root::Vector{Int}                : indices of the cached nodes that are the root(s) of the diagram tree. Each element corresponds to one root.
"""
mutable struct ExpressionTree{V,PARA,F,W}
    name::Symbol
    loopBasis::V
    node::CachedPool{Node{PARA,F},W}
    root::Vector{Int}
    function ExpressionTree(; loopBasis::V, weight::DataType, factor::DataType = weight, nodePara::DataType = Nothing, name = :none) where {V}
        nodePool = CachedPool(:node, Node{nodePara,factor}, weight)
        return new{V,nodePara,factor,weight}(name, loopBasis, nodePool, [])
    end
end

weight(tree::ExpressionTree) = tree.node.current

Base.getindex(diag::ExpressionTree, i) = diag.node.current[diag.root[i]]
Base.firstindex(diag::ExpressionTree) = 1
Base.lastindex(diag::ExpressionTree) = length(diag.root)

"""
    function addPropagator!(diag::ExpressionTree, name, factor = 1.0; site = [], loop = nothing, para = nothing, order::Int = 0)

    Add a propagator into the diagram tree.

# Arguments
- diag           : diagrammatic experssion tree.
- order::Int = 0 : Order of the propagator.
- name = :none   : name of the propagator.
- factor = 1     : Factor of the propagator.
- site = []      : site basis (e.g, time and space coordinate) of the propagator.
- loop = nothing : loop basis (e.g, momentum and frequency) of the propagator.
- para = nothing : Additional paramenter required to evaluate the propagator.
"""
function addpropagator!(diag::ExpressionTree{V,PARA,F,W}, name, factor = 1.0; site = [], loop = nothing, para = nothing, order::Int = 0) where {V,PARA,F,W}
    loopPool = diag.loopBasis
    loopidx = 0
    if isnothing(loop) == false
        @assert typeof(loop) <: AbstractVector "LoopBasis should be a Vector!"
        loopidx = append(loopPool, loop)
    end
    # prop = Propagator{pPARA,F}(name, order, para, factor, loopidx, collect(site))
    prop = Node{PARA,F}(name, para; factor = factor, loopidx = loopidx, siteidx = collect(site))
    # pidx = append(diag.propagator, prop)
    pidx = append(diag.node, prop)
    # return component(pidx, false, propagatorPool[index].name)
    return pidx
end

"""
    function addnode!(diag::ExpressionTree{V,PARA,F,W}, operator, name, children::Union{Tuple, AbstractVector}, factor = 1.0; para = nothing) where {V,PARA,F,W}

    Add a node into the expression tree.

# Arguments
- diag::ExpressionTree  : diagrammatic experssion tree.
- operator::Int         : #1: multiply, 2: add, ...
- name                  : name of the node
- children              : Indices to the cached nodes stored in certain pool. They are the child of the current node in the diagram tree. It should be in the format of Vector{Int}.
- factor = 1.0          : Factor of the node
- para = nothing        : Additional paramenter required to evaluate the node. Set to nothing by default.
"""
function addnode!(diag::ExpressionTree{V,PARA,F,W}, operator, name, children::Union{Tuple,AbstractVector}, factor = 1.0; para = nothing) where {V,PARA,F,W}
    nodePool = diag.node

    for nidx in children
        @assert nidx <= length(diag.node) "Failed to add node with propagator = $propagator, and child =$children. $nidx is not in nodePool."
    end

    node = Node{PARA,F}(name, para; operator = operator, children = children, factor = factor)

    nidx = append(nodePool, node)
    return nidx
    # return component(nidx, true, :none)
end

"""
    function getNode(diag::Diagrams, nidx::Int)

    get Node in the diag with the index nidx.
"""
function getNode(diag, nidx::Int)
    return diag.node.object[nidx]
end

# function getPropagator(diag::Diagrams, pidx::Int)
#     for p in diag.propagatorPool
#         if p.name == poolName
#             return p.object[pidx]
#         end
#     end
#     error("$poolName propagator pool doesn't exist!")
# end

"""
    function getNodeWeight(tree, nidx::Int)

    get Node weight in the diagram experssion tree with the index nidx.
"""
function getNodeWeight(tree, nidx::Int)
    return tree.node.current[nidx]
end

# function addpropagator!(diag, name, factor = 1.0; site = [], loop = nothing, para = nothing, order::Int = 0)
#     pidx = addPropagator!(diag, name, factor; site = site, loop = loop, para = para, order = order)
#     @assert pidx > 0
#     return Component(pidx, false, :propagator, diag.node.object[pidx])
# end

# function addnode!(diag, operator, name, components, factor = 1.0; para = nothing)
#     _components = [c for c in components if c.index > 0]
#     if operator == MUL
#         if length(_components) < length(components)
#             return zero(Component) #if some of the components doesn't exist, then product of the components doens't exist
#         end
#     elseif operator == ADD
#         if length(_components) == 0
#             return zero(Component) #if all of the components doesn't exist, then sum of the components doens't exist
#         end
#     end

#     child = []
#     propagator = []
#     for c in collect(_components)
#         @assert c isa Component "$c is not a DiagTree.Component"
#         if c.isNode
#             push!(child, c.index)
#         else
#             push!(propagator, c.index)
#         end
#     end
#     nidx = addNode!(diag, operator, name, factor; propagator = propagator, child = child, para = para)
#     return Component(nidx, true, diag.node.name, getNode(diag, nidx))
# end