// oils_for_unix.cc: translated from Python by mycpp

// #include "_gen/bin/oils_for_unix.mycpp.h"

#include "cpp/preamble.h"
// BEGIN mycpp output

#include "mycpp/runtime.h"

namespace runtime {  // forward declare
  class TraversalState;
}

namespace vm {  // forward declare
  class ControlFlow;
  class IntControlFlow;
  class ValueControlFlow;
  class _Executor;
  class _AssignBuiltin;
  class _Builtin;
  class _Callable;
  class ctx_Redirect;
  class ctx_ProcessSub;
  class ctx_FlushStdout;
}

namespace format {  // forward declare
}

namespace oils_for_unix {  // forward declare
}

namespace assign_osh {  // forward declare
  class Export;
  class Readonly;
  class NewVar;
  class Unset;
  class Shift;
}

namespace completion_ysh {  // forward declare
  class CompExport;
}

namespace dirs_osh {  // forward declare
  class DirStack;
  class ctx_CdBlock;
  class Cd;
  class Pushd;
  class Popd;
  class Dirs;
  class Pwd;
}

namespace error_ysh {  // forward declare
  class ctx_Try;
  class Try;
  class Failed;
  class Error;
  class BoolStatus;
  class Assert;
}

namespace func_eggex {  // forward declare
  class _MatchCallable;
  class MatchFunc;
  class MatchMethod;
}

namespace func_hay {  // forward declare
  class ParseHay;
  class EvalHay;
  class BlockAsStr;
  class HayFunc;
}

namespace func_misc {  // forward declare
  class Object;
  class Obj_call;
  class Prototype;
  class PropView;
  class Len;
  class Type;
  class Join;
  class Maybe;
  class Bool;
  class Int;
  class Float;
  class Str_;
  class List_;
  class DictFunc;
  class Runes;
  class EncodeRunes;
  class Bytes;
  class EncodeBytes;
  class Split;
  class FloatsEqual;
  class Glob;
  class ToJson8;
  class FromJson8;
  class BashArrayToSparse;
  class SparseOp;
}

namespace func_reflect {  // forward declare
  class Id;
  class GetFrame;
  class BindFrame;
  class Shvar_get;
  class GetVar;
  class SetVar;
  class ParseCommand;
  class ParseExpr;
}

namespace hay_ysh {  // forward declare
  class ctx_HayNode;
  class ctx_HayEval;
  class HayState;
  class Hay;
  class HayNode_;
}

namespace io_osh {  // forward declare
  class Echo;
  class MapFile;
  class Cat;
}

namespace io_ysh {  // forward declare
  class _Builtin;
  class Pp;
  class Write;
  class RunBlock;
}

namespace json_ysh {  // forward declare
  class Json;
}

namespace meta_oils {  // forward declare
  class Eval;
  class ShellFile;
  class Command;
  class Builtin;
  class RunProc;
  class Invoke;
  class Extern;
  class Type;
}

namespace method_dict {  // forward declare
  class Keys;
  class Values;
  class Erase;
  class Get;
}

namespace method_io {  // forward declare
  class EvalExpr;
  class EvalInFrame;
  class Eval;
  class CaptureStdout;
  class PromptVal;
  class Time;
  class Strftime;
  class Glob;
}

namespace method_list {  // forward declare
  class Append;
  class Clear;
  class Extend;
  class Pop;
  class Reverse;
  class IndexOf;
  class LastIndexOf;
}

namespace method_other {  // forward declare
  class SetValue;
}

namespace method_str {  // forward declare
  class HasAffix;
  class Trim;
  class Upper;
  class Lower;
  class SearchMatch;
  class Replace;
  class Split;
}

namespace method_type {  // forward declare
  class Index__;
}

namespace misc_osh {  // forward declare
  class Times;
  class Help;
}

namespace module_ysh {  // forward declare
  class IsMain;
  class SourceGuard;
  class ModuleInvoke;
}

namespace printf_osh {  // forward declare
  class _FormatStringParser;
  class _PrintfState;
  class Printf;
}

namespace process_osh {  // forward declare
  class Jobs;
  class Fg;
  class Bg;
  class Fork;
  class ForkWait;
  class Exec;
  class Wait;
  class Umask;
  class Ulimit;
}

namespace pure_osh {  // forward declare
  class Boolean;
  class Alias;
  class UnAlias;
  class Set;
  class Shopt;
  class Hash;
  class GetOptsState;
  class GetOpts;
}

namespace pure_ysh {  // forward declare
  class Shvar;
  class ctx_Context;
  class Ctx;
  class PushRegisters;
  class Append;
}

namespace read_osh {  // forward declare
  class ctx_TermAttrs;
  class Read;
}

namespace readline_osh {  // forward declare
  class ctx_Keymap;
  class Bind;
  class History;
}

namespace trap_osh {  // forward declare
  class TrapState;
  class Trap;
}

namespace alloc {  // forward declare
  class ctx_SourceCode;
  class Arena;
  class LosslessArena;
  class DynamicArena;
}

namespace bash_impl {  // forward declare
}

namespace comp_ui {  // forward declare
  class PromptState;
  class State;
  class _IDisplay;
  class MinimalDisplay;
  class NiceDisplay;
}

namespace completion {  // forward declare
  class _RetryCompletion;
  class OptionState;
  class ctx_Completing;
  class Lookup;
  class Api;
  class CompletionAction;
  class UsersAction;
  class TestAction;
  class DynamicWordsAction;
  class FileSystemAction;
  class CommandAction;
  class ShellFuncAction;
  class VariablesAction;
  class ExportedVarsAction;
  class ExternalCommandAction;
  class _Predicate;
  class DefaultPredicate;
  class GlobPredicate;
  class UserSpec;
  class RootCompleter;
  class ReadlineCallback;
}

namespace dev {  // forward declare
  class CrashDumper;
  class ctx_Tracer;
  class MultiTracer;
  class Tracer;
}

namespace error {  // forward declare
  class _ErrorWithLocation;
  class Usage;
  class Parse;
  class WordFailure;
  class FailGlob;
  class VarSubFailure;
  class RedirectEval;
  class FatalRuntime;
  class Strict;
  class ErrExit;
  class Expr;
  class Structured;
  class AssertionErr;
  class TypeErrVerbose;
  class TypeErr;
  class Runtime;
  class Decode;
  class Encode;
}

namespace executor {  // forward declare
  class SearchPath;
  class _ProcessSubFrame;
  class ShellExecutor;
}

namespace main_loop {  // forward declare
  class ctx_Descriptors;
  class Headless;
}

namespace num {  // forward declare
}

namespace process {  // forward declare
  class ctx_FileCloser;
  class _RedirFrame;
  class _FdFrame;
  class FdState;
  class ChildStateChange;
  class StdinFromPipe;
  class StdoutToPipe;
  class SetPgid;
  class ExternalProgram;
  class Thunk;
  class ExternalThunk;
  class SubProgramThunk;
  class _HereDocWriterThunk;
  class Job;
  class Process;
  class ctx_Pipe;
  class Pipeline;
  class ctx_TerminalControl;
  class JobControl;
  class JobList;
  class Waiter;
}

namespace sh_init {  // forward declare
  class EnvConfig;
  class ShellFiles;
}

namespace state {  // forward declare
  class ctx_Source;
  class ctx_DebugTrap;
  class ctx_ErrTrap;
  class ctx_Option;
  class ctx_AssignBuiltin;
  class ctx_YshExpr;
  class ctx_ErrExit;
  class OptHook;
  class MutableOpts;
  class _ArgFrame;
  class ctx_FuncCall;
  class ctx_ProcCall;
  class ctx_Temp;
  class ctx_EnvObj;
  class ctx_Registers;
  class ctx_ThisDir;
  class ctx_LoopFrame;
  class ctx_EnclosedFrame;
  class ctx_ModuleEval;
  class ctx_Eval;
  class Mem;
  class Procs;
}

namespace util {  // forward declare
  class UserExit;
  class HistoryError;
  class _DebugFile;
  class NullDebugFile;
  class DebugFile;
}

namespace j8 {  // forward declare
  class InstancePrinter;
  class LexerDecoder;
  class _Parser;
  class Parser;
  class Nil8Parser;
  class J8LinesParser;
}

namespace j8_lite {  // forward declare
}

namespace ansi {  // forward declare
}

namespace pp_hnode {  // forward declare
  class BaseEncoder;
  class HNodeEncoder;
}

namespace pp_value {  // forward declare
  class ValueEncoder;
}

namespace pretty {  // forward declare
  class PrettyPrinter;
}

namespace ui {  // forward declare
  class ctx_Location;
  class ErrorFormatter;
}

namespace args {  // forward declare
  class _Attributes;
  class Reader;
  class _Action;
  class _ArgAction;
  class SetToInt;
  class SetToFloat;
  class SetToString;
  class SetAttachedBool;
  class SetToTrue;
  class SetOption;
  class SetNamedOption;
  class SetAction;
  class SetNamedAction;
}

namespace flag_util {  // forward declare
}

namespace lexer {  // forward declare
  class LineLexer;
  class Lexer;
}

namespace location {  // forward declare
}

namespace parse_lib {  // forward declare
  class _BaseTrail;
  class ctx_Alias;
  class Trail;
  class ParseContext;
}

namespace reader {  // forward declare
  class _Reader;
  class DisallowedLineReader;
  class FileLineReader;
  class VirtualLineReader;
  class InteractiveLineReader;
}

namespace syntax_abbrev {  // forward declare
}

namespace typed_args {  // forward declare
  class Reader;
}

namespace arith_parse {  // forward declare
}

namespace bool_parse {  // forward declare
  class BoolParser;
}

namespace braces {  // forward declare
  class _NotARange;
  class _RangeParser;
  class _StackFrame;
}

namespace cmd_eval {  // forward declare
  class Deps;
  class ctx_LoopLevel;
  class CommandEvaluator;
}

namespace cmd_parse {  // forward declare
  class VarChecker;
  class ctx_VarChecker;
  class ctx_CmdMode;
  class CommandParser;
}

namespace glob_ {  // forward declare
  class _GlobParser;
  class Globber;
}

namespace history {  // forward declare
  class Evaluator;
}

namespace prompt {  // forward declare
  class _PromptEvaluatorCache;
  class Evaluator;
  class UserPlugin;
}

namespace sh_expr_eval {  // forward declare
  class UnsafeArith;
  class ArithEvaluator;
  class BoolEvaluator;
}

namespace split {  // forward declare
  class SplitContext;
  class _BaseSplitter;
  class IfsSplitter;
}

namespace string_ops {  // forward declare
  class GlobReplacer;
}

namespace tdop {  // forward declare
  class TdopParser;
}

namespace word_ {  // forward declare
  class ctx_EmitDocToken;
  class ctx_Multiline;
}

namespace word_compile {  // forward declare
}

namespace word_eval {  // forward declare
  class StringWordEvaluator;
  class TildeEvaluator;
  class AbstractWordEvaluator;
  class NormalWordEvaluator;
  class CompletionWordEvaluator;
}

namespace word_parse {  // forward declare
  class WordEmitter;
  class WordParser;
}

namespace parse {  // forward declare
  class ParseError;
  class _StackItem;
  class Parser;
}

namespace os_path {  // forward declare
}

namespace fmt {  // forward declare
}

namespace ysh_ify {  // forward declare
  class Cursor;
  class YshPrinter;
}

namespace expr_eval {  // forward declare
  class ExprEvaluator;
  class EggexEvaluator;
}

namespace expr_parse {  // forward declare
  class ExprParser;
  class ctx_PNodeAllocator;
}

namespace expr_to_ast {  // forward declare
  class Transformer;
}

namespace func_proc {  // forward declare
}

namespace regex_translate {  // forward declare
}

namespace val_ops {  // forward declare
  class Iterator;
  class StdinIterator;
  class ArrayIter;
  class RangeIterator;
  class ListIterator;
  class DictIterator;
}

namespace bracket_osh {  // forward declare
  class _StringWordEmitter;
  class _WordEvaluator;
  class Test;
}

namespace completion_osh {  // forward declare
  class _FixedWordsAction;
  class _DynamicProcDictAction;
  class _DynamicStrDictAction;
  class SpecBuilder;
  class Complete;
  class CompGen;
  class CompOpt;
  class CompAdjust;
}

namespace shell {  // forward declare
  class ShellOptHook;
}

GLOBAL_STR(S_Aoo, "");
GLOBAL_STR(S_Bkk, "\u0000");
GLOBAL_STR(S_FDc, "\u0001");
GLOBAL_STR(S_ewA, "\u0002");
GLOBAL_STR(S_mve, "\t");
GLOBAL_STR(S_nfs, "\n");
GLOBAL_STR(S_sEF, "\n  ");
GLOBAL_STR(S_raD, "\r");
GLOBAL_STR(S_Avc, "\r\n");
GLOBAL_STR(S_yfk, "\u001b[0;0m");
GLOBAL_STR(S_tiy, "\u001b[1B");
GLOBAL_STR(S_aaF, "\u001b[1m");
GLOBAL_STR(S_wzl, "\u001b[2K");
GLOBAL_STR(S_sqm, "\u001b[31m");
GLOBAL_STR(S_eda, "\u001b[32m");
GLOBAL_STR(S_ysf, "\u001b[33m");
GLOBAL_STR(S_osl, "\u001b[34m");
GLOBAL_STR(S_vie, "\u001b[35m");
GLOBAL_STR(S_mmi, "\u001b[36m");
GLOBAL_STR(S_rpo, "\u001b[37m");
GLOBAL_STR(S_sCc, "\u001b[4m");
GLOBAL_STR(S_woy, "\u001b[7m");
GLOBAL_STR(S_yfw, " ");
GLOBAL_STR(S_jEs, " \t");
GLOBAL_STR(S_xvt, " \t\n");
GLOBAL_STR(S_ubu, " \t\n\"'><=;|&(:");
GLOBAL_STR(S_Dqk, " \n\r\t");
GLOBAL_STR(S_jqf, "  ");
GLOBAL_STR(S_dxy, "  (");
GLOBAL_STR(S_Fev, "  actions: ");
GLOBAL_STR(S_lCD, "  else: ");
GLOBAL_STR(S_gCB, "  extra: ");
GLOBAL_STR(S_kcc, "  predicate: ");
GLOBAL_STR(S_eyD, " \"\"\"");
GLOBAL_STR(S_ppj, " %-");
GLOBAL_STR(S_Brw, " '''");
GLOBAL_STR(S_sge, " (");
GLOBAL_STR(S_oBy, " (noclobber)");
GLOBAL_STR(S_dwC, " )\n");
GLOBAL_STR(S_dtA, " --> ");
GLOBAL_STR(S_gAe, " -> ");
GLOBAL_STR(S_Fem, " -i");
GLOBAL_STR(S_bcl, " ...");
GLOBAL_STR(S_sqv, " ... ");
GLOBAL_STR(S_ryc, " = ");
GLOBAL_STR(S_cDi, " `~!$&*()[]{}\\|;'\"<>?");
GLOBAL_STR(S_iCo, " {");
GLOBAL_STR(S_kao, "!");
GLOBAL_STR(S_krt, "\"");
GLOBAL_STR(S_epy, "\"\"\"\n");
GLOBAL_STR(S_xbi, "\"BashArray\",");
GLOBAL_STR(S_ojw, "\"BashAssoc\",");
GLOBAL_STR(S_vBu, "\"SparseArray\",");
GLOBAL_STR(S_eqo, "\"data\":");
GLOBAL_STR(S_EDa, "\"type\":");
GLOBAL_STR(S_qEd, "#");
GLOBAL_STR(S_eyr, "# +");
GLOBAL_STR(S_Czx, "$");
GLOBAL_STR(S_eaw, "$(");
GLOBAL_STR(S_igC, "$Argc");
GLOBAL_STR(S_jrh, "$[join(ARGV)]");
GLOBAL_STR(S_hqF, "${");
GLOBAL_STR(S_mys, "${SHX_indent}${SHX_punct}${SHX_pid_str} ");
GLOBAL_STR(S_dkr, "%");
GLOBAL_STR(S_bpf, "%%");
GLOBAL_STR(S_Dia, "%+");
GLOBAL_STR(S_aAh, "%-");
GLOBAL_STR(S_BDD, "%5s %15s %15s %7s  %s");
GLOBAL_STR(S_aia, "%H:%M");
GLOBAL_STR(S_rdE, "%X");
GLOBAL_STR(S_qAp, "%Y-%m-%d %H:%M:%S");
GLOBAL_STR(S_Ctu, "%end");
GLOBAL_STR(S_ubF, "%s");
GLOBAL_STR(S_DwB, "%start");
GLOBAL_STR(S_Bfw, "'");
GLOBAL_STR(S_oEq, "'\"");
GLOBAL_STR(S_wvB, "''");
GLOBAL_STR(S_oyy, "'''\n");
GLOBAL_STR(S_ijB, "(");
GLOBAL_STR(S_zxb, "()");
GLOBAL_STR(S_Ehr, "(...)");
GLOBAL_STR(S_AuE, "(test) ");
GLOBAL_STR(S_hxb, ")");
GLOBAL_STR(S_Ezk, ") ");
GLOBAL_STR(S_Fgw, "*");
GLOBAL_STR(S_jnE, "+");
GLOBAL_STR(S_Coy, "+=");
GLOBAL_STR(S_Cce, ",");
GLOBAL_STR(S_tgp, ", ");
GLOBAL_STR(S_izl, ", -");
GLOBAL_STR(S_Bjq, "-");
GLOBAL_STR(S_gpk, "--");
GLOBAL_STR(S_iEq, "---caper");
GLOBAL_STR(S_BAe, "--> ");
GLOBAL_STR(S_FaA, "--all-for-testing");
GLOBAL_STR(S_Ajh, "--all-provided");
GLOBAL_STR(S_pgr, "--dir");
GLOBAL_STR(S_rxp, "--exists");
GLOBAL_STR(S_cyo, "--false");
GLOBAL_STR(S_ugu, "--file");
GLOBAL_STR(S_Aua, "--help");
GLOBAL_STR(S_Arv, "--one-pass-parse requires noexec (-n)");
GLOBAL_STR(S_fic, "--pick");
GLOBAL_STR(S_ukB, "--symlink");
GLOBAL_STR(S_bAm, "--true");
GLOBAL_STR(S_caA, "--version");
GLOBAL_STR(S_zdb, "-1");
GLOBAL_STR(S_Fyt, "-A");
GLOBAL_STR(S_nvg, "-D");
GLOBAL_STR(S_Fha, "-E");
GLOBAL_STR(S_ltE, "-V");
GLOBAL_STR(S_nlm, "-a");
GLOBAL_STR(S_DEp, "-c");
GLOBAL_STR(S_vEu, "-d");
GLOBAL_STR(S_kyo, "-f");
GLOBAL_STR(S_Cbm, "-h");
GLOBAL_STR(S_ygA, "-n");
GLOBAL_STR(S_rba, "-o");
GLOBAL_STR(S_eed, "-s");
GLOBAL_STR(S_FqE, "-t");
GLOBAL_STR(S_ClA, "-u flag not implemented");
GLOBAL_STR(S_vtj, "-v");
GLOBAL_STR(S_cem, "-v expected name or name[index]");
GLOBAL_STR(S_Aru, ".");
GLOBAL_STR(S_mgF, ".*");
GLOBAL_STR(S_Dmc, "..");
GLOBAL_STR(S_otl, "...");
GLOBAL_STR(S_qcx, "... and %d more\n");
GLOBAL_STR(S_jhC, "..<");
GLOBAL_STR(S_rCp, ".config/oils");
GLOBAL_STR(S_Bge, ".|^$()+*?[]{}\\");
GLOBAL_STR(S_ckc, "/");
GLOBAL_STR(S_lFp, "//");
GLOBAL_STR(S_gEs, "///");
GLOBAL_STR(S_fyx, "///osh");
GLOBAL_STR(S_wqi, "///ysh");
GLOBAL_STR(S_wht, "/bin");
GLOBAL_STR(S_rhf, "/bin/sh");
GLOBAL_STR(S_kAx, "/bin:/usr/bin");
GLOBAL_STR(S_jpk, "/dev/tty");
GLOBAL_STR(S_zpA, "/sbin");
GLOBAL_STR(S_pEh, "/usr/bin");
GLOBAL_STR(S_gcD, "/usr/local/bin");
GLOBAL_STR(S_gFs, "/usr/local/sbin");
GLOBAL_STR(S_naE, "/usr/sbin");
GLOBAL_STR(S_wfw, "0");
GLOBAL_STR(S_vrA, "1");
GLOBAL_STR(S_Dfm, "13");
GLOBAL_STR(S_Apn, "14");
GLOBAL_STR(S_rFk, "15");
GLOBAL_STR(S_AEs, "2");
GLOBAL_STR(S_xtx, "3");
GLOBAL_STR(S_bEx, "6");
GLOBAL_STR(S_kqx, "9");
GLOBAL_STR(S_fyj, ":");
GLOBAL_STR(S_ows, ": ");
GLOBAL_STR(S_nbf, ";");
GLOBAL_STR(S_eox, "<");
GLOBAL_STR(S_iDd, "<<<");
GLOBAL_STR(S_uur, "<Ctrl-C>");
GLOBAL_STR(S_peu, "<Error: %s> ");
GLOBAL_STR(S_FDn, "<INVALID CR>");
GLOBAL_STR(S_ylr, "<INVALID NEWLINE>");
GLOBAL_STR(S_bby, "=");
GLOBAL_STR(S_Fos, "=word isn't allowed.  Hint: add a space after =, or quote it");
GLOBAL_STR(S_jye, ">");
GLOBAL_STR(S_olB, "> ");
GLOBAL_STR(S_BAk, "?");
GLOBAL_STR(S_nrb, "???");
GLOBAL_STR(S_Cyx, "???CompletionAction ");
GLOBAL_STR(S_typ, "???Predicate ");
GLOBAL_STR(S_AeE, "@");
GLOBAL_STR(S_xsa, "@(");
GLOBAL_STR(S_Fyz, "@ARGV");
GLOBAL_STR(S_nlt, "A");
GLOBAL_STR(S_ktk, "A target field is required");
GLOBAL_STR(S_wjA, "ARGV");
GLOBAL_STR(S_ztv, "AST not printed.");
GLOBAL_STR(S_cFt, "Argv iteration expects at most 2 loop variables");
GLOBAL_STR(S_ijz, "Assoc array keys must be strings: $x 'x' \"$x\" etc. (OILS-ERR-101)");
GLOBAL_STR(S_tDq, "Attempted to exit from completion hook.");
GLOBAL_STR(S_aEF, "BASHPID");
GLOBAL_STR(S_Dyf, "BASH_LINENO");
GLOBAL_STR(S_Erl, "BASH_REMATCH");
GLOBAL_STR(S_lqk, "BASH_SOURCE");
GLOBAL_STR(S_Fkp, "Backtracking with !! isn't implemented (requires Python/PCRE)");
GLOBAL_STR(S_ulC, "Bash (( not allowed in YSH (parse_dparen, see OILS-ERR-14 for wart)");
GLOBAL_STR(S_mEk, "Bash [[ not allowed in YSH (parse_dbracket)");
GLOBAL_STR(S_laa, "Bash for loops aren't allowed (parse_dparen)");
GLOBAL_STR(S_tDu, "BashArray");
GLOBAL_STR(S_Agv, "BashAssoc");
GLOBAL_STR(S_DCt, "Binary int constant is too large");
GLOBAL_STR(S_stA, "Block param must have type Command");
GLOBAL_STR(S_neb, "Brace expansion not allowed (try adding quotes)");
GLOBAL_STR(S_sjc, "C");
GLOBAL_STR(S_xFC, "CHDIR");
GLOBAL_STR(S_fot, "COMPREPLY");
GLOBAL_STR(S_FiD, "COMP_ARGV");
GLOBAL_STR(S_uox, "COMP_ARGV should be an array");
GLOBAL_STR(S_xcm, "COMP_CWORD");
GLOBAL_STR(S_Ann, "COMP_LINE");
GLOBAL_STR(S_slv, "COMP_POINT");
GLOBAL_STR(S_uhz, "COMP_WORDBREAKS");
GLOBAL_STR(S_yDB, "COMP_WORDS");
GLOBAL_STR(S_vqy, "CPU seconds");
GLOBAL_STR(S_jrg, "Call");
GLOBAL_STR(S_hjj, "Can't accept both block expression and block literal");
GLOBAL_STR(S_ibk, "Can't append an associative array to an indexed array");
GLOBAL_STR(S_hmo, "Can't append an assoxiative array to indexed arrays");
GLOBAL_STR(S_Foc, "Can't append array to string");
GLOBAL_STR(S_waA, "Can't append string to array");
GLOBAL_STR(S_rCi, "Can't append string to associative arrays");
GLOBAL_STR(S_Crq, "Can't assign to items in a string");
GLOBAL_STR(S_kxq, "Can't assign to readonly array");
GLOBAL_STR(S_zFl, "Can't assign to readonly associative array");
GLOBAL_STR(S_tvk, "Can't assign to special variable");
GLOBAL_STR(S_rcx, "Can't assign to this attribute expr");
GLOBAL_STR(S_gzm, "Can't assign to this expression");
GLOBAL_STR(S_kdC, "Can't encode value of type Obj");
GLOBAL_STR(S_Clv, "Can't left shift by negative number");
GLOBAL_STR(S_kfs, "Can't negate this symbol");
GLOBAL_STR(S_lvB, "Can't redirect to more than one file");
GLOBAL_STR(S_abx, "Can't redirect to zero files");
GLOBAL_STR(S_tDc, "Can't right shift by negative number");
GLOBAL_STR(S_dzk, "Can't run a proc while errexit is disabled. Use 'try' or wrap it in a process with $0 myproc");
GLOBAL_STR(S_hlA, "Can't run assignment builtin recursively");
GLOBAL_STR(S_gxh, "Can't slice associative arrays");
GLOBAL_STR(S_jul, "Can't substitute into word");
GLOBAL_STR(S_egA, "Code point can't be greater than U+10ffff");
GLOBAL_STR(S_jma, "Command");
GLOBAL_STR(S_kvC, "Command conditionals should only have one status, not %s (strict_errexit, OILS-ERR-300)");
GLOBAL_STR(S_Awe, "Command evaluated to an empty argv array");
GLOBAL_STR(S_wac, "Comments aren't part of JSON; you may want 'json8 read'");
GLOBAL_STR(S_Aat, "Completing redirect arg");
GLOBAL_STR(S_qcr, "Completing words");
GLOBAL_STR(S_rgn, "Connect stdin and stdout to one end of socketpair() and send control messages.  osh writes debug messages (like this one) to stderr.");
GLOBAL_STR(S_wcu, "Const");
GLOBAL_STR(S_AAe, "Container place not implemented");
GLOBAL_STR(S_qlz, "Control flow shouldn't have environment bindings");
GLOBAL_STR(S_Biw, "Control flow shouldn't have redirects");
GLOBAL_STR(S_mci, "Could not find a context. Did you forget to 'ctx push'?");
GLOBAL_STR(S_pss, "Couldn't find terminator for here doc that starts here");
GLOBAL_STR(S_qgA, "Ctrl-C in completion");
GLOBAL_STR(S_qks, "D");
GLOBAL_STR(S_Fzz, "DEBUG");
GLOBAL_STR(S_vnc, "DESC");
GLOBAL_STR(S_lAz, "DQ");
GLOBAL_STR(S_jgg, "Decimal int constant is too large");
GLOBAL_STR(S_kyn, "Default val for word param must be Str");
GLOBAL_STR(S_jhk, "Default value for block should be Command or Null");
GLOBAL_STR(S_BCt, "Default values can't be mutable");
GLOBAL_STR(S_qAa, "DefaultPredicate ");
GLOBAL_STR(S_BAA, "Destructuring assignment expected List");
GLOBAL_STR(S_vbu, "Dict");
GLOBAL_STR(S_xnB, "Dict index expected Str");
GLOBAL_STR(S_slu, "Dict index should be Str");
GLOBAL_STR(S_ldu, "Dict key should be Str");
GLOBAL_STR(S_mbB, "Dict keys must be strings");
GLOBAL_STR(S_gFA, "Didn't find anything to complete");
GLOBAL_STR(S_mup, "Didn't get a string from redir arg");
GLOBAL_STR(S_Bdr, "Divide by zero");
GLOBAL_STR(S_tcf, "Divisor can't be negative");
GLOBAL_STR(S_koi, "DynamicProcDictAction ");
GLOBAL_STR(S_mdp, "DynamicStrDictAction ");
GLOBAL_STR(S_Ayq, "DynamicWordsAction ");
GLOBAL_STR(S_iyA, "ENV");
GLOBAL_STR(S_ngj, "EOF");
GLOBAL_STR(S_nhc, "EOF received");
GLOBAL_STR(S_ith, "ERE");
GLOBAL_STR(S_zDr, "ERR");
GLOBAL_STR(S_vrm, "EUID");
GLOBAL_STR(S_crw, "EVAL");
GLOBAL_STR(S_BDg, "EXIT");
GLOBAL_STR(S_iBE, "Eggex char class splice expected Str");
GLOBAL_STR(S_rDB, "Eggex splice expected Str or Eggex");
GLOBAL_STR(S_ACD, "Empty arg list not allowed");
GLOBAL_STR(S_dyg, "Empty file with EOF token on invalid line:");
GLOBAL_STR(S_pqq, "Environment binding shouldn't look like an array assignment");
GLOBAL_STR(S_fts, "Environment bindings can't contain array literals");
GLOBAL_STR(S_ilD, "Equality isn't defined on Float values (OILS-ERR-202)");
GLOBAL_STR(S_jcv, "Error expanding tilde (e.g. invalid user)");
GLOBAL_STR(S_sai, "Expected 'proc' after 'typed'");
GLOBAL_STR(S_oCF, "Expected ( after =");
GLOBAL_STR(S_kCu, "Expected ( in typed return");
GLOBAL_STR(S_rbz, "Expected ) in function definition");
GLOBAL_STR(S_ine, "Expected ) to close");
GLOBAL_STR(S_paD, "Expected ) to close bash regex group");
GLOBAL_STR(S_nfD, "Expected ) to end for loop expression");
GLOBAL_STR(S_rfk, "Expected 3 file descriptors");
GLOBAL_STR(S_jod, "Expected : or } in slice");
GLOBAL_STR(S_lun, "Expected ; here");
GLOBAL_STR(S_qsa, "Expected ;; or esac");
GLOBAL_STR(S_amq, "Expected = in environment binding, got +=");
GLOBAL_STR(S_sBF, "Expected BashArray or BashAssoc");
GLOBAL_STR(S_nAf, "Expected Str or Regex for RHS of ~");
GLOBAL_STR(S_sxv, "Expected ] to close subscript");
GLOBAL_STR(S_qul, "Expected ]]");
GLOBAL_STR(S_xDn, "Expected a condition");
GLOBAL_STR(S_Fwi, "Expected a constant argument");
GLOBAL_STR(S_tbs, "Expected a function or method");
GLOBAL_STR(S_oij, "Expected a printf format character");
GLOBAL_STR(S_jgs, "Expected a verb (push, set, emit)");
GLOBAL_STR(S_brv, "Expected a word to match against");
GLOBAL_STR(S_qsa_1, "Expected argument for action");
GLOBAL_STR(S_ntr, "Expected associative array pair");
GLOBAL_STR(S_yDB_1, "Expected at most 2 loop variables");
GLOBAL_STR(S_vky, "Expected case pattern");
GLOBAL_STR(S_txD, "Expected end of var ref expression");
GLOBAL_STR(S_nnd, "Expected loop variable (a constant word)");
GLOBAL_STR(S_mhw, "Expected name=value");
GLOBAL_STR(S_cFv, "Expected pos_args to be a List of Strs");
GLOBAL_STR(S_Fgl, "Expected second ) to end arith statement");
GLOBAL_STR(S_fsD, "Expected second ) to end arith sub");
GLOBAL_STR(S_smu, "Expected var name");
GLOBAL_STR(S_see, "Expected word after ( opening bash regex group");
GLOBAL_STR(S_Fhm, "Expected { after iterable expression");
GLOBAL_STR(S_mpl, "Expected } after length expression");
GLOBAL_STR(S_fpg, "Expected } to close ${");
GLOBAL_STR(S_kds, "Expected: ");
GLOBAL_STR(S_abr, "Exponent can't be a negative number");
GLOBAL_STR(S_fcy, "Expr splice ");
GLOBAL_STR(S_wjw, "Expr sub ");
GLOBAL_STR(S_mjw, "Expression isn't true");
GLOBAL_STR(S_hzl, "Extended glob not allowed in this word");
GLOBAL_STR(S_eez, "Extended glob won't work without FNM_EXTMATCH support in libc");
GLOBAL_STR(S_cli, "Extended globs and arrays can't appear in the same word");
GLOBAL_STR(S_fFs, "ExternalCommandAction ");
GLOBAL_STR(S_gFh, "F");
GLOBAL_STR(S_aos, "FACTOR");
GLOBAL_STR(S_kDk, "FLAG");
GLOBAL_STR(S_lCr, "FUNCNAME");
GLOBAL_STR(S_xmt, "False");
GLOBAL_STR(S_snq, "Fat arrow => expects method or function");
GLOBAL_STR(S_ddv, "Fatal error in posix.fork()");
GLOBAL_STR(S_ezz, "FileSystemAction ");
GLOBAL_STR(S_Ekf, "FixedWordsAction ");
GLOBAL_STR(S_dwl, "Flag can't be negated");
GLOBAL_STR(S_Cqq, "GETPID");
GLOBAL_STR(S_fam, "Generator expression reserved but not implemented");
GLOBAL_STR(S_uyp, "GlobPredicate ");
GLOBAL_STR(S_xCr, "Got -A but RHS isn't an associative array");
GLOBAL_STR(S_rtt, "Got -a but RHS isn't an array");
GLOBAL_STR(S_AbA, "Got 124, trying again ...");
GLOBAL_STR(S_CCx, "Got [ without ]");
GLOBAL_STR(S_hoz, "Got unescaped right bracket");
GLOBAL_STR(S_btq, "Got unescaped trailing backslash");
GLOBAL_STR(S_poi, "Got:      ");
GLOBAL_STR(S_ClC, "H");
GLOBAL_STR(S_avA, "HARD");
GLOBAL_STR(S_omw, "HISTFILE");
GLOBAL_STR(S_xlm, "HOME");
GLOBAL_STR(S_aqr, "HOSTNAME");
GLOBAL_STR(S_jwu, "Here docs aren't allowed in expressions");
GLOBAL_STR(S_uDt, "Hex int constant is too large");
GLOBAL_STR(S_nie, "IFS");
GLOBAL_STR(S_dyC, "IFS shouldn't be an array");
GLOBAL_STR(S_BvB, "INFINITY");
GLOBAL_STR(S_qul_1, "Illegal array word part (strict_array)");
GLOBAL_STR(S_xCq, "Index op expected BashArray, BashAssoc");
GLOBAL_STR(S_hsF, "IntControlFlow in func");
GLOBAL_STR(S_zDl, "Integer is too big");
GLOBAL_STR(S_rdm, "Invalid KSH-style function name");
GLOBAL_STR(S_deg, "Invalid LHS to modify");
GLOBAL_STR(S_scC, "Invalid argument to unary operator");
GLOBAL_STR(S_bio, "Invalid backtick: use $(cmd) or \\` in YSH strings");
GLOBAL_STR(S_gba, "Invalid blank line in multiline mode");
GLOBAL_STR(S_ago, "Invalid block expression argument");
GLOBAL_STR(S_vla, "Invalid char escape in C-style string literal (OILS-ERR-11)");
GLOBAL_STR(S_Bpn, "Invalid char escape in double quoted string (OILS-ERR-12)");
GLOBAL_STR(S_dEh, "Invalid char escape in unquoted word (OILS-ERR-13)");
GLOBAL_STR(S_pBu, "Invalid control flow at top level");
GLOBAL_STR(S_rbb, "Invalid function name");
GLOBAL_STR(S_xco, "Invalid here doc delimiter");
GLOBAL_STR(S_dDj, "Invalid hex escape in YSH string (must be \\xHH)");
GLOBAL_STR(S_myB, "Invalid printf format character");
GLOBAL_STR(S_ucF, "Invalid start of UTF-8 sequence");
GLOBAL_STR(S_apz, "Invalid token after redirect operator");
GLOBAL_STR(S_qDr, "Invalid token in bash regex");
GLOBAL_STR(S_iFj, "Invalid trailing comma");
GLOBAL_STR(S_aug, "Invalid var ref expression");
GLOBAL_STR(S_kmo, "Invalid word after for expression");
GLOBAL_STR(S_pnr, "Invalid word in for loop");
GLOBAL_STR(S_fgr, "Invalid word while parsing command");
GLOBAL_STR(S_wpb, "Invalid word while parsing command list");
GLOBAL_STR(S_Czs, "J8");
GLOBAL_STR(S_ApC, "J8 Lines can't have unescaped ASCII control chars");
GLOBAL_STR(S_dqg, "JSON");
GLOBAL_STR(S_uwF, "Keys op expected Str");
GLOBAL_STR(S_lgF, "LHS array not allowed in assignment builtin");
GLOBAL_STR(S_hdl, "LHS must be Str");
GLOBAL_STR(S_Atu, "LHS must be a string");
GLOBAL_STR(S_lnF, "LHS of 'in' should be Str");
GLOBAL_STR(S_kFk, "LIB_OSH");
GLOBAL_STR(S_llF, "LIB_YSH");
GLOBAL_STR(S_Fvt, "LINENO");
GLOBAL_STR(S_Fpe, "Lambda reserved but not implemented");
GLOBAL_STR(S_avA_1, "Left operand should be Int");
GLOBAL_STR(S_kkj, "Left-hand side of this assignment is invalid");
GLOBAL_STR(S_grF, "Length op expected Str, BashArray, BashAssoc");
GLOBAL_STR(S_bDF, "List");
GLOBAL_STR(S_hon, "List comprehension reserved but not implemented");
GLOBAL_STR(S_dAC, "List index expected Int");
GLOBAL_STR(S_jFB, "List index expected Int, Str, or Slice");
GLOBAL_STR(S_cfe, "List index should be Int");
GLOBAL_STR(S_hFl, "List iteration expects at most 2 loop variables");
GLOBAL_STR(S_oex, "Literal $ should be quoted like \\$");
GLOBAL_STR(S_oFq, "Literal @ starting a word must be quoted (parse_at_all)");
GLOBAL_STR(S_Fnf, "Loop and control flow can't be in different processes");
GLOBAL_STR(S_dwa, "Loop variables look like x, y (fix spaces)");
GLOBAL_STR(S_qiD, "M/");
GLOBAL_STR(S_gzE, "M/accum");
GLOBAL_STR(S_ywz, "M/append");
GLOBAL_STR(S_trz, "M/clear");
GLOBAL_STR(S_bFs, "M/erase");
GLOBAL_STR(S_naE_1, "M/eval");
GLOBAL_STR(S_hqr, "M/evalExpr");
GLOBAL_STR(S_usa, "M/evalInFrame");
GLOBAL_STR(S_ohv, "M/evalToDict");
GLOBAL_STR(S_qCf, "M/extend");
GLOBAL_STR(S_hFC, "M/inc");
GLOBAL_STR(S_yEv, "M/insert");
GLOBAL_STR(S_yhj, "M/pop");
GLOBAL_STR(S_zkE, "M/remove");
GLOBAL_STR(S_zvj, "M/reverse");
GLOBAL_STR(S_CEu, "M/setValue");
GLOBAL_STR(S_sqm_1, "MAIN");
GLOBAL_STR(S_nfF, "MAPFILE");
GLOBAL_STR(S_cdp, "Main");
GLOBAL_STR(S_idh, "Malformed character class; treating as literal");
GLOBAL_STR(S_eio, "Mismatched cases in character range");
GLOBAL_STR(S_zxn, "Missing required applet name.");
GLOBAL_STR(S_ecq, "Module is missing __provide__ List");
GLOBAL_STR(S_Dtp, "Multiple assignment must use =");
GLOBAL_STR(S_ywk, "NAN");
GLOBAL_STR(S_vvs, "Negation expected Int or Float");
GLOBAL_STR(S_Dwc, "No regex capture groups");
GLOBAL_STR(S_oln, "Not equal");
GLOBAL_STR(S_mnf, "Not implemented");
GLOBAL_STR(S_kyd, "OILS_CRASH_DUMP_DIR");
GLOBAL_STR(S_aBF, "OILS_DEBUG_DIR");
GLOBAL_STR(S_dDv, "OILS_HIJACK_SHEBANG");
GLOBAL_STR(S_jam, "OILS_TRACE_DIR");
GLOBAL_STR(S_flz, "OILS_TRACE_DUMPS");
GLOBAL_STR(S_ayE, "OILS_TRACE_STREAMS");
GLOBAL_STR(S_knB, "OILS_VERSION");
GLOBAL_STR(S_iCt, "OIL_VERSION");
GLOBAL_STR(S_FAo, "OLDPWD");
GLOBAL_STR(S_apD, "OPTARG");
GLOBAL_STR(S_fdf, "OPTIND");
GLOBAL_STR(S_hBE, "OSTYPE");
GLOBAL_STR(S_fBA, "Obj __index__ method detected a broken type Obj invariant");
GLOBAL_STR(S_nlA, "Obj __index__ method expected Obj or List");
GLOBAL_STR(S_uny, "Obj attribute should be Str");
GLOBAL_STR(S_eBt, "Obj index should be Str");
GLOBAL_STR(S_iza, "Object");
GLOBAL_STR(S_zhC, "Object() expected Obj or Null");
GLOBAL_STR(S_rDF, "Octal int constant is too large");
GLOBAL_STR(S_sxs, "Only 1 block param is allowed");
GLOBAL_STR(S_ntu, "Only strings can be exported (strict_array)");
GLOBAL_STR(S_Agb, "PARSE");
GLOBAL_STR(S_jip, "PATH");
GLOBAL_STR(S_evu, "PIPESTATUS");
GLOBAL_STR(S_gik, "POSIX EREs don't have groups without capture, so this node needs () around it.");
GLOBAL_STR(S_xil, "POSIX classes can't be negated in ERE");
GLOBAL_STR(S_tFx, "POSIX shell arithmetic isn't allowed (parse_sh_arith)");
GLOBAL_STR(S_gnu, "PPID");
GLOBAL_STR(S_agy, "PROMPT_COMMAND");
GLOBAL_STR(S_Eni, "PS1");
GLOBAL_STR(S_zyo, "PS4");
GLOBAL_STR(S_xxp, "PWD");
GLOBAL_STR(S_ouz, "Parse error in recursive arithmetic");
GLOBAL_STR(S_fFn, "Pat Sub op expected Str, BashArray, BashAssoc");
GLOBAL_STR(S_rqm, "Perl classes can't be negated in ERE");
GLOBAL_STR(S_aBC, "Perl-style repetition isn't implemented with libc");
GLOBAL_STR(S_ynk, "Places in containers not implemented yet");
GLOBAL_STR(S_gur, "PopEnvObj: env.prototype is null");
GLOBAL_STR(S_Fpz, "Positional arg can't appear in group of named args");
GLOBAL_STR(S_jbj, "Process subs not allowed here because status wouldn't be checked (strict_errexit)");
GLOBAL_STR(S_Ahj, "Pure JSON does not accept j\"\" prefix");
GLOBAL_STR(S_lBE, "Quoted range char can't be empty");
GLOBAL_STR(S_wma, "REPLY");
GLOBAL_STR(S_AEu, "RETURN");
GLOBAL_STR(S_fEm, "RHS must be Str");
GLOBAL_STR(S_idc, "RHS of 'in' should be Dict");
GLOBAL_STR(S_tkz, "Range begin should be Int");
GLOBAL_STR(S_rgA, "Range end should be Int");
GLOBAL_STR(S_pix, "Range iteration expects at most 2 loop variables");
GLOBAL_STR(S_Fza, "Range start/end shouldn't have more than one character");
GLOBAL_STR(S_rxx, "Recursive 'hay eval' not allowed");
GLOBAL_STR(S_Cjh, "Redirect descriptor can't be empty");
GLOBAL_STR(S_ioh, "Ref");
GLOBAL_STR(S_cEx, "Reserved syntax");
GLOBAL_STR(S_Cab, "Rest param isn't allowed for blocks");
GLOBAL_STR(S_Epo, "Right operand should be Int");
GLOBAL_STR(S_qsm, "Running Oil in ---caper mode");
GLOBAL_STR(S_xFB, "SECONDS");
GLOBAL_STR(S_hhx, "SETENV");
GLOBAL_STR(S_cvm, "SHELLOPTS");
GLOBAL_STR(S_lra, "SHX_indent");
GLOBAL_STR(S_zbC, "SHX_pid_str");
GLOBAL_STR(S_vki, "SHX_punct");
GLOBAL_STR(S_avu, "SIG");
GLOBAL_STR(S_zrq, "SOFT");
GLOBAL_STR(S_mip, "SQ");
GLOBAL_STR(S_dne, "ShAssignment builtins don't accept blocks");
GLOBAL_STR(S_hur, "Shell-style returns not allowed here");
GLOBAL_STR(S_ndb, "Shouldn't have been bound");
GLOBAL_STR(S_eih, "Shouldn't have called this");
GLOBAL_STR(S_qov, "Signal or trap");
GLOBAL_STR(S_hBp, "Single quotes aren't part of JSON; you may want 'json8 read'");
GLOBAL_STR(S_ljh, "Slice begin should be Int");
GLOBAL_STR(S_vEo, "Slice end should be Int");
GLOBAL_STR(S_npc, "Slice length: Add explicit zero, or omit : for N (strict_parse_slice)");
GLOBAL_STR(S_sDc, "Slice op expected Str or BashArray");
GLOBAL_STR(S_nli, "Source");
GLOBAL_STR(S_ezD, "Space required before (");
GLOBAL_STR(S_qFf, "SparseArray");
GLOBAL_STR(S_etk, "Splice ");
GLOBAL_STR(S_fFz, "Spread expected a Dict");
GLOBAL_STR(S_yys, "Spread expected a List");
GLOBAL_STR(S_ruu, "Stdin iteration expects at most 2 loop variables");
GLOBAL_STR(S_Cwz, "Step can't be 0");
GLOBAL_STR(S_DsF, "Str");
GLOBAL_STR(S_sAl, "Str index expected Int or Slice");
GLOBAL_STR(S_mrm, "Strings with backslashes should look like r'\\n' or u'\\n' or b'\\n'");
GLOBAL_STR(S_lct, "Subscript expected one of (Str List Dict, indexable Obj)");
GLOBAL_STR(S_juC, "Subscript/Attribute not allowed on this LHS expression");
GLOBAL_STR(S_BpF, "Surround this word with either parens or quotes (parse_bare_word)");
GLOBAL_STR(S_wmA, "Syntax error in parseCommand()");
GLOBAL_STR(S_CrF, "Syntax options must be set at the top level (outside any function)");
GLOBAL_STR(S_cor, "T");
GLOBAL_STR(S_wfg, "TODO");
GLOBAL_STR(S_jpy, "TODO-complete-C");
GLOBAL_STR(S_hzv, "TODO: ${.myproc builtin sub}");
GLOBAL_STR(S_Esj, "TODO: --all-for testing not implemented");
GLOBAL_STR(S_Awz, "TODO: --all-provided not implemented");
GLOBAL_STR(S_rsr, "TODO: @{.myproc builtin sub}");
GLOBAL_STR(S_vtm, "TODO: extern");
GLOBAL_STR(S_ksm, "TODO: invoke");
GLOBAL_STR(S_Ejt, "TODO:PARSE");
GLOBAL_STR(S_afz, "TODO:signals");
GLOBAL_STR(S_wqm, "TSV8 format not implemented");
GLOBAL_STR(S_nhf, "TZ");
GLOBAL_STR(S_mAD, "TestAction ");
GLOBAL_STR(S_fAu, "The [ operator doesn't apply to this expression");
GLOBAL_STR(S_tjF, "This is a constant string.  You may want a variable like $x (parse_bare_word)");
GLOBAL_STR(S_dlq, "This kind of class literal term isn't implemented");
GLOBAL_STR(S_Emv, "This word should yield a string, but it contains an array");
GLOBAL_STR(S_vvB, "Token can't be used in infix position");
GLOBAL_STR(S_Dmb, "Token can't be used in prefix position");
GLOBAL_STR(S_iCm, "True");
GLOBAL_STR(S_vuh, "Typed return doesn't take named arguments");
GLOBAL_STR(S_jkf, "Typed return expects one argument");
GLOBAL_STR(S_Bpo, "Typed return is only allowed inside func");
GLOBAL_STR(S_zwr, "UID");
GLOBAL_STR(S_qmF, "UTF-8 decode: Bad encoding");
GLOBAL_STR(S_imE, "UTF-8 decode: Integer too large");
GLOBAL_STR(S_Fqc, "UTF-8 decode: Overlong");
GLOBAL_STR(S_rof, "UTF-8 decode: Surrogate range");
GLOBAL_STR(S_qsv, "UTF-8 decode: Truncated bytes");
GLOBAL_STR(S_kpo, "Unary op expected Str, BashArray, BashAssoc");
GLOBAL_STR(S_fcg, "Unbalanced \\[ and \\]");
GLOBAL_STR(S_ukc, "Undefined value in arithmetic context");
GLOBAL_STR(S_hlc, "Unexpected = (Hint: use var/setvar, or quote it)");
GLOBAL_STR(S_acC, "Unexpected EOF in single-quoted string that began here");
GLOBAL_STR(S_fip, "Unexpected EOF reading double-quoted string that began here");
GLOBAL_STR(S_ilx, "Unexpected EOF reading extended glob that began here");
GLOBAL_STR(S_edt, "Unexpected EOF while looking for closing backtick");
GLOBAL_STR(S_agx, "Unexpected EOF while parsing command");
GLOBAL_STR(S_Arg, "Unexpected array literal");
GLOBAL_STR(S_aya, "Unexpected associative array literal");
GLOBAL_STR(S_dtB, "Unexpected end of input");
GLOBAL_STR(S_bjp, "Unexpected left paren (might need a space before it)");
GLOBAL_STR(S_imw, "Unexpected parts after triple quoted string");
GLOBAL_STR(S_avi, "Unexpected right brace");
GLOBAL_STR(S_rip, "Unexpected token after @()");
GLOBAL_STR(S_AAk, "Unexpected token after Expr splice");
GLOBAL_STR(S_wxA, "Unexpected token after YSH single-quoted string");
GLOBAL_STR(S_bbn, "Unexpected token after array literal");
GLOBAL_STR(S_evz, "Unexpected token after array splice");
GLOBAL_STR(S_tBm, "Unexpected token in ${}");
GLOBAL_STR(S_Ayd, "Unexpected token in array literal");
GLOBAL_STR(S_oDA, "Unexpected trailing input");
GLOBAL_STR(S_mfF, "Unexpected trailing input in J8 Lines");
GLOBAL_STR(S_noa, "Unexpected type parameters");
GLOBAL_STR(S_meF, "Unexpected typed args");
GLOBAL_STR(S_zfb, "Unexpected word after 3 loop variables");
GLOBAL_STR(S_cAo, "Unexpected word after for loop variable");
GLOBAL_STR(S_lrE, "Unexpected word when parsing command");
GLOBAL_STR(S_rBg, "Units suffix not implemented");
GLOBAL_STR(S_aei, "Unknown redirect op");
GLOBAL_STR(S_lut, "Unknown redirect type");
GLOBAL_STR(S_tce, "Unterminated here doc began here");
GLOBAL_STR(S_Aeo, "Use $(cmd) instead of backticks (parse_backticks)");
GLOBAL_STR(S_hkt, "Use 'and' in expression mode (OILS-ERR-15)");
GLOBAL_STR(S_yww, "Use 'or' in expression mode (OILS-ERR-15)");
GLOBAL_STR(S_bkb, "Use ..< for half-open range, or ..= for closed range (OILS-ERR-16)");
GLOBAL_STR(S_qbb, "Use === to be exact, or ~== to convert types");
GLOBAL_STR(S_jmF, "Use \\xhh or \\u{...} instead of octal escapes in YSH strings");
GLOBAL_STR(S_xih, "Use var/setvar to assign in YSH");
GLOBAL_STR(S_oEg, "UserAction ");
GLOBAL_STR(S_Duk, "VOp2");
GLOBAL_STR(S_hgF, "VOp3");
GLOBAL_STR(S_zjj, "Value isn't true: ");
GLOBAL_STR(S_Asx, "Value of type Str can't be indexed (strict_arith)");
GLOBAL_STR(S_dyb, "Value of type Undef can't be indexed (strict_arith)");
GLOBAL_STR(S_CsA, "Var");
GLOBAL_STR(S_lyf, "Var Ref op expected Str");
GLOBAL_STR(S_adr, "VariablesAction ");
GLOBAL_STR(S_cpq, "W");
GLOBAL_STR(S_DBo, "Warning: OSH doesn't implement flags -l or -u (shopt --set ignore_flags_not_impl)");
GLOBAL_STR(S_fEB, "Word eval ");
GLOBAL_STR(S_tvz, "Word has unbalanced { }.  Maybe add a space or quote it like \\{");
GLOBAL_STR(S_Bej, "Word params may only have type Str or Ref");
GLOBAL_STR(S_awm, "X");
GLOBAL_STR(S_xiB, "YSH_HISTFILE");
GLOBAL_STR(S_qCh, "Z");
GLOBAL_STR(S_bvg, "ZSH var subs are parsed, but can't be evaluated");
GLOBAL_STR(S_Eax, "[");
GLOBAL_STR(S_wxv, "[ -c flag ]");
GLOBAL_STR(S_jgf, "[ headless ]");
GLOBAL_STR(S_odD, "[ interactive ]");
GLOBAL_STR(S_Aek, "[...]");
GLOBAL_STR(S_eEf, "[:alpha:][:digit:]_");
GLOBAL_STR(S_Bro, "[:digit:]");
GLOBAL_STR(S_khq, "[:space:]");
GLOBAL_STR(S_jDw, "[Commands]\n");
GLOBAL_STR(S_chm, "[Patterns]\n");
GLOBAL_STR(S_xmu, "[]");
GLOBAL_STR(S_fhC, "[here doc writer]");
GLOBAL_STR(S_zxF, "[pipeline debug info]\n");
GLOBAL_STR(S_xfq, "[process debug info]\n");
GLOBAL_STR(S_iyu, "\\");
GLOBAL_STR(S_xEe, "\\$");
GLOBAL_STR(S_hpd, "\\*?[]-:!()|");
GLOBAL_STR(S_iCa, "\\-");
GLOBAL_STR(S_ivk, "\\?*+{}^$.()|[]");
GLOBAL_STR(S_uDe, "\\D{} not in promptVal()");
GLOBAL_STR(S_uDk, "\\[");
GLOBAL_STR(S_Eef, "\\\\");
GLOBAL_STR(S_dkw, "\\]");
GLOBAL_STR(S_bxh, "\\s-\\v\\$ ");
GLOBAL_STR(S_pcD, "]");
GLOBAL_STR(S_nuz, "]=");
GLOBAL_STR(S_EAB, "^");
GLOBAL_STR(S_neq, "^\n");
GLOBAL_STR(S_coi, "^%([0-9]+)$");
GLOBAL_STR(S_gch, "^D");
GLOBAL_STR(S_tci, "_");
GLOBAL_STR(S_Cet, "__");
GLOBAL_STR(S_hub, "__E__");
GLOBAL_STR(S_FwA, "__NO_COMMAND_SUB__");
GLOBAL_STR(S_Chb, "__NO_PROCESS_SUB__");
GLOBAL_STR(S_aEE, "___ GC: after parsing");
GLOBAL_STR(S_gfw, "___ GC: after printing");
GLOBAL_STR(S_mmF, "__builtins__");
GLOBAL_STR(S_swp, "__cat");
GLOBAL_STR(S_avA_2, "__defaults__");
GLOBAL_STR(S_myz, "__dumpdoc");
GLOBAL_STR(S_jaj, "__fallback");
GLOBAL_STR(S_lgv, "__first");
GLOBAL_STR(S_EBt, "__hack__");
GLOBAL_STR(S_opF, "__index__");
GLOBAL_STR(S_fBo, "__invoke__");
GLOBAL_STR(S_zcz, "__provide__");
GLOBAL_STR(S_oCh, "_a2sp");
GLOBAL_STR(S_iii, "_end");
GLOBAL_STR(S_zzh, "_error");
GLOBAL_STR(S_dfx, "_group");
GLOBAL_STR(S_jcn, "_hay");
GLOBAL_STR(S_asc, "_match");
GLOBAL_STR(S_rtn, "_opsp");
GLOBAL_STR(S_yzD, "_pipeline_status");
GLOBAL_STR(S_mcg, "_process_sub_status");
GLOBAL_STR(S_eys, "_reply");
GLOBAL_STR(S_Dvd, "_start");
GLOBAL_STR(S_EeE, "_status");
GLOBAL_STR(S_ddq, "_this_dir");
GLOBAL_STR(S_gCD, "a");
GLOBAL_STR(S_Btg, "abbrev-");
GLOBAL_STR(S_iBl, "address space size");
GLOBAL_STR(S_nwn, "alias");
GLOBAL_STR(S_gja, "alnum");
GLOBAL_STR(S_EvD, "alpha");
GLOBAL_STR(S_BEq, "append");
GLOBAL_STR(S_qbm, "args");
GLOBAL_STR(S_esE, "argv");
GLOBAL_STR(S_gEr, "argv0");
GLOBAL_STR(S_nrm, "argv_stack");
GLOBAL_STR(S_mqm, "array LHS");
GLOBAL_STR(S_qid, "asdl_");
GLOBAL_STR(S_eln, "assert");
GLOBAL_STR(S_adv, "assertion");
GLOBAL_STR(S_tac, "attrs");
GLOBAL_STR(S_jFv, "b");
GLOBAL_STR(S_vlc, "backticks");
GLOBAL_STR(S_trA, "bad input");
GLOBAL_STR(S_sdC, "bar.py");
GLOBAL_STR(S_llx, "bind");
GLOBAL_STR(S_ksw, "bindFrame");
GLOBAL_STR(S_kAh, "binding");
GLOBAL_STR(S_bdb, "blank");
GLOBAL_STR(S_qko, "bool");
GLOBAL_STR(S_ocd, "boolstatus");
GLOBAL_STR(S_utc, "builtin");
GLOBAL_STR(S_zut, "builtin expects 'define', 'reset' or 'pp'");
GLOBAL_STR(S_lmC, "builtin expects 'read' or 'write'");
GLOBAL_STR(S_AoD, "bytes");
GLOBAL_STR(S_emj, "c");
GLOBAL_STR(S_tzb, "call_line");
GLOBAL_STR(S_eqz, "call_line_num");
GLOBAL_STR(S_ogo, "call_source");
GLOBAL_STR(S_nla, "can only handle one resource at a time; got too many flags");
GLOBAL_STR(S_ltE_1, "cannot replace by eggex on a string with NUL bytes");
GLOBAL_STR(S_pnd, "cannot split a string with a NUL byte");
GLOBAL_STR(S_gDo, "captureStdout");
GLOBAL_STR(S_jaz, "cat-em");
GLOBAL_STR(S_dyr, "cd");
GLOBAL_STR(S_wxv_1, "cd got no argument, and $HOME isn't set");
GLOBAL_STR(S_iBf, "cell_");
GLOBAL_STR(S_ccA, "children");
GLOBAL_STR(S_mij, "cntrl");
GLOBAL_STR(S_gFE, "code");
GLOBAL_STR(S_bAo, "code_str");
GLOBAL_STR(S_zij, "command");
GLOBAL_STR(S_cgB, "command node requires a literal block argument");
GLOBAL_STR(S_qxt, "compadjust");
GLOBAL_STR(S_jgm, "compexport");
GLOBAL_STR(S_pqd, "compgen");
GLOBAL_STR(S_hyn, "complete");
GLOBAL_STR(S_kgx, "completion");
GLOBAL_STR(S_ore, "compopt");
GLOBAL_STR(S_sbp, "compopt: not currently executing a completion function");
GLOBAL_STR(S_pxA, "const can't be inside proc or func.  Use var instead.");
GLOBAL_STR(S_qli, "core dump size");
GLOBAL_STR(S_oFh, "count");
GLOBAL_STR(S_acj, "ctx");
GLOBAL_STR(S_CgA, "cur");
GLOBAL_STR(S_CBu, "cword");
GLOBAL_STR(S_Crn, "d");
GLOBAL_STR(S_qnE, "data segment size");
GLOBAL_STR(S_yzg, "debug_stack");
GLOBAL_STR(S_enx, "declare -");
GLOBAL_STR(S_vlb, "default");
GLOBAL_STR(S_cfl, "define");
GLOBAL_STR(S_nFj, "define expected a name");
GLOBAL_STR(S_Fuj, "deps");
GLOBAL_STR(S_wlA, "dict");
GLOBAL_STR(S_wse, "dict() expected Dict, Obj, or BashAssoc");
GLOBAL_STR(S_Coo, "digit");
GLOBAL_STR(S_wkf, "diouxX");
GLOBAL_STR(S_nmo, "directory");
GLOBAL_STR(S_aml, "dirnames");
GLOBAL_STR(S_nAr, "dirs");
GLOBAL_STR(S_rrt, "do {");
GLOBAL_STR(S_zDr_1, "doesn't accept -f because it's dangerous.  (The code can usually be restructured with 'source')");
GLOBAL_STR(S_vbA, "doesn't accept RHS with -n");
GLOBAL_STR(S_hDg, "doesn't accept resource flags with -a");
GLOBAL_STR(S_Frn, "doesn't accept typed args without --all, or --num-bytes");
GLOBAL_STR(S_chp, "doesn't implement flag -i (shopt --set ignore_flags_not_impl)");
GLOBAL_STR(S_Bxy, "dollar0");
GLOBAL_STR(S_urc, "dot");
GLOBAL_STR(S_efa, "dynamic LHS");
GLOBAL_STR(S_ysz, "e");
GLOBAL_STR(S_ayy, "eEfFgG");
GLOBAL_STR(S_svu, "echo");
GLOBAL_STR(S_gxy, "eggex separators should never match the empty string");
GLOBAL_STR(S_Bop, "eggex should never match the empty string");
GLOBAL_STR(S_lcm, "emacs");
GLOBAL_STR(S_orf, "emit");
GLOBAL_STR(S_nDb, "empty");
GLOBAL_STR(S_kvt, "encodeBytes");
GLOBAL_STR(S_cCw, "encodeRunes");
GLOBAL_STR(S_Ate, "end");
GLOBAL_STR(S_jdf, "end_pos");
GLOBAL_STR(S_vdA, "endsWith");
GLOBAL_STR(S_uFo, "errexit was disabled for this construct");
GLOBAL_STR(S_riE, "error");
GLOBAL_STR(S_tBe, "error: can only use one of the following flags at a time: -");
GLOBAL_STR(S_vwx, "error: cannot mix bind commands with the following flags: -");
GLOBAL_STR(S_jit, "es");
GLOBAL_STR(S_cCk, "eval");
GLOBAL_STR(S_ifC, "eval arg");
GLOBAL_STR(S_mul, "evalHay");
GLOBAL_STR(S_tlu, "eval_unsafe_arith is off");
GLOBAL_STR(S_Evy, "exec");
GLOBAL_STR(S_mrk, "expected 1 or more commands");
GLOBAL_STR(S_fuh, "expected Eggex or Str");
GLOBAL_STR(S_Btr, "expected Int or Str");
GLOBAL_STR(S_rxi, "expected List or BashArray");
GLOBAL_STR(S_Ecv, "expected a -c string, like sh -c");
GLOBAL_STR(S_dae, "expected a block arg");
GLOBAL_STR(S_top, "expected a command to run");
GLOBAL_STR(S_CBb, "expected a message to display");
GLOBAL_STR(S_Bvq, "expected at least 1 arg, or a literal block { }");
GLOBAL_STR(S_twC, "expected expr to eval to a Str");
GLOBAL_STR(S_pDm, "expected flag like --pick after module path");
GLOBAL_STR(S_yBg, "expected pattern to be Eggex or Str");
GLOBAL_STR(S_sdz, "expected separator to be Eggex or Str");
GLOBAL_STR(S_ACu, "expected substitution to be Str or Expr");
GLOBAL_STR(S_tbx, "expected variable name");
GLOBAL_STR(S_rkC, "export");
GLOBAL_STR(S_plg, "export builtin is disabled in YSH (shopt --set no_exported)");
GLOBAL_STR(S_xxu, "export_");
GLOBAL_STR(S_idc_1, "extended glob not allowed in this word");
GLOBAL_STR(S_med, "extended globs not supported in ${x//GLOB/}");
GLOBAL_STR(S_Fvh, "extern");
GLOBAL_STR(S_xaw, "extern_");
GLOBAL_STR(S_ksc, "f");
GLOBAL_STR(S_FAx, "failed");
GLOBAL_STR(S_xho, "failglob: ");
GLOBAL_STR(S_Ctn, "false");
GLOBAL_STR(S_Ect, "fatal: ");
GLOBAL_STR(S_cfh, "fg: No job to put in the foreground");
GLOBAL_STR(S_xeh, "file");
GLOBAL_STR(S_zwg, "file descriptors");
GLOBAL_STR(S_eEz, "file size");
GLOBAL_STR(S_Fqh, "filenames");
GLOBAL_STR(S_Egw, "find");
GLOBAL_STR(S_apg, "first");
GLOBAL_STR(S_boy, "flags");
GLOBAL_STR(S_itx, "float");
GLOBAL_STR(S_fir, "float() expected Int, Float, or Str");
GLOBAL_STR(S_FCw, "floatsEqual");
GLOBAL_STR(S_hex, "fmt");
GLOBAL_STR(S_lqB, "foo");
GLOBAL_STR(S_paw, "foo.py");
GLOBAL_STR(S_hei, "for -Wreturn-type in C++");
GLOBAL_STR(S_saz, "for C++ compiler");
GLOBAL_STR(S_Fmn, "for loop expected List, Dict, Range, or Stdin");
GLOBAL_STR(S_Fzz_1, "fork");
GLOBAL_STR(S_xAx, "forkwait");
GLOBAL_STR(S_DDx, "frame_vars_");
GLOBAL_STR(S_fnB, "fromJson");
GLOBAL_STR(S_qqd, "fromJson8");
GLOBAL_STR(S_fig, "fullMatch");
GLOBAL_STR(S_ggl, "func is a YSH keyword, but this is OSH.");
GLOBAL_STR(S_rwo, "func_name");
GLOBAL_STR(S_pih, "funcs can't be defined inside shell functions");
GLOBAL_STR(S_cgg, "function");
GLOBAL_STR(S_ukF, "g");
GLOBAL_STR(S_Fhp, "gc-stats_");
GLOBAL_STR(S_ylo, "get");
GLOBAL_STR(S_jai, "get() expected Dict or Obj");
GLOBAL_STR(S_wvg, "getFrame");
GLOBAL_STR(S_Aeu, "getVar");
GLOBAL_STR(S_fvi, "glob");
GLOBAL_STR(S_qsz, "got extra arg");
GLOBAL_STR(S_zvw, "got extra arg with -a");
GLOBAL_STR(S_Ezs, "got extra argument");
GLOBAL_STR(S_wrs, "got extra arguments after -r");
GLOBAL_STR(S_xDq, "got invalid LHS expression");
GLOBAL_STR(S_sAk, "got too many arguments");
GLOBAL_STR(S_rDq, "got unexpected typed args");
GLOBAL_STR(S_jji, "graph");
GLOBAL_STR(S_elk, "group");
GLOBAL_STR(S_hjv, "h");
GLOBAL_STR(S_drr, "hash");
GLOBAL_STR(S_ktp, "haynode");
GLOBAL_STR(S_sea, "help");
GLOBAL_STR(S_yqg, "helptopic");
GLOBAL_STR(S_gBD, "history");
GLOBAL_STR(S_qBe, "hostname");
GLOBAL_STR(S_Bql, "https://oils.pub/release");
GLOBAL_STR(S_eil, "i");
GLOBAL_STR(S_huA, "id");
GLOBAL_STR(S_dnf, "id() expected List, Dict, or Obj");
GLOBAL_STR(S_tvw, "index out of range");
GLOBAL_STR(S_dnv, "indexOf");
GLOBAL_STR(S_gcE, "int");
GLOBAL_STR(S_FiA, "int() expected Bool, Int, Float, or Str");
GLOBAL_STR(S_dcr, "invalid (state, ch) pair");
GLOBAL_STR(S_jvb, "invokable");
GLOBAL_STR(S_wwk, "invoke");
GLOBAL_STR(S_Akj, "io");
GLOBAL_STR(S_pFB, "is disabled because Oils wasn't compiled with 'readline'");
GLOBAL_STR(S_wha, "isn't implemented");
GLOBAL_STR(S_mfD, "jlines");
GLOBAL_STR(S_orw, "job");
GLOBAL_STR(S_jtz, "jobs");
GLOBAL_STR(S_gbr, "jobs-not-implemented");
GLOBAL_STR(S_eov, "join");
GLOBAL_STR(S_gzE_1, "join() ");
GLOBAL_STR(S_fiw, "json");
GLOBAL_STR(S_Evb, "json8");
GLOBAL_STR(S_ctf, "json_read");
GLOBAL_STR(S_EFp, "json_write");
GLOBAL_STR(S_zcr, "keys");
GLOBAL_STR(S_evo, "keyword");
GLOBAL_STR(S_zfa, "lastIndexOf");
GLOBAL_STR(S_omo, "leftMatch");
GLOBAL_STR(S_fDC, "len");
GLOBAL_STR(S_rEg, "len() expected Str, List, or Dict");
GLOBAL_STR(S_gzt, "line");
GLOBAL_STR(S_zdb_1, "line_num");
GLOBAL_STR(S_yrn, "list");
GLOBAL_STR(S_Dtg, "list() expected Dict, List, or Range");
GLOBAL_STR(S_btu, "location_start_line");
GLOBAL_STR(S_igc, "location_str");
GLOBAL_STR(S_brz, "lossless-cat");
GLOBAL_STR(S_urB, "lower");
GLOBAL_STR(S_sDc_1, "main");
GLOBAL_STR(S_fhy, "mapfile");
GLOBAL_STR(S_cAk, "may only be used at the top level");
GLOBAL_STR(S_rpn, "maybe");
GLOBAL_STR(S_pBg, "message");
GLOBAL_STR(S_tug, "metric_argv0");
GLOBAL_STR(S_uCh, "missing closing ]");
GLOBAL_STR(S_CBl, "module must be invoked with a proc name argument");
GLOBAL_STR(S_dba, "module-invoke");
GLOBAL_STR(S_zyC, "msg");
GLOBAL_STR(S_Cuq, "mylib.LineReader");
GLOBAL_STR(S_Fig, "mylib.Writer");
GLOBAL_STR(S_rob, "n");
GLOBAL_STR(S_klA, "name");
GLOBAL_STR(S_sAx, "nameref must be a string");
GLOBAL_STR(S_cwj, "new");
GLOBAL_STR(S_CFg, "new_var");
GLOBAL_STR(S_zkk, "nice");
GLOBAL_STR(S_rdE_1, "none");
GLOBAL_STR(S_isi, "nospace");
GLOBAL_STR(S_lbA, "null");
GLOBAL_STR(S_owh, "num_shifted");
GLOBAL_STR(S_Ala, "o");
GLOBAL_STR(S_Bww, "obj[index] expected List or Dict");
GLOBAL_STR(S_rcA, "obj[index] expected List, Dict, or Obj");
GLOBAL_STR(S_fnD, "oil");
GLOBAL_STR(S_ela, "oils warning: umask with symbolic input isn't implemented");
GLOBAL_STR(S_jlA, "oils-err");
GLOBAL_STR(S_afu, "oils-for-unix");
GLOBAL_STR(S_Eur, "oils-usage");
GLOBAL_STR(S_Ffb, "osh");
GLOBAL_STR(S_aCB, "osh printf doesn't support floating point");
GLOBAL_STR(S_syf, "osh printf doesn't support single characters (bytes)");
GLOBAL_STR(S_lAu, "osh warning: GLOB_PERIOD wasn't found in libc, so 'shopt -s dotglob' won't work");
GLOBAL_STR(S_gxD, "osh warning: complete -C not implemented");
GLOBAL_STR(S_nAz, "osh warning: set -o verbose not implemented");
GLOBAL_STR(S_EDi, "osh: Ignoring 'exit' in completion plugin");
GLOBAL_STR(S_Eop, "ouxX");
GLOBAL_STR(S_AdB, "parseCommand");
GLOBAL_STR(S_cCs, "parseCommand()");
GLOBAL_STR(S_upi, "parseExpr");
GLOBAL_STR(S_tEt, "parseHay");
GLOBAL_STR(S_epe, "pid");
GLOBAL_STR(S_zCk, "pipeline");
GLOBAL_STR(S_ccq, "plusdirs");
GLOBAL_STR(S_Dnb, "popd");
GLOBAL_STR(S_uaE, "pos");
GLOBAL_STR(S_Cja, "pos_args");
GLOBAL_STR(S_ntm, "pp");
GLOBAL_STR(S_fmh, "prev");
GLOBAL_STR(S_nld, "print");
GLOBAL_STR(S_Foo, "printf");
GLOBAL_STR(S_kjD, "printf arg");
GLOBAL_STR(S_aFi, "proc");
GLOBAL_STR(S_dsk, "proc is a YSH keyword, but this is OSH.");
GLOBAL_STR(S_qdr, "proc_name\tdoc_comment");
GLOBAL_STR(S_fot_1, "procs can't be defined inside shell functions");
GLOBAL_STR(S_bEz, "promptVal");
GLOBAL_STR(S_zob, "propView");
GLOBAL_STR(S_Clj, "prototype");
GLOBAL_STR(S_syu, "punct");
GLOBAL_STR(S_Cwb, "push");
GLOBAL_STR(S_gma, "push-registers");
GLOBAL_STR(S_kog, "pushd");
GLOBAL_STR(S_CFz, "pushd: no other directory");
GLOBAL_STR(S_xrE, "pwd");
GLOBAL_STR(S_crA, "q");
GLOBAL_STR(S_nAr_1, "r");
GLOBAL_STR(S_hDl, "read");
GLOBAL_STR(S_qnf, "read -t isn't implemented (except t=0)");
GLOBAL_STR(S_wpE, "read got too many args");
GLOBAL_STR(S_noe, "readlink");
GLOBAL_STR(S_qjq, "readlink not translated");
GLOBAL_STR(S_ACj, "readonly");
GLOBAL_STR(S_xzn, "redir");
GLOBAL_STR(S_fdv, "reg_icase");
GLOBAL_STR(S_ABj, "reg_newline");
GLOBAL_STR(S_jAe, "renderPrompt");
GLOBAL_STR(S_Cbl, "replace");
GLOBAL_STR(S_oiw, "requires a code string");
GLOBAL_STR(S_fon, "requires a file path");
GLOBAL_STR(S_rku, "requires a format string");
GLOBAL_STR(S_yie, "requires a module path");
GLOBAL_STR(S_bds, "requires a name");
GLOBAL_STR(S_uxD, "requires a signal or hook name");
GLOBAL_STR(S_seh, "requires an argspec");
GLOBAL_STR(S_Bot, "requires an argument");
GLOBAL_STR(S_kha, "requires an argument when a block is passed");
GLOBAL_STR(S_sFk, "requires arguments");
GLOBAL_STR(S_cpz, "requires code string");
GLOBAL_STR(S_rxc, "requires exactly 1 argument");
GLOBAL_STR(S_lgh, "requires the name of a variable to set");
GLOBAL_STR(S_rCB, "reset");
GLOBAL_STR(S_hjl, "rest");
GLOBAL_STR(S_ubi, "runes");
GLOBAL_STR(S_pkv, "runproc");
GLOBAL_STR(S_anC, "s");
GLOBAL_STR(S_uok, "s ");
GLOBAL_STR(S_vjw, "search");
GLOBAL_STR(S_avu_1, "separator must be non-empty");
GLOBAL_STR(S_flq, "set");
GLOBAL_STR(S_scp, "set ");
GLOBAL_STR(S_akf, "set editing-mode ");
GLOBAL_STR(S_mhp, "set horizontal-scroll-mode on");
GLOBAL_STR(S_riF, "setVar");
GLOBAL_STR(S_uem, "setopt");
GLOBAL_STR(S_scw, "setvar ");
GLOBAL_STR(S_wiE, "sh");
GLOBAL_STR(S_jvC, "shSplit");
GLOBAL_STR(S_jlb, "shell functions can't be defined inside proc or func");
GLOBAL_STR(S_ozu, "shell {");
GLOBAL_STR(S_ene, "shopt");
GLOBAL_STR(S_Ayw, "should be invoked as 'test' (simple_test_builtin)");
GLOBAL_STR(S_Ajx, "should only have 3 arguments or fewer (simple_test_builtin)");
GLOBAL_STR(S_bBe, "shvar");
GLOBAL_STR(S_Cko, "shvarGet");
GLOBAL_STR(S_wzk, "signal");
GLOBAL_STR(S_kqu, "slice");
GLOBAL_STR(S_vfo, "slowc");
GLOBAL_STR(S_cmd, "source");
GLOBAL_STR(S_uiC, "source-guard");
GLOBAL_STR(S_gxr, "source_name");
GLOBAL_STR(S_iya, "space");
GLOBAL_STR(S_umv, "split");
GLOBAL_STR(S_BDe, "stack size");
GLOBAL_STR(S_Ekj, "stacks_");
GLOBAL_STR(S_lra_1, "start");
GLOBAL_STR(S_pBs, "start_pos");
GLOBAL_STR(S_lfz, "startsWith");
GLOBAL_STR(S_iAi, "status");
GLOBAL_STR(S_lla, "status must be a non-zero integer");
GLOBAL_STR(S_pmj, "status wouldn't be checked (strict_errexit)");
GLOBAL_STR(S_EoC, "stdin");
GLOBAL_STR(S_vwz, "stdlib");
GLOBAL_STR(S_AAt, "stopped");
GLOBAL_STR(S_urq, "str");
GLOBAL_STR(S_uCe, "str() ");
GLOBAL_STR(S_Eoc, "strftime");
GLOBAL_STR(S_rgl, "strict_errexit only allows a single command.  Hint: use 'try'.");
GLOBAL_STR(S_vyq, "subst");
GLOBAL_STR(S_xcB, "syntax-tree");
GLOBAL_STR(S_omF, "t");
GLOBAL_STR(S_sph, "tab: complete");
GLOBAL_STR(S_jvs, "test");
GLOBAL_STR(S_zum, "test_");
GLOBAL_STR(S_rtt_1, "time");
GLOBAL_STR(S_Bsg, "toJson");
GLOBAL_STR(S_qwA, "toJson8");
GLOBAL_STR(S_tje, "tokens");
GLOBAL_STR(S_vwh, "too much input");
GLOBAL_STR(S_gFu, "trap");
GLOBAL_STR(S_Dph, "trap DEBUG");
GLOBAL_STR(S_gAc, "trap ERR");
GLOBAL_STR(S_lEC, "trap EXIT");
GLOBAL_STR(S_ECk, "trap arg");
GLOBAL_STR(S_Adn, "trim");
GLOBAL_STR(S_EsB, "trimEnd");
GLOBAL_STR(S_Cjp, "trimStart");
GLOBAL_STR(S_FsF, "true");
GLOBAL_STR(S_zfb_1, "try_");
GLOBAL_STR(S_vbp, "tsv8");
GLOBAL_STR(S_qEi, "type");
GLOBAL_STR(S_hzm, "typed is a YSH keyword, but this is OSH.");
GLOBAL_STR(S_rsz, "u");
GLOBAL_STR(S_otx, "ulimit");
GLOBAL_STR(S_pdn, "umask: unexpected arguments");
GLOBAL_STR(S_Brd, "unalias");
GLOBAL_STR(S_oAe, "unique_id");
GLOBAL_STR(S_zxo, "unix suffix implemented");
GLOBAL_STR(S_bxj, "unlimited");
GLOBAL_STR(S_asd, "unreachable");
GLOBAL_STR(S_FxC, "unset");
GLOBAL_STR(S_fgo, "upper");
GLOBAL_STR(S_eas, "use");
GLOBAL_STR(S_sqx, "user");
GLOBAL_STR(S_Ado, "v");
GLOBAL_STR(S_zrD, "val");
GLOBAL_STR(S_tFk, "value");
GLOBAL_STR(S_eyc, "values");
GLOBAL_STR(S_szq, "var_stack");
GLOBAL_STR(S_unB, "variable");
GLOBAL_STR(S_szc, "vars");
GLOBAL_STR(S_dmp, "vi");
GLOBAL_STR(S_FbA, "vi-delete");
GLOBAL_STR(S_kaF, "vm");
GLOBAL_STR(S_pfC, "w");
GLOBAL_STR(S_Awk, "wait");
GLOBAL_STR(S_jhf, "warning: ");
GLOBAL_STR(S_bsD, "warning: bind -x isn't implemented");
GLOBAL_STR(S_pii, "while !");
GLOBAL_STR(S_bbj, "with --pick expects one or more names");
GLOBAL_STR(S_yzA, "with -f expects function names");
GLOBAL_STR(S_Cbp, "word");
GLOBAL_STR(S_pho, "words");
GLOBAL_STR(S_bhu, "write");
GLOBAL_STR(S_cvm_1, "write got too many args");
GLOBAL_STR(S_rqD, "x");
GLOBAL_STR(S_dgp, "xdigit");
GLOBAL_STR(S_Awp, "ysh");
GLOBAL_STR(S_eyu, "ysh ");
GLOBAL_STR(S_Cha, "ysh-ify");
GLOBAL_STR(S_CpF, "ysh:all");
GLOBAL_STR(S_ato, "{");
GLOBAL_STR(S_qnA, "{...}");
GLOBAL_STR(S_Fni, "{}");
GLOBAL_STR(S_Ebn, "|");
GLOBAL_STR(S_yDp, "|& isn't supported");
GLOBAL_STR(S_cEn, "}");
GLOBAL_STR(S_tve, "}\n");
GLOBAL_STR(S_ior, "} ");
GLOBAL_STR(S_Bhp, "~");
GLOBAL_STR(S_erg, "~ expected Int");
GLOBAL_STR(S_rEw, "~== expects Str, Int, or Bool on the right");
GLOBAL_STR(S_uEa, "~== expects a string on the left");

namespace runtime {  // declare

extern int NO_SPID;
hnode::Record* NewRecord(BigStr* node_type);
hnode::Leaf* NewLeaf(BigStr* s, hnode_asdl::color_t e_color);
class TraversalState {
 public:
  TraversalState();
  Dict<int, bool>* seen{};
  Dict<int, int>* ref_count{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(TraversalState));
  }

  DISALLOW_COPY_AND_ASSIGN(TraversalState)
};

extern BigStr* TRUE_STR;
extern BigStr* FALSE_STR;

}  // declare namespace runtime

namespace vm {  // declare

class ControlFlow {
 public:

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(ControlFlow));
  }

  DISALLOW_COPY_AND_ASSIGN(ControlFlow)
};

class IntControlFlow {
 public:
  IntControlFlow(syntax_asdl::Token* token, int arg);
  bool IsReturn();
  bool IsBreak();
  bool IsContinue();
  int StatusCode();
  runtime_asdl::flow_t HandleLoop();
  syntax_asdl::Token* token{};
  int arg{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(IntControlFlow));
  }

  DISALLOW_COPY_AND_ASSIGN(IntControlFlow)
};

class ValueControlFlow {
 public:
  ValueControlFlow(syntax_asdl::Token* token, value_asdl::value_t* value);
  syntax_asdl::Token* token{};
  value_asdl::value_t* value{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(ValueControlFlow));
  }

  DISALLOW_COPY_AND_ASSIGN(ValueControlFlow)
};

void InitUnsafeArith(state::Mem* mem, word_eval::NormalWordEvaluator* word_ev, sh_expr_eval::UnsafeArith* unsafe_arith);
void InitCircularDeps(sh_expr_eval::ArithEvaluator* arith_ev, sh_expr_eval::BoolEvaluator* bool_ev, expr_eval::ExprEvaluator* expr_ev, word_eval::NormalWordEvaluator* word_ev, cmd_eval::CommandEvaluator* cmd_ev, vm::_Executor* shell_ex, prompt::Evaluator* prompt_ev, value_asdl::Obj* global_io, dev::Tracer* tracer);
class _Executor {
 public:
  _Executor();
  virtual void CheckCircularDeps();
  virtual int RunBuiltin(int builtin_id, cmd_value::Argv* cmd_val);
  virtual int RunSimpleCommand(cmd_value::Argv* cmd_val, runtime_asdl::CommandStatus* cmd_st, int run_flags);
  virtual int RunBackgroundJob(syntax_asdl::command_t* node);
  virtual void RunPipeline(command::Pipeline* node, runtime_asdl::CommandStatus* status_out);
  virtual int RunSubshell(syntax_asdl::command_t* node);
  virtual Tuple2<int, BigStr*> CaptureStdout(syntax_asdl::command_t* node);
  virtual BigStr* RunCommandSub(syntax_asdl::CommandSub* cs_part);
  virtual BigStr* RunProcessSub(syntax_asdl::CommandSub* cs_part);
  virtual void PushRedirects(List<runtime_asdl::RedirValue*>* redirects, List<IOError_OSError*>* err_out);
  virtual void PopRedirects(int num_redirects, List<IOError_OSError*>* err_out);
  virtual void PushProcessSub();
  virtual void PopProcessSub(runtime_asdl::StatusArray* compound_st);
  cmd_eval::CommandEvaluator* cmd_ev{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(_Executor, cmd_ev));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_Executor));
  }

  DISALLOW_COPY_AND_ASSIGN(_Executor)
};

class _AssignBuiltin {
 public:
  _AssignBuiltin();
  virtual int Run(cmd_value::Assign* cmd_val);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_AssignBuiltin));
  }

  DISALLOW_COPY_AND_ASSIGN(_AssignBuiltin)
};

class _Builtin {
 public:
  _Builtin();
  virtual int Run(cmd_value::Argv* cmd_val);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_Builtin));
  }

  DISALLOW_COPY_AND_ASSIGN(_Builtin)
};

class _Callable {
 public:
  _Callable();
  virtual value_asdl::value_t* Call(typed_args::Reader* args);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_Callable));
  }

  DISALLOW_COPY_AND_ASSIGN(_Callable)
};

class ctx_Redirect {
 public:
  ctx_Redirect(vm::_Executor* shell_ex, int num_redirects, List<IOError_OSError*>* err_out);
  ~ctx_Redirect();
  vm::_Executor* shell_ex{};
  List<IOError_OSError*>* err_out{};
  int num_redirects{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Redirect)
};

class ctx_ProcessSub {
 public:
  ctx_ProcessSub(vm::_Executor* shell_ex, runtime_asdl::StatusArray* process_sub_status);
  ~ctx_ProcessSub();
  vm::_Executor* shell_ex{};
  runtime_asdl::StatusArray* process_sub_status{};

  DISALLOW_COPY_AND_ASSIGN(ctx_ProcessSub)
};

class ctx_FlushStdout {
 public:
  ctx_FlushStdout(List<IOError_OSError*>* err_out);
  ~ctx_FlushStdout();
  List<IOError_OSError*>* err_out{};

  DISALLOW_COPY_AND_ASSIGN(ctx_FlushStdout)
};


}  // declare namespace vm

namespace format {  // declare

int _HNodeCount(hnode_asdl::hnode_t* h);
int _DocCount(pretty_asdl::doc_t* d);
void _HNodePrettyPrint(bool perf_stats, bool doc_debug, hnode_asdl::hnode_t* node, mylib::Writer* f, int max_width = 80);
void HNodePrettyPrint(hnode_asdl::hnode_t* node, mylib::Writer* f, int max_width = 80);

}  // declare namespace format

namespace oils_for_unix {  // declare

int CaperDispatch();
int AppBundleMain(List<BigStr*>* argv);
int main(List<BigStr*>* argv);

}  // declare namespace oils_for_unix

namespace assign_osh {  // declare

extern int _OTHER;
extern int _READONLY;
extern int _EXPORT;
int _PrintVariables(state::Mem* mem, cmd_value::Assign* cmd_val, args::_Attributes* attrs, bool print_flags, int builtin = _OTHER);
void _ExportReadonly(state::Mem* mem, runtime_asdl::AssignArg* pair, int flags);
class Export : public ::vm::_AssignBuiltin {
 public:
  Export(state::Mem* mem, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Assign* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_AssignBuiltin::field_mask()
         | maskbit(offsetof(Export, errfmt))
         | maskbit(offsetof(Export, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Export));
  }

  DISALLOW_COPY_AND_ASSIGN(Export)
};

value_asdl::value_t* _ReconcileTypes(value_asdl::value_t* rval, bool flag_a, bool flag_A, syntax_asdl::word_t* blame_word);
class Readonly : public ::vm::_AssignBuiltin {
 public:
  Readonly(state::Mem* mem, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Assign* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_AssignBuiltin::field_mask()
         | maskbit(offsetof(Readonly, errfmt))
         | maskbit(offsetof(Readonly, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Readonly));
  }

  DISALLOW_COPY_AND_ASSIGN(Readonly)
};

class NewVar : public ::vm::_AssignBuiltin {
 public:
  NewVar(state::Mem* mem, state::Procs* procs, optview::Exec* exec_opts, ui::ErrorFormatter* errfmt);
  int _PrintFuncs(List<BigStr*>* names);
  virtual int Run(cmd_value::Assign* cmd_val);

  ui::ErrorFormatter* errfmt{};
  optview::Exec* exec_opts{};
  state::Mem* mem{};
  state::Procs* procs{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_AssignBuiltin::field_mask()
         | maskbit(offsetof(NewVar, errfmt))
         | maskbit(offsetof(NewVar, exec_opts))
         | maskbit(offsetof(NewVar, mem))
         | maskbit(offsetof(NewVar, procs));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(NewVar));
  }

  DISALLOW_COPY_AND_ASSIGN(NewVar)
};

class Unset : public ::vm::_Builtin {
 public:
  Unset(state::Mem* mem, state::Procs* procs, sh_expr_eval::UnsafeArith* unsafe_arith, ui::ErrorFormatter* errfmt);
  bool _UnsetVar(BigStr* arg, syntax_asdl::loc_t* location, bool proc_fallback);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  state::Procs* procs{};
  sh_expr_eval::UnsafeArith* unsafe_arith{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Unset, errfmt))
         | maskbit(offsetof(Unset, mem))
         | maskbit(offsetof(Unset, procs))
         | maskbit(offsetof(Unset, unsafe_arith));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Unset));
  }

  DISALLOW_COPY_AND_ASSIGN(Unset)
};

class Shift : public ::vm::_Builtin {
 public:
  Shift(state::Mem* mem);
  virtual int Run(cmd_value::Argv* cmd_val);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Shift, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Shift));
  }

  DISALLOW_COPY_AND_ASSIGN(Shift)
};


}  // declare namespace assign_osh

namespace completion_ysh {  // declare

class CompExport : public ::vm::_Builtin {
 public:
  CompExport(completion::RootCompleter* root_comp);
  virtual int Run(cmd_value::Argv* cmd_val);

  completion::RootCompleter* root_comp{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(CompExport, root_comp));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(CompExport));
  }

  DISALLOW_COPY_AND_ASSIGN(CompExport)
};


}  // declare namespace completion_ysh

namespace dirs_osh {  // declare

class DirStack {
 public:
  DirStack();
  void Reset();
  void Replace(BigStr* d);
  void Push(BigStr* entry);
  BigStr* Pop();
  List<BigStr*>* Iter();
  List<BigStr*>* stack{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(DirStack));
  }

  DISALLOW_COPY_AND_ASSIGN(DirStack)
};

class ctx_CdBlock {
 public:
  ctx_CdBlock(dirs_osh::DirStack* dir_stack, BigStr* dest_dir, state::Mem* mem, ui::ErrorFormatter* errfmt, List<bool>* out_errs);
  ~ctx_CdBlock();
  dirs_osh::DirStack* dir_stack{};
  state::Mem* mem{};
  ui::ErrorFormatter* errfmt{};
  List<bool>* out_errs{};

  DISALLOW_COPY_AND_ASSIGN(ctx_CdBlock)
};

class Cd : public ::vm::_Builtin {
 public:
  Cd(state::Mem* mem, dirs_osh::DirStack* dir_stack, cmd_eval::CommandEvaluator* cmd_ev, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  dirs_osh::DirStack* dir_stack{};
  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Cd, cmd_ev))
         | maskbit(offsetof(Cd, dir_stack))
         | maskbit(offsetof(Cd, errfmt))
         | maskbit(offsetof(Cd, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Cd));
  }

  DISALLOW_COPY_AND_ASSIGN(Cd)
};

extern int WITH_LINE_NUMBERS;
extern int WITHOUT_LINE_NUMBERS;
extern int SINGLE_LINE;
void _PrintDirStack(dirs_osh::DirStack* dir_stack, int style, BigStr* home_dir);
class Pushd : public ::vm::_Builtin {
 public:
  Pushd(state::Mem* mem, dirs_osh::DirStack* dir_stack, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  dirs_osh::DirStack* dir_stack{};
  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Pushd, dir_stack))
         | maskbit(offsetof(Pushd, errfmt))
         | maskbit(offsetof(Pushd, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Pushd));
  }

  DISALLOW_COPY_AND_ASSIGN(Pushd)
};

bool _PopDirStack(BigStr* label, state::Mem* mem, dirs_osh::DirStack* dir_stack, ui::ErrorFormatter* errfmt, List<bool>* out_errs);
class Popd : public ::vm::_Builtin {
 public:
  Popd(state::Mem* mem, dirs_osh::DirStack* dir_stack, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  dirs_osh::DirStack* dir_stack{};
  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Popd, dir_stack))
         | maskbit(offsetof(Popd, errfmt))
         | maskbit(offsetof(Popd, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Popd));
  }

  DISALLOW_COPY_AND_ASSIGN(Popd)
};

class Dirs : public ::vm::_Builtin {
 public:
  Dirs(state::Mem* mem, dirs_osh::DirStack* dir_stack, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  dirs_osh::DirStack* dir_stack{};
  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Dirs, dir_stack))
         | maskbit(offsetof(Dirs, errfmt))
         | maskbit(offsetof(Dirs, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Dirs));
  }

  DISALLOW_COPY_AND_ASSIGN(Dirs)
};

class Pwd : public ::vm::_Builtin {
 public:
  Pwd(state::Mem* mem, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Pwd, errfmt))
         | maskbit(offsetof(Pwd, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Pwd));
  }

  DISALLOW_COPY_AND_ASSIGN(Pwd)
};


}  // declare namespace dirs_osh

namespace error_ysh {  // declare

class ctx_Try {
 public:
  ctx_Try(state::MutableOpts* mutable_opts);
  ~ctx_Try();
  state::MutableOpts* mutable_opts{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Try)
};

class Try : public ::vm::_Builtin {
 public:
  Try(state::MutableOpts* mutable_opts, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev, vm::_Executor* shell_ex, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  state::MutableOpts* mutable_opts{};
  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Try, cmd_ev))
         | maskbit(offsetof(Try, errfmt))
         | maskbit(offsetof(Try, mem))
         | maskbit(offsetof(Try, mutable_opts))
         | maskbit(offsetof(Try, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Try));
  }

  DISALLOW_COPY_AND_ASSIGN(Try)
};

class Failed : public ::vm::_Builtin {
 public:
  Failed(state::Mem* mem);
  virtual int Run(cmd_value::Argv* cmd_val);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Failed, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Failed));
  }

  DISALLOW_COPY_AND_ASSIGN(Failed)
};

class Error : public ::vm::_Builtin {
 public:
  Error();
  virtual int Run(cmd_value::Argv* cmd_val);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Error));
  }

  DISALLOW_COPY_AND_ASSIGN(Error)
};

class BoolStatus : public ::vm::_Builtin {
 public:
  BoolStatus(vm::_Executor* shell_ex, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(BoolStatus, errfmt))
         | maskbit(offsetof(BoolStatus, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(BoolStatus));
  }

  DISALLOW_COPY_AND_ASSIGN(BoolStatus)
};

class Assert : public ::vm::_Builtin {
 public:
  Assert(expr_eval::ExprEvaluator* expr_ev, ui::ErrorFormatter* errfmt);
  void _AssertComparison(expr::Compare* exp, syntax_asdl::loc_t* blame_loc);
  void _AssertExpression(value::Expr* val, syntax_asdl::loc_t* blame_loc);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  expr_eval::ExprEvaluator* expr_ev{};
  mylib::Writer* f{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Assert, errfmt))
         | maskbit(offsetof(Assert, expr_ev))
         | maskbit(offsetof(Assert, f));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Assert));
  }

  DISALLOW_COPY_AND_ASSIGN(Assert)
};


}  // declare namespace error_ysh

namespace func_eggex {  // declare

extern int G;
extern int S;
extern int E;
class _MatchCallable : public ::vm::_Callable {
 public:
  _MatchCallable(int to_return, expr_eval::ExprEvaluator* expr_ev);
  value_asdl::value_t* _ReturnValue(value_asdl::RegexMatch* match, int group_index, syntax_asdl::loc_t* blame_loc);
  value_asdl::value_t* _Call(value_asdl::RegexMatch* match, value_asdl::value_t* group_arg, syntax_asdl::loc_t* blame_loc);

  expr_eval::ExprEvaluator* expr_ev{};
  int to_return{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(_MatchCallable, expr_ev));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_MatchCallable));
  }

  DISALLOW_COPY_AND_ASSIGN(_MatchCallable)
};

int _GetGroupIndex(value_asdl::value_t* group, value_asdl::eggex_ops_t* ops, syntax_asdl::loc_t* blame_loc);
class MatchFunc : public ::func_eggex::_MatchCallable {
 public:
  MatchFunc(int to_return, expr_eval::ExprEvaluator* expr_ev, state::Mem* mem);
  value_asdl::value_t* Call(typed_args::Reader* rd);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::func_eggex::_MatchCallable::field_mask()
         | maskbit(offsetof(MatchFunc, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(MatchFunc));
  }

  DISALLOW_COPY_AND_ASSIGN(MatchFunc)
};

class MatchMethod : public ::func_eggex::_MatchCallable {
 public:
  MatchMethod(int to_return, expr_eval::ExprEvaluator* expr_ev);
  value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::func_eggex::_MatchCallable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(MatchMethod));
  }

  DISALLOW_COPY_AND_ASSIGN(MatchMethod)
};


}  // declare namespace func_eggex

namespace func_hay {  // declare

class ParseHay : public ::vm::_Callable {
 public:
  ParseHay(process::FdState* fd_state, parse_lib::ParseContext* parse_ctx, state::Mem* mem, ui::ErrorFormatter* errfmt);
  value_asdl::value_t* _Call(BigStr* path);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  ui::ErrorFormatter* errfmt{};
  process::FdState* fd_state{};
  state::Mem* mem{};
  parse_lib::ParseContext* parse_ctx{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(ParseHay, errfmt))
         | maskbit(offsetof(ParseHay, fd_state))
         | maskbit(offsetof(ParseHay, mem))
         | maskbit(offsetof(ParseHay, parse_ctx));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ParseHay));
  }

  DISALLOW_COPY_AND_ASSIGN(ParseHay)
};

class EvalHay : public ::vm::_Callable {
 public:
  EvalHay(hay_ysh::HayState* hay_state, state::MutableOpts* mutable_opts, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev);
  Dict<BigStr*, value_asdl::value_t*>* _Call(syntax_asdl::command_t* cmd);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  cmd_eval::CommandEvaluator* cmd_ev{};
  hay_ysh::HayState* hay_state{};
  state::Mem* mem{};
  state::MutableOpts* mutable_opts{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(EvalHay, cmd_ev))
         | maskbit(offsetof(EvalHay, hay_state))
         | maskbit(offsetof(EvalHay, mem))
         | maskbit(offsetof(EvalHay, mutable_opts));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(EvalHay));
  }

  DISALLOW_COPY_AND_ASSIGN(EvalHay)
};

class BlockAsStr : public ::vm::_Callable {
 public:
  BlockAsStr(alloc::Arena* arena);
  value_asdl::value_t* _Call(value_asdl::value_t* block);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  alloc::Arena* arena{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(BlockAsStr, arena));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(BlockAsStr));
  }

  DISALLOW_COPY_AND_ASSIGN(BlockAsStr)
};

class HayFunc : public ::vm::_Callable {
 public:
  HayFunc(hay_ysh::HayState* hay_state);
  Dict<BigStr*, value_asdl::value_t*>* _Call();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  hay_ysh::HayState* hay_state{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(HayFunc, hay_state));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(HayFunc));
  }

  DISALLOW_COPY_AND_ASSIGN(HayFunc)
};


}  // declare namespace func_hay

namespace func_misc {  // declare

class Object : public ::vm::_Callable {
 public:
  Object();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Object));
  }

  DISALLOW_COPY_AND_ASSIGN(Object)
};

class Obj_call : public ::vm::_Callable {
 public:
  Obj_call();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Obj_call));
  }

  DISALLOW_COPY_AND_ASSIGN(Obj_call)
};

class Prototype : public ::vm::_Callable {
 public:
  Prototype();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Prototype));
  }

  DISALLOW_COPY_AND_ASSIGN(Prototype)
};

class PropView : public ::vm::_Callable {
 public:
  PropView();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(PropView));
  }

  DISALLOW_COPY_AND_ASSIGN(PropView)
};

class Len : public ::vm::_Callable {
 public:
  Len();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Len));
  }

  DISALLOW_COPY_AND_ASSIGN(Len)
};

class Type : public ::vm::_Callable {
 public:
  Type();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Type));
  }

  DISALLOW_COPY_AND_ASSIGN(Type)
};

class Join : public ::vm::_Callable {
 public:
  Join();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Join));
  }

  DISALLOW_COPY_AND_ASSIGN(Join)
};

class Maybe : public ::vm::_Callable {
 public:
  Maybe();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Maybe));
  }

  DISALLOW_COPY_AND_ASSIGN(Maybe)
};

class Bool : public ::vm::_Callable {
 public:
  Bool();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Bool));
  }

  DISALLOW_COPY_AND_ASSIGN(Bool)
};

class Int : public ::vm::_Callable {
 public:
  Int();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Int));
  }

  DISALLOW_COPY_AND_ASSIGN(Int)
};

class Float : public ::vm::_Callable {
 public:
  Float();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Float));
  }

  DISALLOW_COPY_AND_ASSIGN(Float)
};

class Str_ : public ::vm::_Callable {
 public:
  Str_();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Str_));
  }

  DISALLOW_COPY_AND_ASSIGN(Str_)
};

class List_ : public ::vm::_Callable {
 public:
  List_();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(List_));
  }

  DISALLOW_COPY_AND_ASSIGN(List_)
};

class DictFunc : public ::vm::_Callable {
 public:
  DictFunc();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DictFunc));
  }

  DISALLOW_COPY_AND_ASSIGN(DictFunc)
};

class Runes : public ::vm::_Callable {
 public:
  Runes();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Runes));
  }

  DISALLOW_COPY_AND_ASSIGN(Runes)
};

class EncodeRunes : public ::vm::_Callable {
 public:
  EncodeRunes();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(EncodeRunes));
  }

  DISALLOW_COPY_AND_ASSIGN(EncodeRunes)
};

class Bytes : public ::vm::_Callable {
 public:
  Bytes();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Bytes));
  }

  DISALLOW_COPY_AND_ASSIGN(Bytes)
};

class EncodeBytes : public ::vm::_Callable {
 public:
  EncodeBytes();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(EncodeBytes));
  }

  DISALLOW_COPY_AND_ASSIGN(EncodeBytes)
};

class Split : public ::vm::_Callable {
 public:
  Split(split::SplitContext* splitter);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  split::SplitContext* splitter{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(Split, splitter));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Split));
  }

  DISALLOW_COPY_AND_ASSIGN(Split)
};

class FloatsEqual : public ::vm::_Callable {
 public:
  FloatsEqual();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(FloatsEqual));
  }

  DISALLOW_COPY_AND_ASSIGN(FloatsEqual)
};

class Glob : public ::vm::_Callable {
 public:
  Glob(glob_::Globber* globber);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  glob_::Globber* globber{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(Glob, globber));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Glob));
  }

  DISALLOW_COPY_AND_ASSIGN(Glob)
};

class ToJson8 : public ::vm::_Callable {
 public:
  ToJson8(bool is_j8);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  bool is_j8{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ToJson8));
  }

  DISALLOW_COPY_AND_ASSIGN(ToJson8)
};

class FromJson8 : public ::vm::_Callable {
 public:
  FromJson8(bool is_j8);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  bool is_j8{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(FromJson8));
  }

  DISALLOW_COPY_AND_ASSIGN(FromJson8)
};

class BashArrayToSparse : public ::vm::_Callable {
 public:
  BashArrayToSparse();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(BashArrayToSparse));
  }

  DISALLOW_COPY_AND_ASSIGN(BashArrayToSparse)
};

class SparseOp : public ::vm::_Callable {
 public:
  SparseOp();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SparseOp));
  }

  DISALLOW_COPY_AND_ASSIGN(SparseOp)
};


}  // declare namespace func_misc

namespace func_reflect {  // declare

class Id : public ::vm::_Callable {
 public:
  Id();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Id));
  }

  DISALLOW_COPY_AND_ASSIGN(Id)
};

class GetFrame : public ::vm::_Callable {
 public:
  GetFrame(state::Mem* mem);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(GetFrame, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(GetFrame));
  }

  DISALLOW_COPY_AND_ASSIGN(GetFrame)
};

class BindFrame : public ::vm::_Callable {
 public:
  BindFrame();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(BindFrame));
  }

  DISALLOW_COPY_AND_ASSIGN(BindFrame)
};

class Shvar_get : public ::vm::_Callable {
 public:
  Shvar_get(state::Mem* mem);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(Shvar_get, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Shvar_get));
  }

  DISALLOW_COPY_AND_ASSIGN(Shvar_get)
};

class GetVar : public ::vm::_Callable {
 public:
  GetVar(state::Mem* mem);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(GetVar, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(GetVar));
  }

  DISALLOW_COPY_AND_ASSIGN(GetVar)
};

class SetVar : public ::vm::_Callable {
 public:
  SetVar(state::Mem* mem);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(SetVar, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetVar));
  }

  DISALLOW_COPY_AND_ASSIGN(SetVar)
};

class ParseCommand : public ::vm::_Callable {
 public:
  ParseCommand(parse_lib::ParseContext* parse_ctx, state::Mem* mem, ui::ErrorFormatter* errfmt);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  parse_lib::ParseContext* parse_ctx{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(ParseCommand, errfmt))
         | maskbit(offsetof(ParseCommand, mem))
         | maskbit(offsetof(ParseCommand, parse_ctx));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ParseCommand));
  }

  DISALLOW_COPY_AND_ASSIGN(ParseCommand)
};

class ParseExpr : public ::vm::_Callable {
 public:
  ParseExpr(parse_lib::ParseContext* parse_ctx, ui::ErrorFormatter* errfmt);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  ui::ErrorFormatter* errfmt{};
  parse_lib::ParseContext* parse_ctx{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(ParseExpr, errfmt))
         | maskbit(offsetof(ParseExpr, parse_ctx));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ParseExpr));
  }

  DISALLOW_COPY_AND_ASSIGN(ParseExpr)
};


}  // declare namespace func_reflect

namespace hay_ysh {  // declare

extern BigStr* _HAY_ACTION_ERROR;
class ctx_HayNode {
 public:
  ctx_HayNode(hay_ysh::HayState* hay_state, BigStr* hay_name);
  ~ctx_HayNode();
  hay_ysh::HayState* hay_state{};

  DISALLOW_COPY_AND_ASSIGN(ctx_HayNode)
};

class ctx_HayEval {
 public:
  ctx_HayEval(hay_ysh::HayState* hay_state, state::MutableOpts* mutable_opts, state::Mem* mem);
  ~ctx_HayEval();
  hay_ysh::HayState* hay_state{};
  state::MutableOpts* mutable_opts{};
  state::Mem* mem{};

  DISALLOW_COPY_AND_ASSIGN(ctx_HayEval)
};

class HayState {
 public:
  HayState();
  Dict<BigStr*, value_asdl::value_t*>* _MakeOutputNode();
  void PushEval();
  void PopEval();
  void AppendResult(Dict<BigStr*, value_asdl::value_t*>* d);
  Dict<BigStr*, value_asdl::value_t*>* Result();
  Dict<BigStr*, value_asdl::value_t*>* HayRegister();
  bool Resolve(BigStr* first_word);
  void DefinePath(List<BigStr*>* path);
  void Reset();
  void Push(BigStr* hay_name);
  void Pop();
  runtime_asdl::HayNode* root_defs{};
  runtime_asdl::HayNode* cur_defs{};
  List<runtime_asdl::HayNode*>* def_stack{};
  List<Dict<BigStr*, value_asdl::value_t*>*>* result_stack{};
  Dict<BigStr*, value_asdl::value_t*>* output{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(5, sizeof(HayState));
  }

  DISALLOW_COPY_AND_ASSIGN(HayState)
};

class Hay : public ::vm::_Builtin {
 public:
  Hay(hay_ysh::HayState* hay_state, state::MutableOpts* mutable_opts, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  hay_ysh::HayState* hay_state{};
  state::Mem* mem{};
  state::MutableOpts* mutable_opts{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Hay, cmd_ev))
         | maskbit(offsetof(Hay, hay_state))
         | maskbit(offsetof(Hay, mem))
         | maskbit(offsetof(Hay, mutable_opts));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Hay));
  }

  DISALLOW_COPY_AND_ASSIGN(Hay)
};

class HayNode_ : public ::vm::_Builtin {
 public:
  HayNode_(hay_ysh::HayState* hay_state, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev);
  virtual int Run(cmd_value::Argv* cmd_val);

  alloc::Arena* arena{};
  cmd_eval::CommandEvaluator* cmd_ev{};
  hay_ysh::HayState* hay_state{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(HayNode_, arena))
         | maskbit(offsetof(HayNode_, cmd_ev))
         | maskbit(offsetof(HayNode_, hay_state))
         | maskbit(offsetof(HayNode_, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(HayNode_));
  }

  DISALLOW_COPY_AND_ASSIGN(HayNode_)
};


}  // declare namespace hay_ysh

namespace io_osh {  // declare

class Echo : public ::vm::_Builtin {
 public:
  Echo(optview::Exec* exec_opts);
  arg_types::echo* _SimpleFlag();
  virtual int Run(cmd_value::Argv* cmd_val);

  optview::Exec* exec_opts{};
  mylib::Writer* f{};
  arg_types::echo* simple_flag{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Echo, exec_opts))
         | maskbit(offsetof(Echo, f))
         | maskbit(offsetof(Echo, simple_flag));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Echo));
  }

  DISALLOW_COPY_AND_ASSIGN(Echo)
};

class MapFile : public ::vm::_Builtin {
 public:
  MapFile(state::Mem* mem, ui::ErrorFormatter* errfmt, cmd_eval::CommandEvaluator* cmd_ev);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(MapFile, cmd_ev))
         | maskbit(offsetof(MapFile, errfmt))
         | maskbit(offsetof(MapFile, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(MapFile));
  }

  DISALLOW_COPY_AND_ASSIGN(MapFile)
};

class Cat : public ::vm::_Builtin {
 public:
  Cat();
  virtual int Run(cmd_value::Argv* cmd_val);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Cat));
  }

  DISALLOW_COPY_AND_ASSIGN(Cat)
};


}  // declare namespace io_osh

namespace io_ysh {  // declare

class _Builtin : public ::vm::_Builtin {
 public:
  _Builtin(state::Mem* mem, ui::ErrorFormatter* errfmt);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(_Builtin, errfmt))
         | maskbit(offsetof(_Builtin, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_Builtin));
  }

  DISALLOW_COPY_AND_ASSIGN(_Builtin)
};

class Pp : public ::io_ysh::_Builtin {
 public:
  Pp(expr_eval::ExprEvaluator* expr_ev, state::Mem* mem, ui::ErrorFormatter* errfmt, state::Procs* procs, alloc::Arena* arena);
  int _PrettyPrint(cmd_value::Argv* cmd_val);
  int Run(cmd_value::Argv* cmd_val);

  alloc::Arena* arena{};
  expr_eval::ExprEvaluator* expr_ev{};
  state::Procs* procs{};
  mylib::Writer* stdout_{};
  
  static constexpr uint32_t field_mask() {
    return ::io_ysh::_Builtin::field_mask()
         | maskbit(offsetof(Pp, arena))
         | maskbit(offsetof(Pp, expr_ev))
         | maskbit(offsetof(Pp, procs))
         | maskbit(offsetof(Pp, stdout_));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Pp));
  }

  DISALLOW_COPY_AND_ASSIGN(Pp)
};

class Write : public ::io_ysh::_Builtin {
 public:
  Write(state::Mem* mem, ui::ErrorFormatter* errfmt);
  int Run(cmd_value::Argv* cmd_val);

  mylib::Writer* stdout_{};
  
  static constexpr uint32_t field_mask() {
    return ::io_ysh::_Builtin::field_mask()
         | maskbit(offsetof(Write, stdout_));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Write));
  }

  DISALLOW_COPY_AND_ASSIGN(Write)
};

class RunBlock : public ::vm::_Builtin {
 public:
  RunBlock(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(RunBlock, cmd_ev))
         | maskbit(offsetof(RunBlock, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(RunBlock));
  }

  DISALLOW_COPY_AND_ASSIGN(RunBlock)
};


}  // declare namespace io_ysh

namespace json_ysh {  // declare

extern BigStr* _JSON_ACTION_ERROR;
class Json : public ::vm::_Builtin {
 public:
  Json(state::Mem* mem, ui::ErrorFormatter* errfmt, bool is_j8);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  bool is_j8{};
  state::Mem* mem{};
  BigStr* name{};
  mylib::Writer* stdout_{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Json, errfmt))
         | maskbit(offsetof(Json, mem))
         | maskbit(offsetof(Json, name))
         | maskbit(offsetof(Json, stdout_));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Json));
  }

  DISALLOW_COPY_AND_ASSIGN(Json)
};


}  // declare namespace json_ysh

namespace meta_oils {  // declare

class Eval : public ::vm::_Builtin {
 public:
  Eval(parse_lib::ParseContext* parse_ctx, optview::Exec* exec_opts, cmd_eval::CommandEvaluator* cmd_ev, dev::Tracer* tracer, ui::ErrorFormatter* errfmt, state::Mem* mem);
  virtual int Run(cmd_value::Argv* cmd_val);

  alloc::Arena* arena{};
  cmd_eval::CommandEvaluator* cmd_ev{};
  ui::ErrorFormatter* errfmt{};
  optview::Exec* exec_opts{};
  state::Mem* mem{};
  parse_lib::ParseContext* parse_ctx{};
  dev::Tracer* tracer{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Eval, arena))
         | maskbit(offsetof(Eval, cmd_ev))
         | maskbit(offsetof(Eval, errfmt))
         | maskbit(offsetof(Eval, exec_opts))
         | maskbit(offsetof(Eval, mem))
         | maskbit(offsetof(Eval, parse_ctx))
         | maskbit(offsetof(Eval, tracer));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Eval));
  }

  DISALLOW_COPY_AND_ASSIGN(Eval)
};

BigStr* _VarName(BigStr* module_path);
class ShellFile : public ::vm::_Builtin {
 public:
  ShellFile(parse_lib::ParseContext* parse_ctx, executor::SearchPath* search_path, cmd_eval::CommandEvaluator* cmd_ev, process::FdState* fd_state, dev::Tracer* tracer, ui::ErrorFormatter* errfmt, pyutil::_ResourceLoader* loader, vm::_Builtin* module_invoke = nullptr);
  virtual int Run(cmd_value::Argv* cmd_val);
  Tuple2<BigStr*, cmd_parse::CommandParser*> LoadEmbeddedFile(BigStr* embed_path, syntax_asdl::loc_t* blame_loc);
  Tuple2<mylib::LineReader*, cmd_parse::CommandParser*> _LoadDiskFile(BigStr* fs_path, syntax_asdl::loc_t* blame_loc);
  int _SourceExec(cmd_value::Argv* cmd_val, args::Reader* arg_r, BigStr* path, cmd_parse::CommandParser* c_parser);
  value_asdl::Obj* _NewModule();
  int _UseExec(cmd_value::Argv* cmd_val, BigStr* path, syntax_asdl::loc_t* path_loc, cmd_parse::CommandParser* c_parser, Dict<BigStr*, value_asdl::value_t*>* props);
  int _Source(cmd_value::Argv* cmd_val);
  int _BindNames(value_asdl::Obj* module_obj, BigStr* module_name, List<BigStr*>* pick_names, List<syntax_asdl::CompoundWord*>* pick_locs);
  int _Use(cmd_value::Argv* cmd_val);

  Dict<BigStr*, value_asdl::Obj*>* _disk_cache{};
  Dict<BigStr*, value_asdl::Obj*>* _embed_cache{};
  alloc::Arena* arena{};
  BigStr* builtin_name{};
  cmd_eval::CommandEvaluator* cmd_ev{};
  ui::ErrorFormatter* errfmt{};
  process::FdState* fd_state{};
  pyutil::_ResourceLoader* loader{};
  state::Mem* mem{};
  vm::_Builtin* module_invoke{};
  parse_lib::ParseContext* parse_ctx{};
  executor::SearchPath* search_path{};
  dev::Tracer* tracer{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(ShellFile, _disk_cache))
         | maskbit(offsetof(ShellFile, _embed_cache))
         | maskbit(offsetof(ShellFile, arena))
         | maskbit(offsetof(ShellFile, builtin_name))
         | maskbit(offsetof(ShellFile, cmd_ev))
         | maskbit(offsetof(ShellFile, errfmt))
         | maskbit(offsetof(ShellFile, fd_state))
         | maskbit(offsetof(ShellFile, loader))
         | maskbit(offsetof(ShellFile, mem))
         | maskbit(offsetof(ShellFile, module_invoke))
         | maskbit(offsetof(ShellFile, parse_ctx))
         | maskbit(offsetof(ShellFile, search_path))
         | maskbit(offsetof(ShellFile, tracer));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ShellFile));
  }

  DISALLOW_COPY_AND_ASSIGN(ShellFile)
};

void _PrintFreeForm(Tuple3<BigStr*, BigStr*, BigStr*>* row);
void _PrintEntry(arg_types::type* arg, Tuple3<BigStr*, BigStr*, BigStr*>* row);
class Command : public ::vm::_Builtin {
 public:
  Command(vm::_Executor* shell_ex, state::Procs* funcs, Dict<BigStr*, BigStr*>* aliases, executor::SearchPath* search_path);
  virtual int Run(cmd_value::Argv* cmd_val);

  Dict<BigStr*, BigStr*>* aliases{};
  state::Procs* funcs{};
  executor::SearchPath* search_path{};
  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Command, aliases))
         | maskbit(offsetof(Command, funcs))
         | maskbit(offsetof(Command, search_path))
         | maskbit(offsetof(Command, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Command));
  }

  DISALLOW_COPY_AND_ASSIGN(Command)
};

cmd_value::Argv* _ShiftArgv(cmd_value::Argv* cmd_val);
class Builtin : public ::vm::_Builtin {
 public:
  Builtin(vm::_Executor* shell_ex, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Builtin, errfmt))
         | maskbit(offsetof(Builtin, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Builtin));
  }

  DISALLOW_COPY_AND_ASSIGN(Builtin)
};

class RunProc : public ::vm::_Builtin {
 public:
  RunProc(vm::_Executor* shell_ex, state::Procs* procs, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Procs* procs{};
  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(RunProc, errfmt))
         | maskbit(offsetof(RunProc, procs))
         | maskbit(offsetof(RunProc, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(RunProc));
  }

  DISALLOW_COPY_AND_ASSIGN(RunProc)
};

class Invoke : public ::vm::_Builtin {
 public:
  Invoke(vm::_Executor* shell_ex, state::Procs* procs, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Procs* procs{};
  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Invoke, errfmt))
         | maskbit(offsetof(Invoke, procs))
         | maskbit(offsetof(Invoke, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Invoke));
  }

  DISALLOW_COPY_AND_ASSIGN(Invoke)
};

class Extern : public ::vm::_Builtin {
 public:
  Extern(vm::_Executor* shell_ex, state::Procs* procs, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Procs* procs{};
  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Extern, errfmt))
         | maskbit(offsetof(Extern, procs))
         | maskbit(offsetof(Extern, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Extern));
  }

  DISALLOW_COPY_AND_ASSIGN(Extern)
};

List<Tuple3<BigStr*, BigStr*, BigStr*>*>* _ResolveName(BigStr* name, state::Procs* procs, Dict<BigStr*, BigStr*>* aliases, executor::SearchPath* search_path, bool do_all);
class Type : public ::vm::_Builtin {
 public:
  Type(state::Procs* funcs, Dict<BigStr*, BigStr*>* aliases, executor::SearchPath* search_path, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  Dict<BigStr*, BigStr*>* aliases{};
  ui::ErrorFormatter* errfmt{};
  state::Procs* funcs{};
  executor::SearchPath* search_path{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Type, aliases))
         | maskbit(offsetof(Type, errfmt))
         | maskbit(offsetof(Type, funcs))
         | maskbit(offsetof(Type, search_path));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Type));
  }

  DISALLOW_COPY_AND_ASSIGN(Type)
};


}  // declare namespace meta_oils

namespace method_dict {  // declare

class Keys : public ::vm::_Callable {
 public:
  Keys();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Keys));
  }

  DISALLOW_COPY_AND_ASSIGN(Keys)
};

class Values : public ::vm::_Callable {
 public:
  Values();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Values));
  }

  DISALLOW_COPY_AND_ASSIGN(Values)
};

class Erase : public ::vm::_Callable {
 public:
  Erase();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Erase));
  }

  DISALLOW_COPY_AND_ASSIGN(Erase)
};

class Get : public ::vm::_Callable {
 public:
  Get();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Get));
  }

  DISALLOW_COPY_AND_ASSIGN(Get)
};


}  // declare namespace method_dict

namespace method_io {  // declare

extern int EVAL_NULL;
extern int EVAL_DICT;
List<BigStr*>* _CheckPosArgs(List<value_asdl::value_t*>* pos_args_raw, syntax_asdl::loc_t* blame_loc);
class EvalExpr : public ::vm::_Callable {
 public:
  EvalExpr(expr_eval::ExprEvaluator* expr_ev);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  expr_eval::ExprEvaluator* expr_ev{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(EvalExpr, expr_ev))
         | maskbit(offsetof(EvalExpr, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(EvalExpr));
  }

  DISALLOW_COPY_AND_ASSIGN(EvalExpr)
};

void _PrintFrame(BigStr* prefix, Dict<BigStr*, runtime_asdl::Cell*>* frame);
class EvalInFrame : public ::vm::_Callable {
 public:
  EvalInFrame(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  cmd_eval::CommandEvaluator* cmd_ev{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(EvalInFrame, cmd_ev))
         | maskbit(offsetof(EvalInFrame, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(EvalInFrame));
  }

  DISALLOW_COPY_AND_ASSIGN(EvalInFrame)
};

class Eval : public ::vm::_Callable {
 public:
  Eval(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev, int which);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  cmd_eval::CommandEvaluator* cmd_ev{};
  state::Mem* mem{};
  int which{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(Eval, cmd_ev))
         | maskbit(offsetof(Eval, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Eval));
  }

  DISALLOW_COPY_AND_ASSIGN(Eval)
};

class CaptureStdout : public ::vm::_Callable {
 public:
  CaptureStdout(state::Mem* mem, vm::_Executor* shell_ex);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  state::Mem* mem{};
  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(CaptureStdout, mem))
         | maskbit(offsetof(CaptureStdout, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(CaptureStdout));
  }

  DISALLOW_COPY_AND_ASSIGN(CaptureStdout)
};

class PromptVal : public ::vm::_Callable {
 public:
  PromptVal(prompt::Evaluator* prompt_ev);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  prompt::Evaluator* prompt_ev{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(PromptVal, prompt_ev));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(PromptVal));
  }

  DISALLOW_COPY_AND_ASSIGN(PromptVal)
};

class Time : public ::vm::_Callable {
 public:
  Time();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Time));
  }

  DISALLOW_COPY_AND_ASSIGN(Time)
};

class Strftime : public ::vm::_Callable {
 public:
  Strftime();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Strftime));
  }

  DISALLOW_COPY_AND_ASSIGN(Strftime)
};

class Glob : public ::vm::_Callable {
 public:
  Glob();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Glob));
  }

  DISALLOW_COPY_AND_ASSIGN(Glob)
};


}  // declare namespace method_io

namespace method_list {  // declare

class Append : public ::vm::_Callable {
 public:
  Append();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Append));
  }

  DISALLOW_COPY_AND_ASSIGN(Append)
};

class Clear : public ::vm::_Callable {
 public:
  Clear();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Clear));
  }

  DISALLOW_COPY_AND_ASSIGN(Clear)
};

class Extend : public ::vm::_Callable {
 public:
  Extend();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Extend));
  }

  DISALLOW_COPY_AND_ASSIGN(Extend)
};

class Pop : public ::vm::_Callable {
 public:
  Pop();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Pop));
  }

  DISALLOW_COPY_AND_ASSIGN(Pop)
};

class Reverse : public ::vm::_Callable {
 public:
  Reverse();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Reverse));
  }

  DISALLOW_COPY_AND_ASSIGN(Reverse)
};

class IndexOf : public ::vm::_Callable {
 public:
  IndexOf();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(IndexOf));
  }

  DISALLOW_COPY_AND_ASSIGN(IndexOf)
};

class LastIndexOf : public ::vm::_Callable {
 public:
  LastIndexOf();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(LastIndexOf));
  }

  DISALLOW_COPY_AND_ASSIGN(LastIndexOf)
};


}  // declare namespace method_list

namespace method_other {  // declare

class SetValue : public ::vm::_Callable {
 public:
  SetValue(state::Mem* mem);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(SetValue, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetValue));
  }

  DISALLOW_COPY_AND_ASSIGN(SetValue)
};


}  // declare namespace method_other

namespace method_str {  // declare

Tuple3<bool, int, int> _StrMatchStart(BigStr* s, BigStr* p);
Tuple3<bool, int, int> _StrMatchEnd(BigStr* s, BigStr* p);
Tuple3<bool, int, int> _EggexMatchCommon(BigStr* s, value::Eggex* p, BigStr* ere, int empty_p);
Tuple3<bool, int, int> _EggexMatchStart(BigStr* s, value::Eggex* p);
Tuple3<bool, int, int> _EggexMatchEnd(BigStr* s, value::Eggex* p);
extern int START;
extern int END;
class HasAffix : public ::vm::_Callable {
 public:
  HasAffix(int anchor);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  int anchor{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(HasAffix));
  }

  DISALLOW_COPY_AND_ASSIGN(HasAffix)
};

class Trim : public ::vm::_Callable {
 public:
  Trim(int anchor);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  int anchor{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Trim));
  }

  DISALLOW_COPY_AND_ASSIGN(Trim)
};

class Upper : public ::vm::_Callable {
 public:
  Upper();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Upper));
  }

  DISALLOW_COPY_AND_ASSIGN(Upper)
};

class Lower : public ::vm::_Callable {
 public:
  Lower();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Lower));
  }

  DISALLOW_COPY_AND_ASSIGN(Lower)
};

extern int SEARCH;
extern int LEFT_MATCH;
class SearchMatch : public ::vm::_Callable {
 public:
  SearchMatch(int which_method);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  int which_method{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SearchMatch));
  }

  DISALLOW_COPY_AND_ASSIGN(SearchMatch)
};

class Replace : public ::vm::_Callable {
 public:
  Replace(state::Mem* mem, expr_eval::ExprEvaluator* expr_ev);
  BigStr* EvalSubstExpr(value::Expr* expr, syntax_asdl::loc_t* blame_loc);
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);

  expr_eval::ExprEvaluator* expr_ev{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(Replace, expr_ev))
         | maskbit(offsetof(Replace, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Replace));
  }

  DISALLOW_COPY_AND_ASSIGN(Replace)
};

class Split : public ::vm::_Callable {
 public:
  Split();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Split));
  }

  DISALLOW_COPY_AND_ASSIGN(Split)
};


}  // declare namespace method_str

namespace method_type {  // declare

BigStr* _GetStringField(value_asdl::Obj* obj, BigStr* field_name);
class Index__ : public ::vm::_Callable {
 public:
  Index__();
  virtual value_asdl::value_t* Call(typed_args::Reader* rd);
  value_asdl::value_t* _Call(typed_args::Reader* rd);

  Dict<BigStr*, value_asdl::Obj*>* unique_instances{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Callable::field_mask()
         | maskbit(offsetof(Index__, unique_instances));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Index__));
  }

  DISALLOW_COPY_AND_ASSIGN(Index__)
};


}  // declare namespace method_type

namespace misc_osh {  // declare

class Times : public ::vm::_Builtin {
 public:
  Times();
  virtual int Run(cmd_value::Argv* cmd_val);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Times));
  }

  DISALLOW_COPY_AND_ASSIGN(Times)
};

class Help : public ::vm::_Builtin {
 public:
  Help(BigStr* lang, pyutil::_ResourceLoader* loader, Dict<BigStr*, BigStr*>* help_data, ui::ErrorFormatter* errfmt);
  int _ShowTopic(BigStr* topic_id, syntax_asdl::loc_t* blame_loc);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  mylib::Writer* f{};
  Dict<BigStr*, BigStr*>* help_data{};
  BigStr* lang{};
  pyutil::_ResourceLoader* loader{};
  BigStr* version_str{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Help, errfmt))
         | maskbit(offsetof(Help, f))
         | maskbit(offsetof(Help, help_data))
         | maskbit(offsetof(Help, lang))
         | maskbit(offsetof(Help, loader))
         | maskbit(offsetof(Help, version_str));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Help));
  }

  DISALLOW_COPY_AND_ASSIGN(Help)
};


}  // declare namespace misc_osh

namespace module_ysh {  // declare

class IsMain : public ::vm::_Builtin {
 public:
  IsMain(state::Mem* mem);
  virtual int Run(cmd_value::Argv* cmd_val);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(IsMain, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(IsMain));
  }

  DISALLOW_COPY_AND_ASSIGN(IsMain)
};

class SourceGuard : public ::vm::_Builtin {
 public:
  SourceGuard(Dict<BigStr*, bool>* guards, optview::Exec* exec_opts, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  optview::Exec* exec_opts{};
  Dict<BigStr*, bool>* guards{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(SourceGuard, errfmt))
         | maskbit(offsetof(SourceGuard, exec_opts))
         | maskbit(offsetof(SourceGuard, guards));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SourceGuard));
  }

  DISALLOW_COPY_AND_ASSIGN(SourceGuard)
};

class ModuleInvoke : public ::vm::_Builtin {
 public:
  ModuleInvoke(cmd_eval::CommandEvaluator* cmd_ev, dev::Tracer* tracer, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  ui::ErrorFormatter* errfmt{};
  dev::Tracer* tracer{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(ModuleInvoke, cmd_ev))
         | maskbit(offsetof(ModuleInvoke, errfmt))
         | maskbit(offsetof(ModuleInvoke, tracer));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ModuleInvoke));
  }

  DISALLOW_COPY_AND_ASSIGN(ModuleInvoke)
};


}  // declare namespace module_ysh

namespace printf_osh {  // declare

class _FormatStringParser {
 public:
  _FormatStringParser(lexer::Lexer* lexer);
  void _Next(types_asdl::lex_mode_t lex_mode);
  syntax_asdl::printf_part_t* _ParseFormatStr();
  List<syntax_asdl::printf_part_t*>* Parse();
  lexer::Lexer* lexer{};
  syntax_asdl::Token* cur_token{};
  int token_type{};
  id_kind_asdl::Kind_t token_kind{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(_FormatStringParser));
  }

  DISALLOW_COPY_AND_ASSIGN(_FormatStringParser)
};

class _PrintfState {
 public:
  _PrintfState();
  int arg_index{};
  bool backslash_c{};
  int status{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(_PrintfState));
  }

  DISALLOW_COPY_AND_ASSIGN(_PrintfState)
};

class Printf : public ::vm::_Builtin {
 public:
  Printf(state::Mem* mem, parse_lib::ParseContext* parse_ctx, sh_expr_eval::UnsafeArith* unsafe_arith, ui::ErrorFormatter* errfmt);
  BigStr* _Percent(printf_osh::_PrintfState* pr, printf_part::Percent* part, List<BigStr*>* varargs, List<syntax_asdl::CompoundWord*>* locs);
  int _Format(List<syntax_asdl::printf_part_t*>* parts, List<BigStr*>* varargs, List<syntax_asdl::CompoundWord*>* locs, List<BigStr*>* out);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  Dict<BigStr*, List<syntax_asdl::printf_part_t*>*>* parse_cache{};
  parse_lib::ParseContext* parse_ctx{};
  double shell_start_time{};
  sh_expr_eval::UnsafeArith* unsafe_arith{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Printf, errfmt))
         | maskbit(offsetof(Printf, mem))
         | maskbit(offsetof(Printf, parse_cache))
         | maskbit(offsetof(Printf, parse_ctx))
         | maskbit(offsetof(Printf, unsafe_arith));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Printf));
  }

  DISALLOW_COPY_AND_ASSIGN(Printf)
};


}  // declare namespace printf_osh

namespace process_osh {  // declare

class Jobs : public ::vm::_Builtin {
 public:
  Jobs(process::JobList* job_list);
  virtual int Run(cmd_value::Argv* cmd_val);

  process::JobList* job_list{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Jobs, job_list));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Jobs));
  }

  DISALLOW_COPY_AND_ASSIGN(Jobs)
};

class Fg : public ::vm::_Builtin {
 public:
  Fg(process::JobControl* job_control, process::JobList* job_list, process::Waiter* waiter);
  virtual int Run(cmd_value::Argv* cmd_val);

  process::JobControl* job_control{};
  process::JobList* job_list{};
  process::Waiter* waiter{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Fg, job_control))
         | maskbit(offsetof(Fg, job_list))
         | maskbit(offsetof(Fg, waiter));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Fg));
  }

  DISALLOW_COPY_AND_ASSIGN(Fg)
};

class Bg : public ::vm::_Builtin {
 public:
  Bg(process::JobList* job_list);
  virtual int Run(cmd_value::Argv* cmd_val);

  process::JobList* job_list{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Bg, job_list));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Bg));
  }

  DISALLOW_COPY_AND_ASSIGN(Bg)
};

class Fork : public ::vm::_Builtin {
 public:
  Fork(vm::_Executor* shell_ex);
  virtual int Run(cmd_value::Argv* cmd_val);

  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Fork, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Fork));
  }

  DISALLOW_COPY_AND_ASSIGN(Fork)
};

class ForkWait : public ::vm::_Builtin {
 public:
  ForkWait(vm::_Executor* shell_ex);
  virtual int Run(cmd_value::Argv* cmd_val);

  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(ForkWait, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ForkWait));
  }

  DISALLOW_COPY_AND_ASSIGN(ForkWait)
};

class Exec : public ::vm::_Builtin {
 public:
  Exec(state::Mem* mem, process::ExternalProgram* ext_prog, process::FdState* fd_state, executor::SearchPath* search_path, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  process::ExternalProgram* ext_prog{};
  process::FdState* fd_state{};
  state::Mem* mem{};
  executor::SearchPath* search_path{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Exec, errfmt))
         | maskbit(offsetof(Exec, ext_prog))
         | maskbit(offsetof(Exec, fd_state))
         | maskbit(offsetof(Exec, mem))
         | maskbit(offsetof(Exec, search_path));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Exec));
  }

  DISALLOW_COPY_AND_ASSIGN(Exec)
};

class Wait : public ::vm::_Builtin {
 public:
  Wait(process::Waiter* waiter, process::JobList* job_list, state::Mem* mem, dev::Tracer* tracer, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);
  int _Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  process::JobList* job_list{};
  state::Mem* mem{};
  dev::Tracer* tracer{};
  process::Waiter* waiter{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Wait, errfmt))
         | maskbit(offsetof(Wait, job_list))
         | maskbit(offsetof(Wait, mem))
         | maskbit(offsetof(Wait, tracer))
         | maskbit(offsetof(Wait, waiter));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Wait));
  }

  DISALLOW_COPY_AND_ASSIGN(Wait)
};

class Umask : public ::vm::_Builtin {
 public:
  Umask();
  virtual int Run(cmd_value::Argv* cmd_val);
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Umask));
  }

  DISALLOW_COPY_AND_ASSIGN(Umask)
};

BigStr* _LimitString(mops::BigInt lim, int factor);
class Ulimit : public ::vm::_Builtin {
 public:
  Ulimit();
  List<Tuple4<BigStr*, int, int, BigStr*>*>* _Table();
  int _FindFactor(int what);
  virtual int Run(cmd_value::Argv* cmd_val);

  List<Tuple4<BigStr*, int, int, BigStr*>*>* _table{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Ulimit, _table));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Ulimit));
  }

  DISALLOW_COPY_AND_ASSIGN(Ulimit)
};


}  // declare namespace process_osh

namespace pure_osh {  // declare

class Boolean : public ::vm::_Builtin {
 public:
  Boolean(int status);
  virtual int Run(cmd_value::Argv* cmd_val);

  int status{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Boolean));
  }

  DISALLOW_COPY_AND_ASSIGN(Boolean)
};

class Alias : public ::vm::_Builtin {
 public:
  Alias(Dict<BigStr*, BigStr*>* aliases, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  Dict<BigStr*, BigStr*>* aliases{};
  ui::ErrorFormatter* errfmt{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Alias, aliases))
         | maskbit(offsetof(Alias, errfmt));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Alias));
  }

  DISALLOW_COPY_AND_ASSIGN(Alias)
};

class UnAlias : public ::vm::_Builtin {
 public:
  UnAlias(Dict<BigStr*, BigStr*>* aliases, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  Dict<BigStr*, BigStr*>* aliases{};
  ui::ErrorFormatter* errfmt{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(UnAlias, aliases))
         | maskbit(offsetof(UnAlias, errfmt));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(UnAlias));
  }

  DISALLOW_COPY_AND_ASSIGN(UnAlias)
};

void SetOptionsFromFlags(state::MutableOpts* exec_opts, List<Tuple2<BigStr*, bool>*>* opt_changes, List<Tuple2<BigStr*, bool>*>* shopt_changes);
bool ShowOptions(state::MutableOpts* mutable_opts, List<BigStr*>* opt_names);
bool _ShowShoptOptions(state::MutableOpts* mutable_opts, List<int>* opt_nums);
class Set : public ::vm::_Builtin {
 public:
  Set(state::MutableOpts* exec_opts, state::Mem* mem);
  virtual int Run(cmd_value::Argv* cmd_val);

  state::MutableOpts* exec_opts{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Set, exec_opts))
         | maskbit(offsetof(Set, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Set));
  }

  DISALLOW_COPY_AND_ASSIGN(Set)
};

class Shopt : public ::vm::_Builtin {
 public:
  Shopt(optview::Exec* exec_opts, state::MutableOpts* mutable_opts, cmd_eval::CommandEvaluator* cmd_ev, state::Mem* mem, Dict<BigStr*, BigStr*>* environ);
  int _PrintOptions(bool use_set_opts, List<BigStr*>* opt_names);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  Dict<BigStr*, BigStr*>* environ{};
  optview::Exec* exec_opts{};
  state::Mem* mem{};
  state::MutableOpts* mutable_opts{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Shopt, cmd_ev))
         | maskbit(offsetof(Shopt, environ))
         | maskbit(offsetof(Shopt, exec_opts))
         | maskbit(offsetof(Shopt, mem))
         | maskbit(offsetof(Shopt, mutable_opts));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Shopt));
  }

  DISALLOW_COPY_AND_ASSIGN(Shopt)
};

class Hash : public ::vm::_Builtin {
 public:
  Hash(executor::SearchPath* search_path);
  virtual int Run(cmd_value::Argv* cmd_val);

  executor::SearchPath* search_path{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Hash, search_path));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Hash));
  }

  DISALLOW_COPY_AND_ASSIGN(Hash)
};

Dict<BigStr*, bool>* _ParseOptSpec(BigStr* spec_str);
class GetOptsState {
 public:
  GetOptsState(state::Mem* mem, ui::ErrorFormatter* errfmt);
  int _OptInd();
  BigStr* GetArg(List<BigStr*>* argv);
  void IncIndex();
  void SetArg(BigStr* optarg);
  void Fail();
  state::Mem* mem{};
  ui::ErrorFormatter* errfmt{};
  int _optind{};
  int flag_pos{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(GetOptsState));
  }

  DISALLOW_COPY_AND_ASSIGN(GetOptsState)
};

Tuple2<int, BigStr*> _GetOpts(Dict<BigStr*, bool>* spec, List<BigStr*>* argv, pure_osh::GetOptsState* my_state, ui::ErrorFormatter* errfmt);
class GetOpts : public ::vm::_Builtin {
 public:
  GetOpts(state::Mem* mem, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  pure_osh::GetOptsState* my_state{};
  Dict<BigStr*, Dict<BigStr*, bool>*>* spec_cache{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(GetOpts, errfmt))
         | maskbit(offsetof(GetOpts, mem))
         | maskbit(offsetof(GetOpts, my_state))
         | maskbit(offsetof(GetOpts, spec_cache));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(GetOpts));
  }

  DISALLOW_COPY_AND_ASSIGN(GetOpts)
};


}  // declare namespace pure_osh

namespace pure_ysh {  // declare

class Shvar : public ::vm::_Builtin {
 public:
  Shvar(state::Mem* mem, executor::SearchPath* search_path, cmd_eval::CommandEvaluator* cmd_ev);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  state::Mem* mem{};
  executor::SearchPath* search_path{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Shvar, cmd_ev))
         | maskbit(offsetof(Shvar, mem))
         | maskbit(offsetof(Shvar, search_path));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Shvar));
  }

  DISALLOW_COPY_AND_ASSIGN(Shvar)
};

class ctx_Context {
 public:
  ctx_Context(state::Mem* mem, Dict<BigStr*, value_asdl::value_t*>* context);
  ~ctx_Context();
  state::Mem* mem{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Context)
};

class Ctx : public ::vm::_Builtin {
 public:
  Ctx(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev);
  Dict<BigStr*, value_asdl::value_t*>* _GetContext();
  int _Push(Dict<BigStr*, value_asdl::value_t*>* context, syntax_asdl::command_t* block);
  int _Set(Dict<BigStr*, value_asdl::value_t*>* updates);
  int _Emit(BigStr* field, value_asdl::value_t* item, syntax_asdl::loc_t* blame);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Ctx, cmd_ev))
         | maskbit(offsetof(Ctx, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Ctx));
  }

  DISALLOW_COPY_AND_ASSIGN(Ctx)
};

class PushRegisters : public ::vm::_Builtin {
 public:
  PushRegisters(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev);
  virtual int Run(cmd_value::Argv* cmd_val);

  cmd_eval::CommandEvaluator* cmd_ev{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(PushRegisters, cmd_ev))
         | maskbit(offsetof(PushRegisters, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(PushRegisters));
  }

  DISALLOW_COPY_AND_ASSIGN(PushRegisters)
};

class Append : public ::vm::_Builtin {
 public:
  Append(state::Mem* mem, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Append, errfmt))
         | maskbit(offsetof(Append, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Append));
  }

  DISALLOW_COPY_AND_ASSIGN(Append)
};


}  // declare namespace pure_ysh

namespace read_osh {  // declare

Tuple2<bool, bool> _AppendParts(BigStr* s, List<Tuple2<runtime_asdl::span_t, int>*>* spans, int max_results, bool join_next, List<mylib::BufWriter*>* parts);
BigStr* _ReadN(int num_bytes, cmd_eval::CommandEvaluator* cmd_ev);
Tuple2<BigStr*, bool> _ReadPortion(int delim_byte, int max_chars, cmd_eval::CommandEvaluator* cmd_ev);
BigStr* ReadLineSlowly(cmd_eval::CommandEvaluator* cmd_ev, bool with_eol = true);
BigStr* ReadAll();
class ctx_TermAttrs {
 public:
  ctx_TermAttrs(int fd, int local_modes);
  ~ctx_TermAttrs();
  void* term_attrs{};
  int fd{};
  int orig_local_modes{};

  DISALLOW_COPY_AND_ASSIGN(ctx_TermAttrs)
};

class Read : public ::vm::_Builtin {
 public:
  Read(split::SplitContext* splitter, state::Mem* mem, parse_lib::ParseContext* parse_ctx, cmd_eval::CommandEvaluator* cmd_ev, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);
  int _ReadYsh(arg_types::read* arg, args::Reader* arg_r, cmd_value::Argv* cmd_val);
  int _Run(cmd_value::Argv* cmd_val);
  int _Read(arg_types::read* arg, List<BigStr*>* names);

  cmd_eval::CommandEvaluator* cmd_ev{};
  ui::ErrorFormatter* errfmt{};
  state::Mem* mem{};
  parse_lib::ParseContext* parse_ctx{};
  split::SplitContext* splitter{};
  mylib::LineReader* stdin_{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Read, cmd_ev))
         | maskbit(offsetof(Read, errfmt))
         | maskbit(offsetof(Read, mem))
         | maskbit(offsetof(Read, parse_ctx))
         | maskbit(offsetof(Read, splitter))
         | maskbit(offsetof(Read, stdin_));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Read));
  }

  DISALLOW_COPY_AND_ASSIGN(Read)
};


}  // declare namespace read_osh

namespace readline_osh {  // declare

class ctx_Keymap {
 public:
  ctx_Keymap(py_readline::Readline* readline, BigStr* keymap_name = nullptr);
  ~ctx_Keymap();
  py_readline::Readline* readline{};
  BigStr* orig_keymap_name{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Keymap)
};

class Bind : public ::vm::_Builtin {
 public:
  Bind(py_readline::Readline* readline, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  List<BigStr*>* exclusive_flags{};
  py_readline::Readline* readline{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Bind, errfmt))
         | maskbit(offsetof(Bind, exclusive_flags))
         | maskbit(offsetof(Bind, readline));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Bind));
  }

  DISALLOW_COPY_AND_ASSIGN(Bind)
};

class History : public ::vm::_Builtin {
 public:
  History(py_readline::Readline* readline, sh_init::ShellFiles* sh_files, ui::ErrorFormatter* errfmt, mylib::Writer* f);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  mylib::Writer* f{};
  py_readline::Readline* readline{};
  sh_init::ShellFiles* sh_files{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(History, errfmt))
         | maskbit(offsetof(History, f))
         | maskbit(offsetof(History, readline))
         | maskbit(offsetof(History, sh_files));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(History));
  }

  DISALLOW_COPY_AND_ASSIGN(History)
};


}  // declare namespace readline_osh

namespace trap_osh {  // declare

class TrapState {
 public:
  TrapState(iolib::SignalSafe* signal_safe);
  void ClearForSubProgram(bool inherit_errtrace);
  syntax_asdl::command_t* GetHook(BigStr* hook_name);
  void AddUserHook(BigStr* hook_name, syntax_asdl::command_t* handler);
  void RemoveUserHook(BigStr* hook_name);
  void AddUserTrap(int sig_num, syntax_asdl::command_t* handler);
  void RemoveUserTrap(int sig_num);
  List<syntax_asdl::command_t*>* GetPendingTraps();
  bool ThisProcessHasTraps();
  iolib::SignalSafe* signal_safe{};
  Dict<BigStr*, syntax_asdl::command_t*>* hooks{};
  Dict<int, syntax_asdl::command_t*>* traps{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(TrapState));
  }

  DISALLOW_COPY_AND_ASSIGN(TrapState)
};

bool _IsUnsignedInteger(BigStr* s, syntax_asdl::loc_t* blame_loc);
int _GetSignalNumber(BigStr* sig_spec);
extern List<BigStr*>* _HOOK_NAMES;
class Trap : public ::vm::_Builtin {
 public:
  Trap(trap_osh::TrapState* trap_state, parse_lib::ParseContext* parse_ctx, dev::Tracer* tracer, ui::ErrorFormatter* errfmt);
  syntax_asdl::command_t* _ParseTrapCode(BigStr* code_str);
  virtual int Run(cmd_value::Argv* cmd_val);

  alloc::Arena* arena{};
  ui::ErrorFormatter* errfmt{};
  parse_lib::ParseContext* parse_ctx{};
  dev::Tracer* tracer{};
  trap_osh::TrapState* trap_state{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Trap, arena))
         | maskbit(offsetof(Trap, errfmt))
         | maskbit(offsetof(Trap, parse_ctx))
         | maskbit(offsetof(Trap, tracer))
         | maskbit(offsetof(Trap, trap_state));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Trap));
  }

  DISALLOW_COPY_AND_ASSIGN(Trap)
};


}  // declare namespace trap_osh

namespace alloc {  // declare

BigStr* SnipCodeBlock(syntax_asdl::Token* left, syntax_asdl::Token* right, List<syntax_asdl::SourceLine*>* lines);
class ctx_SourceCode {
 public:
  ctx_SourceCode(alloc::Arena* arena, syntax_asdl::source_t* src);
  ~ctx_SourceCode();
  alloc::Arena* arena{};

  DISALLOW_COPY_AND_ASSIGN(ctx_SourceCode)
};

class Arena {
 public:
  Arena(bool save_tokens = false);
  void SaveTokens();
  void PushSource(syntax_asdl::source_t* src);
  void PopSource();
  syntax_asdl::SourceLine* AddLine(BigStr* line, int line_num);
  void DiscardLines();
  List<syntax_asdl::SourceLine*>* SaveLinesAndDiscard(syntax_asdl::Token* left, syntax_asdl::Token* right);
  BigStr* SnipCodeString(syntax_asdl::Token* left, syntax_asdl::Token* right);
  syntax_asdl::Token* NewToken(int id_, int col, int length, syntax_asdl::SourceLine* src_line);
  void UnreadOne();
  syntax_asdl::Token* GetToken(int span_id);
  int GetSpanId(syntax_asdl::Token* tok);
  int LastSpanId();
  List<syntax_asdl::SourceLine*>* lines_list{};
  int num_tokens{};
  bool save_tokens{};
  List<syntax_asdl::source_t*>* source_instances{};
  Dict<syntax_asdl::Token*, int>* span_id_lookup{};
  List<syntax_asdl::Token*>* tokens{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(Arena, lines_list))
         | maskbit(offsetof(Arena, source_instances))
         | maskbit(offsetof(Arena, span_id_lookup))
         | maskbit(offsetof(Arena, tokens));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Arena));
  }

  DISALLOW_COPY_AND_ASSIGN(Arena)
};

class LosslessArena : public ::alloc::Arena {
 public:
  
  static constexpr uint32_t field_mask() {
    return ::alloc::Arena::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(LosslessArena));
  }

  DISALLOW_COPY_AND_ASSIGN(LosslessArena)
};

class DynamicArena : public ::alloc::Arena {
 public:
  
  static constexpr uint32_t field_mask() {
    return ::alloc::Arena::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DynamicArena));
  }

  DISALLOW_COPY_AND_ASSIGN(DynamicArena)
};


}  // declare namespace alloc

namespace bash_impl {  // declare

bool BigInt_Greater(mops::BigInt a, mops::BigInt b);
bool BigInt_Less(mops::BigInt a, mops::BigInt b);
bool BigInt_GreaterEq(mops::BigInt a, mops::BigInt b);
bool BigInt_LessEq(mops::BigInt a, mops::BigInt b);
bool BashArray_IsEmpty(value::BashArray* array_val);
int BashArray_Count(value::BashArray* array_val);
int BashArray_Length(value::BashArray* array_val);
List<int>* BashArray_GetKeys(value::BashArray* array_val);
List<BigStr*>* BashArray_GetValues(value::BashArray* array_val);
void BashArray_AppendValues(value::BashArray* array_val, List<BigStr*>* strs);
Tuple3<int, int, runtime_asdl::error_code_t> _BashArray_CanonicalizeIndex(value::BashArray* array_val, int index);
Tuple2<bool, runtime_asdl::error_code_t> BashArray_HasElement(value::BashArray* array_val, int index);
Tuple2<BigStr*, runtime_asdl::error_code_t> BashArray_GetElement(value::BashArray* array_val, int index);
runtime_asdl::error_code_t BashArray_SetElement(value::BashArray* array_val, int index, BigStr* s);
runtime_asdl::error_code_t BashArray_UnsetElement(value::BashArray* array_val, int index);
bool BashArray_Equals(value::BashArray* lhs, value::BashArray* rhs);
bool _BashArray_HasHoles(value::BashArray* array_val);
BigStr* BashArray_ToStrForShellPrint(value::BashArray* array_val, BigStr* name);
bool BashAssoc_IsEmpty(value::BashAssoc* assoc_val);
int BashAssoc_Count(value::BashAssoc* assoc_val);
Dict<BigStr*, BigStr*>* BashAssoc_GetDict(value::BashAssoc* assoc_val);
void BashAssoc_AppendDict(value::BashAssoc* assoc_val, Dict<BigStr*, BigStr*>* d);
List<BigStr*>* BashAssoc_GetKeys(value::BashAssoc* assoc_val);
List<BigStr*>* BashAssoc_GetValues(value::BashAssoc* assoc_val);
bool BashAssoc_HasElement(value::BashAssoc* assoc_val, BigStr* s);
BigStr* BashAssoc_GetElement(value::BashAssoc* assoc_val, BigStr* s);
void BashAssoc_SetElement(value::BashAssoc* assoc_val, BigStr* key, BigStr* s);
void BashAssoc_UnsetElement(value::BashAssoc* assoc_val, BigStr* key);
bool BashAssoc_Equals(value::BashAssoc* lhs, value::BashAssoc* rhs);
BigStr* BashAssoc_ToStrForShellPrint(value::BashAssoc* assoc_val);
bool SparseArray_IsEmpty(value::SparseArray* sparse_val);
int SparseArray_Count(value::SparseArray* sparse_val);
mops::BigInt SparseArray_Length(value::SparseArray* sparse_val);
List<mops::BigInt>* SparseArray_GetKeys(value::SparseArray* sparse_val);
List<BigStr*>* SparseArray_GetValues(value::SparseArray* sparse_val);
void SparseArray_AppendValues(value::SparseArray* sparse_val, List<BigStr*>* strs);
Tuple2<mops::BigInt, runtime_asdl::error_code_t> _SparseArray_CanonicalizeIndex(value::SparseArray* sparse_val, mops::BigInt index);
Tuple2<bool, runtime_asdl::error_code_t> SparseArray_HasElement(value::SparseArray* sparse_val, mops::BigInt index);
Tuple2<BigStr*, runtime_asdl::error_code_t> SparseArray_GetElement(value::SparseArray* sparse_val, mops::BigInt index);
runtime_asdl::error_code_t SparseArray_SetElement(value::SparseArray* sparse_val, mops::BigInt index, BigStr* s);
runtime_asdl::error_code_t SparseArray_UnsetElement(value::SparseArray* sparse_val, mops::BigInt index);
bool SparseArray_Equals(value::SparseArray* lhs, value::SparseArray* rhs);
BigStr* SparseArray_ToStrForShellPrint(value::SparseArray* sparse_val);

}  // declare namespace bash_impl

namespace comp_ui {  // declare

int _PromptLen(BigStr* prompt_str);
class PromptState {
 public:
  PromptState();
  void SetLastPrompt(BigStr* prompt_str);
  BigStr* last_prompt_str{};
  int last_prompt_len{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(PromptState));
  }

  DISALLOW_COPY_AND_ASSIGN(PromptState)
};

class State {
 public:
  State();
  BigStr* line_until_tab{};
  Dict<BigStr*, BigStr*>* descriptions{};
  int display_pos{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(State));
  }

  DISALLOW_COPY_AND_ASSIGN(State)
};

class _IDisplay {
 public:
  _IDisplay(comp_ui::State* comp_state, comp_ui::PromptState* prompt_state, int num_lines_cap, mylib::Writer* f, util::_DebugFile* debug_f);
  void PrintCandidates(BigStr* unused_subst, List<BigStr*>* matches, int unused_match_len);
  virtual void _PrintCandidates(BigStr* unused_subst, List<BigStr*>* matches, int unused_match_len);
  virtual void Reset();
  virtual void ShowPromptOnRight(BigStr* rendered);
  virtual void EraseLines();
  comp_ui::State* comp_state{};
  util::_DebugFile* debug_f{};
  mylib::Writer* f{};
  int num_lines_cap{};
  comp_ui::PromptState* prompt_state{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(_IDisplay, comp_state))
         | maskbit(offsetof(_IDisplay, debug_f))
         | maskbit(offsetof(_IDisplay, f))
         | maskbit(offsetof(_IDisplay, prompt_state));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_IDisplay));
  }

  DISALLOW_COPY_AND_ASSIGN(_IDisplay)
};

class MinimalDisplay : public ::comp_ui::_IDisplay {
 public:
  MinimalDisplay(comp_ui::State* comp_state, comp_ui::PromptState* prompt_state, util::_DebugFile* debug_f);
  void _RedrawPrompt();
  virtual void _PrintCandidates(BigStr* unused_subst, List<BigStr*>* matches, int unused_match_len);
  
  static constexpr uint32_t field_mask() {
    return ::comp_ui::_IDisplay::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(MinimalDisplay));
  }

  DISALLOW_COPY_AND_ASSIGN(MinimalDisplay)
};

int _PrintPacked(List<BigStr*>* matches, int max_match_len, int term_width, int max_lines, mylib::Writer* f);
int _PrintLong(List<BigStr*>* matches, int max_match_len, int term_width, int max_lines, Dict<BigStr*, BigStr*>* descriptions, mylib::Writer* f);
class NiceDisplay : public ::comp_ui::_IDisplay {
 public:
  NiceDisplay(int term_width, comp_ui::State* comp_state, comp_ui::PromptState* prompt_state, util::_DebugFile* debug_f, py_readline::Readline* readline, iolib::SignalSafe* signal_safe);
  virtual void Reset();
  void _ReturnToPrompt(int num_lines);
  virtual void _PrintCandidates(BigStr* unused_subst, List<BigStr*>* matches, int unused_max_match_len);
  virtual void ShowPromptOnRight(BigStr* rendered);
  virtual void EraseLines();
  int _GetTerminalWidth();

  bool bold_line{};
  int c_count{};
  Dict<int, int>* dupes{};
  int m_count{};
  int num_lines_last_displayed{};
  py_readline::Readline* readline{};
  iolib::SignalSafe* signal_safe{};
  int term_width{};
  
  static constexpr uint32_t field_mask() {
    return ::comp_ui::_IDisplay::field_mask()
         | maskbit(offsetof(NiceDisplay, dupes))
         | maskbit(offsetof(NiceDisplay, readline))
         | maskbit(offsetof(NiceDisplay, signal_safe));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(NiceDisplay));
  }

  DISALLOW_COPY_AND_ASSIGN(NiceDisplay)
};

void ExecutePrintCandidates(comp_ui::_IDisplay* display, BigStr* sub, List<BigStr*>* matches, int max_len);
void InitReadline(py_readline::Readline* readline, BigStr* hist_file, completion::RootCompleter* root_comp, comp_ui::_IDisplay* display, util::_DebugFile* debug_f);

}  // declare namespace comp_ui

namespace completion {  // declare

class _RetryCompletion {
 public:
  _RetryCompletion();

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(_RetryCompletion));
  }

  DISALLOW_COPY_AND_ASSIGN(_RetryCompletion)
};

extern int CH_Break;
extern int CH_Other;
extern int ST_Begin;
extern int ST_Break;
extern int ST_Other;
Tuple2<int, bool> _TRANSITIONS(int state, int ch);
void AdjustArg(BigStr* arg, List<BigStr*>* break_chars, List<BigStr*>* argv_out);
extern Dict<BigStr*, bool>* _DEFAULT_OPTS;
class OptionState {
 public:
  OptionState();
  Dict<BigStr*, bool>* dynamic_opts{};
  bool currently_completing{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(OptionState));
  }

  DISALLOW_COPY_AND_ASSIGN(OptionState)
};

class ctx_Completing {
 public:
  ctx_Completing(completion::OptionState* compopt_state);
  ~ctx_Completing();
  completion::OptionState* compopt_state{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Completing)
};

void _PrintOpts(Dict<BigStr*, bool>* opts, mylib::BufWriter* f);
class Lookup {
 public:
  Lookup();
  BigStr* __str__();
  void PrintSpecs();
  void ClearCommandsChanged();
  List<BigStr*>* GetCommandsChanged();
  void RegisterName(BigStr* name, Dict<BigStr*, bool>* base_opts, completion::UserSpec* user_spec);
  void RegisterGlob(BigStr* glob_pat, Dict<BigStr*, bool>* base_opts, completion::UserSpec* user_spec);
  Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> GetSpecForName(BigStr* argv0);
  Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> GetFirstSpec();
  Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> GetFallback();
  Dict<BigStr*, Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>*>* lookup{};
  List<BigStr*>* commands_with_spec_changes{};
  List<Tuple3<BigStr*, Dict<BigStr*, bool>*, completion::UserSpec*>*>* patterns{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(Lookup));
  }

  DISALLOW_COPY_AND_ASSIGN(Lookup)
};

class Api {
 public:
  Api(BigStr* line, int begin, int end);
  void Update(BigStr* first, BigStr* to_complete, BigStr* prev, int index, List<BigStr*>* partial_argv);
  BigStr* line{};
  BigStr* first{};
  BigStr* to_complete{};
  BigStr* prev{};
  List<BigStr*>* partial_argv{};
  int begin{};
  int end{};
  int index{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(5, sizeof(Api));
  }

  DISALLOW_COPY_AND_ASSIGN(Api)
};

class CompletionAction {
 public:
  CompletionAction();
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  virtual runtime_asdl::comp_action_t ActionKind();
  virtual void Print(mylib::BufWriter* f);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(CompletionAction));
  }

  DISALLOW_COPY_AND_ASSIGN(CompletionAction)
};

class UsersAction : public ::completion::CompletionAction {
 public:
  UsersAction();
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  virtual void Print(mylib::BufWriter* f);
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(UsersAction));
  }

  DISALLOW_COPY_AND_ASSIGN(UsersAction)
};

class TestAction : public ::completion::CompletionAction {
 public:
  TestAction(List<BigStr*>* words, double delay = 0.0);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  virtual void Print(mylib::BufWriter* f);

  double delay{};
  List<BigStr*>* words{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(TestAction, words));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(TestAction));
  }

  DISALLOW_COPY_AND_ASSIGN(TestAction)
};

class DynamicWordsAction : public ::completion::CompletionAction {
 public:
  DynamicWordsAction(word_eval::AbstractWordEvaluator* word_ev, split::SplitContext* splitter, syntax_asdl::CompoundWord* arg_word, ui::ErrorFormatter* errfmt);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  virtual void Print(mylib::BufWriter* f);

  syntax_asdl::CompoundWord* arg_word{};
  ui::ErrorFormatter* errfmt{};
  split::SplitContext* splitter{};
  word_eval::AbstractWordEvaluator* word_ev{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(DynamicWordsAction, arg_word))
         | maskbit(offsetof(DynamicWordsAction, errfmt))
         | maskbit(offsetof(DynamicWordsAction, splitter))
         | maskbit(offsetof(DynamicWordsAction, word_ev));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DynamicWordsAction));
  }

  DISALLOW_COPY_AND_ASSIGN(DynamicWordsAction)
};

class FileSystemAction : public ::completion::CompletionAction {
 public:
  FileSystemAction(bool dirs_only, bool exec_only, bool add_slash);
  virtual runtime_asdl::comp_action_t ActionKind();
  virtual void Print(mylib::BufWriter* f);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);

  bool add_slash{};
  bool dirs_only{};
  bool exec_only{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(FileSystemAction));
  }

  DISALLOW_COPY_AND_ASSIGN(FileSystemAction)
};

class CommandAction : public ::completion::CompletionAction {
 public:
  CommandAction(cmd_eval::CommandEvaluator* cmd_ev, BigStr* command_name);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);

  cmd_eval::CommandEvaluator* cmd_ev{};
  BigStr* command_name{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(CommandAction, cmd_ev))
         | maskbit(offsetof(CommandAction, command_name));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(CommandAction));
  }

  DISALLOW_COPY_AND_ASSIGN(CommandAction)
};

class ShellFuncAction : public ::completion::CompletionAction {
 public:
  ShellFuncAction(cmd_eval::CommandEvaluator* cmd_ev, value::Proc* func, completion::Lookup* comp_lookup);
  virtual void Print(mylib::BufWriter* f);
  virtual runtime_asdl::comp_action_t ActionKind();
  void debug(BigStr* msg);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);

  cmd_eval::CommandEvaluator* cmd_ev{};
  completion::Lookup* comp_lookup{};
  value::Proc* func{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(ShellFuncAction, cmd_ev))
         | maskbit(offsetof(ShellFuncAction, comp_lookup))
         | maskbit(offsetof(ShellFuncAction, func));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ShellFuncAction));
  }

  DISALLOW_COPY_AND_ASSIGN(ShellFuncAction)
};

class VariablesAction : public ::completion::CompletionAction {
 public:
  VariablesAction(state::Mem* mem);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  virtual void Print(mylib::BufWriter* f);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(VariablesAction, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(VariablesAction));
  }

  DISALLOW_COPY_AND_ASSIGN(VariablesAction)
};

class ExportedVarsAction : public ::completion::CompletionAction {
 public:
  ExportedVarsAction(state::Mem* mem);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(ExportedVarsAction, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ExportedVarsAction));
  }

  DISALLOW_COPY_AND_ASSIGN(ExportedVarsAction)
};

class ExternalCommandAction : public ::completion::CompletionAction {
 public:
  ExternalCommandAction(state::Mem* mem);
  virtual void Print(mylib::BufWriter* f);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);

  Dict<Tuple2<BigStr*, int>*, List<BigStr*>*>* cache{};
  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(ExternalCommandAction, cache))
         | maskbit(offsetof(ExternalCommandAction, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ExternalCommandAction));
  }

  DISALLOW_COPY_AND_ASSIGN(ExternalCommandAction)
};

class _Predicate {
 public:
  _Predicate();
  virtual bool Evaluate(BigStr* candidate);
  virtual void Print(mylib::BufWriter* f);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_Predicate));
  }

  DISALLOW_COPY_AND_ASSIGN(_Predicate)
};

class DefaultPredicate : public ::completion::_Predicate {
 public:
  DefaultPredicate();
  virtual bool Evaluate(BigStr* candidate);
  virtual void Print(mylib::BufWriter* f);
  
  static constexpr uint32_t field_mask() {
    return ::completion::_Predicate::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DefaultPredicate));
  }

  DISALLOW_COPY_AND_ASSIGN(DefaultPredicate)
};

class GlobPredicate : public ::completion::_Predicate {
 public:
  GlobPredicate(bool include, BigStr* glob_pat);
  virtual bool Evaluate(BigStr* candidate);
  virtual void Print(mylib::BufWriter* f);

  BigStr* glob_pat{};
  bool include{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::_Predicate::field_mask()
         | maskbit(offsetof(GlobPredicate, glob_pat));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(GlobPredicate));
  }

  DISALLOW_COPY_AND_ASSIGN(GlobPredicate)
};

class UserSpec {
 public:
  UserSpec(List<completion::CompletionAction*>* actions, List<completion::CompletionAction*>* extra_actions, List<completion::CompletionAction*>* else_actions, completion::_Predicate* predicate, BigStr* prefix, BigStr* suffix);
  void PrintSpec(mylib::BufWriter* f);
  void AllMatches(completion::Api* comp, List<Tuple2<BigStr*, runtime_asdl::comp_action_t>*>* YIELD);
  List<completion::CompletionAction*>* actions{};
  List<completion::CompletionAction*>* extra_actions{};
  List<completion::CompletionAction*>* else_actions{};
  completion::_Predicate* predicate{};
  BigStr* prefix{};
  BigStr* suffix{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(6, sizeof(UserSpec));
  }

  DISALLOW_COPY_AND_ASSIGN(UserSpec)
};

bool IsDollar(syntax_asdl::Token* t);
bool IsDummy(syntax_asdl::Token* t);
bool WordEndsWithCompDummy(syntax_asdl::CompoundWord* w);
class RootCompleter {
 public:
  RootCompleter(word_eval::AbstractWordEvaluator* word_ev, state::Mem* mem, completion::Lookup* comp_lookup, completion::OptionState* compopt_state, comp_ui::State* comp_ui_state, parse_lib::ParseContext* parse_ctx, util::_DebugFile* debug_f);
  void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  void _PostProcess(Dict<BigStr*, bool>* base_opts, Dict<BigStr*, bool>* dynamic_opts, completion::UserSpec* user_spec, completion::Api* comp, List<BigStr*>* YIELD);
  word_eval::AbstractWordEvaluator* word_ev{};
  state::Mem* mem{};
  completion::Lookup* comp_lookup{};
  completion::OptionState* compopt_state{};
  comp_ui::State* comp_ui_state{};
  parse_lib::ParseContext* parse_ctx{};
  util::_DebugFile* debug_f{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(7, sizeof(RootCompleter));
  }

  DISALLOW_COPY_AND_ASSIGN(RootCompleter)
};

class ReadlineCallback {
 public:
  ReadlineCallback(py_readline::Readline* readline, completion::RootCompleter* root_comp, util::_DebugFile* debug_f);
  BigStr* _GetNextCompletion(int state);
  BigStr* __call__(BigStr* unused_word, int state);
  py_readline::Readline* readline{};
  completion::RootCompleter* root_comp{};
  util::_DebugFile* debug_f{};
  List<BigStr*>* comp_matches{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(ReadlineCallback));
  }

  DISALLOW_COPY_AND_ASSIGN(ReadlineCallback)
};

BigStr* ExecuteReadlineCallback(completion::ReadlineCallback* cb, BigStr* word, int state);

}  // declare namespace completion

namespace dev {  // declare

class CrashDumper {
 public:
  CrashDumper(BigStr* crash_dump_dir, process::FdState* fd_state);
  void MaybeRecord(cmd_eval::CommandEvaluator* cmd_ev, error::_ErrorWithLocation* err);
  void MaybeDump(int status);
  BigStr* crash_dump_dir{};
  process::FdState* fd_state{};
  List<value_asdl::value_t*>* var_stack{};
  List<value_asdl::value_t*>* argv_stack{};
  List<value_asdl::value_t*>* debug_stack{};
  Dict<BigStr*, value_asdl::value_t*>* error{};
  bool do_collect{};
  bool collected{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(6, sizeof(CrashDumper));
  }

  DISALLOW_COPY_AND_ASSIGN(CrashDumper)
};

class ctx_Tracer {
 public:
  ctx_Tracer(dev::Tracer* tracer, BigStr* label, List<BigStr*>* argv);
  ~ctx_Tracer();
  BigStr* arg{};
  BigStr* label{};
  dev::Tracer* tracer{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Tracer)
};

void _PrintShValue(value_asdl::value_t* val, mylib::BufWriter* buf);
void PrintShellArgv(List<BigStr*>* argv, mylib::BufWriter* buf);
void _PrintYshArgv(List<BigStr*>* argv, mylib::BufWriter* buf);
class MultiTracer {
 public:
  MultiTracer(int shell_pid, BigStr* out_dir, BigStr* dumps, BigStr* streams, process::FdState* fd_state);
  void OnNewProcess(int child_pid);
  void EmitArgv0(BigStr* argv0);
  void WriteDumps();
  BigStr* out_dir{};
  BigStr* dumps{};
  BigStr* streams{};
  process::FdState* fd_state{};
  Dict<BigStr*, int>* hist_argv0{};
  int this_pid{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(5, sizeof(MultiTracer));
  }

  DISALLOW_COPY_AND_ASSIGN(MultiTracer)
};

class Tracer {
 public:
  Tracer(parse_lib::ParseContext* parse_ctx, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, state::Mem* mem, util::_DebugFile* f, dev::MultiTracer* multi_trace);
  void CheckCircularDeps();
  BigStr* _EvalPS4(BigStr* punct);
  void _Inc();
  void _Dec();
  mylib::BufWriter* _ShTraceBegin();
  mylib::BufWriter* _RichTraceBegin(BigStr* punct);
  void OnProcessStart(int pid, runtime_asdl::trace_t* why);
  void OnProcessEnd(int pid, int status);
  void OnNewProcess(int child_pid);
  void PushMessage(BigStr* label, List<BigStr*>* argv);
  void PopMessage(BigStr* label, BigStr* arg);
  void OtherMessage(BigStr* message);
  void OnExec(List<BigStr*>* argv);
  void OnBuiltin(int builtin_id, List<BigStr*>* argv);
  void OnSimpleCommand(List<BigStr*>* argv);
  void OnAssignBuiltin(cmd_value::Assign* cmd_val);
  void OnShAssignment(value_asdl::sh_lvalue_t* lval, syntax_asdl::assign_op_t op, value_asdl::value_t* val, int flags, runtime_asdl::scope_t which_scopes);
  void OnControlFlow(BigStr* keyword, int arg);
  void PrintSourceCode(syntax_asdl::Token* left_tok, syntax_asdl::Token* right_tok, alloc::Arena* arena);
  parse_lib::ParseContext* parse_ctx{};
  optview::Exec* exec_opts{};
  state::MutableOpts* mutable_opts{};
  state::Mem* mem{};
  util::_DebugFile* f{};
  dev::MultiTracer* multi_trace{};
  word_eval::NormalWordEvaluator* word_ev{};
  List<BigStr*>* indents{};
  Dict<BigStr*, syntax_asdl::CompoundWord*>* parse_cache{};
  value::Str* val_indent{};
  value::Str* val_punct{};
  value::Str* val_pid_str{};
  value_asdl::LeftName* lval_indent{};
  value_asdl::LeftName* lval_punct{};
  value_asdl::LeftName* lval_pid_str{};
  int ind{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(15, sizeof(Tracer));
  }

  DISALLOW_COPY_AND_ASSIGN(Tracer)
};


}  // declare namespace dev

namespace error {  // declare

BigStr* _ValType(value_asdl::value_t* val);
class _ErrorWithLocation {
 public:
  _ErrorWithLocation(BigStr* msg, syntax_asdl::loc_t* location);
  bool HasLocation();
  BigStr* UserErrorString();
  syntax_asdl::loc_t* location{};
  BigStr* msg{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(_ErrorWithLocation, location))
         | maskbit(offsetof(_ErrorWithLocation, msg));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_ErrorWithLocation));
  }

  DISALLOW_COPY_AND_ASSIGN(_ErrorWithLocation)
};

class Usage : public ::error::_ErrorWithLocation {
 public:
  Usage(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::_ErrorWithLocation::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Usage));
  }

  DISALLOW_COPY_AND_ASSIGN(Usage)
};

class Parse : public ::error::_ErrorWithLocation {
 public:
  Parse(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::_ErrorWithLocation::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Parse));
  }

  DISALLOW_COPY_AND_ASSIGN(Parse)
};

class WordFailure : public ::error::_ErrorWithLocation {
 public:
  WordFailure(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::_ErrorWithLocation::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(WordFailure));
  }

  DISALLOW_COPY_AND_ASSIGN(WordFailure)
};

class FailGlob : public ::error::WordFailure {
 public:
  FailGlob(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::WordFailure::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(FailGlob));
  }

  DISALLOW_COPY_AND_ASSIGN(FailGlob)
};

class VarSubFailure : public ::error::WordFailure {
 public:
  VarSubFailure(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::WordFailure::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(VarSubFailure));
  }

  DISALLOW_COPY_AND_ASSIGN(VarSubFailure)
};

class RedirectEval : public ::error::_ErrorWithLocation {
 public:
  RedirectEval(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::_ErrorWithLocation::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(RedirectEval));
  }

  DISALLOW_COPY_AND_ASSIGN(RedirectEval)
};

class FatalRuntime : public ::error::_ErrorWithLocation {
 public:
  FatalRuntime(int exit_status, BigStr* msg, syntax_asdl::loc_t* location);
  int ExitStatus();

  int exit_status{};
  
  static constexpr uint32_t field_mask() {
    return ::error::_ErrorWithLocation::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(FatalRuntime));
  }

  DISALLOW_COPY_AND_ASSIGN(FatalRuntime)
};

class Strict : public ::error::FatalRuntime {
 public:
  Strict(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::FatalRuntime::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Strict));
  }

  DISALLOW_COPY_AND_ASSIGN(Strict)
};

class ErrExit : public ::error::FatalRuntime {
 public:
  ErrExit(int exit_status, BigStr* msg, syntax_asdl::loc_t* location, bool show_code = false);

  bool show_code{};
  
  static constexpr uint32_t field_mask() {
    return ::error::FatalRuntime::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ErrExit));
  }

  DISALLOW_COPY_AND_ASSIGN(ErrExit)
};

class Expr : public ::error::FatalRuntime {
 public:
  Expr(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::FatalRuntime::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Expr));
  }

  DISALLOW_COPY_AND_ASSIGN(Expr)
};

class Structured : public ::error::FatalRuntime {
 public:
  Structured(int status, BigStr* msg, syntax_asdl::loc_t* location, Dict<BigStr*, value_asdl::value_t*>* properties = nullptr);
  value::Dict* ToDict();

  Dict<BigStr*, value_asdl::value_t*>* properties{};
  
  static constexpr uint32_t field_mask() {
    return ::error::FatalRuntime::field_mask()
         | maskbit(offsetof(Structured, properties));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Structured));
  }

  DISALLOW_COPY_AND_ASSIGN(Structured)
};

class AssertionErr : public ::error::Expr {
 public:
  AssertionErr(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::Expr::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(AssertionErr));
  }

  DISALLOW_COPY_AND_ASSIGN(AssertionErr)
};

class TypeErrVerbose : public ::error::Expr {
 public:
  TypeErrVerbose(BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::Expr::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(TypeErrVerbose));
  }

  DISALLOW_COPY_AND_ASSIGN(TypeErrVerbose)
};

class TypeErr : public ::error::TypeErrVerbose {
 public:
  TypeErr(value_asdl::value_t* actual_val, BigStr* msg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::error::TypeErrVerbose::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(TypeErr));
  }

  DISALLOW_COPY_AND_ASSIGN(TypeErr)
};

class Runtime {
 public:
  Runtime(BigStr* msg);
  BigStr* UserErrorString();
  BigStr* msg{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(Runtime));
  }

  DISALLOW_COPY_AND_ASSIGN(Runtime)
};

class Decode {
 public:
  Decode(BigStr* msg, BigStr* s, int start_pos, int end_pos, int line_num);
  BigStr* Message();
  BigStr* __str__();
  BigStr* msg{};
  BigStr* s{};
  int start_pos{};
  int end_pos{};
  int line_num{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(Decode));
  }

  DISALLOW_COPY_AND_ASSIGN(Decode)
};

class Encode {
 public:
  Encode(BigStr* msg);
  BigStr* Message();
  BigStr* msg{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(Encode));
  }

  DISALLOW_COPY_AND_ASSIGN(Encode)
};

[[noreturn]] void e_usage(BigStr* msg, syntax_asdl::loc_t* location);
[[noreturn]] void e_strict(BigStr* msg, syntax_asdl::loc_t* location);
[[noreturn]] void p_die(BigStr* msg, syntax_asdl::loc_t* location);
[[noreturn]] void e_die(BigStr* msg, syntax_asdl::loc_t* location = nullptr);
[[noreturn]] void e_die_status(int status, BigStr* msg, syntax_asdl::loc_t* location = nullptr);

}  // declare namespace error

namespace executor {  // declare

BigStr* LookupExecutable(BigStr* name, List<BigStr*>* path_dirs, bool exec_required = true);
class SearchPath {
 public:
  SearchPath(state::Mem* mem, optview::Exec* exec_opts);
  List<BigStr*>* _GetPath();
  BigStr* LookupOne(BigStr* name, bool exec_required = true);
  List<BigStr*>* LookupReflect(BigStr* name, bool do_all);
  BigStr* CachedLookup(BigStr* name);
  void MaybeRemoveEntry(BigStr* name);
  void ClearCache();
  List<BigStr*>* CachedCommands();
  state::Mem* mem{};
  Dict<BigStr*, BigStr*>* cache{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(SearchPath));
  }

  DISALLOW_COPY_AND_ASSIGN(SearchPath)
};

class _ProcessSubFrame {
 public:
  _ProcessSubFrame();
  bool WasModified();
  void Append(process::Process* p, int fd, syntax_asdl::loc_t* status_loc);
  void MaybeWaitOnProcessSubs(process::Waiter* waiter, runtime_asdl::StatusArray* status_array);
  List<process::Process*>* _to_wait{};
  List<int>* _to_close{};
  List<syntax_asdl::loc_t*>* _locs{};
  bool _modified{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(_ProcessSubFrame));
  }

  DISALLOW_COPY_AND_ASSIGN(_ProcessSubFrame)
};

extern int IS_LAST_CMD;
extern int NO_CALL_PROCS;
extern int USE_DEFAULT_PATH;
extern List<BigStr*>* DEFAULT_PATH;
class ShellExecutor : public ::vm::_Executor {
 public:
  ShellExecutor(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, state::Procs* procs, hay_ysh::HayState* hay_state, Dict<int, vm::_Builtin*>* builtins, executor::SearchPath* search_path, process::ExternalProgram* ext_prog, process::Waiter* waiter, dev::Tracer* tracer, process::JobControl* job_control, process::JobList* job_list, process::FdState* fd_state, trap_osh::TrapState* trap_state, ui::ErrorFormatter* errfmt);
  virtual void CheckCircularDeps();
  process::Process* _MakeProcess(syntax_asdl::command_t* node, bool inherit_errexit, bool inherit_errtrace);
  virtual int RunBuiltin(int builtin_id, cmd_value::Argv* cmd_val);
  int RunBuiltinProc(vm::_Builtin* builtin_proc, cmd_value::Argv* cmd_val);
  virtual int RunSimpleCommand(cmd_value::Argv* cmd_val, runtime_asdl::CommandStatus* cmd_st, int run_flags);
  virtual int RunBackgroundJob(syntax_asdl::command_t* node);
  virtual void RunPipeline(command::Pipeline* node, runtime_asdl::CommandStatus* status_out);
  virtual int RunSubshell(syntax_asdl::command_t* node);
  virtual Tuple2<int, BigStr*> CaptureStdout(syntax_asdl::command_t* node);
  virtual BigStr* RunCommandSub(syntax_asdl::CommandSub* cs_part);
  virtual BigStr* RunProcessSub(syntax_asdl::CommandSub* cs_part);
  virtual void PushRedirects(List<runtime_asdl::RedirValue*>* redirects, List<IOError_OSError*>* err_out);
  virtual void PopRedirects(int num_redirects, List<IOError_OSError*>* err_out);
  virtual void PushProcessSub();
  virtual void PopProcessSub(runtime_asdl::StatusArray* compound_st);

  Dict<int, vm::_Builtin*>* builtins{};
  List<executor::_ProcessSubFrame*>* clean_frame_pool{};
  ui::ErrorFormatter* errfmt{};
  optview::Exec* exec_opts{};
  process::ExternalProgram* ext_prog{};
  process::FdState* fd_state{};
  process::Pipeline* fg_pipeline{};
  hay_ysh::HayState* hay_state{};
  process::JobControl* job_control{};
  process::JobList* job_list{};
  state::Mem* mem{};
  dev::MultiTracer* multi_trace{};
  state::MutableOpts* mutable_opts{};
  List<executor::_ProcessSubFrame*>* process_sub_stack{};
  state::Procs* procs{};
  executor::SearchPath* search_path{};
  dev::Tracer* tracer{};
  trap_osh::TrapState* trap_state{};
  process::Waiter* waiter{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Executor::field_mask()
         | maskbit(offsetof(ShellExecutor, builtins))
         | maskbit(offsetof(ShellExecutor, clean_frame_pool))
         | maskbit(offsetof(ShellExecutor, errfmt))
         | maskbit(offsetof(ShellExecutor, exec_opts))
         | maskbit(offsetof(ShellExecutor, ext_prog))
         | maskbit(offsetof(ShellExecutor, fd_state))
         | maskbit(offsetof(ShellExecutor, fg_pipeline))
         | maskbit(offsetof(ShellExecutor, hay_state))
         | maskbit(offsetof(ShellExecutor, job_control))
         | maskbit(offsetof(ShellExecutor, job_list))
         | maskbit(offsetof(ShellExecutor, mem))
         | maskbit(offsetof(ShellExecutor, multi_trace))
         | maskbit(offsetof(ShellExecutor, mutable_opts))
         | maskbit(offsetof(ShellExecutor, process_sub_stack))
         | maskbit(offsetof(ShellExecutor, procs))
         | maskbit(offsetof(ShellExecutor, search_path))
         | maskbit(offsetof(ShellExecutor, tracer))
         | maskbit(offsetof(ShellExecutor, trap_state))
         | maskbit(offsetof(ShellExecutor, waiter));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ShellExecutor));
  }

  DISALLOW_COPY_AND_ASSIGN(ShellExecutor)
};


}  // declare namespace executor

namespace main_loop {  // declare

class ctx_Descriptors {
 public:
  ctx_Descriptors(List<int>* fds);
  ~ctx_Descriptors();
  List<int>* fds{};
  int saved0{};
  int saved1{};
  int saved2{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Descriptors)
};

void fanos_log(BigStr* msg);
void ShowDescriptorState(BigStr* label);
class Headless {
 public:
  Headless(cmd_eval::CommandEvaluator* cmd_ev, parse_lib::ParseContext* parse_ctx, ui::ErrorFormatter* errfmt);
  int Loop();
  BigStr* EVAL(BigStr* arg);
  int _Loop();
  cmd_eval::CommandEvaluator* cmd_ev{};
  parse_lib::ParseContext* parse_ctx{};
  ui::ErrorFormatter* errfmt{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(Headless));
  }

  DISALLOW_COPY_AND_ASSIGN(Headless)
};

int Interactive(arg_types::main* flag, cmd_eval::CommandEvaluator* cmd_ev, cmd_parse::CommandParser* c_parser, comp_ui::_IDisplay* display, prompt::UserPlugin* prompt_plugin, process::Waiter* waiter, ui::ErrorFormatter* errfmt);
int Batch(cmd_eval::CommandEvaluator* cmd_ev, cmd_parse::CommandParser* c_parser, ui::ErrorFormatter* errfmt, int cmd_flags = 0);
syntax_asdl::command_t* ParseWholeFile(cmd_parse::CommandParser* c_parser);

}  // declare namespace main_loop

namespace num {  // declare

value::Int* ToBig(int i);
mops::BigInt Exponent(mops::BigInt x, mops::BigInt y);

}  // declare namespace num

namespace process {  // declare

extern int NO_FD;
extern int _SHELL_MIN_FD;
extern int STYLE_DEFAULT;
extern int STYLE_LONG;
extern int STYLE_PID_ONLY;
extern List<BigStr*>* CURRENT_JOB_SPECS;
class ctx_FileCloser {
 public:
  ctx_FileCloser(mylib::LineReader* f);
  ~ctx_FileCloser();
  mylib::LineReader* f{};

  DISALLOW_COPY_AND_ASSIGN(ctx_FileCloser)
};

void InitInteractiveShell(iolib::SignalSafe* signal_safe);
int SaveFd(int fd);
class _RedirFrame {
 public:
  _RedirFrame(int saved_fd, int orig_fd, bool forget);
  int saved_fd{};
  int orig_fd{};
  bool forget{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(_RedirFrame));
  }

  DISALLOW_COPY_AND_ASSIGN(_RedirFrame)
};

class _FdFrame {
 public:
  _FdFrame();
  void Forget();
  List<process::_RedirFrame*>* saved{};
  List<process::Process*>* need_wait{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(_FdFrame));
  }

  DISALLOW_COPY_AND_ASSIGN(_FdFrame)
};

class FdState {
 public:
  FdState(ui::ErrorFormatter* errfmt, process::JobControl* job_control, process::JobList* job_list, state::Mem* mem, dev::Tracer* tracer, process::Waiter* waiter, optview::Exec* exec_opts);
  mylib::LineReader* Open(BigStr* path);
  mylib::Writer* OpenForWrite(BigStr* path);
  mylib::File* _Open(BigStr* path, BigStr* c_mode, int fd_mode);
  void _WriteFdToMem(BigStr* fd_name, int fd);
  int _ReadFdFromMem(BigStr* fd_name);
  bool _PushSave(int fd);
  int _PushDup(int fd1, syntax_asdl::redir_loc_t* blame_loc);
  bool _PushCloseFd(syntax_asdl::redir_loc_t* blame_loc);
  void _PushClose(int fd);
  void _PushWait(process::Process* proc);
  void _ApplyRedirect(runtime_asdl::RedirValue* r);
  void Push(List<runtime_asdl::RedirValue*>* redirects, List<IOError_OSError*>* err_out);
  bool PushStdinFromPipe(int r);
  void Pop(List<IOError_OSError*>* err_out);
  void MakePermanent();
  ui::ErrorFormatter* errfmt{};
  process::JobControl* job_control{};
  process::JobList* job_list{};
  process::_FdFrame* cur_frame{};
  List<process::_FdFrame*>* stack{};
  state::Mem* mem{};
  dev::Tracer* tracer{};
  process::Waiter* waiter{};
  optview::Exec* exec_opts{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(9, sizeof(FdState));
  }

  DISALLOW_COPY_AND_ASSIGN(FdState)
};

class ChildStateChange {
 public:
  ChildStateChange();
  virtual void Apply();
  virtual void ApplyFromParent(process::Process* proc);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ChildStateChange));
  }

  DISALLOW_COPY_AND_ASSIGN(ChildStateChange)
};

class StdinFromPipe : public ::process::ChildStateChange {
 public:
  StdinFromPipe(int pipe_read_fd, int w);
  virtual void Apply();

  int r{};
  int w{};
  
  static constexpr uint32_t field_mask() {
    return ::process::ChildStateChange::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(StdinFromPipe));
  }

  DISALLOW_COPY_AND_ASSIGN(StdinFromPipe)
};

class StdoutToPipe : public ::process::ChildStateChange {
 public:
  StdoutToPipe(int r, int pipe_write_fd);
  virtual void Apply();

  int r{};
  int w{};
  
  static constexpr uint32_t field_mask() {
    return ::process::ChildStateChange::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(StdoutToPipe));
  }

  DISALLOW_COPY_AND_ASSIGN(StdoutToPipe)
};

extern int INVALID_PGID;
extern int OWN_LEADER;
class SetPgid : public ::process::ChildStateChange {
 public:
  SetPgid(int pgid, dev::Tracer* tracer);
  virtual void Apply();
  virtual void ApplyFromParent(process::Process* proc);

  int pgid{};
  dev::Tracer* tracer{};
  
  static constexpr uint32_t field_mask() {
    return ::process::ChildStateChange::field_mask()
         | maskbit(offsetof(SetPgid, tracer));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetPgid));
  }

  DISALLOW_COPY_AND_ASSIGN(SetPgid)
};

class ExternalProgram {
 public:
  ExternalProgram(BigStr* hijack_shebang, process::FdState* fd_state, ui::ErrorFormatter* errfmt, util::_DebugFile* debug_f);
  void Exec(BigStr* argv0_path, cmd_value::Argv* cmd_val, Dict<BigStr*, BigStr*>* environ);
  void _Exec(BigStr* argv0_path, List<BigStr*>* argv, syntax_asdl::loc_t* argv0_loc, Dict<BigStr*, BigStr*>* environ, bool should_retry);
  BigStr* hijack_shebang{};
  process::FdState* fd_state{};
  ui::ErrorFormatter* errfmt{};
  util::_DebugFile* debug_f{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(ExternalProgram));
  }

  DISALLOW_COPY_AND_ASSIGN(ExternalProgram)
};

class Thunk {
 public:
  Thunk();
  virtual void Run();
  virtual BigStr* UserString();
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Thunk));
  }

  DISALLOW_COPY_AND_ASSIGN(Thunk)
};

class ExternalThunk : public ::process::Thunk {
 public:
  ExternalThunk(process::ExternalProgram* ext_prog, BigStr* argv0_path, cmd_value::Argv* cmd_val, Dict<BigStr*, BigStr*>* environ);
  virtual BigStr* UserString();
  virtual void Run();

  BigStr* argv0_path{};
  cmd_value::Argv* cmd_val{};
  Dict<BigStr*, BigStr*>* environ{};
  process::ExternalProgram* ext_prog{};
  
  static constexpr uint32_t field_mask() {
    return ::process::Thunk::field_mask()
         | maskbit(offsetof(ExternalThunk, argv0_path))
         | maskbit(offsetof(ExternalThunk, cmd_val))
         | maskbit(offsetof(ExternalThunk, environ))
         | maskbit(offsetof(ExternalThunk, ext_prog));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ExternalThunk));
  }

  DISALLOW_COPY_AND_ASSIGN(ExternalThunk)
};

class SubProgramThunk : public ::process::Thunk {
 public:
  SubProgramThunk(cmd_eval::CommandEvaluator* cmd_ev, syntax_asdl::command_t* node, trap_osh::TrapState* trap_state, dev::MultiTracer* multi_trace, bool inherit_errexit, bool inherit_errtrace);
  virtual BigStr* UserString();
  virtual void Run();

  cmd_eval::CommandEvaluator* cmd_ev{};
  bool inherit_errexit{};
  bool inherit_errtrace{};
  dev::MultiTracer* multi_trace{};
  syntax_asdl::command_t* node{};
  trap_osh::TrapState* trap_state{};
  
  static constexpr uint32_t field_mask() {
    return ::process::Thunk::field_mask()
         | maskbit(offsetof(SubProgramThunk, cmd_ev))
         | maskbit(offsetof(SubProgramThunk, multi_trace))
         | maskbit(offsetof(SubProgramThunk, node))
         | maskbit(offsetof(SubProgramThunk, trap_state));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SubProgramThunk));
  }

  DISALLOW_COPY_AND_ASSIGN(SubProgramThunk)
};

class _HereDocWriterThunk : public ::process::Thunk {
 public:
  _HereDocWriterThunk(int w, BigStr* body_str);
  virtual BigStr* UserString();
  virtual void Run();

  BigStr* body_str{};
  int w{};
  
  static constexpr uint32_t field_mask() {
    return ::process::Thunk::field_mask()
         | maskbit(offsetof(_HereDocWriterThunk, body_str));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_HereDocWriterThunk));
  }

  DISALLOW_COPY_AND_ASSIGN(_HereDocWriterThunk)
};

class Job {
 public:
  Job();
  virtual void DisplayJob(int job_id, mylib::Writer* f, int style);
  runtime_asdl::job_state_t State();
  virtual int ProcessGroupId();
  virtual runtime_asdl::wait_status_t* JobWait(process::Waiter* waiter);
  void SetBackground();
  void SetForeground();
  bool in_background{};
  int job_id{};
  runtime_asdl::job_state_t state{};
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Job));
  }

  DISALLOW_COPY_AND_ASSIGN(Job)
};

class Process : public ::process::Job {
 public:
  Process(process::Thunk* thunk, process::JobControl* job_control, process::JobList* job_list, dev::Tracer* tracer);
  void Init_ParentPipeline(process::Pipeline* pi);
  virtual int ProcessGroupId();
  virtual void DisplayJob(int job_id, mylib::Writer* f, int style);
  void AddStateChange(process::ChildStateChange* s);
  void AddPipeToClose(int r, int w);
  void MaybeClosePipe();
  int StartProcess(runtime_asdl::trace_t* why);
  int Wait(process::Waiter* waiter);
  virtual runtime_asdl::wait_status_t* JobWait(process::Waiter* waiter);
  void WhenStopped(int stop_sig);
  void WhenDone(int pid, int status);
  int RunProcess(process::Waiter* waiter, runtime_asdl::trace_t* why);

  int close_r{};
  int close_w{};
  process::JobControl* job_control{};
  process::JobList* job_list{};
  process::Pipeline* parent_pipeline{};
  int pid{};
  List<process::ChildStateChange*>* state_changes{};
  int status{};
  process::Thunk* thunk{};
  dev::Tracer* tracer{};
  
  static constexpr uint32_t field_mask() {
    return ::process::Job::field_mask()
         | maskbit(offsetof(Process, job_control))
         | maskbit(offsetof(Process, job_list))
         | maskbit(offsetof(Process, parent_pipeline))
         | maskbit(offsetof(Process, state_changes))
         | maskbit(offsetof(Process, thunk))
         | maskbit(offsetof(Process, tracer));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Process));
  }

  DISALLOW_COPY_AND_ASSIGN(Process)
};

class ctx_Pipe {
 public:
  ctx_Pipe(process::FdState* fd_state, int fd, List<IOError_OSError*>* err_out);
  ~ctx_Pipe();
  process::FdState* fd_state{};
  List<IOError_OSError*>* err_out{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Pipe)
};

class Pipeline : public ::process::Job {
 public:
  Pipeline(bool sigpipe_status_ok, process::JobControl* job_control, process::JobList* job_list, dev::Tracer* tracer);
  virtual int ProcessGroupId();
  virtual void DisplayJob(int job_id, mylib::Writer* f, int style);
  void DebugPrint();
  void Add(process::Process* p);
  void AddLast(Tuple2<cmd_eval::CommandEvaluator*, syntax_asdl::command_t*>* thunk);
  void StartPipeline(process::Waiter* waiter);
  int LastPid();
  List<int>* Wait(process::Waiter* waiter);
  virtual runtime_asdl::wait_status_t* JobWait(process::Waiter* waiter);
  List<int>* RunLastPart(process::Waiter* waiter, process::FdState* fd_state);
  bool AllDone();
  void WhenDone(int pid, int status);

  process::JobControl* job_control{};
  process::JobList* job_list{};
  Tuple2<int, int>* last_pipe{};
  Tuple2<cmd_eval::CommandEvaluator*, syntax_asdl::command_t*>* last_thunk{};
  int pgid{};
  List<int>* pids{};
  List<int>* pipe_status{};
  List<process::Process*>* procs{};
  bool sigpipe_status_ok{};
  int status{};
  dev::Tracer* tracer{};
  
  static constexpr uint32_t field_mask() {
    return ::process::Job::field_mask()
         | maskbit(offsetof(Pipeline, job_control))
         | maskbit(offsetof(Pipeline, job_list))
         | maskbit(offsetof(Pipeline, last_pipe))
         | maskbit(offsetof(Pipeline, last_thunk))
         | maskbit(offsetof(Pipeline, pids))
         | maskbit(offsetof(Pipeline, pipe_status))
         | maskbit(offsetof(Pipeline, procs))
         | maskbit(offsetof(Pipeline, tracer));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Pipeline));
  }

  DISALLOW_COPY_AND_ASSIGN(Pipeline)
};

BigStr* _JobStateStr(runtime_asdl::job_state_t i);
int _GetTtyFd();
class ctx_TerminalControl {
 public:
  ctx_TerminalControl(process::JobControl* job_control, ui::ErrorFormatter* errfmt);
  ~ctx_TerminalControl();
  process::JobControl* job_control{};
  ui::ErrorFormatter* errfmt{};

  DISALLOW_COPY_AND_ASSIGN(ctx_TerminalControl)
};

class JobControl {
 public:
  JobControl();
  void InitJobControl();
  bool Enabled();
  void MaybeGiveTerminal(int pgid);
  void MaybeTakeTerminal();
  void MaybeReturnTerminal();
  int shell_pid{};
  int shell_pgid{};
  int shell_tty_fd{};
  int original_tty_pgid{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(JobControl));
  }

  DISALLOW_COPY_AND_ASSIGN(JobControl)
};

class JobList {
 public:
  JobList();
  int AddJob(process::Job* job);
  void RemoveJob(int job_id);
  void AddChildProcess(int pid, process::Process* proc);
  void RemoveChildProcess(int pid);
  process::Process* ProcessFromPid(int pid);
  Tuple2<process::Job*, process::Job*> GetCurrentAndPreviousJobs();
  process::Job* GetJobWithSpec(BigStr* job_spec);
  void DisplayJobs(int style);
  void DebugPrint();
  void ListRecent();
  int NumRunning();
  Dict<int, process::Job*>* jobs{};
  Dict<int, process::Process*>* child_procs{};
  List<process::Pipeline*>* debug_pipelines{};
  int job_id{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(JobList));
  }

  DISALLOW_COPY_AND_ASSIGN(JobList)
};

extern int W1_OK;
extern int W1_ECHILD;
extern int W1_AGAIN;
class Waiter {
 public:
  Waiter(process::JobList* job_list, optview::Exec* exec_opts, iolib::SignalSafe* signal_safe, dev::Tracer* tracer);
  int WaitForOne(int waitpid_options = 0);
  void PollNotifications();
  process::JobList* job_list{};
  optview::Exec* exec_opts{};
  iolib::SignalSafe* signal_safe{};
  dev::Tracer* tracer{};
  int last_status{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(Waiter));
  }

  DISALLOW_COPY_AND_ASSIGN(Waiter)
};


}  // declare namespace process

namespace sh_init {  // declare

class EnvConfig {
 public:
  EnvConfig(state::Mem* mem, Dict<BigStr*, value_asdl::value_t*>* defaults);
  value_asdl::value_t* GetVal(BigStr* var_name);
  BigStr* Get(BigStr* var_name);
  void SetDefault(BigStr* var_name, BigStr* s);
  state::Mem* mem{};
  optview::Exec* exec_opts{};
  Dict<BigStr*, value_asdl::value_t*>* defaults{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(EnvConfig));
  }

  DISALLOW_COPY_AND_ASSIGN(EnvConfig)
};

class ShellFiles {
 public:
  ShellFiles(BigStr* lang, BigStr* home_dir, state::Mem* mem, arg_types::main* flag);
  BigStr* HistVar();
  BigStr* DefaultHistoryFile();
  BigStr* HistoryFile();
  BigStr* lang{};
  BigStr* home_dir{};
  state::Mem* mem{};
  arg_types::main* flag{};
  bool init_done{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(ShellFiles));
  }

  DISALLOW_COPY_AND_ASSIGN(ShellFiles)
};

BigStr* GetWorkingDir();
extern BigStr* _READLINE_DELIMS;
void InitDefaultVars(state::Mem* mem);
void CopyVarsFromEnv(optview::Exec* exec_opts, Dict<BigStr*, BigStr*>* environ, state::Mem* mem);
void InitVarsAfterEnv(state::Mem* mem);
void InitInteractive(state::Mem* mem, sh_init::ShellFiles* sh_files, BigStr* lang);
void InitBuiltins(state::Mem* mem, BigStr* version_str, Dict<BigStr*, value_asdl::value_t*>* defaults);

}  // declare namespace sh_init

namespace state {  // declare

extern int SetReadOnly;
extern int ClearReadOnly;
extern int SetExport;
extern int ClearExport;
extern int SetNameref;
extern int ClearNameref;
class ctx_Source {
 public:
  ctx_Source(state::Mem* mem, BigStr* source_name, List<BigStr*>* argv);
  ~ctx_Source();
  state::Mem* mem{};
  List<BigStr*>* argv{};
  bool to_restore{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Source)
};

class ctx_DebugTrap {
 public:
  ctx_DebugTrap(state::Mem* mem);
  ~ctx_DebugTrap();
  state::Mem* mem{};

  DISALLOW_COPY_AND_ASSIGN(ctx_DebugTrap)
};

class ctx_ErrTrap {
 public:
  ctx_ErrTrap(state::Mem* mem);
  ~ctx_ErrTrap();
  state::Mem* mem{};

  DISALLOW_COPY_AND_ASSIGN(ctx_ErrTrap)
};

class ctx_Option {
 public:
  ctx_Option(state::MutableOpts* mutable_opts, List<int>* opt_nums, bool b);
  ~ctx_Option();
  state::MutableOpts* mutable_opts{};
  List<int>* opt_nums{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Option)
};

class ctx_AssignBuiltin {
 public:
  ctx_AssignBuiltin(state::MutableOpts* mutable_opts);
  ~ctx_AssignBuiltin();
  state::MutableOpts* mutable_opts{};
  bool strict{};

  DISALLOW_COPY_AND_ASSIGN(ctx_AssignBuiltin)
};

class ctx_YshExpr {
 public:
  ctx_YshExpr(state::MutableOpts* mutable_opts);
  ~ctx_YshExpr();
  state::MutableOpts* mutable_opts{};

  DISALLOW_COPY_AND_ASSIGN(ctx_YshExpr)
};

class ctx_ErrExit {
 public:
  ctx_ErrExit(state::MutableOpts* mutable_opts, bool b, syntax_asdl::Token* disabled_tok);
  ~ctx_ErrExit();
  state::MutableOpts* mutable_opts{};
  bool strict{};

  DISALLOW_COPY_AND_ASSIGN(ctx_ErrExit)
};

class OptHook {
 public:
  OptHook();
  virtual bool OnChange(List<bool>* opt0_array, BigStr* opt_name, bool b);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(OptHook));
  }

  DISALLOW_COPY_AND_ASSIGN(OptHook)
};

List<bool>* InitOpts();
Tuple3<optview::Parse*, optview::Exec*, state::MutableOpts*> MakeOpts(state::Mem* mem, Dict<BigStr*, BigStr*>* environ, state::OptHook* opt_hook);
void _SetGroup(List<bool>* opt0_array, List<int>* opt_nums, bool b);
optview::Parse* MakeYshParseOpts();
int _AnyOptionNum(BigStr* opt_name, bool ignore_shopt_not_impl);
int _SetOptionNum(BigStr* opt_name);
void _MaybeWarnDotglob();
class MutableOpts {
 public:
  MutableOpts(state::Mem* mem, Dict<BigStr*, BigStr*>* environ, List<bool>* opt0_array, List<List<bool>*>* opt_stacks, state::OptHook* opt_hook);
  void Init();
  void _InitOptionsFromEnv(BigStr* shellopts);
  void Push(int opt_num, bool b);
  bool Pop(int opt_num);
  void PushDynamicScope(bool b);
  void PopDynamicScope();
  bool Get(int opt_num);
  void _Set(int opt_num, bool b);
  void set_interactive();
  void set_redefine_const();
  void set_redefine_source();
  void set_emacs();
  void _SetArrayByNum(int opt_num, bool b);
  void SetDeferredErrExit(bool b);
  void DisableErrExit();
  syntax_asdl::Token* ErrExitDisabledToken();
  bool ErrExitIsDisabled();
  void _SetOldOption(BigStr* opt_name, bool b);
  void SetOldOption(BigStr* opt_name, bool b);
  void SetAnyOption(BigStr* opt_name, bool b, bool ignore_shopt_not_impl = false);
  state::Mem* mem{};
  Dict<BigStr*, BigStr*>* environ{};
  List<bool>* opt0_array{};
  List<List<bool>*>* opt_stacks{};
  List<syntax_asdl::Token*>* errexit_disabled_tok{};
  state::OptHook* opt_hook{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(6, sizeof(MutableOpts));
  }

  DISALLOW_COPY_AND_ASSIGN(MutableOpts)
};

class _ArgFrame {
 public:
  _ArgFrame(List<BigStr*>* argv);
  Dict<BigStr*, value_asdl::value_t*>* Dump();
  value_asdl::value_t* GetArgNum(int arg_num);
  List<BigStr*>* GetArgv();
  int GetNumArgs();
  void SetArgv(List<BigStr*>* argv);
  List<BigStr*>* argv{};
  int num_shifted{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(_ArgFrame));
  }

  DISALLOW_COPY_AND_ASSIGN(_ArgFrame)
};

Dict<BigStr*, value_asdl::value_t*>* _DumpVarFrame(Dict<BigStr*, runtime_asdl::Cell*>* frame);
BigStr* _LineNumber(syntax_asdl::Token* tok);
void _AddCallToken(Dict<BigStr*, value_asdl::value_t*>* d, syntax_asdl::Token* token);
class ctx_FuncCall {
 public:
  ctx_FuncCall(state::Mem* mem, value::Func* func);
  ~ctx_FuncCall();
  Dict<BigStr*, runtime_asdl::Cell*>* saved_globals{};
  state::Mem* mem{};

  DISALLOW_COPY_AND_ASSIGN(ctx_FuncCall)
};

class ctx_ProcCall {
 public:
  ctx_ProcCall(state::Mem* mem, state::MutableOpts* mutable_opts, value::Proc* proc, List<BigStr*>* argv);
  ~ctx_ProcCall();
  Dict<BigStr*, runtime_asdl::Cell*>* saved_globals{};
  state::Mem* mem{};
  state::MutableOpts* mutable_opts{};
  bool sh_compat{};

  DISALLOW_COPY_AND_ASSIGN(ctx_ProcCall)
};

class ctx_Temp {
 public:
  ctx_Temp(state::Mem* mem);
  ~ctx_Temp();
  state::Mem* mem{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Temp)
};

class ctx_EnvObj {
 public:
  ctx_EnvObj(state::Mem* mem, Dict<BigStr*, value_asdl::value_t*>* bindings);
  ~ctx_EnvObj();
  state::Mem* mem{};

  DISALLOW_COPY_AND_ASSIGN(ctx_EnvObj)
};

class ctx_Registers {
 public:
  ctx_Registers(state::Mem* mem);
  ~ctx_Registers();
  state::Mem* mem{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Registers)
};

class ctx_ThisDir {
 public:
  ctx_ThisDir(state::Mem* mem, BigStr* filename);
  ~ctx_ThisDir();
  state::Mem* mem{};
  bool do_pop{};

  DISALLOW_COPY_AND_ASSIGN(ctx_ThisDir)
};

runtime_asdl::Cell* _MakeArgvCell(List<BigStr*>* argv);
class ctx_LoopFrame {
 public:
  ctx_LoopFrame(state::Mem* mem, BigStr* name1);
  ~ctx_LoopFrame();
  state::Mem* mem{};
  BigStr* name1{};
  Dict<BigStr*, runtime_asdl::Cell*>* new_frame{};
  bool do_new_frame{};

  DISALLOW_COPY_AND_ASSIGN(ctx_LoopFrame)
};

class ctx_EnclosedFrame {
 public:
  ctx_EnclosedFrame(state::Mem* mem, Dict<BigStr*, runtime_asdl::Cell*>* to_enclose, Dict<BigStr*, runtime_asdl::Cell*>* module_frame, Dict<BigStr*, value_asdl::value_t*>* out_dict);
  ~ctx_EnclosedFrame();
  state::Mem* mem{};
  Dict<BigStr*, runtime_asdl::Cell*>* to_enclose{};
  Dict<BigStr*, runtime_asdl::Cell*>* module_frame{};
  Dict<BigStr*, value_asdl::value_t*>* out_dict{};
  Dict<BigStr*, runtime_asdl::Cell*>* saved_globals{};
  Dict<BigStr*, runtime_asdl::Cell*>* new_frame{};

  DISALLOW_COPY_AND_ASSIGN(ctx_EnclosedFrame)
};

class ctx_ModuleEval {
 public:
  ctx_ModuleEval(state::Mem* mem, Dict<BigStr*, value_asdl::value_t*>* out_dict, List<BigStr*>* out_errors);
  ~ctx_ModuleEval();
  state::Mem* mem{};
  Dict<BigStr*, value_asdl::value_t*>* out_dict{};
  List<BigStr*>* out_errors{};
  Dict<BigStr*, runtime_asdl::Cell*>* new_frame{};
  Dict<BigStr*, runtime_asdl::Cell*>* saved_frame{};
  bool to_restore{};

  DISALLOW_COPY_AND_ASSIGN(ctx_ModuleEval)
};

class ctx_Eval {
 public:
  ctx_Eval(state::Mem* mem, BigStr* dollar0, List<BigStr*>* pos_args, Dict<BigStr*, value_asdl::value_t*>* vars);
  ~ctx_Eval();
  void _Push(Dict<BigStr*, value_asdl::value_t*>* vars);
  void _Pop();
  state::Mem* mem{};
  BigStr* dollar0{};
  List<BigStr*>* pos_args{};
  Dict<BigStr*, value_asdl::value_t*>* vars{};
  BigStr* restore_dollar0{};
  List<Tuple2<value_asdl::LeftName*, value_asdl::value_t*>*>* restore{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Eval)
};

Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> _FrameLookup(Dict<BigStr*, runtime_asdl::Cell*>* frame, BigStr* name);
class Mem {
 public:
  Mem(BigStr* dollar0, List<BigStr*>* argv, alloc::Arena* arena, List<syntax_asdl::debug_frame_t*>* debug_stack, Dict<BigStr*, value_asdl::value_t*>* env_dict, Dict<BigStr*, value_asdl::value_t*>* defaults = nullptr);
  void AddBuiltin(BigStr* name, value_asdl::value_t* val);
  void SetPwd(BigStr* pwd);
  bool ParsingChangesAllowed();
  Tuple3<List<value_asdl::value_t*>*, List<value_asdl::value_t*>*, List<value_asdl::value_t*>*> Dump();
  void SetLastArgument(BigStr* s);
  void SetTokenForLine(syntax_asdl::Token* tok);
  void SetLocationForExpr(syntax_asdl::loc_t* blame_loc);
  syntax_asdl::loc_t* GetFallbackLocation();
  int LastStatus();
  int TryStatus();
  value::Dict* TryError();
  List<int>* PipeStatus();
  void SetLastStatus(int x);
  void SetTryStatus(int x);
  void SetTryError(value::Dict* x);
  void SetPipeStatus(List<int>* x);
  void SetSimplePipeStatus(int status);
  void SetProcessSubStatus(List<int>* x);
  void PushCall(BigStr* func_name, syntax_asdl::Token* def_tok);
  void PopCall();
  bool ShouldRunDebugTrap();
  bool IsGlobalScope();
  bool InsideFunction();
  Dict<BigStr*, runtime_asdl::Cell*>* GlobalFrame();
  Dict<BigStr*, runtime_asdl::Cell*>* CurrentFrame();
  void PushSource(BigStr* source_name, List<BigStr*>* argv);
  void PopSource(List<BigStr*>* argv);
  void PushTemp();
  void PopTemp();
  void _BindEnvObj();
  void MaybeInitEnvDict(Dict<BigStr*, BigStr*>* environ);
  void PushEnvObj(Dict<BigStr*, value_asdl::value_t*>* bindings);
  void PopEnvObj();
  int Shift(int n);
  value::Str* GetArg0();
  value_asdl::value_t* GetArgNum(int arg_num);
  List<BigStr*>* GetArgv();
  void SetArgv(List<BigStr*>* argv);
  value_asdl::value_t* GetSpecialVar(int op_id);
  Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> _ResolveNameOnly(BigStr* name, runtime_asdl::scope_t which_scopes);
  Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> _ResolveNameOrRef(BigStr* name, runtime_asdl::scope_t which_scopes, List<BigStr*>* ref_trail = nullptr);
  bool IsBashAssoc(BigStr* name);
  void SetPlace(value::Place* place, value_asdl::value_t* val, syntax_asdl::loc_t* blame_loc);
  void SetLocalName(value_asdl::LeftName* lval, value_asdl::value_t* val);
  void SetNamed(value_asdl::LeftName* lval, value_asdl::value_t* val, runtime_asdl::scope_t which_scopes, int flags = 0);
  void SetValue(value_asdl::sh_lvalue_t* lval, value_asdl::value_t* val, runtime_asdl::scope_t which_scopes, int flags = 0);
  void _BindNewArrayWithEntry(Dict<BigStr*, runtime_asdl::Cell*>* var_frame, sh_lvalue::Indexed* lval, value::Str* val, int flags);
  void InternalSetGlobal(BigStr* name, value_asdl::value_t* new_val);
  value_asdl::value_t* GetValue(BigStr* name, runtime_asdl::scope_t which_scopes = scope_e::Shopt);
  runtime_asdl::Cell* GetCell(BigStr* name, runtime_asdl::scope_t which_scopes = scope_e::Shopt);
  bool Unset(value_asdl::sh_lvalue_t* lval, runtime_asdl::scope_t which_scopes);
  runtime_asdl::scope_t ScopesForReading();
  runtime_asdl::scope_t ScopesForWriting();
  bool ClearFlag(BigStr* name, int flag);
  void _FillWithExported(Dict<BigStr*, BigStr*>* new_env);
  void _FillEnvObj(Dict<BigStr*, BigStr*>* new_env, value_asdl::Obj* env_object);
  Dict<BigStr*, BigStr*>* GetEnv();
  List<BigStr*>* VarNames();
  List<BigStr*>* VarNamesStartingWith(BigStr* prefix);
  Dict<BigStr*, BigStr*>* GetAllVars();
  Dict<BigStr*, runtime_asdl::Cell*>* GetAllCells(runtime_asdl::scope_t which_scopes);
  void SetRegexMatch(value_asdl::regex_match_t* match);
  value_asdl::regex_match_t* GetRegexMatch();
  void PushContextStack(Dict<BigStr*, value_asdl::value_t*>* context);
  Dict<BigStr*, value_asdl::value_t*>* GetContext();
  Dict<BigStr*, value_asdl::value_t*>* PopContextStack();
  optview::Exec* exec_opts{};
  sh_expr_eval::UnsafeArith* unsafe_arith{};
  BigStr* dollar0{};
  List<state::_ArgFrame*>* argv_stack{};
  List<Dict<BigStr*, runtime_asdl::Cell*>*>* var_stack{};
  List<syntax_asdl::debug_frame_t*>* debug_stack{};
  Dict<BigStr*, value_asdl::value_t*>* env_dict{};
  value_asdl::Obj* env_object{};
  Dict<BigStr*, value_asdl::value_t*>* defaults{};
  BigStr* pwd{};
  syntax_asdl::Token* token_for_line{};
  syntax_asdl::loc_t* loc_for_expr{};
  BigStr* last_arg{};
  value::Str* line_num{};
  List<int>* last_status{};
  List<int>* try_status{};
  List<value::Dict*>* try_error{};
  List<List<int>*>* pipe_status{};
  List<List<int>*>* process_sub_status{};
  List<BigStr*>* this_dir{};
  List<value_asdl::regex_match_t*>* regex_match{};
  List<Dict<BigStr*, value_asdl::value_t*>*>* ctx_stack{};
  Dict<BigStr*, value_asdl::value_t*>* builtins{};
  sh_init::EnvConfig* env_config{};
  double seconds_start{};
  int root_pid{};
  int last_bg_pid{};
  bool running_debug_trap{};
  bool running_err_trap{};
  bool is_main{};
  bool did_ysh_env{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(24, sizeof(Mem));
  }

  DISALLOW_COPY_AND_ASSIGN(Mem)
};

Tuple2<value_asdl::value_t*, value_asdl::Obj*> ValueIsInvokableObj(value_asdl::value_t* val);
void _AddNames(Dict<BigStr*, bool>* unique, Dict<BigStr*, runtime_asdl::Cell*>* frame);
class Procs {
 public:
  Procs(state::Mem* mem);
  void DefineShellFunc(BigStr* name, value::Proc* proc);
  bool IsShellFunc(BigStr* name);
  value::Proc* GetShellFunc(BigStr* name);
  void EraseShellFunc(BigStr* to_del);
  List<BigStr*>* ShellFuncNames();
  void DefineProc(BigStr* name, value::Proc* proc);
  bool IsProc(BigStr* name);
  bool IsInvokableObj(BigStr* name);
  List<BigStr*>* InvokableNames();
  Tuple2<value_asdl::value_t*, value_asdl::Obj*> GetInvokable(BigStr* name);
  state::Mem* mem{};
  Dict<BigStr*, value::Proc*>* sh_funcs{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(Procs));
  }

  DISALLOW_COPY_AND_ASSIGN(Procs)
};

void OshLanguageSetValue(state::Mem* mem, value_asdl::sh_lvalue_t* lval, value_asdl::value_t* val, int flags = 0);
void BuiltinSetValue(state::Mem* mem, value_asdl::sh_lvalue_t* lval, value_asdl::value_t* val);
void BuiltinSetString(state::Mem* mem, BigStr* name, BigStr* s);
void BuiltinSetArray(state::Mem* mem, BigStr* name, List<BigStr*>* a);
void SetGlobalString(state::Mem* mem, BigStr* name, BigStr* s);
void SetGlobalArray(state::Mem* mem, BigStr* name, List<BigStr*>* a);
void SetGlobalValue(state::Mem* mem, BigStr* name, value_asdl::value_t* val);
void SetLocalValue(state::Mem* mem, BigStr* name, value_asdl::value_t* val);
void ExportGlobalString(state::Mem* mem, BigStr* name, BigStr* s);
void SetStringInEnv(state::Mem* mem, BigStr* var_name, BigStr* s);
value_asdl::value_t* DynamicGetVar(state::Mem* mem, BigStr* name, runtime_asdl::scope_t which_scopes);
BigStr* GetString(state::Mem* mem, BigStr* name);
BigStr* MaybeString(state::Mem* mem, BigStr* name);
int GetInteger(state::Mem* mem, BigStr* name);

}  // declare namespace state

namespace util {  // declare

List<BigStr*>* RegexGroupStrings(BigStr* s, List<int>* indices);
List<BigStr*>* RegexSearch(BigStr* pat, BigStr* s);
class UserExit {
 public:
  UserExit(int status);
  int status{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(UserExit));
  }

  DISALLOW_COPY_AND_ASSIGN(UserExit)
};

class HistoryError {
 public:
  HistoryError(BigStr* msg);
  BigStr* UserErrorString();
  BigStr* msg{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(HistoryError));
  }

  DISALLOW_COPY_AND_ASSIGN(HistoryError)
};

class _DebugFile {
 public:
  _DebugFile();
  virtual void write(BigStr* s);
  virtual void writeln(BigStr* s);
  virtual bool isatty();
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_DebugFile));
  }

  DISALLOW_COPY_AND_ASSIGN(_DebugFile)
};

class NullDebugFile : public ::util::_DebugFile {
 public:
  NullDebugFile();
  
  static constexpr uint32_t field_mask() {
    return ::util::_DebugFile::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(NullDebugFile));
  }

  DISALLOW_COPY_AND_ASSIGN(NullDebugFile)
};

class DebugFile : public ::util::_DebugFile {
 public:
  DebugFile(mylib::Writer* f);
  virtual void write(BigStr* s);
  virtual void writeln(BigStr* s);
  virtual bool isatty();

  mylib::Writer* f{};
  
  static constexpr uint32_t field_mask() {
    return ::util::_DebugFile::field_mask()
         | maskbit(offsetof(DebugFile, f));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DebugFile));
  }

  DISALLOW_COPY_AND_ASSIGN(DebugFile)
};

void PrintTopicHeader(BigStr* topic_id, mylib::Writer* f);
bool PrintEmbeddedHelp(pyutil::_ResourceLoader* loader, BigStr* topic_id, mylib::Writer* f);
void _PrintVersionLine(pyutil::_ResourceLoader* loader, mylib::Writer* f);
void HelpFlag(pyutil::_ResourceLoader* loader, BigStr* topic_id, mylib::Writer* f);
void VersionFlag(pyutil::_ResourceLoader* loader, mylib::Writer* f);

}  // declare namespace util

namespace j8 {  // declare

BigStr* ValType(value_asdl::value_t* val);
int ValueId(value_asdl::value_t* val);
BigStr* ValueIdString(value_asdl::value_t* val);
BigStr* Utf8Encode(int code);
extern int SHOW_CYCLES;
extern int SHOW_NON_DATA;
extern int LOSSY_JSON;
extern int INF_NAN_ARE_NULL;
void _Print(value_asdl::value_t* val, mylib::BufWriter* buf, int indent, int options = 0);
void PrintMessage(value_asdl::value_t* val, mylib::BufWriter* buf, int indent);
void PrintJsonMessage(value_asdl::value_t* val, mylib::BufWriter* buf, int indent);
void PrintLine(value_asdl::value_t* val, mylib::Writer* f);
void EncodeString(BigStr* s, mylib::BufWriter* buf, bool unquoted_ok = false);
BigStr* MaybeEncodeString(BigStr* s);
BigStr* MaybeEncodeJsonString(BigStr* s);
class InstancePrinter {
 public:
  InstancePrinter(mylib::BufWriter* buf, int indent, int options);
  void _ItemIndent(int level);
  void _BracketIndent(int level);
  void _MaybeNewline();
  void _MaybeSpace();
  void _PrintList(value::List* val, int level);
  void _PrintMapping(Dict<BigStr*, value_asdl::value_t*>* d, BigStr* left, BigStr* right, int level);
  void _PrintDict(value::Dict* val, int level);
  void _PrintObj(value_asdl::Obj* val, int level);
  void _PrintBashPrefix(BigStr* type_str, int level);
  void _PrintBashSuffix(int level);
  void _PrintSparseArray(value::SparseArray* val, int level);
  void _PrintBashArray(value::BashArray* val, int level);
  void _PrintBashAssoc(value::BashAssoc* val, int level);
  void Print(value_asdl::value_t* val, int level = 0);
  mylib::BufWriter* buf{};
  Dict<int, bool>* visiting{};
  int indent{};
  int options{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(InstancePrinter));
  }

  DISALLOW_COPY_AND_ASSIGN(InstancePrinter)
};

class LexerDecoder {
 public:
  LexerDecoder(BigStr* s, bool is_j8, BigStr* lang_str);
  error::Decode* _Error(BigStr* msg, int end_pos);
  Tuple3<int, int, BigStr*> Next();
  Tuple3<int, int, BigStr*> NextForLines();
  Tuple3<int, int, BigStr*> _DecodeString(int left_id, int str_pos);
  BigStr* s{};
  BigStr* lang_str{};
  mylib::BufWriter* decoded{};
  bool is_j8{};
  int pos{};
  int cur_line_num{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(LexerDecoder));
  }

  DISALLOW_COPY_AND_ASSIGN(LexerDecoder)
};

class _Parser {
 public:
  _Parser(BigStr* s, bool is_j8);
  void _Next();
  void _Eat(int tok_id);
  void _NextForLines();
  error::Decode* _ParseError(BigStr* msg);
  BigStr* decoded{};
  int end_pos{};
  bool is_j8{};
  BigStr* lang_str{};
  j8::LexerDecoder* lexer{};
  BigStr* s{};
  int start_pos{};
  int tok_id{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(_Parser, decoded))
         | maskbit(offsetof(_Parser, lang_str))
         | maskbit(offsetof(_Parser, lexer))
         | maskbit(offsetof(_Parser, s));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_Parser));
  }

  DISALLOW_COPY_AND_ASSIGN(_Parser)
};

class Parser : public ::j8::_Parser {
 public:
  Parser(BigStr* s, bool is_j8);
  Tuple2<BigStr*, value_asdl::value_t*> _ParsePair();
  value_asdl::value_t* _ParseDict();
  value_asdl::value_t* _ParseList();
  value_asdl::value_t* _ParseValue();
  value_asdl::value_t* ParseValue();
  
  static constexpr uint32_t field_mask() {
    return ::j8::_Parser::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Parser));
  }

  DISALLOW_COPY_AND_ASSIGN(Parser)
};

class Nil8Parser : public ::j8::_Parser {
 public:
  Nil8Parser(BigStr* s, bool is_j8);
  nil8_asdl::nvalue_t* _ParseRecord();
  nil8_asdl::nvalue_t* _ParseList8();
  nil8_asdl::nvalue_t* _ParseNil8();
  nil8_asdl::nvalue_t* ParseNil8();
  
  static constexpr uint32_t field_mask() {
    return ::j8::_Parser::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Nil8Parser));
  }

  DISALLOW_COPY_AND_ASSIGN(Nil8Parser)
};

class J8LinesParser : public ::j8::_Parser {
 public:
  J8LinesParser(BigStr* s);
  void _Show(BigStr* s);
  void _ParseLine(List<BigStr*>* out);
  List<BigStr*>* Parse();
  
  static constexpr uint32_t field_mask() {
    return ::j8::_Parser::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(J8LinesParser));
  }

  DISALLOW_COPY_AND_ASSIGN(J8LinesParser)
};

List<BigStr*>* SplitJ8Lines(BigStr* s);

}  // declare namespace j8

namespace j8_lite {  // declare

BigStr* EncodeString(BigStr* s, bool unquoted_ok = false);
BigStr* YshEncodeString(BigStr* s);
BigStr* MaybeShellEncode(BigStr* s);
BigStr* ShellEncode(BigStr* s);
BigStr* YshEncode(BigStr* s, bool unquoted_ok = false);

}  // declare namespace j8_lite

namespace ansi {  // declare

extern BigStr* RESET;
extern BigStr* BOLD;
extern BigStr* UNDERLINE;
extern BigStr* REVERSE;
extern BigStr* RED;
extern BigStr* GREEN;
extern BigStr* YELLOW;
extern BigStr* BLUE;
extern BigStr* MAGENTA;
extern BigStr* CYAN;
extern BigStr* WHITE;

}  // declare namespace ansi

namespace pp_hnode {  // declare

class BaseEncoder {
 public:
  BaseEncoder();
  void SetIndent(int indent);
  void SetUseStyles(bool use_styles);
  void SetMaxTabularWidth(int max_tabular_width);
  pretty_asdl::MeasuredDoc* _Styled(BigStr* style, pretty_asdl::MeasuredDoc* mdoc);
  pretty_asdl::MeasuredDoc* _StyledAscii(BigStr* style, BigStr* s);
  pretty_asdl::MeasuredDoc* _Surrounded(BigStr* left, pretty_asdl::MeasuredDoc* mdoc, BigStr* right);
  pretty_asdl::MeasuredDoc* _SurroundedAndPrefixed(BigStr* left, pretty_asdl::MeasuredDoc* prefix, BigStr* sep, pretty_asdl::MeasuredDoc* mdoc, BigStr* right);
  pretty_asdl::MeasuredDoc* _Join(List<pretty_asdl::MeasuredDoc*>* items, BigStr* sep, BigStr* space);
  pretty_asdl::MeasuredDoc* _Tabular(List<pretty_asdl::MeasuredDoc*>* items, BigStr* sep);
  int indent{};
  int max_tabular_width{};
  bool use_styles{};
  Dict<int, bool>* visiting{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(BaseEncoder, visiting));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(BaseEncoder));
  }

  DISALLOW_COPY_AND_ASSIGN(BaseEncoder)
};

class HNodeEncoder : public ::pp_hnode::BaseEncoder {
 public:
  HNodeEncoder();
  pretty_asdl::MeasuredDoc* HNode(hnode_asdl::hnode_t* h);
  pretty_asdl::MeasuredDoc* _Field(hnode_asdl::Field* field);
  pretty_asdl::MeasuredDoc* _HNode(hnode_asdl::hnode_t* h);

  BigStr* field_color{};
  BigStr* type_color{};
  
  static constexpr uint32_t field_mask() {
    return ::pp_hnode::BaseEncoder::field_mask()
         | maskbit(offsetof(HNodeEncoder, field_color))
         | maskbit(offsetof(HNodeEncoder, type_color));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(HNodeEncoder));
  }

  DISALLOW_COPY_AND_ASSIGN(HNodeEncoder)
};


}  // declare namespace pp_hnode

namespace pp_value {  // declare

BigStr* ValType(value_asdl::value_t* val);
BigStr* FloatString(double fl);
int TryUnicodeWidth(BigStr* s);
pretty_asdl::MeasuredDoc* UText(BigStr* string);
class ValueEncoder : public ::pp_hnode::BaseEncoder {
 public:
  ValueEncoder();
  List<pretty_asdl::MeasuredDoc*>* TypePrefix(BigStr* type_str);
  pretty_asdl::MeasuredDoc* Value(value_asdl::value_t* val);
  pretty_asdl::MeasuredDoc* _DictKey(BigStr* s);
  pretty_asdl::MeasuredDoc* _StringLiteral(BigStr* s);
  pretty_asdl::MeasuredDoc* _BashStringLiteral(BigStr* s);
  pretty_asdl::MeasuredDoc* _YshList(value::List* vlist);
  List<pretty_asdl::MeasuredDoc*>* _DictMdocs(Dict<BigStr*, value_asdl::value_t*>* d);
  pretty_asdl::MeasuredDoc* _YshDict(value::Dict* vdict);
  pretty_asdl::MeasuredDoc* _BashArray(value::BashArray* varray);
  pretty_asdl::MeasuredDoc* _BashAssoc(value::BashAssoc* vassoc);
  pretty_asdl::MeasuredDoc* _SparseArray(value::SparseArray* val);
  pretty_asdl::MeasuredDoc* _Obj(value_asdl::Obj* obj);
  pretty_asdl::MeasuredDoc* _Value(value_asdl::value_t* val);

  BigStr* bool_style{};
  BigStr* cycle_style{};
  BigStr* float_style{};
  BigStr* int_style{};
  BigStr* null_style{};
  BigStr* string_style{};
  BigStr* type_style{};
  bool ysh_style{};
  
  static constexpr uint32_t field_mask() {
    return ::pp_hnode::BaseEncoder::field_mask()
         | maskbit(offsetof(ValueEncoder, bool_style))
         | maskbit(offsetof(ValueEncoder, cycle_style))
         | maskbit(offsetof(ValueEncoder, float_style))
         | maskbit(offsetof(ValueEncoder, int_style))
         | maskbit(offsetof(ValueEncoder, null_style))
         | maskbit(offsetof(ValueEncoder, string_style))
         | maskbit(offsetof(ValueEncoder, type_style));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ValueEncoder));
  }

  DISALLOW_COPY_AND_ASSIGN(ValueEncoder)
};


}  // declare namespace pp_value

namespace pretty {  // declare

pretty_asdl::Measure* _EmptyMeasure();
pretty_asdl::Measure* _FlattenMeasure(pretty_asdl::Measure* measure);
pretty_asdl::Measure* _ConcatMeasure(pretty_asdl::Measure* m1, pretty_asdl::Measure* m2);
int _SuffixLen(pretty_asdl::Measure* measure);
pretty_asdl::MeasuredDoc* AsciiText(BigStr* string);
pretty_asdl::MeasuredDoc* _Break(BigStr* string);
pretty_asdl::MeasuredDoc* _Indent(int indent, pretty_asdl::MeasuredDoc* mdoc);
pretty_asdl::Measure* _Splice(List<pretty_asdl::MeasuredDoc*>* out, List<pretty_asdl::MeasuredDoc*>* mdocs);
pretty_asdl::MeasuredDoc* _Concat(List<pretty_asdl::MeasuredDoc*>* mdocs);
pretty_asdl::MeasuredDoc* _Group(pretty_asdl::MeasuredDoc* mdoc);
pretty_asdl::MeasuredDoc* _IfFlat(pretty_asdl::MeasuredDoc* flat_mdoc, pretty_asdl::MeasuredDoc* nonflat_mdoc);
pretty_asdl::MeasuredDoc* _Flat(pretty_asdl::MeasuredDoc* mdoc);
class PrettyPrinter {
 public:
  PrettyPrinter(int max_width);
  bool _Fits(int prefix_len, pretty_asdl::MeasuredDoc* group, pretty_asdl::Measure* suffix_measure);
  void PrintDoc(pretty_asdl::MeasuredDoc* document, mylib::BufWriter* buf);
  int max_width{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(PrettyPrinter));
  }

  DISALLOW_COPY_AND_ASSIGN(PrettyPrinter)
};


}  // declare namespace pretty

namespace ui {  // declare

BigStr* ValType(value_asdl::value_t* val);
BigStr* CommandType(syntax_asdl::command_t* cmd);
BigStr* PrettyId(int id_);
BigStr* PrettyToken(syntax_asdl::Token* tok);
BigStr* PrettyDir(BigStr* dir_name, BigStr* home_dir);
void _PrintCodeExcerpt(BigStr* line, int col, int length, mylib::Writer* f);
void _PrintTokenTooLong(loc::TokenTooLong* loc_tok, mylib::Writer* f);
BigStr* GetLineSourceString(syntax_asdl::SourceLine* line, bool quote_filename = false);
void _PrintWithLocation(BigStr* prefix, BigStr* msg, syntax_asdl::loc_t* blame_loc, bool show_code);
Tuple2<BigStr*, BigStr*> CodeExcerptAndPrefix(syntax_asdl::Token* blame_tok);
class ctx_Location {
 public:
  ctx_Location(ui::ErrorFormatter* errfmt, syntax_asdl::loc_t* location);
  ~ctx_Location();
  ui::ErrorFormatter* errfmt{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Location)
};

class ErrorFormatter {
 public:
  ErrorFormatter();
  void OneLineErrExit();
  syntax_asdl::loc_t* _FallbackLocation(syntax_asdl::loc_t* blame_loc);
  void PrefixPrint(BigStr* msg, BigStr* prefix, syntax_asdl::loc_t* blame_loc);
  void Print_(BigStr* msg, syntax_asdl::loc_t* blame_loc = nullptr);
  void PrintMessage(BigStr* msg, syntax_asdl::loc_t* blame_loc = nullptr);
  void StderrLine(BigStr* msg);
  void PrettyPrintError(error::_ErrorWithLocation* err, BigStr* prefix = S_Aoo);
  void PrintErrExit(error::ErrExit* err, int pid);
  List<syntax_asdl::loc_t*>* loc_stack{};
  bool one_line_errexit{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(ErrorFormatter));
  }

  DISALLOW_COPY_AND_ASSIGN(ErrorFormatter)
};

void PrintAst(syntax_asdl::command_t* node, arg_types::main* flag);
bool TypeNotPrinted(value_asdl::value_t* val);
int _GetMaxWidth();
void PrettyPrintValue(BigStr* prefix, value_asdl::value_t* val, mylib::Writer* f, int max_width = -1);

}  // declare namespace ui

namespace args {  // declare

extern int String;
extern int Int;
extern int Float;
extern int Bool;
class _Attributes {
 public:
  _Attributes(Dict<BigStr*, value_asdl::value_t*>* defaults);
  void SetTrue(BigStr* name);
  void Set(BigStr* name, value_asdl::value_t* val);
  Dict<BigStr*, value_asdl::value_t*>* attrs{};
  List<Tuple2<BigStr*, bool>*>* opt_changes{};
  List<Tuple2<BigStr*, bool>*>* shopt_changes{};
  List<BigStr*>* actions{};
  bool show_options{};
  bool saw_double_dash{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(_Attributes));
  }

  DISALLOW_COPY_AND_ASSIGN(_Attributes)
};

class Reader {
 public:
  Reader(List<BigStr*>* argv, List<syntax_asdl::CompoundWord*>* locs = nullptr);
  void Next();
  BigStr* Peek();
  Tuple2<BigStr*, syntax_asdl::loc_t*> Peek2();
  BigStr* ReadRequired(BigStr* error_msg);
  Tuple2<BigStr*, syntax_asdl::loc_t*> ReadRequired2(BigStr* error_msg);
  List<BigStr*>* Rest();
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> Rest2();
  bool AtEnd();
  void Done();
  syntax_asdl::loc_t* _FirstLocation();
  syntax_asdl::loc_t* Location();
  List<BigStr*>* argv{};
  List<syntax_asdl::CompoundWord*>* locs{};
  int n{};
  int i{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(Reader));
  }

  DISALLOW_COPY_AND_ASSIGN(Reader)
};

class _Action {
 public:
  _Action();
  virtual bool OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_Action));
  }

  DISALLOW_COPY_AND_ASSIGN(_Action)
};

class _ArgAction : public ::args::_Action {
 public:
  _ArgAction(BigStr* name, bool quit_parsing_flags, List<BigStr*>* valid = nullptr);
  virtual value_asdl::value_t* _Value(BigStr* arg, syntax_asdl::loc_t* location);
  virtual bool OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out);

  BigStr* name{};
  bool quit_parsing_flags{};
  List<BigStr*>* valid{};
  
  static constexpr uint32_t field_mask() {
    return ::args::_Action::field_mask()
         | maskbit(offsetof(_ArgAction, name))
         | maskbit(offsetof(_ArgAction, valid));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_ArgAction));
  }

  DISALLOW_COPY_AND_ASSIGN(_ArgAction)
};

class SetToInt : public ::args::_ArgAction {
 public:
  SetToInt(BigStr* name);
  virtual value_asdl::value_t* _Value(BigStr* arg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::args::_ArgAction::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetToInt));
  }

  DISALLOW_COPY_AND_ASSIGN(SetToInt)
};

class SetToFloat : public ::args::_ArgAction {
 public:
  SetToFloat(BigStr* name);
  virtual value_asdl::value_t* _Value(BigStr* arg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::args::_ArgAction::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetToFloat));
  }

  DISALLOW_COPY_AND_ASSIGN(SetToFloat)
};

class SetToString : public ::args::_ArgAction {
 public:
  SetToString(BigStr* name, bool quit_parsing_flags, List<BigStr*>* valid = nullptr);
  virtual value_asdl::value_t* _Value(BigStr* arg, syntax_asdl::loc_t* location);
  
  static constexpr uint32_t field_mask() {
    return ::args::_ArgAction::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetToString));
  }

  DISALLOW_COPY_AND_ASSIGN(SetToString)
};

class SetAttachedBool : public ::args::_Action {
 public:
  SetAttachedBool(BigStr* name);
  virtual bool OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out);

  BigStr* name{};
  
  static constexpr uint32_t field_mask() {
    return ::args::_Action::field_mask()
         | maskbit(offsetof(SetAttachedBool, name));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetAttachedBool));
  }

  DISALLOW_COPY_AND_ASSIGN(SetAttachedBool)
};

class SetToTrue : public ::args::_Action {
 public:
  SetToTrue(BigStr* name);
  virtual bool OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out);

  BigStr* name{};
  
  static constexpr uint32_t field_mask() {
    return ::args::_Action::field_mask()
         | maskbit(offsetof(SetToTrue, name));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetToTrue));
  }

  DISALLOW_COPY_AND_ASSIGN(SetToTrue)
};

class SetOption : public ::args::_Action {
 public:
  SetOption(BigStr* name);
  virtual bool OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out);

  BigStr* name{};
  
  static constexpr uint32_t field_mask() {
    return ::args::_Action::field_mask()
         | maskbit(offsetof(SetOption, name));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetOption));
  }

  DISALLOW_COPY_AND_ASSIGN(SetOption)
};

class SetNamedOption : public ::args::_Action {
 public:
  SetNamedOption(bool shopt = false);
  void ArgName(BigStr* name);
  virtual bool OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out);

  List<BigStr*>* names{};
  bool shopt{};
  
  static constexpr uint32_t field_mask() {
    return ::args::_Action::field_mask()
         | maskbit(offsetof(SetNamedOption, names));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetNamedOption));
  }

  DISALLOW_COPY_AND_ASSIGN(SetNamedOption)
};

class SetAction : public ::args::_Action {
 public:
  SetAction(BigStr* name);
  virtual bool OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out);

  BigStr* name{};
  
  static constexpr uint32_t field_mask() {
    return ::args::_Action::field_mask()
         | maskbit(offsetof(SetAction, name));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetAction));
  }

  DISALLOW_COPY_AND_ASSIGN(SetAction)
};

class SetNamedAction : public ::args::_Action {
 public:
  SetNamedAction();
  void ArgName(BigStr* name);
  virtual bool OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out);

  List<BigStr*>* names{};
  
  static constexpr uint32_t field_mask() {
    return ::args::_Action::field_mask()
         | maskbit(offsetof(SetNamedAction, names));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(SetNamedAction));
  }

  DISALLOW_COPY_AND_ASSIGN(SetNamedAction)
};

args::_Attributes* Parse(flag_spec::_FlagSpec* spec, args::Reader* arg_r);
args::_Attributes* ParseLikeEcho(flag_spec::_FlagSpec* spec, args::Reader* arg_r);
args::_Attributes* ParseMore(flag_spec::_FlagSpecAndMore* spec, args::Reader* arg_r);

}  // declare namespace args

namespace flag_util {  // declare

void _DoesNotAccept(runtime_asdl::ProcArgs* proc_args);
Tuple2<args::_Attributes*, args::Reader*> ParseCmdVal(BigStr* spec_name, cmd_value::Argv* cmd_val, bool accept_typed_args = false);
Tuple2<args::_Attributes*, args::Reader*> ParseLikeEcho(BigStr* spec_name, cmd_value::Argv* cmd_val);
args::_Attributes* Parse(BigStr* spec_name, args::Reader* arg_r);
args::_Attributes* ParseMore(BigStr* spec_name, args::Reader* arg_r);

}  // declare namespace flag_util

namespace lexer {  // declare

bool IsPlusEquals(syntax_asdl::Token* tok);
bool TokenContains(syntax_asdl::Token* tok, BigStr* substr);
bool TokenEquals(syntax_asdl::Token* tok, BigStr* s);
bool TokenStartsWith(syntax_asdl::Token* tok, BigStr* s);
bool TokenEndsWith(syntax_asdl::Token* tok, BigStr* s);
BigStr* TokenVal(syntax_asdl::Token* tok);
BigStr* TokenSliceLeft(syntax_asdl::Token* tok, int left_index);
BigStr* TokenSliceRight(syntax_asdl::Token* tok, int right_index);
BigStr* TokenSlice(syntax_asdl::Token* tok, int left, int right);
BigStr* LazyStr(syntax_asdl::Token* tok);
syntax_asdl::Token* DummyToken(int id_, BigStr* val);
class LineLexer {
 public:
  LineLexer(alloc::Arena* arena);
  void Reset(syntax_asdl::SourceLine* src_line, int line_pos);
  bool MaybeUnreadOne();
  syntax_asdl::Token* GetEofToken(int id_);
  int LookAheadOne(types_asdl::lex_mode_t lex_mode);
  void AssertAtEndOfLine();
  int LookPastSpace(types_asdl::lex_mode_t lex_mode);
  bool LookAheadFuncParens(int unread);
  BigStr* ByteLookAhead();
  int ByteLookBack();
  syntax_asdl::Token* Read(types_asdl::lex_mode_t lex_mode);
  alloc::Arena* arena{};
  syntax_asdl::Token* eol_tok{};
  syntax_asdl::SourceLine* src_line{};
  bool replace_last_token{};
  int line_pos{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(LineLexer));
  }

  DISALLOW_COPY_AND_ASSIGN(LineLexer)
};

class Lexer {
 public:
  Lexer(lexer::LineLexer* line_lexer, reader::_Reader* line_reader);
  void ResetInputObjects();
  bool MaybeUnreadOne();
  int LookAheadOne(types_asdl::lex_mode_t lex_mode);
  int LookPastSpace(types_asdl::lex_mode_t lex_mode);
  bool LookAheadFuncParens(int unread);
  BigStr* ByteLookAhead();
  int ByteLookBack();
  void EmitCompDummy();
  void PushHint(int old_id, int new_id);
  bool MoveToNextLine();
  syntax_asdl::Token* _Read(types_asdl::lex_mode_t lex_mode);
  syntax_asdl::Token* Read(types_asdl::lex_mode_t lex_mode);
  lexer::LineLexer* line_lexer{};
  reader::_Reader* line_reader{};
  List<Tuple2<int, int>*>* translation_stack{};
  int line_id{};
  bool emit_comp_dummy{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(Lexer));
  }

  DISALLOW_COPY_AND_ASSIGN(Lexer)
};


}  // declare namespace lexer

namespace location {  // declare

value_asdl::LeftName* LName(BigStr* name);
syntax_asdl::Token* TokenFor(syntax_asdl::loc_t* loc_);
syntax_asdl::Token* TokenForCommand(syntax_asdl::command_t* node);
syntax_asdl::Token* TokenForArith(syntax_asdl::arith_expr_t* node);
syntax_asdl::Token* LeftTokenForWordPart(syntax_asdl::word_part_t* part);
syntax_asdl::Token* _RightTokenForWordPart(syntax_asdl::word_part_t* part);
syntax_asdl::Token* LeftTokenForCompoundWord(syntax_asdl::CompoundWord* w);
syntax_asdl::Token* LeftTokenForWord(syntax_asdl::word_t* w);
syntax_asdl::Token* RightTokenForWord(syntax_asdl::word_t* w);
syntax_asdl::Token* TokenForLhsExpr(syntax_asdl::sh_lhs_t* node);
syntax_asdl::loc_t* TokenForExpr(syntax_asdl::expr_t* node);

}  // declare namespace location

namespace parse_lib {  // declare

class _BaseTrail {
 public:
  _BaseTrail();
  virtual void Clear();
  virtual void SetLatestWords(List<syntax_asdl::CompoundWord*>* words, List<syntax_asdl::Redir*>* redirects);
  virtual void AppendToken(syntax_asdl::Token* token);
  void BeginAliasExpansion();
  void EndAliasExpansion();
  bool _expanding_alias{};
  List<syntax_asdl::CompoundWord*>* alias_words{};
  List<syntax_asdl::Redir*>* redirects{};
  List<syntax_asdl::Token*>* tokens{};
  List<syntax_asdl::CompoundWord*>* words{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(_BaseTrail, alias_words))
         | maskbit(offsetof(_BaseTrail, redirects))
         | maskbit(offsetof(_BaseTrail, tokens))
         | maskbit(offsetof(_BaseTrail, words));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_BaseTrail));
  }

  DISALLOW_COPY_AND_ASSIGN(_BaseTrail)
};

class ctx_Alias {
 public:
  ctx_Alias(parse_lib::_BaseTrail* trail);
  ~ctx_Alias();
  parse_lib::_BaseTrail* trail{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Alias)
};

class Trail : public ::parse_lib::_BaseTrail {
 public:
  Trail();
  virtual void Clear();
  virtual void SetLatestWords(List<syntax_asdl::CompoundWord*>* words, List<syntax_asdl::Redir*>* redirects);
  virtual void AppendToken(syntax_asdl::Token* token);
  
  static constexpr uint32_t field_mask() {
    return ::parse_lib::_BaseTrail::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Trail));
  }

  DISALLOW_COPY_AND_ASSIGN(Trail)
};

class ParseContext {
 public:
  ParseContext(alloc::Arena* arena, optview::Parse* parse_opts, Dict<BigStr*, BigStr*>* aliases, grammar::Grammar* ysh_grammar, bool do_lossless = false);
  void Init_Trail(parse_lib::_BaseTrail* trail);
  lexer::Lexer* MakeLexer(reader::_Reader* line_reader);
  cmd_parse::CommandParser* MakeOshParser(reader::_Reader* line_reader, bool emit_comp_dummy = false);
  cmd_parse::CommandParser* MakeConfigParser(reader::_Reader* line_reader);
  word_parse::WordParser* MakeWordParserForHereDoc(reader::_Reader* line_reader);
  word_parse::WordParser* MakeWordParser(lexer::Lexer* lx, reader::_Reader* line_reader);
  tdop::TdopParser* MakeArithParser(BigStr* code_str);
  cmd_parse::CommandParser* MakeParserForCommandSub(reader::_Reader* line_reader, lexer::Lexer* lexer, int eof_id);
  word_parse::WordParser* MakeWordParserForPlugin(BigStr* code_str);
  expr_parse::ExprParser* _YshParser();
  Tuple2<command::VarDecl*, syntax_asdl::Token*> ParseVarDecl(syntax_asdl::Token* kw_token, lexer::Lexer* lexer);
  Tuple2<command::Mutation*, syntax_asdl::Token*> ParseMutation(syntax_asdl::Token* kw_token, lexer::Lexer* lexer);
  void ParseProcCallArgs(lexer::Lexer* lx, syntax_asdl::ArgList* out, int start_symbol);
  Tuple2<syntax_asdl::expr_t*, syntax_asdl::Token*> ParseYshExpr(lexer::Lexer* lx, int start_symbol);
  Tuple3<syntax_asdl::pat_t*, syntax_asdl::Token*, syntax_asdl::Token*> ParseYshCasePattern(lexer::Lexer* lexer);
  syntax_asdl::Token* ParseProc(lexer::Lexer* lexer, syntax_asdl::Proc* out);
  syntax_asdl::Token* ParseFunc(lexer::Lexer* lexer, syntax_asdl::Func* out);
  alloc::Arena* arena{};
  optview::Parse* parse_opts{};
  Dict<BigStr*, BigStr*>* aliases{};
  grammar::Grammar* ysh_grammar{};
  expr_to_ast::Transformer* tr{};
  parse_lib::_BaseTrail* trail{};
  bool do_lossless{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(6, sizeof(ParseContext));
  }

  DISALLOW_COPY_AND_ASSIGN(ParseContext)
};


}  // declare namespace parse_lib

namespace reader {  // declare

extern BigStr* _PS2;
class _Reader {
 public:
  _Reader(alloc::Arena* arena);
  void SetLineOffset(int n);
  virtual BigStr* _GetLine();
  virtual Tuple2<syntax_asdl::SourceLine*, int> GetLine();
  virtual void Reset();
  virtual bool LastLineHint();
  alloc::Arena* arena{};
  int line_num{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(_Reader, arena));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_Reader));
  }

  DISALLOW_COPY_AND_ASSIGN(_Reader)
};

class DisallowedLineReader : public ::reader::_Reader {
 public:
  DisallowedLineReader(alloc::Arena* arena, syntax_asdl::Token* blame_token);
  virtual BigStr* _GetLine();

  syntax_asdl::Token* blame_token{};
  
  static constexpr uint32_t field_mask() {
    return ::reader::_Reader::field_mask()
         | maskbit(offsetof(DisallowedLineReader, blame_token));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DisallowedLineReader));
  }

  DISALLOW_COPY_AND_ASSIGN(DisallowedLineReader)
};

class FileLineReader : public ::reader::_Reader {
 public:
  FileLineReader(mylib::LineReader* f, alloc::Arena* arena);
  virtual BigStr* _GetLine();
  virtual bool LastLineHint();

  mylib::LineReader* f{};
  bool last_line_hint{};
  
  static constexpr uint32_t field_mask() {
    return ::reader::_Reader::field_mask()
         | maskbit(offsetof(FileLineReader, f));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(FileLineReader));
  }

  DISALLOW_COPY_AND_ASSIGN(FileLineReader)
};

reader::FileLineReader* StringLineReader(BigStr* s, alloc::Arena* arena);
class VirtualLineReader : public ::reader::_Reader {
 public:
  VirtualLineReader(alloc::Arena* arena, List<Tuple2<syntax_asdl::SourceLine*, int>*>* lines, bool do_lossless);
  virtual Tuple2<syntax_asdl::SourceLine*, int> GetLine();

  bool do_lossless{};
  List<Tuple2<syntax_asdl::SourceLine*, int>*>* lines{};
  int num_lines{};
  int pos{};
  
  static constexpr uint32_t field_mask() {
    return ::reader::_Reader::field_mask()
         | maskbit(offsetof(VirtualLineReader, lines));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(VirtualLineReader));
  }

  DISALLOW_COPY_AND_ASSIGN(VirtualLineReader)
};

BigStr* _PlainPromptInput(BigStr* prompt);
class InteractiveLineReader : public ::reader::_Reader {
 public:
  InteractiveLineReader(alloc::Arena* arena, prompt::Evaluator* prompt_ev, history::Evaluator* hist_ev, py_readline::Readline* line_input, comp_ui::PromptState* prompt_state);
  virtual void Reset();
  BigStr* _ReadlinePromptInput();
  virtual BigStr* _GetLine();

  history::Evaluator* hist_ev{};
  py_readline::Readline* line_input{};
  BigStr* prev_line{};
  prompt::Evaluator* prompt_ev{};
  comp_ui::PromptState* prompt_state{};
  BigStr* prompt_str{};
  bool render_ps1{};
  
  static constexpr uint32_t field_mask() {
    return ::reader::_Reader::field_mask()
         | maskbit(offsetof(InteractiveLineReader, hist_ev))
         | maskbit(offsetof(InteractiveLineReader, line_input))
         | maskbit(offsetof(InteractiveLineReader, prev_line))
         | maskbit(offsetof(InteractiveLineReader, prompt_ev))
         | maskbit(offsetof(InteractiveLineReader, prompt_state))
         | maskbit(offsetof(InteractiveLineReader, prompt_str));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(InteractiveLineReader));
  }

  DISALLOW_COPY_AND_ASSIGN(InteractiveLineReader)
};


}  // declare namespace reader

namespace syntax_abbrev {  // declare

void _AbbreviateToken(syntax_asdl::Token* tok, List<hnode_asdl::hnode_t*>* out);
hnode_asdl::hnode_t* _Token(syntax_asdl::Token* obj);
hnode_asdl::hnode_t* _CompoundWord(syntax_asdl::CompoundWord* obj);
hnode_asdl::hnode_t* _DoubleQuoted(syntax_asdl::DoubleQuoted* obj);
hnode_asdl::hnode_t* _SingleQuoted(syntax_asdl::SingleQuoted* obj);
hnode_asdl::hnode_t* _SimpleVarSub(syntax_asdl::SimpleVarSub* obj);
hnode_asdl::hnode_t* _BracedVarSub(syntax_asdl::BracedVarSub* obj);
hnode_asdl::hnode_t* _command__Simple(command::Simple* obj);
hnode_asdl::hnode_t* _expr__Var(expr::Var* obj);
hnode_asdl::hnode_t* _expr__Const(expr::Const* obj);

}  // declare namespace syntax_abbrev

namespace typed_args {  // declare

void DoesNotAccept(runtime_asdl::ProcArgs* proc_args);
syntax_asdl::command_t* OptionalBlockAsFrag(cmd_value::Argv* cmd_val);
syntax_asdl::command_t* RequiredBlockAsFrag(cmd_value::Argv* cmd_val);
value_asdl::LiteralBlock* OptionalLiteralBlock(cmd_value::Argv* cmd_val);
syntax_asdl::command_t* GetCommandFrag(value::Command* bound);
typed_args::Reader* ReaderForProc(cmd_value::Argv* cmd_val);
class Reader {
 public:
  Reader(List<value_asdl::value_t*>* pos_args, Dict<BigStr*, value_asdl::value_t*>* named_args, value_asdl::value_t* block_arg, syntax_asdl::ArgList* arg_list, bool is_bound = false);
  void SetFallbackLocation(syntax_asdl::loc_t* blame_loc);
  syntax_asdl::Token* LeftParenToken();
  syntax_asdl::loc_t* LeastSpecificLocation();
  syntax_asdl::loc_t* BlamePos();
  value_asdl::value_t* PosValue();
  value_asdl::value_t* OptionalValue();
  BigStr* _ToStr(value_asdl::value_t* val);
  bool _ToBool(value_asdl::value_t* val);
  mops::BigInt _ToInt(value_asdl::value_t* val);
  double _ToFloat(value_asdl::value_t* val);
  List<BigStr*>* _ToBashArray(value_asdl::value_t* val);
  value::SparseArray* _ToSparseArray(value_asdl::value_t* val);
  List<value_asdl::value_t*>* _ToList(value_asdl::value_t* val);
  Dict<BigStr*, value_asdl::value_t*>* _ToDict(value_asdl::value_t* val);
  value_asdl::Obj* _ToObj(value_asdl::value_t* val);
  value::Place* _ToPlace(value_asdl::value_t* val);
  value_asdl::RegexMatch* _ToMatch(value_asdl::value_t* val);
  value::Eggex* _ToEggex(value_asdl::value_t* val);
  value::Expr* _ToExpr(value_asdl::value_t* val);
  Dict<BigStr*, runtime_asdl::Cell*>* _ToFrame(value_asdl::value_t* val);
  syntax_asdl::command_t* _ToCommandFrag(value_asdl::value_t* val);
  value::Command* _ToCommand(value_asdl::value_t* val);
  value_asdl::LiteralBlock* _ToLiteralBlock(value_asdl::value_t* val);
  BigStr* PosStr();
  BigStr* OptionalStr(BigStr* default_ = nullptr);
  bool PosBool();
  mops::BigInt PosInt();
  mops::BigInt OptionalInt(int default_);
  double PosFloat();
  List<BigStr*>* PosBashArray();
  value::SparseArray* PosSparseArray();
  List<value_asdl::value_t*>* PosList();
  Dict<BigStr*, value_asdl::value_t*>* PosDict();
  value_asdl::Obj* PosObj();
  value::Place* PosPlace();
  value::Eggex* PosEggex();
  value_asdl::RegexMatch* PosMatch();
  Dict<BigStr*, runtime_asdl::Cell*>* PosFrame();
  syntax_asdl::command_t* PosCommandFrag();
  value::Command* PosCommand();
  value::Expr* PosExpr();
  syntax_asdl::command_t* RequiredBlockAsFrag();
  syntax_asdl::command_t* OptionalBlockAsFrag();
  value_asdl::LiteralBlock* OptionalLiteralBlock();
  List<value_asdl::value_t*>* RestPos();
  syntax_asdl::loc_t* _BlameNamed(BigStr* name);
  BigStr* NamedStr(BigStr* param_name, BigStr* default_);
  bool NamedBool(BigStr* param_name, bool default_);
  mops::BigInt NamedInt(BigStr* param_name, int default_);
  double NamedFloat(BigStr* param_name, double default_);
  List<value_asdl::value_t*>* NamedList(BigStr* param_name, List<value_asdl::value_t*>* default_);
  Dict<BigStr*, value_asdl::value_t*>* NamedDict(BigStr* param_name, Dict<BigStr*, value_asdl::value_t*>* default_);
  Dict<BigStr*, value_asdl::value_t*>* RestNamed();
  void Done();
  List<value_asdl::value_t*>* pos_args{};
  Dict<BigStr*, value_asdl::value_t*>* named_args{};
  value_asdl::value_t* block_arg{};
  syntax_asdl::ArgList* arg_list{};
  syntax_asdl::loc_t* fallback_loc{};
  int pos_consumed{};
  bool is_bound{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(5, sizeof(Reader));
  }

  DISALLOW_COPY_AND_ASSIGN(Reader)
};


}  // declare namespace typed_args

namespace arith_parse {  // declare

syntax_asdl::arith_expr_t* NullIncDec(tdop::TdopParser* p, syntax_asdl::word_t* w, int bp);
syntax_asdl::arith_expr_t* NullUnaryPlus(tdop::TdopParser* p, syntax_asdl::word_t* t, int bp);
syntax_asdl::arith_expr_t* NullUnaryMinus(tdop::TdopParser* p, syntax_asdl::word_t* t, int bp);
syntax_asdl::arith_expr_t* LeftIncDec(tdop::TdopParser* p, syntax_asdl::word_t* w, syntax_asdl::arith_expr_t* left, int rbp);
syntax_asdl::arith_expr_t* LeftIndex(tdop::TdopParser* p, syntax_asdl::word_t* w, syntax_asdl::arith_expr_t* left, int unused_bp);
syntax_asdl::arith_expr_t* LeftTernary(tdop::TdopParser* p, syntax_asdl::word_t* t, syntax_asdl::arith_expr_t* left, int bp);

}  // declare namespace arith_parse

namespace bool_parse {  // declare

class BoolParser {
 public:
  BoolParser(word_parse::WordEmitter* w_parser);
  void _NextOne(types_asdl::lex_mode_t lex_mode = lex_mode_e::DBracket);
  void _Next(types_asdl::lex_mode_t lex_mode = lex_mode_e::DBracket);
  syntax_asdl::word_t* _LookAhead();
  Tuple2<syntax_asdl::bool_expr_t*, syntax_asdl::Token*> Parse();
  bool _TestAtEnd();
  syntax_asdl::bool_expr_t* ParseForBuiltin();
  syntax_asdl::bool_expr_t* ParseExpr();
  syntax_asdl::bool_expr_t* ParseTerm();
  syntax_asdl::bool_expr_t* ParseNegatedFactor();
  syntax_asdl::bool_expr_t* ParseFactor();
  word_parse::WordEmitter* w_parser{};
  List<syntax_asdl::word_t*>* words{};
  syntax_asdl::word_t* cur_word{};
  int bool_id{};
  id_kind_asdl::Kind_t bool_kind{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(BoolParser));
  }

  DISALLOW_COPY_AND_ASSIGN(BoolParser)
};


}  // declare namespace bool_parse

namespace braces {  // declare

extern int NO_STEP;
class _NotARange {
 public:
  _NotARange(BigStr* s);

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(0, sizeof(_NotARange));
  }

  DISALLOW_COPY_AND_ASSIGN(_NotARange)
};

class _RangeParser {
 public:
  _RangeParser(match::SimpleLexer* lexer, syntax_asdl::Token* blame_tok);
  void _Next();
  BigStr* _Eat(int token_type);
  int _ParseStep();
  word_part::BracedRange* _ParseRange(int range_kind);
  word_part::BracedRange* Parse();
  match::SimpleLexer* lexer{};
  syntax_asdl::Token* blame_tok{};
  BigStr* token_val{};
  int token_type{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(_RangeParser));
  }

  DISALLOW_COPY_AND_ASSIGN(_RangeParser)
};

word_part::BracedRange* _RangePartDetect(syntax_asdl::Token* tok);
class _StackFrame {
 public:
  _StackFrame(List<syntax_asdl::word_part_t*>* cur_parts);
  List<syntax_asdl::word_part_t*>* cur_parts{};
  word_part::BracedTuple* alt_part{};
  bool saw_comma{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(_StackFrame));
  }

  DISALLOW_COPY_AND_ASSIGN(_StackFrame)
};

word::BracedTree* BraceDetect(syntax_asdl::CompoundWord* w);
List<syntax_asdl::word_t*>* BraceDetectAll(List<syntax_asdl::CompoundWord*>* words);
int _LeadingZeros(BigStr* s);
BigStr* _IntToString(int i, int width);
List<BigStr*>* _RangeStrings(word_part::BracedRange* part);
List<List<syntax_asdl::word_part_t*>*>* _ExpandPart(List<syntax_asdl::word_part_t*>* parts, int first_alt_index, List<List<syntax_asdl::word_part_t*>*>* suffixes);
List<List<syntax_asdl::word_part_t*>*>* _BraceExpand(List<syntax_asdl::word_part_t*>* parts);
List<syntax_asdl::CompoundWord*>* BraceExpandWords(List<syntax_asdl::word_t*>* words);

}  // declare namespace braces

namespace cmd_eval {  // declare

extern int IsMainProgram;
extern int RaiseControlFlow;
extern int OptimizeSubshells;
extern int MarkLastCommands;
extern int NoDebugTrap;
extern int NoErrTrap;
extern BigStr* _STRICT_ERREXIT_COND_MSG;
cmd_value::Argv* MakeBuiltinArgv(List<BigStr*>* argv1);
class Deps {
 public:
  Deps();
  state::MutableOpts* mutable_opts{};
  dev::CrashDumper* dumper{};
  util::_DebugFile* debug_f{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(Deps));
  }

  DISALLOW_COPY_AND_ASSIGN(Deps)
};

syntax_asdl::command_t* _HasManyStatuses(syntax_asdl::command_t* node);
value_asdl::value_t* PlusEquals(value_asdl::value_t* old_val, value_asdl::value_t* val);
class ctx_LoopLevel {
 public:
  ctx_LoopLevel(cmd_eval::CommandEvaluator* cmd_ev);
  ~ctx_LoopLevel();
  cmd_eval::CommandEvaluator* cmd_ev{};

  DISALLOW_COPY_AND_ASSIGN(ctx_LoopLevel)
};

class CommandEvaluator {
 public:
  CommandEvaluator(state::Mem* mem, optview::Exec* exec_opts, ui::ErrorFormatter* errfmt, state::Procs* procs, Dict<int, vm::_AssignBuiltin*>* assign_builtins, alloc::Arena* arena, cmd_eval::Deps* cmd_deps, trap_osh::TrapState* trap_state, iolib::SignalSafe* signal_safe);
  void CheckCircularDeps();
  int _RunAssignBuiltin(cmd_value::Assign* cmd_val);
  void _CheckStatus(int status, runtime_asdl::CommandStatus* cmd_st, syntax_asdl::command_t* node, syntax_asdl::loc_t* default_loc);
  runtime_asdl::RedirValue* _EvalRedirect(syntax_asdl::Redir* r);
  int _RunSimpleCommand(runtime_asdl::cmd_value_t* cmd_val, runtime_asdl::CommandStatus* cmd_st, int run_flags);
  void _EvalTempEnv(List<syntax_asdl::EnvPair*>* more_env, int flags);
  void _StrictErrExit(syntax_asdl::command_t* node);
  void _StrictErrExitList(List<syntax_asdl::command_t*>* node_list);
  bool _EvalCondition(syntax_asdl::condition_t* cond, syntax_asdl::Token* blame_tok);
  value_asdl::value_t* _EvalCaseArg(syntax_asdl::case_arg_t* arg, syntax_asdl::loc_t* blame);
  int _DoVarDecl(command::VarDecl* node);
  void _DoMutation(command::Mutation* node);
  int _DoSimple(command::Simple* node, runtime_asdl::CommandStatus* cmd_st);
  int _DoExpandedAlias(command::ExpandedAlias* node);
  int _DoPipeline(command::Pipeline* node, runtime_asdl::CommandStatus* cmd_st);
  int _DoShAssignment(command::ShAssignment* node, runtime_asdl::CommandStatus* cmd_st);
  int _DoExpr(command::Expr* node);
  int _DoControlFlow(command::ControlFlow* node);
  int _DoAndOr(command::AndOr* node, runtime_asdl::CommandStatus* cmd_st);
  int _DoWhileUntil(command::WhileUntil* node);
  int _DoForEach(command::ForEach* node);
  int _DoForExpr(command::ForExpr* node);
  void _DoShFunction(command::ShFunction* node);
  void _DoProc(syntax_asdl::Proc* node);
  void _DoFunc(syntax_asdl::Func* node);
  int _DoIf(command::If* node);
  int _DoCase(command::Case* node);
  int _DoTimeBlock(command::TimeBlock* node);
  int _DoRedirect(command::Redirect* node, runtime_asdl::CommandStatus* cmd_st);
  void _LeafTick();
  int _Dispatch(syntax_asdl::command_t* node, runtime_asdl::CommandStatus* cmd_st);
  void RunPendingTraps();
  void RunPendingTrapsAndCatch();
  int _Execute(syntax_asdl::command_t* node);
  int _ExecuteList(List<syntax_asdl::command_t*>* children);
  int LastStatus();
  void _MarkLastCommands(syntax_asdl::command_t* node);
  syntax_asdl::command_t* _RemoveSubshells(syntax_asdl::command_t* node);
  Tuple2<bool, bool> ExecuteAndCatch(syntax_asdl::command_t* node, int cmd_flags);
  int EvalCommandFrag(syntax_asdl::command_t* frag);
  void RunTrapsOnExit(syntax_asdl::IntParamBox* mut_status);
  void _MaybeRunDebugTrap();
  void _MaybeRunErrTrap();
  int RunProc(value::Proc* proc, cmd_value::Argv* cmd_val);
  int RunFuncForCompletion(value::Proc* proc, List<BigStr*>* argv);
  vm::_Executor* shell_ex{};
  sh_expr_eval::ArithEvaluator* arith_ev{};
  sh_expr_eval::BoolEvaluator* bool_ev{};
  expr_eval::ExprEvaluator* expr_ev{};
  word_eval::AbstractWordEvaluator* word_ev{};
  dev::Tracer* tracer{};
  state::Mem* mem{};
  optview::Exec* exec_opts{};
  ui::ErrorFormatter* errfmt{};
  state::Procs* procs{};
  Dict<int, vm::_AssignBuiltin*>* assign_builtins{};
  alloc::Arena* arena{};
  state::MutableOpts* mutable_opts{};
  dev::CrashDumper* dumper{};
  util::_DebugFile* debug_f{};
  trap_osh::TrapState* trap_state{};
  iolib::SignalSafe* signal_safe{};
  List<runtime_asdl::StatusArray*>* status_array_pool{};
  int loop_level{};
  bool check_command_sub_status{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(18, sizeof(CommandEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(CommandEvaluator)
};


}  // declare namespace cmd_eval

namespace cmd_parse {  // declare

extern int TAB_CH;
extern int SPACE_CH;
Tuple2<List<Tuple2<syntax_asdl::SourceLine*, int>*>*, Tuple2<syntax_asdl::SourceLine*, int>*> _ReadHereLines(reader::_Reader* line_reader, syntax_asdl::Redir* h, BigStr* delimiter);
List<syntax_asdl::word_part_t*>* _MakeLiteralHereLines(List<Tuple2<syntax_asdl::SourceLine*, int>*>* here_lines, alloc::Arena* arena, bool do_lossless);
void _ParseHereDocBody(parse_lib::ParseContext* parse_ctx, syntax_asdl::Redir* r, reader::_Reader* line_reader, alloc::Arena* arena);
syntax_asdl::AssignPair* _MakeAssignPair(parse_lib::ParseContext* parse_ctx, syntax_asdl::ParsedAssignment* preparsed, alloc::Arena* arena);
void _AppendMoreEnv(List<syntax_asdl::ParsedAssignment*>* preparsed_list, List<syntax_asdl::EnvPair*>* more_env);
Tuple2<List<syntax_asdl::ParsedAssignment*>*, List<syntax_asdl::CompoundWord*>*> _SplitSimpleCommandPrefix(List<syntax_asdl::CompoundWord*>* words);
command::Simple* _MakeSimpleCommand(List<syntax_asdl::ParsedAssignment*>* preparsed_list, List<syntax_asdl::CompoundWord*>* suffix_words, syntax_asdl::ArgList* typed_args, value_asdl::LiteralBlock* block);
class VarChecker {
 public:
  VarChecker();
  void Push(syntax_asdl::Token* blame_tok);
  void Pop();
  void Check(int keyword_id, BigStr* var_name, syntax_asdl::Token* blame_tok);
  List<syntax_asdl::Token*>* tokens{};
  List<Dict<BigStr*, int>*>* names{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(VarChecker));
  }

  DISALLOW_COPY_AND_ASSIGN(VarChecker)
};

class ctx_VarChecker {
 public:
  ctx_VarChecker(cmd_parse::VarChecker* var_checker, syntax_asdl::Token* blame_tok);
  ~ctx_VarChecker();
  cmd_parse::VarChecker* var_checker{};

  DISALLOW_COPY_AND_ASSIGN(ctx_VarChecker)
};

class ctx_CmdMode {
 public:
  ctx_CmdMode(cmd_parse::CommandParser* cmd_parse, types_asdl::cmd_mode_t new_cmd_mode);
  ~ctx_CmdMode();
  cmd_parse::CommandParser* cmd_parse{};
  types_asdl::cmd_mode_t prev_cmd_mode{};

  DISALLOW_COPY_AND_ASSIGN(ctx_CmdMode)
};

extern List<int>* SECONDARY_KEYWORDS;
class CommandParser {
 public:
  CommandParser(parse_lib::ParseContext* parse_ctx, optview::Parse* parse_opts, word_parse::WordParser* w_parser, lexer::Lexer* lexer, reader::_Reader* line_reader, int eof_id = Id::Eof_Real);
  void Init_AliasesInFlight(List<Tuple2<BigStr*, int>*>* aliases_in_flight);
  void Reset();
  void ResetInputObjects();
  void _SetNext();
  void _SetNextBrack();
  void _GetWord();
  syntax_asdl::word_t* _Eat(int c_id, BigStr* msg = nullptr);
  void _NewlineOk();
  bool _AtSecondaryKeyword();
  syntax_asdl::Redir* ParseRedirect();
  List<syntax_asdl::Redir*>* _ParseRedirectList();
  syntax_asdl::command_t* _MaybeParseRedirectList(syntax_asdl::command_t* node);
  Tuple4<List<syntax_asdl::Redir*>*, List<syntax_asdl::CompoundWord*>*, syntax_asdl::ArgList*, value_asdl::LiteralBlock*> _ScanSimpleCommand();
  syntax_asdl::command_t* _MaybeExpandAliases(List<syntax_asdl::CompoundWord*>* words);
  syntax_asdl::command_t* ParseSimpleCommand();
  syntax_asdl::BraceGroup* ParseBraceGroup();
  command::DoGroup* ParseDoGroup();
  Tuple2<List<syntax_asdl::CompoundWord*>*, syntax_asdl::Token*> ParseForWords();
  command::ForExpr* _ParseForExprLoop(syntax_asdl::Token* for_kw);
  command::ForEach* _ParseForEachLoop(syntax_asdl::Token* for_kw);
  syntax_asdl::command_t* ParseFor();
  syntax_asdl::condition_t* _ParseConditionList();
  command::WhileUntil* ParseWhileUntil(syntax_asdl::Token* keyword);
  syntax_asdl::CaseArm* ParseCaseArm();
  syntax_asdl::CaseArm* ParseYshCaseArm(int discriminant);
  command::Case* ParseYshCase(syntax_asdl::Token* case_kw);
  command::Case* ParseOldCase(syntax_asdl::Token* case_kw);
  command::Case* ParseCase();
  void _ParseYshElifElse(command::If* if_node);
  command::If* _ParseYshIf(syntax_asdl::Token* if_kw, syntax_asdl::condition_t* cond);
  void _ParseElifElse(command::If* if_node);
  command::If* ParseIf();
  syntax_asdl::command_t* ParseTime();
  syntax_asdl::command_t* ParseCompoundCommand();
  command::ShFunction* ParseFunctionDef();
  command::ShFunction* ParseKshFunctionDef();
  syntax_asdl::Proc* ParseYshProc();
  syntax_asdl::Func* ParseYshFunc();
  syntax_asdl::command_t* ParseCoproc();
  command::Subshell* ParseSubshell();
  command::DBracket* ParseDBracket();
  command::DParen* ParseDParen();
  syntax_asdl::command_t* ParseCommand();
  syntax_asdl::command_t* ParsePipeline();
  syntax_asdl::command_t* ParseAndOr();
  syntax_asdl::command_t* _ParseAndOr();
  syntax_asdl::command_t* _ParseCommandLine();
  command::CommandList* _ParseCommandTerm();
  command::CommandList* _ParseCommandList();
  syntax_asdl::command_t* ParseLogicalLine();
  syntax_asdl::parse_result_t* ParseInteractiveLine();
  syntax_asdl::command_t* ParseCommandSub();
  void CheckForPendingHereDocs();
  parse_lib::ParseContext* parse_ctx{};
  Dict<BigStr*, BigStr*>* aliases{};
  optview::Parse* parse_opts{};
  word_parse::WordParser* w_parser{};
  lexer::Lexer* lexer{};
  reader::_Reader* line_reader{};
  alloc::Arena* arena{};
  List<Tuple2<BigStr*, int>*>* aliases_in_flight{};
  List<bool>* hay_attrs_stack{};
  cmd_parse::VarChecker* var_checker{};
  syntax_asdl::word_t* cur_word{};
  List<syntax_asdl::Redir*>* pending_here_docs{};
  int eof_id{};
  bool allow_block{};
  types_asdl::cmd_mode_t cmd_mode{};
  types_asdl::lex_mode_t next_lex_mode{};
  id_kind_asdl::Kind_t c_kind{};
  int c_id{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(12, sizeof(CommandParser));
  }

  DISALLOW_COPY_AND_ASSIGN(CommandParser)
};


}  // declare namespace cmd_parse

namespace glob_ {  // declare

bool LooksLikeGlob(BigStr* s);
bool LooksLikeStaticGlob(syntax_asdl::CompoundWord* w);
extern BigStr* GLOB_META_CHARS;
BigStr* GlobEscape(BigStr* s);
extern BigStr* ERE_META_CHARS;
BigStr* ExtendedRegexEscape(BigStr* s);
BigStr* GlobUnescape(BigStr* s);
class _GlobParser {
 public:
  _GlobParser(match::SimpleLexer* lexer);
  void _Next();
  List<syntax_asdl::glob_part_t*>* _ParseCharClass();
  Tuple2<List<syntax_asdl::glob_part_t*>*, List<BigStr*>*> Parse();
  match::SimpleLexer* lexer{};
  BigStr* token_val{};
  List<BigStr*>* warnings{};
  int token_type{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(_GlobParser));
  }

  DISALLOW_COPY_AND_ASSIGN(_GlobParser)
};

extern BigStr* _REGEX_CHARS_TO_ESCAPE;
BigStr* _GenerateERE(List<syntax_asdl::glob_part_t*>* parts);
Tuple2<BigStr*, List<BigStr*>*> GlobToERE(BigStr* pat);
class Globber {
 public:
  Globber(optview::Exec* exec_opts);
  int _Glob(BigStr* arg, List<BigStr*>* out);
  int Expand(BigStr* arg, List<BigStr*>* out);
  int ExpandExtended(BigStr* glob_pat, BigStr* fnmatch_pat, List<BigStr*>* out);
  optview::Exec* exec_opts{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(Globber));
  }

  DISALLOW_COPY_AND_ASSIGN(Globber)
};


}  // declare namespace glob_

namespace history {  // declare

class Evaluator {
 public:
  Evaluator(py_readline::Readline* readline, parse_lib::ParseContext* parse_ctx, util::_DebugFile* debug_f);
  BigStr* Eval(BigStr* line);
  py_readline::Readline* readline{};
  parse_lib::ParseContext* parse_ctx{};
  util::_DebugFile* debug_f{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(Evaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(Evaluator)
};


}  // declare namespace history

namespace prompt {  // declare

extern BigStr* _ERROR_FMT;
extern BigStr* _UNBALANCED_ERROR;
class _PromptEvaluatorCache {
 public:
  _PromptEvaluatorCache();
  int _GetEuid();
  BigStr* Get(BigStr* name);
  Dict<BigStr*, BigStr*>* cache{};
  int euid{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(_PromptEvaluatorCache));
  }

  DISALLOW_COPY_AND_ASSIGN(_PromptEvaluatorCache)
};

class Evaluator {
 public:
  Evaluator(BigStr* lang, BigStr* version_str, parse_lib::ParseContext* parse_ctx, state::Mem* mem);
  void CheckCircularDeps();
  BigStr* PromptVal(BigStr* what);
  BigStr* PromptSubst(BigStr* ch, BigStr* arg = nullptr);
  BigStr* _ReplaceBackslashCodes(List<Tuple2<int, BigStr*>*>* tokens);
  BigStr* EvalPrompt(BigStr* s);
  BigStr* EvalFirstPrompt();
  word_eval::AbstractWordEvaluator* word_ev{};
  expr_eval::ExprEvaluator* expr_ev{};
  value_asdl::Obj* global_io{};
  BigStr* lang{};
  BigStr* version_str{};
  parse_lib::ParseContext* parse_ctx{};
  state::Mem* mem{};
  prompt::_PromptEvaluatorCache* cache{};
  Dict<BigStr*, List<Tuple2<int, BigStr*>*>*>* tokens_cache{};
  Dict<BigStr*, syntax_asdl::CompoundWord*>* parse_cache{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(10, sizeof(Evaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(Evaluator)
};

extern BigStr* PROMPT_COMMAND;
class UserPlugin {
 public:
  UserPlugin(state::Mem* mem, parse_lib::ParseContext* parse_ctx, cmd_eval::CommandEvaluator* cmd_ev, ui::ErrorFormatter* errfmt);
  void Run();
  state::Mem* mem{};
  parse_lib::ParseContext* parse_ctx{};
  cmd_eval::CommandEvaluator* cmd_ev{};
  ui::ErrorFormatter* errfmt{};
  alloc::Arena* arena{};
  Dict<BigStr*, syntax_asdl::command_t*>* parse_cache{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(6, sizeof(UserPlugin));
  }

  DISALLOW_COPY_AND_ASSIGN(UserPlugin)
};


}  // declare namespace prompt

namespace sh_expr_eval {  // declare

value_asdl::value_t* OldValue(value_asdl::sh_lvalue_t* lval, state::Mem* mem, optview::Exec* exec_opts);
class UnsafeArith {
 public:
  UnsafeArith(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, parse_lib::ParseContext* parse_ctx, sh_expr_eval::ArithEvaluator* arith_ev, ui::ErrorFormatter* errfmt);
  value_asdl::sh_lvalue_t* ParseLValue(BigStr* s, syntax_asdl::loc_t* location);
  syntax_asdl::BracedVarSub* ParseVarRef(BigStr* ref_str, syntax_asdl::Token* blame_tok);
  state::Mem* mem{};
  optview::Exec* exec_opts{};
  state::MutableOpts* mutable_opts{};
  parse_lib::ParseContext* parse_ctx{};
  sh_expr_eval::ArithEvaluator* arith_ev{};
  ui::ErrorFormatter* errfmt{};
  alloc::Arena* arena{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(7, sizeof(UnsafeArith));
  }

  DISALLOW_COPY_AND_ASSIGN(UnsafeArith)
};

Tuple2<bool, mops::BigInt> _ParseOshInteger(BigStr* s, syntax_asdl::loc_t* blame_loc);
class ArithEvaluator {
 public:
  ArithEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, parse_lib::ParseContext* parse_ctx, ui::ErrorFormatter* errfmt);
  void CheckCircularDeps();
  mops::BigInt _StringToBigInt(BigStr* s, syntax_asdl::loc_t* blame_loc);
  mops::BigInt _ValToIntOrError(value_asdl::value_t* val, syntax_asdl::arith_expr_t* blame);
  Tuple2<mops::BigInt, value_asdl::sh_lvalue_t*> _EvalLhsAndLookupArith(syntax_asdl::arith_expr_t* node);
  void _Store(value_asdl::sh_lvalue_t* lval, mops::BigInt new_int);
  mops::BigInt EvalToBigInt(syntax_asdl::arith_expr_t* node);
  int EvalToInt(syntax_asdl::arith_expr_t* node);
  value_asdl::value_t* Eval(syntax_asdl::arith_expr_t* node);
  BigStr* EvalWordToString(syntax_asdl::arith_expr_t* node, syntax_asdl::loc_t* blame_loc = loc::Missing);
  value_asdl::sh_lvalue_t* EvalShellLhs(syntax_asdl::sh_lhs_t* node, runtime_asdl::scope_t which_scopes);
  Tuple2<BigStr*, syntax_asdl::loc_t*> _VarNameOrWord(syntax_asdl::arith_expr_t* anode);
  value_asdl::sh_lvalue_t* EvalArithLhs(syntax_asdl::arith_expr_t* anode);
  ui::ErrorFormatter* errfmt{};
  optview::Exec* exec_opts{};
  state::Mem* mem{};
  state::MutableOpts* mutable_opts{};
  parse_lib::ParseContext* parse_ctx{};
  word_eval::StringWordEvaluator* word_ev{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(ArithEvaluator, errfmt))
         | maskbit(offsetof(ArithEvaluator, exec_opts))
         | maskbit(offsetof(ArithEvaluator, mem))
         | maskbit(offsetof(ArithEvaluator, mutable_opts))
         | maskbit(offsetof(ArithEvaluator, parse_ctx))
         | maskbit(offsetof(ArithEvaluator, word_ev));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ArithEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(ArithEvaluator)
};

class BoolEvaluator : public ::sh_expr_eval::ArithEvaluator {
 public:
  BoolEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, parse_lib::ParseContext* parse_ctx, ui::ErrorFormatter* errfmt, bool bracket = false);
  bool _IsDefined(BigStr* s, syntax_asdl::loc_t* blame_loc);
  mops::BigInt _StringToBigIntOrError(BigStr* s, syntax_asdl::loc_t* blame_loc);
  BigStr* _EvalCompoundWord(syntax_asdl::word_t* word, int eval_flags = 0);
  bool EvalB(syntax_asdl::bool_expr_t* node);

  bool bracket{};
  
  static constexpr uint32_t field_mask() {
    return ::sh_expr_eval::ArithEvaluator::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(BoolEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(BoolEvaluator)
};


}  // declare namespace sh_expr_eval

namespace split {  // declare

extern BigStr* DEFAULT_IFS;
List<BigStr*>* _SpansToParts(BigStr* s, List<Tuple2<runtime_asdl::span_t, int>*>* spans);
class SplitContext {
 public:
  SplitContext(state::Mem* mem);
  split::IfsSplitter* _GetSplitter(BigStr* ifs = nullptr);
  BigStr* GetJoinChar();
  BigStr* Escape(BigStr* s);
  List<BigStr*>* SplitForWordEval(BigStr* s, BigStr* ifs = nullptr);
  List<Tuple2<runtime_asdl::span_t, int>*>* SplitForRead(BigStr* line, bool allow_escape, bool do_split);
  state::Mem* mem{};
  Dict<BigStr*, split::IfsSplitter*>* splitters{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(SplitContext));
  }

  DISALLOW_COPY_AND_ASSIGN(SplitContext)
};

class _BaseSplitter {
 public:
  _BaseSplitter(BigStr* escape_chars);
  BigStr* Escape(BigStr* s);
  BigStr* escape_chars{};
  
  static constexpr uint32_t field_mask() {
    return maskbit(offsetof(_BaseSplitter, escape_chars));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_BaseSplitter));
  }

  DISALLOW_COPY_AND_ASSIGN(_BaseSplitter)
};

class IfsSplitter : public ::split::_BaseSplitter {
 public:
  IfsSplitter(BigStr* ifs_whitespace, BigStr* ifs_other);
  List<Tuple2<runtime_asdl::span_t, int>*>* Split(BigStr* s, bool allow_escape);

  BigStr* ifs_other{};
  BigStr* ifs_whitespace{};
  
  static constexpr uint32_t field_mask() {
    return ::split::_BaseSplitter::field_mask()
         | maskbit(offsetof(IfsSplitter, ifs_other))
         | maskbit(offsetof(IfsSplitter, ifs_whitespace));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(IfsSplitter));
  }

  DISALLOW_COPY_AND_ASSIGN(IfsSplitter)
};


}  // declare namespace split

namespace string_ops {  // declare

extern int UTF8_ERR_OVERLONG;
extern int UTF8_ERR_SURROGATE;
extern int UTF8_ERR_TOO_LARGE;
extern int UTF8_ERR_BAD_ENCODING;
extern int UTF8_ERR_TRUNCATED_BYTES;
BigStr* Utf8Error_str(int error);
int DecodeUtf8Char(BigStr* s, int start);
int NextUtf8Char(BigStr* s, int i);
extern BigStr* _INVALID_START;
int _Utf8CharLen(int starting_byte);
int PreviousUtf8Char(BigStr* s, int i);
int CountUtf8Chars(BigStr* s);
int AdvanceUtf8Chars(BigStr* s, int num_chars, int byte_offset);
extern List<int>* SPACES;
bool _IsSpace(int codepoint);
Tuple2<int, int> StartsWithWhitespaceByteRange(BigStr* s);
Tuple2<int, int> EndsWithWhitespaceByteRange(BigStr* s);
BigStr* DoUnarySuffixOp(BigStr* s, syntax_asdl::Token* op_tok, BigStr* arg, bool is_extglob);
List<Tuple2<int, int>*>* _AllMatchPositions(BigStr* s, BigStr* regex);
BigStr* _PatSubAll(BigStr* s, BigStr* regex, BigStr* replace_str);
class GlobReplacer {
 public:
  GlobReplacer(BigStr* regex, BigStr* replace_str, syntax_asdl::Token* slash_tok);
  BigStr* Replace(BigStr* s, suffix_op::PatSub* op);
  BigStr* regex{};
  BigStr* replace_str{};
  syntax_asdl::Token* slash_tok{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(GlobReplacer));
  }

  DISALLOW_COPY_AND_ASSIGN(GlobReplacer)
};

BigStr* ShellQuoteB(BigStr* s);

}  // declare namespace string_ops

namespace tdop {  // declare

bool IsIndexable(syntax_asdl::arith_expr_t* node);
void CheckLhsExpr(syntax_asdl::arith_expr_t* node, syntax_asdl::word_t* blame_word);
syntax_asdl::arith_expr_t* NullError(tdop::TdopParser* p, syntax_asdl::word_t* t, int bp);
syntax_asdl::arith_expr_t* NullConstant(tdop::TdopParser* p, syntax_asdl::word_t* w, int bp);
syntax_asdl::arith_expr_t* NullParen(tdop::TdopParser* p, syntax_asdl::word_t* t, int bp);
syntax_asdl::arith_expr_t* NullPrefixOp(tdop::TdopParser* p, syntax_asdl::word_t* w, int bp);
syntax_asdl::arith_expr_t* LeftError(tdop::TdopParser* p, syntax_asdl::word_t* t, syntax_asdl::arith_expr_t* left, int rbp);
syntax_asdl::arith_expr_t* LeftBinaryOp(tdop::TdopParser* p, syntax_asdl::word_t* w, syntax_asdl::arith_expr_t* left, int rbp);
syntax_asdl::arith_expr_t* LeftAssign(tdop::TdopParser* p, syntax_asdl::word_t* w, syntax_asdl::arith_expr_t* left, int rbp);
class TdopParser {
 public:
  TdopParser(tdop::ParserSpec* spec, word_parse::WordParser* w_parser, optview::Parse* parse_opts);
  int CurrentId();
  bool AtToken(int token_type);
  void Eat(int token_type);
  bool Next();
  syntax_asdl::arith_expr_t* ParseUntil(int rbp);
  syntax_asdl::arith_expr_t* Parse();
  tdop::ParserSpec* spec{};
  word_parse::WordParser* w_parser{};
  optview::Parse* parse_opts{};
  syntax_asdl::word_t* cur_word{};
  int op_id{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(TdopParser));
  }

  DISALLOW_COPY_AND_ASSIGN(TdopParser)
};


}  // declare namespace tdop

namespace word_ {  // declare

int LiteralId(syntax_asdl::word_part_t* p);
Tuple3<bool, BigStr*, bool> _EvalWordPart(syntax_asdl::word_part_t* part);
BigStr* FastStrEval(syntax_asdl::CompoundWord* w);
Tuple3<bool, BigStr*, bool> StaticEval(syntax_asdl::word_t* UP_w);
syntax_asdl::CompoundWord* TildeDetect(syntax_asdl::word_t* UP_w);
syntax_asdl::CompoundWord* TildeDetect2(syntax_asdl::CompoundWord* w);
void TildeDetectAssign(syntax_asdl::CompoundWord* w);
List<syntax_asdl::word_t*>* TildeDetectAll(List<syntax_asdl::word_t*>* words);
bool HasArrayPart(syntax_asdl::CompoundWord* w);
BigStr* ShFunctionName(syntax_asdl::CompoundWord* w);
syntax_asdl::Token* LooksLikeArithVar(syntax_asdl::word_t* UP_w);
bool IsVarLike(syntax_asdl::CompoundWord* w);
Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int> DetectShAssignment(syntax_asdl::CompoundWord* w);
syntax_asdl::AssocPair* DetectAssocPair(syntax_asdl::CompoundWord* w);
Tuple2<id_kind_asdl::Kind_t, syntax_asdl::Token*> IsControlFlow(syntax_asdl::CompoundWord* w);
syntax_asdl::Token* LiteralToken(syntax_asdl::word_t* UP_w);
syntax_asdl::Token* BraceToken(syntax_asdl::word_t* UP_w);
syntax_asdl::Token* AsKeywordToken(syntax_asdl::word_t* UP_w);
syntax_asdl::Token* AsOperatorToken(syntax_asdl::word_t* word);
int ArithId(syntax_asdl::word_t* w);
int BoolId(syntax_asdl::word_t* w);
int CommandId(syntax_asdl::word_t* w);
id_kind_asdl::Kind_t CommandKind(syntax_asdl::word_t* w);
bool IsVarSub(syntax_asdl::word_t* w);
syntax_asdl::CompoundWord* ErrorWord(BigStr* error_str);
BigStr* Pretty(syntax_asdl::word_t* w);
class ctx_EmitDocToken {
 public:
  ctx_EmitDocToken(word_parse::WordParser* w_parser);
  ~ctx_EmitDocToken();
  word_parse::WordParser* w_parser{};

  DISALLOW_COPY_AND_ASSIGN(ctx_EmitDocToken)
};

class ctx_Multiline {
 public:
  ctx_Multiline(word_parse::WordParser* w_parser);
  ~ctx_Multiline();
  word_parse::WordParser* w_parser{};

  DISALLOW_COPY_AND_ASSIGN(ctx_Multiline)
};


}  // declare namespace word_

namespace word_compile {  // declare

syntax_asdl::CharCode* EvalCharLiteralForRegex(syntax_asdl::Token* tok);
BigStr* EvalCStringToken(int id_, BigStr* value);
BigStr* EvalSingleQuoted(int id_, List<syntax_asdl::Token*>* tokens);
bool _TokenConsistsOf(syntax_asdl::Token* tok, BigStr* byte_set);
bool _IsLeadingSpace(syntax_asdl::Token* tok);
bool _IsTrailingSpace(syntax_asdl::Token* tok);
void RemoveLeadingSpaceDQ(List<syntax_asdl::word_part_t*>* parts);
void RemoveLeadingSpaceSQ(List<syntax_asdl::Token*>* tokens);

}  // declare namespace word_compile

namespace word_eval {  // declare

extern int QUOTED;
extern int IS_SUBST;
extern int EXTGLOB_FILES;
extern int EXTGLOB_MATCH;
extern int EXTGLOB_NESTED;
extern int QUOTE_FNMATCH;
extern int QUOTE_ERE;
extern List<BigStr*>* _STRING_AND_ARRAY;
bool ShouldArrayDecay(BigStr* var_name, optview::Exec* exec_opts, bool is_plain_var_sub = true);
value_asdl::value_t* DecayArray(value_asdl::value_t* val);
bool _DetectMetaBuiltinStr(BigStr* s);
bool _DetectMetaBuiltin(runtime_asdl::part_value_t* val0);
runtime_asdl::AssignArg* _SplitAssignArg(BigStr* arg, syntax_asdl::CompoundWord* blame_word);
BigStr* _BackslashEscape(BigStr* s);
runtime_asdl::part_value_t* _ValueToPartValue(value_asdl::value_t* val, bool quoted, syntax_asdl::word_part_t* part_loc);
List<List<runtime_asdl::Piece*>*>* _MakeWordFrames(List<runtime_asdl::part_value_t*>* part_vals);
BigStr* _DecayPartValuesToString(List<runtime_asdl::part_value_t*>* part_vals, BigStr* join_char);
value_asdl::value_t* _PerformSlice(value_asdl::value_t* val, mops::BigInt offset, int length, bool has_length, syntax_asdl::BracedVarSub* part, value::Str* arg0_val);
class StringWordEvaluator {
 public:
  StringWordEvaluator();
  virtual value::Str* EvalWordToString(syntax_asdl::word_t* w, int eval_flags = 0);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(StringWordEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(StringWordEvaluator)
};

BigStr* _GetDollarHyphen(optview::Exec* exec_opts);
class TildeEvaluator {
 public:
  TildeEvaluator(state::Mem* mem, optview::Exec* exec_opts);
  BigStr* GetMyHomeDir();
  BigStr* Eval(word_part::TildeSub* part);
  state::Mem* mem{};
  optview::Exec* exec_opts{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(TildeEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(TildeEvaluator)
};

class AbstractWordEvaluator : public ::word_eval::StringWordEvaluator {
 public:
  AbstractWordEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, word_eval::TildeEvaluator* tilde_ev, split::SplitContext* splitter, ui::ErrorFormatter* errfmt);
  virtual void CheckCircularDeps();
  virtual runtime_asdl::part_value_t* _EvalCommandSub(syntax_asdl::CommandSub* cs_part, bool quoted);
  virtual runtime_asdl::part_value_t* _EvalProcessSub(syntax_asdl::CommandSub* cs_part);
  value_asdl::value_t* _EvalVarNum(int var_num);
  value_asdl::value_t* _EvalSpecialVar(int op_id, bool quoted, runtime_asdl::VarSubState* vsub_state);
  bool _ApplyTestOp(value_asdl::value_t* val, suffix_op::Unary* op, bool quoted, List<runtime_asdl::part_value_t*>* part_vals, runtime_asdl::VTestPlace* vtest_place, syntax_asdl::Token* blame_token, runtime_asdl::VarSubState* vsub_state);
  int _Count(value_asdl::value_t* val, syntax_asdl::Token* token);
  value_asdl::value_t* _Keys(value_asdl::value_t* val, syntax_asdl::Token* token);
  value_asdl::value_t* _EvalVarRef(value_asdl::value_t* val, syntax_asdl::Token* blame_tok, bool quoted, runtime_asdl::VarSubState* vsub_state, runtime_asdl::VTestPlace* vtest_place);
  value_asdl::value_t* _ApplyUnarySuffixOp(value_asdl::value_t* val, suffix_op::Unary* op);
  value_asdl::value_t* _PatSub(value_asdl::value_t* val, suffix_op::PatSub* op);
  value_asdl::value_t* _Slice(value_asdl::value_t* val, suffix_op::Slice* op, BigStr* var_name, syntax_asdl::BracedVarSub* part);
  Tuple2<value_asdl::value_t*, bool> _Nullary(value_asdl::value_t* val, syntax_asdl::Token* op, BigStr* var_name, syntax_asdl::Token* vsub_token, runtime_asdl::VarSubState* vsub_state);
  value_asdl::value_t* _WholeArray(value_asdl::value_t* val, syntax_asdl::BracedVarSub* part, bool quoted, runtime_asdl::VarSubState* vsub_state);
  value_asdl::value_t* _ArrayIndex(value_asdl::value_t* val, syntax_asdl::BracedVarSub* part, runtime_asdl::VTestPlace* vtest_place);
  void _EvalDoubleQuoted(List<syntax_asdl::word_part_t*>* parts, List<runtime_asdl::part_value_t*>* part_vals);
  BigStr* EvalDoubleQuotedToString(syntax_asdl::DoubleQuoted* dq_part);
  value::Str* _DecayArray(value::BashArray* val);
  value_asdl::value_t* _ProcessUndef(value_asdl::value_t* val, syntax_asdl::Token* name_tok, runtime_asdl::VarSubState* vsub_state);
  value_asdl::value_t* _EvalBracketOp(value_asdl::value_t* val, syntax_asdl::BracedVarSub* part, bool quoted, runtime_asdl::VarSubState* vsub_state, runtime_asdl::VTestPlace* vtest_place);
  value_asdl::value_t* _VarRefValue(syntax_asdl::BracedVarSub* part, bool quoted, runtime_asdl::VarSubState* vsub_state, runtime_asdl::VTestPlace* vtest_place);
  void _EvalBracedVarSub(syntax_asdl::BracedVarSub* part, List<runtime_asdl::part_value_t*>* part_vals, bool quoted);
  BigStr* _ConcatPartVals(List<runtime_asdl::part_value_t*>* part_vals, syntax_asdl::loc_t* location);
  BigStr* EvalBracedVarSubToString(syntax_asdl::BracedVarSub* part);
  void _EvalSimpleVarSub(syntax_asdl::SimpleVarSub* part, List<runtime_asdl::part_value_t*>* part_vals, bool quoted);
  BigStr* EvalSimpleVarSubToString(syntax_asdl::SimpleVarSub* node);
  void _EvalExtGlob(word_part::ExtGlob* part, List<runtime_asdl::part_value_t*>* part_vals);
  void _TranslateExtGlob(List<runtime_asdl::part_value_t*>* part_vals, syntax_asdl::CompoundWord* w, List<BigStr*>* glob_parts, List<BigStr*>* fnmatch_parts);
  void _EvalWordPart(syntax_asdl::word_part_t* part, List<runtime_asdl::part_value_t*>* part_vals, int flags);
  void _EvalRhsWordToParts(syntax_asdl::rhs_word_t* w, List<runtime_asdl::part_value_t*>* part_vals, int eval_flags = 0);
  void _EvalWordToParts(syntax_asdl::CompoundWord* w, List<runtime_asdl::part_value_t*>* part_vals, int eval_flags = 0);
  void _PartValsToString(List<runtime_asdl::part_value_t*>* part_vals, syntax_asdl::CompoundWord* w, int eval_flags, List<BigStr*>* strs);
  virtual value::Str* EvalWordToString(syntax_asdl::word_t* UP_w, int eval_flags = 0);
  Tuple2<value::Str*, bool> EvalWordToPattern(syntax_asdl::rhs_word_t* UP_w);
  value::Str* EvalForPlugin(syntax_asdl::CompoundWord* w);
  value_asdl::value_t* EvalRhsWord(syntax_asdl::rhs_word_t* UP_w);
  void _EvalWordFrame(List<runtime_asdl::Piece*>* frame, List<BigStr*>* argv);
  List<BigStr*>* _EvalWordToArgv(syntax_asdl::CompoundWord* w);
  cmd_value::Assign* _EvalAssignBuiltin(int builtin_id, BigStr* arg0, List<syntax_asdl::CompoundWord*>* words, int meta_offset);
  cmd_value::Assign* _DetectAssignBuiltinStr(BigStr* arg0, List<syntax_asdl::CompoundWord*>* words, int meta_offset);
  cmd_value::Assign* _DetectAssignBuiltin(runtime_asdl::part_value_t* val0, List<syntax_asdl::CompoundWord*>* words, int meta_offset);
  runtime_asdl::cmd_value_t* SimpleEvalWordSequence2(List<syntax_asdl::CompoundWord*>* words, bool is_last_cmd, bool allow_assign);
  runtime_asdl::cmd_value_t* EvalWordSequence2(List<syntax_asdl::CompoundWord*>* words, bool is_last_cmd, bool allow_assign = false);
  List<BigStr*>* EvalWordSequence(List<syntax_asdl::CompoundWord*>* words);

  sh_expr_eval::ArithEvaluator* arith_ev{};
  ui::ErrorFormatter* errfmt{};
  optview::Exec* exec_opts{};
  expr_eval::ExprEvaluator* expr_ev{};
  glob_::Globber* globber{};
  state::Mem* mem{};
  state::MutableOpts* mutable_opts{};
  prompt::Evaluator* prompt_ev{};
  split::SplitContext* splitter{};
  word_eval::TildeEvaluator* tilde_ev{};
  sh_expr_eval::UnsafeArith* unsafe_arith{};
  
  static constexpr uint32_t field_mask() {
    return ::word_eval::StringWordEvaluator::field_mask()
         | maskbit(offsetof(AbstractWordEvaluator, arith_ev))
         | maskbit(offsetof(AbstractWordEvaluator, errfmt))
         | maskbit(offsetof(AbstractWordEvaluator, exec_opts))
         | maskbit(offsetof(AbstractWordEvaluator, expr_ev))
         | maskbit(offsetof(AbstractWordEvaluator, globber))
         | maskbit(offsetof(AbstractWordEvaluator, mem))
         | maskbit(offsetof(AbstractWordEvaluator, mutable_opts))
         | maskbit(offsetof(AbstractWordEvaluator, prompt_ev))
         | maskbit(offsetof(AbstractWordEvaluator, splitter))
         | maskbit(offsetof(AbstractWordEvaluator, tilde_ev))
         | maskbit(offsetof(AbstractWordEvaluator, unsafe_arith));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(AbstractWordEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(AbstractWordEvaluator)
};

class NormalWordEvaluator : public ::word_eval::AbstractWordEvaluator {
 public:
  NormalWordEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, word_eval::TildeEvaluator* tilde_ev, split::SplitContext* splitter, ui::ErrorFormatter* errfmt);
  virtual void CheckCircularDeps();
  virtual runtime_asdl::part_value_t* _EvalCommandSub(syntax_asdl::CommandSub* cs_part, bool quoted);
  virtual runtime_asdl::Piece* _EvalProcessSub(syntax_asdl::CommandSub* cs_part);

  vm::_Executor* shell_ex{};
  
  static constexpr uint32_t field_mask() {
    return ::word_eval::AbstractWordEvaluator::field_mask()
         | maskbit(offsetof(NormalWordEvaluator, shell_ex));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(NormalWordEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(NormalWordEvaluator)
};

extern BigStr* _DUMMY;
class CompletionWordEvaluator : public ::word_eval::AbstractWordEvaluator {
 public:
  CompletionWordEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, word_eval::TildeEvaluator* tilde_ev, split::SplitContext* splitter, ui::ErrorFormatter* errfmt);
  virtual void CheckCircularDeps();
  virtual runtime_asdl::part_value_t* _EvalCommandSub(syntax_asdl::CommandSub* cs_part, bool quoted);
  virtual runtime_asdl::Piece* _EvalProcessSub(syntax_asdl::CommandSub* cs_part);
  
  static constexpr uint32_t field_mask() {
    return ::word_eval::AbstractWordEvaluator::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(CompletionWordEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(CompletionWordEvaluator)
};


}  // declare namespace word_eval

namespace word_parse {  // declare

extern List<id_kind_asdl::Kind_t>* KINDS_THAT_END_WORDS;
class WordEmitter {
 public:
  WordEmitter();
  virtual syntax_asdl::word_t* ReadWord(types_asdl::lex_mode_t lex_mode);
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(WordEmitter));
  }

  DISALLOW_COPY_AND_ASSIGN(WordEmitter)
};

class WordParser : public ::word_parse::WordEmitter {
 public:
  WordParser(parse_lib::ParseContext* parse_ctx, lexer::Lexer* lexer, reader::_Reader* line_reader);
  void Init(types_asdl::lex_mode_t lex_mode);
  void Reset();
  void _GetToken();
  void _SetNext(types_asdl::lex_mode_t lex_mode);
  syntax_asdl::rhs_word_t* _ReadVarOpArg(types_asdl::lex_mode_t arg_lex_mode);
  syntax_asdl::CompoundWord* _ReadVarOpArg2(types_asdl::lex_mode_t arg_lex_mode, int eof_type, bool empty_ok);
  suffix_op::Slice* _ReadSliceVarOp();
  suffix_op::PatSub* _ReadPatSubVarOp();
  syntax_asdl::bracket_op_t* _ReadSubscript();
  syntax_asdl::BracedVarSub* _ParseVarOf();
  syntax_asdl::BracedVarSub* _ParseVarExpr(types_asdl::lex_mode_t arg_lex_mode, bool allow_query = false);
  word_part::ZshVarSub* _ReadZshVarSub(syntax_asdl::Token* left_token);
  Tuple2<syntax_asdl::BracedVarSub*, syntax_asdl::Token*> ReadBracedVarSub(syntax_asdl::Token* left_token);
  syntax_asdl::BracedVarSub* _ReadBracedVarSub(syntax_asdl::Token* left_token, bool d_quoted);
  syntax_asdl::SingleQuoted* _ReadSingleQuoted(syntax_asdl::Token* left_token, types_asdl::lex_mode_t lex_mode);
  syntax_asdl::Token* ReadSingleQuoted(types_asdl::lex_mode_t lex_mode, syntax_asdl::Token* left_token, List<syntax_asdl::Token*>* out_tokens, bool is_ysh_expr);
  syntax_asdl::word_part_t* _ReadDoubleQuotedLeftParts();
  syntax_asdl::CompoundWord* _ReadYshSingleQuoted(int left_id);
  syntax_asdl::word_part_t* _ReadUnquotedLeftParts(syntax_asdl::BoolParamBox* triple_out);
  word_part::ExtGlob* _ReadExtGlob();
  word_part::BashRegexGroup* _ReadBashRegexGroup();
  void _ReadLikeDQ(syntax_asdl::Token* left_token, bool is_ysh_expr, List<syntax_asdl::word_part_t*>* out_parts);
  syntax_asdl::DoubleQuoted* _ReadDoubleQuoted(syntax_asdl::Token* left_token);
  syntax_asdl::Token* ReadDoubleQuoted(syntax_asdl::Token* left_token, List<syntax_asdl::word_part_t*>* parts);
  syntax_asdl::CommandSub* _ReadCommandSub(int left_id, bool d_quoted = false);
  word_part::ExprSub* _ReadExprSub(types_asdl::lex_mode_t lex_mode);
  command::VarDecl* ParseVarDecl(syntax_asdl::Token* kw_token);
  command::Mutation* ParseMutation(syntax_asdl::Token* kw_token, cmd_parse::VarChecker* var_checker);
  syntax_asdl::expr_t* ParseBareDecl();
  syntax_asdl::expr_t* ParseYshExprForCommand();
  syntax_asdl::expr_t* ParseCommandExpr();
  void ParseProc(syntax_asdl::Proc* node);
  void ParseFunc(syntax_asdl::Func* node);
  Tuple2<syntax_asdl::pat_t*, syntax_asdl::Token*> ParseYshCasePattern();
  int NewlineOkForYshCase();
  syntax_asdl::arith_expr_t* _ReadArithExpr(int end_id);
  word_part::ArithSub* _ReadArithSub();
  Tuple2<syntax_asdl::arith_expr_t*, syntax_asdl::Token*> ReadDParen();
  void _NextNonSpace();
  command::ForExpr* ReadForExpression();
  syntax_asdl::word_part_t* _ReadArrayLiteral();
  syntax_asdl::ArgList* ParseProcCallArgs(int start_symbol);
  bool _MaybeReadWordPart(bool is_first, types_asdl::lex_mode_t lex_mode, List<syntax_asdl::word_part_t*>* parts);
  syntax_asdl::CompoundWord* _ReadCompoundWord(types_asdl::lex_mode_t lex_mode);
  syntax_asdl::CompoundWord* _ReadCompoundWord3(types_asdl::lex_mode_t lex_mode, int eof_type, bool empty_ok);
  syntax_asdl::word_t* _ReadArithWord();
  syntax_asdl::word_t* _ReadWord(types_asdl::lex_mode_t word_mode);
  syntax_asdl::BracedVarSub* ParseVarRef();
  int LookPastSpace();
  bool LookAheadFuncParens();
  virtual syntax_asdl::word_t* ReadWord(types_asdl::lex_mode_t word_mode);
  syntax_asdl::word_t* ReadArithWord();
  void ReadHereDocBody(List<syntax_asdl::word_part_t*>* parts);
  syntax_asdl::CompoundWord* ReadForPlugin();
  void EmitDocToken(bool b);
  void Multiline(bool b);

  tdop::TdopParser* a_parser{};
  alloc::Arena* arena{};
  syntax_asdl::word_t* buffered_word{};
  syntax_asdl::Token* cur_token{};
  bool emit_doc_token{};
  lexer::Lexer* lexer{};
  reader::_Reader* line_reader{};
  bool multiline{};
  int newline_state{};
  types_asdl::lex_mode_t next_lex_mode{};
  parse_lib::ParseContext* parse_ctx{};
  optview::Parse* parse_opts{};
  bool returned_newline{};
  id_kind_asdl::Kind_t token_kind{};
  int token_type{};
  
  static constexpr uint32_t field_mask() {
    return ::word_parse::WordEmitter::field_mask()
         | maskbit(offsetof(WordParser, a_parser))
         | maskbit(offsetof(WordParser, arena))
         | maskbit(offsetof(WordParser, buffered_word))
         | maskbit(offsetof(WordParser, cur_token))
         | maskbit(offsetof(WordParser, lexer))
         | maskbit(offsetof(WordParser, line_reader))
         | maskbit(offsetof(WordParser, parse_ctx))
         | maskbit(offsetof(WordParser, parse_opts));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(WordParser));
  }

  DISALLOW_COPY_AND_ASSIGN(WordParser)
};


}  // declare namespace word_parse

namespace parse {  // declare

extern int NT_OFFSET;
class ParseError {
 public:
  ParseError(BigStr* msg, int type_, syntax_asdl::Token* tok);
  BigStr* msg{};
  syntax_asdl::Token* tok{};
  int type{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(ParseError));
  }

  DISALLOW_COPY_AND_ASSIGN(ParseError)
};

class _StackItem {
 public:
  _StackItem(Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* dfa, int state, pnode::PNode* node);
  Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* dfa{};
  pnode::PNode* node{};
  int state{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(_StackItem));
  }

  DISALLOW_COPY_AND_ASSIGN(_StackItem)
};

class Parser {
 public:
  Parser(grammar::Grammar* grammar);
  void setup(int start, pnode::PNodeAllocator* pnode_alloc);
  bool addtoken(int typ, syntax_asdl::Token* opaque, int ilabel);
  void shift(int typ, syntax_asdl::Token* opaque, int newstate);
  void push(int typ, syntax_asdl::Token* opaque, Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* newdfa, int newstate);
  void pop();
  grammar::Grammar* grammar{};
  pnode::PNode* rootnode{};
  List<parse::_StackItem*>* stack{};
  pnode::PNodeAllocator* pnode_alloc{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(Parser));
  }

  DISALLOW_COPY_AND_ASSIGN(Parser)
};


}  // declare namespace parse

namespace os_path {  // declare

extern BigStr* extsep;
extern BigStr* sep;
BigStr* join(BigStr* s1, BigStr* s2);
Tuple2<BigStr*, BigStr*> split(BigStr* p);
Tuple2<BigStr*, BigStr*> _splitext(BigStr* p, BigStr* sep, BigStr* extsep);
Tuple2<BigStr*, BigStr*> splitext(BigStr* p);
BigStr* basename(BigStr* p);
BigStr* dirname(BigStr* p);
BigStr* normpath(BigStr* path);
bool isabs(BigStr* s);
BigStr* abspath(BigStr* path);

}  // declare namespace os_path

namespace fmt {  // declare

void Format(alloc::Arena* arena, syntax_asdl::command_t* node);

}  // declare namespace fmt

namespace ysh_ify {  // declare

class Cursor {
 public:
  Cursor(alloc::Arena* arena, mylib::Writer* f);
  void _PrintUntilSpid(int until_span_id);
  void _SkipUntilSpid(int next_span_id);
  void SkipUntil(syntax_asdl::Token* tok);
  void SkipPast(syntax_asdl::Token* tok);
  void PrintUntil(syntax_asdl::Token* tok);
  void PrintIncluding(syntax_asdl::Token* tok);
  void PrintUntilEnd();
  alloc::Arena* arena{};
  mylib::Writer* f{};
  int next_span_id{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(2, sizeof(Cursor));
  }

  DISALLOW_COPY_AND_ASSIGN(Cursor)
};

void LosslessCat(alloc::Arena* arena);
void PrintTokens(alloc::Arena* arena);
void Ysh_ify(alloc::Arena* arena, syntax_asdl::command_t* node);
runtime_asdl::word_style_t _GetRhsStyle(syntax_asdl::rhs_word_t* w);
class YshPrinter {
 public:
  YshPrinter(ysh_ify::Cursor* cursor, alloc::Arena* arena, mylib::Writer* f);
  void _DebugSpid(int spid);
  void End();
  void DoRedirect(syntax_asdl::Redir* node, Dict<BigStr*, bool>* local_symbols);
  void DoShAssignment(command::ShAssignment* node, bool at_top_level, Dict<BigStr*, bool>* local_symbols);
  void _DoSimple(command::Simple* node, Dict<BigStr*, bool>* local_symbols);
  void DoCommand(syntax_asdl::command_t* node, Dict<BigStr*, bool>* local_symbols, bool at_top_level = false);
  void DoRhsWord(syntax_asdl::rhs_word_t* node, Dict<BigStr*, bool>* local_symbols);
  void DoWordInCommand(syntax_asdl::word_t* node, Dict<BigStr*, bool>* local_symbols);
  void DoWordPart(syntax_asdl::word_part_t* node, Dict<BigStr*, bool>* local_symbols, bool quoted = false);
  ysh_ify::Cursor* cursor{};
  alloc::Arena* arena{};
  mylib::Writer* f{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(3, sizeof(YshPrinter));
  }

  DISALLOW_COPY_AND_ASSIGN(YshPrinter)
};


}  // declare namespace ysh_ify

namespace expr_eval {  // declare

value_asdl::value_t* LookupVar(state::Mem* mem, BigStr* var_name, runtime_asdl::scope_t which_scopes, syntax_asdl::loc_t* var_loc);
mops::BigInt _ConvertToInt(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc);
Tuple3<runtime_asdl::coerced_t, mops::BigInt, double> _ConvertToNumber(value_asdl::value_t* val);
Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double> _ConvertForBinaryOp(value_asdl::value_t* left, value_asdl::value_t* right);
class ExprEvaluator {
 public:
  ExprEvaluator(state::Mem* mem, state::MutableOpts* mutable_opts, Dict<int, Dict<BigStr*, vm::_Callable*>*>* methods, split::SplitContext* splitter, ui::ErrorFormatter* errfmt);
  void CheckCircularDeps();
  value_asdl::value_t* _LookupVar(BigStr* name, syntax_asdl::loc_t* var_loc);
  void EvalAugmented(value_asdl::y_lvalue_t* lval, value_asdl::value_t* rhs_val, syntax_asdl::Token* op, runtime_asdl::scope_t which_scopes);
  value_asdl::value_t* _EvalLeftLocalOrGlobal(syntax_asdl::expr_t* lhs, runtime_asdl::scope_t which_scopes);
  value_asdl::y_lvalue_t* _EvalLhsExpr(syntax_asdl::y_lhs_t* lhs, runtime_asdl::scope_t which_scopes);
  value_asdl::value_t* EvalExprClosure(value::Expr* expr_val, syntax_asdl::loc_t* blame_loc);
  value_asdl::value_t* EvalExpr(syntax_asdl::expr_t* node, syntax_asdl::loc_t* blame_loc);
  value_asdl::y_lvalue_t* EvalLhsExpr(syntax_asdl::y_lhs_t* lhs, runtime_asdl::scope_t which_scopes);
  runtime_asdl::part_value_t* EvalExprSub(word_part::ExprSub* part);
  value_asdl::value_t* PluginCall(value::Func* func_val, List<value_asdl::value_t*>* pos_args);
  value_asdl::value_t* CallConvertFunc(value_asdl::value_t* func_val, value_asdl::value_t* arg, syntax_asdl::Token* convert_tok, syntax_asdl::loc_t* call_loc);
  value_asdl::value_t* _CallMetaMethod(value_asdl::value_t* func_val, List<value_asdl::value_t*>* pos_args, syntax_asdl::loc_t* blame_loc);
  List<BigStr*>* SpliceValue(value_asdl::value_t* val, word_part::Splice* part);
  value_asdl::value_t* _EvalConst(expr::Const* node);
  value_asdl::value_t* _EvalUnary(expr::Unary* node);
  value_asdl::value_t* _ArithIntFloat(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::Token* op);
  value_asdl::value_t* _ArithIntOnly(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::Token* op);
  value_asdl::value_t* _Concat(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::Token* op);
  value_asdl::value_t* _EvalBinary(expr::Binary* node);
  bool _CompareNumeric(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::Token* op);
  value_asdl::value_t* _EvalCompare(expr::Compare* node);
  value_asdl::value_t* _CallFunc(value_asdl::value_t* to_call, typed_args::Reader* rd);
  value_asdl::value_t* _EvalFuncCall(expr::FuncCall* node);
  value_asdl::value_t* _EvalSubscript(value_asdl::value_t* obj, value_asdl::value_t* index, syntax_asdl::loc_t* blame_loc);
  value_asdl::value_t* _ChainedLookup(value_asdl::Obj* obj, value_asdl::Obj* current, BigStr* attr_name);
  value_asdl::value_t* _EvalDot(syntax_asdl::Attribute* node, value_asdl::value_t* val);
  value_asdl::value_t* _EvalRArrow(syntax_asdl::Attribute* node, value_asdl::value_t* val);
  value_asdl::value_t* _EvalAttribute(syntax_asdl::Attribute* node);
  value_asdl::value_t* _EvalExpr(syntax_asdl::expr_t* node);
  value::Eggex* EvalEggex(syntax_asdl::Eggex* node);
  vm::_Executor* shell_ex{};
  cmd_eval::CommandEvaluator* cmd_ev{};
  word_eval::AbstractWordEvaluator* word_ev{};
  state::Mem* mem{};
  state::MutableOpts* mutable_opts{};
  Dict<int, Dict<BigStr*, vm::_Callable*>*>* methods{};
  split::SplitContext* splitter{};
  ui::ErrorFormatter* errfmt{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(8, sizeof(ExprEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(ExprEvaluator)
};

class EggexEvaluator {
 public:
  EggexEvaluator(state::Mem* mem, BigStr* canonical_flags);
  value_asdl::value_t* _LookupVar(BigStr* name, syntax_asdl::loc_t* var_loc);
  void _EvalClassLiteralTerm(syntax_asdl::class_literal_term_t* term, List<syntax_asdl::char_class_term_t*>* out);
  syntax_asdl::re_t* EvalE(syntax_asdl::re_t* node);
  state::Mem* mem{};
  BigStr* canonical_flags{};
  List<value_asdl::value_t*>* convert_funcs{};
  List<syntax_asdl::Token*>* convert_toks{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(EggexEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(EggexEvaluator)
};


}  // declare namespace expr_eval

namespace expr_parse {  // declare

int _Classify(grammar::Grammar* gr, syntax_asdl::Token* tok);
extern Dict<int, int>* _OTHER_BALANCE;
syntax_asdl::Token* _PushYshTokens(parse_lib::ParseContext* parse_ctx, grammar::Grammar* gr, parse::Parser* p, lexer::Lexer* lex);
class ExprParser {
 public:
  ExprParser(parse_lib::ParseContext* parse_ctx, grammar::Grammar* gr);
  Tuple2<pnode::PNode*, syntax_asdl::Token*> Parse(lexer::Lexer* lexer, int start_symbol);
  parse_lib::ParseContext* parse_ctx{};
  grammar::Grammar* gr{};
  parse::Parser* push_parser{};
  pnode::PNodeAllocator* pnode_alloc{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(4, sizeof(ExprParser));
  }

  DISALLOW_COPY_AND_ASSIGN(ExprParser)
};

class ctx_PNodeAllocator {
 public:
  ctx_PNodeAllocator(expr_parse::ExprParser* ep);
  ~ctx_PNodeAllocator();
  expr_parse::ExprParser* expr_parser{};

  DISALLOW_COPY_AND_ASSIGN(ctx_PNodeAllocator)
};


}  // declare namespace expr_parse

namespace expr_to_ast {  // declare

extern Dict<BigStr*, BigStr*>* PERL_CLASSES;
extern List<BigStr*>* POSIX_CLASSES;
extern BigStr* RANGE_POINT_TOO_LONG;
extern BigStr* POS_ARG_MISPLACED;
extern int NT_OFFSET;
class Transformer {
 public:
  Transformer(grammar::Grammar* gr);
  syntax_asdl::expr_t* _LeftAssoc(pnode::PNode* p_node);
  syntax_asdl::expr_t* _Trailer(syntax_asdl::expr_t* base, pnode::PNode* p_trailer);
  Tuple2<syntax_asdl::expr_t*, syntax_asdl::expr_t*> _DictPair(pnode::PNode* p_node);
  expr::Dict* _Dict(pnode::PNode* parent, pnode::PNode* p_node);
  syntax_asdl::expr_t* _Tuple(pnode::PNode* parent);
  syntax_asdl::expr_t* _TestlistComp(pnode::PNode* parent, pnode::PNode* p_node, int id0);
  syntax_asdl::expr_t* _Atom(pnode::PNode* parent);
  syntax_asdl::NameType* _NameType(pnode::PNode* p_node);
  List<syntax_asdl::NameType*>* _NameTypeList(pnode::PNode* p_node);
  syntax_asdl::Comprehension* _CompFor(pnode::PNode* p_node);
  syntax_asdl::expr_t* _CompareChain(pnode::PNode* parent);
  syntax_asdl::expr_t* _Subscript(pnode::PNode* parent);
  syntax_asdl::expr_t* Expr(pnode::PNode* pnode);
  void _CheckLhs(syntax_asdl::expr_t* lhs);
  List<syntax_asdl::y_lhs_t*>* _LhsExprList(pnode::PNode* p_node);
  command::VarDecl* MakeVarDecl(pnode::PNode* p_node);
  command::Mutation* MakeMutation(pnode::PNode* p_node);
  syntax_asdl::EggexFlag* _EggexFlag(pnode::PNode* p_node);
  syntax_asdl::Eggex* _Eggex(pnode::PNode* p_node);
  syntax_asdl::pat_t* YshCasePattern(pnode::PNode* pnode);
  syntax_asdl::expr_t* _BlockArg(pnode::PNode* p_node);
  void _Argument(pnode::PNode* p_node, bool after_semi, syntax_asdl::ArgList* arglist);
  void _ArgGroup(pnode::PNode* p_node, bool after_semi, syntax_asdl::ArgList* arglist);
  void _ArgList(pnode::PNode* p_node, syntax_asdl::ArgList* arglist);
  void ProcCallArgs(pnode::PNode* pnode, syntax_asdl::ArgList* arglist);
  syntax_asdl::TypeExpr* _TypeExpr(pnode::PNode* pnode);
  syntax_asdl::Param* _Param(pnode::PNode* pnode);
  syntax_asdl::ParamGroup* _ParamGroup(pnode::PNode* p_node);
  syntax_asdl::proc_sig_t* Proc(pnode::PNode* p_node);
  void YshFunc(pnode::PNode* p_node, syntax_asdl::Func* out);
  syntax_asdl::CharCode* _RangeCharSingleQuoted(pnode::PNode* p_node);
  syntax_asdl::Token* _OtherRangeToken(pnode::PNode* p_node);
  syntax_asdl::class_literal_term_t* _NonRangeChars(pnode::PNode* p_node);
  syntax_asdl::class_literal_term_t* _ClassLiteralTerm(pnode::PNode* p_node);
  List<syntax_asdl::class_literal_term_t*>* _ClassLiteral(pnode::PNode* p_node);
  syntax_asdl::re_t* _NameInRegex(syntax_asdl::Token* negated_tok, syntax_asdl::Token* tok);
  syntax_asdl::class_literal_term_t* _NameInClass(syntax_asdl::Token* negated_tok, syntax_asdl::Token* tok);
  syntax_asdl::re_t* _ReAtom(pnode::PNode* p_atom);
  syntax_asdl::re_repeat_t* _RepeatOp(pnode::PNode* p_repeat);
  syntax_asdl::re_t* _ReAlt(pnode::PNode* p_node);
  syntax_asdl::re_t* _Regex(pnode::PNode* p_node);
  Dict<int, BigStr*>* number2symbol{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(1, sizeof(Transformer));
  }

  DISALLOW_COPY_AND_ASSIGN(Transformer)
};


}  // declare namespace expr_to_ast

namespace func_proc {  // declare

void _DisallowMutableDefault(value_asdl::value_t* val, syntax_asdl::loc_t* blame_loc);
List<value_asdl::value_t*>* _EvalPosDefaults(expr_eval::ExprEvaluator* expr_ev, List<syntax_asdl::Param*>* pos_params);
Dict<BigStr*, value_asdl::value_t*>* _EvalNamedDefaults(expr_eval::ExprEvaluator* expr_ev, List<syntax_asdl::Param*>* named_params);
Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*> EvalFuncDefaults(expr_eval::ExprEvaluator* expr_ev, syntax_asdl::Func* func);
value_asdl::ProcDefaults* EvalProcDefaults(expr_eval::ExprEvaluator* expr_ev, proc_sig::Closed* sig);
void _EvalPosArgs(expr_eval::ExprEvaluator* expr_ev, List<syntax_asdl::expr_t*>* exprs, List<value_asdl::value_t*>* pos_args);
Dict<BigStr*, value_asdl::value_t*>* _EvalNamedArgs(expr_eval::ExprEvaluator* expr_ev, List<syntax_asdl::NamedArg*>* named_exprs);
Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*> _EvalArgList(expr_eval::ExprEvaluator* expr_ev, syntax_asdl::ArgList* args, value_asdl::value_t* self_val = nullptr);
void EvalTypedArgsToProc(expr_eval::ExprEvaluator* expr_ev, Dict<BigStr*, runtime_asdl::Cell*>* current_frame, Dict<BigStr*, runtime_asdl::Cell*>* module_frame, state::MutableOpts* mutable_opts, command::Simple* node, runtime_asdl::ProcArgs* proc_args);
void _BindWords(BigStr* proc_name, syntax_asdl::ParamGroup* group, List<value_asdl::value_t*>* defaults, cmd_value::Argv* cmd_val, state::Mem* mem, syntax_asdl::loc_t* blame_loc);
void _BindTyped(BigStr* code_name, syntax_asdl::ParamGroup* group, List<value_asdl::value_t*>* defaults, List<value_asdl::value_t*>* pos_args, state::Mem* mem, syntax_asdl::loc_t* blame_loc);
void _BindNamed(BigStr* code_name, syntax_asdl::ParamGroup* group, Dict<BigStr*, value_asdl::value_t*>* defaults, Dict<BigStr*, value_asdl::value_t*>* named_args, state::Mem* mem, syntax_asdl::loc_t* blame_loc);
void _BindFuncArgs(value::Func* func, typed_args::Reader* rd, state::Mem* mem);
void BindProcArgs(value::Proc* proc, cmd_value::Argv* cmd_val, state::Mem* mem);
value_asdl::value_t* CallUserFunc(value::Func* func, typed_args::Reader* rd, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev);

}  // declare namespace func_proc

namespace regex_translate {  // declare

extern Dict<BigStr*, BigStr*>* PERL_CLASS;
extern int CH_RBRACKET;
extern int CH_BACKSLASH;
extern int CH_CARET;
extern int CH_HYPHEN;
extern int FLAG_RBRACKET;
extern int FLAG_BACKSLASH;
extern int FLAG_CARET;
extern int FLAG_HYPHEN;
void _CharCodeToEre(syntax_asdl::CharCode* term, List<BigStr*>* parts, List<int>* special_char_flags);
void _CharClassTermToEre(syntax_asdl::char_class_term_t* term, List<BigStr*>* parts, List<int>* special_char_flags);
void _AsPosixEre(syntax_asdl::re_t* node, List<BigStr*>* parts, List<BigStr*>* capture_names);
BigStr* AsPosixEre(value::Eggex* eggex);
BigStr* CanonicalFlags(List<syntax_asdl::EggexFlag*>* flags);
int LibcFlags(BigStr* canonical_flags);

}  // declare namespace regex_translate

namespace val_ops {  // declare

int ToInt(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc);
double ToFloat(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc);
BigStr* ToStr(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc);
List<value_asdl::value_t*>* ToList(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc);
Dict<BigStr*, value_asdl::value_t*>* ToDict(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc);
syntax_asdl::command_t* ToCommandFrag(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc);
BigStr* Stringify(value_asdl::value_t* val, syntax_asdl::loc_t* blame_loc, BigStr* op_desc);
List<BigStr*>* ToShellArray(value_asdl::value_t* val, syntax_asdl::loc_t* blame_loc, BigStr* prefix = S_Aoo);
class Iterator {
 public:
  Iterator();
  int Index();
  void Next();
  virtual value_asdl::value_t* FirstValue();
  virtual value_asdl::value_t* SecondValue();
  int i{};
  
  static constexpr uint32_t field_mask() {
    return kZeroMask;
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Iterator));
  }

  DISALLOW_COPY_AND_ASSIGN(Iterator)
};

class StdinIterator : public ::val_ops::Iterator {
 public:
  StdinIterator(syntax_asdl::loc_t* blame_loc);
  virtual value_asdl::value_t* FirstValue();

  syntax_asdl::loc_t* blame_loc{};
  mylib::LineReader* f{};
  
  static constexpr uint32_t field_mask() {
    return ::val_ops::Iterator::field_mask()
         | maskbit(offsetof(StdinIterator, blame_loc))
         | maskbit(offsetof(StdinIterator, f));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(StdinIterator));
  }

  DISALLOW_COPY_AND_ASSIGN(StdinIterator)
};

class ArrayIter : public ::val_ops::Iterator {
 public:
  ArrayIter(List<BigStr*>* strs);
  virtual value_asdl::value_t* FirstValue();

  int n{};
  List<BigStr*>* strs{};
  
  static constexpr uint32_t field_mask() {
    return ::val_ops::Iterator::field_mask()
         | maskbit(offsetof(ArrayIter, strs));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ArrayIter));
  }

  DISALLOW_COPY_AND_ASSIGN(ArrayIter)
};

class RangeIterator : public ::val_ops::Iterator {
 public:
  RangeIterator(value::Range* val);
  virtual value_asdl::value_t* FirstValue();

  value::Range* val{};
  
  static constexpr uint32_t field_mask() {
    return ::val_ops::Iterator::field_mask()
         | maskbit(offsetof(RangeIterator, val));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(RangeIterator));
  }

  DISALLOW_COPY_AND_ASSIGN(RangeIterator)
};

class ListIterator : public ::val_ops::Iterator {
 public:
  ListIterator(value::List* val);
  virtual value_asdl::value_t* FirstValue();

  int n{};
  value::List* val{};
  
  static constexpr uint32_t field_mask() {
    return ::val_ops::Iterator::field_mask()
         | maskbit(offsetof(ListIterator, val));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ListIterator));
  }

  DISALLOW_COPY_AND_ASSIGN(ListIterator)
};

class DictIterator : public ::val_ops::Iterator {
 public:
  DictIterator(value::Dict* val);
  virtual value_asdl::value_t* FirstValue();
  virtual value_asdl::value_t* SecondValue();

  List<BigStr*>* keys{};
  int n{};
  List<value_asdl::value_t*>* values{};
  
  static constexpr uint32_t field_mask() {
    return ::val_ops::Iterator::field_mask()
         | maskbit(offsetof(DictIterator, keys))
         | maskbit(offsetof(DictIterator, values));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(DictIterator));
  }

  DISALLOW_COPY_AND_ASSIGN(DictIterator)
};

bool ToBool(value_asdl::value_t* val);
bool ExactlyEqual(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::loc_t* blame_loc);
bool Contains(value_asdl::value_t* needle, value_asdl::value_t* haystack);
bool MatchRegex(value_asdl::value_t* left, value_asdl::value_t* right, state::Mem* mem);
value_asdl::value_t* IndexMetaMethod(value_asdl::Obj* obj);

}  // declare namespace val_ops

namespace bracket_osh {  // declare

class _StringWordEmitter : public ::word_parse::WordEmitter {
 public:
  _StringWordEmitter(cmd_value::Argv* cmd_val);
  virtual word::String* ReadWord(types_asdl::lex_mode_t unused_lex_mode);
  word::String* Read();
  BigStr* Peek(int offset);
  void Rewind(int offset);

  cmd_value::Argv* cmd_val{};
  int i{};
  int n{};
  
  static constexpr uint32_t field_mask() {
    return ::word_parse::WordEmitter::field_mask()
         | maskbit(offsetof(_StringWordEmitter, cmd_val));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_StringWordEmitter));
  }

  DISALLOW_COPY_AND_ASSIGN(_StringWordEmitter)
};

class _WordEvaluator : public ::word_eval::StringWordEvaluator {
 public:
  _WordEvaluator();
  virtual value::Str* EvalWordToString(syntax_asdl::word_t* w, int eval_flags = 0);
  
  static constexpr uint32_t field_mask() {
    return ::word_eval::StringWordEvaluator::field_mask();
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_WordEvaluator));
  }

  DISALLOW_COPY_AND_ASSIGN(_WordEvaluator)
};

syntax_asdl::bool_expr_t* _TwoArgs(bracket_osh::_StringWordEmitter* w_parser);
syntax_asdl::bool_expr_t* _ThreeArgs(bracket_osh::_StringWordEmitter* w_parser);
class Test : public ::vm::_Builtin {
 public:
  Test(bool need_right_bracket, optview::Exec* exec_opts, state::Mem* mem, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  ui::ErrorFormatter* errfmt{};
  optview::Exec* exec_opts{};
  state::Mem* mem{};
  bool need_right_bracket{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Test, errfmt))
         | maskbit(offsetof(Test, exec_opts))
         | maskbit(offsetof(Test, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Test));
  }

  DISALLOW_COPY_AND_ASSIGN(Test)
};


}  // declare namespace bracket_osh

namespace completion_osh {  // declare

class _FixedWordsAction : public ::completion::CompletionAction {
 public:
  _FixedWordsAction(List<BigStr*>* d);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  virtual void Print(mylib::BufWriter* f);

  List<BigStr*>* d{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(_FixedWordsAction, d));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_FixedWordsAction));
  }

  DISALLOW_COPY_AND_ASSIGN(_FixedWordsAction)
};

class _DynamicProcDictAction : public ::completion::CompletionAction {
 public:
  _DynamicProcDictAction(state::Procs* d);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  virtual void Print(mylib::BufWriter* f);

  state::Procs* d{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(_DynamicProcDictAction, d));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_DynamicProcDictAction));
  }

  DISALLOW_COPY_AND_ASSIGN(_DynamicProcDictAction)
};

class _DynamicStrDictAction : public ::completion::CompletionAction {
 public:
  _DynamicStrDictAction(Dict<BigStr*, BigStr*>* d);
  virtual void Matches(completion::Api* comp, List<BigStr*>* YIELD);
  virtual void Print(mylib::BufWriter* f);

  Dict<BigStr*, BigStr*>* d{};
  
  static constexpr uint32_t field_mask() {
    return ::completion::CompletionAction::field_mask()
         | maskbit(offsetof(_DynamicStrDictAction, d));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(_DynamicStrDictAction));
  }

  DISALLOW_COPY_AND_ASSIGN(_DynamicStrDictAction)
};

class SpecBuilder {
 public:
  SpecBuilder(cmd_eval::CommandEvaluator* cmd_ev, parse_lib::ParseContext* parse_ctx, word_eval::NormalWordEvaluator* word_ev, split::SplitContext* splitter, completion::Lookup* comp_lookup, Dict<BigStr*, BigStr*>* help_data, ui::ErrorFormatter* errfmt);
  completion::UserSpec* Build(List<BigStr*>* argv, args::_Attributes* attrs, Dict<BigStr*, bool>* base_opts);
  cmd_eval::CommandEvaluator* cmd_ev{};
  parse_lib::ParseContext* parse_ctx{};
  word_eval::NormalWordEvaluator* word_ev{};
  split::SplitContext* splitter{};
  completion::Lookup* comp_lookup{};
  Dict<BigStr*, BigStr*>* help_data{};
  List<BigStr*>* topic_list{};
  ui::ErrorFormatter* errfmt{};

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassScanned(8, sizeof(SpecBuilder));
  }

  DISALLOW_COPY_AND_ASSIGN(SpecBuilder)
};

class Complete : public ::vm::_Builtin {
 public:
  Complete(completion_osh::SpecBuilder* spec_builder, completion::Lookup* comp_lookup);
  virtual int Run(cmd_value::Argv* cmd_val);

  completion::Lookup* comp_lookup{};
  completion_osh::SpecBuilder* spec_builder{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(Complete, comp_lookup))
         | maskbit(offsetof(Complete, spec_builder));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(Complete));
  }

  DISALLOW_COPY_AND_ASSIGN(Complete)
};

class CompGen : public ::vm::_Builtin {
 public:
  CompGen(completion_osh::SpecBuilder* spec_builder);
  virtual int Run(cmd_value::Argv* cmd_val);

  completion_osh::SpecBuilder* spec_builder{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(CompGen, spec_builder));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(CompGen));
  }

  DISALLOW_COPY_AND_ASSIGN(CompGen)
};

class CompOpt : public ::vm::_Builtin {
 public:
  CompOpt(completion::OptionState* comp_state, ui::ErrorFormatter* errfmt);
  virtual int Run(cmd_value::Argv* cmd_val);

  completion::OptionState* comp_state{};
  ui::ErrorFormatter* errfmt{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(CompOpt, comp_state))
         | maskbit(offsetof(CompOpt, errfmt));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(CompOpt));
  }

  DISALLOW_COPY_AND_ASSIGN(CompOpt)
};

class CompAdjust : public ::vm::_Builtin {
 public:
  CompAdjust(state::Mem* mem);
  virtual int Run(cmd_value::Argv* cmd_val);

  state::Mem* mem{};
  
  static constexpr uint32_t field_mask() {
    return ::vm::_Builtin::field_mask()
         | maskbit(offsetof(CompAdjust, mem));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(CompAdjust));
  }

  DISALLOW_COPY_AND_ASSIGN(CompAdjust)
};


}  // declare namespace completion_osh

namespace shell {  // declare

void _InitDefaultCompletions(cmd_eval::CommandEvaluator* cmd_ev, completion_osh::Complete* complete_builtin, completion::Lookup* comp_lookup);
void _CompletionDemo(completion::Lookup* comp_lookup);
void SourceStartupFile(process::FdState* fd_state, BigStr* rc_path, BigStr* lang, parse_lib::ParseContext* parse_ctx, cmd_eval::CommandEvaluator* cmd_ev, ui::ErrorFormatter* errfmt);
class ShellOptHook : public ::state::OptHook {
 public:
  ShellOptHook(py_readline::Readline* readline);
  virtual bool OnChange(List<bool>* opt0_array, BigStr* opt_name, bool b);

  py_readline::Readline* readline{};
  
  static constexpr uint32_t field_mask() {
    return ::state::OptHook::field_mask()
         | maskbit(offsetof(ShellOptHook, readline));
  }

  static constexpr ObjHeader obj_header() {
    return ObjHeader::ClassFixed(field_mask(), sizeof(ShellOptHook));
  }

  DISALLOW_COPY_AND_ASSIGN(ShellOptHook)
};

void _AddBuiltinFunc(state::Mem* mem, BigStr* name, vm::_Callable* func);
Dict<int, vm::_AssignBuiltin*>* InitAssignmentBuiltins(state::Mem* mem, state::Procs* procs, optview::Exec* exec_opts, ui::ErrorFormatter* errfmt);
int Main(BigStr* lang, args::Reader* arg_r, Dict<BigStr*, BigStr*>* environ, bool login_shell, pyutil::_ResourceLoader* loader, py_readline::Readline* readline);

}  // declare namespace shell

namespace runtime {  // define

using hnode_asdl::hnode;
using hnode_asdl::color_t;
using hnode_asdl::color_e;
int NO_SPID = -1;

hnode::Record* NewRecord(BigStr* node_type) {
  StackRoot _root0(&node_type);

  return Alloc<hnode::Record>(node_type, S_ijB, S_hxb, Alloc<List<hnode_asdl::Field*>>(), nullptr);
}

hnode::Leaf* NewLeaf(BigStr* s, hnode_asdl::color_t e_color) {
  StackRoot _root0(&s);

  if (s == nullptr) {
    return Alloc<hnode::Leaf>(S_tci, color_e::OtherConst);
  }
  else {
    return Alloc<hnode::Leaf>(s, e_color);
  }
}

TraversalState::TraversalState() {
  this->seen = Alloc<Dict<int, bool>>();
  this->ref_count = Alloc<Dict<int, int>>();
}
BigStr* TRUE_STR = S_cor;
BigStr* FALSE_STR = S_gFh;

}  // define namespace runtime

namespace vm {  // define

using id_kind_asdl::Id;
using runtime_asdl::CommandStatus;
using runtime_asdl::StatusArray;
using runtime_asdl::flow_e;
using runtime_asdl::flow_t;
using syntax_asdl::Token;
using value_asdl::value_t;
using value_asdl::Obj;

IntControlFlow::IntControlFlow(syntax_asdl::Token* token, int arg) {
  this->token = token;
  this->arg = arg;
}

bool IntControlFlow::IsReturn() {
  return this->token->id == Id::ControlFlow_Return;
}

bool IntControlFlow::IsBreak() {
  return this->token->id == Id::ControlFlow_Break;
}

bool IntControlFlow::IsContinue() {
  return this->token->id == Id::ControlFlow_Continue;
}

int IntControlFlow::StatusCode() {
  return (this->arg & 255);
}

runtime_asdl::flow_t IntControlFlow::HandleLoop() {
  if (this->IsBreak()) {
    this->arg -= 1;
    if (this->arg == 0) {
      return flow_e::Break;
    }
  }
  else {
    if (this->IsContinue()) {
      this->arg -= 1;
      if (this->arg == 0) {
        return flow_e::Nothing;
      }
    }
  }
  return flow_e::Raise;
}

ValueControlFlow::ValueControlFlow(syntax_asdl::Token* token, value_asdl::value_t* value) {
  this->token = token;
  this->value = value;
}

void InitUnsafeArith(state::Mem* mem, word_eval::NormalWordEvaluator* word_ev, sh_expr_eval::UnsafeArith* unsafe_arith) {
  StackRoot _root0(&mem);
  StackRoot _root1(&word_ev);
  StackRoot _root2(&unsafe_arith);

  mem->unsafe_arith = unsafe_arith;
  word_ev->unsafe_arith = unsafe_arith;
}

void InitCircularDeps(sh_expr_eval::ArithEvaluator* arith_ev, sh_expr_eval::BoolEvaluator* bool_ev, expr_eval::ExprEvaluator* expr_ev, word_eval::NormalWordEvaluator* word_ev, cmd_eval::CommandEvaluator* cmd_ev, vm::_Executor* shell_ex, prompt::Evaluator* prompt_ev, value_asdl::Obj* global_io, dev::Tracer* tracer) {
  StackRoot _root0(&arith_ev);
  StackRoot _root1(&bool_ev);
  StackRoot _root2(&expr_ev);
  StackRoot _root3(&word_ev);
  StackRoot _root4(&cmd_ev);
  StackRoot _root5(&shell_ex);
  StackRoot _root6(&prompt_ev);
  StackRoot _root7(&global_io);
  StackRoot _root8(&tracer);

  arith_ev->word_ev = word_ev;
  bool_ev->word_ev = word_ev;
  if (expr_ev) {
    expr_ev->shell_ex = shell_ex;
    expr_ev->cmd_ev = cmd_ev;
    expr_ev->word_ev = word_ev;
  }
  word_ev->arith_ev = arith_ev;
  word_ev->expr_ev = expr_ev;
  word_ev->prompt_ev = prompt_ev;
  word_ev->shell_ex = shell_ex;
  cmd_ev->shell_ex = shell_ex;
  cmd_ev->arith_ev = arith_ev;
  cmd_ev->bool_ev = bool_ev;
  cmd_ev->expr_ev = expr_ev;
  cmd_ev->word_ev = word_ev;
  cmd_ev->tracer = tracer;
  shell_ex->cmd_ev = cmd_ev;
  prompt_ev->word_ev = word_ev;
  prompt_ev->expr_ev = expr_ev;
  prompt_ev->global_io = global_io;
  tracer->word_ev = word_ev;
  arith_ev->CheckCircularDeps();
  bool_ev->CheckCircularDeps();
  if (expr_ev) {
    expr_ev->CheckCircularDeps();
  }
  word_ev->CheckCircularDeps();
  cmd_ev->CheckCircularDeps();
  shell_ex->CheckCircularDeps();
  prompt_ev->CheckCircularDeps();
  tracer->CheckCircularDeps();
}

_Executor::_Executor() {
  this->cmd_ev = nullptr;
}

void _Executor::CheckCircularDeps() {
  ;  // pass
}

int _Executor::RunBuiltin(int builtin_id, cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  return 0;
}

int _Executor::RunSimpleCommand(cmd_value::Argv* cmd_val, runtime_asdl::CommandStatus* cmd_st, int run_flags) {
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&cmd_st);

  return 0;
}

int _Executor::RunBackgroundJob(syntax_asdl::command_t* node) {
  StackRoot _root0(&node);

  return 0;
}

void _Executor::RunPipeline(command::Pipeline* node, runtime_asdl::CommandStatus* status_out) {
  StackRoot _root0(&node);
  StackRoot _root1(&status_out);

  ;  // pass
}

int _Executor::RunSubshell(syntax_asdl::command_t* node) {
  StackRoot _root0(&node);

  return 0;
}

Tuple2<int, BigStr*> _Executor::CaptureStdout(syntax_asdl::command_t* node) {
  StackRoot _root0(&node);

  return Tuple2<int, BigStr*>(0, S_Aoo);
}

BigStr* _Executor::RunCommandSub(syntax_asdl::CommandSub* cs_part) {
  StackRoot _root0(&cs_part);

  return S_Aoo;
}

BigStr* _Executor::RunProcessSub(syntax_asdl::CommandSub* cs_part) {
  StackRoot _root0(&cs_part);

  return S_Aoo;
}

void _Executor::PushRedirects(List<runtime_asdl::RedirValue*>* redirects, List<IOError_OSError*>* err_out) {
  StackRoot _root0(&redirects);
  StackRoot _root1(&err_out);

  ;  // pass
}

void _Executor::PopRedirects(int num_redirects, List<IOError_OSError*>* err_out) {
  StackRoot _root0(&err_out);

  ;  // pass
}

void _Executor::PushProcessSub() {
  ;  // pass
}

void _Executor::PopProcessSub(runtime_asdl::StatusArray* compound_st) {
  StackRoot _root0(&compound_st);

  ;  // pass
}

_AssignBuiltin::_AssignBuiltin() {
  ;  // pass
}

int _AssignBuiltin::Run(cmd_value::Assign* cmd_val) {
  StackRoot _root0(&cmd_val);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

_Builtin::_Builtin() {
  ;  // pass
}

int _Builtin::Run(cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

_Callable::_Callable() {
  ;  // pass
}

value_asdl::value_t* _Callable::Call(typed_args::Reader* args) {
  StackRoot _root0(&args);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

ctx_Redirect::ctx_Redirect(vm::_Executor* shell_ex, int num_redirects, List<IOError_OSError*>* err_out) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->err_out)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->shell_ex)));
  this->shell_ex = shell_ex;
  this->num_redirects = num_redirects;
  this->err_out = err_out;
}

ctx_Redirect::~ctx_Redirect() {
  this->shell_ex->PopRedirects(this->num_redirects, this->err_out);
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_ProcessSub::ctx_ProcessSub(vm::_Executor* shell_ex, runtime_asdl::StatusArray* process_sub_status) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->process_sub_status)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->shell_ex)));
  shell_ex->PushProcessSub();
  this->shell_ex = shell_ex;
  this->process_sub_status = process_sub_status;
}

ctx_ProcessSub::~ctx_ProcessSub() {
  this->shell_ex->PopProcessSub(this->process_sub_status);
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_FlushStdout::ctx_FlushStdout(List<IOError_OSError*>* err_out) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->err_out)));
  this->err_out = err_out;
}

ctx_FlushStdout::~ctx_FlushStdout() {
  IOError_OSError* err = pyos::FlushStdout();
  if (err != nullptr) {
    this->err_out->append(err);
  }
  gHeap.PopRoot();
}

}  // define namespace vm

namespace format {  // define

using hnode_asdl::hnode;
using hnode_asdl::hnode_e;
using hnode_asdl::hnode_t;
using pretty_asdl::doc;
using pretty_asdl::doc_e;
using pretty_asdl::doc_t;
using pretty_asdl::MeasuredDoc;
using pretty_asdl::List_Measured;

int _HNodeCount(hnode_asdl::hnode_t* h) {
  hnode_asdl::hnode_t* UP_h = nullptr;
  int n;
  StackRoot _root0(&h);
  StackRoot _root1(&UP_h);

  UP_h = h;
  switch (h->tag()) {
    case hnode_e::AlreadySeen: {
      return 1;
    }
      break;
    case hnode_e::Leaf: {
      return 1;
    }
      break;
    case hnode_e::Array: {
      hnode::Array* h = static_cast<hnode::Array*>(UP_h);
      n = 1;
      for (ListIter<hnode_asdl::hnode_t*> it(h->children); !it.Done(); it.Next()) {
        hnode_asdl::hnode_t* child = it.Value();
        StackRoot _for(&child      );
        n += _HNodeCount(child);
      }
      return n;
    }
      break;
    case hnode_e::Record: {
      hnode::Record* h = static_cast<hnode::Record*>(UP_h);
      n = 1;
      for (ListIter<hnode_asdl::Field*> it(h->fields); !it.Done(); it.Next()) {
        hnode_asdl::Field* field = it.Value();
        StackRoot _for(&field      );
        n += _HNodeCount(field->val);
      }
      if (h->unnamed_fields != nullptr) {
        for (ListIter<hnode_asdl::hnode_t*> it(h->unnamed_fields); !it.Done(); it.Next()) {
          hnode_asdl::hnode_t* child = it.Value();
          StackRoot _for(&child        );
          n += _HNodeCount(child);
        }
      }
      return n;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

int _DocCount(pretty_asdl::doc_t* d) {
  pretty_asdl::doc_t* UP_d = nullptr;
  int n;
  StackRoot _root0(&d);
  StackRoot _root1(&UP_d);

  UP_d = d;
  switch (d->tag()) {
    case doc_e::Break: {
      return 1;
    }
      break;
    case doc_e::Text: {
      return 1;
    }
      break;
    case doc_e::Indent: {
      doc::Indent* d = static_cast<doc::Indent*>(UP_d);
      return (1 + _DocCount(d->mdoc->doc));
    }
      break;
    case doc_e::Group: {
      MeasuredDoc* d = static_cast<MeasuredDoc*>(UP_d);
      return (1 + _DocCount(d->doc));
    }
      break;
    case doc_e::Flat: {
      doc::Flat* d = static_cast<doc::Flat*>(UP_d);
      return (1 + _DocCount(d->mdoc->doc));
    }
      break;
    case doc_e::IfFlat: {
      doc::IfFlat* d = static_cast<doc::IfFlat*>(UP_d);
      return ((1 + _DocCount(d->flat_mdoc->doc)) + _DocCount(d->nonflat_mdoc->doc));
    }
      break;
    case doc_e::Concat: {
      List_Measured* d = static_cast<List_Measured*>(UP_d);
      n = 1;
      for (ListIter<pretty_asdl::MeasuredDoc*> it(d); !it.Done(); it.Next()) {
        pretty_asdl::MeasuredDoc* mdoc = it.Value();
        StackRoot _for(&mdoc      );
        n += _DocCount(mdoc->doc);
      }
      return n;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void _HNodePrettyPrint(bool perf_stats, bool doc_debug, hnode_asdl::hnode_t* node, mylib::Writer* f, int max_width) {
  pp_hnode::HNodeEncoder* enc = nullptr;
  pretty_asdl::MeasuredDoc* d = nullptr;
  hnode_asdl::hnode_t* p = nullptr;
  pretty::PrettyPrinter* printer = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&f);
  StackRoot _root2(&enc);
  StackRoot _root3(&d);
  StackRoot _root4(&p);
  StackRoot _root5(&printer);
  StackRoot _root6(&buf);

  mylib::MaybeCollect();
  if (perf_stats) {
    mylib::print_stderr(StrFormat("___ HNODE COUNT %d", _HNodeCount(node)));
    mylib::print_stderr(S_Aoo);
  }
  enc = Alloc<pp_hnode::HNodeEncoder>();
  enc->SetUseStyles(f->isatty());
  enc->SetIndent(2);
  d = enc->HNode(node);
  mylib::MaybeCollect();
  if (perf_stats) {
    if (doc_debug) {
      p = d->PrettyTree(false);
      _HNodePrettyPrint(perf_stats, false, p, f);
    }
    mylib::print_stderr(StrFormat("___ DOC COUNT %d", _DocCount(d)));
    mylib::print_stderr(S_Aoo);
  }
  printer = Alloc<pretty::PrettyPrinter>(max_width);
  buf = Alloc<mylib::BufWriter>();
  printer->PrintDoc(d, buf);
  f->write(buf->getvalue());
  f->write(S_nfs);
  mylib::MaybeCollect();
  if (perf_stats) {
    mylib::print_stderr(S_gfw);
    mylib::PrintGcStats();
    mylib::print_stderr(S_Aoo);
  }
}

void HNodePrettyPrint(hnode_asdl::hnode_t* node, mylib::Writer* f, int max_width) {
  StackRoot _root0(&node);
  StackRoot _root1(&f);

  _HNodePrettyPrint(false, true, node, f, max_width);
}

}  // define namespace format

namespace oils_for_unix {  // define

using syntax_asdl::loc;
using syntax_asdl::CompoundWord;
using mylib::print_stderr;

int CaperDispatch() {
  List<int>* fd_out = nullptr;
  BigStr* msg = nullptr;
  BigStr* command = nullptr;
  BigStr* arg = nullptr;
  StackRoot _root0(&fd_out);
  StackRoot _root1(&msg);
  StackRoot _root2(&command);
  StackRoot _root3(&arg);

  mylib::print_stderr(S_qsm);
  fd_out = Alloc<List<int>>();
  while (true) {
    try {
      msg = fanos::recv(0, fd_out);
    }
    catch (ValueError* e) {
      mylib::print_stderr(StrFormat("FANOS error: %s", e));
      fanos::send(1, StrFormat("ERROR %s", e));
      continue;
    }
    mylib::print_stderr(StrFormat("msg = %r", msg));
    Tuple2<BigStr*, BigStr*> tup0 = mylib::split_once(msg, S_yfw);
    command = tup0.at0();
    arg = tup0.at1();
    if (str_equals(command, S_Cqq)) {
      ;  // pass
    }
    else {
      if (str_equals(command, S_xFC)) {
        ;  // pass
      }
      else {
        if (str_equals(command, S_hhx)) {
          ;  // pass
        }
        else {
          if (str_equals(command, S_sqm_1)) {
            ;  // pass
          }
        }
      }
    }
  }
  return 0;
}

int AppBundleMain(List<BigStr*>* argv) {
  pyutil::_ResourceLoader* loader = nullptr;
  BigStr* b = nullptr;
  BigStr* main_name = nullptr;
  BigStr* ext = nullptr;
  syntax_asdl::CompoundWord* missing = nullptr;
  args::Reader* arg_r = nullptr;
  bool login_shell;
  BigStr* bundle = nullptr;
  BigStr* first_arg = nullptr;
  BigStr* applet = nullptr;
  py_readline::Readline* readline = nullptr;
  Dict<BigStr*, BigStr*>* environ = nullptr;
  StackRoot _root0(&argv);
  StackRoot _root1(&loader);
  StackRoot _root2(&b);
  StackRoot _root3(&main_name);
  StackRoot _root4(&ext);
  StackRoot _root5(&missing);
  StackRoot _root6(&arg_r);
  StackRoot _root7(&bundle);
  StackRoot _root8(&first_arg);
  StackRoot _root9(&applet);
  StackRoot _root10(&readline);
  StackRoot _root11(&environ);

  loader = pyutil::GetResourceLoader();
  b = os_path::basename(argv->at(0));
  Tuple2<BigStr*, BigStr*> tup1 = os_path::splitext(b);
  main_name = tup1.at0();
  ext = tup1.at1();
  missing = nullptr;
  arg_r = Alloc<args::Reader>(argv, list_repeat(missing, len(argv)));
  login_shell = false;
  // if not PYTHON
  {
    bundle = S_afu;
  }
  // endif MYCPP
  if ((str_equals(main_name, bundle) or (str_equals(main_name, S_fnD) and len(ext)))) {
    arg_r->Next();
    first_arg = arg_r->Peek();
    if (first_arg == nullptr) {
      throw Alloc<error::Usage>(S_zxn, loc::Missing);
    }
    if ((str_equals(first_arg, S_Cbm) || str_equals(first_arg, S_Aua))) {
      util::HelpFlag(loader, S_Eur, mylib::Stdout());
      return 0;
    }
    if ((str_equals(first_arg, S_ltE) || str_equals(first_arg, S_caA))) {
      util::VersionFlag(loader, mylib::Stdout());
      return 0;
    }
    if (str_equals(first_arg, S_iEq)) {
      return CaperDispatch();
    }
    applet = first_arg;
  }
  else {
    applet = main_name;
    if (applet->startswith(S_Bjq)) {
      login_shell = true;
      applet = applet->slice(1);
    }
  }
  readline = py_readline::MaybeGetReadline();
  environ = pyos::Environ();
  if ((applet->startswith(S_Awp) or str_equals(applet, S_fnD))) {
    return shell::Main(S_Awp, arg_r, environ, login_shell, loader, readline);
  }
  else {
    if ((applet->startswith(S_Ffb) or applet->endswith(S_wiE))) {
      return shell::Main(S_Ffb, arg_r, environ, login_shell, loader, readline);
    }
    else {
      if (str_equals(applet, S_FsF)) {
        return 0;
      }
      else {
        if (str_equals(applet, S_Ctn)) {
          return 1;
        }
        else {
          if (str_equals(applet, S_noe)) {
            // if not PYTHON
            {
              print_stderr(S_qjq);
              return 2;
            }
            // endif MYCPP
          }
          else {
            throw Alloc<error::Usage>(StrFormat("Invalid applet %r", applet), loc::Missing);
          }
        }
      }
    }
  }
}

int main(List<BigStr*>* argv) {
  StackRoot _root0(&argv);

  try {
    return AppBundleMain(argv);
  }
  catch (error::Usage* e) {
    mylib::print_stderr(StrFormat("oils: %s", e->msg));
    return 2;
  }
  catch (KeyboardInterrupt*) {
    print(S_Aoo);
    return 130;
  }
  catch (IOError_OSError* e) {
    print_stderr(StrFormat("oils I/O error (main): %s", posix::strerror(e->errno_)));
    return 2;
  }
}

}  // define namespace oils_for_unix

namespace assign_osh {  // define

using option_asdl::builtin_i;
using runtime_asdl::scope_e;
using runtime_asdl::cmd_value;
using runtime_asdl::AssignArg;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::LeftName;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::word_t;
using error::e_usage;
int _OTHER = 0;
int _READONLY = 1;
int _EXPORT = 2;

int _PrintVariables(state::Mem* mem, cmd_value::Assign* cmd_val, args::_Attributes* attrs, bool print_flags, int builtin) {
  Dict<BigStr*, value_asdl::value_t*>* flag = nullptr;
  value_asdl::value_t* tmp_g = nullptr;
  value_asdl::value_t* tmp_a = nullptr;
  value_asdl::value_t* tmp_A = nullptr;
  bool flag_g;
  bool flag_a;
  bool flag_A;
  value_asdl::value_t* tmp_n = nullptr;
  value_asdl::value_t* tmp_r = nullptr;
  value_asdl::value_t* tmp_x = nullptr;
  BigStr* flag_n = nullptr;
  BigStr* flag_r = nullptr;
  BigStr* flag_x = nullptr;
  runtime_asdl::scope_t which_scopes;
  bool print_all;
  Dict<BigStr*, runtime_asdl::Cell*>* cells = nullptr;
  List<BigStr*>* names = nullptr;
  BigStr* name = nullptr;
  BigStr* s = nullptr;
  BigStr* invalid = nullptr;
  int count;
  runtime_asdl::Cell* cell = nullptr;
  value_asdl::value_t* val = nullptr;
  List<BigStr*>* decl = nullptr;
  List<BigStr*>* flags = nullptr;
  value::Str* str_val = nullptr;
  value::BashArray* array_val = nullptr;
  value::BashAssoc* assoc_val = nullptr;
  value::SparseArray* sparse_val = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&cmd_val);
  StackRoot _root2(&attrs);
  StackRoot _root3(&flag);
  StackRoot _root4(&tmp_g);
  StackRoot _root5(&tmp_a);
  StackRoot _root6(&tmp_A);
  StackRoot _root7(&tmp_n);
  StackRoot _root8(&tmp_r);
  StackRoot _root9(&tmp_x);
  StackRoot _root10(&flag_n);
  StackRoot _root11(&flag_r);
  StackRoot _root12(&flag_x);
  StackRoot _root13(&cells);
  StackRoot _root14(&names);
  StackRoot _root15(&name);
  StackRoot _root16(&s);
  StackRoot _root17(&invalid);
  StackRoot _root18(&cell);
  StackRoot _root19(&val);
  StackRoot _root20(&decl);
  StackRoot _root21(&flags);
  StackRoot _root22(&str_val);
  StackRoot _root23(&array_val);
  StackRoot _root24(&assoc_val);
  StackRoot _root25(&sparse_val);

  flag = attrs->attrs;
  tmp_g = flag->get(S_ukF);
  tmp_a = flag->get(S_gCD);
  tmp_A = flag->get(S_nlt);
  flag_g = (tmp_g and tmp_g->tag() == value_e::Bool) ? static_cast<value::Bool*>(tmp_g)->b : false;
  flag_a = (tmp_a and tmp_a->tag() == value_e::Bool) ? static_cast<value::Bool*>(tmp_a)->b : false;
  flag_A = (tmp_A and tmp_A->tag() == value_e::Bool) ? static_cast<value::Bool*>(tmp_A)->b : false;
  tmp_n = flag->get(S_rob);
  tmp_r = flag->get(S_nAr_1);
  tmp_x = flag->get(S_rqD);
  flag_n = (tmp_n and tmp_n->tag() == value_e::Str) ? static_cast<value::Str*>(tmp_n)->s : nullptr;
  flag_r = (tmp_r and tmp_r->tag() == value_e::Str) ? static_cast<value::Str*>(tmp_r)->s : nullptr;
  flag_x = (tmp_x and tmp_x->tag() == value_e::Str) ? static_cast<value::Str*>(tmp_x)->s : nullptr;
  if (cmd_val->builtin_id == builtin_i::local) {
    if ((flag_g and !mem->IsGlobalScope())) {
      return 1;
    }
    which_scopes = scope_e::LocalOnly;
  }
  else {
    if (flag_g) {
      which_scopes = scope_e::GlobalOnly;
    }
    else {
      which_scopes = mem->ScopesForReading();
    }
  }
  if (len(cmd_val->pairs) == 0) {
    print_all = true;
    cells = mem->GetAllCells(which_scopes);
    names = sorted(cells);
  }
  else {
    print_all = false;
    names = Alloc<List<BigStr*>>();
    cells = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
    for (ListIter<runtime_asdl::AssignArg*> it(cmd_val->pairs); !it.Done(); it.Next()) {
      runtime_asdl::AssignArg* pair = it.Value();
      StackRoot _for(&pair    );
      name = pair->var_name;
      if ((pair->rval and pair->rval->tag() == value_e::Str)) {
        s = static_cast<value::Str*>(pair->rval)->s;
        invalid = StrFormat("%s=%s", name, s);
        names->append(invalid);
        cells->set(invalid, nullptr);
      }
      else {
        names->append(name);
        cells->set(name, mem->GetCell(name, which_scopes));
      }
    }
  }
  count = 0;
  for (ListIter<BigStr*> it(names); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    cell = cells->at(name);
    if (cell == nullptr) {
      continue;
    }
    val = cell->val;
    if (val->tag() == value_e::Undef) {
      continue;
    }
    if ((builtin == _READONLY and !cell->readonly)) {
      continue;
    }
    if ((builtin == _EXPORT and !cell->exported)) {
      continue;
    }
    if ((maybe_str_equals(flag_n, S_Bjq) and !cell->nameref)) {
      continue;
    }
    if ((maybe_str_equals(flag_n, S_jnE) and cell->nameref)) {
      continue;
    }
    if ((maybe_str_equals(flag_r, S_Bjq) and !cell->readonly)) {
      continue;
    }
    if ((maybe_str_equals(flag_r, S_jnE) and cell->readonly)) {
      continue;
    }
    if ((maybe_str_equals(flag_x, S_Bjq) and !cell->exported)) {
      continue;
    }
    if ((maybe_str_equals(flag_x, S_jnE) and cell->exported)) {
      continue;
    }
    if ((flag_a and (val->tag() != value_e::BashArray && val->tag() != value_e::SparseArray))) {
      continue;
    }
    if ((flag_A and val->tag() != value_e::BashAssoc)) {
      continue;
    }
    decl = Alloc<List<BigStr*>>();
    if (print_flags) {
      flags = Alloc<List<BigStr*>>();
      if (cell->nameref) {
        flags->append(S_rob);
      }
      if (cell->readonly) {
        flags->append(S_nAr_1);
      }
      if (cell->exported) {
        flags->append(S_rqD);
      }
      if ((val->tag() == value_e::BashArray || val->tag() == value_e::SparseArray)) {
        flags->append(S_gCD);
      }
      else {
        if (val->tag() == value_e::BashAssoc) {
          flags->append(S_nlt);
        }
      }
      if (len(flags) == 0) {
        flags->append(S_Bjq);
      }
      decl->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_enx, S_Aoo->join(flags), S_yfw, name}));
    }
    else {
      decl->append(name);
    }
    if (val->tag() == value_e::Str) {
      str_val = static_cast<value::Str*>(val);
      decl->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_bby, j8_lite::MaybeShellEncode(str_val->s)}));
    }
    else {
      if (val->tag() == value_e::BashArray) {
        array_val = static_cast<value::BashArray*>(val);
        decl->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_bby, bash_impl::BashArray_ToStrForShellPrint(array_val, name)}));
      }
      else {
        if (val->tag() == value_e::BashAssoc) {
          assoc_val = static_cast<value::BashAssoc*>(val);
          decl->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_bby, bash_impl::BashAssoc_ToStrForShellPrint(assoc_val)}));
        }
        else {
          if (val->tag() == value_e::SparseArray) {
            sparse_val = static_cast<value::SparseArray*>(val);
            decl->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_bby, bash_impl::SparseArray_ToStrForShellPrint(sparse_val)}));
          }
          else {
            ;  // pass
          }
        }
      }
    }
    print(S_Aoo->join(decl));
    count += 1;
  }
  if ((print_all or count == len(names))) {
    return 0;
  }
  else {
    return 1;
  }
}

void _ExportReadonly(state::Mem* mem, runtime_asdl::AssignArg* pair, int flags) {
  runtime_asdl::scope_t which_scopes;
  value_asdl::LeftName* lval = nullptr;
  value_asdl::value_t* old_val = nullptr;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&pair);
  StackRoot _root2(&lval);
  StackRoot _root3(&old_val);
  StackRoot _root4(&val);

  which_scopes = mem->ScopesForWriting();
  lval = Alloc<LeftName>(pair->var_name, pair->blame_word);
  if (pair->plus_eq) {
    old_val = sh_expr_eval::OldValue(lval, mem, nullptr);
    val = cmd_eval::PlusEquals(old_val, pair->rval);
  }
  else {
    val = pair->rval;
  }
  mem->SetNamed(lval, val, which_scopes, flags);
}

Export::Export(state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->errfmt = errfmt;
}

int Export::Run(cmd_value::Assign* cmd_val) {
  args::Reader* arg_r = nullptr;
  args::_Attributes* attrs = nullptr;
  arg_types::export_* arg = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&attrs);
  StackRoot _root3(&arg);

  if (this->mem->exec_opts->no_exported()) {
    this->errfmt->Print_(S_plg, cmd_val->arg_locs->at(0));
    return 1;
  }
  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  attrs = flag_util::Parse(S_xxu, arg_r);
  arg = Alloc<arg_types::export_>(attrs->attrs);
  if (arg->f) {
    e_usage(S_zDr_1, loc::Missing);
  }
  if ((arg->p or len(cmd_val->pairs) == 0)) {
    return _PrintVariables(this->mem, cmd_val, attrs, true, _EXPORT);
  }
  if (arg->n) {
    for (ListIter<runtime_asdl::AssignArg*> it(cmd_val->pairs); !it.Done(); it.Next()) {
      runtime_asdl::AssignArg* pair = it.Value();
      StackRoot _for(&pair    );
      if (pair->rval != nullptr) {
        e_usage(S_vbA, Alloc<loc::Word>(pair->blame_word));
      }
      this->mem->ClearFlag(pair->var_name, state::ClearExport);
    }
  }
  else {
    for (ListIter<runtime_asdl::AssignArg*> it(cmd_val->pairs); !it.Done(); it.Next()) {
      runtime_asdl::AssignArg* pair = it.Value();
      StackRoot _for(&pair    );
      _ExportReadonly(this->mem, pair, state::SetExport);
    }
  }
  return 0;
}

value_asdl::value_t* _ReconcileTypes(value_asdl::value_t* rval, bool flag_a, bool flag_A, syntax_asdl::word_t* blame_word) {
  value::BashArray* array_val = nullptr;
  Dict<BigStr*, BigStr*>* tmp = nullptr;
  StackRoot _root0(&rval);
  StackRoot _root1(&blame_word);
  StackRoot _root2(&array_val);
  StackRoot _root3(&tmp);

  if ((flag_a and (rval != nullptr and rval->tag() != value_e::BashArray))) {
    e_usage(S_rtt, Alloc<loc::Word>(blame_word));
  }
  if ((flag_A and rval)) {
    if (rval->tag() == value_e::BashArray) {
      array_val = static_cast<value::BashArray*>(rval);
      if (len(array_val->strs) == 0) {
        tmp = Alloc<Dict<BigStr*, BigStr*>>();
        return Alloc<value::BashAssoc>(tmp);
      }
    }
    if (rval->tag() != value_e::BashAssoc) {
      e_usage(S_xCr, Alloc<loc::Word>(blame_word));
    }
  }
  return rval;
}

Readonly::Readonly(state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->errfmt = errfmt;
}

int Readonly::Run(cmd_value::Assign* cmd_val) {
  args::Reader* arg_r = nullptr;
  args::_Attributes* attrs = nullptr;
  arg_types::readonly* arg = nullptr;
  value_asdl::value_t* rval = nullptr;
  Dict<BigStr*, BigStr*>* tmp = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&attrs);
  StackRoot _root3(&arg);
  StackRoot _root4(&rval);
  StackRoot _root5(&tmp);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  attrs = flag_util::Parse(S_ACj, arg_r);
  arg = Alloc<arg_types::readonly>(attrs->attrs);
  if ((arg->p or len(cmd_val->pairs) == 0)) {
    return _PrintVariables(this->mem, cmd_val, attrs, true, _READONLY);
  }
  for (ListIter<runtime_asdl::AssignArg*> it(cmd_val->pairs); !it.Done(); it.Next()) {
    runtime_asdl::AssignArg* pair = it.Value();
    StackRoot _for(&pair  );
    if (pair->rval == nullptr) {
      if (arg->a) {
        rval = Alloc<value::BashArray>(Alloc<List<BigStr*>>());
      }
      else {
        if (arg->A) {
          tmp = Alloc<Dict<BigStr*, BigStr*>>();
          rval = Alloc<value::BashAssoc>(tmp);
        }
        else {
          rval = nullptr;
        }
      }
    }
    else {
      rval = pair->rval;
    }
    rval = _ReconcileTypes(rval, arg->a, arg->A, pair->blame_word);
    _ExportReadonly(this->mem, pair, state::SetReadOnly);
  }
  return 0;
}

NewVar::NewVar(state::Mem* mem, state::Procs* procs, optview::Exec* exec_opts, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->procs = procs;
  this->exec_opts = exec_opts;
  this->errfmt = errfmt;
}

int NewVar::_PrintFuncs(List<BigStr*>* names) {
  int status;
  StackRoot _root0(&names);

  status = 0;
  for (ListIter<BigStr*> it(names); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    if (this->procs->GetShellFunc(name)) {
      print(name);
    }
    else {
      status = 1;
    }
  }
  return status;
}

int NewVar::Run(cmd_value::Assign* cmd_val) {
  args::Reader* arg_r = nullptr;
  args::_Attributes* attrs = nullptr;
  arg_types::new_var* arg = nullptr;
  int status;
  List<BigStr*>* names = nullptr;
  runtime_asdl::scope_t which_scopes;
  int flags;
  value_asdl::value_t* rval = nullptr;
  value_asdl::value_t* old_val = nullptr;
  Dict<BigStr*, BigStr*>* tmp = nullptr;
  value_asdl::LeftName* lval = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&attrs);
  StackRoot _root3(&arg);
  StackRoot _root4(&names);
  StackRoot _root5(&rval);
  StackRoot _root6(&old_val);
  StackRoot _root7(&tmp);
  StackRoot _root8(&lval);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  attrs = flag_util::Parse(S_CFg, arg_r);
  arg = Alloc<arg_types::new_var>(attrs->attrs);
  status = 0;
  if (arg->f) {
    names = arg_r->Rest();
    if (len(names)) {
      status = this->_PrintFuncs(names);
    }
    else {
      e_usage(S_yzA, loc::Missing);
    }
    return status;
  }
  if (arg->F) {
    names = arg_r->Rest();
    if (len(names)) {
      status = this->_PrintFuncs(names);
    }
    else {
      for (ListIter<BigStr*> it(this->procs->ShellFuncNames()); !it.Done(); it.Next()) {
        BigStr* func_name = it.Value();
        StackRoot _for(&func_name      );
        print(StrFormat("declare -f %s", func_name));
      }
    }
    return status;
  }
  if (arg->p) {
    return _PrintVariables(this->mem, cmd_val, attrs, true);
  }
  else {
    if (len(cmd_val->pairs) == 0) {
      return _PrintVariables(this->mem, cmd_val, attrs, false);
    }
  }
  if (!this->exec_opts->ignore_flags_not_impl()) {
    if (arg->i) {
      e_usage(S_chp, loc::Missing);
    }
    if ((arg->l or arg->u)) {
      this->errfmt->Print_(S_DBo, loc::Missing);
    }
  }
  if (cmd_val->builtin_id == builtin_i::local) {
    which_scopes = scope_e::LocalOnly;
  }
  else {
    if (arg->g) {
      which_scopes = scope_e::GlobalOnly;
    }
    else {
      which_scopes = scope_e::LocalOnly;
    }
  }
  flags = 0;
  if (maybe_str_equals(arg->x, S_Bjq)) {
    flags |= state::SetExport;
  }
  if (maybe_str_equals(arg->r, S_Bjq)) {
    flags |= state::SetReadOnly;
  }
  if (maybe_str_equals(arg->n, S_Bjq)) {
    flags |= state::SetNameref;
  }
  if (maybe_str_equals(arg->x, S_jnE)) {
    flags |= state::ClearExport;
  }
  if (maybe_str_equals(arg->r, S_jnE)) {
    flags |= state::ClearReadOnly;
  }
  if (maybe_str_equals(arg->n, S_jnE)) {
    flags |= state::ClearNameref;
  }
  for (ListIter<runtime_asdl::AssignArg*> it(cmd_val->pairs); !it.Done(); it.Next()) {
    runtime_asdl::AssignArg* pair = it.Value();
    StackRoot _for(&pair  );
    rval = pair->rval;
    if ((rval == nullptr and (arg->a or arg->A))) {
      old_val = this->mem->GetValue(pair->var_name);
      if (arg->a) {
        if ((old_val->tag() != value_e::BashArray && old_val->tag() != value_e::SparseArray)) {
          rval = Alloc<value::BashArray>(Alloc<List<BigStr*>>());
        }
      }
      else {
        if (arg->A) {
          if (old_val->tag() != value_e::BashAssoc) {
            tmp = Alloc<Dict<BigStr*, BigStr*>>();
            rval = Alloc<value::BashAssoc>(tmp);
          }
        }
      }
    }
    lval = Alloc<LeftName>(pair->var_name, pair->blame_word);
    if (pair->plus_eq) {
      old_val = sh_expr_eval::OldValue(lval, this->mem, nullptr);
      rval = cmd_eval::PlusEquals(old_val, pair->rval);
    }
    else {
      rval = _ReconcileTypes(rval, arg->a, arg->A, pair->blame_word);
    }
    this->mem->SetNamed(lval, rval, which_scopes, flags);
  }
  return status;
}

Unset::Unset(state::Mem* mem, state::Procs* procs, sh_expr_eval::UnsafeArith* unsafe_arith, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->procs = procs;
  this->unsafe_arith = unsafe_arith;
  this->errfmt = errfmt;
}

bool Unset::_UnsetVar(BigStr* arg, syntax_asdl::loc_t* location, bool proc_fallback) {
  value_asdl::sh_lvalue_t* lval = nullptr;
  bool found;
  BigStr* msg = nullptr;
  StackRoot _root0(&arg);
  StackRoot _root1(&location);
  StackRoot _root2(&lval);
  StackRoot _root3(&msg);

  lval = this->unsafe_arith->ParseLValue(arg, location);
  found = false;
  try {
    found = this->mem->Unset(lval, scope_e::Shopt);
  }
  catch (error::Runtime* e) {
    msg = e->UserErrorString();
    this->errfmt->Print_(msg, location);
    return false;
  }
  if ((proc_fallback and !found)) {
    this->procs->EraseShellFunc(arg);
  }
  return true;
}

int Unset::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::unset* arg = nullptr;
  List<BigStr*>* argv = nullptr;
  List<syntax_asdl::CompoundWord*>* arg_locs = nullptr;
  int i;
  syntax_asdl::CompoundWord* location = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&argv);
  StackRoot _root5(&arg_locs);
  StackRoot _root6(&location);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_FxC, cmd_val);
  attrs = tup0.at0();
  arg_r = tup0.at1();
  arg = Alloc<arg_types::unset>(attrs->attrs);
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup1 = arg_r->Rest2();
  argv = tup1.at0();
  arg_locs = tup1.at1();
  i = 0;
  for (ListIter<BigStr*> it(argv); !it.Done(); it.Next(), ++i) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    location = arg_locs->at(i);
    if (arg->f) {
      this->procs->EraseShellFunc(name);
    }
    else {
      if (arg->v) {
        if (!this->_UnsetVar(name, location, false)) {
          return 1;
        }
      }
      else {
        if (!this->_UnsetVar(name, location, true)) {
          return 1;
        }
      }
    }
  }
  return 0;
}

Shift::Shift(state::Mem* mem) {
  this->mem = mem;
}

int Shift::Run(cmd_value::Argv* cmd_val) {
  int num_args;
  int n;
  BigStr* arg = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg);

  num_args = (len(cmd_val->argv) - 1);
  if (num_args == 0) {
    n = 1;
  }
  else {
    if (num_args == 1) {
      arg = cmd_val->argv->at(1);
      try {
        n = to_int(arg);
      }
      catch (ValueError*) {
        e_usage(StrFormat("Invalid shift argument %r", arg), loc::Missing);
      }
    }
    else {
      e_usage(S_sAk, loc::Missing);
    }
  }
  return this->mem->Shift(n);
}

}  // define namespace assign_osh

namespace completion_ysh {  // define

using syntax_asdl::loc;
using error::e_usage;

CompExport::CompExport(completion::RootCompleter* root_comp) {
  this->root_comp = root_comp;
}

int CompExport::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  args::_Attributes* attrs = nullptr;
  arg_types::compexport* arg = nullptr;
  int arg_begin;
  int arg_end;
  int begin;
  int end;
  completion::Api* comp = nullptr;
  List<BigStr*>* comp_matches = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&attrs);
  StackRoot _root3(&arg);
  StackRoot _root4(&comp);
  StackRoot _root5(&comp_matches);
  StackRoot _root6(&buf);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  attrs = flag_util::ParseMore(S_jgm, arg_r);
  arg = Alloc<arg_types::compexport>(attrs->attrs);
  if (arg->c == nullptr) {
    e_usage(S_Ecv, loc::Missing);
  }
  arg_begin = mops::BigTruncate(arg->begin);
  arg_end = mops::BigTruncate(arg->end);
  begin = arg_begin == -1 ? 0 : arg_begin;
  end = arg_end == -1 ? len(arg->c) : arg_end;
  comp = Alloc<completion::Api>(arg->c, begin, end);
  List<BigStr*> YIELD_it;
  this->root_comp->Matches(comp, &YIELD_it);
  ListIter<BigStr*> it(&YIELD_it);
  comp_matches = list(it);
  comp_matches->reverse();
  if (maybe_str_equals(arg->format, S_mfD)) {
    buf = Alloc<mylib::BufWriter>();
    for (ListIter<BigStr*> it(comp_matches); !it.Done(); it.Next()) {
      BigStr* m = it.Value();
      StackRoot _for(&m    );
      j8::EncodeString(m, buf);
      print(buf->getvalue());
      buf->clear();
    }
  }
  else {
    if (maybe_str_equals(arg->format, S_vbp)) {
      mylib::print_stderr(S_wqm);
    }
    else {
      assert(0);  // AssertionError
    }
  }
  return 0;
}

}  // define namespace completion_ysh

namespace dirs_osh {  // define

using runtime_asdl::cmd_value;
using error::e_usage;

DirStack::DirStack() {
  this->stack = Alloc<List<BigStr*>>();
  this->Reset();
}

void DirStack::Reset() {
  this->stack->clear();
  this->stack->append(posix::getcwd());
}

void DirStack::Replace(BigStr* d) {
  StackRoot _root0(&d);

  this->stack->set(-1, d);
}

void DirStack::Push(BigStr* entry) {
  StackRoot _root0(&entry);

  this->stack->append(entry);
}

BigStr* DirStack::Pop() {
  if (len(this->stack) <= 1) {
    return nullptr;
  }
  this->stack->pop();
  return this->stack->at(-1);
}

List<BigStr*>* DirStack::Iter() {
  List<BigStr*>* ret = nullptr;
  StackRoot _root0(&ret);

  ret = Alloc<List<BigStr*>>();
  ret->extend(this->stack);
  ret->reverse();
  return ret;
}

ctx_CdBlock::ctx_CdBlock(dirs_osh::DirStack* dir_stack, BigStr* dest_dir, state::Mem* mem, ui::ErrorFormatter* errfmt, List<bool>* out_errs) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->dir_stack)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->errfmt)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->out_errs)));
  dir_stack->Push(dest_dir);
  this->dir_stack = dir_stack;
  this->mem = mem;
  this->errfmt = errfmt;
  this->out_errs = out_errs;
}

ctx_CdBlock::~ctx_CdBlock() {
  _PopDirStack(S_dyr, this->mem, this->dir_stack, this->errfmt, this->out_errs);
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
}

Cd::Cd(state::Mem* mem, dirs_osh::DirStack* dir_stack, cmd_eval::CommandEvaluator* cmd_ev, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->dir_stack = dir_stack;
  this->cmd_ev = cmd_ev;
  this->errfmt = errfmt;
}

int Cd::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::cd* arg = nullptr;
  syntax_asdl::command_t* cmd_frag = nullptr;
  BigStr* dest_dir = nullptr;
  syntax_asdl::loc_t* arg_loc = nullptr;
  BigStr* extra = nullptr;
  syntax_asdl::loc_t* extra_loc = nullptr;
  BigStr* old_pwd = nullptr;
  BigStr* abspath = nullptr;
  BigStr* real_dest_dir = nullptr;
  int err_num;
  List<bool>* out_errs = nullptr;
  int unused;
  (void)unused;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&cmd_frag);
  StackRoot _root5(&dest_dir);
  StackRoot _root6(&arg_loc);
  StackRoot _root7(&extra);
  StackRoot _root8(&extra_loc);
  StackRoot _root9(&old_pwd);
  StackRoot _root10(&abspath);
  StackRoot _root11(&real_dest_dir);
  StackRoot _root12(&out_errs);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_dyr, cmd_val, true);
  attrs = tup0.at0();
  arg_r = tup0.at1();
  arg = Alloc<arg_types::cd>(attrs->attrs);
  cmd_frag = typed_args::OptionalBlockAsFrag(cmd_val);
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup1 = arg_r->Peek2();
  dest_dir = tup1.at0();
  arg_loc = tup1.at1();
  if (dest_dir == nullptr) {
    if (cmd_frag) {
      throw Alloc<error::Usage>(S_kha, cmd_val->arg_locs->at(0));
    }
    else {
      dest_dir = this->mem->env_config->Get(S_xlm);
      if (dest_dir == nullptr) {
        this->errfmt->Print_(S_wxv_1);
        return 1;
      }
    }
  }
  arg_r->Next();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup2 = arg_r->Peek2();
  extra = tup2.at0();
  extra_loc = tup2.at1();
  if (extra != nullptr) {
    throw Alloc<error::Usage>(S_sAk, extra_loc);
  }
  if (str_equals(dest_dir, S_Bjq)) {
    try {
      dest_dir = state::GetString(this->mem, S_FAo);
      print(dest_dir);
    }
    catch (error::Runtime* e) {
      this->errfmt->Print_(e->UserErrorString());
      return 1;
    }
  }
  old_pwd = this->mem->pwd;
  abspath = os_path::join(old_pwd, dest_dir);
  if (arg->P) {
    real_dest_dir = libc::realpath(abspath);
  }
  else {
    real_dest_dir = os_path::normpath(abspath);
  }
  err_num = pyos::Chdir(real_dest_dir);
  if (err_num != 0) {
    this->errfmt->Print_(StrFormat("cd %r: %s", real_dest_dir, posix::strerror(err_num)), arg_loc);
    return 1;
  }
  state::ExportGlobalString(this->mem, S_xxp, real_dest_dir);
  this->mem->SetPwd(real_dest_dir);
  if (cmd_frag) {
    out_errs = Alloc<List<bool>>();
    {  // with
      ctx_CdBlock ctx{this->dir_stack, real_dest_dir, this->mem, this->errfmt, out_errs};

      unused = this->cmd_ev->EvalCommandFrag(cmd_frag);
    }
    if (len(out_errs)) {
      return 1;
    }
  }
  else {
    state::ExportGlobalString(this->mem, S_FAo, old_pwd);
    this->dir_stack->Replace(real_dest_dir);
  }
  return 0;
}
int WITH_LINE_NUMBERS = 1;
int WITHOUT_LINE_NUMBERS = 2;
int SINGLE_LINE = 3;

void _PrintDirStack(dirs_osh::DirStack* dir_stack, int style, BigStr* home_dir) {
  int i;
  List<BigStr*>* parts = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&dir_stack);
  StackRoot _root1(&home_dir);
  StackRoot _root2(&parts);
  StackRoot _root3(&s);

  if (style == WITH_LINE_NUMBERS) {
    i = 0;
    for (ListIter<BigStr*> it(dir_stack->Iter()); !it.Done(); it.Next(), ++i) {
      BigStr* entry = it.Value();
      StackRoot _for(&entry    );
      print(StrFormat("%2d  %s", i, ui::PrettyDir(entry, home_dir)));
    }
  }
  else {
    if (style == WITHOUT_LINE_NUMBERS) {
      for (ListIter<BigStr*> it(dir_stack->Iter()); !it.Done(); it.Next()) {
        BigStr* entry = it.Value();
        StackRoot _for(&entry      );
        print(ui::PrettyDir(entry, home_dir));
      }
    }
    else {
      if (style == SINGLE_LINE) {
        parts = Alloc<List<BigStr*>>();
        for (ListIter<BigStr*> it(dir_stack->Iter()); !it.Done(); it.Next()) {
          BigStr* entry = it.Value();
          parts->append(ui::PrettyDir(entry, home_dir));
        }
        s = S_yfw->join(parts);
        print(s);
      }
    }
  }
}

Pushd::Pushd(state::Mem* mem, dirs_osh::DirStack* dir_stack, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->dir_stack = dir_stack;
  this->errfmt = errfmt;
}

int Pushd::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* dir_arg = nullptr;
  syntax_asdl::loc_t* dir_arg_loc = nullptr;
  BigStr* extra = nullptr;
  syntax_asdl::loc_t* extra_loc = nullptr;
  BigStr* dest_dir = nullptr;
  int err_num;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&dir_arg);
  StackRoot _root3(&dir_arg_loc);
  StackRoot _root4(&extra);
  StackRoot _root5(&extra_loc);
  StackRoot _root6(&dest_dir);

  Tuple2<args::_Attributes*, args::Reader*> tup3 = flag_util::ParseCmdVal(S_kog, cmd_val);
  arg_r = tup3.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup4 = arg_r->Peek2();
  dir_arg = tup4.at0();
  dir_arg_loc = tup4.at1();
  if (dir_arg == nullptr) {
    this->errfmt->Print_(S_CFz);
    return 1;
  }
  arg_r->Next();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup5 = arg_r->Peek2();
  extra = tup5.at0();
  extra_loc = tup5.at1();
  if (extra != nullptr) {
    e_usage(S_sAk, extra_loc);
  }
  dest_dir = os_path::abspath(dir_arg);
  err_num = pyos::Chdir(dest_dir);
  if (err_num != 0) {
    this->errfmt->Print_(StrFormat("pushd: %r: %s", dest_dir, posix::strerror(err_num)), dir_arg_loc);
    return 1;
  }
  this->dir_stack->Push(dest_dir);
  _PrintDirStack(this->dir_stack, SINGLE_LINE, state::MaybeString(this->mem, S_xlm));
  state::ExportGlobalString(this->mem, S_xxp, dest_dir);
  this->mem->SetPwd(dest_dir);
  return 0;
}

bool _PopDirStack(BigStr* label, state::Mem* mem, dirs_osh::DirStack* dir_stack, ui::ErrorFormatter* errfmt, List<bool>* out_errs) {
  BigStr* dest_dir = nullptr;
  int err_num;
  StackRoot _root0(&label);
  StackRoot _root1(&mem);
  StackRoot _root2(&dir_stack);
  StackRoot _root3(&errfmt);
  StackRoot _root4(&out_errs);
  StackRoot _root5(&dest_dir);

  dest_dir = dir_stack->Pop();
  if (dest_dir == nullptr) {
    errfmt->Print_(StrFormat("%s: directory stack is empty", label));
    out_errs->append(true);
    return false;
  }
  err_num = pyos::Chdir(dest_dir);
  if (err_num != 0) {
    errfmt->Print_(StrFormat("%s: %r: %s", label, dest_dir, posix::strerror(err_num)));
    out_errs->append(true);
    return false;
  }
  state::SetGlobalString(mem, S_xxp, dest_dir);
  mem->SetPwd(dest_dir);
  return true;
}

Popd::Popd(state::Mem* mem, dirs_osh::DirStack* dir_stack, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->dir_stack = dir_stack;
  this->errfmt = errfmt;
}

int Popd::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* extra = nullptr;
  syntax_asdl::loc_t* extra_loc = nullptr;
  List<bool>* out_errs = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&extra);
  StackRoot _root3(&extra_loc);
  StackRoot _root4(&out_errs);

  Tuple2<args::_Attributes*, args::Reader*> tup6 = flag_util::ParseCmdVal(S_kog, cmd_val);
  arg_r = tup6.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup7 = arg_r->Peek2();
  extra = tup7.at0();
  extra_loc = tup7.at1();
  if (extra != nullptr) {
    e_usage(S_Ezs, extra_loc);
  }
  out_errs = Alloc<List<bool>>();
  _PopDirStack(S_Dnb, this->mem, this->dir_stack, this->errfmt, out_errs);
  if (len(out_errs)) {
    return 1;
  }
  _PrintDirStack(this->dir_stack, SINGLE_LINE, state::MaybeString(this->mem, S_xlm));
  return 0;
}

Dirs::Dirs(state::Mem* mem, dirs_osh::DirStack* dir_stack, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->dir_stack = dir_stack;
  this->errfmt = errfmt;
}

int Dirs::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::dirs* arg = nullptr;
  BigStr* home_dir = nullptr;
  int style;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&home_dir);

  Tuple2<args::_Attributes*, args::Reader*> tup8 = flag_util::ParseCmdVal(S_nAr, cmd_val);
  attrs = tup8.at0();
  arg_r = tup8.at1();
  arg = Alloc<arg_types::dirs>(attrs->attrs);
  home_dir = state::MaybeString(this->mem, S_xlm);
  style = SINGLE_LINE;
  if (arg->l) {
    home_dir = nullptr;
  }
  if (arg->c) {
    this->dir_stack->Reset();
    return 0;
  }
  else {
    if (arg->v) {
      style = WITH_LINE_NUMBERS;
    }
    else {
      if (arg->p) {
        style = WITHOUT_LINE_NUMBERS;
      }
    }
  }
  _PrintDirStack(this->dir_stack, style, home_dir);
  return 0;
}

Pwd::Pwd(state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->errfmt = errfmt;
}

int Pwd::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::pwd* arg = nullptr;
  BigStr* pwd = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&pwd);

  Tuple2<args::_Attributes*, args::Reader*> tup9 = flag_util::ParseCmdVal(S_xrE, cmd_val);
  attrs = tup9.at0();
  arg_r = tup9.at1();
  arg = Alloc<arg_types::pwd>(attrs->attrs);
  pwd = this->mem->pwd;
  if (arg->P) {
    pwd = libc::realpath(pwd);
  }
  print(pwd);
  return 0;
}

}  // define namespace dirs_osh

namespace error_ysh {  // define

using option_asdl::option_i;
using id_kind_asdl::Id;
using runtime_asdl::cmd_value;
using runtime_asdl::CommandStatus;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::expr;
using syntax_asdl::expr_e;
using value_asdl::value;
using value_asdl::value_e;
using error::e_die_status;
using error::e_usage;

ctx_Try::ctx_Try(state::MutableOpts* mutable_opts) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mutable_opts)));
  mutable_opts->Push(option_i::errexit, true);
  this->mutable_opts = mutable_opts;
}

ctx_Try::~ctx_Try() {
  this->mutable_opts->Pop(option_i::errexit);
  gHeap.PopRoot();
}

Try::Try(state::MutableOpts* mutable_opts, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev, vm::_Executor* shell_ex, ui::ErrorFormatter* errfmt) {
  this->mutable_opts = mutable_opts;
  this->mem = mem;
  this->shell_ex = shell_ex;
  this->cmd_ev = cmd_ev;
  this->errfmt = errfmt;
}

int Try::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  typed_args::Reader* rd = nullptr;
  syntax_asdl::command_t* cmd = nullptr;
  value::Dict* error_dict = nullptr;
  int status;
  int unused;
  (void)unused;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&rd);
  StackRoot _root3(&cmd);
  StackRoot _root4(&error_dict);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_zfb_1, cmd_val, true);
  arg_r = tup0.at1();
  rd = typed_args::ReaderForProc(cmd_val);
  cmd = rd->RequiredBlockAsFrag();
  rd->Done();
  error_dict = nullptr;
  status = 0;
  try {
    {  // with
      ctx_Try ctx{this->mutable_opts};

      unused = this->cmd_ev->EvalCommandFrag(cmd);
    }
  }
  catch (error::Expr* e) {
    status = e->ExitStatus();
  }
  catch (error::ErrExit* e) {
    status = e->ExitStatus();
  }
  catch (error::Structured* e) {
    status = e->ExitStatus();
    error_dict = e->ToDict();
  }
  if (error_dict == nullptr) {
    error_dict = Alloc<value::Dict>(Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_gFE}, std::initializer_list<value_asdl::value_t*>{num::ToBig(status)}));
  }
  this->mem->SetTryError(error_dict);
  this->mem->SetTryStatus(status);
  return 0;
}

Failed::Failed(state::Mem* mem) {
  this->mem = mem;
}

int Failed::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  value::Dict* err = nullptr;
  value_asdl::value_t* code = nullptr;
  value_asdl::value_t* UP_code = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&err);
  StackRoot _root3(&code);
  StackRoot _root4(&UP_code);

  Tuple2<args::_Attributes*, args::Reader*> tup1 = flag_util::ParseCmdVal(S_FAx, cmd_val);
  arg_r = tup1.at1();
  arg_r->Done();
  err = this->mem->TryError();
  code = err->d->get(S_gFE);
  if (code == nullptr) {
    return 1;
  }
  UP_code = code;
  switch (code->tag()) {
    case value_e::Int: {
      value::Int* code = static_cast<value::Int*>(UP_code);
      return mops::Equal(code->i, mops::ZERO) ? 1 : 0;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

Error::Error() {
  ;  // pass
}

int Error::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* message = nullptr;
  typed_args::Reader* rd = nullptr;
  int status;
  Dict<BigStr*, value_asdl::value_t*>* properties = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&message);
  StackRoot _root3(&rd);
  StackRoot _root4(&properties);

  Tuple2<args::_Attributes*, args::Reader*> tup2 = flag_util::ParseCmdVal(S_riE, cmd_val, true);
  arg_r = tup2.at1();
  message = arg_r->Peek();
  if (message == nullptr) {
    throw Alloc<error::Usage>(S_CBb, cmd_val->arg_locs->at(0));
  }
  rd = typed_args::ReaderForProc(cmd_val);
  status = mops::BigTruncate(rd->NamedInt(S_gFE, 10));
  properties = rd->RestNamed();
  rd->Done();
  if (status == 0) {
    throw Alloc<error::Usage>(S_lla, cmd_val->arg_locs->at(0));
  }
  throw Alloc<error::Structured>(status, message, cmd_val->arg_locs->at(0), properties);
}

BoolStatus::BoolStatus(vm::_Executor* shell_ex, ui::ErrorFormatter* errfmt) {
  this->shell_ex = shell_ex;
  this->errfmt = errfmt;
}

int BoolStatus::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  List<BigStr*>* argv = nullptr;
  List<syntax_asdl::CompoundWord*>* locs = nullptr;
  cmd_value::Argv* cmd_val2 = nullptr;
  runtime_asdl::CommandStatus* cmd_st = nullptr;
  int run_flags;
  int status;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&argv);
  StackRoot _root3(&locs);
  StackRoot _root4(&cmd_val2);
  StackRoot _root5(&cmd_st);

  Tuple2<args::_Attributes*, args::Reader*> tup3 = flag_util::ParseCmdVal(S_ocd, cmd_val);
  arg_r = tup3.at1();
  if (arg_r->Peek() == nullptr) {
    e_usage(S_top, loc::Missing);
  }
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup4 = arg_r->Rest2();
  argv = tup4.at0();
  locs = tup4.at1();
  cmd_val2 = Alloc<cmd_value::Argv>(argv, locs, cmd_val->is_last_cmd, cmd_val->self_obj, cmd_val->proc_args);
  cmd_st = CommandStatus::CreateNull(true);
  run_flags = cmd_val->is_last_cmd ? executor::IS_LAST_CMD : 0;
  status = this->shell_ex->RunSimpleCommand(cmd_val2, cmd_st, run_flags);
  if ((status != 0 && status != 1)) {
    e_die_status(status, StrFormat("boolstatus expected status 0 or 1, got %d", status), locs->at(0));
  }
  return status;
}

Assert::Assert(expr_eval::ExprEvaluator* expr_ev, ui::ErrorFormatter* errfmt) {
  this->expr_ev = expr_ev;
  this->errfmt = errfmt;
  this->f = mylib::Stdout();
}

void Assert::_AssertComparison(expr::Compare* exp, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* expected = nullptr;
  value_asdl::value_t* actual = nullptr;
  StackRoot _root0(&exp);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&expected);
  StackRoot _root3(&actual);

  expected = this->expr_ev->EvalExpr(exp->left, loc::Missing);
  actual = this->expr_ev->EvalExpr(exp->comparators->at(0), loc::Missing);
  if (!val_ops::ExactlyEqual(expected, actual, blame_loc)) {
    this->f->write(S_nfs);
    ui::PrettyPrintValue(S_kds, expected, this->f);
    ui::PrettyPrintValue(S_poi, actual, this->f);
    throw Alloc<error::Expr>(S_oln, exp->ops->at(0));
  }
}

void Assert::_AssertExpression(value::Expr* val, syntax_asdl::loc_t* blame_loc) {
  syntax_asdl::expr_t* exp = nullptr;
  syntax_asdl::expr_t* UP_exp = nullptr;
  value_asdl::value_t* result = nullptr;
  bool b;
  StackRoot _root0(&val);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&exp);
  StackRoot _root3(&UP_exp);
  StackRoot _root4(&result);

  exp = val->e;
  UP_exp = exp;
  switch (exp->tag()) {
    case expr_e::Compare: {
      expr::Compare* exp = static_cast<expr::Compare*>(UP_exp);
      if ((len(exp->ops) == 1 and exp->ops->at(0)->id == Id::Expr_TEqual)) {
        this->_AssertComparison(exp, blame_loc);
        return ;
      }
    }
      break;
  }
  result = this->expr_ev->EvalExpr(val->e, blame_loc);
  b = val_ops::ToBool(result);
  if (!b) {
    throw Alloc<error::Expr>(S_mjw, blame_loc);
  }
}

int Assert::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  typed_args::Reader* rd = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  bool b;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&rd);
  StackRoot _root3(&val);
  StackRoot _root4(&UP_val);

  Tuple2<args::_Attributes*, args::Reader*> tup5 = flag_util::ParseCmdVal(S_eln, cmd_val, true);
  arg_r = tup5.at1();
  rd = typed_args::ReaderForProc(cmd_val);
  val = rd->PosValue();
  rd->Done();
  UP_val = val;
  switch (val->tag()) {
    case value_e::Expr: {
      value::Expr* val = static_cast<value::Expr*>(UP_val);
      this->_AssertExpression(val, rd->LeftParenToken());
    }
      break;
    default: {
      b = val_ops::ToBool(val);
      if (!b) {
        this->f->write(S_nfs);
        ui::PrettyPrintValue(S_zjj, val, this->f);
        throw Alloc<error::Expr>(S_adv, rd->LeftParenToken());
      }
    }
  }
  return 0;
}

}  // define namespace error_ysh

namespace func_eggex {  // define

using syntax_asdl::loc_t;
using syntax_asdl::Token;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::eggex_ops;
using value_asdl::eggex_ops_e;
using value_asdl::eggex_ops_t;
using value_asdl::regex_match_e;
using value_asdl::RegexMatch;
int G = 0;
int S = 1;
int E = 2;

_MatchCallable::_MatchCallable(int to_return, expr_eval::ExprEvaluator* expr_ev) {
  this->to_return = to_return;
  this->expr_ev = expr_ev;
}

value_asdl::value_t* _MatchCallable::_ReturnValue(value_asdl::RegexMatch* match, int group_index, syntax_asdl::loc_t* blame_loc) {
  int num_groups;
  int start;
  int end;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* convert_func = nullptr;
  syntax_asdl::Token* convert_tok = nullptr;
  eggex_ops::Yes* ops = nullptr;
  StackRoot _root0(&match);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&val);
  StackRoot _root3(&convert_func);
  StackRoot _root4(&convert_tok);
  StackRoot _root5(&ops);

  num_groups = (len(match->indices) / 2);
  if (group_index < num_groups) {
    start = match->indices->at((2 * group_index));
    if (this->to_return == S) {
      return num::ToBig(start);
    }
    end = match->indices->at(((2 * group_index) + 1));
    if (this->to_return == E) {
      return num::ToBig(end);
    }
    if (start == -1) {
      return value::Null;
    }
    else {
      val = Alloc<value::Str>(match->s->slice(start, end));
      convert_func = nullptr;
      convert_tok = nullptr;
      switch (match->ops->tag()) {
        case eggex_ops_e::Yes: {
          ops = static_cast<eggex_ops::Yes*>(match->ops);
          if ((len(ops->convert_funcs) and group_index != 0)) {
            convert_func = ops->convert_funcs->at((group_index - 1));
            convert_tok = ops->convert_toks->at((group_index - 1));
          }
        }
          break;
      }
      if (convert_func != nullptr) {
        val = this->expr_ev->CallConvertFunc(convert_func, val, convert_tok, blame_loc);
      }
      return val;
    }
  }
  else {
    throw Alloc<error::Expr>(StrFormat("Expected capture group less than %d, got %d", num_groups, group_index), blame_loc);
  }
}

value_asdl::value_t* _MatchCallable::_Call(value_asdl::RegexMatch* match, value_asdl::value_t* group_arg, syntax_asdl::loc_t* blame_loc) {
  int group_index;
  StackRoot _root0(&match);
  StackRoot _root1(&group_arg);
  StackRoot _root2(&blame_loc);

  group_index = _GetGroupIndex(group_arg, match->ops, blame_loc);
  return this->_ReturnValue(match, group_index, blame_loc);
}

int _GetGroupIndex(value_asdl::value_t* group, value_asdl::eggex_ops_t* ops, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_group = nullptr;
  int group_index;
  mops::BigInt group_index_big;
  value_asdl::eggex_ops_t* UP_ops = nullptr;
  int i;
  StackRoot _root0(&group);
  StackRoot _root1(&ops);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_group);
  StackRoot _root4(&UP_ops);

  UP_group = group;
  group_index = -1;
  switch (group->tag()) {
    case value_e::Int: {
      value::Int* group = static_cast<value::Int*>(UP_group);
      group_index_big = group->i;
      group_index = mops::BigTruncate(group_index_big);
    }
      break;
    case value_e::Str: {
      value::Str* group = static_cast<value::Str*>(UP_group);
      UP_ops = ops;
      switch (ops->tag()) {
        case eggex_ops_e::No: {
          throw Alloc<error::Expr>(StrFormat("ERE captures don't have names (%r)", group->s), blame_loc);
        }
          break;
        case eggex_ops_e::Yes: {
          eggex_ops::Yes* ops = static_cast<eggex_ops::Yes*>(UP_ops);
          i = 0;
          for (ListIter<BigStr*> it(ops->capture_names); !it.Done(); it.Next(), ++i) {
            BigStr* name = it.Value();
            StackRoot _for(&name          );
            if (str_equals(name, group->s)) {
              group_index = (i + 1);
              break;
            }
          }
          if (group_index == -1) {
            throw Alloc<error::Expr>(StrFormat("No such group %r", group->s), blame_loc);
          }
        }
          break;
      }
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(group, S_Btr, blame_loc);
    }
  }
  return group_index;
}

MatchFunc::MatchFunc(int to_return, expr_eval::ExprEvaluator* expr_ev, state::Mem* mem) : ::func_eggex::_MatchCallable(to_return, expr_ev) {
  this->mem = mem;
}

value_asdl::value_t* MatchFunc::Call(typed_args::Reader* rd) {
  value_asdl::value_t* group_arg = nullptr;
  value_asdl::regex_match_t* match = nullptr;
  value_asdl::regex_match_t* UP_match = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&group_arg);
  StackRoot _root2(&match);
  StackRoot _root3(&UP_match);

  group_arg = rd->PosValue();
  rd->Done();
  match = this->mem->GetRegexMatch();
  UP_match = match;
  switch (match->tag()) {
    case regex_match_e::No: {
      throw Alloc<error::Expr>(S_Dwc, rd->LeftParenToken());
    }
      break;
    case regex_match_e::Yes: {
      RegexMatch* match = static_cast<RegexMatch*>(UP_match);
      return this->_Call(match, group_arg, rd->LeftParenToken());
    }
      break;
  }
  assert(0);  // AssertionError
}

MatchMethod::MatchMethod(int to_return, expr_eval::ExprEvaluator* expr_ev) : ::func_eggex::_MatchCallable(to_return, expr_ev) {
}

value_asdl::value_t* MatchMethod::Call(typed_args::Reader* rd) {
  value_asdl::RegexMatch* match = nullptr;
  value_asdl::value_t* group_arg = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&match);
  StackRoot _root2(&group_arg);

  match = rd->PosMatch();
  group_arg = rd->PosValue();
  rd->Done();
  return this->_Call(match, group_arg, rd->LeftParenToken());
}

}  // define namespace func_eggex

namespace func_hay {  // define

using syntax_asdl::source;
using syntax_asdl::loc;
using syntax_asdl::command_t;
using value_asdl::value;
using value_asdl::cmd_frag;

ParseHay::ParseHay(process::FdState* fd_state, parse_lib::ParseContext* parse_ctx, state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->fd_state = fd_state;
  this->parse_ctx = parse_ctx;
  this->mem = mem;
  this->errfmt = errfmt;
}

value_asdl::value_t* ParseHay::_Call(BigStr* path) {
  syntax_asdl::loc__Missing* call_loc = nullptr;
  mylib::LineReader* f = nullptr;
  BigStr* msg = nullptr;
  alloc::Arena* arena = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  optview::Parse* parse_opts = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  source::OtherFile* src = nullptr;
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&path);
  StackRoot _root1(&call_loc);
  StackRoot _root2(&f);
  StackRoot _root3(&msg);
  StackRoot _root4(&arena);
  StackRoot _root5(&line_reader);
  StackRoot _root6(&parse_opts);
  StackRoot _root7(&c_parser);
  StackRoot _root8(&src);
  StackRoot _root9(&node);

  call_loc = loc::Missing;
  try {
    f = this->fd_state->Open(path);
  }
  catch (IOError_OSError* e) {
    msg = posix::strerror(e->errno_);
    throw Alloc<error::Expr>(StrFormat("Couldn't open %r: %s", path, msg), call_loc);
  }
  arena = this->parse_ctx->arena;
  line_reader = Alloc<reader::FileLineReader>(f, arena);
  parse_opts = state::MakeYshParseOpts();
  c_parser = this->parse_ctx->MakeConfigParser(line_reader);
  src = Alloc<source::OtherFile>(path, call_loc);
  try {
    {  // with
      alloc::ctx_SourceCode ctx{arena, src};

      node = main_loop::ParseWholeFile(c_parser);
    }
  }
  catch (error::Parse* e) {
    this->errfmt->PrettyPrintError(e);
    return nullptr;
  }
  return Alloc<value::Command>(Alloc<cmd_frag::Expr>(node), this->mem->CurrentFrame(), this->mem->GlobalFrame());
}

value_asdl::value_t* ParseHay::Call(typed_args::Reader* rd) {
  BigStr* string = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&string);

  string = rd->PosStr();
  rd->Done();
  return this->_Call(string);
}

EvalHay::EvalHay(hay_ysh::HayState* hay_state, state::MutableOpts* mutable_opts, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev) {
  this->hay_state = hay_state;
  this->mutable_opts = mutable_opts;
  this->mem = mem;
  this->cmd_ev = cmd_ev;
}

Dict<BigStr*, value_asdl::value_t*>* EvalHay::_Call(syntax_asdl::command_t* cmd) {
  int unused;
  (void)unused;
  StackRoot _root0(&cmd);

  {  // with
    hay_ysh::ctx_HayEval ctx{this->hay_state, this->mutable_opts, this->mem};

    unused = this->cmd_ev->EvalCommandFrag(cmd);
  }
  return this->hay_state->Result();
}

value_asdl::value_t* EvalHay::Call(typed_args::Reader* rd) {
  syntax_asdl::command_t* cmd = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&cmd);

  cmd = rd->PosCommandFrag();
  rd->Done();
  return Alloc<value::Dict>(this->_Call(cmd));
}

BlockAsStr::BlockAsStr(alloc::Arena* arena) {
  this->arena = arena;
}

value_asdl::value_t* BlockAsStr::_Call(value_asdl::value_t* block) {
  StackRoot _root0(&block);

  return block;
}

value_asdl::value_t* BlockAsStr::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);

  val = rd->PosValue();
  rd->Done();
  return this->_Call(val);
}

HayFunc::HayFunc(hay_ysh::HayState* hay_state) {
  this->hay_state = hay_state;
}

Dict<BigStr*, value_asdl::value_t*>* HayFunc::_Call() {
  return this->hay_state->HayRegister();
}

value_asdl::value_t* HayFunc::Call(typed_args::Reader* rd) {
  StackRoot _root0(&rd);

  return Alloc<value::Dict>(this->_Call());
}

}  // define namespace func_hay

namespace func_misc {  // define

using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::value_str;
using value_asdl::Obj;

Object::Object() {
  ;  // pass
}

value_asdl::value_t* Object::Call(typed_args::Reader* rd) {
  value_asdl::value_t* prototype = nullptr;
  syntax_asdl::loc_t* proto_loc = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* props = nullptr;
  value_asdl::Obj* chain = nullptr;
  value_asdl::value_t* UP_prototype = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&prototype);
  StackRoot _root2(&proto_loc);
  StackRoot _root3(&props);
  StackRoot _root4(&chain);
  StackRoot _root5(&UP_prototype);

  prototype = rd->PosValue();
  proto_loc = rd->BlamePos();
  props = rd->PosDict();
  rd->Done();
  chain = nullptr;
  UP_prototype = prototype;
  switch (prototype->tag()) {
    case value_e::Null: {
      ;  // pass
    }
      break;
    case value_e::Obj: {
      Obj* prototype = static_cast<Obj*>(UP_prototype);
      chain = prototype;
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(prototype, S_zhC, proto_loc);
    }
  }
  return Alloc<Obj>(chain, props);
}

Obj_call::Obj_call() {
  ;  // pass
}

value_asdl::value_t* Obj_call::Call(typed_args::Reader* rd) {
  Dict<BigStr*, value_asdl::value_t*>* props = nullptr;
  value_asdl::value_t* prototype = nullptr;
  syntax_asdl::loc_t* proto_loc = nullptr;
  value_asdl::Obj* chain = nullptr;
  value_asdl::value_t* UP_prototype = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&props);
  StackRoot _root2(&prototype);
  StackRoot _root3(&proto_loc);
  StackRoot _root4(&chain);
  StackRoot _root5(&UP_prototype);

  props = rd->PosDict();
  prototype = rd->OptionalValue();
  proto_loc = rd->BlamePos();
  rd->Done();
  chain = nullptr;
  if (prototype != nullptr) {
    UP_prototype = prototype;
    switch (prototype->tag()) {
      case value_e::Null: {
        ;  // pass
      }
        break;
      case value_e::Obj: {
        Obj* prototype = static_cast<Obj*>(UP_prototype);
        chain = prototype;
      }
        break;
      default: {
        throw Alloc<error::TypeErr>(prototype, S_zhC, proto_loc);
      }
    }
  }
  return Alloc<Obj>(chain, props);
}

Prototype::Prototype() {
  ;  // pass
}

value_asdl::value_t* Prototype::Call(typed_args::Reader* rd) {
  value_asdl::Obj* obj = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&obj);

  obj = rd->PosObj();
  rd->Done();
  if (obj->prototype == nullptr) {
    return value::Null;
  }
  return obj->prototype;
}

PropView::PropView() {
  ;  // pass
}

value_asdl::value_t* PropView::Call(typed_args::Reader* rd) {
  value_asdl::Obj* obj = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&obj);

  obj = rd->PosObj();
  rd->Done();
  return Alloc<value::Dict>(obj->d);
}

Len::Len() {
  ;  // pass
}

value_asdl::value_t* Len::Call(typed_args::Reader* rd) {
  value_asdl::value_t* x = nullptr;
  value_asdl::value_t* UP_x = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&x);
  StackRoot _root2(&UP_x);

  x = rd->PosValue();
  rd->Done();
  UP_x = x;
  switch (x->tag()) {
    case value_e::List: {
      value::List* x = static_cast<value::List*>(UP_x);
      return num::ToBig(len(x->items));
    }
      break;
    case value_e::Dict: {
      value::Dict* x = static_cast<value::Dict*>(UP_x);
      return num::ToBig(len(x->d));
    }
      break;
    case value_e::Str: {
      value::Str* x = static_cast<value::Str*>(UP_x);
      return num::ToBig(len(x->s));
    }
      break;
  }
  throw Alloc<error::TypeErr>(x, S_rEg, rd->BlamePos());
}

Type::Type() {
  ;  // pass
}

value_asdl::value_t* Type::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);

  val = rd->PosValue();
  rd->Done();
  return Alloc<value::Str>(ui::ValType(val));
}

Join::Join() {
  ;  // pass
}

value_asdl::value_t* Join::Call(typed_args::Reader* rd) {
  List<value_asdl::value_t*>* li = nullptr;
  BigStr* delim = nullptr;
  List<BigStr*>* strs = nullptr;
  int i;
  StackRoot _root0(&rd);
  StackRoot _root1(&li);
  StackRoot _root2(&delim);
  StackRoot _root3(&strs);

  li = rd->PosList();
  delim = rd->OptionalStr(S_Aoo);
  rd->Done();
  strs = Alloc<List<BigStr*>>();
  i = 0;
  for (ListIter<value_asdl::value_t*> it(li); !it.Done(); it.Next(), ++i) {
    value_asdl::value_t* el = it.Value();
    StackRoot _for(&el  );
    strs->append(val_ops::Stringify(el, rd->LeftParenToken(), S_gzE_1));
  }
  return Alloc<value::Str>(delim->join(strs));
}

Maybe::Maybe() {
  ;  // pass
}

value_asdl::value_t* Maybe::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);
  StackRoot _root2(&s);

  val = rd->PosValue();
  rd->Done();
  if (val == value::Null) {
    return Alloc<value::List>(Alloc<List<value_asdl::value_t*>>());
  }
  s = val_ops::ToStr(val, StrFormat("maybe() expected Str, but got %s", value_str(val->tag())), rd->LeftParenToken());
  if (len(s)) {
    return Alloc<value::List>(NewList<value_asdl::value_t*>(std::initializer_list<value_asdl::value_t*>{val}));
  }
  return Alloc<value::List>(Alloc<List<value_asdl::value_t*>>());
}

Bool::Bool() {
  ;  // pass
}

value_asdl::value_t* Bool::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);

  val = rd->PosValue();
  rd->Done();
  return Alloc<value::Bool>(val_ops::ToBool(val));
}

Int::Int() {
  ;  // pass
}

value_asdl::value_t* Int::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  bool ok;
  mops::BigInt big_int;
  BigStr* s = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_val);
  StackRoot _root3(&s);

  val = rd->PosValue();
  rd->Done();
  UP_val = val;
  switch (val->tag()) {
    case value_e::Int: {
      return val;
    }
      break;
    case value_e::Bool: {
      value::Bool* val = static_cast<value::Bool*>(UP_val);
      return Alloc<value::Int>(mops::FromBool(val->b));
    }
      break;
    case value_e::Float: {
      value::Float* val = static_cast<value::Float*>(UP_val);
      Tuple2<bool, mops::BigInt> tup0 = mops::FromFloat(val->f);
      ok = tup0.at0();
      big_int = tup0.at1();
      if (ok) {
        return Alloc<value::Int>(big_int);
      }
      else {
        throw Alloc<error::Expr>(StrFormat("Can't convert float %s to Int", pp_value::FloatString(val->f)), rd->BlamePos());
      }
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      if (!match::LooksLikeYshInt(val->s)) {
        throw Alloc<error::Expr>(StrFormat("Can't convert %s to Int", val->s), rd->BlamePos());
      }
      s = val->s->replace(S_tci, S_Aoo);
      Tuple2<bool, mops::BigInt> tup1 = mops::FromStr2(s);
      ok = tup1.at0();
      big_int = tup1.at1();
      if (!ok) {
        throw Alloc<error::Expr>(StrFormat("Integer too big: %s", val->s), rd->BlamePos());
      }
      return Alloc<value::Int>(big_int);
    }
      break;
  }
  throw Alloc<error::TypeErr>(val, S_FiA, rd->BlamePos());
}

Float::Float() {
  ;  // pass
}

value_asdl::value_t* Float::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_val);

  val = rd->PosValue();
  rd->Done();
  UP_val = val;
  switch (val->tag()) {
    case value_e::Int: {
      value::Int* val = static_cast<value::Int*>(UP_val);
      return Alloc<value::Float>(mops::ToFloat(val->i));
    }
      break;
    case value_e::Float: {
      return val;
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      if (!match::LooksLikeYshFloat(val->s)) {
        throw Alloc<error::Expr>(StrFormat("Cannot convert %s to Float", val->s), rd->BlamePos());
      }
      return Alloc<value::Float>(to_float(val->s));
    }
      break;
  }
  throw Alloc<error::TypeErr>(val, S_fir, rd->BlamePos());
}

Str_::Str_() {
  ;  // pass
}

value_asdl::value_t* Str_::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);
  StackRoot _root2(&s);

  val = rd->PosValue();
  rd->Done();
  switch (val->tag()) {
    case value_e::Str: {
      return val;
    }
      break;
    default: {
      s = val_ops::Stringify(val, rd->LeftParenToken(), S_uCe);
      return Alloc<value::Str>(s);
    }
  }
}

List_::List_() {
  ;  // pass
}

value_asdl::value_t* List_::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  List<value_asdl::value_t*>* l = nullptr;
  val_ops::Iterator* it = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  value_asdl::value_t* first = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);
  StackRoot _root2(&l);
  StackRoot _root3(&it);
  StackRoot _root4(&UP_val);
  StackRoot _root5(&first);

  val = rd->PosValue();
  rd->Done();
  l = Alloc<List<value_asdl::value_t*>>();
  it = nullptr;
  UP_val = val;
  switch (val->tag()) {
    case value_e::List: {
      value::List* val = static_cast<value::List*>(UP_val);
      it = Alloc<val_ops::ListIterator>(val);
    }
      break;
    case value_e::Dict: {
      value::Dict* val = static_cast<value::Dict*>(UP_val);
      it = Alloc<val_ops::DictIterator>(val);
    }
      break;
    case value_e::Range: {
      value::Range* val = static_cast<value::Range*>(UP_val);
      it = Alloc<val_ops::RangeIterator>(val);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_Dtg, rd->BlamePos());
    }
  }
  while (true) {
    first = it->FirstValue();
    if (first == nullptr) {
      break;
    }
    l->append(first);
    it->Next();
  }
  return Alloc<value::List>(l);
}

DictFunc::DictFunc() {
  ;  // pass
}

value_asdl::value_t* DictFunc::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_val);
  StackRoot _root3(&d);

  val = rd->PosValue();
  rd->Done();
  UP_val = val;
  switch (val->tag()) {
    case value_e::Dict: {
      value::Dict* val = static_cast<value::Dict*>(UP_val);
      d = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      for (DictIter<BigStr*, value_asdl::value_t*> it(val->d); !it.Done(); it.Next()) {
        BigStr* k = it.Key();
        value_asdl::value_t* v = it.Value();
        d->set(k, v);
      }
      return Alloc<value::Dict>(d);
    }
      break;
    case value_e::Obj: {
      Obj* val = static_cast<Obj*>(UP_val);
      d = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      for (DictIter<BigStr*, value_asdl::value_t*> it(val->d); !it.Done(); it.Next()) {
        BigStr* k = it.Key();
        value_asdl::value_t* v = it.Value();
        d->set(k, v);
      }
      return Alloc<value::Dict>(d);
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      d = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      for (DictIter<BigStr*, BigStr*> it(val->d); !it.Done(); it.Next()) {
        BigStr* k = it.Key();
        BigStr* s = it.Value();
        d->set(k, Alloc<value::Str>(s));
      }
      return Alloc<value::Dict>(d);
    }
      break;
    case value_e::Frame: {
      value::Frame* val = static_cast<value::Frame*>(UP_val);
      d = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      for (DictIter<BigStr*, runtime_asdl::Cell*> it(val->frame); !it.Done(); it.Next()) {
        BigStr* k = it.Key();
        runtime_asdl::Cell* cell = it.Value();
        d->set(k, cell->val);
      }
      return Alloc<value::Dict>(d);
    }
      break;
  }
  throw Alloc<error::TypeErr>(val, S_wse, rd->BlamePos());
}

Runes::Runes() {
  ;  // pass
}

value_asdl::value_t* Runes::Call(typed_args::Reader* rd) {
  StackRoot _root0(&rd);

  return value::Null;
}

EncodeRunes::EncodeRunes() {
  ;  // pass
}

value_asdl::value_t* EncodeRunes::Call(typed_args::Reader* rd) {
  StackRoot _root0(&rd);

  return value::Null;
}

Bytes::Bytes() {
  ;  // pass
}

value_asdl::value_t* Bytes::Call(typed_args::Reader* rd) {
  StackRoot _root0(&rd);

  return value::Null;
}

EncodeBytes::EncodeBytes() {
  ;  // pass
}

value_asdl::value_t* EncodeBytes::Call(typed_args::Reader* rd) {
  StackRoot _root0(&rd);

  return value::Null;
}

Split::Split(split::SplitContext* splitter) : ::vm::_Callable() {
  this->splitter = splitter;
}

value_asdl::value_t* Split::Call(typed_args::Reader* rd) {
  BigStr* s = nullptr;
  BigStr* ifs = nullptr;
  List<value_asdl::value_t*>* l = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&s);
  StackRoot _root2(&ifs);
  StackRoot _root3(&l);

  s = rd->PosStr();
  ifs = rd->OptionalStr();
  rd->Done();
  l = Alloc<List<value_asdl::value_t*>>();
  for (ListIter<BigStr*> it(this->splitter->SplitForWordEval(s, ifs)); !it.Done(); it.Next()) {
    BigStr* elem = it.Value();
    l->append(Alloc<value::Str>(elem));
  }
  return Alloc<value::List>(l);
}

FloatsEqual::FloatsEqual() {
  ;  // pass
}

value_asdl::value_t* FloatsEqual::Call(typed_args::Reader* rd) {
  double left;
  double right;
  StackRoot _root0(&rd);

  left = rd->PosFloat();
  right = rd->PosFloat();
  rd->Done();
  return Alloc<value::Bool>(left == right);
}

Glob::Glob(glob_::Globber* globber) : ::vm::_Callable() {
  this->globber = globber;
}

value_asdl::value_t* Glob::Call(typed_args::Reader* rd) {
  BigStr* s = nullptr;
  List<BigStr*>* out = nullptr;
  List<value_asdl::value_t*>* l = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&s);
  StackRoot _root2(&out);
  StackRoot _root3(&l);

  s = rd->PosStr();
  rd->Done();
  out = Alloc<List<BigStr*>>();
  this->globber->_Glob(s, out);
  l = Alloc<List<value_asdl::value_t*>>();
  for (ListIter<BigStr*> it(out); !it.Done(); it.Next()) {
    BigStr* elem = it.Value();
    l->append(Alloc<value::Str>(elem));
  }
  return Alloc<value::List>(l);
}

ToJson8::ToJson8(bool is_j8) {
  this->is_j8 = is_j8;
}

value_asdl::value_t* ToJson8::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  int space;
  int indent;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);
  StackRoot _root2(&buf);

  val = rd->PosValue();
  space = mops::BigTruncate(rd->NamedInt(S_iya, 0));
  rd->Done();
  if (space <= 0) {
    indent = -1;
  }
  else {
    indent = space;
  }
  buf = Alloc<mylib::BufWriter>();
  try {
    if (this->is_j8) {
      j8::PrintMessage(val, buf, indent);
    }
    else {
      j8::PrintJsonMessage(val, buf, indent);
    }
  }
  catch (error::Encode* e) {
    throw Alloc<error::Structured>(4, e->Message(), rd->LeftParenToken());
  }
  return Alloc<value::Str>(buf->getvalue());
}

FromJson8::FromJson8(bool is_j8) {
  this->is_j8 = is_j8;
}

value_asdl::value_t* FromJson8::Call(typed_args::Reader* rd) {
  BigStr* s = nullptr;
  j8::Parser* p = nullptr;
  value_asdl::value_t* val = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* props = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&s);
  StackRoot _root2(&p);
  StackRoot _root3(&val);
  StackRoot _root4(&props);

  s = rd->PosStr();
  rd->Done();
  p = Alloc<j8::Parser>(s, this->is_j8);
  try {
    val = p->ParseValue();
  }
  catch (error::Decode* e) {
    props = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_pBs, S_jdf}, std::initializer_list<value_asdl::value_t*>{num::ToBig(e->start_pos), num::ToBig(e->end_pos)});
    throw Alloc<error::Structured>(4, e->Message(), rd->LeftParenToken(), props);
  }
  return val;
}

BashArrayToSparse::BashArrayToSparse() {
  ;  // pass
}

value_asdl::value_t* BashArrayToSparse::Call(typed_args::Reader* rd) {
  List<BigStr*>* strs = nullptr;
  Dict<mops::BigInt, BigStr*>* d = nullptr;
  mops::BigInt max_index;
  int i;
  mops::BigInt big_i;
  StackRoot _root0(&rd);
  StackRoot _root1(&strs);
  StackRoot _root2(&d);

  strs = rd->PosBashArray();
  rd->Done();
  d = Alloc<Dict<mops::BigInt, BigStr*>>();
  max_index = mops::MINUS_ONE;
  i = 0;
  for (ListIter<BigStr*> it(strs); !it.Done(); it.Next(), ++i) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    if (s != nullptr) {
      big_i = mops::IntWiden(i);
      d->set(big_i, s);
      if (mops::Greater(big_i, max_index)) {
        max_index = big_i;
      }
    }
  }
  return Alloc<value::SparseArray>(d, max_index);
}

SparseOp::SparseOp() {
  ;  // pass
}

value_asdl::value_t* SparseOp::Call(typed_args::Reader* rd) {
  value::SparseArray* sp = nullptr;
  Dict<mops::BigInt, BigStr*>* d = nullptr;
  BigStr* op_name = nullptr;
  BigStr* no_str = nullptr;
  mops::BigInt index;
  BigStr* s = nullptr;
  mops::BigInt max_index;
  List<mops::BigInt>* keys = nullptr;
  List<BigStr*>* items = nullptr;
  int j;
  mops::BigInt start;
  mops::BigInt end;
  int n;
  List<BigStr*>* items2 = nullptr;
  mops::BigInt i;
  List<BigStr*>* strs = nullptr;
  mops::BigInt i2;
  StackRoot _root0(&rd);
  StackRoot _root1(&sp);
  StackRoot _root2(&d);
  StackRoot _root3(&op_name);
  StackRoot _root4(&no_str);
  StackRoot _root5(&s);
  StackRoot _root6(&keys);
  StackRoot _root7(&items);
  StackRoot _root8(&items2);
  StackRoot _root9(&strs);

  sp = rd->PosSparseArray();
  d = sp->d;
  op_name = rd->PosStr();
  no_str = nullptr;
  if (str_equals(op_name, S_fDC)) {
    rd->Done();
    return num::ToBig(len(d));
  }
  else {
    if (str_equals(op_name, S_ylo)) {
      index = rd->PosInt();
      rd->Done();
      s = d->get(index);
      if (s == nullptr) {
        return value::Null;
      }
      else {
        return Alloc<value::Str>(s);
      }
    }
    else {
      if (str_equals(op_name, S_flq)) {
        index = rd->PosInt();
        s = rd->PosStr();
        rd->Done();
        d->set(index, s);
        if (mops::Greater(index, sp->max_index)) {
          sp->max_index = index;
        }
        return Alloc<value::Int>(mops::ZERO);
      }
      else {
        if (str_equals(op_name, S_FxC)) {
          index = rd->PosInt();
          rd->Done();
          mylib::dict_erase(d, index);
          max_index = mops::MINUS_ONE;
          for (DictIter<mops::BigInt, BigStr*> it(d); !it.Done(); it.Next()) {
            mops::BigInt i1 = it.Key();
            if (mops::Greater(i1, max_index)) {
              max_index = i1;
            }
          }
          sp->max_index = max_index;
          return Alloc<value::Int>(mops::ZERO);
        }
        else {
          if (str_equals(op_name, S_vyq)) {
            keys = d->keys();
            mylib::BigIntSort(keys);
            items = list_repeat(no_str, len(d));
            j = 0;
            for (ListIter<mops::BigInt> it(keys); !it.Done(); it.Next()) {
              mops::BigInt i = it.Value();
              s = d->get(i);
              items->set(j, s);
              j += 1;
            }
            return Alloc<value::BashArray>(items);
          }
          else {
            if (str_equals(op_name, S_zcr)) {
              keys = d->keys();
              mylib::BigIntSort(keys);
              items = Alloc<List<BigStr*>>();
              for (ListIter<mops::BigInt> it(keys); !it.Done(); it.Next()) {
                mops::BigInt k = it.Value();
                items->append(mops::ToStr(k));
              }
              return Alloc<value::BashArray>(items);
            }
            else {
              if (str_equals(op_name, S_kqu)) {
                start = rd->PosInt();
                end = rd->PosInt();
                rd->Done();
                n = mops::BigTruncate(mops::Sub(end, start));
                items2 = list_repeat(no_str, n);
                j = 0;
                i = start;
                while (mops::Greater(end, i)) {
                  s = d->get(i);
                  if (s != nullptr) {
                    items2->set(j, s);
                    j += 1;
                  }
                  i = mops::Add(i, mops::ONE);
                }
                return Alloc<value::BashArray>(items2);
              }
              else {
                if (str_equals(op_name, S_BEq)) {
                  strs = rd->PosBashArray();
{
                    max_index = sp->max_index;
                  }
                  i2 = mops::Add(max_index, mops::ONE);
                  for (ListIter<BigStr*> it(strs); !it.Done(); it.Next()) {
                    BigStr* s = it.Value();
                    StackRoot _for(&s                  );
                    d->set(i2, s);
                    i2 = mops::Add(i2, mops::ONE);
                  }
                  sp->max_index = mops::Add(sp->max_index, mops::IntWiden(len(strs)));
                  return Alloc<value::Int>(mops::ZERO);
                }
                else {
                  print(StrFormat("Invalid SparseArray operation %r", op_name));
                  return Alloc<value::Int>(mops::ZERO);
                }
              }
            }
          }
        }
      }
    }
  }
}

}  // define namespace func_misc

namespace func_reflect {  // define

using runtime_asdl::scope_e;
using syntax_asdl::source;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::cmd_frag;

Id::Id() : ::vm::_Callable() {
}

value_asdl::value_t* Id::Call(typed_args::Reader* rd) {
  value_asdl::value_t* unused_vm = nullptr;
  (void)unused_vm;
  value_asdl::value_t* val = nullptr;
  int id_;
  StackRoot _root0(&rd);
  StackRoot _root1(&unused_vm);
  StackRoot _root2(&val);

  unused_vm = rd->PosValue();
  val = rd->PosValue();
  rd->Done();
  switch (val->tag()) {
    case value_e::List: 
    case value_e::Dict: 
    case value_e::Obj: {
      id_ = j8::HeapValueId(val);
      return Alloc<value::Int>(mops::IntWiden(id_));
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_dnf, rd->BlamePos());
    }
  }
  assert(0);  // AssertionError
}

GetFrame::GetFrame(state::Mem* mem) : ::vm::_Callable() {
  this->mem = mem;
}

value_asdl::value_t* GetFrame::Call(typed_args::Reader* rd) {
  value_asdl::Obj* unused_self = nullptr;
  (void)unused_self;
  int index;
  int length;
  StackRoot _root0(&rd);
  StackRoot _root1(&unused_self);

  unused_self = rd->PosObj();
  index = mops::BigTruncate(rd->PosInt());
  rd->Done();
  length = len(this->mem->var_stack);
  if (index < 0) {
    index += length;
  }
  if ((0 <= index and index < length)) {
    return Alloc<value::Frame>(this->mem->var_stack->at(index));
  }
  else {
    throw Alloc<error::Structured>(3, StrFormat("Invalid frame %d", index), rd->LeftParenToken());
  }
}

BindFrame::BindFrame() : ::vm::_Callable() {
}

value_asdl::value_t* BindFrame::Call(typed_args::Reader* rd) {
  syntax_asdl::command_t* frag = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* frame = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&frag);
  StackRoot _root2(&frame);

  frag = rd->PosCommandFrag();
  frame = rd->PosFrame();
  rd->Done();
  return value::Null;
}

Shvar_get::Shvar_get(state::Mem* mem) : ::vm::_Callable() {
  this->mem = mem;
}

value_asdl::value_t* Shvar_get::Call(typed_args::Reader* rd) {
  BigStr* name = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&name);

  name = rd->PosStr();
  rd->Done();
  return state::DynamicGetVar(this->mem, name, scope_e::Dynamic);
}

GetVar::GetVar(state::Mem* mem) : ::vm::_Callable() {
  this->mem = mem;
}

value_asdl::value_t* GetVar::Call(typed_args::Reader* rd) {
  BigStr* name = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&name);

  name = rd->PosStr();
  rd->Done();
  return state::DynamicGetVar(this->mem, name, scope_e::LocalOrGlobal);
}

SetVar::SetVar(state::Mem* mem) : ::vm::_Callable() {
  this->mem = mem;
}

value_asdl::value_t* SetVar::Call(typed_args::Reader* rd) {
  BigStr* var_name = nullptr;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&var_name);
  StackRoot _root2(&val);

  var_name = rd->PosStr();
  val = rd->PosValue();
  rd->Done();
  this->mem->SetNamed(location::LName(var_name), val, scope_e::LocalOnly);
  return value::Null;
}

ParseCommand::ParseCommand(parse_lib::ParseContext* parse_ctx, state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->parse_ctx = parse_ctx;
  this->mem = mem;
  this->errfmt = errfmt;
}

value_asdl::value_t* ParseCommand::Call(typed_args::Reader* rd) {
  BigStr* code_str = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  source::Dynamic* src = nullptr;
  syntax_asdl::command_t* cmd = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&code_str);
  StackRoot _root2(&line_reader);
  StackRoot _root3(&c_parser);
  StackRoot _root4(&src);
  StackRoot _root5(&cmd);

  code_str = rd->PosStr();
  rd->Done();
  line_reader = reader::StringLineReader(code_str, this->parse_ctx->arena);
  c_parser = this->parse_ctx->MakeOshParser(line_reader);
  src = Alloc<source::Dynamic>(S_cCs, rd->LeftParenToken());
  {  // with
    alloc::ctx_SourceCode ctx{this->parse_ctx->arena, src};

    try {
      cmd = main_loop::ParseWholeFile(c_parser);
    }
    catch (error::Parse* e) {
      this->errfmt->PrettyPrintError(e);
      throw Alloc<error::Structured>(3, S_wmA, rd->LeftParenToken());
    }
  }
  return Alloc<value::Command>(Alloc<cmd_frag::Expr>(cmd), this->mem->CurrentFrame(), this->mem->GlobalFrame());
}

ParseExpr::ParseExpr(parse_lib::ParseContext* parse_ctx, ui::ErrorFormatter* errfmt) {
  this->parse_ctx = parse_ctx;
  this->errfmt = errfmt;
}

value_asdl::value_t* ParseExpr::Call(typed_args::Reader* rd) {
  BigStr* code_str = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&code_str);

  code_str = rd->PosStr();
  rd->Done();
  return value::Null;
}

}  // define namespace func_reflect

namespace hay_ysh {  // define

using option_asdl::option_i;
using runtime_asdl::scope_e;
using runtime_asdl::HayNode;
using syntax_asdl::loc;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
namespace fmt = format;
using error::e_usage;
using error::e_die;
BigStr* _HAY_ACTION_ERROR = S_zut;

ctx_HayNode::ctx_HayNode(hay_ysh::HayState* hay_state, BigStr* hay_name) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->hay_state)));
  this->hay_state = hay_state;
  this->hay_state->Push(hay_name);
}

ctx_HayNode::~ctx_HayNode() {
  this->hay_state->Pop();
  gHeap.PopRoot();
}

ctx_HayEval::ctx_HayEval(hay_ysh::HayState* hay_state, state::MutableOpts* mutable_opts, state::Mem* mem) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->hay_state)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mutable_opts)));
  this->hay_state = hay_state;
  this->mutable_opts = mutable_opts;
  this->mem = mem;
  if (mutable_opts->Get(option_i::_running_hay)) {
    e_die(S_rxx);
  }
  for (ListIter<int> it(consts::YSH_ALL); !it.Done(); it.Next()) {
    int opt_num = it.Value();
    mutable_opts->Push(opt_num, true);
  }
  mutable_opts->Push(option_i::_running_hay, true);
  this->hay_state->PushEval();
  this->mem->PushTemp();
}

ctx_HayEval::~ctx_HayEval() {
  this->mem->PopTemp();
  this->hay_state->PopEval();
  this->mutable_opts->Pop(option_i::_running_hay);
  for (ListIter<int> it(consts::YSH_ALL); !it.Done(); it.Next()) {
    int opt_num = it.Value();
    this->mutable_opts->Pop(opt_num);
  }
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
}

HayState::HayState() {
  auto* ch = Alloc<Dict<BigStr*, runtime_asdl::HayNode*>>();
  this->root_defs = Alloc<HayNode>(ch);
  this->cur_defs = this->root_defs;
  this->def_stack = NewList<runtime_asdl::HayNode*>(std::initializer_list<runtime_asdl::HayNode*>{this->root_defs});
  Dict<BigStr*, value_asdl::value_t*>* node = this->_MakeOutputNode();
  this->result_stack = NewList<Dict<BigStr*, value_asdl::value_t*>*>(std::initializer_list<Dict<BigStr*, value_asdl::value_t*>*>{node});
  this->output = nullptr;
}

Dict<BigStr*, value_asdl::value_t*>* HayState::_MakeOutputNode() {
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  StackRoot _root0(&d);

  d = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  d->set(S_cmd, value::Null);
  d->set(S_ccA, Alloc<value::List>(Alloc<List<value_asdl::value_t*>>()));
  return d;
}

void HayState::PushEval() {
  Dict<BigStr*, value_asdl::value_t*>* node = nullptr;
  StackRoot _root0(&node);

  node = this->_MakeOutputNode();
  this->result_stack = NewList<Dict<BigStr*, value_asdl::value_t*>*>(std::initializer_list<Dict<BigStr*, value_asdl::value_t*>*>{node});
  this->output = nullptr;
}

void HayState::PopEval() {
  Dict<BigStr*, value_asdl::value_t*>* node = nullptr;
  StackRoot _root0(&node);

  this->output = this->result_stack->at(0);
  node = this->_MakeOutputNode();
  this->result_stack = NewList<Dict<BigStr*, value_asdl::value_t*>*>(std::initializer_list<Dict<BigStr*, value_asdl::value_t*>*>{node});
}

void HayState::AppendResult(Dict<BigStr*, value_asdl::value_t*>* d) {
  value_asdl::value_t* UP_children = nullptr;
  StackRoot _root0(&d);
  StackRoot _root1(&UP_children);

  UP_children = this->result_stack->at(-1)->at(S_ccA);
  value::List* children = static_cast<value::List*>(UP_children);
  children->items->append(Alloc<value::Dict>(d));
}

Dict<BigStr*, value_asdl::value_t*>* HayState::Result() {
  return this->output;
}

Dict<BigStr*, value_asdl::value_t*>* HayState::HayRegister() {
  return this->result_stack->at(0);
}

bool HayState::Resolve(BigStr* first_word) {
  StackRoot _root0(&first_word);

  return dict_contains(this->cur_defs->children, first_word);
}

void HayState::DefinePath(List<BigStr*>* path) {
  runtime_asdl::HayNode* current = nullptr;
  Dict<BigStr*, runtime_asdl::HayNode*>* ch = nullptr;
  StackRoot _root0(&path);
  StackRoot _root1(&current);
  StackRoot _root2(&ch);

  current = this->root_defs;
  for (ListIter<BigStr*> it(path); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    if (!dict_contains(current->children, name)) {
      ch = Alloc<Dict<BigStr*, runtime_asdl::HayNode*>>();
      current->children->set(name, Alloc<HayNode>(ch));
    }
    current = current->children->at(name);
  }
}

void HayState::Reset() {
  Dict<BigStr*, runtime_asdl::HayNode*>* ch = nullptr;
  StackRoot _root0(&ch);

  ch = Alloc<Dict<BigStr*, runtime_asdl::HayNode*>>();
  this->root_defs = Alloc<HayNode>(ch);
  this->cur_defs = this->root_defs;
  this->PopEval();
}

void HayState::Push(BigStr* hay_name) {
  Dict<BigStr*, value_asdl::value_t*>* top = nullptr;
  value::List* children = nullptr;
  value::Dict* last_child = nullptr;
  StackRoot _root0(&hay_name);
  StackRoot _root1(&top);
  StackRoot _root2(&children);
  StackRoot _root3(&last_child);

  top = this->result_stack->at(-1);
  children = static_cast<value::List*>(top->at(S_ccA));
  last_child = static_cast<value::Dict*>(children->items->at(-1));
  this->result_stack->append(last_child->d);
  if (hay_name == nullptr) {
    this->def_stack->append(this->cur_defs);
  }
  else {
    this->cur_defs = this->cur_defs->children->at(hay_name);
    this->def_stack->append(this->cur_defs);
  }
}

void HayState::Pop() {
  this->def_stack->pop();
  this->cur_defs = this->def_stack->at(-1);
  this->result_stack->pop();
}

Hay::Hay(hay_ysh::HayState* hay_state, state::MutableOpts* mutable_opts, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev) {
  this->hay_state = hay_state;
  this->mutable_opts = mutable_opts;
  this->mem = mem;
  this->cmd_ev = cmd_ev;
}

int Hay::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* action = nullptr;
  syntax_asdl::loc_t* action_loc = nullptr;
  BigStr* first = nullptr;
  List<BigStr*>* names = nullptr;
  List<syntax_asdl::CompoundWord*>* name_locs = nullptr;
  int i;
  List<BigStr*>* path = nullptr;
  BigStr* var_name = nullptr;
  syntax_asdl::command_t* cmd = nullptr;
  int unused;
  (void)unused;
  Dict<BigStr*, value_asdl::value_t*>* result = nullptr;
  value::Dict* val = nullptr;
  hnode_asdl::hnode_t* h = nullptr;
  mylib::Writer* f = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&action);
  StackRoot _root3(&action_loc);
  StackRoot _root4(&first);
  StackRoot _root5(&names);
  StackRoot _root6(&name_locs);
  StackRoot _root7(&path);
  StackRoot _root8(&var_name);
  StackRoot _root9(&cmd);
  StackRoot _root10(&result);
  StackRoot _root11(&val);
  StackRoot _root12(&h);
  StackRoot _root13(&f);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup0 = arg_r->Peek2();
  action = tup0.at0();
  action_loc = tup0.at1();
  if (action == nullptr) {
    e_usage(_HAY_ACTION_ERROR, action_loc);
  }
  arg_r->Next();
  if (str_equals(action, S_cfl)) {
    Tuple2<BigStr*, syntax_asdl::loc_t*> tup1 = arg_r->Peek2();
    first = tup1.at0();
    if (first == nullptr) {
      e_usage(S_nFj, action_loc);
    }
    Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup2 = arg_r->Rest2();
    names = tup2.at0();
    name_locs = tup2.at1();
    i = 0;
    for (ListIter<BigStr*> it(names); !it.Done(); it.Next(), ++i) {
      BigStr* name = it.Value();
      StackRoot _for(&name    );
      path = name->split(S_ckc);
      for (ListIter<BigStr*> it(path); !it.Done(); it.Next()) {
        BigStr* p = it.Value();
        StackRoot _for(&p      );
        if (len(p) == 0) {
          e_usage(StrFormat("got invalid path %r.  Parts can't be empty.", name), name_locs->at(i));
        }
      }
      this->hay_state->DefinePath(path);
    }
  }
  else {
    if (str_equals(action, S_cCk)) {
      Tuple2<BigStr*, syntax_asdl::loc_t*> tup3 = arg_r->ReadRequired2(S_tbx);
      var_name = tup3.at0();
      if (var_name->startswith(S_fyj)) {
        var_name = var_name->slice(1);
      }
      cmd = typed_args::RequiredBlockAsFrag(cmd_val);
      {  // with
        ctx_HayEval ctx{this->hay_state, this->mutable_opts, this->mem};

        unused = this->cmd_ev->EvalCommandFrag(cmd);
      }
      result = this->hay_state->Result();
      val = Alloc<value::Dict>(result);
      this->mem->SetNamed(location::LName(var_name), val, scope_e::LocalOnly);
    }
    else {
      if (str_equals(action, S_rCB)) {
        this->hay_state->Reset();
      }
      else {
        if (str_equals(action, S_ntm)) {
          h = this->hay_state->root_defs->PrettyTree(false);
          f = mylib::Stdout();
          fmt::HNodePrettyPrint(h, f);
          f->write(S_nfs);
        }
        else {
          e_usage(_HAY_ACTION_ERROR, action_loc);
        }
      }
    }
  }
  return 0;
}

HayNode_::HayNode_(hay_ysh::HayState* hay_state, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev) {
  this->hay_state = hay_state;
  this->mem = mem;
  this->cmd_ev = cmd_ev;
  this->arena = cmd_ev->arena;
}

int HayNode_::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* hay_name = nullptr;
  syntax_asdl::loc_t* arg0_loc = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* result = nullptr;
  BigStr* node_type = nullptr;
  List<BigStr*>* arguments = nullptr;
  value_asdl::LiteralBlock* lit_block = nullptr;
  List<value_asdl::value_t*>* items = nullptr;
  syntax_asdl::BraceGroup* brace_group = nullptr;
  syntax_asdl::SourceLine* line = nullptr;
  BigStr* code_str = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* block_attrs = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* attrs = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&hay_name);
  StackRoot _root3(&arg0_loc);
  StackRoot _root4(&result);
  StackRoot _root5(&node_type);
  StackRoot _root6(&arguments);
  StackRoot _root7(&lit_block);
  StackRoot _root8(&items);
  StackRoot _root9(&brace_group);
  StackRoot _root10(&line);
  StackRoot _root11(&code_str);
  StackRoot _root12(&block_attrs);
  StackRoot _root13(&attrs);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup4 = arg_r->Peek2();
  hay_name = tup4.at0();
  arg0_loc = tup4.at1();
  if (maybe_str_equals(hay_name, S_ktp)) {
    arg_r->Next();
    hay_name = nullptr;
  }
  result = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup5 = arg_r->Peek2();
  node_type = tup5.at0();
  result->set(S_qEi, Alloc<value::Str>(node_type));
  arg_r->Next();
  arguments = arg_r->Rest();
  lit_block = typed_args::OptionalLiteralBlock(cmd_val);
  if ((len(arguments) == 0 and lit_block == nullptr)) {
    e_usage(S_Bvq, arg0_loc);
  }
  items = Alloc<List<value_asdl::value_t*>>();
  for (ListIter<BigStr*> it(arguments); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    items->append(Alloc<value::Str>(s));
  }
  result->set(S_qbm, Alloc<value::List>(items));
  if (node_type->isupper()) {
    if (lit_block == nullptr) {
      e_usage(S_cgB, loc::Missing);
    }
{
      brace_group = lit_block->brace_group;
      line = brace_group->left->line;
      result->set(S_igc, Alloc<value::Str>(ui::GetLineSourceString(line)));
      result->set(S_btu, num::ToBig(line->line_num));
      code_str = alloc::SnipCodeBlock(brace_group->left, brace_group->right, lit_block->lines);
      result->set(S_bAo, Alloc<value::Str>(code_str));
    }
    this->hay_state->AppendResult(result);
  }
  else {
    this->hay_state->AppendResult(result);
    if (lit_block) {
      result->set(S_ccA, Alloc<value::List>(Alloc<List<value_asdl::value_t*>>()));
      {  // with
        state::ctx_Temp ctx{this->mem};

        {  // with
          ctx_HayNode ctx{this->hay_state, hay_name};

          this->cmd_ev->EvalCommandFrag(lit_block->brace_group);
        }
        block_attrs = this->mem->CurrentFrame();
      }
      attrs = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      for (DictIter<BigStr*, runtime_asdl::Cell*> it(block_attrs); !it.Done(); it.Next()) {
        BigStr* name = it.Key();
        runtime_asdl::Cell* cell = it.Value();
        if (name->endswith(S_tci)) {
          continue;
        }
        attrs->set(name, cell->val);
      }
      result->set(S_tac, Alloc<value::Dict>(attrs));
    }
  }
  return 0;
}

}  // define namespace hay_ysh

namespace io_osh {  // define

using id_kind_asdl::Id;
using value_asdl::value;
using value_asdl::value_t;
using error::e_die_status;

Echo::Echo(optview::Exec* exec_opts) {
  this->exec_opts = exec_opts;
  this->f = mylib::Stdout();
  this->simple_flag = nullptr;
}

arg_types::echo* Echo::_SimpleFlag() {
  Dict<BigStr*, value_asdl::value_t*>* attrs = nullptr;
  StackRoot _root0(&attrs);

  if (this->simple_flag == nullptr) {
    attrs = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
    attrs->set(S_ysz, Alloc<value::Bool>(false));
    attrs->set(S_rob, Alloc<value::Bool>(false));
    this->simple_flag = Alloc<arg_types::echo>(attrs);
  }
  return this->simple_flag;
}

int Echo::Run(cmd_value::Argv* cmd_val) {
  List<BigStr*>* argv = nullptr;
  arg_types::echo* arg = nullptr;
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  bool backslash_c;
  List<BigStr*>* new_argv = nullptr;
  List<BigStr*>* parts = nullptr;
  match::SimpleLexer* lex = nullptr;
  int id_;
  BigStr* s = nullptr;
  BigStr* p = nullptr;
  mylib::BufWriter* buf = nullptr;
  int i;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&argv);
  StackRoot _root2(&arg);
  StackRoot _root3(&attrs);
  StackRoot _root4(&arg_r);
  StackRoot _root5(&new_argv);
  StackRoot _root6(&parts);
  StackRoot _root7(&lex);
  StackRoot _root8(&s);
  StackRoot _root9(&p);
  StackRoot _root10(&buf);

  argv = cmd_val->argv->slice(1);
  if (this->exec_opts->simple_echo()) {
    typed_args::DoesNotAccept(cmd_val->proc_args);
    arg = this->_SimpleFlag();
  }
  else {
    Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseLikeEcho(S_svu, cmd_val);
    attrs = tup0.at0();
    arg_r = tup0.at1();
    arg = Alloc<arg_types::echo>(attrs->attrs);
    argv = arg_r->Rest();
  }
  backslash_c = false;
  if (arg->e) {
    new_argv = Alloc<List<BigStr*>>();
    for (ListIter<BigStr*> it(argv); !it.Done(); it.Next()) {
      BigStr* a = it.Value();
      StackRoot _for(&a    );
      parts = Alloc<List<BigStr*>>();
      lex = match::EchoLexer(a);
      while (!backslash_c) {
        Tuple2<int, BigStr*> tup1 = lex->Next();
        id_ = tup1.at0();
        s = tup1.at1();
        if (id_ == Id::Eol_Tok) {
          break;
        }
        p = word_compile::EvalCStringToken(id_, s);
        if (p == nullptr) {
          backslash_c = true;
          break;
        }
        parts->append(p);
      }
      new_argv->append(S_Aoo->join(parts));
      if (backslash_c) {
        break;
      }
    }
    argv = new_argv;
  }
  buf = Alloc<mylib::BufWriter>();
  i = 0;
  for (ListIter<BigStr*> it(argv); !it.Done(); it.Next(), ++i) {
    BigStr* a = it.Value();
    StackRoot _for(&a  );
    if (i != 0) {
      buf->write(S_yfw);
    }
    buf->write(a);
  }
  if ((!arg->n and !backslash_c)) {
    buf->write(S_nfs);
  }
  this->f->write(buf->getvalue());
  return 0;
}

MapFile::MapFile(state::Mem* mem, ui::ErrorFormatter* errfmt, cmd_eval::CommandEvaluator* cmd_ev) {
  this->mem = mem;
  this->errfmt = errfmt;
  this->cmd_ev = cmd_ev;
}

int MapFile::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::mapfile* arg = nullptr;
  BigStr* var_name = nullptr;
  List<BigStr*>* lines = nullptr;
  BigStr* line = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&var_name);
  StackRoot _root5(&lines);
  StackRoot _root6(&line);

  Tuple2<args::_Attributes*, args::Reader*> tup2 = flag_util::ParseCmdVal(S_fhy, cmd_val);
  attrs = tup2.at0();
  arg_r = tup2.at1();
  arg = Alloc<arg_types::mapfile>(attrs->attrs);
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup3 = arg_r->Peek2();
  var_name = tup3.at0();
  if (var_name == nullptr) {
    var_name = S_nfF;
  }
  lines = Alloc<List<BigStr*>>();
  while (true) {
    try {
      line = read_osh::ReadLineSlowly(this->cmd_ev, !arg->t);
    }
    catch (pyos::ReadError* e) {
      this->errfmt->PrintMessage(StrFormat("mapfile: read() error: %s", posix::strerror(e->err_num)));
      return 1;
    }
    if (len(line) == 0) {
      break;
    }
    lines->append(line);
  }
  state::BuiltinSetArray(this->mem, var_name, lines);
  return 0;
}

Cat::Cat() : ::vm::_Builtin() {
}

int Cat::Run(cmd_value::Argv* cmd_val) {
  List<BigStr*>* chunks = nullptr;
  int n;
  int err_num;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&chunks);

  chunks = Alloc<List<BigStr*>>();
  while (true) {
    Tuple2<int, int> tup4 = pyos::Read(0, 4096, chunks);
    n = tup4.at0();
    err_num = tup4.at1();
    if (n < 0) {
      if (err_num == EINTR) {
        ;  // pass
      }
      else {
        e_die_status(2, StrFormat("osh I/O error: %s", posix::strerror(err_num)));
      }
    }
    else {
      if (n == 0) {
        break;
      }
      else {
        mylib::Stdout()->write(chunks->at(0));
        chunks->pop();
      }
    }
  }
  return 0;
}

}  // define namespace io_osh

namespace io_ysh {  // define

using runtime_asdl::cmd_value;
using syntax_asdl::command_e;
using syntax_asdl::BraceGroup;
using value_asdl::value;
using value_asdl::value_e;
namespace fmt = format;
using error::e_usage;

_Builtin::_Builtin(state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->errfmt = errfmt;
}

Pp::Pp(expr_eval::ExprEvaluator* expr_ev, state::Mem* mem, ui::ErrorFormatter* errfmt, state::Procs* procs, alloc::Arena* arena) : ::io_ysh::_Builtin(mem, errfmt) {
  this->expr_ev = expr_ev;
  this->procs = procs;
  this->arena = arena;
  this->stdout_ = mylib::Stdout();
}

int Pp::_PrettyPrint(cmd_value::Argv* cmd_val) {
  typed_args::Reader* rd = nullptr;
  value_asdl::value_t* val = nullptr;
  syntax_asdl::Token* blame_tok = nullptr;
  BigStr* excerpt = nullptr;
  BigStr* prefix = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&rd);
  StackRoot _root2(&val);
  StackRoot _root3(&blame_tok);
  StackRoot _root4(&excerpt);
  StackRoot _root5(&prefix);

  rd = typed_args::ReaderForProc(cmd_val);
  val = rd->PosValue();
  rd->Done();
  blame_tok = rd->LeftParenToken();
  this->stdout_->write(S_nfs);
  Tuple2<BigStr*, BigStr*> tup0 = ui::CodeExcerptAndPrefix(blame_tok);
  excerpt = tup0.at0();
  prefix = tup0.at1();
  this->stdout_->write(excerpt);
  ui::PrettyPrintValue(prefix, val, this->stdout_);
  return 0;
}

int Pp::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* arg = nullptr;
  args::Reader* arg_r = nullptr;
  BigStr* action = nullptr;
  syntax_asdl::loc_t* action_loc = nullptr;
  typed_args::Reader* rd = nullptr;
  value_asdl::value_t* val = nullptr;
  hnode_asdl::hnode_t* tree = nullptr;
  int max_width;
  BigStr* ysh_type = nullptr;
  List<BigStr*>* argv = nullptr;
  List<syntax_asdl::CompoundWord*>* locs = nullptr;
  int status;
  int i;
  runtime_asdl::Cell* cell = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* top = nullptr;
  List<BigStr*>* names = nullptr;
  value_asdl::value_t* node = nullptr;
  value_asdl::value_t* proc_val = nullptr;
  value::Proc* user_proc = nullptr;
  syntax_asdl::command_t* body = nullptr;
  BigStr* doc = nullptr;
  syntax_asdl::BraceGroup* bgroup = nullptr;
  syntax_asdl::Token* token = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&action);
  StackRoot _root4(&action_loc);
  StackRoot _root5(&rd);
  StackRoot _root6(&val);
  StackRoot _root7(&tree);
  StackRoot _root8(&ysh_type);
  StackRoot _root9(&argv);
  StackRoot _root10(&locs);
  StackRoot _root11(&cell);
  StackRoot _root12(&top);
  StackRoot _root13(&names);
  StackRoot _root14(&node);
  StackRoot _root15(&proc_val);
  StackRoot _root16(&user_proc);
  StackRoot _root17(&body);
  StackRoot _root18(&doc);
  StackRoot _root19(&bgroup);
  StackRoot _root20(&token);
  StackRoot _root21(&buf);

  Tuple2<args::_Attributes*, args::Reader*> tup1 = flag_util::ParseCmdVal(S_ntm, cmd_val, true);
  arg = tup1.at0();
  arg_r = tup1.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup2 = arg_r->Peek2();
  action = tup2.at0();
  action_loc = tup2.at1();
  if (action == nullptr) {
    return this->_PrettyPrint(cmd_val);
  }
  arg_r->Next();
  if (str_equals(action, S_tFk)) {
    rd = typed_args::ReaderForProc(cmd_val);
    val = rd->PosValue();
    rd->Done();
    ui::PrettyPrintValue(S_Aoo, val, this->stdout_);
    return 0;
  }
  if (str_equals(action, S_qid)) {
    rd = typed_args::ReaderForProc(cmd_val);
    val = rd->PosValue();
    rd->Done();
    tree = val->PrettyTree(false);
    max_width = ui::_GetMaxWidth();
    fmt::HNodePrettyPrint(tree, this->stdout_, max_width);
    return 0;
  }
  if (str_equals(action, S_zum)) {
    rd = typed_args::ReaderForProc(cmd_val);
    val = rd->PosValue();
    rd->Done();
    if (ui::TypeNotPrinted(val)) {
      ysh_type = ui::ValType(val);
      this->stdout_->write(StrFormat("(%s)   ", ysh_type));
    }
    j8::PrintLine(val, this->stdout_);
    return 0;
  }
  if (str_equals(action, S_iBf)) {
    Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup3 = arg_r->Rest2();
    argv = tup3.at0();
    locs = tup3.at1();
    status = 0;
    i = 0;
    for (ListIter<BigStr*> it(argv); !it.Done(); it.Next(), ++i) {
      BigStr* name = it.Value();
      StackRoot _for(&name    );
      if (!match::IsValidVarName(name)) {
        throw Alloc<error::Usage>(StrFormat("got invalid variable name %r", name), locs->at(i));
      }
      cell = this->mem->GetCell(name);
      if (cell == nullptr) {
        this->errfmt->Print_(StrFormat("Couldn't find a variable named %r", name), locs->at(i));
        status = 1;
      }
      else {
        this->stdout_->write(StrFormat("%s = ", name));
        fmt::HNodePrettyPrint(cell->PrettyTree(false), this->stdout_);
      }
    }
    return status;
  }
  if (str_equals(action, S_Ekj)) {
    return 0;
  }
  if (str_equals(action, S_DDx)) {
    top = this->mem->var_stack->at(-1);
    print(StrFormat("    [frame_vars_] %s", S_yfw->join(top->keys())));
    return 0;
  }
  if (str_equals(action, S_Fhp)) {
    print(S_wfg);
    return 0;
  }
  if (str_equals(action, S_aFi)) {
    Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup4 = arg_r->Rest2();
    names = tup4.at0();
    locs = tup4.at1();
    if (len(names)) {
      i = 0;
      for (ListIter<BigStr*> it(names); !it.Done(); it.Next(), ++i) {
        BigStr* name = it.Value();
        StackRoot _for(&name      );
        Tuple2<value_asdl::value_t*, value_asdl::Obj*> tup5 = this->procs->GetInvokable(name);
        node = tup5.at0();
        if (node == nullptr) {
          this->errfmt->Print_(StrFormat("Invalid proc %r", name), locs->at(i));
          return 1;
        }
      }
    }
    else {
      names = this->procs->InvokableNames();
    }
    print(S_qdr);
    for (ListIter<BigStr*> it(names); !it.Done(); it.Next()) {
      BigStr* name = it.Value();
      StackRoot _for(&name    );
      Tuple2<value_asdl::value_t*, value_asdl::Obj*> tup6 = this->procs->GetInvokable(name);
      proc_val = tup6.at0();
      if (proc_val->tag() != value_e::Proc) {
        continue;
      }
      user_proc = static_cast<value::Proc*>(proc_val);
      body = user_proc->body;
      doc = S_Aoo;
      if (body->tag() == command_e::BraceGroup) {
        bgroup = static_cast<BraceGroup*>(body);
        if (bgroup->doc_token) {
          token = bgroup->doc_token;
          doc = token->line->content->slice((token->col + 1), (token->col + token->length));
        }
      }
      buf = Alloc<mylib::BufWriter>();
      j8::EncodeString(name, buf, true);
      buf->write(S_mve);
      j8::EncodeString(doc, buf, true);
      print(buf->getvalue());
    }
    return 0;
  }
  e_usage(StrFormat("got invalid action %r", action), action_loc);
}

Write::Write(state::Mem* mem, ui::ErrorFormatter* errfmt) : ::io_ysh::_Builtin(mem, errfmt) {
  this->stdout_ = mylib::Stdout();
}

int Write::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::write* arg = nullptr;
  int i;
  BigStr* s = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&s);

  Tuple2<args::_Attributes*, args::Reader*> tup7 = flag_util::ParseCmdVal(S_bhu, cmd_val);
  attrs = tup7.at0();
  arg_r = tup7.at1();
  arg = Alloc<arg_types::write>(attrs->attrs);
  i = 0;
  while (!arg_r->AtEnd()) {
    if (i != 0) {
      this->stdout_->write(arg->sep);
    }
    s = arg_r->Peek();
    if (arg->json) {
      s = j8::MaybeEncodeJsonString(s);
    }
    else {
      if (arg->j8) {
        s = j8::MaybeEncodeString(s);
      }
    }
    this->stdout_->write(s);
    arg_r->Next();
    i += 1;
  }
  if (arg->n) {
    ;  // pass
  }
  else {
    if (len(arg->end)) {
      this->stdout_->write(arg->end);
    }
  }
  return 0;
}

RunBlock::RunBlock(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev) {
  this->mem = mem;
  this->cmd_ev = cmd_ev;
}

int RunBlock::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  syntax_asdl::command_t* cmd_frag = nullptr;
  int unused;
  (void)unused;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&cmd_frag);

  Tuple2<args::_Attributes*, args::Reader*> tup8 = flag_util::ParseCmdVal(S_xzn, cmd_val, true);
  arg_r = tup8.at1();
  cmd_frag = typed_args::RequiredBlockAsFrag(cmd_val);
  unused = this->cmd_ev->EvalCommandFrag(cmd_frag);
  return 0;
}

}  // define namespace io_ysh

namespace json_ysh {  // define

using runtime_asdl::cmd_value;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using value_asdl::value;
using value_asdl::LeftName;
using error::e_usage;
BigStr* _JSON_ACTION_ERROR = S_lmC;

Json::Json(state::Mem* mem, ui::ErrorFormatter* errfmt, bool is_j8) {
  this->mem = mem;
  this->errfmt = errfmt;
  this->is_j8 = is_j8;
  this->name = is_j8 ? S_Evb : S_fiw;
  this->stdout_ = mylib::Stdout();
}

int Json::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* action = nullptr;
  syntax_asdl::loc_t* action_loc = nullptr;
  args::_Attributes* attrs = nullptr;
  arg_types::json_write* arg_jw = nullptr;
  typed_args::Reader* rd = nullptr;
  value_asdl::value_t* val = nullptr;
  int space;
  int indent;
  mylib::BufWriter* buf = nullptr;
  value::Place* place = nullptr;
  syntax_asdl::loc_t* blame_loc = nullptr;
  BigStr* var_name = nullptr;
  BigStr* contents = nullptr;
  j8::Parser* p = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&action);
  StackRoot _root3(&action_loc);
  StackRoot _root4(&attrs);
  StackRoot _root5(&arg_jw);
  StackRoot _root6(&rd);
  StackRoot _root7(&val);
  StackRoot _root8(&buf);
  StackRoot _root9(&place);
  StackRoot _root10(&blame_loc);
  StackRoot _root11(&var_name);
  StackRoot _root12(&contents);
  StackRoot _root13(&p);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup0 = arg_r->Peek2();
  action = tup0.at0();
  action_loc = tup0.at1();
  if (action == nullptr) {
    throw Alloc<error::Usage>(_JSON_ACTION_ERROR, loc::Missing);
  }
  arg_r->Next();
  if (str_equals(action, S_bhu)) {
    attrs = flag_util::Parse(S_EFp, arg_r);
    arg_jw = Alloc<arg_types::json_write>(attrs->attrs);
    if (!arg_r->AtEnd()) {
      e_usage(S_cvm_1, arg_r->Location());
    }
    rd = typed_args::ReaderForProc(cmd_val);
    val = rd->PosValue();
    space = mops::BigTruncate(rd->NamedInt(S_iya, 2));
    rd->Done();
    if (space <= 0) {
      indent = -1;
    }
    else {
      indent = space;
    }
    buf = Alloc<mylib::BufWriter>();
    try {
      if (this->is_j8) {
        j8::PrintMessage(val, buf, indent);
      }
      else {
        j8::PrintJsonMessage(val, buf, indent);
      }
    }
    catch (error::Encode* e) {
      this->errfmt->PrintMessage(StrFormat("%s write: %s", this->name, e->Message()), action_loc);
      return 1;
    }
    this->stdout_->write(buf->getvalue());
    this->stdout_->write(S_nfs);
  }
  else {
    if (str_equals(action, S_hDl)) {
      attrs = flag_util::Parse(S_ctf, arg_r);
      if (cmd_val->proc_args) {
        rd = typed_args::ReaderForProc(cmd_val);
        place = rd->PosPlace();
        rd->Done();
        blame_loc = cmd_val->proc_args->typed_args->left;
      }
      else {
        var_name = S_eys;
        blame_loc = cmd_val->arg_locs->at(0);
        place = Alloc<value::Place>(Alloc<LeftName>(var_name, blame_loc), this->mem->CurrentFrame());
      }
      if (!arg_r->AtEnd()) {
        e_usage(S_wpE, arg_r->Location());
      }
      try {
        contents = read_osh::ReadAll();
      }
      catch (pyos::ReadError* e) {
        this->errfmt->PrintMessage(StrFormat("read error: %s", posix::strerror(e->err_num)));
        return 1;
      }
      p = Alloc<j8::Parser>(contents, this->is_j8);
      try {
        val = p->ParseValue();
      }
      catch (error::Decode* err) {
        this->errfmt->Print_(StrFormat("%s read: %s", this->name, err->Message()), action_loc);
        return 1;
      }
      this->mem->SetPlace(place, val, blame_loc);
    }
    else {
      throw Alloc<error::Usage>(_JSON_ACTION_ERROR, action_loc);
    }
  }
  return 0;
}

}  // define namespace json_ysh

namespace meta_oils {  // define

using runtime_asdl::cmd_value;
using runtime_asdl::CommandStatus;
using syntax_asdl::source;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::CompoundWord;
using value_asdl::Obj;
using value_asdl::value;
using value_asdl::value_t;
using error::e_usage;
using mylib::print_stderr;

Eval::Eval(parse_lib::ParseContext* parse_ctx, optview::Exec* exec_opts, cmd_eval::CommandEvaluator* cmd_ev, dev::Tracer* tracer, ui::ErrorFormatter* errfmt, state::Mem* mem) {
  this->parse_ctx = parse_ctx;
  this->arena = parse_ctx->arena;
  this->exec_opts = exec_opts;
  this->cmd_ev = cmd_ev;
  this->tracer = tracer;
  this->errfmt = errfmt;
  this->mem = mem;
}

int Eval::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* code_str = nullptr;
  syntax_asdl::loc_t* eval_loc = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  source::Dynamic* src = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&code_str);
  StackRoot _root3(&eval_loc);
  StackRoot _root4(&line_reader);
  StackRoot _root5(&c_parser);
  StackRoot _root6(&src);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_cCk, cmd_val);
  arg_r = tup0.at1();
  if (this->exec_opts->simple_eval_builtin()) {
    Tuple2<BigStr*, syntax_asdl::loc_t*> tup1 = arg_r->ReadRequired2(S_cpz);
    code_str = tup1.at0();
    eval_loc = tup1.at1();
    if (!arg_r->AtEnd()) {
      e_usage(S_rxc, loc::Missing);
    }
  }
  else {
    code_str = S_yfw->join(arg_r->Rest());
    eval_loc = cmd_val->arg_locs->at(0);
  }
  line_reader = reader::StringLineReader(code_str, this->arena);
  c_parser = this->parse_ctx->MakeOshParser(line_reader);
  src = Alloc<source::Dynamic>(S_ifC, eval_loc);
  {  // with
    dev::ctx_Tracer ctx{this->tracer, S_cCk, nullptr};

    {  // with
      alloc::ctx_SourceCode ctx{this->arena, src};

      return main_loop::Batch(this->cmd_ev, c_parser, this->errfmt, cmd_eval::RaiseControlFlow);
    }
  }
}

BigStr* _VarName(BigStr* module_path) {
  BigStr* basename = nullptr;
  int i;
  StackRoot _root0(&module_path);
  StackRoot _root1(&basename);

  basename = os_path::basename(module_path);
  i = basename->rfind(S_Aru);
  if (i != -1) {
    basename = basename->slice(0, i);
  }
  return basename;
}

ShellFile::ShellFile(parse_lib::ParseContext* parse_ctx, executor::SearchPath* search_path, cmd_eval::CommandEvaluator* cmd_ev, process::FdState* fd_state, dev::Tracer* tracer, ui::ErrorFormatter* errfmt, pyutil::_ResourceLoader* loader, vm::_Builtin* module_invoke) {
  this->parse_ctx = parse_ctx;
  this->arena = parse_ctx->arena;
  this->search_path = search_path;
  this->cmd_ev = cmd_ev;
  this->fd_state = fd_state;
  this->tracer = tracer;
  this->errfmt = errfmt;
  this->loader = loader;
  this->module_invoke = module_invoke;
  this->builtin_name = module_invoke ? S_eas : S_cmd;
  this->mem = cmd_ev->mem;
  this->_disk_cache = Alloc<Dict<BigStr*, value_asdl::Obj*>>();
  this->_embed_cache = Alloc<Dict<BigStr*, value_asdl::Obj*>>();
}

int ShellFile::Run(cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  if (this->module_invoke) {
    return this->_Use(cmd_val);
  }
  else {
    return this->_Source(cmd_val);
  }
}

Tuple2<BigStr*, cmd_parse::CommandParser*> ShellFile::LoadEmbeddedFile(BigStr* embed_path, syntax_asdl::loc_t* blame_loc) {
  BigStr* load_path = nullptr;
  BigStr* contents = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  StackRoot _root0(&embed_path);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&load_path);
  StackRoot _root3(&contents);
  StackRoot _root4(&line_reader);
  StackRoot _root5(&c_parser);

  try {
    load_path = os_path::join(S_vwz, embed_path);
    contents = this->loader->Get(load_path);
  }
  catch (IOError_OSError*) {
    this->errfmt->Print_(StrFormat("%r failed: No builtin file %r", this->builtin_name, load_path), blame_loc);
    return Tuple2<BigStr*, cmd_parse::CommandParser*>(nullptr, nullptr);
  }
  line_reader = reader::StringLineReader(contents, this->arena);
  c_parser = this->parse_ctx->MakeOshParser(line_reader);
  return Tuple2<BigStr*, cmd_parse::CommandParser*>(load_path, c_parser);
}

Tuple2<mylib::LineReader*, cmd_parse::CommandParser*> ShellFile::_LoadDiskFile(BigStr* fs_path, syntax_asdl::loc_t* blame_loc) {
  mylib::LineReader* f = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  StackRoot _root0(&fs_path);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&f);
  StackRoot _root3(&line_reader);
  StackRoot _root4(&c_parser);

  try {
    f = this->fd_state->Open(fs_path);
  }
  catch (IOError_OSError* e) {
    this->errfmt->Print_(StrFormat("%s %r failed: %s", this->builtin_name, fs_path, pyutil::strerror(e)), blame_loc);
    return Tuple2<mylib::LineReader*, cmd_parse::CommandParser*>(nullptr, nullptr);
  }
  line_reader = Alloc<reader::FileLineReader>(f, this->arena);
  c_parser = this->parse_ctx->MakeOshParser(line_reader);
  return Tuple2<mylib::LineReader*, cmd_parse::CommandParser*>(f, c_parser);
}

int ShellFile::_SourceExec(cmd_value::Argv* cmd_val, args::Reader* arg_r, BigStr* path, cmd_parse::CommandParser* c_parser) {
  syntax_asdl::CompoundWord* call_loc = nullptr;
  List<BigStr*>* source_argv = nullptr;
  source::OtherFile* src = nullptr;
  int status;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&path);
  StackRoot _root3(&c_parser);
  StackRoot _root4(&call_loc);
  StackRoot _root5(&source_argv);
  StackRoot _root6(&src);

  call_loc = cmd_val->arg_locs->at(0);
  {  // with
    dev::ctx_Tracer ctx{this->tracer, S_cmd, cmd_val->argv};

    source_argv = arg_r->Rest();
    {  // with
      state::ctx_Source ctx{this->mem, path, source_argv};

      {  // with
        state::ctx_ThisDir ctx{this->mem, path};

        src = Alloc<source::OtherFile>(path, call_loc);
        {  // with
          alloc::ctx_SourceCode ctx{this->arena, src};

          try {
            status = main_loop::Batch(this->cmd_ev, c_parser, this->errfmt, cmd_eval::RaiseControlFlow);
          }
          catch (vm::IntControlFlow* e) {
            if (e->IsReturn()) {
              status = e->StatusCode();
            }
            else {
              throw;
            }
          }
        }
      }
    }
  }
  return status;
}

value_asdl::Obj* ShellFile::_NewModule() {
  value_asdl::Obj* methods = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* props = nullptr;
  value_asdl::Obj* module_obj = nullptr;
  StackRoot _root0(&methods);
  StackRoot _root1(&props);
  StackRoot _root2(&module_obj);

  methods = Alloc<Obj>(nullptr, Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_fBo}, std::initializer_list<value_asdl::value_t*>{Alloc<value::BuiltinProc>(this->module_invoke)}));
  props = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  module_obj = Alloc<Obj>(methods, props);
  return module_obj;
}

int ShellFile::_UseExec(cmd_value::Argv* cmd_val, BigStr* path, syntax_asdl::loc_t* path_loc, cmd_parse::CommandParser* c_parser, Dict<BigStr*, value_asdl::value_t*>* props) {
  List<BigStr*>* error_strs = nullptr;
  source::OtherFile* src = nullptr;
  int status;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&path);
  StackRoot _root2(&path_loc);
  StackRoot _root3(&c_parser);
  StackRoot _root4(&props);
  StackRoot _root5(&error_strs);
  StackRoot _root6(&src);

  error_strs = Alloc<List<BigStr*>>();
  {  // with
    dev::ctx_Tracer ctx{this->tracer, S_eas, cmd_val->argv};

    {  // with
      state::ctx_ModuleEval ctx{this->mem, props, error_strs};

      {  // with
        state::ctx_ThisDir ctx{this->mem, path};

        src = Alloc<source::OtherFile>(path, path_loc);
        {  // with
          alloc::ctx_SourceCode ctx{this->arena, src};

          try {
            status = main_loop::Batch(this->cmd_ev, c_parser, this->errfmt, cmd_eval::RaiseControlFlow);
          }
          catch (vm::IntControlFlow* e) {
            if (e->IsReturn()) {
              status = e->StatusCode();
            }
            else {
              throw;
            }
          }
          if (status != 0) {
            return status;
          }
        }
      }
    }
  }
  if (len(error_strs)) {
    for (ListIter<BigStr*> it(error_strs); !it.Done(); it.Next()) {
      BigStr* s = it.Value();
      StackRoot _for(&s    );
      this->errfmt->PrintMessage(StrFormat("Error: %s", s), path_loc);
    }
    return 1;
  }
  return 0;
}

int ShellFile::_Source(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::source* arg = nullptr;
  BigStr* path_arg = nullptr;
  syntax_asdl::loc_t* path_loc = nullptr;
  BigStr* embed_path = nullptr;
  BigStr* load_path = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  BigStr* resolved = nullptr;
  mylib::LineReader* f = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&path_arg);
  StackRoot _root5(&path_loc);
  StackRoot _root6(&embed_path);
  StackRoot _root7(&load_path);
  StackRoot _root8(&c_parser);
  StackRoot _root9(&resolved);
  StackRoot _root10(&f);

  Tuple2<args::_Attributes*, args::Reader*> tup2 = flag_util::ParseCmdVal(S_cmd, cmd_val);
  attrs = tup2.at0();
  arg_r = tup2.at1();
  arg = Alloc<arg_types::source>(attrs->attrs);
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup3 = arg_r->ReadRequired2(S_fon);
  path_arg = tup3.at0();
  path_loc = tup3.at1();
  embed_path = nullptr;
  if (arg->builtin) {
    embed_path = path_arg;
  }
  else {
    if (path_arg->startswith(S_gEs)) {
      embed_path = path_arg->slice(3);
    }
  }
  if (embed_path != nullptr) {
    Tuple2<BigStr*, cmd_parse::CommandParser*> tup4 = this->LoadEmbeddedFile(embed_path, path_loc);
    load_path = tup4.at0();
    c_parser = tup4.at1();
    if (c_parser == nullptr) {
      return 1;
    }
    return this->_SourceExec(cmd_val, arg_r, load_path, c_parser);
  }
  else {
    resolved = this->search_path->LookupOne(path_arg, false);
    if (resolved == nullptr) {
      resolved = path_arg;
    }
    Tuple2<mylib::LineReader*, cmd_parse::CommandParser*> tup5 = this->_LoadDiskFile(resolved, path_loc);
    f = tup5.at0();
    c_parser = tup5.at1();
    if (c_parser == nullptr) {
      return 1;
    }
    {  // with
      process::ctx_FileCloser ctx{f};

      return this->_SourceExec(cmd_val, arg_r, path_arg, c_parser);
    }
  }
  assert(0);  // AssertionError
}

int ShellFile::_BindNames(value_asdl::Obj* module_obj, BigStr* module_name, List<BigStr*>* pick_names, List<syntax_asdl::CompoundWord*>* pick_locs) {
  int i;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&module_obj);
  StackRoot _root1(&module_name);
  StackRoot _root2(&pick_names);
  StackRoot _root3(&pick_locs);
  StackRoot _root4(&val);

  state::SetGlobalValue(this->mem, module_name, module_obj);
  if (pick_names == nullptr) {
    return 0;
  }
  i = 0;
  for (ListIter<BigStr*> it(pick_names); !it.Done(); it.Next(), ++i) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    val = module_obj->d->get(name);
    if (val == nullptr) {
      this->errfmt->Print_(StrFormat("use: module doesn't provide name %r", name), pick_locs->at(i));
      return 1;
    }
    state::SetGlobalValue(this->mem, name, val);
  }
  return 0;
}

int ShellFile::_Use(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::use* arg = nullptr;
  BigStr* path_arg = nullptr;
  syntax_asdl::loc_t* path_loc = nullptr;
  List<BigStr*>* pick_names = nullptr;
  List<syntax_asdl::CompoundWord*>* pick_locs = nullptr;
  BigStr* flag = nullptr;
  syntax_asdl::loc_t* flag_loc = nullptr;
  BigStr* p = nullptr;
  BigStr* embed_path = nullptr;
  BigStr* var_name = nullptr;
  value_asdl::Obj* cached_obj = nullptr;
  BigStr* load_path = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  value_asdl::Obj* module_obj = nullptr;
  int status;
  BigStr* normalized = nullptr;
  mylib::LineReader* f = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&path_arg);
  StackRoot _root5(&path_loc);
  StackRoot _root6(&pick_names);
  StackRoot _root7(&pick_locs);
  StackRoot _root8(&flag);
  StackRoot _root9(&flag_loc);
  StackRoot _root10(&p);
  StackRoot _root11(&embed_path);
  StackRoot _root12(&var_name);
  StackRoot _root13(&cached_obj);
  StackRoot _root14(&load_path);
  StackRoot _root15(&c_parser);
  StackRoot _root16(&module_obj);
  StackRoot _root17(&normalized);
  StackRoot _root18(&f);

  Tuple2<args::_Attributes*, args::Reader*> tup6 = flag_util::ParseCmdVal(S_eas, cmd_val);
  attrs = tup6.at0();
  arg_r = tup6.at1();
  arg = Alloc<arg_types::use>(attrs->attrs);
  if (arg->extern_) {
    return 0;
  }
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup7 = arg_r->ReadRequired2(S_yie);
  path_arg = tup7.at0();
  path_loc = tup7.at1();
  pick_names = nullptr;
  pick_locs = nullptr;
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup8 = arg_r->Peek2();
  flag = tup8.at0();
  flag_loc = tup8.at1();
  if (flag != nullptr) {
    if (str_equals(flag, S_fic)) {
      arg_r->Next();
      p = arg_r->Peek();
      if (p == nullptr) {
        throw Alloc<error::Usage>(S_bbj, flag_loc);
      }
      Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup9 = arg_r->Rest2();
      pick_names = tup9.at0();
      pick_locs = tup9.at1();
    }
    else {
      if (str_equals(flag, S_Ajh)) {
        arg_r->Next();
        arg_r->Done();
        print(S_Awz);
      }
      else {
        if (str_equals(flag, S_FaA)) {
          arg_r->Next();
          arg_r->Done();
          print(S_Esj);
        }
        else {
          throw Alloc<error::Usage>(S_pDm, flag_loc);
        }
      }
    }
  }
  if (path_arg->startswith(S_gEs)) {
    embed_path = path_arg->slice(3);
  }
  else {
    embed_path = nullptr;
  }
  if (this->mem->InsideFunction()) {
    throw Alloc<error::Usage>(S_cAk, path_loc);
  }
  var_name = _VarName(path_arg);
  if (embed_path != nullptr) {
    cached_obj = this->_embed_cache->get(embed_path);
    if (cached_obj) {
      return this->_BindNames(cached_obj, var_name, pick_names, pick_locs);
    }
    Tuple2<BigStr*, cmd_parse::CommandParser*> tup10 = this->LoadEmbeddedFile(embed_path, path_loc);
    load_path = tup10.at0();
    c_parser = tup10.at1();
    if (c_parser == nullptr) {
      return 1;
    }
    module_obj = this->_NewModule();
    this->_embed_cache->set(embed_path, module_obj);
    status = this->_UseExec(cmd_val, load_path, path_loc, c_parser, module_obj->d);
    if (status != 0) {
      return status;
    }
    return this->_BindNames(module_obj, var_name, pick_names, pick_locs);
  }
  else {
    normalized = libc::realpath(path_arg);
    if (normalized == nullptr) {
      this->errfmt->Print_(StrFormat("use: couldn't find %r", path_arg), path_loc);
      return 1;
    }
    cached_obj = this->_disk_cache->get(normalized);
    if (cached_obj) {
      return this->_BindNames(cached_obj, var_name, pick_names, pick_locs);
    }
    Tuple2<mylib::LineReader*, cmd_parse::CommandParser*> tup11 = this->_LoadDiskFile(normalized, path_loc);
    f = tup11.at0();
    c_parser = tup11.at1();
    if (c_parser == nullptr) {
      return 1;
    }
    module_obj = this->_NewModule();
    this->_disk_cache->set(normalized, module_obj);
    {  // with
      process::ctx_FileCloser ctx{f};

      status = this->_UseExec(cmd_val, path_arg, path_loc, c_parser, module_obj->d);
    }
    if (status != 0) {
      return status;
    }
    return this->_BindNames(module_obj, var_name, pick_names, pick_locs);
  }
  return 0;
}

void _PrintFreeForm(Tuple3<BigStr*, BigStr*, BigStr*>* row) {
  BigStr* name = nullptr;
  BigStr* kind = nullptr;
  BigStr* resolved = nullptr;
  BigStr* what = nullptr;
  StackRoot _root0(&row);
  StackRoot _root1(&name);
  StackRoot _root2(&kind);
  StackRoot _root3(&resolved);
  StackRoot _root4(&what);

  Tuple3<BigStr*, BigStr*, BigStr*>* tup12 = row;
  name = tup12->at0();
  kind = tup12->at1();
  resolved = tup12->at2();
  if (str_equals(kind, S_xeh)) {
    what = resolved;
  }
  else {
    if (str_equals(kind, S_nwn)) {
      what = StrFormat("an alias for %s", j8_lite::EncodeString(resolved, true));
    }
    else {
      if ((str_equals(kind, S_aFi) || str_equals(kind, S_jvb))) {
        what = StrFormat("a YSH %s", kind);
      }
      else {
        what = StrFormat("a shell %s", kind);
      }
    }
  }
  print(StrFormat("%s is %s", name, what));
}

void _PrintEntry(arg_types::type* arg, Tuple3<BigStr*, BigStr*, BigStr*>* row) {
  BigStr* kind = nullptr;
  BigStr* resolved = nullptr;
  StackRoot _root0(&arg);
  StackRoot _root1(&row);
  StackRoot _root2(&kind);
  StackRoot _root3(&resolved);

  Tuple3<BigStr*, BigStr*, BigStr*>* tup13 = row;
  kind = tup13->at1();
  resolved = tup13->at2();
  if (arg->t) {
    print(kind);
  }
  else {
    if (arg->p) {
      if (str_equals(kind, S_xeh)) {
        print(resolved);
      }
    }
    else {
      _PrintFreeForm(row);
    }
  }
}

Command::Command(vm::_Executor* shell_ex, state::Procs* funcs, Dict<BigStr*, BigStr*>* aliases, executor::SearchPath* search_path) {
  this->shell_ex = shell_ex;
  this->funcs = funcs;
  this->aliases = aliases;
  this->search_path = search_path;
}

int Command::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::command* arg = nullptr;
  List<BigStr*>* argv = nullptr;
  List<syntax_asdl::CompoundWord*>* locs = nullptr;
  int status;
  List<Tuple3<BigStr*, BigStr*, BigStr*>*>* r = nullptr;
  Tuple3<BigStr*, BigStr*, BigStr*>* row = nullptr;
  BigStr* name = nullptr;
  BigStr* path = nullptr;
  cmd_value::Argv* cmd_val2 = nullptr;
  runtime_asdl::CommandStatus* cmd_st = nullptr;
  int run_flags;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&argv);
  StackRoot _root5(&locs);
  StackRoot _root6(&r);
  StackRoot _root7(&row);
  StackRoot _root8(&name);
  StackRoot _root9(&path);
  StackRoot _root10(&cmd_val2);
  StackRoot _root11(&cmd_st);

  Tuple2<args::_Attributes*, args::Reader*> tup14 = flag_util::ParseCmdVal(S_zij, cmd_val, true);
  attrs = tup14.at0();
  arg_r = tup14.at1();
  arg = Alloc<arg_types::command>(attrs->attrs);
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup15 = arg_r->Rest2();
  argv = tup15.at0();
  locs = tup15.at1();
  if ((arg->v or arg->V)) {
    status = 0;
    for (ListIter<BigStr*> it(argv); !it.Done(); it.Next()) {
      BigStr* argument = it.Value();
      StackRoot _for(&argument    );
      r = _ResolveName(argument, this->funcs, this->aliases, this->search_path, false);
      if (len(r)) {
        row = r->at(0);
        if (arg->v) {
          Tuple3<BigStr*, BigStr*, BigStr*>* tup16 = row;
          name = tup16->at0();
          path = tup16->at2();
          if (path != nullptr) {
            print(path);
          }
          else {
            print(name);
          }
        }
        else {
          _PrintFreeForm(row);
        }
      }
      else {
        print_stderr(StrFormat("%s: not found", argument));
        status = 1;
      }
    }
    return status;
  }
  cmd_val2 = Alloc<cmd_value::Argv>(argv, locs, cmd_val->is_last_cmd, cmd_val->self_obj, cmd_val->proc_args);
  cmd_st = CommandStatus::CreateNull(true);
  run_flags = executor::NO_CALL_PROCS;
  if (cmd_val->is_last_cmd) {
    run_flags |= executor::IS_LAST_CMD;
  }
  if (arg->p) {
    run_flags |= executor::USE_DEFAULT_PATH;
  }
  return this->shell_ex->RunSimpleCommand(cmd_val2, cmd_st, run_flags);
}

cmd_value::Argv* _ShiftArgv(cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  return Alloc<cmd_value::Argv>(cmd_val->argv->slice(1), cmd_val->arg_locs->slice(1), cmd_val->is_last_cmd, cmd_val->self_obj, cmd_val->proc_args);
}

Builtin::Builtin(vm::_Executor* shell_ex, ui::ErrorFormatter* errfmt) {
  this->shell_ex = shell_ex;
  this->errfmt = errfmt;
}

int Builtin::Run(cmd_value::Argv* cmd_val) {
  BigStr* name = nullptr;
  int to_run;
  syntax_asdl::CompoundWord* location = nullptr;
  cmd_value::Argv* cmd_val2 = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&name);
  StackRoot _root2(&location);
  StackRoot _root3(&cmd_val2);

  if (len(cmd_val->argv) == 1) {
    return 0;
  }
  name = cmd_val->argv->at(1);
  to_run = consts::LookupNormalBuiltin(name);
  if (to_run == consts::NO_INDEX) {
    to_run = consts::LookupSpecialBuiltin(name);
  }
  if (to_run == consts::NO_INDEX) {
    location = cmd_val->arg_locs->at(1);
    if (consts::LookupAssignBuiltin(name) != consts::NO_INDEX) {
      this->errfmt->Print_(S_hlA, location);
    }
    else {
      this->errfmt->Print_(StrFormat("%r isn't a shell builtin", name), location);
    }
    return 1;
  }
  cmd_val2 = _ShiftArgv(cmd_val);
  return this->shell_ex->RunBuiltin(to_run, cmd_val2);
}

RunProc::RunProc(vm::_Executor* shell_ex, state::Procs* procs, ui::ErrorFormatter* errfmt) {
  this->shell_ex = shell_ex;
  this->procs = procs;
  this->errfmt = errfmt;
}

int RunProc::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  List<BigStr*>* argv = nullptr;
  List<syntax_asdl::CompoundWord*>* locs = nullptr;
  BigStr* name = nullptr;
  value_asdl::value_t* proc = nullptr;
  cmd_value::Argv* cmd_val2 = nullptr;
  runtime_asdl::CommandStatus* cmd_st = nullptr;
  int run_flags;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&argv);
  StackRoot _root3(&locs);
  StackRoot _root4(&name);
  StackRoot _root5(&proc);
  StackRoot _root6(&cmd_val2);
  StackRoot _root7(&cmd_st);

  Tuple2<args::_Attributes*, args::Reader*> tup17 = flag_util::ParseCmdVal(S_pkv, cmd_val, true);
  arg_r = tup17.at1();
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup18 = arg_r->Rest2();
  argv = tup18.at0();
  locs = tup18.at1();
  if (len(argv) == 0) {
    throw Alloc<error::Usage>(S_sFk, loc::Missing);
  }
  name = argv->at(0);
  Tuple2<value_asdl::value_t*, value_asdl::Obj*> tup19 = this->procs->GetInvokable(name);
  proc = tup19.at0();
  if (!proc) {
    this->errfmt->PrintMessage(StrFormat("runproc: no invokable named %r", name));
    return 1;
  }
  cmd_val2 = Alloc<cmd_value::Argv>(argv, locs, cmd_val->is_last_cmd, cmd_val->self_obj, cmd_val->proc_args);
  cmd_st = CommandStatus::CreateNull(true);
  run_flags = cmd_val->is_last_cmd ? executor::IS_LAST_CMD : 0;
  return this->shell_ex->RunSimpleCommand(cmd_val2, cmd_st, run_flags);
}

Invoke::Invoke(vm::_Executor* shell_ex, state::Procs* procs, ui::ErrorFormatter* errfmt) {
  this->shell_ex = shell_ex;
  this->procs = procs;
  this->errfmt = errfmt;
}

int Invoke::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);

  Tuple2<args::_Attributes*, args::Reader*> tup20 = flag_util::ParseCmdVal(S_wwk, cmd_val, true);
  arg_r = tup20.at1();
  print(S_ksm);
  return 0;
}

Extern::Extern(vm::_Executor* shell_ex, state::Procs* procs, ui::ErrorFormatter* errfmt) {
  this->shell_ex = shell_ex;
  this->procs = procs;
  this->errfmt = errfmt;
}

int Extern::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);

  Tuple2<args::_Attributes*, args::Reader*> tup21 = flag_util::ParseCmdVal(S_Fvh, cmd_val, true);
  arg_r = tup21.at1();
  print(S_vtm);
  return 0;
}

List<Tuple3<BigStr*, BigStr*, BigStr*>*>* _ResolveName(BigStr* name, state::Procs* procs, Dict<BigStr*, BigStr*>* aliases, executor::SearchPath* search_path, bool do_all) {
  BigStr* no_str = nullptr;
  List<Tuple3<BigStr*, BigStr*, BigStr*>*>* results = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&procs);
  StackRoot _root2(&aliases);
  StackRoot _root3(&search_path);
  StackRoot _root4(&no_str);
  StackRoot _root5(&results);

  no_str = nullptr;
  results = Alloc<List<Tuple3<BigStr*, BigStr*, BigStr*>*>>();
  if (procs) {
    if (procs->IsShellFunc(name)) {
      results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_cgg, no_str)));
    }
    if (procs->IsProc(name)) {
      results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_aFi, no_str)));
    }
    else {
      if (procs->IsInvokableObj(name)) {
        results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_jvb, no_str)));
      }
    }
  }
  if (dict_contains(aliases, name)) {
    results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_nwn, aliases->at(name))));
  }
  if (consts::LookupNormalBuiltin(name) != 0) {
    results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_utc, no_str)));
  }
  else {
    if (consts::LookupSpecialBuiltin(name) != 0) {
      results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_utc, no_str)));
    }
    else {
      if (consts::LookupAssignBuiltin(name) != 0) {
        results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_utc, no_str)));
      }
    }
  }
  if (consts::IsControlFlow(name)) {
    results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_evo, no_str)));
  }
  else {
    if (consts::IsKeyword(name)) {
      results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_evo, no_str)));
    }
  }
  for (ListIter<BigStr*> it(search_path->LookupReflect(name, do_all)); !it.Done(); it.Next()) {
    BigStr* path = it.Value();
    StackRoot _for(&path  );
    if (posix::access(path, X_OK)) {
      results->append((Alloc<Tuple3<BigStr*, BigStr*, BigStr*>>(name, S_xeh, path)));
    }
  }
  return results;
}

Type::Type(state::Procs* funcs, Dict<BigStr*, BigStr*>* aliases, executor::SearchPath* search_path, ui::ErrorFormatter* errfmt) {
  this->funcs = funcs;
  this->aliases = aliases;
  this->search_path = search_path;
  this->errfmt = errfmt;
}

int Type::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::type* arg = nullptr;
  state::Procs* funcs = nullptr;
  int status;
  List<BigStr*>* names = nullptr;
  List<BigStr*>* paths = nullptr;
  List<Tuple3<BigStr*, BigStr*, BigStr*>*>* r = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&funcs);
  StackRoot _root5(&names);
  StackRoot _root6(&paths);
  StackRoot _root7(&r);

  Tuple2<args::_Attributes*, args::Reader*> tup22 = flag_util::ParseCmdVal(S_qEi, cmd_val);
  attrs = tup22.at0();
  arg_r = tup22.at1();
  arg = Alloc<arg_types::type>(attrs->attrs);
  if (arg->f) {
    funcs = nullptr;
  }
  else {
    funcs = this->funcs;
  }
  status = 0;
  names = arg_r->Rest();
  if (arg->P) {
    for (ListIter<BigStr*> it(names); !it.Done(); it.Next()) {
      BigStr* name = it.Value();
      StackRoot _for(&name    );
      paths = this->search_path->LookupReflect(name, arg->a);
      if (len(paths)) {
        for (ListIter<BigStr*> it(paths); !it.Done(); it.Next()) {
          BigStr* path = it.Value();
          StackRoot _for(&path        );
          print(path);
        }
      }
      else {
        status = 1;
      }
    }
    return status;
  }
  for (ListIter<BigStr*> it(names); !it.Done(); it.Next()) {
    BigStr* argument = it.Value();
    StackRoot _for(&argument  );
    r = _ResolveName(argument, funcs, this->aliases, this->search_path, arg->a);
    if (arg->a) {
      for (ListIter<Tuple3<BigStr*, BigStr*, BigStr*>*> it(r); !it.Done(); it.Next()) {
        Tuple3<BigStr*, BigStr*, BigStr*>* row = it.Value();
        _PrintEntry(arg, row);
      }
    }
    else {
      if (len(r)) {
        _PrintEntry(arg, r->at(0));
      }
    }
    if (len(r) == 0) {
      if (!arg->t) {
        print_stderr(StrFormat("%s: not found", argument));
      }
      status = 1;
    }
  }
  return status;
}

}  // define namespace meta_oils

namespace method_dict {  // define

using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::Obj;

Keys::Keys() {
  ;  // pass
}

value_asdl::value_t* Keys::Call(typed_args::Reader* rd) {
  Dict<BigStr*, value_asdl::value_t*>* dictionary = nullptr;
  List<value_asdl::value_t*>* keys = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&dictionary);
  StackRoot _root2(&keys);

  dictionary = rd->PosDict();
  rd->Done();
  keys = Alloc<List<value_asdl::value_t*>>();
  for (ListIter<BigStr*> it(dictionary->keys()); !it.Done(); it.Next()) {
    BigStr* k = it.Value();
    keys->append(Alloc<value::Str>(k));
  }
  return Alloc<value::List>(keys);
}

Values::Values() {
  ;  // pass
}

value_asdl::value_t* Values::Call(typed_args::Reader* rd) {
  Dict<BigStr*, value_asdl::value_t*>* dictionary = nullptr;
  List<value_asdl::value_t*>* values = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&dictionary);
  StackRoot _root2(&values);

  dictionary = rd->PosDict();
  rd->Done();
  values = dictionary->values();
  return Alloc<value::List>(values);
}

Erase::Erase() {
  ;  // pass
}

value_asdl::value_t* Erase::Call(typed_args::Reader* rd) {
  Dict<BigStr*, value_asdl::value_t*>* dictionary = nullptr;
  BigStr* key = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&dictionary);
  StackRoot _root2(&key);

  dictionary = rd->PosDict();
  key = rd->PosStr();
  rd->Done();
  mylib::dict_erase(dictionary, key);
  return value::Null;
}

Get::Get() {
  ;  // pass
}

value_asdl::value_t* Get::Call(typed_args::Reader* rd) {
  value_asdl::value_t* obj = nullptr;
  BigStr* key = nullptr;
  value_asdl::value_t* default_value = nullptr;
  value_asdl::value_t* UP_obj = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&obj);
  StackRoot _root2(&key);
  StackRoot _root3(&default_value);
  StackRoot _root4(&UP_obj);
  StackRoot _root5(&d);

  obj = rd->PosValue();
  key = rd->PosStr();
  default_value = rd->OptionalValue();
  rd->Done();
  UP_obj = obj;
  switch (obj->tag()) {
    case value_e::Dict: {
      value::Dict* obj = static_cast<value::Dict*>(UP_obj);
      d = obj->d;
    }
      break;
    case value_e::Obj: {
      Obj* obj = static_cast<Obj*>(UP_obj);
      d = obj->d;
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(obj, S_jai, rd->BlamePos());
    }
  }
  if (default_value == nullptr) {
    default_value = value::Null;
  }
  return d->get(key, default_value);
}

}  // define namespace method_dict

namespace method_io {  // define

using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using syntax_asdl::loc_t;
int EVAL_NULL = 1;
int EVAL_DICT = 2;

List<BigStr*>* _CheckPosArgs(List<value_asdl::value_t*>* pos_args_raw, syntax_asdl::loc_t* blame_loc) {
  List<BigStr*>* pos_args = nullptr;
  StackRoot _root0(&pos_args_raw);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&pos_args);

  if (pos_args_raw == nullptr) {
    return nullptr;
  }
  pos_args = Alloc<List<BigStr*>>();
  for (ListIter<value_asdl::value_t*> it(pos_args_raw); !it.Done(); it.Next()) {
    value_asdl::value_t* arg = it.Value();
    StackRoot _for(&arg  );
    if (arg->tag() != value_e::Str) {
      throw Alloc<error::TypeErr>(arg, S_cFv, blame_loc);
    }
    pos_args->append(static_cast<value::Str*>(arg)->s);
  }
  return pos_args;
}

EvalExpr::EvalExpr(expr_eval::ExprEvaluator* expr_ev) {
  this->expr_ev = expr_ev;
  this->mem = expr_ev->mem;
}

value_asdl::value_t* EvalExpr::Call(typed_args::Reader* rd) {
  value_asdl::Obj* unused_self = nullptr;
  (void)unused_self;
  value::Expr* lazy = nullptr;
  BigStr* dollar0 = nullptr;
  List<value_asdl::value_t*>* pos_args_raw = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* vars_ = nullptr;
  List<BigStr*>* pos_args = nullptr;
  value_asdl::value_t* result = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&unused_self);
  StackRoot _root2(&lazy);
  StackRoot _root3(&dollar0);
  StackRoot _root4(&pos_args_raw);
  StackRoot _root5(&vars_);
  StackRoot _root6(&pos_args);
  StackRoot _root7(&result);

  unused_self = rd->PosObj();
  lazy = rd->PosExpr();
  dollar0 = rd->NamedStr(S_Bxy, nullptr);
  pos_args_raw = rd->NamedList(S_Cja, nullptr);
  vars_ = rd->NamedDict(S_szc, nullptr);
  rd->Done();
  pos_args = _CheckPosArgs(pos_args_raw, rd->LeftParenToken());
  {  // with
    state::ctx_EnclosedFrame ctx{this->mem, lazy->captured_frame, lazy->module_frame, nullptr};

    {  // with
      state::ctx_Eval ctx{this->mem, dollar0, pos_args, vars_};

      result = this->expr_ev->EvalExpr(lazy->e, rd->LeftParenToken());
    }
  }
  return result;
}

void _PrintFrame(BigStr* prefix, Dict<BigStr*, runtime_asdl::Cell*>* frame) {
  runtime_asdl::Cell* rear = nullptr;
  value_asdl::value_t* rear_val = nullptr;
  value::Frame* r = nullptr;
  StackRoot _root0(&prefix);
  StackRoot _root1(&frame);
  StackRoot _root2(&rear);
  StackRoot _root3(&rear_val);
  StackRoot _root4(&r);

  print(StrFormat("%s %s", prefix, S_yfw->join(frame->keys())));
  rear = frame->get(S_hub);
  if (rear) {
    rear_val = rear->val;
    if (rear_val->tag() == value_e::Frame) {
      r = static_cast<value::Frame*>(rear_val);
      _PrintFrame(str_concat(S_BAe, prefix), r->frame);
    }
  }
}

EvalInFrame::EvalInFrame(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev) {
  this->mem = mem;
  this->cmd_ev = cmd_ev;
}

value_asdl::value_t* EvalInFrame::Call(typed_args::Reader* rd) {
  syntax_asdl::command_t* frag = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* bound = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&frag);
  StackRoot _root2(&bound);

  frag = rd->PosCommandFrag();
  bound = rd->PosFrame();
  return value::Null;
}

Eval::Eval(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev, int which) {
  this->mem = mem;
  this->cmd_ev = cmd_ev;
  this->which = which;
}

value_asdl::value_t* Eval::Call(typed_args::Reader* rd) {
  value_asdl::value_t* unused = nullptr;
  (void)unused;
  value::Command* bound = nullptr;
  syntax_asdl::command_t* cmd = nullptr;
  BigStr* dollar0 = nullptr;
  List<value_asdl::value_t*>* pos_args_raw = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* vars_ = nullptr;
  List<BigStr*>* pos_args = nullptr;
  int unused_status;
  (void)unused_status;
  Dict<BigStr*, value_asdl::value_t*>* bindings = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&unused);
  StackRoot _root2(&bound);
  StackRoot _root3(&cmd);
  StackRoot _root4(&dollar0);
  StackRoot _root5(&pos_args_raw);
  StackRoot _root6(&vars_);
  StackRoot _root7(&pos_args);
  StackRoot _root8(&bindings);

  unused = rd->PosValue();
  bound = rd->PosCommand();
  cmd = typed_args::GetCommandFrag(bound);
  dollar0 = rd->NamedStr(S_Bxy, nullptr);
  pos_args_raw = rd->NamedList(S_Cja, nullptr);
  vars_ = rd->NamedDict(S_szc, nullptr);
  rd->Done();
  pos_args = _CheckPosArgs(pos_args_raw, rd->LeftParenToken());
  if (this->which == EVAL_NULL) {
    {  // with
      state::ctx_EnclosedFrame ctx{this->mem, bound->captured_frame, bound->module_frame, nullptr};

      {  // with
        state::ctx_Eval ctx{this->mem, dollar0, pos_args, vars_};

        unused_status = this->cmd_ev->EvalCommandFrag(cmd);
      }
    }
    return value::Null;
  }
  else {
    if (this->which == EVAL_DICT) {
      bindings = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      {  // with
        state::ctx_EnclosedFrame ctx{this->mem, bound->captured_frame, bound->module_frame, bindings};

        unused_status = this->cmd_ev->EvalCommandFrag(cmd);
      }
      return Alloc<value::Dict>(bindings);
    }
    else {
      assert(0);  // AssertionError
    }
  }
}

CaptureStdout::CaptureStdout(state::Mem* mem, vm::_Executor* shell_ex) {
  this->mem = mem;
  this->shell_ex = shell_ex;
}

value_asdl::value_t* CaptureStdout::Call(typed_args::Reader* rd) {
  value_asdl::value_t* unused = nullptr;
  (void)unused;
  value::Command* cmd = nullptr;
  syntax_asdl::command_t* frag = nullptr;
  int status;
  BigStr* stdout_str = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* properties = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&unused);
  StackRoot _root2(&cmd);
  StackRoot _root3(&frag);
  StackRoot _root4(&stdout_str);
  StackRoot _root5(&properties);

  unused = rd->PosValue();
  cmd = rd->PosCommand();
  rd->Done();
  frag = typed_args::GetCommandFrag(cmd);
  {  // with
    state::ctx_EnclosedFrame ctx{this->mem, cmd->captured_frame, cmd->module_frame, nullptr};

    Tuple2<int, BigStr*> tup0 = this->shell_ex->CaptureStdout(frag);
    status = tup0.at0();
    stdout_str = tup0.at1();
  }
  if (status != 0) {
    properties = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_iAi}, std::initializer_list<value_asdl::value_t*>{num::ToBig(status)});
    throw Alloc<error::Structured>(4, StrFormat("captureStdout(): command failed with status %d", status), rd->LeftParenToken(), properties);
  }
  return Alloc<value::Str>(stdout_str);
}

PromptVal::PromptVal(prompt::Evaluator* prompt_ev) {
  this->prompt_ev = prompt_ev;
}

value_asdl::value_t* PromptVal::Call(typed_args::Reader* rd) {
  value_asdl::value_t* unused = nullptr;
  (void)unused;
  BigStr* what = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&unused);
  StackRoot _root2(&what);

  unused = rd->PosValue();
  what = rd->PosStr();
  rd->Done();
  if (len(what) != 1) {
    throw Alloc<error::Expr>(StrFormat("promptVal() expected a single char, got %r", what), rd->LeftParenToken());
  }
  return Alloc<value::Str>(this->prompt_ev->PromptVal(what));
}

Time::Time() {
  ;  // pass
}

value_asdl::value_t* Time::Call(typed_args::Reader* rd) {
  StackRoot _root0(&rd);

  return value::Null;
}

Strftime::Strftime() {
  ;  // pass
}

value_asdl::value_t* Strftime::Call(typed_args::Reader* rd) {
  StackRoot _root0(&rd);

  return value::Null;
}

Glob::Glob() {
  ;  // pass
}

value_asdl::value_t* Glob::Call(typed_args::Reader* rd) {
  StackRoot _root0(&rd);

  return value::Null;
}

}  // define namespace method_io

namespace method_list {  // define

using value_asdl::value;
using value_asdl::value_t;

Append::Append() {
  ;  // pass
}

value_asdl::value_t* Append::Call(typed_args::Reader* rd) {
  List<value_asdl::value_t*>* items = nullptr;
  value_asdl::value_t* to_append = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&items);
  StackRoot _root2(&to_append);

  items = rd->PosList();
  to_append = rd->PosValue();
  rd->Done();
  items->append(to_append);
  return value::Null;
}

Clear::Clear() {
  ;  // pass
}

value_asdl::value_t* Clear::Call(typed_args::Reader* rd) {
  List<value_asdl::value_t*>* li = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&li);

  li = rd->PosList();
  rd->Done();
  li->clear();
  return value::Null;
}

Extend::Extend() {
  ;  // pass
}

value_asdl::value_t* Extend::Call(typed_args::Reader* rd) {
  List<value_asdl::value_t*>* a = nullptr;
  List<value_asdl::value_t*>* b = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&a);
  StackRoot _root2(&b);

  a = rd->PosList();
  b = rd->PosList();
  rd->Done();
  a->extend(b);
  return value::Null;
}

Pop::Pop() {
  ;  // pass
}

value_asdl::value_t* Pop::Call(typed_args::Reader* rd) {
  List<value_asdl::value_t*>* items = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&items);

  items = rd->PosList();
  rd->Done();
  return items->pop();
}

Reverse::Reverse() {
  ;  // pass
}

value_asdl::value_t* Reverse::Call(typed_args::Reader* rd) {
  List<value_asdl::value_t*>* li = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&li);

  li = rd->PosList();
  rd->Done();
  li->reverse();
  return value::Null;
}

IndexOf::IndexOf() {
  ;  // pass
}

value_asdl::value_t* IndexOf::Call(typed_args::Reader* rd) {
  List<value_asdl::value_t*>* li = nullptr;
  value_asdl::value_t* needle = nullptr;
  int i;
  StackRoot _root0(&rd);
  StackRoot _root1(&li);
  StackRoot _root2(&needle);

  li = rd->PosList();
  needle = rd->PosValue();
  rd->Done();
  i = 0;
  while (i < len(li)) {
    if (val_ops::ExactlyEqual(li->at(i), needle, rd->LeftParenToken())) {
      return num::ToBig(i);
    }
    i += 1;
  }
  return Alloc<value::Int>(mops::MINUS_ONE);
}

LastIndexOf::LastIndexOf() {
  ;  // pass
}

value_asdl::value_t* LastIndexOf::Call(typed_args::Reader* rd) {
  List<value_asdl::value_t*>* li = nullptr;
  value_asdl::value_t* needle = nullptr;
  int i;
  StackRoot _root0(&rd);
  StackRoot _root1(&li);
  StackRoot _root2(&needle);

  li = rd->PosList();
  needle = rd->PosValue();
  rd->Done();
  i = (len(li) - 1);
  while (i > -1) {
    if (val_ops::ExactlyEqual(li->at(i), needle, rd->LeftParenToken())) {
      return num::ToBig(i);
    }
    i -= 1;
  }
  return Alloc<value::Int>(mops::MINUS_ONE);
}

}  // define namespace method_list

namespace method_other {  // define

using value_asdl::value;
using value_asdl::value_t;

SetValue::SetValue(state::Mem* mem) {
  this->mem = mem;
}

value_asdl::value_t* SetValue::Call(typed_args::Reader* rd) {
  value::Place* place = nullptr;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&place);
  StackRoot _root2(&val);

  place = rd->PosPlace();
  val = rd->PosValue();
  rd->Done();
  this->mem->SetPlace(place, val, rd->LeftParenToken());
  return value::Null;
}

}  // define namespace method_other

namespace method_str {  // define

using syntax_asdl::loc_t;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::eggex_ops;
using value_asdl::eggex_ops_t;
using value_asdl::RegexMatch;

Tuple3<bool, int, int> _StrMatchStart(BigStr* s, BigStr* p) {
  StackRoot _root0(&s);
  StackRoot _root1(&p);

  if (s->startswith(p)) {
    return Tuple3<bool, int, int>(true, 0, len(p));
  }
  else {
    return Tuple3<bool, int, int>(false, 0, 0);
  }
}

Tuple3<bool, int, int> _StrMatchEnd(BigStr* s, BigStr* p) {
  int len_s;
  StackRoot _root0(&s);
  StackRoot _root1(&p);

  len_s = len(s);
  if (s->endswith(p)) {
    return Tuple3<bool, int, int>(true, (len_s - len(p)), len_s);
  }
  else {
    return Tuple3<bool, int, int>(false, len_s, len_s);
  }
}

Tuple3<bool, int, int> _EggexMatchCommon(BigStr* s, value::Eggex* p, BigStr* ere, int empty_p) {
  int cflags;
  int eflags;
  List<int>* indices = nullptr;
  int start;
  int end;
  StackRoot _root0(&s);
  StackRoot _root1(&p);
  StackRoot _root2(&ere);
  StackRoot _root3(&indices);

  cflags = regex_translate::LibcFlags(p->canonical_flags);
  eflags = 0;
  indices = libc::regex_search(ere, cflags, s, eflags);
  if (indices == nullptr) {
    return Tuple3<bool, int, int>(false, empty_p, empty_p);
  }
  start = indices->at(0);
  end = indices->at(1);
  return Tuple3<bool, int, int>(true, start, end);
}

Tuple3<bool, int, int> _EggexMatchStart(BigStr* s, value::Eggex* p) {
  BigStr* ere = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&p);
  StackRoot _root2(&ere);

  ere = regex_translate::AsPosixEre(p);
  if (!ere->startswith(S_EAB)) {
    ere = str_concat(S_EAB, ere);
  }
  return _EggexMatchCommon(s, p, ere, 0);
}

Tuple3<bool, int, int> _EggexMatchEnd(BigStr* s, value::Eggex* p) {
  BigStr* ere = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&p);
  StackRoot _root2(&ere);

  ere = regex_translate::AsPosixEre(p);
  if (!ere->endswith(S_Czx)) {
    ere = str_concat(ere, S_Czx);
  }
  return _EggexMatchCommon(s, p, ere, len(s));
}
int START = 1;
int END = 2;

HasAffix::HasAffix(int anchor) {
  this->anchor = anchor;
}

value_asdl::value_t* HasAffix::Call(typed_args::Reader* rd) {
  BigStr* string = nullptr;
  value_asdl::value_t* pattern_val = nullptr;
  BigStr* pattern_str = nullptr;
  value::Eggex* pattern_eggex = nullptr;
  bool matched;
  StackRoot _root0(&rd);
  StackRoot _root1(&string);
  StackRoot _root2(&pattern_val);
  StackRoot _root3(&pattern_str);
  StackRoot _root4(&pattern_eggex);

  string = rd->PosStr();
  pattern_val = rd->PosValue();
  pattern_str = nullptr;
  pattern_eggex = nullptr;
  switch (pattern_val->tag()) {
    case value_e::Eggex: {
      pattern_eggex = static_cast<value::Eggex*>(pattern_val);
    }
      break;
    case value_e::Str: {
      pattern_str = static_cast<value::Str*>(pattern_val)->s;
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(pattern_val, S_yBg, rd->LeftParenToken());
    }
  }
  rd->Done();
  matched = false;
  try {
    if (pattern_str != nullptr) {
      if ((this->anchor & START)) {
        Tuple3<bool, int, int> tup0 = _StrMatchStart(string, pattern_str);
        matched = tup0.at0();
      }
      else {
        Tuple3<bool, int, int> tup1 = _StrMatchEnd(string, pattern_str);
        matched = tup1.at0();
      }
    }
    else {
      if ((this->anchor & START)) {
        Tuple3<bool, int, int> tup2 = _EggexMatchStart(string, pattern_eggex);
        matched = tup2.at0();
      }
      else {
        Tuple3<bool, int, int> tup3 = _EggexMatchEnd(string, pattern_eggex);
        matched = tup3.at0();
      }
    }
  }
  catch (error::Strict* e) {
    throw Alloc<error::Expr>(e->msg, e->location);
  }
  return Alloc<value::Bool>(matched);
}

Trim::Trim(int anchor) {
  this->anchor = anchor;
}

value_asdl::value_t* Trim::Call(typed_args::Reader* rd) {
  BigStr* string = nullptr;
  value_asdl::value_t* pattern_val = nullptr;
  BigStr* pattern_str = nullptr;
  value::Eggex* pattern_eggex = nullptr;
  int start;
  int end;
  BigStr* res = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&string);
  StackRoot _root2(&pattern_val);
  StackRoot _root3(&pattern_str);
  StackRoot _root4(&pattern_eggex);
  StackRoot _root5(&res);

  string = rd->PosStr();
  pattern_val = rd->OptionalValue();
  pattern_str = nullptr;
  pattern_eggex = nullptr;
  if (pattern_val) {
    switch (pattern_val->tag()) {
      case value_e::Eggex: {
        pattern_eggex = static_cast<value::Eggex*>(pattern_val);
      }
        break;
      case value_e::Str: {
        pattern_str = static_cast<value::Str*>(pattern_val)->s;
      }
        break;
      default: {
        throw Alloc<error::TypeErr>(pattern_val, S_yBg, rd->LeftParenToken());
      }
    }
  }
  rd->Done();
  start = 0;
  end = len(string);
  try {
    if (pattern_str != nullptr) {
      if ((this->anchor & START)) {
        Tuple3<bool, int, int> tup4 = _StrMatchStart(string, pattern_str);
        start = tup4.at2();
      }
      if ((this->anchor & END)) {
        Tuple3<bool, int, int> tup5 = _StrMatchEnd(string, pattern_str);
        end = tup5.at1();
      }
    }
    else {
      if (pattern_eggex != nullptr) {
        if ((this->anchor & START)) {
          Tuple3<bool, int, int> tup6 = _EggexMatchStart(string, pattern_eggex);
          start = tup6.at2();
        }
        if ((this->anchor & END)) {
          Tuple3<bool, int, int> tup7 = _EggexMatchEnd(string, pattern_eggex);
          end = tup7.at1();
        }
      }
      else {
        if ((this->anchor & START)) {
          Tuple2<int, int> tup8 = string_ops::StartsWithWhitespaceByteRange(string);
          start = tup8.at1();
        }
        if ((this->anchor & END)) {
          Tuple2<int, int> tup9 = string_ops::EndsWithWhitespaceByteRange(string);
          end = tup9.at0();
        }
      }
    }
  }
  catch (error::Strict* e) {
    throw Alloc<error::Expr>(e->msg, e->location);
  }
  res = string->slice(start, end);
  return Alloc<value::Str>(res);
}

Upper::Upper() {
  ;  // pass
}

value_asdl::value_t* Upper::Call(typed_args::Reader* rd) {
  BigStr* s = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&s);

  s = rd->PosStr();
  rd->Done();
  return Alloc<value::Str>(s->upper());
}

Lower::Lower() {
  ;  // pass
}

value_asdl::value_t* Lower::Call(typed_args::Reader* rd) {
  BigStr* s = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&s);

  s = rd->PosStr();
  rd->Done();
  return Alloc<value::Str>(s->lower());
}
int SEARCH = 0;
int LEFT_MATCH = 1;

SearchMatch::SearchMatch(int which_method) {
  this->which_method = which_method;
}

value_asdl::value_t* SearchMatch::Call(typed_args::Reader* rd) {
  BigStr* string = nullptr;
  value_asdl::value_t* pattern = nullptr;
  value::Eggex* eggex_val = nullptr;
  BigStr* ere = nullptr;
  int cflags;
  value_asdl::eggex_ops_t* capture = nullptr;
  int pos;
  int eflags;
  List<int>* indices = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&string);
  StackRoot _root2(&pattern);
  StackRoot _root3(&eggex_val);
  StackRoot _root4(&ere);
  StackRoot _root5(&capture);
  StackRoot _root6(&indices);

  string = rd->PosStr();
  pattern = rd->PosValue();
  switch (pattern->tag()) {
    case value_e::Eggex: {
      eggex_val = static_cast<value::Eggex*>(pattern);
      ere = regex_translate::AsPosixEre(eggex_val);
      cflags = regex_translate::LibcFlags(eggex_val->canonical_flags);
      capture = Alloc<eggex_ops::Yes>(eggex_val->convert_funcs, eggex_val->convert_toks, eggex_val->capture_names);
    }
      break;
    case value_e::Str: {
      ere = static_cast<value::Str*>(pattern)->s;
      cflags = 0;
      capture = eggex_ops::No;
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(pattern, S_fuh, rd->LeftParenToken());
    }
  }
  pos = mops::BigTruncate(rd->NamedInt(S_uaE, 0));
  rd->Done();
  if ((this->which_method == LEFT_MATCH and !ere->startswith(S_EAB))) {
    ere = str_concat(S_EAB, ere);
  }
  if (this->which_method == LEFT_MATCH) {
    eflags = 0;
  }
  else {
    eflags = pos == 0 ? 0 : REG_NOTBOL;
  }
  indices = libc::regex_search(ere, cflags, string, eflags, pos);
  if (indices == nullptr) {
    return value::Null;
  }
  return Alloc<RegexMatch>(string, indices, capture);
}

Replace::Replace(state::Mem* mem, expr_eval::ExprEvaluator* expr_ev) {
  this->mem = mem;
  this->expr_ev = expr_ev;
}

BigStr* Replace::EvalSubstExpr(value::Expr* expr, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* res = nullptr;
  StackRoot _root0(&expr);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&res);

  res = this->expr_ev->EvalExprClosure(expr, blame_loc);
  if (res->tag() == value_e::Str) {
    return static_cast<value::Str*>(res)->s;
  }
  throw Alloc<error::TypeErr>(res, S_twC, blame_loc);
}

value_asdl::value_t* Replace::Call(typed_args::Reader* rd) {
  BigStr* string = nullptr;
  value::Str* string_val = nullptr;
  value::Eggex* eggex_val = nullptr;
  value::Str* subst_str = nullptr;
  value::Expr* subst_expr = nullptr;
  value_asdl::value_t* pattern = nullptr;
  value::Eggex* eggex_val_ = nullptr;
  value::Str* string_val_ = nullptr;
  value_asdl::value_t* subst = nullptr;
  value::Str* subst_str_ = nullptr;
  value::Expr* subst_expr_ = nullptr;
  int count;
  BigStr* s = nullptr;
  BigStr* result = nullptr;
  BigStr* ere = nullptr;
  int cflags;
  int pos;
  List<BigStr*>* parts = nullptr;
  int replace_count;
  List<int>* indices = nullptr;
  BigStr* arg0 = nullptr;
  List<BigStr*>* argv = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_vars = nullptr;
  int num_groups;
  int start;
  int end;
  BigStr* captured = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* convert_func = nullptr;
  syntax_asdl::Token* convert_tok = nullptr;
  BigStr* val_str = nullptr;
  BigStr* name = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&string);
  StackRoot _root2(&string_val);
  StackRoot _root3(&eggex_val);
  StackRoot _root4(&subst_str);
  StackRoot _root5(&subst_expr);
  StackRoot _root6(&pattern);
  StackRoot _root7(&eggex_val_);
  StackRoot _root8(&string_val_);
  StackRoot _root9(&subst);
  StackRoot _root10(&subst_str_);
  StackRoot _root11(&subst_expr_);
  StackRoot _root12(&s);
  StackRoot _root13(&result);
  StackRoot _root14(&ere);
  StackRoot _root15(&parts);
  StackRoot _root16(&indices);
  StackRoot _root17(&arg0);
  StackRoot _root18(&argv);
  StackRoot _root19(&named_vars);
  StackRoot _root20(&captured);
  StackRoot _root21(&val);
  StackRoot _root22(&convert_func);
  StackRoot _root23(&convert_tok);
  StackRoot _root24(&val_str);
  StackRoot _root25(&name);

  string = rd->PosStr();
  string_val = nullptr;
  eggex_val = nullptr;
  subst_str = nullptr;
  subst_expr = nullptr;
  pattern = rd->PosValue();
  switch (pattern->tag()) {
    case value_e::Eggex: {
      eggex_val_ = static_cast<value::Eggex*>(pattern);
      eggex_val = eggex_val_;
    }
      break;
    case value_e::Str: {
      string_val_ = static_cast<value::Str*>(pattern);
      string_val = string_val_;
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(pattern, S_yBg, rd->LeftParenToken());
    }
  }
  subst = rd->PosValue();
  switch (subst->tag()) {
    case value_e::Str: {
      subst_str_ = static_cast<value::Str*>(subst);
      subst_str = subst_str_;
    }
      break;
    case value_e::Expr: {
      subst_expr_ = static_cast<value::Expr*>(subst);
      subst_expr = subst_expr_;
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(subst, S_ACu, rd->LeftParenToken());
    }
  }
  count = mops::BigTruncate(rd->NamedInt(S_oFh, -1));
  rd->Done();
  if (count == 0) {
    return Alloc<value::Str>(string);
  }
  if (string_val) {
    if (subst_str) {
      s = subst_str->s;
    }
    if (subst_expr) {
      {  // with
        state::ctx_Eval ctx{this->mem, string_val->s, nullptr, nullptr};

        s = this->EvalSubstExpr(subst_expr, rd->LeftParenToken());
      }
    }
    result = string->replace(string_val->s, s, count);
    return Alloc<value::Str>(result);
  }
  if (eggex_val) {
    if (str_contains(string, S_Bkk)) {
      throw Alloc<error::Structured>(3, S_ltE_1, rd->LeftParenToken());
    }
    ere = regex_translate::AsPosixEre(eggex_val);
    cflags = regex_translate::LibcFlags(eggex_val->canonical_flags);
    pos = 0;
    parts = Alloc<List<BigStr*>>();
    replace_count = 0;
    while (pos < len(string)) {
      indices = libc::regex_search(ere, cflags, string, 0, pos);
      if (indices == nullptr) {
        break;
      }
      arg0 = nullptr;
      argv = Alloc<List<BigStr*>>();
      named_vars = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      num_groups = (len(indices) / 2);
      for (int group = 0; group < num_groups; ++group) {
        start = indices->at((2 * group));
        end = indices->at(((2 * group) + 1));
        captured = string->slice(start, end);
        val = Alloc<value::Str>(captured);
        if ((len(eggex_val->convert_funcs) and group != 0)) {
          convert_func = eggex_val->convert_funcs->at((group - 1));
          convert_tok = eggex_val->convert_toks->at((group - 1));
          if (convert_func) {
            val = this->expr_ev->CallConvertFunc(convert_func, val, convert_tok, rd->LeftParenToken());
          }
        }
        val_str = val_ops::Stringify(val, rd->LeftParenToken(), S_Aoo);
        if (group == 0) {
          arg0 = val_str;
        }
        else {
          argv->append(val_str);
        }
        if (group != 0) {
          name = eggex_val->capture_names->at((group - 2));
          if (name != nullptr) {
            named_vars->set(name, val);
          }
        }
      }
      if (subst_str) {
        s = subst_str->s;
      }
      if (subst_expr) {
        {  // with
          state::ctx_Eval ctx{this->mem, arg0, argv, named_vars};

          s = this->EvalSubstExpr(subst_expr, rd->LeftParenToken());
        }
      }
      start = indices->at(0);
      end = indices->at(1);
      if (pos == end) {
        throw Alloc<error::Structured>(3, S_Bop, rd->LeftParenToken());
      }
      parts->append(string->slice(pos, start));
      parts->append(s);
      pos = end;
      replace_count += 1;
      if ((count != -1 and replace_count == count)) {
        break;
      }
    }
    parts->append(string->slice(pos));
    return Alloc<value::Str>(S_Aoo->join(parts));
  }
  assert(0);  // AssertionError
}

Split::Split() {
  ;  // pass
}

value_asdl::value_t* Split::Call(typed_args::Reader* rd) {
  BigStr* string = nullptr;
  BigStr* string_sep = nullptr;
  value::Eggex* eggex_sep = nullptr;
  value_asdl::value_t* sep = nullptr;
  value::Eggex* eggex_sep_ = nullptr;
  value::Str* string_sep_ = nullptr;
  int count;
  int cursor;
  List<value_asdl::value_t*>* chunks = nullptr;
  int next;
  BigStr* regex = nullptr;
  int cflags;
  List<int>* m = nullptr;
  int start;
  int end;
  StackRoot _root0(&rd);
  StackRoot _root1(&string);
  StackRoot _root2(&string_sep);
  StackRoot _root3(&eggex_sep);
  StackRoot _root4(&sep);
  StackRoot _root5(&eggex_sep_);
  StackRoot _root6(&string_sep_);
  StackRoot _root7(&chunks);
  StackRoot _root8(&regex);
  StackRoot _root9(&m);

  string = rd->PosStr();
  string_sep = nullptr;
  eggex_sep = nullptr;
  sep = rd->PosValue();
  switch (sep->tag()) {
    case value_e::Eggex: {
      eggex_sep_ = static_cast<value::Eggex*>(sep);
      eggex_sep = eggex_sep_;
    }
      break;
    case value_e::Str: {
      string_sep_ = static_cast<value::Str*>(sep);
      string_sep = string_sep_->s;
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(sep, S_sdz, rd->LeftParenToken());
    }
  }
  count = mops::BigTruncate(rd->NamedInt(S_oFh, -1));
  rd->Done();
  if (len(string) == 0) {
    return Alloc<value::List>(Alloc<List<value_asdl::value_t*>>());
  }
  if (string_sep != nullptr) {
    if (len(string_sep) == 0) {
      throw Alloc<error::Structured>(3, S_avu_1, rd->LeftParenToken());
    }
    cursor = 0;
    chunks = Alloc<List<value_asdl::value_t*>>();
    while ((cursor < len(string) and count != 0)) {
      next = string->find(string_sep, cursor);
      if (next == -1) {
        break;
      }
      chunks->append(Alloc<value::Str>(string->slice(cursor, next)));
      cursor = (next + len(string_sep));
      count -= 1;
    }
    chunks->append(Alloc<value::Str>(string->slice(cursor)));
    return Alloc<value::List>(chunks);
  }
  if (eggex_sep != nullptr) {
    if (str_contains(string, S_Bkk)) {
      throw Alloc<error::Structured>(3, S_pnd, rd->LeftParenToken());
    }
    regex = regex_translate::AsPosixEre(eggex_sep);
    cflags = regex_translate::LibcFlags(eggex_sep->canonical_flags);
    cursor = 0;
    chunks = Alloc<List<value_asdl::value_t*>>();
    while ((cursor < len(string) and count != 0)) {
      m = libc::regex_search(regex, cflags, string, 0, cursor);
      if (m == nullptr) {
        break;
      }
      start = m->at(0);
      end = m->at(1);
      if (start == end) {
        throw Alloc<error::Structured>(3, S_gxy, rd->LeftParenToken());
      }
      chunks->append(Alloc<value::Str>(string->slice(cursor, start)));
      cursor = end;
      count -= 1;
    }
    chunks->append(Alloc<value::Str>(string->slice(cursor)));
    return Alloc<value::List>(chunks);
  }
  assert(0);  // AssertionError
}

}  // define namespace method_str

namespace method_type {  // define

using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::Obj;

BigStr* _GetStringField(value_asdl::Obj* obj, BigStr* field_name) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&field_name);
  StackRoot _root2(&val);

  val = obj->d->get(field_name);
  if (val == nullptr) {
    return nullptr;
  }
  if (val->tag() != value_e::Str) {
    return nullptr;
  }
  return static_cast<value::Str*>(val)->s;
}

Index__::Index__() {
  this->unique_instances = Alloc<Dict<BigStr*, value_asdl::Obj*>>();
}

value_asdl::value_t* Index__::Call(typed_args::Reader* rd) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&val);

  val = this->_Call(rd);
  if (val == nullptr) {
    throw Alloc<error::Expr>(S_fBA, rd->LeastSpecificLocation());
  }
  return val;
}

value_asdl::value_t* Index__::_Call(typed_args::Reader* rd) {
  value_asdl::Obj* left_obj = nullptr;
  value_asdl::value_t* right = nullptr;
  BigStr* left_name = nullptr;
  value_asdl::value_t* UP_right = nullptr;
  List<value_asdl::Obj*>* objects = nullptr;
  int i;
  int expected_params;
  int actual;
  mylib::BufWriter* buf = nullptr;
  BigStr* r_unique_id = nullptr;
  BigStr* r_name = nullptr;
  BigStr* unique_id = nullptr;
  value_asdl::Obj* obj_with_params = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* props = nullptr;
  StackRoot _root0(&rd);
  StackRoot _root1(&left_obj);
  StackRoot _root2(&right);
  StackRoot _root3(&left_name);
  StackRoot _root4(&UP_right);
  StackRoot _root5(&objects);
  StackRoot _root6(&buf);
  StackRoot _root7(&r_unique_id);
  StackRoot _root8(&r_name);
  StackRoot _root9(&unique_id);
  StackRoot _root10(&obj_with_params);
  StackRoot _root11(&props);

  left_obj = rd->PosObj();
  right = rd->PosValue();
  rd->Done();
  left_name = _GetStringField(left_obj, S_klA);
  if (left_name == nullptr) {
    return nullptr;
  }
  UP_right = right;
  objects = Alloc<List<value_asdl::Obj*>>();
  switch (right->tag()) {
    case value_e::Obj: {
      Obj* right = static_cast<Obj*>(UP_right);
      objects->append(right);
    }
      break;
    case value_e::List: {
      value::List* right = static_cast<value::List*>(UP_right);
      i = 0;
      for (ListIter<value_asdl::value_t*> it(right->items); !it.Done(); it.Next(), ++i) {
        value_asdl::value_t* val = it.Value();
        StackRoot _for(&val      );
        if (val->tag() != value_e::Obj) {
          return nullptr;
        }
        objects->append(static_cast<Obj*>(val));
      }
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(right, S_nlA, rd->LeastSpecificLocation());
    }
  }
  switch (len(left_name)) {
    case 4: {
      if (str_equals_c(left_name, "List", 4)) {
        expected_params = 1;
      }
      else if (str_equals_c(left_name, "Dict", 4)) {
        expected_params = 2;
      }
      else {
        goto str_switch_default;
      }
    }
      break;

    str_switch_default:
    default: {
      expected_params = 0;
    }
  }
  actual = len(objects);
  if (expected_params != actual) {
    throw Alloc<error::Expr>(StrFormat("Obj __index__ method expected %d params, got %d", expected_params, actual), rd->LeastSpecificLocation());
  }
  buf = Alloc<mylib::BufWriter>();
  buf->write(left_name);
  buf->write(S_Eax);
  i = 0;
  for (ListIter<value_asdl::Obj*> it(objects); !it.Done(); it.Next(), ++i) {
    value_asdl::Obj* r = it.Value();
    StackRoot _for(&r  );
    if (i != 0) {
      buf->write(S_Cce);
    }
    r_unique_id = _GetStringField(r, S_oAe);
    if (r_unique_id != nullptr) {
      buf->write(r_unique_id);
    }
    else {
      r_name = _GetStringField(r, S_klA);
      if (r_name == nullptr) {
        return nullptr;
      }
      buf->write(r_name);
    }
  }
  buf->write(S_pcD);
  unique_id = buf->getvalue();
  obj_with_params = this->unique_instances->get(unique_id);
  if (obj_with_params == nullptr) {
    props = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_oAe}, std::initializer_list<value_asdl::value_t*>{Alloc<value::Str>(unique_id)});
    obj_with_params = Alloc<Obj>(nullptr, props);
    this->unique_instances->set(unique_id, obj_with_params);
  }
  return obj_with_params;
}

}  // define namespace method_type

namespace misc_osh {  // define

using runtime_asdl::cmd_value;
using syntax_asdl::loc_t;

Times::Times() : ::vm::_Builtin() {
}

int Times::Run(cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  pyos::PrintTimes();
  return 0;
}

Help::Help(BigStr* lang, pyutil::_ResourceLoader* loader, Dict<BigStr*, BigStr*>* help_data, ui::ErrorFormatter* errfmt) {
  this->lang = lang;
  this->loader = loader;
  this->help_data = help_data;
  this->errfmt = errfmt;
  this->version_str = pyutil::GetVersion(this->loader);
  this->f = mylib::Stdout();
}

int Help::_ShowTopic(BigStr* topic_id, syntax_asdl::loc_t* blame_loc) {
  BigStr* prefix = nullptr;
  BigStr* chapter_name = nullptr;
  BigStr* lower = nullptr;
  bool found;
  StackRoot _root0(&topic_id);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&prefix);
  StackRoot _root3(&chapter_name);
  StackRoot _root4(&lower);

  prefix = S_Bql;
  chapter_name = this->help_data->get(topic_id);
  if (chapter_name != nullptr) {
    util::PrintTopicHeader(topic_id, this->f);
    print(StrFormat("    %s/%s/doc/ref/chap-%s.html#%s", prefix, this->version_str, chapter_name, topic_id));
    print(S_Aoo);
    return 0;
  }
  lower = topic_id->lower();
  if (lower->startswith(S_jlA)) {
    print(S_Aoo);
    print(StrFormat("    %s/%s/doc/error-catalog.html#%s", prefix, this->version_str, lower));
    print(S_Aoo);
    return 0;
  }
  found = util::PrintEmbeddedHelp(this->loader, topic_id, this->f);
  if (!found) {
    this->errfmt->Print_(StrFormat("no help topics match %r", topic_id), blame_loc);
    return 1;
  }
  return 0;
}

int Help::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  BigStr* topic_id = nullptr;
  syntax_asdl::loc_t* blame_loc = nullptr;
  bool unused_found;
  (void)unused_found;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&topic_id);
  StackRoot _root4(&blame_loc);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_sea, cmd_val);
  attrs = tup0.at0();
  arg_r = tup0.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup1 = arg_r->Peek2();
  topic_id = tup1.at0();
  blame_loc = tup1.at1();
  if (topic_id == nullptr) {
    unused_found = this->_ShowTopic(S_sea, blame_loc) == 0;
    unused_found = this->_ShowTopic(StrFormat("%s-chapters", this->lang), blame_loc) == 0;
    print(StrFormat("All docs: https://oils.pub/release/%s/doc/", this->version_str));
    print(S_Aoo);
    return 0;
  }
  else {
    arg_r->Next();
  }
  return this->_ShowTopic(topic_id, blame_loc);
}

}  // define namespace misc_osh

namespace module_ysh {  // define

using runtime_asdl::cmd_value;
using value_asdl::value;
using value_asdl::value_e;

IsMain::IsMain(state::Mem* mem) {
  this->mem = mem;
}

int IsMain::Run(cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  return this->mem->is_main ? 0 : 1;
}

SourceGuard::SourceGuard(Dict<BigStr*, bool>* guards, optview::Exec* exec_opts, ui::ErrorFormatter* errfmt) {
  this->guards = guards;
  this->exec_opts = exec_opts;
  this->errfmt = errfmt;
}

int SourceGuard::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* name = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&name);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_uiC, cmd_val);
  arg_r = tup0.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup1 = arg_r->ReadRequired2(S_bds);
  name = tup1.at0();
  if (dict_contains(this->guards, name)) {
    if (this->exec_opts->redefine_source()) {
      this->errfmt->PrintMessage(StrFormat("(interactive) Reloading source file %r", name));
      return 0;
    }
    else {
      return 1;
    }
  }
  this->guards->set(name, true);
  return 0;
}

ModuleInvoke::ModuleInvoke(cmd_eval::CommandEvaluator* cmd_ev, dev::Tracer* tracer, ui::ErrorFormatter* errfmt) {
  this->cmd_ev = cmd_ev;
  this->tracer = tracer;
  this->errfmt = errfmt;
}

int ModuleInvoke::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* invokable_name = nullptr;
  syntax_asdl::loc_t* invokable_loc = nullptr;
  List<BigStr*>* argv = nullptr;
  List<syntax_asdl::CompoundWord*>* locs = nullptr;
  value_asdl::Obj* self_obj = nullptr;
  value_asdl::value_t* val = nullptr;
  cmd_value::Argv* cmd_val2 = nullptr;
  value::Proc* proc = nullptr;
  int status;
  value_asdl::value_t* proc_val = nullptr;
  value_asdl::Obj* self_obj2 = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&invokable_name);
  StackRoot _root3(&invokable_loc);
  StackRoot _root4(&argv);
  StackRoot _root5(&locs);
  StackRoot _root6(&self_obj);
  StackRoot _root7(&val);
  StackRoot _root8(&cmd_val2);
  StackRoot _root9(&proc);
  StackRoot _root10(&proc_val);
  StackRoot _root11(&self_obj2);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup2 = arg_r->Peek2();
  invokable_name = tup2.at0();
  invokable_loc = tup2.at1();
  if (invokable_name == nullptr) {
    throw Alloc<error::Usage>(S_CBl, cmd_val->arg_locs->at(0));
  }
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup3 = arg_r->Rest2();
  argv = tup3.at0();
  locs = tup3.at1();
  self_obj = cmd_val->self_obj;
  val = self_obj->d->get(invokable_name);
  if (val != nullptr) {
    cmd_val2 = Alloc<cmd_value::Argv>(argv, locs, cmd_val->is_last_cmd, nullptr, cmd_val->proc_args);
    if (val->tag() == value_e::Proc) {
      proc = static_cast<value::Proc*>(val);
      {  // with
        dev::ctx_Tracer ctx{this->tracer, S_dba, cmd_val->argv};

        status = this->cmd_ev->RunProc(proc, cmd_val2);
      }
      return status;
    }
    Tuple2<value_asdl::value_t*, value_asdl::Obj*> tup4 = state::ValueIsInvokableObj(val);
    proc_val = tup4.at0();
    self_obj2 = tup4.at1();
    cmd_val2->self_obj = self_obj2;
    if (proc_val) {
      if (proc_val->tag() != value_e::Proc) {
        throw Alloc<error::TypeErr>(proc_val, StrFormat("__invoke__ on %r should be a user-defined Proc", invokable_name), invokable_loc);
      }
      proc = static_cast<value::Proc*>(proc_val);
      {  // with
        dev::ctx_Tracer ctx{this->tracer, S_dba, cmd_val->argv};

        status = this->cmd_ev->RunProc(proc, cmd_val2);
      }
      return status;
    }
  }
  throw Alloc<error::Usage>(StrFormat("module doesn't contain invokable %r", invokable_name), invokable_loc);
}

}  // define namespace module_ysh

namespace printf_osh {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using id_kind_asdl::Id_str;
using id_kind_asdl::Kind;
using id_kind_asdl::Kind_t;
using runtime_asdl::cmd_value;
using syntax_asdl::loc;
using syntax_asdl::loc_e;
using syntax_asdl::loc_t;
using syntax_asdl::source;
using syntax_asdl::Token;
using syntax_asdl::CompoundWord;
using syntax_asdl::printf_part;
using syntax_asdl::printf_part_e;
using syntax_asdl::printf_part_t;
using types_asdl::lex_mode_e;
using types_asdl::lex_mode_t;
using value_asdl::value;
using value_asdl::value_e;
using error::p_die;

_FormatStringParser::_FormatStringParser(lexer::Lexer* lexer) {
  this->lexer = lexer;
  this->cur_token = nullptr;
  this->token_type = Id::Undefined_Tok;
  this->token_kind = Kind::Undefined;
}

void _FormatStringParser::_Next(types_asdl::lex_mode_t lex_mode) {
  this->cur_token = this->lexer->Read(lex_mode);
  this->token_type = this->cur_token->id;
  this->token_kind = consts::GetKind(this->token_type);
}

syntax_asdl::printf_part_t* _FormatStringParser::_ParseFormatStr() {
  printf_part::Percent* part = nullptr;
  BigStr* flag = nullptr;
  BigStr* type_val = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&flag);
  StackRoot _root2(&type_val);

  this->_Next(lex_mode_e::PrintfPercent);
  part = printf_part::Percent::CreateNull(true);
  while ((this->token_type == Id::Format_Flag || this->token_type == Id::Format_Zero)) {
    flag = lexer::TokenVal(this->cur_token);
    if (str_contains(S_eyr, flag)) {
      p_die(StrFormat("osh printf doesn't support the %r flag", flag), this->cur_token);
    }
    part->flags->append(this->cur_token);
    this->_Next(lex_mode_e::PrintfPercent);
  }
  if ((this->token_type == Id::Format_Num || this->token_type == Id::Format_Star)) {
    part->width = this->cur_token;
    this->_Next(lex_mode_e::PrintfPercent);
  }
  if (this->token_type == Id::Format_Dot) {
    part->precision = this->cur_token;
    this->_Next(lex_mode_e::PrintfPercent);
    if ((this->token_type == Id::Format_Num || this->token_type == Id::Format_Star || this->token_type == Id::Format_Zero)) {
      part->precision = this->cur_token;
      this->_Next(lex_mode_e::PrintfPercent);
    }
  }
  if ((this->token_type == Id::Format_Type || this->token_type == Id::Format_Time)) {
    part->type = this->cur_token;
    type_val = lexer::TokenVal(part->type);
    if (str_contains(S_ayy, type_val)) {
      p_die(S_aCB, part->type);
    }
    if (str_equals(type_val, S_emj)) {
      p_die(S_syf, part->type);
    }
  }
  else {
    if (this->token_type == Id::Unknown_Tok) {
      p_die(S_myB, this->cur_token);
    }
    else {
      p_die(S_oij, this->cur_token);
    }
  }
  return part;
}

List<syntax_asdl::printf_part_t*>* _FormatStringParser::Parse() {
  List<syntax_asdl::printf_part_t*>* parts = nullptr;
  StackRoot _root0(&parts);

  this->_Next(lex_mode_e::PrintfOuter);
  parts = Alloc<List<syntax_asdl::printf_part_t*>>();
  while (true) {
    if (((this->token_kind == Kind::Lit || this->token_kind == Kind::Char) or (this->token_type == Id::Format_EscapedPercent || this->token_type == Id::Unknown_Backslash))) {
      parts->append(this->cur_token);
    }
    else {
      if (this->token_type == Id::Format_Percent) {
        parts->append(this->_ParseFormatStr());
      }
      else {
        if ((this->token_type == Id::Eof_Real || this->token_type == Id::Eol_Tok)) {
          break;
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
    this->_Next(lex_mode_e::PrintfOuter);
  }
  return parts;
}

_PrintfState::_PrintfState() {
  this->arg_index = 0;
  this->backslash_c = false;
  this->status = 0;
}

Printf::Printf(state::Mem* mem, parse_lib::ParseContext* parse_ctx, sh_expr_eval::UnsafeArith* unsafe_arith, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->parse_ctx = parse_ctx;
  this->unsafe_arith = unsafe_arith;
  this->errfmt = errfmt;
  this->parse_cache = Alloc<Dict<BigStr*, List<syntax_asdl::printf_part_t*>*>>();
  this->shell_start_time = time_::time();
}

BigStr* Printf::_Percent(printf_osh::_PrintfState* pr, printf_part::Percent* part, List<BigStr*>* varargs, List<syntax_asdl::CompoundWord*>* locs) {
  int num_args;
  List<BigStr*>* flags = nullptr;
  int width;
  BigStr* width_str = nullptr;
  syntax_asdl::loc_t* width_loc = nullptr;
  int precision;
  BigStr* precision_str = nullptr;
  syntax_asdl::loc_t* precision_loc = nullptr;
  BigStr* s = nullptr;
  syntax_asdl::loc_t* word_loc = nullptr;
  bool has_arg;
  BigStr* typ = nullptr;
  List<BigStr*>* c_parts = nullptr;
  match::SimpleLexer* lex = nullptr;
  int id_;
  BigStr* tok_val = nullptr;
  BigStr* p = nullptr;
  bool ok;
  mops::BigInt d;
  int num_bytes;
  int small_i;
  syntax_asdl::loc_t* blame_loc = nullptr;
  runtime_asdl::Cell* tzcell = nullptr;
  value::Str* tzval = nullptr;
  double ts;
  int zero_pad;
  bool negative;
  BigStr* digits = nullptr;
  BigStr* sign = nullptr;
  int n;
  StackRoot _root0(&pr);
  StackRoot _root1(&part);
  StackRoot _root2(&varargs);
  StackRoot _root3(&locs);
  StackRoot _root4(&flags);
  StackRoot _root5(&width_str);
  StackRoot _root6(&width_loc);
  StackRoot _root7(&precision_str);
  StackRoot _root8(&precision_loc);
  StackRoot _root9(&s);
  StackRoot _root10(&word_loc);
  StackRoot _root11(&typ);
  StackRoot _root12(&c_parts);
  StackRoot _root13(&lex);
  StackRoot _root14(&tok_val);
  StackRoot _root15(&p);
  StackRoot _root16(&blame_loc);
  StackRoot _root17(&tzcell);
  StackRoot _root18(&tzval);
  StackRoot _root19(&digits);
  StackRoot _root20(&sign);

  num_args = len(varargs);
  flags = Alloc<List<BigStr*>>();
  if (len(part->flags) > 0) {
    for (ListIter<syntax_asdl::Token*> it(part->flags); !it.Done(); it.Next()) {
      syntax_asdl::Token* flag_token = it.Value();
      StackRoot _for(&flag_token    );
      flags->append(lexer::TokenVal(flag_token));
    }
  }
  width = -1;
  if (part->width) {
    if ((part->width->id == Id::Format_Num || part->width->id == Id::Format_Zero)) {
      width_str = lexer::TokenVal(part->width);
      width_loc = part->width;
    }
    else {
      if (part->width->id == Id::Format_Star) {
        if (pr->arg_index < num_args) {
          width_str = varargs->at(pr->arg_index);
          width_loc = locs->at(pr->arg_index);
          pr->arg_index += 1;
        }
        else {
          width_str = S_Aoo;
          width_loc = loc::Missing;
        }
      }
      else {
        assert(0);  // AssertionError
      }
    }
    try {
      width = to_int(width_str);
    }
    catch (ValueError*) {
      if (width_loc->tag() == loc_e::Missing) {
        width_loc = part->width;
      }
      this->errfmt->Print_(StrFormat("printf got invalid width %r", width_str), width_loc);
      pr->status = 1;
      return nullptr;
    }
  }
  precision = -1;
  if (part->precision) {
    if (part->precision->id == Id::Format_Dot) {
      precision_str = S_wfw;
      precision_loc = part->precision;
    }
    else {
      if ((part->precision->id == Id::Format_Num || part->precision->id == Id::Format_Zero)) {
        precision_str = lexer::TokenVal(part->precision);
        precision_loc = part->precision;
      }
      else {
        if (part->precision->id == Id::Format_Star) {
          if (pr->arg_index < num_args) {
            precision_str = varargs->at(pr->arg_index);
            precision_loc = locs->at(pr->arg_index);
            pr->arg_index += 1;
          }
          else {
            precision_str = S_Aoo;
            precision_loc = loc::Missing;
          }
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
    try {
      precision = to_int(precision_str);
    }
    catch (ValueError*) {
      if (precision_loc->tag() == loc_e::Missing) {
        precision_loc = part->precision;
      }
      this->errfmt->Print_(StrFormat("printf got invalid precision %r", precision_str), precision_loc);
      pr->status = 1;
      return nullptr;
    }
  }
  if (pr->arg_index < num_args) {
    s = varargs->at(pr->arg_index);
    word_loc = locs->at(pr->arg_index);
    pr->arg_index += 1;
    has_arg = true;
  }
  else {
    s = S_Aoo;
    word_loc = loc::Missing;
    has_arg = false;
  }
  typ = lexer::TokenVal(part->type);
  if (str_equals(typ, S_anC)) {
    if (precision >= 0) {
      s = s->slice(0, precision);
    }
  }
  else {
    if (str_equals(typ, S_crA)) {
      s = j8_lite::MaybeShellEncode(s);
    }
    else {
      if (str_equals(typ, S_jFv)) {
        c_parts = Alloc<List<BigStr*>>();
        lex = match::EchoLexer(s);
        while (true) {
          Tuple2<int, BigStr*> tup0 = lex->Next();
          id_ = tup0.at0();
          tok_val = tup0.at1();
          if (id_ == Id::Eol_Tok) {
            break;
          }
          p = word_compile::EvalCStringToken(id_, tok_val);
          if (p == nullptr) {
            pr->backslash_c = true;
            break;
          }
          c_parts->append(p);
        }
        s = S_Aoo->join(c_parts);
      }
      else {
        if ((part->type->id == Id::Format_Time or str_contains(S_wkf, typ))) {
          if (match::LooksLikeInteger(s)) {
            Tuple2<bool, mops::BigInt> tup1 = mops::FromStr2(s);
            ok = tup1.at0();
            d = tup1.at1();
            if (!ok) {
              this->errfmt->Print_(StrFormat("Integer too big: %s", s), word_loc);
              pr->status = 1;
              return nullptr;
            }
          }
          else {
            num_bytes = len(s);
            if ((num_bytes > 0 and str_contains(S_oEq, s->at(0)))) {
              if (num_bytes == 1) {
                d = mops::ZERO;
              }
              else {
                if (num_bytes == 2) {
                  d = mops::IntWiden(ord(s->at(1)));
                }
                else {
                  try {
                    small_i = string_ops::DecodeUtf8Char(s, 1);
                  }
                  catch (error::Expr* e) {
                    this->errfmt->Print_(StrFormat("Warning: %s", e->UserErrorString()), word_loc);
                    small_i = ord(s->at(1));
                  }
                  d = mops::IntWiden(small_i);
                }
              }
            }
            else {
              if ((!has_arg and part->type->id == Id::Format_Time)) {
                d = mops::MINUS_ONE;
              }
              else {
                if (has_arg) {
                  blame_loc = word_loc;
                }
                else {
                  blame_loc = part->type;
                }
                this->errfmt->Print_(StrFormat("printf expected an integer, got %r", s), blame_loc);
                pr->status = 1;
                return nullptr;
              }
            }
          }
          if (part->type->id == Id::Format_Time) {
            tzcell = this->mem->GetCell(S_nhf);
            if ((tzcell and (tzcell->exported and tzcell->val->tag() == value_e::Str))) {
              tzval = static_cast<value::Str*>(tzcell->val);
              posix::putenv(S_nhf, tzval->s);
            }
            time_::tzset();
            if (mops::Equal(d, mops::MINUS_ONE)) {
              ts = time_::time();
            }
            else {
              if (mops::Equal(d, mops::MINUS_TWO)) {
                ts = this->shell_start_time;
              }
              else {
                ts = mops::BigTruncate(d);
              }
            }
            s = time_::strftime(typ->slice(1, -2), time_::localtime(ts));
            if (precision >= 0) {
              s = s->slice(0, precision);
            }
          }
          else {
            if ((mops::Greater(mops::ZERO, d) and str_contains(S_Eop, typ))) {
              this->errfmt->Print_(StrFormat("Can't format negative number with %%%s: %d", typ, mops::BigTruncate(d)), part->type);
              pr->status = 1;
              return nullptr;
            }
            if (str_equals(typ, S_Ala)) {
              s = mops::ToOctal(d);
            }
            else {
              if (str_equals(typ, S_rqD)) {
                s = mops::ToHexLower(d);
              }
              else {
                if (str_equals(typ, S_awm)) {
                  s = mops::ToHexUpper(d);
                }
                else {
                  s = mops::ToStr(d);
                }
              }
            }
            zero_pad = 0;
            if ((width >= 0 and list_contains(flags, S_wfw))) {
              zero_pad = 1;
            }
            else {
              if ((precision > 0 and len(s) < precision)) {
                zero_pad = 2;
              }
            }
            if (zero_pad) {
              negative = str_equals(s->at(0), S_Bjq);
              if (negative) {
                digits = s->slice(1);
                sign = S_Bjq;
                if (zero_pad == 1) {
                  n = (width - 1);
                }
                else {
                  n = precision;
                }
              }
              else {
                digits = s;
                sign = S_Aoo;
                if (zero_pad == 1) {
                  n = width;
                }
                else {
                  n = precision;
                }
              }
              s = str_concat(sign, digits->rjust(n, S_wfw));
            }
          }
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
  }
  if (width >= 0) {
    if (list_contains(flags, S_Bjq)) {
      s = s->ljust(width, S_yfw);
    }
    else {
      s = s->rjust(width, S_yfw);
    }
  }
  return s;
}

int Printf::_Format(List<syntax_asdl::printf_part_t*>* parts, List<BigStr*>* varargs, List<syntax_asdl::CompoundWord*>* locs, List<BigStr*>* out) {
  printf_osh::_PrintfState* pr = nullptr;
  int num_args;
  syntax_asdl::printf_part_t* UP_part = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&parts);
  StackRoot _root1(&varargs);
  StackRoot _root2(&locs);
  StackRoot _root3(&out);
  StackRoot _root4(&pr);
  StackRoot _root5(&UP_part);
  StackRoot _root6(&s);

  pr = Alloc<_PrintfState>();
  num_args = len(varargs);
  while (true) {
    for (ListIter<syntax_asdl::printf_part_t*> it(parts); !it.Done(); it.Next()) {
      syntax_asdl::printf_part_t* part = it.Value();
      StackRoot _for(&part    );
      UP_part = part;
      if (part->tag() == printf_part_e::Literal) {
        Token* part = static_cast<Token*>(UP_part);
        if (part->id == Id::Format_EscapedPercent) {
          s = S_dkr;
        }
        else {
          s = word_compile::EvalCStringToken(part->id, lexer::LazyStr(part));
        }
      }
      else {
        if (part->tag() == printf_part_e::Percent) {
          printf_part::Percent* part = static_cast<printf_part::Percent*>(UP_part);
          s = this->_Percent(pr, part, varargs, locs);
          if (pr->status != 0) {
            return pr->status;
          }
        }
        else {
          assert(0);  // AssertionError
        }
      }
      out->append(s);
      if (pr->backslash_c) {
        break;
      }
    }
    if (pr->arg_index == 0) {
      break;
    }
    if (pr->arg_index >= num_args) {
      break;
    }
  }
  return 0;
}

int Printf::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::printf* arg = nullptr;
  BigStr* fmt = nullptr;
  syntax_asdl::loc_t* fmt_loc = nullptr;
  List<BigStr*>* varargs = nullptr;
  List<syntax_asdl::CompoundWord*>* locs = nullptr;
  alloc::Arena* arena = nullptr;
  List<syntax_asdl::printf_part_t*>* parts = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  lexer::Lexer* lexer = nullptr;
  printf_osh::_FormatStringParser* parser = nullptr;
  List<BigStr*>* out = nullptr;
  int status;
  BigStr* result = nullptr;
  syntax_asdl::loc__Missing* v_loc = nullptr;
  value_asdl::sh_lvalue_t* lval = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&fmt);
  StackRoot _root5(&fmt_loc);
  StackRoot _root6(&varargs);
  StackRoot _root7(&locs);
  StackRoot _root8(&arena);
  StackRoot _root9(&parts);
  StackRoot _root10(&line_reader);
  StackRoot _root11(&lexer);
  StackRoot _root12(&parser);
  StackRoot _root13(&out);
  StackRoot _root14(&result);
  StackRoot _root15(&v_loc);
  StackRoot _root16(&lval);

  Tuple2<args::_Attributes*, args::Reader*> tup2 = flag_util::ParseCmdVal(S_Foo, cmd_val);
  attrs = tup2.at0();
  arg_r = tup2.at1();
  arg = Alloc<arg_types::printf>(attrs->attrs);
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup3 = arg_r->ReadRequired2(S_rku);
  fmt = tup3.at0();
  fmt_loc = tup3.at1();
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup4 = arg_r->Rest2();
  varargs = tup4.at0();
  locs = tup4.at1();
  arena = this->parse_ctx->arena;
  if (dict_contains(this->parse_cache, fmt)) {
    parts = this->parse_cache->at(fmt);
  }
  else {
    line_reader = reader::StringLineReader(fmt, arena);
    lexer = this->parse_ctx->MakeLexer(line_reader);
    parser = Alloc<_FormatStringParser>(lexer);
    {  // with
      alloc::ctx_SourceCode ctx{arena, Alloc<source::Dynamic>(S_kjD, fmt_loc)};

      try {
        parts = parser->Parse();
      }
      catch (error::Parse* e) {
        this->errfmt->PrettyPrintError(e);
        return 2;
      }
    }
    this->parse_cache->set(fmt, parts);
  }
  out = Alloc<List<BigStr*>>();
  status = this->_Format(parts, varargs, locs, out);
  if (status != 0) {
    return status;
  }
  result = S_Aoo->join(out);
  if (arg->v != nullptr) {
    v_loc = loc::Missing;
    lval = this->unsafe_arith->ParseLValue(arg->v, v_loc);
    state::BuiltinSetValue(this->mem, lval, Alloc<value::Str>(result));
  }
  else {
    mylib::Stdout()->write(result);
  }
  return 0;
}

}  // define namespace printf_osh

namespace process_osh {  // define

using syntax_asdl::loc;
using runtime_asdl::cmd_value;
using runtime_asdl::job_state_e;
using runtime_asdl::wait_status;
using runtime_asdl::wait_status_e;
using error::e_usage;
using error::e_die_status;
using mylib::print_stderr;

Jobs::Jobs(process::JobList* job_list) {
  this->job_list = job_list;
}

int Jobs::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::jobs* arg = nullptr;
  int style;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_jtz, cmd_val);
  attrs = tup0.at0();
  arg_r = tup0.at1();
  arg = Alloc<arg_types::jobs>(attrs->attrs);
  if (arg->l) {
    style = process::STYLE_LONG;
  }
  else {
    if (arg->p) {
      style = process::STYLE_PID_ONLY;
    }
    else {
      style = process::STYLE_DEFAULT;
    }
  }
  this->job_list->DisplayJobs(style);
  if (arg->debug) {
    this->job_list->DebugPrint();
  }
  return 0;
}

Fg::Fg(process::JobControl* job_control, process::JobList* job_list, process::Waiter* waiter) {
  this->job_control = job_control;
  this->job_list = job_list;
  this->waiter = waiter;
}

int Fg::Run(cmd_value::Argv* cmd_val) {
  BigStr* job_spec = nullptr;
  process::Job* job = nullptr;
  int pgid;
  int status;
  runtime_asdl::wait_status_t* wait_st = nullptr;
  runtime_asdl::wait_status_t* UP_wait_st = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&job_spec);
  StackRoot _root2(&job);
  StackRoot _root3(&wait_st);
  StackRoot _root4(&UP_wait_st);

  job_spec = S_Aoo;
  if (len(cmd_val->argv) > 1) {
    job_spec = cmd_val->argv->at(1);
  }
  job = this->job_list->GetJobWithSpec(job_spec);
  if (job == nullptr) {
    print_stderr(S_cfh);
    return 1;
  }
  pgid = job->ProcessGroupId();
  print_stderr(StrFormat("fg: PID %d Continued", pgid));
  this->job_control->MaybeGiveTerminal(pgid);
  job->SetForeground();
  job->state = job_state_e::Running;
  posix::killpg(pgid, SIGCONT);
  status = -1;
  wait_st = job->JobWait(this->waiter);
  UP_wait_st = wait_st;
  switch (wait_st->tag()) {
    case wait_status_e::Proc: {
      wait_status::Proc* wait_st = static_cast<wait_status::Proc*>(UP_wait_st);
      status = wait_st->code;
    }
      break;
    case wait_status_e::Pipeline: {
      wait_status::Pipeline* wait_st = static_cast<wait_status::Pipeline*>(UP_wait_st);
      status = wait_st->codes->at(-1);
    }
      break;
    case wait_status_e::Cancelled: {
      wait_status::Cancelled* wait_st = static_cast<wait_status::Cancelled*>(UP_wait_st);
      status = (128 + wait_st->sig_num);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  return status;
}

Bg::Bg(process::JobList* job_list) {
  this->job_list = job_list;
}

int Bg::Run(cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  throw Alloc<error::Usage>(S_wha, loc::Missing);
}

Fork::Fork(vm::_Executor* shell_ex) {
  this->shell_ex = shell_ex;
}

int Fork::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* arg = nullptr;
  syntax_asdl::loc_t* location = nullptr;
  syntax_asdl::command_t* cmd_frag = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&arg);
  StackRoot _root3(&location);
  StackRoot _root4(&cmd_frag);

  Tuple2<args::_Attributes*, args::Reader*> tup1 = flag_util::ParseCmdVal(S_Fzz_1, cmd_val, true);
  arg_r = tup1.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup2 = arg_r->Peek2();
  arg = tup2.at0();
  location = tup2.at1();
  if (arg != nullptr) {
    e_usage(StrFormat("got unexpected argument %r", arg), location);
  }
  cmd_frag = typed_args::RequiredBlockAsFrag(cmd_val);
  return this->shell_ex->RunBackgroundJob(cmd_frag);
}

ForkWait::ForkWait(vm::_Executor* shell_ex) {
  this->shell_ex = shell_ex;
}

int ForkWait::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* arg = nullptr;
  syntax_asdl::loc_t* location = nullptr;
  syntax_asdl::command_t* cmd_frag = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&arg);
  StackRoot _root3(&location);
  StackRoot _root4(&cmd_frag);

  Tuple2<args::_Attributes*, args::Reader*> tup3 = flag_util::ParseCmdVal(S_xAx, cmd_val, true);
  arg_r = tup3.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup4 = arg_r->Peek2();
  arg = tup4.at0();
  location = tup4.at1();
  if (arg != nullptr) {
    e_usage(StrFormat("got unexpected argument %r", arg), location);
  }
  cmd_frag = typed_args::RequiredBlockAsFrag(cmd_val);
  return this->shell_ex->RunSubshell(cmd_frag);
}

Exec::Exec(state::Mem* mem, process::ExternalProgram* ext_prog, process::FdState* fd_state, executor::SearchPath* search_path, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->ext_prog = ext_prog;
  this->fd_state = fd_state;
  this->search_path = search_path;
  this->errfmt = errfmt;
}

int Exec::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  Dict<BigStr*, BigStr*>* environ = nullptr;
  int i;
  BigStr* cmd = nullptr;
  BigStr* argv0_path = nullptr;
  cmd_value::Argv* c2 = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&environ);
  StackRoot _root3(&cmd);
  StackRoot _root4(&argv0_path);
  StackRoot _root5(&c2);

  Tuple2<args::_Attributes*, args::Reader*> tup5 = flag_util::ParseCmdVal(S_Evy, cmd_val);
  arg_r = tup5.at1();
  if (arg_r->AtEnd()) {
    this->fd_state->MakePermanent();
    return 0;
  }
  environ = this->mem->GetEnv();
  i = arg_r->i;
  cmd = cmd_val->argv->at(i);
  argv0_path = this->search_path->CachedLookup(cmd);
  if (argv0_path == nullptr) {
    e_die_status(127, StrFormat("exec: %r not found", cmd), cmd_val->arg_locs->at(1));
  }
  c2 = Alloc<cmd_value::Argv>(cmd_val->argv->slice(i), cmd_val->arg_locs->slice(i), cmd_val->is_last_cmd, cmd_val->self_obj, nullptr);
  this->ext_prog->Exec(argv0_path, c2, environ);
  assert(0);  // AssertionError
}

Wait::Wait(process::Waiter* waiter, process::JobList* job_list, state::Mem* mem, dev::Tracer* tracer, ui::ErrorFormatter* errfmt) {
  this->waiter = waiter;
  this->job_list = job_list;
  this->mem = mem;
  this->tracer = tracer;
  this->errfmt = errfmt;
}

int Wait::Run(cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  {  // with
    dev::ctx_Tracer ctx{this->tracer, S_Awk, cmd_val->argv};

    return this->_Run(cmd_val);
  }
}

int Wait::_Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::wait* arg = nullptr;
  List<BigStr*>* job_ids = nullptr;
  List<syntax_asdl::CompoundWord*>* arg_locs = nullptr;
  int n;
  int status;
  int target;
  int result;
  List<process::Job*>* jobs = nullptr;
  int i;
  syntax_asdl::CompoundWord* location = nullptr;
  process::Job* job = nullptr;
  int pid;
  runtime_asdl::wait_status_t* wait_st = nullptr;
  runtime_asdl::wait_status_t* UP_wait_st = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&job_ids);
  StackRoot _root5(&arg_locs);
  StackRoot _root6(&jobs);
  StackRoot _root7(&location);
  StackRoot _root8(&job);
  StackRoot _root9(&wait_st);
  StackRoot _root10(&UP_wait_st);

  Tuple2<args::_Attributes*, args::Reader*> tup6 = flag_util::ParseCmdVal(S_Awk, cmd_val);
  attrs = tup6.at0();
  arg_r = tup6.at1();
  arg = Alloc<arg_types::wait>(attrs->attrs);
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup7 = arg_r->Rest2();
  job_ids = tup7.at0();
  arg_locs = tup7.at1();
  if (arg->n) {
    n = this->job_list->NumRunning();
    if (n == 0) {
      status = 127;
    }
    else {
      target = (n - 1);
      status = 0;
      while (this->job_list->NumRunning() > target) {
        result = this->waiter->WaitForOne();
        if (result == process::W1_OK) {
          status = this->waiter->last_status;
        }
        else {
          if (result == process::W1_ECHILD) {
            status = 127;
            break;
          }
          else {
            if (result >= 0) {
              status = (128 + result);
              break;
            }
          }
        }
      }
    }
    return status;
  }
  if (len(job_ids) == 0) {
    status = 0;
    while (this->job_list->NumRunning() != 0) {
      result = this->waiter->WaitForOne();
      if (result == process::W1_ECHILD) {
        break;
      }
      else {
        if (result >= 0) {
          status = (128 + result);
          break;
        }
      }
    }
    return status;
  }
  jobs = Alloc<List<process::Job*>>();
  i = 0;
  for (ListIter<BigStr*> it(job_ids); !it.Done(); it.Next(), ++i) {
    BigStr* job_id = it.Value();
    StackRoot _for(&job_id  );
    location = arg_locs->at(i);
    job = nullptr;
    if ((str_equals(job_id, S_Aoo) or job_id->startswith(S_dkr))) {
      job = this->job_list->GetJobWithSpec(job_id);
    }
    if (job == nullptr) {
      try {
        pid = to_int(job_id);
      }
      catch (ValueError*) {
        throw Alloc<error::Usage>(StrFormat("expected PID or jobspec, got %r", job_id), location);
      }
      job = this->job_list->ProcessFromPid(pid);
    }
    if (job == nullptr) {
      this->errfmt->Print_(StrFormat("%s isn't a child of this shell", job_id), location);
      return 127;
    }
    jobs->append(job);
  }
  status = 1;
  for (ListIter<process::Job*> it(jobs); !it.Done(); it.Next()) {
    process::Job* job = it.Value();
    StackRoot _for(&job  );
    wait_st = job->JobWait(this->waiter);
    UP_wait_st = wait_st;
    switch (wait_st->tag()) {
      case wait_status_e::Proc: {
        wait_status::Proc* wait_st = static_cast<wait_status::Proc*>(UP_wait_st);
        status = wait_st->code;
      }
        break;
      case wait_status_e::Pipeline: {
        wait_status::Pipeline* wait_st = static_cast<wait_status::Pipeline*>(UP_wait_st);
        status = wait_st->codes->at(-1);
      }
        break;
      case wait_status_e::Cancelled: {
        wait_status::Cancelled* wait_st = static_cast<wait_status::Cancelled*>(UP_wait_st);
        status = (128 + wait_st->sig_num);
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  return status;
}

Umask::Umask() {
  ;  // pass
}

int Umask::Run(cmd_value::Argv* cmd_val) {
  List<BigStr*>* argv = nullptr;
  int mask;
  BigStr* a = nullptr;
  int new_mask;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&argv);
  StackRoot _root2(&a);

  argv = cmd_val->argv->slice(1);
  if (len(argv) == 0) {
    mask = posix::umask(0);
    posix::umask(mask);
    print(StrFormat("0%03o", mask));
    return 0;
  }
  if (len(argv) == 1) {
    a = argv->at(0);
    try {
      new_mask = to_int(a, 8);
    }
    catch (ValueError*) {
      print_stderr(S_ela);
      return 1;
    }
    posix::umask(new_mask);
    return 0;
  }
  e_usage(S_pdn, loc::Missing);
}

BigStr* _LimitString(mops::BigInt lim, int factor) {
  mops::BigInt i;
  if (mops::Equal(lim, mops::FromC(RLIM_INFINITY))) {
    return S_bxj;
  }
  else {
    i = mops::Div(lim, mops::IntWiden(factor));
    return mops::ToStr(i);
  }
}

Ulimit::Ulimit() {
  this->_table = nullptr;
}

List<Tuple4<BigStr*, int, int, BigStr*>*>* Ulimit::_Table() {
  if (this->_table == nullptr) {
    this->_table = NewList<Tuple4<BigStr*, int, int, BigStr*>*>(std::initializer_list<Tuple4<BigStr*, int, int, BigStr*>*>{(Alloc<Tuple4<BigStr*, int, int, BigStr*>>(S_DEp, RLIMIT_CORE, 512, S_qli)), (Alloc<Tuple4<BigStr*, int, int, BigStr*>>(S_vEu, RLIMIT_DATA, 1024, S_qnE)), (Alloc<Tuple4<BigStr*, int, int, BigStr*>>(S_kyo, RLIMIT_FSIZE, 512, S_eEz)), (Alloc<Tuple4<BigStr*, int, int, BigStr*>>(S_ygA, RLIMIT_NOFILE, 1, S_zwg)), (Alloc<Tuple4<BigStr*, int, int, BigStr*>>(S_eed, RLIMIT_STACK, 1024, S_BDe)), (Alloc<Tuple4<BigStr*, int, int, BigStr*>>(S_FqE, RLIMIT_CPU, 1, S_vqy)), (Alloc<Tuple4<BigStr*, int, int, BigStr*>>(S_vtj, RLIMIT_AS, 1024, S_iBl))});
  }
  return this->_table;
}

int Ulimit::_FindFactor(int what) {
  for (ListIter<Tuple4<BigStr*, int, int, BigStr*>*> it(this->_Table()); !it.Done(); it.Next()) {
    Tuple4<BigStr*, int, int, BigStr*>* tup8 = it.Value();
    int w = tup8->at1();
    int factor = tup8->at2();
    if (w == what) {
      return factor;
    }
  }
  assert(0);  // AssertionError
}

int Ulimit::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::ulimit* arg = nullptr;
  int what;
  int num_what_flags;
  bool show_all;
  BigStr* extra = nullptr;
  syntax_asdl::loc_t* extra_loc = nullptr;
  BigStr* fmt = nullptr;
  mops::BigInt soft;
  mops::BigInt hard;
  BigStr* soft2 = nullptr;
  BigStr* hard2 = nullptr;
  BigStr* s = nullptr;
  syntax_asdl::loc_t* s_loc = nullptr;
  int factor;
  mops::BigInt limit;
  bool ok;
  mops::BigInt big_int;
  mops::BigInt fac;
  BigStr* extra2 = nullptr;
  syntax_asdl::loc_t* extra_loc2 = nullptr;
  mops::BigInt old_soft;
  mops::BigInt old_hard;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&extra);
  StackRoot _root5(&extra_loc);
  StackRoot _root6(&fmt);
  StackRoot _root7(&soft2);
  StackRoot _root8(&hard2);
  StackRoot _root9(&s);
  StackRoot _root10(&s_loc);
  StackRoot _root11(&extra2);
  StackRoot _root12(&extra_loc2);

  Tuple2<args::_Attributes*, args::Reader*> tup9 = flag_util::ParseCmdVal(S_otx, cmd_val);
  attrs = tup9.at0();
  arg_r = tup9.at1();
  arg = Alloc<arg_types::ulimit>(attrs->attrs);
  what = 0;
  num_what_flags = 0;
  if (arg->c) {
    what = RLIMIT_CORE;
    num_what_flags += 1;
  }
  if (arg->d) {
    what = RLIMIT_DATA;
    num_what_flags += 1;
  }
  if (arg->f) {
    what = RLIMIT_FSIZE;
    num_what_flags += 1;
  }
  if (arg->n) {
    what = RLIMIT_NOFILE;
    num_what_flags += 1;
  }
  if (arg->s) {
    what = RLIMIT_STACK;
    num_what_flags += 1;
  }
  if (arg->t) {
    what = RLIMIT_CPU;
    num_what_flags += 1;
  }
  if (arg->v) {
    what = RLIMIT_AS;
    num_what_flags += 1;
  }
  if (num_what_flags > 1) {
    throw Alloc<error::Usage>(S_nla, cmd_val->arg_locs->at(0));
  }
  show_all = (arg->a or arg->all);
  if (show_all) {
    if (num_what_flags > 0) {
      throw Alloc<error::Usage>(S_hDg, cmd_val->arg_locs->at(0));
    }
    Tuple2<BigStr*, syntax_asdl::loc_t*> tup10 = arg_r->Peek2();
    extra = tup10.at0();
    extra_loc = tup10.at1();
    if (extra != nullptr) {
      throw Alloc<error::Usage>(S_zvw, extra_loc);
    }
    fmt = S_BDD;
    print(StrFormat(fmt, S_kDk, S_zrq, S_avA, S_aos, S_vnc));
    for (ListIter<Tuple4<BigStr*, int, int, BigStr*>*> it(this->_Table()); !it.Done(); it.Next()) {
      Tuple4<BigStr*, int, int, BigStr*>* tup11 = it.Value();
      BigStr* flag = tup11->at0();
      StackRoot _unpack_0(&flag);
      int what = tup11->at1();
      int factor = tup11->at2();
      BigStr* desc = tup11->at3();
      StackRoot _unpack_3(&desc);
      Tuple2<mops::BigInt, mops::BigInt> tup12 = pyos::GetRLimit(what);
      soft = tup12.at0();
      hard = tup12.at1();
      soft2 = _LimitString(soft, factor);
      hard2 = _LimitString(hard, factor);
      print(StrFormat(fmt, flag, soft2, hard2, str(factor), desc));
    }
    return 0;
  }
  if (num_what_flags == 0) {
    what = RLIMIT_FSIZE;
  }
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup13 = arg_r->Peek2();
  s = tup13.at0();
  s_loc = tup13.at1();
  if (s == nullptr) {
    factor = this->_FindFactor(what);
    Tuple2<mops::BigInt, mops::BigInt> tup14 = pyos::GetRLimit(what);
    soft = tup14.at0();
    hard = tup14.at1();
    if (arg->H) {
      print(_LimitString(hard, factor));
    }
    else {
      print(_LimitString(soft, factor));
    }
    return 0;
  }
  if (str_equals(s, S_bxj)) {
    limit = mops::FromC(RLIM_INFINITY);
  }
  else {
    if (match::LooksLikeInteger(s)) {
      Tuple2<bool, mops::BigInt> tup15 = mops::FromStr2(s);
      ok = tup15.at0();
      big_int = tup15.at1();
      if (!ok) {
        throw Alloc<error::Usage>(StrFormat("Integer too big: %s", s), s_loc);
      }
    }
    else {
      throw Alloc<error::Usage>(StrFormat("expected a number or 'unlimited', got %r", s), s_loc);
    }
    if (mops::Greater(mops::IntWiden(0), big_int)) {
      throw Alloc<error::Usage>(StrFormat("doesn't accept negative numbers, got %r", s), s_loc);
    }
    factor = this->_FindFactor(what);
    fac = mops::IntWiden(factor);
    limit = mops::Mul(big_int, fac);
    if (!mops::Equal(mops::Div(limit, fac), big_int)) {
      throw Alloc<error::Usage>(StrFormat("detected integer overflow: %s", mops::ToStr(big_int)), s_loc);
    }
  }
  arg_r->Next();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup16 = arg_r->Peek2();
  extra2 = tup16.at0();
  extra_loc2 = tup16.at1();
  if (extra2 != nullptr) {
    throw Alloc<error::Usage>(S_qsz, extra_loc2);
  }
  Tuple2<mops::BigInt, mops::BigInt> tup17 = pyos::GetRLimit(what);
  soft = tup17.at0();
  hard = tup17.at1();
  old_soft = soft;
  old_hard = hard;
  if ((!arg->S and !arg->H)) {
    soft = limit;
    hard = limit;
  }
  if (arg->S) {
    soft = limit;
  }
  if (arg->H) {
    hard = limit;
  }
  // if not PYTHON
  {
    try {
      pyos::SetRLimit(what, soft, hard);
    }
    catch (IOError_OSError* e) {
      print_stderr(StrFormat("oils: ulimit error: %s", pyutil::strerror(e)));
      return 1;
    }
  }
  // endif MYCPP
  return 0;
}

}  // define namespace process_osh

namespace pure_osh {  // define

using syntax_asdl::loc;
using types_asdl::opt_group_i;
using error::e_usage;
using mylib::print_stderr;

Boolean::Boolean(int status) {
  this->status = status;
}

int Boolean::Run(cmd_value::Argv* cmd_val) {
  StackRoot _root0(&cmd_val);

  typed_args::DoesNotAccept(cmd_val->proc_args);
  return this->status;
}

Alias::Alias(Dict<BigStr*, BigStr*>* aliases, ui::ErrorFormatter* errfmt) {
  this->aliases = aliases;
  this->errfmt = errfmt;
}

int Alias::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  List<BigStr*>* argv = nullptr;
  BigStr* alias_exp = nullptr;
  int status;
  int i;
  BigStr* name = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&argv);
  StackRoot _root3(&alias_exp);
  StackRoot _root4(&name);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_nwn, cmd_val);
  arg_r = tup0.at1();
  argv = arg_r->Rest();
  if (len(argv) == 0) {
    for (ListIter<BigStr*> it(sorted(this->aliases)); !it.Done(); it.Next()) {
      BigStr* name = it.Value();
      StackRoot _for(&name    );
      alias_exp = this->aliases->at(name);
      print(StrFormat("alias %s=%r", name, alias_exp));
    }
    return 0;
  }
  status = 0;
  i = 0;
  for (ListIter<BigStr*> it(argv); !it.Done(); it.Next(), ++i) {
    BigStr* arg = it.Value();
    StackRoot _for(&arg  );
    Tuple2<BigStr*, BigStr*> tup1 = mylib::split_once(arg, S_bby);
    name = tup1.at0();
    alias_exp = tup1.at1();
    if (alias_exp == nullptr) {
      alias_exp = this->aliases->get(name);
      if (alias_exp == nullptr) {
        this->errfmt->Print_(StrFormat("No alias named %r", name), cmd_val->arg_locs->at(i));
        status = 1;
      }
      else {
        print(StrFormat("alias %s=%r", name, alias_exp));
      }
    }
    else {
      this->aliases->set(name, alias_exp);
    }
  }
  return status;
}

UnAlias::UnAlias(Dict<BigStr*, BigStr*>* aliases, ui::ErrorFormatter* errfmt) {
  this->aliases = aliases;
  this->errfmt = errfmt;
}

int UnAlias::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::unalias* arg = nullptr;
  List<BigStr*>* argv = nullptr;
  int status;
  int i;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&argv);

  Tuple2<args::_Attributes*, args::Reader*> tup2 = flag_util::ParseCmdVal(S_Brd, cmd_val);
  attrs = tup2.at0();
  arg_r = tup2.at1();
  arg = Alloc<arg_types::unalias>(attrs->attrs);
  if (arg->a) {
    this->aliases->clear();
    return 0;
  }
  argv = arg_r->Rest();
  if (len(argv) == 0) {
    e_usage(S_Bot, loc::Missing);
  }
  status = 0;
  i = 0;
  for (ListIter<BigStr*> it(argv); !it.Done(); it.Next(), ++i) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    if (dict_contains(this->aliases, name)) {
      mylib::dict_erase(this->aliases, name);
    }
    else {
      this->errfmt->Print_(StrFormat("No alias named %r", name), cmd_val->arg_locs->at(i));
      status = 1;
    }
  }
  return status;
}

void SetOptionsFromFlags(state::MutableOpts* exec_opts, List<Tuple2<BigStr*, bool>*>* opt_changes, List<Tuple2<BigStr*, bool>*>* shopt_changes) {
  StackRoot _root0(&exec_opts);
  StackRoot _root1(&opt_changes);
  StackRoot _root2(&shopt_changes);

  for (ListIter<Tuple2<BigStr*, bool>*> it(opt_changes); !it.Done(); it.Next()) {
    Tuple2<BigStr*, bool>* tup3 = it.Value();
    BigStr* opt_name = tup3->at0();
    StackRoot _unpack_0(&opt_name);
    bool b = tup3->at1();
    exec_opts->SetAnyOption(opt_name, b);
  }
  for (ListIter<Tuple2<BigStr*, bool>*> it(shopt_changes); !it.Done(); it.Next()) {
    Tuple2<BigStr*, bool>* tup4 = it.Value();
    BigStr* opt_name = tup4->at0();
    StackRoot _unpack_0(&opt_name);
    bool b = tup4->at1();
    exec_opts->SetAnyOption(opt_name, b);
  }
}

bool ShowOptions(state::MutableOpts* mutable_opts, List<BigStr*>* opt_names) {
  bool any_false;
  int opt_num;
  bool b;
  StackRoot _root0(&mutable_opts);
  StackRoot _root1(&opt_names);

  if (len(opt_names) == 0) {
    opt_names = Alloc<List<BigStr*>>();
    for (ListIter<int> it(consts::SET_OPTION_NUMS); !it.Done(); it.Next()) {
      int i = it.Value();
      opt_names->append(consts::OptionName(i));
    }
  }
  any_false = false;
  for (ListIter<BigStr*> it(opt_names); !it.Done(); it.Next()) {
    BigStr* opt_name = it.Value();
    StackRoot _for(&opt_name  );
    opt_num = state::_SetOptionNum(opt_name);
    b = mutable_opts->Get(opt_num);
    if (!b) {
      any_false = true;
    }
    print(StrFormat("set %so %s", b ? S_Bjq : S_jnE, opt_name));
  }
  return any_false;
}

bool _ShowShoptOptions(state::MutableOpts* mutable_opts, List<int>* opt_nums) {
  bool any_false;
  bool b;
  StackRoot _root0(&mutable_opts);
  StackRoot _root1(&opt_nums);

  if (len(opt_nums) == 0) {
    opt_nums->extend(consts::VISIBLE_SHOPT_NUMS);
  }
  any_false = false;
  for (ListIter<int> it(opt_nums); !it.Done(); it.Next()) {
    int opt_num = it.Value();
    b = mutable_opts->Get(opt_num);
    if (!b) {
      any_false = true;
    }
    print(StrFormat("shopt -%s %s", b ? S_anC : S_rsz, consts::OptionName(opt_num)));
  }
  return any_false;
}

Set::Set(state::MutableOpts* exec_opts, state::Mem* mem) {
  this->exec_opts = exec_opts;
  this->mem = mem;
}

int Set::Run(cmd_value::Argv* cmd_val) {
  Dict<BigStr*, BigStr*>* mapping = nullptr;
  BigStr* str_val = nullptr;
  BigStr* code_str = nullptr;
  args::Reader* arg_r = nullptr;
  args::_Attributes* arg = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&mapping);
  StackRoot _root2(&str_val);
  StackRoot _root3(&code_str);
  StackRoot _root4(&arg_r);
  StackRoot _root5(&arg);

  if (len(cmd_val->argv) == 1) {
    mapping = this->mem->GetAllVars();
    for (ListIter<BigStr*> it(sorted(mapping)); !it.Done(); it.Next()) {
      BigStr* name = it.Value();
      StackRoot _for(&name    );
      str_val = mapping->at(name);
      code_str = StrFormat("%s=%s", name, j8_lite::MaybeShellEncode(str_val));
      print(code_str);
    }
    return 0;
  }
  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  arg = flag_util::ParseMore(S_flq, arg_r);
  if (arg->show_options) {
    ShowOptions(this->exec_opts, Alloc<List<BigStr*>>());
    return 0;
  }
  for (ListIter<Tuple2<BigStr*, bool>*> it(arg->opt_changes); !it.Done(); it.Next()) {
    Tuple2<BigStr*, bool>* tup5 = it.Value();
    BigStr* opt_name = tup5->at0();
    StackRoot _unpack_0(&opt_name);
    bool b = tup5->at1();
    this->exec_opts->SetOldOption(opt_name, b);
  }
  for (ListIter<Tuple2<BigStr*, bool>*> it(arg->shopt_changes); !it.Done(); it.Next()) {
    Tuple2<BigStr*, bool>* tup6 = it.Value();
    BigStr* opt_name = tup6->at0();
    StackRoot _unpack_0(&opt_name);
    bool b = tup6->at1();
    this->exec_opts->SetAnyOption(opt_name, b);
  }
  if ((arg->saw_double_dash or !arg_r->AtEnd())) {
    this->mem->SetArgv(arg_r->Rest());
  }
  return 0;
}

Shopt::Shopt(optview::Exec* exec_opts, state::MutableOpts* mutable_opts, cmd_eval::CommandEvaluator* cmd_ev, state::Mem* mem, Dict<BigStr*, BigStr*>* environ) {
  this->exec_opts = exec_opts;
  this->mutable_opts = mutable_opts;
  this->cmd_ev = cmd_ev;
  this->mem = mem;
  this->environ = environ;
}

int Shopt::_PrintOptions(bool use_set_opts, List<BigStr*>* opt_names) {
  bool any_false;
  bool any_single_names;
  List<int>* opt_nums = nullptr;
  int opt_group;
  int index;
  StackRoot _root0(&opt_names);
  StackRoot _root1(&opt_nums);

  if (use_set_opts) {
    any_false = ShowOptions(this->mutable_opts, opt_names);
    if (len(opt_names)) {
      return any_false ? 1 : 0;
    }
    else {
      return 0;
    }
  }
  else {
    any_single_names = false;
    opt_nums = Alloc<List<int>>();
    for (ListIter<BigStr*> it(opt_names); !it.Done(); it.Next()) {
      BigStr* opt_name = it.Value();
      StackRoot _for(&opt_name    );
      opt_group = consts::OptionGroupNum(opt_name);
      if (opt_group == opt_group_i::YshUpgrade) {
        opt_nums->extend(consts::YSH_UPGRADE);
      }
      else {
        if (opt_group == opt_group_i::YshAll) {
          opt_nums->extend(consts::YSH_ALL);
        }
        else {
          if (opt_group == opt_group_i::StrictAll) {
            opt_nums->extend(consts::STRICT_ALL);
          }
          else {
            index = consts::OptionNum(opt_name);
            if (index == 0) {
              if (this->exec_opts->ignore_shopt_not_impl()) {
                index = consts::UnimplOptionNum(opt_name);
              }
              if (index == 0) {
                e_usage(StrFormat("got invalid option %r", opt_name), loc::Missing);
              }
            }
            opt_nums->append(index);
            any_single_names = true;
          }
        }
      }
    }
    any_false = _ShowShoptOptions(this->mutable_opts, opt_nums);
    if (any_single_names) {
      return any_false ? 1 : 0;
    }
    else {
      return 0;
    }
  }
}

int Shopt::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::shopt* arg = nullptr;
  List<BigStr*>* opt_names = nullptr;
  int index;
  bool b;
  syntax_asdl::command_t* cmd_frag = nullptr;
  List<int>* opt_nums = nullptr;
  int opt_group;
  int unused;
  (void)unused;
  bool ignore_shopt_not_impl;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&opt_names);
  StackRoot _root5(&cmd_frag);
  StackRoot _root6(&opt_nums);

  Tuple2<args::_Attributes*, args::Reader*> tup7 = flag_util::ParseCmdVal(S_ene, cmd_val, true);
  attrs = tup7.at0();
  arg_r = tup7.at1();
  arg = Alloc<arg_types::shopt>(attrs->attrs);
  opt_names = arg_r->Rest();
  if (arg->q) {
    for (ListIter<BigStr*> it(opt_names); !it.Done(); it.Next()) {
      BigStr* name = it.Value();
      StackRoot _for(&name    );
      index = consts::OptionNum(name);
      if (index == 0) {
        if (this->exec_opts->ignore_shopt_not_impl()) {
          index = consts::UnimplOptionNum(name);
        }
        if (index == 0) {
          return 2;
        }
      }
      if (!this->mutable_opts->opt0_array->at(index)) {
        return 1;
      }
    }
    return 0;
  }
  if (arg->s) {
    b = true;
  }
  else {
    if (arg->u) {
      b = false;
    }
    else {
      if (arg->p) {
        return this->_PrintOptions(arg->o, opt_names);
      }
      else {
        return this->_PrintOptions(arg->o, opt_names);
      }
    }
  }
  cmd_frag = typed_args::OptionalBlockAsFrag(cmd_val);
  if (cmd_frag) {
    opt_nums = Alloc<List<int>>();
    for (ListIter<BigStr*> it(opt_names); !it.Done(); it.Next()) {
      BigStr* opt_name = it.Value();
      StackRoot _for(&opt_name    );
      opt_group = consts::OptionGroupNum(opt_name);
      if (opt_group == opt_group_i::YshUpgrade) {
        opt_nums->extend(consts::YSH_UPGRADE);
        if (b) {
          this->mem->MaybeInitEnvDict(this->environ);
        }
        continue;
      }
      if (opt_group == opt_group_i::YshAll) {
        opt_nums->extend(consts::YSH_ALL);
        if (b) {
          this->mem->MaybeInitEnvDict(this->environ);
        }
        continue;
      }
      if (opt_group == opt_group_i::StrictAll) {
        opt_nums->extend(consts::STRICT_ALL);
        continue;
      }
      index = consts::OptionNum(opt_name);
      if (index == 0) {
        if (this->exec_opts->ignore_shopt_not_impl()) {
          index = consts::UnimplOptionNum(opt_name);
        }
        if (index == 0) {
          e_usage(StrFormat("got invalid option %r", opt_name), loc::Missing);
        }
      }
      opt_nums->append(index);
    }
    {  // with
      state::ctx_Option ctx{this->mutable_opts, opt_nums, b};

      unused = this->cmd_ev->EvalCommandFrag(cmd_frag);
    }
    return 0;
  }
  ignore_shopt_not_impl = this->exec_opts->ignore_shopt_not_impl();
  for (ListIter<BigStr*> it(opt_names); !it.Done(); it.Next()) {
    BigStr* opt_name = it.Value();
    StackRoot _for(&opt_name  );
    this->mutable_opts->SetAnyOption(opt_name, b, ignore_shopt_not_impl);
  }
  return 0;
}

Hash::Hash(executor::SearchPath* search_path) {
  this->search_path = search_path;
}

int Hash::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::hash* arg = nullptr;
  List<BigStr*>* rest = nullptr;
  int status;
  BigStr* full_path = nullptr;
  List<BigStr*>* commands = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&rest);
  StackRoot _root5(&full_path);
  StackRoot _root6(&commands);

  Tuple2<args::_Attributes*, args::Reader*> tup8 = flag_util::ParseCmdVal(S_drr, cmd_val);
  attrs = tup8.at0();
  arg_r = tup8.at1();
  arg = Alloc<arg_types::hash>(attrs->attrs);
  rest = arg_r->Rest();
  if (arg->r) {
    if (len(rest)) {
      e_usage(S_wrs, loc::Missing);
    }
    this->search_path->ClearCache();
    return 0;
  }
  status = 0;
  if (len(rest)) {
    for (ListIter<BigStr*> it(rest); !it.Done(); it.Next()) {
      BigStr* cmd = it.Value();
      StackRoot _for(&cmd    );
      full_path = this->search_path->CachedLookup(cmd);
      if (full_path == nullptr) {
        print_stderr(StrFormat("hash: %r not found", cmd));
        status = 1;
      }
    }
  }
  else {
    commands = this->search_path->CachedCommands();
    commands->sort();
    for (ListIter<BigStr*> it(commands); !it.Done(); it.Next()) {
      BigStr* cmd = it.Value();
      StackRoot _for(&cmd    );
      print(cmd);
    }
  }
  return status;
}

Dict<BigStr*, bool>* _ParseOptSpec(BigStr* spec_str) {
  Dict<BigStr*, bool>* spec = nullptr;
  int i;
  int n;
  BigStr* ch = nullptr;
  StackRoot _root0(&spec_str);
  StackRoot _root1(&spec);
  StackRoot _root2(&ch);

  spec = Alloc<Dict<BigStr*, bool>>();
  i = 0;
  n = len(spec_str);
  while (true) {
    if (i >= n) {
      break;
    }
    ch = spec_str->at(i);
    spec->set(ch, false);
    i += 1;
    if (i >= n) {
      break;
    }
    if (str_equals(spec_str->at(i), S_fyj)) {
      spec->set(ch, true);
      i += 1;
    }
  }
  return spec;
}

GetOptsState::GetOptsState(state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->errfmt = errfmt;
  this->_optind = -1;
  this->flag_pos = 1;
}

int GetOptsState::_OptInd() {
  int result;
  try {
    result = state::GetInteger(this->mem, S_fdf);
  }
  catch (error::Runtime* e) {
    this->errfmt->Print_(e->UserErrorString());
    result = -1;
  }
  return result;
}

BigStr* GetOptsState::GetArg(List<BigStr*>* argv) {
  int optind;
  int i;
  StackRoot _root0(&argv);

  optind = this->_OptInd();
  if (optind == -1) {
    return nullptr;
  }
  this->_optind = optind;
  i = (optind - 1);
  if ((0 <= i and i < len(argv))) {
    return argv->at(i);
  }
  else {
    return nullptr;
  }
}

void GetOptsState::IncIndex() {
  state::BuiltinSetString(this->mem, S_fdf, str((this->_optind + 1)));
  this->flag_pos = 1;
}

void GetOptsState::SetArg(BigStr* optarg) {
  StackRoot _root0(&optarg);

  state::BuiltinSetString(this->mem, S_apD, optarg);
}

void GetOptsState::Fail() {
  state::BuiltinSetString(this->mem, S_apD, S_Aoo);
}

Tuple2<int, BigStr*> _GetOpts(Dict<BigStr*, bool>* spec, List<BigStr*>* argv, pure_osh::GetOptsState* my_state, ui::ErrorFormatter* errfmt) {
  BigStr* current = nullptr;
  BigStr* flag_char = nullptr;
  bool more_chars;
  BigStr* optarg = nullptr;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&spec);
  StackRoot _root1(&argv);
  StackRoot _root2(&my_state);
  StackRoot _root3(&errfmt);
  StackRoot _root4(&current);
  StackRoot _root5(&flag_char);
  StackRoot _root6(&optarg);
  StackRoot _root7(&tmp);

  current = my_state->GetArg(argv);
  if (current == nullptr) {
    my_state->Fail();
    return Tuple2<int, BigStr*>(1, S_BAk);
  }
  if ((!current->startswith(S_Bjq) or str_equals(current, S_Bjq))) {
    my_state->Fail();
    return Tuple2<int, BigStr*>(1, S_BAk);
  }
  flag_char = current->at(my_state->flag_pos);
  if (my_state->flag_pos < (len(current) - 1)) {
    my_state->flag_pos += 1;
    more_chars = true;
  }
  else {
    my_state->IncIndex();
    my_state->flag_pos = 1;
    more_chars = false;
  }
  if (!dict_contains(spec, flag_char)) {
    return Tuple2<int, BigStr*>(0, S_BAk);
  }
  if (spec->at(flag_char)) {
    if (more_chars) {
      optarg = current->slice(my_state->flag_pos);
    }
    else {
      optarg = my_state->GetArg(argv);
      if (optarg == nullptr) {
        my_state->Fail();
        errfmt->Print_(StrFormat("getopts: option %r requires an argument.", current));
        tmp = Alloc<List<BigStr*>>();
        for (ListIter<BigStr*> it(argv); !it.Done(); it.Next()) {
          BigStr* a = it.Value();
          tmp->append(j8_lite::MaybeShellEncode(a));
        }
        print_stderr(StrFormat("(getopts argv: %s)", S_yfw->join(tmp)));
        return Tuple2<int, BigStr*>(0, S_BAk);
      }
    }
    my_state->IncIndex();
    my_state->SetArg(optarg);
  }
  else {
    my_state->SetArg(S_Aoo);
  }
  return Tuple2<int, BigStr*>(0, flag_char);
}

GetOpts::GetOpts(state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->errfmt = errfmt;
  this->my_state = Alloc<GetOptsState>(mem, errfmt);
  this->spec_cache = Alloc<Dict<BigStr*, Dict<BigStr*, bool>*>>();
}

int GetOpts::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  BigStr* spec_str = nullptr;
  BigStr* var_name = nullptr;
  syntax_asdl::loc_t* var_loc = nullptr;
  Dict<BigStr*, bool>* spec = nullptr;
  List<BigStr*>* user_argv = nullptr;
  int status;
  BigStr* flag_char = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&spec_str);
  StackRoot _root3(&var_name);
  StackRoot _root4(&var_loc);
  StackRoot _root5(&spec);
  StackRoot _root6(&user_argv);
  StackRoot _root7(&flag_char);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  spec_str = arg_r->ReadRequired(S_seh);
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup9 = arg_r->ReadRequired2(S_lgh);
  var_name = tup9.at0();
  var_loc = tup9.at1();
  spec = this->spec_cache->get(spec_str);
  if (spec == nullptr) {
    spec = _ParseOptSpec(spec_str);
    this->spec_cache->set(spec_str, spec);
  }
  user_argv = arg_r->AtEnd() ? this->mem->GetArgv() : arg_r->Rest();
  Tuple2<int, BigStr*> tup10 = _GetOpts(spec, user_argv, this->my_state, this->errfmt);
  status = tup10.at0();
  flag_char = tup10.at1();
  if (match::IsValidVarName(var_name)) {
    state::BuiltinSetString(this->mem, var_name, flag_char);
  }
  else {
    throw Alloc<error::Usage>(StrFormat("got invalid variable name %r", var_name), var_loc);
  }
  return status;
}

}  // define namespace pure_osh

namespace pure_ysh {  // define

using runtime_asdl::cmd_value;
using syntax_asdl::command_t;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;

Shvar::Shvar(state::Mem* mem, executor::SearchPath* search_path, cmd_eval::CommandEvaluator* cmd_ev) {
  this->mem = mem;
  this->search_path = search_path;
  this->cmd_ev = cmd_ev;
}

int Shvar::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  syntax_asdl::command_t* cmd_frag = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* vars = nullptr;
  List<BigStr*>* args = nullptr;
  List<syntax_asdl::CompoundWord*>* arg_locs = nullptr;
  int i;
  BigStr* name = nullptr;
  BigStr* s = nullptr;
  value_asdl::value_t* v = nullptr;
  int unused;
  (void)unused;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&cmd_frag);
  StackRoot _root3(&vars);
  StackRoot _root4(&args);
  StackRoot _root5(&arg_locs);
  StackRoot _root6(&name);
  StackRoot _root7(&s);
  StackRoot _root8(&v);

  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_bBe, cmd_val, true);
  arg_r = tup0.at1();
  cmd_frag = typed_args::RequiredBlockAsFrag(cmd_val);
  vars = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup1 = arg_r->Rest2();
  args = tup1.at0();
  arg_locs = tup1.at1();
  if (len(args) == 0) {
    throw Alloc<error::Usage>(S_mhw, loc::Missing);
  }
  i = 0;
  for (ListIter<BigStr*> it(args); !it.Done(); it.Next(), ++i) {
    BigStr* arg = it.Value();
    StackRoot _for(&arg  );
    Tuple2<BigStr*, BigStr*> tup2 = mylib::split_once(arg, S_bby);
    name = tup2.at0();
    s = tup2.at1();
    if (s == nullptr) {
      throw Alloc<error::Usage>(S_mhw, arg_locs->at(i));
    }
    v = Alloc<value::Str>(s);
    vars->set(name, v);
    if (str_equals(name, S_jip)) {
      this->search_path->ClearCache();
    }
  }
  {  // with
    state::ctx_Eval ctx{this->mem, nullptr, nullptr, vars};

    unused = this->cmd_ev->EvalCommandFrag(cmd_frag);
  }
  return 0;
}

ctx_Context::ctx_Context(state::Mem* mem, Dict<BigStr*, value_asdl::value_t*>* context) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  this->mem = mem;
  this->mem->PushContextStack(context);
}

ctx_Context::~ctx_Context() {
  this->mem->PopContextStack();
  gHeap.PopRoot();
}

Ctx::Ctx(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev) {
  this->mem = mem;
  this->cmd_ev = cmd_ev;
}

Dict<BigStr*, value_asdl::value_t*>* Ctx::_GetContext() {
  Dict<BigStr*, value_asdl::value_t*>* ctx = nullptr;
  StackRoot _root0(&ctx);

  ctx = this->mem->GetContext();
  if (ctx == nullptr) {
    throw Alloc<error::Expr>(S_mci, loc::Missing);
  }
  return ctx;
}

int Ctx::_Push(Dict<BigStr*, value_asdl::value_t*>* context, syntax_asdl::command_t* block) {
  StackRoot _root0(&context);
  StackRoot _root1(&block);

  {  // with
    ctx_Context ctx{this->mem, context};

    return this->cmd_ev->EvalCommandFrag(block);
  }
}

int Ctx::_Set(Dict<BigStr*, value_asdl::value_t*>* updates) {
  Dict<BigStr*, value_asdl::value_t*>* ctx = nullptr;
  StackRoot _root0(&updates);
  StackRoot _root1(&ctx);

  ctx = this->_GetContext();
  ctx->update(updates);
  return 0;
}

int Ctx::_Emit(BigStr* field, value_asdl::value_t* item, syntax_asdl::loc_t* blame) {
  Dict<BigStr*, value_asdl::value_t*>* ctx = nullptr;
  value_asdl::value_t* UP_arr = nullptr;
  StackRoot _root0(&field);
  StackRoot _root1(&item);
  StackRoot _root2(&blame);
  StackRoot _root3(&ctx);
  StackRoot _root4(&UP_arr);

  ctx = this->_GetContext();
  if (!dict_contains(ctx, field)) {
    ctx->set(field, Alloc<value::List>(Alloc<List<value_asdl::value_t*>>()));
  }
  UP_arr = ctx->at(field);
  if (UP_arr->tag() != value_e::List) {
    throw Alloc<error::TypeErr>(UP_arr, StrFormat("Expected the context item '%s' to be a List", field), blame);
  }
  value::List* arr = static_cast<value::List*>(UP_arr);
  arr->items->append(item);
  return 0;
}

int Ctx::Run(cmd_value::Argv* cmd_val) {
  typed_args::Reader* rd = nullptr;
  args::Reader* arg_r = nullptr;
  BigStr* verb = nullptr;
  syntax_asdl::loc_t* verb_loc = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* context = nullptr;
  syntax_asdl::command_t* block = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* updates = nullptr;
  BigStr* field = nullptr;
  syntax_asdl::loc_t* field_loc = nullptr;
  value_asdl::value_t* item = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&rd);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&verb);
  StackRoot _root4(&verb_loc);
  StackRoot _root5(&context);
  StackRoot _root6(&block);
  StackRoot _root7(&updates);
  StackRoot _root8(&field);
  StackRoot _root9(&field_loc);
  StackRoot _root10(&item);

  rd = typed_args::ReaderForProc(cmd_val);
  Tuple2<args::_Attributes*, args::Reader*> tup3 = flag_util::ParseCmdVal(S_acj, cmd_val, true);
  arg_r = tup3.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup4 = arg_r->ReadRequired2(S_jgs);
  verb = tup4.at0();
  verb_loc = tup4.at1();
  if (str_equals(verb, S_Cwb)) {
    context = rd->PosDict();
    block = rd->RequiredBlockAsFrag();
    rd->Done();
    arg_r->AtEnd();
    return this->_Push(context, block);
  }
  else {
    if (str_equals(verb, S_flq)) {
      updates = rd->RestNamed();
      rd->Done();
      arg_r->AtEnd();
      return this->_Set(updates);
    }
    else {
      if (str_equals(verb, S_orf)) {
        Tuple2<BigStr*, syntax_asdl::loc_t*> tup5 = arg_r->ReadRequired2(S_ktk);
        field = tup5.at0();
        field_loc = tup5.at1();
        item = rd->PosValue();
        rd->Done();
        arg_r->AtEnd();
        return this->_Emit(field, item, field_loc);
      }
      else {
        throw Alloc<error::Usage>(StrFormat("Unknown verb '%s'", verb), verb_loc);
      }
    }
  }
}

PushRegisters::PushRegisters(state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev) {
  this->mem = mem;
  this->cmd_ev = cmd_ev;
}

int PushRegisters::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  syntax_asdl::command_t* cmd_frag = nullptr;
  int unused;
  (void)unused;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&cmd_frag);

  Tuple2<args::_Attributes*, args::Reader*> tup6 = flag_util::ParseCmdVal(S_gma, cmd_val, true);
  arg_r = tup6.at1();
  cmd_frag = typed_args::RequiredBlockAsFrag(cmd_val);
  {  // with
    state::ctx_Registers ctx{this->mem};

    unused = this->cmd_ev->EvalCommandFrag(cmd_frag);
  }
  return this->mem->last_status->at(-1);
}

Append::Append(state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->errfmt = errfmt;
}

int Append::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  typed_args::Reader* rd = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  List<value_asdl::value_t*>* typed = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&rd);
  StackRoot _root3(&val);
  StackRoot _root4(&UP_val);
  StackRoot _root5(&typed);

  Tuple2<args::_Attributes*, args::Reader*> tup7 = flag_util::ParseCmdVal(S_BEq, cmd_val, true);
  arg_r = tup7.at1();
  rd = typed_args::ReaderForProc(cmd_val);
  val = rd->PosValue();
  rd->Done();
  UP_val = val;
  switch (val->tag()) {
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      bash_impl::BashArray_AppendValues(val, arg_r->Rest());
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
      bash_impl::SparseArray_AppendValues(val, arg_r->Rest());
    }
      break;
    case value_e::List: {
      value::List* val = static_cast<value::List*>(UP_val);
      typed = Alloc<List<value_asdl::value_t*>>();
      for (ListIter<BigStr*> it(arg_r->Rest()); !it.Done(); it.Next()) {
        BigStr* s = it.Value();
        typed->append(Alloc<value::Str>(s));
      }
      val->items->extend(typed);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_rxi, loc::Missing);
    }
  }
  return 0;
}

}  // define namespace pure_ysh

namespace read_osh {  // define

using runtime_asdl::span_e;
using runtime_asdl::cmd_value;
using syntax_asdl::source;
using syntax_asdl::loc_t;
using value_asdl::value;
using value_asdl::LeftName;
using error::e_die;

Tuple2<bool, bool> _AppendParts(BigStr* s, List<Tuple2<runtime_asdl::span_t, int>*>* spans, int max_results, bool join_next, List<mylib::BufWriter*>* parts) {
  int start_index;
  bool last_span_was_black;
  mylib::BufWriter* buf = nullptr;
  bool done;
  runtime_asdl::span_t last_span_type;
  StackRoot _root0(&s);
  StackRoot _root1(&spans);
  StackRoot _root2(&parts);
  StackRoot _root3(&buf);

  start_index = 0;
  last_span_was_black = false;
  for (ListIter<Tuple2<runtime_asdl::span_t, int>*> it(spans); !it.Done(); it.Next()) {
    Tuple2<runtime_asdl::span_t, int>* tup0 = it.Value();
    runtime_asdl::span_t span_type = tup0->at0();
    int end_index = tup0->at1();
    if (span_type == span_e::Black) {
      if ((join_next and len(parts))) {
        parts->at(-1)->write(s->slice(start_index, end_index));
        join_next = false;
      }
      else {
        buf = Alloc<mylib::BufWriter>();
        buf->write(s->slice(start_index, end_index));
        parts->append(buf);
      }
      last_span_was_black = true;
    }
    else {
      if (span_type == span_e::Delim) {
        if (join_next) {
          parts->at(-1)->write(s->slice(start_index, end_index));
          join_next = false;
        }
        last_span_was_black = false;
      }
      else {
        if (span_type == span_e::Backslash) {
          if (last_span_was_black) {
            join_next = true;
          }
          last_span_was_black = false;
        }
      }
    }
    if ((max_results and len(parts) >= max_results)) {
      join_next = true;
    }
    start_index = end_index;
  }
  done = true;
  if (len(spans)) {
    Tuple2<runtime_asdl::span_t, int>* tup1 = spans->at(-1);
    last_span_type = tup1->at0();
    if (last_span_type == span_e::Backslash) {
      done = false;
    }
  }
  return Tuple2<bool, bool>(done, join_next);
}

BigStr* _ReadN(int num_bytes, cmd_eval::CommandEvaluator* cmd_ev) {
  List<BigStr*>* chunks = nullptr;
  int bytes_left;
  int n;
  int err_num;
  StackRoot _root0(&cmd_ev);
  StackRoot _root1(&chunks);

  chunks = Alloc<List<BigStr*>>();
  bytes_left = num_bytes;
  while (bytes_left > 0) {
    Tuple2<int, int> tup2 = pyos::Read(STDIN_FILENO, bytes_left, chunks);
    n = tup2.at0();
    err_num = tup2.at1();
    if (n < 0) {
      if (err_num == EINTR) {
        cmd_ev->RunPendingTraps();
      }
      else {
        throw Alloc<pyos::ReadError>(err_num);
      }
    }
    else {
      if (n == 0) {
        break;
      }
      else {
        bytes_left -= n;
      }
    }
  }
  return S_Aoo->join(chunks);
}

Tuple2<BigStr*, bool> _ReadPortion(int delim_byte, int max_chars, cmd_eval::CommandEvaluator* cmd_ev) {
  bool eof;
  List<int>* ch_array = nullptr;
  int bytes_read;
  int ch;
  int err_num;
  StackRoot _root0(&cmd_ev);
  StackRoot _root1(&ch_array);

  eof = false;
  ch_array = Alloc<List<int>>();
  bytes_read = 0;
  while (true) {
    if ((max_chars >= 0 and bytes_read >= max_chars)) {
      break;
    }
    Tuple2<int, int> tup3 = pyos::ReadByte(0);
    ch = tup3.at0();
    err_num = tup3.at1();
    if (ch < 0) {
      if (err_num == EINTR) {
        cmd_ev->RunPendingTraps();
      }
      else {
        throw Alloc<pyos::ReadError>(err_num);
      }
    }
    else {
      if (ch == pyos::EOF_SENTINEL) {
        eof = true;
        break;
      }
      else {
        if (ch == delim_byte) {
          break;
        }
        else {
          ch_array->append(ch);
        }
      }
    }
    bytes_read += 1;
  }
  return Tuple2<BigStr*, bool>(pyutil::ChArrayToString(ch_array), eof);
}

BigStr* ReadLineSlowly(cmd_eval::CommandEvaluator* cmd_ev, bool with_eol) {
  List<int>* ch_array = nullptr;
  int ch;
  int err_num;
  StackRoot _root0(&cmd_ev);
  StackRoot _root1(&ch_array);

  ch_array = Alloc<List<int>>();
  while (true) {
    Tuple2<int, int> tup4 = pyos::ReadByte(0);
    ch = tup4.at0();
    err_num = tup4.at1();
    if (ch < 0) {
      if (err_num == EINTR) {
        cmd_ev->RunPendingTraps();
      }
      else {
        throw Alloc<pyos::ReadError>(err_num);
      }
    }
    else {
      if (ch == pyos::EOF_SENTINEL) {
        break;
      }
      else {
        ch_array->append(ch);
      }
    }
    if (ch == pyos::NEWLINE_CH) {
      if (!with_eol) {
        ch_array->pop();
      }
      break;
    }
  }
  return pyutil::ChArrayToString(ch_array);
}

BigStr* ReadAll() {
  List<BigStr*>* chunks = nullptr;
  int n;
  int err_num;
  StackRoot _root0(&chunks);

  chunks = Alloc<List<BigStr*>>();
  while (true) {
    Tuple2<int, int> tup5 = pyos::Read(0, 4096, chunks);
    n = tup5.at0();
    err_num = tup5.at1();
    if (n < 0) {
      if (err_num == EINTR) {
        ;  // pass
      }
      else {
        throw Alloc<pyos::ReadError>(err_num);
      }
    }
    else {
      if (n == 0) {
        break;
      }
    }
  }
  return S_Aoo->join(chunks);
}

ctx_TermAttrs::ctx_TermAttrs(int fd, int local_modes) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->term_attrs)));
  this->fd = fd;
  Tuple2<int, void*> tup6 = pyos::PushTermAttrs(fd, local_modes);
  this->orig_local_modes = tup6.at0();
  this->term_attrs = tup6.at1();
}

ctx_TermAttrs::~ctx_TermAttrs() {
  pyos::PopTermAttrs(this->fd, this->orig_local_modes, this->term_attrs);
  gHeap.PopRoot();
}

Read::Read(split::SplitContext* splitter, state::Mem* mem, parse_lib::ParseContext* parse_ctx, cmd_eval::CommandEvaluator* cmd_ev, ui::ErrorFormatter* errfmt) {
  this->splitter = splitter;
  this->mem = mem;
  this->parse_ctx = parse_ctx;
  this->cmd_ev = cmd_ev;
  this->errfmt = errfmt;
  this->stdin_ = mylib::Stdin();
}

int Read::Run(cmd_value::Argv* cmd_val) {
  int status;
  StackRoot _root0(&cmd_val);

  try {
    status = this->_Run(cmd_val);
  }
  catch (pyos::ReadError* e) {
    this->errfmt->PrintMessage(StrFormat("Oils read error: %s", posix::strerror(e->err_num)));
    status = 1;
  }
  catch (IOError_OSError* e) {
    this->errfmt->PrintMessage(StrFormat("Oils read I/O error: %s", pyutil::strerror(e)));
    status = 1;
  }
  return status;
}

int Read::_ReadYsh(arg_types::read* arg, args::Reader* arg_r, cmd_value::Argv* cmd_val) {
  value::Place* place = nullptr;
  typed_args::Reader* rd = nullptr;
  syntax_asdl::loc_t* blame_loc = nullptr;
  BigStr* var_name = nullptr;
  BigStr* next_arg = nullptr;
  syntax_asdl::loc_t* next_loc = nullptr;
  int num_bytes;
  BigStr* contents = nullptr;
  int status;
  StackRoot _root0(&arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&cmd_val);
  StackRoot _root3(&place);
  StackRoot _root4(&rd);
  StackRoot _root5(&blame_loc);
  StackRoot _root6(&var_name);
  StackRoot _root7(&next_arg);
  StackRoot _root8(&next_loc);
  StackRoot _root9(&contents);

  place = nullptr;
  if (cmd_val->proc_args) {
    rd = typed_args::ReaderForProc(cmd_val);
    place = rd->PosPlace();
    rd->Done();
    blame_loc = cmd_val->proc_args->typed_args->left;
  }
  else {
    var_name = S_eys;
    blame_loc = cmd_val->arg_locs->at(0);
    place = Alloc<value::Place>(Alloc<LeftName>(var_name, blame_loc), this->mem->CurrentFrame());
  }
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup7 = arg_r->Peek2();
  next_arg = tup7.at0();
  next_loc = tup7.at1();
  if (next_arg != nullptr) {
    throw Alloc<error::Usage>(S_Ezs, next_loc);
  }
  num_bytes = mops::BigTruncate(arg->num_bytes);
  if (num_bytes != -1) {
    contents = _ReadN(num_bytes, this->cmd_ev);
    status = 0;
  }
  else {
    if (arg->raw_line) {
      contents = ReadLineSlowly(this->cmd_ev, arg->with_eol);
      status = len(contents) ? 0 : 1;
    }
    else {
      if (arg->all) {
        contents = ReadAll();
        status = 0;
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  this->mem->SetPlace(place, Alloc<value::Str>(contents), blame_loc);
  return status;
}

int Read::_Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::read* arg = nullptr;
  List<BigStr*>* names = nullptr;
  int bits;
  int status;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&names);

  Tuple2<args::_Attributes*, args::Reader*> tup8 = flag_util::ParseCmdVal(S_hDl, cmd_val, true);
  attrs = tup8.at0();
  arg_r = tup8.at1();
  arg = Alloc<arg_types::read>(attrs->attrs);
  names = arg_r->Rest();
  if (arg->u != mops::MINUS_ONE) {
    throw Alloc<error::Usage>(S_ClA, cmd_val->arg_locs->at(0));
  }
  if ((arg->raw_line or (arg->all or mops::BigTruncate(arg->num_bytes) != -1))) {
    return this->_ReadYsh(arg, arg_r, cmd_val);
  }
  if (cmd_val->proc_args) {
    throw Alloc<error::Usage>(S_Frn, cmd_val->proc_args->typed_args->left);
  }
  if (arg->t >= 0.0) {
    if (arg->t != 0.0) {
      e_die(S_qnf);
    }
    else {
      return pyos::InputAvailable(STDIN_FILENO) ? 0 : 1;
    }
  }
  bits = 0;
  if (this->stdin_->isatty()) {
    if ((arg->d != nullptr or mops::BigTruncate(arg->n) >= 0)) {
      bits |= pyos::TERM_ICANON;
    }
    if (arg->s) {
      bits |= pyos::TERM_ECHO;
    }
    if (arg->p != nullptr) {
      mylib::Stderr()->write(arg->p);
    }
  }
  if (bits == 0) {
    status = this->_Read(arg, names);
  }
  else {
    {  // with
      ctx_TermAttrs ctx{STDIN_FILENO, ~bits};

      status = this->_Read(arg, names);
    }
  }
  return status;
}

int Read::_Read(arg_types::read* arg, List<BigStr*>* names) {
  int arg_N;
  BigStr* s = nullptr;
  BigStr* name = nullptr;
  bool do_split;
  int max_results;
  bool raw;
  int delim_byte;
  List<mylib::BufWriter*>* parts = nullptr;
  bool join_next;
  int status;
  BigStr* chunk = nullptr;
  bool eof;
  List<Tuple2<runtime_asdl::span_t, int>*>* spans = nullptr;
  bool done;
  List<BigStr*>* entries = nullptr;
  int num_parts;
  BigStr* var_name = nullptr;
  StackRoot _root0(&arg);
  StackRoot _root1(&names);
  StackRoot _root2(&s);
  StackRoot _root3(&name);
  StackRoot _root4(&parts);
  StackRoot _root5(&chunk);
  StackRoot _root6(&spans);
  StackRoot _root7(&entries);
  StackRoot _root8(&var_name);

  arg_N = mops::BigTruncate(arg->N);
  if (arg_N >= 0) {
    s = _ReadN(arg_N, this->cmd_ev);
    if (len(names)) {
      name = names->at(0);
      for (int i = 1; i < len(names); ++i) {
        state::BuiltinSetString(this->mem, names->at(i), S_Aoo);
      }
    }
    else {
      name = S_wma;
    }
    state::BuiltinSetString(this->mem, name, s);
    return len(s) == arg_N ? 0 : 1;
  }
  do_split = false;
  if (len(names)) {
    do_split = true;
  }
  else {
    names->append(S_wma);
  }
  if (arg->a != nullptr) {
    max_results = 0;
    do_split = true;
  }
  else {
    max_results = len(names);
  }
  if (arg->Z) {
    do_split = false;
    raw = true;
    delim_byte = 0;
  }
  else {
    raw = arg->r;
    if (arg->d != nullptr) {
      if (len(arg->d)) {
        delim_byte = ord(arg->d->at(0));
      }
      else {
        delim_byte = 0;
      }
    }
    else {
      delim_byte = pyos::NEWLINE_CH;
    }
  }
  parts = Alloc<List<mylib::BufWriter*>>();
  join_next = false;
  status = 0;
  while (true) {
    Tuple2<BigStr*, bool> tup9 = _ReadPortion(delim_byte, mops::BigTruncate(arg->n), this->cmd_ev);
    chunk = tup9.at0();
    eof = tup9.at1();
    if (eof) {
      status = 1;
    }
    if (len(chunk) == 0) {
      break;
    }
    spans = this->splitter->SplitForRead(chunk, !raw, do_split);
    Tuple2<bool, bool> tup10 = _AppendParts(chunk, spans, max_results, join_next, parts);
    done = tup10.at0();
    join_next = tup10.at1();
    if (done) {
      break;
    }
  }
  entries = Alloc<List<BigStr*>>();
  for (ListIter<mylib::BufWriter*> it(parts); !it.Done(); it.Next()) {
    mylib::BufWriter* buf = it.Value();
    entries->append(buf->getvalue());
  }
  num_parts = len(entries);
  if (arg->a != nullptr) {
    state::BuiltinSetArray(this->mem, arg->a, entries);
  }
  else {
    for (int i = 0; i < max_results; ++i) {
      if (i < num_parts) {
        s = entries->at(i);
      }
      else {
        s = S_Aoo;
      }
      var_name = names->at(i);
      state::BuiltinSetString(this->mem, var_name, s);
    }
  }
  return status;
}

}  // define namespace read_osh

namespace readline_osh {  // define

using syntax_asdl::loc;
using value_asdl::value_e;
using error::e_usage;

ctx_Keymap::ctx_Keymap(py_readline::Readline* readline, BigStr* keymap_name) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->orig_keymap_name)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->readline)));
  this->readline = readline;
  this->orig_keymap_name = keymap_name;
}

ctx_Keymap::~ctx_Keymap() {
  if (this->orig_keymap_name != nullptr) {
    this->readline->restore_orig_keymap();
  }
  gHeap.PopRoot();
  gHeap.PopRoot();
}

Bind::Bind(py_readline::Readline* readline, ui::ErrorFormatter* errfmt) {
  this->readline = readline;
  this->errfmt = errfmt;
  this->exclusive_flags = NewList<BigStr*>(std::initializer_list<BigStr*>{S_crA, S_rsz, S_nAr_1, S_rqD, S_ksc});
}

int Bind::Run(cmd_value::Argv* cmd_val) {
  py_readline::Readline* readline = nullptr;
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  bool found;
  arg_types::bind* arg = nullptr;
  List<BigStr*>* bindings = nullptr;
  List<syntax_asdl::CompoundWord*>* arg_locs = nullptr;
  int i;
  BigStr* msg = nullptr;
  BigStr* msg2 = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&readline);
  StackRoot _root2(&attrs);
  StackRoot _root3(&arg_r);
  StackRoot _root4(&arg);
  StackRoot _root5(&bindings);
  StackRoot _root6(&arg_locs);
  StackRoot _root7(&msg);
  StackRoot _root8(&msg2);

  readline = this->readline;
  if (!readline) {
    e_usage(S_pFB, loc::Missing);
  }
  Tuple2<args::_Attributes*, args::Reader*> tup0 = flag_util::ParseCmdVal(S_llx, cmd_val);
  attrs = tup0.at0();
  arg_r = tup0.at1();
  found = false;
  for (ListIter<BigStr*> it(this->exclusive_flags); !it.Done(); it.Next()) {
    BigStr* flag = it.Value();
    StackRoot _for(&flag  );
    if ((dict_contains(attrs->attrs, flag) and attrs->attrs->at(flag)->tag() != value_e::Undef)) {
      if (found) {
        this->errfmt->Print_(str_concat(S_tBe, S_izl->join(this->exclusive_flags)), cmd_val->arg_locs->at(0));
        return 1;
      }
      else {
        found = true;
      }
    }
  }
  if ((found and !arg_r->AtEnd())) {
    this->errfmt->Print_(str_concat(S_vwx, S_izl->join(this->exclusive_flags)), cmd_val->arg_locs->at(0));
    return 1;
  }
  arg = Alloc<arg_types::bind>(attrs->attrs);
  try {
    {  // with
      ctx_Keymap ctx{readline, arg->m};

      if (arg->l) {
        readline->list_funmap_names();
      }
      if (arg->p) {
        readline->function_dumper(true);
      }
      if (arg->P) {
        readline->function_dumper(false);
      }
      if (arg->s) {
        readline->macro_dumper(true);
      }
      if (arg->S) {
        readline->macro_dumper(false);
      }
      if (arg->v) {
        readline->variable_dumper(true);
      }
      if (arg->V) {
        readline->variable_dumper(false);
      }
      if (arg->f != nullptr) {
        readline->read_init_file(arg->f);
      }
      if (arg->q != nullptr) {
        readline->query_bindings(arg->q);
      }
      if (arg->u != nullptr) {
        readline->unbind_rl_function(arg->u);
      }
      if (arg->r != nullptr) {
        readline->unbind_keyseq(arg->r);
      }
      if (arg->x != nullptr) {
        this->errfmt->Print_(S_bsD, cmd_val->arg_locs->at(0));
        return 1;
      }
      if (arg->X) {
        readline->print_shell_cmd_map();
      }
      Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> tup1 = arg_r->Rest2();
      bindings = tup1.at0();
      arg_locs = tup1.at1();
      i = 0;
      for (ListIter<BigStr*> it(bindings); !it.Done(); it.Next(), ++i) {
        BigStr* binding = it.Value();
        StackRoot _for(&binding      );
        try {
          readline->parse_and_bind(binding);
        }
        catch (ValueError* e) {
          msg = e->message;
          this->errfmt->Print_(StrFormat("bind error: %s", msg), arg_locs->at(i));
          return 1;
        }
      }
    }
  }
  catch (ValueError* e) {
    msg2 = e->message;
    if ((msg2 != nullptr and len(msg2) > 0)) {
      this->errfmt->Print_(StrFormat("bind error: %s", msg2), loc::Missing);
    }
    return 1;
  }
  return 0;
}

History::History(py_readline::Readline* readline, sh_init::ShellFiles* sh_files, ui::ErrorFormatter* errfmt, mylib::Writer* f) {
  this->readline = readline;
  this->sh_files = sh_files;
  this->errfmt = errfmt;
  this->f = f;
}

int History::Run(cmd_value::Argv* cmd_val) {
  py_readline::Readline* readline = nullptr;
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::history* arg = nullptr;
  BigStr* hist_file = nullptr;
  int arg_d;
  int cmd_index;
  int num_items;
  BigStr* num_arg = nullptr;
  syntax_asdl::loc_t* num_arg_loc = nullptr;
  int start_index;
  int num_to_show;
  BigStr* item = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&readline);
  StackRoot _root2(&attrs);
  StackRoot _root3(&arg_r);
  StackRoot _root4(&arg);
  StackRoot _root5(&hist_file);
  StackRoot _root6(&num_arg);
  StackRoot _root7(&num_arg_loc);
  StackRoot _root8(&item);

  readline = this->readline;
  if (!readline) {
    e_usage(S_pFB, loc::Missing);
  }
  Tuple2<args::_Attributes*, args::Reader*> tup2 = flag_util::ParseCmdVal(S_gBD, cmd_val);
  attrs = tup2.at0();
  arg_r = tup2.at1();
  arg = Alloc<arg_types::history>(attrs->attrs);
  if (arg->c) {
    readline->clear_history();
    return 0;
  }
  if (arg->a) {
    hist_file = this->sh_files->HistoryFile();
    if (hist_file == nullptr) {
      return 1;
    }
    try {
      readline->write_history_file(hist_file);
    }
    catch (IOError_OSError* e) {
      this->errfmt->Print_(StrFormat("Error writing HISTFILE %r: %s", hist_file, pyutil::strerror(e)), loc::Missing);
      return 1;
    }
    return 0;
  }
  if (arg->r) {
    hist_file = this->sh_files->HistoryFile();
    if (hist_file == nullptr) {
      return 1;
    }
    try {
      readline->read_history_file(hist_file);
    }
    catch (IOError_OSError* e) {
      this->errfmt->Print_(StrFormat("Error reading HISTFILE %r: %s", hist_file, pyutil::strerror(e)), loc::Missing);
      return 1;
    }
    return 0;
  }
  arg_d = mops::BigTruncate(arg->d);
  if (arg_d >= 0) {
    cmd_index = (arg_d - 1);
    try {
      readline->remove_history_item(cmd_index);
    }
    catch (ValueError*) {
      e_usage(StrFormat("couldn't find item %d", arg_d), loc::Missing);
    }
    return 0;
  }
  num_items = readline->get_current_history_length();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup3 = arg_r->Peek2();
  num_arg = tup3.at0();
  num_arg_loc = tup3.at1();
  if (num_arg == nullptr) {
    start_index = 1;
  }
  else {
    try {
      num_to_show = to_int(num_arg);
    }
    catch (ValueError*) {
      e_usage(StrFormat("got invalid argument %r", num_arg), num_arg_loc);
    }
    start_index = max(1, ((num_items + 1) - num_to_show));
  }
  arg_r->Next();
  if (!arg_r->AtEnd()) {
    e_usage(S_sAk, loc::Missing);
  }
  for (int i = start_index; i < (num_items + 1); ++i) {
    item = readline->get_history_item(i);
    this->f->write(StrFormat("%5d  %s\n", i, item));
  }
  return 0;
}

}  // define namespace readline_osh

namespace trap_osh {  // define

using runtime_asdl::cmd_value;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::source;
using mylib::print_stderr;

TrapState::TrapState(iolib::SignalSafe* signal_safe) {
  this->signal_safe = signal_safe;
  this->hooks = Alloc<Dict<BigStr*, syntax_asdl::command_t*>>();
  this->traps = Alloc<Dict<int, syntax_asdl::command_t*>>();
}

void TrapState::ClearForSubProgram(bool inherit_errtrace) {
  syntax_asdl::command_t* hook_err = nullptr;
  StackRoot _root0(&hook_err);

  hook_err = this->hooks->get(S_zDr);
  this->hooks->clear();
  if ((hook_err != nullptr and inherit_errtrace)) {
    this->hooks->set(S_zDr, hook_err);
  }
  this->traps->clear();
}

syntax_asdl::command_t* TrapState::GetHook(BigStr* hook_name) {
  StackRoot _root0(&hook_name);

  return this->hooks->get(hook_name, nullptr);
}

void TrapState::AddUserHook(BigStr* hook_name, syntax_asdl::command_t* handler) {
  StackRoot _root0(&hook_name);
  StackRoot _root1(&handler);

  this->hooks->set(hook_name, handler);
}

void TrapState::RemoveUserHook(BigStr* hook_name) {
  StackRoot _root0(&hook_name);

  mylib::dict_erase(this->hooks, hook_name);
}

void TrapState::AddUserTrap(int sig_num, syntax_asdl::command_t* handler) {
  StackRoot _root0(&handler);

  this->traps->set(sig_num, handler);
  if (sig_num == SIGINT) {
    this->signal_safe->SetSigIntTrapped(true);
  }
  else {
    if (sig_num == SIGWINCH) {
      this->signal_safe->SetSigWinchCode(SIGWINCH);
    }
    else {
      iolib::RegisterSignalInterest(sig_num);
    }
  }
}

void TrapState::RemoveUserTrap(int sig_num) {
  mylib::dict_erase(this->traps, sig_num);
  if (sig_num == SIGINT) {
    this->signal_safe->SetSigIntTrapped(false);
    ;  // pass
  }
  else {
    if (sig_num == SIGWINCH) {
      this->signal_safe->SetSigWinchCode(iolib::UNTRAPPED_SIGWINCH);
    }
    else {
      iolib::sigaction(sig_num, SIG_DFL);
    }
  }
}

List<syntax_asdl::command_t*>* TrapState::GetPendingTraps() {
  List<int>* signals = nullptr;
  List<syntax_asdl::command_t*>* run_list = nullptr;
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&signals);
  StackRoot _root1(&run_list);
  StackRoot _root2(&node);

  signals = this->signal_safe->TakePendingSignals();
  if (len(signals) == 0) {
    this->signal_safe->ReuseEmptyList(signals);
    return nullptr;
  }
  run_list = Alloc<List<syntax_asdl::command_t*>>();
  for (ListIter<int> it(signals); !it.Done(); it.Next()) {
    int sig_num = it.Value();
    node = this->traps->get(sig_num, nullptr);
    if (node != nullptr) {
      run_list->append(node);
    }
  }
  signals->clear();
  this->signal_safe->ReuseEmptyList(signals);
  return run_list;
}

bool TrapState::ThisProcessHasTraps() {
  return (len(this->traps) != 0 or len(this->hooks) != 0);
}

bool _IsUnsignedInteger(BigStr* s, syntax_asdl::loc_t* blame_loc) {
  bool ok;
  mops::BigInt big_int;
  StackRoot _root0(&s);
  StackRoot _root1(&blame_loc);

  if (!match::LooksLikeInteger(s)) {
    return false;
  }
  Tuple2<bool, mops::BigInt> tup0 = mops::FromStr2(s);
  ok = tup0.at0();
  big_int = tup0.at1();
  if (!ok) {
    throw Alloc<error::Usage>(StrFormat("integer too big: %s", s), blame_loc);
  }
  return !mops::Greater(mops::ZERO, big_int);
}

int _GetSignalNumber(BigStr* sig_spec) {
  StackRoot _root0(&sig_spec);

  if ((str_equals(sig_spec->strip(), S_vrA) || str_equals(sig_spec->strip(), S_AEs) || str_equals(sig_spec->strip(), S_xtx) || str_equals(sig_spec->strip(), S_bEx) || str_equals(sig_spec->strip(), S_kqx) || str_equals(sig_spec->strip(), S_Dfm) || str_equals(sig_spec->strip(), S_Apn) || str_equals(sig_spec->strip(), S_rFk))) {
    return to_int(sig_spec);
  }
  if (sig_spec->startswith(S_avu)) {
    sig_spec = sig_spec->slice(3);
  }
  return signal_def::GetNumber(sig_spec);
}
GLOBAL_LIST(_HOOK_NAMES, BigStr*, 4, {S_BDg COMMA S_zDr COMMA S_AEu COMMA S_Fzz});

Trap::Trap(trap_osh::TrapState* trap_state, parse_lib::ParseContext* parse_ctx, dev::Tracer* tracer, ui::ErrorFormatter* errfmt) {
  this->trap_state = trap_state;
  this->parse_ctx = parse_ctx;
  this->arena = parse_ctx->arena;
  this->tracer = tracer;
  this->errfmt = errfmt;
}

syntax_asdl::command_t* Trap::_ParseTrapCode(BigStr* code_str) {
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  source::Dynamic* src = nullptr;
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&code_str);
  StackRoot _root1(&line_reader);
  StackRoot _root2(&c_parser);
  StackRoot _root3(&src);
  StackRoot _root4(&node);

  line_reader = reader::StringLineReader(code_str, this->arena);
  c_parser = this->parse_ctx->MakeOshParser(line_reader);
  src = Alloc<source::Dynamic>(S_ECk, loc::Missing);
  {  // with
    alloc::ctx_SourceCode ctx{this->arena, src};

    try {
      node = main_loop::ParseWholeFile(c_parser);
    }
    catch (error::Parse* e) {
      this->errfmt->PrettyPrintError(e);
      return nullptr;
    }
  }
  return node;
}

int Trap::Run(cmd_value::Argv* cmd_val) {
  args::_Attributes* attrs = nullptr;
  args::Reader* arg_r = nullptr;
  arg_types::trap* arg = nullptr;
  BigStr* code_str = nullptr;
  syntax_asdl::loc_t* code_loc = nullptr;
  BigStr* sig_spec = nullptr;
  syntax_asdl::loc_t* sig_loc = nullptr;
  BigStr* sig_key = nullptr;
  int sig_num;
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&attrs);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&arg);
  StackRoot _root4(&code_str);
  StackRoot _root5(&code_loc);
  StackRoot _root6(&sig_spec);
  StackRoot _root7(&sig_loc);
  StackRoot _root8(&sig_key);
  StackRoot _root9(&node);

  Tuple2<args::_Attributes*, args::Reader*> tup1 = flag_util::ParseCmdVal(S_gFu, cmd_val);
  attrs = tup1.at0();
  arg_r = tup1.at1();
  arg = Alloc<arg_types::trap>(attrs->attrs);
  if (arg->p) {
    for (DictIter<BigStr*, syntax_asdl::command_t*> it(this->trap_state->hooks); !it.Done(); it.Next()) {
      BigStr* name = it.Key();
      syntax_asdl::command_t* _ = it.Value();
      print(StrFormat("%s TrapState", name));
    }
    for (DictIter<int, syntax_asdl::command_t*> it(this->trap_state->traps); !it.Done(); it.Next()) {
      int sig_num = it.Key();
      syntax_asdl::command_t* _ = it.Value();
      print(StrFormat("%d TrapState", sig_num));
    }
    return 0;
  }
  if (arg->l) {
    for (ListIter<BigStr*> it(_HOOK_NAMES); !it.Done(); it.Next()) {
      BigStr* hook_name = it.Value();
      StackRoot _for(&hook_name    );
      print(StrFormat("   %s", hook_name));
    }
    signal_def::PrintSignals();
    return 0;
  }
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup2 = arg_r->ReadRequired2(S_oiw);
  code_str = tup2.at0();
  code_loc = tup2.at1();
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup3 = arg_r->ReadRequired2(S_uxD);
  sig_spec = tup3.at0();
  sig_loc = tup3.at1();
  sig_key = nullptr;
  sig_num = signal_def::NO_SIGNAL;
  if (list_contains(_HOOK_NAMES, sig_spec)) {
    sig_key = sig_spec;
  }
  else {
    if (str_equals(sig_spec, S_wfw)) {
      sig_key = S_BDg;
    }
    else {
      sig_num = _GetSignalNumber(sig_spec);
      if (sig_num != signal_def::NO_SIGNAL) {
        sig_key = str(sig_num);
      }
    }
  }
  if (sig_key == nullptr) {
    this->errfmt->Print_(StrFormat("Invalid signal or hook %r", sig_spec), cmd_val->arg_locs->at(2));
    return 1;
  }
  if ((str_equals(code_str, S_Bjq) or _IsUnsignedInteger(code_str, code_loc))) {
    if (list_contains(_HOOK_NAMES, sig_key)) {
      this->trap_state->RemoveUserHook(sig_key);
      return 0;
    }
    if (sig_num != signal_def::NO_SIGNAL) {
      this->trap_state->RemoveUserTrap(sig_num);
      return 0;
    }
    assert(0);  // AssertionError
  }
  node = this->_ParseTrapCode(code_str);
  if (node == nullptr) {
    return 1;
  }
  if (list_contains(_HOOK_NAMES, sig_key)) {
    if (str_equals(sig_key, S_AEu)) {
      print_stderr(StrFormat("osh warning: The %r hook isn't implemented", sig_spec));
    }
    this->trap_state->AddUserHook(sig_key, node);
    return 0;
  }
  if (sig_num != signal_def::NO_SIGNAL) {
    if ((sig_num == SIGKILL || sig_num == SIGSTOP)) {
      this->errfmt->Print_(StrFormat("Signal %r can't be handled", sig_spec), sig_loc);
      return 1;
    }
    this->trap_state->AddUserTrap(sig_num, node);
    return 0;
  }
  assert(0);  // AssertionError
}

}  // define namespace trap_osh

namespace alloc {  // define

using syntax_asdl::source_t;
using syntax_asdl::Token;
using syntax_asdl::SourceLine;
using syntax_asdl::loc;

BigStr* SnipCodeBlock(syntax_asdl::Token* left, syntax_asdl::Token* right, List<syntax_asdl::SourceLine*>* lines) {
  List<BigStr*>* pieces = nullptr;
  BigStr* piece = nullptr;
  bool saving;
  bool found_left;
  bool found_right;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&lines);
  StackRoot _root3(&pieces);
  StackRoot _root4(&piece);

  pieces = Alloc<List<BigStr*>>();
  pieces->append(str_repeat(S_yfw, (left->col + 1)));
  if (left->line == right->line) {
    for (ListIter<syntax_asdl::SourceLine*> it(lines); !it.Done(); it.Next()) {
      syntax_asdl::SourceLine* li = it.Value();
      StackRoot _for(&li    );
      if (li == left->line) {
        piece = li->content->slice((left->col + left->length), right->col);
        pieces->append(piece);
      }
    }
    return S_Aoo->join(pieces);
  }
  saving = false;
  found_left = false;
  found_right = false;
  for (ListIter<syntax_asdl::SourceLine*> it(lines); !it.Done(); it.Next()) {
    syntax_asdl::SourceLine* li = it.Value();
    StackRoot _for(&li  );
    if (li == left->line) {
      found_left = true;
      saving = true;
      piece = li->content->slice((left->col + left->length));
      pieces->append(piece);
      continue;
    }
    if (li == right->line) {
      found_right = true;
      piece = li->content->slice(0, right->col);
      pieces->append(piece);
      saving = false;
      break;
    }
    if (saving) {
      pieces->append(li->content);
    }
  }
  return S_Aoo->join(pieces);
}

ctx_SourceCode::ctx_SourceCode(alloc::Arena* arena, syntax_asdl::source_t* src) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->arena)));
  arena->PushSource(src);
  this->arena = arena;
}

ctx_SourceCode::~ctx_SourceCode() {
  this->arena->PopSource();
  gHeap.PopRoot();
}

Arena::Arena(bool save_tokens) {
  this->save_tokens = save_tokens;
  this->tokens = Alloc<List<syntax_asdl::Token*>>();
  this->num_tokens = 0;
  this->span_id_lookup = Alloc<Dict<syntax_asdl::Token*, int>>();
  this->lines_list = Alloc<List<syntax_asdl::SourceLine*>>();
  this->source_instances = Alloc<List<syntax_asdl::source_t*>>();
}

void Arena::SaveTokens() {
  this->save_tokens = true;
}

void Arena::PushSource(syntax_asdl::source_t* src) {
  StackRoot _root0(&src);

  this->source_instances->append(src);
}

void Arena::PopSource() {
  this->source_instances->pop();
}

syntax_asdl::SourceLine* Arena::AddLine(BigStr* line, int line_num) {
  syntax_asdl::SourceLine* src_line = nullptr;
  StackRoot _root0(&line);
  StackRoot _root1(&src_line);

  src_line = Alloc<SourceLine>(line_num, line, this->source_instances->at(-1));
  this->lines_list->append(src_line);
  return src_line;
}

void Arena::DiscardLines() {
  this->lines_list->clear();
}

List<syntax_asdl::SourceLine*>* Arena::SaveLinesAndDiscard(syntax_asdl::Token* left, syntax_asdl::Token* right) {
  List<syntax_asdl::SourceLine*>* saved = nullptr;
  bool saving;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&saved);

  saved = Alloc<List<syntax_asdl::SourceLine*>>();
  saving = false;
  for (ListIter<syntax_asdl::SourceLine*> it(this->lines_list); !it.Done(); it.Next()) {
    syntax_asdl::SourceLine* li = it.Value();
    StackRoot _for(&li  );
    if (li == left->line) {
      saving = true;
    }
    if (saving) {
      saved->append(li);
    }
    if (li == right->line) {
      saving = false;
      break;
    }
  }
  this->DiscardLines();
  return saved;
}

BigStr* Arena::SnipCodeString(syntax_asdl::Token* left, syntax_asdl::Token* right) {
  BigStr* piece = nullptr;
  List<BigStr*>* pieces = nullptr;
  bool saving;
  bool found_left;
  bool found_right;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&piece);
  StackRoot _root3(&pieces);

  if (left->line == right->line) {
    for (ListIter<syntax_asdl::SourceLine*> it(this->lines_list); !it.Done(); it.Next()) {
      syntax_asdl::SourceLine* li = it.Value();
      StackRoot _for(&li    );
      if (li == left->line) {
        piece = li->content->slice(left->col, (right->col + right->length));
        return piece;
      }
    }
  }
  pieces = Alloc<List<BigStr*>>();
  saving = false;
  found_left = false;
  found_right = false;
  for (ListIter<syntax_asdl::SourceLine*> it(this->lines_list); !it.Done(); it.Next()) {
    syntax_asdl::SourceLine* li = it.Value();
    StackRoot _for(&li  );
    if (li == left->line) {
      found_left = true;
      saving = true;
      piece = li->content->slice(left->col);
      pieces->append(piece);
      continue;
    }
    if (li == right->line) {
      found_right = true;
      piece = li->content->slice(0, (right->col + right->length));
      pieces->append(piece);
      saving = false;
      break;
    }
    if (saving) {
      pieces->append(li->content);
    }
  }
  return S_Aoo->join(pieces);
}

syntax_asdl::Token* Arena::NewToken(int id_, int col, int length, syntax_asdl::SourceLine* src_line) {
  syntax_asdl::Token* tok = nullptr;
  int span_id;
  StackRoot _root0(&src_line);
  StackRoot _root1(&tok);

  if (length >= 65536) {
    throw Alloc<error::Parse>(S_Aoo, Alloc<loc::TokenTooLong>(src_line, id_, length, col));
  }
  tok = Alloc<Token>(id_, length, col, src_line, nullptr);
  if (this->save_tokens) {
    span_id = this->num_tokens;
    this->num_tokens += 1;
    this->tokens->append(tok);
    this->span_id_lookup->set(tok, span_id);
  }
  return tok;
}

void Arena::UnreadOne() {
  if (this->save_tokens) {
    this->tokens->pop();
    this->num_tokens -= 1;
  }
}

syntax_asdl::Token* Arena::GetToken(int span_id) {
  return this->tokens->at(span_id);
}

int Arena::GetSpanId(syntax_asdl::Token* tok) {
  StackRoot _root0(&tok);

  return this->span_id_lookup->at(tok);
}

int Arena::LastSpanId() {
  return len(this->tokens);
}

}  // define namespace alloc

namespace bash_impl {  // define

using runtime_asdl::error_code_e;
using runtime_asdl::error_code_t;
using value_asdl::value;

bool BigInt_Greater(mops::BigInt a, mops::BigInt b) {
  return mops::Greater(a, b);
}

bool BigInt_Less(mops::BigInt a, mops::BigInt b) {
  return mops::Greater(b, a);
}

bool BigInt_GreaterEq(mops::BigInt a, mops::BigInt b) {
  return !mops::Greater(b, a);
}

bool BigInt_LessEq(mops::BigInt a, mops::BigInt b) {
  return !mops::Greater(a, b);
}

bool BashArray_IsEmpty(value::BashArray* array_val) {
  StackRoot _root0(&array_val);

  return len(array_val->strs) == 0;
}

int BashArray_Count(value::BashArray* array_val) {
  int length;
  StackRoot _root0(&array_val);

  length = 0;
  for (ListIter<BigStr*> it(array_val->strs); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    if (s != nullptr) {
      length += 1;
    }
  }
  return length;
}

int BashArray_Length(value::BashArray* array_val) {
  StackRoot _root0(&array_val);

  return len(array_val->strs);
}

List<int>* BashArray_GetKeys(value::BashArray* array_val) {
  List<int>* indices = nullptr;
  int i;
  StackRoot _root0(&array_val);
  StackRoot _root1(&indices);

  indices = Alloc<List<int>>();
  i = 0;
  for (ListIter<BigStr*> it(array_val->strs); !it.Done(); it.Next(), ++i) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    if (s != nullptr) {
      indices->append(i);
    }
  }
  return indices;
}

List<BigStr*>* BashArray_GetValues(value::BashArray* array_val) {
  StackRoot _root0(&array_val);

  return array_val->strs;
}

void BashArray_AppendValues(value::BashArray* array_val, List<BigStr*>* strs) {
  StackRoot _root0(&array_val);
  StackRoot _root1(&strs);

  array_val->strs->extend(strs);
}

Tuple3<int, int, runtime_asdl::error_code_t> _BashArray_CanonicalizeIndex(value::BashArray* array_val, int index) {
  int n;
  StackRoot _root0(&array_val);

  n = len(array_val->strs);
  if (index < 0) {
    index += n;
    if (index < 0) {
      return Tuple3<int, int, runtime_asdl::error_code_t>(-1, n, error_code_e::IndexOutOfRange);
    }
  }
  return Tuple3<int, int, runtime_asdl::error_code_t>(index, n, error_code_e::OK);
}

Tuple2<bool, runtime_asdl::error_code_t> BashArray_HasElement(value::BashArray* array_val, int index) {
  int n;
  runtime_asdl::error_code_t error_code;
  StackRoot _root0(&array_val);

  Tuple3<int, int, runtime_asdl::error_code_t> tup0 = _BashArray_CanonicalizeIndex(array_val, index);
  index = tup0.at0();
  n = tup0.at1();
  error_code = tup0.at2();
  if (error_code != error_code_e::OK) {
    return Tuple2<bool, runtime_asdl::error_code_t>(false, error_code);
  }
  if (index < n) {
    return Tuple2<bool, runtime_asdl::error_code_t>(array_val->strs->at(index) != nullptr, error_code_e::OK);
  }
  return Tuple2<bool, runtime_asdl::error_code_t>(false, error_code_e::OK);
}

Tuple2<BigStr*, runtime_asdl::error_code_t> BashArray_GetElement(value::BashArray* array_val, int index) {
  int n;
  runtime_asdl::error_code_t error_code;
  BigStr* s = nullptr;
  StackRoot _root0(&array_val);
  StackRoot _root1(&s);

  Tuple3<int, int, runtime_asdl::error_code_t> tup1 = _BashArray_CanonicalizeIndex(array_val, index);
  index = tup1.at0();
  n = tup1.at1();
  error_code = tup1.at2();
  if (error_code != error_code_e::OK) {
    return Tuple2<BigStr*, runtime_asdl::error_code_t>(nullptr, error_code);
  }
  if (index < n) {
    s = array_val->strs->at(index);
  }
  else {
    s = nullptr;
  }
  return Tuple2<BigStr*, runtime_asdl::error_code_t>(s, error_code_e::OK);
}

runtime_asdl::error_code_t BashArray_SetElement(value::BashArray* array_val, int index, BigStr* s) {
  List<BigStr*>* strs = nullptr;
  int n;
  runtime_asdl::error_code_t error_code;
  StackRoot _root0(&array_val);
  StackRoot _root1(&s);
  StackRoot _root2(&strs);

  strs = array_val->strs;
  Tuple3<int, int, runtime_asdl::error_code_t> tup2 = _BashArray_CanonicalizeIndex(array_val, index);
  index = tup2.at0();
  n = tup2.at1();
  error_code = tup2.at2();
  if (error_code != error_code_e::OK) {
    return error_code;
  }
  if (index < n) {
    array_val->strs->set(index, s);
  }
  else {
    for (int i = 0; i < ((index - n) + 1); ++i) {
      array_val->strs->append(nullptr);
    }
    array_val->strs->set(index, s);
  }
  return error_code_e::OK;
}

runtime_asdl::error_code_t BashArray_UnsetElement(value::BashArray* array_val, int index) {
  List<BigStr*>* strs = nullptr;
  int n;
  int last_index;
  StackRoot _root0(&array_val);
  StackRoot _root1(&strs);

  strs = array_val->strs;
  n = len(strs);
  last_index = (n - 1);
  if (index < 0) {
    index += n;
    if (index < 0) {
      return error_code_e::IndexOutOfRange;
    }
  }
  if (index == last_index) {
    strs->pop();
    while ((len(strs) > 0 and strs->at(-1) == nullptr)) {
      strs->pop();
    }
  }
  else {
    if (index < last_index) {
      strs->set(index, nullptr);
    }
    else {
      ;  // pass
    }
  }
  return error_code_e::OK;
}

bool BashArray_Equals(value::BashArray* lhs, value::BashArray* rhs) {
  int len_lhs;
  int len_rhs;
  StackRoot _root0(&lhs);
  StackRoot _root1(&rhs);

  len_lhs = len(lhs->strs);
  len_rhs = len(rhs->strs);
  if (len_lhs != len_rhs) {
    return false;
  }
  for (int i = 0; i < len_lhs; ++i) {
    if (!(str_equals(lhs->strs->at(i), rhs->strs->at(i)))) {
      return false;
    }
  }
  return true;
}

bool _BashArray_HasHoles(value::BashArray* array_val) {
  StackRoot _root0(&array_val);

  for (ListIter<BigStr*> it(array_val->strs); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    if (s == nullptr) {
      return true;
    }
  }
  return false;
}

BigStr* BashArray_ToStrForShellPrint(value::BashArray* array_val, BigStr* name) {
  List<BigStr*>* buff = nullptr;
  bool first;
  int i;
  StackRoot _root0(&array_val);
  StackRoot _root1(&name);
  StackRoot _root2(&buff);

  buff = Alloc<List<BigStr*>>();
  first = true;
  if (_BashArray_HasHoles(array_val)) {
    if (name != nullptr) {
      buff->append(S_zxb);
      i = 0;
      for (ListIter<BigStr*> it(array_val->strs); !it.Done(); it.Next(), ++i) {
        BigStr* element = it.Value();
        StackRoot _for(&element      );
        if (element != nullptr) {
          if (first) {
            buff->append(S_nbf);
            first = false;
          }
          buff->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_yfw, name, S_Eax, str(i), S_nuz, j8_lite::MaybeShellEncode(element)}));
        }
      }
    }
    else {
      buff->append(S_ijB);
      i = 0;
      for (ListIter<BigStr*> it(array_val->strs); !it.Done(); it.Next(), ++i) {
        BigStr* element = it.Value();
        StackRoot _for(&element      );
        if (element != nullptr) {
          if (!first) {
            buff->append(S_yfw);
          }
          else {
            first = false;
            buff->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_Eax, str(i), S_nuz, j8_lite::MaybeShellEncode(element)}));
          }
        }
      }
      buff->append(S_hxb);
    }
  }
  else {
    buff->append(S_ijB);
    for (ListIter<BigStr*> it(array_val->strs); !it.Done(); it.Next()) {
      BigStr* element = it.Value();
      StackRoot _for(&element    );
      if (!first) {
        buff->append(S_yfw);
      }
      else {
        first = false;
      }
      buff->append(j8_lite::MaybeShellEncode(element));
    }
    buff->append(S_hxb);
  }
  return S_Aoo->join(buff);
}

bool BashAssoc_IsEmpty(value::BashAssoc* assoc_val) {
  StackRoot _root0(&assoc_val);

  return len(assoc_val->d) == 0;
}

int BashAssoc_Count(value::BashAssoc* assoc_val) {
  StackRoot _root0(&assoc_val);

  return len(assoc_val->d);
}

Dict<BigStr*, BigStr*>* BashAssoc_GetDict(value::BashAssoc* assoc_val) {
  StackRoot _root0(&assoc_val);

  return assoc_val->d;
}

void BashAssoc_AppendDict(value::BashAssoc* assoc_val, Dict<BigStr*, BigStr*>* d) {
  StackRoot _root0(&assoc_val);
  StackRoot _root1(&d);

  for (DictIter<BigStr*, BigStr*> it(d); !it.Done(); it.Next()) {
    BigStr* key = it.Key();
    StackRoot _for(&key  );
    assoc_val->d->set(key, d->at(key));
  }
}

List<BigStr*>* BashAssoc_GetKeys(value::BashAssoc* assoc_val) {
  StackRoot _root0(&assoc_val);

  return assoc_val->d->keys();
}

List<BigStr*>* BashAssoc_GetValues(value::BashAssoc* assoc_val) {
  StackRoot _root0(&assoc_val);

  return assoc_val->d->values();
}

bool BashAssoc_HasElement(value::BashAssoc* assoc_val, BigStr* s) {
  StackRoot _root0(&assoc_val);
  StackRoot _root1(&s);

  return dict_contains(assoc_val->d, s);
}

BigStr* BashAssoc_GetElement(value::BashAssoc* assoc_val, BigStr* s) {
  StackRoot _root0(&assoc_val);
  StackRoot _root1(&s);

  return assoc_val->d->get(s);
}

void BashAssoc_SetElement(value::BashAssoc* assoc_val, BigStr* key, BigStr* s) {
  StackRoot _root0(&assoc_val);
  StackRoot _root1(&key);
  StackRoot _root2(&s);

  assoc_val->d->set(key, s);
}

void BashAssoc_UnsetElement(value::BashAssoc* assoc_val, BigStr* key) {
  StackRoot _root0(&assoc_val);
  StackRoot _root1(&key);

  mylib::dict_erase(assoc_val->d, key);
}

bool BashAssoc_Equals(value::BashAssoc* lhs, value::BashAssoc* rhs) {
  StackRoot _root0(&lhs);
  StackRoot _root1(&rhs);

  if (len(lhs->d) != len(rhs->d)) {
    return false;
  }
  for (DictIter<BigStr*, BigStr*> it(lhs->d); !it.Done(); it.Next()) {
    BigStr* k = it.Key();
    StackRoot _for(&k  );
    if ((!dict_contains(rhs->d, k) or !(str_equals(rhs->d->at(k), lhs->d->at(k))))) {
      return false;
    }
  }
  return true;
}

BigStr* BashAssoc_ToStrForShellPrint(value::BashAssoc* assoc_val) {
  List<BigStr*>* buff = nullptr;
  bool first;
  BigStr* key_quoted = nullptr;
  BigStr* value_quoted = nullptr;
  StackRoot _root0(&assoc_val);
  StackRoot _root1(&buff);
  StackRoot _root2(&key_quoted);
  StackRoot _root3(&value_quoted);

  buff = NewList<BigStr*>(std::initializer_list<BigStr*>{S_ijB});
  first = true;
  for (ListIter<BigStr*> it(sorted(assoc_val->d)); !it.Done(); it.Next()) {
    BigStr* key = it.Value();
    StackRoot _for(&key  );
    if (!first) {
      buff->append(S_yfw);
    }
    else {
      first = false;
    }
    key_quoted = j8_lite::ShellEncode(key);
    value_quoted = j8_lite::MaybeShellEncode(assoc_val->d->at(key));
    buff->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_Eax, key_quoted, S_nuz, value_quoted}));
  }
  buff->append(S_hxb);
  return S_Aoo->join(buff);
}

bool SparseArray_IsEmpty(value::SparseArray* sparse_val) {
  StackRoot _root0(&sparse_val);

  return len(sparse_val->d) == 0;
}

int SparseArray_Count(value::SparseArray* sparse_val) {
  StackRoot _root0(&sparse_val);

  return len(sparse_val->d);
}

mops::BigInt SparseArray_Length(value::SparseArray* sparse_val) {
  StackRoot _root0(&sparse_val);

  return mops::Add(sparse_val->max_index, mops::ONE);
}

List<mops::BigInt>* SparseArray_GetKeys(value::SparseArray* sparse_val) {
  List<mops::BigInt>* keys = nullptr;
  StackRoot _root0(&sparse_val);
  StackRoot _root1(&keys);

  keys = sparse_val->d->keys();
  mylib::BigIntSort(keys);
  return keys;
}

List<BigStr*>* SparseArray_GetValues(value::SparseArray* sparse_val) {
  List<BigStr*>* values = nullptr;
  StackRoot _root0(&sparse_val);
  StackRoot _root1(&values);

  values = Alloc<List<BigStr*>>();
  for (ListIter<mops::BigInt> it(SparseArray_GetKeys(sparse_val)); !it.Done(); it.Next()) {
    mops::BigInt index = it.Value();
    values->append(sparse_val->d->at(index));
  }
  return values;
}

void SparseArray_AppendValues(value::SparseArray* sparse_val, List<BigStr*>* strs) {
  StackRoot _root0(&sparse_val);
  StackRoot _root1(&strs);

  for (ListIter<BigStr*> it(strs); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    sparse_val->max_index = mops::Add(sparse_val->max_index, mops::ONE);
    sparse_val->d->set(sparse_val->max_index, s);
  }
}

Tuple2<mops::BigInt, runtime_asdl::error_code_t> _SparseArray_CanonicalizeIndex(value::SparseArray* sparse_val, mops::BigInt index) {
  StackRoot _root0(&sparse_val);

  if (BigInt_Less(index, mops::ZERO)) {
    index = mops::Add(index, mops::Add(sparse_val->max_index, mops::ONE));
    if (BigInt_Less(index, mops::ZERO)) {
      return Tuple2<mops::BigInt, runtime_asdl::error_code_t>(mops::MINUS_ONE, error_code_e::IndexOutOfRange);
    }
  }
  return Tuple2<mops::BigInt, runtime_asdl::error_code_t>(index, error_code_e::OK);
}

Tuple2<bool, runtime_asdl::error_code_t> SparseArray_HasElement(value::SparseArray* sparse_val, mops::BigInt index) {
  runtime_asdl::error_code_t error_code;
  StackRoot _root0(&sparse_val);

  Tuple2<mops::BigInt, runtime_asdl::error_code_t> tup3 = _SparseArray_CanonicalizeIndex(sparse_val, index);
  index = tup3.at0();
  error_code = tup3.at1();
  if (error_code != error_code_e::OK) {
    return Tuple2<bool, runtime_asdl::error_code_t>(false, error_code);
  }
  return Tuple2<bool, runtime_asdl::error_code_t>(dict_contains(sparse_val->d, index), error_code_e::OK);
}

Tuple2<BigStr*, runtime_asdl::error_code_t> SparseArray_GetElement(value::SparseArray* sparse_val, mops::BigInt index) {
  runtime_asdl::error_code_t error_code;
  StackRoot _root0(&sparse_val);

  Tuple2<mops::BigInt, runtime_asdl::error_code_t> tup4 = _SparseArray_CanonicalizeIndex(sparse_val, index);
  index = tup4.at0();
  error_code = tup4.at1();
  if (error_code != error_code_e::OK) {
    return Tuple2<BigStr*, runtime_asdl::error_code_t>(nullptr, error_code);
  }
  return Tuple2<BigStr*, runtime_asdl::error_code_t>(sparse_val->d->get(index), error_code_e::OK);
}

runtime_asdl::error_code_t SparseArray_SetElement(value::SparseArray* sparse_val, mops::BigInt index, BigStr* s) {
  runtime_asdl::error_code_t error_code;
  StackRoot _root0(&sparse_val);
  StackRoot _root1(&s);

  Tuple2<mops::BigInt, runtime_asdl::error_code_t> tup5 = _SparseArray_CanonicalizeIndex(sparse_val, index);
  index = tup5.at0();
  error_code = tup5.at1();
  if (error_code != error_code_e::OK) {
    return error_code;
  }
  if (BigInt_Greater(index, sparse_val->max_index)) {
    sparse_val->max_index = index;
  }
  sparse_val->d->set(index, s);
  return error_code_e::OK;
}

runtime_asdl::error_code_t SparseArray_UnsetElement(value::SparseArray* sparse_val, mops::BigInt index) {
  runtime_asdl::error_code_t error_code;
  StackRoot _root0(&sparse_val);

  Tuple2<mops::BigInt, runtime_asdl::error_code_t> tup6 = _SparseArray_CanonicalizeIndex(sparse_val, index);
  index = tup6.at0();
  error_code = tup6.at1();
  if (error_code != error_code_e::OK) {
    return error_code;
  }
  mylib::dict_erase(sparse_val->d, index);
  if (mops::Equal(index, sparse_val->max_index)) {
    sparse_val->max_index = mops::MINUS_ONE;
    for (DictIter<mops::BigInt, BigStr*> it(sparse_val->d); !it.Done(); it.Next()) {
      mops::BigInt index = it.Key();
      if (mops::Greater(index, sparse_val->max_index)) {
        sparse_val->max_index = index;
      }
    }
  }
  return error_code_e::OK;
}

bool SparseArray_Equals(value::SparseArray* lhs, value::SparseArray* rhs) {
  int len_lhs;
  int len_rhs;
  StackRoot _root0(&lhs);
  StackRoot _root1(&rhs);

  len_lhs = len(lhs->d);
  len_rhs = len(rhs->d);
  if (len_lhs != len_rhs) {
    return false;
  }
  for (DictIter<mops::BigInt, BigStr*> it(lhs->d); !it.Done(); it.Next()) {
    mops::BigInt index = it.Key();
    if ((!dict_contains(rhs->d, index) or !(str_equals(rhs->d->at(index), lhs->d->at(index))))) {
      return false;
    }
  }
  return true;
}

BigStr* SparseArray_ToStrForShellPrint(value::SparseArray* sparse_val) {
  List<BigStr*>* body = nullptr;
  StackRoot _root0(&sparse_val);
  StackRoot _root1(&body);

  body = Alloc<List<BigStr*>>();
  for (ListIter<mops::BigInt> it(SparseArray_GetKeys(sparse_val)); !it.Done(); it.Next()) {
    mops::BigInt index = it.Value();
    if (len(body) > 0) {
      body->append(S_yfw);
    }
    body->extend(NewList<BigStr*>(std::initializer_list<BigStr*>{S_Eax, mops::ToStr(index), S_nuz, j8_lite::MaybeShellEncode(sparse_val->d->at(index))}));
  }
  return StrFormat("(%s)", S_Aoo->join(body));
}

}  // define namespace bash_impl

namespace comp_ui {  // define


int _PromptLen(BigStr* prompt_str) {
  bool escaped;
  BigStr* display_str = nullptr;
  BigStr* last_line = nullptr;
  StackRoot _root0(&prompt_str);
  StackRoot _root1(&display_str);
  StackRoot _root2(&last_line);

  escaped = false;
  display_str = S_Aoo;
  for (StrIter it(prompt_str); !it.Done(); it.Next()) {
    BigStr* c = it.Value();
    StackRoot _for(&c  );
    if (str_equals(c, S_FDc)) {
      escaped = true;
    }
    else {
      if (str_equals(c, S_ewA)) {
        escaped = false;
      }
      else {
        if (!escaped) {
          display_str = str_concat(display_str, c);
        }
      }
    }
  }
  last_line = display_str->split(S_nfs)->at(-1);
  return pp_value::TryUnicodeWidth(last_line);
}

PromptState::PromptState() {
  this->last_prompt_str = nullptr;
  this->last_prompt_len = -1;
}

void PromptState::SetLastPrompt(BigStr* prompt_str) {
  StackRoot _root0(&prompt_str);

  this->last_prompt_str = prompt_str;
  this->last_prompt_len = _PromptLen(prompt_str);
}

State::State() {
  this->line_until_tab = nullptr;
  this->display_pos = -1;
  this->descriptions = Alloc<Dict<BigStr*, BigStr*>>();
}

_IDisplay::_IDisplay(comp_ui::State* comp_state, comp_ui::PromptState* prompt_state, int num_lines_cap, mylib::Writer* f, util::_DebugFile* debug_f) {
  this->comp_state = comp_state;
  this->prompt_state = prompt_state;
  this->num_lines_cap = num_lines_cap;
  this->f = f;
  this->debug_f = debug_f;
}

void _IDisplay::PrintCandidates(BigStr* unused_subst, List<BigStr*>* matches, int unused_match_len) {
  StackRoot _root0(&unused_subst);
  StackRoot _root1(&matches);

  try {
    this->_PrintCandidates(unused_subst, matches, unused_match_len);
  }
  catch (Exception*) {
  }
}

void _IDisplay::_PrintCandidates(BigStr* unused_subst, List<BigStr*>* matches, int unused_match_len) {
  StackRoot _root0(&unused_subst);
  StackRoot _root1(&matches);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

void _IDisplay::Reset() {
  ;  // pass
}

void _IDisplay::ShowPromptOnRight(BigStr* rendered) {
  StackRoot _root0(&rendered);

  ;  // pass
}

void _IDisplay::EraseLines() {
  ;  // pass
}

MinimalDisplay::MinimalDisplay(comp_ui::State* comp_state, comp_ui::PromptState* prompt_state, util::_DebugFile* debug_f) : ::comp_ui::_IDisplay(comp_state, prompt_state, 10, mylib::Stdout(), debug_f) {
}

void MinimalDisplay::_RedrawPrompt() {
  this->f->write(this->prompt_state->last_prompt_str);
  this->f->write(this->comp_state->line_until_tab);
}

void MinimalDisplay::_PrintCandidates(BigStr* unused_subst, List<BigStr*>* matches, int unused_match_len) {
  int display_pos;
  bool too_many;
  int i;
  int num_left;
  StackRoot _root0(&unused_subst);
  StackRoot _root1(&matches);

  this->f->write(S_nfs);
  display_pos = this->comp_state->display_pos;
  too_many = false;
  i = 0;
  for (ListIter<BigStr*> it(matches); !it.Done(); it.Next()) {
    BigStr* m = it.Value();
    StackRoot _for(&m  );
    this->f->write(StrFormat(" %s\n", m->slice(display_pos)));
    if (i == this->num_lines_cap) {
      too_many = true;
      i += 1;
      break;
    }
    i += 1;
  }
  if (too_many) {
    num_left = (len(matches) - i);
    if (num_left) {
      this->f->write(StrFormat(" ... and %d more\n", num_left));
    }
  }
  this->_RedrawPrompt();
}

int _PrintPacked(List<BigStr*>* matches, int max_match_len, int term_width, int max_lines, mylib::Writer* f) {
  int w;
  int num_per_line;
  BigStr* fmt = nullptr;
  int num_lines;
  bool too_many;
  int remainder;
  int i;
  BigStr* fmt2 = nullptr;
  int num_left;
  StackRoot _root0(&matches);
  StackRoot _root1(&f);
  StackRoot _root2(&fmt);
  StackRoot _root3(&fmt2);

  w = (max_match_len + 2);
  num_per_line = max(1, ((term_width - 2) / w));
  fmt = str_concat(str_concat(S_aAh, str(w)), S_anC);
  num_lines = 0;
  too_many = false;
  remainder = (num_per_line - 1);
  i = 0;
  for (ListIter<BigStr*> it(matches); !it.Done(); it.Next()) {
    BigStr* m = it.Value();
    StackRoot _for(&m  );
    if ((i % num_per_line) == 0) {
      f->write(S_yfw);
    }
    f->write(StrFormat(fmt, m));
    if ((i % num_per_line) == remainder) {
      f->write(S_nfs);
      num_lines += 1;
      if (num_lines == max_lines) {
        too_many = true;
        i += 1;
        break;
      }
    }
    i += 1;
  }
  if ((i % num_per_line) != 0) {
    f->write(S_nfs);
    num_lines += 1;
  }
  if (too_many) {
    fmt2 = str_concat(str_concat(str_concat(str_concat(str_concat(ansi::BOLD, ansi::BLUE), S_dkr), str((term_width - 2))), S_anC), ansi::RESET);
    num_left = (len(matches) - i);
    if (num_left) {
      f->write(StrFormat(StrFormat(fmt2, S_qcx), num_left));
      num_lines += 1;
    }
  }
  return num_lines;
}

int _PrintLong(List<BigStr*>* matches, int max_match_len, int term_width, int max_lines, Dict<BigStr*, BigStr*>* descriptions, mylib::Writer* f) {
  int max_desc;
  BigStr* fmt = nullptr;
  int num_lines;
  BigStr* desc = nullptr;
  BigStr* fmt2 = nullptr;
  int num_left;
  StackRoot _root0(&matches);
  StackRoot _root1(&descriptions);
  StackRoot _root2(&f);
  StackRoot _root3(&fmt);
  StackRoot _root4(&desc);
  StackRoot _root5(&fmt2);

  max_desc = max(0, ((term_width - max_match_len) - 3));
  fmt = str_concat(str_concat(str_concat(str_concat(str_concat(str_concat(S_ppj, str(max_match_len)), S_uok), ansi::YELLOW), S_ubF), ansi::RESET), S_nfs);
  num_lines = 0;
  for (ListIter<BigStr*> it(matches); !it.Done(); it.Next()) {
    BigStr* rl_match = it.Value();
    StackRoot _for(&rl_match  );
    desc = descriptions->get(rl_match);
    if (desc == nullptr) {
      desc = S_Aoo;
    }
    if (max_desc == 0) {
      f->write(StrFormat(" %s\n", rl_match));
    }
    else {
      if (len(desc) > max_desc) {
        desc = str_concat(desc->slice(0, (max_desc - 5)), S_sqv);
      }
      f->write(StrFormat(fmt, rl_match, desc));
    }
    num_lines += 1;
    if (num_lines == max_lines) {
      fmt2 = str_concat(str_concat(str_concat(str_concat(str_concat(ansi::BOLD, ansi::BLUE), S_dkr), str((term_width - 1))), S_anC), ansi::RESET);
      num_left = (len(matches) - num_lines);
      if (num_left) {
        f->write(StrFormat(StrFormat(fmt2, S_qcx), num_left));
        num_lines += 1;
      }
      break;
    }
  }
  return num_lines;
}

NiceDisplay::NiceDisplay(int term_width, comp_ui::State* comp_state, comp_ui::PromptState* prompt_state, util::_DebugFile* debug_f, py_readline::Readline* readline, iolib::SignalSafe* signal_safe) : ::comp_ui::_IDisplay(comp_state, prompt_state, 10, mylib::Stdout(), debug_f) {
  this->term_width = term_width;
  this->readline = readline;
  this->signal_safe = signal_safe;
  this->bold_line = false;
  this->num_lines_last_displayed = 0;
  this->c_count = 0;
  this->m_count = 0;
  this->dupes = Alloc<Dict<int, int>>();
}

void NiceDisplay::Reset() {
  this->num_lines_last_displayed = 0;
  this->dupes->clear();
}

void NiceDisplay::_ReturnToPrompt(int num_lines) {
  int orig_len;
  int last_prompt_len;
  int n;
  orig_len = len(this->comp_state->line_until_tab);
  this->f->write(StrFormat("\u001b[%dA", num_lines));
  last_prompt_len = this->prompt_state->last_prompt_len;
  n = (orig_len + last_prompt_len);
  n = (n % this->_GetTerminalWidth());
  this->f->write(StrFormat("\u001b[%dC", n));
  if (this->bold_line) {
    this->f->write(ansi::BOLD);
  }
  this->f->flush();
}

void NiceDisplay::_PrintCandidates(BigStr* unused_subst, List<BigStr*>* matches, int unused_max_match_len) {
  int term_width;
  int display_pos;
  int comp_id;
  int max_lines;
  List<BigStr*>* to_display = nullptr;
  List<int>* lens = nullptr;
  int max_match_len;
  int num_lines;
  StackRoot _root0(&unused_subst);
  StackRoot _root1(&matches);
  StackRoot _root2(&to_display);
  StackRoot _root3(&lens);

  term_width = this->_GetTerminalWidth();
  display_pos = this->comp_state->display_pos;
  this->debug_f->write(StrFormat("DISPLAY POS in _PrintCandidates = %d\n", display_pos));
  this->f->write(S_nfs);
  this->EraseLines();
  comp_id = hash(S_Aoo->join(matches));
  if (dict_contains(this->dupes, comp_id)) {
    this->dupes->set(comp_id, (this->dupes->at(comp_id) + 1));
  }
  else {
    this->dupes->clear();
    this->dupes->set(comp_id, 1);
  }
  max_lines = (this->num_lines_cap * this->dupes->at(comp_id));
  if (display_pos == 0) {
    to_display = matches;
  }
  else {
    to_display = Alloc<List<BigStr*>>();
    for (ListIter<BigStr*> it(matches); !it.Done(); it.Next()) {
      BigStr* m = it.Value();
      to_display->append(m->slice(display_pos));
    }
  }
  lens = Alloc<List<int>>();
  for (ListIter<BigStr*> it(to_display); !it.Done(); it.Next()) {
    BigStr* m = it.Value();
    lens->append(len(m));
  }
  max_match_len = max(lens);
  if ((this->comp_state->descriptions != nullptr and len(this->comp_state->descriptions) > 0)) {
    num_lines = _PrintLong(to_display, max_match_len, term_width, max_lines, this->comp_state->descriptions, this->f);
  }
  else {
    num_lines = _PrintPacked(to_display, max_match_len, term_width, max_lines, this->f);
  }
  this->_ReturnToPrompt((num_lines + 1));
  this->num_lines_last_displayed = num_lines;
  this->c_count += 1;
}

void NiceDisplay::ShowPromptOnRight(BigStr* rendered) {
  int n;
  BigStr* spaces = nullptr;
  StackRoot _root0(&rendered);
  StackRoot _root1(&spaces);

  n = ((this->_GetTerminalWidth() - 2) - len(rendered));
  spaces = str_repeat(S_yfw, n);
  this->f->write(str_concat(str_concat(str_concat(str_concat(str_concat(str_concat(spaces, ansi::REVERSE), S_yfw), rendered), S_yfw), ansi::RESET), S_Avc));
}

void NiceDisplay::EraseLines() {
  int n;
  if (this->bold_line) {
    this->f->write(ansi::RESET);
    this->f->flush();
  }
  n = this->num_lines_last_displayed;
  if (n == 0) {
    return ;
  }
  for (int i = 0; i < n; ++i) {
    this->f->write(S_wzl);
    this->f->write(S_tiy);
  }
  this->f->write(StrFormat("\u001b[%dA", n));
  this->f->flush();
}

int NiceDisplay::_GetTerminalWidth() {
  if (this->signal_safe->PollSigWinch()) {
    try {
      this->term_width = libc::get_terminal_width();
    }
    catch (IOError_OSError*) {
      this->term_width = 80;
    }
  }
  return this->term_width;
}

void ExecutePrintCandidates(comp_ui::_IDisplay* display, BigStr* sub, List<BigStr*>* matches, int max_len) {
  StackRoot _root0(&display);
  StackRoot _root1(&sub);
  StackRoot _root2(&matches);

  display->PrintCandidates(sub, matches, max_len);
}

void InitReadline(py_readline::Readline* readline, BigStr* hist_file, completion::RootCompleter* root_comp, comp_ui::_IDisplay* display, util::_DebugFile* debug_f) {
  completion::ReadlineCallback* complete_cb = nullptr;
  StackRoot _root0(&readline);
  StackRoot _root1(&hist_file);
  StackRoot _root2(&root_comp);
  StackRoot _root3(&display);
  StackRoot _root4(&debug_f);
  StackRoot _root5(&complete_cb);

  if (hist_file != nullptr) {
    try {
      readline->read_history_file(hist_file);
    }
    catch (IOError_OSError*) {
      ;  // pass
    }
  }
  readline->parse_and_bind(S_sph);
  readline->parse_and_bind(S_mhp);
  complete_cb = Alloc<completion::ReadlineCallback>(readline, root_comp, debug_f);
  readline->set_completer(complete_cb);
  readline->set_completer_delims(S_Aoo);
  readline->set_completion_display_matches_hook(display);
}

}  // define namespace comp_ui

namespace completion {  // define

using id_kind_asdl::Id;
using syntax_asdl::CompoundWord;
using syntax_asdl::word_part_e;
using syntax_asdl::word_t;
using syntax_asdl::redir_param_e;
using syntax_asdl::Token;
using runtime_asdl::scope_e;
using runtime_asdl::comp_action_e;
using runtime_asdl::comp_action_t;
using types_asdl::redir_arg_type_e;
using value_asdl::value;
using value_asdl::value_e;
using mylib::print_stderr;
using string_ops::ShellQuoteB;

_RetryCompletion::_RetryCompletion() {
  ;  // pass
}
int CH_Break = 0;
int CH_Other = 1;
int ST_Begin = 0;
int ST_Break = 1;
int ST_Other = 2;

Tuple2<int, bool> _TRANSITIONS(int state, int ch) {
  if ((state == ST_Begin and ch == CH_Break)) {
    return Tuple2<int, bool>(ST_Break, false);
  }
  if ((state == ST_Begin and ch == CH_Other)) {
    return Tuple2<int, bool>(ST_Other, false);
  }
  if ((state == ST_Break and ch == CH_Break)) {
    return Tuple2<int, bool>(ST_Break, false);
  }
  if ((state == ST_Break and ch == CH_Other)) {
    return Tuple2<int, bool>(ST_Other, true);
  }
  if ((state == ST_Other and ch == CH_Break)) {
    return Tuple2<int, bool>(ST_Break, true);
  }
  if ((state == ST_Other and ch == CH_Other)) {
    return Tuple2<int, bool>(ST_Other, false);
  }
  throw Alloc<ValueError>(S_dcr);
}

void AdjustArg(BigStr* arg, List<BigStr*>* break_chars, List<BigStr*>* argv_out) {
  List<int>* end_indices = nullptr;
  int state;
  int i;
  int ch;
  bool emit_span;
  int begin;
  StackRoot _root0(&arg);
  StackRoot _root1(&break_chars);
  StackRoot _root2(&argv_out);
  StackRoot _root3(&end_indices);

  end_indices = Alloc<List<int>>();
  state = ST_Begin;
  i = 0;
  for (StrIter it(arg); !it.Done(); it.Next(), ++i) {
    BigStr* c = it.Value();
    StackRoot _for(&c  );
    ch = list_contains(break_chars, c) ? CH_Break : CH_Other;
    Tuple2<int, bool> tup0 = _TRANSITIONS(state, ch);
    state = tup0.at0();
    emit_span = tup0.at1();
    if (emit_span) {
      end_indices->append(i);
    }
  }
  end_indices->append(len(arg));
  begin = 0;
  for (ListIter<int> it(end_indices); !it.Done(); it.Next()) {
    int end = it.Value();
    argv_out->append(arg->slice(begin, end));
    begin = end;
  }
}
GLOBAL_DICT(_DEFAULT_OPTS, BigStr*, bool, 0, {}, {});

OptionState::OptionState() {
  this->currently_completing = false;
  this->dynamic_opts = nullptr;
}

ctx_Completing::ctx_Completing(completion::OptionState* compopt_state) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->compopt_state)));
  compopt_state->currently_completing = true;
  this->compopt_state = compopt_state;
}

ctx_Completing::~ctx_Completing() {
  this->compopt_state->currently_completing = false;
  gHeap.PopRoot();
}

void _PrintOpts(Dict<BigStr*, bool>* opts, mylib::BufWriter* f) {
  StackRoot _root0(&opts);
  StackRoot _root1(&f);

  f->write(S_dxy);
  for (DictIter<BigStr*, bool> it(opts); !it.Done(); it.Next()) {
    BigStr* k = it.Key();
    bool v = it.Value();
    f->write(StrFormat(" %s=%s", k, v ? S_vrA : S_wfw));
  }
  f->write(S_dwC);
}

Lookup::Lookup() {
  completion::UserSpec* empty_spec = Alloc<UserSpec>(Alloc<List<completion::CompletionAction*>>(), Alloc<List<completion::CompletionAction*>>(), Alloc<List<completion::CompletionAction*>>(), Alloc<DefaultPredicate>(), S_Aoo, S_Aoo);
  Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>* do_nothing = (Alloc<Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>>(_DEFAULT_OPTS, empty_spec));
  this->lookup = Alloc<Dict<BigStr*, Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>*>>(std::initializer_list<BigStr*>{S_jaj, S_lgv}, std::initializer_list<Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>*>{do_nothing, do_nothing});
  this->commands_with_spec_changes = Alloc<List<BigStr*>>();
  this->patterns = Alloc<List<Tuple3<BigStr*, Dict<BigStr*, bool>*, completion::UserSpec*>*>>();
}

BigStr* Lookup::__str__() {
  return StrFormat("<completion.Lookup %s>", this->lookup);
}

void Lookup::PrintSpecs() {
  mylib::BufWriter* f = nullptr;
  Dict<BigStr*, bool>* base_opts = nullptr;
  completion::UserSpec* user_spec = nullptr;
  StackRoot _root0(&f);
  StackRoot _root1(&base_opts);
  StackRoot _root2(&user_spec);

  f = Alloc<mylib::BufWriter>();
  f->write(S_jDw);
  for (ListIter<BigStr*> it(sorted(this->lookup)); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>* tup1 = this->lookup->at(name);
    base_opts = tup1->at0();
    user_spec = tup1->at1();
    f->write(StrFormat("%s:\n", name));
    _PrintOpts(base_opts, f);
    user_spec->PrintSpec(f);
  }
  f->write(S_chm);
  for (ListIter<Tuple3<BigStr*, Dict<BigStr*, bool>*, completion::UserSpec*>*> it(this->patterns); !it.Done(); it.Next()) {
    Tuple3<BigStr*, Dict<BigStr*, bool>*, completion::UserSpec*>* tup2 = it.Value();
    BigStr* pat = tup2->at0();
    StackRoot _unpack_0(&pat);
    Dict<BigStr*, bool>* base_opts = tup2->at1();
    StackRoot _unpack_1(&base_opts);
    completion::UserSpec* spec = tup2->at2();
    StackRoot _unpack_2(&spec);
    f->write(StrFormat("%s:\n", pat));
    _PrintOpts(base_opts, f);
    user_spec->PrintSpec(f);
  }
  print_stderr(f->getvalue());
}

void Lookup::ClearCommandsChanged() {
  this->commands_with_spec_changes->clear();
}

List<BigStr*>* Lookup::GetCommandsChanged() {
  return this->commands_with_spec_changes;
}

void Lookup::RegisterName(BigStr* name, Dict<BigStr*, bool>* base_opts, completion::UserSpec* user_spec) {
  StackRoot _root0(&name);
  StackRoot _root1(&base_opts);
  StackRoot _root2(&user_spec);

  this->lookup->set(name, (Alloc<Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>>(base_opts, user_spec)));
  if ((!str_equals(name, S_jaj) && !str_equals(name, S_lgv))) {
    this->commands_with_spec_changes->append(name);
  }
}

void Lookup::RegisterGlob(BigStr* glob_pat, Dict<BigStr*, bool>* base_opts, completion::UserSpec* user_spec) {
  StackRoot _root0(&glob_pat);
  StackRoot _root1(&base_opts);
  StackRoot _root2(&user_spec);

  this->patterns->append((Alloc<Tuple3<BigStr*, Dict<BigStr*, bool>*, completion::UserSpec*>>(glob_pat, base_opts, user_spec)));
}

Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> Lookup::GetSpecForName(BigStr* argv0) {
  Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>* pair = nullptr;
  Dict<BigStr*, bool>* a = nullptr;
  completion::UserSpec* b = nullptr;
  BigStr* key = nullptr;
  StackRoot _root0(&argv0);
  StackRoot _root1(&pair);
  StackRoot _root2(&a);
  StackRoot _root3(&b);
  StackRoot _root4(&key);

  pair = this->lookup->get(argv0);
  if (pair) {
    Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>* tup3 = pair;
    a = tup3->at0();
    b = tup3->at1();
    return Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>(a, b);
  }
  key = os_path::basename(argv0);
  pair = this->lookup->get(key);
  if (pair) {
    Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>* tup4 = pair;
    a = tup4->at0();
    b = tup4->at1();
    return Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>(a, b);
  }
  for (ListIter<Tuple3<BigStr*, Dict<BigStr*, bool>*, completion::UserSpec*>*> it(this->patterns); !it.Done(); it.Next()) {
    Tuple3<BigStr*, Dict<BigStr*, bool>*, completion::UserSpec*>* tup5 = it.Value();
    BigStr* glob_pat = tup5->at0();
    StackRoot _unpack_0(&glob_pat);
    Dict<BigStr*, bool>* base_opts = tup5->at1();
    StackRoot _unpack_1(&base_opts);
    completion::UserSpec* user_spec = tup5->at2();
    StackRoot _unpack_2(&user_spec);
    if (libc::fnmatch(glob_pat, key)) {
      return Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>(base_opts, user_spec);
    }
  }
  return Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>(nullptr, nullptr);
}

Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> Lookup::GetFirstSpec() {
  Dict<BigStr*, bool>* a = nullptr;
  completion::UserSpec* b = nullptr;
  StackRoot _root0(&a);
  StackRoot _root1(&b);

  Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>* tup6 = this->lookup->at(S_lgv);
  a = tup6->at0();
  b = tup6->at1();
  return Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>(a, b);
}

Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> Lookup::GetFallback() {
  Dict<BigStr*, bool>* a = nullptr;
  completion::UserSpec* b = nullptr;
  StackRoot _root0(&a);
  StackRoot _root1(&b);

  Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>* tup7 = this->lookup->at(S_jaj);
  a = tup7->at0();
  b = tup7->at1();
  return Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*>(a, b);
}

Api::Api(BigStr* line, int begin, int end) {
  this->line = line;
  this->begin = begin;
  this->end = end;
  this->first = nullptr;
  this->to_complete = nullptr;
  this->prev = nullptr;
  this->index = -1;
  this->partial_argv = Alloc<List<BigStr*>>();
}

void Api::Update(BigStr* first, BigStr* to_complete, BigStr* prev, int index, List<BigStr*>* partial_argv) {
  StackRoot _root0(&first);
  StackRoot _root1(&to_complete);
  StackRoot _root2(&prev);
  StackRoot _root3(&partial_argv);

  this->first = first;
  this->to_complete = to_complete;
  this->prev = prev;
  this->index = index;
  this->partial_argv = partial_argv;
  if (this->partial_argv == nullptr) {
    this->partial_argv = Alloc<List<BigStr*>>();
  }
}

CompletionAction::CompletionAction() {
  ;  // pass
}

void CompletionAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  StackRoot _root0(&comp);

  ;  // pass
}

runtime_asdl::comp_action_t CompletionAction::ActionKind() {
  return comp_action_e::Other;
}

void CompletionAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_Cyx);
}

UsersAction::UsersAction() {
  ;  // pass
}

void UsersAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  BigStr* name = nullptr;
  StackRoot _root0(&comp);
  StackRoot _root1(&name);

  for (ListIter<pyos::PasswdEntry*> it(pyos::GetAllUsers()); !it.Done(); it.Next()) {
    pyos::PasswdEntry* u = it.Value();
    StackRoot _for(&u  );
    name = u->pw_name;
    if (name->startswith(comp->to_complete)) {
      YIELD->append(name);
    }
  }
}

void UsersAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_oEg);
}

TestAction::TestAction(List<BigStr*>* words, double delay) {
  this->words = words;
  this->delay = delay;
}

void TestAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  StackRoot _root0(&comp);

  for (ListIter<BigStr*> it(this->words); !it.Done(); it.Next()) {
    BigStr* w = it.Value();
    StackRoot _for(&w  );
    if (w->startswith(comp->to_complete)) {
      if (this->delay != 0.0) {
        time_::sleep(this->delay);
      }
      YIELD->append(w);
    }
  }
}

void TestAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_mAD);
}

DynamicWordsAction::DynamicWordsAction(word_eval::AbstractWordEvaluator* word_ev, split::SplitContext* splitter, syntax_asdl::CompoundWord* arg_word, ui::ErrorFormatter* errfmt) {
  this->word_ev = word_ev;
  this->splitter = splitter;
  this->arg_word = arg_word;
  this->errfmt = errfmt;
}

void DynamicWordsAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  value::Str* val = nullptr;
  List<BigStr*>* candidates = nullptr;
  StackRoot _root0(&comp);
  StackRoot _root1(&val);
  StackRoot _root2(&candidates);

  try {
    val = this->word_ev->EvalWordToString(this->arg_word);
  }
  catch (error::FatalRuntime* e) {
    this->errfmt->PrettyPrintError(e);
    throw;
  }
  candidates = this->splitter->SplitForWordEval(val->s);
  for (ListIter<BigStr*> it(candidates); !it.Done(); it.Next()) {
    BigStr* c = it.Value();
    StackRoot _for(&c  );
    if (c->startswith(comp->to_complete)) {
      YIELD->append(c);
    }
  }
}

void DynamicWordsAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_Ayq);
}

FileSystemAction::FileSystemAction(bool dirs_only, bool exec_only, bool add_slash) {
  this->dirs_only = dirs_only;
  this->exec_only = exec_only;
  this->add_slash = add_slash;
}

runtime_asdl::comp_action_t FileSystemAction::ActionKind() {
  return comp_action_e::FileSystem;
}

void FileSystemAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_ezz);
}

void FileSystemAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  BigStr* to_complete = nullptr;
  BigStr* dirname = nullptr;
  BigStr* basename = nullptr;
  BigStr* to_list = nullptr;
  List<BigStr*>* names = nullptr;
  BigStr* path = nullptr;
  StackRoot _root0(&comp);
  StackRoot _root1(&to_complete);
  StackRoot _root2(&dirname);
  StackRoot _root3(&basename);
  StackRoot _root4(&to_list);
  StackRoot _root5(&names);
  StackRoot _root6(&path);

  to_complete = comp->to_complete;
  Tuple2<BigStr*, BigStr*> tup8 = os_path::split(to_complete);
  dirname = tup8.at0();
  basename = tup8.at1();
  if (str_equals(dirname, S_Aoo)) {
    to_list = S_Aru;
  }
  else {
    to_list = dirname;
  }
  try {
    names = posix::listdir(to_list);
  }
  catch (IOError_OSError* e) {
    return ;
  }
  for (ListIter<BigStr*> it(names); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    path = os_path::join(dirname, name);
    if (path->startswith(to_complete)) {
      if (this->dirs_only) {
        if (path_stat::isdir(path)) {
          YIELD->append(path);
        }
        continue;
      }
      if (this->exec_only) {
        if (!posix::access(path, X_OK)) {
          continue;
        }
      }
      if ((this->add_slash and path_stat::isdir(path))) {
        path = str_concat(path, S_ckc);
        YIELD->append(path);
      }
      else {
        YIELD->append(path);
      }
    }
  }
}

CommandAction::CommandAction(cmd_eval::CommandEvaluator* cmd_ev, BigStr* command_name) {
  this->cmd_ev = cmd_ev;
  this->command_name = command_name;
}

void CommandAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  StackRoot _root0(&comp);

  for (ListIter<BigStr*> it(NewList<BigStr*>(std::initializer_list<BigStr*>{S_jpy})); !it.Done(); it.Next()) {
    BigStr* candidate = it.Value();
    StackRoot _for(&candidate  );
    YIELD->append(candidate);
  }
}

ShellFuncAction::ShellFuncAction(cmd_eval::CommandEvaluator* cmd_ev, value::Proc* func, completion::Lookup* comp_lookup) {
  this->cmd_ev = cmd_ev;
  this->func = func;
  this->comp_lookup = comp_lookup;
}

void ShellFuncAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(StrFormat("[ShellFuncAction %s] ", this->func->name));
}

runtime_asdl::comp_action_t ShellFuncAction::ActionKind() {
  return comp_action_e::BashFunc;
}

void ShellFuncAction::debug(BigStr* msg) {
  StackRoot _root0(&msg);

  this->cmd_ev->debug_f->writeln(msg);
}

void ShellFuncAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  List<BigStr*>* comp_words = nullptr;
  int comp_cword;
  List<BigStr*>* argv = nullptr;
  int status;
  List<BigStr*>* commands_changed = nullptr;
  BigStr* cmd = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&comp);
  StackRoot _root1(&comp_words);
  StackRoot _root2(&argv);
  StackRoot _root3(&commands_changed);
  StackRoot _root4(&cmd);
  StackRoot _root5(&val);
  StackRoot _root6(&UP_val);
  StackRoot _root7(&strs);

  state::SetGlobalArray(this->cmd_ev->mem, S_fot, Alloc<List<BigStr*>>());
  state::SetGlobalArray(this->cmd_ev->mem, S_FiD, comp->partial_argv);
  comp_words = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(comp->partial_argv); !it.Done(); it.Next()) {
    BigStr* a = it.Value();
    StackRoot _for(&a  );
    AdjustArg(a, NewList<BigStr*>(std::initializer_list<BigStr*>{S_fyj, S_bby}), comp_words);
  }
  if (comp->index == -1) {
    comp_cword = comp->index;
  }
  else {
    comp_cword = (len(comp_words) - 1);
  }
  state::SetGlobalArray(this->cmd_ev->mem, S_yDB, comp_words);
  state::SetGlobalString(this->cmd_ev->mem, S_xcm, str(comp_cword));
  state::SetGlobalString(this->cmd_ev->mem, S_Ann, comp->line);
  state::SetGlobalString(this->cmd_ev->mem, S_slv, str(comp->end));
  argv = NewList<BigStr*>(std::initializer_list<BigStr*>{comp->first, comp->to_complete, comp->prev});
  this->debug(StrFormat("Running completion function %r with %d arguments", this->func->name, len(argv)));
  this->comp_lookup->ClearCommandsChanged();
  status = this->cmd_ev->RunFuncForCompletion(this->func, argv);
  commands_changed = this->comp_lookup->GetCommandsChanged();
  this->debug(StrFormat("comp.first %r, commands_changed: %s", comp->first, S_tgp->join(commands_changed)));
  if (status == 124) {
    cmd = os_path::basename(comp->first);
    if (list_contains(commands_changed, cmd)) {
      throw Alloc<_RetryCompletion>();
    }
    else {
      this->debug(StrFormat("Function %r returned 124, but the completion spec for %r wasn't changed", this->func->name, cmd));
      return ;
    }
  }
  val = this->cmd_ev->mem->GetValue(S_fot, scope_e::GlobalOnly);
  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      print_stderr(StrFormat("osh error: Ran function %r but COMPREPLY was unset", this->func->name));
      return ;
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      strs = NewList<BigStr*>(std::initializer_list<BigStr*>{val->s});
    }
      break;
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      strs = bash_impl::BashArray_GetValues(val);
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
      strs = bash_impl::SparseArray_GetValues(val);
    }
      break;
    default: {
      print_stderr(StrFormat("osh error: COMPREPLY should be an array or a string, got %s", ui::ValType(val)));
      return ;
    }
  }
  for (ListIter<BigStr*> it(strs); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    YIELD->append(s);
  }
}

VariablesAction::VariablesAction(state::Mem* mem) {
  this->mem = mem;
}

void VariablesAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  StackRoot _root0(&comp);

  for (ListIter<BigStr*> it(this->mem->VarNames()); !it.Done(); it.Next()) {
    BigStr* var_name = it.Value();
    StackRoot _for(&var_name  );
    YIELD->append(var_name);
  }
}

void VariablesAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_adr);
}

ExportedVarsAction::ExportedVarsAction(state::Mem* mem) {
  this->mem = mem;
}

void ExportedVarsAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  Dict<BigStr*, BigStr*>* d = nullptr;
  StackRoot _root0(&comp);
  StackRoot _root1(&d);

  d = this->mem->GetEnv();
  for (DictIter<BigStr*, BigStr*> it(d); !it.Done(); it.Next()) {
    BigStr* var_name = it.Key();
    StackRoot _for(&var_name  );
    YIELD->append(var_name);
  }
}

ExternalCommandAction::ExternalCommandAction(state::Mem* mem) {
  this->mem = mem;
  this->cache = Alloc<Dict<Tuple2<BigStr*, int>*, List<BigStr*>*>>();
}

void ExternalCommandAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_fFs);
}

void ExternalCommandAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  BigStr* path_str = nullptr;
  List<BigStr*>* path_dirs = nullptr;
  List<BigStr*>* executables = nullptr;
  Tuple2<BigStr*, int>* key = nullptr;
  List<BigStr*>* dir_exes = nullptr;
  List<BigStr*>* entries = nullptr;
  BigStr* path = nullptr;
  StackRoot _root0(&comp);
  StackRoot _root1(&path_str);
  StackRoot _root2(&path_dirs);
  StackRoot _root3(&executables);
  StackRoot _root4(&key);
  StackRoot _root5(&dir_exes);
  StackRoot _root6(&entries);
  StackRoot _root7(&path);

  path_str = this->mem->env_config->Get(S_jip);
  if (path_str == nullptr) {
    return ;
  }
  path_dirs = path_str->split(S_fyj);
  executables = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(path_dirs); !it.Done(); it.Next()) {
    BigStr* d = it.Value();
    StackRoot _for(&d  );
    try {
      key = pyos::MakeDirCacheKey(d);
    }
    catch (IOError_OSError* e) {
      continue;
    }
    dir_exes = this->cache->get(key);
    if (dir_exes == nullptr) {
      entries = posix::listdir(d);
      dir_exes = Alloc<List<BigStr*>>();
      for (ListIter<BigStr*> it(entries); !it.Done(); it.Next()) {
        BigStr* name = it.Value();
        StackRoot _for(&name      );
        path = os_path::join(d, name);
        if (!posix::access(path, X_OK)) {
          continue;
        }
        dir_exes->append(name);
      }
      this->cache->set(key, dir_exes);
    }
    executables->extend(dir_exes);
  }
  for (ListIter<BigStr*> it(executables); !it.Done(); it.Next()) {
    BigStr* word = it.Value();
    StackRoot _for(&word  );
    if (word->startswith(comp->to_complete)) {
      YIELD->append(word);
    }
  }
}

_Predicate::_Predicate() {
  ;  // pass
}

bool _Predicate::Evaluate(BigStr* candidate) {
  StackRoot _root0(&candidate);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

void _Predicate::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_typ);
}

DefaultPredicate::DefaultPredicate() {
  ;  // pass
}

bool DefaultPredicate::Evaluate(BigStr* candidate) {
  StackRoot _root0(&candidate);

  return true;
}

void DefaultPredicate::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_qAa);
}

GlobPredicate::GlobPredicate(bool include, BigStr* glob_pat) {
  this->include = include;
  this->glob_pat = glob_pat;
}

bool GlobPredicate::Evaluate(BigStr* candidate) {
  bool matched;
  StackRoot _root0(&candidate);

  matched = libc::fnmatch(this->glob_pat, candidate);
  if (this->include) {
    return !matched;
  }
  else {
    return matched;
  }
}

void GlobPredicate::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_uyp);
}

UserSpec::UserSpec(List<completion::CompletionAction*>* actions, List<completion::CompletionAction*>* extra_actions, List<completion::CompletionAction*>* else_actions, completion::_Predicate* predicate, BigStr* prefix, BigStr* suffix) {
  this->actions = actions;
  this->extra_actions = extra_actions;
  this->else_actions = else_actions;
  this->predicate = predicate;
  this->prefix = prefix;
  this->suffix = suffix;
}

void UserSpec::PrintSpec(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_Fev);
  for (ListIter<completion::CompletionAction*> it(this->actions); !it.Done(); it.Next()) {
    completion::CompletionAction* a = it.Value();
    StackRoot _for(&a  );
    a->Print(f);
  }
  f->write(S_nfs);
  f->write(S_gCB);
  for (ListIter<completion::CompletionAction*> it(this->extra_actions); !it.Done(); it.Next()) {
    completion::CompletionAction* a = it.Value();
    StackRoot _for(&a  );
    a->Print(f);
  }
  f->write(S_nfs);
  f->write(S_lCD);
  for (ListIter<completion::CompletionAction*> it(this->else_actions); !it.Done(); it.Next()) {
    completion::CompletionAction* a = it.Value();
    StackRoot _for(&a  );
    a->Print(f);
  }
  f->write(S_nfs);
  f->write(S_kcc);
  this->predicate->Print(f);
  f->write(S_nfs);
  f->write(StrFormat("  prefix: %s\n", this->prefix));
  f->write(StrFormat("  suffix: %s\n", this->prefix));
}

void UserSpec::AllMatches(completion::Api* comp, List<Tuple2<BigStr*, runtime_asdl::comp_action_t>*>* YIELD) {
  int num_matches;
  runtime_asdl::comp_action_t action_kind;
  bool show;
  StackRoot _root0(&comp);

  num_matches = 0;
  for (ListIter<completion::CompletionAction*> it(this->actions); !it.Done(); it.Next()) {
    completion::CompletionAction* a = it.Value();
    StackRoot _for(&a  );
    action_kind = a->ActionKind();
    List<BigStr*> YIELD_for_9;
    a->Matches(comp, &YIELD_for_9);
    for (ListIter<BigStr*> it(&YIELD_for_9); !it.Done(); it.Next()) {
      BigStr* match = it.Value();
      StackRoot _for(&match    );
      show = (this->predicate->Evaluate(match) and (match->startswith(comp->to_complete) or action_kind == comp_action_e::BashFunc));
      if (show) {
        YIELD->append((Alloc<Tuple2<BigStr*, runtime_asdl::comp_action_t>>(str_concat(str_concat(this->prefix, match), this->suffix), action_kind)));
        num_matches += 1;
      }
    }
  }
  for (ListIter<completion::CompletionAction*> it(this->extra_actions); !it.Done(); it.Next()) {
    completion::CompletionAction* a = it.Value();
    StackRoot _for(&a  );
    List<BigStr*> YIELD_for_10;
    a->Matches(comp, &YIELD_for_10);
    for (ListIter<BigStr*> it(&YIELD_for_10); !it.Done(); it.Next()) {
      BigStr* match = it.Value();
      StackRoot _for(&match    );
      YIELD->append((Alloc<Tuple2<BigStr*, runtime_asdl::comp_action_t>>(match, comp_action_e::FileSystem)));
    }
  }
  if (num_matches == 0) {
    for (ListIter<completion::CompletionAction*> it(this->else_actions); !it.Done(); it.Next()) {
      completion::CompletionAction* a = it.Value();
      StackRoot _for(&a    );
      List<BigStr*> YIELD_for_11;
      a->Matches(comp, &YIELD_for_11);
      for (ListIter<BigStr*> it(&YIELD_for_11); !it.Done(); it.Next()) {
        BigStr* match = it.Value();
        StackRoot _for(&match      );
        YIELD->append((Alloc<Tuple2<BigStr*, runtime_asdl::comp_action_t>>(match, comp_action_e::FileSystem)));
      }
    }
  }
}

bool IsDollar(syntax_asdl::Token* t) {
  StackRoot _root0(&t);

  return t->id == Id::Lit_Dollar;
}

bool IsDummy(syntax_asdl::Token* t) {
  StackRoot _root0(&t);

  return t->id == Id::Lit_CompDummy;
}

bool WordEndsWithCompDummy(syntax_asdl::CompoundWord* w) {
  syntax_asdl::word_part_t* last_part = nullptr;
  syntax_asdl::word_part_t* UP_part = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&last_part);
  StackRoot _root2(&UP_part);

  last_part = w->parts->at(-1);
  UP_part = last_part;
  if (last_part->tag() == word_part_e::Literal) {
    Token* last_part = static_cast<Token*>(UP_part);
    return last_part->id == Id::Lit_CompDummy;
  }
  else {
    return false;
  }
}

RootCompleter::RootCompleter(word_eval::AbstractWordEvaluator* word_ev, state::Mem* mem, completion::Lookup* comp_lookup, completion::OptionState* compopt_state, comp_ui::State* comp_ui_state, parse_lib::ParseContext* parse_ctx, util::_DebugFile* debug_f) {
  this->word_ev = word_ev;
  this->mem = mem;
  this->comp_lookup = comp_lookup;
  this->compopt_state = compopt_state;
  this->comp_ui_state = comp_ui_state;
  this->parse_ctx = parse_ctx;
  this->debug_f = debug_f;
}

void RootCompleter::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  BigStr* line_until_tab = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  util::_DebugFile* debug_f = nullptr;
  parse_lib::_BaseTrail* trail = nullptr;
  List<syntax_asdl::Token*>* tokens = nullptr;
  int last;
  syntax_asdl::Token* t1 = nullptr;
  syntax_asdl::Token* t2 = nullptr;
  BigStr* to_complete = nullptr;
  int n;
  List<syntax_asdl::word_part_t*>* parts = nullptr;
  syntax_asdl::Token* tilde_tok = nullptr;
  BigStr* name = nullptr;
  BigStr* s = nullptr;
  syntax_asdl::Token* chars_tok = nullptr;
  syntax_asdl::Redir* r = nullptr;
  syntax_asdl::redir_param_t* arg_word = nullptr;
  syntax_asdl::redir_param_t* UP_word = nullptr;
  value::Str* val = nullptr;
  syntax_asdl::Token* tok = nullptr;
  completion::FileSystemAction* action = nullptr;
  Dict<BigStr*, bool>* base_opts = nullptr;
  completion::UserSpec* user_spec = nullptr;
  List<BigStr*>* partial_argv = nullptr;
  int num_partial;
  BigStr* first = nullptr;
  List<syntax_asdl::word_t*>* trail_words = nullptr;
  List<syntax_asdl::word_t*>* words2 = nullptr;
  BigStr* alias_first = nullptr;
  syntax_asdl::word_t* w = nullptr;
  int index;
  BigStr* prev = nullptr;
  Dict<BigStr*, bool>* dynamic_opts = nullptr;
  bool done;
  StackRoot _root0(&comp);
  StackRoot _root1(&line_until_tab);
  StackRoot _root2(&line_reader);
  StackRoot _root3(&c_parser);
  StackRoot _root4(&debug_f);
  StackRoot _root5(&trail);
  StackRoot _root6(&tokens);
  StackRoot _root7(&t1);
  StackRoot _root8(&t2);
  StackRoot _root9(&to_complete);
  StackRoot _root10(&parts);
  StackRoot _root11(&tilde_tok);
  StackRoot _root12(&name);
  StackRoot _root13(&s);
  StackRoot _root14(&chars_tok);
  StackRoot _root15(&r);
  StackRoot _root16(&arg_word);
  StackRoot _root17(&UP_word);
  StackRoot _root18(&val);
  StackRoot _root19(&tok);
  StackRoot _root20(&action);
  StackRoot _root21(&base_opts);
  StackRoot _root22(&user_spec);
  StackRoot _root23(&partial_argv);
  StackRoot _root24(&first);
  StackRoot _root25(&trail_words);
  StackRoot _root26(&words2);
  StackRoot _root27(&alias_first);
  StackRoot _root28(&w);
  StackRoot _root29(&prev);
  StackRoot _root30(&dynamic_opts);

  line_until_tab = comp->line->slice(0, comp->end);
  this->comp_ui_state->line_until_tab = line_until_tab;
  this->parse_ctx->trail->Clear();
  line_reader = reader::StringLineReader(line_until_tab, this->parse_ctx->arena);
  c_parser = this->parse_ctx->MakeOshParser(line_reader, true);
  try {
    c_parser->ParseLogicalLine();
  }
  catch (error::Parse* e) {
    ;  // pass
  }
  debug_f = this->debug_f;
  trail = this->parse_ctx->trail;
  tokens = trail->tokens;
  last = -1;
  if (tokens->at(-1)->id == Id::Eof_Real) {
    last -= 1;
  }
  try {
    t1 = tokens->at(last);
  }
  catch (IndexError*) {
    t1 = nullptr;
  }
  try {
    t2 = tokens->at((last - 1));
  }
  catch (IndexError*) {
    t2 = nullptr;
  }
  debug_f->writeln(StrFormat("line: %r", comp->line));
  debug_f->writeln(StrFormat("rl_slice from byte %d to %d: %r", comp->begin, comp->end, comp->line->slice(comp->begin, comp->end)));
  if (t1) {
    ;  // pass
  }
  if (t2) {
    ;  // pass
  }
  if (t2) {
    if ((IsDollar(t2) and IsDummy(t1))) {
      this->comp_ui_state->display_pos = (t2->col + 1);
      for (ListIter<BigStr*> it(this->mem->VarNames()); !it.Done(); it.Next()) {
        BigStr* name = it.Value();
        StackRoot _for(&name      );
        YIELD->append(str_concat(line_until_tab, name));
      }
      return ;
    }
    if ((t2->id == Id::Left_DollarBrace and IsDummy(t1))) {
      this->comp_ui_state->display_pos = (t2->col + 2);
      for (ListIter<BigStr*> it(this->mem->VarNames()); !it.Done(); it.Next()) {
        BigStr* name = it.Value();
        StackRoot _for(&name      );
        YIELD->append(str_concat(line_until_tab, name));
      }
      return ;
    }
    if ((t2->id == Id::VSub_DollarName and IsDummy(t1))) {
      this->comp_ui_state->display_pos = (t2->col + 1);
      to_complete = lexer::LazyStr(t2);
      n = len(to_complete);
      for (ListIter<BigStr*> it(this->mem->VarNames()); !it.Done(); it.Next()) {
        BigStr* name = it.Value();
        StackRoot _for(&name      );
        if (name->startswith(to_complete)) {
          YIELD->append(str_concat(line_until_tab, name->slice(n)));
        }
      }
      return ;
    }
    if ((t2->id == Id::VSub_Name and IsDummy(t1))) {
      this->comp_ui_state->display_pos = t2->col;
      to_complete = lexer::LazyStr(t2);
      n = len(to_complete);
      for (ListIter<BigStr*> it(this->mem->VarNames()); !it.Done(); it.Next()) {
        BigStr* name = it.Value();
        StackRoot _for(&name      );
        if (name->startswith(to_complete)) {
          YIELD->append(str_concat(line_until_tab, name->slice(n)));
        }
      }
      return ;
    }
    if ((t2->id == Id::Lit_ArithVarLike and IsDummy(t1))) {
      this->comp_ui_state->display_pos = t2->col;
      to_complete = lexer::LazyStr(t2);
      n = len(to_complete);
      for (ListIter<BigStr*> it(this->mem->VarNames()); !it.Done(); it.Next()) {
        BigStr* name = it.Value();
        StackRoot _for(&name      );
        if (name->startswith(to_complete)) {
          YIELD->append(str_concat(line_until_tab, name->slice(n)));
        }
      }
      return ;
    }
  }
  if (len(trail->words) > 0) {
    parts = trail->words->at(-1)->parts;
    if ((len(parts) > 0 and word_::LiteralId(parts->at(0)) == Id::Lit_Tilde)) {
      if ((len(parts) == 2 and word_::LiteralId(parts->at(1)) == Id::Lit_CompDummy)) {
        tilde_tok = static_cast<Token*>(parts->at(0));
        this->comp_ui_state->display_pos = (tilde_tok->col + 1);
        to_complete = S_Aoo;
        for (ListIter<pyos::PasswdEntry*> it(pyos::GetAllUsers()); !it.Done(); it.Next()) {
          pyos::PasswdEntry* u = it.Value();
          StackRoot _for(&u        );
          name = u->pw_name;
          s = str_concat(str_concat(line_until_tab, ShellQuoteB(name)), S_ckc);
          YIELD->append(s);
        }
        return ;
      }
      if ((len(parts) == 3 and (word_::LiteralId(parts->at(1)) == Id::Lit_Chars and word_::LiteralId(parts->at(2)) == Id::Lit_CompDummy))) {
        chars_tok = static_cast<Token*>(parts->at(1));
        this->comp_ui_state->display_pos = chars_tok->col;
        to_complete = lexer::TokenVal(chars_tok);
        n = len(to_complete);
        for (ListIter<pyos::PasswdEntry*> it(pyos::GetAllUsers()); !it.Done(); it.Next()) {
          pyos::PasswdEntry* u = it.Value();
          StackRoot _for(&u        );
          name = u->pw_name;
          if (name->startswith(to_complete)) {
            s = str_concat(str_concat(line_until_tab, ShellQuoteB(name->slice(n))), S_ckc);
            YIELD->append(s);
          }
        }
        return ;
      }
    }
  }
  if (len(trail->redirects) > 0) {
    r = trail->redirects->at(-1);
    if ((r->arg->tag() == redir_param_e::Word and consts::RedirArgType(r->op->id) == redir_arg_type_e::Path)) {
      arg_word = r->arg;
      UP_word = arg_word;
      CompoundWord* arg_word = static_cast<CompoundWord*>(UP_word);
      if (WordEndsWithCompDummy(arg_word)) {
        debug_f->writeln(S_Aat);
        try {
          val = this->word_ev->EvalWordToString(arg_word);
        }
        catch (error::FatalRuntime* e) {
          debug_f->writeln(StrFormat("Error evaluating redirect word: %s", e));
          return ;
        }
        if (val->tag() != value_e::Str) {
          debug_f->writeln(S_mup);
          return ;
        }
        tok = location::LeftTokenForWord(arg_word);
        this->comp_ui_state->display_pos = tok->col;
        comp->Update(S_Aoo, val->s, S_Aoo, 0, Alloc<List<BigStr*>>());
        n = len(val->s);
        action = Alloc<FileSystemAction>(false, false, true);
        List<BigStr*> YIELD_for_12;
        action->Matches(comp, &YIELD_for_12);
        for (ListIter<BigStr*> it(&YIELD_for_12); !it.Done(); it.Next()) {
          BigStr* name = it.Value();
          StackRoot _for(&name        );
          YIELD->append(str_concat(line_until_tab, ShellQuoteB(name->slice(n))));
        }
        return ;
      }
    }
  }
  base_opts = nullptr;
  user_spec = nullptr;
  partial_argv = Alloc<List<BigStr*>>();
  num_partial = -1;
  first = nullptr;
  if (len(trail->words) > 0) {
    if (WordEndsWithCompDummy(trail->words->at(-1))) {
      debug_f->writeln(S_qcr);
      trail_words = Alloc<List<syntax_asdl::word_t*>>();
      for (ListIter<syntax_asdl::CompoundWord*> it(trail->words); !it.Done(); it.Next()) {
        syntax_asdl::CompoundWord* w = it.Value();
        trail_words->append(static_cast<word_t*>(w));
      }
      words2 = word_::TildeDetectAll(trail_words);
      for (ListIter<syntax_asdl::word_t*> it(words2); !it.Done(); it.Next()) {
        syntax_asdl::word_t* w = it.Value();
        StackRoot _for(&w      );
        try {
          val = this->word_ev->EvalWordToString(w);
        }
        catch (error::FatalRuntime*) {
          continue;
        }
        if (val->tag() == value_e::Str) {
          partial_argv->append(val->s);
        }
        else {
          ;  // pass
        }
      }
      debug_f->writeln(StrFormat("partial_argv: [%s]", S_Cce->join(partial_argv)));
      num_partial = len(partial_argv);
      first = partial_argv->at(0);
      alias_first = nullptr;
      if (len(trail->alias_words) > 0) {
        w = trail->alias_words->at(0);
        try {
          val = this->word_ev->EvalWordToString(w);
        }
        catch (error::FatalRuntime*) {
          ;  // pass
        }
        alias_first = val->s;
        debug_f->writeln(StrFormat("alias_first: %s", alias_first));
      }
      if (num_partial == 0) {
        assert(0);  // AssertionError
      }
      else {
        if (num_partial == 1) {
          Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> tup13 = this->comp_lookup->GetFirstSpec();
          base_opts = tup13.at0();
          user_spec = tup13.at1();
          tok = location::LeftTokenForWord(trail->words->at(0));
          this->comp_ui_state->display_pos = tok->col;
          this->debug_f->writeln(StrFormat("** DISPLAY_POS = %d", this->comp_ui_state->display_pos));
        }
        else {
          Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> tup14 = this->comp_lookup->GetSpecForName(first);
          base_opts = tup14.at0();
          user_spec = tup14.at1();
          if ((!user_spec and alias_first != nullptr)) {
            Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> tup15 = this->comp_lookup->GetSpecForName(alias_first);
            base_opts = tup15.at0();
            user_spec = tup15.at1();
            if (user_spec) {
              first = alias_first;
            }
          }
          if (!user_spec) {
            Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> tup16 = this->comp_lookup->GetFallback();
            base_opts = tup16.at0();
            user_spec = tup16.at1();
          }
          tok = location::LeftTokenForWord(trail->words->at(-1));
          this->comp_ui_state->display_pos = tok->col;
          this->debug_f->writeln(StrFormat("display_pos %d", this->comp_ui_state->display_pos));
        }
      }
      index = (len(partial_argv) - 1);
      prev = index == 0 ? S_Aoo : partial_argv->at((index - 1));
      comp->Update(first, partial_argv->at(-1), prev, index, partial_argv);
    }
  }
  if (!user_spec) {
    debug_f->writeln(S_gFA);
    return ;
  }
  dynamic_opts = Alloc<Dict<BigStr*, bool>>();
  this->compopt_state->dynamic_opts = dynamic_opts;
  {  // with
    ctx_Completing ctx{this->compopt_state};

    done = false;
    while (!done) {
      done = true;
      try {
        List<BigStr*> YIELD_for_17;
        this->_PostProcess(base_opts, dynamic_opts, user_spec, comp, &YIELD_for_17);
        for (ListIter<BigStr*> it(&YIELD_for_17); !it.Done(); it.Next()) {
          BigStr* candidate = it.Value();
          StackRoot _for(&candidate        );
          YIELD->append(candidate);
        }
      }
      catch (_RetryCompletion* e) {
        debug_f->writeln(S_AbA);
        done = false;
        if (num_partial == 0) {
          assert(0);  // AssertionError
        }
        else {
          if (num_partial == 1) {
            Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> tup18 = this->comp_lookup->GetFirstSpec();
            base_opts = tup18.at0();
            user_spec = tup18.at1();
          }
          else {
            Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> tup19 = this->comp_lookup->GetSpecForName(first);
            base_opts = tup19.at0();
            user_spec = tup19.at1();
            if (!user_spec) {
              Tuple2<Dict<BigStr*, bool>*, completion::UserSpec*> tup20 = this->comp_lookup->GetFallback();
              base_opts = tup20.at0();
              user_spec = tup20.at1();
            }
          }
        }
      }
    }
  }
}

void RootCompleter::_PostProcess(Dict<BigStr*, bool>* base_opts, Dict<BigStr*, bool>* dynamic_opts, completion::UserSpec* user_spec, completion::Api* comp, List<BigStr*>* YIELD) {
  double start_time;
  int i;
  BigStr* line_until_tab = nullptr;
  BigStr* line_until_word = nullptr;
  bool opt_filenames;
  BigStr* s = nullptr;
  bool opt_nospace;
  BigStr* sp = nullptr;
  BigStr* cand = nullptr;
  double elapsed_ms;
  BigStr* plural = nullptr;
  StackRoot _root0(&base_opts);
  StackRoot _root1(&dynamic_opts);
  StackRoot _root2(&user_spec);
  StackRoot _root3(&comp);
  StackRoot _root4(&line_until_tab);
  StackRoot _root5(&line_until_word);
  StackRoot _root6(&s);
  StackRoot _root7(&sp);
  StackRoot _root8(&cand);
  StackRoot _root9(&plural);

  this->debug_f->writeln(StrFormat("Completing %r ... (Ctrl-C to cancel)", comp->line));
  start_time = time_::time();
  i = 0;
  List<Tuple2<BigStr*, runtime_asdl::comp_action_t>*> YIELD_for_21;
  user_spec->AllMatches(comp, &YIELD_for_21);
  for (ListIter<Tuple2<BigStr*, runtime_asdl::comp_action_t>*> it(&YIELD_for_21); !it.Done(); it.Next()) {
    Tuple2<BigStr*, runtime_asdl::comp_action_t>* tup22 = it.Value();
    BigStr* candidate = tup22->at0();
    StackRoot _unpack_0(&candidate);
    runtime_asdl::comp_action_t action_kind = tup22->at1();
    line_until_tab = this->comp_ui_state->line_until_tab;
    line_until_word = line_until_tab->slice(0, this->comp_ui_state->display_pos);
    opt_filenames = base_opts->get(S_Fqh, false);
    if (dict_contains(dynamic_opts, S_Fqh)) {
      opt_filenames = dynamic_opts->at(S_Fqh);
    }
    if ((action_kind == comp_action_e::FileSystem or opt_filenames)) {
      if (path_stat::isdir(candidate)) {
        s = str_concat(str_concat(line_until_word, ShellQuoteB(candidate)), S_ckc);
        YIELD->append(s);
        continue;
      }
    }
    opt_nospace = base_opts->get(S_isi, false);
    if (dict_contains(dynamic_opts, S_isi)) {
      opt_nospace = dynamic_opts->at(S_isi);
    }
    sp = opt_nospace ? S_Aoo : S_yfw;
    cand = action_kind == comp_action_e::BashFunc ? candidate : ShellQuoteB(candidate);
    YIELD->append(str_concat(str_concat(line_until_word, cand), sp));
    i += 1;
    elapsed_ms = ((time_::time() - start_time) * 1000.0);
    plural = i == 1 ? S_Aoo : S_jit;
  }
  elapsed_ms = ((time_::time() - start_time) * 1000.0);
  plural = i == 1 ? S_Aoo : S_jit;
  this->debug_f->writeln(StrFormat("Found %d match%s for %r in %d ms", i, plural, comp->line, elapsed_ms));
}

ReadlineCallback::ReadlineCallback(py_readline::Readline* readline, completion::RootCompleter* root_comp, util::_DebugFile* debug_f) {
  this->readline = readline;
  this->root_comp = root_comp;
  this->debug_f = debug_f;
  // if not PYTHON
  {
    this->comp_matches = nullptr;
  }
  // endif MYCPP
}

BigStr* ReadlineCallback::_GetNextCompletion(int state) {
  BigStr* buf = nullptr;
  int begin;
  int end;
  completion::Api* comp = nullptr;
  BigStr* next_completion = nullptr;
  StackRoot _root0(&buf);
  StackRoot _root1(&comp);
  StackRoot _root2(&next_completion);

  if (state == 0) {
    buf = this->readline->get_line_buffer();
    begin = this->readline->get_begidx();
    end = this->readline->get_endidx();
    comp = Alloc<Api>(buf, begin, end);
    this->debug_f->writeln(StrFormat("Api %r %d %d", buf, begin, end));
    // if not PYTHON
    {
      List<BigStr*> YIELD_it;
      this->root_comp->Matches(comp, &YIELD_it);
      ListIter<BigStr*> it(&YIELD_it);
      this->comp_matches = list(it);
      this->comp_matches->reverse();
    }
    // endif MYCPP
  }
  // if not PYTHON
  {
    try {
      next_completion = this->comp_matches->pop();
    }
    catch (IndexError*) {
      next_completion = nullptr;
    }
  }
  // endif MYCPP
  return next_completion;
}

BigStr* ReadlineCallback::__call__(BigStr* unused_word, int state) {
  StackRoot _root0(&unused_word);

  try {
    return this->_GetNextCompletion(state);
  }
  catch (util::UserExit* e) {
    print_stderr(S_EDi);
  }
  catch (error::FatalRuntime* e) {
    print_stderr(StrFormat("osh: Runtime error while completing: %s", e->UserErrorString()));
    this->debug_f->writeln(StrFormat("Runtime error while completing: %s", e->UserErrorString()));
  }
  catch (IOError_OSError* e) {
    print_stderr(StrFormat("osh: I/O error (completion): %s", posix::strerror(e->errno_)));
  }
  catch (KeyboardInterrupt*) {
    print_stderr(S_qgA);
  }
  catch (Exception* e) {
    print_stderr(StrFormat("osh: Unhandled exception while completing: %s", e));
    this->debug_f->writeln(StrFormat("Unhandled exception while completing: %s", e));
  }
  catch (SystemExit* e) {
    posix::_exit(e->code);
  }
  return nullptr;
}

BigStr* ExecuteReadlineCallback(completion::ReadlineCallback* cb, BigStr* word, int state) {
  StackRoot _root0(&cb);
  StackRoot _root1(&word);

  return cb->__call__(word, state);
}

}  // define namespace completion

namespace dev {  // define

using option_asdl::option_i;
using option_asdl::builtin_i;
using option_asdl::builtin_t;
using runtime_asdl::cmd_value;
using runtime_asdl::scope_e;
using runtime_asdl::trace;
using runtime_asdl::trace_e;
using runtime_asdl::trace_t;
using syntax_asdl::assign_op_e;
using syntax_asdl::Token;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::sh_lvalue;
using value_asdl::sh_lvalue_e;
using value_asdl::LeftName;
using mylib::print_stderr;

CrashDumper::CrashDumper(BigStr* crash_dump_dir, process::FdState* fd_state) {
  this->crash_dump_dir = crash_dump_dir;
  this->fd_state = fd_state;
  this->do_collect = to_bool(crash_dump_dir);
  this->collected = false;
  this->var_stack = nullptr;
  this->argv_stack = nullptr;
  this->debug_stack = nullptr;
  this->error = nullptr;
}

void CrashDumper::MaybeRecord(cmd_eval::CommandEvaluator* cmd_ev, error::_ErrorWithLocation* err) {
  syntax_asdl::Token* blame_tok = nullptr;
  StackRoot _root0(&cmd_ev);
  StackRoot _root1(&err);
  StackRoot _root2(&blame_tok);

  if (!this->do_collect) {
    return ;
  }
  Tuple3<List<value_asdl::value_t*>*, List<value_asdl::value_t*>*, List<value_asdl::value_t*>*> tup0 = cmd_ev->mem->Dump();
  this->var_stack = tup0.at0();
  this->argv_stack = tup0.at1();
  this->debug_stack = tup0.at2();
  blame_tok = location::TokenFor(err->location);
  this->error = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_zyC}, std::initializer_list<value_asdl::value_t*>{Alloc<value::Str>(err->UserErrorString())});
  if (blame_tok) {
    this->error->set(S_cmd, Alloc<value::Str>(ui::GetLineSourceString(blame_tok->line)));
    this->error->set(S_zdb_1, num::ToBig(blame_tok->line->line_num));
    this->error->set(S_gzt, Alloc<value::Str>(blame_tok->line->content));
  }
  this->do_collect = false;
  this->collected = true;
}

void CrashDumper::MaybeDump(int status) {
  int my_pid;
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  BigStr* path = nullptr;
  mylib::BufWriter* buf = nullptr;
  BigStr* json_str = nullptr;
  mylib::Writer* f = nullptr;
  StackRoot _root0(&d);
  StackRoot _root1(&path);
  StackRoot _root2(&buf);
  StackRoot _root3(&json_str);
  StackRoot _root4(&f);

  if (!this->collected) {
    return ;
  }
  my_pid = posix::getpid();
  d = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_szq, S_nrm, S_yzg, S_riE, S_iAi, S_epe}, std::initializer_list<value_asdl::value_t*>{Alloc<value::List>(this->var_stack), Alloc<value::List>(this->argv_stack), Alloc<value::List>(this->debug_stack), Alloc<value::Dict>(this->error), num::ToBig(status), num::ToBig(my_pid)});
  path = os_path::join(this->crash_dump_dir, StrFormat("%d-osh-crash-dump.json", my_pid));
  buf = Alloc<mylib::BufWriter>();
  j8::PrintMessage(Alloc<value::Dict>(d), buf, 2);
  json_str = buf->getvalue();
  try {
    f = this->fd_state->OpenForWrite(path);
  }
  catch (IOError_OSError* e) {
    return ;
  }
  f->write(json_str);
  print_stderr(StrFormat("[%d] Wrote crash dump to %s", my_pid, path));
}

ctx_Tracer::ctx_Tracer(dev::Tracer* tracer, BigStr* label, List<BigStr*>* argv) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->arg)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->label)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->tracer)));
  this->arg = nullptr;
  if ((str_equals(label, S_aFi) || str_equals(label, S_dba))) {
    this->arg = argv->at(0);
  }
  else {
    if ((str_equals(label, S_cmd) || str_equals(label, S_eas))) {
      this->arg = argv->at(1);
    }
  }
  tracer->PushMessage(label, argv);
  this->label = label;
  this->tracer = tracer;
}

ctx_Tracer::~ctx_Tracer() {
  this->tracer->PopMessage(this->label, this->arg);
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
}

void _PrintShValue(value_asdl::value_t* val, mylib::BufWriter* buf) {
  BigStr* result = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&buf);
  StackRoot _root2(&result);
  StackRoot _root3(&UP_val);

  result = S_BAk;
  UP_val = val;
  switch (val->tag()) {
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      result = j8_lite::MaybeShellEncode(val->s);
    }
      break;
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      result = bash_impl::BashArray_ToStrForShellPrint(val, nullptr);
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      result = bash_impl::BashAssoc_ToStrForShellPrint(val);
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
      result = bash_impl::SparseArray_ToStrForShellPrint(val);
    }
      break;
  }
  buf->write(result);
}

void PrintShellArgv(List<BigStr*>* argv, mylib::BufWriter* buf) {
  int i;
  StackRoot _root0(&argv);
  StackRoot _root1(&buf);

  i = 0;
  for (ListIter<BigStr*> it(argv); !it.Done(); it.Next(), ++i) {
    BigStr* arg = it.Value();
    StackRoot _for(&arg  );
    if (i != 0) {
      buf->write(S_yfw);
    }
    buf->write(j8_lite::MaybeShellEncode(arg));
  }
}

void _PrintYshArgv(List<BigStr*>* argv, mylib::BufWriter* buf) {
  StackRoot _root0(&argv);
  StackRoot _root1(&buf);

  for (ListIter<BigStr*> it(argv); !it.Done(); it.Next()) {
    BigStr* arg = it.Value();
    StackRoot _for(&arg  );
    buf->write(S_yfw);
    buf->write(j8_lite::MaybeShellEncode(arg));
  }
  buf->write(S_nfs);
}

MultiTracer::MultiTracer(int shell_pid, BigStr* out_dir, BigStr* dumps, BigStr* streams, process::FdState* fd_state) {
  this->out_dir = out_dir;
  this->dumps = dumps;
  this->streams = streams;
  this->fd_state = fd_state;
  this->this_pid = shell_pid;
  this->hist_argv0 = Alloc<Dict<BigStr*, int>>();
}

void MultiTracer::OnNewProcess(int child_pid) {
  this->this_pid = child_pid;
  this->hist_argv0->clear();
}

void MultiTracer::EmitArgv0(BigStr* argv0) {
  StackRoot _root0(&argv0);

  if (!dict_contains(this->hist_argv0, argv0)) {
    this->hist_argv0->set(argv0, 1);
  }
  else {
    this->hist_argv0->set(argv0, (this->hist_argv0->at(argv0) + 1));
  }
}

void MultiTracer::WriteDumps() {
  List<value_asdl::value_t*>* metric_argv0 = nullptr;
  value::Str* a = nullptr;
  value::Int* c = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* j = nullptr;
  BigStr* path = nullptr;
  mylib::BufWriter* buf = nullptr;
  BigStr* json8_str = nullptr;
  mylib::Writer* f = nullptr;
  StackRoot _root0(&metric_argv0);
  StackRoot _root1(&a);
  StackRoot _root2(&c);
  StackRoot _root3(&d);
  StackRoot _root4(&j);
  StackRoot _root5(&path);
  StackRoot _root6(&buf);
  StackRoot _root7(&json8_str);
  StackRoot _root8(&f);

  if (len(this->out_dir) == 0) {
    return ;
  }
  metric_argv0 = Alloc<List<value_asdl::value_t*>>();
  for (DictIter<BigStr*, int> it(this->hist_argv0); !it.Done(); it.Next()) {
    BigStr* argv0 = it.Key();
    int count = it.Value();
    a = Alloc<value::Str>(argv0);
    c = Alloc<value::Int>(mops::IntWiden(count));
    d = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_gEr, S_oFh}, std::initializer_list<value_asdl::value_t*>{a, c});
    metric_argv0->append(Alloc<value::Dict>(d));
  }
  j = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_epe, S_tug}, std::initializer_list<value_asdl::value_t*>{Alloc<value::Int>(mops::IntWiden(this->this_pid)), Alloc<value::List>(metric_argv0)});
  path = os_path::join(this->out_dir, StrFormat("%d.argv0.json", this->this_pid));
  buf = Alloc<mylib::BufWriter>();
  j8::PrintMessage(Alloc<value::Dict>(j), buf, 2);
  json8_str = buf->getvalue();
  try {
    f = this->fd_state->OpenForWrite(path);
  }
  catch (IOError_OSError* e) {
    return ;
  }
  f->write(json8_str);
  f->close();
  print_stderr(StrFormat("[%d] Wrote metrics dump to %s", this->this_pid, path));
}

Tracer::Tracer(parse_lib::ParseContext* parse_ctx, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, state::Mem* mem, util::_DebugFile* f, dev::MultiTracer* multi_trace) {
  this->parse_ctx = parse_ctx;
  this->exec_opts = exec_opts;
  this->mutable_opts = mutable_opts;
  this->mem = mem;
  this->f = f;
  this->multi_trace = multi_trace;
  this->word_ev = nullptr;
  this->ind = 0;
  this->indents = NewList<BigStr*>(std::initializer_list<BigStr*>{S_Aoo});
  this->parse_cache = Alloc<Dict<BigStr*, syntax_asdl::CompoundWord*>>();
  this->val_indent = Alloc<value::Str>(S_Aoo);
  this->val_punct = Alloc<value::Str>(S_Aoo);
  this->val_pid_str = Alloc<value::Str>(S_Aoo);
  this->lval_indent = location::LName(S_lra);
  this->lval_punct = location::LName(S_vki);
  this->lval_pid_str = location::LName(S_zbC);
}

void Tracer::CheckCircularDeps() {
}

BigStr* Tracer::_EvalPS4(BigStr* punct) {
  value_asdl::value_t* val = nullptr;
  BigStr* ps4 = nullptr;
  syntax_asdl::CompoundWord* ps4_word = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  value::Str* prefix = nullptr;
  StackRoot _root0(&punct);
  StackRoot _root1(&val);
  StackRoot _root2(&ps4);
  StackRoot _root3(&ps4_word);
  StackRoot _root4(&w_parser);
  StackRoot _root5(&prefix);

  val = this->mem->GetValue(S_zyo);
  if (val->tag() == value_e::Str) {
    ps4 = static_cast<value::Str*>(val)->s;
  }
  else {
    ps4 = S_Aoo;
  }
  ps4_word = this->parse_cache->get(ps4);
  if (ps4_word == nullptr) {
    w_parser = this->parse_ctx->MakeWordParserForPlugin(ps4);
    try {
      ps4_word = w_parser->ReadForPlugin();
    }
    catch (error::Parse* e) {
      ps4_word = word_::ErrorWord(StrFormat("<ERROR: Can't parse PS4: %s>", e->UserErrorString()));
    }
    this->parse_cache->set(ps4, ps4_word);
  }
  if (this->exec_opts->xtrace_rich()) {
    this->val_indent->s = this->indents->at(this->ind);
  }
  else {
    this->val_indent->s = S_Aoo;
  }
  this->val_punct->s = punct;
  {  // with
    state::ctx_Option ctx{this->mutable_opts, NewList<int>(std::initializer_list<int>{option_i::xtrace}), false};

    {  // with
      state::ctx_Temp ctx{this->mem};

      this->mem->SetNamed(this->lval_indent, this->val_indent, scope_e::LocalOnly);
      this->mem->SetNamed(this->lval_punct, this->val_punct, scope_e::LocalOnly);
      this->mem->SetNamed(this->lval_pid_str, this->val_pid_str, scope_e::LocalOnly);
      prefix = this->word_ev->EvalForPlugin(ps4_word);
    }
  }
  return prefix->s;
}

void Tracer::_Inc() {
  this->ind += 1;
  if (this->ind >= len(this->indents)) {
    this->indents->append(str_repeat(S_jqf, this->ind));
  }
}

void Tracer::_Dec() {
  this->ind -= 1;
}

mylib::BufWriter* Tracer::_ShTraceBegin() {
  BigStr* prefix = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&prefix);
  StackRoot _root1(&buf);

  if ((!this->exec_opts->xtrace() or !this->exec_opts->xtrace_details())) {
    return nullptr;
  }
  prefix = this->_EvalPS4(S_jnE);
  buf = Alloc<mylib::BufWriter>();
  buf->write(prefix);
  return buf;
}

mylib::BufWriter* Tracer::_RichTraceBegin(BigStr* punct) {
  BigStr* prefix = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&punct);
  StackRoot _root1(&prefix);
  StackRoot _root2(&buf);

  if ((!this->exec_opts->xtrace() or !this->exec_opts->xtrace_rich())) {
    return nullptr;
  }
  prefix = this->_EvalPS4(punct);
  buf = Alloc<mylib::BufWriter>();
  buf->write(prefix);
  return buf;
}

void Tracer::OnProcessStart(int pid, runtime_asdl::trace_t* why) {
  runtime_asdl::trace_t* UP_why = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&why);
  StackRoot _root1(&UP_why);
  StackRoot _root2(&buf);

  UP_why = why;
  switch (why->tag()) {
    case trace_e::External: {
      trace::External* why = static_cast<trace::External*>(UP_why);
      this->multi_trace->EmitArgv0(why->argv->at(0));
    }
      break;
  }
  buf = this->_RichTraceBegin(S_Ebn);
  if (!buf) {
    return ;
  }
  switch (why->tag()) {
    case trace_e::External: {
      trace::External* why = static_cast<trace::External*>(UP_why);
      buf->write(StrFormat("command %d:", pid));
      _PrintYshArgv(why->argv, buf);
    }
      break;
    case trace_e::ForkWait: {
      buf->write(StrFormat("forkwait %d\n", pid));
    }
      break;
    case trace_e::CommandSub: {
      buf->write(StrFormat("command sub %d\n", pid));
    }
      break;
    case trace_e::ProcessSub: {
      buf->write(StrFormat("proc sub %d\n", pid));
    }
      break;
    case trace_e::HereDoc: {
      buf->write(StrFormat("here doc %d\n", pid));
    }
      break;
    case trace_e::Fork: {
      buf->write(StrFormat("fork %d\n", pid));
    }
      break;
    case trace_e::PipelinePart: {
      buf->write(StrFormat("part %d\n", pid));
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  this->f->write(buf->getvalue());
}

void Tracer::OnProcessEnd(int pid, int status) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&buf);

  buf = this->_RichTraceBegin(S_nbf);
  if (!buf) {
    return ;
  }
  buf->write(StrFormat("process %d: status %d\n", pid, status));
  this->f->write(buf->getvalue());
}

void Tracer::OnNewProcess(int child_pid) {
  this->val_pid_str->s = StrFormat(" %d", child_pid);
  this->_Inc();
  this->multi_trace->OnNewProcess(child_pid);
}

void Tracer::PushMessage(BigStr* label, List<BigStr*>* argv) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&label);
  StackRoot _root1(&argv);
  StackRoot _root2(&buf);

  buf = this->_RichTraceBegin(S_jye);
  if (buf) {
    buf->write(label);
    if ((str_equals(label, S_aFi) || str_equals(label, S_dba))) {
      _PrintYshArgv(argv, buf);
    }
    else {
      if ((str_equals(label, S_cmd) || str_equals(label, S_eas))) {
        _PrintYshArgv(argv->slice(1), buf);
      }
      else {
        if (str_equals(label, S_Awk)) {
          _PrintYshArgv(argv->slice(1), buf);
        }
        else {
          buf->write(S_nfs);
        }
      }
    }
    this->f->write(buf->getvalue());
  }
  this->_Inc();
}

void Tracer::PopMessage(BigStr* label, BigStr* arg) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&label);
  StackRoot _root1(&arg);
  StackRoot _root2(&buf);

  this->_Dec();
  buf = this->_RichTraceBegin(S_eox);
  if (buf) {
    buf->write(label);
    if (arg != nullptr) {
      buf->write(S_yfw);
      buf->write(j8_lite::MaybeShellEncode(arg));
    }
    buf->write(S_nfs);
    this->f->write(buf->getvalue());
  }
}

void Tracer::OtherMessage(BigStr* message) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&message);
  StackRoot _root1(&buf);

  buf = this->_RichTraceBegin(S_kao);
  if (!buf) {
    return ;
  }
  buf->write(message);
  buf->write(S_nfs);
  this->f->write(buf->getvalue());
}

void Tracer::OnExec(List<BigStr*>* argv) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&argv);
  StackRoot _root1(&buf);

  buf = this->_RichTraceBegin(S_Aru);
  if (!buf) {
    return ;
  }
  buf->write(S_Evy);
  _PrintYshArgv(argv, buf);
  this->f->write(buf->getvalue());
}

void Tracer::OnBuiltin(int builtin_id, List<BigStr*>* argv) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&argv);
  StackRoot _root1(&buf);

  if ((builtin_id == builtin_i::eval || builtin_id == builtin_i::source || builtin_id == builtin_i::use || builtin_id == builtin_i::wait)) {
    return ;
  }
  buf = this->_RichTraceBegin(S_Aru);
  if (!buf) {
    return ;
  }
  buf->write(S_utc);
  _PrintYshArgv(argv, buf);
  this->f->write(buf->getvalue());
}

void Tracer::OnSimpleCommand(List<BigStr*>* argv) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&argv);
  StackRoot _root1(&buf);

  buf = this->_ShTraceBegin();
  if (!buf) {
    return ;
  }
  if (this->exec_opts->xtrace_rich()) {
    return ;
  }
  PrintShellArgv(argv, buf);
  buf->write(S_nfs);
  this->f->write(buf->getvalue());
}

void Tracer::OnAssignBuiltin(cmd_value::Assign* cmd_val) {
  mylib::BufWriter* buf = nullptr;
  int i;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&buf);

  buf = this->_ShTraceBegin();
  if (!buf) {
    return ;
  }
  i = 0;
  for (ListIter<BigStr*> it(cmd_val->argv); !it.Done(); it.Next(), ++i) {
    BigStr* arg = it.Value();
    StackRoot _for(&arg  );
    if (i != 0) {
      buf->write(S_yfw);
    }
    buf->write(arg);
  }
  for (ListIter<runtime_asdl::AssignArg*> it(cmd_val->pairs); !it.Done(); it.Next()) {
    runtime_asdl::AssignArg* pair = it.Value();
    StackRoot _for(&pair  );
    buf->write(S_yfw);
    buf->write(pair->var_name);
    buf->write(pair->plus_eq ? S_Coy : S_bby);
    if (pair->rval) {
      _PrintShValue(pair->rval, buf);
    }
  }
  buf->write(S_nfs);
  this->f->write(buf->getvalue());
}

void Tracer::OnShAssignment(value_asdl::sh_lvalue_t* lval, syntax_asdl::assign_op_t op, value_asdl::value_t* val, int flags, runtime_asdl::scope_t which_scopes) {
  mylib::BufWriter* buf = nullptr;
  BigStr* left = nullptr;
  value_asdl::sh_lvalue_t* UP_lval = nullptr;
  StackRoot _root0(&lval);
  StackRoot _root1(&val);
  StackRoot _root2(&buf);
  StackRoot _root3(&left);
  StackRoot _root4(&UP_lval);

  buf = this->_ShTraceBegin();
  if (!buf) {
    return ;
  }
  left = S_BAk;
  UP_lval = lval;
  switch (lval->tag()) {
    case sh_lvalue_e::Var: {
      LeftName* lval = static_cast<LeftName*>(UP_lval);
      left = lval->name;
    }
      break;
    case sh_lvalue_e::Indexed: {
      sh_lvalue::Indexed* lval = static_cast<sh_lvalue::Indexed*>(UP_lval);
      left = StrFormat("%s[%d]", lval->name, lval->index);
    }
      break;
    case sh_lvalue_e::Keyed: {
      sh_lvalue::Keyed* lval = static_cast<sh_lvalue::Keyed*>(UP_lval);
      left = StrFormat("%s[%s]", lval->name, j8_lite::MaybeShellEncode(lval->key));
    }
      break;
  }
  buf->write(left);
  buf->write(op == assign_op_e::PlusEqual ? S_Coy : S_bby);
  _PrintShValue(val, buf);
  buf->write(S_nfs);
  this->f->write(buf->getvalue());
}

void Tracer::OnControlFlow(BigStr* keyword, int arg) {
  BigStr* prefix = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&keyword);
  StackRoot _root1(&prefix);
  StackRoot _root2(&buf);

  if (!this->exec_opts->xtrace()) {
    return ;
  }
  prefix = this->_EvalPS4(S_jnE);
  buf = Alloc<mylib::BufWriter>();
  buf->write(prefix);
  buf->write(keyword);
  buf->write(S_yfw);
  buf->write(str(arg));
  buf->write(S_nfs);
  this->f->write(buf->getvalue());
}

void Tracer::PrintSourceCode(syntax_asdl::Token* left_tok, syntax_asdl::Token* right_tok, alloc::Arena* arena) {
  mylib::BufWriter* buf = nullptr;
  BigStr* line = nullptr;
  int start;
  int end;
  StackRoot _root0(&left_tok);
  StackRoot _root1(&right_tok);
  StackRoot _root2(&arena);
  StackRoot _root3(&buf);
  StackRoot _root4(&line);

  buf = this->_ShTraceBegin();
  if (!buf) {
    return ;
  }
  line = left_tok->line->content;
  start = left_tok->col;
  if (left_tok->line == right_tok->line) {
    end = (right_tok->col + right_tok->length);
    buf->write(line->slice(start, end));
  }
  else {
    end = line->endswith(S_nfs) ? -1 : len(line);
    buf->write(line->slice(start, end));
    buf->write(S_bcl);
  }
  buf->write(S_nfs);
  this->f->write(buf->getvalue());
}

}  // define namespace dev

namespace error {  // define

using syntax_asdl::loc_e;
using syntax_asdl::loc_t;
using syntax_asdl::loc;
using value_asdl::value;
using value_asdl::value_t;
using value_asdl::value_str;

BigStr* _ValType(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  return value_str(val->tag(), false);
}

_ErrorWithLocation::_ErrorWithLocation(BigStr* msg, syntax_asdl::loc_t* location) {
  this->msg = msg;
  if (location == nullptr) {
    this->location = loc::Missing;
  }
  else {
    this->location = location;
  }
}

bool _ErrorWithLocation::HasLocation() {
  return this->location->tag() != loc_e::Missing;
}

BigStr* _ErrorWithLocation::UserErrorString() {
  return this->msg;
}

Usage::Usage(BigStr* msg, syntax_asdl::loc_t* location) : ::error::_ErrorWithLocation(msg, location) {
}

Parse::Parse(BigStr* msg, syntax_asdl::loc_t* location) : ::error::_ErrorWithLocation(msg, location) {
}

WordFailure::WordFailure(BigStr* msg, syntax_asdl::loc_t* location) : ::error::_ErrorWithLocation(msg, location) {
}

FailGlob::FailGlob(BigStr* msg, syntax_asdl::loc_t* location) : ::error::WordFailure(str_concat(S_xho, msg), location) {
}

VarSubFailure::VarSubFailure(BigStr* msg, syntax_asdl::loc_t* location) : ::error::WordFailure(msg, location) {
}

RedirectEval::RedirectEval(BigStr* msg, syntax_asdl::loc_t* location) : ::error::_ErrorWithLocation(msg, location) {
}

FatalRuntime::FatalRuntime(int exit_status, BigStr* msg, syntax_asdl::loc_t* location) : ::error::_ErrorWithLocation(msg, location) {
  this->exit_status = exit_status;
}

int FatalRuntime::ExitStatus() {
  return this->exit_status;
}

Strict::Strict(BigStr* msg, syntax_asdl::loc_t* location) : ::error::FatalRuntime(1, msg, location) {
}

ErrExit::ErrExit(int exit_status, BigStr* msg, syntax_asdl::loc_t* location, bool show_code) : ::error::FatalRuntime(exit_status, msg, location) {
  this->show_code = show_code;
}

Expr::Expr(BigStr* msg, syntax_asdl::loc_t* location) : ::error::FatalRuntime(3, msg, location) {
}

Structured::Structured(int status, BigStr* msg, syntax_asdl::loc_t* location, Dict<BigStr*, value_asdl::value_t*>* properties) : ::error::FatalRuntime(status, msg, location) {
  this->properties = properties;
}

value::Dict* Structured::ToDict() {
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  StackRoot _root0(&d);

  d = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  if (this->properties != nullptr) {
    d->update(this->properties);
  }
  d->set(S_gFE, num::ToBig(this->ExitStatus()));
  d->set(S_pBg, Alloc<value::Str>(this->msg));
  return Alloc<value::Dict>(d);
}

AssertionErr::AssertionErr(BigStr* msg, syntax_asdl::loc_t* location) : ::error::Expr(msg, location) {
}

TypeErrVerbose::TypeErrVerbose(BigStr* msg, syntax_asdl::loc_t* location) : ::error::Expr(msg, location) {
}

TypeErr::TypeErr(value_asdl::value_t* actual_val, BigStr* msg, syntax_asdl::loc_t* location) : ::error::TypeErrVerbose(StrFormat("%s, got %s", msg, _ValType(actual_val)), location) {
}

Runtime::Runtime(BigStr* msg) {
  this->msg = msg;
}

BigStr* Runtime::UserErrorString() {
  return this->msg;
}

Decode::Decode(BigStr* msg, BigStr* s, int start_pos, int end_pos, int line_num) {
  this->msg = msg;
  this->s = s;
  this->start_pos = start_pos;
  this->end_pos = end_pos;
  this->line_num = line_num;
}

BigStr* Decode::Message() {
  int start;
  int end;
  BigStr* part = nullptr;
  StackRoot _root0(&part);

  start = max(0, (this->start_pos - 4));
  end = min(len(this->s), (this->end_pos + 4));
  part = this->s->slice(start, end);
  return str_concat(this->msg, StrFormat(" (line %d, offset %d-%d: %r)", this->line_num, this->start_pos, this->end_pos, part));
}

BigStr* Decode::__str__() {
  return this->Message();
}

Encode::Encode(BigStr* msg) {
  this->msg = msg;
}

BigStr* Encode::Message() {
  return this->msg;
}

[[noreturn]] void e_usage(BigStr* msg, syntax_asdl::loc_t* location) {
  StackRoot _root0(&msg);
  StackRoot _root1(&location);

  throw Alloc<Usage>(msg, location);
}

[[noreturn]] void e_strict(BigStr* msg, syntax_asdl::loc_t* location) {
  StackRoot _root0(&msg);
  StackRoot _root1(&location);

  throw Alloc<Strict>(msg, location);
}

[[noreturn]] void p_die(BigStr* msg, syntax_asdl::loc_t* location) {
  StackRoot _root0(&msg);
  StackRoot _root1(&location);

  throw Alloc<Parse>(msg, location);
}

[[noreturn]] void e_die(BigStr* msg, syntax_asdl::loc_t* location) {
  StackRoot _root0(&msg);
  StackRoot _root1(&location);

  throw Alloc<FatalRuntime>(1, msg, location);
}

[[noreturn]] void e_die_status(int status, BigStr* msg, syntax_asdl::loc_t* location) {
  StackRoot _root0(&msg);
  StackRoot _root1(&location);

  throw Alloc<FatalRuntime>(status, msg, location);
}

}  // define namespace error

namespace executor {  // define

using id_kind_asdl::Id;
using option_asdl::builtin_i;
using runtime_asdl::RedirValue;
using runtime_asdl::trace;
using syntax_asdl::command;
using syntax_asdl::command_e;
using syntax_asdl::CommandSub;
using syntax_asdl::CompoundWord;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using value_asdl::value;
using value_asdl::value_e;
using error::e_die;
using error::e_die_status;
using mylib::print_stderr;

BigStr* LookupExecutable(BigStr* name, List<BigStr*>* path_dirs, bool exec_required) {
  BigStr* full_path = nullptr;
  bool found;
  StackRoot _root0(&name);
  StackRoot _root1(&path_dirs);
  StackRoot _root2(&full_path);

  if (len(name) == 0) {
    return nullptr;
  }
  if (str_contains(name, S_ckc)) {
    return path_stat::exists(name) ? name : nullptr;
  }
  for (ListIter<BigStr*> it(path_dirs); !it.Done(); it.Next()) {
    BigStr* path_dir = it.Value();
    StackRoot _for(&path_dir  );
    full_path = os_path::join(path_dir, name);
    if (exec_required) {
      found = posix::access(full_path, X_OK);
    }
    else {
      found = path_stat::exists(full_path);
    }
    if (found) {
      return full_path;
    }
  }
  return nullptr;
}

SearchPath::SearchPath(state::Mem* mem, optview::Exec* exec_opts) {
  this->mem = mem;
  this->cache = Alloc<Dict<BigStr*, BigStr*>>();
}

List<BigStr*>* SearchPath::_GetPath() {
  BigStr* s = nullptr;
  StackRoot _root0(&s);

  s = this->mem->env_config->Get(S_jip);
  if (s == nullptr) {
    return Alloc<List<BigStr*>>();
  }
  return s->split(S_fyj);
}

BigStr* SearchPath::LookupOne(BigStr* name, bool exec_required) {
  StackRoot _root0(&name);

  return LookupExecutable(name, this->_GetPath(), exec_required);
}

List<BigStr*>* SearchPath::LookupReflect(BigStr* name, bool do_all) {
  List<BigStr*>* results = nullptr;
  BigStr* full_path = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&results);
  StackRoot _root2(&full_path);

  if (len(name) == 0) {
    return Alloc<List<BigStr*>>();
  }
  if (str_contains(name, S_ckc)) {
    if (path_stat::exists(name)) {
      return NewList<BigStr*>(std::initializer_list<BigStr*>{name});
    }
    else {
      return Alloc<List<BigStr*>>();
    }
  }
  results = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(this->_GetPath()); !it.Done(); it.Next()) {
    BigStr* path_dir = it.Value();
    StackRoot _for(&path_dir  );
    full_path = os_path::join(path_dir, name);
    if (path_stat::exists(full_path)) {
      results->append(full_path);
      if (!do_all) {
        return results;
      }
    }
  }
  return results;
}

BigStr* SearchPath::CachedLookup(BigStr* name) {
  BigStr* full_path = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&full_path);

  if (dict_contains(this->cache, name)) {
    return this->cache->at(name);
  }
  full_path = this->LookupOne(name);
  if (full_path != nullptr) {
    this->cache->set(name, full_path);
  }
  return full_path;
}

void SearchPath::MaybeRemoveEntry(BigStr* name) {
  StackRoot _root0(&name);

  mylib::dict_erase(this->cache, name);
}

void SearchPath::ClearCache() {
  this->cache->clear();
}

List<BigStr*>* SearchPath::CachedCommands() {
  return this->cache->values();
}

_ProcessSubFrame::_ProcessSubFrame() {
  this->_to_wait = Alloc<List<process::Process*>>();
  this->_to_close = Alloc<List<int>>();
  this->_locs = Alloc<List<syntax_asdl::loc_t*>>();
  this->_modified = false;
}

bool _ProcessSubFrame::WasModified() {
  return this->_modified;
}

void _ProcessSubFrame::Append(process::Process* p, int fd, syntax_asdl::loc_t* status_loc) {
  StackRoot _root0(&p);
  StackRoot _root1(&status_loc);

  this->_modified = true;
  this->_to_wait->append(p);
  this->_to_close->append(fd);
  this->_locs->append(status_loc);
}

void _ProcessSubFrame::MaybeWaitOnProcessSubs(process::Waiter* waiter, runtime_asdl::StatusArray* status_array) {
  List<int>* codes = nullptr;
  List<syntax_asdl::loc_t*>* locs = nullptr;
  int i;
  int st;
  StackRoot _root0(&waiter);
  StackRoot _root1(&status_array);
  StackRoot _root2(&codes);
  StackRoot _root3(&locs);

  for (ListIter<int> it(this->_to_close); !it.Done(); it.Next()) {
    int fd = it.Value();
    posix::close(fd);
  }
  codes = Alloc<List<int>>();
  locs = Alloc<List<syntax_asdl::loc_t*>>();
  i = 0;
  for (ListIter<process::Process*> it(this->_to_wait); !it.Done(); it.Next(), ++i) {
    process::Process* p = it.Value();
    StackRoot _for(&p  );
    st = p->Wait(waiter);
    codes->append(st);
    locs->append(this->_locs->at(i));
  }
  status_array->codes = codes;
  status_array->locs = locs;
}
int IS_LAST_CMD = (1 << 1);
int NO_CALL_PROCS = (1 << 2);
int USE_DEFAULT_PATH = (1 << 3);
GLOBAL_LIST(DEFAULT_PATH, BigStr*, 6, {S_gFs COMMA S_gcD COMMA S_naE COMMA S_pEh COMMA S_zpA COMMA S_wht});

ShellExecutor::ShellExecutor(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, state::Procs* procs, hay_ysh::HayState* hay_state, Dict<int, vm::_Builtin*>* builtins, executor::SearchPath* search_path, process::ExternalProgram* ext_prog, process::Waiter* waiter, dev::Tracer* tracer, process::JobControl* job_control, process::JobList* job_list, process::FdState* fd_state, trap_osh::TrapState* trap_state, ui::ErrorFormatter* errfmt) : ::vm::_Executor() {
  this->mem = mem;
  this->exec_opts = exec_opts;
  this->mutable_opts = mutable_opts;
  this->procs = procs;
  this->hay_state = hay_state;
  this->builtins = builtins;
  this->search_path = search_path;
  this->ext_prog = ext_prog;
  this->waiter = waiter;
  this->tracer = tracer;
  this->multi_trace = tracer->multi_trace;
  this->job_control = job_control;
  this->job_list = job_list;
  this->fd_state = fd_state;
  this->trap_state = trap_state;
  this->errfmt = errfmt;
  this->process_sub_stack = Alloc<List<executor::_ProcessSubFrame*>>();
  this->clean_frame_pool = Alloc<List<executor::_ProcessSubFrame*>>();
  this->fg_pipeline = nullptr;
}

void ShellExecutor::CheckCircularDeps() {
}

process::Process* ShellExecutor::_MakeProcess(syntax_asdl::command_t* node, bool inherit_errexit, bool inherit_errtrace) {
  syntax_asdl::command_t* UP_node = nullptr;
  process::SubProgramThunk* thunk = nullptr;
  process::Process* p = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);
  StackRoot _root2(&thunk);
  StackRoot _root3(&p);

  UP_node = node;
  if (node->tag() == command_e::ControlFlow) {
    command::ControlFlow* node = static_cast<command::ControlFlow*>(UP_node);
    if (node->keyword->id != Id::ControlFlow_Exit) {
      e_die(StrFormat("Invalid control flow %r in pipeline / subshell / background", lexer::TokenVal(node->keyword)), node->keyword);
    }
  }
  thunk = Alloc<process::SubProgramThunk>(this->cmd_ev, node, this->trap_state, this->multi_trace, inherit_errexit, inherit_errtrace);
  p = Alloc<process::Process>(thunk, this->job_control, this->job_list, this->tracer);
  return p;
}

int ShellExecutor::RunBuiltin(int builtin_id, cmd_value::Argv* cmd_val) {
  vm::_Builtin* builtin_proc = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&builtin_proc);

  this->tracer->OnBuiltin(builtin_id, cmd_val->argv);
  builtin_proc = this->builtins->at(builtin_id);
  return this->RunBuiltinProc(builtin_proc, cmd_val);
}

int ShellExecutor::RunBuiltinProc(vm::_Builtin* builtin_proc, cmd_value::Argv* cmd_val) {
  List<IOError_OSError*>* io_errors = nullptr;
  int status;
  BigStr* arg0 = nullptr;
  StackRoot _root0(&builtin_proc);
  StackRoot _root1(&cmd_val);
  StackRoot _root2(&io_errors);
  StackRoot _root3(&arg0);

  io_errors = Alloc<List<IOError_OSError*>>();
  {  // with
    vm::ctx_FlushStdout ctx{io_errors};

    {  // with
      ui::ctx_Location ctx{this->errfmt, cmd_val->arg_locs->at(0)};

      try {
        status = builtin_proc->Run(cmd_val);
      }
      catch (IOError_OSError* e) {
        this->errfmt->PrintMessage(StrFormat("%s builtin I/O error: %s", cmd_val->argv->at(0), pyutil::strerror(e)), cmd_val->arg_locs->at(0));
        return 1;
      }
      catch (error::Usage* e) {
        arg0 = cmd_val->argv->at(0);
        this->errfmt->PrefixPrint(e->msg, StrFormat("%r ", arg0), e->location);
        return 2;
      }
    }
  }
  if (len(io_errors)) {
    this->errfmt->PrintMessage(StrFormat("%s builtin I/O error: %s", cmd_val->argv->at(0), pyutil::strerror(io_errors->at(0))), cmd_val->arg_locs->at(0));
    return 1;
  }
  return status;
}

int ShellExecutor::RunSimpleCommand(cmd_value::Argv* cmd_val, runtime_asdl::CommandStatus* cmd_st, int run_flags) {
  List<BigStr*>* argv = nullptr;
  syntax_asdl::loc_t* arg0_loc = nullptr;
  BigStr* arg0 = nullptr;
  int builtin_id;
  int status;
  bool call_procs;
  value_asdl::value_t* proc_val = nullptr;
  value_asdl::Obj* self_obj = nullptr;
  syntax_asdl::Token* disabled_tok = nullptr;
  value::BuiltinProc* builtin_proc = nullptr;
  vm::_Builtin* b = nullptr;
  value::Proc* proc = nullptr;
  Dict<BigStr*, BigStr*>* environ = nullptr;
  BigStr* argv0_path = nullptr;
  bool do_fork;
  process::ExternalThunk* thunk = nullptr;
  process::Process* p = nullptr;
  int pgid;
  process::SetPgid* change = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&cmd_st);
  StackRoot _root2(&argv);
  StackRoot _root3(&arg0_loc);
  StackRoot _root4(&arg0);
  StackRoot _root5(&proc_val);
  StackRoot _root6(&self_obj);
  StackRoot _root7(&disabled_tok);
  StackRoot _root8(&builtin_proc);
  StackRoot _root9(&b);
  StackRoot _root10(&proc);
  StackRoot _root11(&environ);
  StackRoot _root12(&argv0_path);
  StackRoot _root13(&thunk);
  StackRoot _root14(&p);
  StackRoot _root15(&change);

  argv = cmd_val->argv;
  if (len(cmd_val->arg_locs)) {
    arg0_loc = cmd_val->arg_locs->at(0);
  }
  else {
    arg0_loc = loc::Missing;
  }
  if (len(argv) == 0) {
    if (this->exec_opts->strict_argv()) {
      e_die(S_Awe, arg0_loc);
    }
    else {
      return 0;
    }
  }
  arg0 = argv->at(0);
  builtin_id = consts::LookupAssignBuiltin(arg0);
  if (builtin_id != consts::NO_INDEX) {
    this->errfmt->Print_(S_hlA, arg0_loc);
    return 1;
  }
  builtin_id = consts::LookupSpecialBuiltin(arg0);
  if (builtin_id != consts::NO_INDEX) {
    cmd_st->show_code = true;
    status = this->RunBuiltin(builtin_id, cmd_val);
    return status;
  }
  call_procs = !(run_flags & NO_CALL_PROCS);
  if (call_procs) {
    Tuple2<value_asdl::value_t*, value_asdl::Obj*> tup0 = this->procs->GetInvokable(arg0);
    proc_val = tup0.at0();
    self_obj = tup0.at1();
    cmd_val->self_obj = self_obj;
    if (proc_val != nullptr) {
      if (this->exec_opts->strict_errexit()) {
        disabled_tok = this->mutable_opts->ErrExitDisabledToken();
        if (disabled_tok) {
          this->errfmt->Print_(S_uFo, disabled_tok);
          this->errfmt->StderrLine(S_Aoo);
          e_die(S_dzk, arg0_loc);
        }
      }
      switch (proc_val->tag()) {
        case value_e::BuiltinProc: {
          builtin_proc = static_cast<value::BuiltinProc*>(proc_val);
          b = static_cast<vm::_Builtin*>(builtin_proc->builtin);
          status = this->RunBuiltinProc(b, cmd_val);
        }
          break;
        case value_e::Proc: {
          proc = static_cast<value::Proc*>(proc_val);
          {  // with
            dev::ctx_Tracer ctx{this->tracer, S_aFi, argv};

            status = this->cmd_ev->RunProc(proc, cmd_val);
          }
        }
          break;
        default: {
          assert(0);  // AssertionError
        }
      }
      return status;
    }
  }
  if (this->hay_state->Resolve(arg0)) {
    return this->RunBuiltin(builtin_i::haynode, cmd_val);
  }
  builtin_id = consts::LookupNormalBuiltin(arg0);
  if (this->exec_opts->_running_hay()) {
    if ((builtin_id == builtin_i::haynode || builtin_id == builtin_i::use || builtin_id == builtin_i::echo || builtin_id == builtin_i::write)) {
      cmd_st->show_code = true;
      return this->RunBuiltin(builtin_id, cmd_val);
    }
    this->errfmt->Print_(StrFormat("Unknown command %r while running hay", arg0), arg0_loc);
    return 127;
  }
  if (builtin_id != consts::NO_INDEX) {
    cmd_st->show_code = true;
    return this->RunBuiltin(builtin_id, cmd_val);
  }
  environ = this->mem->GetEnv();
  if (cmd_val->proc_args) {
    e_die(StrFormat("%r appears to be external. External commands don't accept typed args (OILS-ERR-200)", arg0), cmd_val->proc_args->typed_args->left);
  }
  if ((run_flags & USE_DEFAULT_PATH)) {
    argv0_path = LookupExecutable(arg0, DEFAULT_PATH);
  }
  else {
    argv0_path = this->search_path->CachedLookup(arg0);
  }
  if (argv0_path == nullptr) {
    this->errfmt->Print_(StrFormat("%r not found (OILS-ERR-100)", arg0), arg0_loc);
    return 127;
  }
  if (this->trap_state->ThisProcessHasTraps()) {
    do_fork = true;
  }
  else {
    do_fork = !cmd_val->is_last_cmd;
  }
  if (do_fork) {
    thunk = Alloc<process::ExternalThunk>(this->ext_prog, argv0_path, cmd_val, environ);
    p = Alloc<process::Process>(thunk, this->job_control, this->job_list, this->tracer);
    if (this->job_control->Enabled()) {
      if (this->fg_pipeline != nullptr) {
        pgid = this->fg_pipeline->ProcessGroupId();
        change = Alloc<process::SetPgid>(pgid, this->tracer);
        this->fg_pipeline = nullptr;
      }
      else {
        change = Alloc<process::SetPgid>(process::OWN_LEADER, this->tracer);
      }
      p->AddStateChange(change);
    }
    status = p->RunProcess(this->waiter, Alloc<trace::External>(cmd_val->argv));
    cmd_st->show_code = true;
    return status;
  }
  this->tracer->OnExec(cmd_val->argv);
  this->ext_prog->Exec(argv0_path, cmd_val, environ);
  assert(0);  // AssertionError
}

int ShellExecutor::RunBackgroundJob(syntax_asdl::command_t* node) {
  syntax_asdl::command_t* UP_node = nullptr;
  process::Pipeline* pi = nullptr;
  process::Process* p = nullptr;
  int last_pid;
  int job_id;
  int pid;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);
  StackRoot _root2(&pi);
  StackRoot _root3(&p);

  UP_node = node;
  if (UP_node->tag() == command_e::Pipeline) {
    command::Pipeline* node = static_cast<command::Pipeline*>(UP_node);
    pi = Alloc<process::Pipeline>(this->exec_opts->sigpipe_status_ok(), this->job_control, this->job_list, this->tracer);
    for (ListIter<syntax_asdl::command_t*> it(node->children); !it.Done(); it.Next()) {
      syntax_asdl::command_t* child = it.Value();
      StackRoot _for(&child    );
      p = this->_MakeProcess(child, true, this->exec_opts->errtrace());
      p->Init_ParentPipeline(pi);
      pi->Add(p);
    }
    pi->StartPipeline(this->waiter);
    pi->SetBackground();
    last_pid = pi->LastPid();
    this->mem->last_bg_pid = last_pid;
    job_id = this->job_list->AddJob(pi);
  }
  else {
    p = this->_MakeProcess(node, true, this->exec_opts->errtrace());
    if (this->job_control->Enabled()) {
      p->AddStateChange(Alloc<process::SetPgid>(process::OWN_LEADER, this->tracer));
    }
    p->SetBackground();
    pid = p->StartProcess(trace::Fork);
    this->mem->last_bg_pid = pid;
    job_id = this->job_list->AddJob(p);
  }
  if (this->exec_opts->interactive()) {
    print_stderr(StrFormat("[%%%d] PID %d Started", job_id, this->mem->last_bg_pid));
  }
  return 0;
}

void ShellExecutor::RunPipeline(command::Pipeline* node, runtime_asdl::CommandStatus* status_out) {
  process::Pipeline* pi = nullptr;
  List<syntax_asdl::loc_t*>* pipe_locs = nullptr;
  int n;
  syntax_asdl::command_t* child = nullptr;
  process::Process* p = nullptr;
  syntax_asdl::command_t* last_child = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&status_out);
  StackRoot _root2(&pi);
  StackRoot _root3(&pipe_locs);
  StackRoot _root4(&child);
  StackRoot _root5(&p);
  StackRoot _root6(&last_child);

  pi = Alloc<process::Pipeline>(this->exec_opts->sigpipe_status_ok(), this->job_control, this->job_list, this->tracer);
  pipe_locs = Alloc<List<syntax_asdl::loc_t*>>();
  n = len(node->children);
  for (int i = 0; i < (n - 1); ++i) {
    child = node->children->at(i);
    pipe_locs->append(Alloc<loc::Command>(child));
    p = this->_MakeProcess(child, true, this->exec_opts->errtrace());
    p->Init_ParentPipeline(pi);
    pi->Add(p);
  }
  last_child = node->children->at((n - 1));
  pi->AddLast((Alloc<Tuple2<cmd_eval::CommandEvaluator*, syntax_asdl::command_t*>>(this->cmd_ev, last_child)));
  pipe_locs->append(Alloc<loc::Command>(last_child));
  {  // with
    dev::ctx_Tracer ctx{this->tracer, S_zCk, nullptr};

    pi->StartPipeline(this->waiter);
    this->fg_pipeline = pi;
    status_out->pipe_status = pi->RunLastPart(this->waiter, this->fd_state);
    this->fg_pipeline = nullptr;
  }
  status_out->pipe_locs = pipe_locs;
}

int ShellExecutor::RunSubshell(syntax_asdl::command_t* node) {
  process::Process* p = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&p);

  p = this->_MakeProcess(node, true, this->exec_opts->errtrace());
  if (this->job_control->Enabled()) {
    p->AddStateChange(Alloc<process::SetPgid>(process::OWN_LEADER, this->tracer));
  }
  return p->RunProcess(this->waiter, trace::ForkWait);
}

Tuple2<int, BigStr*> ShellExecutor::CaptureStdout(syntax_asdl::command_t* node) {
  process::Process* p = nullptr;
  int r;
  int w;
  List<BigStr*>* chunks = nullptr;
  int n;
  int err_num;
  int status;
  BigStr* stdout_str = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&p);
  StackRoot _root2(&chunks);
  StackRoot _root3(&stdout_str);

  p = this->_MakeProcess(node, this->exec_opts->inherit_errexit(), this->exec_opts->errtrace());
  Tuple2<int, int> tup1 = posix::pipe();
  r = tup1.at0();
  w = tup1.at1();
  p->AddStateChange(Alloc<process::StdoutToPipe>(r, w));
  p->StartProcess(trace::CommandSub);
  chunks = Alloc<List<BigStr*>>();
  posix::close(w);
  while (true) {
    Tuple2<int, int> tup2 = pyos::Read(r, 4096, chunks);
    n = tup2.at0();
    err_num = tup2.at1();
    if (n < 0) {
      if (err_num == EINTR) {
        ;  // pass
      }
      else {
        e_die_status(2, StrFormat("Oils I/O error (read): %s", posix::strerror(err_num)));
      }
    }
    else {
      if (n == 0) {
        break;
      }
    }
  }
  posix::close(r);
  status = p->Wait(this->waiter);
  stdout_str = S_Aoo->join(chunks)->rstrip(S_nfs);
  return Tuple2<int, BigStr*>(status, stdout_str);
}

BigStr* ShellExecutor::RunCommandSub(syntax_asdl::CommandSub* cs_part) {
  BigStr* why = nullptr;
  syntax_asdl::command_t* node = nullptr;
  command::Redirect* redir_node = nullptr;
  syntax_asdl::Token* tok = nullptr;
  syntax_asdl::CompoundWord* cat_word = nullptr;
  syntax_asdl::Token* blame_tok = nullptr;
  command::Simple* simple = nullptr;
  int status;
  BigStr* stdout_str = nullptr;
  BigStr* msg = nullptr;
  StackRoot _root0(&cs_part);
  StackRoot _root1(&why);
  StackRoot _root2(&node);
  StackRoot _root3(&redir_node);
  StackRoot _root4(&tok);
  StackRoot _root5(&cat_word);
  StackRoot _root6(&blame_tok);
  StackRoot _root7(&simple);
  StackRoot _root8(&stdout_str);
  StackRoot _root9(&msg);

  if (!this->exec_opts->_allow_command_sub()) {
    if (!this->exec_opts->_allow_process_sub()) {
      why = S_pmj;
    }
    else {
      why = S_tlu;
    }
    e_die(StrFormat("Command subs not allowed here because %s", why), Alloc<loc::WordPart>(cs_part));
  }
  node = cs_part->child;
  if (node->tag() == command_e::Redirect) {
    redir_node = static_cast<command::Redirect*>(node);
    if ((len(redir_node->redirects) == 1 and (redir_node->redirects->at(0)->op->id == Id::Redir_Less and redir_node->child->tag() == command_e::NoOp))) {
      tok = lexer::DummyToken(Id::Lit_Chars, S_swp);
      cat_word = Alloc<CompoundWord>(NewList<syntax_asdl::word_part_t*>(std::initializer_list<syntax_asdl::word_part_t*>{tok}));
      blame_tok = redir_node->redirects->at(0)->op;
      simple = Alloc<command::Simple>(blame_tok, Alloc<List<syntax_asdl::EnvPair*>>(), NewList<syntax_asdl::word_t*>(std::initializer_list<syntax_asdl::word_t*>{cat_word}), nullptr, nullptr, false);
      redir_node->child = simple;
    }
  }
  Tuple2<int, BigStr*> tup3 = this->CaptureStdout(node);
  status = tup3.at0();
  stdout_str = tup3.at1();
  if (this->exec_opts->command_sub_errexit()) {
    if (status != 0) {
      msg = StrFormat("Command Sub exited with status %d", status);
      throw Alloc<error::ErrExit>(status, msg, Alloc<loc::WordPart>(cs_part));
    }
  }
  else {
    this->cmd_ev->check_command_sub_status = true;
    this->mem->SetLastStatus(status);
  }
  return stdout_str;
}

BigStr* ShellExecutor::RunProcessSub(syntax_asdl::CommandSub* cs_part) {
  loc::WordPart* cs_loc = nullptr;
  process::Process* p = nullptr;
  int r;
  int w;
  int op_id;
  process::ChildStateChange* redir = nullptr;
  executor::_ProcessSubFrame* ps_frame = nullptr;
  StackRoot _root0(&cs_part);
  StackRoot _root1(&cs_loc);
  StackRoot _root2(&p);
  StackRoot _root3(&redir);
  StackRoot _root4(&ps_frame);

  cs_loc = Alloc<loc::WordPart>(cs_part);
  if (!this->exec_opts->_allow_process_sub()) {
    e_die(S_jbj, cs_loc);
  }
  p = this->_MakeProcess(cs_part->child, true, this->exec_opts->errtrace());
  Tuple2<int, int> tup4 = posix::pipe();
  r = tup4.at0();
  w = tup4.at1();
  op_id = cs_part->left_token->id;
  if (op_id == Id::Left_ProcSubIn) {
    redir = Alloc<process::StdoutToPipe>(r, w);
  }
  else {
    if (op_id == Id::Left_ProcSubOut) {
      redir = Alloc<process::StdinFromPipe>(r, w);
    }
    else {
      assert(0);  // AssertionError
    }
  }
  p->AddStateChange(redir);
  if (this->job_control->Enabled()) {
    p->AddStateChange(Alloc<process::SetPgid>(process::OWN_LEADER, this->tracer));
  }
  p->StartProcess(trace::ProcessSub);
  ps_frame = this->process_sub_stack->at(-1);
  if (op_id == Id::Left_ProcSubIn) {
    posix::close(w);
    ps_frame->Append(p, r, cs_loc);
  }
  else {
    if (op_id == Id::Left_ProcSubOut) {
      posix::close(r);
      ps_frame->Append(p, w, cs_loc);
    }
    else {
      assert(0);  // AssertionError
    }
  }
  if (op_id == Id::Left_ProcSubIn) {
    return StrFormat("/dev/fd/%d", r);
  }
  else {
    if (op_id == Id::Left_ProcSubOut) {
      return StrFormat("/dev/fd/%d", w);
    }
    else {
      assert(0);  // AssertionError
    }
  }
}

void ShellExecutor::PushRedirects(List<runtime_asdl::RedirValue*>* redirects, List<IOError_OSError*>* err_out) {
  StackRoot _root0(&redirects);
  StackRoot _root1(&err_out);

  if (len(redirects) == 0) {
    return ;
  }
  this->fd_state->Push(redirects, err_out);
}

void ShellExecutor::PopRedirects(int num_redirects, List<IOError_OSError*>* err_out) {
  StackRoot _root0(&err_out);

  if (num_redirects == 0) {
    return ;
  }
  this->fd_state->Pop(err_out);
}

void ShellExecutor::PushProcessSub() {
  executor::_ProcessSubFrame* new_frame = nullptr;
  StackRoot _root0(&new_frame);

  if (len(this->clean_frame_pool)) {
    new_frame = this->clean_frame_pool->pop();
  }
  else {
    new_frame = Alloc<_ProcessSubFrame>();
  }
  this->process_sub_stack->append(new_frame);
}

void ShellExecutor::PopProcessSub(runtime_asdl::StatusArray* compound_st) {
  executor::_ProcessSubFrame* frame = nullptr;
  StackRoot _root0(&compound_st);
  StackRoot _root1(&frame);

  frame = this->process_sub_stack->pop();
  if (frame->WasModified()) {
    frame->MaybeWaitOnProcessSubs(this->waiter, compound_st);
  }
  else {
    this->clean_frame_pool->append(frame);
  }
}

}  // define namespace executor

namespace main_loop {  // define

using syntax_asdl::command;
using syntax_asdl::command_t;
using syntax_asdl::parse_result;
using syntax_asdl::parse_result_e;
using mylib::print_stderr;

ctx_Descriptors::ctx_Descriptors(List<int>* fds) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->fds)));
  this->saved0 = process::SaveFd(0);
  this->saved1 = process::SaveFd(1);
  this->saved2 = process::SaveFd(2);
  posix::dup2(fds->at(0), 0);
  posix::dup2(fds->at(1), 1);
  posix::dup2(fds->at(2), 2);
  this->fds = fds;
}

ctx_Descriptors::~ctx_Descriptors() {
  posix::dup2(this->saved0, 0);
  posix::dup2(this->saved1, 1);
  posix::dup2(this->saved2, 2);
  posix::close(this->saved0);
  posix::close(this->saved1);
  posix::close(this->saved2);
  posix::close(this->fds->at(0));
  posix::close(this->fds->at(1));
  posix::close(this->fds->at(2));
  gHeap.PopRoot();
}

void fanos_log(BigStr* msg) {
  StackRoot _root0(&msg);

  print_stderr(StrFormat("[FANOS] %s", msg));
}

void ShowDescriptorState(BigStr* label) {
  StackRoot _root0(&label);

}

Headless::Headless(cmd_eval::CommandEvaluator* cmd_ev, parse_lib::ParseContext* parse_ctx, ui::ErrorFormatter* errfmt) {
  this->cmd_ev = cmd_ev;
  this->parse_ctx = parse_ctx;
  this->errfmt = errfmt;
}

int Headless::Loop() {
  try {
    return this->_Loop();
  }
  catch (ValueError* e) {
    fanos::send(1, StrFormat("ERROR %s", e));
    return 1;
  }
}

BigStr* Headless::EVAL(BigStr* arg) {
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  int unused_status;
  (void)unused_status;
  StackRoot _root0(&arg);
  StackRoot _root1(&line_reader);
  StackRoot _root2(&c_parser);

  line_reader = reader::StringLineReader(arg, this->parse_ctx->arena);
  c_parser = this->parse_ctx->MakeOshParser(line_reader);
  unused_status = Batch(this->cmd_ev, c_parser, this->errfmt, 0);
  return S_Aoo;
}

int Headless::_Loop() {
  List<int>* fd_out = nullptr;
  BigStr* blob = nullptr;
  List<BigStr*>* bs = nullptr;
  BigStr* command = nullptr;
  BigStr* arg = nullptr;
  BigStr* reply = nullptr;
  StackRoot _root0(&fd_out);
  StackRoot _root1(&blob);
  StackRoot _root2(&bs);
  StackRoot _root3(&command);
  StackRoot _root4(&arg);
  StackRoot _root5(&reply);

  fanos_log(S_rgn);
  fd_out = Alloc<List<int>>();
  while (true) {
    try {
      blob = fanos::recv(0, fd_out);
    }
    catch (ValueError* e) {
      fanos_log(StrFormat("protocol error: %s", e));
      throw;
    }
    if (blob == nullptr) {
      fanos_log(S_nhc);
      break;
    }
    fanos_log(StrFormat("received blob %r", blob));
    if (str_contains(blob, S_yfw)) {
      bs = blob->split(S_yfw, 1);
      command = bs->at(0);
      arg = bs->at(1);
    }
    else {
      command = blob;
      arg = S_Aoo;
    }
    if (str_equals(command, S_Cqq)) {
      reply = str(posix::getpid());
    }
    else {
      if (str_equals(command, S_crw)) {
        if (len(fd_out) != 3) {
          throw Alloc<ValueError>(S_rfk);
        }
        for (ListIter<int> it(fd_out); !it.Done(); it.Next()) {
          int fd = it.Value();
          fanos_log(StrFormat("received descriptor %d", fd));
        }
        {  // with
          ctx_Descriptors ctx{fd_out};

          reply = this->EVAL(arg);
        }
      }
      else {
        if (str_equals(command, S_Agb)) {
          reply = S_Ejt;
        }
        else {
          fanos_log(StrFormat("Invalid command %r", command));
          throw Alloc<ValueError>(StrFormat("Invalid command %r", command));
        }
      }
    }
    fanos::send(1, StrFormat("OK %s", reply));
    fd_out->clear();
  }
  return 0;
}

int Interactive(arg_types::main* flag, cmd_eval::CommandEvaluator* cmd_ev, cmd_parse::CommandParser* c_parser, comp_ui::_IDisplay* display, prompt::UserPlugin* prompt_plugin, process::Waiter* waiter, ui::ErrorFormatter* errfmt) {
  int status;
  bool done;
  bool quit;
  syntax_asdl::parse_result_t* result = nullptr;
  syntax_asdl::parse_result_t* UP_result = nullptr;
  syntax_asdl::command_t* node = nullptr;
  bool is_return;
  StackRoot _root0(&flag);
  StackRoot _root1(&cmd_ev);
  StackRoot _root2(&c_parser);
  StackRoot _root3(&display);
  StackRoot _root4(&prompt_plugin);
  StackRoot _root5(&waiter);
  StackRoot _root6(&errfmt);
  StackRoot _root7(&result);
  StackRoot _root8(&UP_result);
  StackRoot _root9(&node);

  status = 0;
  done = false;
  while (!done) {
    mylib::MaybeCollect();
    while (true) {
      quit = false;
      prompt_plugin->Run();
      try {
        result = c_parser->ParseInteractiveLine();
        UP_result = result;
        switch (result->tag()) {
          case parse_result_e::EmptyLine: {
            display->EraseLines();
            waiter->PollNotifications();
            quit = true;
          }
            break;
          case parse_result_e::Eof: {
            display->EraseLines();
            done = true;
            quit = true;
          }
            break;
          case parse_result_e::Node: {
            parse_result::Node* result = static_cast<parse_result::Node*>(UP_result);
            node = result->cmd;
          }
            break;
          default: {
            assert(0);  // AssertionError
          }
        }
      }
      catch (util::HistoryError* e) {
        display->EraseLines();
        print(e->UserErrorString());
        quit = true;
      }
      catch (error::Parse* e) {
        display->EraseLines();
        errfmt->PrettyPrintError(e);
        status = 2;
        cmd_ev->mem->SetLastStatus(status);
        quit = true;
      }
      catch (KeyboardInterrupt*) {
        print(S_Aoo);
        display->EraseLines();
        quit = true;
      }
      if (quit) {
        break;
      }
      display->EraseLines();
      if (cmd_ev->exec_opts->noexec()) {
        ui::PrintAst(node, flag);
        break;
      }
      try {
        Tuple2<bool, bool> tup0 = cmd_ev->ExecuteAndCatch(node, 0);
        is_return = tup0.at0();
      }
      catch (KeyboardInterrupt*) {
        is_return = false;
        display->EraseLines();
        status = 130;
        cmd_ev->mem->SetLastStatus(status);
        break;
      }
      status = cmd_ev->LastStatus();
      waiter->PollNotifications();
      if (is_return) {
        done = true;
        break;
      }
      break;
    }
    c_parser->arena->DiscardLines();
    cmd_ev->RunPendingTraps();
    c_parser->Reset();
    c_parser->ResetInputObjects();
    display->Reset();
    if (flag->print_status) {
      print(StrFormat("STATUS\t%r", status));
    }
  }
  return status;
}

int Batch(cmd_eval::CommandEvaluator* cmd_ev, cmd_parse::CommandParser* c_parser, ui::ErrorFormatter* errfmt, int cmd_flags) {
  int status;
  syntax_asdl::command_t* node = nullptr;
  bool is_return;
  bool is_fatal;
  StackRoot _root0(&cmd_ev);
  StackRoot _root1(&c_parser);
  StackRoot _root2(&errfmt);
  StackRoot _root3(&node);

  status = 0;
  while (true) {
    DTRACE_PROBE(main_loop, Batch_parse_enter);
    try {
      node = c_parser->ParseLogicalLine();
      if (node == nullptr) {
        c_parser->CheckForPendingHereDocs();
        break;
      }
    }
    catch (error::Parse* e) {
      errfmt->PrettyPrintError(e);
      status = 2;
      break;
    }
    c_parser->arena->DiscardLines();
    if (((cmd_flags & cmd_eval::IsMainProgram) and c_parser->line_reader->LastLineHint())) {
      cmd_flags |= cmd_eval::OptimizeSubshells;
      if (!cmd_ev->exec_opts->verbose_errexit()) {
        cmd_flags |= cmd_eval::MarkLastCommands;
      }
    }
    DTRACE_PROBE(main_loop, Batch_parse_exit);
    DTRACE_PROBE(main_loop, Batch_execute_enter);
    Tuple2<bool, bool> tup1 = cmd_ev->ExecuteAndCatch(node, cmd_flags);
    is_return = tup1.at0();
    is_fatal = tup1.at1();
    status = cmd_ev->LastStatus();
    if ((is_return or is_fatal)) {
      break;
    }
    DTRACE_PROBE(main_loop, Batch_execute_exit);
    DTRACE_PROBE(main_loop, Batch_collect_enter);
    mylib::MaybeCollect();
    DTRACE_PROBE(main_loop, Batch_collect_exit);
  }
  return status;
}

syntax_asdl::command_t* ParseWholeFile(cmd_parse::CommandParser* c_parser) {
  List<syntax_asdl::command_t*>* children = nullptr;
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&c_parser);
  StackRoot _root1(&children);
  StackRoot _root2(&node);

  children = Alloc<List<syntax_asdl::command_t*>>();
  while (true) {
    node = c_parser->ParseLogicalLine();
    if (node == nullptr) {
      c_parser->CheckForPendingHereDocs();
      break;
    }
    children->append(node);
    mylib::MaybeCollect();
  }
  if (len(children) == 1) {
    return children->at(0);
  }
  else {
    return Alloc<command::CommandList>(children);
  }
}

}  // define namespace main_loop

namespace num {  // define

using value_asdl::value;

value::Int* ToBig(int i) {
  return Alloc<value::Int>(mops::IntWiden(i));
}

mops::BigInt Exponent(mops::BigInt x, mops::BigInt y) {
  int y_int;
  mops::BigInt result;
  y_int = mops::BigTruncate(y);
  result = mops::BigInt(1);
  for (int i = 0; i < y_int; ++i) {
    result = mops::Mul(result, x);
  }
  return result;
}

}  // define namespace num

namespace process {  // define

using id_kind_asdl::Id;
using runtime_asdl::job_state_e;
using runtime_asdl::job_state_t;
using runtime_asdl::job_state_str;
using runtime_asdl::wait_status;
using runtime_asdl::wait_status_t;
using runtime_asdl::RedirValue;
using runtime_asdl::redirect_arg;
using runtime_asdl::redirect_arg_e;
using runtime_asdl::trace;
using runtime_asdl::trace_t;
using syntax_asdl::loc_t;
using syntax_asdl::redir_loc;
using syntax_asdl::redir_loc_e;
using syntax_asdl::redir_loc_t;
using value_asdl::value;
using value_asdl::value_e;
using error::e_die;
using mylib::print_stderr;
int NO_FD = -1;
int _SHELL_MIN_FD = 100;
int STYLE_DEFAULT = 0;
int STYLE_LONG = 1;
int STYLE_PID_ONLY = 2;
GLOBAL_LIST(CURRENT_JOB_SPECS, BigStr*, 4, {S_Aoo COMMA S_dkr COMMA S_bpf COMMA S_Dia});

ctx_FileCloser::ctx_FileCloser(mylib::LineReader* f) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->f)));
  this->f = f;
}

ctx_FileCloser::~ctx_FileCloser() {
  this->f->close();
  gHeap.PopRoot();
}

void InitInteractiveShell(iolib::SignalSafe* signal_safe) {
  StackRoot _root0(&signal_safe);

  iolib::sigaction(SIGQUIT, SIG_IGN);
  iolib::sigaction(SIGTSTP, SIG_IGN);
  iolib::sigaction(SIGTTOU, SIG_IGN);
  iolib::sigaction(SIGTTIN, SIG_IGN);
  iolib::RegisterSignalInterest(SIGWINCH);
}

int SaveFd(int fd) {
  int saved;
  saved = fcntl_::fcntl(fd, F_DUPFD, _SHELL_MIN_FD);
  return saved;
}

_RedirFrame::_RedirFrame(int saved_fd, int orig_fd, bool forget) {
  this->saved_fd = saved_fd;
  this->orig_fd = orig_fd;
  this->forget = forget;
}

_FdFrame::_FdFrame() {
  this->saved = Alloc<List<process::_RedirFrame*>>();
  this->need_wait = Alloc<List<process::Process*>>();
}

void _FdFrame::Forget() {
  for (ReverseListIter<process::_RedirFrame*> it(this->saved); !it.Done(); it.Next()) {
    process::_RedirFrame* rf = it.Value();
    StackRoot _for(&rf  );
    if ((rf->saved_fd != NO_FD and rf->forget)) {
      posix::close(rf->saved_fd);
    }
  }
  this->saved->clear();
  this->need_wait->clear();
}

FdState::FdState(ui::ErrorFormatter* errfmt, process::JobControl* job_control, process::JobList* job_list, state::Mem* mem, dev::Tracer* tracer, process::Waiter* waiter, optview::Exec* exec_opts) {
  this->errfmt = errfmt;
  this->job_control = job_control;
  this->job_list = job_list;
  this->cur_frame = Alloc<_FdFrame>();
  this->stack = NewList<process::_FdFrame*>(std::initializer_list<process::_FdFrame*>{this->cur_frame});
  this->mem = mem;
  this->tracer = tracer;
  this->waiter = waiter;
  this->exec_opts = exec_opts;
}

mylib::LineReader* FdState::Open(BigStr* path) {
  int fd_mode;
  mylib::File* f = nullptr;
  StackRoot _root0(&path);
  StackRoot _root1(&f);

  fd_mode = O_RDONLY;
  f = this->_Open(path, S_nAr_1, fd_mode);
  return static_cast<mylib::LineReader*>(f);
}

mylib::Writer* FdState::OpenForWrite(BigStr* path) {
  int fd_mode;
  mylib::File* f = nullptr;
  StackRoot _root0(&path);
  StackRoot _root1(&f);

  fd_mode = (O_CREAT | O_RDWR);
  f = this->_Open(path, S_pfC, fd_mode);
  return reinterpret_cast<mylib::Writer*>(f);
}

mylib::File* FdState::_Open(BigStr* path, BigStr* c_mode, int fd_mode) {
  int fd;
  int new_fd;
  mylib::File* f = nullptr;
  StackRoot _root0(&path);
  StackRoot _root1(&c_mode);
  StackRoot _root2(&f);

  fd = posix::open(path, fd_mode, 438);
  new_fd = SaveFd(fd);
  posix::close(fd);
  f = posix::fdopen(new_fd, c_mode);
  return f;
}

void FdState::_WriteFdToMem(BigStr* fd_name, int fd) {
  StackRoot _root0(&fd_name);

  if (this->mem) {
    state::OshLanguageSetValue(this->mem, location::LName(fd_name), Alloc<value::Str>(str(fd)));
  }
}

int FdState::_ReadFdFromMem(BigStr* fd_name) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&fd_name);
  StackRoot _root1(&val);

  val = this->mem->GetValue(fd_name);
  if (val->tag() == value_e::Str) {
    try {
      return to_int(static_cast<value::Str*>(val)->s);
    }
    catch (ValueError*) {
      return NO_FD;
    }
  }
  return NO_FD;
}

bool FdState::_PushSave(int fd) {
  bool ok;
  int new_fd;
  ok = true;
  try {
    new_fd = SaveFd(fd);
  }
  catch (IOError_OSError* e) {
    ok = false;
    if (e->errno_ != EBADF) {
      throw;
    }
  }
  if (ok) {
    posix::close(fd);
    fcntl_::fcntl(new_fd, F_SETFD, FD_CLOEXEC);
    this->cur_frame->saved->append(Alloc<_RedirFrame>(new_fd, fd, true));
  }
  else {
    this->_PushClose(fd);
  }
  return ok;
}

int FdState::_PushDup(int fd1, syntax_asdl::redir_loc_t* blame_loc) {
  syntax_asdl::redir_loc_t* UP_loc = nullptr;
  BigStr* fd2_name = nullptr;
  int new_fd;
  int fd2;
  bool need_restore;
  process::_RedirFrame* rf = nullptr;
  StackRoot _root0(&blame_loc);
  StackRoot _root1(&UP_loc);
  StackRoot _root2(&fd2_name);
  StackRoot _root3(&rf);

  UP_loc = blame_loc;
  if (blame_loc->tag() == redir_loc_e::VarName) {
    fd2_name = static_cast<redir_loc::VarName*>(UP_loc)->name;
    try {
      new_fd = fcntl_::fcntl(fd1, F_DUPFD, _SHELL_MIN_FD);
    }
    catch (IOError_OSError* e) {
      if (e->errno_ == EBADF) {
        print_stderr(StrFormat("F_DUPFD fd %d: %s", fd1, pyutil::strerror(e)));
        return NO_FD;
      }
      else {
        throw;
      }
    }
    this->_WriteFdToMem(fd2_name, new_fd);
  }
  else {
    if (blame_loc->tag() == redir_loc_e::Fd) {
      fd2 = static_cast<redir_loc::Fd*>(UP_loc)->fd;
      if (fd1 == fd2) {
        return NO_FD;
      }
      try {
        fcntl_::fcntl(fd1, F_GETFD);
      }
      catch (IOError_OSError* e) {
        print_stderr(StrFormat("F_GETFD fd %d: %s", fd1, pyutil::strerror(e)));
        throw;
      }
      need_restore = this->_PushSave(fd2);
      try {
        posix::dup2(fd1, fd2);
      }
      catch (IOError_OSError* e) {
        print_stderr(StrFormat("dup2(%d, %d): %s", fd1, fd2, pyutil::strerror(e)));
        if (need_restore) {
          rf = this->cur_frame->saved->pop();
          posix::dup2(rf->saved_fd, rf->orig_fd);
          posix::close(rf->saved_fd);
        }
        throw;
      }
      new_fd = fd2;
    }
    else {
      assert(0);  // AssertionError
    }
  }
  return new_fd;
}

bool FdState::_PushCloseFd(syntax_asdl::redir_loc_t* blame_loc) {
  syntax_asdl::redir_loc_t* UP_loc = nullptr;
  BigStr* fd_name = nullptr;
  int fd;
  StackRoot _root0(&blame_loc);
  StackRoot _root1(&UP_loc);
  StackRoot _root2(&fd_name);

  UP_loc = blame_loc;
  if (blame_loc->tag() == redir_loc_e::VarName) {
    fd_name = static_cast<redir_loc::VarName*>(UP_loc)->name;
    fd = this->_ReadFdFromMem(fd_name);
    if (fd == NO_FD) {
      return false;
    }
  }
  else {
    if (blame_loc->tag() == redir_loc_e::Fd) {
      fd = static_cast<redir_loc::Fd*>(UP_loc)->fd;
    }
    else {
      assert(0);  // AssertionError
    }
  }
  this->_PushSave(fd);
  return true;
}

void FdState::_PushClose(int fd) {
  this->cur_frame->saved->append(Alloc<_RedirFrame>(NO_FD, fd, false));
}

void FdState::_PushWait(process::Process* proc) {
  StackRoot _root0(&proc);

  this->cur_frame->need_wait->append(proc);
}

void FdState::_ApplyRedirect(runtime_asdl::RedirValue* r) {
  runtime_asdl::redirect_arg_t* arg = nullptr;
  runtime_asdl::redirect_arg_t* UP_arg = nullptr;
  int noclobber_mode;
  int mode;
  int open_fd;
  BigStr* extra = nullptr;
  int new_fd;
  syntax_asdl::redir_loc_t* UP_loc = nullptr;
  int fd;
  int read_fd;
  int write_fd;
  process::_HereDocWriterThunk* thunk = nullptr;
  bool start_process;
  process::Process* here_proc = nullptr;
  StackRoot _root0(&r);
  StackRoot _root1(&arg);
  StackRoot _root2(&UP_arg);
  StackRoot _root3(&extra);
  StackRoot _root4(&UP_loc);
  StackRoot _root5(&thunk);
  StackRoot _root6(&here_proc);

  arg = r->arg;
  UP_arg = arg;
  switch (arg->tag()) {
    case redirect_arg_e::Path: {
      redirect_arg::Path* arg = static_cast<redirect_arg::Path*>(UP_arg);
      noclobber_mode = this->exec_opts->noclobber() ? O_EXCL : 0;
      if ((r->op_id == Id::Redir_Great || r->op_id == Id::Redir_AndGreat)) {
        mode = (((O_CREAT | O_WRONLY) | O_TRUNC) | noclobber_mode);
      }
      else {
        if (r->op_id == Id::Redir_Clobber) {
          mode = ((O_CREAT | O_WRONLY) | O_TRUNC);
        }
        else {
          if ((r->op_id == Id::Redir_DGreat || r->op_id == Id::Redir_AndDGreat)) {
            mode = (((O_CREAT | O_WRONLY) | O_APPEND) | noclobber_mode);
          }
          else {
            if (r->op_id == Id::Redir_Less) {
              mode = O_RDONLY;
            }
            else {
              if (r->op_id == Id::Redir_LessGreat) {
                mode = (O_CREAT | O_RDWR);
              }
              else {
                FAIL(kNotImplemented);  // Python NotImplementedError
              }
            }
          }
        }
      }
      try {
        open_fd = posix::open(arg->filename, mode, 438);
      }
      catch (IOError_OSError* e) {
        if ((e->errno_ == EEXIST and this->exec_opts->noclobber())) {
          extra = S_oBy;
        }
        else {
          extra = S_Aoo;
        }
        this->errfmt->Print_(StrFormat("Can't open %r: %s%s", arg->filename, pyutil::strerror(e), extra), r->op_loc);
        throw;
      }
      new_fd = this->_PushDup(open_fd, r->loc);
      if (new_fd != NO_FD) {
        posix::close(open_fd);
      }
      if ((r->op_id == Id::Redir_AndGreat || r->op_id == Id::Redir_AndDGreat)) {
        this->_PushDup(new_fd, Alloc<redir_loc::Fd>(2));
      }
    }
      break;
    case redirect_arg_e::CopyFd: {
      redirect_arg::CopyFd* arg = static_cast<redirect_arg::CopyFd*>(UP_arg);
      if (r->op_id == Id::Redir_GreatAnd) {
        this->_PushDup(arg->target_fd, r->loc);
      }
      else {
        if (r->op_id == Id::Redir_LessAnd) {
          this->_PushDup(arg->target_fd, r->loc);
        }
        else {
          FAIL(kNotImplemented);  // Python NotImplementedError
        }
      }
    }
      break;
    case redirect_arg_e::MoveFd: {
      redirect_arg::MoveFd* arg = static_cast<redirect_arg::MoveFd*>(UP_arg);
      new_fd = this->_PushDup(arg->target_fd, r->loc);
      if (new_fd != NO_FD) {
        posix::close(arg->target_fd);
        UP_loc = r->loc;
        if (r->loc->tag() == redir_loc_e::Fd) {
          fd = static_cast<redir_loc::Fd*>(UP_loc)->fd;
        }
        else {
          fd = NO_FD;
        }
        this->cur_frame->saved->append(Alloc<_RedirFrame>(new_fd, fd, false));
      }
    }
      break;
    case redirect_arg_e::CloseFd: {
      this->_PushCloseFd(r->loc);
    }
      break;
    case redirect_arg_e::HereDoc: {
      redirect_arg::HereDoc* arg = static_cast<redirect_arg::HereDoc*>(UP_arg);
      Tuple2<int, int> tup0 = posix::pipe();
      read_fd = tup0.at0();
      write_fd = tup0.at1();
      this->_PushDup(read_fd, r->loc);
      this->_PushClose(read_fd);
      thunk = Alloc<_HereDocWriterThunk>(write_fd, arg->body);
      start_process = len(arg->body) > 4096;
      if (start_process) {
        here_proc = Alloc<Process>(thunk, this->job_control, this->job_list, this->tracer);
        here_proc->StartProcess(trace::HereDoc);
        this->_PushWait(here_proc);
        posix::close(write_fd);
      }
      else {
        posix::write(write_fd, arg->body);
        posix::close(write_fd);
      }
    }
      break;
  }
}

void FdState::Push(List<runtime_asdl::RedirValue*>* redirects, List<IOError_OSError*>* err_out) {
  process::_FdFrame* new_frame = nullptr;
  StackRoot _root0(&redirects);
  StackRoot _root1(&err_out);
  StackRoot _root2(&new_frame);

  new_frame = Alloc<_FdFrame>();
  this->stack->append(new_frame);
  this->cur_frame = new_frame;
  for (ListIter<runtime_asdl::RedirValue*> it(redirects); !it.Done(); it.Next()) {
    runtime_asdl::RedirValue* r = it.Value();
    StackRoot _for(&r  );
    {  // with
      ui::ctx_Location ctx{this->errfmt, r->op_loc};

      try {
        this->_ApplyRedirect(r);
      }
      catch (IOError_OSError* e) {
        err_out->append(e);
        this->Pop(err_out);
        return ;
      }
    }
  }
}

bool FdState::PushStdinFromPipe(int r) {
  process::_FdFrame* new_frame = nullptr;
  StackRoot _root0(&new_frame);

  new_frame = Alloc<_FdFrame>();
  this->stack->append(new_frame);
  this->cur_frame = new_frame;
  this->_PushDup(r, Alloc<redir_loc::Fd>(0));
  return true;
}

void FdState::Pop(List<IOError_OSError*>* err_out) {
  process::_FdFrame* frame = nullptr;
  int unused_status;
  (void)unused_status;
  StackRoot _root0(&err_out);
  StackRoot _root1(&frame);

  frame = this->stack->pop();
  for (ReverseListIter<process::_RedirFrame*> it(frame->saved); !it.Done(); it.Next()) {
    process::_RedirFrame* rf = it.Value();
    StackRoot _for(&rf  );
    if (rf->saved_fd == NO_FD) {
      try {
        posix::close(rf->orig_fd);
      }
      catch (IOError_OSError* e) {
        err_out->append(e);
        mylib::print_stderr(StrFormat("Error closing descriptor %d: %s", rf->orig_fd, pyutil::strerror(e)));
        return ;
      }
    }
    else {
      try {
        posix::dup2(rf->saved_fd, rf->orig_fd);
      }
      catch (IOError_OSError* e) {
        err_out->append(e);
        mylib::print_stderr(StrFormat("dup2(%d, %d) error: %s", rf->saved_fd, rf->orig_fd, pyutil::strerror(e)));
        return ;
      }
      posix::close(rf->saved_fd);
    }
  }
  for (ListIter<process::Process*> it(frame->need_wait); !it.Done(); it.Next()) {
    process::Process* proc = it.Value();
    StackRoot _for(&proc  );
    unused_status = proc->Wait(this->waiter);
  }
}

void FdState::MakePermanent() {
  this->cur_frame->Forget();
}

ChildStateChange::ChildStateChange() {
  ;  // pass
}

void ChildStateChange::Apply() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

void ChildStateChange::ApplyFromParent(process::Process* proc) {
  StackRoot _root0(&proc);

  ;  // pass
}

StdinFromPipe::StdinFromPipe(int pipe_read_fd, int w) {
  this->r = pipe_read_fd;
  this->w = w;
}

void StdinFromPipe::Apply() {
  posix::dup2(this->r, 0);
  posix::close(this->r);
  posix::close(this->w);
}

StdoutToPipe::StdoutToPipe(int r, int pipe_write_fd) {
  this->r = r;
  this->w = pipe_write_fd;
}

void StdoutToPipe::Apply() {
  posix::dup2(this->w, 1);
  posix::close(this->w);
  posix::close(this->r);
}
int INVALID_PGID = -1;
int OWN_LEADER = 0;

SetPgid::SetPgid(int pgid, dev::Tracer* tracer) {
  this->pgid = pgid;
  this->tracer = tracer;
}

void SetPgid::Apply() {
  try {
    posix::setpgid(0, this->pgid);
  }
  catch (IOError_OSError* e) {
    this->tracer->OtherMessage(StrFormat("osh: child %d failed to set its process group to %d: %s", posix::getpid(), this->pgid, pyutil::strerror(e)));
  }
}

void SetPgid::ApplyFromParent(process::Process* proc) {
  StackRoot _root0(&proc);

  try {
    posix::setpgid(proc->pid, this->pgid);
  }
  catch (IOError_OSError* e) {
    this->tracer->OtherMessage(StrFormat("osh: parent failed to set process group for PID %d to %d: %s", proc->pid, this->pgid, pyutil::strerror(e)));
  }
}

ExternalProgram::ExternalProgram(BigStr* hijack_shebang, process::FdState* fd_state, ui::ErrorFormatter* errfmt, util::_DebugFile* debug_f) {
  this->hijack_shebang = hijack_shebang;
  this->fd_state = fd_state;
  this->errfmt = errfmt;
  this->debug_f = debug_f;
}

void ExternalProgram::Exec(BigStr* argv0_path, cmd_value::Argv* cmd_val, Dict<BigStr*, BigStr*>* environ) {
  StackRoot _root0(&argv0_path);
  StackRoot _root1(&cmd_val);
  StackRoot _root2(&environ);

  DTRACE_PROBE1(process, ExternalProgram_Exec, argv0_path->data());
  this->_Exec(argv0_path, cmd_val->argv, cmd_val->arg_locs->at(0), environ, true);
}

void ExternalProgram::_Exec(BigStr* argv0_path, List<BigStr*>* argv, syntax_asdl::loc_t* argv0_loc, Dict<BigStr*, BigStr*>* environ, bool should_retry) {
  bool opened;
  mylib::LineReader* f = nullptr;
  BigStr* line = nullptr;
  List<BigStr*>* h_argv = nullptr;
  List<BigStr*>* new_argv = nullptr;
  int status;
  StackRoot _root0(&argv0_path);
  StackRoot _root1(&argv);
  StackRoot _root2(&argv0_loc);
  StackRoot _root3(&environ);
  StackRoot _root4(&f);
  StackRoot _root5(&line);
  StackRoot _root6(&h_argv);
  StackRoot _root7(&new_argv);

  if (len(this->hijack_shebang)) {
    opened = true;
    try {
      f = this->fd_state->Open(argv0_path);
    }
    catch (IOError_OSError* e) {
      opened = false;
    }
    if (opened) {
      {  // with
        ctx_FileCloser ctx{f};

        line = f->readline();
        if (match::ShouldHijack(line)) {
          h_argv = NewList<BigStr*>(std::initializer_list<BigStr*>{this->hijack_shebang, argv0_path});
          h_argv->extend(argv->slice(1));
          argv = h_argv;
          argv0_path = this->hijack_shebang;
          this->debug_f->writeln(StrFormat("Hijacked: %s", argv0_path));
        }
        else {
          ;  // pass
        }
      }
    }
  }
  try {
    posix::execve(argv0_path, argv, environ);
  }
  catch (IOError_OSError* e) {
    if ((e->errno_ == ENOEXEC and should_retry)) {
      new_argv = NewList<BigStr*>(std::initializer_list<BigStr*>{S_rhf, argv0_path});
      new_argv->extend(argv->slice(1));
      this->_Exec(S_rhf, new_argv, argv0_loc, environ, false);
    }
    this->errfmt->Print_(StrFormat("Can't execute %r: %s", argv0_path, pyutil::strerror(e)), argv0_loc);
    if (e->errno_ == EACCES) {
      status = 126;
    }
    else {
      if (e->errno_ == ENOENT) {
        status = 127;
      }
      else {
        status = 127;
      }
    }
    posix::_exit(status);
  }
}

Thunk::Thunk() {
  ;  // pass
}

void Thunk::Run() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

BigStr* Thunk::UserString() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

ExternalThunk::ExternalThunk(process::ExternalProgram* ext_prog, BigStr* argv0_path, cmd_value::Argv* cmd_val, Dict<BigStr*, BigStr*>* environ) {
  this->ext_prog = ext_prog;
  this->argv0_path = argv0_path;
  this->cmd_val = cmd_val;
  this->environ = environ;
}

BigStr* ExternalThunk::UserString() {
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&tmp);

  tmp = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(this->cmd_val->argv); !it.Done(); it.Next()) {
    BigStr* a = it.Value();
    tmp->append(j8_lite::MaybeShellEncode(a));
  }
  return StrFormat("[process] %s", S_yfw->join(tmp));
}

void ExternalThunk::Run() {
  this->ext_prog->Exec(this->argv0_path, this->cmd_val, this->environ);
}

SubProgramThunk::SubProgramThunk(cmd_eval::CommandEvaluator* cmd_ev, syntax_asdl::command_t* node, trap_osh::TrapState* trap_state, dev::MultiTracer* multi_trace, bool inherit_errexit, bool inherit_errtrace) {
  this->cmd_ev = cmd_ev;
  this->node = node;
  this->trap_state = trap_state;
  this->multi_trace = multi_trace;
  this->inherit_errexit = inherit_errexit;
  this->inherit_errtrace = inherit_errtrace;
}

BigStr* SubProgramThunk::UserString() {
  BigStr* thunk_str = nullptr;
  StackRoot _root0(&thunk_str);

  thunk_str = ui::CommandType(this->node);
  return StrFormat("[subprog] %s", thunk_str);
}

void SubProgramThunk::Run() {
  int status;
  DTRACE_PROBE(process, SubProgramThunk_Run);
  this->trap_state->ClearForSubProgram(this->inherit_errtrace);
  if (!this->inherit_errexit) {
    this->cmd_ev->mutable_opts->DisableErrExit();
  }
  try {
    this->cmd_ev->ExecuteAndCatch(this->node, (cmd_eval::OptimizeSubshells | cmd_eval::MarkLastCommands));
    status = this->cmd_ev->LastStatus();
  }
  catch (util::UserExit* e) {
    status = e->status;
  }
  catch (KeyboardInterrupt*) {
    print(S_Aoo);
    status = 130;
  }
  catch (IOError_OSError* e) {
    print_stderr(StrFormat("oils I/O error (subprogram): %s", pyutil::strerror(e)));
    status = 2;
  }
  pyos::FlushStdout();
  this->multi_trace->WriteDumps();
  posix::_exit(status);
}

_HereDocWriterThunk::_HereDocWriterThunk(int w, BigStr* body_str) {
  this->w = w;
  this->body_str = body_str;
}

BigStr* _HereDocWriterThunk::UserString() {
  return S_fhC;
}

void _HereDocWriterThunk::Run() {
  DTRACE_PROBE(process, HereDocWriterThunk_Run);
  posix::write(this->w, this->body_str);
  posix::close(this->w);
  posix::_exit(0);
}

Job::Job() {
  this->state = job_state_e::Running;
  this->job_id = -1;
  this->in_background = false;
}

void Job::DisplayJob(int job_id, mylib::Writer* f, int style) {
  StackRoot _root0(&f);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

runtime_asdl::job_state_t Job::State() {
  return this->state;
}

int Job::ProcessGroupId() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

runtime_asdl::wait_status_t* Job::JobWait(process::Waiter* waiter) {
  StackRoot _root0(&waiter);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

void Job::SetBackground() {
  this->in_background = true;
}

void Job::SetForeground() {
  this->in_background = false;
}

Process::Process(process::Thunk* thunk, process::JobControl* job_control, process::JobList* job_list, dev::Tracer* tracer) : ::process::Job() {
  this->thunk = thunk;
  this->job_control = job_control;
  this->job_list = job_list;
  this->tracer = tracer;
  this->parent_pipeline = nullptr;
  this->state_changes = Alloc<List<process::ChildStateChange*>>();
  this->close_r = -1;
  this->close_w = -1;
  this->pid = -1;
  this->status = -1;
}

void Process::Init_ParentPipeline(process::Pipeline* pi) {
  StackRoot _root0(&pi);

  this->parent_pipeline = pi;
}

int Process::ProcessGroupId() {
  if (this->parent_pipeline) {
    return this->parent_pipeline->ProcessGroupId();
  }
  return this->pid;
}

void Process::DisplayJob(int job_id, mylib::Writer* f, int style) {
  BigStr* job_id_str = nullptr;
  StackRoot _root0(&f);
  StackRoot _root1(&job_id_str);

  if (job_id == -1) {
    job_id_str = S_jqf;
  }
  else {
    job_id_str = StrFormat("%%%d", job_id);
  }
  if (style == STYLE_PID_ONLY) {
    f->write(StrFormat("%d\n", this->pid));
  }
  else {
    f->write(StrFormat("%s %d %7s ", job_id_str, this->pid, _JobStateStr(this->state)));
    f->write(this->thunk->UserString());
    f->write(S_nfs);
  }
}

void Process::AddStateChange(process::ChildStateChange* s) {
  StackRoot _root0(&s);

  this->state_changes->append(s);
}

void Process::AddPipeToClose(int r, int w) {
  this->close_r = r;
  this->close_w = w;
}

void Process::MaybeClosePipe() {
  if (this->close_r != -1) {
    posix::close(this->close_r);
    posix::close(this->close_w);
  }
}

int Process::StartProcess(runtime_asdl::trace_t* why) {
  int pid;
  StackRoot _root0(&why);

  pid = posix::fork();
  if (pid < 0) {
    e_die(S_ddv);
  }
  else {
    if (pid == 0) {
      for (ListIter<process::ChildStateChange*> it(this->state_changes); !it.Done(); it.Next()) {
        process::ChildStateChange* st = it.Value();
        StackRoot _for(&st      );
        st->Apply();
      }
      iolib::sigaction(SIGPIPE, SIG_DFL);
      iolib::sigaction(SIGQUIT, SIG_DFL);
      pid = posix::getpid();
      if ((posix::getpgid(0) == pid and this->parent_pipeline == nullptr)) {
        iolib::sigaction(SIGTSTP, SIG_DFL);
      }
      iolib::sigaction(SIGTTOU, SIG_DFL);
      iolib::sigaction(SIGTTIN, SIG_DFL);
      this->tracer->OnNewProcess(pid);
      this->thunk->Run();
    }
  }
  this->tracer->OnProcessStart(pid, why);
  this->pid = pid;
  for (ListIter<process::ChildStateChange*> it(this->state_changes); !it.Done(); it.Next()) {
    process::ChildStateChange* st = it.Value();
    StackRoot _for(&st  );
    st->ApplyFromParent(this);
  }
  this->job_list->AddChildProcess(pid, this);
  return pid;
}

int Process::Wait(process::Waiter* waiter) {
  StackRoot _root0(&waiter);

  while (this->state == job_state_e::Running) {
    if (waiter->WaitForOne() == W1_ECHILD) {
      break;
    }
  }
  return this->status;
}

runtime_asdl::wait_status_t* Process::JobWait(process::Waiter* waiter) {
  int result;
  StackRoot _root0(&waiter);

  while (this->state == job_state_e::Running) {
    result = waiter->WaitForOne();
    if (result >= 0) {
      return Alloc<wait_status::Cancelled>(result);
    }
    if (result == W1_ECHILD) {
      break;
    }
  }
  return Alloc<wait_status::Proc>(this->status);
}

void Process::WhenStopped(int stop_sig) {
  this->status = (128 + stop_sig);
  this->state = job_state_e::Stopped;
  if (this->job_id == -1) {
    this->job_list->AddJob(this);
  }
  if (!this->in_background) {
    this->job_control->MaybeTakeTerminal();
    this->SetBackground();
  }
}

void Process::WhenDone(int pid, int status) {
  this->status = status;
  this->state = job_state_e::Done;
  if (this->parent_pipeline) {
    this->parent_pipeline->WhenDone(pid, status);
  }
  else {
    if (this->job_id != -1) {
      if (this->in_background) {
        print_stderr(StrFormat("[%%%d] PID %d Done", this->job_id, this->pid));
      }
      this->job_list->RemoveJob(this->job_id);
    }
    this->job_list->RemoveChildProcess(this->pid);
    if (!this->in_background) {
      this->job_control->MaybeTakeTerminal();
    }
  }
}

int Process::RunProcess(process::Waiter* waiter, runtime_asdl::trace_t* why) {
  StackRoot _root0(&waiter);
  StackRoot _root1(&why);

  this->StartProcess(why);
  if (this->parent_pipeline == nullptr) {
    this->job_control->MaybeGiveTerminal(posix::getpgid(this->pid));
  }
  return this->Wait(waiter);
}

ctx_Pipe::ctx_Pipe(process::FdState* fd_state, int fd, List<IOError_OSError*>* err_out) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->err_out)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->fd_state)));
  fd_state->PushStdinFromPipe(fd);
  this->fd_state = fd_state;
  this->err_out = err_out;
}

ctx_Pipe::~ctx_Pipe() {
  this->fd_state->Pop(this->err_out);
  gHeap.PopRoot();
  gHeap.PopRoot();
}

Pipeline::Pipeline(bool sigpipe_status_ok, process::JobControl* job_control, process::JobList* job_list, dev::Tracer* tracer) : ::process::Job() {
  this->job_control = job_control;
  this->job_list = job_list;
  this->tracer = tracer;
  this->procs = Alloc<List<process::Process*>>();
  this->pids = Alloc<List<int>>();
  this->pipe_status = Alloc<List<int>>();
  this->status = -1;
  this->pgid = INVALID_PGID;
  this->last_thunk = nullptr;
  this->last_pipe = nullptr;
  this->sigpipe_status_ok = sigpipe_status_ok;
}

int Pipeline::ProcessGroupId() {
  return this->pgid;
}

void Pipeline::DisplayJob(int job_id, mylib::Writer* f, int style) {
  int i;
  BigStr* job_id_str = nullptr;
  StackRoot _root0(&f);
  StackRoot _root1(&job_id_str);

  if (style == STYLE_PID_ONLY) {
    f->write(StrFormat("%d\n", this->procs->at(0)->pid));
  }
  else {
    i = 0;
    for (ListIter<process::Process*> it(this->procs); !it.Done(); it.Next(), ++i) {
      process::Process* proc = it.Value();
      StackRoot _for(&proc    );
      if (i == 0) {
        job_id_str = StrFormat("%%%d", job_id);
      }
      else {
        job_id_str = S_jqf;
        f->write(StrFormat("%s %d %7s ", job_id_str, proc->pid, _JobStateStr(proc->state)));
        f->write(proc->thunk->UserString());
        f->write(S_nfs);
      }
    }
  }
}

void Pipeline::DebugPrint() {
  print(StrFormat("Pipeline in state %s", _JobStateStr(this->state)));
}

void Pipeline::Add(process::Process* p) {
  int r;
  int w;
  process::Process* prev = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&prev);

  if (len(this->procs) == 0) {
    this->procs->append(p);
    return ;
  }
  Tuple2<int, int> tup1 = posix::pipe();
  r = tup1.at0();
  w = tup1.at1();
  prev = this->procs->at(-1);
  prev->AddStateChange(Alloc<StdoutToPipe>(r, w));
  p->AddStateChange(Alloc<StdinFromPipe>(r, w));
  p->AddPipeToClose(r, w);
  this->procs->append(p);
}

void Pipeline::AddLast(Tuple2<cmd_eval::CommandEvaluator*, syntax_asdl::command_t*>* thunk) {
  int r;
  int w;
  process::Process* prev = nullptr;
  StackRoot _root0(&thunk);
  StackRoot _root1(&prev);

  this->last_thunk = thunk;
  Tuple2<int, int> tup2 = posix::pipe();
  r = tup2.at0();
  w = tup2.at1();
  prev = this->procs->at(-1);
  prev->AddStateChange(Alloc<StdoutToPipe>(r, w));
  this->last_pipe = (Alloc<Tuple2<int, int>>(r, w));
}

void Pipeline::StartPipeline(process::Waiter* waiter) {
  int i;
  int pid;
  StackRoot _root0(&waiter);

  if (this->job_control->Enabled()) {
    this->pgid = OWN_LEADER;
  }
  i = 0;
  for (ListIter<process::Process*> it(this->procs); !it.Done(); it.Next(), ++i) {
    process::Process* proc = it.Value();
    StackRoot _for(&proc  );
    if (this->pgid != INVALID_PGID) {
      proc->AddStateChange(Alloc<SetPgid>(this->pgid, this->tracer));
    }
    pid = proc->StartProcess(trace::PipelinePart);
    if ((i == 0 and this->pgid != INVALID_PGID)) {
      this->pgid = pid;
    }
    this->pids->append(pid);
    this->pipe_status->append(-1);
    proc->MaybeClosePipe();
  }
  if (this->last_thunk) {
    this->pipe_status->append(-1);
  }
}

int Pipeline::LastPid() {
  return this->pids->at(-1);
}

List<int>* Pipeline::Wait(process::Waiter* waiter) {
  StackRoot _root0(&waiter);

  while (this->state == job_state_e::Running) {
    if (waiter->WaitForOne() == W1_ECHILD) {
      break;
    }
  }
  return this->pipe_status;
}

runtime_asdl::wait_status_t* Pipeline::JobWait(process::Waiter* waiter) {
  int result;
  StackRoot _root0(&waiter);

  while (this->state == job_state_e::Running) {
    result = waiter->WaitForOne();
    if (result >= 0) {
      return Alloc<wait_status::Cancelled>(result);
    }
    if (result == W1_ECHILD) {
      break;
    }
  }
  return Alloc<wait_status::Pipeline>(this->pipe_status);
}

List<int>* Pipeline::RunLastPart(process::Waiter* waiter, process::FdState* fd_state) {
  cmd_eval::CommandEvaluator* cmd_ev = nullptr;
  syntax_asdl::command_t* last_node = nullptr;
  int r;
  int w;
  int cmd_flags;
  List<IOError_OSError*>* io_errors = nullptr;
  StackRoot _root0(&waiter);
  StackRoot _root1(&fd_state);
  StackRoot _root2(&cmd_ev);
  StackRoot _root3(&last_node);
  StackRoot _root4(&io_errors);

  this->job_control->MaybeGiveTerminal(this->pgid);
  Tuple2<cmd_eval::CommandEvaluator*, syntax_asdl::command_t*>* tup3 = this->last_thunk;
  cmd_ev = tup3->at0();
  last_node = tup3->at1();
  Tuple2<int, int>* tup4 = this->last_pipe;
  r = tup4->at0();
  w = tup4->at1();
  posix::close(w);
  cmd_flags = this->job_control->Enabled() ? cmd_eval::NoDebugTrap : 0;
  cmd_flags |= cmd_eval::NoErrTrap;
  io_errors = Alloc<List<IOError_OSError*>>();
  {  // with
    ctx_Pipe ctx{fd_state, r, io_errors};

    cmd_ev->ExecuteAndCatch(last_node, cmd_flags);
  }
  if (len(io_errors)) {
    e_die(StrFormat("Error setting up last part of pipeline: %s", pyutil::strerror(io_errors->at(0))));
  }
  posix::close(r);
  this->pipe_status->set(-1, cmd_ev->LastStatus());
  if (this->AllDone()) {
    this->state = job_state_e::Done;
  }
  return this->Wait(waiter);
}

bool Pipeline::AllDone() {
  for (ListIter<int> it(this->pipe_status); !it.Done(); it.Next()) {
    int status = it.Value();
    if (status == -1) {
      return false;
    }
  }
  return true;
}

void Pipeline::WhenDone(int pid, int status) {
  int i;
  i = this->pids->index(pid);
  if ((status == 141 and this->sigpipe_status_ok)) {
    status = 0;
  }
  this->job_list->RemoveChildProcess(pid);
  this->pipe_status->set(i, status);
  if (this->AllDone()) {
    if (this->job_id != -1) {
      if (this->in_background) {
        print_stderr(StrFormat("[%%%d] PGID %d Done", this->job_id, this->pids->at(0)));
      }
      this->job_list->RemoveJob(this->job_id);
    }
    this->status = this->pipe_status->at(-1);
    this->state = job_state_e::Done;
    if (!this->in_background) {
      this->job_control->MaybeTakeTerminal();
    }
  }
}

BigStr* _JobStateStr(runtime_asdl::job_state_t i) {
  return job_state_str(i)->slice(10);
}

int _GetTtyFd() {
  try {
    return posix::open(S_jpk, ((O_NONBLOCK | O_NOCTTY) | O_RDWR), 438);
  }
  catch (IOError_OSError* e) {
    return -1;
  }
}

ctx_TerminalControl::ctx_TerminalControl(process::JobControl* job_control, ui::ErrorFormatter* errfmt) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->errfmt)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->job_control)));
  job_control->InitJobControl();
  this->job_control = job_control;
  this->errfmt = errfmt;
}

ctx_TerminalControl::~ctx_TerminalControl() {
  try {
    this->job_control->MaybeReturnTerminal();
  }
  catch (error::FatalRuntime* e) {
    this->errfmt->PrettyPrintError(e);
  }
  gHeap.PopRoot();
  gHeap.PopRoot();
}

JobControl::JobControl() {
  this->shell_pid = -1;
  this->shell_pgid = -1;
  this->shell_tty_fd = -1;
  this->original_tty_pgid = -1;
}

void JobControl::InitJobControl() {
  int orig_shell_pgid;
  this->shell_pid = posix::getpid();
  orig_shell_pgid = posix::getpgid(0);
  this->shell_pgid = orig_shell_pgid;
  this->shell_tty_fd = _GetTtyFd();
  if (this->shell_pgid != this->shell_pid) {
    try {
      posix::setpgid(this->shell_pid, this->shell_pid);
      this->shell_pgid = this->shell_pid;
    }
    catch (IOError_OSError* e) {
      this->shell_tty_fd = -1;
    }
  }
  if (this->shell_tty_fd != -1) {
    this->original_tty_pgid = posix::tcgetpgrp(this->shell_tty_fd);
    try {
      posix::tcsetpgrp(this->shell_tty_fd, this->shell_pgid);
    }
    catch (IOError_OSError* e) {
      this->shell_tty_fd = -1;
      this->shell_pgid = orig_shell_pgid;
      posix::setpgid(this->shell_pid, this->shell_pgid);
    }
  }
}

bool JobControl::Enabled() {
  return (this->shell_tty_fd != -1 and posix::getpid() == this->shell_pid);
}

void JobControl::MaybeGiveTerminal(int pgid) {
  if (!this->Enabled()) {
    return ;
  }
  try {
    posix::tcsetpgrp(this->shell_tty_fd, pgid);
  }
  catch (IOError_OSError* e) {
    e_die(StrFormat("osh: Failed to move process group %d to foreground: %s", pgid, pyutil::strerror(e)));
  }
}

void JobControl::MaybeTakeTerminal() {
  this->MaybeGiveTerminal(this->shell_pgid);
}

void JobControl::MaybeReturnTerminal() {
  this->MaybeGiveTerminal(this->original_tty_pgid);
}

JobList::JobList() {
  this->jobs = Alloc<Dict<int, process::Job*>>();
  this->child_procs = Alloc<Dict<int, process::Process*>>();
  this->debug_pipelines = Alloc<List<process::Pipeline*>>();
  this->job_id = 1;
}

int JobList::AddJob(process::Job* job) {
  int job_id;
  StackRoot _root0(&job);

  job_id = this->job_id;
  this->jobs->set(job_id, job);
  job->job_id = job_id;
  this->job_id += 1;
  return job_id;
}

void JobList::RemoveJob(int job_id) {
  mylib::dict_erase(this->jobs, job_id);
  if (len(this->jobs) == 0) {
    this->job_id = 1;
  }
}

void JobList::AddChildProcess(int pid, process::Process* proc) {
  StackRoot _root0(&proc);

  this->child_procs->set(pid, proc);
}

void JobList::RemoveChildProcess(int pid) {
  mylib::dict_erase(this->child_procs, pid);
}

process::Process* JobList::ProcessFromPid(int pid) {
  return this->child_procs->get(pid);
}

Tuple2<process::Job*, process::Job*> JobList::GetCurrentAndPreviousJobs() {
  List<process::Job*>* stopped_jobs = nullptr;
  List<process::Job*>* running_jobs = nullptr;
  process::Job* job = nullptr;
  process::Job* current = nullptr;
  process::Job* previous = nullptr;
  StackRoot _root0(&stopped_jobs);
  StackRoot _root1(&running_jobs);
  StackRoot _root2(&job);
  StackRoot _root3(&current);
  StackRoot _root4(&previous);

  stopped_jobs = Alloc<List<process::Job*>>();
  running_jobs = Alloc<List<process::Job*>>();
  for (int i = 0; i < this->job_id; ++i) {
    job = this->jobs->get(i, nullptr);
    if (!job) {
      continue;
    }
    if (job->state == job_state_e::Stopped) {
      stopped_jobs->append(job);
    }
    else {
      if (job->state == job_state_e::Running) {
        running_jobs->append(job);
      }
    }
  }
  current = nullptr;
  previous = nullptr;
  if (len(stopped_jobs) > 0) {
    current = stopped_jobs->pop();
  }
  if (len(stopped_jobs) > 0) {
    previous = stopped_jobs->pop();
  }
  if ((len(running_jobs) > 0 and !current)) {
    current = running_jobs->pop();
  }
  if ((len(running_jobs) > 0 and !previous)) {
    previous = running_jobs->pop();
  }
  if (!previous) {
    previous = current;
  }
  return Tuple2<process::Job*, process::Job*>(current, previous);
}

process::Job* JobList::GetJobWithSpec(BigStr* job_spec) {
  process::Job* current = nullptr;
  process::Job* previous = nullptr;
  List<BigStr*>* m = nullptr;
  int job_id;
  StackRoot _root0(&job_spec);
  StackRoot _root1(&current);
  StackRoot _root2(&previous);
  StackRoot _root3(&m);

  if (list_contains(CURRENT_JOB_SPECS, job_spec)) {
    Tuple2<process::Job*, process::Job*> tup5 = this->GetCurrentAndPreviousJobs();
    current = tup5.at0();
    return current;
  }
  if (str_equals(job_spec, S_aAh)) {
    Tuple2<process::Job*, process::Job*> tup6 = this->GetCurrentAndPreviousJobs();
    previous = tup6.at1();
    return previous;
  }
  m = util::RegexSearch(S_coi, job_spec);
  if (m != nullptr) {
    job_id = to_int(m->at(1));
    if (dict_contains(this->jobs, job_id)) {
      return this->jobs->at(job_id);
    }
  }
  return nullptr;
}

void JobList::DisplayJobs(int style) {
  mylib::Writer* f = nullptr;
  StackRoot _root0(&f);

  f = mylib::Stdout();
  for (DictIter<int, process::Job*> it(this->jobs); !it.Done(); it.Next()) {
    int job_id = it.Key();
    process::Job* job = it.Value();
    job->DisplayJob(job_id, f, style);
  }
}

void JobList::DebugPrint() {
  mylib::Writer* f = nullptr;
  StackRoot _root0(&f);

  f = mylib::Stdout();
  f->write(S_nfs);
  f->write(S_xfq);
  for (DictIter<int, process::Process*> it(this->child_procs); !it.Done(); it.Next()) {
    int pid = it.Key();
    process::Process* proc = it.Value();
    proc->DisplayJob(-1, f, STYLE_DEFAULT);
  }
  if (len(this->debug_pipelines)) {
    f->write(S_nfs);
    f->write(S_zxF);
    for (ListIter<process::Pipeline*> it(this->debug_pipelines); !it.Done(); it.Next()) {
      process::Pipeline* pi = it.Value();
      StackRoot _for(&pi    );
      pi->DebugPrint();
    }
  }
}

void JobList::ListRecent() {
  ;  // pass
}

int JobList::NumRunning() {
  int count;
  count = 0;
  for (DictIter<int, process::Job*> it(this->jobs); !it.Done(); it.Next()) {
    int _ = it.Key();
    process::Job* job = it.Value();
    if (job->State() == job_state_e::Running) {
      count += 1;
    }
  }
  return count;
}
int W1_OK = -2;
int W1_ECHILD = -3;
int W1_AGAIN = -4;

Waiter::Waiter(process::JobList* job_list, optview::Exec* exec_opts, iolib::SignalSafe* signal_safe, dev::Tracer* tracer) {
  this->job_list = job_list;
  this->exec_opts = exec_opts;
  this->signal_safe = signal_safe;
  this->tracer = tracer;
  this->last_status = 127;
}

int Waiter::WaitForOne(int waitpid_options) {
  int pid;
  int status;
  int err_num;
  process::Process* proc = nullptr;
  int term_sig;
  int stop_sig;
  StackRoot _root0(&proc);

  Tuple2<int, int> tup7 = pyos::WaitPid(waitpid_options);
  pid = tup7.at0();
  status = tup7.at1();
  if (pid == 0) {
    return W1_AGAIN;
  }
  else {
    if (pid < 0) {
      err_num = status;
      if (err_num == ECHILD) {
        return W1_ECHILD;
      }
      else {
        if (err_num == EINTR) {
          return this->signal_safe->LastSignal();
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
  }
  if (!dict_contains(this->job_list->child_procs, pid)) {
    print_stderr(StrFormat("oils: PID %d Stopped, but osh didn't start it", pid));
    return W1_OK;
  }
  proc = this->job_list->child_procs->at(pid);
  if (WIFSIGNALED(status)) {
    term_sig = WTERMSIG(status);
    status = (128 + term_sig);
    if (term_sig == SIGINT) {
      print(S_Aoo);
    }
    proc->WhenDone(pid, status);
  }
  else {
    if (WIFEXITED(status)) {
      status = WEXITSTATUS(status);
      proc->WhenDone(pid, status);
    }
    else {
      if (WIFSTOPPED(status)) {
        stop_sig = WSTOPSIG(status);
        print_stderr(S_Aoo);
        print_stderr(StrFormat("oils: PID %d Stopped with signal %d", pid, stop_sig));
        proc->WhenStopped(stop_sig);
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  this->last_status = status;
  this->tracer->OnProcessEnd(pid, status);
  return W1_OK;
}

void Waiter::PollNotifications() {
  while (this->WaitForOne(WNOHANG) == W1_OK) {
    continue;
  }
}

}  // define namespace process

namespace sh_init {  // define

using runtime_asdl::scope_e;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using error::e_die;

EnvConfig::EnvConfig(state::Mem* mem, Dict<BigStr*, value_asdl::value_t*>* defaults) {
  this->mem = mem;
  this->exec_opts = mem->exec_opts;
  this->defaults = defaults;
}

value_asdl::value_t* EnvConfig::GetVal(BigStr* var_name) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&var_name);
  StackRoot _root1(&val);

  if (this->mem->exec_opts->env_obj()) {
    val = this->mem->env_dict->get(var_name);
    if (val == nullptr) {
      val = this->defaults->get(var_name);
    }
    if (val == nullptr) {
      return value::Undef;
    }
  }
  else {
    val = this->mem->GetValue(var_name);
  }
  return val;
}

BigStr* EnvConfig::Get(BigStr* var_name) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&var_name);
  StackRoot _root1(&val);

  val = this->GetVal(var_name);
  if (val->tag() != value_e::Str) {
    return nullptr;
  }
  return static_cast<value::Str*>(val)->s;
}

void EnvConfig::SetDefault(BigStr* var_name, BigStr* s) {
  StackRoot _root0(&var_name);
  StackRoot _root1(&s);

  if (this->mem->exec_opts->env_obj()) {
    this->mem->defaults->set(var_name, Alloc<value::Str>(s));
  }
  else {
    state::SetGlobalString(this->mem, var_name, s);
  }
}

ShellFiles::ShellFiles(BigStr* lang, BigStr* home_dir, state::Mem* mem, arg_types::main* flag) {
  this->lang = lang;
  this->home_dir = home_dir;
  this->mem = mem;
  this->flag = flag;
  this->init_done = false;
}

BigStr* ShellFiles::HistVar() {
  return str_equals(this->lang, S_Ffb) ? S_omw : S_xiB;
}

BigStr* ShellFiles::DefaultHistoryFile() {
  return os_path::join(this->home_dir, StrFormat(".local/share/oils/%s_history", this->lang));
}

BigStr* ShellFiles::HistoryFile() {
  return this->mem->env_config->Get(this->HistVar());
}

BigStr* GetWorkingDir() {
  try {
    return posix::getcwd();
  }
  catch (IOError_OSError* e) {
    e_die(StrFormat("Can't determine the working dir: %s", pyutil::strerror(e)));
  }
}
BigStr* _READLINE_DELIMS = S_ubu;

void InitDefaultVars(state::Mem* mem) {
  StackRoot _root0(&mem);

  state::SetGlobalString(mem, S_zwr, str(posix::getuid()));
  state::SetGlobalString(mem, S_vrm, str(posix::geteuid()));
  state::SetGlobalString(mem, S_gnu, str(posix::getppid()));
  state::SetGlobalString(mem, S_fdf, S_vrA);
  state::SetGlobalString(mem, S_nie, split::DEFAULT_IFS);
  state::SetGlobalString(mem, S_aqr, libc::gethostname());
  state::SetGlobalString(mem, S_hBE, pyos::OsType());
  state::SetGlobalString(mem, S_zyo, S_mys);
  state::SetGlobalString(mem, S_uhz, _READLINE_DELIMS);
}

void CopyVarsFromEnv(optview::Exec* exec_opts, Dict<BigStr*, BigStr*>* environ, state::Mem* mem) {
  StackRoot _root0(&exec_opts);
  StackRoot _root1(&environ);
  StackRoot _root2(&mem);

  if (!exec_opts->no_exported()) {
    for (DictIter<BigStr*, BigStr*> it(environ); !it.Done(); it.Next()) {
      BigStr* n = it.Key();
      BigStr* v = it.Value();
      mem->SetNamed(location::LName(n), Alloc<value::Str>(v), scope_e::GlobalOnly, state::SetExport);
    }
  }
  if (exec_opts->env_obj()) {
    mem->MaybeInitEnvDict(environ);
  }
}

void InitVarsAfterEnv(state::Mem* mem) {
  BigStr* s = nullptr;
  value_asdl::value_t* val = nullptr;
  BigStr* pwd = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&s);
  StackRoot _root2(&val);
  StackRoot _root3(&pwd);

  s = mem->env_config->Get(S_jip);
  if (s == nullptr) {
    mem->env_config->SetDefault(S_jip, S_kAx);
  }
  if (!mem->exec_opts->no_init_globals()) {
    val = mem->GetValue(S_cvm);
    if (val->tag() == value_e::Undef) {
      state::SetGlobalString(mem, S_cvm, S_Aoo);
    }
    mem->SetNamed(location::LName(S_cvm), nullptr, scope_e::GlobalOnly, state::SetReadOnly);
    val = mem->GetValue(S_xxp);
    if (val->tag() == value_e::Undef) {
      state::SetGlobalString(mem, S_xxp, GetWorkingDir());
    }
    mem->SetNamed(location::LName(S_xxp), nullptr, scope_e::GlobalOnly, state::SetExport);
    val = mem->GetValue(S_xxp);
    pwd = static_cast<value::Str*>(val)->s;
    mem->SetPwd(pwd);
  }
  else {
    mem->SetPwd(GetWorkingDir());
  }
}

void InitInteractive(state::Mem* mem, sh_init::ShellFiles* sh_files, BigStr* lang) {
  BigStr* ps1_str = nullptr;
  BigStr* hist_var = nullptr;
  BigStr* hist_str = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&sh_files);
  StackRoot _root2(&lang);
  StackRoot _root3(&ps1_str);
  StackRoot _root4(&hist_var);
  StackRoot _root5(&hist_str);

  ps1_str = mem->env_config->Get(S_Eni);
  if (ps1_str == nullptr) {
    mem->env_config->SetDefault(S_Eni, S_bxh);
  }
  else {
    if (str_equals(lang, S_Awp)) {
      mem->env_dict->set(S_Eni, Alloc<value::Str>(str_concat(S_eyu, ps1_str)));
    }
  }
  hist_var = sh_files->HistVar();
  hist_str = mem->env_config->Get(hist_var);
  if (hist_str == nullptr) {
    mem->env_config->SetDefault(hist_var, sh_files->DefaultHistoryFile());
  }
  sh_files->init_done = true;
}

void InitBuiltins(state::Mem* mem, BigStr* version_str, Dict<BigStr*, value_asdl::value_t*>* defaults) {
  StackRoot _root0(&mem);
  StackRoot _root1(&version_str);
  StackRoot _root2(&defaults);

  mem->builtins->set(S_iCt, Alloc<value::Str>(version_str));
  mem->builtins->set(S_knB, Alloc<value::Str>(version_str));
  mem->builtins->set(S_avA_2, Alloc<value::Dict>(defaults));
  mem->builtins->set(S_kFk, Alloc<value::Str>(S_fyx));
  mem->builtins->set(S_llF, Alloc<value::Str>(S_wqi));
  mem->builtins->set(S_ywk, Alloc<value::Float>(pyutil::nan()));
  mem->builtins->set(S_BvB, Alloc<value::Float>(pyutil::infinity()));
}

}  // define namespace sh_init

namespace state {  // define

using id_kind_asdl::Id;
using option_asdl::option_i;
using runtime_asdl::error_code_e;
using runtime_asdl::scope_e;
using runtime_asdl::scope_t;
using runtime_asdl::Cell;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::Token;
using syntax_asdl::debug_frame;
using syntax_asdl::debug_frame_e;
using syntax_asdl::debug_frame_t;
using types_asdl::opt_group_i;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::Obj;
using value_asdl::sh_lvalue;
using value_asdl::sh_lvalue_e;
using value_asdl::sh_lvalue_t;
using value_asdl::LeftName;
using value_asdl::y_lvalue_e;
using value_asdl::regex_match;
using value_asdl::regex_match_e;
using value_asdl::regex_match_t;
using value_asdl::RegexMatch;
using error::e_usage;
using error::e_die;
using mylib::print_stderr;
int SetReadOnly = (1 << 0);
int ClearReadOnly = (1 << 1);
int SetExport = (1 << 2);
int ClearExport = (1 << 3);
int SetNameref = (1 << 4);
int ClearNameref = (1 << 5);

ctx_Source::ctx_Source(state::Mem* mem, BigStr* source_name, List<BigStr*>* argv) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->argv)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  mem->PushSource(source_name, argv);
  this->mem = mem;
  this->argv = argv;
  this->to_restore = this->mem->is_main;
  this->mem->is_main = false;
}

ctx_Source::~ctx_Source() {
  this->mem->PopSource(this->argv);
  this->mem->is_main = this->to_restore;
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_DebugTrap::ctx_DebugTrap(state::Mem* mem) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  mem->running_debug_trap = true;
  this->mem = mem;
}

ctx_DebugTrap::~ctx_DebugTrap() {
  this->mem->running_debug_trap = false;
  gHeap.PopRoot();
}

ctx_ErrTrap::ctx_ErrTrap(state::Mem* mem) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  mem->running_err_trap = true;
  this->mem = mem;
}

ctx_ErrTrap::~ctx_ErrTrap() {
  this->mem->running_err_trap = false;
  gHeap.PopRoot();
}

ctx_Option::ctx_Option(state::MutableOpts* mutable_opts, List<int>* opt_nums, bool b) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mutable_opts)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->opt_nums)));
  for (ListIter<int> it(opt_nums); !it.Done(); it.Next()) {
    int opt_num = it.Value();
    mutable_opts->Push(opt_num, b);
    if (opt_num == option_i::errexit) {
      mutable_opts->errexit_disabled_tok->append(nullptr);
    }
  }
  this->mutable_opts = mutable_opts;
  this->opt_nums = opt_nums;
}

ctx_Option::~ctx_Option() {
  for (ListIter<int> it(this->opt_nums); !it.Done(); it.Next()) {
    int opt_num = it.Value();
    if (opt_num == option_i::errexit) {
      this->mutable_opts->errexit_disabled_tok->pop();
    }
    this->mutable_opts->Pop(opt_num);
  }
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_AssignBuiltin::ctx_AssignBuiltin(state::MutableOpts* mutable_opts) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mutable_opts)));
  this->strict = false;
  if (mutable_opts->Get(option_i::strict_errexit)) {
    mutable_opts->Push(option_i::_allow_command_sub, false);
    mutable_opts->Push(option_i::_allow_process_sub, false);
    this->strict = true;
  }
  this->mutable_opts = mutable_opts;
}

ctx_AssignBuiltin::~ctx_AssignBuiltin() {
  if (this->strict) {
    this->mutable_opts->Pop(option_i::_allow_command_sub);
    this->mutable_opts->Pop(option_i::_allow_process_sub);
  }
  gHeap.PopRoot();
}

ctx_YshExpr::ctx_YshExpr(state::MutableOpts* mutable_opts) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mutable_opts)));
  mutable_opts->Push(option_i::command_sub_errexit, true);
  mutable_opts->Push(option_i::errexit, true);
  mutable_opts->Push(option_i::pipefail, true);
  mutable_opts->Push(option_i::inherit_errexit, true);
  mutable_opts->Push(option_i::strict_errexit, true);
  this->mutable_opts = mutable_opts;
}

ctx_YshExpr::~ctx_YshExpr() {
  this->mutable_opts->Pop(option_i::command_sub_errexit);
  this->mutable_opts->Pop(option_i::errexit);
  this->mutable_opts->Pop(option_i::pipefail);
  this->mutable_opts->Pop(option_i::inherit_errexit);
  this->mutable_opts->Pop(option_i::strict_errexit);
  gHeap.PopRoot();
}

ctx_ErrExit::ctx_ErrExit(state::MutableOpts* mutable_opts, bool b, syntax_asdl::Token* disabled_tok) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mutable_opts)));
  mutable_opts->Push(option_i::errexit, b);
  mutable_opts->errexit_disabled_tok->append(disabled_tok);
  this->strict = false;
  if (mutable_opts->Get(option_i::strict_errexit)) {
    mutable_opts->Push(option_i::_allow_command_sub, false);
    mutable_opts->Push(option_i::_allow_process_sub, false);
    this->strict = true;
  }
  this->mutable_opts = mutable_opts;
}

ctx_ErrExit::~ctx_ErrExit() {
  this->mutable_opts->errexit_disabled_tok->pop();
  this->mutable_opts->Pop(option_i::errexit);
  if (this->strict) {
    this->mutable_opts->Pop(option_i::_allow_command_sub);
    this->mutable_opts->Pop(option_i::_allow_process_sub);
  }
  gHeap.PopRoot();
}

OptHook::OptHook() {
  ;  // pass
}

bool OptHook::OnChange(List<bool>* opt0_array, BigStr* opt_name, bool b) {
  StackRoot _root0(&opt0_array);
  StackRoot _root1(&opt_name);

  return true;
}

List<bool>* InitOpts() {
  List<bool>* opt0_array = nullptr;
  StackRoot _root0(&opt0_array);

  opt0_array = list_repeat(false, option_i::ARRAY_SIZE);
  for (ListIter<int> it(consts::DEFAULT_TRUE); !it.Done(); it.Next()) {
    int opt_num = it.Value();
    opt0_array->set(opt_num, true);
  }
  return opt0_array;
}

Tuple3<optview::Parse*, optview::Exec*, state::MutableOpts*> MakeOpts(state::Mem* mem, Dict<BigStr*, BigStr*>* environ, state::OptHook* opt_hook) {
  List<bool>* opt0_array = nullptr;
  List<bool>* no_stack = nullptr;
  List<List<bool>*>* opt_stacks = nullptr;
  optview::Parse* parse_opts = nullptr;
  optview::Exec* exec_opts = nullptr;
  state::MutableOpts* mutable_opts = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&environ);
  StackRoot _root2(&opt_hook);
  StackRoot _root3(&opt0_array);
  StackRoot _root4(&no_stack);
  StackRoot _root5(&opt_stacks);
  StackRoot _root6(&parse_opts);
  StackRoot _root7(&exec_opts);
  StackRoot _root8(&mutable_opts);

  opt0_array = InitOpts();
  no_stack = nullptr;
  opt_stacks = list_repeat(no_stack, option_i::ARRAY_SIZE);
  parse_opts = Alloc<optview::Parse>(opt0_array, opt_stacks);
  exec_opts = Alloc<optview::Exec>(opt0_array, opt_stacks);
  mutable_opts = Alloc<MutableOpts>(mem, environ, opt0_array, opt_stacks, opt_hook);
  return Tuple3<optview::Parse*, optview::Exec*, state::MutableOpts*>(parse_opts, exec_opts, mutable_opts);
}

void _SetGroup(List<bool>* opt0_array, List<int>* opt_nums, bool b) {
  bool b2;
  StackRoot _root0(&opt0_array);
  StackRoot _root1(&opt_nums);

  for (ListIter<int> it(opt_nums); !it.Done(); it.Next()) {
    int opt_num = it.Value();
    b2 = list_contains(consts::DEFAULT_TRUE, opt_num) ? !b : b;
    opt0_array->set(opt_num, b2);
  }
}

optview::Parse* MakeYshParseOpts() {
  List<bool>* opt0_array = nullptr;
  List<bool>* no_stack = nullptr;
  List<List<bool>*>* opt_stacks = nullptr;
  optview::Parse* parse_opts = nullptr;
  StackRoot _root0(&opt0_array);
  StackRoot _root1(&no_stack);
  StackRoot _root2(&opt_stacks);
  StackRoot _root3(&parse_opts);

  opt0_array = InitOpts();
  _SetGroup(opt0_array, consts::YSH_ALL, true);
  no_stack = nullptr;
  opt_stacks = list_repeat(no_stack, option_i::ARRAY_SIZE);
  parse_opts = Alloc<optview::Parse>(opt0_array, opt_stacks);
  return parse_opts;
}

int _AnyOptionNum(BigStr* opt_name, bool ignore_shopt_not_impl) {
  int opt_num;
  StackRoot _root0(&opt_name);

  opt_num = consts::OptionNum(opt_name);
  if (opt_num == 0) {
    if (ignore_shopt_not_impl) {
      opt_num = consts::UnimplOptionNum(opt_name);
    }
    if (opt_num == 0) {
      e_usage(StrFormat("got invalid option %r", opt_name), loc::Missing);
    }
  }
  return opt_num;
}

int _SetOptionNum(BigStr* opt_name) {
  int opt_num;
  StackRoot _root0(&opt_name);

  opt_num = consts::OptionNum(opt_name);
  if (opt_num == 0) {
    e_usage(StrFormat("got invalid option %r", opt_name), loc::Missing);
  }
  if (!list_contains(consts::SET_OPTION_NUMS, opt_num)) {
    e_usage(StrFormat("invalid option %r (try shopt)", opt_name), loc::Missing);
  }
  return opt_num;
}

void _MaybeWarnDotglob() {
  if (HAVE_GLOB_PERIOD == 0) {
    print_stderr(S_lAu);
  }
}

MutableOpts::MutableOpts(state::Mem* mem, Dict<BigStr*, BigStr*>* environ, List<bool>* opt0_array, List<List<bool>*>* opt_stacks, state::OptHook* opt_hook) {
  this->mem = mem;
  this->environ = environ;
  this->opt0_array = opt0_array;
  this->opt_stacks = opt_stacks;
  this->errexit_disabled_tok = Alloc<List<syntax_asdl::Token*>>();
  this->opt_hook = opt_hook;
}

void MutableOpts::Init() {
  value_asdl::value_t* shellopts = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&shellopts);
  StackRoot _root1(&s);

  shellopts = this->mem->GetValue(S_cvm);
  if (shellopts->tag() == value_e::Str) {
    s = static_cast<value::Str*>(shellopts)->s;
    this->_InitOptionsFromEnv(s);
  }
}

void MutableOpts::_InitOptionsFromEnv(BigStr* shellopts) {
  List<BigStr*>* lookup = nullptr;
  BigStr* name = nullptr;
  StackRoot _root0(&shellopts);
  StackRoot _root1(&lookup);
  StackRoot _root2(&name);

  lookup = shellopts->split(S_fyj);
  for (ListIter<int> it(consts::SET_OPTION_NUMS); !it.Done(); it.Next()) {
    int opt_num = it.Value();
    name = consts::OptionName(opt_num);
    if (list_contains(lookup, name)) {
      this->_SetOldOption(name, true);
    }
  }
}

void MutableOpts::Push(int opt_num, bool b) {
  List<bool>* overlay = nullptr;
  StackRoot _root0(&overlay);

  if (opt_num == option_i::dotglob) {
    _MaybeWarnDotglob();
  }
  overlay = this->opt_stacks->at(opt_num);
  if ((overlay == nullptr or len(overlay) == 0)) {
    this->opt_stacks->set(opt_num, NewList<bool>(std::initializer_list<bool>{b}));
  }
  else {
    overlay->append(b);
  }
}

bool MutableOpts::Pop(int opt_num) {
  List<bool>* overlay = nullptr;
  StackRoot _root0(&overlay);

  overlay = this->opt_stacks->at(opt_num);
  return overlay->pop();
}

void MutableOpts::PushDynamicScope(bool b) {
  if (!this->Get(option_i::dynamic_scope)) {
    b = false;
  }
  this->Push(option_i::dynamic_scope, b);
}

void MutableOpts::PopDynamicScope() {
  this->Pop(option_i::dynamic_scope);
}

bool MutableOpts::Get(int opt_num) {
  List<bool>* overlay = nullptr;
  StackRoot _root0(&overlay);

  overlay = this->opt_stacks->at(opt_num);
  if ((overlay == nullptr or len(overlay) == 0)) {
    return this->opt0_array->at(opt_num);
  }
  else {
    return overlay->at(-1);
  }
}

void MutableOpts::_Set(int opt_num, bool b) {
  List<bool>* overlay = nullptr;
  StackRoot _root0(&overlay);

  if (opt_num == option_i::dotglob) {
    _MaybeWarnDotglob();
  }
  overlay = this->opt_stacks->at(opt_num);
  if ((overlay == nullptr or len(overlay) == 0)) {
    this->opt0_array->set(opt_num, b);
  }
  else {
    overlay->set(-1, b);
  }
}

void MutableOpts::set_interactive() {
  this->_Set(option_i::interactive, true);
}

void MutableOpts::set_redefine_const() {
  this->_Set(option_i::redefine_const, true);
}

void MutableOpts::set_redefine_source() {
  this->_Set(option_i::redefine_source, true);
}

void MutableOpts::set_emacs() {
  this->_Set(option_i::emacs, true);
}

void MutableOpts::_SetArrayByNum(int opt_num, bool b) {
  if ((list_contains(consts::PARSE_OPTION_NUMS, opt_num) and !this->mem->ParsingChangesAllowed())) {
    e_die(S_CrF);
  }
  this->_Set(opt_num, b);
}

void MutableOpts::SetDeferredErrExit(bool b) {
  this->opt0_array->set(option_i::errexit, b);
}

void MutableOpts::DisableErrExit() {
  this->_Set(option_i::errexit, false);
}

syntax_asdl::Token* MutableOpts::ErrExitDisabledToken() {
  if (this->Get(option_i::_running_trap)) {
    return nullptr;
  }
  if (len(this->errexit_disabled_tok) == 0) {
    return nullptr;
  }
  return this->errexit_disabled_tok->at(-1);
}

bool MutableOpts::ErrExitIsDisabled() {
  if (len(this->errexit_disabled_tok) == 0) {
    return false;
  }
  return this->errexit_disabled_tok->at(-1) != nullptr;
}

void MutableOpts::_SetOldOption(BigStr* opt_name, bool b) {
  int opt_num;
  bool success;
  StackRoot _root0(&opt_name);

  opt_num = consts::OptionNum(opt_name);
  if (opt_num == option_i::errexit) {
    this->SetDeferredErrExit(b);
  }
  else {
    if ((opt_num == option_i::verbose and b)) {
      print_stderr(S_nAz);
    }
    this->_SetArrayByNum(opt_num, b);
  }
  success = this->opt_hook->OnChange(this->opt0_array, opt_name, b);
}

void MutableOpts::SetOldOption(BigStr* opt_name, bool b) {
  int unused;
  (void)unused;
  value_asdl::value_t* UP_val = nullptr;
  BigStr* shellopts = nullptr;
  value::Str* new_val = nullptr;
  List<BigStr*>* names = nullptr;
  StackRoot _root0(&opt_name);
  StackRoot _root1(&UP_val);
  StackRoot _root2(&shellopts);
  StackRoot _root3(&new_val);
  StackRoot _root4(&names);

  unused = _SetOptionNum(opt_name);
  this->_SetOldOption(opt_name, b);
  if (!this->Get(option_i::no_init_globals)) {
    UP_val = this->mem->GetValue(S_cvm);
    value::Str* val = static_cast<value::Str*>(UP_val);
    shellopts = val->s;
    if (b) {
      if (!str_contains(shellopts, opt_name)) {
        new_val = Alloc<value::Str>(StrFormat("%s:%s", shellopts, opt_name));
        this->mem->InternalSetGlobal(S_cvm, new_val);
      }
    }
    else {
      if (str_contains(shellopts, opt_name)) {
        names = Alloc<List<BigStr*>>();
        for (ListIter<BigStr*> it(shellopts->split(S_fyj)); !it.Done(); it.Next()) {
          BigStr* n = it.Value();
          if (!(str_equals(n, opt_name))) {
            names->append(n);
          }
        }
        new_val = Alloc<value::Str>(S_fyj->join(names));
        this->mem->InternalSetGlobal(S_cvm, new_val);
      }
    }
  }
}

void MutableOpts::SetAnyOption(BigStr* opt_name, bool b, bool ignore_shopt_not_impl) {
  int opt_group;
  int opt_num;
  StackRoot _root0(&opt_name);

  opt_group = consts::OptionGroupNum(opt_name);
  if (opt_group == opt_group_i::YshUpgrade) {
    _SetGroup(this->opt0_array, consts::YSH_UPGRADE, b);
    this->SetDeferredErrExit(b);
    if (b) {
      this->mem->MaybeInitEnvDict(this->environ);
    }
    return ;
  }
  if (opt_group == opt_group_i::YshAll) {
    _SetGroup(this->opt0_array, consts::YSH_ALL, b);
    this->SetDeferredErrExit(b);
    if (b) {
      this->mem->MaybeInitEnvDict(this->environ);
    }
    return ;
  }
  if (opt_group == opt_group_i::StrictAll) {
    _SetGroup(this->opt0_array, consts::STRICT_ALL, b);
    return ;
  }
  opt_num = _AnyOptionNum(opt_name, ignore_shopt_not_impl);
  if (opt_num == option_i::errexit) {
    this->SetDeferredErrExit(b);
    return ;
  }
  this->_SetArrayByNum(opt_num, b);
}

_ArgFrame::_ArgFrame(List<BigStr*>* argv) {
  this->argv = argv;
  this->num_shifted = 0;
}

Dict<BigStr*, value_asdl::value_t*>* _ArgFrame::Dump() {
  List<value_asdl::value_t*>* items = nullptr;
  value::List* argv = nullptr;
  StackRoot _root0(&items);
  StackRoot _root1(&argv);

  items = Alloc<List<value_asdl::value_t*>>();
  for (ListIter<BigStr*> it(this->argv); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    items->append(Alloc<value::Str>(s));
  }
  argv = Alloc<value::List>(items);
  return Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_esE, S_owh}, std::initializer_list<value_asdl::value_t*>{argv, num::ToBig(this->num_shifted)});
}

value_asdl::value_t* _ArgFrame::GetArgNum(int arg_num) {
  int index;
  index = ((this->num_shifted + arg_num) - 1);
  if (index >= len(this->argv)) {
    return value::Undef;
  }
  return Alloc<value::Str>(this->argv->at(index));
}

List<BigStr*>* _ArgFrame::GetArgv() {
  return this->argv->slice(this->num_shifted);
}

int _ArgFrame::GetNumArgs() {
  return (len(this->argv) - this->num_shifted);
}

void _ArgFrame::SetArgv(List<BigStr*>* argv) {
  StackRoot _root0(&argv);

  this->argv = argv;
  this->num_shifted = 0;
}

Dict<BigStr*, value_asdl::value_t*>* _DumpVarFrame(Dict<BigStr*, runtime_asdl::Cell*>* frame) {
  Dict<BigStr*, value_asdl::value_t*>* vars_json = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* cell_json = nullptr;
  mylib::BufWriter* buf = nullptr;
  BigStr* flags = nullptr;
  StackRoot _root0(&frame);
  StackRoot _root1(&vars_json);
  StackRoot _root2(&cell_json);
  StackRoot _root3(&buf);
  StackRoot _root4(&flags);

  vars_json = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  for (DictIter<BigStr*, runtime_asdl::Cell*> it(frame); !it.Done(); it.Next()) {
    BigStr* name = it.Key();
    runtime_asdl::Cell* cell = it.Value();
    cell_json = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
    buf = Alloc<mylib::BufWriter>();
    if (cell->exported) {
      buf->write(S_rqD);
    }
    if (cell->readonly) {
      buf->write(S_nAr_1);
    }
    flags = buf->getvalue();
    if (len(flags)) {
      cell_json->set(S_boy, Alloc<value::Str>(flags));
    }
    switch (cell->val->tag()) {
      case value_e::Undef: {
        cell_json->set(S_zrD, value::Null);
      }
        break;
      case value_e::Str: 
      case value_e::BashArray: 
      case value_e::BashAssoc: 
      case value_e::SparseArray: {
        cell_json->set(S_zrD, cell->val);
      }
        break;
      default: {
        ;  // pass
      }
    }
    vars_json->set(name, Alloc<value::Dict>(cell_json));
  }
  return vars_json;
}

BigStr* _LineNumber(syntax_asdl::Token* tok) {
  StackRoot _root0(&tok);

  if (tok == nullptr) {
    return S_zdb;
  }
  return str(tok->line->line_num);
}

void _AddCallToken(Dict<BigStr*, value_asdl::value_t*>* d, syntax_asdl::Token* token) {
  StackRoot _root0(&d);
  StackRoot _root1(&token);

  if (token == nullptr) {
    return ;
  }
  d->set(S_ogo, Alloc<value::Str>(ui::GetLineSourceString(token->line)));
  d->set(S_eqz, num::ToBig(token->line->line_num));
  d->set(S_tzb, Alloc<value::Str>(token->line->content));
}

ctx_FuncCall::ctx_FuncCall(state::Mem* mem, value::Func* func) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->saved_globals)));
  this->saved_globals = mem->var_stack->at(0);
  mem->var_stack->set(0, func->module_frame);
  auto* frame = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
  mem->var_stack->append(frame);
  mem->PushCall(func->name, func->parsed->name);
  this->mem = mem;
}

ctx_FuncCall::~ctx_FuncCall() {
  this->mem->PopCall();
  this->mem->var_stack->pop();
  this->mem->var_stack->set(0, this->saved_globals);
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_ProcCall::ctx_ProcCall(state::Mem* mem, state::MutableOpts* mutable_opts, value::Proc* proc, List<BigStr*>* argv) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mutable_opts)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->saved_globals)));
  this->saved_globals = mem->var_stack->at(0);
  mem->var_stack->set(0, proc->module_frame);
  auto* frame = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
  if (proc->sh_compat) {
    mem->argv_stack->append(Alloc<_ArgFrame>(argv));
  }
  else {
    frame->set(S_wjA, _MakeArgvCell(argv));
  }
  mem->var_stack->append(frame);
  mem->PushCall(proc->name, proc->name_tok);
  mutable_opts->PushDynamicScope(proc->sh_compat);
  this->mem = mem;
  this->mutable_opts = mutable_opts;
  this->sh_compat = proc->sh_compat;
}

ctx_ProcCall::~ctx_ProcCall() {
  this->mutable_opts->PopDynamicScope();
  this->mem->PopCall();
  this->mem->var_stack->pop();
  if (this->sh_compat) {
    this->mem->argv_stack->pop();
  }
  this->mem->var_stack->set(0, this->saved_globals);
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_Temp::ctx_Temp(state::Mem* mem) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  this->mem = mem;
  mem->PushTemp();
}

ctx_Temp::~ctx_Temp() {
  this->mem->PopTemp();
  gHeap.PopRoot();
}

ctx_EnvObj::ctx_EnvObj(state::Mem* mem, Dict<BigStr*, value_asdl::value_t*>* bindings) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  this->mem = mem;
  mem->PushEnvObj(bindings);
}

ctx_EnvObj::~ctx_EnvObj() {
  this->mem->PopEnvObj();
  gHeap.PopRoot();
}

ctx_Registers::ctx_Registers(state::Mem* mem) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  int last = mem->last_status->at(-1);
  mem->last_status->append(last);
  mem->try_status->append(0);
  mem->try_error->append(Alloc<value::Dict>(Alloc<Dict<BigStr*, value_asdl::value_t*>>()));
  mem->pipe_status->append(Alloc<List<int>>());
  mem->process_sub_status->append(Alloc<List<int>>());
  mem->regex_match->append(regex_match::No);
  this->mem = mem;
}

ctx_Registers::~ctx_Registers() {
  this->mem->regex_match->pop();
  this->mem->process_sub_status->pop();
  this->mem->pipe_status->pop();
  this->mem->try_error->pop();
  this->mem->try_status->pop();
  this->mem->last_status->pop();
  gHeap.PopRoot();
}

ctx_ThisDir::ctx_ThisDir(state::Mem* mem, BigStr* filename) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  this->do_pop = false;
  if (filename != nullptr) {
    BigStr* d = os_path::dirname(os_path::abspath(filename));
    mem->this_dir->append(d);
    this->do_pop = true;
  }
  this->mem = mem;
}

ctx_ThisDir::~ctx_ThisDir() {
  if (this->do_pop) {
    this->mem->this_dir->pop();
  }
  gHeap.PopRoot();
}

runtime_asdl::Cell* _MakeArgvCell(List<BigStr*>* argv) {
  List<value_asdl::value_t*>* items = nullptr;
  StackRoot _root0(&argv);
  StackRoot _root1(&items);

  items = Alloc<List<value_asdl::value_t*>>();
  for (ListIter<BigStr*> it(argv); !it.Done(); it.Next()) {
    BigStr* a = it.Value();
    items->append(Alloc<value::Str>(a));
  }
  return Alloc<Cell>(false, false, false, Alloc<value::List>(items));
}

ctx_LoopFrame::ctx_LoopFrame(state::Mem* mem, BigStr* name1) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->name1)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->new_frame)));
  this->mem = mem;
  this->name1 = name1;
  this->do_new_frame = str_equals(name1, S_EBt);
  if (this->do_new_frame) {
    Dict<BigStr*, runtime_asdl::Cell*>* to_enclose = this->mem->var_stack->at(-1);
    this->new_frame = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
    this->new_frame->set(S_hub, Alloc<Cell>(false, false, false, Alloc<value::Frame>(to_enclose)));
    mem->var_stack->append(this->new_frame);
  }
}

ctx_LoopFrame::~ctx_LoopFrame() {
  if (this->do_new_frame) {
    this->mem->var_stack->pop();
  }
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_EnclosedFrame::ctx_EnclosedFrame(state::Mem* mem, Dict<BigStr*, runtime_asdl::Cell*>* to_enclose, Dict<BigStr*, runtime_asdl::Cell*>* module_frame, Dict<BigStr*, value_asdl::value_t*>* out_dict) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->module_frame)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->new_frame)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->out_dict)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->saved_globals)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->to_enclose)));
  this->mem = mem;
  this->to_enclose = to_enclose;
  this->module_frame = module_frame;
  this->out_dict = out_dict;
  if (module_frame != nullptr) {
    this->saved_globals = this->mem->var_stack->at(0);
    this->mem->var_stack->set(0, module_frame);
  }
  this->new_frame = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
  this->new_frame->set(S_hub, Alloc<Cell>(false, false, false, Alloc<value::Frame>(to_enclose)));
  mem->var_stack->append(this->new_frame);
}

ctx_EnclosedFrame::~ctx_EnclosedFrame() {
  if (this->out_dict != nullptr) {
    for (DictIter<BigStr*, runtime_asdl::Cell*> it(this->new_frame); !it.Done(); it.Next()) {
      BigStr* name = it.Key();
      runtime_asdl::Cell* cell = it.Value();
      if (name->endswith(S_tci)) {
        continue;
      }
      this->out_dict->set(name, cell->val);
    }
  }
  this->mem->var_stack->pop();
  if (this->module_frame != nullptr) {
    this->mem->var_stack->set(0, this->saved_globals);
  }
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_ModuleEval::ctx_ModuleEval(state::Mem* mem, Dict<BigStr*, value_asdl::value_t*>* out_dict, List<BigStr*>* out_errors) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->new_frame)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->out_dict)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->out_errors)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->saved_frame)));
  this->mem = mem;
  this->out_dict = out_dict;
  this->out_errors = out_errors;
  this->new_frame = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
  this->saved_frame = mem->var_stack->at(0);
  runtime_asdl::Cell* ps4 = this->saved_frame->get(S_zyo);
  if (ps4) {
    this->new_frame->set(S_zyo, ps4);
  }
  runtime_asdl::Cell* env = this->saved_frame->get(S_iyA);
  if (env) {
    this->new_frame->set(S_iyA, env);
  }
  mem->var_stack->set(0, this->new_frame);
  this->to_restore = this->mem->is_main;
  this->mem->is_main = false;
}

ctx_ModuleEval::~ctx_ModuleEval() {
  this->mem->is_main = this->to_restore;
  this->mem->var_stack->set(0, this->saved_frame);
  runtime_asdl::Cell* cell = this->new_frame->get(S_zcz);
  if (cell == nullptr) {
    this->out_errors->append(S_ecq);
    return ;
  }
  value_asdl::value_t* provide_val = cell->val;
  switch (provide_val->tag()) {
    case value_e::List: {
      for (ListIter<value_asdl::value_t*> it(static_cast<value::List*>(provide_val)->items); !it.Done(); it.Next()) {
        value_asdl::value_t* val = it.Value();
        StackRoot _for(&val      );
        if (val->tag() == value_e::Str) {
          BigStr* name = static_cast<value::Str*>(val)->s;
          runtime_asdl::Cell* cell = this->new_frame->get(name);
          if (cell == nullptr) {
            this->out_errors->append(StrFormat("Name %r was provided, but not defined", name));
            continue;
          }
          this->out_dict->set(name, cell->val);
        }
        else {
          this->out_errors->append(StrFormat("Expected Str in __provide__ List, got %s", ui::ValType(val)));
        }
      }
    }
      break;
    default: {
      this->out_errors->append(StrFormat("__provide__ should be a List, got %s", ui::ValType(provide_val)));
    }
  }
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
}

ctx_Eval::ctx_Eval(state::Mem* mem, BigStr* dollar0, List<BigStr*>* pos_args, Dict<BigStr*, value_asdl::value_t*>* vars) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->dollar0)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->mem)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->pos_args)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->restore)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->restore_dollar0)));
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->vars)));
  this->mem = mem;
  this->dollar0 = dollar0;
  this->pos_args = pos_args;
  this->vars = vars;
  if (dollar0 != nullptr) {
    this->restore_dollar0 = this->mem->dollar0;
    this->mem->dollar0 = dollar0;
  }
  if (pos_args != nullptr) {
    mem->argv_stack->append(Alloc<_ArgFrame>(pos_args));
  }
  if (vars != nullptr) {
    this->restore = Alloc<List<Tuple2<value_asdl::LeftName*, value_asdl::value_t*>*>>();
    this->_Push(vars);
  }
}

ctx_Eval::~ctx_Eval() {
  if (this->vars != nullptr) {
    this->_Pop();
  }
  if (this->pos_args != nullptr) {
    this->mem->argv_stack->pop();
  }
  if (this->dollar0 != nullptr) {
    this->mem->dollar0 = this->restore_dollar0;
  }
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
  gHeap.PopRoot();
}

void ctx_Eval::_Push(Dict<BigStr*, value_asdl::value_t*>* vars) {
  value_asdl::LeftName* lval = nullptr;
  value_asdl::value_t* old_val = nullptr;
  StackRoot _root0(&vars);
  StackRoot _root1(&lval);
  StackRoot _root2(&old_val);

  for (DictIter<BigStr*, value_asdl::value_t*> it(vars); !it.Done(); it.Next()) {
    BigStr* name = it.Key();
    StackRoot _for(&name  );
    lval = location::LName(name);
    old_val = this->mem->GetValue(name, scope_e::LocalOnly);
    this->restore->append((Alloc<Tuple2<value_asdl::LeftName*, value_asdl::value_t*>>(lval, old_val)));
    this->mem->SetNamed(lval, vars->at(name), scope_e::LocalOnly);
  }
}

void ctx_Eval::_Pop() {
  for (ListIter<Tuple2<value_asdl::LeftName*, value_asdl::value_t*>*> it(this->restore); !it.Done(); it.Next()) {
    Tuple2<value_asdl::LeftName*, value_asdl::value_t*>* tup0 = it.Value();
    value_asdl::LeftName* lval = tup0->at0();
    StackRoot _unpack_0(&lval);
    value_asdl::value_t* old_val = tup0->at1();
    StackRoot _unpack_1(&old_val);
    if (old_val->tag() == value_e::Undef) {
      this->mem->Unset(lval, scope_e::LocalOnly);
    }
    else {
      this->mem->SetNamed(lval, old_val, scope_e::LocalOnly);
    }
  }
}

Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> _FrameLookup(Dict<BigStr*, runtime_asdl::Cell*>* frame, BigStr* name) {
  runtime_asdl::Cell* cell = nullptr;
  runtime_asdl::Cell* rear_cell = nullptr;
  value_asdl::value_t* rear_val = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* to_enclose = nullptr;
  StackRoot _root0(&frame);
  StackRoot _root1(&name);
  StackRoot _root2(&cell);
  StackRoot _root3(&rear_cell);
  StackRoot _root4(&rear_val);
  StackRoot _root5(&to_enclose);

  cell = frame->get(name);
  if (cell) {
    return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(cell, frame);
  }
  rear_cell = frame->get(S_hub);
  if (rear_cell) {
    rear_val = rear_cell->val;
    if (rear_val->tag() == value_e::Frame) {
      to_enclose = static_cast<value::Frame*>(rear_val)->frame;
      return _FrameLookup(to_enclose, name);
    }
  }
  return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(nullptr, nullptr);
}

Mem::Mem(BigStr* dollar0, List<BigStr*>* argv, alloc::Arena* arena, List<syntax_asdl::debug_frame_t*>* debug_stack, Dict<BigStr*, value_asdl::value_t*>* env_dict, Dict<BigStr*, value_asdl::value_t*>* defaults) {
  this->exec_opts = nullptr;
  this->unsafe_arith = nullptr;
  this->dollar0 = dollar0;
  this->argv_stack = NewList<state::_ArgFrame*>(std::initializer_list<state::_ArgFrame*>{Alloc<_ArgFrame>(argv)});
  auto* frame = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
  frame->set(S_wjA, _MakeArgvCell(argv));
  this->var_stack = NewList<Dict<BigStr*, runtime_asdl::Cell*>*>(std::initializer_list<Dict<BigStr*, runtime_asdl::Cell*>*>{frame});
  this->debug_stack = debug_stack;
  this->env_dict = env_dict;
  this->env_object = Alloc<Obj>(nullptr, env_dict);
  if (defaults == nullptr) {
    this->defaults = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  }
  else {
    this->defaults = defaults;
  }
  this->pwd = nullptr;
  this->seconds_start = time_::time();
  this->token_for_line = nullptr;
  this->loc_for_expr = loc::Missing;
  this->last_arg = S_Aoo;
  this->line_num = Alloc<value::Str>(S_Aoo);
  this->root_pid = posix::getpid();
  this->last_status = NewList<int>(std::initializer_list<int>{0});
  this->try_status = NewList<int>(std::initializer_list<int>{0});
  this->try_error = NewList<value::Dict*>(std::initializer_list<value::Dict*>{Alloc<value::Dict>(Alloc<Dict<BigStr*, value_asdl::value_t*>>())});
  this->pipe_status = NewList<List<int>*>(std::initializer_list<List<int>*>{Alloc<List<int>>()});
  this->process_sub_status = NewList<List<int>*>(std::initializer_list<List<int>*>{Alloc<List<int>>()});
  this->this_dir = Alloc<List<BigStr*>>();
  this->regex_match = NewList<value_asdl::regex_match_t*>(std::initializer_list<value_asdl::regex_match_t*>{regex_match::No});
  this->last_bg_pid = -1;
  this->running_debug_trap = false;
  this->running_err_trap = false;
  this->is_main = true;
  this->ctx_stack = Alloc<List<Dict<BigStr*, value_asdl::value_t*>*>>();
  this->builtins = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  value_asdl::Obj* builtins_module = Alloc<Obj>(nullptr, this->builtins);
  this->builtins->set(S_mmF, builtins_module);
  this->did_ysh_env = false;
  this->env_config = Alloc<sh_init::EnvConfig>(this, defaults);
}

void Mem::AddBuiltin(BigStr* name, value_asdl::value_t* val) {
  StackRoot _root0(&name);
  StackRoot _root1(&val);

  this->builtins->set(name, val);
}

void Mem::SetPwd(BigStr* pwd) {
  StackRoot _root0(&pwd);

  this->pwd = pwd;
}

bool Mem::ParsingChangesAllowed() {
  return (len(this->var_stack) == 1 or len(this->argv_stack) == 1);
}

Tuple3<List<value_asdl::value_t*>*, List<value_asdl::value_t*>*, List<value_asdl::value_t*>*> Mem::Dump() {
  List<value_asdl::value_t*>* var_stack = nullptr;
  List<value_asdl::value_t*>* argv_stack = nullptr;
  List<value_asdl::value_t*>* debug_stack = nullptr;
  value::Str* t_call = nullptr;
  value::Str* t_source = nullptr;
  value::Str* t_main = nullptr;
  syntax_asdl::debug_frame_t* UP_frame = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  StackRoot _root0(&var_stack);
  StackRoot _root1(&argv_stack);
  StackRoot _root2(&debug_stack);
  StackRoot _root3(&t_call);
  StackRoot _root4(&t_source);
  StackRoot _root5(&t_main);
  StackRoot _root6(&UP_frame);
  StackRoot _root7(&d);

  var_stack = Alloc<List<value_asdl::value_t*>>();
  for (ListIter<Dict<BigStr*, runtime_asdl::Cell*>*> it(this->var_stack); !it.Done(); it.Next()) {
    Dict<BigStr*, runtime_asdl::Cell*>* frame = it.Value();
    var_stack->append(Alloc<value::Dict>(_DumpVarFrame(frame)));
  }
  argv_stack = Alloc<List<value_asdl::value_t*>>();
  for (ListIter<state::_ArgFrame*> it(this->argv_stack); !it.Done(); it.Next()) {
    state::_ArgFrame* frame = it.Value();
    argv_stack->append(Alloc<value::Dict>(frame->Dump()));
  }
  debug_stack = Alloc<List<value_asdl::value_t*>>();
  t_call = Alloc<value::Str>(S_jrg);
  t_source = Alloc<value::Str>(S_nli);
  t_main = Alloc<value::Str>(S_cdp);
  for (ReverseListIter<syntax_asdl::debug_frame_t*> it(this->debug_stack); !it.Done(); it.Next()) {
    syntax_asdl::debug_frame_t* frame = it.Value();
    StackRoot _for(&frame  );
    UP_frame = frame;
    switch (frame->tag()) {
      case debug_frame_e::Call: {
        debug_frame::Call* frame = static_cast<debug_frame::Call*>(UP_frame);
        d = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_qEi, S_rwo}, std::initializer_list<value_asdl::value_t*>{t_call, Alloc<value::Str>(frame->func_name)});
        _AddCallToken(d, frame->call_tok);
      }
        break;
      case debug_frame_e::Source: {
        debug_frame::Source* frame = static_cast<debug_frame::Source*>(UP_frame);
        d = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_qEi, S_gxr}, std::initializer_list<value_asdl::value_t*>{t_source, Alloc<value::Str>(frame->source_name)});
        _AddCallToken(d, frame->call_tok);
      }
        break;
      case debug_frame_e::Main: {
        debug_frame::Main* frame = static_cast<debug_frame::Main*>(UP_frame);
        d = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_qEi, S_Bxy}, std::initializer_list<value_asdl::value_t*>{t_main, Alloc<value::Str>(frame->dollar0)});
      }
        break;
    }
    debug_stack->append(Alloc<value::Dict>(d));
  }
  return Tuple3<List<value_asdl::value_t*>*, List<value_asdl::value_t*>*, List<value_asdl::value_t*>*>(var_stack, argv_stack, debug_stack);
}

void Mem::SetLastArgument(BigStr* s) {
  StackRoot _root0(&s);

  this->last_arg = s;
}

void Mem::SetTokenForLine(syntax_asdl::Token* tok) {
  StackRoot _root0(&tok);

  if ((this->running_debug_trap or this->running_err_trap)) {
    return ;
  }
  this->loc_for_expr = loc::Missing;
  this->token_for_line = tok;
}

void Mem::SetLocationForExpr(syntax_asdl::loc_t* blame_loc) {
  StackRoot _root0(&blame_loc);

  this->loc_for_expr = blame_loc;
}

syntax_asdl::loc_t* Mem::GetFallbackLocation() {
  if (this->loc_for_expr != loc::Missing) {
    return this->loc_for_expr;
  }
  if (this->token_for_line) {
    return this->token_for_line;
  }
  return loc::Missing;
}

int Mem::LastStatus() {
  return this->last_status->at(-1);
}

int Mem::TryStatus() {
  return this->try_status->at(-1);
}

value::Dict* Mem::TryError() {
  return this->try_error->at(-1);
}

List<int>* Mem::PipeStatus() {
  return this->pipe_status->at(-1);
}

void Mem::SetLastStatus(int x) {
  this->last_status->set(-1, x);
}

void Mem::SetTryStatus(int x) {
  this->try_status->set(-1, x);
}

void Mem::SetTryError(value::Dict* x) {
  StackRoot _root0(&x);

  this->try_error->set(-1, x);
}

void Mem::SetPipeStatus(List<int>* x) {
  StackRoot _root0(&x);

  this->pipe_status->set(-1, x);
}

void Mem::SetSimplePipeStatus(int status) {
  List<int>* top = nullptr;
  StackRoot _root0(&top);

  top = this->pipe_status->at(-1);
  if (len(top) == 1) {
    top->set(0, status);
  }
  else {
    this->pipe_status->set(-1, NewList<int>(std::initializer_list<int>{status}));
  }
}

void Mem::SetProcessSubStatus(List<int>* x) {
  StackRoot _root0(&x);

  this->process_sub_status->set(-1, x);
}

void Mem::PushCall(BigStr* func_name, syntax_asdl::Token* def_tok) {
  StackRoot _root0(&func_name);
  StackRoot _root1(&def_tok);

  this->debug_stack->append(Alloc<debug_frame::Call>(this->token_for_line, def_tok, func_name));
}

void Mem::PopCall() {
  this->debug_stack->pop();
}

bool Mem::ShouldRunDebugTrap() {
  if (this->running_debug_trap) {
    return false;
  }
  if (len(this->var_stack) > 1) {
    return false;
  }
  return true;
}

bool Mem::IsGlobalScope() {
  return len(this->var_stack) == 1;
}

bool Mem::InsideFunction() {
  return len(this->var_stack) > 1;
}

Dict<BigStr*, runtime_asdl::Cell*>* Mem::GlobalFrame() {
  return this->var_stack->at(0);
}

Dict<BigStr*, runtime_asdl::Cell*>* Mem::CurrentFrame() {
  return this->var_stack->at(-1);
}

void Mem::PushSource(BigStr* source_name, List<BigStr*>* argv) {
  StackRoot _root0(&source_name);
  StackRoot _root1(&argv);

  if (len(argv)) {
    this->argv_stack->append(Alloc<_ArgFrame>(argv));
  }
  this->debug_stack->append(Alloc<debug_frame::Source>(this->token_for_line, source_name));
}

void Mem::PopSource(List<BigStr*>* argv) {
  StackRoot _root0(&argv);

  this->debug_stack->pop();
  if (len(argv)) {
    this->argv_stack->pop();
  }
}

void Mem::PushTemp() {
  Dict<BigStr*, runtime_asdl::Cell*>* frame = nullptr;
  StackRoot _root0(&frame);

  frame = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
  this->var_stack->append(frame);
}

void Mem::PopTemp() {
  this->var_stack->pop();
}

void Mem::_BindEnvObj() {
  this->SetNamed(location::LName(S_iyA), this->env_object, scope_e::GlobalOnly);
}

void Mem::MaybeInitEnvDict(Dict<BigStr*, BigStr*>* environ) {
  StackRoot _root0(&environ);

  if (this->did_ysh_env) {
    return ;
  }
  for (DictIter<BigStr*, BigStr*> it(environ); !it.Done(); it.Next()) {
    BigStr* name = it.Key();
    BigStr* s = it.Value();
    this->env_dict->set(name, Alloc<value::Str>(s));
  }
  this->_BindEnvObj();
  this->did_ysh_env = true;
}

void Mem::PushEnvObj(Dict<BigStr*, value_asdl::value_t*>* bindings) {
  StackRoot _root0(&bindings);

  this->env_object = Alloc<Obj>(this->env_object, bindings);
  this->_BindEnvObj();
}

void Mem::PopEnvObj() {
  this->env_object = this->env_object->prototype;
  if (this->env_object == nullptr) {
    e_die(S_gur, loc::Missing);
  }
  this->_BindEnvObj();
}

int Mem::Shift(int n) {
  state::_ArgFrame* frame = nullptr;
  int num_args;
  StackRoot _root0(&frame);

  frame = this->argv_stack->at(-1);
  num_args = len(frame->argv);
  if ((frame->num_shifted + n) <= num_args) {
    frame->num_shifted += n;
    return 0;
  }
  else {
    return 1;
  }
}

value::Str* Mem::GetArg0() {
  return Alloc<value::Str>(this->dollar0);
}

value_asdl::value_t* Mem::GetArgNum(int arg_num) {
  if (arg_num == 0) {
    return Alloc<value::Str>(this->dollar0);
  }
  return this->argv_stack->at(-1)->GetArgNum(arg_num);
}

List<BigStr*>* Mem::GetArgv() {
  return this->argv_stack->at(-1)->GetArgv();
}

void Mem::SetArgv(List<BigStr*>* argv) {
  StackRoot _root0(&argv);

  this->argv_stack->at(-1)->SetArgv(argv);
}

value_asdl::value_t* Mem::GetSpecialVar(int op_id) {
  int n;
  if (op_id == Id::VSub_Bang) {
    n = this->last_bg_pid;
    if (n == -1) {
      return value::Undef;
    }
  }
  else {
    if (op_id == Id::VSub_QMark) {
      n = this->last_status->at(-1);
    }
    else {
      if (op_id == Id::VSub_Pound) {
        n = this->argv_stack->at(-1)->GetNumArgs();
      }
      else {
        if (op_id == Id::VSub_Dollar) {
          n = this->root_pid;
        }
        else {
          FAIL(kNotImplemented);  // Python NotImplementedError
        }
      }
    }
  }
  return Alloc<value::Str>(str(n));
}

Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> Mem::_ResolveNameOnly(BigStr* name, runtime_asdl::scope_t which_scopes) {
  Dict<BigStr*, runtime_asdl::Cell*>* var_frame = nullptr;
  runtime_asdl::Cell* cell = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* result_frame = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&var_frame);
  StackRoot _root2(&cell);
  StackRoot _root3(&result_frame);

  if (which_scopes == scope_e::Dynamic) {
    for (int i = (len(this->var_stack) - 1); i > -1; i += -1) {
      var_frame = this->var_stack->at(i);
      Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup1 = _FrameLookup(var_frame, name);
      cell = tup1.at0();
      result_frame = tup1.at1();
      if (cell) {
        return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(cell, result_frame);
      }
    }
    return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(nullptr, this->var_stack->at(0));
  }
  if (which_scopes == scope_e::LocalOnly) {
    var_frame = this->var_stack->at(-1);
    Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup2 = _FrameLookup(var_frame, name);
    cell = tup2.at0();
    result_frame = tup2.at1();
    if (cell) {
      return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(cell, result_frame);
    }
    return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(nullptr, var_frame);
  }
  if (which_scopes == scope_e::GlobalOnly) {
    var_frame = this->var_stack->at(0);
    Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup3 = _FrameLookup(var_frame, name);
    cell = tup3.at0();
    result_frame = tup3.at1();
    if (cell) {
      return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(cell, result_frame);
    }
    return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(nullptr, var_frame);
  }
  if (which_scopes == scope_e::LocalOrGlobal) {
    var_frame = this->var_stack->at(-1);
    Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup4 = _FrameLookup(var_frame, name);
    cell = tup4.at0();
    result_frame = tup4.at1();
    if (cell) {
      return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(cell, result_frame);
    }
    var_frame = this->var_stack->at(0);
    Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup5 = _FrameLookup(var_frame, name);
    cell = tup5.at0();
    result_frame = tup5.at1();
    if (cell) {
      return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(cell, result_frame);
    }
    return Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*>(nullptr, var_frame);
  }
  assert(0);  // AssertionError
}

Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> Mem::_ResolveNameOrRef(BigStr* name, runtime_asdl::scope_t which_scopes, List<BigStr*>* ref_trail) {
  runtime_asdl::Cell* cell = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* var_frame = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  BigStr* new_name = nullptr;
  BigStr* cell_name = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&ref_trail);
  StackRoot _root2(&cell);
  StackRoot _root3(&var_frame);
  StackRoot _root4(&val);
  StackRoot _root5(&UP_val);
  StackRoot _root6(&new_name);
  StackRoot _root7(&cell_name);

  Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup6 = this->_ResolveNameOnly(name, which_scopes);
  cell = tup6.at0();
  var_frame = tup6.at1();
  if ((cell == nullptr or !cell->nameref)) {
    return Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*>(cell, var_frame, name);
  }
  val = cell->val;
  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      if (this->exec_opts->strict_nameref()) {
        e_die(StrFormat("nameref %r is undefined", name));
      }
      else {
        return Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*>(cell, var_frame, name);
      }
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      new_name = val->s;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  if (!match::IsValidVarName(new_name)) {
    if (this->exec_opts->strict_nameref()) {
      e_die(StrFormat("nameref %r contains invalid variable name %r", name, new_name));
    }
    else {
      cell->nameref = false;
      return Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*>(cell, var_frame, name);
    }
  }
  if (ref_trail == nullptr) {
    ref_trail = NewList<BigStr*>(std::initializer_list<BigStr*>{name});
  }
  else {
    if (list_contains(ref_trail, new_name)) {
      e_die(StrFormat("Circular nameref %s", S_gAe->join(ref_trail)));
    }
  }
  ref_trail->append(new_name);
  Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> tup7 = this->_ResolveNameOrRef(new_name, scope_e::Dynamic, ref_trail);
  cell = tup7.at0();
  var_frame = tup7.at1();
  cell_name = tup7.at2();
  return Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*>(cell, var_frame, cell_name);
}

bool Mem::IsBashAssoc(BigStr* name) {
  runtime_asdl::Cell* cell = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&cell);

  Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> tup8 = this->_ResolveNameOrRef(name, this->ScopesForReading());
  cell = tup8.at0();
  return (cell != nullptr and cell->val->tag() == value_e::BashAssoc);
}

void Mem::SetPlace(value::Place* place, value_asdl::value_t* val, syntax_asdl::loc_t* blame_loc) {
  value_asdl::y_lvalue_t* yval = nullptr;
  value_asdl::y_lvalue_t* UP_yval = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* frame = nullptr;
  runtime_asdl::Cell* cell = nullptr;
  StackRoot _root0(&place);
  StackRoot _root1(&val);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&yval);
  StackRoot _root4(&UP_yval);
  StackRoot _root5(&frame);
  StackRoot _root6(&cell);

  yval = place->lval;
  UP_yval = yval;
  switch (yval->tag()) {
    case y_lvalue_e::Local: {
      LeftName* yval = static_cast<LeftName*>(UP_yval);
      frame = place->frame;
      cell = frame->get(yval->name);
      if (cell == nullptr) {
        cell = Alloc<Cell>(false, false, false, val);
        frame->set(yval->name, cell);
      }
      else {
        cell->val = val;
      }
    }
      break;
    case y_lvalue_e::Container: {
      e_die(S_AAe, blame_loc);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void Mem::SetLocalName(value_asdl::LeftName* lval, value_asdl::value_t* val) {
  Dict<BigStr*, runtime_asdl::Cell*>* var_frame = nullptr;
  runtime_asdl::Cell* cell = nullptr;
  StackRoot _root0(&lval);
  StackRoot _root1(&val);
  StackRoot _root2(&var_frame);
  StackRoot _root3(&cell);

  var_frame = this->var_stack->at(-1);
  cell = var_frame->get(lval->name);
  if (cell) {
    if (cell->readonly) {
      e_die(StrFormat("Can't assign to readonly value %r", lval->name), lval->blame_loc);
    }
    cell->val = val;
  }
  else {
    cell = Alloc<Cell>(false, false, false, val);
    var_frame->set(lval->name, cell);
  }
}

void Mem::SetNamed(value_asdl::LeftName* lval, value_asdl::value_t* val, runtime_asdl::scope_t which_scopes, int flags) {
  runtime_asdl::Cell* cell = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* var_frame = nullptr;
  BigStr* cell_name = nullptr;
  StackRoot _root0(&lval);
  StackRoot _root1(&val);
  StackRoot _root2(&cell);
  StackRoot _root3(&var_frame);
  StackRoot _root4(&cell_name);

  if (((flags & SetNameref) or (flags & ClearNameref))) {
    Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup9 = this->_ResolveNameOnly(lval->name, which_scopes);
    cell = tup9.at0();
    var_frame = tup9.at1();
    cell_name = lval->name;
  }
  else {
    Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> tup10 = this->_ResolveNameOrRef(lval->name, which_scopes);
    cell = tup10.at0();
    var_frame = tup10.at1();
    cell_name = tup10.at2();
  }
  if (cell) {
    if ((flags & ClearExport)) {
      cell->exported = false;
    }
    if ((flags & ClearReadOnly)) {
      cell->readonly = false;
    }
    if ((flags & ClearNameref)) {
      cell->nameref = false;
    }
    if (val != nullptr) {
      if (cell->readonly) {
        e_die(StrFormat("Can't assign to readonly value %r", lval->name), lval->blame_loc);
      }
      cell->val = val;
    }
    if ((flags & SetExport)) {
      cell->exported = true;
    }
    if ((flags & SetReadOnly)) {
      cell->readonly = true;
    }
    if ((flags & SetNameref)) {
      cell->nameref = true;
    }
  }
  else {
    if (val == nullptr) {
      val = value::Undef;
    }
    cell = Alloc<Cell>(to_bool((flags & SetExport)), to_bool((flags & SetReadOnly)), to_bool((flags & SetNameref)), val);
    var_frame->set(cell_name, cell);
  }
  if ((cell->val->tag() != value_e::Undef && cell->val->tag() != value_e::Str)) {
    if (cell->exported) {
      if (this->exec_opts->strict_array()) {
        e_die(S_ntu, lval->blame_loc);
      }
    }
    if (cell->nameref) {
      e_die(S_sAx, lval->blame_loc);
    }
  }
}

void Mem::SetValue(value_asdl::sh_lvalue_t* lval, value_asdl::value_t* val, runtime_asdl::scope_t which_scopes, int flags) {
  value_asdl::sh_lvalue_t* UP_lval = nullptr;
  value::Str* rval = nullptr;
  syntax_asdl::loc_t* left_loc = nullptr;
  runtime_asdl::Cell* cell = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* var_frame = nullptr;
  value_asdl::value_t* UP_cell_val = nullptr;
  runtime_asdl::error_code_t error_code;
  int n;
  mops::BigInt n_big;
  value::BashAssoc* cell_val2 = nullptr;
  StackRoot _root0(&lval);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_lval);
  StackRoot _root3(&rval);
  StackRoot _root4(&left_loc);
  StackRoot _root5(&cell);
  StackRoot _root6(&var_frame);
  StackRoot _root7(&UP_cell_val);
  StackRoot _root8(&cell_val2);

  UP_lval = lval;
  switch (lval->tag()) {
    case sh_lvalue_e::Var: {
      LeftName* lval = static_cast<LeftName*>(UP_lval);
      this->SetNamed(lval, val, which_scopes, flags);
    }
      break;
    case sh_lvalue_e::Indexed: {
      sh_lvalue::Indexed* lval = static_cast<sh_lvalue::Indexed*>(UP_lval);
      rval = static_cast<value::Str*>(val);
      left_loc = lval->blame_loc;
      Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> tup11 = this->_ResolveNameOrRef(lval->name, which_scopes);
      cell = tup11.at0();
      var_frame = tup11.at1();
      if (!cell) {
        this->_BindNewArrayWithEntry(var_frame, lval, rval, flags);
        return ;
      }
      if (cell->readonly) {
        e_die(S_kxq, left_loc);
      }
      UP_cell_val = cell->val;
      switch (UP_cell_val->tag()) {
        case value_e::Undef: {
          this->_BindNewArrayWithEntry(var_frame, lval, rval, flags);
          return ;
        }
          break;
        case value_e::Str: {
          e_die(S_Crq, left_loc);
        }
          break;
        case value_e::BashArray: {
          value::BashArray* cell_val = static_cast<value::BashArray*>(UP_cell_val);
          error_code = bash_impl::BashArray_SetElement(cell_val, lval->index, rval->s);
          if (error_code == error_code_e::IndexOutOfRange) {
            n = bash_impl::BashArray_Length(cell_val);
            e_die(StrFormat("Index %d is out of bounds for array of length %d", lval->index, n), left_loc);
          }
          return ;
        }
          break;
        case value_e::SparseArray: {
          value::SparseArray* lhs_sp = static_cast<value::SparseArray*>(UP_cell_val);
          error_code = bash_impl::SparseArray_SetElement(lhs_sp, mops::IntWiden(lval->index), rval->s);
          if (error_code == error_code_e::IndexOutOfRange) {
            n_big = bash_impl::SparseArray_Length(lhs_sp);
            e_die(StrFormat("Index %d is out of bounds for array of length %s", lval->index, mops::ToStr(n_big)), left_loc);
          }
          return ;
        }
          break;
      }
      e_die(StrFormat("Value of type %s can't be indexed", ui::ValType(cell->val)), left_loc);
    }
      break;
    case sh_lvalue_e::Keyed: {
      sh_lvalue::Keyed* lval = static_cast<sh_lvalue::Keyed*>(UP_lval);
      rval = static_cast<value::Str*>(val);
      left_loc = lval->blame_loc;
      Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> tup12 = this->_ResolveNameOrRef(lval->name, which_scopes);
      cell = tup12.at0();
      var_frame = tup12.at1();
      if (cell->readonly) {
        e_die(S_zFl, left_loc);
      }
      cell_val2 = static_cast<value::BashAssoc*>(cell->val);
      bash_impl::BashAssoc_SetElement(cell_val2, lval->key, rval->s);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void Mem::_BindNewArrayWithEntry(Dict<BigStr*, runtime_asdl::Cell*>* var_frame, sh_lvalue::Indexed* lval, value::Str* val, int flags) {
  BigStr* no_str = nullptr;
  List<BigStr*>* items = nullptr;
  value::BashArray* new_value = nullptr;
  bool readonly;
  StackRoot _root0(&var_frame);
  StackRoot _root1(&lval);
  StackRoot _root2(&val);
  StackRoot _root3(&no_str);
  StackRoot _root4(&items);
  StackRoot _root5(&new_value);

  no_str = nullptr;
  items = list_repeat(no_str, lval->index);
  items->append(val->s);
  new_value = Alloc<value::BashArray>(items);
  readonly = to_bool((flags & SetReadOnly));
  var_frame->set(lval->name, Alloc<Cell>(false, readonly, false, new_value));
}

void Mem::InternalSetGlobal(BigStr* name, value_asdl::value_t* new_val) {
  runtime_asdl::Cell* cell = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&new_val);
  StackRoot _root2(&cell);

  cell = this->var_stack->at(0)->at(name);
  cell->val = new_val;
}

value_asdl::value_t* Mem::GetValue(BigStr* name, runtime_asdl::scope_t which_scopes) {
  List<BigStr*>* strs2 = nullptr;
  List<value_asdl::value_t*>* items = nullptr;
  value_asdl::regex_match_t* top_match = nullptr;
  List<BigStr*>* groups = nullptr;
  value_asdl::RegexMatch* m = nullptr;
  List<BigStr*>* strs = nullptr;
  syntax_asdl::debug_frame_t* UP_frame = nullptr;
  BigStr* source_str = nullptr;
  double f;
  bool ok;
  mops::BigInt big_int;
  runtime_asdl::Cell* cell = nullptr;
  value_asdl::value_t* builtin_val = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&strs2);
  StackRoot _root2(&items);
  StackRoot _root3(&top_match);
  StackRoot _root4(&groups);
  StackRoot _root5(&m);
  StackRoot _root6(&strs);
  StackRoot _root7(&UP_frame);
  StackRoot _root8(&source_str);
  StackRoot _root9(&cell);
  StackRoot _root10(&builtin_val);

  if (which_scopes == scope_e::Shopt) {
    which_scopes = this->ScopesForReading();
  }
  switch (len(name)) {
    case 1: {
      if (str_equals_c(name, "_", 1)) {
        return Alloc<value::Str>(this->last_arg);
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 6: {
      if (str_equals_c(name, "_error", 6)) {
        return this->TryError();
      }
      else if (str_equals_c(name, "LINENO", 6)) {
        this->line_num->s = str(this->token_for_line->line->line_num);
        return this->line_num;
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 7: {
      if (str_equals_c(name, "_status", 7)) {
        return num::ToBig(this->TryStatus());
      }
      else if (str_equals_c(name, "BASHPID", 7)) {
        return Alloc<value::Str>(str(posix::getpid()));
      }
      else if (str_equals_c(name, "SECONDS", 7)) {
        f = (time_::time() - this->seconds_start);
        Tuple2<bool, mops::BigInt> tup13 = mops::FromFloat(f);
        ok = tup13.at0();
        big_int = tup13.at1();
        return Alloc<value::Int>(big_int);
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 8: {
      if (str_equals_c(name, "FUNCNAME", 8)) {
        strs = Alloc<List<BigStr*>>();
        for (ReverseListIter<syntax_asdl::debug_frame_t*> it(this->debug_stack); !it.Done(); it.Next()) {
          syntax_asdl::debug_frame_t* frame = it.Value();
          StackRoot _for(&frame        );
          UP_frame = frame;
          switch (frame->tag()) {
            case debug_frame_e::Call: {
              debug_frame::Call* frame = static_cast<debug_frame::Call*>(UP_frame);
              strs->append(frame->func_name);
            }
              break;
            case debug_frame_e::Source: {
              strs->append(S_cmd);
            }
              break;
            case debug_frame_e::Main: {
              strs->append(S_sDc_1);
            }
              break;
          }
        }
        return Alloc<value::BashArray>(strs);
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 9: {
      if (str_equals_c(name, "_this_dir", 9)) {
        if (len(this->this_dir) == 0) {
          return value::Undef;
        }
        else {
          return Alloc<value::Str>(this->this_dir->at(-1));
        }
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 10: {
      if (str_equals_c(name, "PIPESTATUS", 10)) {
        strs2 = Alloc<List<BigStr*>>();
        for (ListIter<int> it(this->pipe_status->at(-1)); !it.Done(); it.Next()) {
          int i = it.Value();
          strs2->append(str(i));
        }
        return Alloc<value::BashArray>(strs2);
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 11: {
      if (str_equals_c(name, "BASH_SOURCE", 11)) {
        strs = Alloc<List<BigStr*>>();
        for (ReverseListIter<syntax_asdl::debug_frame_t*> it(this->debug_stack); !it.Done(); it.Next()) {
          syntax_asdl::debug_frame_t* frame = it.Value();
          StackRoot _for(&frame        );
          UP_frame = frame;
          switch (frame->tag()) {
            case debug_frame_e::Call: {
              debug_frame::Call* frame = static_cast<debug_frame::Call*>(UP_frame);
              source_str = ui::GetLineSourceString(frame->def_tok->line);
              strs->append(source_str);
            }
              break;
            case debug_frame_e::Source: {
              debug_frame::Source* frame = static_cast<debug_frame::Source*>(UP_frame);
              strs->append(frame->source_name);
            }
              break;
            case debug_frame_e::Main: {
              debug_frame::Main* frame = static_cast<debug_frame::Main*>(UP_frame);
              strs->append(frame->dollar0);
            }
              break;
          }
        }
        return Alloc<value::BashArray>(strs);
      }
      else if (str_equals_c(name, "BASH_LINENO", 11)) {
        strs = Alloc<List<BigStr*>>();
        for (ReverseListIter<syntax_asdl::debug_frame_t*> it(this->debug_stack); !it.Done(); it.Next()) {
          syntax_asdl::debug_frame_t* frame = it.Value();
          StackRoot _for(&frame        );
          UP_frame = frame;
          switch (frame->tag()) {
            case debug_frame_e::Call: {
              debug_frame::Call* frame = static_cast<debug_frame::Call*>(UP_frame);
              strs->append(_LineNumber(frame->call_tok));
            }
              break;
            case debug_frame_e::Source: {
              debug_frame::Source* frame = static_cast<debug_frame::Source*>(UP_frame);
              strs->append(_LineNumber(frame->call_tok));
            }
              break;
            case debug_frame_e::Main: {
              strs->append(S_wfw);
            }
              break;
          }
        }
        return Alloc<value::BashArray>(strs);
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 12: {
      if (str_equals_c(name, "BASH_REMATCH", 12)) {
        top_match = this->regex_match->at(-1);
        switch (top_match->tag()) {
          case regex_match_e::No: {
            groups = Alloc<List<BigStr*>>();
          }
            break;
          case regex_match_e::Yes: {
            m = static_cast<RegexMatch*>(top_match);
            groups = util::RegexGroupStrings(m->s, m->indices);
          }
            break;
        }
        return Alloc<value::BashArray>(groups);
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 16: {
      if (str_equals_c(name, "_pipeline_status", 16)) {
        items = Alloc<List<value_asdl::value_t*>>();
        for (ListIter<int> it(this->pipe_status->at(-1)); !it.Done(); it.Next()) {
          int i = it.Value();
          items->append(num::ToBig(i));
        }
        return Alloc<value::List>(items);
      }
      else {
        goto str_switch_default;
      }
    }
      break;
    case 19: {
      if (str_equals_c(name, "_process_sub_status", 19)) {
        items = Alloc<List<value_asdl::value_t*>>();
        for (ListIter<int> it(this->process_sub_status->at(-1)); !it.Done(); it.Next()) {
          int i = it.Value();
          items->append(num::ToBig(i));
        }
        return Alloc<value::List>(items);
      }
      else {
        goto str_switch_default;
      }
    }
      break;

    str_switch_default:
    default: {
      Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> tup14 = this->_ResolveNameOrRef(name, which_scopes);
      cell = tup14.at0();
      if (cell) {
        return cell->val;
      }
      builtin_val = this->builtins->get(name);
      if (builtin_val) {
        return builtin_val;
      }
      return value::Undef;
    }
  }
}

runtime_asdl::Cell* Mem::GetCell(BigStr* name, runtime_asdl::scope_t which_scopes) {
  runtime_asdl::Cell* cell = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&cell);

  if (which_scopes == scope_e::Shopt) {
    which_scopes = this->ScopesForReading();
  }
  Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup15 = this->_ResolveNameOnly(name, which_scopes);
  cell = tup15.at0();
  return cell;
}

bool Mem::Unset(value_asdl::sh_lvalue_t* lval, runtime_asdl::scope_t which_scopes) {
  value_asdl::sh_lvalue_t* UP_lval = nullptr;
  BigStr* var_name = nullptr;
  runtime_asdl::Cell* cell = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* var_frame = nullptr;
  BigStr* cell_name = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  runtime_asdl::error_code_t error_code;
  int n;
  mops::BigInt big_length;
  StackRoot _root0(&lval);
  StackRoot _root1(&UP_lval);
  StackRoot _root2(&var_name);
  StackRoot _root3(&cell);
  StackRoot _root4(&var_frame);
  StackRoot _root5(&cell_name);
  StackRoot _root6(&val);
  StackRoot _root7(&UP_val);

  UP_lval = lval;
  switch (lval->tag()) {
    case sh_lvalue_e::Var: {
      LeftName* lval = static_cast<LeftName*>(UP_lval);
      var_name = lval->name;
    }
      break;
    case sh_lvalue_e::Indexed: {
      sh_lvalue::Indexed* lval = static_cast<sh_lvalue::Indexed*>(UP_lval);
      var_name = lval->name;
    }
      break;
    case sh_lvalue_e::Keyed: {
      sh_lvalue::Keyed* lval = static_cast<sh_lvalue::Keyed*>(UP_lval);
      var_name = lval->name;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  if (which_scopes == scope_e::Shopt) {
    which_scopes = this->ScopesForWriting();
  }
  Tuple3<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*, BigStr*> tup16 = this->_ResolveNameOrRef(var_name, which_scopes);
  cell = tup16.at0();
  var_frame = tup16.at1();
  cell_name = tup16.at2();
  if (!cell) {
    return false;
  }
  if (cell->readonly) {
    throw Alloc<error::Runtime>(StrFormat("Can't unset readonly variable %r", var_name));
  }
  switch (lval->tag()) {
    case sh_lvalue_e::Var: {
      mylib::dict_erase(var_frame, cell_name);
    }
      break;
    case sh_lvalue_e::Indexed: {
      sh_lvalue::Indexed* lval = static_cast<sh_lvalue::Indexed*>(UP_lval);
      val = cell->val;
      UP_val = val;
      if (val->tag() == value_e::BashArray) {
        value::BashArray* val = static_cast<value::BashArray*>(UP_val);
        error_code = bash_impl::BashArray_UnsetElement(val, lval->index);
        if (error_code == error_code_e::IndexOutOfRange) {
          n = bash_impl::BashArray_Length(val);
          throw Alloc<error::Runtime>(StrFormat("%s[%d]: Index is out of bounds for array of length %d", var_name, lval->index, n));
        }
      }
      else {
        if (val->tag() == value_e::SparseArray) {
          value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
          error_code = bash_impl::SparseArray_UnsetElement(val, mops::IntWiden(lval->index));
          if (error_code == error_code_e::IndexOutOfRange) {
            big_length = bash_impl::SparseArray_Length(val);
            throw Alloc<error::Runtime>(StrFormat("%s[%d]: Index is out of bounds for array of length %s", var_name, lval->index, mops::ToStr(big_length)));
          }
        }
        else {
          throw Alloc<error::Runtime>(StrFormat("%r isn't an array", var_name));
        }
      }
    }
      break;
    case sh_lvalue_e::Keyed: {
      sh_lvalue::Keyed* lval = static_cast<sh_lvalue::Keyed*>(UP_lval);
      val = cell->val;
      UP_val = val;
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      bash_impl::BashAssoc_UnsetElement(val, lval->key);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  return true;
}

runtime_asdl::scope_t Mem::ScopesForReading() {
  return this->exec_opts->dynamic_scope() ? scope_e::Dynamic : scope_e::LocalOrGlobal;
}

runtime_asdl::scope_t Mem::ScopesForWriting() {
  return this->exec_opts->dynamic_scope() ? scope_e::Dynamic : scope_e::LocalOnly;
}

bool Mem::ClearFlag(BigStr* name, int flag) {
  runtime_asdl::Cell* cell = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* var_frame = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&cell);
  StackRoot _root2(&var_frame);

  Tuple2<runtime_asdl::Cell*, Dict<BigStr*, runtime_asdl::Cell*>*> tup17 = this->_ResolveNameOnly(name, this->ScopesForReading());
  cell = tup17.at0();
  var_frame = tup17.at1();
  if (cell) {
    if ((flag & ClearExport)) {
      cell->exported = false;
    }
    if ((flag & ClearNameref)) {
      cell->nameref = false;
    }
    return true;
  }
  else {
    return false;
  }
}

void Mem::_FillWithExported(Dict<BigStr*, BigStr*>* new_env) {
  value::Str* val = nullptr;
  StackRoot _root0(&new_env);
  StackRoot _root1(&val);

  for (ListIter<Dict<BigStr*, runtime_asdl::Cell*>*> it(this->var_stack); !it.Done(); it.Next()) {
    Dict<BigStr*, runtime_asdl::Cell*>* scope = it.Value();
    StackRoot _for(&scope  );
    for (DictIter<BigStr*, runtime_asdl::Cell*> it(scope); !it.Done(); it.Next()) {
      BigStr* name = it.Key();
      runtime_asdl::Cell* cell = it.Value();
      if ((cell->exported and cell->val->tag() == value_e::Str)) {
        val = static_cast<value::Str*>(cell->val);
        new_env->set(name, val->s);
      }
    }
  }
}

void Mem::_FillEnvObj(Dict<BigStr*, BigStr*>* new_env, value_asdl::Obj* env_object) {
  StackRoot _root0(&new_env);
  StackRoot _root1(&env_object);

  if (env_object->prototype != nullptr) {
    this->_FillEnvObj(new_env, env_object->prototype);
  }
  for (DictIter<BigStr*, value_asdl::value_t*> it(env_object->d); !it.Done(); it.Next()) {
    BigStr* name = it.Key();
    value_asdl::value_t* val = it.Value();
    if (val->tag() != value_e::Str) {
      continue;
    }
    new_env->set(name, static_cast<value::Str*>(val)->s);
  }
}

Dict<BigStr*, BigStr*>* Mem::GetEnv() {
  Dict<BigStr*, BigStr*>* new_env = nullptr;
  StackRoot _root0(&new_env);

  new_env = Alloc<Dict<BigStr*, BigStr*>>();
  if (!this->exec_opts->no_exported()) {
    this->_FillWithExported(new_env);
  }
  if (this->exec_opts->env_obj()) {
    this->_FillEnvObj(new_env, this->env_object);
  }
  return new_env;
}

List<BigStr*>* Mem::VarNames() {
  List<BigStr*>* ret = nullptr;
  StackRoot _root0(&ret);

  ret = Alloc<List<BigStr*>>();
  for (ListIter<Dict<BigStr*, runtime_asdl::Cell*>*> it(this->var_stack); !it.Done(); it.Next()) {
    Dict<BigStr*, runtime_asdl::Cell*>* scope = it.Value();
    StackRoot _for(&scope  );
    for (DictIter<BigStr*, runtime_asdl::Cell*> it(scope); !it.Done(); it.Next()) {
      BigStr* name = it.Key();
      StackRoot _for(&name    );
      ret->append(name);
    }
  }
  return ret;
}

List<BigStr*>* Mem::VarNamesStartingWith(BigStr* prefix) {
  List<BigStr*>* names = nullptr;
  StackRoot _root0(&prefix);
  StackRoot _root1(&names);

  names = Alloc<List<BigStr*>>();
  for (ListIter<Dict<BigStr*, runtime_asdl::Cell*>*> it(this->var_stack); !it.Done(); it.Next()) {
    Dict<BigStr*, runtime_asdl::Cell*>* scope = it.Value();
    StackRoot _for(&scope  );
    for (DictIter<BigStr*, runtime_asdl::Cell*> it(scope); !it.Done(); it.Next()) {
      BigStr* name = it.Key();
      StackRoot _for(&name    );
      if (name->startswith(prefix)) {
        names->append(name);
      }
    }
  }
  return names;
}

Dict<BigStr*, BigStr*>* Mem::GetAllVars() {
  Dict<BigStr*, BigStr*>* result = nullptr;
  value_asdl::value_t* val = nullptr;
  value::Str* str_val = nullptr;
  StackRoot _root0(&result);
  StackRoot _root1(&val);
  StackRoot _root2(&str_val);

  result = Alloc<Dict<BigStr*, BigStr*>>();
  for (ListIter<Dict<BigStr*, runtime_asdl::Cell*>*> it(this->var_stack); !it.Done(); it.Next()) {
    Dict<BigStr*, runtime_asdl::Cell*>* scope = it.Value();
    StackRoot _for(&scope  );
    for (DictIter<BigStr*, runtime_asdl::Cell*> it(scope); !it.Done(); it.Next()) {
      BigStr* name = it.Key();
      runtime_asdl::Cell* cell = it.Value();
      val = cell->val;
      if (val->tag() == value_e::Str) {
        str_val = static_cast<value::Str*>(val);
        result->set(name, str_val->s);
      }
    }
  }
  return result;
}

Dict<BigStr*, runtime_asdl::Cell*>* Mem::GetAllCells(runtime_asdl::scope_t which_scopes) {
  Dict<BigStr*, runtime_asdl::Cell*>* result = nullptr;
  List<Dict<BigStr*, runtime_asdl::Cell*>*>* scopes = nullptr;
  StackRoot _root0(&result);
  StackRoot _root1(&scopes);

  result = Alloc<Dict<BigStr*, runtime_asdl::Cell*>>();
  if (which_scopes == scope_e::Dynamic) {
    scopes = this->var_stack;
  }
  else {
    if (which_scopes == scope_e::LocalOnly) {
      scopes = this->var_stack->slice(-1);
    }
    else {
      if (which_scopes == scope_e::GlobalOnly) {
        scopes = this->var_stack->slice(0, 1);
      }
      else {
        if (which_scopes == scope_e::LocalOrGlobal) {
          scopes = NewList<Dict<BigStr*, runtime_asdl::Cell*>*>(std::initializer_list<Dict<BigStr*, runtime_asdl::Cell*>*>{this->var_stack->at(0)});
          if (len(this->var_stack) > 1) {
            scopes->append(this->var_stack->at(-1));
          }
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
  }
  for (ListIter<Dict<BigStr*, runtime_asdl::Cell*>*> it(scopes); !it.Done(); it.Next()) {
    Dict<BigStr*, runtime_asdl::Cell*>* scope = it.Value();
    StackRoot _for(&scope  );
    for (DictIter<BigStr*, runtime_asdl::Cell*> it(scope); !it.Done(); it.Next()) {
      BigStr* name = it.Key();
      runtime_asdl::Cell* cell = it.Value();
      result->set(name, cell);
    }
  }
  return result;
}

void Mem::SetRegexMatch(value_asdl::regex_match_t* match) {
  StackRoot _root0(&match);

  this->regex_match->set(-1, match);
}

value_asdl::regex_match_t* Mem::GetRegexMatch() {
  return this->regex_match->at(-1);
}

void Mem::PushContextStack(Dict<BigStr*, value_asdl::value_t*>* context) {
  StackRoot _root0(&context);

  this->ctx_stack->append(context);
}

Dict<BigStr*, value_asdl::value_t*>* Mem::GetContext() {
  if (len(this->ctx_stack)) {
    return this->ctx_stack->at(-1);
  }
  return nullptr;
}

Dict<BigStr*, value_asdl::value_t*>* Mem::PopContextStack() {
  return this->ctx_stack->pop();
}

Tuple2<value_asdl::value_t*, value_asdl::Obj*> ValueIsInvokableObj(value_asdl::value_t* val) {
  value_asdl::Obj* obj = nullptr;
  value_asdl::value_t* invoke_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&obj);
  StackRoot _root2(&invoke_val);

  if (val->tag() != value_e::Obj) {
    return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(nullptr, nullptr);
  }
  obj = static_cast<Obj*>(val);
  if (!obj->prototype) {
    return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(nullptr, nullptr);
  }
  invoke_val = obj->prototype->d->get(S_fBo);
  if (invoke_val == nullptr) {
    return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(nullptr, nullptr);
  }
  if ((invoke_val->tag() == value_e::Proc || invoke_val->tag() == value_e::BuiltinProc)) {
    return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(invoke_val, obj);
  }
  return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(nullptr, nullptr);
}

void _AddNames(Dict<BigStr*, bool>* unique, Dict<BigStr*, runtime_asdl::Cell*>* frame) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* proc = nullptr;
  StackRoot _root0(&unique);
  StackRoot _root1(&frame);
  StackRoot _root2(&val);
  StackRoot _root3(&proc);

  for (DictIter<BigStr*, runtime_asdl::Cell*> it(frame); !it.Done(); it.Next()) {
    BigStr* name = it.Key();
    StackRoot _for(&name  );
    val = frame->at(name)->val;
    if (val->tag() == value_e::Proc) {
      unique->set(name, true);
    }
    Tuple2<value_asdl::value_t*, value_asdl::Obj*> tup18 = ValueIsInvokableObj(val);
    proc = tup18.at0();
    if (proc != nullptr) {
      unique->set(name, true);
    }
  }
}

Procs::Procs(state::Mem* mem) {
  this->mem = mem;
  this->sh_funcs = Alloc<Dict<BigStr*, value::Proc*>>();
}

void Procs::DefineShellFunc(BigStr* name, value::Proc* proc) {
  StackRoot _root0(&name);
  StackRoot _root1(&proc);

  this->sh_funcs->set(name, proc);
}

bool Procs::IsShellFunc(BigStr* name) {
  StackRoot _root0(&name);

  return dict_contains(this->sh_funcs, name);
}

value::Proc* Procs::GetShellFunc(BigStr* name) {
  StackRoot _root0(&name);

  return this->sh_funcs->get(name);
}

void Procs::EraseShellFunc(BigStr* to_del) {
  StackRoot _root0(&to_del);

  mylib::dict_erase(this->sh_funcs, to_del);
}

List<BigStr*>* Procs::ShellFuncNames() {
  List<BigStr*>* names = nullptr;
  StackRoot _root0(&names);

  names = this->sh_funcs->keys();
  names->sort();
  return names;
}

void Procs::DefineProc(BigStr* name, value::Proc* proc) {
  StackRoot _root0(&name);
  StackRoot _root1(&proc);

  this->mem->var_stack->at(-1)->set(name, Alloc<Cell>(false, false, false, proc));
}

bool Procs::IsProc(BigStr* name) {
  value_asdl::value_t* maybe_proc = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&maybe_proc);

  maybe_proc = this->mem->GetValue(name);
  return maybe_proc->tag() == value_e::Proc;
}

bool Procs::IsInvokableObj(BigStr* name) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* proc = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&val);
  StackRoot _root2(&proc);

  val = this->mem->GetValue(name);
  Tuple2<value_asdl::value_t*, value_asdl::Obj*> tup19 = ValueIsInvokableObj(val);
  proc = tup19.at0();
  return proc != nullptr;
}

List<BigStr*>* Procs::InvokableNames() {
  Dict<BigStr*, bool>* unique = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* top_frame = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* global_frame = nullptr;
  List<BigStr*>* names = nullptr;
  StackRoot _root0(&unique);
  StackRoot _root1(&top_frame);
  StackRoot _root2(&global_frame);
  StackRoot _root3(&names);

  unique = Alloc<Dict<BigStr*, bool>>();
  for (DictIter<BigStr*, value::Proc*> it(this->sh_funcs); !it.Done(); it.Next()) {
    BigStr* name = it.Key();
    StackRoot _for(&name  );
    unique->set(name, true);
  }
  top_frame = this->mem->var_stack->at(-1);
  _AddNames(unique, top_frame);
  global_frame = this->mem->var_stack->at(0);
  if (global_frame != top_frame) {
    _AddNames(unique, global_frame);
  }
  names = unique->keys();
  names->sort();
  return names;
}

Tuple2<value_asdl::value_t*, value_asdl::Obj*> Procs::GetInvokable(BigStr* name) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* proc = nullptr;
  value_asdl::Obj* self_val = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&val);
  StackRoot _root2(&proc);
  StackRoot _root3(&self_val);

  val = this->mem->GetValue(name);
  if (val->tag() == value_e::Proc) {
    return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(static_cast<value::Proc*>(val), nullptr);
  }
  Tuple2<value_asdl::value_t*, value_asdl::Obj*> tup20 = ValueIsInvokableObj(val);
  proc = tup20.at0();
  self_val = tup20.at1();
  if (proc) {
    return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(proc, self_val);
  }
  if (dict_contains(this->sh_funcs, name)) {
    return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(this->sh_funcs->at(name), nullptr);
  }
  return Tuple2<value_asdl::value_t*, value_asdl::Obj*>(nullptr, nullptr);
}

void OshLanguageSetValue(state::Mem* mem, value_asdl::sh_lvalue_t* lval, value_asdl::value_t* val, int flags) {
  runtime_asdl::scope_t which_scopes;
  StackRoot _root0(&mem);
  StackRoot _root1(&lval);
  StackRoot _root2(&val);

  which_scopes = mem->ScopesForWriting();
  mem->SetValue(lval, val, which_scopes, flags);
}

void BuiltinSetValue(state::Mem* mem, value_asdl::sh_lvalue_t* lval, value_asdl::value_t* val) {
  StackRoot _root0(&mem);
  StackRoot _root1(&lval);
  StackRoot _root2(&val);

  mem->SetValue(lval, val, mem->ScopesForWriting());
}

void BuiltinSetString(state::Mem* mem, BigStr* name, BigStr* s) {
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&s);

  BuiltinSetValue(mem, location::LName(name), Alloc<value::Str>(s));
}

void BuiltinSetArray(state::Mem* mem, BigStr* name, List<BigStr*>* a) {
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&a);

  BuiltinSetValue(mem, location::LName(name), Alloc<value::BashArray>(a));
}

void SetGlobalString(state::Mem* mem, BigStr* name, BigStr* s) {
  value::Str* val = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&s);
  StackRoot _root3(&val);

  val = Alloc<value::Str>(s);
  mem->SetNamed(location::LName(name), val, scope_e::GlobalOnly);
}

void SetGlobalArray(state::Mem* mem, BigStr* name, List<BigStr*>* a) {
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&a);

  mem->SetNamed(location::LName(name), Alloc<value::BashArray>(a), scope_e::GlobalOnly);
}

void SetGlobalValue(state::Mem* mem, BigStr* name, value_asdl::value_t* val) {
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&val);

  mem->SetNamed(location::LName(name), val, scope_e::GlobalOnly);
}

void SetLocalValue(state::Mem* mem, BigStr* name, value_asdl::value_t* val) {
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&val);

  mem->SetNamed(location::LName(name), val, scope_e::LocalOnly);
}

void ExportGlobalString(state::Mem* mem, BigStr* name, BigStr* s) {
  value::Str* val = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&s);
  StackRoot _root3(&val);

  val = Alloc<value::Str>(s);
  mem->SetNamed(location::LName(name), val, scope_e::GlobalOnly, SetExport);
}

void SetStringInEnv(state::Mem* mem, BigStr* var_name, BigStr* s) {
  StackRoot _root0(&mem);
  StackRoot _root1(&var_name);
  StackRoot _root2(&s);

  if (mem->exec_opts->env_obj()) {
    mem->env_dict->set(var_name, Alloc<value::Str>(s));
  }
  else {
    SetGlobalString(mem, var_name, s);
  }
}

value_asdl::value_t* DynamicGetVar(state::Mem* mem, BigStr* name, runtime_asdl::scope_t which_scopes) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&val);

  val = mem->GetValue(name, which_scopes);
  if (val->tag() == value_e::Undef) {
    return value::Null;
  }
  return val;
}

BigStr* GetString(state::Mem* mem, BigStr* name) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&val);
  StackRoot _root3(&UP_val);

  val = mem->GetValue(name);
  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      throw Alloc<error::Runtime>(StrFormat("$%s isn't defined", name));
    }
      break;
    case value_e::Str: {
      return static_cast<value::Str*>(UP_val)->s;
    }
      break;
    default: {
      throw Alloc<error::Runtime>(StrFormat("$%s should be a string", name));
    }
  }
}

BigStr* MaybeString(state::Mem* mem, BigStr* name) {
  StackRoot _root0(&mem);
  StackRoot _root1(&name);

  try {
    return GetString(mem, name);
  }
  catch (error::Runtime*) {
    return nullptr;
  }
}

int GetInteger(state::Mem* mem, BigStr* name) {
  value_asdl::value_t* val = nullptr;
  BigStr* s = nullptr;
  int i;
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&val);
  StackRoot _root3(&s);

  val = mem->GetValue(name);
  if (val->tag() != value_e::Str) {
    throw Alloc<error::Runtime>(StrFormat("$%s should be a string, got %s", name, ui::ValType(val)));
  }
  s = static_cast<value::Str*>(val)->s;
  try {
    i = to_int(s);
  }
  catch (ValueError*) {
    throw Alloc<error::Runtime>(StrFormat("$%s doesn't look like an integer, got %r", name, s));
  }
  return i;
}

}  // define namespace state

namespace util {  // define


List<BigStr*>* RegexGroupStrings(BigStr* s, List<int>* indices) {
  List<BigStr*>* groups = nullptr;
  int n;
  int start;
  int end;
  StackRoot _root0(&s);
  StackRoot _root1(&indices);
  StackRoot _root2(&groups);

  groups = Alloc<List<BigStr*>>();
  n = len(indices);
  for (int i = 0; i < (n / 2); ++i) {
    start = indices->at((2 * i));
    end = indices->at(((2 * i) + 1));
    if (start == -1) {
      groups->append(S_Aoo);
    }
    else {
      groups->append(s->slice(start, end));
    }
  }
  return groups;
}

List<BigStr*>* RegexSearch(BigStr* pat, BigStr* s) {
  List<int>* indices = nullptr;
  StackRoot _root0(&pat);
  StackRoot _root1(&s);
  StackRoot _root2(&indices);

  indices = libc::regex_search(pat, 0, s, 0);
  if (indices == nullptr) {
    return nullptr;
  }
  return RegexGroupStrings(s, indices);
}

UserExit::UserExit(int status) {
  this->status = status;
}

HistoryError::HistoryError(BigStr* msg) {
  this->msg = msg;
}

BigStr* HistoryError::UserErrorString() {
  return StrFormat("history: %s", this->msg);
}

_DebugFile::_DebugFile() {
  ;  // pass
}

void _DebugFile::write(BigStr* s) {
  StackRoot _root0(&s);

  ;  // pass
}

void _DebugFile::writeln(BigStr* s) {
  StackRoot _root0(&s);

  ;  // pass
}

bool _DebugFile::isatty() {
  return false;
}

NullDebugFile::NullDebugFile() : ::util::_DebugFile() {
}

DebugFile::DebugFile(mylib::Writer* f) : ::util::_DebugFile() {
  this->f = f;
}

void DebugFile::write(BigStr* s) {
  StackRoot _root0(&s);

  this->f->write(s);
}

void DebugFile::writeln(BigStr* s) {
  StackRoot _root0(&s);

  this->write(str_concat(s, S_nfs));
  this->f->flush();
}

bool DebugFile::isatty() {
  return this->f->isatty();
}

void PrintTopicHeader(BigStr* topic_id, mylib::Writer* f) {
  StackRoot _root0(&topic_id);
  StackRoot _root1(&f);

  if (f->isatty()) {
    f->write(StrFormat("%s %s %s\n", ansi::REVERSE, topic_id, ansi::RESET));
  }
  else {
    f->write(StrFormat("~~~ %s ~~~\n", topic_id));
  }
  f->write(S_nfs);
}

bool PrintEmbeddedHelp(pyutil::_ResourceLoader* loader, BigStr* topic_id, mylib::Writer* f) {
  BigStr* contents = nullptr;
  StackRoot _root0(&loader);
  StackRoot _root1(&topic_id);
  StackRoot _root2(&f);
  StackRoot _root3(&contents);

  try {
    contents = loader->Get(StrFormat("_devbuild/help/%s", topic_id));
  }
  catch (IOError_OSError*) {
    return false;
  }
  PrintTopicHeader(topic_id, f);
  f->write(contents);
  f->write(S_nfs);
  return true;
}

void _PrintVersionLine(pyutil::_ResourceLoader* loader, mylib::Writer* f) {
  BigStr* v = nullptr;
  StackRoot _root0(&loader);
  StackRoot _root1(&f);
  StackRoot _root2(&v);

  v = pyutil::GetVersion(loader);
  f->write(StrFormat("Oils %s\t\thttps://oils.pub/\n", v));
}

void HelpFlag(pyutil::_ResourceLoader* loader, BigStr* topic_id, mylib::Writer* f) {
  bool found;
  StackRoot _root0(&loader);
  StackRoot _root1(&topic_id);
  StackRoot _root2(&f);

  _PrintVersionLine(loader, f);
  f->write(S_nfs);
  found = PrintEmbeddedHelp(loader, topic_id, f);
}

void VersionFlag(pyutil::_ResourceLoader* loader, mylib::Writer* f) {
  StackRoot _root0(&loader);
  StackRoot _root1(&f);

  _PrintVersionLine(loader, f);
  f->write(S_nfs);
  pyutil::PrintVersionDetails(loader);
}

}  // define namespace util

namespace j8 {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using id_kind_asdl::Id_str;
using nil8_asdl::nvalue;
using nil8_asdl::nvalue_t;
using runtime_asdl::error_code_e;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::value_str;
using value_asdl::Obj;

BigStr* ValType(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  return value_str(val->tag(), false);
}

int ValueId(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  switch (val->tag()) {
    case value_e::Null: 
    case value_e::Bool: 
    case value_e::Int: 
    case value_e::Float: 
    case value_e::Str: {
      return -1;
    }
      break;
    default: {
      return HeapValueId(val);
    }
  }
}

BigStr* ValueIdString(value_asdl::value_t* val) {
  int heap_id;
  StackRoot _root0(&val);

  heap_id = ValueId(val);
  if (heap_id == -1) {
    return S_Aoo;
  }
  else {
    return StrFormat(" 0x%s", mylib::hex_lower(heap_id));
  }
}

BigStr* Utf8Encode(int code) {
  int num_cont_bytes;
  List<int>* bytes_ = nullptr;
  int b;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&bytes_);
  StackRoot _root1(&tmp);

  num_cont_bytes = 0;
  if (code <= 127) {
    return chr((code & 127));
  }
  else {
    if (code <= 2047) {
      num_cont_bytes = 1;
    }
    else {
      if (code <= 65535) {
        num_cont_bytes = 2;
      }
      else {
        num_cont_bytes = 3;
      }
    }
  }
  bytes_ = Alloc<List<int>>();
  for (int _ = 0; _ < num_cont_bytes; ++_) {
    bytes_->append((128 | (code & 63)));
    code >>= 6;
  }
  b = ((30 << (6 - num_cont_bytes)) | (code & (63 >> num_cont_bytes)));
  bytes_->append(b);
  bytes_->reverse();
  tmp = Alloc<List<BigStr*>>();
  for (ListIter<int> it(bytes_); !it.Done(); it.Next()) {
    int b = it.Value();
    tmp->append(chr((b & 255)));
  }
  return S_Aoo->join(tmp);
}
int SHOW_CYCLES = (1 << 1);
int SHOW_NON_DATA = (1 << 2);
int LOSSY_JSON = (1 << 3);
int INF_NAN_ARE_NULL = (1 << 4);

void _Print(value_asdl::value_t* val, mylib::BufWriter* buf, int indent, int options) {
  j8::InstancePrinter* p = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&buf);
  StackRoot _root2(&p);

  p = Alloc<InstancePrinter>(buf, indent, options);
  p->Print(val);
}

void PrintMessage(value_asdl::value_t* val, mylib::BufWriter* buf, int indent) {
  StackRoot _root0(&val);
  StackRoot _root1(&buf);

  _Print(val, buf, indent);
}

void PrintJsonMessage(value_asdl::value_t* val, mylib::BufWriter* buf, int indent) {
  StackRoot _root0(&val);
  StackRoot _root1(&buf);

  _Print(val, buf, indent, (LOSSY_JSON | INF_NAN_ARE_NULL));
}

void PrintLine(value_asdl::value_t* val, mylib::Writer* f) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&f);
  StackRoot _root2(&buf);

  buf = Alloc<mylib::BufWriter>();
  _Print(val, buf, -1, (SHOW_CYCLES | SHOW_NON_DATA));
  f->write(buf->getvalue());
  f->write(S_nfs);
}

void EncodeString(BigStr* s, mylib::BufWriter* buf, bool unquoted_ok) {
  StackRoot _root0(&s);
  StackRoot _root1(&buf);

  if ((unquoted_ok and fastfunc::CanOmitQuotes(s))) {
    buf->write(s);
    return ;
  }
  _Print(Alloc<value::Str>(s), buf, -1);
}

BigStr* MaybeEncodeString(BigStr* s) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&buf);

  buf = Alloc<mylib::BufWriter>();
  _Print(Alloc<value::Str>(s), buf, -1);
  return buf->getvalue();
}

BigStr* MaybeEncodeJsonString(BigStr* s) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&buf);

  buf = Alloc<mylib::BufWriter>();
  _Print(Alloc<value::Str>(s), buf, -1, LOSSY_JSON);
  return buf->getvalue();
}

InstancePrinter::InstancePrinter(mylib::BufWriter* buf, int indent, int options) {
  this->buf = buf;
  this->indent = indent;
  this->options = options;
  this->visiting = Alloc<Dict<int, bool>>();
}

void InstancePrinter::_ItemIndent(int level) {
  if (this->indent == -1) {
    return ;
  }
  this->buf->write_spaces(((level + 1) * this->indent));
}

void InstancePrinter::_BracketIndent(int level) {
  if (this->indent == -1) {
    return ;
  }
  this->buf->write_spaces((level * this->indent));
}

void InstancePrinter::_MaybeNewline() {
  if (this->indent == -1) {
    return ;
  }
  this->buf->write(S_nfs);
}

void InstancePrinter::_MaybeSpace() {
  if (this->indent == -1) {
    return ;
  }
  this->buf->write(S_yfw);
}

void InstancePrinter::_PrintList(value::List* val, int level) {
  int i;
  StackRoot _root0(&val);

  if (len(val->items) == 0) {
    this->buf->write(S_xmu);
  }
  else {
    this->buf->write(S_Eax);
    this->_MaybeNewline();
    i = 0;
    for (ListIter<value_asdl::value_t*> it(val->items); !it.Done(); it.Next(), ++i) {
      value_asdl::value_t* item = it.Value();
      StackRoot _for(&item    );
      if (i != 0) {
        this->buf->write(S_Cce);
        this->_MaybeNewline();
      }
      this->_ItemIndent(level);
      this->Print(item, (level + 1));
    }
    this->_MaybeNewline();
    this->_BracketIndent(level);
    this->buf->write(S_pcD);
  }
}

void InstancePrinter::_PrintMapping(Dict<BigStr*, value_asdl::value_t*>* d, BigStr* left, BigStr* right, int level) {
  int i;
  StackRoot _root0(&d);
  StackRoot _root1(&left);
  StackRoot _root2(&right);

  if (len(d) == 0) {
    this->buf->write(left);
    this->buf->write(right);
  }
  else {
    this->buf->write(left);
    this->_MaybeNewline();
    i = 0;
    for (DictIter<BigStr*, value_asdl::value_t*> it(d); !it.Done(); it.Next()) {
      BigStr* k = it.Key();
      value_asdl::value_t* v = it.Value();
      if (i != 0) {
        this->buf->write(S_Cce);
        this->_MaybeNewline();
      }
      this->_ItemIndent(level);
      pyj8::WriteString(k, this->options, this->buf);
      this->buf->write(S_fyj);
      this->_MaybeSpace();
      this->Print(v, (level + 1));
      i += 1;
    }
    this->_MaybeNewline();
    this->_BracketIndent(level);
    this->buf->write(right);
  }
}

void InstancePrinter::_PrintDict(value::Dict* val, int level) {
  StackRoot _root0(&val);

  this->_PrintMapping(val->d, S_ato, S_cEn, level);
}

void InstancePrinter::_PrintObj(value_asdl::Obj* val, int level) {
  StackRoot _root0(&val);

  this->_PrintMapping(val->d, S_ijB, S_hxb, level);
  if (val->prototype) {
    this->buf->write(S_dtA);
    this->_PrintObj(val->prototype, level);
  }
}

void InstancePrinter::_PrintBashPrefix(BigStr* type_str, int level) {
  StackRoot _root0(&type_str);

  this->buf->write(S_ato);
  this->_MaybeNewline();
  this->_ItemIndent(level);
  this->buf->write(S_EDa);
  this->_MaybeSpace();
  this->buf->write(type_str);
  this->_MaybeNewline();
  this->_ItemIndent(level);
  this->buf->write(S_eqo);
  this->_MaybeSpace();
}

void InstancePrinter::_PrintBashSuffix(int level) {
  this->_MaybeNewline();
  this->_BracketIndent(level);
  this->buf->write(S_cEn);
}

void InstancePrinter::_PrintSparseArray(value::SparseArray* val, int level) {
  int i;
  BigStr* v = nullptr;
  runtime_asdl::error_code_t error_code;
  StackRoot _root0(&val);
  StackRoot _root1(&v);

  this->_PrintBashPrefix(S_vBu, level);
  if (bash_impl::SparseArray_Count(val) == 0) {
    this->buf->write(S_Fni);
  }
  else {
    this->buf->write(S_ato);
    this->_MaybeNewline();
    i = 0;
    for (ListIter<mops::BigInt> it(bash_impl::SparseArray_GetKeys(val)); !it.Done(); it.Next()) {
      mops::BigInt k = it.Value();
      if (i != 0) {
        this->buf->write(S_Cce);
        this->_MaybeNewline();
      }
      this->_ItemIndent((level + 1));
      pyj8::WriteString(mops::ToStr(k), this->options, this->buf);
      this->buf->write(S_fyj);
      this->_MaybeSpace();
      Tuple2<BigStr*, runtime_asdl::error_code_t> tup0 = bash_impl::SparseArray_GetElement(val, k);
      v = tup0.at0();
      error_code = tup0.at1();
      pyj8::WriteString(v, this->options, this->buf);
      i += 1;
    }
    this->_MaybeNewline();
    this->_BracketIndent((level + 1));
    this->buf->write(S_cEn);
  }
  this->_PrintBashSuffix(level);
}

void InstancePrinter::_PrintBashArray(value::BashArray* val, int level) {
  bool first;
  int i;
  StackRoot _root0(&val);

  this->_PrintBashPrefix(S_xbi, level);
  if (bash_impl::BashArray_Count(val) == 0) {
    this->buf->write(S_Fni);
  }
  else {
    this->buf->write(S_ato);
    this->_MaybeNewline();
    first = true;
    i = 0;
    for (ListIter<BigStr*> it(bash_impl::BashArray_GetValues(val)); !it.Done(); it.Next(), ++i) {
      BigStr* s = it.Value();
      StackRoot _for(&s    );
      if (s == nullptr) {
        continue;
      }
      if (!first) {
        this->buf->write(S_Cce);
        this->_MaybeNewline();
      }
      this->_ItemIndent((level + 1));
      pyj8::WriteString(str(i), this->options, this->buf);
      this->buf->write(S_fyj);
      this->_MaybeSpace();
      pyj8::WriteString(s, this->options, this->buf);
      first = false;
    }
    this->_MaybeNewline();
    this->_BracketIndent((level + 1));
    this->buf->write(S_cEn);
  }
  this->_PrintBashSuffix(level);
}

void InstancePrinter::_PrintBashAssoc(value::BashAssoc* val, int level) {
  int i;
  StackRoot _root0(&val);

  this->_PrintBashPrefix(S_ojw, level);
  if (bash_impl::BashAssoc_Count(val) == 0) {
    this->buf->write(S_Fni);
  }
  else {
    this->buf->write(S_ato);
    this->_MaybeNewline();
    i = 0;
    for (DictIter<BigStr*, BigStr*> it(bash_impl::BashAssoc_GetDict(val)); !it.Done(); it.Next()) {
      BigStr* k2 = it.Key();
      BigStr* v2 = it.Value();
      if (i != 0) {
        this->buf->write(S_Cce);
        this->_MaybeNewline();
      }
      this->_ItemIndent((level + 1));
      pyj8::WriteString(k2, this->options, this->buf);
      this->buf->write(S_fyj);
      this->_MaybeSpace();
      pyj8::WriteString(v2, this->options, this->buf);
      i += 1;
    }
    this->_MaybeNewline();
    this->_BracketIndent((level + 1));
    this->buf->write(S_cEn);
  }
  this->_PrintBashSuffix(level);
}

void InstancePrinter::Print(value_asdl::value_t* val, int level) {
  value_asdl::value_t* UP_val = nullptr;
  double fl;
  BigStr* s = nullptr;
  int heap_id;
  BigStr* ysh_type = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&UP_val);
  StackRoot _root2(&s);
  StackRoot _root3(&ysh_type);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Null: {
      this->buf->write(S_lbA);
    }
      break;
    case value_e::Bool: {
      value::Bool* val = static_cast<value::Bool*>(UP_val);
      this->buf->write(val->b ? S_FsF : S_Ctn);
    }
      break;
    case value_e::Int: {
      value::Int* val = static_cast<value::Int*>(UP_val);
      this->buf->write(mops::ToStr(val->i));
    }
      break;
    case value_e::Float: {
      value::Float* val = static_cast<value::Float*>(UP_val);
      fl = val->f;
      if (math::isinf(fl)) {
        if ((this->options & INF_NAN_ARE_NULL)) {
          s = S_lbA;
        }
        else {
          s = S_BvB;
          if (fl < 0) {
            s = str_concat(S_Bjq, s);
          }
        }
      }
      else {
        if (math::isnan(fl)) {
          if ((this->options & INF_NAN_ARE_NULL)) {
            s = S_lbA;
          }
          else {
            s = S_ywk;
          }
        }
        else {
          s = str(fl);
        }
      }
      this->buf->write(s);
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      pyj8::WriteString(val->s, this->options, this->buf);
    }
      break;
    case value_e::List: {
      value::List* val = static_cast<value::List*>(UP_val);
      heap_id = HeapValueId(val);
      if (this->visiting->get(heap_id, false)) {
        if ((this->options & SHOW_CYCLES)) {
          this->buf->write(S_Aek);
          return ;
        }
        else {
          throw Alloc<error::Encode>(StrFormat("Can't encode List%s in object cycle", ValueIdString(val)));
        }
      }
      else {
        this->visiting->set(heap_id, true);
        this->_PrintList(val, level);
        this->visiting->set(heap_id, false);
      }
    }
      break;
    case value_e::Dict: {
      value::Dict* val = static_cast<value::Dict*>(UP_val);
      heap_id = HeapValueId(val);
      if (this->visiting->get(heap_id, false)) {
        if ((this->options & SHOW_CYCLES)) {
          this->buf->write(S_qnA);
          return ;
        }
        else {
          throw Alloc<error::Encode>(StrFormat("Can't encode Dict%s in object cycle", ValueIdString(val)));
        }
      }
      else {
        this->visiting->set(heap_id, true);
        this->_PrintDict(val, level);
        this->visiting->set(heap_id, false);
      }
    }
      break;
    case value_e::Obj: {
      Obj* val = static_cast<Obj*>(UP_val);
      if (!(this->options & SHOW_NON_DATA)) {
        throw Alloc<error::Encode>(S_kdC);
      }
      heap_id = HeapValueId(val);
      if (this->visiting->get(heap_id, false)) {
        if ((this->options & SHOW_CYCLES)) {
          this->buf->write(S_Ehr);
          return ;
        }
        else {
          throw Alloc<error::Encode>(StrFormat("Can't encode Obj%s in object cycle", ValueIdString(val)));
        }
      }
      else {
        this->visiting->set(heap_id, true);
        this->_PrintObj(val, level);
        this->visiting->set(heap_id, false);
      }
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
      this->_PrintSparseArray(val, level);
    }
      break;
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      this->_PrintBashArray(val, level);
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      this->_PrintBashAssoc(val, level);
    }
      break;
    default: {
      ;  // pass
      if ((this->options & SHOW_NON_DATA)) {
        ysh_type = ValType(val);
        this->buf->write(StrFormat("<%s>", ysh_type));
      }
      else {
        throw Alloc<error::Encode>(StrFormat("Can't serialize object of type %s", ValType(val)));
      }
    }
  }
}

LexerDecoder::LexerDecoder(BigStr* s, bool is_j8, BigStr* lang_str) {
  this->s = s;
  this->is_j8 = is_j8;
  this->lang_str = lang_str;
  this->pos = 0;
  this->cur_line_num = 1;
  this->decoded = Alloc<mylib::BufWriter>();
}

error::Decode* LexerDecoder::_Error(BigStr* msg, int end_pos) {
  StackRoot _root0(&msg);

  return Alloc<error::Decode>(msg, this->s, this->pos, end_pos, this->cur_line_num);
}

Tuple3<int, int, BigStr*> LexerDecoder::Next() {
  int tok_id;
  int end_pos;
  Tuple2<int, int> tup1 = match::MatchJ8Token(this->s, this->pos);
  tok_id = tup1.at0();
  end_pos = tup1.at1();
  if (!this->is_j8) {
    if ((tok_id == Id::Left_BSingleQuote || tok_id == Id::Left_USingleQuote)) {
      throw this->_Error(S_hBp, end_pos);
    }
    if (tok_id == Id::Ignored_Comment) {
      throw this->_Error(S_wac, end_pos);
    }
  }
  if ((tok_id == Id::Left_DoubleQuote || tok_id == Id::Left_BSingleQuote || tok_id == Id::Left_USingleQuote)) {
    return this->_DecodeString(tok_id, end_pos);
  }
  if (tok_id == Id::Left_JDoubleQuote) {
    if (this->is_j8) {
      return this->_DecodeString(tok_id, end_pos);
    }
    else {
      throw this->_Error(S_Ahj, end_pos);
    }
  }
  if (tok_id == Id::Ignored_Newline) {
    this->cur_line_num += 1;
  }
  this->pos = end_pos;
  return Tuple3<int, int, BigStr*>(tok_id, end_pos, nullptr);
}

Tuple3<int, int, BigStr*> LexerDecoder::NextForLines() {
  int tok_id;
  int end_pos;
  Tuple2<int, int> tup2 = match::MatchJ8LinesToken(this->s, this->pos);
  tok_id = tup2.at0();
  end_pos = tup2.at1();
  if ((tok_id == Id::Left_DoubleQuote || tok_id == Id::Left_JDoubleQuote || tok_id == Id::Left_BSingleQuote || tok_id == Id::Left_USingleQuote)) {
    return this->_DecodeString(tok_id, end_pos);
  }
  if ((tok_id == Id::Lit_Chars and !pyj8::PartIsUtf8(this->s, this->pos, end_pos))) {
    throw this->_Error(StrFormat("Invalid UTF-8 in %s string literal", this->lang_str), end_pos);
  }
  if (tok_id == Id::Char_AsciiControl) {
    throw this->_Error(S_ApC, end_pos);
  }
  if (tok_id == Id::J8_Newline) {
    this->cur_line_num += 1;
  }
  this->pos = end_pos;
  return Tuple3<int, int, BigStr*>(tok_id, end_pos, nullptr);
}

Tuple3<int, int, BigStr*> LexerDecoder::_DecodeString(int left_id, int str_pos) {
  int tok_id;
  int str_end;
  BigStr* s = nullptr;
  BigStr* part = nullptr;
  BigStr* ch = nullptr;
  BigStr* h = nullptr;
  int i;
  BigStr* h1 = nullptr;
  BigStr* h2 = nullptr;
  int i1;
  int i2;
  int code_point;
  StackRoot _root0(&s);
  StackRoot _root1(&part);
  StackRoot _root2(&ch);
  StackRoot _root3(&h);
  StackRoot _root4(&h1);
  StackRoot _root5(&h2);

  while (true) {
    if ((left_id == Id::Left_DoubleQuote || left_id == Id::Left_JDoubleQuote)) {
      Tuple2<int, int> tup3 = match::MatchJsonStrToken(this->s, str_pos);
      tok_id = tup3.at0();
      str_end = tup3.at1();
    }
    else {
      Tuple2<int, int> tup4 = match::MatchJ8StrToken(this->s, str_pos);
      tok_id = tup4.at0();
      str_end = tup4.at1();
    }
    if (tok_id == Id::Eol_Tok) {
      throw this->_Error(StrFormat("Unexpected EOF while lexing %s string", this->lang_str), str_end);
    }
    if (tok_id == Id::Unknown_Backslash) {
      throw this->_Error(StrFormat("Bad backslash escape in %s string", this->lang_str), str_end);
    }
    if (tok_id == Id::Char_AsciiControl) {
      throw this->_Error(StrFormat("%s strings can't have unescaped ASCII control chars", this->lang_str), str_end);
    }
    if ((tok_id == Id::Right_SingleQuote || tok_id == Id::Right_DoubleQuote)) {
      this->pos = str_end;
      s = this->decoded->getvalue();
      this->decoded->clear();
      return Tuple3<int, int, BigStr*>(Id::J8_String, str_end, s);
    }
    if (tok_id == Id::Lit_Chars) {
      part = this->s->slice(str_pos, str_end);
      if (!pyj8::PartIsUtf8(this->s, str_pos, str_end)) {
        throw this->_Error(StrFormat("Invalid UTF-8 in %s string literal", this->lang_str), str_end);
      }
    }
    else {
      if (tok_id == Id::Char_OneChar) {
        ch = this->s->at((str_pos + 1));
        part = consts::LookupCharC(ch);
      }
      else {
        if (tok_id == Id::Char_UBraced) {
          h = this->s->slice((str_pos + 3), (str_end - 1));
          i = to_int(h, 16);
          if (i > 1114111) {
            throw this->_Error(S_egA, str_end);
          }
          if ((55296 <= i and i < 57344)) {
            throw this->_Error(StrFormat("\\u{%s} escape is illegal because it's in the surrogate range", h), str_end);
          }
          part = Utf8Encode(i);
        }
        else {
          if (tok_id == Id::Char_YHex) {
            h = this->s->slice((str_pos + 2), str_end);
            if (left_id != Id::Left_BSingleQuote) {
              throw this->_Error(StrFormat("\\y%s escapes not allowed in u'' strings", h), str_end);
            }
            i = to_int(h, 16);
            part = chr(i);
          }
          else {
            if (tok_id == Id::Char_SurrogatePair) {
              h1 = this->s->slice((str_pos + 2), (str_pos + 6));
              h2 = this->s->slice((str_pos + 8), (str_pos + 12));
              i1 = (to_int(h1, 16) - 55296);
              i2 = (to_int(h2, 16) - 56320);
              code_point = ((65536 + (i1 << 10)) + i2);
              part = Utf8Encode(code_point);
            }
            else {
              if (tok_id == Id::Char_Unicode4) {
                h = this->s->slice((str_pos + 2), str_end);
                i = to_int(h, 16);
                part = Utf8Encode(i);
              }
              else {
                assert(0);  // AssertionError
              }
            }
          }
        }
      }
    }
    this->decoded->write(part);
    str_pos = str_end;
  }
}

_Parser::_Parser(BigStr* s, bool is_j8) {
  this->s = s;
  this->is_j8 = is_j8;
  this->lang_str = is_j8 ? S_Czs : S_dqg;
  this->lexer = Alloc<LexerDecoder>(s, is_j8, this->lang_str);
  this->tok_id = Id::Undefined_Tok;
  this->start_pos = 0;
  this->end_pos = 0;
  this->decoded = S_Aoo;
}

void _Parser::_Next() {
  while (true) {
    this->start_pos = this->end_pos;
    Tuple3<int, int, BigStr*> tup5 = this->lexer->Next();
    this->tok_id = tup5.at0();
    this->end_pos = tup5.at1();
    this->decoded = tup5.at2();
    if ((this->tok_id != Id::Ignored_Space && this->tok_id != Id::Ignored_Newline && this->tok_id != Id::Ignored_Comment)) {
      break;
    }
  }
}

void _Parser::_Eat(int tok_id) {
  if (this->tok_id != tok_id) {
    throw this->_ParseError(StrFormat("Expected %s, got %s", Id_str(tok_id), Id_str(this->tok_id)));
  }
  this->_Next();
}

void _Parser::_NextForLines() {
  this->start_pos = this->end_pos;
  Tuple3<int, int, BigStr*> tup6 = this->lexer->NextForLines();
  this->tok_id = tup6.at0();
  this->end_pos = tup6.at1();
  this->decoded = tup6.at2();
}

error::Decode* _Parser::_ParseError(BigStr* msg) {
  StackRoot _root0(&msg);

  return Alloc<error::Decode>(msg, this->s, this->start_pos, this->end_pos, this->lexer->cur_line_num);
}

Parser::Parser(BigStr* s, bool is_j8) : ::j8::_Parser(s, is_j8) {
}

Tuple2<BigStr*, value_asdl::value_t*> Parser::_ParsePair() {
  BigStr* k = nullptr;
  value_asdl::value_t* v = nullptr;
  StackRoot _root0(&k);
  StackRoot _root1(&v);

  k = this->decoded;
  this->_Eat(Id::J8_String);
  this->_Eat(Id::J8_Colon);
  v = this->_ParseValue();
  return Tuple2<BigStr*, value_asdl::value_t*>(k, v);
}

value_asdl::value_t* Parser::_ParseDict() {
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  BigStr* k = nullptr;
  value_asdl::value_t* v = nullptr;
  StackRoot _root0(&d);
  StackRoot _root1(&k);
  StackRoot _root2(&v);

  d = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  this->_Next();
  if (this->tok_id == Id::J8_RBrace) {
    this->_Next();
    return Alloc<value::Dict>(d);
  }
  Tuple2<BigStr*, value_asdl::value_t*> tup7 = this->_ParsePair();
  k = tup7.at0();
  v = tup7.at1();
  d->set(k, v);
  while (this->tok_id == Id::J8_Comma) {
    this->_Next();
    Tuple2<BigStr*, value_asdl::value_t*> tup8 = this->_ParsePair();
    k = tup8.at0();
    v = tup8.at1();
    d->set(k, v);
  }
  this->_Eat(Id::J8_RBrace);
  return Alloc<value::Dict>(d);
}

value_asdl::value_t* Parser::_ParseList() {
  List<value_asdl::value_t*>* items = nullptr;
  StackRoot _root0(&items);

  items = Alloc<List<value_asdl::value_t*>>();
  this->_Next();
  if (this->tok_id == Id::J8_RBracket) {
    this->_Next();
    return Alloc<value::List>(items);
  }
  items->append(this->_ParseValue());
  while (this->tok_id == Id::J8_Comma) {
    this->_Next();
    items->append(this->_ParseValue());
  }
  this->_Eat(Id::J8_RBracket);
  return Alloc<value::List>(items);
}

value_asdl::value_t* Parser::_ParseValue() {
  value::Bool* b = nullptr;
  BigStr* part = nullptr;
  bool ok;
  mops::BigInt big;
  value::Str* str_val = nullptr;
  StackRoot _root0(&b);
  StackRoot _root1(&part);
  StackRoot _root2(&str_val);

  if (this->tok_id == Id::J8_LBrace) {
    return this->_ParseDict();
  }
  else {
    if (this->tok_id == Id::J8_LBracket) {
      return this->_ParseList();
    }
    else {
      if (this->tok_id == Id::J8_Null) {
        this->_Next();
        return value::Null;
      }
      else {
        if (this->tok_id == Id::J8_Bool) {
          b = Alloc<value::Bool>(str_equals(this->s->at(this->start_pos), S_omF));
          this->_Next();
          return b;
        }
        else {
          if (this->tok_id == Id::J8_Int) {
            part = this->s->slice(this->start_pos, this->end_pos);
            this->_Next();
            Tuple2<bool, mops::BigInt> tup9 = mops::FromStr2(part);
            ok = tup9.at0();
            big = tup9.at1();
            if (!ok) {
              throw this->_ParseError(S_zDl);
            }
            return Alloc<value::Int>(big);
          }
          else {
            if (this->tok_id == Id::J8_Float) {
              part = this->s->slice(this->start_pos, this->end_pos);
              this->_Next();
              return Alloc<value::Float>(to_float(part));
            }
            else {
              if (this->tok_id == Id::J8_String) {
                str_val = Alloc<value::Str>(this->decoded);
                this->_Next();
                return str_val;
              }
              else {
                if (this->tok_id == Id::Eol_Tok) {
                  throw this->_ParseError(StrFormat("Unexpected EOF while parsing %s", this->lang_str));
                }
                else {
                  throw this->_ParseError(StrFormat("Invalid token while parsing %s: %s", this->lang_str, Id_str(this->tok_id)));
                }
              }
            }
          }
        }
      }
    }
  }
}

value_asdl::value_t* Parser::ParseValue() {
  value_asdl::value_t* obj = nullptr;
  int n;
  int extra;
  StackRoot _root0(&obj);

  this->_Next();
  obj = this->_ParseValue();
  n = len(this->s);
  if (this->start_pos != n) {
    extra = (n - this->start_pos);
    throw this->_ParseError(StrFormat("Got %d bytes of unexpected trailing input", extra));
  }
  return obj;
}

Nil8Parser::Nil8Parser(BigStr* s, bool is_j8) : ::j8::_Parser(s, is_j8) {
}

nil8_asdl::nvalue_t* Nil8Parser::_ParseRecord() {
  List<nil8_asdl::nvalue_t*>* items = nullptr;
  StackRoot _root0(&items);

  items = Alloc<List<nil8_asdl::nvalue_t*>>();
  this->_Next();
  if (this->tok_id == Id::J8_RParen) {
    this->_Next();
    return Alloc<nvalue::List>(items);
  }
  while (this->tok_id != Id::J8_RParen) {
    items->append(this->_ParseNil8());
  }
  this->_Eat(Id::J8_RParen);
  return Alloc<nvalue::List>(items);
}

nil8_asdl::nvalue_t* Nil8Parser::_ParseList8() {
  List<nil8_asdl::nvalue_t*>* items = nullptr;
  StackRoot _root0(&items);

  items = Alloc<List<nil8_asdl::nvalue_t*>>();
  this->_Next();
  if (this->tok_id == Id::J8_RBracket) {
    this->_Next();
    return Alloc<nvalue::List>(items);
  }
  while (this->tok_id != Id::J8_RBracket) {
    items->append(this->_ParseNil8());
  }
  this->_Eat(Id::J8_RBracket);
  return Alloc<nvalue::List>(items);
}

nil8_asdl::nvalue_t* Nil8Parser::_ParseNil8() {
  nil8_asdl::nvalue_t* obj = nullptr;
  nvalue::Bool* b = nullptr;
  BigStr* part = nullptr;
  nvalue::Str* str_val = nullptr;
  nvalue::Symbol* op = nullptr;
  nil8_asdl::nvalue_t* operand2 = nullptr;
  nil8_asdl::nvalue_t* infix = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&b);
  StackRoot _root2(&part);
  StackRoot _root3(&str_val);
  StackRoot _root4(&op);
  StackRoot _root5(&operand2);
  StackRoot _root6(&infix);

  if (this->tok_id == Id::J8_LParen) {
    obj = this->_ParseRecord();
  }
  else {
    if (this->tok_id == Id::J8_LBracket) {
      obj = this->_ParseList8();
    }
    else {
      if (this->tok_id == Id::J8_Null) {
        this->_Next();
        obj = nvalue::Null;
      }
      else {
        if (this->tok_id == Id::J8_Bool) {
          b = Alloc<nvalue::Bool>(str_equals(this->s->at(this->start_pos), S_omF));
          this->_Next();
          obj = b;
        }
        else {
          if (this->tok_id == Id::J8_Int) {
            part = this->s->slice(this->start_pos, this->end_pos);
            this->_Next();
            obj = Alloc<nvalue::Int>(to_int(part));
          }
          else {
            if (this->tok_id == Id::J8_Float) {
              part = this->s->slice(this->start_pos, this->end_pos);
              this->_Next();
              obj = Alloc<nvalue::Float>(to_float(part));
            }
            else {
              if (this->tok_id == Id::J8_String) {
                str_val = Alloc<nvalue::Str>(this->decoded);
                this->_Next();
                obj = str_val;
              }
              else {
                if ((this->tok_id == Id::J8_Identifier || this->tok_id == Id::J8_Operator || this->tok_id == Id::J8_Colon || this->tok_id == Id::J8_Comma)) {
                  part = this->s->slice(this->start_pos, this->end_pos);
                  this->_Next();
                  obj = Alloc<nvalue::Symbol>(part);
                }
                else {
                  if (this->tok_id == Id::Eol_Tok) {
                    throw this->_ParseError(StrFormat("Unexpected EOF while parsing %s", this->lang_str));
                  }
                  else {
                    throw this->_ParseError(StrFormat("Invalid token while parsing %s: %s", this->lang_str, Id_str(this->tok_id)));
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  if ((this->tok_id == Id::J8_Operator || this->tok_id == Id::J8_Colon || this->tok_id == Id::J8_Comma)) {
    part = this->s->slice(this->start_pos, this->end_pos);
    op = Alloc<nvalue::Symbol>(part);
    this->_Next();
    operand2 = this->_ParseNil8();
    infix = Alloc<nvalue::List>(NewList<nil8_asdl::nvalue_t*>(std::initializer_list<nil8_asdl::nvalue_t*>{op, obj, operand2}));
    return infix;
  }
  return obj;
}

nil8_asdl::nvalue_t* Nil8Parser::ParseNil8() {
  nil8_asdl::nvalue_t* obj = nullptr;
  StackRoot _root0(&obj);

  this->_Next();
  obj = this->_ParseNil8();
  if (this->tok_id != Id::Eol_Tok) {
    throw this->_ParseError(S_oDA);
  }
  return obj;
}

J8LinesParser::J8LinesParser(BigStr* s) : ::j8::_Parser(s, true) {
}

void J8LinesParser::_Show(BigStr* s) {
  StackRoot _root0(&s);

  mylib::print_stderr(StrFormat("%s tok_id %s %d-%d", s, Id_str(this->tok_id), this->start_pos, this->end_pos));
}

void J8LinesParser::_ParseLine(List<BigStr*>* out) {
  int string_start;
  int prev_id;
  int prev_start;
  int string_end;
  StackRoot _root0(&out);

  if (this->tok_id == Id::WS_Space) {
    this->_NextForLines();
  }
  if ((this->tok_id == Id::J8_Newline || this->tok_id == Id::Eol_Tok)) {
    this->_NextForLines();
    return ;
  }
  if (this->tok_id == Id::J8_String) {
    out->append(this->decoded);
    this->_NextForLines();
    if (this->tok_id == Id::WS_Space) {
      this->_NextForLines();
    }
    if ((this->tok_id != Id::J8_Newline && this->tok_id != Id::Eol_Tok)) {
      throw this->_ParseError(StrFormat("Unexpected text after J8 Line (%s)", Id_str(this->tok_id)));
    }
    this->_NextForLines();
    return ;
  }
  if (this->tok_id == Id::Lit_Chars) {
    string_start = this->start_pos;
    while (true) {
      prev_id = this->tok_id;
      prev_start = this->start_pos;
      this->_NextForLines();
      if ((this->tok_id == Id::J8_Newline || this->tok_id == Id::Eol_Tok)) {
        break;
      }
    }
    if (prev_id == Id::WS_Space) {
      string_end = prev_start;
    }
    else {
      string_end = this->start_pos;
    }
    out->append(this->s->slice(string_start, string_end));
    this->_NextForLines();
    return ;
  }
  assert(0);  // AssertionError
}

List<BigStr*>* J8LinesParser::Parse() {
  List<BigStr*>* lines = nullptr;
  StackRoot _root0(&lines);

  this->_NextForLines();
  lines = Alloc<List<BigStr*>>();
  while (this->tok_id != Id::Eol_Tok) {
    this->_ParseLine(lines);
  }
  if (this->tok_id != Id::Eol_Tok) {
    throw this->_ParseError(S_mfF);
  }
  return lines;
}

List<BigStr*>* SplitJ8Lines(BigStr* s) {
  j8::J8LinesParser* p = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&p);

  p = Alloc<J8LinesParser>(s);
  return p->Parse();
}

}  // define namespace j8

namespace j8_lite {  // define


BigStr* EncodeString(BigStr* s, bool unquoted_ok) {
  StackRoot _root0(&s);

  if ((unquoted_ok and fastfunc::CanOmitQuotes(s))) {
    return s;
  }
  return fastfunc::J8EncodeString(s, 1);
}

BigStr* YshEncodeString(BigStr* s) {
  StackRoot _root0(&s);

  return fastfunc::ShellEncodeString(s, 1);
}

BigStr* MaybeShellEncode(BigStr* s) {
  StackRoot _root0(&s);

  if (fastfunc::CanOmitQuotes(s)) {
    return s;
  }
  return fastfunc::ShellEncodeString(s, 0);
}

BigStr* ShellEncode(BigStr* s) {
  StackRoot _root0(&s);

  return fastfunc::ShellEncodeString(s, 0);
}

BigStr* YshEncode(BigStr* s, bool unquoted_ok) {
  StackRoot _root0(&s);

  if ((unquoted_ok and fastfunc::CanOmitQuotes(s))) {
    return s;
  }
  return fastfunc::ShellEncodeString(s, 1);
}

}  // define namespace j8_lite

namespace ansi {  // define

BigStr* RESET = S_yfk;
BigStr* BOLD = S_aaF;
BigStr* UNDERLINE = S_sCc;
BigStr* REVERSE = S_woy;
BigStr* RED = S_sqm;
BigStr* GREEN = S_eda;
BigStr* YELLOW = S_ysf;
BigStr* BLUE = S_osl;
BigStr* MAGENTA = S_vie;
BigStr* CYAN = S_mmi;
BigStr* WHITE = S_rpo;

}  // define namespace ansi

namespace pp_hnode {  // define

using hnode_asdl::hnode;
using hnode_asdl::hnode_e;
using hnode_asdl::hnode_t;
using hnode_asdl::Field;
using hnode_asdl::color_e;
using pretty_asdl::doc;
using pretty_asdl::MeasuredDoc;
using pretty_asdl::Measure;
using pretty::_Break;
using pretty::_Concat;
using pretty::_Flat;
using pretty::_Group;
using pretty::_IfFlat;
using pretty::_Indent;
using pretty::_EmptyMeasure;
using pretty::AsciiText;

BaseEncoder::BaseEncoder() {
  this->indent = 4;
  this->use_styles = true;
  this->max_tabular_width = 22;
  this->visiting = Alloc<Dict<int, bool>>();
}

void BaseEncoder::SetIndent(int indent) {
  this->indent = indent;
}

void BaseEncoder::SetUseStyles(bool use_styles) {
  this->use_styles = use_styles;
}

void BaseEncoder::SetMaxTabularWidth(int max_tabular_width) {
  this->max_tabular_width = max_tabular_width;
}

pretty_asdl::MeasuredDoc* BaseEncoder::_Styled(BigStr* style, pretty_asdl::MeasuredDoc* mdoc) {
  StackRoot _root0(&style);
  StackRoot _root1(&mdoc);

  if (this->use_styles) {
    return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{Alloc<MeasuredDoc>(Alloc<doc::Text>(style), _EmptyMeasure()), mdoc, Alloc<MeasuredDoc>(Alloc<doc::Text>(ansi::RESET), _EmptyMeasure())}));
  }
  else {
    return mdoc;
  }
}

pretty_asdl::MeasuredDoc* BaseEncoder::_StyledAscii(BigStr* style, BigStr* s) {
  pretty_asdl::Measure* measure = nullptr;
  StackRoot _root0(&style);
  StackRoot _root1(&s);
  StackRoot _root2(&measure);

  measure = Alloc<Measure>(len(s), -1);
  if (this->use_styles) {
    s = StrFormat("%s%s%s", style, s, ansi::RESET);
  }
  return Alloc<MeasuredDoc>(Alloc<doc::Text>(s), measure);
}

pretty_asdl::MeasuredDoc* BaseEncoder::_Surrounded(BigStr* left, pretty_asdl::MeasuredDoc* mdoc, BigStr* right) {
  StackRoot _root0(&left);
  StackRoot _root1(&mdoc);
  StackRoot _root2(&right);

  return _Group(_Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(left), _Indent(this->indent, _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{_Break(S_Aoo), mdoc}))), _Break(S_Aoo), AsciiText(right)})));
}

pretty_asdl::MeasuredDoc* BaseEncoder::_SurroundedAndPrefixed(BigStr* left, pretty_asdl::MeasuredDoc* prefix, BigStr* sep, pretty_asdl::MeasuredDoc* mdoc, BigStr* right) {
  StackRoot _root0(&left);
  StackRoot _root1(&prefix);
  StackRoot _root2(&sep);
  StackRoot _root3(&mdoc);
  StackRoot _root4(&right);

  return _Group(_Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(left), prefix, _Indent(this->indent, _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{_Break(sep), mdoc}))), _Break(S_Aoo), AsciiText(right)})));
}

pretty_asdl::MeasuredDoc* BaseEncoder::_Join(List<pretty_asdl::MeasuredDoc*>* items, BigStr* sep, BigStr* space) {
  List<pretty_asdl::MeasuredDoc*>* seq = nullptr;
  int i;
  StackRoot _root0(&items);
  StackRoot _root1(&sep);
  StackRoot _root2(&space);
  StackRoot _root3(&seq);

  seq = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  i = 0;
  for (ListIter<pretty_asdl::MeasuredDoc*> it(items); !it.Done(); it.Next(), ++i) {
    pretty_asdl::MeasuredDoc* item = it.Value();
    StackRoot _for(&item  );
    if (i != 0) {
      seq->append(AsciiText(sep));
      seq->append(_Break(space));
    }
    seq->append(item);
  }
  return _Concat(seq);
}

pretty_asdl::MeasuredDoc* BaseEncoder::_Tabular(List<pretty_asdl::MeasuredDoc*>* items, BigStr* sep) {
  int max_flat_len;
  List<pretty_asdl::MeasuredDoc*>* seq = nullptr;
  int i;
  pretty_asdl::MeasuredDoc* non_tabular = nullptr;
  int sep_width;
  List<pretty_asdl::MeasuredDoc*>* tabular_seq = nullptr;
  int padding;
  pretty_asdl::MeasuredDoc* tabular = nullptr;
  StackRoot _root0(&items);
  StackRoot _root1(&sep);
  StackRoot _root2(&seq);
  StackRoot _root3(&non_tabular);
  StackRoot _root4(&tabular_seq);
  StackRoot _root5(&tabular);

  if (len(items) == 0) {
    return AsciiText(S_Aoo);
  }
  max_flat_len = 0;
  seq = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  i = 0;
  for (ListIter<pretty_asdl::MeasuredDoc*> it(items); !it.Done(); it.Next(), ++i) {
    pretty_asdl::MeasuredDoc* item = it.Value();
    StackRoot _for(&item  );
    if (i != 0) {
      seq->append(AsciiText(sep));
      seq->append(_Break(S_yfw));
    }
    seq->append(item);
    max_flat_len = max(max_flat_len, item->measure->flat);
  }
  non_tabular = _Concat(seq);
  sep_width = len(sep);
  if (((max_flat_len + sep_width) + 1) <= this->max_tabular_width) {
    tabular_seq = Alloc<List<pretty_asdl::MeasuredDoc*>>();
    i = 0;
    for (ListIter<pretty_asdl::MeasuredDoc*> it(items); !it.Done(); it.Next(), ++i) {
      pretty_asdl::MeasuredDoc* item = it.Value();
      StackRoot _for(&item    );
      tabular_seq->append(_Flat(item));
      if (i != (len(items) - 1)) {
        padding = ((max_flat_len - item->measure->flat) + 1);
        tabular_seq->append(AsciiText(sep));
        tabular_seq->append(_Group(_Break(str_repeat(S_yfw, padding))));
      }
    }
    tabular = _Concat(tabular_seq);
    return _Group(_IfFlat(non_tabular, tabular));
  }
  else {
    return non_tabular;
  }
}

HNodeEncoder::HNodeEncoder() : ::pp_hnode::BaseEncoder() {
  this->type_color = ansi::YELLOW;
  this->field_color = ansi::MAGENTA;
}

pretty_asdl::MeasuredDoc* HNodeEncoder::HNode(hnode_asdl::hnode_t* h) {
  StackRoot _root0(&h);

  this->visiting->clear();
  return this->_HNode(h);
}

pretty_asdl::MeasuredDoc* HNodeEncoder::_Field(hnode_asdl::Field* field) {
  pretty_asdl::MeasuredDoc* name = nullptr;
  StackRoot _root0(&field);
  StackRoot _root1(&name);

  name = AsciiText(str_concat(field->name, S_fyj));
  return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{name, this->_HNode(field->val)}));
}

pretty_asdl::MeasuredDoc* HNodeEncoder::_HNode(hnode_asdl::hnode_t* h) {
  hnode_asdl::hnode_t* UP_h = nullptr;
  BigStr* color = nullptr;
  BigStr* s = nullptr;
  List<pretty_asdl::MeasuredDoc*>* children = nullptr;
  pretty_asdl::MeasuredDoc* type_name = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  List<pretty_asdl::MeasuredDoc*>* m = nullptr;
  pretty_asdl::MeasuredDoc* child = nullptr;
  StackRoot _root0(&h);
  StackRoot _root1(&UP_h);
  StackRoot _root2(&color);
  StackRoot _root3(&s);
  StackRoot _root4(&children);
  StackRoot _root5(&type_name);
  StackRoot _root6(&mdocs);
  StackRoot _root7(&m);
  StackRoot _root8(&child);

  UP_h = h;
  switch (h->tag()) {
    case hnode_e::AlreadySeen: {
      hnode::AlreadySeen* h = static_cast<hnode::AlreadySeen*>(UP_h);
      return pretty::AsciiText(StrFormat("...0x%s", mylib::hex_lower(h->heap_id)));
    }
      break;
    case hnode_e::Leaf: {
      hnode::Leaf* h = static_cast<hnode::Leaf*>(UP_h);
      switch (h->color) {
        case color_e::TypeName: {
          color = ansi::YELLOW;
        }
          break;
        case color_e::StringConst: {
          color = ansi::BOLD;
        }
          break;
        case color_e::OtherConst: {
          color = ansi::GREEN;
        }
          break;
        case color_e::External: {
          color = str_concat(ansi::BOLD, ansi::BLUE);
        }
          break;
        case color_e::UserType: {
          color = ansi::GREEN;
        }
          break;
        default: {
          assert(0);  // AssertionError
        }
      }
      s = j8_lite::EncodeString(h->s, true);
      return this->_StyledAscii(color, s);
    }
      break;
    case hnode_e::Array: {
      hnode::Array* h = static_cast<hnode::Array*>(UP_h);
      mylib::MaybeCollect();
      if (len(h->children) == 0) {
        return AsciiText(S_xmu);
      }
      children = Alloc<List<pretty_asdl::MeasuredDoc*>>();
      for (ListIter<hnode_asdl::hnode_t*> it(h->children); !it.Done(); it.Next()) {
        hnode_asdl::hnode_t* item = it.Value();
        children->append(this->_HNode(item));
      }
      return this->_Surrounded(S_Eax, this->_Tabular(children, S_Aoo), S_pcD);
    }
      break;
    case hnode_e::Record: {
      hnode::Record* h = static_cast<hnode::Record*>(UP_h);
      type_name = nullptr;
      if (len(h->node_type)) {
        type_name = this->_StyledAscii(this->type_color, h->node_type);
      }
      mdocs = nullptr;
      if ((h->unnamed_fields != nullptr and len(h->unnamed_fields))) {
        mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
        for (ListIter<hnode_asdl::hnode_t*> it(h->unnamed_fields); !it.Done(); it.Next()) {
          hnode_asdl::hnode_t* item = it.Value();
          mdocs->append(this->_HNode(item));
        }
      }
      else {
        if (len(h->fields) != 0) {
          mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
          for (ListIter<hnode_asdl::Field*> it(h->fields); !it.Done(); it.Next()) {
            hnode_asdl::Field* field = it.Value();
            mdocs->append(this->_Field(field));
          }
        }
      }
      if (mdocs == nullptr) {
        m = NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(h->left)});
        if (type_name != nullptr) {
          m->append(type_name);
        }
        m->append(AsciiText(h->right));
        return _Concat(m);
      }
      child = this->_Join(mdocs, S_Aoo, S_yfw);
      if (type_name != nullptr) {
        return this->_SurroundedAndPrefixed(h->left, type_name, S_yfw, child, h->right);
      }
      else {
        return this->_Surrounded(h->left, child, h->right);
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

}  // define namespace pp_hnode

namespace pp_value {  // define

using pretty_asdl::doc;
using pretty_asdl::Measure;
using pretty_asdl::MeasuredDoc;
using runtime_asdl::error_code_e;
using value_asdl::Obj;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::value_str;
using pretty::_Break;
using pretty::_Concat;
using pretty::AsciiText;

BigStr* ValType(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  return value_str(val->tag(), false);
}

BigStr* FloatString(double fl) {
  BigStr* s = nullptr;
  StackRoot _root0(&s);

  if (math::isinf(fl)) {
    s = S_BvB;
    if (fl < 0) {
      s = str_concat(S_Bjq, s);
    }
  }
  else {
    if (math::isnan(fl)) {
      s = S_ywk;
    }
    else {
      s = str(fl);
    }
  }
  return s;
}

int TryUnicodeWidth(BigStr* s) {
  int width;
  StackRoot _root0(&s);

  try {
    width = libc::wcswidth(s);
  }
  catch (UnicodeError*) {
    width = len(s);
  }
  if (width == -1) {
    return len(s);
  }
  return width;
}

pretty_asdl::MeasuredDoc* UText(BigStr* string) {
  StackRoot _root0(&string);

  return Alloc<MeasuredDoc>(Alloc<doc::Text>(string), Alloc<Measure>(TryUnicodeWidth(string), -1));
}

ValueEncoder::ValueEncoder() : ::pp_hnode::BaseEncoder() {
  this->ysh_style = true;
  this->int_style = ansi::YELLOW;
  this->float_style = ansi::BLUE;
  this->null_style = ansi::RED;
  this->bool_style = ansi::CYAN;
  this->string_style = ansi::GREEN;
  this->cycle_style = str_concat(ansi::BOLD, ansi::BLUE);
  this->type_style = ansi::MAGENTA;
}

List<pretty_asdl::MeasuredDoc*>* ValueEncoder::TypePrefix(BigStr* type_str) {
  pretty_asdl::MeasuredDoc* type_name = nullptr;
  int n;
  BigStr* spaces = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  StackRoot _root0(&type_str);
  StackRoot _root1(&type_name);
  StackRoot _root2(&spaces);
  StackRoot _root3(&mdocs);

  type_name = this->_Styled(this->type_style, AsciiText(type_str));
  n = len(type_str);
  spaces = str_repeat(S_yfw, (6 - n));
  mdocs = NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_ijB), type_name, AsciiText(S_hxb), _Break(spaces)});
  return mdocs;
}

pretty_asdl::MeasuredDoc* ValueEncoder::Value(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  this->visiting->clear();
  return this->_Value(val);
}

pretty_asdl::MeasuredDoc* ValueEncoder::_DictKey(BigStr* s) {
  BigStr* encoded = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&encoded);

  if (match::IsValidVarName(s)) {
    encoded = s;
  }
  else {
    if (this->ysh_style) {
      encoded = j8_lite::YshEncodeString(s);
    }
    else {
      encoded = j8_lite::EncodeString(s);
    }
  }
  return UText(encoded);
}

pretty_asdl::MeasuredDoc* ValueEncoder::_StringLiteral(BigStr* s) {
  BigStr* encoded = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&encoded);

  if (this->ysh_style) {
    encoded = j8_lite::YshEncodeString(s);
  }
  else {
    encoded = j8_lite::EncodeString(s);
  }
  return this->_Styled(this->string_style, UText(encoded));
}

pretty_asdl::MeasuredDoc* ValueEncoder::_BashStringLiteral(BigStr* s) {
  BigStr* encoded = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&encoded);

  encoded = j8_lite::ShellEncode(s);
  return this->_Styled(this->string_style, UText(encoded));
}

pretty_asdl::MeasuredDoc* ValueEncoder::_YshList(value::List* vlist) {
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  StackRoot _root0(&vlist);
  StackRoot _root1(&mdocs);

  if (len(vlist->items) == 0) {
    return AsciiText(S_xmu);
  }
  mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  for (ListIter<value_asdl::value_t*> it(vlist->items); !it.Done(); it.Next()) {
    value_asdl::value_t* item = it.Value();
    mdocs->append(this->_Value(item));
  }
  return this->_Surrounded(S_Eax, this->_Tabular(mdocs, S_Cce), S_pcD);
}

List<pretty_asdl::MeasuredDoc*>* ValueEncoder::_DictMdocs(Dict<BigStr*, value_asdl::value_t*>* d) {
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  StackRoot _root0(&d);
  StackRoot _root1(&mdocs);

  mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  for (DictIter<BigStr*, value_asdl::value_t*> it(d); !it.Done(); it.Next()) {
    BigStr* k = it.Key();
    value_asdl::value_t* v = it.Value();
    mdocs->append(_Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{this->_DictKey(k), AsciiText(S_ows), this->_Value(v)})));
  }
  return mdocs;
}

pretty_asdl::MeasuredDoc* ValueEncoder::_YshDict(value::Dict* vdict) {
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  StackRoot _root0(&vdict);
  StackRoot _root1(&mdocs);

  if (len(vdict->d) == 0) {
    return AsciiText(S_Fni);
  }
  mdocs = this->_DictMdocs(vdict->d);
  return this->_Surrounded(S_ato, this->_Join(mdocs, S_Cce, S_yfw), S_cEn);
}

pretty_asdl::MeasuredDoc* ValueEncoder::_BashArray(value::BashArray* varray) {
  pretty_asdl::MeasuredDoc* type_name = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  StackRoot _root0(&varray);
  StackRoot _root1(&type_name);
  StackRoot _root2(&mdocs);

  type_name = this->_Styled(this->type_style, AsciiText(S_tDu));
  if (bash_impl::BashArray_Count(varray) == 0) {
    return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_ijB), type_name, AsciiText(S_hxb)}));
  }
  mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  for (ListIter<BigStr*> it(bash_impl::BashArray_GetValues(varray)); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    StackRoot _for(&s  );
    if (s == nullptr) {
      mdocs->append(AsciiText(S_lbA));
    }
    else {
      mdocs->append(this->_BashStringLiteral(s));
    }
  }
  return this->_SurroundedAndPrefixed(S_ijB, type_name, S_yfw, this->_Tabular(mdocs, S_Aoo), S_hxb);
}

pretty_asdl::MeasuredDoc* ValueEncoder::_BashAssoc(value::BashAssoc* vassoc) {
  pretty_asdl::MeasuredDoc* type_name = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  StackRoot _root0(&vassoc);
  StackRoot _root1(&type_name);
  StackRoot _root2(&mdocs);

  type_name = this->_Styled(this->type_style, AsciiText(S_Agv));
  if (bash_impl::BashAssoc_Count(vassoc) == 0) {
    return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_ijB), type_name, AsciiText(S_hxb)}));
  }
  mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  for (DictIter<BigStr*, BigStr*> it(bash_impl::BashAssoc_GetDict(vassoc)); !it.Done(); it.Next()) {
    BigStr* k2 = it.Key();
    BigStr* v2 = it.Value();
    mdocs->append(_Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_Eax), this->_BashStringLiteral(k2), AsciiText(S_nuz), this->_BashStringLiteral(v2)})));
  }
  return this->_SurroundedAndPrefixed(S_ijB, type_name, S_yfw, this->_Join(mdocs, S_Aoo, S_yfw), S_hxb);
}

pretty_asdl::MeasuredDoc* ValueEncoder::_SparseArray(value::SparseArray* val) {
  pretty_asdl::MeasuredDoc* type_name = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  BigStr* v2 = nullptr;
  runtime_asdl::error_code_t error_code;
  StackRoot _root0(&val);
  StackRoot _root1(&type_name);
  StackRoot _root2(&mdocs);
  StackRoot _root3(&v2);

  type_name = this->_Styled(this->type_style, AsciiText(S_qFf));
  if (bash_impl::SparseArray_Count(val) == 0) {
    return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_ijB), type_name, AsciiText(S_hxb)}));
  }
  mdocs = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  for (ListIter<mops::BigInt> it(bash_impl::SparseArray_GetKeys(val)); !it.Done(); it.Next()) {
    mops::BigInt k2 = it.Value();
    Tuple2<BigStr*, runtime_asdl::error_code_t> tup0 = bash_impl::SparseArray_GetElement(val, k2);
    v2 = tup0.at0();
    error_code = tup0.at1();
    mdocs->append(_Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_Eax), this->_Styled(this->int_style, AsciiText(mops::ToStr(k2))), AsciiText(S_nuz), this->_BashStringLiteral(v2)})));
  }
  return this->_SurroundedAndPrefixed(S_ijB, type_name, S_yfw, this->_Join(mdocs, S_Aoo, S_yfw), S_hxb);
}

pretty_asdl::MeasuredDoc* ValueEncoder::_Obj(value_asdl::Obj* obj) {
  List<pretty_asdl::MeasuredDoc*>* chain = nullptr;
  value_asdl::Obj* cur = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&chain);
  StackRoot _root2(&cur);
  StackRoot _root3(&mdocs);

  chain = Alloc<List<pretty_asdl::MeasuredDoc*>>();
  cur = obj;
  while (cur != nullptr) {
    mdocs = this->_DictMdocs(cur->d);
    chain->append(this->_Surrounded(S_ijB, this->_Join(mdocs, S_Cce, S_yfw), S_hxb));
    cur = cur->prototype;
    if (cur != nullptr) {
      chain->append(AsciiText(S_dtA));
    }
  }
  return _Concat(chain);
}

pretty_asdl::MeasuredDoc* ValueEncoder::_Value(value_asdl::value_t* val) {
  bool b;
  mops::BigInt i;
  double f;
  BigStr* s = nullptr;
  value::Range* r = nullptr;
  pretty_asdl::MeasuredDoc* type_name = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  value::List* vlist = nullptr;
  int heap_id;
  pretty_asdl::MeasuredDoc* result = nullptr;
  value::Dict* vdict = nullptr;
  value::SparseArray* sparse = nullptr;
  value::BashArray* varray = nullptr;
  value::BashAssoc* vassoc = nullptr;
  value_asdl::Obj* vaobj = nullptr;
  BigStr* id_str = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&s);
  StackRoot _root2(&r);
  StackRoot _root3(&type_name);
  StackRoot _root4(&mdocs);
  StackRoot _root5(&vlist);
  StackRoot _root6(&result);
  StackRoot _root7(&vdict);
  StackRoot _root8(&sparse);
  StackRoot _root9(&varray);
  StackRoot _root10(&vassoc);
  StackRoot _root11(&vaobj);
  StackRoot _root12(&id_str);

  switch (val->tag()) {
    case value_e::Null: {
      return this->_Styled(this->null_style, AsciiText(S_lbA));
    }
      break;
    case value_e::Bool: {
      b = static_cast<value::Bool*>(val)->b;
      return this->_Styled(this->bool_style, AsciiText(b ? S_FsF : S_Ctn));
    }
      break;
    case value_e::Int: {
      i = static_cast<value::Int*>(val)->i;
      return this->_Styled(this->int_style, AsciiText(mops::ToStr(i)));
    }
      break;
    case value_e::Float: {
      f = static_cast<value::Float*>(val)->f;
      return this->_Styled(this->float_style, AsciiText(FloatString(f)));
    }
      break;
    case value_e::Str: {
      s = static_cast<value::Str*>(val)->s;
      return this->_StringLiteral(s);
    }
      break;
    case value_e::Range: {
      r = static_cast<value::Range*>(val);
      type_name = this->_Styled(this->type_style, AsciiText(ValType(r)));
      mdocs = NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(str(r->lower)), AsciiText(S_jhC), AsciiText(str(r->upper))});
      return this->_SurroundedAndPrefixed(S_ijB, type_name, S_yfw, this->_Join(mdocs, S_Aoo, S_yfw), S_hxb);
    }
      break;
    case value_e::List: {
      vlist = static_cast<value::List*>(val);
      heap_id = j8::HeapValueId(vlist);
      if (this->visiting->get(heap_id, false)) {
        return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_Eax), this->_Styled(this->cycle_style, AsciiText(S_otl)), AsciiText(S_pcD)}));
      }
      else {
        this->visiting->set(heap_id, true);
        result = this->_YshList(vlist);
        this->visiting->set(heap_id, false);
        return result;
      }
    }
      break;
    case value_e::Dict: {
      vdict = static_cast<value::Dict*>(val);
      heap_id = j8::HeapValueId(vdict);
      if (this->visiting->get(heap_id, false)) {
        return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_ato), this->_Styled(this->cycle_style, AsciiText(S_otl)), AsciiText(S_cEn)}));
      }
      else {
        this->visiting->set(heap_id, true);
        result = this->_YshDict(vdict);
        this->visiting->set(heap_id, false);
        return result;
      }
    }
      break;
    case value_e::SparseArray: {
      sparse = static_cast<value::SparseArray*>(val);
      return this->_SparseArray(sparse);
    }
      break;
    case value_e::BashArray: {
      varray = static_cast<value::BashArray*>(val);
      return this->_BashArray(varray);
    }
      break;
    case value_e::BashAssoc: {
      vassoc = static_cast<value::BashAssoc*>(val);
      return this->_BashAssoc(vassoc);
    }
      break;
    case value_e::Obj: {
      vaobj = static_cast<Obj*>(val);
      heap_id = j8::HeapValueId(vaobj);
      if (this->visiting->get(heap_id, false)) {
        return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_ijB), this->_Styled(this->cycle_style, AsciiText(S_otl)), AsciiText(S_hxb)}));
      }
      else {
        this->visiting->set(heap_id, true);
        result = this->_Obj(vaobj);
        this->visiting->set(heap_id, false);
        return result;
      }
    }
      break;
    case value_e::Stdin: 
    case value_e::Interrupted: {
      type_name = this->_Styled(this->type_style, AsciiText(ValType(val)));
      return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_eox), type_name, AsciiText(S_jye)}));
    }
      break;
    default: {
      type_name = this->_Styled(this->type_style, AsciiText(ValType(val)));
      id_str = j8::ValueIdString(val);
      return _Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{AsciiText(S_eox), type_name, AsciiText(str_concat(id_str, S_jye))}));
    }
  }
}

}  // define namespace pp_value

namespace pretty {  // define

using pretty_asdl::doc;
using pretty_asdl::doc_e;
using pretty_asdl::DocFragment;
using pretty_asdl::Measure;
using pretty_asdl::MeasuredDoc;
using pretty_asdl::List_Measured;
using mylib::BufWriter;

pretty_asdl::Measure* _EmptyMeasure() {
  return Alloc<Measure>(0, -1);
}

pretty_asdl::Measure* _FlattenMeasure(pretty_asdl::Measure* measure) {
  StackRoot _root0(&measure);

  return Alloc<Measure>(measure->flat, -1);
}

pretty_asdl::Measure* _ConcatMeasure(pretty_asdl::Measure* m1, pretty_asdl::Measure* m2) {
  StackRoot _root0(&m1);
  StackRoot _root1(&m2);

  if (m1->nonflat != -1) {
    return Alloc<Measure>((m1->flat + m2->flat), m1->nonflat);
  }
  else {
    if (m2->nonflat != -1) {
      return Alloc<Measure>((m1->flat + m2->flat), (m1->flat + m2->nonflat));
    }
    else {
      return Alloc<Measure>((m1->flat + m2->flat), -1);
    }
  }
}

int _SuffixLen(pretty_asdl::Measure* measure) {
  StackRoot _root0(&measure);

  if (measure->nonflat != -1) {
    return measure->nonflat;
  }
  else {
    return measure->flat;
  }
}

pretty_asdl::MeasuredDoc* AsciiText(BigStr* string) {
  StackRoot _root0(&string);

  return Alloc<MeasuredDoc>(Alloc<doc::Text>(string), Alloc<Measure>(len(string), -1));
}

pretty_asdl::MeasuredDoc* _Break(BigStr* string) {
  StackRoot _root0(&string);

  return Alloc<MeasuredDoc>(Alloc<doc::Break>(string), Alloc<Measure>(len(string), 0));
}

pretty_asdl::MeasuredDoc* _Indent(int indent, pretty_asdl::MeasuredDoc* mdoc) {
  StackRoot _root0(&mdoc);

  return Alloc<MeasuredDoc>(Alloc<doc::Indent>(indent, mdoc), mdoc->measure);
}

pretty_asdl::Measure* _Splice(List<pretty_asdl::MeasuredDoc*>* out, List<pretty_asdl::MeasuredDoc*>* mdocs) {
  pretty_asdl::Measure* measure = nullptr;
  pretty_asdl::List_Measured* child = nullptr;
  StackRoot _root0(&out);
  StackRoot _root1(&mdocs);
  StackRoot _root2(&measure);
  StackRoot _root3(&child);

  measure = _EmptyMeasure();
  for (ListIter<pretty_asdl::MeasuredDoc*> it(mdocs); !it.Done(); it.Next()) {
    pretty_asdl::MeasuredDoc* mdoc = it.Value();
    StackRoot _for(&mdoc  );
    switch (mdoc->doc->tag()) {
      case doc_e::Concat: {
        child = static_cast<List_Measured*>(mdoc->doc);
        _Splice(out, child);
      }
        break;
      default: {
        out->append(mdoc);
      }
    }
    measure = _ConcatMeasure(measure, mdoc->measure);
  }
  return measure;
}

pretty_asdl::MeasuredDoc* _Concat(List<pretty_asdl::MeasuredDoc*>* mdocs) {
  pretty_asdl::List_Measured* flattened = nullptr;
  pretty_asdl::Measure* measure = nullptr;
  StackRoot _root0(&mdocs);
  StackRoot _root1(&flattened);
  StackRoot _root2(&measure);

  flattened = List_Measured::New();
  measure = _Splice(flattened, mdocs);
  return Alloc<MeasuredDoc>(flattened, measure);
}

pretty_asdl::MeasuredDoc* _Group(pretty_asdl::MeasuredDoc* mdoc) {
  StackRoot _root0(&mdoc);

  return Alloc<MeasuredDoc>(mdoc, mdoc->measure);
}

pretty_asdl::MeasuredDoc* _IfFlat(pretty_asdl::MeasuredDoc* flat_mdoc, pretty_asdl::MeasuredDoc* nonflat_mdoc) {
  StackRoot _root0(&flat_mdoc);
  StackRoot _root1(&nonflat_mdoc);

  return Alloc<MeasuredDoc>(Alloc<doc::IfFlat>(flat_mdoc, nonflat_mdoc), Alloc<Measure>(flat_mdoc->measure->flat, nonflat_mdoc->measure->nonflat));
}

pretty_asdl::MeasuredDoc* _Flat(pretty_asdl::MeasuredDoc* mdoc) {
  StackRoot _root0(&mdoc);

  return Alloc<MeasuredDoc>(Alloc<doc::Flat>(mdoc), _FlattenMeasure(mdoc->measure));
}

PrettyPrinter::PrettyPrinter(int max_width) {
  this->max_width = max_width;
}

bool PrettyPrinter::_Fits(int prefix_len, pretty_asdl::MeasuredDoc* group, pretty_asdl::Measure* suffix_measure) {
  pretty_asdl::Measure* measure = nullptr;
  StackRoot _root0(&group);
  StackRoot _root1(&suffix_measure);
  StackRoot _root2(&measure);

  measure = _ConcatMeasure(_FlattenMeasure(group->measure), suffix_measure);
  return (prefix_len + _SuffixLen(measure)) <= this->max_width;
}

void PrettyPrinter::PrintDoc(pretty_asdl::MeasuredDoc* document, mylib::BufWriter* buf) {
  int prefix_len;
  List<pretty_asdl::DocFragment*>* fragments = nullptr;
  int max_stack;
  pretty_asdl::DocFragment* frag = nullptr;
  pretty_asdl::doc_t* UP_doc = nullptr;
  pretty_asdl::Measure* measure = nullptr;
  bool is_flat;
  pretty_asdl::MeasuredDoc* subdoc = nullptr;
  StackRoot _root0(&document);
  StackRoot _root1(&buf);
  StackRoot _root2(&fragments);
  StackRoot _root3(&frag);
  StackRoot _root4(&UP_doc);
  StackRoot _root5(&measure);
  StackRoot _root6(&subdoc);

  prefix_len = 0;
  fragments = NewList<pretty_asdl::DocFragment*>(std::initializer_list<pretty_asdl::DocFragment*>{Alloc<DocFragment>(_Group(document), 0, false, _EmptyMeasure())});
  max_stack = len(fragments);
  while (len(fragments) > 0) {
    max_stack = max(max_stack, len(fragments));
    frag = fragments->pop();
    UP_doc = frag->mdoc->doc;
    switch (UP_doc->tag()) {
      case doc_e::Text: {
        doc::Text* text = static_cast<doc::Text*>(UP_doc);
        buf->write(text->string);
        prefix_len += frag->mdoc->measure->flat;
      }
        break;
      case doc_e::Break: {
        doc::Break* break_ = static_cast<doc::Break*>(UP_doc);
        if (frag->is_flat) {
          buf->write(break_->string);
          prefix_len += frag->mdoc->measure->flat;
        }
        else {
          buf->write(S_nfs);
          buf->write_spaces(frag->indent);
          prefix_len = frag->indent;
        }
      }
        break;
      case doc_e::Indent: {
        doc::Indent* indented = static_cast<doc::Indent*>(UP_doc);
        fragments->append(Alloc<DocFragment>(indented->mdoc, (frag->indent + indented->indent), frag->is_flat, frag->measure));
      }
        break;
      case doc_e::Concat: {
        List_Measured* concat = static_cast<List_Measured*>(UP_doc);
        measure = frag->measure;
        for (ReverseListIter<pretty_asdl::MeasuredDoc*> it(concat); !it.Done(); it.Next()) {
          pretty_asdl::MeasuredDoc* mdoc = it.Value();
          StackRoot _for(&mdoc        );
          fragments->append(Alloc<DocFragment>(mdoc, frag->indent, frag->is_flat, measure));
          measure = _ConcatMeasure(mdoc->measure, measure);
        }
      }
        break;
      case doc_e::Group: {
        MeasuredDoc* group = static_cast<MeasuredDoc*>(UP_doc);
        is_flat = this->_Fits(prefix_len, group, frag->measure);
        fragments->append(Alloc<DocFragment>(group, frag->indent, is_flat, frag->measure));
      }
        break;
      case doc_e::IfFlat: {
        doc::IfFlat* if_flat = static_cast<doc::IfFlat*>(UP_doc);
        if (frag->is_flat) {
          subdoc = if_flat->flat_mdoc;
        }
        else {
          subdoc = if_flat->nonflat_mdoc;
        }
        fragments->append(Alloc<DocFragment>(subdoc, frag->indent, frag->is_flat, frag->measure));
      }
        break;
      case doc_e::Flat: {
        doc::Flat* flat_doc = static_cast<doc::Flat*>(UP_doc);
        fragments->append(Alloc<DocFragment>(flat_doc->mdoc, frag->indent, true, frag->measure));
      }
        break;
    }
  }
}

}  // define namespace pretty

namespace ui {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using id_kind_asdl::Id_str;
using syntax_asdl::Token;
using syntax_asdl::SourceLine;
using syntax_asdl::loc;
using syntax_asdl::loc_e;
using syntax_asdl::loc_t;
using syntax_asdl::command_t;
using syntax_asdl::command_str;
using syntax_asdl::source;
using syntax_asdl::source_e;
using value_asdl::value_e;
using value_asdl::value_t;
namespace fmt = format;
using mylib::print_stderr;

BigStr* ValType(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  return pp_value::ValType(val);
}

BigStr* CommandType(syntax_asdl::command_t* cmd) {
  StackRoot _root0(&cmd);

  return command_str(cmd->tag(), false);
}

BigStr* PrettyId(int id_) {
  return Id_str(id_);
}

BigStr* PrettyToken(syntax_asdl::Token* tok) {
  BigStr* val = nullptr;
  StackRoot _root0(&tok);
  StackRoot _root1(&val);

  if (tok->id == Id::Eof_Real) {
    return S_ngj;
  }
  val = tok->line->content->slice(tok->col, (tok->col + tok->length));
  return repr(val);
}

BigStr* PrettyDir(BigStr* dir_name, BigStr* home_dir) {
  StackRoot _root0(&dir_name);
  StackRoot _root1(&home_dir);

  if (home_dir != nullptr) {
    if ((str_equals(dir_name, home_dir) or dir_name->startswith(str_concat(home_dir, S_ckc)))) {
      return str_concat(S_Bhp, dir_name->slice(len(home_dir)));
    }
  }
  return dir_name;
}

void _PrintCodeExcerpt(BigStr* line, int col, int length, mylib::Writer* f) {
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&line);
  StackRoot _root1(&f);
  StackRoot _root2(&buf);

  buf = Alloc<mylib::BufWriter>();
  buf->write(S_jqf);
  buf->write(line->rstrip());
  buf->write(S_sEF);
  for (StrIter it(line->slice(0, col)); !it.Done(); it.Next()) {
    BigStr* c = it.Value();
    StackRoot _for(&c  );
    buf->write(str_equals(c, S_mve) ? S_mve : S_yfw);
  }
  buf->write(S_EAB);
  buf->write(str_repeat(S_Bhp, (length - 1)));
  buf->write(S_nfs);
  f->write(buf->getvalue());
}

void _PrintTokenTooLong(loc::TokenTooLong* loc_tok, mylib::Writer* f) {
  syntax_asdl::SourceLine* line = nullptr;
  int col;
  mylib::BufWriter* buf = nullptr;
  BigStr* source_str = nullptr;
  StackRoot _root0(&loc_tok);
  StackRoot _root1(&f);
  StackRoot _root2(&line);
  StackRoot _root3(&buf);
  StackRoot _root4(&source_str);

  line = loc_tok->line;
  col = loc_tok->col;
  buf = Alloc<mylib::BufWriter>();
  buf->write(S_jqf);
  buf->write(line->content->slice(0, (col + 10))->rstrip());
  buf->write(S_sEF);
  for (StrIter it(line->content->slice(0, col)); !it.Done(); it.Next()) {
    BigStr* c = it.Value();
    StackRoot _for(&c  );
    buf->write(str_equals(c, S_mve) ? S_mve : S_yfw);
  }
  buf->write(S_neq);
  source_str = GetLineSourceString(loc_tok->line, true);
  buf->write(StrFormat("%s:%d: Token starting at column %d is too long: %d bytes (%s)\n", source_str, line->line_num, loc_tok->col, loc_tok->length, Id_str(loc_tok->id)));
  f->write(buf->getvalue());
}

BigStr* GetLineSourceString(syntax_asdl::SourceLine* line, bool quote_filename) {
  syntax_asdl::source_t* src = nullptr;
  syntax_asdl::source_t* UP_src = nullptr;
  BigStr* s = nullptr;
  syntax_asdl::Token* blame_tok = nullptr;
  int line_num;
  BigStr* outer_source = nullptr;
  BigStr* var_name = nullptr;
  BigStr* where = nullptr;
  syntax_asdl::Token* orig_tok = nullptr;
  syntax_asdl::Token* span2 = nullptr;
  StackRoot _root0(&line);
  StackRoot _root1(&src);
  StackRoot _root2(&UP_src);
  StackRoot _root3(&s);
  StackRoot _root4(&blame_tok);
  StackRoot _root5(&outer_source);
  StackRoot _root6(&var_name);
  StackRoot _root7(&where);
  StackRoot _root8(&orig_tok);
  StackRoot _root9(&span2);

  src = line->src;
  UP_src = src;
  switch (src->tag()) {
    case source_e::Interactive: {
      s = S_odD;
    }
      break;
    case source_e::Headless: {
      s = S_jgf;
    }
      break;
    case source_e::CFlag: {
      s = S_wxv;
    }
      break;
    case source_e::Stdin: {
      source::Stdin* src = static_cast<source::Stdin*>(UP_src);
      s = StrFormat("[ stdin%s ]", src->comment);
    }
      break;
    case source_e::MainFile: {
      source::MainFile* src = static_cast<source::MainFile*>(UP_src);
      s = src->path;
      if (quote_filename) {
        s = j8_lite::EncodeString(s, true);
      }
    }
      break;
    case source_e::OtherFile: {
      source::OtherFile* src = static_cast<source::OtherFile*>(UP_src);
      s = src->path;
      if (quote_filename) {
        s = j8_lite::EncodeString(s, true);
      }
    }
      break;
    case source_e::Dynamic: {
      source::Dynamic* src = static_cast<source::Dynamic*>(UP_src);
      blame_tok = location::TokenFor(src->location);
      if (blame_tok == nullptr) {
        s = StrFormat("[ %s at ? ]", src->what);
      }
      else {
        line = blame_tok->line;
        line_num = line->line_num;
        outer_source = GetLineSourceString(line, quote_filename);
        s = StrFormat("[ %s at line %d of %s ]", src->what, line_num, outer_source);
      }
    }
      break;
    case source_e::Variable: {
      source::Variable* src = static_cast<source::Variable*>(UP_src);
      if (src->var_name == nullptr) {
        var_name = S_BAk;
      }
      else {
        var_name = repr(src->var_name);
      }
      if (src->location->tag() == loc_e::Missing) {
        where = S_BAk;
      }
      else {
        blame_tok = location::TokenFor(src->location);
        line_num = blame_tok->line->line_num;
        outer_source = GetLineSourceString(blame_tok->line, quote_filename);
        where = StrFormat("line %d of %s", line_num, outer_source);
      }
      s = StrFormat("[ var %s at %s ]", var_name, where);
    }
      break;
    case source_e::VarRef: {
      source::VarRef* src = static_cast<source::VarRef*>(UP_src);
      orig_tok = src->orig_tok;
      line_num = orig_tok->line->line_num;
      outer_source = GetLineSourceString(orig_tok->line, quote_filename);
      where = StrFormat("line %d of %s", line_num, outer_source);
      var_name = lexer::TokenVal(orig_tok);
      s = StrFormat("[ contents of var %r at %s ]", var_name, where);
    }
      break;
    case source_e::Alias: {
      source::Alias* src = static_cast<source::Alias*>(UP_src);
      s = StrFormat("[ expansion of alias %r ]", src->argv0);
    }
      break;
    case source_e::Reparsed: {
      source::Reparsed* src = static_cast<source::Reparsed*>(UP_src);
      span2 = src->left_token;
      outer_source = GetLineSourceString(span2->line, quote_filename);
      s = StrFormat("[ %s in %s ]", src->what, outer_source);
    }
      break;
    case source_e::Synthetic: {
      source::Synthetic* src = static_cast<source::Synthetic*>(UP_src);
      s = StrFormat("-- %s", src->s);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  return s;
}

void _PrintWithLocation(BigStr* prefix, BigStr* msg, syntax_asdl::loc_t* blame_loc, bool show_code) {
  mylib::Writer* f = nullptr;
  syntax_asdl::Token* blame_tok = nullptr;
  int orig_col;
  syntax_asdl::source_t* src = nullptr;
  BigStr* line = nullptr;
  int line_num;
  syntax_asdl::source_t* UP_src = nullptr;
  syntax_asdl::Token* tok2 = nullptr;
  BigStr* line2 = nullptr;
  int lbracket_col;
  BigStr* source_str = nullptr;
  StackRoot _root0(&prefix);
  StackRoot _root1(&msg);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&f);
  StackRoot _root4(&blame_tok);
  StackRoot _root5(&src);
  StackRoot _root6(&line);
  StackRoot _root7(&UP_src);
  StackRoot _root8(&tok2);
  StackRoot _root9(&line2);
  StackRoot _root10(&source_str);

  f = mylib::Stderr();
  if (blame_loc->tag() == loc_e::TokenTooLong) {
    _PrintTokenTooLong(static_cast<loc::TokenTooLong*>(blame_loc), f);
    return ;
  }
  blame_tok = location::TokenFor(blame_loc);
  if (blame_tok == nullptr) {
    f->write(StrFormat("[??? no location ???] %s%s\n", prefix, msg));
    return ;
  }
  orig_col = blame_tok->col;
  src = blame_tok->line->src;
  line = blame_tok->line->content;
  line_num = blame_tok->line->line_num;
  if (show_code) {
    UP_src = src;
    switch (src->tag()) {
      case source_e::Reparsed: {
        source::Reparsed* src = static_cast<source::Reparsed*>(UP_src);
        tok2 = src->left_token;
        line_num = tok2->line->line_num;
        line2 = tok2->line->content;
        lbracket_col = (tok2->col + tok2->length);
        _PrintCodeExcerpt(line2, (orig_col + lbracket_col), 1, f);
      }
        break;
      case source_e::Dynamic: {
        source::Dynamic* src = static_cast<source::Dynamic*>(UP_src);
        _PrintCodeExcerpt(line, blame_tok->col, blame_tok->length, f);
        source_str = GetLineSourceString(blame_tok->line, true);
        f->write(StrFormat("%s:%d\n", source_str, line_num));
        f->write(S_nfs);
        _PrintWithLocation(prefix, msg, src->location, show_code);
        return ;
      }
        break;
      default: {
        _PrintCodeExcerpt(line, blame_tok->col, blame_tok->length, f);
      }
    }
  }
  source_str = GetLineSourceString(blame_tok->line, true);
  f->write(StrFormat("%s:%d: %s%s\n", source_str, line_num, prefix, msg));
}

Tuple2<BigStr*, BigStr*> CodeExcerptAndPrefix(syntax_asdl::Token* blame_tok) {
  syntax_asdl::SourceLine* line = nullptr;
  mylib::BufWriter* buf = nullptr;
  BigStr* source_str = nullptr;
  BigStr* prefix = nullptr;
  StackRoot _root0(&blame_tok);
  StackRoot _root1(&line);
  StackRoot _root2(&buf);
  StackRoot _root3(&source_str);
  StackRoot _root4(&prefix);

  line = blame_tok->line;
  buf = Alloc<mylib::BufWriter>();
  _PrintCodeExcerpt(line->content, blame_tok->col, blame_tok->length, buf);
  source_str = GetLineSourceString(line, true);
  prefix = StrFormat("%s:%d: ", source_str, blame_tok->line->line_num);
  return Tuple2<BigStr*, BigStr*>(buf->getvalue(), prefix);
}

ctx_Location::ctx_Location(ui::ErrorFormatter* errfmt, syntax_asdl::loc_t* location) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->errfmt)));
  errfmt->loc_stack->append(location);
  this->errfmt = errfmt;
}

ctx_Location::~ctx_Location() {
  this->errfmt->loc_stack->pop();
  gHeap.PopRoot();
}

ErrorFormatter::ErrorFormatter() {
  this->loc_stack = Alloc<List<syntax_asdl::loc_t*>>();
  this->one_line_errexit = false;
}

void ErrorFormatter::OneLineErrExit() {
  this->one_line_errexit = true;
}

syntax_asdl::loc_t* ErrorFormatter::_FallbackLocation(syntax_asdl::loc_t* blame_loc) {
  StackRoot _root0(&blame_loc);

  if ((blame_loc == nullptr or blame_loc->tag() == loc_e::Missing)) {
    if (len(this->loc_stack)) {
      return this->loc_stack->at(-1);
    }
    return loc::Missing;
  }
  return blame_loc;
}

void ErrorFormatter::PrefixPrint(BigStr* msg, BigStr* prefix, syntax_asdl::loc_t* blame_loc) {
  StackRoot _root0(&msg);
  StackRoot _root1(&prefix);
  StackRoot _root2(&blame_loc);

  _PrintWithLocation(prefix, msg, this->_FallbackLocation(blame_loc), true);
}

void ErrorFormatter::Print_(BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  StackRoot _root0(&msg);
  StackRoot _root1(&blame_loc);

  _PrintWithLocation(S_Aoo, msg, this->_FallbackLocation(blame_loc), true);
}

void ErrorFormatter::PrintMessage(BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  StackRoot _root0(&msg);
  StackRoot _root1(&blame_loc);

  _PrintWithLocation(S_Aoo, msg, this->_FallbackLocation(blame_loc), false);
}

void ErrorFormatter::StderrLine(BigStr* msg) {
  StackRoot _root0(&msg);

  print_stderr(msg);
}

void ErrorFormatter::PrettyPrintError(error::_ErrorWithLocation* err, BigStr* prefix) {
  StackRoot _root0(&err);
  StackRoot _root1(&prefix);

  _PrintWithLocation(prefix, err->UserErrorString(), err->location, true);
}

void ErrorFormatter::PrintErrExit(error::ErrExit* err, int pid) {
  BigStr* prefix = nullptr;
  StackRoot _root0(&err);
  StackRoot _root1(&prefix);

  prefix = StrFormat("errexit PID %d: ", pid);
  _PrintWithLocation(prefix, err->UserErrorString(), err->location, err->show_code);
}

void PrintAst(syntax_asdl::command_t* node, arg_types::main* flag) {
  mylib::Writer* f = nullptr;
  bool do_abbrev;
  bool perf_stats;
  hnode_asdl::hnode_t* tree = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&flag);
  StackRoot _root2(&f);
  StackRoot _root3(&tree);

  if (maybe_str_equals(flag->ast_format, S_rdE_1)) {
    print_stderr(S_ztv);
  }
  else {
    f = mylib::Stdout();
    do_abbrev = str_contains(flag->ast_format, S_Btg);
    perf_stats = flag->ast_format->startswith(S_Cet);
    if (perf_stats) {
      mylib::print_stderr(S_Aoo);
      mylib::print_stderr(S_aEE);
      mylib::PrintGcStats();
      mylib::print_stderr(S_Aoo);
    }
    tree = node->PrettyTree(do_abbrev);
    if (perf_stats) {
      fmt::_HNodePrettyPrint(true, maybe_str_equals(flag->ast_format, S_myz), tree, f, _GetMaxWidth());
    }
    else {
      fmt::HNodePrettyPrint(tree, f, _GetMaxWidth());
    }
  }
}

bool TypeNotPrinted(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  return (val->tag() == value_e::Null || val->tag() == value_e::Bool || val->tag() == value_e::Int || val->tag() == value_e::Float || val->tag() == value_e::Str || val->tag() == value_e::List || val->tag() == value_e::Dict || val->tag() == value_e::Obj);
}

int _GetMaxWidth() {
  int max_width;
  int width;
  max_width = 80;
  try {
    width = libc::get_terminal_width();
    if (width > 0) {
      max_width = width;
    }
  }
  catch (IOError_OSError*) {
    ;  // pass
  }
  return max_width;
}

void PrettyPrintValue(BigStr* prefix, value_asdl::value_t* val, mylib::Writer* f, int max_width) {
  pp_value::ValueEncoder* encoder = nullptr;
  List<pretty_asdl::MeasuredDoc*>* mdocs = nullptr;
  pretty_asdl::MeasuredDoc* doc = nullptr;
  pretty::PrettyPrinter* printer = nullptr;
  mylib::BufWriter* buf = nullptr;
  StackRoot _root0(&prefix);
  StackRoot _root1(&val);
  StackRoot _root2(&f);
  StackRoot _root3(&encoder);
  StackRoot _root4(&mdocs);
  StackRoot _root5(&doc);
  StackRoot _root6(&printer);
  StackRoot _root7(&buf);

  encoder = Alloc<pp_value::ValueEncoder>();
  encoder->SetUseStyles(f->isatty());
  if (TypeNotPrinted(val)) {
    mdocs = encoder->TypePrefix(pp_value::ValType(val));
    mdocs->append(encoder->Value(val));
    doc = pretty::_Concat(mdocs);
  }
  else {
    doc = encoder->Value(val);
  }
  if (len(prefix)) {
    doc = pretty::_Concat(NewList<pretty_asdl::MeasuredDoc*>(std::initializer_list<pretty_asdl::MeasuredDoc*>{pretty::AsciiText(prefix), pretty::_Indent(4, doc)}));
  }
  if (max_width == -1) {
    max_width = _GetMaxWidth();
  }
  printer = Alloc<pretty::PrettyPrinter>(max_width);
  buf = Alloc<mylib::BufWriter>();
  printer->PrintDoc(doc, buf);
  f->write(buf->getvalue());
  f->write(S_nfs);
}

}  // define namespace ui

namespace args {  // define

using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::CompoundWord;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using error::e_usage;
int String = 1;
int Int = 2;
int Float = 3;
int Bool = 4;

_Attributes::_Attributes(Dict<BigStr*, value_asdl::value_t*>* defaults) {
  this->attrs = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  this->opt_changes = Alloc<List<Tuple2<BigStr*, bool>*>>();
  this->shopt_changes = Alloc<List<Tuple2<BigStr*, bool>*>>();
  this->show_options = false;
  this->actions = Alloc<List<BigStr*>>();
  this->saw_double_dash = false;
  for (DictIter<BigStr*, value_asdl::value_t*> it(defaults); !it.Done(); it.Next()) {
    BigStr* name = it.Key();
    value_asdl::value_t* v = it.Value();
    this->Set(name, v);
  }
}

void _Attributes::SetTrue(BigStr* name) {
  StackRoot _root0(&name);

  this->Set(name, Alloc<value::Bool>(true));
}

void _Attributes::Set(BigStr* name, value_asdl::value_t* val) {
  StackRoot _root0(&name);
  StackRoot _root1(&val);

  name = name->replace(S_Bjq, S_tci);
  if (str_equals(name, S_Fvh)) {
    name = S_xaw;
  }
  this->attrs->set(name, val);
}

Reader::Reader(List<BigStr*>* argv, List<syntax_asdl::CompoundWord*>* locs) {
  this->argv = argv;
  this->locs = locs;
  this->n = len(argv);
  this->i = 0;
}

void Reader::Next() {
  this->i += 1;
}

BigStr* Reader::Peek() {
  if (this->i >= this->n) {
    return nullptr;
  }
  else {
    return this->argv->at(this->i);
  }
}

Tuple2<BigStr*, syntax_asdl::loc_t*> Reader::Peek2() {
  if (this->i >= this->n) {
    return Tuple2<BigStr*, syntax_asdl::loc_t*>(nullptr, loc::Missing);
  }
  else {
    return Tuple2<BigStr*, syntax_asdl::loc_t*>(this->argv->at(this->i), this->locs->at(this->i));
  }
}

BigStr* Reader::ReadRequired(BigStr* error_msg) {
  BigStr* arg = nullptr;
  StackRoot _root0(&error_msg);
  StackRoot _root1(&arg);

  arg = this->Peek();
  if (arg == nullptr) {
    e_usage(error_msg, this->_FirstLocation());
  }
  this->Next();
  return arg;
}

Tuple2<BigStr*, syntax_asdl::loc_t*> Reader::ReadRequired2(BigStr* error_msg) {
  BigStr* arg = nullptr;
  syntax_asdl::CompoundWord* location = nullptr;
  StackRoot _root0(&error_msg);
  StackRoot _root1(&arg);
  StackRoot _root2(&location);

  arg = this->Peek();
  if (arg == nullptr) {
    e_usage(error_msg, this->_FirstLocation());
  }
  location = this->locs->at(this->i);
  this->Next();
  return Tuple2<BigStr*, syntax_asdl::loc_t*>(arg, location);
}

List<BigStr*>* Reader::Rest() {
  return this->argv->slice(this->i);
}

Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*> Reader::Rest2() {
  return Tuple2<List<BigStr*>*, List<syntax_asdl::CompoundWord*>*>(this->argv->slice(this->i), this->locs->slice(this->i));
}

bool Reader::AtEnd() {
  return this->i >= this->n;
}

void Reader::Done() {
  if (!this->AtEnd()) {
    e_usage(S_sAk, this->Location());
  }
}

syntax_asdl::loc_t* Reader::_FirstLocation() {
  if ((this->locs != nullptr and this->locs->at(0) != nullptr)) {
    return this->locs->at(0);
  }
  else {
    return loc::Missing;
  }
}

syntax_asdl::loc_t* Reader::Location() {
  int i;
  if (this->locs != nullptr) {
    if (this->i == this->n) {
      i = (this->n - 1);
    }
    else {
      i = this->i;
    }
    if (this->locs->at(i) != nullptr) {
      return this->locs->at(i);
    }
    else {
      return loc::Missing;
    }
  }
  else {
    return loc::Missing;
  }
}

_Action::_Action() {
  ;  // pass
}

bool _Action::OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out) {
  StackRoot _root0(&attached_arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

_ArgAction::_ArgAction(BigStr* name, bool quit_parsing_flags, List<BigStr*>* valid) {
  this->name = name;
  this->quit_parsing_flags = quit_parsing_flags;
  this->valid = valid;
}

value_asdl::value_t* _ArgAction::_Value(BigStr* arg, syntax_asdl::loc_t* location) {
  StackRoot _root0(&arg);
  StackRoot _root1(&location);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

bool _ArgAction::OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out) {
  BigStr* arg = nullptr;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&attached_arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);
  StackRoot _root3(&arg);
  StackRoot _root4(&val);

  if (attached_arg != nullptr) {
    arg = attached_arg;
  }
  else {
    arg_r->Next();
    arg = arg_r->Peek();
    if (arg == nullptr) {
      e_usage(StrFormat("expected argument to %r", str_concat(S_Bjq, this->name)), arg_r->Location());
    }
  }
  val = this->_Value(arg, arg_r->Location());
  out->Set(this->name, val);
  return this->quit_parsing_flags;
}

SetToInt::SetToInt(BigStr* name) : ::args::_ArgAction(name, false, nullptr) {
}

value_asdl::value_t* SetToInt::_Value(BigStr* arg, syntax_asdl::loc_t* location) {
  bool ok;
  mops::BigInt i;
  StackRoot _root0(&arg);
  StackRoot _root1(&location);

  if (true) {
    Tuple2<bool, mops::BigInt> tup0 = mops::FromStr2(arg);
    ok = tup0.at0();
    i = tup0.at1();
    if (!ok) {
      e_usage(StrFormat("expected integer after %s, got %r", str_concat(S_Bjq, this->name), arg), location);
    }
  }
  else {
    ;  // pass
  }
  if (mops::Greater(mops::BigInt(0), i)) {
    e_usage(StrFormat("got invalid integer for %s: %s", str_concat(S_Bjq, this->name), arg), location);
  }
  return Alloc<value::Int>(i);
}

SetToFloat::SetToFloat(BigStr* name) : ::args::_ArgAction(name, false, nullptr) {
}

value_asdl::value_t* SetToFloat::_Value(BigStr* arg, syntax_asdl::loc_t* location) {
  double f;
  StackRoot _root0(&arg);
  StackRoot _root1(&location);

  try {
    f = to_float(arg);
  }
  catch (ValueError*) {
    e_usage(StrFormat("expected number after %r, got %r", str_concat(S_Bjq, this->name), arg), location);
  }
  if (f < 0) {
    e_usage(StrFormat("got invalid float for %s: %s", str_concat(S_Bjq, this->name), arg), location);
  }
  return Alloc<value::Float>(f);
}

SetToString::SetToString(BigStr* name, bool quit_parsing_flags, List<BigStr*>* valid) : ::args::_ArgAction(name, quit_parsing_flags, valid) {
}

value_asdl::value_t* SetToString::_Value(BigStr* arg, syntax_asdl::loc_t* location) {
  StackRoot _root0(&arg);
  StackRoot _root1(&location);

  if ((this->valid != nullptr and !list_contains(this->valid, arg))) {
    e_usage(StrFormat("got invalid argument %r to %r, expected one of: %s", arg, str_concat(S_Bjq, this->name), S_Ebn->join(this->valid)), location);
  }
  return Alloc<value::Str>(arg);
}

SetAttachedBool::SetAttachedBool(BigStr* name) {
  this->name = name;
}

bool SetAttachedBool::OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out) {
  bool b;
  StackRoot _root0(&attached_arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);

  if (attached_arg != nullptr) {
    if ((str_equals(attached_arg, S_wfw) || str_equals(attached_arg, S_gFh) || str_equals(attached_arg, S_Ctn) || str_equals(attached_arg, S_xmt))) {
      b = false;
    }
    else {
      if ((str_equals(attached_arg, S_vrA) || str_equals(attached_arg, S_cor) || str_equals(attached_arg, S_FsF) || str_equals(attached_arg, S_iCm))) {
        b = true;
      }
      else {
        e_usage(StrFormat("got invalid argument to boolean flag: %r", attached_arg), loc::Missing);
      }
    }
  }
  else {
    b = true;
  }
  out->Set(this->name, Alloc<value::Bool>(b));
  return false;
}

SetToTrue::SetToTrue(BigStr* name) {
  this->name = name;
}

bool SetToTrue::OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out) {
  StackRoot _root0(&attached_arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);

  out->SetTrue(this->name);
  return false;
}

SetOption::SetOption(BigStr* name) {
  this->name = name;
}

bool SetOption::OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out) {
  bool b;
  StackRoot _root0(&attached_arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);

  b = maybe_str_equals(attached_arg, S_Bjq);
  out->opt_changes->append((Alloc<Tuple2<BigStr*, bool>>(this->name, b)));
  return false;
}

SetNamedOption::SetNamedOption(bool shopt) {
  this->names = Alloc<List<BigStr*>>();
  this->shopt = shopt;
}

void SetNamedOption::ArgName(BigStr* name) {
  StackRoot _root0(&name);

  this->names->append(name);
}

bool SetNamedOption::OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out) {
  bool b;
  BigStr* arg = nullptr;
  BigStr* attr_name = nullptr;
  List<Tuple2<BigStr*, bool>*>* changes = nullptr;
  StackRoot _root0(&attached_arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);
  StackRoot _root3(&arg);
  StackRoot _root4(&attr_name);
  StackRoot _root5(&changes);

  b = maybe_str_equals(attached_arg, S_Bjq);
  arg_r->Next();
  arg = arg_r->Peek();
  if (arg == nullptr) {
    out->show_options = true;
    return true;
  }
  attr_name = arg;
  if ((len(this->names) and !list_contains(this->names, attr_name))) {
    e_usage(StrFormat("Invalid option %r", arg), loc::Missing);
  }
  changes = this->shopt ? out->shopt_changes : out->opt_changes;
  changes->append((Alloc<Tuple2<BigStr*, bool>>(attr_name, b)));
  return false;
}

SetAction::SetAction(BigStr* name) {
  this->name = name;
}

bool SetAction::OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out) {
  StackRoot _root0(&attached_arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);

  out->actions->append(this->name);
  return false;
}

SetNamedAction::SetNamedAction() {
  this->names = Alloc<List<BigStr*>>();
}

void SetNamedAction::ArgName(BigStr* name) {
  StackRoot _root0(&name);

  this->names->append(name);
}

bool SetNamedAction::OnMatch(BigStr* attached_arg, args::Reader* arg_r, args::_Attributes* out) {
  BigStr* arg = nullptr;
  BigStr* attr_name = nullptr;
  StackRoot _root0(&attached_arg);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);
  StackRoot _root3(&arg);
  StackRoot _root4(&attr_name);

  arg_r->Next();
  arg = arg_r->Peek();
  if (arg == nullptr) {
    e_usage(S_qsa_1, loc::Missing);
  }
  attr_name = arg;
  if ((len(this->names) and !list_contains(this->names, attr_name))) {
    e_usage(StrFormat("Invalid action name %r", arg), loc::Missing);
  }
  out->actions->append(attr_name);
  return false;
}

args::_Attributes* Parse(flag_spec::_FlagSpec* spec, args::Reader* arg_r) {
  args::_Attributes* out = nullptr;
  BigStr* arg = nullptr;
  int pos;
  BigStr* suffix = nullptr;
  BigStr* flag_name = nullptr;
  args::_Action* action = nullptr;
  int n;
  BigStr* ch = nullptr;
  BigStr* attached_arg = nullptr;
  StackRoot _root0(&spec);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);
  StackRoot _root3(&arg);
  StackRoot _root4(&suffix);
  StackRoot _root5(&flag_name);
  StackRoot _root6(&action);
  StackRoot _root7(&ch);
  StackRoot _root8(&attached_arg);

  out = Alloc<_Attributes>(spec->defaults);
  while (!arg_r->AtEnd()) {
    arg = arg_r->Peek();
    if (maybe_str_equals(arg, S_gpk)) {
      out->saw_double_dash = true;
      arg_r->Next();
      break;
    }
    if ((len(spec->actions_long) and arg->startswith(S_gpk))) {
      pos = arg->find(S_bby, 2);
      if (pos == -1) {
        suffix = nullptr;
        flag_name = arg->slice(2);
      }
      else {
        suffix = arg->slice((pos + 1));
        flag_name = arg->slice(2, pos);
      }
      action = spec->actions_long->get(flag_name);
      if (action == nullptr) {
        e_usage(StrFormat("got invalid flag %r", arg), arg_r->Location());
      }
      action->OnMatch(suffix, arg_r, out);
      arg_r->Next();
      continue;
    }
    else {
      if ((arg->startswith(S_Bjq) and len(arg) > 1)) {
        n = len(arg);
        for (int i = 1; i < n; ++i) {
          ch = arg->at(i);
          if (str_equals(ch, S_wfw)) {
            ch = S_qCh;
          }
          if (list_contains(spec->plus_flags, ch)) {
            out->Set(ch, Alloc<value::Str>(S_Bjq));
            continue;
          }
          if (list_contains(spec->arity0, ch)) {
            out->SetTrue(ch);
            continue;
          }
          if (dict_contains(spec->arity1, ch)) {
            action = spec->arity1->at(ch);
            attached_arg = i < (n - 1) ? arg->slice((i + 1)) : nullptr;
            action->OnMatch(attached_arg, arg_r, out);
            break;
          }
          e_usage(StrFormat("doesn't accept flag %s", str_concat(S_Bjq, ch)), arg_r->Location());
        }
        arg_r->Next();
      }
      else {
        if ((len(spec->plus_flags) and (arg->startswith(S_jnE) and len(arg) > 1))) {
          n = len(arg);
          for (int i = 1; i < n; ++i) {
            ch = arg->at(i);
            if (list_contains(spec->plus_flags, ch)) {
              out->Set(ch, Alloc<value::Str>(S_jnE));
              continue;
            }
            e_usage(StrFormat("doesn't accept option %s", str_concat(S_jnE, ch)), arg_r->Location());
          }
          arg_r->Next();
        }
        else {
          break;
        }
      }
    }
  }
  return out;
}

args::_Attributes* ParseLikeEcho(flag_spec::_FlagSpec* spec, args::Reader* arg_r) {
  args::_Attributes* out = nullptr;
  BigStr* arg = nullptr;
  BigStr* chars = nullptr;
  bool done;
  StackRoot _root0(&spec);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);
  StackRoot _root3(&arg);
  StackRoot _root4(&chars);

  out = Alloc<_Attributes>(spec->defaults);
  while (!arg_r->AtEnd()) {
    arg = arg_r->Peek();
    chars = arg->slice(1);
    if ((arg->startswith(S_Bjq) and len(chars))) {
      done = false;
      for (StrIter it(chars); !it.Done(); it.Next()) {
        BigStr* c = it.Value();
        StackRoot _for(&c      );
        if (!list_contains(spec->arity0, c)) {
          done = true;
          break;
        }
      }
      if (done) {
        break;
      }
      for (StrIter it(chars); !it.Done(); it.Next()) {
        BigStr* ch = it.Value();
        StackRoot _for(&ch      );
        out->SetTrue(ch);
      }
    }
    else {
      break;
    }
    arg_r->Next();
  }
  return out;
}

args::_Attributes* ParseMore(flag_spec::_FlagSpecAndMore* spec, args::Reader* arg_r) {
  args::_Attributes* out = nullptr;
  bool quit;
  BigStr* arg = nullptr;
  args::_Action* action = nullptr;
  BigStr* char0 = nullptr;
  BigStr* attached_arg = nullptr;
  StackRoot _root0(&spec);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&out);
  StackRoot _root3(&arg);
  StackRoot _root4(&action);
  StackRoot _root5(&char0);
  StackRoot _root6(&attached_arg);

  out = Alloc<_Attributes>(spec->defaults);
  quit = false;
  while (!arg_r->AtEnd()) {
    arg = arg_r->Peek();
    if (maybe_str_equals(arg, S_gpk)) {
      out->saw_double_dash = true;
      arg_r->Next();
      break;
    }
    if (arg->startswith(S_gpk)) {
      action = spec->actions_long->get(arg->slice(2));
      if (action == nullptr) {
        e_usage(StrFormat("got invalid flag %r", arg), arg_r->Location());
      }
      action->OnMatch(nullptr, arg_r, out);
      arg_r->Next();
      continue;
    }
    if (((arg->startswith(S_Bjq) or arg->startswith(S_jnE)) and len(arg) > 1)) {
      char0 = arg->at(0);
      for (StrIter it(arg->slice(1)); !it.Done(); it.Next()) {
        BigStr* ch = it.Value();
        StackRoot _for(&ch      );
        action = spec->actions_short->get(ch);
        if (action == nullptr) {
          e_usage(StrFormat("got invalid flag %r", str_concat(S_Bjq, ch)), arg_r->Location());
        }
        attached_arg = list_contains(spec->plus_flags, ch) ? char0 : nullptr;
        quit = action->OnMatch(attached_arg, arg_r, out);
      }
      arg_r->Next();
      if (quit) {
        break;
      }
      else {
        continue;
      }
    }
    break;
  }
  return out;
}

}  // define namespace args

namespace flag_util {  // define

using runtime_asdl::cmd_value;
using runtime_asdl::ProcArgs;
using error::e_usage;

void _DoesNotAccept(runtime_asdl::ProcArgs* proc_args) {
  StackRoot _root0(&proc_args);

  if (proc_args != nullptr) {
    e_usage(S_rDq, proc_args->typed_args->left);
  }
}

Tuple2<args::_Attributes*, args::Reader*> ParseCmdVal(BigStr* spec_name, cmd_value::Argv* cmd_val, bool accept_typed_args) {
  args::Reader* arg_r = nullptr;
  flag_spec::_FlagSpec* spec = nullptr;
  StackRoot _root0(&spec_name);
  StackRoot _root1(&cmd_val);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&spec);

  if (!accept_typed_args) {
    _DoesNotAccept(cmd_val->proc_args);
  }
  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  spec = LookupFlagSpec(spec_name);
  return Tuple2<args::_Attributes*, args::Reader*>(args::Parse(spec, arg_r), arg_r);
}

Tuple2<args::_Attributes*, args::Reader*> ParseLikeEcho(BigStr* spec_name, cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  flag_spec::_FlagSpec* spec = nullptr;
  StackRoot _root0(&spec_name);
  StackRoot _root1(&cmd_val);
  StackRoot _root2(&arg_r);
  StackRoot _root3(&spec);

  _DoesNotAccept(cmd_val->proc_args);
  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  spec = LookupFlagSpec(spec_name);
  return Tuple2<args::_Attributes*, args::Reader*>(args::ParseLikeEcho(spec, arg_r), arg_r);
}

args::_Attributes* Parse(BigStr* spec_name, args::Reader* arg_r) {
  flag_spec::_FlagSpec* spec = nullptr;
  StackRoot _root0(&spec_name);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&spec);

  spec = LookupFlagSpec(spec_name);
  return args::Parse(spec, arg_r);
}

args::_Attributes* ParseMore(BigStr* spec_name, args::Reader* arg_r) {
  flag_spec::_FlagSpecAndMore* spec = nullptr;
  StackRoot _root0(&spec_name);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&spec);

  spec = LookupFlagSpec2(spec_name);
  return args::ParseMore(spec, arg_r);
}

}  // define namespace flag_util

namespace lexer {  // define

using syntax_asdl::Token;
using syntax_asdl::SourceLine;
using types_asdl::lex_mode_t;
using types_asdl::lex_mode_e;
using id_kind_asdl::Id_t;
using id_kind_asdl::Id;
using id_kind_asdl::Id_str;

bool IsPlusEquals(syntax_asdl::Token* tok) {
  int i;
  StackRoot _root0(&tok);

  i = ((tok->col + tok->length) - 2);
  return tok->line->content->find(S_jnE, i, (i + 1)) != -1;
}

bool TokenContains(syntax_asdl::Token* tok, BigStr* substr) {
  StackRoot _root0(&tok);
  StackRoot _root1(&substr);

  return tok->line->content->find(substr, tok->col, (tok->col + tok->length)) != -1;
}

bool TokenEquals(syntax_asdl::Token* tok, BigStr* s) {
  StackRoot _root0(&tok);
  StackRoot _root1(&s);

  if (len(s) != tok->length) {
    return false;
  }
  return TokenContains(tok, s);
}

bool TokenStartsWith(syntax_asdl::Token* tok, BigStr* s) {
  StackRoot _root0(&tok);
  StackRoot _root1(&s);

  return tok->line->content->find(s, tok->col, (tok->col + len(s))) != -1;
}

bool TokenEndsWith(syntax_asdl::Token* tok, BigStr* s) {
  int end;
  StackRoot _root0(&tok);
  StackRoot _root1(&s);

  end = (tok->col + tok->length);
  return tok->line->content->find(s, (end - len(s)), end) != -1;
}

BigStr* TokenVal(syntax_asdl::Token* tok) {
  StackRoot _root0(&tok);

  return tok->line->content->slice(tok->col, (tok->col + tok->length));
}

BigStr* TokenSliceLeft(syntax_asdl::Token* tok, int left_index) {
  int start;
  StackRoot _root0(&tok);

  start = (tok->col + left_index);
  return tok->line->content->slice(start, (tok->col + tok->length));
}

BigStr* TokenSliceRight(syntax_asdl::Token* tok, int right_index) {
  int end;
  StackRoot _root0(&tok);

  end = ((tok->col + tok->length) + right_index);
  return tok->line->content->slice(tok->col, end);
}

BigStr* TokenSlice(syntax_asdl::Token* tok, int left, int right) {
  int start;
  int end;
  StackRoot _root0(&tok);

  start = (tok->col + left);
  end = ((tok->col + tok->length) + right);
  return tok->line->content->slice(start, end);
}

BigStr* LazyStr(syntax_asdl::Token* tok) {
  StackRoot _root0(&tok);

  if (tok->tval == nullptr) {
    if ((tok->id == Id::VSub_DollarName || tok->id == Id::VSub_Number)) {
      tok->tval = TokenSliceLeft(tok, 1);
    }
    else {
      tok->tval = TokenVal(tok);
    }
  }
  return tok->tval;
}

syntax_asdl::Token* DummyToken(int id_, BigStr* val) {
  int col;
  int length;
  StackRoot _root0(&val);

  col = -1;
  length = -1;
  return Alloc<Token>(id_, length, col, nullptr, val);
}

LineLexer::LineLexer(alloc::Arena* arena) {
  this->arena = arena;
  this->replace_last_token = false;
  this->eol_tok = DummyToken(Id::Eol_Tok, S_Aoo);
  this->Reset(nullptr, 0);
}

void LineLexer::Reset(syntax_asdl::SourceLine* src_line, int line_pos) {
  StackRoot _root0(&src_line);

  this->src_line = src_line;
  this->line_pos = line_pos;
}

bool LineLexer::MaybeUnreadOne() {
  if (this->line_pos == 0) {
    return false;
  }
  else {
    this->line_pos -= 1;
    this->replace_last_token = true;
    return true;
  }
}

syntax_asdl::Token* LineLexer::GetEofToken(int id_) {
  syntax_asdl::SourceLine* src_line = nullptr;
  StackRoot _root0(&src_line);

  if (this->src_line == nullptr) {
    src_line = this->arena->AddLine(S_Aoo, 0);
  }
  else {
    src_line = this->src_line;
  }
  return this->arena->NewToken(id_, this->line_pos, 0, src_line);
}

int LineLexer::LookAheadOne(types_asdl::lex_mode_t lex_mode) {
  int pos;
  BigStr* line_str = nullptr;
  int n;
  int tok_type;
  StackRoot _root0(&line_str);

  pos = this->line_pos;
  line_str = this->src_line->content;
  n = len(line_str);
  if (pos == n) {
    return Id::Unknown_Tok;
  }
  else {
    Tuple2<int, int> tup0 = match::OneToken(lex_mode, line_str, pos);
    tok_type = tup0.at0();
    return tok_type;
  }
}

void LineLexer::AssertAtEndOfLine() {
}

int LineLexer::LookPastSpace(types_asdl::lex_mode_t lex_mode) {
  int pos;
  BigStr* line_str = nullptr;
  int n;
  int tok_type;
  int end_pos;
  StackRoot _root0(&line_str);

  pos = this->line_pos;
  line_str = this->src_line->content;
  n = len(line_str);
  while (true) {
    if (pos == n) {
      return Id::Unknown_Tok;
    }
    Tuple2<int, int> tup1 = match::OneToken(lex_mode, line_str, pos);
    tok_type = tup1.at0();
    end_pos = tup1.at1();
    if ((tok_type != Id::WS_Space and tok_type != Id::Ignored_Space)) {
      break;
    }
    pos = end_pos;
  }
  return tok_type;
}

bool LineLexer::LookAheadFuncParens(int unread) {
  int pos;
  int tok_type;
  pos = (this->line_pos - unread);
  Tuple2<int, int> tup2 = match::OneToken(lex_mode_e::FuncParens, this->src_line->content, pos);
  tok_type = tup2.at0();
  return tok_type == Id::LookAhead_FuncParens;
}

BigStr* LineLexer::ByteLookAhead() {
  int pos;
  pos = this->line_pos;
  if (pos == len(this->src_line->content)) {
    return S_Aoo;
  }
  else {
    return this->src_line->content->at(pos);
  }
}

int LineLexer::ByteLookBack() {
  int pos;
  pos = (this->line_pos - 2);
  if (pos < 0) {
    return -1;
  }
  else {
    return ord(this->src_line->content->at(pos));
  }
}

syntax_asdl::Token* LineLexer::Read(types_asdl::lex_mode_t lex_mode) {
  BigStr* line_str = nullptr;
  int line_pos;
  int tok_type;
  int end_pos;
  int tok_len;
  syntax_asdl::Token* t = nullptr;
  StackRoot _root0(&line_str);
  StackRoot _root1(&t);

  if (this->src_line) {
    line_str = this->src_line->content;
  }
  else {
    line_str = S_Aoo;
  }
  line_pos = this->line_pos;
  Tuple2<int, int> tup3 = match::OneToken(lex_mode, line_str, line_pos);
  tok_type = tup3.at0();
  end_pos = tup3.at1();
  if (tok_type == Id::Eol_Tok) {
    return this->eol_tok;
  }
  if (this->replace_last_token) {
    this->arena->UnreadOne();
    this->replace_last_token = false;
  }
  tok_len = (end_pos - line_pos);
  t = this->arena->NewToken(tok_type, line_pos, tok_len, this->src_line);
  this->line_pos = end_pos;
  return t;
}

Lexer::Lexer(lexer::LineLexer* line_lexer, reader::_Reader* line_reader) {
  this->line_lexer = line_lexer;
  this->line_reader = line_reader;
  this->line_id = -1;
  this->translation_stack = Alloc<List<Tuple2<int, int>*>>();
  this->emit_comp_dummy = false;
}

void Lexer::ResetInputObjects() {
  this->line_lexer->Reset(nullptr, 0);
}

bool Lexer::MaybeUnreadOne() {
  return this->line_lexer->MaybeUnreadOne();
}

int Lexer::LookAheadOne(types_asdl::lex_mode_t lex_mode) {
  return this->line_lexer->LookAheadOne(lex_mode);
}

int Lexer::LookPastSpace(types_asdl::lex_mode_t lex_mode) {
  return this->line_lexer->LookPastSpace(lex_mode);
}

bool Lexer::LookAheadFuncParens(int unread) {
  return this->line_lexer->LookAheadFuncParens(unread);
}

BigStr* Lexer::ByteLookAhead() {
  return this->line_lexer->ByteLookAhead();
}

int Lexer::ByteLookBack() {
  return this->line_lexer->ByteLookBack();
}

void Lexer::EmitCompDummy() {
  this->emit_comp_dummy = true;
}

void Lexer::PushHint(int old_id, int new_id) {
  this->translation_stack->append((Alloc<Tuple2<int, int>>(old_id, new_id)));
}

bool Lexer::MoveToNextLine() {
  syntax_asdl::SourceLine* src_line = nullptr;
  int line_pos;
  StackRoot _root0(&src_line);

  this->line_lexer->AssertAtEndOfLine();
  Tuple2<syntax_asdl::SourceLine*, int> tup4 = this->line_reader->GetLine();
  src_line = tup4.at0();
  line_pos = tup4.at1();
  if (src_line == nullptr) {
    return false;
  }
  this->line_lexer->Reset(src_line, line_pos);
  return true;
}

syntax_asdl::Token* Lexer::_Read(types_asdl::lex_mode_t lex_mode) {
  syntax_asdl::Token* t = nullptr;
  syntax_asdl::SourceLine* src_line = nullptr;
  int line_pos;
  int id_;
  int old_id;
  int new_id;
  StackRoot _root0(&t);
  StackRoot _root1(&src_line);

  t = this->line_lexer->Read(lex_mode);
  if (t->id == Id::Eol_Tok) {
    Tuple2<syntax_asdl::SourceLine*, int> tup5 = this->line_reader->GetLine();
    src_line = tup5.at0();
    line_pos = tup5.at1();
    if (src_line == nullptr) {
      if (this->emit_comp_dummy) {
        id_ = Id::Lit_CompDummy;
        this->emit_comp_dummy = false;
      }
      else {
        id_ = Id::Eof_Real;
      }
      return this->line_lexer->GetEofToken(id_);
    }
    this->line_lexer->Reset(src_line, line_pos);
    t = this->line_lexer->Read(lex_mode);
  }
  if (len(this->translation_stack)) {
    Tuple2<int, int>* tup6 = this->translation_stack->at(-1);
    old_id = tup6->at0();
    new_id = tup6->at1();
    if (t->id == old_id) {
      this->translation_stack->pop();
      t->id = new_id;
    }
  }
  return t;
}

syntax_asdl::Token* Lexer::Read(types_asdl::lex_mode_t lex_mode) {
  syntax_asdl::Token* t = nullptr;
  StackRoot _root0(&t);

  while (true) {
    t = this->_Read(lex_mode);
    if (t->id != Id::Ignored_LineCont) {
      break;
    }
  }
  return t;
}

}  // define namespace lexer

namespace location {  // define

using syntax_asdl::expr;
using syntax_asdl::expr_t;
using syntax_asdl::expr_e;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::loc_e;
using syntax_asdl::loc_str;
using syntax_asdl::command;
using syntax_asdl::command_e;
using syntax_asdl::command_t;
using syntax_asdl::sh_lhs;
using syntax_asdl::sh_lhs_e;
using syntax_asdl::sh_lhs_t;
using syntax_asdl::word;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::word_part;
using syntax_asdl::word_part_e;
using syntax_asdl::word_part_t;
using syntax_asdl::CompoundWord;
using syntax_asdl::Token;
using syntax_asdl::SimpleVarSub;
using syntax_asdl::ShArrayLiteral;
using syntax_asdl::SingleQuoted;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::CommandSub;
using syntax_asdl::BracedVarSub;
using syntax_asdl::BraceGroup;
using syntax_asdl::Subscript;
using syntax_asdl::Attribute;
using syntax_asdl::arith_expr;
using syntax_asdl::arith_expr_e;
using syntax_asdl::arith_expr_t;
using syntax_asdl::Eggex;
using value_asdl::LeftName;

value_asdl::LeftName* LName(BigStr* name) {
  StackRoot _root0(&name);

  return Alloc<LeftName>(name, loc::Missing);
}

syntax_asdl::Token* TokenFor(syntax_asdl::loc_t* loc_) {
  syntax_asdl::loc_t* UP_location = nullptr;
  StackRoot _root0(&loc_);
  StackRoot _root1(&UP_location);

  UP_location = loc_;
  switch (loc_->tag()) {
    case loc_e::Missing: {
      return nullptr;
    }
      break;
    case loc_e::Token: {
      Token* tok = static_cast<Token*>(UP_location);
      if (tok) {
        return tok;
      }
      else {
        return nullptr;
      }
    }
      break;
    case loc_e::ArgWord: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_location);
      return LeftTokenForWord(w);
    }
      break;
    case loc_e::WordPart: {
      loc::WordPart* loc_ = static_cast<loc::WordPart*>(UP_location);
      if (loc_->p) {
        return LeftTokenForWordPart(loc_->p);
      }
      else {
        return nullptr;
      }
    }
      break;
    case loc_e::Word: {
      loc::Word* loc_ = static_cast<loc::Word*>(UP_location);
      if (loc_->w) {
        return LeftTokenForWord(loc_->w);
      }
      else {
        return nullptr;
      }
    }
      break;
    case loc_e::Command: {
      loc::Command* loc_ = static_cast<loc::Command*>(UP_location);
      if (loc_->c) {
        return TokenForCommand(loc_->c);
      }
      else {
        return nullptr;
      }
    }
      break;
    case loc_e::Arith: {
      loc::Arith* loc_ = static_cast<loc::Arith*>(UP_location);
      if (loc_->a) {
        return TokenForArith(loc_->a);
      }
      else {
        return nullptr;
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

syntax_asdl::Token* TokenForCommand(syntax_asdl::command_t* node) {
  syntax_asdl::command_t* UP_node = nullptr;
  int tag;
  syntax_asdl::Redir* first = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);
  StackRoot _root2(&first);

  UP_node = node;
  tag = node->tag();
  if (tag == command_e::Redirect) {
    command::Redirect* node = static_cast<command::Redirect*>(UP_node);
    first = node->redirects->at(0);
    return first->op;
  }
  if (tag == command_e::Sentence) {
    command::Sentence* node = static_cast<command::Sentence*>(UP_node);
    return node->terminator;
  }
  if (tag == command_e::Simple) {
    command::Simple* node = static_cast<command::Simple*>(UP_node);
    return node->blame_tok;
  }
  if (tag == command_e::ShAssignment) {
    command::ShAssignment* node = static_cast<command::ShAssignment*>(UP_node);
    return node->left;
  }
  if (tag == command_e::Pipeline) {
    command::Pipeline* node = static_cast<command::Pipeline*>(UP_node);
    if (len(node->ops)) {
      return node->ops->at(0);
    }
    else {
      return node->negated;
    }
  }
  if (tag == command_e::AndOr) {
    command::AndOr* node = static_cast<command::AndOr*>(UP_node);
    return node->ops->at(0);
  }
  if (tag == command_e::DoGroup) {
    command::DoGroup* node = static_cast<command::DoGroup*>(UP_node);
    return node->left;
  }
  if (tag == command_e::BraceGroup) {
    BraceGroup* node = static_cast<BraceGroup*>(UP_node);
    return node->left;
  }
  if (tag == command_e::Subshell) {
    command::Subshell* node = static_cast<command::Subshell*>(UP_node);
    return node->left;
  }
  if (tag == command_e::WhileUntil) {
    command::WhileUntil* node = static_cast<command::WhileUntil*>(UP_node);
    return node->keyword;
  }
  if (tag == command_e::If) {
    command::If* node = static_cast<command::If*>(UP_node);
    return node->if_kw;
  }
  if (tag == command_e::Case) {
    command::Case* node = static_cast<command::Case*>(UP_node);
    return node->case_kw;
  }
  if (tag == command_e::TimeBlock) {
    command::TimeBlock* node = static_cast<command::TimeBlock*>(UP_node);
    return node->keyword;
  }
  return nullptr;
}

syntax_asdl::Token* TokenForArith(syntax_asdl::arith_expr_t* node) {
  syntax_asdl::arith_expr_t* UP_node = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);

  UP_node = node;
  switch (node->tag()) {
    case arith_expr_e::VarSub: {
      Token* vsub = static_cast<Token*>(UP_node);
      return vsub;
    }
      break;
    case arith_expr_e::Word: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_node);
      return LeftTokenForWord(w);
    }
      break;
    case arith_expr_e::Unary: {
      arith_expr::Unary* node = static_cast<arith_expr::Unary*>(UP_node);
      return TokenForArith(node->child);
    }
      break;
    case arith_expr_e::Binary: {
      arith_expr::Binary* node = static_cast<arith_expr::Binary*>(UP_node);
      return TokenForArith(node->op);
    }
      break;
    case arith_expr_e::TernaryOp: {
      arith_expr::TernaryOp* node = static_cast<arith_expr::TernaryOp*>(UP_node);
      return TokenForArith(node->cond);
    }
      break;
  }
  return nullptr;
}

syntax_asdl::Token* LeftTokenForWordPart(syntax_asdl::word_part_t* part) {
  syntax_asdl::word_part_t* UP_part = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&UP_part);

  UP_part = part;
  switch (part->tag()) {
    case word_part_e::ShArrayLiteral: {
      ShArrayLiteral* part = static_cast<ShArrayLiteral*>(UP_part);
      return part->left;
    }
      break;
    case word_part_e::BashAssocLiteral: {
      word_part::BashAssocLiteral* part = static_cast<word_part::BashAssocLiteral*>(UP_part);
      return part->left;
    }
      break;
    case word_part_e::Literal: {
      Token* tok = static_cast<Token*>(UP_part);
      return tok;
    }
      break;
    case word_part_e::EscapedLiteral: {
      word_part::EscapedLiteral* part = static_cast<word_part::EscapedLiteral*>(UP_part);
      return part->token;
    }
      break;
    case word_part_e::SingleQuoted: {
      SingleQuoted* part = static_cast<SingleQuoted*>(UP_part);
      return part->left;
    }
      break;
    case word_part_e::DoubleQuoted: {
      DoubleQuoted* part = static_cast<DoubleQuoted*>(UP_part);
      return part->left;
    }
      break;
    case word_part_e::SimpleVarSub: {
      SimpleVarSub* part = static_cast<SimpleVarSub*>(UP_part);
      return part->tok;
    }
      break;
    case word_part_e::BracedVarSub: {
      BracedVarSub* part = static_cast<BracedVarSub*>(UP_part);
      return part->left;
    }
      break;
    case word_part_e::CommandSub: {
      CommandSub* part = static_cast<CommandSub*>(UP_part);
      return part->left_token;
    }
      break;
    case word_part_e::TildeSub: {
      word_part::TildeSub* part = static_cast<word_part::TildeSub*>(UP_part);
      return part->left;
    }
      break;
    case word_part_e::ArithSub: {
      word_part::ArithSub* part = static_cast<word_part::ArithSub*>(UP_part);
      return part->left;
    }
      break;
    case word_part_e::ExtGlob: {
      word_part::ExtGlob* part = static_cast<word_part::ExtGlob*>(UP_part);
      return part->op;
    }
      break;
    case word_part_e::BracedRange: {
      word_part::BracedRange* part = static_cast<word_part::BracedRange*>(UP_part);
      return part->blame_tok;
    }
      break;
    case word_part_e::BracedTuple: {
      word_part::BracedTuple* part = static_cast<word_part::BracedTuple*>(UP_part);
      return nullptr;
    }
      break;
    case word_part_e::Splice: {
      word_part::Splice* part = static_cast<word_part::Splice*>(UP_part);
      return part->blame_tok;
    }
      break;
    case word_part_e::ExprSub: {
      word_part::ExprSub* part = static_cast<word_part::ExprSub*>(UP_part);
      return part->left;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

syntax_asdl::Token* _RightTokenForWordPart(syntax_asdl::word_part_t* part) {
  syntax_asdl::word_part_t* UP_part = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&UP_part);

  UP_part = part;
  switch (part->tag()) {
    case word_part_e::ShArrayLiteral: {
      ShArrayLiteral* part = static_cast<ShArrayLiteral*>(UP_part);
      return part->right;
    }
      break;
    case word_part_e::BashAssocLiteral: {
      word_part::BashAssocLiteral* part = static_cast<word_part::BashAssocLiteral*>(UP_part);
      return part->right;
    }
      break;
    case word_part_e::Literal: {
      Token* tok = static_cast<Token*>(UP_part);
      return tok;
    }
      break;
    case word_part_e::EscapedLiteral: {
      word_part::EscapedLiteral* part = static_cast<word_part::EscapedLiteral*>(UP_part);
      return part->token;
    }
      break;
    case word_part_e::SingleQuoted: {
      SingleQuoted* part = static_cast<SingleQuoted*>(UP_part);
      return part->right;
    }
      break;
    case word_part_e::DoubleQuoted: {
      DoubleQuoted* part = static_cast<DoubleQuoted*>(UP_part);
      return part->right;
    }
      break;
    case word_part_e::SimpleVarSub: {
      SimpleVarSub* part = static_cast<SimpleVarSub*>(UP_part);
      return part->tok;
    }
      break;
    case word_part_e::BracedVarSub: {
      BracedVarSub* part = static_cast<BracedVarSub*>(UP_part);
      return part->right;
    }
      break;
    case word_part_e::CommandSub: {
      CommandSub* part = static_cast<CommandSub*>(UP_part);
      return part->right;
    }
      break;
    case word_part_e::TildeSub: {
      word_part::TildeSub* part = static_cast<word_part::TildeSub*>(UP_part);
      if (part->name != nullptr) {
        return part->name;
      }
      else {
        return part->left;
      }
    }
      break;
    case word_part_e::ArithSub: {
      word_part::ArithSub* part = static_cast<word_part::ArithSub*>(UP_part);
      return part->right;
    }
      break;
    case word_part_e::ExtGlob: {
      word_part::ExtGlob* part = static_cast<word_part::ExtGlob*>(UP_part);
      return part->right;
    }
      break;
    case word_part_e::BracedRange: {
      word_part::BracedRange* part = static_cast<word_part::BracedRange*>(UP_part);
      return part->blame_tok;
    }
      break;
    case word_part_e::BracedTuple: {
      word_part::BracedTuple* part = static_cast<word_part::BracedTuple*>(UP_part);
      return nullptr;
    }
      break;
    case word_part_e::Splice: {
      word_part::Splice* part = static_cast<word_part::Splice*>(UP_part);
      return part->blame_tok;
    }
      break;
    case word_part_e::ExprSub: {
      word_part::ExprSub* part = static_cast<word_part::ExprSub*>(UP_part);
      return part->right;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

syntax_asdl::Token* LeftTokenForCompoundWord(syntax_asdl::CompoundWord* w) {
  StackRoot _root0(&w);

  if (len(w->parts)) {
    return LeftTokenForWordPart(w->parts->at(0));
  }
  else {
    return nullptr;
  }
}

syntax_asdl::Token* LeftTokenForWord(syntax_asdl::word_t* w) {
  syntax_asdl::word_t* UP_w = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&UP_w);

  if (w == nullptr) {
    return nullptr;
  }
  UP_w = w;
  switch (w->tag()) {
    case word_e::Compound: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_w);
      return LeftTokenForCompoundWord(w);
    }
      break;
    case word_e::Operator: {
      Token* tok = static_cast<Token*>(UP_w);
      return tok;
    }
      break;
    case word_e::BracedTree: {
      word::BracedTree* w = static_cast<word::BracedTree*>(UP_w);
      return LeftTokenForWordPart(w->parts->at(0));
    }
      break;
    case word_e::String: {
      word::String* w = static_cast<word::String*>(UP_w);
      return LeftTokenForWord(w->blame_loc);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

syntax_asdl::Token* RightTokenForWord(syntax_asdl::word_t* w) {
  syntax_asdl::word_t* UP_w = nullptr;
  syntax_asdl::word_part_t* end = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&UP_w);
  StackRoot _root2(&end);

  UP_w = w;
  switch (w->tag()) {
    case word_e::Compound: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_w);
      if (len(w->parts)) {
        end = w->parts->at(-1);
        return _RightTokenForWordPart(end);
      }
      else {
        return nullptr;
      }
    }
      break;
    case word_e::Operator: {
      Token* tok = static_cast<Token*>(UP_w);
      return tok;
    }
      break;
    case word_e::BracedTree: {
      word::BracedTree* w = static_cast<word::BracedTree*>(UP_w);
      return _RightTokenForWordPart(w->parts->at(-1));
    }
      break;
    case word_e::String: {
      word::String* w = static_cast<word::String*>(UP_w);
      return RightTokenForWord(w->blame_loc);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

syntax_asdl::Token* TokenForLhsExpr(syntax_asdl::sh_lhs_t* node) {
  syntax_asdl::sh_lhs_t* UP_node = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);

  UP_node = node;
  switch (node->tag()) {
    case sh_lhs_e::Name: {
      sh_lhs::Name* node = static_cast<sh_lhs::Name*>(UP_node);
      return node->left;
    }
      break;
    case sh_lhs_e::IndexedName: {
      sh_lhs::IndexedName* node = static_cast<sh_lhs::IndexedName*>(UP_node);
      return node->left;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

syntax_asdl::loc_t* TokenForExpr(syntax_asdl::expr_t* node) {
  syntax_asdl::expr_t* UP_node = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);

  UP_node = node;
  switch (node->tag()) {
    case expr_e::Const: {
      expr::Const* node = static_cast<expr::Const*>(UP_node);
      return node->c;
    }
      break;
    case expr_e::Var: {
      expr::Var* node = static_cast<expr::Var*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::Place: {
      expr::Place* node = static_cast<expr::Place*>(UP_node);
      return node->blame_tok;
    }
      break;
    case expr_e::CommandSub: {
      CommandSub* node = static_cast<CommandSub*>(UP_node);
      return node->left_token;
    }
      break;
    case expr_e::ShArrayLiteral: {
      ShArrayLiteral* node = static_cast<ShArrayLiteral*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::DoubleQuoted: {
      DoubleQuoted* node = static_cast<DoubleQuoted*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::SingleQuoted: {
      SingleQuoted* node = static_cast<SingleQuoted*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::BracedVarSub: {
      BracedVarSub* node = static_cast<BracedVarSub*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::SimpleVarSub: {
      SimpleVarSub* node = static_cast<SimpleVarSub*>(UP_node);
      return node->tok;
    }
      break;
    case expr_e::Unary: {
      expr::Unary* node = static_cast<expr::Unary*>(UP_node);
      return node->op;
    }
      break;
    case expr_e::Binary: {
      expr::Binary* node = static_cast<expr::Binary*>(UP_node);
      return node->op;
    }
      break;
    case expr_e::Slice: {
      expr::Slice* node = static_cast<expr::Slice*>(UP_node);
      return node->op;
    }
      break;
    case expr_e::Range: {
      expr::Range* node = static_cast<expr::Range*>(UP_node);
      return node->op;
    }
      break;
    case expr_e::Compare: {
      expr::Compare* node = static_cast<expr::Compare*>(UP_node);
      return TokenForExpr(node->left);
    }
      break;
    case expr_e::IfExp: {
      return loc::Missing;
    }
      break;
    case expr_e::List: {
      expr::List* node = static_cast<expr::List*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::Tuple: {
      expr::Tuple* node = static_cast<expr::Tuple*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::Dict: {
      expr::Dict* node = static_cast<expr::Dict*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::ListComp: {
      expr::ListComp* node = static_cast<expr::ListComp*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::GeneratorExp: {
      return loc::Missing;
    }
      break;
    case expr_e::Lambda: {
      return loc::Missing;
    }
      break;
    case expr_e::FuncCall: {
      expr::FuncCall* node = static_cast<expr::FuncCall*>(UP_node);
      return node->args->left;
    }
      break;
    case expr_e::Subscript: {
      Subscript* node = static_cast<Subscript*>(UP_node);
      return node->left;
    }
      break;
    case expr_e::Attribute: {
      Attribute* node = static_cast<Attribute*>(UP_node);
      return node->op;
    }
      break;
    case expr_e::Eggex: {
      Eggex* node = static_cast<Eggex*>(UP_node);
      return node->left;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

}  // define namespace location

namespace parse_lib {  // define

using id_kind_asdl::Id_t;
using syntax_asdl::Token;
using syntax_asdl::CompoundWord;
using syntax_asdl::expr_t;
using syntax_asdl::Redir;
using syntax_asdl::ArgList;
using syntax_asdl::Proc;
using syntax_asdl::Func;
using syntax_asdl::command;
using syntax_asdl::pat_t;
using types_asdl::lex_mode_e;
namespace fmt = format;
using expr_parse::ctx_PNodeAllocator;

_BaseTrail::_BaseTrail() {
  this->words = Alloc<List<syntax_asdl::CompoundWord*>>();
  this->redirects = Alloc<List<syntax_asdl::Redir*>>();
  this->tokens = Alloc<List<syntax_asdl::Token*>>();
  this->alias_words = Alloc<List<syntax_asdl::CompoundWord*>>();
  this->_expanding_alias = false;
}

void _BaseTrail::Clear() {
  ;  // pass
}

void _BaseTrail::SetLatestWords(List<syntax_asdl::CompoundWord*>* words, List<syntax_asdl::Redir*>* redirects) {
  StackRoot _root0(&words);
  StackRoot _root1(&redirects);

  ;  // pass
}

void _BaseTrail::AppendToken(syntax_asdl::Token* token) {
  StackRoot _root0(&token);

  ;  // pass
}

void _BaseTrail::BeginAliasExpansion() {
  ;  // pass
}

void _BaseTrail::EndAliasExpansion() {
  ;  // pass
}

ctx_Alias::ctx_Alias(parse_lib::_BaseTrail* trail) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->trail)));
  trail->_expanding_alias = true;
  this->trail = trail;
}

ctx_Alias::~ctx_Alias() {
  this->trail->_expanding_alias = false;
  gHeap.PopRoot();
}

Trail::Trail() : ::parse_lib::_BaseTrail() {
}

void Trail::Clear() {
  this->words->clear();
  this->redirects->clear();
  this->tokens->clear();
  this->alias_words->clear();
}

void Trail::SetLatestWords(List<syntax_asdl::CompoundWord*>* words, List<syntax_asdl::Redir*>* redirects) {
  StackRoot _root0(&words);
  StackRoot _root1(&redirects);

  if (this->_expanding_alias) {
    this->alias_words = words;
    return ;
  }
  this->words = words;
  this->redirects = redirects;
}

void Trail::AppendToken(syntax_asdl::Token* token) {
  StackRoot _root0(&token);

  if (this->_expanding_alias) {
    return ;
  }
  this->tokens->append(token);
}

ParseContext::ParseContext(alloc::Arena* arena, optview::Parse* parse_opts, Dict<BigStr*, BigStr*>* aliases, grammar::Grammar* ysh_grammar, bool do_lossless) {
  this->arena = arena;
  this->parse_opts = parse_opts;
  this->aliases = aliases;
  this->ysh_grammar = ysh_grammar;
  this->do_lossless = do_lossless;
  if (ysh_grammar) {
    this->tr = Alloc<expr_to_ast::Transformer>(ysh_grammar);
  }
  else {
    this->tr = nullptr;
  }
  this->trail = Alloc<_BaseTrail>();
}

void ParseContext::Init_Trail(parse_lib::_BaseTrail* trail) {
  StackRoot _root0(&trail);

  this->trail = trail;
}

lexer::Lexer* ParseContext::MakeLexer(reader::_Reader* line_reader) {
  lexer::LineLexer* line_lexer = nullptr;
  StackRoot _root0(&line_reader);
  StackRoot _root1(&line_lexer);

  line_lexer = Alloc<lexer::LineLexer>(line_reader->arena);
  return Alloc<lexer::Lexer>(line_lexer, line_reader);
}

cmd_parse::CommandParser* ParseContext::MakeOshParser(reader::_Reader* line_reader, bool emit_comp_dummy) {
  lexer::Lexer* lx = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  StackRoot _root0(&line_reader);
  StackRoot _root1(&lx);
  StackRoot _root2(&w_parser);
  StackRoot _root3(&c_parser);

  lx = this->MakeLexer(line_reader);
  if (emit_comp_dummy) {
    lx->EmitCompDummy();
  }
  w_parser = Alloc<word_parse::WordParser>(this, lx, line_reader);
  c_parser = Alloc<cmd_parse::CommandParser>(this, this->parse_opts, w_parser, lx, line_reader);
  return c_parser;
}

cmd_parse::CommandParser* ParseContext::MakeConfigParser(reader::_Reader* line_reader) {
  lexer::Lexer* lx = nullptr;
  optview::Parse* parse_opts = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  StackRoot _root0(&line_reader);
  StackRoot _root1(&lx);
  StackRoot _root2(&parse_opts);
  StackRoot _root3(&w_parser);
  StackRoot _root4(&c_parser);

  lx = this->MakeLexer(line_reader);
  parse_opts = state::MakeYshParseOpts();
  w_parser = Alloc<word_parse::WordParser>(this, lx, line_reader);
  c_parser = Alloc<cmd_parse::CommandParser>(this, parse_opts, w_parser, lx, line_reader);
  return c_parser;
}

word_parse::WordParser* ParseContext::MakeWordParserForHereDoc(reader::_Reader* line_reader) {
  lexer::Lexer* lx = nullptr;
  StackRoot _root0(&line_reader);
  StackRoot _root1(&lx);

  lx = this->MakeLexer(line_reader);
  return Alloc<word_parse::WordParser>(this, lx, line_reader);
}

word_parse::WordParser* ParseContext::MakeWordParser(lexer::Lexer* lx, reader::_Reader* line_reader) {
  StackRoot _root0(&lx);
  StackRoot _root1(&line_reader);

  return Alloc<word_parse::WordParser>(this, lx, line_reader);
}

tdop::TdopParser* ParseContext::MakeArithParser(BigStr* code_str) {
  reader::FileLineReader* line_reader = nullptr;
  lexer::Lexer* lx = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  tdop::TdopParser* a_parser = nullptr;
  StackRoot _root0(&code_str);
  StackRoot _root1(&line_reader);
  StackRoot _root2(&lx);
  StackRoot _root3(&w_parser);
  StackRoot _root4(&a_parser);

  line_reader = reader::StringLineReader(code_str, this->arena);
  lx = this->MakeLexer(line_reader);
  w_parser = Alloc<word_parse::WordParser>(this, lx, line_reader);
  w_parser->Init(lex_mode_e::Arith);
  a_parser = Alloc<tdop::TdopParser>(arith_parse::Spec(), w_parser, this->parse_opts);
  return a_parser;
}

cmd_parse::CommandParser* ParseContext::MakeParserForCommandSub(reader::_Reader* line_reader, lexer::Lexer* lexer, int eof_id) {
  word_parse::WordParser* w_parser = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  StackRoot _root0(&line_reader);
  StackRoot _root1(&lexer);
  StackRoot _root2(&w_parser);
  StackRoot _root3(&c_parser);

  w_parser = Alloc<word_parse::WordParser>(this, lexer, line_reader);
  c_parser = Alloc<cmd_parse::CommandParser>(this, this->parse_opts, w_parser, lexer, line_reader, eof_id);
  return c_parser;
}

word_parse::WordParser* ParseContext::MakeWordParserForPlugin(BigStr* code_str) {
  reader::FileLineReader* line_reader = nullptr;
  lexer::Lexer* lx = nullptr;
  StackRoot _root0(&code_str);
  StackRoot _root1(&line_reader);
  StackRoot _root2(&lx);

  line_reader = reader::StringLineReader(code_str, this->arena);
  lx = this->MakeLexer(line_reader);
  return Alloc<word_parse::WordParser>(this, lx, line_reader);
}

expr_parse::ExprParser* ParseContext::_YshParser() {
  return Alloc<expr_parse::ExprParser>(this, this->ysh_grammar);
}

Tuple2<command::VarDecl*, syntax_asdl::Token*> ParseContext::ParseVarDecl(syntax_asdl::Token* kw_token, lexer::Lexer* lexer) {
  expr_parse::ExprParser* e_parser = nullptr;
  pnode::PNode* pnode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  command::VarDecl* ast_node = nullptr;
  StackRoot _root0(&kw_token);
  StackRoot _root1(&lexer);
  StackRoot _root2(&e_parser);
  StackRoot _root3(&pnode);
  StackRoot _root4(&last_token);
  StackRoot _root5(&ast_node);

  e_parser = this->_YshParser();
  {  // with
    ctx_PNodeAllocator ctx{e_parser};

    Tuple2<pnode::PNode*, syntax_asdl::Token*> tup0 = e_parser->Parse(lexer, grammar_nt::ysh_var_decl);
    pnode = tup0.at0();
    last_token = tup0.at1();
    ast_node = this->tr->MakeVarDecl(pnode);
    ast_node->keyword = kw_token;
  }
  return Tuple2<command::VarDecl*, syntax_asdl::Token*>(ast_node, last_token);
}

Tuple2<command::Mutation*, syntax_asdl::Token*> ParseContext::ParseMutation(syntax_asdl::Token* kw_token, lexer::Lexer* lexer) {
  expr_parse::ExprParser* e_parser = nullptr;
  pnode::PNode* pnode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  command::Mutation* ast_node = nullptr;
  StackRoot _root0(&kw_token);
  StackRoot _root1(&lexer);
  StackRoot _root2(&e_parser);
  StackRoot _root3(&pnode);
  StackRoot _root4(&last_token);
  StackRoot _root5(&ast_node);

  e_parser = this->_YshParser();
  {  // with
    ctx_PNodeAllocator ctx{e_parser};

    Tuple2<pnode::PNode*, syntax_asdl::Token*> tup1 = e_parser->Parse(lexer, grammar_nt::ysh_mutation);
    pnode = tup1.at0();
    last_token = tup1.at1();
    ast_node = this->tr->MakeMutation(pnode);
    ast_node->keyword = kw_token;
  }
  return Tuple2<command::Mutation*, syntax_asdl::Token*>(ast_node, last_token);
}

void ParseContext::ParseProcCallArgs(lexer::Lexer* lx, syntax_asdl::ArgList* out, int start_symbol) {
  expr_parse::ExprParser* e_parser = nullptr;
  pnode::PNode* pnode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&lx);
  StackRoot _root1(&out);
  StackRoot _root2(&e_parser);
  StackRoot _root3(&pnode);
  StackRoot _root4(&last_token);

  e_parser = this->_YshParser();
  {  // with
    ctx_PNodeAllocator ctx{e_parser};

    Tuple2<pnode::PNode*, syntax_asdl::Token*> tup2 = e_parser->Parse(lx, start_symbol);
    pnode = tup2.at0();
    last_token = tup2.at1();
    this->tr->ProcCallArgs(pnode, out);
    out->right = last_token;
  }
}

Tuple2<syntax_asdl::expr_t*, syntax_asdl::Token*> ParseContext::ParseYshExpr(lexer::Lexer* lx, int start_symbol) {
  expr_parse::ExprParser* e_parser = nullptr;
  pnode::PNode* pnode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  syntax_asdl::expr_t* ast_node = nullptr;
  StackRoot _root0(&lx);
  StackRoot _root1(&e_parser);
  StackRoot _root2(&pnode);
  StackRoot _root3(&last_token);
  StackRoot _root4(&ast_node);

  e_parser = this->_YshParser();
  {  // with
    ctx_PNodeAllocator ctx{e_parser};

    Tuple2<pnode::PNode*, syntax_asdl::Token*> tup3 = e_parser->Parse(lx, start_symbol);
    pnode = tup3.at0();
    last_token = tup3.at1();
    ast_node = this->tr->Expr(pnode);
  }
  return Tuple2<syntax_asdl::expr_t*, syntax_asdl::Token*>(ast_node, last_token);
}

Tuple3<syntax_asdl::pat_t*, syntax_asdl::Token*, syntax_asdl::Token*> ParseContext::ParseYshCasePattern(lexer::Lexer* lexer) {
  expr_parse::ExprParser* e_parser = nullptr;
  pnode::PNode* pnode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  syntax_asdl::Token* left_tok = nullptr;
  syntax_asdl::pat_t* pattern = nullptr;
  StackRoot _root0(&lexer);
  StackRoot _root1(&e_parser);
  StackRoot _root2(&pnode);
  StackRoot _root3(&last_token);
  StackRoot _root4(&left_tok);
  StackRoot _root5(&pattern);

  e_parser = this->_YshParser();
  {  // with
    ctx_PNodeAllocator ctx{e_parser};

    Tuple2<pnode::PNode*, syntax_asdl::Token*> tup4 = e_parser->Parse(lexer, grammar_nt::ysh_case_pat);
    pnode = tup4.at0();
    last_token = tup4.at1();
    left_tok = pnode->GetChild(0)->tok;
    pattern = this->tr->YshCasePattern(pnode);
  }
  return Tuple3<syntax_asdl::pat_t*, syntax_asdl::Token*, syntax_asdl::Token*>(pattern, left_tok, last_token);
}

syntax_asdl::Token* ParseContext::ParseProc(lexer::Lexer* lexer, syntax_asdl::Proc* out) {
  expr_parse::ExprParser* e_parser = nullptr;
  pnode::PNode* pnode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&lexer);
  StackRoot _root1(&out);
  StackRoot _root2(&e_parser);
  StackRoot _root3(&pnode);
  StackRoot _root4(&last_token);

  e_parser = this->_YshParser();
  {  // with
    ctx_PNodeAllocator ctx{e_parser};

    Tuple2<pnode::PNode*, syntax_asdl::Token*> tup5 = e_parser->Parse(lexer, grammar_nt::ysh_proc);
    pnode = tup5.at0();
    last_token = tup5.at1();
    out->sig = this->tr->Proc(pnode);
  }
  return last_token;
}

syntax_asdl::Token* ParseContext::ParseFunc(lexer::Lexer* lexer, syntax_asdl::Func* out) {
  expr_parse::ExprParser* e_parser = nullptr;
  pnode::PNode* pnode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&lexer);
  StackRoot _root1(&out);
  StackRoot _root2(&e_parser);
  StackRoot _root3(&pnode);
  StackRoot _root4(&last_token);

  e_parser = this->_YshParser();
  {  // with
    ctx_PNodeAllocator ctx{e_parser};

    Tuple2<pnode::PNode*, syntax_asdl::Token*> tup6 = e_parser->Parse(lexer, grammar_nt::ysh_func);
    pnode = tup6.at0();
    last_token = tup6.at1();
    this->tr->YshFunc(pnode, out);
  }
  return last_token;
}

}  // define namespace parse_lib

namespace reader {  // define

using id_kind_asdl::Id;
using error::p_die;
BigStr* _PS2 = S_olB;

_Reader::_Reader(alloc::Arena* arena) {
  this->arena = arena;
  this->line_num = 1;
}

void _Reader::SetLineOffset(int n) {
  this->line_num = n;
}

BigStr* _Reader::_GetLine() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

Tuple2<syntax_asdl::SourceLine*, int> _Reader::GetLine() {
  BigStr* line_str = nullptr;
  syntax_asdl::SourceLine* eof_line = nullptr;
  syntax_asdl::SourceLine* src_line = nullptr;
  StackRoot _root0(&line_str);
  StackRoot _root1(&eof_line);
  StackRoot _root2(&src_line);

  line_str = this->_GetLine();
  if (line_str == nullptr) {
    eof_line = nullptr;
    return Tuple2<syntax_asdl::SourceLine*, int>(eof_line, 0);
  }
  src_line = this->arena->AddLine(line_str, this->line_num);
  this->line_num += 1;
  return Tuple2<syntax_asdl::SourceLine*, int>(src_line, 0);
}

void _Reader::Reset() {
  ;  // pass
}

bool _Reader::LastLineHint() {
  return false;
}

DisallowedLineReader::DisallowedLineReader(alloc::Arena* arena, syntax_asdl::Token* blame_token) : ::reader::_Reader(arena) {
  this->blame_token = blame_token;
}

BigStr* DisallowedLineReader::_GetLine() {
  p_die(S_jwu, this->blame_token);
}

FileLineReader::FileLineReader(mylib::LineReader* f, alloc::Arena* arena) : ::reader::_Reader(arena) {
  this->f = f;
  this->last_line_hint = false;
}

BigStr* FileLineReader::_GetLine() {
  BigStr* line = nullptr;
  StackRoot _root0(&line);

  line = this->f->readline();
  if (len(line) == 0) {
    return nullptr;
  }
  if (!line->endswith(S_nfs)) {
    this->last_line_hint = true;
  }
  return line;
}

bool FileLineReader::LastLineHint() {
  return this->last_line_hint;
}

reader::FileLineReader* StringLineReader(BigStr* s, alloc::Arena* arena) {
  StackRoot _root0(&s);
  StackRoot _root1(&arena);

  return Alloc<FileLineReader>(Alloc<mylib::BufLineReader>(s), arena);
}

VirtualLineReader::VirtualLineReader(alloc::Arena* arena, List<Tuple2<syntax_asdl::SourceLine*, int>*>* lines, bool do_lossless) : ::reader::_Reader(arena) {
  this->lines = lines;
  this->do_lossless = do_lossless;
  this->num_lines = len(lines);
  this->pos = 0;
}

Tuple2<syntax_asdl::SourceLine*, int> VirtualLineReader::GetLine() {
  syntax_asdl::SourceLine* eof_line = nullptr;
  syntax_asdl::SourceLine* src_line = nullptr;
  int start_offset;
  StackRoot _root0(&eof_line);
  StackRoot _root1(&src_line);

  if (this->pos == this->num_lines) {
    eof_line = nullptr;
    return Tuple2<syntax_asdl::SourceLine*, int>(eof_line, 0);
  }
  Tuple2<syntax_asdl::SourceLine*, int>* tup0 = this->lines->at(this->pos);
  src_line = tup0->at0();
  start_offset = tup0->at1();
  this->pos += 1;
  if (this->do_lossless) {
    if (start_offset != 0) {
      this->arena->NewToken(Id::Lit_CharsWithoutPrefix, start_offset, 0, src_line);
    }
  }
  return Tuple2<syntax_asdl::SourceLine*, int>(src_line, start_offset);
}

BigStr* _PlainPromptInput(BigStr* prompt) {
  mylib::Writer* w = nullptr;
  BigStr* line = nullptr;
  StackRoot _root0(&prompt);
  StackRoot _root1(&w);
  StackRoot _root2(&line);

  w = mylib::Stderr();
  w->write(prompt);
  w->flush();
  line = mylib::Stdin()->readline();
  if (len(line) == 0) {
    throw Alloc<EOFError>();
  }
  return line;
}

InteractiveLineReader::InteractiveLineReader(alloc::Arena* arena, prompt::Evaluator* prompt_ev, history::Evaluator* hist_ev, py_readline::Readline* line_input, comp_ui::PromptState* prompt_state) : ::reader::_Reader(arena) {
  this->prompt_ev = prompt_ev;
  this->hist_ev = hist_ev;
  this->line_input = line_input;
  this->prompt_state = prompt_state;
  this->prev_line = nullptr;
  this->prompt_str = S_Aoo;
  this->Reset();
}

void InteractiveLineReader::Reset() {
  this->render_ps1 = true;
}

BigStr* InteractiveLineReader::_ReadlinePromptInput() {
  BigStr* line = nullptr;
  StackRoot _root0(&line);

  // if MYCPP
  {
    line = this->line_input->prompt_input(this->prompt_str);
  }
  // endif MYCPP
  return line;
}

BigStr* InteractiveLineReader::_GetLine() {
  BigStr* line = nullptr;
  StackRoot _root0(&line);

  if (this->render_ps1) {
    this->prompt_str = this->prompt_ev->EvalFirstPrompt();
    this->prompt_state->SetLastPrompt(this->prompt_str);
  }
  line = nullptr;
  try {
    if ((!this->line_input or (!mylib::Stdout()->isatty() or !mylib::Stdin()->isatty()))) {
      line = _PlainPromptInput(this->prompt_str);
    }
    else {
      line = this->_ReadlinePromptInput();
    }
  }
  catch (EOFError*) {
    print(S_gch);
  }
  if (line != nullptr) {
    line = this->hist_ev->Eval(line);
    if ((len(line->strip()) and (!(maybe_str_equals(line, this->prev_line)) and this->line_input != nullptr))) {
      this->line_input->add_history(line->rstrip());
      this->prev_line = line;
    }
  }
  this->prompt_str = _PS2;
  this->prompt_state->SetLastPrompt(this->prompt_str);
  this->render_ps1 = false;
  return line;
}

}  // define namespace reader

namespace syntax_abbrev {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_str;
using hnode_asdl::hnode;
using hnode_asdl::hnode_t;
using hnode_asdl::color_e;

void _AbbreviateToken(syntax_asdl::Token* tok, List<hnode_asdl::hnode_t*>* out) {
  BigStr* tok_str = nullptr;
  hnode::Leaf* n1 = nullptr;
  hnode::Leaf* n2 = nullptr;
  StackRoot _root0(&tok);
  StackRoot _root1(&out);
  StackRoot _root2(&tok_str);
  StackRoot _root3(&n1);
  StackRoot _root4(&n2);

  tok_str = tok->line->content->slice(tok->col, (tok->col + tok->length));
  n1 = runtime::NewLeaf(Id_str(tok->id, false), color_e::OtherConst);
  out->append(n1);
  n2 = runtime::NewLeaf(tok_str, color_e::StringConst);
  out->append(n2);
}

hnode_asdl::hnode_t* _Token(syntax_asdl::Token* obj) {
  hnode::Record* p_node = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);

  p_node = runtime::NewRecord(S_Aoo);
  p_node->left = S_eox;
  p_node->right = S_jye;
  p_node->unnamed_fields = Alloc<List<hnode_asdl::hnode_t*>>();
  _AbbreviateToken(obj, p_node->unnamed_fields);
  return p_node;
}

hnode_asdl::hnode_t* _CompoundWord(syntax_asdl::CompoundWord* obj) {
  hnode::Record* p_node = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);

  p_node = runtime::NewRecord(S_pfC);
  p_node->left = S_ijB;
  p_node->right = S_hxb;
  p_node->unnamed_fields = Alloc<List<hnode_asdl::hnode_t*>>();
  for (ListIter<syntax_asdl::word_part_t*> it(obj->parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    p_node->unnamed_fields->append(part->PrettyTree(true));
  }
  return p_node;
}

hnode_asdl::hnode_t* _DoubleQuoted(syntax_asdl::DoubleQuoted* obj) {
  hnode::Record* p_node = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);

  if (obj->left->id != Id::Left_DoubleQuote) {
    return nullptr;
  }
  p_node = runtime::NewRecord(S_lAz);
  p_node->unnamed_fields = Alloc<List<hnode_asdl::hnode_t*>>();
  for (ListIter<syntax_asdl::word_part_t*> it(obj->parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    p_node->unnamed_fields->append(part->PrettyTree(true));
  }
  return p_node;
}

hnode_asdl::hnode_t* _SingleQuoted(syntax_asdl::SingleQuoted* obj) {
  hnode::Record* p_node = nullptr;
  hnode::Leaf* n2 = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);
  StackRoot _root2(&n2);

  if (obj->left->id != Id::Left_SingleQuote) {
    return nullptr;
  }
  p_node = runtime::NewRecord(S_mip);
  p_node->unnamed_fields = Alloc<List<hnode_asdl::hnode_t*>>();
  n2 = runtime::NewLeaf(obj->sval, color_e::StringConst);
  p_node->unnamed_fields->append(n2);
  return p_node;
}

hnode_asdl::hnode_t* _SimpleVarSub(syntax_asdl::SimpleVarSub* obj) {
  hnode::Record* p_node = nullptr;
  syntax_asdl::Token* tok = nullptr;
  BigStr* var_name = nullptr;
  hnode::Leaf* n1 = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);
  StackRoot _root2(&tok);
  StackRoot _root3(&var_name);
  StackRoot _root4(&n1);

  p_node = runtime::NewRecord(S_Czx);
  p_node->unnamed_fields = Alloc<List<hnode_asdl::hnode_t*>>();
  if ((obj->tok->id == Id::VSub_DollarName || obj->tok->id == Id::VSub_Number)) {
    tok = obj->tok;
    var_name = tok->line->content->slice((tok->col + 1), (tok->col + tok->length));
    n1 = runtime::NewLeaf(var_name, color_e::StringConst);
    p_node->unnamed_fields->append(n1);
  }
  else {
    n1 = runtime::NewLeaf(Id_str(obj->tok->id, false), color_e::OtherConst);
    p_node->unnamed_fields->append(n1);
  }
  return p_node;
}

hnode_asdl::hnode_t* _BracedVarSub(syntax_asdl::BracedVarSub* obj) {
  hnode::Record* p_node = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);

  p_node = runtime::NewRecord(S_hqF);
  if ((obj->prefix_op != nullptr or (obj->bracket_op != nullptr or obj->suffix_op != nullptr))) {
    return nullptr;
  }
  p_node->unnamed_fields = Alloc<List<hnode_asdl::hnode_t*>>();
  _AbbreviateToken(obj->name_tok, p_node->unnamed_fields);
  return p_node;
}

hnode_asdl::hnode_t* _command__Simple(command::Simple* obj) {
  hnode::Record* p_node = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);

  p_node = runtime::NewRecord(S_sjc);
  if ((len(obj->more_env) or (obj->typed_args != nullptr or (obj->block != nullptr or obj->is_last_cmd == true)))) {
    return nullptr;
  }
  p_node->unnamed_fields = Alloc<List<hnode_asdl::hnode_t*>>();
  for (ListIter<syntax_asdl::word_t*> it(obj->words); !it.Done(); it.Next()) {
    syntax_asdl::word_t* w = it.Value();
    StackRoot _for(&w  );
    p_node->unnamed_fields->append(w->PrettyTree(true));
  }
  return p_node;
}

hnode_asdl::hnode_t* _expr__Var(expr::Var* obj) {
  hnode::Record* p_node = nullptr;
  hnode::Leaf* n1 = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);
  StackRoot _root2(&n1);

  p_node = runtime::NewRecord(S_CsA);
  n1 = runtime::NewLeaf(obj->name, color_e::StringConst);
  p_node->unnamed_fields = NewList<hnode_asdl::hnode_t*>(std::initializer_list<hnode_asdl::hnode_t*>{n1});
  return p_node;
}

hnode_asdl::hnode_t* _expr__Const(expr::Const* obj) {
  hnode::Record* p_node = nullptr;
  syntax_asdl::Token* tok = nullptr;
  hnode::Leaf* n1 = nullptr;
  hnode::Leaf* n2 = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&p_node);
  StackRoot _root2(&tok);
  StackRoot _root3(&n1);
  StackRoot _root4(&n2);

  p_node = runtime::NewRecord(S_wcu);
  tok = obj->c;
  n1 = runtime::NewLeaf(Id_str(tok->id, false), color_e::OtherConst);
  n2 = runtime::NewLeaf(tok->tval, color_e::StringConst);
  p_node->unnamed_fields = NewList<hnode_asdl::hnode_t*>(std::initializer_list<hnode_asdl::hnode_t*>{n1, n2});
  return p_node;
}

}  // define namespace syntax_abbrev

namespace typed_args {  // define

using runtime_asdl::cmd_value;
using runtime_asdl::ProcArgs;
using runtime_asdl::Cell;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::ArgList;
using syntax_asdl::command_t;
using syntax_asdl::Token;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::RegexMatch;
using value_asdl::Obj;
using value_asdl::cmd_frag;
using value_asdl::cmd_frag_e;
using value_asdl::cmd_frag_str;
using value_asdl::LiteralBlock;
using error::e_usage;

void DoesNotAccept(runtime_asdl::ProcArgs* proc_args) {
  StackRoot _root0(&proc_args);

  if (proc_args != nullptr) {
    e_usage(S_rDq, proc_args->typed_args->left);
  }
}

syntax_asdl::command_t* OptionalBlockAsFrag(cmd_value::Argv* cmd_val) {
  typed_args::Reader* r = nullptr;
  syntax_asdl::command_t* cmd = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&r);
  StackRoot _root2(&cmd);

  r = ReaderForProc(cmd_val);
  cmd = r->OptionalBlockAsFrag();
  r->Done();
  return cmd;
}

syntax_asdl::command_t* RequiredBlockAsFrag(cmd_value::Argv* cmd_val) {
  typed_args::Reader* r = nullptr;
  syntax_asdl::command_t* cmd = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&r);
  StackRoot _root2(&cmd);

  r = ReaderForProc(cmd_val);
  cmd = r->RequiredBlockAsFrag();
  r->Done();
  return cmd;
}

value_asdl::LiteralBlock* OptionalLiteralBlock(cmd_value::Argv* cmd_val) {
  value_asdl::LiteralBlock* block = nullptr;
  typed_args::Reader* r = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&block);
  StackRoot _root2(&r);

  block = nullptr;
  if (cmd_val->proc_args) {
    r = ReaderForProc(cmd_val);
    block = r->OptionalLiteralBlock();
    r->Done();
  }
  return block;
}

syntax_asdl::command_t* GetCommandFrag(value::Command* bound) {
  value_asdl::cmd_frag_t* frag = nullptr;
  value_asdl::LiteralBlock* lit = nullptr;
  cmd_frag::Expr* expr = nullptr;
  StackRoot _root0(&bound);
  StackRoot _root1(&frag);
  StackRoot _root2(&lit);
  StackRoot _root3(&expr);

  frag = bound->frag;
  switch (frag->tag()) {
    case cmd_frag_e::LiteralBlock: {
      lit = static_cast<LiteralBlock*>(frag);
      return lit->brace_group;
    }
      break;
    case cmd_frag_e::Expr: {
      expr = static_cast<cmd_frag::Expr*>(frag);
      return expr->c;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

typed_args::Reader* ReaderForProc(cmd_value::Argv* cmd_val) {
  runtime_asdl::ProcArgs* proc_args = nullptr;
  List<value_asdl::value_t*>* pos_args = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_args = nullptr;
  syntax_asdl::ArgList* arg_list = nullptr;
  value_asdl::value_t* block_arg = nullptr;
  typed_args::Reader* rd = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&proc_args);
  StackRoot _root2(&pos_args);
  StackRoot _root3(&named_args);
  StackRoot _root4(&arg_list);
  StackRoot _root5(&block_arg);
  StackRoot _root6(&rd);

  proc_args = cmd_val->proc_args;
  if (proc_args) {
    pos_args = proc_args->pos_args != nullptr ? proc_args->pos_args : Alloc<List<value_asdl::value_t*>>();
    named_args = proc_args->named_args != nullptr ? proc_args->named_args : Alloc<Dict<BigStr*, value_asdl::value_t*>>();
    arg_list = proc_args->typed_args != nullptr ? proc_args->typed_args : ArgList::CreateNull();
    block_arg = proc_args->block_arg;
  }
  else {
    pos_args = Alloc<List<value_asdl::value_t*>>();
    named_args = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
    arg_list = ArgList::CreateNull();
    block_arg = nullptr;
  }
  rd = Alloc<Reader>(pos_args, named_args, block_arg, arg_list);
  rd->SetFallbackLocation(cmd_val->arg_locs->at(0));
  return rd;
}

Reader::Reader(List<value_asdl::value_t*>* pos_args, Dict<BigStr*, value_asdl::value_t*>* named_args, value_asdl::value_t* block_arg, syntax_asdl::ArgList* arg_list, bool is_bound) {
  this->pos_args = pos_args;
  this->pos_consumed = 0;
  this->is_bound = is_bound;
  this->named_args = named_args;
  this->block_arg = block_arg;
  this->arg_list = arg_list;
  this->fallback_loc = loc::Missing;
}

void Reader::SetFallbackLocation(syntax_asdl::loc_t* blame_loc) {
  StackRoot _root0(&blame_loc);

  this->fallback_loc = blame_loc;
}

syntax_asdl::Token* Reader::LeftParenToken() {
  return this->arg_list->left;
}

syntax_asdl::loc_t* Reader::LeastSpecificLocation() {
  if (this->arg_list->left) {
    return this->arg_list->left;
  }
  return this->fallback_loc;
}

syntax_asdl::loc_t* Reader::BlamePos() {
  int pos;
  syntax_asdl::loc_t* l = nullptr;
  StackRoot _root0(&l);

  pos = (this->pos_consumed - 1);
  if (this->is_bound) {
    pos -= 1;
  }
  if (this->arg_list->pos_args == nullptr) {
    return this->LeastSpecificLocation();
  }
  if ((0 <= pos and pos < len(this->arg_list->pos_args))) {
    l = location::TokenForExpr(this->arg_list->pos_args->at(pos));
    if (l != nullptr) {
      return l;
    }
  }
  return this->LeastSpecificLocation();
}

value_asdl::value_t* Reader::PosValue() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  if (len(this->pos_args) == 0) {
    throw Alloc<error::TypeErrVerbose>(StrFormat("Expected at least %d typed args, but only got %d", (this->pos_consumed + 1), this->pos_consumed), this->LeastSpecificLocation());
  }
  this->pos_consumed += 1;
  val = this->pos_args->pop(0);
  return val;
}

value_asdl::value_t* Reader::OptionalValue() {
  if (len(this->pos_args) == 0) {
    return nullptr;
  }
  this->pos_consumed += 1;
  return this->pos_args->pop(0);
}

BigStr* Reader::_ToStr(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Str) {
    return static_cast<value::Str*>(val)->s;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Str", this->pos_consumed), this->BlamePos());
}

bool Reader::_ToBool(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Bool) {
    return static_cast<value::Bool*>(val)->b;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Bool", this->pos_consumed), this->BlamePos());
}

mops::BigInt Reader::_ToInt(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Int) {
    return static_cast<value::Int*>(val)->i;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be an Int", this->pos_consumed), this->BlamePos());
}

double Reader::_ToFloat(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Float) {
    return static_cast<value::Float*>(val)->f;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Float", this->pos_consumed), this->BlamePos());
}

List<BigStr*>* Reader::_ToBashArray(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::BashArray) {
    return static_cast<value::BashArray*>(val)->strs;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a BashArray", this->pos_consumed), this->BlamePos());
}

value::SparseArray* Reader::_ToSparseArray(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::SparseArray) {
    return static_cast<value::SparseArray*>(val);
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a SparseArray", this->pos_consumed), this->BlamePos());
}

List<value_asdl::value_t*>* Reader::_ToList(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::List) {
    return static_cast<value::List*>(val)->items;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a List", this->pos_consumed), this->BlamePos());
}

Dict<BigStr*, value_asdl::value_t*>* Reader::_ToDict(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Dict) {
    return static_cast<value::Dict*>(val)->d;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Dict", this->pos_consumed), this->BlamePos());
}

value_asdl::Obj* Reader::_ToObj(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Obj) {
    return static_cast<Obj*>(val);
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Obj", this->pos_consumed), this->BlamePos());
}

value::Place* Reader::_ToPlace(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Place) {
    return static_cast<value::Place*>(val);
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Place", this->pos_consumed), this->BlamePos());
}

value_asdl::RegexMatch* Reader::_ToMatch(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Match) {
    return static_cast<RegexMatch*>(val);
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Match", this->pos_consumed), this->BlamePos());
}

value::Eggex* Reader::_ToEggex(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Eggex) {
    return static_cast<value::Eggex*>(val);
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be an Eggex", this->pos_consumed), this->BlamePos());
}

value::Expr* Reader::_ToExpr(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Expr) {
    return static_cast<value::Expr*>(val);
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Expr", this->pos_consumed), this->BlamePos());
}

Dict<BigStr*, runtime_asdl::Cell*>* Reader::_ToFrame(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Frame) {
    return static_cast<value::Frame*>(val)->frame;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Frame", this->pos_consumed), this->BlamePos());
}

syntax_asdl::command_t* Reader::_ToCommandFrag(value_asdl::value_t* val) {
  value::Command* bound = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&bound);

  if (val->tag() == value_e::CommandFrag) {
    return static_cast<value::CommandFrag*>(val)->c;
  }
  if (val->tag() == value_e::Command) {
    bound = static_cast<value::Command*>(val);
    return GetCommandFrag(bound);
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a CommandFrag", this->pos_consumed), this->BlamePos());
}

value::Command* Reader::_ToCommand(value_asdl::value_t* val) {
  StackRoot _root0(&val);

  if (val->tag() == value_e::Command) {
    return static_cast<value::Command*>(val);
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a Command", this->pos_consumed), this->BlamePos());
}

value_asdl::LiteralBlock* Reader::_ToLiteralBlock(value_asdl::value_t* val) {
  value_asdl::cmd_frag_t* frag = nullptr;
  value_asdl::LiteralBlock* lit = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&frag);
  StackRoot _root2(&lit);

  if (val->tag() == value_e::Command) {
    frag = static_cast<value::Command*>(val)->frag;
    switch (frag->tag()) {
      case cmd_frag_e::LiteralBlock: {
        lit = static_cast<LiteralBlock*>(frag);
        return lit;
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Arg %d should be a LiteralBlock", this->pos_consumed), this->BlamePos());
}

BigStr* Reader::PosStr() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToStr(val);
}

BigStr* Reader::OptionalStr(BigStr* default_) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&default_);
  StackRoot _root1(&val);

  val = this->OptionalValue();
  if (val == nullptr) {
    return default_;
  }
  return this->_ToStr(val);
}

bool Reader::PosBool() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToBool(val);
}

mops::BigInt Reader::PosInt() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToInt(val);
}

mops::BigInt Reader::OptionalInt(int default_) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->OptionalValue();
  if (val == nullptr) {
    return mops::IntWiden(default_);
  }
  return this->_ToInt(val);
}

double Reader::PosFloat() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToFloat(val);
}

List<BigStr*>* Reader::PosBashArray() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToBashArray(val);
}

value::SparseArray* Reader::PosSparseArray() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToSparseArray(val);
}

List<value_asdl::value_t*>* Reader::PosList() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToList(val);
}

Dict<BigStr*, value_asdl::value_t*>* Reader::PosDict() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToDict(val);
}

value_asdl::Obj* Reader::PosObj() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToObj(val);
}

value::Place* Reader::PosPlace() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToPlace(val);
}

value::Eggex* Reader::PosEggex() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToEggex(val);
}

value_asdl::RegexMatch* Reader::PosMatch() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToMatch(val);
}

Dict<BigStr*, runtime_asdl::Cell*>* Reader::PosFrame() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToFrame(val);
}

syntax_asdl::command_t* Reader::PosCommandFrag() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToCommandFrag(val);
}

value::Command* Reader::PosCommand() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToCommand(val);
}

value::Expr* Reader::PosExpr() {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&val);

  val = this->PosValue();
  return this->_ToExpr(val);
}

syntax_asdl::command_t* Reader::RequiredBlockAsFrag() {
  if (this->block_arg == nullptr) {
    throw Alloc<error::Usage>(S_dae, this->LeastSpecificLocation());
  }
  return this->_ToCommandFrag(this->block_arg);
}

syntax_asdl::command_t* Reader::OptionalBlockAsFrag() {
  if (this->block_arg == nullptr) {
    return nullptr;
  }
  return this->_ToCommandFrag(this->block_arg);
}

value_asdl::LiteralBlock* Reader::OptionalLiteralBlock() {
  if (this->block_arg == nullptr) {
    return nullptr;
  }
  return this->_ToLiteralBlock(this->block_arg);
}

List<value_asdl::value_t*>* Reader::RestPos() {
  List<value_asdl::value_t*>* ret = nullptr;
  StackRoot _root0(&ret);

  ret = this->pos_args;
  this->pos_args = Alloc<List<value_asdl::value_t*>>();
  return ret;
}

syntax_asdl::loc_t* Reader::_BlameNamed(BigStr* name) {
  StackRoot _root0(&name);

  return this->LeastSpecificLocation();
}

BigStr* Reader::NamedStr(BigStr* param_name, BigStr* default_) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&param_name);
  StackRoot _root1(&default_);
  StackRoot _root2(&val);
  StackRoot _root3(&UP_val);

  if (!dict_contains(this->named_args, param_name)) {
    return default_;
  }
  val = this->named_args->at(param_name);
  UP_val = val;
  if (val->tag() == value_e::Str) {
    mylib::dict_erase(this->named_args, param_name);
    value::Str* val = static_cast<value::Str*>(UP_val);
    return val->s;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Named arg %r should be a Str", param_name), this->_BlameNamed(param_name));
}

bool Reader::NamedBool(BigStr* param_name, bool default_) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&param_name);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_val);

  if (!dict_contains(this->named_args, param_name)) {
    return default_;
  }
  val = this->named_args->at(param_name);
  UP_val = val;
  if (val->tag() == value_e::Bool) {
    value::Bool* val = static_cast<value::Bool*>(UP_val);
    mylib::dict_erase(this->named_args, param_name);
    return val->b;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Named arg %r should be a Bool", param_name), this->_BlameNamed(param_name));
}

mops::BigInt Reader::NamedInt(BigStr* param_name, int default_) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&param_name);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_val);

  if (!dict_contains(this->named_args, param_name)) {
    return mops::IntWiden(default_);
  }
  val = this->named_args->at(param_name);
  UP_val = val;
  if (val->tag() == value_e::Int) {
    value::Int* val = static_cast<value::Int*>(UP_val);
    mylib::dict_erase(this->named_args, param_name);
    return val->i;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Named arg %r should be a Int", param_name), this->_BlameNamed(param_name));
}

double Reader::NamedFloat(BigStr* param_name, double default_) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&param_name);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_val);

  if (!dict_contains(this->named_args, param_name)) {
    return default_;
  }
  val = this->named_args->at(param_name);
  UP_val = val;
  if (val->tag() == value_e::Float) {
    value::Float* val = static_cast<value::Float*>(UP_val);
    mylib::dict_erase(this->named_args, param_name);
    return val->f;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Named arg %r should be a Float", param_name), this->_BlameNamed(param_name));
}

List<value_asdl::value_t*>* Reader::NamedList(BigStr* param_name, List<value_asdl::value_t*>* default_) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&param_name);
  StackRoot _root1(&default_);
  StackRoot _root2(&val);
  StackRoot _root3(&UP_val);

  if (!dict_contains(this->named_args, param_name)) {
    return default_;
  }
  val = this->named_args->at(param_name);
  UP_val = val;
  if (val->tag() == value_e::List) {
    value::List* val = static_cast<value::List*>(UP_val);
    mylib::dict_erase(this->named_args, param_name);
    return val->items;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Named arg %r should be a List", param_name), this->_BlameNamed(param_name));
}

Dict<BigStr*, value_asdl::value_t*>* Reader::NamedDict(BigStr* param_name, Dict<BigStr*, value_asdl::value_t*>* default_) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&param_name);
  StackRoot _root1(&default_);
  StackRoot _root2(&val);
  StackRoot _root3(&UP_val);

  if (!dict_contains(this->named_args, param_name)) {
    return default_;
  }
  val = this->named_args->at(param_name);
  UP_val = val;
  if (val->tag() == value_e::Dict) {
    value::Dict* val = static_cast<value::Dict*>(UP_val);
    mylib::dict_erase(this->named_args, param_name);
    return val->d;
  }
  throw Alloc<error::TypeErr>(val, StrFormat("Named arg %r should be a Dict", param_name), this->_BlameNamed(param_name));
}

Dict<BigStr*, value_asdl::value_t*>* Reader::RestNamed() {
  Dict<BigStr*, value_asdl::value_t*>* ret = nullptr;
  StackRoot _root0(&ret);

  ret = this->named_args;
  this->named_args = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  return ret;
}

void Reader::Done() {
  int n;
  BigStr* bad_args = nullptr;
  syntax_asdl::loc_t* blame = nullptr;
  StackRoot _root0(&bad_args);
  StackRoot _root1(&blame);

  if (len(this->pos_args)) {
    n = this->pos_consumed;
    if (this->is_bound) {
      n -= 1;
    }
    this->pos_consumed += 1;
    throw Alloc<error::TypeErrVerbose>(StrFormat("Expected %d typed args, but got %d", n, (n + len(this->pos_args))), this->BlamePos());
  }
  if (len(this->named_args)) {
    bad_args = S_tgp->join(this->named_args->keys());
    blame = this->arg_list->semi_tok;
    if (blame == nullptr) {
      blame = this->LeastSpecificLocation();
    }
    throw Alloc<error::TypeErrVerbose>(StrFormat("Got unexpected named args: %s", bad_args), blame);
  }
}

}  // define namespace typed_args

namespace arith_parse {  // define

using id_kind_asdl::Id;
using syntax_asdl::loc;
using syntax_asdl::arith_expr;
using syntax_asdl::arith_expr_t;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::Token;
using error::p_die;

syntax_asdl::arith_expr_t* NullIncDec(tdop::TdopParser* p, syntax_asdl::word_t* w, int bp) {
  syntax_asdl::arith_expr_t* right = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&w);
  StackRoot _root2(&right);

  right = p->ParseUntil(bp);
  tdop::CheckLhsExpr(right, w);
  return Alloc<arith_expr::UnaryAssign>(word_::ArithId(w), right);
}

syntax_asdl::arith_expr_t* NullUnaryPlus(tdop::TdopParser* p, syntax_asdl::word_t* t, int bp) {
  syntax_asdl::arith_expr_t* right = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&t);
  StackRoot _root2(&right);

  right = p->ParseUntil(bp);
  return Alloc<arith_expr::Unary>(Id::Node_UnaryPlus, right);
}

syntax_asdl::arith_expr_t* NullUnaryMinus(tdop::TdopParser* p, syntax_asdl::word_t* t, int bp) {
  syntax_asdl::arith_expr_t* right = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&t);
  StackRoot _root2(&right);

  right = p->ParseUntil(bp);
  return Alloc<arith_expr::Unary>(Id::Node_UnaryMinus, right);
}

syntax_asdl::arith_expr_t* LeftIncDec(tdop::TdopParser* p, syntax_asdl::word_t* w, syntax_asdl::arith_expr_t* left, int rbp) {
  int arith_id;
  int op_id;
  StackRoot _root0(&p);
  StackRoot _root1(&w);
  StackRoot _root2(&left);

  arith_id = word_::ArithId(w);
  if (arith_id == Id::Arith_DPlus) {
    op_id = Id::Node_PostDPlus;
  }
  else {
    if (arith_id == Id::Arith_DMinus) {
      op_id = Id::Node_PostDMinus;
    }
    else {
      assert(0);  // AssertionError
    }
  }
  tdop::CheckLhsExpr(left, w);
  return Alloc<arith_expr::UnaryAssign>(op_id, left);
}

syntax_asdl::arith_expr_t* LeftIndex(tdop::TdopParser* p, syntax_asdl::word_t* w, syntax_asdl::arith_expr_t* left, int unused_bp) {
  syntax_asdl::arith_expr_t* index = nullptr;
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&w);
  StackRoot _root2(&left);
  StackRoot _root3(&index);
  StackRoot _root4(&tok);

  if (!tdop::IsIndexable(left)) {
    p_die(S_fAu, Alloc<loc::Word>(w));
  }
  index = p->ParseUntil(0);
  p->Eat(Id::Arith_RBracket);
  tok = static_cast<Token*>(w);
  return Alloc<arith_expr::Binary>(tok, left, index);
}

syntax_asdl::arith_expr_t* LeftTernary(tdop::TdopParser* p, syntax_asdl::word_t* t, syntax_asdl::arith_expr_t* left, int bp) {
  syntax_asdl::arith_expr_t* true_expr = nullptr;
  syntax_asdl::arith_expr_t* false_expr = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&t);
  StackRoot _root2(&left);
  StackRoot _root3(&true_expr);
  StackRoot _root4(&false_expr);

  true_expr = p->ParseUntil(0);
  p->Eat(Id::Arith_Colon);
  false_expr = p->ParseUntil(bp);
  return Alloc<arith_expr::TernaryOp>(left, true_expr, false_expr);
}

}  // define namespace arith_parse

namespace bool_parse {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Kind;
using types_asdl::lex_mode_t;
using types_asdl::lex_mode_e;
using syntax_asdl::loc;
using syntax_asdl::word_t;
using syntax_asdl::word_e;
using syntax_asdl::bool_expr;
using syntax_asdl::bool_expr_t;
using syntax_asdl::Token;
using error::p_die;

BoolParser::BoolParser(word_parse::WordEmitter* w_parser) {
  this->w_parser = w_parser;
  this->words = Alloc<List<syntax_asdl::word_t*>>();
  this->cur_word = nullptr;
  this->bool_id = Id::Undefined_Tok;
  this->bool_kind = Kind::Undefined;
}

void BoolParser::_NextOne(types_asdl::lex_mode_t lex_mode) {
  int n;
  syntax_asdl::word_t* w = nullptr;
  StackRoot _root0(&w);

  n = len(this->words);
  if (n == 2) {
    this->words->set(0, this->words->at(1));
    this->cur_word = this->words->at(0);
    this->words->pop();
  }
  else {
    if ((n == 0 || n == 1)) {
      w = this->w_parser->ReadWord(lex_mode);
      if (n == 0) {
        this->words->append(w);
      }
      else {
        this->words->set(0, w);
      }
      this->cur_word = w;
    }
  }
  this->bool_id = word_::BoolId(this->cur_word);
  this->bool_kind = consts::GetKind(this->bool_id);
}

void BoolParser::_Next(types_asdl::lex_mode_t lex_mode) {
  while (true) {
    this->_NextOne(lex_mode);
    if (this->bool_id != Id::Op_Newline) {
      break;
    }
  }
}

syntax_asdl::word_t* BoolParser::_LookAhead() {
  int n;
  syntax_asdl::word_t* w = nullptr;
  StackRoot _root0(&w);

  n = len(this->words);
  if (n != 1) {
    assert(0);  // AssertionError
  }
  w = this->w_parser->ReadWord(lex_mode_e::DBracket);
  this->words->append(w);
  return w;
}

Tuple2<syntax_asdl::bool_expr_t*, syntax_asdl::Token*> BoolParser::Parse() {
  syntax_asdl::bool_expr_t* node = nullptr;
  syntax_asdl::Token* right = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&right);

  this->_Next();
  node = this->ParseExpr();
  if (this->bool_id != Id::Lit_DRightBracket) {
    p_die(S_qul, Alloc<loc::Word>(this->cur_word));
  }
  right = word_::LiteralToken(this->cur_word);
  return Tuple2<syntax_asdl::bool_expr_t*, syntax_asdl::Token*>(node, right);
}

bool BoolParser::_TestAtEnd() {
  return this->bool_id == Id::Lit_DRightBracket;
}

syntax_asdl::bool_expr_t* BoolParser::ParseForBuiltin() {
  syntax_asdl::bool_expr_t* node = nullptr;
  StackRoot _root0(&node);

  this->_Next();
  node = this->ParseExpr();
  if (this->bool_id != Id::Eof_Real) {
    p_die(StrFormat("Unexpected trailing word %s", word_::Pretty(this->cur_word)), Alloc<loc::Word>(this->cur_word));
  }
  return node;
}

syntax_asdl::bool_expr_t* BoolParser::ParseExpr() {
  syntax_asdl::bool_expr_t* left = nullptr;
  syntax_asdl::bool_expr_t* right = nullptr;
  StackRoot _root0(&left);
  StackRoot _root1(&right);

  left = this->ParseTerm();
  if ((this->bool_id == Id::Op_DPipe || this->bool_id == Id::BoolUnary_o)) {
    this->_Next();
    right = this->ParseExpr();
    return Alloc<bool_expr::LogicalOr>(left, right);
  }
  else {
    return left;
  }
}

syntax_asdl::bool_expr_t* BoolParser::ParseTerm() {
  syntax_asdl::bool_expr_t* left = nullptr;
  syntax_asdl::bool_expr_t* right = nullptr;
  StackRoot _root0(&left);
  StackRoot _root1(&right);

  left = this->ParseNegatedFactor();
  if ((this->bool_id == Id::Op_DAmp || this->bool_id == Id::BoolUnary_a)) {
    this->_Next();
    right = this->ParseTerm();
    return Alloc<bool_expr::LogicalAnd>(left, right);
  }
  else {
    return left;
  }
}

syntax_asdl::bool_expr_t* BoolParser::ParseNegatedFactor() {
  syntax_asdl::bool_expr_t* child = nullptr;
  StackRoot _root0(&child);

  if (this->bool_id == Id::KW_Bang) {
    this->_Next();
    child = this->ParseFactor();
    return Alloc<bool_expr::LogicalNot>(child);
  }
  else {
    return this->ParseFactor();
  }
}

syntax_asdl::bool_expr_t* BoolParser::ParseFactor() {
  int op;
  syntax_asdl::word_t* w = nullptr;
  int tag;
  syntax_asdl::CompoundWord* tilde = nullptr;
  syntax_asdl::bool_expr_t* node = nullptr;
  syntax_asdl::word_t* t2 = nullptr;
  int t2_bool_id;
  id_kind_asdl::Kind_t t2_bool_kind;
  syntax_asdl::word_t* left = nullptr;
  syntax_asdl::word_t* right = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&tilde);
  StackRoot _root2(&node);
  StackRoot _root3(&t2);
  StackRoot _root4(&left);
  StackRoot _root5(&right);

  if (this->bool_kind == Kind::BoolUnary) {
    op = this->bool_id;
    this->_Next();
    w = this->cur_word;
    tag = w->tag();
    if ((tag != word_e::Compound and tag != word_e::String)) {
      p_die(S_scC, Alloc<loc::Word>(w));
    }
    this->_Next();
    tilde = word_::TildeDetect(w);
    if (tilde) {
      w = tilde;
    }
    node = Alloc<bool_expr::Unary>(op, w);
    return node;
  }
  if (this->bool_kind == Kind::Word) {
    t2 = this->_LookAhead();
    t2_bool_id = word_::BoolId(t2);
    t2_bool_kind = consts::GetKind(t2_bool_id);
    if ((t2_bool_kind == Kind::BoolBinary or (t2_bool_id == Id::Op_Less || t2_bool_id == Id::Op_Great))) {
      left = this->cur_word;
      this->_Next();
      op = this->bool_id;
      if (t2_bool_id == Id::BoolBinary_EqualTilde) {
        this->_Next(lex_mode_e::BashRegex);
      }
      else {
        this->_Next();
      }
      right = this->cur_word;
      this->_Next();
      tilde = word_::TildeDetect(left);
      if (tilde) {
        left = tilde;
      }
      tilde = word_::TildeDetect(right);
      if (tilde) {
        right = tilde;
      }
      return Alloc<bool_expr::Binary>(op, left, right);
    }
    else {
      w = this->cur_word;
      tilde = word_::TildeDetect(w);
      if (tilde) {
        w = tilde;
      }
      this->_Next();
      return Alloc<bool_expr::WordTest>(w);
    }
  }
  if (this->bool_id == Id::Op_LParen) {
    this->_Next();
    node = this->ParseExpr();
    if (this->bool_id != Id::Op_RParen) {
      p_die(StrFormat("Expected ), got %s", word_::Pretty(this->cur_word)), Alloc<loc::Word>(this->cur_word));
    }
    this->_Next();
    return node;
  }
  p_die(StrFormat("Unexpected token in boolean expression (%s)", ui::PrettyId(this->bool_id)), Alloc<loc::Word>(this->cur_word));
}

}  // define namespace bool_parse

namespace braces {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using syntax_asdl::Token;
using syntax_asdl::CompoundWord;
using syntax_asdl::word;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::word_part;
using syntax_asdl::word_part_e;
using syntax_asdl::word_part_t;
using error::p_die;
int NO_STEP = 0;

_NotARange::_NotARange(BigStr* s) {
  ;  // pass
}

_RangeParser::_RangeParser(match::SimpleLexer* lexer, syntax_asdl::Token* blame_tok) {
  this->lexer = lexer;
  this->blame_tok = blame_tok;
  this->token_type = Id::Undefined_Tok;
  this->token_val = S_Aoo;
}

void _RangeParser::_Next() {
  Tuple2<int, BigStr*> tup0 = this->lexer->Next();
  this->token_type = tup0.at0();
  this->token_val = tup0.at1();
}

BigStr* _RangeParser::_Eat(int token_type) {
  BigStr* val = nullptr;
  StackRoot _root0(&val);

  if (this->token_type != token_type) {
    throw Alloc<_NotARange>(StrFormat("Expected %d, got %d", token_type, this->token_type));
  }
  val = this->token_val;
  this->_Next();
  return val;
}

int _RangeParser::_ParseStep() {
  int step;
  this->_Next();
  step = to_int(this->_Eat(Id::Range_Int));
  if (step == 0) {
    p_die(S_Cwz, this->blame_tok);
  }
  return step;
}

word_part::BracedRange* _RangeParser::_ParseRange(int range_kind) {
  BigStr* start = nullptr;
  BigStr* end = nullptr;
  int step;
  word_part::BracedRange* part = nullptr;
  StackRoot _root0(&start);
  StackRoot _root1(&end);
  StackRoot _root2(&part);

  start = this->token_val;
  this->_Next();
  this->_Eat(Id::Range_Dots);
  end = this->_Eat(range_kind);
  if (this->token_type == Id::Range_Dots) {
    step = this->_ParseStep();
  }
  else {
    step = NO_STEP;
  }
  part = Alloc<word_part::BracedRange>(this->blame_tok, range_kind, start, end, step);
  return part;
}

word_part::BracedRange* _RangeParser::Parse() {
  word_part::BracedRange* part = nullptr;
  int start;
  int end;
  int start_num;
  int end_num;
  bool upper1;
  bool upper2;
  StackRoot _root0(&part);

  this->_Next();
  if (this->token_type == Id::Range_Int) {
    part = this->_ParseRange(this->token_type);
    start = to_int(part->start);
    end = to_int(part->end);
    if (start < end) {
      if (part->step == NO_STEP) {
        part->step = 1;
      }
      if (part->step <= 0) {
        p_die(StrFormat("Invalid step %d for ascending integer range", part->step), this->blame_tok);
      }
    }
    else {
      if (start > end) {
        if (part->step == NO_STEP) {
          part->step = -1;
        }
        if (part->step >= 0) {
          p_die(StrFormat("Invalid step %d for descending integer range", part->step), this->blame_tok);
        }
      }
      else {
        part->step = 1;
      }
    }
  }
  else {
    if (this->token_type == Id::Range_Char) {
      part = this->_ParseRange(this->token_type);
      start_num = ord(part->start->at(0));
      end_num = ord(part->end->at(0));
      if (start_num < end_num) {
        if (part->step == NO_STEP) {
          part->step = 1;
        }
        if (part->step <= 0) {
          p_die(StrFormat("Invalid step %d for ascending character range", part->step), this->blame_tok);
        }
      }
      else {
        if (start_num > end_num) {
          if (part->step == NO_STEP) {
            part->step = -1;
          }
          if (part->step >= 0) {
            p_die(StrFormat("Invalid step %d for descending character range", part->step), this->blame_tok);
          }
        }
        else {
          part->step = 1;
        }
      }
      upper1 = part->start->isupper();
      upper2 = part->end->isupper();
      if (upper1 != upper2) {
        p_die(S_eio, this->blame_tok);
      }
    }
    else {
      throw Alloc<_NotARange>(S_Aoo);
    }
  }
  this->_Eat(Id::Eol_Tok);
  return part;
}

word_part::BracedRange* _RangePartDetect(syntax_asdl::Token* tok) {
  match::SimpleLexer* lx = nullptr;
  braces::_RangeParser* p = nullptr;
  word_part::BracedRange* part = nullptr;
  StackRoot _root0(&tok);
  StackRoot _root1(&lx);
  StackRoot _root2(&p);
  StackRoot _root3(&part);

  lx = match::BraceRangeLexer(lexer::TokenVal(tok));
  p = Alloc<_RangeParser>(lx, tok);
  try {
    part = p->Parse();
  }
  catch (_NotARange* e) {
    return nullptr;
  }
  return part;
}

_StackFrame::_StackFrame(List<syntax_asdl::word_part_t*>* cur_parts) {
  this->cur_parts = cur_parts;
  this->alt_part = Alloc<word_part::BracedTuple>(Alloc<List<syntax_asdl::CompoundWord*>>());
  this->saw_comma = false;
}

word::BracedTree* BraceDetect(syntax_asdl::CompoundWord* w) {
  List<syntax_asdl::word_part_t*>* cur_parts = nullptr;
  List<braces::_StackFrame*>* stack = nullptr;
  bool found;
  int i;
  bool append;
  syntax_asdl::word_part_t* UP_part = nullptr;
  int id_;
  braces::_StackFrame* new_frame = nullptr;
  syntax_asdl::word_part_t* range_part = nullptr;
  syntax_asdl::word_part_t* part2 = nullptr;
  syntax_asdl::Token* tok = nullptr;
  braces::_StackFrame* frame = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&cur_parts);
  StackRoot _root2(&stack);
  StackRoot _root3(&UP_part);
  StackRoot _root4(&new_frame);
  StackRoot _root5(&range_part);
  StackRoot _root6(&part2);
  StackRoot _root7(&tok);
  StackRoot _root8(&frame);

  cur_parts = Alloc<List<syntax_asdl::word_part_t*>>();
  stack = Alloc<List<braces::_StackFrame*>>();
  found = false;
  i = 0;
  for (ListIter<syntax_asdl::word_part_t*> it(w->parts); !it.Done(); it.Next(), ++i) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    append = true;
    UP_part = part;
    if (part->tag() == word_part_e::Literal) {
      Token* part = static_cast<Token*>(UP_part);
      id_ = part->id;
      if (id_ == Id::Lit_LBrace) {
        new_frame = Alloc<_StackFrame>(cur_parts);
        stack->append(new_frame);
        cur_parts = Alloc<List<syntax_asdl::word_part_t*>>();
        append = false;
        found = true;
      }
      else {
        if (id_ == Id::Lit_Comma) {
          if (len(stack)) {
            stack->at(-1)->saw_comma = true;
            stack->at(-1)->alt_part->words->append(Alloc<CompoundWord>(cur_parts));
            cur_parts = Alloc<List<syntax_asdl::word_part_t*>>();
            append = false;
          }
        }
        else {
          if (id_ == Id::Lit_RBrace) {
            if (len(stack) == 0) {
              return nullptr;
            }
            range_part = nullptr;
            if ((!stack->at(-1)->saw_comma and len(cur_parts) == 1)) {
              part2 = cur_parts->at(0);
              if (part2->tag() == word_part_e::Literal) {
                tok = static_cast<Token*>(part2);
                if (tok->id == Id::Lit_Chars) {
                  range_part = _RangePartDetect(tok);
                  if (range_part) {
                    frame = stack->pop();
                    cur_parts = frame->cur_parts;
                    cur_parts->append(range_part);
                    append = false;
                  }
                }
              }
            }
            if (!range_part) {
              if (!stack->at(-1)->saw_comma) {
                return nullptr;
              }
              stack->at(-1)->alt_part->words->append(Alloc<CompoundWord>(cur_parts));
              frame = stack->pop();
              cur_parts = frame->cur_parts;
              cur_parts->append(frame->alt_part);
              append = false;
            }
          }
        }
      }
    }
    if (append) {
      cur_parts->append(part);
    }
  }
  if (len(stack) != 0) {
    return nullptr;
  }
  if (found) {
    return Alloc<word::BracedTree>(cur_parts);
  }
  else {
    return nullptr;
  }
}

List<syntax_asdl::word_t*>* BraceDetectAll(List<syntax_asdl::CompoundWord*>* words) {
  List<syntax_asdl::word_t*>* out = nullptr;
  word::BracedTree* brace_tree = nullptr;
  StackRoot _root0(&words);
  StackRoot _root1(&out);
  StackRoot _root2(&brace_tree);

  out = Alloc<List<syntax_asdl::word_t*>>();
  for (ListIter<syntax_asdl::CompoundWord*> it(words); !it.Done(); it.Next()) {
    syntax_asdl::CompoundWord* w = it.Value();
    StackRoot _for(&w  );
    if (len(w->parts) >= 3) {
      brace_tree = BraceDetect(w);
      if (brace_tree) {
        out->append(brace_tree);
        continue;
      }
    }
    out->append(w);
  }
  return out;
}

int _LeadingZeros(BigStr* s) {
  int n;
  StackRoot _root0(&s);

  n = 0;
  for (StrIter it(s); !it.Done(); it.Next()) {
    BigStr* c = it.Value();
    StackRoot _for(&c  );
    if (str_equals(c, S_wfw)) {
      n += 1;
    }
    else {
      break;
    }
  }
  return n;
}

BigStr* _IntToString(int i, int width) {
  BigStr* s = nullptr;
  int n;
  BigStr* pad = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&pad);

  s = str(i);
  n = len(s);
  if (n < width) {
    pad = str_repeat(S_wfw, (width - n));
    return str_concat(pad, s);
  }
  else {
    return s;
  }
}

List<BigStr*>* _RangeStrings(word_part::BracedRange* part) {
  List<BigStr*>* nums = nullptr;
  int z1;
  int z2;
  int width;
  int n;
  int end;
  int step;
  List<BigStr*>* chars = nullptr;
  int ord_end;
  StackRoot _root0(&part);
  StackRoot _root1(&nums);
  StackRoot _root2(&chars);

  if (part->kind == Id::Range_Int) {
    nums = Alloc<List<BigStr*>>();
    z1 = _LeadingZeros(part->start);
    z2 = _LeadingZeros(part->end);
    if ((z1 == 0 and z2 == 0)) {
      width = 0;
    }
    else {
      if (z1 < z2) {
        width = len(part->end);
      }
      else {
        width = len(part->start);
      }
    }
    n = to_int(part->start);
    end = to_int(part->end);
    step = part->step;
    if (step > 0) {
      while (true) {
        nums->append(_IntToString(n, width));
        n += step;
        if (n > end) {
          break;
        }
      }
    }
    else {
      while (true) {
        nums->append(_IntToString(n, width));
        n += step;
        if (n < end) {
          break;
        }
      }
    }
    return nums;
  }
  else {
    chars = Alloc<List<BigStr*>>();
    n = ord(part->start);
    ord_end = ord(part->end);
    step = part->step;
    if (step > 0) {
      while (true) {
        chars->append(chr(n));
        n += step;
        if (n > ord_end) {
          break;
        }
      }
    }
    else {
      while (true) {
        chars->append(chr(n));
        n += step;
        if (n < ord_end) {
          break;
        }
      }
    }
    return chars;
  }
}

List<List<syntax_asdl::word_part_t*>*>* _ExpandPart(List<syntax_asdl::word_part_t*>* parts, int first_alt_index, List<List<syntax_asdl::word_part_t*>*>* suffixes) {
  List<List<syntax_asdl::word_part_t*>*>* out = nullptr;
  List<syntax_asdl::word_part_t*>* prefix = nullptr;
  syntax_asdl::word_part_t* expand_part = nullptr;
  syntax_asdl::word_part_t* UP_part = nullptr;
  List<List<syntax_asdl::word_part_t*>*>* expanded_alts = nullptr;
  List<syntax_asdl::word_part_t*>* out_parts = nullptr;
  List<BigStr*>* strs = nullptr;
  List<syntax_asdl::word_part_t*>* out_parts_ = nullptr;
  syntax_asdl::Token* t = nullptr;
  StackRoot _root0(&parts);
  StackRoot _root1(&suffixes);
  StackRoot _root2(&out);
  StackRoot _root3(&prefix);
  StackRoot _root4(&expand_part);
  StackRoot _root5(&UP_part);
  StackRoot _root6(&expanded_alts);
  StackRoot _root7(&out_parts);
  StackRoot _root8(&strs);
  StackRoot _root9(&out_parts_);
  StackRoot _root10(&t);

  out = Alloc<List<List<syntax_asdl::word_part_t*>*>>();
  prefix = parts->slice(0, first_alt_index);
  expand_part = parts->at(first_alt_index);
  UP_part = expand_part;
  switch (expand_part->tag()) {
    case word_part_e::BracedTuple: {
      word_part::BracedTuple* expand_part = static_cast<word_part::BracedTuple*>(UP_part);
      expanded_alts = Alloc<List<List<syntax_asdl::word_part_t*>*>>();
      for (ListIter<syntax_asdl::CompoundWord*> it(expand_part->words); !it.Done(); it.Next()) {
        syntax_asdl::CompoundWord* w = it.Value();
        StackRoot _for(&w      );
        expanded_alts->extend(_BraceExpand(w->parts));
      }
      for (ListIter<List<syntax_asdl::word_part_t*>*> it(expanded_alts); !it.Done(); it.Next()) {
        List<syntax_asdl::word_part_t*>* alt_parts = it.Value();
        StackRoot _for(&alt_parts      );
        for (ListIter<List<syntax_asdl::word_part_t*>*> it(suffixes); !it.Done(); it.Next()) {
          List<syntax_asdl::word_part_t*>* suffix = it.Value();
          StackRoot _for(&suffix        );
          out_parts = Alloc<List<syntax_asdl::word_part_t*>>();
          out_parts->extend(prefix);
          out_parts->extend(alt_parts);
          out_parts->extend(suffix);
          out->append(out_parts);
        }
      }
    }
      break;
    case word_part_e::BracedRange: {
      word_part::BracedRange* expand_part = static_cast<word_part::BracedRange*>(UP_part);
      strs = _RangeStrings(expand_part);
      for (ListIter<BigStr*> it(strs); !it.Done(); it.Next()) {
        BigStr* s = it.Value();
        StackRoot _for(&s      );
        for (ListIter<List<syntax_asdl::word_part_t*>*> it(suffixes); !it.Done(); it.Next()) {
          List<syntax_asdl::word_part_t*>* suffix = it.Value();
          StackRoot _for(&suffix        );
          out_parts_ = Alloc<List<syntax_asdl::word_part_t*>>();
          out_parts_->extend(prefix);
          t = lexer::DummyToken(Id::Lit_Chars, s);
          out_parts_->append(t);
          out_parts_->extend(suffix);
          out->append(out_parts_);
        }
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  return out;
}

List<List<syntax_asdl::word_part_t*>*>* _BraceExpand(List<syntax_asdl::word_part_t*>* parts) {
  int num_alts;
  int first_alt_index;
  int i;
  int tag;
  List<syntax_asdl::word_part_t*>* suffix = nullptr;
  List<syntax_asdl::word_part_t*>* tail_parts = nullptr;
  List<List<syntax_asdl::word_part_t*>*>* suffixes = nullptr;
  StackRoot _root0(&parts);
  StackRoot _root1(&suffix);
  StackRoot _root2(&tail_parts);
  StackRoot _root3(&suffixes);

  mylib::MaybeCollect();
  num_alts = 0;
  first_alt_index = -1;
  i = 0;
  for (ListIter<syntax_asdl::word_part_t*> it(parts); !it.Done(); it.Next(), ++i) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    tag = part->tag();
    if ((tag == word_part_e::BracedTuple || tag == word_part_e::BracedRange)) {
      num_alts += 1;
      if (num_alts == 1) {
        first_alt_index = i;
      }
      else {
        if (num_alts == 2) {
          break;
        }
      }
    }
  }
  if (num_alts == 0) {
    return NewList<List<syntax_asdl::word_part_t*>*>(std::initializer_list<List<syntax_asdl::word_part_t*>*>{parts});
  }
  else {
    if (num_alts == 1) {
      suffix = parts->slice((first_alt_index + 1));
      return _ExpandPart(parts, first_alt_index, NewList<List<syntax_asdl::word_part_t*>*>(std::initializer_list<List<syntax_asdl::word_part_t*>*>{suffix}));
    }
    else {
      tail_parts = parts->slice((first_alt_index + 1));
      suffixes = _BraceExpand(tail_parts);
      return _ExpandPart(parts, first_alt_index, suffixes);
    }
  }
}

List<syntax_asdl::CompoundWord*>* BraceExpandWords(List<syntax_asdl::word_t*>* words) {
  List<syntax_asdl::CompoundWord*>* out = nullptr;
  syntax_asdl::word_t* UP_w = nullptr;
  List<List<syntax_asdl::word_part_t*>*>* parts_list = nullptr;
  syntax_asdl::CompoundWord* expanded = nullptr;
  syntax_asdl::CompoundWord* ti = nullptr;
  StackRoot _root0(&words);
  StackRoot _root1(&out);
  StackRoot _root2(&UP_w);
  StackRoot _root3(&parts_list);
  StackRoot _root4(&expanded);
  StackRoot _root5(&ti);

  out = Alloc<List<syntax_asdl::CompoundWord*>>();
  for (ListIter<syntax_asdl::word_t*> it(words); !it.Done(); it.Next()) {
    syntax_asdl::word_t* w = it.Value();
    StackRoot _for(&w  );
    UP_w = w;
    switch (w->tag()) {
      case word_e::BracedTree: {
        word::BracedTree* w = static_cast<word::BracedTree*>(UP_w);
        parts_list = _BraceExpand(w->parts);
        for (ListIter<List<syntax_asdl::word_part_t*>*> it(parts_list); !it.Done(); it.Next()) {
          List<syntax_asdl::word_part_t*>* parts = it.Value();
          StackRoot _for(&parts        );
          expanded = Alloc<CompoundWord>(parts);
          ti = word_::TildeDetect2(expanded);
          if (ti) {
            out->append(ti);
          }
          else {
            out->append(expanded);
          }
        }
      }
        break;
      case word_e::Compound: {
        CompoundWord* w = static_cast<CompoundWord*>(UP_w);
        out->append(w);
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  return out;
}

}  // define namespace braces

namespace cmd_eval {  // define

using id_kind_asdl::Id;
using option_asdl::option_i;
using syntax_asdl::IntParamBox;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::loc_e;
using syntax_asdl::Token;
using syntax_asdl::CompoundWord;
using syntax_asdl::command;
using syntax_asdl::command_e;
using syntax_asdl::command_t;
using syntax_asdl::command_str;
using syntax_asdl::condition;
using syntax_asdl::condition_e;
using syntax_asdl::condition_t;
using syntax_asdl::case_arg;
using syntax_asdl::case_arg_e;
using syntax_asdl::case_arg_t;
using syntax_asdl::BraceGroup;
using syntax_asdl::Proc;
using syntax_asdl::Func;
using syntax_asdl::assign_op_e;
using syntax_asdl::expr_t;
using syntax_asdl::proc_sig;
using syntax_asdl::proc_sig_e;
using syntax_asdl::redir_param;
using syntax_asdl::redir_param_e;
using syntax_asdl::for_iter;
using syntax_asdl::for_iter_e;
using syntax_asdl::pat;
using syntax_asdl::pat_e;
using syntax_asdl::word;
using syntax_asdl::Eggex;
using syntax_asdl::List_of_command;
using runtime_asdl::cmd_value;
using runtime_asdl::cmd_value_e;
using runtime_asdl::CommandStatus;
using runtime_asdl::flow_e;
using runtime_asdl::RedirValue;
using runtime_asdl::redirect_arg;
using runtime_asdl::ProcArgs;
using runtime_asdl::scope_e;
using runtime_asdl::StatusArray;
using types_asdl::redir_arg_type_e;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::y_lvalue;
using value_asdl::y_lvalue_e;
using value_asdl::y_lvalue_t;
using value_asdl::LeftName;
using value_asdl::Obj;
using error::e_die;
using error::e_die_status;
int IsMainProgram = (1 << 0);
int RaiseControlFlow = (1 << 1);
int OptimizeSubshells = (1 << 2);
int MarkLastCommands = (1 << 3);
int NoDebugTrap = (1 << 4);
int NoErrTrap = (1 << 5);
BigStr* _STRICT_ERREXIT_COND_MSG = S_kvC;

cmd_value::Argv* MakeBuiltinArgv(List<BigStr*>* argv1) {
  List<BigStr*>* argv = nullptr;
  syntax_asdl::CompoundWord* missing = nullptr;
  StackRoot _root0(&argv1);
  StackRoot _root1(&argv);
  StackRoot _root2(&missing);

  argv = NewList<BigStr*>(std::initializer_list<BigStr*>{S_Aoo});
  argv->extend(argv1);
  missing = nullptr;
  return Alloc<cmd_value::Argv>(argv, list_repeat(missing, len(argv)), false, nullptr, nullptr);
}

Deps::Deps() {
  this->mutable_opts = nullptr;
  this->dumper = nullptr;
  this->debug_f = nullptr;
}

syntax_asdl::command_t* _HasManyStatuses(syntax_asdl::command_t* node) {
  syntax_asdl::command_t* UP_node = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);

  UP_node = node;
  switch (node->tag()) {
    case command_e::Simple: 
    case command_e::DBracket: 
    case command_e::DParen: {
      return nullptr;
    }
      break;
    case command_e::Redirect: {
      command::Redirect* node = static_cast<command::Redirect*>(UP_node);
      return _HasManyStatuses(node->child);
    }
      break;
    case command_e::Sentence: {
      command::Sentence* node = static_cast<command::Sentence*>(UP_node);
      return _HasManyStatuses(node->child);
    }
      break;
    case command_e::Pipeline: {
      command::Pipeline* node = static_cast<command::Pipeline*>(UP_node);
      if (len(node->children) == 1) {
        return _HasManyStatuses(node->children->at(0));
      }
      else {
        return node;
      }
    }
      break;
    case command_e::AndOr: {
      command::AndOr* node = static_cast<command::AndOr*>(UP_node);
      for (ListIter<syntax_asdl::command_t*> it(node->children); !it.Done(); it.Next()) {
        syntax_asdl::command_t* c = it.Value();
        StackRoot _for(&c      );
        if (_HasManyStatuses(c)) {
          return c;
        }
      }
      return nullptr;
    }
      break;
  }
  return node;
}

value_asdl::value_t* PlusEquals(value_asdl::value_t* old_val, value_asdl::value_t* val) {
  value_asdl::value_t* UP_old_val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  int tag;
  List<BigStr*>* strs = nullptr;
  Dict<BigStr*, BigStr*>* d = nullptr;
  StackRoot _root0(&old_val);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_old_val);
  StackRoot _root3(&UP_val);
  StackRoot _root4(&strs);
  StackRoot _root5(&d);

  UP_old_val = old_val;
  UP_val = val;
  tag = val->tag();
  switch (old_val->tag()) {
    case value_e::Undef: {
      ;  // pass
    }
      break;
    case value_e::Str: {
      if (tag == value_e::Str) {
        value::Str* old_val = static_cast<value::Str*>(UP_old_val);
        value::Str* str_to_append = static_cast<value::Str*>(UP_val);
        val = Alloc<value::Str>(str_concat(old_val->s, str_to_append->s));
      }
      else {
        if ((tag == value_e::BashArray || tag == value_e::SparseArray || tag == value_e::BashAssoc)) {
          e_die(S_Foc);
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
      break;
    case value_e::BashArray: 
    case value_e::SparseArray: {
      if (tag == value_e::Str) {
        e_die(S_waA);
      }
      else {
        if ((tag == value_e::BashArray || tag == value_e::SparseArray)) {
          if (tag == value_e::BashArray) {
            value::BashArray* array_rhs = static_cast<value::BashArray*>(UP_val);
            strs = bash_impl::BashArray_GetValues(array_rhs);
          }
          else {
            value::SparseArray* sparse_rhs = static_cast<value::SparseArray*>(UP_val);
            strs = bash_impl::SparseArray_GetValues(sparse_rhs);
          }
          if (old_val->tag() == value_e::BashArray) {
            value::BashArray* array_lhs = static_cast<value::BashArray*>(UP_old_val);
            bash_impl::BashArray_AppendValues(array_lhs, strs);
            val = array_lhs;
          }
          else {
            value::SparseArray* sparse_lhs = static_cast<value::SparseArray*>(UP_old_val);
            bash_impl::SparseArray_AppendValues(sparse_lhs, strs);
            val = sparse_lhs;
          }
        }
        else {
          if (tag == value_e::BashAssoc) {
            e_die(S_ibk);
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
    }
      break;
    case value_e::BashAssoc: {
      if (tag == value_e::Str) {
        e_die(S_rCi);
      }
      else {
        if ((tag == value_e::BashArray || tag == value_e::SparseArray)) {
          e_die(S_hmo);
        }
        else {
          if (tag == value_e::BashAssoc) {
            value::BashAssoc* assoc_lhs = static_cast<value::BashAssoc*>(UP_old_val);
            value::BashAssoc* assoc_rhs = static_cast<value::BashAssoc*>(UP_val);
            d = bash_impl::BashAssoc_GetDict(assoc_rhs);
            bash_impl::BashAssoc_AppendDict(assoc_lhs, d);
            val = assoc_lhs;
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
    }
      break;
    default: {
      e_die(StrFormat("Can't append to value of type %s", ui::ValType(old_val)));
    }
  }
  return val;
}

ctx_LoopLevel::ctx_LoopLevel(cmd_eval::CommandEvaluator* cmd_ev) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->cmd_ev)));
  cmd_ev->loop_level += 1;
  this->cmd_ev = cmd_ev;
}

ctx_LoopLevel::~ctx_LoopLevel() {
  this->cmd_ev->loop_level -= 1;
  gHeap.PopRoot();
}

CommandEvaluator::CommandEvaluator(state::Mem* mem, optview::Exec* exec_opts, ui::ErrorFormatter* errfmt, state::Procs* procs, Dict<int, vm::_AssignBuiltin*>* assign_builtins, alloc::Arena* arena, cmd_eval::Deps* cmd_deps, trap_osh::TrapState* trap_state, iolib::SignalSafe* signal_safe) {
  this->shell_ex = nullptr;
  this->arith_ev = nullptr;
  this->bool_ev = nullptr;
  this->expr_ev = nullptr;
  this->word_ev = nullptr;
  this->tracer = nullptr;
  this->mem = mem;
  this->exec_opts = exec_opts;
  this->errfmt = errfmt;
  this->procs = procs;
  this->assign_builtins = assign_builtins;
  this->arena = arena;
  this->mutable_opts = cmd_deps->mutable_opts;
  this->dumper = cmd_deps->dumper;
  this->debug_f = cmd_deps->debug_f;
  this->trap_state = trap_state;
  this->signal_safe = signal_safe;
  this->loop_level = 0;
  this->check_command_sub_status = false;
  this->status_array_pool = Alloc<List<runtime_asdl::StatusArray*>>();
}

void CommandEvaluator::CheckCircularDeps() {
}

int CommandEvaluator::_RunAssignBuiltin(cmd_value::Assign* cmd_val) {
  vm::_AssignBuiltin* builtin_func = nullptr;
  List<IOError_OSError*>* io_errors = nullptr;
  int status;
  BigStr* arg0 = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&builtin_func);
  StackRoot _root2(&io_errors);
  StackRoot _root3(&arg0);

  builtin_func = this->assign_builtins->get(cmd_val->builtin_id);
  if (builtin_func == nullptr) {
    e_die(StrFormat("Assignment builtin %r not configured", cmd_val->argv->at(0)), cmd_val->arg_locs->at(0));
  }
  io_errors = Alloc<List<IOError_OSError*>>();
  {  // with
    vm::ctx_FlushStdout ctx{io_errors};

    {  // with
      ui::ctx_Location ctx{this->errfmt, cmd_val->arg_locs->at(0)};

      try {
        status = builtin_func->Run(cmd_val);
      }
      catch (IOError_OSError* e) {
        this->errfmt->PrintMessage(StrFormat("%s builtin I/O error: %s", cmd_val->argv->at(0), pyutil::strerror(e)), cmd_val->arg_locs->at(0));
        return 1;
      }
      catch (error::Usage* e) {
        arg0 = cmd_val->argv->at(0);
        this->errfmt->PrefixPrint(e->msg, StrFormat("%r ", arg0), e->location);
        return 2;
      }
    }
  }
  if (len(io_errors)) {
    this->errfmt->PrintMessage(StrFormat("%s builtin I/O: %s", cmd_val->argv->at(0), pyutil::strerror(io_errors->at(0))), cmd_val->arg_locs->at(0));
    return 1;
  }
  return status;
}

void CommandEvaluator::_CheckStatus(int status, runtime_asdl::CommandStatus* cmd_st, syntax_asdl::command_t* node, syntax_asdl::loc_t* default_loc) {
  syntax_asdl::command_t* UP_node = nullptr;
  BigStr* desc = nullptr;
  syntax_asdl::loc_t* blame_loc = nullptr;
  BigStr* msg = nullptr;
  StackRoot _root0(&cmd_st);
  StackRoot _root1(&node);
  StackRoot _root2(&default_loc);
  StackRoot _root3(&UP_node);
  StackRoot _root4(&desc);
  StackRoot _root5(&blame_loc);
  StackRoot _root6(&msg);

  if (status == 0) {
    return ;
  }
  this->_MaybeRunErrTrap();
  if (this->exec_opts->errexit()) {
    UP_node = node;
    switch (node->tag()) {
      case command_e::ShAssignment: {
        command::ShAssignment* node = static_cast<command::ShAssignment*>(UP_node);
        cmd_st->show_code = true;
      }
        break;
      case command_e::Subshell: {
        command::Subshell* node = static_cast<command::Subshell*>(UP_node);
        cmd_st->show_code = true;
      }
        break;
      case command_e::Pipeline: {
        command::Pipeline* node = static_cast<command::Pipeline*>(UP_node);
        cmd_st->show_code = true;
      }
        break;
    }
    desc = command_str(node->tag());
    if (default_loc->tag() != loc_e::Missing) {
      blame_loc = default_loc;
    }
    else {
      blame_loc = location::TokenForCommand(node);
    }
    msg = StrFormat("%s failed with status %d", desc, status);
    throw Alloc<error::ErrExit>(status, msg, blame_loc, cmd_st->show_code);
  }
}

runtime_asdl::RedirValue* CommandEvaluator::_EvalRedirect(syntax_asdl::Redir* r) {
  runtime_asdl::RedirValue* result = nullptr;
  syntax_asdl::redir_param_t* arg = nullptr;
  syntax_asdl::redir_param_t* UP_arg = nullptr;
  types_asdl::redir_arg_type_t redir_type;
  word::BracedTree* b = nullptr;
  List<BigStr*>* files = nullptr;
  int n;
  value::Str* val = nullptr;
  BigStr* t = nullptr;
  int target_fd;
  BigStr* s = nullptr;
  syntax_asdl::CompoundWord* w = nullptr;
  StackRoot _root0(&r);
  StackRoot _root1(&result);
  StackRoot _root2(&arg);
  StackRoot _root3(&UP_arg);
  StackRoot _root4(&b);
  StackRoot _root5(&files);
  StackRoot _root6(&val);
  StackRoot _root7(&t);
  StackRoot _root8(&s);
  StackRoot _root9(&w);

  result = Alloc<RedirValue>(r->op->id, r->op, r->loc, nullptr);
  arg = r->arg;
  UP_arg = arg;
  switch (arg->tag()) {
    case redir_param_e::Word: {
      CompoundWord* arg_word = static_cast<CompoundWord*>(UP_arg);
      this->mem->SetTokenForLine(r->op);
      redir_type = consts::RedirArgType(r->op->id);
      if (redir_type == redir_arg_type_e::Path) {
        b = braces::BraceDetect(arg_word);
        if (b != nullptr) {
          throw Alloc<error::RedirectEval>(S_neb, arg_word);
        }
        files = this->word_ev->EvalWordSequence(NewList<syntax_asdl::CompoundWord*>(std::initializer_list<syntax_asdl::CompoundWord*>{arg_word}));
        n = len(files);
        if (n == 0) {
          throw Alloc<error::RedirectEval>(S_abx, arg_word);
        }
        if (n > 1) {
          throw Alloc<error::RedirectEval>(S_lvB, arg_word);
        }
        result->arg = Alloc<redirect_arg::Path>(files->at(0));
        return result;
      }
      else {
        if (redir_type == redir_arg_type_e::Desc) {
          val = this->word_ev->EvalWordToString(arg_word);
          t = val->s;
          if (len(t) == 0) {
            throw Alloc<error::RedirectEval>(S_Cjh, arg_word);
            return nullptr;
          }
          try {
            if (str_equals(t, S_Bjq)) {
              result->arg = redirect_arg::CloseFd;
            }
            else {
              if (str_equals(t->at(-1), S_Bjq)) {
                target_fd = to_int(t->slice(0, -1));
                result->arg = Alloc<redirect_arg::MoveFd>(target_fd);
              }
              else {
                result->arg = Alloc<redirect_arg::CopyFd>(to_int(t));
              }
            }
          }
          catch (ValueError*) {
            throw Alloc<error::RedirectEval>(StrFormat("Invalid descriptor %r.  Expected D, -, or D- where D is an integer", t), arg_word);
            return nullptr;
          }
          return result;
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
      break;
    case redir_param_e::HereWord: {
      redir_param::HereWord* arg = static_cast<redir_param::HereWord*>(UP_arg);
      val = this->word_ev->EvalWordToString(arg->w);
      if (arg->is_multiline) {
        s = val->s;
      }
      else {
        s = str_concat(val->s, S_nfs);
      }
      result->arg = Alloc<redirect_arg::HereDoc>(s);
      return result;
    }
      break;
    case redir_param_e::HereDoc: {
      redir_param::HereDoc* arg = static_cast<redir_param::HereDoc*>(UP_arg);
      w = Alloc<CompoundWord>(arg->stdin_parts);
      val = this->word_ev->EvalWordToString(w);
      result->arg = Alloc<redirect_arg::HereDoc>(val->s);
      return result;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

int CommandEvaluator::_RunSimpleCommand(runtime_asdl::cmd_value_t* cmd_val, runtime_asdl::CommandStatus* cmd_st, int run_flags) {
  runtime_asdl::cmd_value_t* UP_cmd_val = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&cmd_st);
  StackRoot _root2(&UP_cmd_val);

  UP_cmd_val = cmd_val;
  switch (UP_cmd_val->tag()) {
    case cmd_value_e::Argv: {
      cmd_value::Argv* cmd_val = static_cast<cmd_value::Argv*>(UP_cmd_val);
      this->tracer->OnSimpleCommand(cmd_val->argv);
      return this->shell_ex->RunSimpleCommand(cmd_val, cmd_st, run_flags);
    }
      break;
    case cmd_value_e::Assign: {
      cmd_value::Assign* cmd_val = static_cast<cmd_value::Assign*>(UP_cmd_val);
      this->tracer->OnAssignBuiltin(cmd_val);
      return this->_RunAssignBuiltin(cmd_val);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void CommandEvaluator::_EvalTempEnv(List<syntax_asdl::EnvPair*>* more_env, int flags) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&more_env);
  StackRoot _root1(&val);

  for (ListIter<syntax_asdl::EnvPair*> it(more_env); !it.Done(); it.Next()) {
    syntax_asdl::EnvPair* e_pair = it.Value();
    StackRoot _for(&e_pair  );
    val = this->word_ev->EvalRhsWord(e_pair->val);
    this->mem->SetNamed(location::LName(e_pair->name), val, scope_e::LocalOnly, flags);
  }
}

void CommandEvaluator::_StrictErrExit(syntax_asdl::command_t* node) {
  syntax_asdl::command_t* bad_node = nullptr;
  BigStr* node_str = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&bad_node);
  StackRoot _root2(&node_str);

  if (!(this->exec_opts->errexit() and this->exec_opts->strict_errexit())) {
    return ;
  }
  bad_node = _HasManyStatuses(node);
  if (bad_node) {
    node_str = ui::CommandType(bad_node);
    e_die(StrFormat(_STRICT_ERREXIT_COND_MSG, node_str), Alloc<loc::Command>(bad_node));
  }
}

void CommandEvaluator::_StrictErrExitList(List<syntax_asdl::command_t*>* node_list) {
  syntax_asdl::command_t* node = nullptr;
  syntax_asdl::command_t* bad_node = nullptr;
  BigStr* node_str = nullptr;
  StackRoot _root0(&node_list);
  StackRoot _root1(&node);
  StackRoot _root2(&bad_node);
  StackRoot _root3(&node_str);

  if (!(this->exec_opts->errexit() and this->exec_opts->strict_errexit())) {
    return ;
  }
  if (len(node_list) > 1) {
    e_die(S_rgl, Alloc<loc::Command>(node_list->at(0)));
  }
  node = node_list->at(0);
  bad_node = _HasManyStatuses(node);
  if (bad_node) {
    node_str = ui::CommandType(bad_node);
    e_die(StrFormat(_STRICT_ERREXIT_COND_MSG, node_str), Alloc<loc::Command>(bad_node));
  }
}

bool CommandEvaluator::_EvalCondition(syntax_asdl::condition_t* cond, syntax_asdl::Token* blame_tok) {
  bool b;
  syntax_asdl::condition_t* UP_cond = nullptr;
  int cond_status;
  value_asdl::value_t* obj = nullptr;
  StackRoot _root0(&cond);
  StackRoot _root1(&blame_tok);
  StackRoot _root2(&UP_cond);
  StackRoot _root3(&obj);

  b = false;
  UP_cond = cond;
  switch (cond->tag()) {
    case condition_e::Shell: {
      List_of_command* cond = static_cast<List_of_command*>(UP_cond);
      this->_StrictErrExitList(cond);
      {  // with
        state::ctx_ErrExit ctx{this->mutable_opts, false, blame_tok};

        cond_status = this->_ExecuteList(cond);
      }
      b = cond_status == 0;
    }
      break;
    case condition_e::YshExpr: {
      condition::YshExpr* cond = static_cast<condition::YshExpr*>(UP_cond);
      obj = this->expr_ev->EvalExpr(cond->e, blame_tok);
      b = val_ops::ToBool(obj);
    }
      break;
  }
  return b;
}

value_asdl::value_t* CommandEvaluator::_EvalCaseArg(syntax_asdl::case_arg_t* arg, syntax_asdl::loc_t* blame) {
  syntax_asdl::case_arg_t* UP_arg = nullptr;
  StackRoot _root0(&arg);
  StackRoot _root1(&blame);
  StackRoot _root2(&UP_arg);

  UP_arg = arg;
  switch (arg->tag()) {
    case case_arg_e::Word: {
      case_arg::Word* arg = static_cast<case_arg::Word*>(UP_arg);
      return this->word_ev->EvalWordToString(arg->w);
    }
      break;
    case case_arg_e::YshExpr: {
      case_arg::YshExpr* arg = static_cast<case_arg::YshExpr*>(UP_arg);
      return this->expr_ev->EvalExpr(arg->e, blame);
    }
      break;
    default: {
      FAIL(kNotImplemented);  // Python NotImplementedError
    }
  }
}

int CommandEvaluator::_DoVarDecl(command::VarDecl* node) {
  syntax_asdl::NameType* lhs0 = nullptr;
  value_asdl::LeftName* lval = nullptr;
  value_asdl::value_t* val = nullptr;
  int flags;
  int i;
  value_asdl::value_t* right_val = nullptr;
  List<value_asdl::LeftName*>* lvals = nullptr;
  List<value_asdl::value_t*>* rhs_vals = nullptr;
  int num_lhs;
  List<value_asdl::value_t*>* items = nullptr;
  int num_rhs;
  value_asdl::value_t* rval = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&lhs0);
  StackRoot _root2(&lval);
  StackRoot _root3(&val);
  StackRoot _root4(&right_val);
  StackRoot _root5(&lvals);
  StackRoot _root6(&rhs_vals);
  StackRoot _root7(&items);
  StackRoot _root8(&rval);

  if (node->keyword == nullptr) {
    lhs0 = node->lhs->at(0);
    lval = Alloc<LeftName>(lhs0->name, lhs0->left);
    val = this->expr_ev->EvalExpr(node->rhs, loc::Missing);
    this->mem->SetNamed(lval, val, scope_e::LocalOnly, state::SetReadOnly);
  }
  else {
    flags = node->keyword->id == Id::KW_Const ? state::SetReadOnly : 0;
    if (node->rhs == nullptr) {
      i = 0;
      for (ListIter<syntax_asdl::NameType*> it(node->lhs); !it.Done(); it.Next(), ++i) {
        syntax_asdl::NameType* lhs_val = it.Value();
        StackRoot _for(&lhs_val      );
        lval = Alloc<LeftName>(lhs_val->name, lhs_val->left);
        this->mem->SetNamed(lval, value::Null, scope_e::LocalOnly, flags);
      }
      return 0;
    }
    right_val = this->expr_ev->EvalExpr(node->rhs, loc::Missing);
    lvals = nullptr;
    rhs_vals = nullptr;
    num_lhs = len(node->lhs);
    if (num_lhs == 1) {
      lhs0 = node->lhs->at(0);
      lvals = NewList<value_asdl::LeftName*>(std::initializer_list<value_asdl::LeftName*>{Alloc<LeftName>(lhs0->name, lhs0->left)});
      rhs_vals = NewList<value_asdl::value_t*>(std::initializer_list<value_asdl::value_t*>{right_val});
    }
    else {
      items = val_ops::ToList(right_val, S_BAA, node->keyword);
      num_rhs = len(items);
      if (num_lhs != num_rhs) {
        throw Alloc<error::Expr>(StrFormat("Got %d places on the left, but %d values on right", num_lhs, num_rhs), node->keyword);
      }
      lvals = Alloc<List<value_asdl::LeftName*>>();
      rhs_vals = Alloc<List<value_asdl::value_t*>>();
      i = 0;
      for (ListIter<syntax_asdl::NameType*> it(node->lhs); !it.Done(); it.Next(), ++i) {
        syntax_asdl::NameType* lhs_val = it.Value();
        StackRoot _for(&lhs_val      );
        lval = Alloc<LeftName>(lhs_val->name, lhs_val->left);
        lvals->append(lval);
        rhs_vals->append(items->at(i));
      }
    }
    i = 0;
    for (ListIter<value_asdl::LeftName*> it(lvals); !it.Done(); it.Next(), ++i) {
      value_asdl::LeftName* lval = it.Value();
      StackRoot _for(&lval    );
      rval = rhs_vals->at(i);
      this->mem->SetNamed(lval, rval, scope_e::LocalOnly, flags);
    }
  }
  return 0;
}

void CommandEvaluator::_DoMutation(command::Mutation* node) {
  runtime_asdl::scope_t which_scopes;
  value_asdl::value_t* right_val = nullptr;
  List<value_asdl::y_lvalue_t*>* lvals = nullptr;
  List<value_asdl::value_t*>* rhs_vals = nullptr;
  int num_lhs;
  List<value_asdl::value_t*>* items = nullptr;
  int num_rhs;
  int i;
  value_asdl::value_t* rval = nullptr;
  value_asdl::y_lvalue_t* UP_lval = nullptr;
  value_asdl::value_t* obj = nullptr;
  value_asdl::value_t* UP_obj = nullptr;
  mops::BigInt index;
  BigStr* key = nullptr;
  value_asdl::y_lvalue_t* aug_lval = nullptr;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&right_val);
  StackRoot _root2(&lvals);
  StackRoot _root3(&rhs_vals);
  StackRoot _root4(&items);
  StackRoot _root5(&rval);
  StackRoot _root6(&UP_lval);
  StackRoot _root7(&obj);
  StackRoot _root8(&UP_obj);
  StackRoot _root9(&key);
  StackRoot _root10(&aug_lval);
  StackRoot _root11(&val);

  switch (node->keyword->id) {
    case Id::KW_SetVar: {
      which_scopes = scope_e::LocalOnly;
    }
      break;
    case Id::KW_SetGlobal: {
      which_scopes = scope_e::GlobalOnly;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  if (node->op->id == Id::Arith_Equal) {
    right_val = this->expr_ev->EvalExpr(node->rhs, loc::Missing);
    lvals = nullptr;
    rhs_vals = nullptr;
    num_lhs = len(node->lhs);
    if (num_lhs == 1) {
      lvals = NewList<value_asdl::y_lvalue_t*>(std::initializer_list<value_asdl::y_lvalue_t*>{this->expr_ev->EvalLhsExpr(node->lhs->at(0), which_scopes)});
      rhs_vals = NewList<value_asdl::value_t*>(std::initializer_list<value_asdl::value_t*>{right_val});
    }
    else {
      items = val_ops::ToList(right_val, S_BAA, node->keyword);
      num_rhs = len(items);
      if (num_lhs != num_rhs) {
        throw Alloc<error::Expr>(StrFormat("Got %d places on the left, but %d values on the right", num_lhs, num_rhs), node->keyword);
      }
      lvals = Alloc<List<value_asdl::y_lvalue_t*>>();
      rhs_vals = Alloc<List<value_asdl::value_t*>>();
      i = 0;
      for (ListIter<syntax_asdl::y_lhs_t*> it(node->lhs); !it.Done(); it.Next(), ++i) {
        syntax_asdl::y_lhs_t* lhs_val = it.Value();
        StackRoot _for(&lhs_val      );
        lvals->append(this->expr_ev->EvalLhsExpr(lhs_val, which_scopes));
        rhs_vals->append(items->at(i));
      }
    }
    i = 0;
    for (ListIter<value_asdl::y_lvalue_t*> it(lvals); !it.Done(); it.Next(), ++i) {
      value_asdl::y_lvalue_t* lval = it.Value();
      StackRoot _for(&lval    );
      rval = rhs_vals->at(i);
      UP_lval = lval;
      if (lval->tag() == y_lvalue_e::Local) {
        LeftName* lval = static_cast<LeftName*>(UP_lval);
        this->mem->SetNamed(lval, rval, which_scopes);
      }
      else {
        if (lval->tag() == y_lvalue_e::Container) {
          y_lvalue::Container* lval = static_cast<y_lvalue::Container*>(UP_lval);
          obj = lval->obj;
          UP_obj = obj;
          switch (obj->tag()) {
            case value_e::List: {
              value::List* obj = static_cast<value::List*>(UP_obj);
              index = expr_eval::_ConvertToInt(lval->index, S_cfe, loc::Missing);
              i = mops::BigTruncate(index);
              try {
                obj->items->set(i, rval);
              }
              catch (IndexError*) {
                throw Alloc<error::Expr>(S_tvw, loc::Missing);
              }
            }
              break;
            case value_e::Dict: {
              value::Dict* obj = static_cast<value::Dict*>(UP_obj);
              key = val_ops::ToStr(lval->index, S_slu, loc::Missing);
              obj->d->set(key, rval);
            }
              break;
            case value_e::Obj: {
              Obj* obj = static_cast<Obj*>(UP_obj);
              key = val_ops::ToStr(lval->index, S_eBt, loc::Missing);
              obj->d->set(key, rval);
            }
              break;
            default: {
              throw Alloc<error::TypeErr>(obj, S_rcA, loc::Missing);
            }
          }
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
  }
  else {
    aug_lval = this->expr_ev->EvalLhsExpr(node->lhs->at(0), which_scopes);
    val = this->expr_ev->EvalExpr(node->rhs, loc::Missing);
    this->expr_ev->EvalAugmented(aug_lval, val, node->op, which_scopes);
  }
}

int CommandEvaluator::_DoSimple(command::Simple* node, runtime_asdl::CommandStatus* cmd_st) {
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  runtime_asdl::cmd_value_t* cmd_val = nullptr;
  runtime_asdl::cmd_value_t* UP_cmd_val = nullptr;
  int run_flags;
  Dict<BigStr*, value_asdl::value_t*>* bindings = nullptr;
  int status;
  bool is_other_special;
  StackRoot _root0(&node);
  StackRoot _root1(&cmd_st);
  StackRoot _root2(&words);
  StackRoot _root3(&cmd_val);
  StackRoot _root4(&UP_cmd_val);
  StackRoot _root5(&bindings);

  DTRACE_PROBE(cmd_eval, _DoSimple_enter);
  words = braces::BraceExpandWords(node->words);
  cmd_val = this->word_ev->EvalWordSequence2(words, node->is_last_cmd, true);
  UP_cmd_val = cmd_val;
  if (UP_cmd_val->tag() == cmd_value_e::Argv) {
    cmd_value::Argv* cmd_val = static_cast<cmd_value::Argv*>(UP_cmd_val);
    if (len(cmd_val->argv)) {
      this->mem->SetLastArgument(cmd_val->argv->at(-1));
    }
    else {
      this->mem->SetLastArgument(S_Aoo);
    }
    if ((node->typed_args or node->block)) {
      cmd_val->proc_args = Alloc<ProcArgs>(node->typed_args, nullptr, nullptr, nullptr);
      func_proc::EvalTypedArgsToProc(this->expr_ev, this->mem->CurrentFrame(), this->mem->GlobalFrame(), this->mutable_opts, node, cmd_val->proc_args);
    }
  }
  else {
    if (node->block) {
      e_die(S_dne, node->block->brace_group->left);
    }
    cmd_value::Assign* cmd_val = static_cast<cmd_value::Assign*>(UP_cmd_val);
  }
  run_flags = node->is_last_cmd ? executor::IS_LAST_CMD : 0;
  if (len(node->more_env)) {
    if (this->exec_opts->env_obj()) {
      bindings = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      {  // with
        state::ctx_EnclosedFrame ctx{this->mem, this->mem->CurrentFrame(), this->mem->GlobalFrame(), bindings};

        this->_EvalTempEnv(node->more_env, 0);
      }
      {  // with
        state::ctx_EnvObj ctx{this->mem, bindings};

        status = this->_RunSimpleCommand(cmd_val, cmd_st, run_flags);
      }
    }
    else {
      is_other_special = false;
      if ((cmd_val->tag() == cmd_value_e::Assign or is_other_special)) {
        this->_EvalTempEnv(node->more_env, 0);
        status = this->_RunSimpleCommand(cmd_val, cmd_st, run_flags);
      }
      else {
        {  // with
          state::ctx_Temp ctx{this->mem};

          this->_EvalTempEnv(node->more_env, state::SetExport);
          status = this->_RunSimpleCommand(cmd_val, cmd_st, run_flags);
        }
      }
    }
  }
  else {
    status = this->_RunSimpleCommand(cmd_val, cmd_st, run_flags);
  }
  DTRACE_PROBE1(cmd_eval, _DoSimple_exit, status);
  return status;
}

int CommandEvaluator::_DoExpandedAlias(command::ExpandedAlias* node) {
  StackRoot _root0(&node);

  if (len(node->more_env)) {
    {  // with
      state::ctx_Temp ctx{this->mem};

      this->_EvalTempEnv(node->more_env, state::SetExport);
      return this->_Execute(node->child);
    }
  }
  else {
    return this->_Execute(node->child);
  }
}

int CommandEvaluator::_DoPipeline(command::Pipeline* node, runtime_asdl::CommandStatus* cmd_st) {
  int status;
  int tmp_status;
  StackRoot _root0(&node);
  StackRoot _root1(&cmd_st);

  cmd_st->check_errexit = true;
  for (ListIter<syntax_asdl::Token*> it(node->ops); !it.Done(); it.Next()) {
    syntax_asdl::Token* op = it.Value();
    StackRoot _for(&op  );
    if (op->id != Id::Op_Pipe) {
      e_die(S_yDp, op);
    }
  }
  this->mem->SetLastArgument(S_Aoo);
  status = -1;
  if (node->negated != nullptr) {
    this->_StrictErrExit(node);
    {  // with
      state::ctx_ErrExit ctx{this->mutable_opts, false, node->negated};

      if (len(node->children) == 1) {
        tmp_status = this->_Execute(node->children->at(0));
        status = tmp_status == 0 ? 1 : 0;
      }
      else {
        this->shell_ex->RunPipeline(node, cmd_st);
        cmd_st->pipe_negated = true;
      }
    }
    cmd_st->check_errexit = false;
  }
  else {
    this->shell_ex->RunPipeline(node, cmd_st);
  }
  return status;
}

int CommandEvaluator::_DoShAssignment(command::ShAssignment* node, runtime_asdl::CommandStatus* cmd_st) {
  runtime_asdl::scope_t which_scopes;
  value_asdl::value_t* rhs = nullptr;
  value_asdl::sh_lvalue_t* lval = nullptr;
  value_asdl::value_t* old_val = nullptr;
  value_asdl::value_t* val = nullptr;
  int flags;
  int last_status;
  StackRoot _root0(&node);
  StackRoot _root1(&cmd_st);
  StackRoot _root2(&rhs);
  StackRoot _root3(&lval);
  StackRoot _root4(&old_val);
  StackRoot _root5(&val);

  which_scopes = this->mem->ScopesForWriting();
  for (ListIter<syntax_asdl::AssignPair*> it(node->pairs); !it.Done(); it.Next()) {
    syntax_asdl::AssignPair* pair = it.Value();
    StackRoot _for(&pair  );
    if (pair->op == assign_op_e::PlusEqual) {
      rhs = this->word_ev->EvalRhsWord(pair->rhs);
      lval = this->arith_ev->EvalShellLhs(pair->lhs, which_scopes);
      old_val = sh_expr_eval::OldValue(lval, this->mem, nullptr);
      val = PlusEquals(old_val, rhs);
    }
    else {
      lval = this->arith_ev->EvalShellLhs(pair->lhs, which_scopes);
      if (pair->rhs) {
        rhs = this->word_ev->EvalRhsWord(pair->rhs);
      }
      else {
        rhs = nullptr;
      }
      val = rhs;
    }
    flags = 0;
    this->mem->SetValue(lval, val, which_scopes, flags);
    this->tracer->OnShAssignment(lval, pair->op, rhs, flags, which_scopes);
  }
  if (this->check_command_sub_status) {
    last_status = this->mem->LastStatus();
    this->_CheckStatus(last_status, cmd_st, node, loc::Missing);
    return last_status;
  }
  else {
    return 0;
  }
}

int CommandEvaluator::_DoExpr(command::Expr* node) {
  value_asdl::value_t* val = nullptr;
  List<IOError_OSError*>* io_errors = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&val);
  StackRoot _root2(&io_errors);

  val = this->expr_ev->EvalExpr(node->e, loc::Missing);
  if (node->keyword->id == Id::Lit_Equals) {
    io_errors = Alloc<List<IOError_OSError*>>();
    {  // with
      vm::ctx_FlushStdout ctx{io_errors};

      try {
        ui::PrettyPrintValue(S_Aoo, val, mylib::Stdout());
      }
      catch (IOError_OSError* e) {
        this->errfmt->PrintMessage(StrFormat("I/O error during = keyword: %s", pyutil::strerror(e)), node->keyword);
        return 1;
      }
    }
    if (len(io_errors)) {
      this->errfmt->PrintMessage(StrFormat("I/O error during = keyword: %s", pyutil::strerror(io_errors->at(0))), node->keyword);
      return 1;
    }
  }
  return 0;
}

int CommandEvaluator::_DoControlFlow(command::ControlFlow* node) {
  syntax_asdl::Token* keyword = nullptr;
  value::Str* str_val = nullptr;
  int arg;
  BigStr* msg = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&keyword);
  StackRoot _root2(&str_val);
  StackRoot _root3(&msg);

  keyword = node->keyword;
  if (node->arg_word) {
    str_val = this->word_ev->EvalWordToString(node->arg_word);
    if ((len(str_val->s) == 0 and !this->exec_opts->strict_control_flow())) {
      arg = 0;
    }
    else {
      try {
        arg = to_int(str_val->s);
      }
      catch (ValueError*) {
        e_die(StrFormat("%r expected a small integer, got %r", lexer::TokenVal(keyword), str_val->s), Alloc<loc::Word>(node->arg_word));
      }
    }
  }
  else {
    if ((keyword->id == Id::ControlFlow_Exit || keyword->id == Id::ControlFlow_Return)) {
      arg = this->mem->LastStatus();
    }
    else {
      arg = 1;
    }
  }
  this->tracer->OnControlFlow(consts::ControlFlowName(keyword->id), arg);
  if (((keyword->id == Id::ControlFlow_Break || keyword->id == Id::ControlFlow_Continue) and this->loop_level == 0)) {
    msg = S_pBu;
    if (this->exec_opts->strict_control_flow()) {
      e_die(msg, keyword);
    }
    else {
      this->errfmt->PrefixPrint(msg, S_jhf, keyword);
      return 0;
    }
  }
  if (keyword->id == Id::ControlFlow_Exit) {
    throw Alloc<util::UserExit>(arg);
  }
  else {
    throw Alloc<vm::IntControlFlow>(keyword, arg);
  }
}

int CommandEvaluator::_DoAndOr(command::AndOr* node, runtime_asdl::CommandStatus* cmd_st) {
  syntax_asdl::command_t* left = nullptr;
  int status;
  int i;
  int n;
  syntax_asdl::command_t* child = nullptr;
  syntax_asdl::Token* op = nullptr;
  int op_id;
  StackRoot _root0(&node);
  StackRoot _root1(&cmd_st);
  StackRoot _root2(&left);
  StackRoot _root3(&child);
  StackRoot _root4(&op);

  left = node->children->at(0);
  this->_StrictErrExit(left);
  {  // with
    state::ctx_ErrExit ctx{this->mutable_opts, false, node->ops->at(0)};

    status = this->_Execute(left);
  }
  i = 1;
  n = len(node->children);
  while (i < n) {
    child = node->children->at(i);
    op = node->ops->at((i - 1));
    op_id = op->id;
    if ((op_id == Id::Op_DPipe and status == 0)) {
      i += 1;
      continue;
    }
    else {
      if ((op_id == Id::Op_DAmp and status != 0)) {
        i += 1;
        continue;
      }
    }
    if (i == (n - 1)) {
      status = this->_Execute(child);
    }
    else {
      this->_StrictErrExit(child);
      {  // with
        state::ctx_ErrExit ctx{this->mutable_opts, false, op};

        status = this->_Execute(child);
      }
    }
    i += 1;
  }
  return status;
}

int CommandEvaluator::_DoWhileUntil(command::WhileUntil* node) {
  int status;
  bool b;
  runtime_asdl::flow_t action;
  StackRoot _root0(&node);

  status = 0;
  {  // with
    ctx_LoopLevel ctx{this};

    while (true) {
      try {
        b = this->_EvalCondition(node->cond, node->keyword);
        if (node->keyword->id == Id::KW_Until) {
          b = !b;
        }
        if (!b) {
          break;
        }
        status = this->_Execute(node->body);
      }
      catch (vm::IntControlFlow* e) {
        status = 0;
        action = e->HandleLoop();
        if (action == flow_e::Break) {
          break;
        }
        else {
          if (action == flow_e::Raise) {
            throw;
          }
        }
      }
    }
  }
  return status;
}

int CommandEvaluator::_DoForEach(command::ForEach* node) {
  List<BigStr*>* iter_list = nullptr;
  syntax_asdl::expr_t* iter_expr = nullptr;
  syntax_asdl::loc_t* expr_blame = nullptr;
  syntax_asdl::for_iter_t* iterable = nullptr;
  syntax_asdl::for_iter_t* UP_iterable = nullptr;
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  int n;
  value_asdl::LeftName* i_name = nullptr;
  value_asdl::LeftName* name1 = nullptr;
  value_asdl::LeftName* name2 = nullptr;
  val_ops::Iterator* it2 = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  int status;
  value_asdl::value_t* first = nullptr;
  runtime_asdl::flow_t action;
  StackRoot _root0(&node);
  StackRoot _root1(&iter_list);
  StackRoot _root2(&iter_expr);
  StackRoot _root3(&expr_blame);
  StackRoot _root4(&iterable);
  StackRoot _root5(&UP_iterable);
  StackRoot _root6(&words);
  StackRoot _root7(&i_name);
  StackRoot _root8(&name1);
  StackRoot _root9(&name2);
  StackRoot _root10(&it2);
  StackRoot _root11(&val);
  StackRoot _root12(&UP_val);
  StackRoot _root13(&first);

  iter_list = nullptr;
  iter_expr = nullptr;
  expr_blame = nullptr;
  iterable = node->iterable;
  UP_iterable = iterable;
  switch (node->iterable->tag()) {
    case for_iter_e::Args: {
      iter_list = this->mem->GetArgv();
    }
      break;
    case for_iter_e::Words: {
      for_iter::Words* iterable = static_cast<for_iter::Words*>(UP_iterable);
      words = braces::BraceExpandWords(iterable->words);
      iter_list = this->word_ev->EvalWordSequence(words);
    }
      break;
    case for_iter_e::YshExpr: {
      for_iter::YshExpr* iterable = static_cast<for_iter::YshExpr*>(UP_iterable);
      iter_expr = iterable->e;
      expr_blame = iterable->blame;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  n = len(node->iter_names);
  i_name = nullptr;
  name1 = nullptr;
  name2 = nullptr;
  it2 = nullptr;
  if (iter_expr) {
    val = this->expr_ev->EvalExpr(iter_expr, expr_blame);
    UP_val = val;
    switch (val->tag()) {
      case value_e::List: {
        value::List* val = static_cast<value::List*>(UP_val);
        it2 = Alloc<val_ops::ListIterator>(val);
        if (n == 1) {
          name1 = location::LName(node->iter_names->at(0));
        }
        else {
          if (n == 2) {
            i_name = location::LName(node->iter_names->at(0));
            name1 = location::LName(node->iter_names->at(1));
          }
          else {
            e_die_status(2, S_hFl, node->keyword);
          }
        }
      }
        break;
      case value_e::Dict: {
        value::Dict* val = static_cast<value::Dict*>(UP_val);
        it2 = Alloc<val_ops::DictIterator>(val);
        if (n == 1) {
          name1 = location::LName(node->iter_names->at(0));
        }
        else {
          if (n == 2) {
            name1 = location::LName(node->iter_names->at(0));
            name2 = location::LName(node->iter_names->at(1));
          }
          else {
            if (n == 3) {
              i_name = location::LName(node->iter_names->at(0));
              name1 = location::LName(node->iter_names->at(1));
              name2 = location::LName(node->iter_names->at(2));
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
      }
        break;
      case value_e::Range: {
        value::Range* val = static_cast<value::Range*>(UP_val);
        it2 = Alloc<val_ops::RangeIterator>(val);
        if (n == 1) {
          name1 = location::LName(node->iter_names->at(0));
        }
        else {
          if (n == 2) {
            i_name = location::LName(node->iter_names->at(0));
            name1 = location::LName(node->iter_names->at(1));
          }
          else {
            e_die_status(2, S_pix, node->keyword);
          }
        }
      }
        break;
      case value_e::Stdin: {
        it2 = Alloc<val_ops::StdinIterator>(expr_blame);
        if (n == 1) {
          name1 = location::LName(node->iter_names->at(0));
        }
        else {
          if (n == 2) {
            i_name = location::LName(node->iter_names->at(0));
            name1 = location::LName(node->iter_names->at(1));
          }
          else {
            e_die_status(2, S_ruu, node->keyword);
          }
        }
      }
        break;
      default: {
        throw Alloc<error::TypeErr>(val, S_Fmn, node->keyword);
      }
    }
  }
  else {
    it2 = Alloc<val_ops::ArrayIter>(iter_list);
    if (n == 1) {
      name1 = location::LName(node->iter_names->at(0));
    }
    else {
      if (n == 2) {
        i_name = location::LName(node->iter_names->at(0));
        name1 = location::LName(node->iter_names->at(1));
      }
      else {
        e_die_status(2, S_cFt, node->keyword);
      }
    }
  }
  status = 0;
  {  // with
    ctx_LoopLevel ctx{this};

    while (true) {
      {  // with
        state::ctx_LoopFrame ctx{this->mem, name1->name};

        first = it2->FirstValue();
        if (first == nullptr) {
          break;
        }
        if (first->tag() == value_e::Interrupted) {
          this->RunPendingTraps();
          continue;
        }
        this->mem->SetLocalName(name1, first);
        if (name2) {
          this->mem->SetLocalName(name2, it2->SecondValue());
        }
        if (i_name) {
          this->mem->SetLocalName(i_name, num::ToBig(it2->Index()));
        }
        it2->Next();
        try {
          status = this->_Execute(node->body);
        }
        catch (vm::IntControlFlow* e) {
          status = 0;
          action = e->HandleLoop();
          if (action == flow_e::Break) {
            break;
          }
          else {
            if (action == flow_e::Raise) {
              throw;
            }
          }
        }
      }
    }
  }
  return status;
}

int CommandEvaluator::_DoForExpr(command::ForExpr* node) {
  int status;
  syntax_asdl::arith_expr_t* init = nullptr;
  syntax_asdl::arith_expr_t* for_cond = nullptr;
  syntax_asdl::command_t* body = nullptr;
  syntax_asdl::arith_expr_t* update = nullptr;
  mops::BigInt cond_int;
  runtime_asdl::flow_t action;
  StackRoot _root0(&node);
  StackRoot _root1(&init);
  StackRoot _root2(&for_cond);
  StackRoot _root3(&body);
  StackRoot _root4(&update);

  status = 0;
  init = node->init;
  for_cond = node->cond;
  body = node->body;
  update = node->update;
  this->arith_ev->Eval(init);
  {  // with
    ctx_LoopLevel ctx{this};

    while (true) {
      cond_int = this->arith_ev->EvalToBigInt(for_cond);
      if (mops::Equal(cond_int, mops::ZERO)) {
        break;
      }
      try {
        status = this->_Execute(body);
      }
      catch (vm::IntControlFlow* e) {
        status = 0;
        action = e->HandleLoop();
        if (action == flow_e::Break) {
          break;
        }
        else {
          if (action == flow_e::Raise) {
            throw;
          }
        }
      }
      this->arith_ev->Eval(update);
    }
  }
  return status;
}

void CommandEvaluator::_DoShFunction(command::ShFunction* node) {
  value::Proc* sh_func = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&sh_func);

  sh_func = Alloc<value::Proc>(node->name, node->name_tok, proc_sig::Open, node->body, nullptr, true, this->mem->GlobalFrame());
  this->procs->DefineShellFunc(node->name, sh_func);
}

void CommandEvaluator::_DoProc(syntax_asdl::Proc* node) {
  BigStr* proc_name = nullptr;
  proc_sig::Closed* sig = nullptr;
  value_asdl::ProcDefaults* proc_defaults = nullptr;
  value::Proc* proc = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&proc_name);
  StackRoot _root2(&sig);
  StackRoot _root3(&proc_defaults);
  StackRoot _root4(&proc);

  proc_name = lexer::TokenVal(node->name);
  if (node->sig->tag() == proc_sig_e::Closed) {
    sig = static_cast<proc_sig::Closed*>(node->sig);
    proc_defaults = func_proc::EvalProcDefaults(this->expr_ev, sig);
  }
  else {
    proc_defaults = nullptr;
  }
  proc = Alloc<value::Proc>(proc_name, node->name, node->sig, node->body, proc_defaults, false, this->mem->GlobalFrame());
  this->procs->DefineProc(proc_name, proc);
}

void CommandEvaluator::_DoFunc(syntax_asdl::Func* node) {
  BigStr* name = nullptr;
  value_asdl::LeftName* lval = nullptr;
  List<value_asdl::value_t*>* pos_defaults = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_defaults = nullptr;
  value::Func* func_val = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&name);
  StackRoot _root2(&lval);
  StackRoot _root3(&pos_defaults);
  StackRoot _root4(&named_defaults);
  StackRoot _root5(&func_val);

  name = lexer::TokenVal(node->name);
  lval = location::LName(name);
  Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*> tup0 = func_proc::EvalFuncDefaults(this->expr_ev, node);
  pos_defaults = tup0.at0();
  named_defaults = tup0.at1();
  func_val = Alloc<value::Func>(name, node, pos_defaults, named_defaults, this->mem->GlobalFrame());
  this->mem->SetNamed(lval, func_val, scope_e::LocalOnly);
}

int CommandEvaluator::_DoIf(command::If* node) {
  int status;
  bool done;
  bool b;
  StackRoot _root0(&node);

  status = -1;
  done = false;
  for (ListIter<syntax_asdl::IfArm*> it(node->arms); !it.Done(); it.Next()) {
    syntax_asdl::IfArm* if_arm = it.Value();
    StackRoot _for(&if_arm  );
    b = this->_EvalCondition(if_arm->cond, if_arm->keyword);
    if (b) {
      status = this->_ExecuteList(if_arm->action);
      done = true;
      break;
    }
  }
  if ((!done and node->else_action != nullptr)) {
    status = this->_ExecuteList(node->else_action);
  }
  return status;
}

int CommandEvaluator::_DoCase(command::Case* node) {
  value_asdl::value_t* to_match = nullptr;
  int fnmatch_flags;
  int status;
  bool done;
  bool ignore_next_cond;
  value::Str* to_match_str = nullptr;
  pat::Words* pat_words = nullptr;
  bool this_arm_matches;
  value::Str* word_val = nullptr;
  int id_;
  pat::YshExprs* pat_exprs = nullptr;
  value_asdl::value_t* expr_val = nullptr;
  syntax_asdl::Eggex* eggex = nullptr;
  value::Eggex* eggex_val = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&to_match);
  StackRoot _root2(&to_match_str);
  StackRoot _root3(&pat_words);
  StackRoot _root4(&word_val);
  StackRoot _root5(&pat_exprs);
  StackRoot _root6(&expr_val);
  StackRoot _root7(&eggex);
  StackRoot _root8(&eggex_val);

  to_match = this->_EvalCaseArg(node->to_match, node->case_kw);
  fnmatch_flags = this->exec_opts->nocasematch() ? FNM_CASEFOLD : 0;
  status = 0;
  done = false;
  ignore_next_cond = false;
  for (ListIter<syntax_asdl::CaseArm*> it(node->arms); !it.Done(); it.Next()) {
    syntax_asdl::CaseArm* case_arm = it.Value();
    StackRoot _for(&case_arm  );
    switch (case_arm->pattern->tag()) {
      case pat_e::Words: {
        if (to_match->tag() != value_e::Str) {
          continue;
        }
        to_match_str = static_cast<value::Str*>(to_match);
        pat_words = static_cast<pat::Words*>(case_arm->pattern);
        this_arm_matches = false;
        if (ignore_next_cond) {
          this_arm_matches = true;
          ignore_next_cond = false;
        }
        else {
          for (ListIter<syntax_asdl::word_t*> it(pat_words->words); !it.Done(); it.Next()) {
            syntax_asdl::word_t* pat_word = it.Value();
            StackRoot _for(&pat_word          );
            word_val = this->word_ev->EvalWordToString(pat_word, word_eval::QUOTE_FNMATCH);
            if (libc::fnmatch(word_val->s, to_match_str->s, fnmatch_flags)) {
              this_arm_matches = true;
              break;
            }
          }
        }
        if (this_arm_matches) {
          status = this->_ExecuteList(case_arm->action);
          done = true;
          if (case_arm->right) {
            id_ = case_arm->right->id;
            if (id_ == Id::Op_SemiAmp) {
              ignore_next_cond = true;
              done = false;
            }
            else {
              if (id_ == Id::Op_DSemiAmp) {
                done = false;
              }
            }
          }
        }
      }
        break;
      case pat_e::YshExprs: {
        pat_exprs = static_cast<pat::YshExprs*>(case_arm->pattern);
        for (ListIter<syntax_asdl::expr_t*> it(pat_exprs->exprs); !it.Done(); it.Next()) {
          syntax_asdl::expr_t* pat_expr = it.Value();
          StackRoot _for(&pat_expr        );
          expr_val = this->expr_ev->EvalExpr(pat_expr, case_arm->left);
          if (val_ops::ExactlyEqual(expr_val, to_match, case_arm->left)) {
            status = this->_ExecuteList(case_arm->action);
            done = true;
            break;
          }
        }
      }
        break;
      case pat_e::Eggex: {
        eggex = static_cast<Eggex*>(case_arm->pattern);
        eggex_val = this->expr_ev->EvalEggex(eggex);
        if (val_ops::MatchRegex(to_match, eggex_val, this->mem)) {
          status = this->_ExecuteList(case_arm->action);
          done = true;
          break;
        }
      }
        break;
      case pat_e::Else: {
        status = this->_ExecuteList(case_arm->action);
        done = true;
        break;
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
    if (done) {
      break;
    }
  }
  return status;
}

int CommandEvaluator::_DoTimeBlock(command::TimeBlock* node) {
  double s_real;
  double s_user;
  double s_sys;
  int status;
  double e_real;
  double e_user;
  double e_sys;
  StackRoot _root0(&node);

  Tuple3<double, double, double> tup1 = pyos::Time();
  s_real = tup1.at0();
  s_user = tup1.at1();
  s_sys = tup1.at2();
  status = this->_Execute(node->pipeline);
  Tuple3<double, double, double> tup2 = pyos::Time();
  e_real = tup2.at0();
  e_user = tup2.at1();
  e_sys = tup2.at2();
  libc::print_time((e_real - s_real), (e_user - s_user), (e_sys - s_sys));
  return status;
}

int CommandEvaluator::_DoRedirect(command::Redirect* node, runtime_asdl::CommandStatus* cmd_st) {
  int status;
  List<runtime_asdl::RedirValue*>* redirects = nullptr;
  List<IOError_OSError*>* io_errors = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&cmd_st);
  StackRoot _root2(&redirects);
  StackRoot _root3(&io_errors);

  status = 0;
  redirects = Alloc<List<runtime_asdl::RedirValue*>>();
  try {
    for (ListIter<syntax_asdl::Redir*> it(node->redirects); !it.Done(); it.Next()) {
      syntax_asdl::Redir* redir = it.Value();
      StackRoot _for(&redir    );
      redirects->append(this->_EvalRedirect(redir));
    }
  }
  catch (error::RedirectEval* e) {
    this->errfmt->PrettyPrintError(e);
    redirects = nullptr;
  }
  catch (error::WordFailure* e) {
    if (!e->HasLocation()) {
      e->location = this->mem->GetFallbackLocation();
    }
    this->errfmt->PrettyPrintError(e);
    redirects = nullptr;
  }
  if (redirects == nullptr) {
    status = 1;
  }
  io_errors = Alloc<List<IOError_OSError*>>();
  if (status == 0) {
    this->shell_ex->PushRedirects(redirects, io_errors);
    if (len(io_errors)) {
      this->errfmt->PrintMessage(StrFormat("I/O error applying redirect: %s", pyutil::strerror(io_errors->at(0))), this->mem->GetFallbackLocation());
      status = 1;
    }
  }
  if (status == 0) {
    {  // with
      vm::ctx_Redirect ctx{this->shell_ex, len(redirects), io_errors};

      status = this->_Execute(node->child);
    }
    if (len(io_errors)) {
      e_die(StrFormat("Fatal error popping redirect: %s", pyutil::strerror(io_errors->at(0))));
    }
  }
  return status;
}

void CommandEvaluator::_LeafTick() {
  this->RunPendingTraps();
  if (this->signal_safe->PollUntrappedSigInt()) {
    throw Alloc<KeyboardInterrupt>();
  }
  mylib::MaybeCollect();
}

int CommandEvaluator::_Dispatch(syntax_asdl::command_t* node, runtime_asdl::CommandStatus* cmd_st) {
  syntax_asdl::command_t* UP_node = nullptr;
  int status;
  bool result;
  mops::BigInt i;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&cmd_st);
  StackRoot _root2(&UP_node);
  StackRoot _root3(&val);

  DTRACE_PROBE1(cmd_eval, _Dispatch, node->tag());
  this->check_command_sub_status = false;
  UP_node = node;
  switch (node->tag()) {
    case command_e::Simple: {
      command::Simple* node = static_cast<command::Simple*>(UP_node);
      if (node->blame_tok != nullptr) {
        this->mem->SetTokenForLine(node->blame_tok);
      }
      this->_MaybeRunDebugTrap();
      cmd_st->check_errexit = true;
      status = this->_DoSimple(node, cmd_st);
      this->_LeafTick();
    }
      break;
    case command_e::ShAssignment: {
      command::ShAssignment* node = static_cast<command::ShAssignment*>(UP_node);
      this->mem->SetTokenForLine(node->pairs->at(0)->left);
      this->_MaybeRunDebugTrap();
      status = this->_DoShAssignment(node, cmd_st);
      this->_LeafTick();
    }
      break;
    case command_e::Sentence: {
      command::Sentence* node = static_cast<command::Sentence*>(UP_node);
      if (node->terminator->id == Id::Op_Semi) {
        status = this->_Execute(node->child);
      }
      else {
        status = this->shell_ex->RunBackgroundJob(node->child);
      }
    }
      break;
    case command_e::DBracket: {
      command::DBracket* node = static_cast<command::DBracket*>(UP_node);
      this->mem->SetTokenForLine(node->left);
      this->_MaybeRunDebugTrap();
      this->tracer->PrintSourceCode(node->left, node->right, this->arena);
      cmd_st->check_errexit = true;
      cmd_st->show_code = true;
      result = this->bool_ev->EvalB(node->expr);
      status = result ? 0 : 1;
      this->_LeafTick();
    }
      break;
    case command_e::DParen: {
      command::DParen* node = static_cast<command::DParen*>(UP_node);
      this->mem->SetTokenForLine(node->left);
      this->_MaybeRunDebugTrap();
      this->tracer->PrintSourceCode(node->left, node->right, this->arena);
      cmd_st->check_errexit = true;
      cmd_st->show_code = true;
      i = this->arith_ev->EvalToBigInt(node->child);
      status = mops::Equal(i, mops::ZERO) ? 1 : 0;
      this->_LeafTick();
    }
      break;
    case command_e::ControlFlow: {
      command::ControlFlow* node = static_cast<command::ControlFlow*>(UP_node);
      this->mem->SetTokenForLine(node->keyword);
      this->_MaybeRunDebugTrap();
      status = this->_DoControlFlow(node);
    }
      break;
    case command_e::NoOp: {
      status = 0;
    }
      break;
    case command_e::VarDecl: {
      command::VarDecl* node = static_cast<command::VarDecl*>(UP_node);
      this->mem->SetTokenForLine(node->lhs->at(0)->left);
      status = this->_DoVarDecl(node);
      this->_LeafTick();
    }
      break;
    case command_e::Mutation: {
      command::Mutation* node = static_cast<command::Mutation*>(UP_node);
      this->mem->SetTokenForLine(node->keyword);
      this->_DoMutation(node);
      status = 0;
      this->_LeafTick();
    }
      break;
    case command_e::Expr: {
      command::Expr* node = static_cast<command::Expr*>(UP_node);
      this->mem->SetTokenForLine(node->keyword);
      status = this->_DoExpr(node);
      this->_LeafTick();
    }
      break;
    case command_e::Retval: {
      command::Retval* node = static_cast<command::Retval*>(UP_node);
      this->mem->SetTokenForLine(node->keyword);
      val = this->expr_ev->EvalExpr(node->val, node->keyword);
      this->_LeafTick();
      throw Alloc<vm::ValueControlFlow>(node->keyword, val);
    }
      break;
    case command_e::ExpandedAlias: {
      command::ExpandedAlias* node = static_cast<command::ExpandedAlias*>(UP_node);
      status = this->_DoExpandedAlias(node);
    }
      break;
    case command_e::CommandList: {
      command::CommandList* node = static_cast<command::CommandList*>(UP_node);
      status = this->_ExecuteList(node->children);
    }
      break;
    case command_e::DoGroup: {
      command::DoGroup* node = static_cast<command::DoGroup*>(UP_node);
      status = this->_ExecuteList(node->children);
    }
      break;
    case command_e::BraceGroup: {
      BraceGroup* node = static_cast<BraceGroup*>(UP_node);
      status = this->_ExecuteList(node->children);
    }
      break;
    case command_e::AndOr: {
      command::AndOr* node = static_cast<command::AndOr*>(UP_node);
      status = this->_DoAndOr(node, cmd_st);
    }
      break;
    case command_e::If: {
      command::If* node = static_cast<command::If*>(UP_node);
      this->_LeafTick();
      status = this->_DoIf(node);
    }
      break;
    case command_e::Case: {
      command::Case* node = static_cast<command::Case*>(UP_node);
      this->_LeafTick();
      this->mem->SetTokenForLine(node->case_kw);
      this->_MaybeRunDebugTrap();
      status = this->_DoCase(node);
    }
      break;
    case command_e::WhileUntil: {
      command::WhileUntil* node = static_cast<command::WhileUntil*>(UP_node);
      this->mem->SetTokenForLine(node->keyword);
      status = this->_DoWhileUntil(node);
    }
      break;
    case command_e::ForEach: {
      command::ForEach* node = static_cast<command::ForEach*>(UP_node);
      this->mem->SetTokenForLine(node->keyword);
      status = this->_DoForEach(node);
    }
      break;
    case command_e::ForExpr: {
      command::ForExpr* node = static_cast<command::ForExpr*>(UP_node);
      this->mem->SetTokenForLine(node->keyword);
      status = this->_DoForExpr(node);
    }
      break;
    case command_e::Redirect: {
      command::Redirect* node = static_cast<command::Redirect*>(UP_node);
      cmd_st->check_errexit = true;
      status = this->_DoRedirect(node, cmd_st);
    }
      break;
    case command_e::Pipeline: {
      command::Pipeline* node = static_cast<command::Pipeline*>(UP_node);
      status = this->_DoPipeline(node, cmd_st);
    }
      break;
    case command_e::Subshell: {
      command::Subshell* node = static_cast<command::Subshell*>(UP_node);
      cmd_st->check_errexit = true;
      if (node->is_last_cmd) {
        status = this->_Execute(node->child);
      }
      else {
        status = this->shell_ex->RunSubshell(node->child);
      }
    }
      break;
    case command_e::ShFunction: {
      command::ShFunction* node = static_cast<command::ShFunction*>(UP_node);
      this->_DoShFunction(node);
      status = 0;
    }
      break;
    case command_e::Proc: {
      Proc* node = static_cast<Proc*>(UP_node);
      this->_DoProc(node);
      status = 0;
    }
      break;
    case command_e::Func: {
      Func* node = static_cast<Func*>(UP_node);
      this->mem->SetTokenForLine(node->name);
      this->_DoFunc(node);
      status = 0;
    }
      break;
    case command_e::TimeBlock: {
      command::TimeBlock* node = static_cast<command::TimeBlock*>(UP_node);
      status = this->_DoTimeBlock(node);
    }
      break;
    default: {
      FAIL(kNotImplemented);  // Python NotImplementedError
    }
  }
  return status;
}

void CommandEvaluator::RunPendingTraps() {
  List<syntax_asdl::command_t*>* trap_nodes = nullptr;
  StackRoot _root0(&trap_nodes);

  trap_nodes = this->trap_state->GetPendingTraps();
  if (trap_nodes != nullptr) {
    {  // with
      state::ctx_Option ctx{this->mutable_opts, NewList<int>(std::initializer_list<int>{option_i::_running_trap}), true};

      for (ListIter<syntax_asdl::command_t*> it(trap_nodes); !it.Done(); it.Next()) {
        syntax_asdl::command_t* trap_node = it.Value();
        StackRoot _for(&trap_node      );
        {  // with
          state::ctx_Registers ctx{this->mem};

          {  // with
            dev::ctx_Tracer ctx{this->tracer, S_gFu, nullptr};

            this->_Execute(trap_node);
          }
        }
      }
    }
  }
}

void CommandEvaluator::RunPendingTrapsAndCatch() {
  List<syntax_asdl::command_t*>* trap_nodes = nullptr;
  StackRoot _root0(&trap_nodes);

  trap_nodes = this->trap_state->GetPendingTraps();
  if (trap_nodes != nullptr) {
    {  // with
      state::ctx_Option ctx{this->mutable_opts, NewList<int>(std::initializer_list<int>{option_i::_running_trap}), true};

      for (ListIter<syntax_asdl::command_t*> it(trap_nodes); !it.Done(); it.Next()) {
        syntax_asdl::command_t* trap_node = it.Value();
        StackRoot _for(&trap_node      );
        {  // with
          state::ctx_Registers ctx{this->mem};

          {  // with
            dev::ctx_Tracer ctx{this->tracer, S_gFu, nullptr};

            try {
              this->ExecuteAndCatch(trap_node, 0);
            }
            catch (util::UserExit*) {
              break;
            }
          }
        }
      }
    }
  }
}

int CommandEvaluator::_Execute(syntax_asdl::command_t* node) {
  runtime_asdl::CommandStatus* cmd_st = nullptr;
  runtime_asdl::StatusArray* process_sub_st = nullptr;
  int status;
  List<int>* pipe_status = nullptr;
  syntax_asdl::loc_t* errexit_loc = nullptr;
  int i;
  List<int>* codes = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&cmd_st);
  StackRoot _root2(&process_sub_st);
  StackRoot _root3(&pipe_status);
  StackRoot _root4(&errexit_loc);
  StackRoot _root5(&codes);

  cmd_st = CommandStatus::CreateNull();
  if (len(this->status_array_pool)) {
    process_sub_st = this->status_array_pool->pop();
  }
  else {
    process_sub_st = StatusArray::CreateNull();
  }
  {  // with
    vm::ctx_ProcessSub ctx{this->shell_ex, process_sub_st};

    try {
      status = this->_Dispatch(node, cmd_st);
    }
    catch (error::WordFailure* e) {
      if (!e->HasLocation()) {
        e->location = this->mem->GetFallbackLocation();
      }
      this->errfmt->PrettyPrintError(e);
      status = 1;
      cmd_st->check_errexit = true;
    }
  }
  pipe_status = cmd_st->pipe_status;
  errexit_loc = loc::Missing;
  if (pipe_status != nullptr) {
    this->mem->SetPipeStatus(pipe_status);
    if (this->exec_opts->pipefail()) {
      status = 0;
      i = 0;
      for (ListIter<int> it(pipe_status); !it.Done(); it.Next(), ++i) {
        int st = it.Value();
        if (st != 0) {
          status = st;
          errexit_loc = cmd_st->pipe_locs->at(i);
        }
      }
    }
    else {
      status = pipe_status->at(-1);
    }
    if (cmd_st->pipe_negated) {
      status = status == 0 ? 1 : 0;
    }
  }
  if (process_sub_st->codes == nullptr) {
    this->status_array_pool->append(process_sub_st);
  }
  else {
    codes = process_sub_st->codes;
    this->mem->SetProcessSubStatus(codes);
    if ((status == 0 and this->exec_opts->process_sub_fail())) {
      i = 0;
      for (ListIter<int> it(codes); !it.Done(); it.Next(), ++i) {
        int st = it.Value();
        if (st != 0) {
          status = st;
          errexit_loc = process_sub_st->locs->at(i);
        }
      }
    }
  }
  this->mem->SetLastStatus(status);
  if (cmd_st->check_errexit) {
    this->_CheckStatus(status, cmd_st, node, errexit_loc);
  }
  return status;
}

int CommandEvaluator::_ExecuteList(List<syntax_asdl::command_t*>* children) {
  int status;
  StackRoot _root0(&children);

  status = 0;
  for (ListIter<syntax_asdl::command_t*> it(children); !it.Done(); it.Next()) {
    syntax_asdl::command_t* child = it.Value();
    StackRoot _for(&child  );
    status = this->_Execute(child);
  }
  return status;
}

int CommandEvaluator::LastStatus() {
  return this->mem->LastStatus();
}

void CommandEvaluator::_MarkLastCommands(syntax_asdl::command_t* node) {
  syntax_asdl::command_t* UP_node = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);

  UP_node = node;
  switch (node->tag()) {
    case command_e::Simple: {
      command::Simple* node = static_cast<command::Simple*>(UP_node);
      node->is_last_cmd = true;
    }
      break;
    case command_e::Subshell: {
      command::Subshell* node = static_cast<command::Subshell*>(UP_node);
      node->is_last_cmd = true;
      this->_MarkLastCommands(node->child);
    }
      break;
    case command_e::Pipeline: {
      command::Pipeline* node = static_cast<command::Pipeline*>(UP_node);
      if ((node->negated == nullptr and !this->exec_opts->pipefail())) {
        this->_MarkLastCommands(node->children->at(-1));
      }
    }
      break;
    case command_e::Sentence: {
      command::Sentence* node = static_cast<command::Sentence*>(UP_node);
      this->_MarkLastCommands(node->child);
    }
      break;
    case command_e::Redirect: {
      command::Sentence* node = static_cast<command::Sentence*>(UP_node);
      this->_MarkLastCommands(node->child);
    }
      break;
    case command_e::CommandList: {
      command::CommandList* node = static_cast<command::CommandList*>(UP_node);
      this->_MarkLastCommands(node->children->at(-1));
    }
      break;
    case command_e::BraceGroup: {
      BraceGroup* node = static_cast<BraceGroup*>(UP_node);
      this->_MarkLastCommands(node->children->at(-1));
    }
      break;
  }
}

syntax_asdl::command_t* CommandEvaluator::_RemoveSubshells(syntax_asdl::command_t* node) {
  syntax_asdl::command_t* UP_node = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);

  UP_node = node;
  switch (node->tag()) {
    case command_e::Subshell: {
      command::Subshell* node = static_cast<command::Subshell*>(UP_node);
      return this->_RemoveSubshells(node->child);
    }
      break;
  }
  return node;
}

Tuple2<bool, bool> CommandEvaluator::ExecuteAndCatch(syntax_asdl::command_t* node, int cmd_flags) {
  bool is_return;
  bool is_fatal;
  bool is_errexit;
  error::FatalRuntime* err = nullptr;
  int status;
  List<int>* options = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&err);
  StackRoot _root2(&options);

  if ((cmd_flags & OptimizeSubshells)) {
    node = this->_RemoveSubshells(node);
  }
  if ((cmd_flags & MarkLastCommands)) {
    this->_MarkLastCommands(node);
  }
  is_return = false;
  is_fatal = false;
  is_errexit = false;
  err = nullptr;
  status = -1;
  try {
    options = Alloc<List<int>>();
    if ((cmd_flags & NoDebugTrap)) {
      options->append(option_i::_no_debug_trap);
    }
    if ((cmd_flags & NoErrTrap)) {
      options->append(option_i::_no_err_trap);
    }
    {  // with
      state::ctx_Option ctx{this->mutable_opts, options, true};

      status = this->_Execute(node);
    }
  }
  catch (vm::IntControlFlow* e) {
    if ((cmd_flags & RaiseControlFlow)) {
      throw;
    }
    else {
      if (e->IsReturn()) {
        is_return = true;
        status = e->StatusCode();
      }
      else {
        this->errfmt->Print_(S_Fnf, e->token);
        is_fatal = true;
        status = 1;
      }
    }
  }
  catch (error::Parse* e) {
    this->dumper->MaybeRecord(this, e);
    throw;
  }
  catch (error::ErrExit* e) {
    err = e;
    is_errexit = true;
  }
  catch (error::FatalRuntime* e) {
    err = e;
  }
  if (err) {
    status = err->ExitStatus();
    is_fatal = true;
    this->dumper->MaybeRecord(this, err);
    if (!err->HasLocation()) {
      err->location = this->mem->GetFallbackLocation();
    }
    if (is_errexit) {
      if (this->exec_opts->verbose_errexit()) {
        this->errfmt->PrintErrExit(static_cast<error::ErrExit*>(err), posix::getpid());
      }
    }
    else {
      this->errfmt->PrettyPrintError(err, S_Ect);
    }
  }
  this->dumper->MaybeDump(status);
  this->mem->SetLastStatus(status);
  return Tuple2<bool, bool>(is_return, is_fatal);
}

int CommandEvaluator::EvalCommandFrag(syntax_asdl::command_t* frag) {
  StackRoot _root0(&frag);

  return this->_Execute(frag);
}

void CommandEvaluator::RunTrapsOnExit(syntax_asdl::IntParamBox* mut_status) {
  syntax_asdl::command_t* node = nullptr;
  bool is_return;
  bool is_fatal;
  StackRoot _root0(&mut_status);
  StackRoot _root1(&node);

  this->RunPendingTrapsAndCatch();
  node = this->trap_state->GetHook(S_BDg);
  if (node) {
    {  // with
      dev::ctx_Tracer ctx{this->tracer, S_lEC, nullptr};

      try {
        Tuple2<bool, bool> tup3 = this->ExecuteAndCatch(node, 0);
        is_return = tup3.at0();
        is_fatal = tup3.at1();
      }
      catch (util::UserExit* e) {
        mut_status->i = e->status;
        return ;
      }
      if (is_return) {
        mut_status->i = this->LastStatus();
      }
    }
  }
}

void CommandEvaluator::_MaybeRunDebugTrap() {
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&node);

  node = this->trap_state->GetHook(S_Fzz);
  if (node == nullptr) {
    return ;
  }
  if (this->exec_opts->_no_debug_trap()) {
    return ;
  }
  if (!this->mem->ShouldRunDebugTrap()) {
    return ;
  }
  {  // with
    dev::ctx_Tracer ctx{this->tracer, S_Dph, nullptr};

    {  // with
      state::ctx_Registers ctx{this->mem};

      {  // with
        state::ctx_DebugTrap ctx{this->mem};

        this->_Execute(node);
      }
    }
  }
}

void CommandEvaluator::_MaybeRunErrTrap() {
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&node);

  node = this->trap_state->GetHook(S_zDr);
  if (node == nullptr) {
    return ;
  }
  if (this->exec_opts->_no_err_trap()) {
    return ;
  }
  if (this->mem->running_err_trap) {
    return ;
  }
  if (this->mutable_opts->ErrExitIsDisabled()) {
    return ;
  }
  if ((!this->exec_opts->errtrace() and this->mem->InsideFunction())) {
    return ;
  }
  {  // with
    dev::ctx_Tracer ctx{this->tracer, S_gAc, nullptr};

    {  // with
      state::ctx_ErrTrap ctx{this->mem};

      this->_Execute(node);
    }
  }
}

int CommandEvaluator::RunProc(value::Proc* proc, cmd_value::Argv* cmd_val) {
  syntax_asdl::proc_sig_t* sig = nullptr;
  List<BigStr*>* proc_argv = nullptr;
  int status;
  StackRoot _root0(&proc);
  StackRoot _root1(&cmd_val);
  StackRoot _root2(&sig);
  StackRoot _root3(&proc_argv);

  sig = proc->sig;
  if (sig->tag() == proc_sig_e::Closed) {
    proc_argv = Alloc<List<BigStr*>>();
  }
  else {
    proc_argv = cmd_val->argv->slice(1);
  }
  {  // with
    state::ctx_ProcCall ctx{this->mem, this->mutable_opts, proc, proc_argv};

    func_proc::BindProcArgs(proc, cmd_val, this->mem);
    try {
      status = this->_Execute(proc->body);
    }
    catch (vm::IntControlFlow* e) {
      if (e->IsReturn()) {
        status = e->StatusCode();
      }
      else {
        e_die(StrFormat("Unexpected %r (in proc call)", lexer::TokenVal(e->token)), e->token);
      }
    }
    catch (error::FatalRuntime* e) {
      this->dumper->MaybeRecord(this, e);
      throw;
    }
  }
  return status;
}

int CommandEvaluator::RunFuncForCompletion(value::Proc* proc, List<BigStr*>* argv) {
  cmd_value::Argv* cmd_val = nullptr;
  int status;
  StackRoot _root0(&proc);
  StackRoot _root1(&argv);
  StackRoot _root2(&cmd_val);

  cmd_val = MakeBuiltinArgv(argv);
  try {
    status = this->RunProc(proc, cmd_val);
  }
  catch (error::FatalRuntime* e) {
    this->errfmt->PrettyPrintError(e);
    status = e->ExitStatus();
  }
  catch (vm::IntControlFlow* e) {
    this->errfmt->Print_(S_tDq, e->token);
    status = 1;
  }
  return status;
}

}  // define namespace cmd_eval

namespace cmd_parse {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using id_kind_asdl::Id_str;
using id_kind_asdl::Kind;
using id_kind_asdl::Kind_str;
using types_asdl::lex_mode_e;
using types_asdl::cmd_mode_e;
using types_asdl::cmd_mode_t;
using syntax_asdl::loc;
using syntax_asdl::SourceLine;
using syntax_asdl::source;
using syntax_asdl::parse_result;
using syntax_asdl::parse_result_t;
using syntax_asdl::command;
using syntax_asdl::command_t;
using syntax_asdl::condition;
using syntax_asdl::condition_t;
using syntax_asdl::for_iter;
using syntax_asdl::ArgList;
using syntax_asdl::BraceGroup;
using syntax_asdl::CaseArm;
using syntax_asdl::case_arg;
using syntax_asdl::IfArm;
using syntax_asdl::pat;
using syntax_asdl::pat_t;
using syntax_asdl::Redir;
using syntax_asdl::redir_param;
using syntax_asdl::redir_loc;
using syntax_asdl::redir_loc_t;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::CompoundWord;
using syntax_asdl::Token;
using syntax_asdl::word_part_e;
using syntax_asdl::word_part_t;
using syntax_asdl::rhs_word;
using syntax_asdl::rhs_word_t;
using syntax_asdl::sh_lhs;
using syntax_asdl::sh_lhs_t;
using syntax_asdl::AssignPair;
using syntax_asdl::EnvPair;
using syntax_asdl::ParsedAssignment;
using syntax_asdl::assign_op_e;
using syntax_asdl::NameType;
using syntax_asdl::proc_sig;
using syntax_asdl::proc_sig_e;
using syntax_asdl::Proc;
using syntax_asdl::Func;
using syntax_asdl::SingleQuoted;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::List_of_command;
using value_asdl::LiteralBlock;
using error::p_die;
int TAB_CH = 9;
int SPACE_CH = 32;

Tuple2<List<Tuple2<syntax_asdl::SourceLine*, int>*>*, Tuple2<syntax_asdl::SourceLine*, int>*> _ReadHereLines(reader::_Reader* line_reader, syntax_asdl::Redir* h, BigStr* delimiter) {
  List<Tuple2<syntax_asdl::SourceLine*, int>*>* here_lines = nullptr;
  Tuple2<syntax_asdl::SourceLine*, int>* last_line = nullptr;
  bool strip_leading_tabs;
  syntax_asdl::SourceLine* src_line = nullptr;
  BigStr* line = nullptr;
  int start_offset;
  int n;
  int i;
  StackRoot _root0(&line_reader);
  StackRoot _root1(&h);
  StackRoot _root2(&delimiter);
  StackRoot _root3(&here_lines);
  StackRoot _root4(&last_line);
  StackRoot _root5(&src_line);
  StackRoot _root6(&line);

  here_lines = Alloc<List<Tuple2<syntax_asdl::SourceLine*, int>*>>();
  last_line = nullptr;
  strip_leading_tabs = h->op->id == Id::Redir_DLessDash;
  while (true) {
    Tuple2<syntax_asdl::SourceLine*, int> tup0 = line_reader->GetLine();
    src_line = tup0.at0();
    if (src_line == nullptr) {
      p_die(S_pss, h->op);
    }
    line = src_line->content;
    start_offset = 0;
    if (strip_leading_tabs) {
      n = len(line);
      i = 0;
      while (i < n) {
        if (!(str_equals(line->at(i), S_mve))) {
          break;
        }
        i += 1;
      }
      start_offset = i;
    }
    if (str_equals(line->slice(start_offset)->rstrip(), delimiter)) {
      last_line = (Alloc<Tuple2<syntax_asdl::SourceLine*, int>>(src_line, start_offset));
      break;
    }
    here_lines->append((Alloc<Tuple2<syntax_asdl::SourceLine*, int>>(src_line, start_offset)));
  }
  return Tuple2<List<Tuple2<syntax_asdl::SourceLine*, int>*>*, Tuple2<syntax_asdl::SourceLine*, int>*>(here_lines, last_line);
}

List<syntax_asdl::word_part_t*>* _MakeLiteralHereLines(List<Tuple2<syntax_asdl::SourceLine*, int>*>* here_lines, alloc::Arena* arena, bool do_lossless) {
  List<syntax_asdl::word_part_t*>* tokens = nullptr;
  syntax_asdl::Token* t = nullptr;
  StackRoot _root0(&here_lines);
  StackRoot _root1(&arena);
  StackRoot _root2(&tokens);
  StackRoot _root3(&t);

  tokens = Alloc<List<syntax_asdl::word_part_t*>>();
  for (ListIter<Tuple2<syntax_asdl::SourceLine*, int>*> it(here_lines); !it.Done(); it.Next()) {
    Tuple2<syntax_asdl::SourceLine*, int>* tup1 = it.Value();
    syntax_asdl::SourceLine* src_line = tup1->at0();
    StackRoot _unpack_0(&src_line);
    int start_offset = tup1->at1();
    if (do_lossless) {
      arena->NewToken(Id::Lit_CharsWithoutPrefix, start_offset, 0, src_line);
    }
    t = arena->NewToken(Id::Lit_Chars, start_offset, len(src_line->content), src_line);
    tokens->append(t);
  }
  return tokens;
}

void _ParseHereDocBody(parse_lib::ParseContext* parse_ctx, syntax_asdl::Redir* r, reader::_Reader* line_reader, alloc::Arena* arena) {
  redir_param::HereDoc* h = nullptr;
  bool ok;
  BigStr* delimiter = nullptr;
  bool delim_quoted;
  List<Tuple2<syntax_asdl::SourceLine*, int>*>* here_lines = nullptr;
  Tuple2<syntax_asdl::SourceLine*, int>* last_line = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  syntax_asdl::SourceLine* end_line = nullptr;
  int start_offset;
  StackRoot _root0(&parse_ctx);
  StackRoot _root1(&r);
  StackRoot _root2(&line_reader);
  StackRoot _root3(&arena);
  StackRoot _root4(&h);
  StackRoot _root5(&delimiter);
  StackRoot _root6(&here_lines);
  StackRoot _root7(&last_line);
  StackRoot _root8(&w_parser);
  StackRoot _root9(&end_line);

  h = static_cast<redir_param::HereDoc*>(r->arg);
  Tuple3<bool, BigStr*, bool> tup2 = word_::StaticEval(h->here_begin);
  ok = tup2.at0();
  delimiter = tup2.at1();
  delim_quoted = tup2.at2();
  if (!ok) {
    p_die(S_xco, Alloc<loc::Word>(h->here_begin));
  }
  Tuple2<List<Tuple2<syntax_asdl::SourceLine*, int>*>*, Tuple2<syntax_asdl::SourceLine*, int>*> tup3 = _ReadHereLines(line_reader, r, delimiter);
  here_lines = tup3.at0();
  last_line = tup3.at1();
  if (delim_quoted) {
    h->stdin_parts = _MakeLiteralHereLines(here_lines, arena, parse_ctx->do_lossless);
  }
  else {
    line_reader = Alloc<reader::VirtualLineReader>(arena, here_lines, parse_ctx->do_lossless);
    w_parser = parse_ctx->MakeWordParserForHereDoc(line_reader);
    w_parser->ReadHereDocBody(h->stdin_parts);
  }
  Tuple2<syntax_asdl::SourceLine*, int>* tup4 = last_line;
  end_line = tup4->at0();
  start_offset = tup4->at1();
  if (parse_ctx->do_lossless) {
    arena->NewToken(Id::Lit_CharsWithoutPrefix, start_offset, 0, end_line);
  }
  h->here_end_tok = arena->NewToken(Id::Undefined_Tok, start_offset, len(end_line->content), end_line);
}

syntax_asdl::AssignPair* _MakeAssignPair(parse_lib::ParseContext* parse_ctx, syntax_asdl::ParsedAssignment* preparsed, alloc::Arena* arena) {
  syntax_asdl::Token* left_token = nullptr;
  syntax_asdl::Token* close_token = nullptr;
  syntax_asdl::sh_lhs_t* lhs = nullptr;
  BigStr* var_name = nullptr;
  syntax_asdl::assign_op_t op;
  int left_pos;
  BigStr* index_str = nullptr;
  int s;
  BigStr* code_str = nullptr;
  tdop::TdopParser* a_parser = nullptr;
  source::Reparsed* src = nullptr;
  syntax_asdl::arith_expr_t* index_node = nullptr;
  List<syntax_asdl::word_part_t*>* parts = nullptr;
  int offset;
  int n;
  syntax_asdl::rhs_word_t* rhs = nullptr;
  syntax_asdl::CompoundWord* w = nullptr;
  StackRoot _root0(&parse_ctx);
  StackRoot _root1(&preparsed);
  StackRoot _root2(&arena);
  StackRoot _root3(&left_token);
  StackRoot _root4(&close_token);
  StackRoot _root5(&lhs);
  StackRoot _root6(&var_name);
  StackRoot _root7(&index_str);
  StackRoot _root8(&code_str);
  StackRoot _root9(&a_parser);
  StackRoot _root10(&src);
  StackRoot _root11(&index_node);
  StackRoot _root12(&parts);
  StackRoot _root13(&rhs);
  StackRoot _root14(&w);

  left_token = preparsed->left;
  close_token = preparsed->close;
  lhs = nullptr;
  if (left_token->id == Id::Lit_VarLike) {
    if (lexer::IsPlusEquals(left_token)) {
      var_name = lexer::TokenSliceRight(left_token, -2);
      op = assign_op_e::PlusEqual;
    }
    else {
      var_name = lexer::TokenSliceRight(left_token, -1);
      op = assign_op_e::Equal;
    }
    lhs = Alloc<sh_lhs::Name>(left_token, var_name);
  }
  else {
    if ((left_token->id == Id::Lit_ArrayLhsOpen and parse_ctx->do_lossless)) {
      var_name = lexer::TokenSliceRight(left_token, -1);
      if (lexer::IsPlusEquals(close_token)) {
        op = assign_op_e::PlusEqual;
      }
      else {
        op = assign_op_e::Equal;
      }
      left_pos = (left_token->col + left_token->length);
      index_str = left_token->line->content->slice(left_pos, close_token->col);
      lhs = Alloc<sh_lhs::UnparsedIndex>(left_token, var_name, index_str);
    }
    else {
      if (left_token->id == Id::Lit_ArrayLhsOpen) {
        var_name = lexer::TokenSliceRight(left_token, -1);
        if (lexer::IsPlusEquals(close_token)) {
          op = assign_op_e::PlusEqual;
        }
        else {
          op = assign_op_e::Equal;
        }
        if (left_token->line == close_token->line) {
          s = (left_token->col + left_token->length);
          code_str = left_token->line->content->slice(s, close_token->col);
        }
        else {
          FAIL(kNotImplemented);  // Python NotImplementedError
        }
        a_parser = parse_ctx->MakeArithParser(code_str);
        src = Alloc<source::Reparsed>(S_mqm, left_token, close_token);
        {  // with
          alloc::ctx_SourceCode ctx{arena, src};

          index_node = a_parser->Parse();
        }
        lhs = Alloc<sh_lhs::IndexedName>(left_token, var_name, index_node);
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  parts = preparsed->w->parts;
  offset = preparsed->part_offset;
  n = len(parts);
  if (offset == n) {
    rhs = rhs_word::Empty;
  }
  else {
    w = Alloc<CompoundWord>(parts->slice(offset));
    word_::TildeDetectAssign(w);
    rhs = w;
  }
  return Alloc<AssignPair>(left_token, lhs, op, rhs);
}

void _AppendMoreEnv(List<syntax_asdl::ParsedAssignment*>* preparsed_list, List<syntax_asdl::EnvPair*>* more_env) {
  syntax_asdl::Token* left_token = nullptr;
  BigStr* var_name = nullptr;
  List<syntax_asdl::word_part_t*>* parts = nullptr;
  int n;
  int offset;
  syntax_asdl::rhs_word_t* rhs = nullptr;
  syntax_asdl::CompoundWord* w = nullptr;
  StackRoot _root0(&preparsed_list);
  StackRoot _root1(&more_env);
  StackRoot _root2(&left_token);
  StackRoot _root3(&var_name);
  StackRoot _root4(&parts);
  StackRoot _root5(&rhs);
  StackRoot _root6(&w);

  for (ListIter<syntax_asdl::ParsedAssignment*> it(preparsed_list); !it.Done(); it.Next()) {
    syntax_asdl::ParsedAssignment* preparsed = it.Value();
    StackRoot _for(&preparsed  );
    left_token = preparsed->left;
    if (left_token->id != Id::Lit_VarLike) {
      p_die(S_pqq, left_token);
    }
    if (lexer::IsPlusEquals(left_token)) {
      p_die(S_amq, left_token);
    }
    var_name = lexer::TokenSliceRight(left_token, -1);
    parts = preparsed->w->parts;
    n = len(parts);
    offset = preparsed->part_offset;
    if (offset == n) {
      rhs = rhs_word::Empty;
    }
    else {
      w = Alloc<CompoundWord>(parts->slice(offset));
      word_::TildeDetectAssign(w);
      rhs = w;
    }
    more_env->append(Alloc<EnvPair>(left_token, var_name, rhs));
  }
}

Tuple2<List<syntax_asdl::ParsedAssignment*>*, List<syntax_asdl::CompoundWord*>*> _SplitSimpleCommandPrefix(List<syntax_asdl::CompoundWord*>* words) {
  List<syntax_asdl::ParsedAssignment*>* preparsed_list = nullptr;
  List<syntax_asdl::CompoundWord*>* suffix_words = nullptr;
  bool done_prefix;
  syntax_asdl::Token* left_token = nullptr;
  syntax_asdl::Token* close_token = nullptr;
  int part_offset;
  StackRoot _root0(&words);
  StackRoot _root1(&preparsed_list);
  StackRoot _root2(&suffix_words);
  StackRoot _root3(&left_token);
  StackRoot _root4(&close_token);

  preparsed_list = Alloc<List<syntax_asdl::ParsedAssignment*>>();
  suffix_words = Alloc<List<syntax_asdl::CompoundWord*>>();
  done_prefix = false;
  for (ListIter<syntax_asdl::CompoundWord*> it(words); !it.Done(); it.Next()) {
    syntax_asdl::CompoundWord* w = it.Value();
    StackRoot _for(&w  );
    if (done_prefix) {
      suffix_words->append(w);
      continue;
    }
    Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int> tup5 = word_::DetectShAssignment(w);
    left_token = tup5.at0();
    close_token = tup5.at1();
    part_offset = tup5.at2();
    if (left_token) {
      preparsed_list->append(Alloc<ParsedAssignment>(left_token, close_token, part_offset, w));
    }
    else {
      done_prefix = true;
      suffix_words->append(w);
    }
  }
  return Tuple2<List<syntax_asdl::ParsedAssignment*>*, List<syntax_asdl::CompoundWord*>*>(preparsed_list, suffix_words);
}

command::Simple* _MakeSimpleCommand(List<syntax_asdl::ParsedAssignment*>* preparsed_list, List<syntax_asdl::CompoundWord*>* suffix_words, syntax_asdl::ArgList* typed_args, value_asdl::LiteralBlock* block) {
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::Token* blame_tok = nullptr;
  List<syntax_asdl::word_t*>* words2 = nullptr;
  List<syntax_asdl::word_t*>* words3 = nullptr;
  List<syntax_asdl::EnvPair*>* more_env = nullptr;
  StackRoot _root0(&preparsed_list);
  StackRoot _root1(&suffix_words);
  StackRoot _root2(&typed_args);
  StackRoot _root3(&block);
  StackRoot _root4(&part0);
  StackRoot _root5(&blame_tok);
  StackRoot _root6(&words2);
  StackRoot _root7(&words3);
  StackRoot _root8(&more_env);

  for (ListIter<syntax_asdl::ParsedAssignment*> it(preparsed_list); !it.Done(); it.Next()) {
    syntax_asdl::ParsedAssignment* preparsed = it.Value();
    StackRoot _for(&preparsed  );
    if (word_::HasArrayPart(preparsed->w)) {
      p_die(S_fts, Alloc<loc::Word>(preparsed->w));
    }
  }
  part0 = suffix_words->at(0)->parts->at(0);
  blame_tok = location::LeftTokenForWordPart(part0);
  words2 = braces::BraceDetectAll(suffix_words);
  words3 = word_::TildeDetectAll(words2);
  more_env = Alloc<List<syntax_asdl::EnvPair*>>();
  _AppendMoreEnv(preparsed_list, more_env);
  return Alloc<command::Simple>(blame_tok, more_env, words3, typed_args, block, false);
}

VarChecker::VarChecker() {
  this->tokens = Alloc<List<syntax_asdl::Token*>>();
  this->names = Alloc<List<Dict<BigStr*, int>*>>();
}

void VarChecker::Push(syntax_asdl::Token* blame_tok) {
  Dict<BigStr*, int>* entry = nullptr;
  StackRoot _root0(&blame_tok);
  StackRoot _root1(&entry);

  if (len(this->tokens) != 0) {
    if ((this->tokens->at(0)->id != Id::KW_Proc && this->tokens->at(0)->id != Id::KW_Func)) {
      if (blame_tok->id == Id::KW_Proc) {
        p_die(S_fot_1, blame_tok);
      }
      if (blame_tok->id == Id::KW_Func) {
        p_die(S_pih, blame_tok);
      }
    }
    else {
      if ((blame_tok->id != Id::KW_Proc && blame_tok->id != Id::KW_Func)) {
        p_die(S_jlb, blame_tok);
      }
    }
  }
  this->tokens->append(blame_tok);
  entry = Alloc<Dict<BigStr*, int>>();
  this->names->append(entry);
}

void VarChecker::Pop() {
  this->names->pop();
  this->tokens->pop();
}

void VarChecker::Check(int keyword_id, BigStr* var_name, syntax_asdl::Token* blame_tok) {
  Dict<BigStr*, int>* top = nullptr;
  StackRoot _root0(&var_name);
  StackRoot _root1(&blame_tok);
  StackRoot _root2(&top);

  if (len(this->names) == 0) {
    return ;
  }
  top = this->names->at(-1);
  if (keyword_id == Id::KW_Var) {
    if (dict_contains(top, var_name)) {
      p_die(StrFormat("%r was already declared", var_name), blame_tok);
    }
    else {
      top->set(var_name, keyword_id);
    }
  }
  if (keyword_id == Id::KW_SetVar) {
    if (!dict_contains(top, var_name)) {
      p_die(StrFormat("setvar couldn't find matching 'var %s' (OILS-ERR-10)", var_name), blame_tok);
    }
  }
}

ctx_VarChecker::ctx_VarChecker(cmd_parse::VarChecker* var_checker, syntax_asdl::Token* blame_tok) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->var_checker)));
  var_checker->Push(blame_tok);
  this->var_checker = var_checker;
}

ctx_VarChecker::~ctx_VarChecker() {
  this->var_checker->Pop();
  gHeap.PopRoot();
}

ctx_CmdMode::ctx_CmdMode(cmd_parse::CommandParser* cmd_parse, types_asdl::cmd_mode_t new_cmd_mode) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->cmd_parse)));
  this->cmd_parse = cmd_parse;
  this->prev_cmd_mode = cmd_parse->cmd_mode;
  cmd_parse->cmd_mode = new_cmd_mode;
}

ctx_CmdMode::~ctx_CmdMode() {
  this->cmd_parse->cmd_mode = this->prev_cmd_mode;
  gHeap.PopRoot();
}
GLOBAL_LIST(SECONDARY_KEYWORDS, int, 7, {Id::KW_Do COMMA Id::KW_Done COMMA Id::KW_Then COMMA Id::KW_Fi COMMA Id::KW_Elif COMMA Id::KW_Else COMMA Id::KW_Esac});

CommandParser::CommandParser(parse_lib::ParseContext* parse_ctx, optview::Parse* parse_opts, word_parse::WordParser* w_parser, lexer::Lexer* lexer, reader::_Reader* line_reader, int eof_id) {
  this->parse_ctx = parse_ctx;
  this->aliases = parse_ctx->aliases;
  this->parse_opts = parse_opts;
  this->w_parser = w_parser;
  this->lexer = lexer;
  this->line_reader = line_reader;
  this->eof_id = eof_id;
  this->arena = line_reader->arena;
  this->aliases_in_flight = Alloc<List<Tuple2<BigStr*, int>*>>();
  this->allow_block = true;
  this->hay_attrs_stack = Alloc<List<bool>>();
  this->var_checker = Alloc<VarChecker>();
  this->cmd_mode = cmd_mode_e::Shell;
  this->Reset();
}

void CommandParser::Init_AliasesInFlight(List<Tuple2<BigStr*, int>*>* aliases_in_flight) {
  StackRoot _root0(&aliases_in_flight);

  this->aliases_in_flight = aliases_in_flight;
}

void CommandParser::Reset() {
  this->next_lex_mode = lex_mode_e::ShCommand;
  this->cur_word = nullptr;
  this->c_kind = Kind::Undefined;
  this->c_id = Id::Undefined_Tok;
  this->pending_here_docs = Alloc<List<syntax_asdl::Redir*>>();
}

void CommandParser::ResetInputObjects() {
  this->w_parser->Reset();
  this->lexer->ResetInputObjects();
  this->line_reader->Reset();
}

void CommandParser::_SetNext() {
  this->next_lex_mode = lex_mode_e::ShCommand;
}

void CommandParser::_SetNextBrack() {
  this->next_lex_mode = lex_mode_e::ShCommandFakeBrack;
}

void CommandParser::_GetWord() {
  syntax_asdl::word_t* w = nullptr;
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&tok);

  if (this->next_lex_mode != lex_mode_e::Undefined) {
    w = this->w_parser->ReadWord(this->next_lex_mode);
    if (w->tag() == word_e::Operator) {
      tok = static_cast<Token*>(w);
      if (tok->id == Id::Op_Newline) {
        for (ListIter<syntax_asdl::Redir*> it(this->pending_here_docs); !it.Done(); it.Next()) {
          syntax_asdl::Redir* h = it.Value();
          StackRoot _for(&h        );
          _ParseHereDocBody(this->parse_ctx, h, this->line_reader, this->arena);
        }
        this->pending_here_docs->clear();
      }
    }
    this->cur_word = w;
    this->c_kind = word_::CommandKind(this->cur_word);
    this->c_id = word_::CommandId(this->cur_word);
    this->next_lex_mode = lex_mode_e::Undefined;
  }
}

syntax_asdl::word_t* CommandParser::_Eat(int c_id, BigStr* msg) {
  syntax_asdl::word_t* skipped = nullptr;
  StackRoot _root0(&msg);
  StackRoot _root1(&skipped);

  this->_GetWord();
  if (this->c_id != c_id) {
    if (msg == nullptr) {
      msg = StrFormat("Expected word type %s, got %s", ui::PrettyId(c_id), ui::PrettyId(this->c_id));
    }
    p_die(msg, Alloc<loc::Word>(this->cur_word));
  }
  skipped = this->cur_word;
  this->_SetNext();
  return skipped;
}

void CommandParser::_NewlineOk() {
  this->_GetWord();
  if (this->c_id == Id::Op_Newline) {
    this->_SetNext();
  }
}

bool CommandParser::_AtSecondaryKeyword() {
  this->_GetWord();
  if (list_contains(SECONDARY_KEYWORDS, this->c_id)) {
    return true;
  }
  return false;
}

syntax_asdl::Redir* CommandParser::ParseRedirect() {
  syntax_asdl::Token* op_tok = nullptr;
  BigStr* op_val = nullptr;
  int pos;
  syntax_asdl::redir_loc_t* where = nullptr;
  redir_param::HereDoc* arg = nullptr;
  syntax_asdl::Redir* r = nullptr;
  syntax_asdl::CompoundWord* arg_word = nullptr;
  syntax_asdl::CompoundWord* tilde = nullptr;
  syntax_asdl::word_part_t* part0 = nullptr;
  bool is_multiline;
  syntax_asdl::SingleQuoted* sq = nullptr;
  syntax_asdl::DoubleQuoted* dq = nullptr;
  redir_param::HereWord* param = nullptr;
  StackRoot _root0(&op_tok);
  StackRoot _root1(&op_val);
  StackRoot _root2(&where);
  StackRoot _root3(&arg);
  StackRoot _root4(&r);
  StackRoot _root5(&arg_word);
  StackRoot _root6(&tilde);
  StackRoot _root7(&part0);
  StackRoot _root8(&sq);
  StackRoot _root9(&dq);
  StackRoot _root10(&param);

  this->_GetWord();
  op_tok = static_cast<Token*>(this->cur_word);
  op_val = lexer::TokenVal(op_tok);
  if (str_equals(op_val->at(0), S_ato)) {
    pos = op_val->find(S_cEn);
    where = Alloc<redir_loc::VarName>(op_val->slice(1, pos));
  }
  else {
    if (op_val->at(0)->isdigit()) {
      pos = 1;
      if (op_val->at(1)->isdigit()) {
        pos = 2;
      }
      where = Alloc<redir_loc::Fd>(to_int(op_val->slice(0, pos)));
    }
    else {
      where = Alloc<redir_loc::Fd>(consts::RedirDefaultFd(op_tok->id));
    }
  }
  this->_SetNext();
  this->_GetWord();
  if (this->c_kind != Kind::Word) {
    p_die(S_apz, Alloc<loc::Word>(this->cur_word));
  }
  if ((op_tok->id == Id::Redir_DLess || op_tok->id == Id::Redir_DLessDash)) {
    arg = redir_param::HereDoc::CreateNull();
    arg->here_begin = this->cur_word;
    arg->stdin_parts = Alloc<List<syntax_asdl::word_part_t*>>();
    r = Alloc<Redir>(op_tok, where, arg);
    this->pending_here_docs->append(r);
    this->_SetNext();
    return r;
  }
  arg_word = static_cast<CompoundWord*>(this->cur_word);
  tilde = word_::TildeDetect(arg_word);
  if (tilde) {
    arg_word = tilde;
  }
  this->_SetNext();
  if (op_tok->id == Id::Redir_TLess) {
    part0 = arg_word->parts->at(0);
    is_multiline = false;
    switch (part0->tag()) {
      case word_part_e::SingleQuoted: {
        sq = static_cast<SingleQuoted*>(part0);
        if ((sq->left->id == Id::Left_TSingleQuote || sq->left->id == Id::Left_RTSingleQuote || sq->left->id == Id::Left_UTSingleQuote || sq->left->id == Id::Left_BTSingleQuote)) {
          is_multiline = true;
        }
      }
        break;
      case word_part_e::DoubleQuoted: {
        dq = static_cast<DoubleQuoted*>(part0);
        if ((dq->left->id == Id::Left_TDoubleQuote || dq->left->id == Id::Left_DollarTDoubleQuote)) {
          is_multiline = true;
        }
      }
        break;
    }
    param = Alloc<redir_param::HereWord>(arg_word, is_multiline);
    return Alloc<Redir>(op_tok, where, param);
  }
  return Alloc<Redir>(op_tok, where, arg_word);
}

List<syntax_asdl::Redir*>* CommandParser::_ParseRedirectList() {
  List<syntax_asdl::Redir*>* redirects = nullptr;
  syntax_asdl::Redir* node = nullptr;
  StackRoot _root0(&redirects);
  StackRoot _root1(&node);

  redirects = Alloc<List<syntax_asdl::Redir*>>();
  while (true) {
    this->_GetWord();
    if (this->c_kind != Kind::Redir) {
      break;
    }
    node = this->ParseRedirect();
    redirects->append(node);
    this->_SetNext();
  }
  return redirects;
}

syntax_asdl::command_t* CommandParser::_MaybeParseRedirectList(syntax_asdl::command_t* node) {
  List<syntax_asdl::Redir*>* redirects = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&redirects);

  this->_GetWord();
  if (this->c_kind != Kind::Redir) {
    return node;
  }
  redirects = NewList<syntax_asdl::Redir*>(std::initializer_list<syntax_asdl::Redir*>{this->ParseRedirect()});
  while (true) {
    this->_GetWord();
    if (this->c_kind != Kind::Redir) {
      break;
    }
    redirects->append(this->ParseRedirect());
    this->_SetNext();
  }
  return Alloc<command::Redirect>(node, redirects);
}

Tuple4<List<syntax_asdl::Redir*>*, List<syntax_asdl::CompoundWord*>*, syntax_asdl::ArgList*, value_asdl::LiteralBlock*> CommandParser::_ScanSimpleCommand() {
  List<syntax_asdl::Redir*>* redirects = nullptr;
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  syntax_asdl::ArgList* typed_args = nullptr;
  value_asdl::LiteralBlock* block = nullptr;
  bool first_word_caps;
  int i;
  id_kind_asdl::Kind_t kind2;
  syntax_asdl::Redir* node = nullptr;
  syntax_asdl::CompoundWord* w = nullptr;
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::Token* tok = nullptr;
  bool ok;
  BigStr* word_str = nullptr;
  bool quoted;
  int prev_byte;
  int next_id;
  syntax_asdl::BraceGroup* brace_group = nullptr;
  List<syntax_asdl::SourceLine*>* lines = nullptr;
  StackRoot _root0(&redirects);
  StackRoot _root1(&words);
  StackRoot _root2(&typed_args);
  StackRoot _root3(&block);
  StackRoot _root4(&node);
  StackRoot _root5(&w);
  StackRoot _root6(&part0);
  StackRoot _root7(&tok);
  StackRoot _root8(&word_str);
  StackRoot _root9(&brace_group);
  StackRoot _root10(&lines);

  redirects = Alloc<List<syntax_asdl::Redir*>>();
  words = Alloc<List<syntax_asdl::CompoundWord*>>();
  typed_args = nullptr;
  block = nullptr;
  first_word_caps = false;
  i = 0;
  while (true) {
    this->_GetWord();
    kind2 = this->c_kind;
    if ((kind2 == Kind::Word and (this->parse_opts->parse_brace() and (this->c_id == Id::Lit_LBrace || this->c_id == Id::Lit_RBrace)))) {
      kind2 = Kind::Op;
    }
    if (kind2 == Kind::Redir) {
      node = this->ParseRedirect();
      redirects->append(node);
    }
    else {
      if (kind2 == Kind::Word) {
        w = static_cast<CompoundWord*>(this->cur_word);
        if (i == 0) {
          part0 = w->parts->at(0);
          if (part0->tag() == word_part_e::Literal) {
            tok = static_cast<Token*>(part0);
            if (tok->id == Id::Lit_Equals) {
              p_die(S_Fos, tok);
            }
          }
          Tuple3<bool, BigStr*, bool> tup6 = word_::StaticEval(w);
          ok = tup6.at0();
          word_str = tup6.at1();
          quoted = tup6.at2();
          if ((ok and (len(word_str) and (word_str->at(0)->isupper() and !word_str->isupper())))) {
            first_word_caps = true;
          }
        }
        words->append(w);
      }
      else {
        break;
      }
    }
    this->_SetNextBrack();
    i += 1;
  }
  this->_GetWord();
  if (this->c_id == Id::Op_LParen) {
    prev_byte = this->lexer->ByteLookBack();
    if ((prev_byte != SPACE_CH && prev_byte != TAB_CH)) {
      if (this->parse_opts->parse_at()) {
        p_die(S_ezD, Alloc<loc::Word>(this->cur_word));
      }
      else {
        p_die(S_bjp, Alloc<loc::Word>(this->cur_word));
      }
    }
    next_id = this->lexer->LookPastSpace(lex_mode_e::ShCommand);
    if (next_id == Id::Op_RParen) {
      p_die(S_ACD, Alloc<loc::Word>(this->cur_word));
    }
    typed_args = this->w_parser->ParseProcCallArgs(grammar_nt::ysh_eager_arglist);
    this->_SetNext();
  }
  else {
    if (this->c_id == Id::Op_LBracket) {
      typed_args = this->w_parser->ParseProcCallArgs(grammar_nt::ysh_lazy_arglist);
      this->_SetNext();
    }
  }
  this->_GetWord();
  if (this->c_kind == Kind::Redir) {
    redirects->extend(this->_ParseRedirectList());
  }
  if ((this->parse_opts->parse_brace() and (this->c_id == Id::Lit_LBrace and this->allow_block))) {
    this->hay_attrs_stack->append(first_word_caps);
    brace_group = this->ParseBraceGroup();
    lines = this->arena->SaveLinesAndDiscard(brace_group->left, brace_group->right);
    block = Alloc<LiteralBlock>(brace_group, lines);
    this->hay_attrs_stack->pop();
  }
  this->_GetWord();
  if (this->c_kind == Kind::Redir) {
    redirects->extend(this->_ParseRedirectList());
  }
  return Tuple4<List<syntax_asdl::Redir*>*, List<syntax_asdl::CompoundWord*>*, syntax_asdl::ArgList*, value_asdl::LiteralBlock*>(redirects, words, typed_args, block);
}

syntax_asdl::command_t* CommandParser::_MaybeExpandAliases(List<syntax_asdl::CompoundWord*>* words) {
  List<Tuple2<BigStr*, int>*>* aliases_in_flight = nullptr;
  BigStr* first_word_str = nullptr;
  loc::Word* argv0_loc = nullptr;
  List<BigStr*>* expanded = nullptr;
  int i;
  int n;
  syntax_asdl::CompoundWord* w = nullptr;
  bool ok;
  BigStr* word_str = nullptr;
  bool quoted;
  BigStr* alias_exp = nullptr;
  syntax_asdl::Token* left_tok = nullptr;
  syntax_asdl::Token* right_tok = nullptr;
  BigStr* words_str = nullptr;
  BigStr* code_str = nullptr;
  alloc::Arena* arena = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* cp = nullptr;
  source::Alias* src = nullptr;
  command::CommandList* node = nullptr;
  StackRoot _root0(&words);
  StackRoot _root1(&aliases_in_flight);
  StackRoot _root2(&first_word_str);
  StackRoot _root3(&argv0_loc);
  StackRoot _root4(&expanded);
  StackRoot _root5(&w);
  StackRoot _root6(&word_str);
  StackRoot _root7(&alias_exp);
  StackRoot _root8(&left_tok);
  StackRoot _root9(&right_tok);
  StackRoot _root10(&words_str);
  StackRoot _root11(&code_str);
  StackRoot _root12(&arena);
  StackRoot _root13(&line_reader);
  StackRoot _root14(&cp);
  StackRoot _root15(&src);
  StackRoot _root16(&node);

  aliases_in_flight = len(this->aliases_in_flight) ? this->aliases_in_flight : Alloc<List<Tuple2<BigStr*, int>*>>();
  first_word_str = nullptr;
  argv0_loc = Alloc<loc::Word>(words->at(0));
  expanded = Alloc<List<BigStr*>>();
  i = 0;
  n = len(words);
  while (i < n) {
    w = words->at(i);
    Tuple3<bool, BigStr*, bool> tup7 = word_::StaticEval(w);
    ok = tup7.at0();
    word_str = tup7.at1();
    quoted = tup7.at2();
    if ((!ok or quoted)) {
      break;
    }
    alias_exp = this->aliases->get(word_str);
    if (alias_exp == nullptr) {
      break;
    }
    if (list_contains(aliases_in_flight, (Alloc<Tuple2<BigStr*, int>>(word_str, i)))) {
      break;
    }
    if (i == 0) {
      first_word_str = word_str;
    }
    aliases_in_flight->append((Alloc<Tuple2<BigStr*, int>>(word_str, i)));
    expanded->append(alias_exp);
    i += 1;
    if (!alias_exp->endswith(S_yfw)) {
      expanded->append(S_yfw);
      break;
    }
  }
  if (len(expanded) == 0) {
    return nullptr;
  }
  if (i < n) {
    left_tok = location::LeftTokenForWord(words->at(i));
    right_tok = location::RightTokenForWord(words->at(-1));
    words_str = this->arena->SnipCodeString(left_tok, right_tok);
    expanded->append(words_str);
  }
  code_str = S_Aoo->join(expanded);
  arena = this->arena;
  line_reader = reader::StringLineReader(code_str, arena);
  cp = this->parse_ctx->MakeOshParser(line_reader);
  cp->Init_AliasesInFlight(aliases_in_flight);
  src = Alloc<source::Alias>(first_word_str, argv0_loc);
  {  // with
    alloc::ctx_SourceCode ctx{arena, src};

    {  // with
      parse_lib::ctx_Alias ctx{this->parse_ctx->trail};

      try {
        node = cp->_ParseCommandTerm();
      }
      catch (error::Parse* e) {
        throw;
      }
    }
  }
  return node;
}

syntax_asdl::command_t* CommandParser::ParseSimpleCommand() {
  List<syntax_asdl::Redir*>* redirects = nullptr;
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  syntax_asdl::ArgList* typed_args = nullptr;
  value_asdl::LiteralBlock* block = nullptr;
  syntax_asdl::Token* typed_loc = nullptr;
  List<syntax_asdl::ParsedAssignment*>* preparsed_list = nullptr;
  List<syntax_asdl::CompoundWord*>* suffix_words = nullptr;
  List<syntax_asdl::AssignPair*>* pairs = nullptr;
  syntax_asdl::Token* left_tok = nullptr;
  command::ShAssignment* assign_node = nullptr;
  id_kind_asdl::Kind_t kind;
  syntax_asdl::Token* kw_token = nullptr;
  syntax_asdl::word_t* arg_word = nullptr;
  syntax_asdl::command_t* expanded_node = nullptr;
  List<syntax_asdl::EnvPair*>* more_env = nullptr;
  command::ExpandedAlias* exp = nullptr;
  command::Simple* node = nullptr;
  StackRoot _root0(&redirects);
  StackRoot _root1(&words);
  StackRoot _root2(&typed_args);
  StackRoot _root3(&block);
  StackRoot _root4(&typed_loc);
  StackRoot _root5(&preparsed_list);
  StackRoot _root6(&suffix_words);
  StackRoot _root7(&pairs);
  StackRoot _root8(&left_tok);
  StackRoot _root9(&assign_node);
  StackRoot _root10(&kw_token);
  StackRoot _root11(&arg_word);
  StackRoot _root12(&expanded_node);
  StackRoot _root13(&more_env);
  StackRoot _root14(&exp);
  StackRoot _root15(&node);

  Tuple4<List<syntax_asdl::Redir*>*, List<syntax_asdl::CompoundWord*>*, syntax_asdl::ArgList*, value_asdl::LiteralBlock*> tup8 = this->_ScanSimpleCommand();
  redirects = tup8.at0();
  words = tup8.at1();
  typed_args = tup8.at2();
  block = tup8.at3();
  typed_loc = nullptr;
  if (block) {
    typed_loc = block->brace_group->left;
  }
  if (typed_args) {
    typed_loc = typed_args->left;
  }
  if (len(words) == 0) {
    if (typed_loc != nullptr) {
      p_die(S_meF, typed_loc);
    }
    return Alloc<command::Redirect>(command::NoOp, redirects);
  }
  Tuple2<List<syntax_asdl::ParsedAssignment*>*, List<syntax_asdl::CompoundWord*>*> tup9 = _SplitSimpleCommandPrefix(words);
  preparsed_list = tup9.at0();
  suffix_words = tup9.at1();
  if (len(preparsed_list)) {
    if (len(suffix_words) == 0) {
      if ((this->cmd_mode != cmd_mode_e::Shell or (len(this->hay_attrs_stack) and this->hay_attrs_stack->at(-1)))) {
        p_die(S_xih, preparsed_list->at(0)->left);
      }
    }
  }
  this->parse_ctx->trail->SetLatestWords(suffix_words, redirects);
  if (len(suffix_words) == 0) {
    if (typed_loc != nullptr) {
      p_die(S_meF, typed_loc);
    }
    pairs = Alloc<List<syntax_asdl::AssignPair*>>();
    for (ListIter<syntax_asdl::ParsedAssignment*> it(preparsed_list); !it.Done(); it.Next()) {
      syntax_asdl::ParsedAssignment* preparsed = it.Value();
      StackRoot _for(&preparsed    );
      pairs->append(_MakeAssignPair(this->parse_ctx, preparsed, this->arena));
    }
    left_tok = location::LeftTokenForCompoundWord(words->at(0));
    assign_node = Alloc<command::ShAssignment>(left_tok, pairs);
    if (len(redirects)) {
      return Alloc<command::Redirect>(assign_node, redirects);
    }
    else {
      return assign_node;
    }
  }
  Tuple2<id_kind_asdl::Kind_t, syntax_asdl::Token*> tup10 = word_::IsControlFlow(suffix_words->at(0));
  kind = tup10.at0();
  kw_token = tup10.at1();
  if (kind == Kind::ControlFlow) {
    if ((!this->parse_opts->parse_ignored() and len(redirects))) {
      p_die(S_Biw, kw_token);
    }
    if (len(preparsed_list)) {
      p_die(S_qlz, preparsed_list->at(0)->left);
    }
    if (kw_token->id == Id::ControlFlow_Return) {
      if (typed_args == nullptr) {
        if ((this->cmd_mode != cmd_mode_e::Shell && this->cmd_mode != cmd_mode_e::Proc)) {
          p_die(S_hur, kw_token);
        }
      }
      else {
        if (this->cmd_mode != cmd_mode_e::Func) {
          p_die(S_Bpo, typed_loc);
        }
        if (len(typed_args->pos_args) != 1) {
          p_die(S_jkf, typed_loc);
        }
        if (len(typed_args->named_args) != 0) {
          p_die(S_vuh, typed_loc);
        }
        if (typed_args->left->id != Id::Op_LParen) {
          p_die(S_kCu, typed_args->left);
        }
        return Alloc<command::Retval>(kw_token, typed_args->pos_args->at(0));
      }
    }
    if (typed_loc != nullptr) {
      p_die(S_meF, typed_loc);
    }
    if (len(suffix_words) == 1) {
      arg_word = nullptr;
    }
    else {
      if (len(suffix_words) == 2) {
        arg_word = suffix_words->at(1);
      }
      else {
        p_die(StrFormat("Unexpected argument to %r", lexer::TokenVal(kw_token)), Alloc<loc::Word>(suffix_words->at(2)));
      }
    }
    return Alloc<command::ControlFlow>(kw_token, arg_word);
  }
  if ((!typed_args and (!block and this->parse_opts->expand_aliases()))) {
    expanded_node = this->_MaybeExpandAliases(suffix_words);
    if (expanded_node) {
      more_env = Alloc<List<syntax_asdl::EnvPair*>>();
      _AppendMoreEnv(preparsed_list, more_env);
      exp = Alloc<command::ExpandedAlias>(expanded_node, more_env);
      if (len(redirects)) {
        return Alloc<command::Redirect>(exp, redirects);
      }
      else {
        return exp;
      }
    }
  }
  node = _MakeSimpleCommand(preparsed_list, suffix_words, typed_args, block);
  if (len(redirects)) {
    return Alloc<command::Redirect>(node, redirects);
  }
  else {
    return node;
  }
}

syntax_asdl::BraceGroup* CommandParser::ParseBraceGroup() {
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* left = nullptr;
  syntax_asdl::word_t* doc_word = nullptr;
  syntax_asdl::Token* doc_token = nullptr;
  command::CommandList* c_list = nullptr;
  syntax_asdl::Token* right = nullptr;
  StackRoot _root0(&ate);
  StackRoot _root1(&left);
  StackRoot _root2(&doc_word);
  StackRoot _root3(&doc_token);
  StackRoot _root4(&c_list);
  StackRoot _root5(&right);

  ate = this->_Eat(Id::Lit_LBrace);
  left = word_::BraceToken(ate);
  doc_word = nullptr;
  this->_GetWord();
  if (this->c_id == Id::Op_Newline) {
    this->_SetNext();
    {  // with
      word_::ctx_EmitDocToken ctx{this->w_parser};

      this->_GetWord();
    }
  }
  if (this->c_id == Id::Ignored_Comment) {
    doc_word = this->cur_word;
    this->_SetNext();
  }
  doc_token = static_cast<Token*>(doc_word);
  c_list = this->_ParseCommandList();
  ate = this->_Eat(Id::Lit_RBrace);
  right = word_::BraceToken(ate);
  return Alloc<BraceGroup>(left, doc_token, c_list->children, right);
}

command::DoGroup* CommandParser::ParseDoGroup() {
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* do_kw = nullptr;
  command::CommandList* c_list = nullptr;
  syntax_asdl::Token* done_kw = nullptr;
  StackRoot _root0(&ate);
  StackRoot _root1(&do_kw);
  StackRoot _root2(&c_list);
  StackRoot _root3(&done_kw);

  ate = this->_Eat(Id::KW_Do);
  do_kw = word_::AsKeywordToken(ate);
  c_list = this->_ParseCommandList();
  ate = this->_Eat(Id::KW_Done);
  done_kw = word_::AsKeywordToken(ate);
  return Alloc<command::DoGroup>(do_kw, c_list->children, done_kw);
}

Tuple2<List<syntax_asdl::CompoundWord*>*, syntax_asdl::Token*> CommandParser::ParseForWords() {
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  syntax_asdl::Token* semi_tok = nullptr;
  syntax_asdl::Token* tok = nullptr;
  syntax_asdl::CompoundWord* w2 = nullptr;
  StackRoot _root0(&words);
  StackRoot _root1(&semi_tok);
  StackRoot _root2(&tok);
  StackRoot _root3(&w2);

  words = Alloc<List<syntax_asdl::CompoundWord*>>();
  semi_tok = nullptr;
  while (true) {
    this->_GetWord();
    if (this->c_id == Id::Op_Semi) {
      tok = static_cast<Token*>(this->cur_word);
      semi_tok = tok;
      this->_SetNext();
      this->_NewlineOk();
      break;
    }
    else {
      if (this->c_id == Id::Op_Newline) {
        this->_SetNext();
        break;
      }
      else {
        if ((this->parse_opts->parse_brace() and this->c_id == Id::Lit_LBrace)) {
          break;
        }
      }
    }
    if (this->cur_word->tag() != word_e::Compound) {
      p_die(S_pnr, Alloc<loc::Word>(this->cur_word));
    }
    w2 = static_cast<CompoundWord*>(this->cur_word);
    words->append(w2);
    this->_SetNext();
  }
  return Tuple2<List<syntax_asdl::CompoundWord*>*, syntax_asdl::Token*>(words, semi_tok);
}

command::ForExpr* CommandParser::_ParseForExprLoop(syntax_asdl::Token* for_kw) {
  command::ForExpr* node = nullptr;
  StackRoot _root0(&for_kw);
  StackRoot _root1(&node);

  node = this->w_parser->ReadForExpression();
  node->keyword = for_kw;
  this->_SetNext();
  this->_GetWord();
  if (this->c_id == Id::Op_Semi) {
    this->_SetNext();
    this->_NewlineOk();
  }
  else {
    if (this->c_id == Id::Op_Newline) {
      this->_SetNext();
    }
    else {
      if (this->c_id == Id::KW_Do) {
        ;  // pass
      }
      else {
        if (this->c_id == Id::Lit_LBrace) {
          ;  // pass
        }
        else {
          p_die(S_kmo, Alloc<loc::Word>(this->cur_word));
        }
      }
    }
  }
  if (this->c_id == Id::Lit_LBrace) {
    node->body = this->ParseBraceGroup();
  }
  else {
    node->body = this->ParseDoGroup();
  }
  return node;
}

command::ForEach* CommandParser::_ParseForEachLoop(syntax_asdl::Token* for_kw) {
  command::ForEach* node = nullptr;
  int num_iter_names;
  syntax_asdl::word_t* w = nullptr;
  syntax_asdl::word_t* UP_w = nullptr;
  bool ok;
  BigStr* iter_name = nullptr;
  bool quoted;
  syntax_asdl::Token* expr_blame = nullptr;
  int next_id;
  syntax_asdl::expr_t* enode = nullptr;
  syntax_asdl::Token* semi_tok = nullptr;
  List<syntax_asdl::CompoundWord*>* iter_words = nullptr;
  BigStr* s = nullptr;
  List<syntax_asdl::word_t*>* words2 = nullptr;
  List<syntax_asdl::word_t*>* words3 = nullptr;
  StackRoot _root0(&for_kw);
  StackRoot _root1(&node);
  StackRoot _root2(&w);
  StackRoot _root3(&UP_w);
  StackRoot _root4(&iter_name);
  StackRoot _root5(&expr_blame);
  StackRoot _root6(&enode);
  StackRoot _root7(&semi_tok);
  StackRoot _root8(&iter_words);
  StackRoot _root9(&s);
  StackRoot _root10(&words2);
  StackRoot _root11(&words3);

  node = command::ForEach::CreateNull(true);
  node->keyword = for_kw;
  num_iter_names = 0;
  while (true) {
    w = this->cur_word;
    UP_w = w;
    if (w->tag() == word_e::Compound) {
      CompoundWord* w = static_cast<CompoundWord*>(UP_w);
      if (word_::LiteralId(w->parts->at(-1)) == Id::Lit_Comma) {
        w->parts->pop();
      }
    }
    Tuple3<bool, BigStr*, bool> tup11 = word_::StaticEval(w);
    ok = tup11.at0();
    iter_name = tup11.at1();
    quoted = tup11.at2();
    if ((!ok or quoted)) {
      p_die(S_nnd, Alloc<loc::Word>(w));
    }
    if (!match::IsValidVarName(iter_name)) {
      if (str_contains(iter_name, S_Cce)) {
        p_die(S_dwa, Alloc<loc::Word>(w));
      }
      p_die(StrFormat("Invalid loop variable name %r", iter_name), Alloc<loc::Word>(w));
    }
    node->iter_names->append(iter_name);
    num_iter_names += 1;
    this->_SetNext();
    this->_GetWord();
    if (((this->c_id == Id::KW_In || this->c_id == Id::KW_Do) or this->c_kind == Kind::Op)) {
      break;
    }
    if (num_iter_names == 3) {
      p_die(S_zfb, Alloc<loc::Word>(this->cur_word));
    }
  }
  this->_NewlineOk();
  this->_GetWord();
  if (this->c_id == Id::KW_In) {
    expr_blame = word_::AsKeywordToken(this->cur_word);
    this->_SetNext();
    next_id = this->w_parser->LookPastSpace();
    if (next_id == Id::Op_LParen) {
      enode = this->w_parser->ParseYshExprForCommand();
      node->iterable = Alloc<for_iter::YshExpr>(enode, expr_blame);
      this->_GetWord();
      if (this->c_id != Id::Lit_LBrace) {
        p_die(S_Fhm, Alloc<loc::Word>(this->cur_word));
      }
    }
    else {
      if (next_id == Id::Redir_LessGreat) {
        w = this->_Eat(Id::Redir_LessGreat);
        p_die(S_cEx, Alloc<loc::Word>(this->cur_word));
      }
      else {
        if (next_id == Id::Redir_Less) {
          w = this->_Eat(Id::Redir_Less);
          p_die(S_cEx, Alloc<loc::Word>(this->cur_word));
        }
        else {
          semi_tok = nullptr;
          Tuple2<List<syntax_asdl::CompoundWord*>*, syntax_asdl::Token*> tup12 = this->ParseForWords();
          iter_words = tup12.at0();
          semi_tok = tup12.at1();
          node->semi_tok = semi_tok;
          if ((!this->parse_opts->parse_bare_word() and len(iter_words) == 1)) {
            Tuple3<bool, BigStr*, bool> tup13 = word_::StaticEval(iter_words->at(0));
            ok = tup13.at0();
            s = tup13.at1();
            quoted = tup13.at2();
            if ((ok and (match::IsValidVarName(s) and !quoted))) {
              p_die(S_BpF, Alloc<loc::Word>(iter_words->at(0)));
            }
          }
          words2 = braces::BraceDetectAll(iter_words);
          words3 = word_::TildeDetectAll(words2);
          node->iterable = Alloc<for_iter::Words>(words3);
          if (num_iter_names > 2) {
            p_die(S_yDB_1, for_kw);
          }
        }
      }
    }
  }
  else {
    if (this->c_id == Id::KW_Do) {
      node->iterable = for_iter::Args;
    }
    else {
      if (this->c_id == Id::Op_Semi) {
        node->iterable = for_iter::Args;
        this->_SetNext();
      }
      else {
        p_die(S_cAo, Alloc<loc::Word>(this->cur_word));
      }
    }
  }
  this->_GetWord();
  if (this->c_id == Id::Lit_LBrace) {
    node->body = this->ParseBraceGroup();
  }
  else {
    node->body = this->ParseDoGroup();
  }
  return node;
}

syntax_asdl::command_t* CommandParser::ParseFor() {
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* for_kw = nullptr;
  command::ForExpr* n1 = nullptr;
  command::ForEach* n2 = nullptr;
  StackRoot _root0(&ate);
  StackRoot _root1(&for_kw);
  StackRoot _root2(&n1);
  StackRoot _root3(&n2);

  ate = this->_Eat(Id::KW_For);
  for_kw = word_::AsKeywordToken(ate);
  this->_GetWord();
  if (this->c_id == Id::Op_DLeftParen) {
    if (!this->parse_opts->parse_dparen()) {
      p_die(S_laa, Alloc<loc::Word>(this->cur_word));
    }
    n1 = this->_ParseForExprLoop(for_kw);
    return this->_MaybeParseRedirectList(n1);
  }
  else {
    n2 = this->_ParseForEachLoop(for_kw);
    return this->_MaybeParseRedirectList(n2);
  }
}

syntax_asdl::condition_t* CommandParser::_ParseConditionList() {
  command::CommandList* commands = nullptr;
  StackRoot _root0(&commands);

  this->allow_block = false;
  commands = this->_ParseCommandList();
  this->allow_block = true;
  if (len(commands->children) == 0) {
    p_die(S_xDn, Alloc<loc::Word>(this->cur_word));
  }
  return List_of_command::Take(commands->children);
}

command::WhileUntil* CommandParser::ParseWhileUntil(syntax_asdl::Token* keyword) {
  syntax_asdl::expr_t* enode = nullptr;
  syntax_asdl::condition_t* cond = nullptr;
  syntax_asdl::command_t* body_node = nullptr;
  StackRoot _root0(&keyword);
  StackRoot _root1(&enode);
  StackRoot _root2(&cond);
  StackRoot _root3(&body_node);

  this->_SetNext();
  if ((this->parse_opts->parse_paren() and this->w_parser->LookPastSpace() == Id::Op_LParen)) {
    enode = this->w_parser->ParseYshExprForCommand();
    cond = Alloc<condition::YshExpr>(enode);
  }
  else {
    cond = this->_ParseConditionList();
  }
  this->_GetWord();
  if ((this->parse_opts->parse_brace() and this->c_id == Id::Lit_LBrace)) {
    body_node = this->ParseBraceGroup();
  }
  else {
    body_node = this->ParseDoGroup();
  }
  return Alloc<command::WhileUntil>(keyword, cond, body_node);
}

syntax_asdl::CaseArm* CommandParser::ParseCaseArm() {
  syntax_asdl::Token* left_tok = nullptr;
  List<syntax_asdl::word_t*>* pat_words = nullptr;
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* middle_tok = nullptr;
  command::CommandList* c_list = nullptr;
  List<syntax_asdl::command_t*>* action_children = nullptr;
  syntax_asdl::Token* dsemi_tok = nullptr;
  StackRoot _root0(&left_tok);
  StackRoot _root1(&pat_words);
  StackRoot _root2(&ate);
  StackRoot _root3(&middle_tok);
  StackRoot _root4(&c_list);
  StackRoot _root5(&action_children);
  StackRoot _root6(&dsemi_tok);

  this->lexer->PushHint(Id::Op_RParen, Id::Right_CasePat);
  left_tok = location::LeftTokenForWord(this->cur_word);
  if (this->c_id == Id::Op_LParen) {
    this->_SetNext();
  }
  pat_words = Alloc<List<syntax_asdl::word_t*>>();
  while (true) {
    this->_GetWord();
    if (this->c_kind != Kind::Word) {
      p_die(S_vky, Alloc<loc::Word>(this->cur_word));
    }
    pat_words->append(this->cur_word);
    this->_SetNext();
    this->_GetWord();
    if (this->c_id == Id::Op_Pipe) {
      this->_SetNext();
    }
    else {
      break;
    }
  }
  ate = this->_Eat(Id::Right_CasePat);
  middle_tok = word_::AsOperatorToken(ate);
  this->_NewlineOk();
  this->_GetWord();
  if ((this->c_id != Id::Op_DSemi && this->c_id != Id::Op_SemiAmp && this->c_id != Id::Op_DSemiAmp && this->c_id != Id::KW_Esac)) {
    c_list = this->_ParseCommandTerm();
    action_children = c_list->children;
  }
  else {
    action_children = Alloc<List<syntax_asdl::command_t*>>();
  }
  dsemi_tok = nullptr;
  this->_GetWord();
  if (this->c_id == Id::KW_Esac) {
    ;  // pass
  }
  else {
    if ((this->c_id == Id::Op_DSemi || this->c_id == Id::Op_SemiAmp || this->c_id == Id::Op_DSemiAmp)) {
      dsemi_tok = word_::AsOperatorToken(this->cur_word);
      this->_SetNext();
    }
    else {
      p_die(S_qsa, Alloc<loc::Word>(this->cur_word));
    }
  }
  this->_NewlineOk();
  return Alloc<CaseArm>(left_tok, Alloc<pat::Words>(pat_words), middle_tok, action_children, dsemi_tok);
}

syntax_asdl::CaseArm* CommandParser::ParseYshCaseArm(int discriminant) {
  syntax_asdl::Token* left_tok = nullptr;
  syntax_asdl::pat_t* pattern = nullptr;
  List<syntax_asdl::word_t*>* pat_words = nullptr;
  syntax_asdl::BraceGroup* action = nullptr;
  StackRoot _root0(&left_tok);
  StackRoot _root1(&pattern);
  StackRoot _root2(&pat_words);
  StackRoot _root3(&action);

  left_tok = nullptr;
  pattern = nullptr;
  if ((discriminant == Id::Op_LParen || discriminant == Id::Arith_Slash)) {
    Tuple2<syntax_asdl::pat_t*, syntax_asdl::Token*> tup14 = this->w_parser->ParseYshCasePattern();
    pattern = tup14.at0();
    left_tok = tup14.at1();
  }
  else {
    pat_words = Alloc<List<syntax_asdl::word_t*>>();
    while (true) {
      this->_GetWord();
      if (this->c_kind != Kind::Word) {
        p_die(S_vky, Alloc<loc::Word>(this->cur_word));
      }
      pat_words->append(this->cur_word);
      this->_SetNext();
      if (!left_tok) {
        left_tok = location::LeftTokenForWord(this->cur_word);
      }
      this->_NewlineOk();
      this->_GetWord();
      if (this->c_id == Id::Op_Pipe) {
        this->_SetNext();
        this->_NewlineOk();
      }
      else {
        break;
      }
    }
    pattern = Alloc<pat::Words>(pat_words);
  }
  this->_NewlineOk();
  action = this->ParseBraceGroup();
  return Alloc<CaseArm>(left_tok, pattern, action->left, action->children, action->right);
}

command::Case* CommandParser::ParseYshCase(syntax_asdl::Token* case_kw) {
  syntax_asdl::expr_t* enode = nullptr;
  case_arg::YshExpr* to_match = nullptr;
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* arms_start = nullptr;
  int discriminant;
  List<syntax_asdl::CaseArm*>* arms = nullptr;
  syntax_asdl::CaseArm* arm = nullptr;
  syntax_asdl::Token* arms_end = nullptr;
  StackRoot _root0(&case_kw);
  StackRoot _root1(&enode);
  StackRoot _root2(&to_match);
  StackRoot _root3(&ate);
  StackRoot _root4(&arms_start);
  StackRoot _root5(&arms);
  StackRoot _root6(&arm);
  StackRoot _root7(&arms_end);

  enode = this->w_parser->ParseYshExprForCommand();
  to_match = Alloc<case_arg::YshExpr>(enode);
  ate = this->_Eat(Id::Lit_LBrace);
  arms_start = word_::BraceToken(ate);
  discriminant = this->w_parser->NewlineOkForYshCase();
  arms = Alloc<List<syntax_asdl::CaseArm*>>();
  while (discriminant != Id::Op_RBrace) {
    arm = this->ParseYshCaseArm(discriminant);
    arms->append(arm);
    discriminant = this->w_parser->NewlineOkForYshCase();
  }
  ate = this->_Eat(Id::Op_RBrace);
  arms_end = word_::AsOperatorToken(ate);
  arms_end->id = Id::Lit_RBrace;
  return Alloc<command::Case>(case_kw, to_match, arms_start, arms, arms_end);
}

command::Case* CommandParser::ParseOldCase(syntax_asdl::Token* case_kw) {
  syntax_asdl::word_t* w = nullptr;
  bool ok;
  BigStr* s = nullptr;
  bool quoted;
  case_arg::Word* to_match = nullptr;
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* arms_start = nullptr;
  List<syntax_asdl::CaseArm*>* arms = nullptr;
  syntax_asdl::CaseArm* arm = nullptr;
  syntax_asdl::Token* arms_end = nullptr;
  StackRoot _root0(&case_kw);
  StackRoot _root1(&w);
  StackRoot _root2(&s);
  StackRoot _root3(&to_match);
  StackRoot _root4(&ate);
  StackRoot _root5(&arms_start);
  StackRoot _root6(&arms);
  StackRoot _root7(&arm);
  StackRoot _root8(&arms_end);

  this->_GetWord();
  w = this->cur_word;
  if (!this->parse_opts->parse_bare_word()) {
    Tuple3<bool, BigStr*, bool> tup15 = word_::StaticEval(w);
    ok = tup15.at0();
    s = tup15.at1();
    quoted = tup15.at2();
    if ((ok and !quoted)) {
      p_die(S_tjF, Alloc<loc::Word>(w));
    }
  }
  if (w->tag() != word_e::Compound) {
    p_die(S_brv, Alloc<loc::Word>(w));
  }
  to_match = Alloc<case_arg::Word>(w);
  this->_SetNext();
  this->_NewlineOk();
  ate = this->_Eat(Id::KW_In);
  arms_start = word_::AsKeywordToken(ate);
  this->_NewlineOk();
  arms = Alloc<List<syntax_asdl::CaseArm*>>();
  while (true) {
    this->_GetWord();
    if (this->c_id == Id::KW_Esac) {
      break;
    }
    if ((this->c_kind != Kind::Word and this->c_id != Id::Op_LParen)) {
      break;
    }
    arm = this->ParseCaseArm();
    arms->append(arm);
  }
  ate = this->_Eat(Id::KW_Esac);
  arms_end = word_::AsKeywordToken(ate);
  return Alloc<command::Case>(case_kw, to_match, arms_start, arms, arms_end);
}

command::Case* CommandParser::ParseCase() {
  syntax_asdl::Token* case_kw = nullptr;
  StackRoot _root0(&case_kw);

  case_kw = word_::AsKeywordToken(this->cur_word);
  this->_SetNext();
  if (this->w_parser->LookPastSpace() == Id::Op_LParen) {
    return this->ParseYshCase(case_kw);
  }
  else {
    return this->ParseOldCase(case_kw);
  }
}

void CommandParser::_ParseYshElifElse(command::If* if_node) {
  List<syntax_asdl::IfArm*>* arms = nullptr;
  syntax_asdl::Token* elif_kw = nullptr;
  syntax_asdl::expr_t* enode = nullptr;
  syntax_asdl::condition_t* cond = nullptr;
  command::CommandList* commands = nullptr;
  syntax_asdl::BraceGroup* body = nullptr;
  syntax_asdl::IfArm* arm = nullptr;
  StackRoot _root0(&if_node);
  StackRoot _root1(&arms);
  StackRoot _root2(&elif_kw);
  StackRoot _root3(&enode);
  StackRoot _root4(&cond);
  StackRoot _root5(&commands);
  StackRoot _root6(&body);
  StackRoot _root7(&arm);

  arms = if_node->arms;
  while (this->c_id == Id::KW_Elif) {
    elif_kw = word_::AsKeywordToken(this->cur_word);
    this->_SetNext();
    if ((this->parse_opts->parse_paren() and this->w_parser->LookPastSpace() == Id::Op_LParen)) {
      enode = this->w_parser->ParseYshExprForCommand();
      cond = Alloc<condition::YshExpr>(enode);
    }
    else {
      this->allow_block = false;
      commands = this->_ParseCommandList();
      this->allow_block = true;
      cond = List_of_command::Take(commands->children);
    }
    body = this->ParseBraceGroup();
    this->_GetWord();
    arm = Alloc<IfArm>(elif_kw, cond, nullptr, body->children, nullptr);
    arms->append(arm);
  }
  this->_GetWord();
  if (this->c_id == Id::KW_Else) {
    this->_SetNext();
    body = this->ParseBraceGroup();
    if_node->else_action = body->children;
  }
}

command::If* CommandParser::_ParseYshIf(syntax_asdl::Token* if_kw, syntax_asdl::condition_t* cond) {
  command::If* if_node = nullptr;
  syntax_asdl::BraceGroup* body1 = nullptr;
  syntax_asdl::IfArm* arm = nullptr;
  StackRoot _root0(&if_kw);
  StackRoot _root1(&cond);
  StackRoot _root2(&if_node);
  StackRoot _root3(&body1);
  StackRoot _root4(&arm);

  if_node = command::If::CreateNull(true);
  if_node->if_kw = if_kw;
  body1 = this->ParseBraceGroup();
  arm = Alloc<IfArm>(if_kw, cond, nullptr, body1->children, nullptr);
  if_node->arms->append(arm);
  this->_GetWord();
  if ((this->c_id == Id::KW_Elif || this->c_id == Id::KW_Else)) {
    this->_ParseYshElifElse(if_node);
  }
  return if_node;
}

void CommandParser::_ParseElifElse(command::If* if_node) {
  List<syntax_asdl::IfArm*>* arms = nullptr;
  syntax_asdl::Token* elif_kw = nullptr;
  syntax_asdl::condition_t* cond = nullptr;
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* then_kw = nullptr;
  command::CommandList* body = nullptr;
  syntax_asdl::IfArm* arm = nullptr;
  syntax_asdl::Token* else_kw = nullptr;
  StackRoot _root0(&if_node);
  StackRoot _root1(&arms);
  StackRoot _root2(&elif_kw);
  StackRoot _root3(&cond);
  StackRoot _root4(&ate);
  StackRoot _root5(&then_kw);
  StackRoot _root6(&body);
  StackRoot _root7(&arm);
  StackRoot _root8(&else_kw);

  arms = if_node->arms;
  this->_GetWord();
  while (this->c_id == Id::KW_Elif) {
    elif_kw = word_::AsKeywordToken(this->cur_word);
    this->_SetNext();
    cond = this->_ParseConditionList();
    ate = this->_Eat(Id::KW_Then);
    then_kw = word_::AsKeywordToken(ate);
    body = this->_ParseCommandList();
    arm = Alloc<IfArm>(elif_kw, cond, then_kw, body->children, then_kw);
    arms->append(arm);
  }
  this->_GetWord();
  if (this->c_id == Id::KW_Else) {
    else_kw = word_::AsKeywordToken(this->cur_word);
    this->_SetNext();
    body = this->_ParseCommandList();
    if_node->else_action = body->children;
  }
  else {
    else_kw = nullptr;
  }
  if_node->else_kw = else_kw;
}

command::If* CommandParser::ParseIf() {
  command::If* if_node = nullptr;
  syntax_asdl::Token* if_kw = nullptr;
  syntax_asdl::expr_t* enode = nullptr;
  syntax_asdl::condition_t* cond = nullptr;
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* then_kw = nullptr;
  command::CommandList* body = nullptr;
  syntax_asdl::IfArm* arm = nullptr;
  StackRoot _root0(&if_node);
  StackRoot _root1(&if_kw);
  StackRoot _root2(&enode);
  StackRoot _root3(&cond);
  StackRoot _root4(&ate);
  StackRoot _root5(&then_kw);
  StackRoot _root6(&body);
  StackRoot _root7(&arm);

  if_node = command::If::CreateNull(true);
  if_kw = word_::AsKeywordToken(this->cur_word);
  if_node->if_kw = if_kw;
  this->_SetNext();
  if ((this->parse_opts->parse_paren() and this->w_parser->LookPastSpace() == Id::Op_LParen)) {
    enode = this->w_parser->ParseYshExprForCommand();
    cond = Alloc<condition::YshExpr>(enode);
  }
  else {
    cond = this->_ParseConditionList();
  }
  this->_GetWord();
  if ((this->parse_opts->parse_brace() and this->c_id == Id::Lit_LBrace)) {
    return this->_ParseYshIf(if_kw, cond);
  }
  ate = this->_Eat(Id::KW_Then);
  then_kw = word_::AsKeywordToken(ate);
  body = this->_ParseCommandList();
  arm = Alloc<IfArm>(if_kw, cond, then_kw, body->children, then_kw);
  if_node->arms->append(arm);
  if ((this->c_id == Id::KW_Elif || this->c_id == Id::KW_Else)) {
    this->_ParseElifElse(if_node);
  }
  ate = this->_Eat(Id::KW_Fi);
  if_node->fi_kw = word_::AsKeywordToken(ate);
  return if_node;
}

syntax_asdl::command_t* CommandParser::ParseTime() {
  syntax_asdl::Token* time_kw = nullptr;
  syntax_asdl::command_t* pipeline = nullptr;
  StackRoot _root0(&time_kw);
  StackRoot _root1(&pipeline);

  time_kw = word_::AsKeywordToken(this->cur_word);
  this->_SetNext();
  pipeline = this->ParsePipeline();
  return Alloc<command::TimeBlock>(time_kw, pipeline);
}

syntax_asdl::command_t* CommandParser::ParseCompoundCommand() {
  syntax_asdl::BraceGroup* n1 = nullptr;
  command::Subshell* n2 = nullptr;
  syntax_asdl::Token* keyword = nullptr;
  command::WhileUntil* n3 = nullptr;
  command::If* n4 = nullptr;
  command::Case* n5 = nullptr;
  command::DBracket* n6 = nullptr;
  command::DParen* n7 = nullptr;
  StackRoot _root0(&n1);
  StackRoot _root1(&n2);
  StackRoot _root2(&keyword);
  StackRoot _root3(&n3);
  StackRoot _root4(&n4);
  StackRoot _root5(&n5);
  StackRoot _root6(&n6);
  StackRoot _root7(&n7);

  this->_GetWord();
  if (this->c_id == Id::Lit_LBrace) {
    n1 = this->ParseBraceGroup();
    return this->_MaybeParseRedirectList(n1);
  }
  if (this->c_id == Id::Op_LParen) {
    n2 = this->ParseSubshell();
    return this->_MaybeParseRedirectList(n2);
  }
  if (this->c_id == Id::KW_For) {
    return this->ParseFor();
  }
  if ((this->c_id == Id::KW_While || this->c_id == Id::KW_Until)) {
    keyword = word_::AsKeywordToken(this->cur_word);
    n3 = this->ParseWhileUntil(keyword);
    return this->_MaybeParseRedirectList(n3);
  }
  if (this->c_id == Id::KW_If) {
    n4 = this->ParseIf();
    return this->_MaybeParseRedirectList(n4);
  }
  if (this->c_id == Id::KW_Case) {
    n5 = this->ParseCase();
    return this->_MaybeParseRedirectList(n5);
  }
  if (this->c_id == Id::KW_DLeftBracket) {
    if (!this->parse_opts->parse_dbracket()) {
      p_die(S_mEk, Alloc<loc::Word>(this->cur_word));
    }
    n6 = this->ParseDBracket();
    return this->_MaybeParseRedirectList(n6);
  }
  if (this->c_id == Id::Op_DLeftParen) {
    if (!this->parse_opts->parse_dparen()) {
      p_die(S_ulC, Alloc<loc::Word>(this->cur_word));
    }
    n7 = this->ParseDParen();
    return this->_MaybeParseRedirectList(n7);
  }
  if (this->c_id == Id::KW_Time) {
    return this->ParseTime();
  }
  p_die(StrFormat("Unexpected word while parsing compound command (%s)", Id_str(this->c_id)), Alloc<loc::Word>(this->cur_word));
}

command::ShFunction* CommandParser::ParseFunctionDef() {
  syntax_asdl::CompoundWord* word0 = nullptr;
  BigStr* name = nullptr;
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::Token* blame_tok = nullptr;
  command::ShFunction* func = nullptr;
  StackRoot _root0(&word0);
  StackRoot _root1(&name);
  StackRoot _root2(&part0);
  StackRoot _root3(&blame_tok);
  StackRoot _root4(&func);

  word0 = static_cast<CompoundWord*>(this->cur_word);
  name = word_::ShFunctionName(word0);
  if (len(name) == 0) {
    p_die(S_rbb, Alloc<loc::Word>(word0));
  }
  part0 = word0->parts->at(0);
  blame_tok = static_cast<Token*>(part0);
  this->_SetNext();
  this->_GetWord();
  this->lexer->PushHint(Id::Op_RParen, Id::Right_ShFunction);
  this->_SetNext();
  this->_GetWord();
  if (this->c_id == Id::Right_ShFunction) {
    this->_SetNext();
    this->_NewlineOk();
    func = command::ShFunction::CreateNull();
    func->name = name;
    {  // with
      ctx_VarChecker ctx{this->var_checker, blame_tok};

      func->body = this->ParseCompoundCommand();
    }
    func->name_tok = location::LeftTokenForCompoundWord(word0);
    return func;
  }
  else {
    p_die(S_rbz, Alloc<loc::Word>(this->cur_word));
    return nullptr;
  }
}

command::ShFunction* CommandParser::ParseKshFunctionDef() {
  syntax_asdl::Token* keyword_tok = nullptr;
  syntax_asdl::CompoundWord* cur_word = nullptr;
  BigStr* name = nullptr;
  syntax_asdl::word_t* name_word = nullptr;
  command::ShFunction* func = nullptr;
  StackRoot _root0(&keyword_tok);
  StackRoot _root1(&cur_word);
  StackRoot _root2(&name);
  StackRoot _root3(&name_word);
  StackRoot _root4(&func);

  keyword_tok = word_::AsKeywordToken(this->cur_word);
  this->_SetNext();
  this->_GetWord();
  cur_word = static_cast<CompoundWord*>(this->cur_word);
  name = word_::ShFunctionName(cur_word);
  if (len(name) == 0) {
    p_die(S_rdm, Alloc<loc::Word>(cur_word));
  }
  name_word = this->cur_word;
  this->_SetNext();
  this->_GetWord();
  if (this->c_id == Id::Op_LParen) {
    this->lexer->PushHint(Id::Op_RParen, Id::Right_ShFunction);
    this->_SetNext();
    this->_Eat(Id::Right_ShFunction);
  }
  this->_NewlineOk();
  func = command::ShFunction::CreateNull();
  func->name = name;
  {  // with
    ctx_VarChecker ctx{this->var_checker, keyword_tok};

    func->body = this->ParseCompoundCommand();
  }
  func->keyword = keyword_tok;
  func->name_tok = location::LeftTokenForWord(name_word);
  return func;
}

syntax_asdl::Proc* CommandParser::ParseYshProc() {
  syntax_asdl::Proc* node = nullptr;
  syntax_asdl::Token* keyword_tok = nullptr;
  proc_sig::Closed* sig = nullptr;
  syntax_asdl::ParamGroup* wp = nullptr;
  syntax_asdl::RestParam* r = nullptr;
  syntax_asdl::ParamGroup* posit = nullptr;
  syntax_asdl::ParamGroup* named = nullptr;
  syntax_asdl::Param* b = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&keyword_tok);
  StackRoot _root2(&sig);
  StackRoot _root3(&wp);
  StackRoot _root4(&r);
  StackRoot _root5(&posit);
  StackRoot _root6(&named);
  StackRoot _root7(&b);

  node = Proc::CreateNull(true);
  keyword_tok = word_::AsKeywordToken(this->cur_word);
  node->keyword = keyword_tok;
  {  // with
    ctx_VarChecker ctx{this->var_checker, keyword_tok};

    {  // with
      ctx_CmdMode ctx{this, cmd_mode_e::Proc};

      this->w_parser->ParseProc(node);
      if (node->sig->tag() == proc_sig_e::Closed) {
        sig = static_cast<proc_sig::Closed*>(node->sig);
        wp = sig->word;
        if (wp) {
          for (ListIter<syntax_asdl::Param*> it(wp->params); !it.Done(); it.Next()) {
            syntax_asdl::Param* param = it.Value();
            StackRoot _for(&param          );
            this->var_checker->Check(Id::KW_Var, param->name, param->blame_tok);
          }
          if (wp->rest_of) {
            r = wp->rest_of;
            this->var_checker->Check(Id::KW_Var, r->name, r->blame_tok);
          }
        }
        posit = sig->positional;
        if (posit) {
          for (ListIter<syntax_asdl::Param*> it(posit->params); !it.Done(); it.Next()) {
            syntax_asdl::Param* param = it.Value();
            StackRoot _for(&param          );
            this->var_checker->Check(Id::KW_Var, param->name, param->blame_tok);
          }
          if (posit->rest_of) {
            r = posit->rest_of;
            this->var_checker->Check(Id::KW_Var, r->name, r->blame_tok);
          }
        }
        named = sig->named;
        if (named) {
          for (ListIter<syntax_asdl::Param*> it(named->params); !it.Done(); it.Next()) {
            syntax_asdl::Param* param = it.Value();
            StackRoot _for(&param          );
            this->var_checker->Check(Id::KW_Var, param->name, param->blame_tok);
          }
          if (named->rest_of) {
            r = named->rest_of;
            this->var_checker->Check(Id::KW_Var, r->name, r->blame_tok);
          }
        }
        if (sig->block_param) {
          b = sig->block_param;
          this->var_checker->Check(Id::KW_Var, b->name, b->blame_tok);
        }
      }
      this->_SetNext();
      node->body = this->ParseBraceGroup();
    }
  }
  return node;
}

syntax_asdl::Func* CommandParser::ParseYshFunc() {
  syntax_asdl::Func* node = nullptr;
  syntax_asdl::Token* keyword_tok = nullptr;
  syntax_asdl::ParamGroup* posit = nullptr;
  syntax_asdl::RestParam* r = nullptr;
  syntax_asdl::ParamGroup* named = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&keyword_tok);
  StackRoot _root2(&posit);
  StackRoot _root3(&r);
  StackRoot _root4(&named);

  node = Func::CreateNull(true);
  keyword_tok = word_::AsKeywordToken(this->cur_word);
  node->keyword = keyword_tok;
  {  // with
    ctx_VarChecker ctx{this->var_checker, keyword_tok};

    this->w_parser->ParseFunc(node);
    posit = node->positional;
    if (posit) {
      for (ListIter<syntax_asdl::Param*> it(posit->params); !it.Done(); it.Next()) {
        syntax_asdl::Param* param = it.Value();
        StackRoot _for(&param      );
        this->var_checker->Check(Id::KW_Var, param->name, param->blame_tok);
      }
      if (posit->rest_of) {
        r = posit->rest_of;
        this->var_checker->Check(Id::KW_Var, r->name, r->blame_tok);
      }
    }
    named = node->named;
    if (named) {
      for (ListIter<syntax_asdl::Param*> it(named->params); !it.Done(); it.Next()) {
        syntax_asdl::Param* param = it.Value();
        StackRoot _for(&param      );
        this->var_checker->Check(Id::KW_Var, param->name, param->blame_tok);
      }
      if (named->rest_of) {
        r = named->rest_of;
        this->var_checker->Check(Id::KW_Var, r->name, r->blame_tok);
      }
    }
    this->_SetNext();
    {  // with
      ctx_CmdMode ctx{this, cmd_mode_e::Func};

      node->body = this->ParseBraceGroup();
    }
  }
  return node;
}

syntax_asdl::command_t* CommandParser::ParseCoproc() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

command::Subshell* CommandParser::ParseSubshell() {
  syntax_asdl::Token* left = nullptr;
  command::CommandList* c_list = nullptr;
  syntax_asdl::command_t* child = nullptr;
  syntax_asdl::word_t* ate = nullptr;
  syntax_asdl::Token* right = nullptr;
  StackRoot _root0(&left);
  StackRoot _root1(&c_list);
  StackRoot _root2(&child);
  StackRoot _root3(&ate);
  StackRoot _root4(&right);

  left = word_::AsOperatorToken(this->cur_word);
  this->_SetNext();
  this->lexer->PushHint(Id::Op_RParen, Id::Right_Subshell);
  c_list = this->_ParseCommandList();
  if (len(c_list->children) == 1) {
    child = c_list->children->at(0);
  }
  else {
    child = c_list;
  }
  ate = this->_Eat(Id::Right_Subshell);
  right = word_::AsOperatorToken(ate);
  return Alloc<command::Subshell>(left, child, right, false);
}

command::DBracket* CommandParser::ParseDBracket() {
  syntax_asdl::Token* left = nullptr;
  bool_parse::BoolParser* b_parser = nullptr;
  syntax_asdl::bool_expr_t* bnode = nullptr;
  syntax_asdl::Token* right = nullptr;
  StackRoot _root0(&left);
  StackRoot _root1(&b_parser);
  StackRoot _root2(&bnode);
  StackRoot _root3(&right);

  left = word_::AsKeywordToken(this->cur_word);
  this->_SetNext();
  b_parser = Alloc<bool_parse::BoolParser>(this->w_parser);
  Tuple2<syntax_asdl::bool_expr_t*, syntax_asdl::Token*> tup16 = b_parser->Parse();
  bnode = tup16.at0();
  right = tup16.at1();
  return Alloc<command::DBracket>(left, bnode, right);
}

command::DParen* CommandParser::ParseDParen() {
  syntax_asdl::Token* left = nullptr;
  syntax_asdl::arith_expr_t* anode = nullptr;
  syntax_asdl::Token* right = nullptr;
  StackRoot _root0(&left);
  StackRoot _root1(&anode);
  StackRoot _root2(&right);

  left = word_::AsOperatorToken(this->cur_word);
  this->_SetNext();
  Tuple2<syntax_asdl::arith_expr_t*, syntax_asdl::Token*> tup17 = this->w_parser->ReadDParen();
  anode = tup17.at0();
  right = tup17.at1();
  return Alloc<command::DParen>(left, anode, right);
}

syntax_asdl::command_t* CommandParser::ParseCommand() {
  int keyword_id;
  syntax_asdl::Token* kw_token = nullptr;
  command::VarDecl* n8 = nullptr;
  command::Mutation* n9 = nullptr;
  syntax_asdl::Token* keyword = nullptr;
  syntax_asdl::expr_t* enode = nullptr;
  syntax_asdl::CompoundWord* cur_word = nullptr;
  List<syntax_asdl::word_part_t*>* parts = nullptr;
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&kw_token);
  StackRoot _root1(&n8);
  StackRoot _root2(&n9);
  StackRoot _root3(&keyword);
  StackRoot _root4(&enode);
  StackRoot _root5(&cur_word);
  StackRoot _root6(&parts);
  StackRoot _root7(&part0);
  StackRoot _root8(&tok);

  if (this->_AtSecondaryKeyword()) {
    p_die(S_lrE, Alloc<loc::Word>(this->cur_word));
  }
  if (this->c_id == Id::KW_Proc) {
    if (this->parse_opts->parse_proc()) {
      return this->ParseYshProc();
    }
    else {
      p_die(S_dsk, Alloc<loc::Word>(this->cur_word));
    }
  }
  if (this->c_id == Id::KW_Typed) {
    this->_SetNext();
    this->_GetWord();
    if (this->c_id != Id::KW_Proc) {
      p_die(S_sai, Alloc<loc::Word>(this->cur_word));
    }
    if (this->parse_opts->parse_proc()) {
      return this->ParseYshProc();
    }
    else {
      p_die(S_hzm, Alloc<loc::Word>(this->cur_word));
    }
  }
  if (this->c_id == Id::KW_Func) {
    if (this->parse_opts->parse_func()) {
      return this->ParseYshFunc();
    }
    else {
      p_die(S_ggl, Alloc<loc::Word>(this->cur_word));
    }
  }
  if ((this->c_id == Id::KW_Const and this->cmd_mode != cmd_mode_e::Shell)) {
    p_die(S_pxA, Alloc<loc::Word>(this->cur_word));
  }
  if ((this->c_id == Id::KW_Var || this->c_id == Id::KW_Const)) {
    keyword_id = this->c_id;
    kw_token = word_::LiteralToken(this->cur_word);
    this->_SetNext();
    n8 = this->w_parser->ParseVarDecl(kw_token);
    for (ListIter<syntax_asdl::NameType*> it(n8->lhs); !it.Done(); it.Next()) {
      syntax_asdl::NameType* lhs = it.Value();
      StackRoot _for(&lhs    );
      this->var_checker->Check(keyword_id, lhs->name, lhs->left);
    }
    return n8;
  }
  if ((this->c_id == Id::KW_SetVar || this->c_id == Id::KW_SetGlobal)) {
    kw_token = word_::LiteralToken(this->cur_word);
    this->_SetNext();
    n9 = this->w_parser->ParseMutation(kw_token, this->var_checker);
    return n9;
  }
  if ((this->c_id == Id::KW_Call || this->c_id == Id::Lit_Equals)) {
    keyword = word_::LiteralToken(this->cur_word);
    this->_SetNext();
    enode = this->w_parser->ParseCommandExpr();
    return Alloc<command::Expr>(keyword, enode);
  }
  if (this->c_id == Id::KW_Function) {
    return this->ParseKshFunctionDef();
  }
  if ((this->c_id == Id::KW_DLeftBracket || this->c_id == Id::Op_DLeftParen || this->c_id == Id::Op_LParen || this->c_id == Id::Lit_LBrace || this->c_id == Id::KW_For || this->c_id == Id::KW_While || this->c_id == Id::KW_Until || this->c_id == Id::KW_If || this->c_id == Id::KW_Case || this->c_id == Id::KW_Time)) {
    return this->ParseCompoundCommand();
  }
  if (this->c_id == Id::Lit_RBrace) {
    p_die(S_avi, Alloc<loc::Word>(this->cur_word));
  }
  if (this->c_kind == Kind::Redir) {
    return this->ParseSimpleCommand();
  }
  if (this->c_kind == Kind::Word) {
    cur_word = static_cast<CompoundWord*>(this->cur_word);
    if ((this->w_parser->LookAheadFuncParens() and !word_::IsVarLike(cur_word))) {
      return this->ParseFunctionDef();
    }
    parts = cur_word->parts;
    if ((this->parse_opts->parse_equals() and len(parts) == 1)) {
      part0 = parts->at(0);
      if (part0->tag() == word_part_e::Literal) {
        tok = static_cast<Token*>(part0);
        if ((tok->id == Id::Lit_Chars and (this->w_parser->LookPastSpace() == Id::Lit_Equals and match::IsValidVarName(lexer::LazyStr(tok))))) {
          if ((len(this->hay_attrs_stack) and this->hay_attrs_stack->at(-1))) {
            enode = this->w_parser->ParseBareDecl();
            this->_SetNext();
            return Alloc<command::VarDecl>(nullptr, NewList<syntax_asdl::NameType*>(std::initializer_list<syntax_asdl::NameType*>{Alloc<NameType>(tok, lexer::TokenVal(tok), nullptr)}), enode);
          }
          else {
            this->_SetNext();
            this->_GetWord();
            p_die(S_hlc, Alloc<loc::Word>(this->cur_word));
          }
        }
      }
    }
    return this->ParseSimpleCommand();
  }
  if (this->c_kind == Kind::Eof) {
    p_die(S_agx, Alloc<loc::Word>(this->cur_word));
  }
  p_die(S_fgr, Alloc<loc::Word>(this->cur_word));
}

syntax_asdl::command_t* CommandParser::ParsePipeline() {
  syntax_asdl::Token* negated = nullptr;
  syntax_asdl::command_t* child = nullptr;
  List<syntax_asdl::command_t*>* children = nullptr;
  command::Pipeline* node = nullptr;
  List<syntax_asdl::Token*>* ops = nullptr;
  syntax_asdl::Token* op = nullptr;
  StackRoot _root0(&negated);
  StackRoot _root1(&child);
  StackRoot _root2(&children);
  StackRoot _root3(&node);
  StackRoot _root4(&ops);
  StackRoot _root5(&op);

  negated = nullptr;
  this->_GetWord();
  if (this->c_id == Id::KW_Bang) {
    negated = word_::AsKeywordToken(this->cur_word);
    this->_SetNext();
  }
  child = this->ParseCommand();
  children = NewList<syntax_asdl::command_t*>(std::initializer_list<syntax_asdl::command_t*>{child});
  this->_GetWord();
  if ((this->c_id != Id::Op_Pipe && this->c_id != Id::Op_PipeAmp)) {
    if (negated != nullptr) {
      node = Alloc<command::Pipeline>(negated, children, Alloc<List<syntax_asdl::Token*>>());
      return node;
    }
    else {
      return child;
    }
  }
  ops = Alloc<List<syntax_asdl::Token*>>();
  while (true) {
    op = word_::AsOperatorToken(this->cur_word);
    ops->append(op);
    this->_SetNext();
    this->_NewlineOk();
    child = this->ParseCommand();
    children->append(child);
    this->_GetWord();
    if ((this->c_id != Id::Op_Pipe && this->c_id != Id::Op_PipeAmp)) {
      break;
    }
  }
  return Alloc<command::Pipeline>(negated, children, ops);
}

syntax_asdl::command_t* CommandParser::ParseAndOr() {
  this->_GetWord();
  if (this->c_id == Id::Lit_TDot) {
    this->_SetNext();
    {  // with
      word_::ctx_Multiline ctx{this->w_parser};

      return this->_ParseAndOr();
    }
  }
  return this->_ParseAndOr();
}

syntax_asdl::command_t* CommandParser::_ParseAndOr() {
  syntax_asdl::command_t* child = nullptr;
  List<syntax_asdl::Token*>* ops = nullptr;
  List<syntax_asdl::command_t*>* children = nullptr;
  StackRoot _root0(&child);
  StackRoot _root1(&ops);
  StackRoot _root2(&children);

  child = this->ParsePipeline();
  this->_GetWord();
  if ((this->c_id != Id::Op_DPipe && this->c_id != Id::Op_DAmp)) {
    return child;
  }
  ops = Alloc<List<syntax_asdl::Token*>>();
  children = NewList<syntax_asdl::command_t*>(std::initializer_list<syntax_asdl::command_t*>{child});
  while (true) {
    ops->append(word_::AsOperatorToken(this->cur_word));
    this->_SetNext();
    this->_NewlineOk();
    child = this->ParsePipeline();
    children->append(child);
    this->_GetWord();
    if ((this->c_id != Id::Op_DPipe && this->c_id != Id::Op_DAmp)) {
      break;
    }
  }
  return Alloc<command::AndOr>(children, ops);
}

syntax_asdl::command_t* CommandParser::_ParseCommandLine() {
  List<int>* END_LIST = nullptr;
  List<syntax_asdl::command_t*>* children = nullptr;
  bool done;
  syntax_asdl::command_t* child = nullptr;
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&END_LIST);
  StackRoot _root1(&children);
  StackRoot _root2(&child);
  StackRoot _root3(&tok);

  END_LIST = NewList<int>(std::initializer_list<int>{Id::Op_Newline, Id::Eof_Real});
  children = Alloc<List<syntax_asdl::command_t*>>();
  done = false;
  while (!done) {
    child = this->ParseAndOr();
    this->_GetWord();
    if ((this->c_id == Id::Op_Semi || this->c_id == Id::Op_Amp)) {
      tok = static_cast<Token*>(this->cur_word);
      child = Alloc<command::Sentence>(child, tok);
      this->_SetNext();
      this->_GetWord();
      if (list_contains(END_LIST, this->c_id)) {
        done = true;
      }
    }
    else {
      if (list_contains(END_LIST, this->c_id)) {
        done = true;
      }
      else {
        p_die(StrFormat("Invalid word while parsing command line (%s)", Id_str(this->c_id)), Alloc<loc::Word>(this->cur_word));
      }
    }
    children->append(child);
  }
  if (len(children) > 1) {
    return Alloc<command::CommandList>(children);
  }
  else {
    return children->at(0);
  }
}

command::CommandList* CommandParser::_ParseCommandTerm() {
  List<int>* END_LIST = nullptr;
  List<syntax_asdl::command_t*>* children = nullptr;
  bool done;
  syntax_asdl::command_t* child = nullptr;
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&END_LIST);
  StackRoot _root1(&children);
  StackRoot _root2(&child);
  StackRoot _root3(&tok);

  END_LIST = NewList<int>(std::initializer_list<int>{this->eof_id, Id::Right_Subshell, Id::Lit_RBrace, Id::Op_DSemi, Id::Op_SemiAmp, Id::Op_DSemiAmp});
  children = Alloc<List<syntax_asdl::command_t*>>();
  done = false;
  while (!done) {
    if (this->_AtSecondaryKeyword()) {
      break;
    }
    child = this->ParseAndOr();
    this->_GetWord();
    if (this->c_id == Id::Op_Newline) {
      this->_SetNext();
      this->_GetWord();
      if (list_contains(END_LIST, this->c_id)) {
        done = true;
      }
    }
    else {
      if ((this->c_id == Id::Op_Semi || this->c_id == Id::Op_Amp)) {
        tok = static_cast<Token*>(this->cur_word);
        child = Alloc<command::Sentence>(child, tok);
        this->_SetNext();
        this->_GetWord();
        if (this->c_id == Id::Op_Newline) {
          this->_SetNext();
          this->_GetWord();
          if (list_contains(END_LIST, this->c_id)) {
            done = true;
          }
        }
        else {
          if (list_contains(END_LIST, this->c_id)) {
            done = true;
          }
        }
      }
      else {
        if (list_contains(END_LIST, this->c_id)) {
          done = true;
        }
        else {
          if ((this->parse_opts->parse_brace() and this->c_id == Id::Lit_LBrace)) {
            done = true;
          }
          else {
            if (this->c_kind != Kind::Word) {
              p_die(S_wpb, Alloc<loc::Word>(this->cur_word));
            }
          }
        }
      }
    }
    children->append(child);
  }
  return Alloc<command::CommandList>(children);
}

command::CommandList* CommandParser::_ParseCommandList() {
  this->_NewlineOk();
  return this->_ParseCommandTerm();
}

syntax_asdl::command_t* CommandParser::ParseLogicalLine() {
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&node);

  this->_NewlineOk();
  this->_GetWord();
  if (this->c_id == Id::Eof_Real) {
    return nullptr;
  }
  node = this->_ParseCommandLine();
  return node;
}

syntax_asdl::parse_result_t* CommandParser::ParseInteractiveLine() {
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&node);

  this->_GetWord();
  if (this->c_id == Id::Op_Newline) {
    return parse_result::EmptyLine;
  }
  if (this->c_id == Id::Eof_Real) {
    return parse_result::Eof;
  }
  node = this->_ParseCommandLine();
  return Alloc<parse_result::Node>(node);
}

syntax_asdl::command_t* CommandParser::ParseCommandSub() {
  command::CommandList* c_list = nullptr;
  StackRoot _root0(&c_list);

  this->_NewlineOk();
  this->_GetWord();
  if (this->c_kind == Kind::Eof) {
    return command::NoOp;
  }
  c_list = this->_ParseCommandTerm();
  if (len(c_list->children) == 1) {
    return c_list->children->at(0);
  }
  else {
    return c_list;
  }
}

void CommandParser::CheckForPendingHereDocs() {
  syntax_asdl::Redir* node = nullptr;
  redir_param::HereDoc* h = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&h);

  if (len(this->pending_here_docs)) {
    node = this->pending_here_docs->at(0);
    h = static_cast<redir_param::HereDoc*>(node->arg);
    p_die(S_tce, Alloc<loc::Word>(h->here_begin));
  }
}

}  // define namespace cmd_parse

namespace glob_ {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using syntax_asdl::CompoundWord;
using syntax_asdl::Token;
using syntax_asdl::word_part_e;
using syntax_asdl::glob_part;
using syntax_asdl::glob_part_e;
using syntax_asdl::glob_part_t;
using mylib::print_stderr;

bool LooksLikeGlob(BigStr* s) {
  bool left_bracket;
  int i;
  int n;
  int c;
  StackRoot _root0(&s);

  left_bracket = false;
  i = 0;
  n = len(s);
  while (i < n) {
    c = mylib::ByteAt(s, i);
    if (mylib::ByteEquals(c, S_iyu)) {
      i += 1;
    }
    else {
      if ((mylib::ByteEquals(c, S_Fgw) or mylib::ByteEquals(c, S_BAk))) {
        return true;
      }
      else {
        if (mylib::ByteEquals(c, S_Eax)) {
          left_bracket = true;
        }
        else {
          if ((mylib::ByteEquals(c, S_pcD) and left_bracket)) {
            return true;
          }
        }
      }
    }
    i += 1;
  }
  return false;
}

bool LooksLikeStaticGlob(syntax_asdl::CompoundWord* w) {
  bool left_bracket;
  int id_;
  StackRoot _root0(&w);

  left_bracket = false;
  for (ListIter<syntax_asdl::word_part_t*> it(w->parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    if (part->tag() == word_part_e::Literal) {
      id_ = static_cast<Token*>(part)->id;
      if ((id_ == Id::Lit_Star || id_ == Id::Lit_QMark)) {
        return true;
      }
      else {
        if (id_ == Id::Lit_LBracket) {
          left_bracket = true;
        }
        else {
          if ((id_ == Id::Lit_RBracket and left_bracket)) {
            return true;
          }
        }
      }
    }
  }
  return false;
}
BigStr* GLOB_META_CHARS = S_hpd;

BigStr* GlobEscape(BigStr* s) {
  StackRoot _root0(&s);

  return pyutil::BackslashEscape(s, GLOB_META_CHARS);
}
BigStr* ERE_META_CHARS = S_ivk;

BigStr* ExtendedRegexEscape(BigStr* s) {
  StackRoot _root0(&s);

  return pyutil::BackslashEscape(s, ERE_META_CHARS);
}

BigStr* GlobUnescape(BigStr* s) {
  List<int>* unescaped = nullptr;
  int i;
  int n;
  int c;
  int c2;
  StackRoot _root0(&s);
  StackRoot _root1(&unescaped);

  unescaped = Alloc<List<int>>();
  i = 0;
  n = len(s);
  while (i < n) {
    c = mylib::ByteAt(s, i);
    if ((mylib::ByteEquals(c, S_iyu) and i != (n - 1))) {
      i += 1;
      c2 = mylib::ByteAt(s, i);
      if (mylib::ByteInSet(c2, GLOB_META_CHARS)) {
        unescaped->append(c2);
      }
      else {
        assert(0);  // AssertionError
      }
    }
    else {
      unescaped->append(c);
    }
    i += 1;
  }
  return mylib::JoinBytes(unescaped);
}

_GlobParser::_GlobParser(match::SimpleLexer* lexer) {
  this->lexer = lexer;
  this->token_type = Id::Undefined_Tok;
  this->token_val = S_Aoo;
  this->warnings = Alloc<List<BigStr*>>();
}

void _GlobParser::_Next() {
  Tuple2<int, BigStr*> tup0 = this->lexer->Next();
  this->token_type = tup0.at0();
  this->token_val = tup0.at1();
}

List<syntax_asdl::glob_part_t*>* _GlobParser::_ParseCharClass() {
  glob_part::Literal* first_token = nullptr;
  int balance;
  List<Tuple2<int, BigStr*>*>* tokens = nullptr;
  List<syntax_asdl::glob_part_t*>* parts = nullptr;
  bool negated;
  int id1;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&first_token);
  StackRoot _root1(&tokens);
  StackRoot _root2(&parts);
  StackRoot _root3(&strs);

  first_token = Alloc<glob_part::Literal>(this->token_type, this->token_val);
  balance = 1;
  tokens = Alloc<List<Tuple2<int, BigStr*>*>>();
  while (true) {
    this->_Next();
    if (this->token_type == Id::Eol_Tok) {
      this->warnings->append(S_idh);
      parts = NewList<syntax_asdl::glob_part_t*>(std::initializer_list<syntax_asdl::glob_part_t*>{first_token});
      for (ListIter<Tuple2<int, BigStr*>*> it(tokens); !it.Done(); it.Next()) {
        Tuple2<int, BigStr*>* tup1 = it.Value();
        int id_ = tup1->at0();
        BigStr* s = tup1->at1();
        StackRoot _unpack_1(&s);
        parts->append(Alloc<glob_part::Literal>(id_, s));
      }
      return parts;
    }
    if (this->token_type == Id::Glob_LBracket) {
      balance += 1;
    }
    else {
      if (this->token_type == Id::Glob_RBracket) {
        balance -= 1;
      }
    }
    if (balance == 0) {
      break;
    }
    tokens->append((Alloc<Tuple2<int, BigStr*>>(this->token_type, this->token_val)));
  }
  negated = false;
  if (len(tokens)) {
    Tuple2<int, BigStr*>* tup2 = tokens->at(0);
    id1 = tup2->at0();
    if ((id1 == Id::Glob_Bang || id1 == Id::Glob_Caret)) {
      negated = true;
      tokens = tokens->slice(1);
    }
  }
  strs = Alloc<List<BigStr*>>();
  for (ListIter<Tuple2<int, BigStr*>*> it(tokens); !it.Done(); it.Next()) {
    Tuple2<int, BigStr*>* tup3 = it.Value();
    BigStr* s = tup3->at1();
    StackRoot _unpack_1(&s);
    strs->append(s);
  }
  return NewList<syntax_asdl::glob_part_t*>(std::initializer_list<syntax_asdl::glob_part_t*>{Alloc<glob_part::CharClass>(negated, strs)});
}

Tuple2<List<syntax_asdl::glob_part_t*>*, List<BigStr*>*> _GlobParser::Parse() {
  List<syntax_asdl::glob_part_t*>* parts = nullptr;
  int id_;
  BigStr* s = nullptr;
  StackRoot _root0(&parts);
  StackRoot _root1(&s);

  parts = Alloc<List<syntax_asdl::glob_part_t*>>();
  while (true) {
    this->_Next();
    id_ = this->token_type;
    s = this->token_val;
    if (id_ == Id::Eol_Tok) {
      break;
    }
    if ((id_ == Id::Glob_Star || id_ == Id::Glob_QMark)) {
      parts->append(Alloc<glob_part::Operator>(id_));
    }
    else {
      if (id_ == Id::Glob_LBracket) {
        parts->extend(this->_ParseCharClass());
      }
      else {
        parts->append(Alloc<glob_part::Literal>(id_, s));
      }
    }
    if (id_ == Id::Glob_RBracket) {
      this->warnings->append(S_hoz);
    }
    if (id_ == Id::Glob_BadBackslash) {
      this->warnings->append(S_btq);
    }
  }
  return Tuple2<List<syntax_asdl::glob_part_t*>*, List<BigStr*>*>(parts, this->warnings);
}
BigStr* _REGEX_CHARS_TO_ESCAPE = S_Bge;

BigStr* _GenerateERE(List<syntax_asdl::glob_part_t*>* parts) {
  List<BigStr*>* out = nullptr;
  int tag;
  syntax_asdl::glob_part_t* UP_part = nullptr;
  BigStr* c = nullptr;
  List<BigStr*>* good = nullptr;
  bool literal_hyphen;
  bool literal_rbracket;
  StackRoot _root0(&parts);
  StackRoot _root1(&out);
  StackRoot _root2(&UP_part);
  StackRoot _root3(&c);
  StackRoot _root4(&good);

  out = Alloc<List<BigStr*>>();
  for (ListIter<syntax_asdl::glob_part_t*> it(parts); !it.Done(); it.Next()) {
    syntax_asdl::glob_part_t* part = it.Value();
    StackRoot _for(&part  );
    tag = part->tag();
    UP_part = part;
    if (tag == glob_part_e::Literal) {
      glob_part::Literal* part = static_cast<glob_part::Literal*>(UP_part);
      if (part->id == Id::Glob_EscapedChar) {
        c = part->s->at(1);
        if (str_contains(_REGEX_CHARS_TO_ESCAPE, c)) {
          out->append(S_iyu);
        }
        out->append(c);
      }
      else {
        if ((part->id == Id::Glob_CleanLiterals || part->id == Id::Glob_Bang)) {
          out->append(part->s);
        }
        else {
          if ((part->id == Id::Glob_OtherLiteral || part->id == Id::Glob_Caret)) {
            c = part->s;
            if (str_contains(_REGEX_CHARS_TO_ESCAPE, c)) {
              out->append(S_iyu);
            }
            out->append(c);
          }
          else {
            if (part->id == Id::Glob_LBracket) {
              out->append(S_uDk);
            }
            else {
              if (part->id == Id::Glob_RBracket) {
                out->append(S_dkw);
              }
              else {
                if (part->id == Id::Glob_BadBackslash) {
                  out->append(S_Eef);
                }
                else {
                  if (part->id == Id::Glob_Caret) {
                    out->append(S_EAB);
                  }
                  else {
                    assert(0);  // AssertionError
                  }
                }
              }
            }
          }
        }
      }
    }
    else {
      if (tag == glob_part_e::Operator) {
        glob_part::Operator* part = static_cast<glob_part::Operator*>(UP_part);
        if (part->op_id == Id::Glob_QMark) {
          out->append(S_Aru);
        }
        else {
          if (part->op_id == Id::Glob_Star) {
            out->append(S_mgF);
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
      else {
        if (tag == glob_part_e::CharClass) {
          glob_part::CharClass* part = static_cast<glob_part::CharClass*>(UP_part);
          out->append(S_Eax);
          if (part->negated) {
            out->append(S_EAB);
          }
          good = Alloc<List<BigStr*>>();
          literal_hyphen = false;
          literal_rbracket = false;
          for (ListIter<BigStr*> it(part->strs); !it.Done(); it.Next()) {
            BigStr* s = it.Value();
            StackRoot _for(&s          );
            if (str_equals(s, S_iCa)) {
              literal_hyphen = true;
              continue;
            }
            if (str_equals(s, S_dkw)) {
              literal_rbracket = true;
              continue;
            }
            good->append(s);
          }
          if (literal_rbracket) {
            out->append(S_pcD);
          }
          out->extend(good);
          if (literal_hyphen) {
            out->append(S_Bjq);
          }
          out->append(S_pcD);
        }
      }
    }
  }
  return S_Aoo->join(out);
}

Tuple2<BigStr*, List<BigStr*>*> GlobToERE(BigStr* pat) {
  match::SimpleLexer* lexer = nullptr;
  glob_::_GlobParser* p = nullptr;
  List<syntax_asdl::glob_part_t*>* parts = nullptr;
  List<BigStr*>* warnings = nullptr;
  BigStr* regex = nullptr;
  StackRoot _root0(&pat);
  StackRoot _root1(&lexer);
  StackRoot _root2(&p);
  StackRoot _root3(&parts);
  StackRoot _root4(&warnings);
  StackRoot _root5(&regex);

  lexer = match::GlobLexer(pat);
  p = Alloc<_GlobParser>(lexer);
  Tuple2<List<syntax_asdl::glob_part_t*>*, List<BigStr*>*> tup4 = p->Parse();
  parts = tup4.at0();
  warnings = tup4.at1();
  regex = _GenerateERE(parts);
  return Tuple2<BigStr*, List<BigStr*>*>(regex, warnings);
}

Globber::Globber(optview::Exec* exec_opts) {
  this->exec_opts = exec_opts;
}

int Globber::_Glob(BigStr* arg, List<BigStr*>* out) {
  int flags;
  List<BigStr*>* results = nullptr;
  BigStr* msg = nullptr;
  int n;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&arg);
  StackRoot _root1(&out);
  StackRoot _root2(&results);
  StackRoot _root3(&msg);
  StackRoot _root4(&tmp);

  try {
    flags = 0;
    if ((this->exec_opts->dotglob() and HAVE_GLOB_PERIOD)) {
      flags |= GLOB_PERIOD;
    }
    results = libc::glob(arg, flags);
  }
  catch (RuntimeError* e) {
    msg = e->message;
    print_stderr(StrFormat("Error expanding glob %r: %s", arg, msg));
    throw;
  }
  n = len(results);
  if (n) {
    if (!this->exec_opts->dashglob()) {
      tmp = Alloc<List<BigStr*>>();
      for (ListIter<BigStr*> it(results); !it.Done(); it.Next()) {
        BigStr* s = it.Value();
        if (!s->startswith(S_Bjq)) {
          tmp->append(s);
        }
      }
      results = tmp;
      n = len(results);
    }
    n = 0;
    for (ListIter<BigStr*> it(results); !it.Done(); it.Next()) {
      BigStr* s = it.Value();
      StackRoot _for(&s    );
      if ((!str_equals(s, S_Aru) && !str_equals(s, S_Dmc))) {
        out->append(s);
        n += 1;
      }
    }
    return n;
  }
  return 0;
}

int Globber::Expand(BigStr* arg, List<BigStr*>* out) {
  int n;
  StackRoot _root0(&arg);
  StackRoot _root1(&out);

  if (this->exec_opts->noglob()) {
    out->append(arg);
    return 1;
  }
  n = this->_Glob(arg, out);
  if (n) {
    return n;
  }
  if (this->exec_opts->failglob()) {
    return -1;
  }
  if (this->exec_opts->nullglob()) {
    return 0;
  }
  else {
    out->append(GlobUnescape(arg));
    return 1;
  }
}

int Globber::ExpandExtended(BigStr* glob_pat, BigStr* fnmatch_pat, List<BigStr*>* out) {
  List<BigStr*>* tmp = nullptr;
  List<BigStr*>* filtered = nullptr;
  int n;
  StackRoot _root0(&glob_pat);
  StackRoot _root1(&fnmatch_pat);
  StackRoot _root2(&out);
  StackRoot _root3(&tmp);
  StackRoot _root4(&filtered);

  if (this->exec_opts->noglob()) {
    out->append(fnmatch_pat);
    return 1;
  }
  tmp = Alloc<List<BigStr*>>();
  this->_Glob(glob_pat, tmp);
  filtered = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(tmp); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    if (libc::fnmatch(fnmatch_pat, s)) {
      filtered->append(s);
    }
  }
  n = len(filtered);
  if (n) {
    out->extend(filtered);
    return n;
  }
  if (this->exec_opts->failglob()) {
    return -1;
  }
  if (this->exec_opts->nullglob()) {
    return 0;
  }
  else {
    out->append(GlobUnescape(fnmatch_pat));
    return 1;
  }
}

}  // define namespace glob_

namespace history {  // define

using id_kind_asdl::Id;

Evaluator::Evaluator(py_readline::Readline* readline, parse_lib::ParseContext* parse_ctx, util::_DebugFile* debug_f) {
  this->readline = readline;
  this->parse_ctx = parse_ctx;
  this->debug_f = debug_f;
}

BigStr* Evaluator::Eval(BigStr* line) {
  List<Tuple2<int, BigStr*>*>* tokens = nullptr;
  bool ok;
  int history_len;
  List<BigStr*>* parts = nullptr;
  BigStr* out = nullptr;
  BigStr* prev = nullptr;
  BigStr* ch = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  syntax_asdl::CompoundWord* w = nullptr;
  syntax_asdl::Token* tok1 = nullptr;
  syntax_asdl::Token* tok2 = nullptr;
  syntax_asdl::CompoundWord* w1 = nullptr;
  syntax_asdl::CompoundWord* w2 = nullptr;
  int begin;
  int end;
  int index;
  int num;
  BigStr* last_char = nullptr;
  BigStr* val = nullptr;
  BigStr* prefix = nullptr;
  BigStr* substring = nullptr;
  BigStr* cmd = nullptr;
  StackRoot _root0(&line);
  StackRoot _root1(&tokens);
  StackRoot _root2(&parts);
  StackRoot _root3(&out);
  StackRoot _root4(&prev);
  StackRoot _root5(&ch);
  StackRoot _root6(&line_reader);
  StackRoot _root7(&c_parser);
  StackRoot _root8(&words);
  StackRoot _root9(&w);
  StackRoot _root10(&tok1);
  StackRoot _root11(&tok2);
  StackRoot _root12(&w1);
  StackRoot _root13(&w2);
  StackRoot _root14(&last_char);
  StackRoot _root15(&val);
  StackRoot _root16(&prefix);
  StackRoot _root17(&substring);
  StackRoot _root18(&cmd);

  if (!this->readline) {
    return line;
  }
  tokens = match::HistoryTokens(line);
  ok = true;
  for (ListIter<Tuple2<int, BigStr*>*> it(tokens); !it.Done(); it.Next()) {
    Tuple2<int, BigStr*>* tup0 = it.Value();
    int id_ = tup0->at0();
    if (id_ != Id::History_Other) {
      ok = false;
      break;
    }
  }
  if (ok) {
    return line;
  }
  history_len = this->readline->get_current_history_length();
  if (history_len <= 0) {
    return line;
  }
  this->debug_f->writeln(StrFormat("history length = %d", history_len));
  parts = Alloc<List<BigStr*>>();
  for (ListIter<Tuple2<int, BigStr*>*> it(tokens); !it.Done(); it.Next()) {
    Tuple2<int, BigStr*>* tup1 = it.Value();
    int id_ = tup1->at0();
    BigStr* val = tup1->at1();
    StackRoot _unpack_1(&val);
    if (id_ == Id::History_Other) {
      out = val;
    }
    else {
      if (id_ == Id::History_Op) {
        prev = this->readline->get_history_item(history_len);
        ch = val->at(1);
        if (str_equals(ch, S_kao)) {
          out = prev;
        }
        else {
          this->parse_ctx->trail->Clear();
          line_reader = reader::StringLineReader(prev, this->parse_ctx->arena);
          c_parser = this->parse_ctx->MakeOshParser(line_reader);
          try {
            c_parser->ParseLogicalLine();
          }
          catch (error::Parse* e) {
            this->debug_f->writeln(StrFormat("Couldn't parse historical command %r: %s", prev, e->UserErrorString()));
          }
          words = this->parse_ctx->trail->words;
          if (str_equals(ch, S_EAB)) {
            try {
              w = words->at(1);
            }
            catch (IndexError*) {
              throw Alloc<util::HistoryError>(StrFormat("No first word in %r", prev));
            }
            tok1 = location::LeftTokenForWord(w);
            tok2 = location::RightTokenForWord(w);
          }
          else {
            if (str_equals(ch, S_Czx)) {
              try {
                w = words->at(-1);
              }
              catch (IndexError*) {
                throw Alloc<util::HistoryError>(StrFormat("No last word in %r", prev));
              }
              tok1 = location::LeftTokenForWord(w);
              tok2 = location::RightTokenForWord(w);
            }
            else {
              if (str_equals(ch, S_Fgw)) {
                try {
                  w1 = words->at(1);
                  w2 = words->at(-1);
                }
                catch (IndexError*) {
                  throw Alloc<util::HistoryError>(StrFormat("Couldn't find words in %r", prev));
                }
                tok1 = location::LeftTokenForWord(w1);
                tok2 = location::RightTokenForWord(w2);
              }
              else {
                assert(0);  // AssertionError
              }
            }
          }
          begin = tok1->col;
          end = (tok2->col + tok2->length);
          out = prev->slice(begin, end);
        }
      }
      else {
        if (id_ == Id::History_Num) {
          index = to_int(val->slice(1));
          if (index < 0) {
            num = ((history_len + 1) + index);
          }
          else {
            num = index;
          }
          out = this->readline->get_history_item(num);
          if (out == nullptr) {
            throw Alloc<util::HistoryError>(StrFormat("%s: not found", val));
          }
        }
        else {
          if (id_ == Id::History_Search) {
            last_char = val->at(-1);
            val = val->slice(0, -1);
            prefix = nullptr;
            substring = S_Aoo;
            if (str_equals(val->at(1), S_BAk)) {
              substring = val->slice(2);
            }
            else {
              prefix = val->slice(1);
            }
            out = nullptr;
            for (int i = history_len; i > 1; i += -1) {
              cmd = this->readline->get_history_item(i);
              if ((prefix != nullptr and cmd->startswith(prefix))) {
                out = cmd;
              }
              if ((len(substring) and str_contains(cmd, substring))) {
                out = cmd;
              }
              if (out != nullptr) {
                out = str_concat(out, last_char);
                break;
              }
            }
            if (out == nullptr) {
              throw Alloc<util::HistoryError>(StrFormat("%r found no results", val));
            }
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
    }
    parts->append(out);
  }
  line = S_Aoo->join(parts);
  print(StrFormat("! %s", line));
  return line;
}

}  // define namespace history

namespace prompt {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using syntax_asdl::loc;
using syntax_asdl::command_t;
using syntax_asdl::source;
using syntax_asdl::CompoundWord;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::Obj;
BigStr* _ERROR_FMT = S_peu;
BigStr* _UNBALANCED_ERROR = S_fcg;

_PromptEvaluatorCache::_PromptEvaluatorCache() {
  this->cache = Alloc<Dict<BigStr*, BigStr*>>();
  this->euid = -1;
}

int _PromptEvaluatorCache::_GetEuid() {
  if (this->euid == -1) {
    this->euid = posix::geteuid();
  }
  return this->euid;
}

BigStr* _PromptEvaluatorCache::Get(BigStr* name) {
  BigStr* value = nullptr;
  StackRoot _root0(&name);
  StackRoot _root1(&value);

  if (dict_contains(this->cache, name)) {
    return this->cache->at(name);
  }
  if (str_equals(name, S_Czx)) {
    value = this->_GetEuid() == 0 ? S_qEd : S_Czx;
  }
  else {
    if (str_equals(name, S_qBe)) {
      value = libc::gethostname();
    }
    else {
      if (str_equals(name, S_sqx)) {
        value = pyos::GetUserName(this->_GetEuid());
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  this->cache->set(name, value);
  return value;
}

Evaluator::Evaluator(BigStr* lang, BigStr* version_str, parse_lib::ParseContext* parse_ctx, state::Mem* mem) {
  this->word_ev = nullptr;
  this->expr_ev = nullptr;
  this->global_io = nullptr;
  this->lang = lang;
  this->version_str = version_str;
  this->parse_ctx = parse_ctx;
  this->mem = mem;
  this->cache = Alloc<_PromptEvaluatorCache>();
  this->tokens_cache = Alloc<Dict<BigStr*, List<Tuple2<int, BigStr*>*>*>>();
  this->parse_cache = Alloc<Dict<BigStr*, syntax_asdl::CompoundWord*>>();
}

void Evaluator::CheckCircularDeps() {
}

BigStr* Evaluator::PromptVal(BigStr* what) {
  StackRoot _root0(&what);

  if (str_equals(what, S_qks)) {
    return StrFormat(_ERROR_FMT, S_uDe);
  }
  else {
    return this->PromptSubst(what);
  }
}

BigStr* Evaluator::PromptSubst(BigStr* ch, BigStr* arg) {
  BigStr* r = nullptr;
  BigStr* hostname = nullptr;
  double now;
  BigStr* fmt = nullptr;
  BigStr* home = nullptr;
  StackRoot _root0(&ch);
  StackRoot _root1(&arg);
  StackRoot _root2(&r);
  StackRoot _root3(&hostname);
  StackRoot _root4(&fmt);
  StackRoot _root5(&home);

  if (str_equals(ch, S_Czx)) {
    r = this->cache->Get(S_Czx);
  }
  else {
    if (str_equals(ch, S_rsz)) {
      r = this->cache->Get(S_sqx);
    }
    else {
      if (str_equals(ch, S_hjv)) {
        hostname = this->cache->Get(S_qBe);
        Tuple2<BigStr*, BigStr*> tup0 = mylib::split_once(hostname, S_Aru);
        r = tup0.at0();
      }
      else {
        if (str_equals(ch, S_ClC)) {
          r = this->cache->Get(S_qBe);
        }
        else {
          if (str_equals(ch, S_anC)) {
            r = this->lang;
          }
          else {
            if (str_equals(ch, S_Ado)) {
              r = this->version_str;
            }
            else {
              if (str_equals(ch, S_nlt)) {
                now = time_::time();
                r = time_::strftime(S_aia, time_::localtime(now));
              }
              else {
                if (str_equals(ch, S_qks)) {
                  now = time_::time();
                  if (len(arg) == 0) {
                    fmt = S_rdE;
                  }
                  else {
                    fmt = arg;
                  }
                  r = time_::strftime(fmt, time_::localtime(now));
                }
                else {
                  if (str_equals(ch, S_pfC)) {
                    home = state::MaybeString(this->mem, S_xlm);
                    r = ui::PrettyDir(this->mem->pwd, home);
                  }
                  else {
                    if (str_equals(ch, S_cpq)) {
                      r = os_path::basename(this->mem->pwd);
                    }
                    else {
                      r = consts::LookupCharPrompt(ch);
                      if (r == nullptr) {
                        r = StrFormat(_ERROR_FMT, StrFormat("\\%s is invalid or unimplemented in $PS1", ch));
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  return r;
}

BigStr* Evaluator::_ReplaceBackslashCodes(List<Tuple2<int, BigStr*>*>* tokens) {
  List<BigStr*>* ret = nullptr;
  int non_printing;
  int i;
  BigStr* ch = nullptr;
  BigStr* arg = nullptr;
  BigStr* r = nullptr;
  StackRoot _root0(&tokens);
  StackRoot _root1(&ret);
  StackRoot _root2(&ch);
  StackRoot _root3(&arg);
  StackRoot _root4(&r);

  ret = Alloc<List<BigStr*>>();
  non_printing = 0;
  for (ListIter<Tuple2<int, BigStr*>*> it(tokens); !it.Done(); it.Next()) {
    Tuple2<int, BigStr*>* tup1 = it.Value();
    int id_ = tup1->at0();
    BigStr* s = tup1->at1();
    StackRoot _unpack_1(&s);
    if ((id_ == Id::PS_Literals || id_ == Id::PS_BadBackslash)) {
      ret->append(s);
    }
    else {
      if (id_ == Id::PS_Octal3) {
        i = to_int(s->slice(1), 8);
        ret->append(chr((i % 256)));
      }
      else {
        if (id_ == Id::PS_LBrace) {
          non_printing += 1;
          ret->append(S_FDc);
        }
        else {
          if (id_ == Id::PS_RBrace) {
            non_printing -= 1;
            if (non_printing < 0) {
              return StrFormat(_ERROR_FMT, _UNBALANCED_ERROR);
            }
            ret->append(S_ewA);
          }
          else {
            if (id_ == Id::PS_Subst) {
              ch = s->at(1);
              arg = nullptr;
              if (str_equals(ch, S_qks)) {
                arg = s->slice(3, -1);
              }
              r = this->PromptSubst(ch, arg);
              ret->append(r->replace(S_Czx, S_xEe));
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
      }
    }
  }
  if (non_printing != 0) {
    return StrFormat(_ERROR_FMT, _UNBALANCED_ERROR);
  }
  return S_Aoo->join(ret);
}

BigStr* Evaluator::EvalPrompt(BigStr* s) {
  List<Tuple2<int, BigStr*>*>* tokens = nullptr;
  BigStr* ps1_str = nullptr;
  syntax_asdl::CompoundWord* ps1_word = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  value::Str* val2 = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&tokens);
  StackRoot _root2(&ps1_str);
  StackRoot _root3(&ps1_word);
  StackRoot _root4(&w_parser);
  StackRoot _root5(&val2);

  tokens = this->tokens_cache->get(s);
  if (tokens == nullptr) {
    tokens = match::Ps1Tokens(s);
    this->tokens_cache->set(s, tokens);
  }
  ps1_str = this->_ReplaceBackslashCodes(tokens);
  ps1_word = this->parse_cache->get(ps1_str);
  if (ps1_word == nullptr) {
    w_parser = this->parse_ctx->MakeWordParserForPlugin(ps1_str);
    try {
      ps1_word = w_parser->ReadForPlugin();
    }
    catch (error::Parse* e) {
      ps1_word = word_::ErrorWord(StrFormat("<ERROR: Can't parse PS1: %s>", e->UserErrorString()));
    }
    this->parse_cache->set(ps1_str, ps1_word);
  }
  val2 = this->word_ev->EvalForPlugin(ps1_word);
  return val2->s;
}

BigStr* Evaluator::EvalFirstPrompt() {
  value_asdl::value_t* UP_func_val = nullptr;
  List<value_asdl::value_t*>* pos_args = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  BigStr* msg = nullptr;
  value_asdl::value_t* ps1_val = nullptr;
  value_asdl::value_t* UP_ps1_val = nullptr;
  StackRoot _root0(&UP_func_val);
  StackRoot _root1(&pos_args);
  StackRoot _root2(&val);
  StackRoot _root3(&UP_val);
  StackRoot _root4(&msg);
  StackRoot _root5(&ps1_val);
  StackRoot _root6(&UP_ps1_val);

  UP_func_val = this->mem->GetValue(S_jAe);
  if (UP_func_val->tag() == value_e::Func) {
    value::Func* func_val = static_cast<value::Func*>(UP_func_val);
    pos_args = NewList<value_asdl::value_t*>(std::initializer_list<value_asdl::value_t*>{this->global_io});
    val = this->expr_ev->PluginCall(func_val, pos_args);
    UP_val = val;
    switch (val->tag()) {
      case value_e::Str: {
        value::Str* val = static_cast<value::Str*>(UP_val);
        return val->s;
      }
        break;
      default: {
        msg = StrFormat("renderPrompt() should return Str, got %s", ui::ValType(val));
        return StrFormat(_ERROR_FMT, msg);
      }
    }
  }
  ps1_val = this->mem->env_config->GetVal(S_Eni);
  UP_ps1_val = ps1_val;
  if (UP_ps1_val->tag() == value_e::Str) {
    value::Str* ps1_val = static_cast<value::Str*>(UP_ps1_val);
    return this->EvalPrompt(ps1_val->s);
  }
  else {
    return S_Aoo;
  }
}
BigStr* PROMPT_COMMAND = S_agy;

UserPlugin::UserPlugin(state::Mem* mem, parse_lib::ParseContext* parse_ctx, cmd_eval::CommandEvaluator* cmd_ev, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->parse_ctx = parse_ctx;
  this->cmd_ev = cmd_ev;
  this->errfmt = errfmt;
  this->arena = parse_ctx->arena;
  this->parse_cache = Alloc<Dict<BigStr*, syntax_asdl::command_t*>>();
}

void UserPlugin::Run() {
  value_asdl::value_t* val = nullptr;
  BigStr* prompt_cmd = nullptr;
  syntax_asdl::command_t* node = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  source::Variable* src = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&prompt_cmd);
  StackRoot _root2(&node);
  StackRoot _root3(&line_reader);
  StackRoot _root4(&c_parser);
  StackRoot _root5(&src);

  val = this->mem->GetValue(PROMPT_COMMAND);
  if (val->tag() != value_e::Str) {
    return ;
  }
  prompt_cmd = static_cast<value::Str*>(val)->s;
  node = this->parse_cache->get(prompt_cmd);
  if (node == nullptr) {
    line_reader = reader::StringLineReader(prompt_cmd, this->arena);
    c_parser = this->parse_ctx->MakeOshParser(line_reader);
    src = Alloc<source::Variable>(PROMPT_COMMAND, loc::Missing);
    {  // with
      alloc::ctx_SourceCode ctx{this->arena, src};

      try {
        node = main_loop::ParseWholeFile(c_parser);
      }
      catch (error::Parse* e) {
        this->errfmt->PrettyPrintError(e);
        return ;
      }
    }
    this->parse_cache->set(prompt_cmd, node);
  }
  {  // with
    state::ctx_Registers ctx{this->mem};

    this->cmd_ev->ExecuteAndCatch(node, 0);
  }
}

}  // define namespace prompt

namespace sh_expr_eval {  // define

using id_kind_asdl::Id;
using runtime_asdl::error_code_e;
using runtime_asdl::scope_t;
using syntax_asdl::word_t;
using syntax_asdl::CompoundWord;
using syntax_asdl::Token;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::source;
using syntax_asdl::arith_expr;
using syntax_asdl::arith_expr_e;
using syntax_asdl::arith_expr_t;
using syntax_asdl::bool_expr;
using syntax_asdl::bool_expr_e;
using syntax_asdl::bool_expr_t;
using syntax_asdl::sh_lhs;
using syntax_asdl::sh_lhs_e;
using syntax_asdl::sh_lhs_t;
using syntax_asdl::BracedVarSub;
using option_asdl::option_i;
using types_asdl::bool_arg_type_e;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::sh_lvalue;
using value_asdl::sh_lvalue_e;
using value_asdl::sh_lvalue_t;
using value_asdl::LeftName;
using value_asdl::eggex_ops;
using value_asdl::regex_match;
using value_asdl::RegexMatch;
using error::e_die;
using error::e_die_status;
using error::e_strict;
using error::e_usage;
using mylib::str_cmp;

value_asdl::value_t* OldValue(value_asdl::sh_lvalue_t* lval, state::Mem* mem, optview::Exec* exec_opts) {
  value_asdl::sh_lvalue_t* UP_lval = nullptr;
  BigStr* var_name = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  BigStr* s = nullptr;
  value::BashAssoc* assoc_val = nullptr;
  StackRoot _root0(&lval);
  StackRoot _root1(&mem);
  StackRoot _root2(&exec_opts);
  StackRoot _root3(&UP_lval);
  StackRoot _root4(&var_name);
  StackRoot _root5(&val);
  StackRoot _root6(&UP_val);
  StackRoot _root7(&s);
  StackRoot _root8(&assoc_val);

  UP_lval = lval;
  switch (lval->tag()) {
    case sh_lvalue_e::Var: {
      LeftName* lval = static_cast<LeftName*>(UP_lval);
      var_name = lval->name;
    }
      break;
    case sh_lvalue_e::Indexed: {
      sh_lvalue::Indexed* lval = static_cast<sh_lvalue::Indexed*>(UP_lval);
      var_name = lval->name;
    }
      break;
    case sh_lvalue_e::Keyed: {
      sh_lvalue::Keyed* lval = static_cast<sh_lvalue::Keyed*>(UP_lval);
      var_name = lval->name;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  val = mem->GetValue(var_name);
  if ((exec_opts and (exec_opts->nounset() and val->tag() == value_e::Undef))) {
    e_die(StrFormat("Undefined variable %r", var_name));
  }
  UP_val = val;
  switch (lval->tag()) {
    case sh_lvalue_e::Var: {
      return val;
    }
      break;
    case sh_lvalue_e::Indexed: {
      sh_lvalue::Indexed* lval = static_cast<sh_lvalue::Indexed*>(UP_lval);
      switch (val->tag()) {
        case value_e::Undef: {
          s = nullptr;
        }
          break;
        case value_e::BashArray: {
          value::BashArray* array_val = static_cast<value::BashArray*>(UP_val);
          Tuple2<BigStr*, runtime_asdl::error_code_t> tup0 = bash_impl::BashArray_GetElement(array_val, lval->index);
          s = tup0.at0();
        }
          break;
        case value_e::SparseArray: {
          value::SparseArray* sparse_val = static_cast<value::SparseArray*>(UP_val);
          Tuple2<BigStr*, runtime_asdl::error_code_t> tup1 = bash_impl::SparseArray_GetElement(sparse_val, mops::IntWiden(lval->index));
          s = tup1.at0();
        }
          break;
        default: {
          e_die(StrFormat("Can't use [] on value of type %s", ui::ValType(val)));
        }
      }
      if (s == nullptr) {
        val = Alloc<value::Str>(S_Aoo);
      }
      else {
        val = Alloc<value::Str>(s);
      }
    }
      break;
    case sh_lvalue_e::Keyed: {
      sh_lvalue::Keyed* lval = static_cast<sh_lvalue::Keyed*>(UP_lval);
      assoc_val = nullptr;
      switch (val->tag()) {
        case value_e::Undef: {
          assert(0);  // AssertionError
        }
          break;
        case value_e::BashAssoc: {
          value::BashAssoc* tmp2 = static_cast<value::BashAssoc*>(UP_val);
          assoc_val = tmp2;
          s = bash_impl::BashAssoc_GetElement(assoc_val, lval->key);
        }
          break;
        default: {
          e_die(StrFormat("Can't use [] on value of type %s", ui::ValType(val)));
        }
      }
      if (s == nullptr) {
        val = Alloc<value::Str>(S_Aoo);
      }
      else {
        val = Alloc<value::Str>(s);
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  return val;
}

UnsafeArith::UnsafeArith(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, parse_lib::ParseContext* parse_ctx, sh_expr_eval::ArithEvaluator* arith_ev, ui::ErrorFormatter* errfmt) {
  this->mem = mem;
  this->exec_opts = exec_opts;
  this->mutable_opts = mutable_opts;
  this->parse_ctx = parse_ctx;
  this->arith_ev = arith_ev;
  this->errfmt = errfmt;
  this->arena = this->parse_ctx->arena;
}

value_asdl::sh_lvalue_t* UnsafeArith::ParseLValue(BigStr* s, syntax_asdl::loc_t* location) {
  tdop::TdopParser* a_parser = nullptr;
  syntax_asdl::arith_expr_t* anode = nullptr;
  value_asdl::sh_lvalue_t* lval = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&location);
  StackRoot _root2(&a_parser);
  StackRoot _root3(&anode);
  StackRoot _root4(&lval);

  if (!this->parse_ctx->parse_opts->parse_sh_arith()) {
    if (!match::IsValidVarName(s)) {
      e_die(StrFormat("Invalid variable name %r (parse_sh_arith is off)", s), location);
    }
    return Alloc<LeftName>(s, location);
  }
  a_parser = this->parse_ctx->MakeArithParser(s);
  {  // with
    alloc::ctx_SourceCode ctx{this->arena, Alloc<source::Dynamic>(S_efa, location)};

    try {
      anode = a_parser->Parse();
    }
    catch (error::Parse* e) {
      this->errfmt->PrettyPrintError(e);
      e_usage(S_xDq, location);
    }
  }
  if (this->exec_opts->eval_unsafe_arith()) {
    lval = this->arith_ev->EvalArithLhs(anode);
  }
  else {
    {  // with
      state::ctx_Option ctx{this->mutable_opts, NewList<int>(std::initializer_list<int>{option_i::_allow_command_sub}), false};

      lval = this->arith_ev->EvalArithLhs(anode);
    }
  }
  return lval;
}

syntax_asdl::BracedVarSub* UnsafeArith::ParseVarRef(BigStr* ref_str, syntax_asdl::Token* blame_tok) {
  reader::FileLineReader* line_reader = nullptr;
  lexer::Lexer* lexer = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  source::VarRef* src = nullptr;
  syntax_asdl::BracedVarSub* bvs_part = nullptr;
  StackRoot _root0(&ref_str);
  StackRoot _root1(&blame_tok);
  StackRoot _root2(&line_reader);
  StackRoot _root3(&lexer);
  StackRoot _root4(&w_parser);
  StackRoot _root5(&src);
  StackRoot _root6(&bvs_part);

  line_reader = reader::StringLineReader(ref_str, this->arena);
  lexer = this->parse_ctx->MakeLexer(line_reader);
  w_parser = this->parse_ctx->MakeWordParser(lexer, line_reader);
  src = Alloc<source::VarRef>(blame_tok);
  {  // with
    alloc::ctx_SourceCode ctx{this->arena, src};

    try {
      bvs_part = w_parser->ParseVarRef();
    }
    catch (error::Parse* e) {
      this->errfmt->PrettyPrintError(e);
      e_die(S_aug, blame_tok);
    }
  }
  return bvs_part;
}

Tuple2<bool, mops::BigInt> _ParseOshInteger(BigStr* s, syntax_asdl::loc_t* blame_loc) {
  int id_;
  int pos;
  bool ok;
  mops::BigInt big_int;
  BigStr* b = nullptr;
  BigStr* digits = nullptr;
  int base;
  mops::BigInt integer;
  int digit;
  StackRoot _root0(&s);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&b);
  StackRoot _root3(&digits);

  Tuple2<int, int> tup2 = match::MatchShNumberToken(s, 0);
  id_ = tup2.at0();
  pos = tup2.at1();
  if (pos != len(s)) {
    return Tuple2<bool, mops::BigInt>(false, mops::BigInt(0));
  }
  if (id_ == Id::ShNumber_Dec) {
    Tuple2<bool, mops::BigInt> tup3 = mops::FromStr2(s);
    ok = tup3.at0();
    big_int = tup3.at1();
    if (!ok) {
      e_die(StrFormat("Integer too big: %s", s), blame_loc);
    }
    return Tuple2<bool, mops::BigInt>(true, big_int);
  }
  else {
    if (id_ == Id::ShNumber_Oct) {
      Tuple2<bool, mops::BigInt> tup4 = mops::FromStr2(s->slice(1), 8);
      ok = tup4.at0();
      big_int = tup4.at1();
      if (!ok) {
        e_die(StrFormat("Octal integer too big: %s", s), blame_loc);
      }
      return Tuple2<bool, mops::BigInt>(true, big_int);
    }
    else {
      if (id_ == Id::ShNumber_Hex) {
        Tuple2<bool, mops::BigInt> tup5 = mops::FromStr2(s->slice(2), 16);
        ok = tup5.at0();
        big_int = tup5.at1();
        if (!ok) {
          e_die(StrFormat("Hex integer too big: %s", s), blame_loc);
        }
        return Tuple2<bool, mops::BigInt>(true, big_int);
      }
      else {
        if (id_ == Id::ShNumber_BaseN) {
          Tuple2<BigStr*, BigStr*> tup6 = mylib::split_once(s, S_qEd);
          b = tup6.at0();
          digits = tup6.at1();
          try {
            base = to_int(b);
          }
          catch (ValueError*) {
            assert(0);  // AssertionError
          }
          if (base > 64) {
            e_strict(StrFormat("Base %d cannot be larger than 64", base), blame_loc);
          }
          if (base < 2) {
            e_strict(StrFormat("Base %d must be larger than 2", base), blame_loc);
          }
          integer = mops::ZERO;
          for (StrIter it(digits); !it.Done(); it.Next()) {
            BigStr* ch = it.Value();
            StackRoot _for(&ch          );
            if (IsLower(ch)) {
              digit = ((ord(ch) - ord(S_gCD)) + 10);
            }
            else {
              if (IsUpper(ch)) {
                digit = ((ord(ch) - ord(S_nlt)) + 36);
              }
              else {
                if (str_equals(ch, S_AeE)) {
                  digit = 62;
                }
                else {
                  if (str_equals(ch, S_tci)) {
                    digit = 63;
                  }
                  else {
                    if (ch->isdigit()) {
                      digit = to_int(ch);
                    }
                    else {
                      assert(0);  // AssertionError
                    }
                  }
                }
              }
            }
            if (digit >= base) {
              e_strict(StrFormat("Digits %r out of range for base %d", digits, base), blame_loc);
            }
            integer = mops::Add(mops::Mul(integer, mops::IntWiden(base)), mops::IntWiden(digit));
          }
          return Tuple2<bool, mops::BigInt>(true, integer);
        }
        else {
          return Tuple2<bool, mops::BigInt>(false, mops::BigInt(0));
        }
      }
    }
  }
}

ArithEvaluator::ArithEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, parse_lib::ParseContext* parse_ctx, ui::ErrorFormatter* errfmt) {
  this->word_ev = nullptr;
  this->mem = mem;
  this->exec_opts = exec_opts;
  this->mutable_opts = mutable_opts;
  this->parse_ctx = parse_ctx;
  this->errfmt = errfmt;
}

void ArithEvaluator::CheckCircularDeps() {
}

mops::BigInt ArithEvaluator::_StringToBigInt(BigStr* s, syntax_asdl::loc_t* blame_loc) {
  bool ok;
  mops::BigInt i;
  tdop::TdopParser* a_parser = nullptr;
  syntax_asdl::arith_expr_t* node2 = nullptr;
  mops::BigInt integer;
  StackRoot _root0(&s);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&a_parser);
  StackRoot _root3(&node2);

  s = s->strip();
  Tuple2<bool, mops::BigInt> tup7 = _ParseOshInteger(s, blame_loc);
  ok = tup7.at0();
  i = tup7.at1();
  if (ok) {
    return i;
  }
  if (this->parse_ctx == nullptr) {
    if ((len(s) == 0 or match::IsValidVarName(s))) {
      e_strict(StrFormat("Invalid integer constant %r", s), blame_loc);
    }
    else {
      e_die(StrFormat("Invalid integer constant %r", s), blame_loc);
    }
  }
  if (len(s) == 0) {
    return mops::ZERO;
  }
  a_parser = this->parse_ctx->MakeArithParser(s);
  try {
    node2 = a_parser->Parse();
  }
  catch (error::Parse* e) {
    this->errfmt->PrettyPrintError(e);
    e_die(S_ouz, e->location);
  }
  if (node2->tag() == arith_expr_e::Word) {
    e_die(StrFormat("Invalid integer constant %r", s), blame_loc);
  }
  if (this->exec_opts->eval_unsafe_arith()) {
    integer = this->EvalToBigInt(node2);
  }
  else {
    {  // with
      state::ctx_Option ctx{this->mutable_opts, NewList<int>(std::initializer_list<int>{option_i::_allow_command_sub}), false};

      integer = this->EvalToBigInt(node2);
    }
  }
  return integer;
}

mops::BigInt ArithEvaluator::_ValToIntOrError(value_asdl::value_t* val, syntax_asdl::arith_expr_t* blame) {
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&blame);
  StackRoot _root2(&UP_val);

  try {
    UP_val = val;
    switch (val->tag()) {
      case value_e::Undef: {
        e_strict(S_ukc, Alloc<loc::Arith>(blame));
      }
        break;
      case value_e::Int: {
        value::Int* val = static_cast<value::Int*>(UP_val);
        return val->i;
      }
        break;
      case value_e::Str: {
        value::Str* val = static_cast<value::Str*>(UP_val);
        return this->_StringToBigInt(val->s, Alloc<loc::Arith>(blame));
      }
        break;
    }
  }
  catch (error::Strict* e) {
    if (this->exec_opts->strict_arith()) {
      throw;
    }
    else {
      return mops::ZERO;
    }
  }
  e_die(StrFormat("Expected a value convertible to integer, got %s", ui::ValType(val)), Alloc<loc::Arith>(blame));
}

Tuple2<mops::BigInt, value_asdl::sh_lvalue_t*> ArithEvaluator::_EvalLhsAndLookupArith(syntax_asdl::arith_expr_t* node) {
  value_asdl::sh_lvalue_t* lval = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::LeftName* named_lval = nullptr;
  mops::BigInt i;
  StackRoot _root0(&node);
  StackRoot _root1(&lval);
  StackRoot _root2(&val);
  StackRoot _root3(&named_lval);

  lval = this->EvalArithLhs(node);
  val = OldValue(lval, this->mem, this->exec_opts);
  if (((val->tag() == value_e::BashArray || val->tag() == value_e::BashAssoc || val->tag() == value_e::SparseArray) and lval->tag() == sh_lvalue_e::Var)) {
    named_lval = static_cast<LeftName*>(lval);
    if (word_eval::ShouldArrayDecay(named_lval->name, this->exec_opts)) {
      if ((val->tag() == value_e::BashArray || val->tag() == value_e::SparseArray)) {
        lval = Alloc<sh_lvalue::Indexed>(named_lval->name, 0, loc::Missing);
      }
      else {
        if (val->tag() == value_e::BashAssoc) {
          lval = Alloc<sh_lvalue::Keyed>(named_lval->name, S_wfw, loc::Missing);
        }
      }
      val = word_eval::DecayArray(val);
    }
  }
  i = this->_ValToIntOrError(val, node);
  return Tuple2<mops::BigInt, value_asdl::sh_lvalue_t*>(i, lval);
}

void ArithEvaluator::_Store(value_asdl::sh_lvalue_t* lval, mops::BigInt new_int) {
  value::Str* val = nullptr;
  StackRoot _root0(&lval);
  StackRoot _root1(&val);

  val = Alloc<value::Str>(mops::ToStr(new_int));
  state::OshLanguageSetValue(this->mem, lval, val);
}

mops::BigInt ArithEvaluator::EvalToBigInt(syntax_asdl::arith_expr_t* node) {
  value_asdl::value_t* val = nullptr;
  syntax_asdl::Token* vsub = nullptr;
  mops::BigInt i;
  StackRoot _root0(&node);
  StackRoot _root1(&val);
  StackRoot _root2(&vsub);

  val = this->Eval(node);
  if (((val->tag() == value_e::BashArray || val->tag() == value_e::BashAssoc || val->tag() == value_e::SparseArray) and node->tag() == arith_expr_e::VarSub)) {
    vsub = static_cast<Token*>(node);
    if (word_eval::ShouldArrayDecay(lexer::LazyStr(vsub), this->exec_opts)) {
      val = word_eval::DecayArray(val);
    }
  }
  i = this->_ValToIntOrError(val, node);
  return i;
}

int ArithEvaluator::EvalToInt(syntax_asdl::arith_expr_t* node) {
  StackRoot _root0(&node);

  return mops::BigTruncate(this->EvalToBigInt(node));
}

value_asdl::value_t* ArithEvaluator::Eval(syntax_asdl::arith_expr_t* node) {
  syntax_asdl::arith_expr_t* UP_node = nullptr;
  BigStr* var_name = nullptr;
  value_asdl::value_t* val = nullptr;
  int op_id;
  mops::BigInt old_big;
  value_asdl::sh_lvalue_t* lval = nullptr;
  mops::BigInt new_big;
  mops::BigInt result;
  mops::BigInt rhs_big;
  mops::BigInt i;
  mops::BigInt lhs_big;
  value_asdl::value_t* left = nullptr;
  value_asdl::value_t* UP_left = nullptr;
  int small_i;
  BigStr* s = nullptr;
  runtime_asdl::error_code_t error_code;
  int small_length;
  mops::BigInt length;
  BigStr* key = nullptr;
  mops::BigInt index;
  mops::BigInt cond;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);
  StackRoot _root2(&var_name);
  StackRoot _root3(&val);
  StackRoot _root4(&lval);
  StackRoot _root5(&left);
  StackRoot _root6(&UP_left);
  StackRoot _root7(&s);
  StackRoot _root8(&key);

  UP_node = node;
  switch (node->tag()) {
    case arith_expr_e::EmptyZero: {
      return Alloc<value::Int>(mops::ZERO);
    }
      break;
    case arith_expr_e::EmptyOne: {
      return Alloc<value::Int>(mops::ONE);
    }
      break;
    case arith_expr_e::VarSub: {
      Token* vsub = static_cast<Token*>(UP_node);
      var_name = lexer::LazyStr(vsub);
      val = this->mem->GetValue(var_name);
      if ((val->tag() == value_e::Undef and this->exec_opts->nounset())) {
        e_die(StrFormat("Undefined variable %r", var_name), vsub);
      }
      return val;
    }
      break;
    case arith_expr_e::Word: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_node);
      return this->word_ev->EvalWordToString(w);
    }
      break;
    case arith_expr_e::UnaryAssign: {
      arith_expr::UnaryAssign* node = static_cast<arith_expr::UnaryAssign*>(UP_node);
      op_id = node->op_id;
      Tuple2<mops::BigInt, value_asdl::sh_lvalue_t*> tup8 = this->_EvalLhsAndLookupArith(node->child);
      old_big = tup8.at0();
      lval = tup8.at1();
      if (op_id == Id::Node_PostDPlus) {
        new_big = mops::Add(old_big, mops::ONE);
        result = old_big;
      }
      else {
        if (op_id == Id::Node_PostDMinus) {
          new_big = mops::Sub(old_big, mops::ONE);
          result = old_big;
        }
        else {
          if (op_id == Id::Arith_DPlus) {
            new_big = mops::Add(old_big, mops::ONE);
            result = new_big;
          }
          else {
            if (op_id == Id::Arith_DMinus) {
              new_big = mops::Sub(old_big, mops::ONE);
              result = new_big;
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
      }
      this->_Store(lval, new_big);
      return Alloc<value::Int>(result);
    }
      break;
    case arith_expr_e::BinaryAssign: {
      arith_expr::BinaryAssign* node = static_cast<arith_expr::BinaryAssign*>(UP_node);
      op_id = node->op_id;
      if (op_id == Id::Arith_Equal) {
        lval = this->EvalArithLhs(node->left);
        rhs_big = this->EvalToBigInt(node->right);
        this->_Store(lval, rhs_big);
        return Alloc<value::Int>(rhs_big);
      }
      Tuple2<mops::BigInt, value_asdl::sh_lvalue_t*> tup9 = this->_EvalLhsAndLookupArith(node->left);
      old_big = tup9.at0();
      lval = tup9.at1();
      rhs_big = this->EvalToBigInt(node->right);
      if (op_id == Id::Arith_PlusEqual) {
        new_big = mops::Add(old_big, rhs_big);
      }
      else {
        if (op_id == Id::Arith_MinusEqual) {
          new_big = mops::Sub(old_big, rhs_big);
        }
        else {
          if (op_id == Id::Arith_StarEqual) {
            new_big = mops::Mul(old_big, rhs_big);
          }
          else {
            if (op_id == Id::Arith_SlashEqual) {
              if (mops::Equal(rhs_big, mops::ZERO)) {
                e_die(S_Bdr);
              }
              new_big = mops::Div(old_big, rhs_big);
            }
            else {
              if (op_id == Id::Arith_PercentEqual) {
                if (mops::Equal(rhs_big, mops::ZERO)) {
                  e_die(S_Bdr);
                }
                new_big = mops::Rem(old_big, rhs_big);
              }
              else {
                if (op_id == Id::Arith_DGreatEqual) {
                  new_big = mops::RShift(old_big, rhs_big);
                }
                else {
                  if (op_id == Id::Arith_DLessEqual) {
                    new_big = mops::LShift(old_big, rhs_big);
                  }
                  else {
                    if (op_id == Id::Arith_AmpEqual) {
                      new_big = mops::BitAnd(old_big, rhs_big);
                    }
                    else {
                      if (op_id == Id::Arith_PipeEqual) {
                        new_big = mops::BitOr(old_big, rhs_big);
                      }
                      else {
                        if (op_id == Id::Arith_CaretEqual) {
                          new_big = mops::BitXor(old_big, rhs_big);
                        }
                        else {
                          assert(0);  // AssertionError
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
      this->_Store(lval, new_big);
      return Alloc<value::Int>(new_big);
    }
      break;
    case arith_expr_e::Unary: {
      arith_expr::Unary* node = static_cast<arith_expr::Unary*>(UP_node);
      op_id = node->op_id;
      i = this->EvalToBigInt(node->child);
      if (op_id == Id::Node_UnaryPlus) {
        result = i;
      }
      else {
        if (op_id == Id::Node_UnaryMinus) {
          result = mops::Sub(mops::ZERO, i);
        }
        else {
          if (op_id == Id::Arith_Bang) {
            if (mops::Equal(i, mops::ZERO)) {
              result = mops::ONE;
            }
            else {
              result = mops::ZERO;
            }
          }
          else {
            if (op_id == Id::Arith_Tilde) {
              result = mops::BitNot(i);
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
      }
      return Alloc<value::Int>(result);
    }
      break;
    case arith_expr_e::Binary: {
      arith_expr::Binary* node = static_cast<arith_expr::Binary*>(UP_node);
      op_id = node->op->id;
      if (op_id == Id::Arith_DPipe) {
        lhs_big = this->EvalToBigInt(node->left);
        if (mops::Equal(lhs_big, mops::ZERO)) {
          rhs_big = this->EvalToBigInt(node->right);
          if (mops::Equal(rhs_big, mops::ZERO)) {
            result = mops::ZERO;
          }
          else {
            result = mops::ONE;
          }
        }
        else {
          result = mops::ONE;
        }
        return Alloc<value::Int>(result);
      }
      if (op_id == Id::Arith_DAmp) {
        lhs_big = this->EvalToBigInt(node->left);
        if (mops::Equal(lhs_big, mops::ZERO)) {
          result = mops::ZERO;
        }
        else {
          rhs_big = this->EvalToBigInt(node->right);
          if (mops::Equal(rhs_big, mops::ZERO)) {
            result = mops::ZERO;
          }
          else {
            result = mops::ONE;
          }
        }
        return Alloc<value::Int>(result);
      }
      if (op_id == Id::Arith_LBracket) {
        left = this->Eval(node->left);
        UP_left = left;
        switch (left->tag()) {
          case value_e::BashArray: {
            value::BashArray* array_val = static_cast<value::BashArray*>(UP_left);
            small_i = mops::BigTruncate(this->EvalToBigInt(node->right));
            Tuple2<BigStr*, runtime_asdl::error_code_t> tup10 = bash_impl::BashArray_GetElement(array_val, small_i);
            s = tup10.at0();
            error_code = tup10.at1();
            if (error_code == error_code_e::IndexOutOfRange) {
              small_length = bash_impl::BashArray_Length(array_val);
              this->errfmt->Print_(StrFormat("Index %d out of bounds for array of length %d", small_i, small_length), node->op);
            }
          }
            break;
          case value_e::SparseArray: {
            value::SparseArray* sparse_val = static_cast<value::SparseArray*>(UP_left);
            i = this->EvalToBigInt(node->right);
            Tuple2<BigStr*, runtime_asdl::error_code_t> tup11 = bash_impl::SparseArray_GetElement(sparse_val, i);
            s = tup11.at0();
            error_code = tup11.at1();
            if (error_code == error_code_e::IndexOutOfRange) {
              length = bash_impl::SparseArray_Length(sparse_val);
              this->errfmt->Print_(StrFormat("Index %s out of bounds for array of length %s", mops::ToStr(i), mops::ToStr(length)), node->op);
            }
          }
            break;
          case value_e::BashAssoc: {
            value::BashAssoc* left = static_cast<value::BashAssoc*>(UP_left);
            key = this->EvalWordToString(node->right);
            s = bash_impl::BashAssoc_GetElement(left, key);
          }
            break;
          case value_e::Str: {
            value::Str* left = static_cast<value::Str*>(UP_left);
            if (this->exec_opts->strict_arith()) {
              e_die(S_Asx, node->op);
            }
            index = this->EvalToBigInt(node->right);
            s = mops::Equal(index, mops::ZERO) ? left->s : nullptr;
          }
            break;
          case value_e::Undef: {
            if (this->exec_opts->strict_arith()) {
              e_die(S_dyb, node->op);
            }
            s = nullptr;
          }
            break;
          default: {
            e_die(StrFormat("Value of type %s can't be indexed", ui::ValType(left)), node->op);
          }
        }
        if (s == nullptr) {
          val = value::Undef;
        }
        else {
          val = Alloc<value::Str>(s);
        }
        return val;
      }
      if (op_id == Id::Arith_Comma) {
        this->EvalToBigInt(node->left);
        result = this->EvalToBigInt(node->right);
        return Alloc<value::Int>(result);
      }
      lhs_big = this->EvalToBigInt(node->left);
      rhs_big = this->EvalToBigInt(node->right);
      if (op_id == Id::Arith_Plus) {
        result = mops::Add(lhs_big, rhs_big);
      }
      else {
        if (op_id == Id::Arith_Minus) {
          result = mops::Sub(lhs_big, rhs_big);
        }
        else {
          if (op_id == Id::Arith_Star) {
            result = mops::Mul(lhs_big, rhs_big);
          }
          else {
            if (op_id == Id::Arith_Slash) {
              if (mops::Equal(rhs_big, mops::ZERO)) {
                e_die(S_Bdr, node->op);
              }
              result = mops::Div(lhs_big, rhs_big);
            }
            else {
              if (op_id == Id::Arith_Percent) {
                if (mops::Equal(rhs_big, mops::ZERO)) {
                  e_die(S_Bdr, node->op);
                }
                result = mops::Rem(lhs_big, rhs_big);
              }
              else {
                if (op_id == Id::Arith_DStar) {
                  if (mops::Greater(mops::ZERO, rhs_big)) {
                    e_die(S_abr, Alloc<loc::Arith>(node->right));
                  }
                  result = num::Exponent(lhs_big, rhs_big);
                }
                else {
                  if (op_id == Id::Arith_DEqual) {
                    result = mops::FromBool(mops::Equal(lhs_big, rhs_big));
                  }
                  else {
                    if (op_id == Id::Arith_NEqual) {
                      result = mops::FromBool(!mops::Equal(lhs_big, rhs_big));
                    }
                    else {
                      if (op_id == Id::Arith_Great) {
                        result = mops::FromBool(mops::Greater(lhs_big, rhs_big));
                      }
                      else {
                        if (op_id == Id::Arith_GreatEqual) {
                          result = mops::FromBool((mops::Greater(lhs_big, rhs_big) or mops::Equal(lhs_big, rhs_big)));
                        }
                        else {
                          if (op_id == Id::Arith_Less) {
                            result = mops::FromBool(mops::Greater(rhs_big, lhs_big));
                          }
                          else {
                            if (op_id == Id::Arith_LessEqual) {
                              result = mops::FromBool((mops::Greater(rhs_big, lhs_big) or mops::Equal(lhs_big, rhs_big)));
                            }
                            else {
                              if (op_id == Id::Arith_Pipe) {
                                result = mops::BitOr(lhs_big, rhs_big);
                              }
                              else {
                                if (op_id == Id::Arith_Amp) {
                                  result = mops::BitAnd(lhs_big, rhs_big);
                                }
                                else {
                                  if (op_id == Id::Arith_Caret) {
                                    result = mops::BitXor(lhs_big, rhs_big);
                                  }
                                  else {
                                    if (op_id == Id::Arith_DLess) {
                                      if (mops::Greater(mops::ZERO, rhs_big)) {
                                        throw Alloc<error::Expr>(S_Clv, node->op);
                                      }
                                      result = mops::LShift(lhs_big, rhs_big);
                                    }
                                    else {
                                      if (op_id == Id::Arith_DGreat) {
                                        if (mops::Greater(mops::ZERO, rhs_big)) {
                                          throw Alloc<error::Expr>(S_tDc, node->op);
                                        }
                                        result = mops::RShift(lhs_big, rhs_big);
                                      }
                                      else {
                                        assert(0);  // AssertionError
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
      return Alloc<value::Int>(result);
    }
      break;
    case arith_expr_e::TernaryOp: {
      arith_expr::TernaryOp* node = static_cast<arith_expr::TernaryOp*>(UP_node);
      cond = this->EvalToBigInt(node->cond);
      if (mops::Equal(cond, mops::ZERO)) {
        return this->Eval(node->false_expr);
      }
      else {
        return this->Eval(node->true_expr);
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

BigStr* ArithEvaluator::EvalWordToString(syntax_asdl::arith_expr_t* node, syntax_asdl::loc_t* blame_loc) {
  syntax_asdl::arith_expr_t* UP_node = nullptr;
  value::Str* val = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&UP_node);
  StackRoot _root3(&val);

  UP_node = node;
  if (node->tag() == arith_expr_e::Word) {
    CompoundWord* w = static_cast<CompoundWord*>(UP_node);
    val = this->word_ev->EvalWordToString(w);
    return val->s;
  }
  else {
    e_die(S_ijz, blame_loc);
  }
}

value_asdl::sh_lvalue_t* ArithEvaluator::EvalShellLhs(syntax_asdl::sh_lhs_t* node, runtime_asdl::scope_t which_scopes) {
  syntax_asdl::sh_lhs_t* UP_node = nullptr;
  value_asdl::sh_lvalue_t* lval = nullptr;
  value_asdl::LeftName* lval1 = nullptr;
  BigStr* key = nullptr;
  sh_lvalue::Keyed* lval2 = nullptr;
  int index;
  sh_lvalue::Indexed* lval3 = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);
  StackRoot _root2(&lval);
  StackRoot _root3(&lval1);
  StackRoot _root4(&key);
  StackRoot _root5(&lval2);
  StackRoot _root6(&lval3);

  UP_node = node;
  lval = nullptr;
  switch (node->tag()) {
    case sh_lhs_e::Name: {
      sh_lhs::Name* node = static_cast<sh_lhs::Name*>(UP_node);
      lval1 = Alloc<LeftName>(node->name, node->left);
      lval = lval1;
    }
      break;
    case sh_lhs_e::IndexedName: {
      sh_lhs::IndexedName* node = static_cast<sh_lhs::IndexedName*>(UP_node);
      if (this->mem->IsBashAssoc(node->name)) {
        key = this->EvalWordToString(node->index, node->left);
        lval2 = Alloc<sh_lvalue::Keyed>(node->name, key, node->left);
        lval = lval2;
      }
      else {
        index = mops::BigTruncate(this->EvalToBigInt(node->index));
        lval3 = Alloc<sh_lvalue::Indexed>(node->name, index, node->left);
        lval = lval3;
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  return lval;
}

Tuple2<BigStr*, syntax_asdl::loc_t*> ArithEvaluator::_VarNameOrWord(syntax_asdl::arith_expr_t* anode) {
  syntax_asdl::arith_expr_t* UP_anode = nullptr;
  BigStr* var_name = nullptr;
  BigStr* no_str = nullptr;
  StackRoot _root0(&anode);
  StackRoot _root1(&UP_anode);
  StackRoot _root2(&var_name);
  StackRoot _root3(&no_str);

  UP_anode = anode;
  switch (anode->tag()) {
    case arith_expr_e::VarSub: {
      Token* tok = static_cast<Token*>(UP_anode);
      return Tuple2<BigStr*, syntax_asdl::loc_t*>(lexer::LazyStr(tok), tok);
    }
      break;
    case arith_expr_e::Word: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_anode);
      var_name = this->EvalWordToString(w);
      return Tuple2<BigStr*, syntax_asdl::loc_t*>(var_name, w);
    }
      break;
  }
  no_str = nullptr;
  return Tuple2<BigStr*, syntax_asdl::loc_t*>(no_str, loc::Missing);
}

value_asdl::sh_lvalue_t* ArithEvaluator::EvalArithLhs(syntax_asdl::arith_expr_t* anode) {
  syntax_asdl::arith_expr_t* UP_anode = nullptr;
  BigStr* var_name = nullptr;
  syntax_asdl::loc_t* blame_loc = nullptr;
  syntax_asdl::Token* arith_loc = nullptr;
  BigStr* key = nullptr;
  int index;
  StackRoot _root0(&anode);
  StackRoot _root1(&UP_anode);
  StackRoot _root2(&var_name);
  StackRoot _root3(&blame_loc);
  StackRoot _root4(&arith_loc);
  StackRoot _root5(&key);

  UP_anode = anode;
  if (anode->tag() == arith_expr_e::Binary) {
    arith_expr::Binary* anode = static_cast<arith_expr::Binary*>(UP_anode);
    if (anode->op->id == Id::Arith_LBracket) {
      Tuple2<BigStr*, syntax_asdl::loc_t*> tup12 = this->_VarNameOrWord(anode->left);
      var_name = tup12.at0();
      blame_loc = tup12.at1();
      if (!match::IsValidVarName(var_name)) {
        e_die(StrFormat("Invalid variable name %r", var_name), blame_loc);
      }
      if (var_name != nullptr) {
        if (this->mem->IsBashAssoc(var_name)) {
          arith_loc = location::TokenForArith(anode);
          key = this->EvalWordToString(anode->right, arith_loc);
          return Alloc<sh_lvalue::Keyed>(var_name, key, blame_loc);
        }
        else {
          index = mops::BigTruncate(this->EvalToBigInt(anode->right));
          return Alloc<sh_lvalue::Indexed>(var_name, index, blame_loc);
        }
      }
    }
  }
  Tuple2<BigStr*, syntax_asdl::loc_t*> tup13 = this->_VarNameOrWord(anode);
  var_name = tup13.at0();
  blame_loc = tup13.at1();
  if (var_name != nullptr) {
    return Alloc<LeftName>(var_name, blame_loc);
  }
  e_die_status(2, S_deg, blame_loc);
}

BoolEvaluator::BoolEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, parse_lib::ParseContext* parse_ctx, ui::ErrorFormatter* errfmt, bool bracket) : ::sh_expr_eval::ArithEvaluator(mem, exec_opts, mutable_opts, parse_ctx, errfmt) {
  this->bracket = bracket;
}

bool BoolEvaluator::_IsDefined(BigStr* s, syntax_asdl::loc_t* blame_loc) {
  List<BigStr*>* m = nullptr;
  BigStr* var_name = nullptr;
  BigStr* index_str = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  int index;
  bool result;
  runtime_asdl::error_code_t error_code;
  int length;
  mops::BigInt big_length;
  StackRoot _root0(&s);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&m);
  StackRoot _root3(&var_name);
  StackRoot _root4(&index_str);
  StackRoot _root5(&val);
  StackRoot _root6(&UP_val);

  m = util::RegexSearch(consts::TEST_V_RE, s);
  if (m == nullptr) {
    if (this->exec_opts->strict_word_eval()) {
      e_die(S_cem, blame_loc);
    }
    return false;
  }
  var_name = m->at(1);
  index_str = m->at(3);
  val = this->mem->GetValue(var_name);
  if (len(index_str) == 0) {
    return val->tag() != value_e::Undef;
  }
  UP_val = val;
  switch (val->tag()) {
    case value_e::BashArray: 
    case value_e::SparseArray: {
      try {
        index = to_int(index_str);
      }
      catch (ValueError* e) {
        if (this->exec_opts->strict_word_eval()) {
          e_die(StrFormat("-v got BashArray and invalid index %r", index_str), blame_loc);
        }
        return false;
      }
      if (val->tag() == value_e::BashArray) {
        value::BashArray* array_val = static_cast<value::BashArray*>(UP_val);
        Tuple2<bool, runtime_asdl::error_code_t> tup14 = bash_impl::BashArray_HasElement(array_val, index);
        result = tup14.at0();
        error_code = tup14.at1();
        if (error_code == error_code_e::IndexOutOfRange) {
          length = bash_impl::BashArray_Length(array_val);
          e_die(StrFormat("-v got index %s, which is out of bounds for array of length %d", index_str, length), blame_loc);
        }
      }
      else {
        if (val->tag() == value_e::SparseArray) {
          value::SparseArray* sparse_val = static_cast<value::SparseArray*>(UP_val);
          Tuple2<bool, runtime_asdl::error_code_t> tup15 = bash_impl::SparseArray_HasElement(sparse_val, mops::IntWiden(index));
          result = tup15.at0();
          error_code = tup15.at1();
          if (error_code == error_code_e::IndexOutOfRange) {
            big_length = bash_impl::SparseArray_Length(sparse_val);
            e_die(StrFormat("-v got index %s, which is out of bounds for array of length %s", index_str, mops::ToStr(big_length)), blame_loc);
          }
        }
        else {
          assert(0);  // AssertionError
        }
      }
      return result;
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      return bash_impl::BashAssoc_HasElement(val, index_str);
    }
      break;
    default: {
      ;  // pass
      if (this->exec_opts->strict_word_eval()) {
        throw Alloc<error::TypeErr>(val, S_sBF, blame_loc);
      }
      return false;
    }
  }
  assert(0);  // AssertionError
}

mops::BigInt BoolEvaluator::_StringToBigIntOrError(BigStr* s, syntax_asdl::loc_t* blame_loc) {
  bool ok;
  mops::BigInt i;
  StackRoot _root0(&s);
  StackRoot _root1(&blame_loc);

  if (this->bracket) {
    if (match::LooksLikeInteger(s)) {
      Tuple2<bool, mops::BigInt> tup16 = mops::FromStr2(s);
      ok = tup16.at0();
      i = tup16.at1();
    }
    else {
      ok = false;
    }
    if (!ok) {
      e_die(StrFormat("Invalid integer %r", s), blame_loc);
    }
    return i;
  }
  else {
    try {
      i = this->_StringToBigInt(s, blame_loc);
    }
    catch (error::Strict* e) {
      if ((this->bracket or this->exec_opts->strict_arith())) {
        throw;
      }
      else {
        i = mops::ZERO;
      }
    }
    return i;
  }
}

BigStr* BoolEvaluator::_EvalCompoundWord(syntax_asdl::word_t* word, int eval_flags) {
  value::Str* val = nullptr;
  StackRoot _root0(&word);
  StackRoot _root1(&val);

  val = this->word_ev->EvalWordToString(word, eval_flags);
  return val->s;
}

bool BoolEvaluator::EvalB(syntax_asdl::bool_expr_t* node) {
  syntax_asdl::bool_expr_t* UP_node = nullptr;
  BigStr* s = nullptr;
  bool b;
  int op_id;
  types_asdl::bool_arg_type_t arg_type;
  int index;
  int eval_flags;
  BigStr* s1 = nullptr;
  BigStr* s2 = nullptr;
  mops::BigInt i1;
  mops::BigInt i2;
  int fnmatch_flags;
  int regex_flags;
  List<int>* indices = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);
  StackRoot _root2(&s);
  StackRoot _root3(&s1);
  StackRoot _root4(&s2);
  StackRoot _root5(&indices);

  UP_node = node;
  switch (node->tag()) {
    case bool_expr_e::WordTest: {
      bool_expr::WordTest* node = static_cast<bool_expr::WordTest*>(UP_node);
      s = this->_EvalCompoundWord(node->w);
      return to_bool(s);
    }
      break;
    case bool_expr_e::LogicalNot: {
      bool_expr::LogicalNot* node = static_cast<bool_expr::LogicalNot*>(UP_node);
      b = this->EvalB(node->child);
      return !b;
    }
      break;
    case bool_expr_e::LogicalAnd: {
      bool_expr::LogicalAnd* node = static_cast<bool_expr::LogicalAnd*>(UP_node);
      if (this->EvalB(node->left)) {
        return this->EvalB(node->right);
      }
      else {
        return false;
      }
    }
      break;
    case bool_expr_e::LogicalOr: {
      bool_expr::LogicalOr* node = static_cast<bool_expr::LogicalOr*>(UP_node);
      if (this->EvalB(node->left)) {
        return true;
      }
      else {
        return this->EvalB(node->right);
      }
    }
      break;
    case bool_expr_e::Unary: {
      bool_expr::Unary* node = static_cast<bool_expr::Unary*>(UP_node);
      op_id = node->op_id;
      s = this->_EvalCompoundWord(node->child);
      arg_type = consts::BoolArgType(op_id);
      if (arg_type == bool_arg_type_e::Path) {
        return bool_stat::DoUnaryOp(op_id, s);
      }
      if (arg_type == bool_arg_type_e::Str) {
        if (op_id == Id::BoolUnary_z) {
          return !to_bool(s);
        }
        if (op_id == Id::BoolUnary_n) {
          return to_bool(s);
        }
        if (op_id == Id::BoolUnary_true) {
          return str_equals(s, S_FsF);
        }
        if (op_id == Id::BoolUnary_false) {
          return str_equals(s, S_Ctn);
        }
        assert(0);  // AssertionError
      }
      if (arg_type == bool_arg_type_e::Other) {
        if (op_id == Id::BoolUnary_t) {
          return bool_stat::isatty(s, node->child);
        }
        if (op_id == Id::BoolUnary_o) {
          index = consts::OptionNum(s);
          if (index == 0) {
            return false;
          }
          else {
            return this->exec_opts->opt0_array->at(index);
          }
        }
        if (op_id == Id::BoolUnary_v) {
          return this->_IsDefined(s, Alloc<loc::Word>(node->child));
        }
        e_die(StrFormat("%s isn't implemented", ui::PrettyId(op_id)));
      }
      assert(0);  // AssertionError
    }
      break;
    case bool_expr_e::Binary: {
      bool_expr::Binary* node = static_cast<bool_expr::Binary*>(UP_node);
      op_id = node->op_id;
      eval_flags = 0;
      switch (op_id) {
        case Id::BoolBinary_GlobEqual: 
        case Id::BoolBinary_GlobDEqual: 
        case Id::BoolBinary_GlobNEqual: {
          eval_flags |= word_eval::QUOTE_FNMATCH;
        }
          break;
        case Id::BoolBinary_EqualTilde: {
          eval_flags |= word_eval::QUOTE_ERE;
        }
          break;
      }
      s1 = this->_EvalCompoundWord(node->left);
      s2 = this->_EvalCompoundWord(node->right, eval_flags);
      arg_type = consts::BoolArgType(op_id);
      if (arg_type == bool_arg_type_e::Path) {
        return bool_stat::DoBinaryOp(op_id, s1, s2);
      }
      if (arg_type == bool_arg_type_e::Int) {
        i1 = this->_StringToBigIntOrError(s1, Alloc<loc::Word>(node->left));
        i2 = this->_StringToBigIntOrError(s2, Alloc<loc::Word>(node->right));
        if (op_id == Id::BoolBinary_eq) {
          return mops::Equal(i1, i2);
        }
        if (op_id == Id::BoolBinary_ne) {
          return !mops::Equal(i1, i2);
        }
        if (op_id == Id::BoolBinary_gt) {
          return mops::Greater(i1, i2);
        }
        if (op_id == Id::BoolBinary_ge) {
          return (mops::Greater(i1, i2) or mops::Equal(i1, i2));
        }
        if (op_id == Id::BoolBinary_lt) {
          return mops::Greater(i2, i1);
        }
        if (op_id == Id::BoolBinary_le) {
          return (mops::Greater(i2, i1) or mops::Equal(i1, i2));
        }
        assert(0);  // AssertionError
      }
      if (arg_type == bool_arg_type_e::Str) {
        fnmatch_flags = this->exec_opts->nocasematch() ? FNM_CASEFOLD : 0;
        if ((op_id == Id::BoolBinary_GlobEqual || op_id == Id::BoolBinary_GlobDEqual)) {
          return libc::fnmatch(s2, s1, fnmatch_flags);
        }
        if (op_id == Id::BoolBinary_GlobNEqual) {
          return !libc::fnmatch(s2, s1, fnmatch_flags);
        }
        if ((op_id == Id::BoolBinary_Equal || op_id == Id::BoolBinary_DEqual)) {
          return str_equals(s1, s2);
        }
        if (op_id == Id::BoolBinary_NEqual) {
          return !(str_equals(s1, s2));
        }
        if (op_id == Id::BoolBinary_EqualTilde) {
          regex_flags = this->exec_opts->nocasematch() ? REG_ICASE : 0;
          try {
            indices = libc::regex_search(s2, regex_flags, s1, 0);
          }
          catch (ValueError* e) {
            e_die_status(2, e->message, Alloc<loc::Word>(node->right));
          }
          if (indices != nullptr) {
            this->mem->SetRegexMatch(Alloc<RegexMatch>(s1, indices, eggex_ops::No));
            return true;
          }
          else {
            this->mem->SetRegexMatch(regex_match::No);
            return false;
          }
        }
        if (op_id == Id::Op_Less) {
          return str_cmp(s1, s2) < 0;
        }
        if (op_id == Id::Op_Great) {
          return str_cmp(s1, s2) > 0;
        }
        assert(0);  // AssertionError
      }
    }
      break;
  }
  assert(0);  // AssertionError
}

}  // define namespace sh_expr_eval

namespace split {  // define

using runtime_asdl::scope_e;
using runtime_asdl::span_e;
using runtime_asdl::emit_i;
using runtime_asdl::char_kind_i;
using runtime_asdl::state_i;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
BigStr* DEFAULT_IFS = S_xvt;

List<BigStr*>* _SpansToParts(BigStr* s, List<Tuple2<runtime_asdl::span_t, int>*>* spans) {
  List<mylib::BufWriter*>* parts = nullptr;
  int start_index;
  bool join_next;
  bool last_span_was_black;
  mylib::BufWriter* buf = nullptr;
  List<BigStr*>* result = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&spans);
  StackRoot _root2(&parts);
  StackRoot _root3(&buf);
  StackRoot _root4(&result);

  parts = Alloc<List<mylib::BufWriter*>>();
  start_index = 0;
  join_next = false;
  last_span_was_black = false;
  for (ListIter<Tuple2<runtime_asdl::span_t, int>*> it(spans); !it.Done(); it.Next()) {
    Tuple2<runtime_asdl::span_t, int>* tup0 = it.Value();
    runtime_asdl::span_t span_type = tup0->at0();
    int end_index = tup0->at1();
    if (span_type == span_e::Black) {
      if ((len(parts) and join_next)) {
        parts->at(-1)->write(s->slice(start_index, end_index));
        join_next = false;
      }
      else {
        buf = Alloc<mylib::BufWriter>();
        buf->write(s->slice(start_index, end_index));
        parts->append(buf);
      }
      last_span_was_black = true;
    }
    else {
      if (span_type == span_e::Backslash) {
        if (last_span_was_black) {
          join_next = true;
        }
        last_span_was_black = false;
      }
      else {
        last_span_was_black = false;
      }
    }
    start_index = end_index;
  }
  result = Alloc<List<BigStr*>>();
  for (ListIter<mylib::BufWriter*> it(parts); !it.Done(); it.Next()) {
    mylib::BufWriter* buf = it.Value();
    result->append(buf->getvalue());
  }
  return result;
}

SplitContext::SplitContext(state::Mem* mem) {
  this->mem = mem;
  this->splitters = Alloc<Dict<BigStr*, split::IfsSplitter*>>();
}

split::IfsSplitter* SplitContext::_GetSplitter(BigStr* ifs) {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  split::IfsSplitter* sp = nullptr;
  mylib::BufWriter* ifs_whitespace = nullptr;
  mylib::BufWriter* ifs_other = nullptr;
  StackRoot _root0(&ifs);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_val);
  StackRoot _root3(&sp);
  StackRoot _root4(&ifs_whitespace);
  StackRoot _root5(&ifs_other);

  if (ifs == nullptr) {
    val = this->mem->GetValue(S_nie, scope_e::Dynamic);
    UP_val = val;
    switch (val->tag()) {
      case value_e::Undef: {
        ifs = DEFAULT_IFS;
      }
        break;
      case value_e::Str: {
        value::Str* val = static_cast<value::Str*>(UP_val);
        ifs = val->s;
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  sp = this->splitters->get(ifs);
  if (sp == nullptr) {
    ifs_whitespace = Alloc<mylib::BufWriter>();
    ifs_other = Alloc<mylib::BufWriter>();
    for (StrIter it(ifs); !it.Done(); it.Next()) {
      BigStr* c = it.Value();
      StackRoot _for(&c    );
      if (str_contains(S_xvt, c)) {
        ifs_whitespace->write(c);
      }
      else {
        ifs_other->write(c);
      }
    }
    sp = Alloc<IfsSplitter>(ifs_whitespace->getvalue(), ifs_other->getvalue());
    this->splitters->set(ifs, sp);
  }
  return sp;
}

BigStr* SplitContext::GetJoinChar() {
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&UP_val);

  val = this->mem->GetValue(S_nie, scope_e::Dynamic);
  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      return S_yfw;
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      if (len(val->s)) {
        return val->s->at(0);
      }
      else {
        return S_Aoo;
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

BigStr* SplitContext::Escape(BigStr* s) {
  split::IfsSplitter* sp = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&sp);

  sp = this->_GetSplitter();
  return sp->Escape(s);
}

List<BigStr*>* SplitContext::SplitForWordEval(BigStr* s, BigStr* ifs) {
  split::IfsSplitter* sp = nullptr;
  List<Tuple2<runtime_asdl::span_t, int>*>* spans = nullptr;
  StackRoot _root0(&s);
  StackRoot _root1(&ifs);
  StackRoot _root2(&sp);
  StackRoot _root3(&spans);

  sp = this->_GetSplitter(ifs);
  spans = sp->Split(s, true);
  return _SpansToParts(s, spans);
}

List<Tuple2<runtime_asdl::span_t, int>*>* SplitContext::SplitForRead(BigStr* line, bool allow_escape, bool do_split) {
  BigStr* ifs = nullptr;
  split::IfsSplitter* sp = nullptr;
  StackRoot _root0(&line);
  StackRoot _root1(&ifs);
  StackRoot _root2(&sp);

  ifs = do_split ? nullptr : S_Aoo;
  sp = this->_GetSplitter(ifs);
  return sp->Split(line, allow_escape);
}

_BaseSplitter::_BaseSplitter(BigStr* escape_chars) {
  this->escape_chars = str_concat(escape_chars, S_iyu);
}

BigStr* _BaseSplitter::Escape(BigStr* s) {
  StackRoot _root0(&s);

  return pyutil::BackslashEscape(s, this->escape_chars);
}

IfsSplitter::IfsSplitter(BigStr* ifs_whitespace, BigStr* ifs_other) : ::split::_BaseSplitter(str_concat(ifs_whitespace, ifs_other)) {
  this->ifs_whitespace = ifs_whitespace;
  this->ifs_other = ifs_other;
}

List<Tuple2<runtime_asdl::span_t, int>*>* IfsSplitter::Split(BigStr* s, bool allow_escape) {
  BigStr* ws_chars = nullptr;
  BigStr* other_chars = nullptr;
  int n;
  List<Tuple2<runtime_asdl::span_t, int>*>* spans = nullptr;
  int i;
  int state;
  int byte;
  int ch;
  int new_state;
  int action;
  StackRoot _root0(&s);
  StackRoot _root1(&ws_chars);
  StackRoot _root2(&other_chars);
  StackRoot _root3(&spans);

  ws_chars = this->ifs_whitespace;
  other_chars = this->ifs_other;
  n = len(s);
  spans = Alloc<List<Tuple2<runtime_asdl::span_t, int>*>>();
  if (n == 0) {
    return spans;
  }
  i = 0;
  while ((i < n and mylib::ByteInSet(mylib::ByteAt(s, i), ws_chars))) {
    i += 1;
  }
  if (i != 0) {
    spans->append((Alloc<Tuple2<runtime_asdl::span_t, int>>(span_e::Delim, i)));
  }
  if (i == n) {
    return spans;
  }
  state = state_i::Start;
  while (state != state_i::Done) {
    if (i < n) {
      byte = mylib::ByteAt(s, i);
      if (mylib::ByteInSet(byte, ws_chars)) {
        ch = char_kind_i::DE_White;
      }
      else {
        if (mylib::ByteInSet(byte, other_chars)) {
          ch = char_kind_i::DE_Gray;
        }
        else {
          if ((allow_escape and mylib::ByteEquals(byte, S_iyu))) {
            ch = char_kind_i::Backslash;
          }
          else {
            ch = char_kind_i::Black;
          }
        }
      }
    }
    else {
      if (i == n) {
        ch = char_kind_i::Sentinel;
      }
      else {
        assert(0);  // AssertionError
      }
    }
    Tuple2<int, int> tup1 = consts::IfsEdge(state, ch);
    new_state = tup1.at0();
    action = tup1.at1();
    if (new_state == state_i::Invalid) {
      assert(0);  // AssertionError
    }
    if (action == emit_i::Part) {
      spans->append((Alloc<Tuple2<runtime_asdl::span_t, int>>(span_e::Black, i)));
    }
    else {
      if (action == emit_i::Delim) {
        spans->append((Alloc<Tuple2<runtime_asdl::span_t, int>>(span_e::Delim, i)));
      }
      else {
        if (action == emit_i::Empty) {
          spans->append((Alloc<Tuple2<runtime_asdl::span_t, int>>(span_e::Delim, i)));
          spans->append((Alloc<Tuple2<runtime_asdl::span_t, int>>(span_e::Black, i)));
        }
        else {
          if (action == emit_i::Escape) {
            spans->append((Alloc<Tuple2<runtime_asdl::span_t, int>>(span_e::Backslash, i)));
          }
          else {
            if (action == emit_i::Nothing) {
              ;  // pass
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
      }
    }
    state = new_state;
    i += 1;
  }
  return spans;
}

}  // define namespace split

namespace string_ops {  // define

using id_kind_asdl::Id;
using syntax_asdl::loc;
using syntax_asdl::Token;
using syntax_asdl::suffix_op;
using error::e_die;
using error::e_strict;
int UTF8_ERR_OVERLONG = -1;
int UTF8_ERR_SURROGATE = -2;
int UTF8_ERR_TOO_LARGE = -3;
int UTF8_ERR_BAD_ENCODING = -4;
int UTF8_ERR_TRUNCATED_BYTES = -5;

BigStr* Utf8Error_str(int error) {
  if (error == UTF8_ERR_OVERLONG) {
    return S_Fqc;
  }
  if (error == UTF8_ERR_SURROGATE) {
    return S_rof;
  }
  if (error == UTF8_ERR_TOO_LARGE) {
    return S_imE;
  }
  if (error == UTF8_ERR_BAD_ENCODING) {
    return S_qmF;
  }
  if (error == UTF8_ERR_TRUNCATED_BYTES) {
    return S_qsv;
  }
  assert(0);  // AssertionError
}

int DecodeUtf8Char(BigStr* s, int start) {
  int codepoint_or_error;
  int _bytes_read;
  StackRoot _root0(&s);

  Tuple2<int, int> tup0 = fastfunc::Utf8DecodeOne(s, start);
  codepoint_or_error = tup0.at0();
  _bytes_read = tup0.at1();
  if (codepoint_or_error < 0) {
    throw Alloc<error::Expr>(StrFormat("%s at offset %d in string of %d bytes", Utf8Error_str(codepoint_or_error), start, len(s)), loc::Missing);
  }
  return codepoint_or_error;
}

int NextUtf8Char(BigStr* s, int i) {
  int codepoint_or_error;
  int bytes_read;
  StackRoot _root0(&s);

  Tuple2<int, int> tup1 = fastfunc::Utf8DecodeOne(s, i);
  codepoint_or_error = tup1.at0();
  bytes_read = tup1.at1();
  if (codepoint_or_error < 0) {
    e_strict(StrFormat("%s at offset %d in string of %d bytes", Utf8Error_str(codepoint_or_error), i, len(s)), loc::Missing);
  }
  return (i + bytes_read);
}
BigStr* _INVALID_START = S_ucF;

int _Utf8CharLen(int starting_byte) {
  if ((starting_byte >> 7) == 0) {
    return 1;
  }
  else {
    if ((starting_byte >> 5) == 6) {
      return 2;
    }
    else {
      if ((starting_byte >> 4) == 14) {
        return 3;
      }
      else {
        if ((starting_byte >> 3) == 30) {
          return 4;
        }
        else {
          e_strict(_INVALID_START, loc::Missing);
        }
      }
    }
  }
}

int PreviousUtf8Char(BigStr* s, int i) {
  int orig_i;
  int byte_as_int;
  int offset;
  StackRoot _root0(&s);

  orig_i = i;
  while (i > 0) {
    i -= 1;
    byte_as_int = mylib::ByteAt(s, i);
    if ((byte_as_int >> 6) != 2) {
      offset = (orig_i - i);
      if (offset != _Utf8CharLen(byte_as_int)) {
        e_strict(_INVALID_START, loc::Missing);
      }
      return i;
    }
  }
  e_strict(_INVALID_START, loc::Missing);
}

int CountUtf8Chars(BigStr* s) {
  int num_chars;
  int num_bytes;
  int i;
  StackRoot _root0(&s);

  num_chars = 0;
  num_bytes = len(s);
  i = 0;
  while (i < num_bytes) {
    i = NextUtf8Char(s, i);
    num_chars += 1;
  }
  return num_chars;
}

int AdvanceUtf8Chars(BigStr* s, int num_chars, int byte_offset) {
  int num_bytes;
  int i;
  StackRoot _root0(&s);

  num_bytes = len(s);
  i = byte_offset;
  for (int _ = 0; _ < num_chars; ++_) {
    if (i >= num_bytes) {
      return i;
    }
    i = NextUtf8Char(s, i);
  }
  return i;
}
GLOBAL_LIST(SPACES, int, 8, {9 COMMA 10 COMMA 11 COMMA 12 COMMA 13 COMMA 32 COMMA 160 COMMA 65279});

bool _IsSpace(int codepoint) {
  return list_contains(SPACES, codepoint);
}

Tuple2<int, int> StartsWithWhitespaceByteRange(BigStr* s) {
  int len_s;
  int i;
  int codepoint;
  int start;
  int end;
  StackRoot _root0(&s);

  len_s = len(s);
  i = 0;
  while (i < len_s) {
    codepoint = DecodeUtf8Char(s, i);
    if (!_IsSpace(codepoint)) {
      break;
    }
    try {
      i = NextUtf8Char(s, i);
    }
    catch (error::Strict*) {
    }
  }
  start = 0;
  end = i;
  return Tuple2<int, int>(start, end);
}

Tuple2<int, int> EndsWithWhitespaceByteRange(BigStr* s) {
  int len_s;
  int i;
  int prev;
  int codepoint;
  int start;
  int end;
  StackRoot _root0(&s);

  len_s = len(s);
  i = len_s;
  while (i > 0) {
    prev = PreviousUtf8Char(s, i);
    codepoint = DecodeUtf8Char(s, prev);
    if (!_IsSpace(codepoint)) {
      break;
    }
    i = prev;
  }
  start = i;
  end = len_s;
  return Tuple2<int, int>(start, end);
}

BigStr* DoUnarySuffixOp(BigStr* s, syntax_asdl::Token* op_tok, BigStr* arg, bool is_extglob) {
  int id_;
  int n;
  int i;
  StackRoot _root0(&s);
  StackRoot _root1(&op_tok);
  StackRoot _root2(&arg);

  id_ = op_tok->id;
  if ((!is_extglob and !glob_::LooksLikeGlob(arg))) {
    arg = glob_::GlobUnescape(arg);
    if ((id_ == Id::VOp1_Pound || id_ == Id::VOp1_DPound)) {
      if ((len(arg) and s->startswith(arg))) {
        return s->slice(len(arg));
      }
      else {
        return s;
      }
    }
    else {
      if ((id_ == Id::VOp1_Percent || id_ == Id::VOp1_DPercent)) {
        if ((len(arg) and s->endswith(arg))) {
          return s->slice(0, -len(arg));
        }
        else {
          return s;
        }
      }
      else {
        if (id_ == Id::VOp1_Comma) {
          if (!(str_equals(arg, S_Aoo))) {
            e_die(StrFormat("%s can't have an argument", ui::PrettyId(id_)), op_tok);
          }
          if (len(s)) {
            return str_concat(s->at(0)->lower(), s->slice(1));
          }
          else {
            return s;
          }
        }
        else {
          if (id_ == Id::VOp1_DComma) {
            if (!(str_equals(arg, S_Aoo))) {
              e_die(StrFormat("%s can't have an argument", ui::PrettyId(id_)), op_tok);
            }
            return s->lower();
          }
          else {
            if (id_ == Id::VOp1_Caret) {
              if (!(str_equals(arg, S_Aoo))) {
                e_die(StrFormat("%s can't have an argument", ui::PrettyId(id_)), op_tok);
              }
              if (len(s)) {
                return str_concat(s->at(0)->upper(), s->slice(1));
              }
              else {
                return s;
              }
            }
            else {
              if (id_ == Id::VOp1_DCaret) {
                if (!(str_equals(arg, S_Aoo))) {
                  e_die(StrFormat("%s can't have an argument", ui::PrettyId(id_)), op_tok);
                }
                return s->upper();
              }
              else {
                assert(0);  // AssertionError
              }
            }
          }
        }
      }
    }
  }
  n = len(s);
  if (id_ == Id::VOp1_Pound) {
    i = 0;
    while (true) {
      if (libc::fnmatch(arg, s->slice(0, i))) {
        return s->slice(i);
      }
      if (i >= n) {
        break;
      }
      i = NextUtf8Char(s, i);
    }
    return s;
  }
  else {
    if (id_ == Id::VOp1_DPound) {
      i = n;
      while (true) {
        if (libc::fnmatch(arg, s->slice(0, i))) {
          return s->slice(i);
        }
        if (i == 0) {
          break;
        }
        i = PreviousUtf8Char(s, i);
      }
      return s;
    }
    else {
      if (id_ == Id::VOp1_Percent) {
        i = n;
        while (true) {
          if (libc::fnmatch(arg, s->slice(i))) {
            return s->slice(0, i);
          }
          if (i == 0) {
            break;
          }
          i = PreviousUtf8Char(s, i);
        }
        return s;
      }
      else {
        if (id_ == Id::VOp1_DPercent) {
          i = 0;
          while (true) {
            if (libc::fnmatch(arg, s->slice(i))) {
              return s->slice(0, i);
            }
            if (i >= n) {
              break;
            }
            i = NextUtf8Char(s, i);
          }
          return s;
        }
        else {
          FAIL(kNotImplemented);  // Python NotImplementedError
        }
      }
    }
  }
}

List<Tuple2<int, int>*>* _AllMatchPositions(BigStr* s, BigStr* regex) {
  List<Tuple2<int, int>*>* matches = nullptr;
  int pos;
  int n;
  Tuple2<int, int>* m = nullptr;
  int start;
  int end;
  StackRoot _root0(&s);
  StackRoot _root1(&regex);
  StackRoot _root2(&matches);
  StackRoot _root3(&m);

  matches = Alloc<List<Tuple2<int, int>*>>();
  pos = 0;
  n = len(s);
  while (pos < n) {
    m = libc::regex_first_group_match(regex, s, pos);
    if (m == nullptr) {
      break;
    }
    matches->append(m);
    Tuple2<int, int>* tup2 = m;
    start = tup2->at0();
    end = tup2->at1();
    pos = end;
  }
  return matches;
}

BigStr* _PatSubAll(BigStr* s, BigStr* regex, BigStr* replace_str) {
  List<BigStr*>* parts = nullptr;
  int prev_end;
  StackRoot _root0(&s);
  StackRoot _root1(&regex);
  StackRoot _root2(&replace_str);
  StackRoot _root3(&parts);

  parts = Alloc<List<BigStr*>>();
  prev_end = 0;
  for (ListIter<Tuple2<int, int>*> it(_AllMatchPositions(s, regex)); !it.Done(); it.Next()) {
    Tuple2<int, int>* tup3 = it.Value();
    int start = tup3->at0();
    int end = tup3->at1();
    parts->append(s->slice(prev_end, start));
    parts->append(replace_str);
    prev_end = end;
  }
  parts->append(s->slice(prev_end));
  return S_Aoo->join(parts);
}

GlobReplacer::GlobReplacer(BigStr* regex, BigStr* replace_str, syntax_asdl::Token* slash_tok) {
  this->regex = regex;
  this->replace_str = replace_str;
  this->slash_tok = slash_tok;
}

BigStr* GlobReplacer::Replace(BigStr* s, suffix_op::PatSub* op) {
  BigStr* regex = nullptr;
  BigStr* msg = nullptr;
  Tuple2<int, int>* m = nullptr;
  int start;
  int end;
  StackRoot _root0(&s);
  StackRoot _root1(&op);
  StackRoot _root2(&regex);
  StackRoot _root3(&msg);
  StackRoot _root4(&m);

  regex = StrFormat("(%s)", this->regex);
  if (op->replace_mode == Id::Lit_Slash) {
    if (len(this->regex) == 0) {
      return s;
    }
    try {
      return _PatSubAll(s, regex, this->replace_str);
    }
    catch (RuntimeError* e) {
      msg = e->message;
      e_die(StrFormat("Error matching regex %r: %s", regex, msg), this->slash_tok);
    }
  }
  if (op->replace_mode == Id::Lit_Pound) {
    regex = str_concat(S_EAB, regex);
  }
  else {
    if (op->replace_mode == Id::Lit_Percent) {
      regex = str_concat(regex, S_Czx);
    }
  }
  m = libc::regex_first_group_match(regex, s, 0);
  if (m == nullptr) {
    return s;
  }
  Tuple2<int, int>* tup4 = m;
  start = tup4->at0();
  end = tup4->at1();
  return str_concat(str_concat(s->slice(0, start), this->replace_str), s->slice(end));
}

BigStr* ShellQuoteB(BigStr* s) {
  StackRoot _root0(&s);

  s = s->replace(S_raD, S_FDn)->replace(S_nfs, S_ylr);
  return pyutil::BackslashEscape(s, S_cDi);
}

}  // define namespace string_ops

namespace tdop {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using syntax_asdl::loc;
using syntax_asdl::arith_expr;
using syntax_asdl::arith_expr_e;
using syntax_asdl::arith_expr_t;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::CompoundWord;
using syntax_asdl::Token;
using error::p_die;

bool IsIndexable(syntax_asdl::arith_expr_t* node) {
  StackRoot _root0(&node);

  switch (node->tag()) {
    case arith_expr_e::VarSub: 
    case arith_expr_e::Word: {
      return true;
    }
      break;
  }
  return false;
}

void CheckLhsExpr(syntax_asdl::arith_expr_t* node, syntax_asdl::word_t* blame_word) {
  syntax_asdl::arith_expr_t* UP_node = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&blame_word);
  StackRoot _root2(&UP_node);

  UP_node = node;
  if (node->tag() == arith_expr_e::Binary) {
    arith_expr::Binary* node = static_cast<arith_expr::Binary*>(UP_node);
    if ((node->op->id == Id::Arith_LBracket and IsIndexable(node->left))) {
      return ;
    }
  }
  if (IsIndexable(node)) {
    return ;
  }
  p_die(S_kkj, Alloc<loc::Word>(blame_word));
}

syntax_asdl::arith_expr_t* NullError(tdop::TdopParser* p, syntax_asdl::word_t* t, int bp) {
  StackRoot _root0(&p);
  StackRoot _root1(&t);

  p_die(S_Dmb, Alloc<loc::Word>(t));
  return nullptr;
}

syntax_asdl::arith_expr_t* NullConstant(tdop::TdopParser* p, syntax_asdl::word_t* w, int bp) {
  syntax_asdl::Token* name_tok = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&w);
  StackRoot _root2(&name_tok);

  name_tok = word_::LooksLikeArithVar(w);
  if (name_tok) {
    return name_tok;
  }
  return static_cast<CompoundWord*>(w);
}

syntax_asdl::arith_expr_t* NullParen(tdop::TdopParser* p, syntax_asdl::word_t* t, int bp) {
  syntax_asdl::arith_expr_t* r = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&t);
  StackRoot _root2(&r);

  r = p->ParseUntil(bp);
  p->Eat(Id::Arith_RParen);
  return r;
}

syntax_asdl::arith_expr_t* NullPrefixOp(tdop::TdopParser* p, syntax_asdl::word_t* w, int bp) {
  syntax_asdl::arith_expr_t* right = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&w);
  StackRoot _root2(&right);

  right = p->ParseUntil(bp);
  return Alloc<arith_expr::Unary>(word_::ArithId(w), right);
}

syntax_asdl::arith_expr_t* LeftError(tdop::TdopParser* p, syntax_asdl::word_t* t, syntax_asdl::arith_expr_t* left, int rbp) {
  StackRoot _root0(&p);
  StackRoot _root1(&t);
  StackRoot _root2(&left);

  p_die(S_vvB, Alloc<loc::Word>(t));
  return nullptr;
}

syntax_asdl::arith_expr_t* LeftBinaryOp(tdop::TdopParser* p, syntax_asdl::word_t* w, syntax_asdl::arith_expr_t* left, int rbp) {
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&w);
  StackRoot _root2(&left);
  StackRoot _root3(&tok);

  tok = static_cast<Token*>(w);
  return Alloc<arith_expr::Binary>(tok, left, p->ParseUntil(rbp));
}

syntax_asdl::arith_expr_t* LeftAssign(tdop::TdopParser* p, syntax_asdl::word_t* w, syntax_asdl::arith_expr_t* left, int rbp) {
  StackRoot _root0(&p);
  StackRoot _root1(&w);
  StackRoot _root2(&left);

  CheckLhsExpr(left, w);
  return Alloc<arith_expr::BinaryAssign>(word_::ArithId(w), left, p->ParseUntil(rbp));
}

TdopParser::TdopParser(tdop::ParserSpec* spec, word_parse::WordParser* w_parser, optview::Parse* parse_opts) {
  this->spec = spec;
  this->w_parser = w_parser;
  this->parse_opts = parse_opts;
  this->cur_word = nullptr;
  this->op_id = Id::Undefined_Tok;
}

int TdopParser::CurrentId() {
  return word_::ArithId(this->cur_word);
}

bool TdopParser::AtToken(int token_type) {
  return this->op_id == token_type;
}

void TdopParser::Eat(int token_type) {
  if (!this->AtToken(token_type)) {
    p_die(StrFormat("Parser expected %s, got %s", ui::PrettyId(token_type), ui::PrettyId(this->op_id)), Alloc<loc::Word>(this->cur_word));
  }
  this->Next();
}

bool TdopParser::Next() {
  this->cur_word = this->w_parser->ReadArithWord();
  this->op_id = word_::ArithId(this->cur_word);
  return true;
}

syntax_asdl::arith_expr_t* TdopParser::ParseUntil(int rbp) {
  syntax_asdl::word_t* t = nullptr;
  tdop::NullInfo* null_info = nullptr;
  syntax_asdl::arith_expr_t* node = nullptr;
  tdop::LeftInfo* left_info = nullptr;
  StackRoot _root0(&t);
  StackRoot _root1(&null_info);
  StackRoot _root2(&node);
  StackRoot _root3(&left_info);

  if ((this->op_id == Id::Eof_Real || this->op_id == Id::Eof_RParen || this->op_id == Id::Eof_Backtick)) {
    p_die(S_dtB, Alloc<loc::Word>(this->cur_word));
  }
  t = this->cur_word;
  null_info = this->spec->LookupNud(this->op_id);
  this->Next();
  node = null_info->nud(this, t, null_info->bp);
  while (true) {
    t = this->cur_word;
    left_info = this->spec->LookupLed(this->op_id);
    if (rbp >= left_info->lbp) {
      break;
    }
    this->Next();
    node = left_info->led(this, t, node, left_info->rbp);
  }
  return node;
}

syntax_asdl::arith_expr_t* TdopParser::Parse() {
  this->Next();
  if (!this->parse_opts->parse_sh_arith()) {
    p_die(S_tFx, Alloc<loc::Word>(this->cur_word));
  }
  return this->ParseUntil(0);
}

}  // define namespace tdop

namespace word_ {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Kind;
using id_kind_asdl::Id_t;
using id_kind_asdl::Kind_t;
using syntax_asdl::Token;
using syntax_asdl::CompoundWord;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::SingleQuoted;
using syntax_asdl::word;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::word_str;
using syntax_asdl::word_part;
using syntax_asdl::word_part_t;
using syntax_asdl::word_part_e;
using syntax_asdl::AssocPair;

int LiteralId(syntax_asdl::word_part_t* p) {
  syntax_asdl::word_part_t* UP_part = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&UP_part);

  UP_part = p;
  if (p->tag() == word_part_e::Literal) {
    return static_cast<Token*>(UP_part)->id;
  }
  else {
    return Id::Undefined_Tok;
  }
}

Tuple3<bool, BigStr*, bool> _EvalWordPart(syntax_asdl::word_part_t* part) {
  syntax_asdl::word_part_t* UP_part = nullptr;
  BigStr* s = nullptr;
  List<BigStr*>* strs = nullptr;
  bool ok;
  StackRoot _root0(&part);
  StackRoot _root1(&UP_part);
  StackRoot _root2(&s);
  StackRoot _root3(&strs);

  UP_part = part;
  switch (part->tag()) {
    case word_part_e::Literal: {
      Token* tok = static_cast<Token*>(UP_part);
      return Tuple3<bool, BigStr*, bool>(true, lexer::TokenVal(tok), false);
    }
      break;
    case word_part_e::EscapedLiteral: {
      word_part::EscapedLiteral* part = static_cast<word_part::EscapedLiteral*>(UP_part);
      s = lexer::TokenSliceLeft(part->token, 1);
      return Tuple3<bool, BigStr*, bool>(true, s, true);
    }
      break;
    case word_part_e::SingleQuoted: {
      SingleQuoted* part = static_cast<SingleQuoted*>(UP_part);
      return Tuple3<bool, BigStr*, bool>(true, part->sval, true);
    }
      break;
    case word_part_e::DoubleQuoted: {
      DoubleQuoted* part = static_cast<DoubleQuoted*>(UP_part);
      strs = Alloc<List<BigStr*>>();
      for (ListIter<syntax_asdl::word_part_t*> it(part->parts); !it.Done(); it.Next()) {
        syntax_asdl::word_part_t* p = it.Value();
        StackRoot _for(&p      );
        Tuple3<bool, BigStr*, bool> tup0 = _EvalWordPart(p);
        ok = tup0.at0();
        s = tup0.at1();
        if (!ok) {
          return Tuple3<bool, BigStr*, bool>(false, S_Aoo, true);
        }
        strs->append(s);
      }
      return Tuple3<bool, BigStr*, bool>(true, S_Aoo->join(strs), true);
    }
      break;
    case word_part_e::ShArrayLiteral: 
    case word_part_e::BashAssocLiteral: 
    case word_part_e::ZshVarSub: 
    case word_part_e::CommandSub: 
    case word_part_e::SimpleVarSub: 
    case word_part_e::BracedVarSub: 
    case word_part_e::TildeSub: 
    case word_part_e::ArithSub: 
    case word_part_e::ExtGlob: 
    case word_part_e::Splice: 
    case word_part_e::ExprSub: {
      return Tuple3<bool, BigStr*, bool>(false, S_Aoo, false);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

BigStr* FastStrEval(syntax_asdl::CompoundWord* w) {
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::word_part_t* UP_part0 = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&part0);
  StackRoot _root2(&UP_part0);

  if (len(w->parts) != 1) {
    return nullptr;
  }
  part0 = w->parts->at(0);
  UP_part0 = part0;
  switch (part0->tag()) {
    case word_part_e::Literal: {
      Token* part0 = static_cast<Token*>(UP_part0);
      if ((part0->id == Id::Lit_Chars || part0->id == Id::Lit_LBracket || part0->id == Id::Lit_RBracket)) {
        return lexer::LazyStr(part0);
      }
      else {
        return nullptr;
      }
    }
      break;
    case word_part_e::SingleQuoted: {
      SingleQuoted* part0 = static_cast<SingleQuoted*>(UP_part0);
      return part0->sval;
    }
      break;
    default: {
      return nullptr;
    }
  }
}

Tuple3<bool, BigStr*, bool> StaticEval(syntax_asdl::word_t* UP_w) {
  bool quoted;
  List<BigStr*>* strs = nullptr;
  bool ok;
  BigStr* s = nullptr;
  bool q;
  StackRoot _root0(&UP_w);
  StackRoot _root1(&strs);
  StackRoot _root2(&s);

  quoted = false;
  if (UP_w->tag() != word_e::Compound) {
    return Tuple3<bool, BigStr*, bool>(false, S_Aoo, quoted);
  }
  CompoundWord* w = static_cast<CompoundWord*>(UP_w);
  strs = Alloc<List<BigStr*>>();
  for (ListIter<syntax_asdl::word_part_t*> it(w->parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    Tuple3<bool, BigStr*, bool> tup1 = _EvalWordPart(part);
    ok = tup1.at0();
    s = tup1.at1();
    q = tup1.at2();
    if (!ok) {
      return Tuple3<bool, BigStr*, bool>(false, S_Aoo, quoted);
    }
    if (q) {
      quoted = true;
    }
    strs->append(s);
  }
  return Tuple3<bool, BigStr*, bool>(true, S_Aoo->join(strs), quoted);
}

syntax_asdl::CompoundWord* TildeDetect(syntax_asdl::word_t* UP_w) {
  StackRoot _root0(&UP_w);

  if (UP_w->tag() != word_e::Compound) {
    return nullptr;
  }
  CompoundWord* w = static_cast<CompoundWord*>(UP_w);
  return TildeDetect2(w);
}

syntax_asdl::CompoundWord* TildeDetect2(syntax_asdl::CompoundWord* w) {
  syntax_asdl::word_part_t* part0 = nullptr;
  int id0;
  syntax_asdl::Token* tok0 = nullptr;
  List<syntax_asdl::word_part_t*>* new_parts = nullptr;
  int id1;
  syntax_asdl::Token* tok1 = nullptr;
  int id2;
  StackRoot _root0(&w);
  StackRoot _root1(&part0);
  StackRoot _root2(&tok0);
  StackRoot _root3(&new_parts);
  StackRoot _root4(&tok1);

  if (len(w->parts) == 0) {
    return nullptr;
  }
  part0 = w->parts->at(0);
  id0 = LiteralId(part0);
  if (id0 != Id::Lit_Tilde) {
    return nullptr;
  }
  tok0 = static_cast<Token*>(part0);
  new_parts = Alloc<List<syntax_asdl::word_part_t*>>();
  if (len(w->parts) == 1) {
    new_parts->append(Alloc<word_part::TildeSub>(tok0, nullptr, nullptr));
    return Alloc<CompoundWord>(new_parts);
  }
  id1 = LiteralId(w->parts->at(1));
  if (id1 == Id::Lit_Slash) {
    new_parts->append(Alloc<word_part::TildeSub>(tok0, nullptr, nullptr));
    new_parts->extend(w->parts->slice(1));
    return Alloc<CompoundWord>(new_parts);
  }
  if (id1 != Id::Lit_Chars) {
    return nullptr;
  }
  tok1 = static_cast<Token*>(w->parts->at(1));
  if (len(w->parts) == 2) {
    new_parts->append(Alloc<word_part::TildeSub>(tok0, tok1, lexer::TokenVal(tok1)));
    return Alloc<CompoundWord>(new_parts);
  }
  id2 = LiteralId(w->parts->at(2));
  if (id2 != Id::Lit_Slash) {
    return nullptr;
  }
  new_parts->append(Alloc<word_part::TildeSub>(tok0, tok1, lexer::TokenVal(tok1)));
  new_parts->extend(w->parts->slice(2));
  return Alloc<CompoundWord>(new_parts);
}

void TildeDetectAssign(syntax_asdl::CompoundWord* w) {
  List<syntax_asdl::word_part_t*>* parts = nullptr;
  bool has_tilde;
  List<syntax_asdl::word_part_t*>* new_parts = nullptr;
  bool tilde_could_be_next;
  int i;
  int n;
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::word_part_t* part1 = nullptr;
  syntax_asdl::word_part_t* part2 = nullptr;
  syntax_asdl::Token* tok0 = nullptr;
  int id1;
  syntax_asdl::Token* tok1 = nullptr;
  int id2;
  StackRoot _root0(&w);
  StackRoot _root1(&parts);
  StackRoot _root2(&new_parts);
  StackRoot _root3(&part0);
  StackRoot _root4(&part1);
  StackRoot _root5(&part2);
  StackRoot _root6(&tok0);
  StackRoot _root7(&tok1);

  parts = w->parts;
  has_tilde = false;
  for (ListIter<syntax_asdl::word_part_t*> it(parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    if (LiteralId(part) == Id::Lit_Tilde) {
      has_tilde = true;
      break;
    }
  }
  if (!has_tilde) {
    return ;
  }
  parts->append(nullptr);
  parts->append(nullptr);
  new_parts = Alloc<List<syntax_asdl::word_part_t*>>();
  tilde_could_be_next = true;
  i = 0;
  n = len(parts);
  while (i < n) {
    part0 = parts->at(i);
    if (part0 == nullptr) {
      break;
    }
    if ((tilde_could_be_next and LiteralId(part0) == Id::Lit_Tilde)) {
      part1 = parts->at((i + 1));
      part2 = parts->at((i + 2));
      tok0 = static_cast<Token*>(part0);
      if (part1 == nullptr) {
        new_parts->append(Alloc<word_part::TildeSub>(tok0, nullptr, nullptr));
        break;
      }
      id1 = LiteralId(part1);
      if ((id1 == Id::Lit_Slash || id1 == Id::Lit_Colon)) {
        new_parts->append(Alloc<word_part::TildeSub>(tok0, nullptr, nullptr));
        new_parts->append(part1);
        i += 2;
        continue;
      }
      if (id1 != Id::Lit_Chars) {
        new_parts->append(part0);
        new_parts->append(part1);
        i += 2;
        continue;
      }
      tok1 = static_cast<Token*>(part1);
      if (part2 == nullptr) {
        new_parts->append(Alloc<word_part::TildeSub>(tok0, tok1, lexer::TokenVal(tok1)));
        break;
      }
      id2 = LiteralId(part2);
      if ((id2 != Id::Lit_Slash && id2 != Id::Lit_Colon)) {
        new_parts->append(part0);
        new_parts->append(part1);
        new_parts->append(part2);
        i += 3;
        continue;
      }
      new_parts->append(Alloc<word_part::TildeSub>(tok0, tok1, lexer::TokenVal(tok1)));
      new_parts->append(part2);
      i += 3;
      tilde_could_be_next = id2 == Id::Lit_Colon;
    }
    else {
      new_parts->append(part0);
      i += 1;
      tilde_could_be_next = LiteralId(part0) == Id::Lit_Colon;
    }
  }
  parts->pop();
  parts->pop();
  w->parts = new_parts;
}

List<syntax_asdl::word_t*>* TildeDetectAll(List<syntax_asdl::word_t*>* words) {
  List<syntax_asdl::word_t*>* out = nullptr;
  syntax_asdl::CompoundWord* t = nullptr;
  StackRoot _root0(&words);
  StackRoot _root1(&out);
  StackRoot _root2(&t);

  out = Alloc<List<syntax_asdl::word_t*>>();
  for (ListIter<syntax_asdl::word_t*> it(words); !it.Done(); it.Next()) {
    syntax_asdl::word_t* w = it.Value();
    StackRoot _for(&w  );
    t = TildeDetect(w);
    if (t) {
      out->append(t);
    }
    else {
      out->append(w);
    }
  }
  return out;
}

bool HasArrayPart(syntax_asdl::CompoundWord* w) {
  StackRoot _root0(&w);

  for (ListIter<syntax_asdl::word_part_t*> it(w->parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    if (part->tag() == word_part_e::ShArrayLiteral) {
      return true;
    }
  }
  return false;
}

BigStr* ShFunctionName(syntax_asdl::CompoundWord* w) {
  bool ok;
  BigStr* s = nullptr;
  bool quoted;
  StackRoot _root0(&w);
  StackRoot _root1(&s);

  Tuple3<bool, BigStr*, bool> tup2 = StaticEval(w);
  ok = tup2.at0();
  s = tup2.at1();
  quoted = tup2.at2();
  if ((!ok or quoted)) {
    return S_Aoo;
  }
  return s;
}

syntax_asdl::Token* LooksLikeArithVar(syntax_asdl::word_t* UP_w) {
  syntax_asdl::word_part_t* UP_part0 = nullptr;
  StackRoot _root0(&UP_w);
  StackRoot _root1(&UP_part0);

  if (UP_w->tag() != word_e::Compound) {
    return nullptr;
  }
  CompoundWord* w = static_cast<CompoundWord*>(UP_w);
  if (len(w->parts) != 1) {
    return nullptr;
  }
  UP_part0 = w->parts->at(0);
  if (LiteralId(UP_part0) != Id::Lit_ArithVarLike) {
    return nullptr;
  }
  return static_cast<Token*>(UP_part0);
}

bool IsVarLike(syntax_asdl::CompoundWord* w) {
  StackRoot _root0(&w);

  if (len(w->parts) == 0) {
    return false;
  }
  return LiteralId(w->parts->at(0)) == Id::Lit_VarLike;
}

Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int> DetectShAssignment(syntax_asdl::CompoundWord* w) {
  syntax_asdl::Token* no_token = nullptr;
  int n;
  syntax_asdl::word_part_t* UP_part0 = nullptr;
  int id0;
  syntax_asdl::word_part_t* UP_part = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&no_token);
  StackRoot _root2(&UP_part0);
  StackRoot _root3(&UP_part);

  no_token = nullptr;
  n = len(w->parts);
  if (n == 0) {
    return Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int>(no_token, no_token, 0);
  }
  UP_part0 = w->parts->at(0);
  id0 = LiteralId(UP_part0);
  if (id0 == Id::Lit_VarLike) {
    Token* tok = static_cast<Token*>(UP_part0);
    return Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int>(tok, no_token, 1);
  }
  if (id0 == Id::Lit_ArrayLhsOpen) {
    Token* tok0 = static_cast<Token*>(UP_part0);
    if (n < 2) {
      return Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int>(no_token, no_token, 0);
    }
    for (int i = 1; i < n; ++i) {
      UP_part = w->parts->at(i);
      if (LiteralId(UP_part) == Id::Lit_ArrayLhsClose) {
        Token* tok_close = static_cast<Token*>(UP_part);
        return Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int>(tok0, tok_close, (i + 1));
      }
    }
  }
  return Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int>(no_token, no_token, 0);
}

syntax_asdl::AssocPair* DetectAssocPair(syntax_asdl::CompoundWord* w) {
  List<syntax_asdl::word_part_t*>* parts = nullptr;
  int n;
  int id_;
  syntax_asdl::CompoundWord* key = nullptr;
  syntax_asdl::CompoundWord* value = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&parts);
  StackRoot _root2(&key);
  StackRoot _root3(&value);

  parts = w->parts;
  if (LiteralId(parts->at(0)) != Id::Lit_LBracket) {
    return nullptr;
  }
  n = len(parts);
  for (int i = 0; i < n; ++i) {
    id_ = LiteralId(parts->at(i));
    if (id_ == Id::Lit_ArrayLhsClose) {
      key = Alloc<CompoundWord>(parts->slice(1, i));
      value = Alloc<CompoundWord>(parts->slice((i + 1)));
      return Alloc<AssocPair>(key, value);
    }
  }
  return nullptr;
}

Tuple2<id_kind_asdl::Kind_t, syntax_asdl::Token*> IsControlFlow(syntax_asdl::CompoundWord* w) {
  syntax_asdl::Token* no_token = nullptr;
  syntax_asdl::word_part_t* UP_part0 = nullptr;
  int token_type;
  id_kind_asdl::Kind_t token_kind;
  StackRoot _root0(&w);
  StackRoot _root1(&no_token);
  StackRoot _root2(&UP_part0);

  no_token = nullptr;
  if (len(w->parts) != 1) {
    return Tuple2<id_kind_asdl::Kind_t, syntax_asdl::Token*>(Kind::Undefined, no_token);
  }
  UP_part0 = w->parts->at(0);
  token_type = LiteralId(UP_part0);
  if (token_type == Id::Undefined_Tok) {
    return Tuple2<id_kind_asdl::Kind_t, syntax_asdl::Token*>(Kind::Undefined, no_token);
  }
  token_kind = consts::GetKind(token_type);
  if (token_kind == Kind::ControlFlow) {
    return Tuple2<id_kind_asdl::Kind_t, syntax_asdl::Token*>(token_kind, static_cast<Token*>(UP_part0));
  }
  return Tuple2<id_kind_asdl::Kind_t, syntax_asdl::Token*>(Kind::Undefined, no_token);
}

syntax_asdl::Token* LiteralToken(syntax_asdl::word_t* UP_w) {
  syntax_asdl::word_part_t* part0 = nullptr;
  StackRoot _root0(&UP_w);
  StackRoot _root1(&part0);

  CompoundWord* w = static_cast<CompoundWord*>(UP_w);
  if (len(w->parts) != 1) {
    return nullptr;
  }
  part0 = w->parts->at(0);
  if (part0->tag() == word_part_e::Literal) {
    return static_cast<Token*>(part0);
  }
  return nullptr;
}

syntax_asdl::Token* BraceToken(syntax_asdl::word_t* UP_w) {
  StackRoot _root0(&UP_w);

  switch (UP_w->tag()) {
    case word_e::Operator: {
      Token* tok = static_cast<Token*>(UP_w);
      return tok;
    }
      break;
    case word_e::Compound: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_w);
      return LiteralToken(w);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

syntax_asdl::Token* AsKeywordToken(syntax_asdl::word_t* UP_w) {
  syntax_asdl::word_part_t* part = nullptr;
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&UP_w);
  StackRoot _root1(&part);
  StackRoot _root2(&tok);

  CompoundWord* w = static_cast<CompoundWord*>(UP_w);
  part = w->parts->at(0);
  tok = static_cast<Token*>(part);
  return tok;
}

syntax_asdl::Token* AsOperatorToken(syntax_asdl::word_t* word) {
  StackRoot _root0(&word);

  return static_cast<Token*>(word);
}

int ArithId(syntax_asdl::word_t* w) {
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&tok);

  if (w->tag() == word_e::Operator) {
    tok = static_cast<Token*>(w);
    return tok->id;
  }
  return Id::Word_Compound;
}

int BoolId(syntax_asdl::word_t* w) {
  syntax_asdl::word_t* UP_w = nullptr;
  int token_type;
  id_kind_asdl::Kind_t token_kind;
  StackRoot _root0(&w);
  StackRoot _root1(&UP_w);

  UP_w = w;
  switch (w->tag()) {
    case word_e::String: {
      word::String* w = static_cast<word::String*>(UP_w);
      return w->id;
    }
      break;
    case word_e::Operator: {
      Token* tok = static_cast<Token*>(UP_w);
      return tok->id;
    }
      break;
    case word_e::Compound: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_w);
      if (len(w->parts) != 1) {
        return Id::Word_Compound;
      }
      token_type = LiteralId(w->parts->at(0));
      if (token_type == Id::Undefined_Tok) {
        return Id::Word_Compound;
      }
      if ((token_type == Id::KW_Bang || token_type == Id::Lit_DRightBracket)) {
        return token_type;
      }
      token_kind = consts::GetKind(token_type);
      if ((token_kind == Kind::BoolUnary || token_kind == Kind::BoolBinary)) {
        return token_type;
      }
      return Id::Word_Compound;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

int CommandId(syntax_asdl::word_t* w) {
  syntax_asdl::word_t* UP_w = nullptr;
  int token_type;
  id_kind_asdl::Kind_t token_kind;
  StackRoot _root0(&w);
  StackRoot _root1(&UP_w);

  UP_w = w;
  switch (w->tag()) {
    case word_e::Operator: {
      Token* tok = static_cast<Token*>(UP_w);
      return tok->id;
    }
      break;
    case word_e::Compound: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_w);
      if (len(w->parts) != 1) {
        return Id::Word_Compound;
      }
      token_type = LiteralId(w->parts->at(0));
      if (token_type == Id::Undefined_Tok) {
        return Id::Word_Compound;
      }
      if ((token_type == Id::Lit_LBrace || token_type == Id::Lit_RBrace || token_type == Id::Lit_Equals || token_type == Id::Lit_TDot)) {
        return token_type;
      }
      token_kind = consts::GetKind(token_type);
      if (token_kind == Kind::KW) {
        return token_type;
      }
      return Id::Word_Compound;
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

id_kind_asdl::Kind_t CommandKind(syntax_asdl::word_t* w) {
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&tok);

  if (w->tag() == word_e::Operator) {
    tok = static_cast<Token*>(w);
    return consts::GetKind(tok->id);
  }
  return Kind::Word;
}

bool IsVarSub(syntax_asdl::word_t* w) {
  StackRoot _root0(&w);

  return false;
}

syntax_asdl::CompoundWord* ErrorWord(BigStr* error_str) {
  syntax_asdl::Token* t = nullptr;
  StackRoot _root0(&error_str);
  StackRoot _root1(&t);

  t = lexer::DummyToken(Id::Lit_Chars, error_str);
  return Alloc<CompoundWord>(NewList<syntax_asdl::word_part_t*>(std::initializer_list<syntax_asdl::word_part_t*>{t}));
}

BigStr* Pretty(syntax_asdl::word_t* w) {
  syntax_asdl::word_t* UP_w = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&UP_w);

  UP_w = w;
  if (w->tag() == word_e::String) {
    word::String* w = static_cast<word::String*>(UP_w);
    if (w->id == Id::Eof_Real) {
      return S_ngj;
    }
    else {
      return repr(w->s);
    }
  }
  else {
    return word_str(w->tag());
  }
}

ctx_EmitDocToken::ctx_EmitDocToken(word_parse::WordParser* w_parser) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->w_parser)));
  w_parser->EmitDocToken(true);
  this->w_parser = w_parser;
}

ctx_EmitDocToken::~ctx_EmitDocToken() {
  this->w_parser->EmitDocToken(false);
  gHeap.PopRoot();
}

ctx_Multiline::ctx_Multiline(word_parse::WordParser* w_parser) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->w_parser)));
  w_parser->Multiline(true);
  this->w_parser = w_parser;
}

ctx_Multiline::~ctx_Multiline() {
  this->w_parser->Multiline(false);
  gHeap.PopRoot();
}

}  // define namespace word_

namespace word_compile {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using id_kind_asdl::Id_str;
using syntax_asdl::Token;
using syntax_asdl::CharCode;
using syntax_asdl::word_part_e;
using syntax_asdl::word_part_t;
using error::p_die;

syntax_asdl::CharCode* EvalCharLiteralForRegex(syntax_asdl::Token* tok) {
  int id_;
  BigStr* value = nullptr;
  BigStr* s = nullptr;
  int i;
  BigStr* one_char_str = nullptr;
  StackRoot _root0(&tok);
  StackRoot _root1(&value);
  StackRoot _root2(&s);
  StackRoot _root3(&one_char_str);

  id_ = tok->id;
  value = lexer::TokenVal(tok);
  switch (id_) {
    case Id::Char_UBraced: {
      s = lexer::TokenSlice(tok, 3, -1);
      i = to_int(s, 16);
      return Alloc<CharCode>(tok, i, true);
    }
      break;
    case Id::Char_OneChar: {
      one_char_str = consts::LookupCharC(value->at(1));
      return Alloc<CharCode>(tok, ord(one_char_str), false);
    }
      break;
    case Id::Char_Hex: {
      s = lexer::TokenSliceLeft(tok, 2);
      i = to_int(s, 16);
      return Alloc<CharCode>(tok, i, false);
    }
      break;
    case Id::Lit_Chars: 
    case Id::Expr_Name: 
    case Id::Expr_DecInt: {
      return Alloc<CharCode>(tok, ord(value->at(0)), false);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

BigStr* EvalCStringToken(int id_, BigStr* value) {
  int code_point;
  BigStr* c = nullptr;
  BigStr* s = nullptr;
  int i;
  StackRoot _root0(&value);
  StackRoot _root1(&c);
  StackRoot _root2(&s);

  code_point = -1;
  if ((id_ == Id::Lit_Chars || id_ == Id::Lit_CharsWithoutPrefix || id_ == Id::Unknown_Backslash)) {
    return value;
  }
  else {
    if (id_ == Id::Right_SingleQuote) {
      return value;
    }
    else {
      if (id_ == Id::Char_OneChar) {
        c = value->at(1);
        return consts::LookupCharC(c);
      }
      else {
        if (id_ == Id::Char_Stop) {
          return nullptr;
        }
        else {
          if ((id_ == Id::Char_Octal3 || id_ == Id::Char_Octal4)) {
            if (id_ == Id::Char_Octal3) {
              s = value->slice(1);
            }
            else {
              s = value->slice(2);
            }
            i = to_int(s, 8);
            if (i >= 256) {
              i = (i % 256);
            }
            return chr(i);
          }
          else {
            if ((id_ == Id::Char_Hex || id_ == Id::Char_YHex)) {
              s = value->slice(2);
              i = to_int(s, 16);
              return chr(i);
            }
            else {
              if ((id_ == Id::Char_Unicode4 || id_ == Id::Char_Unicode8)) {
                s = value->slice(2);
                code_point = to_int(s, 16);
                return j8::Utf8Encode(code_point);
              }
              else {
                if (id_ == Id::Char_UBraced) {
                  s = value->slice(3, -1);
                  code_point = to_int(s, 16);
                  return j8::Utf8Encode(code_point);
                }
                else {
                  assert(0);  // AssertionError
                }
              }
            }
          }
        }
      }
    }
  }
}

BigStr* EvalSingleQuoted(int id_, List<syntax_asdl::Token*>* tokens) {
  List<BigStr*>* strs = nullptr;
  BigStr* s = nullptr;
  int code_point;
  StackRoot _root0(&tokens);
  StackRoot _root1(&strs);
  StackRoot _root2(&s);

  if ((id_ == Id::Left_SingleQuote || id_ == Id::Left_RSingleQuote || id_ == Id::Left_TSingleQuote || id_ == Id::Left_RTSingleQuote)) {
    strs = Alloc<List<BigStr*>>();
    for (ListIter<syntax_asdl::Token*> it(tokens); !it.Done(); it.Next()) {
      syntax_asdl::Token* t = it.Value();
      strs->append(lexer::TokenVal(t));
    }
  }
  else {
    if ((id_ == Id::Left_DollarSingleQuote || id_ == Id::Left_USingleQuote || id_ == Id::Left_BSingleQuote || id_ == Id::Left_UTSingleQuote || id_ == Id::Left_BTSingleQuote)) {
      strs = Alloc<List<BigStr*>>();
      for (ListIter<syntax_asdl::Token*> it(tokens); !it.Done(); it.Next()) {
        syntax_asdl::Token* t = it.Value();
        StackRoot _for(&t      );
        if (t->id == Id::Char_UBraced) {
          s = lexer::TokenSlice(t, 3, -1);
          code_point = to_int(s, 16);
          if (code_point > 1114111) {
            p_die(S_egA, t);
          }
          if ((55296 <= code_point and code_point < 57344)) {
            p_die(StrFormat("%s escape is illegal because it's in the surrogate range", lexer::TokenVal(t)), t);
          }
        }
        strs->append(EvalCStringToken(t->id, lexer::TokenVal(t)));
      }
    }
    else {
      assert(0);  // AssertionError
    }
  }
  return S_Aoo->join(strs);
}

bool _TokenConsistsOf(syntax_asdl::Token* tok, BigStr* byte_set) {
  int start;
  int end;
  int b;
  StackRoot _root0(&tok);
  StackRoot _root1(&byte_set);

  start = tok->col;
  end = (tok->col + tok->length);
  for (int i = start; i < end; ++i) {
    b = mylib::ByteAt(tok->line->content, i);
    if (!mylib::ByteInSet(b, byte_set)) {
      return false;
    }
  }
  return true;
}

bool _IsLeadingSpace(syntax_asdl::Token* tok) {
  StackRoot _root0(&tok);

  return _TokenConsistsOf(tok, S_jEs);
}

bool _IsTrailingSpace(syntax_asdl::Token* tok) {
  StackRoot _root0(&tok);

  return _TokenConsistsOf(tok, S_Dqk);
}

void RemoveLeadingSpaceDQ(List<syntax_asdl::word_part_t*>* parts) {
  syntax_asdl::word_part_t* UP_first = nullptr;
  syntax_asdl::word_part_t* UP_last = nullptr;
  BigStr* to_strip = nullptr;
  int n;
  syntax_asdl::Token* lit_tok = nullptr;
  StackRoot _root0(&parts);
  StackRoot _root1(&UP_first);
  StackRoot _root2(&UP_last);
  StackRoot _root3(&to_strip);
  StackRoot _root4(&lit_tok);

  if (len(parts) <= 1) {
    return ;
  }
  UP_first = parts->at(0);
  if (UP_first->tag() == word_part_e::Literal) {
    Token* first = static_cast<Token*>(UP_first);
    if (_IsTrailingSpace(first)) {
      parts->pop(0);
    }
  }
  UP_last = parts->at(-1);
  to_strip = nullptr;
  if (UP_last->tag() == word_part_e::Literal) {
    Token* last = static_cast<Token*>(UP_last);
    if (_IsLeadingSpace(last)) {
      to_strip = lexer::TokenVal(last);
      parts->pop();
    }
  }
  if (to_strip == nullptr) {
    return ;
  }
  n = len(to_strip);
  for (ListIter<syntax_asdl::word_part_t*> it(parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* part = it.Value();
    StackRoot _for(&part  );
    if (part->tag() != word_part_e::Literal) {
      continue;
    }
    lit_tok = static_cast<Token*>(part);
    if ((lit_tok->col == 0 and lexer::TokenStartsWith(lit_tok, to_strip))) {
      lit_tok->col = n;
      lit_tok->length -= n;
      lit_tok->id = Id::Lit_CharsWithoutPrefix;
    }
  }
}

void RemoveLeadingSpaceSQ(List<syntax_asdl::Token*>* tokens) {
  syntax_asdl::Token* first = nullptr;
  syntax_asdl::Token* last = nullptr;
  BigStr* to_strip = nullptr;
  int n;
  StackRoot _root0(&tokens);
  StackRoot _root1(&first);
  StackRoot _root2(&last);
  StackRoot _root3(&to_strip);

  if (len(tokens) <= 1) {
    return ;
  }
  first = tokens->at(0);
  if (first->id == Id::Lit_Chars) {
    if (_IsTrailingSpace(first)) {
      tokens->pop(0);
    }
  }
  last = tokens->at(-1);
  to_strip = nullptr;
  if (last->id == Id::Lit_Chars) {
    if (_IsLeadingSpace(last)) {
      to_strip = lexer::TokenVal(last);
      tokens->pop();
    }
  }
  if (to_strip == nullptr) {
    return ;
  }
  n = len(to_strip);
  for (ListIter<syntax_asdl::Token*> it(tokens); !it.Done(); it.Next()) {
    syntax_asdl::Token* tok = it.Value();
    StackRoot _for(&tok  );
    if ((tok->col == 0 and lexer::TokenStartsWith(tok, to_strip))) {
      tok->col = n;
      tok->length -= n;
      tok->id = Id::Lit_CharsWithoutPrefix;
    }
  }
}

}  // define namespace word_compile

namespace word_eval {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Kind;
using id_kind_asdl::Kind_str;
using syntax_asdl::Token;
using syntax_asdl::SimpleVarSub;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::BracedVarSub;
using syntax_asdl::CommandSub;
using syntax_asdl::bracket_op;
using syntax_asdl::bracket_op_e;
using syntax_asdl::suffix_op;
using syntax_asdl::suffix_op_e;
using syntax_asdl::ShArrayLiteral;
using syntax_asdl::SingleQuoted;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::CompoundWord;
using syntax_asdl::rhs_word;
using syntax_asdl::rhs_word_e;
using syntax_asdl::rhs_word_t;
using syntax_asdl::word_part;
using syntax_asdl::word_part_e;
using runtime_asdl::part_value;
using runtime_asdl::part_value_e;
using runtime_asdl::part_value_t;
using runtime_asdl::cmd_value;
using runtime_asdl::cmd_value_e;
using runtime_asdl::cmd_value_t;
using runtime_asdl::error_code_e;
using runtime_asdl::AssignArg;
using runtime_asdl::a_index;
using runtime_asdl::a_index_e;
using runtime_asdl::VTestPlace;
using runtime_asdl::VarSubState;
using runtime_asdl::Piece;
using option_asdl::option_i;
using option_asdl::builtin_i;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::sh_lvalue;
using value_asdl::sh_lvalue_t;
using error::e_die;
int QUOTED = (1 << 0);
int IS_SUBST = (1 << 1);
int EXTGLOB_FILES = (1 << 2);
int EXTGLOB_MATCH = (1 << 3);
int EXTGLOB_NESTED = (1 << 4);
int QUOTE_FNMATCH = (1 << 5);
int QUOTE_ERE = (1 << 6);
GLOBAL_LIST(_STRING_AND_ARRAY, BigStr*, 3, {S_lqk COMMA S_lCr COMMA S_Dyf});

bool ShouldArrayDecay(BigStr* var_name, optview::Exec* exec_opts, bool is_plain_var_sub) {
  StackRoot _root0(&var_name);
  StackRoot _root1(&exec_opts);

  return (!exec_opts->strict_array() or (is_plain_var_sub and list_contains(_STRING_AND_ARRAY, var_name)));
}

value_asdl::value_t* DecayArray(value_asdl::value_t* val) {
  value::BashArray* array_val = nullptr;
  BigStr* s = nullptr;
  runtime_asdl::error_code_t error_code;
  value::SparseArray* sparse_val = nullptr;
  value::BashAssoc* assoc_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&array_val);
  StackRoot _root2(&s);
  StackRoot _root3(&sparse_val);
  StackRoot _root4(&assoc_val);

  if ((val->tag() == value_e::BashArray || val->tag() == value_e::SparseArray)) {
    if (val->tag() == value_e::BashArray) {
      array_val = static_cast<value::BashArray*>(val);
      Tuple2<BigStr*, runtime_asdl::error_code_t> tup0 = bash_impl::BashArray_GetElement(array_val, 0);
      s = tup0.at0();
      error_code = tup0.at1();
    }
    else {
      if (val->tag() == value_e::SparseArray) {
        sparse_val = static_cast<value::SparseArray*>(val);
        Tuple2<BigStr*, runtime_asdl::error_code_t> tup1 = bash_impl::SparseArray_GetElement(sparse_val, mops::ZERO);
        s = tup1.at0();
        error_code = tup1.at1();
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  else {
    if (val->tag() == value_e::BashAssoc) {
      assoc_val = static_cast<value::BashAssoc*>(val);
      s = bash_impl::BashAssoc_GetElement(assoc_val, S_wfw);
    }
    else {
      assert(0);  // AssertionError
    }
  }
  if (s == nullptr) {
    return value::Undef;
  }
  else {
    return Alloc<value::Str>(s);
  }
}

bool _DetectMetaBuiltinStr(BigStr* s) {
  StackRoot _root0(&s);

  return (consts::LookupNormalBuiltin(s) == builtin_i::builtin || consts::LookupNormalBuiltin(s) == builtin_i::command);
}

bool _DetectMetaBuiltin(runtime_asdl::part_value_t* val0) {
  runtime_asdl::part_value_t* UP_val0 = nullptr;
  StackRoot _root0(&val0);
  StackRoot _root1(&UP_val0);

  UP_val0 = val0;
  if (val0->tag() == part_value_e::String) {
    Piece* val0 = static_cast<Piece*>(UP_val0);
    if (!val0->quoted) {
      return _DetectMetaBuiltinStr(val0->s);
    }
  }
  return false;
}

runtime_asdl::AssignArg* _SplitAssignArg(BigStr* arg, syntax_asdl::CompoundWord* blame_word) {
  List<BigStr*>* m = nullptr;
  BigStr* var_name = nullptr;
  BigStr* op = nullptr;
  value_asdl::value_t* val = nullptr;
  bool append;
  StackRoot _root0(&arg);
  StackRoot _root1(&blame_word);
  StackRoot _root2(&m);
  StackRoot _root3(&var_name);
  StackRoot _root4(&op);
  StackRoot _root5(&val);

  m = util::RegexSearch(consts::ASSIGN_ARG_RE, arg);
  if (m == nullptr) {
    e_die(StrFormat("Assignment builtin expected NAME=value, got %r", arg), blame_word);
  }
  var_name = m->at(1);
  op = m->at(3);
  if (len(op)) {
    val = Alloc<value::Str>(m->at(4));
    append = str_equals(op->at(0), S_jnE);
  }
  else {
    val = nullptr;
    append = false;
  }
  return Alloc<AssignArg>(var_name, val, append, blame_word);
}

BigStr* _BackslashEscape(BigStr* s) {
  StackRoot _root0(&s);

  return s->replace(S_iyu, S_Eef);
}

runtime_asdl::part_value_t* _ValueToPartValue(value_asdl::value_t* val, bool quoted, syntax_asdl::word_part_t* part_loc) {
  value_asdl::value_t* UP_val = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&part_loc);
  StackRoot _root2(&UP_val);
  StackRoot _root3(&s);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      return Alloc<Piece>(S_Aoo, quoted, !quoted);
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      return Alloc<Piece>(val->s, quoted, !quoted);
    }
      break;
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      return Alloc<part_value::Array>(bash_impl::BashArray_GetValues(val));
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
      return Alloc<part_value::Array>(bash_impl::SparseArray_GetValues(val));
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      return Alloc<part_value::Array>(bash_impl::BashAssoc_GetValues(val));
    }
      break;
    case value_e::Null: 
    case value_e::Bool: 
    case value_e::Int: 
    case value_e::Float: 
    case value_e::Eggex: 
    case value_e::List: {
      s = val_ops::Stringify(val, Alloc<loc::WordPart>(part_loc), S_fEB);
      return Alloc<Piece>(s, quoted, !quoted);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_jul, Alloc<loc::WordPart>(part_loc));
    }
  }
  assert(0);  // AssertionError
}

List<List<runtime_asdl::Piece*>*>* _MakeWordFrames(List<runtime_asdl::part_value_t*>* part_vals) {
  List<runtime_asdl::Piece*>* current = nullptr;
  List<List<runtime_asdl::Piece*>*>* frames = nullptr;
  runtime_asdl::part_value_t* UP_p = nullptr;
  bool is_first;
  runtime_asdl::Piece* piece = nullptr;
  StackRoot _root0(&part_vals);
  StackRoot _root1(&current);
  StackRoot _root2(&frames);
  StackRoot _root3(&UP_p);
  StackRoot _root4(&piece);

  current = Alloc<List<runtime_asdl::Piece*>>();
  frames = NewList<List<runtime_asdl::Piece*>*>(std::initializer_list<List<runtime_asdl::Piece*>*>{current});
  for (ListIter<runtime_asdl::part_value_t*> it(part_vals); !it.Done(); it.Next()) {
    runtime_asdl::part_value_t* p = it.Value();
    StackRoot _for(&p  );
    UP_p = p;
    switch (p->tag()) {
      case part_value_e::String: {
        Piece* p = static_cast<Piece*>(UP_p);
        current->append(p);
      }
        break;
      case part_value_e::Array: {
        part_value::Array* p = static_cast<part_value::Array*>(UP_p);
        is_first = true;
        for (ListIter<BigStr*> it(p->strs); !it.Done(); it.Next()) {
          BigStr* s = it.Value();
          StackRoot _for(&s        );
          if (s == nullptr) {
            continue;
          }
          piece = Alloc<Piece>(s, true, false);
          if (is_first) {
            current->append(piece);
            is_first = false;
          }
          else {
            current = NewList<runtime_asdl::Piece*>(std::initializer_list<runtime_asdl::Piece*>{piece});
            frames->append(current);
          }
        }
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  return frames;
}

BigStr* _DecayPartValuesToString(List<runtime_asdl::part_value_t*>* part_vals, BigStr* join_char) {
  List<BigStr*>* out = nullptr;
  runtime_asdl::part_value_t* UP_p = nullptr;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&part_vals);
  StackRoot _root1(&join_char);
  StackRoot _root2(&out);
  StackRoot _root3(&UP_p);
  StackRoot _root4(&tmp);

  out = Alloc<List<BigStr*>>();
  for (ListIter<runtime_asdl::part_value_t*> it(part_vals); !it.Done(); it.Next()) {
    runtime_asdl::part_value_t* p = it.Value();
    StackRoot _for(&p  );
    UP_p = p;
    switch (p->tag()) {
      case part_value_e::String: {
        Piece* p = static_cast<Piece*>(UP_p);
        out->append(p->s);
      }
        break;
      case part_value_e::Array: {
        part_value::Array* p = static_cast<part_value::Array*>(UP_p);
        tmp = Alloc<List<BigStr*>>();
        for (ListIter<BigStr*> it(p->strs); !it.Done(); it.Next()) {
          BigStr* s = it.Value();
          if (s != nullptr) {
            tmp->append(s);
          }
        }
        out->append(join_char->join(tmp));
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  return S_Aoo->join(out);
}

value_asdl::value_t* _PerformSlice(value_asdl::value_t* val, mops::BigInt offset, int length, bool has_length, syntax_asdl::BracedVarSub* part, value::Str* arg0_val) {
  value_asdl::value_t* UP_val = nullptr;
  BigStr* s = nullptr;
  int n;
  int begin;
  int byte_begin;
  int num_iters;
  int byte_end;
  BigStr* substr = nullptr;
  value_asdl::value_t* result = nullptr;
  mops::BigInt array_length;
  List<BigStr*>* strs = nullptr;
  bool prepends_arg0;
  List<BigStr*>* orig = nullptr;
  int i;
  int count;
  List<BigStr*>* new_list = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&part);
  StackRoot _root2(&arg0_val);
  StackRoot _root3(&UP_val);
  StackRoot _root4(&s);
  StackRoot _root5(&substr);
  StackRoot _root6(&result);
  StackRoot _root7(&strs);
  StackRoot _root8(&orig);
  StackRoot _root9(&new_list);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      s = val->s;
      n = len(s);
      begin = mops::BigTruncate(offset);
      if (begin < 0) {
        byte_begin = n;
        num_iters = -begin;
        for (int _ = 0; _ < num_iters; ++_) {
          byte_begin = string_ops::PreviousUtf8Char(s, byte_begin);
        }
      }
      else {
        byte_begin = string_ops::AdvanceUtf8Chars(s, begin, 0);
      }
      if (has_length) {
        if (length < 0) {
          byte_end = n;
          num_iters = -length;
          for (int _ = 0; _ < num_iters; ++_) {
            byte_end = string_ops::PreviousUtf8Char(s, byte_end);
          }
        }
        else {
          byte_end = string_ops::AdvanceUtf8Chars(s, length, byte_begin);
        }
      }
      else {
        byte_end = len(s);
      }
      substr = s->slice(byte_begin, byte_end);
      result = Alloc<value::Str>(substr);
    }
      break;
    case value_e::BashArray: 
    case value_e::SparseArray: {
      if ((has_length and length < 0)) {
        e_die(StrFormat("Array slice can't have negative length: %d", length), Alloc<loc::WordPart>(part));
      }
      if (bash_impl::BigInt_Less(offset, mops::ZERO)) {
        if (val->tag() == value_e::BashArray) {
          value::BashArray* val = static_cast<value::BashArray*>(UP_val);
          array_length = mops::IntWiden(bash_impl::BashArray_Length(val));
        }
        else {
          if (val->tag() == value_e::SparseArray) {
            value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
            array_length = bash_impl::SparseArray_Length(val);
          }
          else {
            assert(0);  // AssertionError
          }
        }
        if (arg0_val != nullptr) {
          array_length = mops::Add(array_length, mops::ONE);
        }
        offset = mops::Add(offset, array_length);
      }
      if (bash_impl::BigInt_Less(offset, mops::ZERO)) {
        strs = Alloc<List<BigStr*>>();
      }
      else {
        prepends_arg0 = false;
        if (arg0_val != nullptr) {
          if (bash_impl::BigInt_Greater(offset, mops::ZERO)) {
            offset = mops::Sub(offset, mops::ONE);
          }
          else {
            if ((!has_length or length >= 1)) {
              prepends_arg0 = true;
              length = (length - 1);
            }
          }
        }
        if ((has_length and length == 0)) {
          strs = Alloc<List<BigStr*>>();
        }
        else {
          if (val->tag() == value_e::BashArray) {
            value::BashArray* val = static_cast<value::BashArray*>(UP_val);
            orig = bash_impl::BashArray_GetValues(val);
            n = len(orig);
            strs = Alloc<List<BigStr*>>();
            i = mops::BigTruncate(offset);
            count = 0;
            while (i < n) {
              if ((has_length and count == length)) {
                break;
              }
              s = orig->at(i);
              if (s != nullptr) {
                strs->append(s);
                count += 1;
              }
              i += 1;
            }
          }
          else {
            if (val->tag() == value_e::SparseArray) {
              value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
              i = 0;
              for (ListIter<mops::BigInt> it(bash_impl::SparseArray_GetKeys(val)); !it.Done(); it.Next()) {
                mops::BigInt index = it.Value();
                if (bash_impl::BigInt_GreaterEq(index, offset)) {
                  break;
                }
                i = (i + 1);
              }
              if (has_length) {
                strs = bash_impl::SparseArray_GetValues(val)->slice(i, (i + length));
              }
              else {
                strs = bash_impl::SparseArray_GetValues(val)->slice(i);
              }
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
        if (prepends_arg0) {
          new_list = NewList<BigStr*>(std::initializer_list<BigStr*>{arg0_val->s});
          new_list->extend(strs);
          strs = new_list;
        }
      }
      result = Alloc<value::BashArray>(strs);
    }
      break;
    case value_e::BashAssoc: {
      e_die(S_gxh, Alloc<loc::WordPart>(part));
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_sDc, Alloc<loc::WordPart>(part));
    }
  }
  return result;
}

StringWordEvaluator::StringWordEvaluator() {
  ;  // pass
}

value::Str* StringWordEvaluator::EvalWordToString(syntax_asdl::word_t* w, int eval_flags) {
  StackRoot _root0(&w);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

BigStr* _GetDollarHyphen(optview::Exec* exec_opts) {
  List<BigStr*>* chars = nullptr;
  StackRoot _root0(&exec_opts);
  StackRoot _root1(&chars);

  chars = Alloc<List<BigStr*>>();
  if (exec_opts->interactive()) {
    chars->append(S_eil);
  }
  if (exec_opts->errexit()) {
    chars->append(S_ysz);
  }
  if (exec_opts->noglob()) {
    chars->append(S_ksc);
  }
  if (exec_opts->noexec()) {
    chars->append(S_rob);
  }
  if (exec_opts->nounset()) {
    chars->append(S_rsz);
  }
  if (exec_opts->xtrace()) {
    chars->append(S_rqD);
  }
  if (exec_opts->noclobber()) {
    chars->append(S_sjc);
  }
  return S_Aoo->join(chars);
}

TildeEvaluator::TildeEvaluator(state::Mem* mem, optview::Exec* exec_opts) {
  this->mem = mem;
  this->exec_opts = exec_opts;
}

BigStr* TildeEvaluator::GetMyHomeDir() {
  BigStr* s = nullptr;
  StackRoot _root0(&s);

  s = this->mem->env_config->Get(S_xlm);
  if (s != nullptr) {
    return s;
  }
  return pyos::GetMyHomeDir();
}

BigStr* TildeEvaluator::Eval(word_part::TildeSub* part) {
  BigStr* result = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&result);

  if (part->user_name == nullptr) {
    result = this->GetMyHomeDir();
  }
  else {
    result = pyos::GetHomeDir(part->user_name);
  }
  if (result == nullptr) {
    if (this->exec_opts->strict_tilde()) {
      e_die(S_jcv, part->left);
    }
    else {
      result = S_Bhp;
      if (part->user_name != nullptr) {
        result = str_concat(result, part->user_name);
      }
    }
  }
  return result;
}

AbstractWordEvaluator::AbstractWordEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, word_eval::TildeEvaluator* tilde_ev, split::SplitContext* splitter, ui::ErrorFormatter* errfmt) {
  this->arith_ev = nullptr;
  this->expr_ev = nullptr;
  this->prompt_ev = nullptr;
  this->unsafe_arith = nullptr;
  this->tilde_ev = tilde_ev;
  this->mem = mem;
  this->exec_opts = exec_opts;
  this->mutable_opts = mutable_opts;
  this->splitter = splitter;
  this->errfmt = errfmt;
  this->globber = Alloc<glob_::Globber>(exec_opts);
}

void AbstractWordEvaluator::CheckCircularDeps() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

runtime_asdl::part_value_t* AbstractWordEvaluator::_EvalCommandSub(syntax_asdl::CommandSub* cs_part, bool quoted) {
  StackRoot _root0(&cs_part);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

runtime_asdl::part_value_t* AbstractWordEvaluator::_EvalProcessSub(syntax_asdl::CommandSub* cs_part) {
  StackRoot _root0(&cs_part);

  FAIL(kNotImplemented);  // Python NotImplementedError
}

value_asdl::value_t* AbstractWordEvaluator::_EvalVarNum(int var_num) {
  return this->mem->GetArgNum(var_num);
}

value_asdl::value_t* AbstractWordEvaluator::_EvalSpecialVar(int op_id, bool quoted, runtime_asdl::VarSubState* vsub_state) {
  List<BigStr*>* argv = nullptr;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&vsub_state);
  StackRoot _root1(&argv);
  StackRoot _root2(&val);

  if ((op_id == Id::VSub_At || op_id == Id::VSub_Star)) {
    argv = this->mem->GetArgv();
    val = Alloc<value::BashArray>(argv);
    if (op_id == Id::VSub_At) {
      vsub_state->join_array = !quoted;
    }
    else {
      vsub_state->join_array = true;
    }
  }
  else {
    if (op_id == Id::VSub_Hyphen) {
      val = Alloc<value::Str>(_GetDollarHyphen(this->exec_opts));
    }
    else {
      val = this->mem->GetSpecialVar(op_id);
    }
  }
  return val;
}

bool AbstractWordEvaluator::_ApplyTestOp(value_asdl::value_t* val, suffix_op::Unary* op, bool quoted, List<runtime_asdl::part_value_t*>* part_vals, runtime_asdl::VTestPlace* vtest_place, syntax_asdl::Token* blame_token, runtime_asdl::VarSubState* vsub_state) {
  int eval_flags;
  syntax_asdl::Token* tok = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  bool is_falsey;
  List<BigStr*>* strs = nullptr;
  int sep_width;
  List<runtime_asdl::part_value_t*>* assign_part_vals = nullptr;
  BigStr* rhs_str = nullptr;
  value_asdl::sh_lvalue_t* lval = nullptr;
  BigStr* var_name = nullptr;
  runtime_asdl::a_index_t* var_index = nullptr;
  runtime_asdl::a_index_t* UP_var_index = nullptr;
  List<runtime_asdl::part_value_t*>* error_part_vals = nullptr;
  BigStr* error_str = nullptr;
  BigStr* actual = nullptr;
  BigStr* suffix = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&op);
  StackRoot _root2(&part_vals);
  StackRoot _root3(&vtest_place);
  StackRoot _root4(&blame_token);
  StackRoot _root5(&vsub_state);
  StackRoot _root6(&tok);
  StackRoot _root7(&UP_val);
  StackRoot _root8(&strs);
  StackRoot _root9(&assign_part_vals);
  StackRoot _root10(&rhs_str);
  StackRoot _root11(&lval);
  StackRoot _root12(&var_name);
  StackRoot _root13(&var_index);
  StackRoot _root14(&UP_var_index);
  StackRoot _root15(&error_part_vals);
  StackRoot _root16(&error_str);
  StackRoot _root17(&actual);
  StackRoot _root18(&suffix);

  eval_flags = IS_SUBST;
  if (quoted) {
    eval_flags |= QUOTED;
  }
  tok = op->op;
  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      is_falsey = true;
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      if ((tok->id == Id::VTest_ColonHyphen || tok->id == Id::VTest_ColonEquals || tok->id == Id::VTest_ColonQMark || tok->id == Id::VTest_ColonPlus)) {
        is_falsey = len(val->s) == 0;
      }
      else {
        is_falsey = false;
      }
    }
      break;
    case value_e::BashArray: 
    case value_e::SparseArray: 
    case value_e::BashAssoc: {
      if (val->tag() == value_e::BashArray) {
        value::BashArray* val = static_cast<value::BashArray*>(UP_val);
        strs = bash_impl::BashArray_GetValues(val);
      }
      else {
        if (val->tag() == value_e::SparseArray) {
          value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
          strs = bash_impl::SparseArray_GetValues(val);
        }
        else {
          if (val->tag() == value_e::BashAssoc) {
            value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
            strs = bash_impl::BashAssoc_GetValues(val);
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
      if ((tok->id == Id::VTest_ColonHyphen || tok->id == Id::VTest_ColonEquals || tok->id == Id::VTest_ColonQMark || tok->id == Id::VTest_ColonPlus)) {
        if ((quoted and vsub_state->join_array)) {
          sep_width = len(this->splitter->GetJoinChar());
        }
        else {
          sep_width = 1;
        }
        if (sep_width == 0) {
          is_falsey = true;
          for (ListIter<BigStr*> it(strs); !it.Done(); it.Next()) {
            BigStr* s = it.Value();
            StackRoot _for(&s          );
            if (len(s) != 0) {
              is_falsey = false;
              break;
            }
          }
        }
        else {
          is_falsey = (len(strs) == 0 or (len(strs) == 1 and len(strs->at(0)) == 0));
        }
      }
      else {
        is_falsey = len(strs) == 0;
      }
    }
      break;
    default: {
      is_falsey = false;
    }
  }
  if ((tok->id == Id::VTest_ColonHyphen || tok->id == Id::VTest_Hyphen)) {
    if (is_falsey) {
      this->_EvalRhsWordToParts(op->arg_word, part_vals, eval_flags);
      return true;
    }
    else {
      return false;
    }
  }
  else {
    if ((tok->id == Id::VTest_ColonPlus || tok->id == Id::VTest_Plus)) {
      if (is_falsey) {
        return false;
      }
      else {
        this->_EvalRhsWordToParts(op->arg_word, part_vals, eval_flags);
        return true;
      }
    }
    else {
      if ((tok->id == Id::VTest_ColonEquals || tok->id == Id::VTest_Equals)) {
        if (is_falsey) {
          assign_part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
          this->_EvalRhsWordToParts(op->arg_word, assign_part_vals, eval_flags);
          part_vals->extend(assign_part_vals);
          if (vtest_place->name == nullptr) {
            e_die(S_tvk);
          }
          else {
            rhs_str = _DecayPartValuesToString(assign_part_vals, this->splitter->GetJoinChar());
            if (vtest_place->index == nullptr) {
              lval = location::LName(vtest_place->name);
            }
            else {
              var_name = vtest_place->name;
              var_index = vtest_place->index;
              UP_var_index = var_index;
              switch (var_index->tag()) {
                case a_index_e::Int: {
                  a_index::Int* var_index = static_cast<a_index::Int*>(UP_var_index);
                  lval = Alloc<sh_lvalue::Indexed>(var_name, var_index->i, loc::Missing);
                }
                  break;
                case a_index_e::Str: {
                  a_index::Str* var_index = static_cast<a_index::Str*>(UP_var_index);
                  lval = Alloc<sh_lvalue::Keyed>(var_name, var_index->s, loc::Missing);
                }
                  break;
                default: {
                  assert(0);  // AssertionError
                }
              }
            }
            state::OshLanguageSetValue(this->mem, lval, Alloc<value::Str>(rhs_str));
          }
          return true;
        }
        else {
          return false;
        }
      }
      else {
        if ((tok->id == Id::VTest_ColonQMark || tok->id == Id::VTest_QMark)) {
          if (is_falsey) {
            error_part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
            this->_EvalRhsWordToParts(op->arg_word, error_part_vals, eval_flags);
            error_str = _DecayPartValuesToString(error_part_vals, this->splitter->GetJoinChar());
            if (vtest_place->name == nullptr) {
              var_name = S_nrb;
            }
            else {
              var_name = vtest_place->name;
            }
            if (val->tag() == value_e::Undef) {
              actual = S_FxC;
            }
            else {
              actual = S_nDb;
            }
            if (len(error_str)) {
              suffix = StrFormat(": %r", error_str);
            }
            else {
              suffix = S_Aoo;
            }
            e_die(StrFormat("Var %s is %s%s", var_name, actual, suffix), blame_token);
          }
          else {
            return false;
          }
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
  }
}

int AbstractWordEvaluator::_Count(value_asdl::value_t* val, syntax_asdl::Token* token) {
  value_asdl::value_t* UP_val = nullptr;
  int count;
  StackRoot _root0(&val);
  StackRoot _root1(&token);
  StackRoot _root2(&UP_val);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      try {
        count = string_ops::CountUtf8Chars(val->s);
      }
      catch (error::Strict* e) {
        e->location = token;
        if (this->exec_opts->strict_word_eval()) {
          throw;
        }
        else {
          this->errfmt->PrettyPrintError(e, S_jhf);
          return -1;
        }
      }
    }
      break;
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      count = bash_impl::BashArray_Count(val);
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      count = bash_impl::BashAssoc_Count(val);
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
      count = bash_impl::SparseArray_Count(val);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_grF, token);
    }
  }
  return count;
}

value_asdl::value_t* AbstractWordEvaluator::_Keys(value_asdl::value_t* val, syntax_asdl::Token* token) {
  value_asdl::value_t* UP_val = nullptr;
  List<BigStr*>* indices = nullptr;
  List<BigStr*>* keys = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&token);
  StackRoot _root2(&UP_val);
  StackRoot _root3(&indices);
  StackRoot _root4(&keys);

  UP_val = val;
  switch (val->tag()) {
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      indices = Alloc<List<BigStr*>>();
      for (ListIter<int> it(bash_impl::BashArray_GetKeys(val)); !it.Done(); it.Next()) {
        int i = it.Value();
        indices->append(str(i));
      }
      return Alloc<value::BashArray>(indices);
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      keys = bash_impl::BashAssoc_GetKeys(val);
      return Alloc<value::BashArray>(keys);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_uwF, token);
    }
  }
}

value_asdl::value_t* AbstractWordEvaluator::_EvalVarRef(value_asdl::value_t* val, syntax_asdl::Token* blame_tok, bool quoted, runtime_asdl::VarSubState* vsub_state, runtime_asdl::VTestPlace* vtest_place) {
  value_asdl::value_t* UP_val = nullptr;
  BigStr* var_ref_str = nullptr;
  syntax_asdl::BracedVarSub* bvs_part = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&blame_tok);
  StackRoot _root2(&vsub_state);
  StackRoot _root3(&vtest_place);
  StackRoot _root4(&UP_val);
  StackRoot _root5(&var_ref_str);
  StackRoot _root6(&bvs_part);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      var_ref_str = S_Aoo;
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      var_ref_str = val->s;
    }
      break;
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      var_ref_str = S_yfw->join(bash_impl::BashArray_GetValues(val));
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      var_ref_str = S_yfw->join(bash_impl::BashAssoc_GetValues(val));
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_lyf, blame_tok);
    }
  }
  try {
    bvs_part = this->unsafe_arith->ParseVarRef(var_ref_str, blame_tok);
  }
  catch (error::FatalRuntime* e) {
    throw Alloc<error::VarSubFailure>(e->msg, e->location);
  }
  return this->_VarRefValue(bvs_part, quoted, vsub_state, vtest_place);
}

value_asdl::value_t* AbstractWordEvaluator::_ApplyUnarySuffixOp(value_asdl::value_t* val, suffix_op::Unary* op) {
  id_kind_asdl::Kind_t op_kind;
  value::Str* arg_val = nullptr;
  bool has_extglob;
  value_asdl::value_t* UP_val = nullptr;
  BigStr* s = nullptr;
  value_asdl::value_t* new_val = nullptr;
  List<BigStr*>* values = nullptr;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&op);
  StackRoot _root2(&arg_val);
  StackRoot _root3(&UP_val);
  StackRoot _root4(&s);
  StackRoot _root5(&new_val);
  StackRoot _root6(&values);
  StackRoot _root7(&strs);

  op_kind = consts::GetKind(op->op->id);
  if (op_kind == Kind::VOp1) {
    Tuple2<value::Str*, bool> tup2 = this->EvalWordToPattern(op->arg_word);
    arg_val = tup2.at0();
    has_extglob = tup2.at1();
    UP_val = val;
    switch (val->tag()) {
      case value_e::Str: {
        value::Str* val = static_cast<value::Str*>(UP_val);
        s = string_ops::DoUnarySuffixOp(val->s, op->op, arg_val->s, has_extglob);
        new_val = Alloc<value::Str>(s);
      }
        break;
      case value_e::BashArray: 
      case value_e::SparseArray: 
      case value_e::BashAssoc: {
        if (val->tag() == value_e::BashArray) {
          value::BashArray* val = static_cast<value::BashArray*>(UP_val);
          values = bash_impl::BashArray_GetValues(val);
        }
        else {
          if (val->tag() == value_e::SparseArray) {
            value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
            values = bash_impl::SparseArray_GetValues(val);
          }
          else {
            if (val->tag() == value_e::BashAssoc) {
              value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
              values = bash_impl::BashAssoc_GetValues(val);
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
        strs = Alloc<List<BigStr*>>();
        for (ListIter<BigStr*> it(values); !it.Done(); it.Next()) {
          BigStr* s = it.Value();
          strs->append(string_ops::DoUnarySuffixOp(s, op->op, arg_val->s, has_extglob));
        }
        new_val = Alloc<value::BashArray>(strs);
      }
        break;
      default: {
        throw Alloc<error::TypeErr>(val, S_kpo, op->op);
      }
    }
  }
  else {
    assert(0);  // AssertionError
  }
  return new_val;
}

value_asdl::value_t* AbstractWordEvaluator::_PatSub(value_asdl::value_t* val, suffix_op::PatSub* op) {
  value::Str* pat_val = nullptr;
  bool has_extglob;
  value_asdl::value_t* replace_val = nullptr;
  BigStr* replace_str = nullptr;
  BigStr* regex = nullptr;
  List<BigStr*>* warnings = nullptr;
  string_ops::GlobReplacer* replacer = nullptr;
  value::Str* str_val = nullptr;
  BigStr* s = nullptr;
  value::BashArray* array_val = nullptr;
  List<BigStr*>* values = nullptr;
  value::SparseArray* sparse_val = nullptr;
  value::BashAssoc* assoc_val = nullptr;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&op);
  StackRoot _root2(&pat_val);
  StackRoot _root3(&replace_val);
  StackRoot _root4(&replace_str);
  StackRoot _root5(&regex);
  StackRoot _root6(&warnings);
  StackRoot _root7(&replacer);
  StackRoot _root8(&str_val);
  StackRoot _root9(&s);
  StackRoot _root10(&array_val);
  StackRoot _root11(&values);
  StackRoot _root12(&sparse_val);
  StackRoot _root13(&assoc_val);
  StackRoot _root14(&strs);

  Tuple2<value::Str*, bool> tup3 = this->EvalWordToPattern(op->pat);
  pat_val = tup3.at0();
  has_extglob = tup3.at1();
  if (has_extglob) {
    e_die(S_med, op->pat);
  }
  if (op->replace) {
    replace_val = this->EvalRhsWord(op->replace);
    replace_str = static_cast<value::Str*>(replace_val)->s;
  }
  else {
    replace_str = S_Aoo;
  }
  Tuple2<BigStr*, List<BigStr*>*> tup4 = glob_::GlobToERE(pat_val->s);
  regex = tup4.at0();
  warnings = tup4.at1();
  if (len(warnings)) {
    ;  // pass
  }
  replacer = Alloc<string_ops::GlobReplacer>(regex, replace_str, op->slash_tok);
  switch (val->tag()) {
    case value_e::Str: {
      str_val = static_cast<value::Str*>(val);
      s = replacer->Replace(str_val->s, op);
      val = Alloc<value::Str>(s);
    }
      break;
    case value_e::BashArray: 
    case value_e::SparseArray: 
    case value_e::BashAssoc: {
      if (val->tag() == value_e::BashArray) {
        array_val = static_cast<value::BashArray*>(val);
        values = bash_impl::BashArray_GetValues(array_val);
      }
      else {
        if (val->tag() == value_e::SparseArray) {
          sparse_val = static_cast<value::SparseArray*>(val);
          values = bash_impl::SparseArray_GetValues(sparse_val);
        }
        else {
          if (val->tag() == value_e::BashAssoc) {
            assoc_val = static_cast<value::BashAssoc*>(val);
            values = bash_impl::BashAssoc_GetValues(assoc_val);
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
      strs = Alloc<List<BigStr*>>();
      for (ListIter<BigStr*> it(values); !it.Done(); it.Next()) {
        BigStr* s = it.Value();
        strs->append(replacer->Replace(s, op));
      }
      val = Alloc<value::BashArray>(strs);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_fFn, op->slash_tok);
    }
  }
  return val;
}

value_asdl::value_t* AbstractWordEvaluator::_Slice(value_asdl::value_t* val, suffix_op::Slice* op, BigStr* var_name, syntax_asdl::BracedVarSub* part) {
  mops::BigInt begin;
  bool has_length;
  int length;
  value::Str* arg0_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&op);
  StackRoot _root2(&var_name);
  StackRoot _root3(&part);
  StackRoot _root4(&arg0_val);

  begin = this->arith_ev->EvalToBigInt(op->begin);
  has_length = false;
  length = -1;
  if (op->length) {
    has_length = true;
    length = this->arith_ev->EvalToInt(op->length);
  }
  try {
    arg0_val = nullptr;
    if (var_name == nullptr) {
      arg0_val = this->mem->GetArg0();
    }
    val = _PerformSlice(val, begin, length, has_length, part, arg0_val);
  }
  catch (error::Strict* e) {
    if (this->exec_opts->strict_word_eval()) {
      throw;
    }
    else {
      this->errfmt->PrettyPrintError(e, S_jhf);
      switch (val->tag()) {
        case value_e::Str: {
          val = Alloc<value::Str>(S_Aoo);
        }
          break;
        case value_e::BashArray: {
          val = Alloc<value::BashArray>(Alloc<List<BigStr*>>());
        }
          break;
        default: {
          FAIL(kNotImplemented);  // Python NotImplementedError
        }
      }
    }
  }
  return val;
}

Tuple2<value_asdl::value_t*, bool> AbstractWordEvaluator::_Nullary(value_asdl::value_t* val, syntax_asdl::Token* op, BigStr* var_name, syntax_asdl::Token* vsub_token, runtime_asdl::VarSubState* vsub_state) {
  bool quoted2;
  int op_id;
  value_asdl::value_t* UP_val = nullptr;
  value_asdl::value_t* result = nullptr;
  BigStr* prompt = nullptr;
  BigStr* p = nullptr;
  List<BigStr*>* values = nullptr;
  List<BigStr*>* tmp = nullptr;
  List<BigStr*>* chars = nullptr;
  runtime_asdl::Cell* cell = nullptr;
  int count;
  StackRoot _root0(&val);
  StackRoot _root1(&op);
  StackRoot _root2(&var_name);
  StackRoot _root3(&vsub_token);
  StackRoot _root4(&vsub_state);
  StackRoot _root5(&UP_val);
  StackRoot _root6(&result);
  StackRoot _root7(&prompt);
  StackRoot _root8(&p);
  StackRoot _root9(&values);
  StackRoot _root10(&tmp);
  StackRoot _root11(&chars);
  StackRoot _root12(&cell);

  quoted2 = false;
  op_id = op->id;
  if (op_id == Id::VOp0_P) {
    val = this->_ProcessUndef(val, vsub_token, vsub_state);
    UP_val = val;
    switch (val->tag()) {
      case value_e::Undef: {
        result = Alloc<value::Str>(S_Aoo);
      }
        break;
      case value_e::Str: {
        value::Str* str_val = static_cast<value::Str*>(UP_val);
        prompt = this->prompt_ev->EvalPrompt(str_val->s);
        p = prompt->replace(S_FDc, S_Aoo)->replace(S_ewA, S_Aoo);
        result = Alloc<value::Str>(p);
      }
        break;
      case value_e::BashArray: 
      case value_e::SparseArray: 
      case value_e::BashAssoc: {
        if (val->tag() == value_e::BashArray) {
          value::BashArray* val = static_cast<value::BashArray*>(UP_val);
          values = Alloc<List<BigStr*>>();
          for (ListIter<BigStr*> it(bash_impl::BashArray_GetValues(val)); !it.Done(); it.Next()) {
            BigStr* s = it.Value();
            if (s != nullptr) {
              values->append(s);
            }
          }
        }
        else {
          if (val->tag() == value_e::SparseArray) {
            value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
            values = bash_impl::SparseArray_GetValues(val);
          }
          else {
            if (val->tag() == value_e::BashAssoc) {
              value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
              values = bash_impl::BashAssoc_GetValues(val);
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
        tmp = Alloc<List<BigStr*>>();
        for (ListIter<BigStr*> it(values); !it.Done(); it.Next()) {
          BigStr* s = it.Value();
          tmp->append(this->prompt_ev->EvalPrompt(s)->replace(S_FDc, S_Aoo)->replace(S_ewA, S_Aoo));
        }
        result = Alloc<value::BashArray>(tmp);
      }
        break;
      default: {
        e_die(StrFormat("Can't use @P on %s", ui::ValType(val)), op);
      }
    }
  }
  else {
    if (op_id == Id::VOp0_Q) {
      UP_val = val;
      switch (val->tag()) {
        case value_e::Undef: {
          this->_ProcessUndef(val, vsub_token, vsub_state);
          if (vsub_state->array_ref != nullptr) {
            result = Alloc<value::BashArray>(Alloc<List<BigStr*>>());
          }
          else {
            result = Alloc<value::Str>(S_Aoo);
          }
        }
          break;
        case value_e::Str: {
          value::Str* str_val = static_cast<value::Str*>(UP_val);
          result = Alloc<value::Str>(j8_lite::MaybeShellEncode(str_val->s));
          quoted2 = true;
        }
          break;
        case value_e::BashArray: 
        case value_e::SparseArray: 
        case value_e::BashAssoc: {
          if (val->tag() == value_e::BashArray) {
            value::BashArray* val = static_cast<value::BashArray*>(UP_val);
            values = Alloc<List<BigStr*>>();
            for (ListIter<BigStr*> it(bash_impl::BashArray_GetValues(val)); !it.Done(); it.Next()) {
              BigStr* s = it.Value();
              if (s != nullptr) {
                values->append(s);
              }
            }
          }
          else {
            if (val->tag() == value_e::SparseArray) {
              value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
              values = bash_impl::SparseArray_GetValues(val);
            }
            else {
              if (val->tag() == value_e::BashAssoc) {
                value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
                values = bash_impl::BashAssoc_GetValues(val);
              }
              else {
                assert(0);  // AssertionError
              }
            }
          }
          tmp = Alloc<List<BigStr*>>();
          for (ListIter<BigStr*> it(values); !it.Done(); it.Next()) {
            BigStr* s = it.Value();
            tmp->append(j8_lite::MaybeShellEncode(s));
          }
          result = Alloc<value::BashArray>(tmp);
        }
          break;
        default: {
          e_die(StrFormat("Can't use @Q on %s", ui::ValType(val)), op);
        }
      }
    }
    else {
      if (op_id == Id::VOp0_a) {
        val = this->_ProcessUndef(val, vsub_token, vsub_state);
        UP_val = val;
        chars = Alloc<List<BigStr*>>();
        switch (vsub_state->h_value->tag()) {
          case value_e::BashArray: 
          case value_e::SparseArray: {
            chars->append(S_gCD);
          }
            break;
          case value_e::BashAssoc: {
            chars->append(S_nlt);
          }
            break;
        }
        if (var_name != nullptr) {
          cell = this->mem->GetCell(var_name);
          if (cell) {
            if (cell->readonly) {
              chars->append(S_nAr_1);
            }
            if (cell->exported) {
              chars->append(S_rqD);
            }
            if (cell->nameref) {
              chars->append(S_rob);
            }
          }
        }
        count = 1;
        switch (val->tag()) {
          case value_e::Undef: {
            count = 0;
          }
            break;
          case value_e::BashArray: {
            value::BashArray* val = static_cast<value::BashArray*>(UP_val);
            count = bash_impl::BashArray_Count(val);
          }
            break;
          case value_e::SparseArray: {
            value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
            count = bash_impl::SparseArray_Count(val);
          }
            break;
          case value_e::BashAssoc: {
            value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
            count = bash_impl::BashAssoc_Count(val);
          }
            break;
        }
        result = Alloc<value::BashArray>(list_repeat(S_Aoo->join(chars), count));
      }
      else {
        e_die(StrFormat("Var op %r not implemented", lexer::TokenVal(op)), op);
      }
    }
  }
  return Tuple2<value_asdl::value_t*, bool>(result, quoted2);
}

value_asdl::value_t* AbstractWordEvaluator::_WholeArray(value_asdl::value_t* val, syntax_asdl::BracedVarSub* part, bool quoted, runtime_asdl::VarSubState* vsub_state) {
  int op_id;
  BigStr* op_str = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&part);
  StackRoot _root2(&vsub_state);
  StackRoot _root3(&op_str);

  op_id = static_cast<bracket_op::WholeArray*>(part->bracket_op)->op_id;
  if (op_id == Id::Lit_At) {
    op_str = S_AeE;
    vsub_state->join_array = !quoted;
  }
  else {
    if (op_id == Id::Arith_Star) {
      op_str = S_Fgw;
      vsub_state->join_array = true;
    }
    else {
      assert(0);  // AssertionError
    }
  }
  switch (val->tag()) {
    case value_e::Undef: {
      vsub_state->array_ref = part->name_tok;
    }
      break;
    case value_e::Str: {
      if (this->exec_opts->strict_array()) {
        e_die(StrFormat("Can't index string with %s", op_str), Alloc<loc::WordPart>(part));
      }
    }
      break;
    case value_e::BashArray: 
    case value_e::SparseArray: 
    case value_e::BashAssoc: {
      ;  // pass
    }
      break;
    default: {
      ;  // pass
    }
  }
  return val;
}

value_asdl::value_t* AbstractWordEvaluator::_ArrayIndex(value_asdl::value_t* val, syntax_asdl::BracedVarSub* part, runtime_asdl::VTestPlace* vtest_place) {
  syntax_asdl::arith_expr_t* anode = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  int index;
  BigStr* s = nullptr;
  runtime_asdl::error_code_t error_code;
  mops::BigInt big_index;
  mops::BigInt big_length;
  BigStr* key = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&part);
  StackRoot _root2(&vtest_place);
  StackRoot _root3(&anode);
  StackRoot _root4(&UP_val);
  StackRoot _root5(&s);
  StackRoot _root6(&key);

  anode = static_cast<bracket_op::ArrayIndex*>(part->bracket_op)->expr;
  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      ;  // pass
    }
      break;
    case value_e::Str: {
      e_die(StrFormat("Can't index string %r with integer", part->var_name), part->name_tok);
    }
      break;
    case value_e::BashArray: {
      value::BashArray* array_val = static_cast<value::BashArray*>(UP_val);
      index = this->arith_ev->EvalToInt(anode);
      vtest_place->index = Alloc<a_index::Int>(index);
      Tuple2<BigStr*, runtime_asdl::error_code_t> tup5 = bash_impl::BashArray_GetElement(array_val, index);
      s = tup5.at0();
      error_code = tup5.at1();
      if (error_code == error_code_e::IndexOutOfRange) {
        this->errfmt->Print_(StrFormat("Index %d out of bounds for array of length %d", index, bash_impl::BashArray_Length(array_val)), part->name_tok);
      }
      if (s == nullptr) {
        val = value::Undef;
      }
      else {
        val = Alloc<value::Str>(s);
      }
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* sparse_val = static_cast<value::SparseArray*>(UP_val);
      big_index = this->arith_ev->EvalToBigInt(anode);
      vtest_place->index = Alloc<a_index::Int>(mops::BigTruncate(big_index));
      Tuple2<BigStr*, runtime_asdl::error_code_t> tup6 = bash_impl::SparseArray_GetElement(sparse_val, big_index);
      s = tup6.at0();
      error_code = tup6.at1();
      if (error_code == error_code_e::IndexOutOfRange) {
        big_length = bash_impl::SparseArray_Length(sparse_val);
        this->errfmt->Print_(StrFormat("Index %s out of bounds for array of length %s", mops::ToStr(big_index), mops::ToStr(big_length)), part->name_tok);
      }
      if (s == nullptr) {
        val = value::Undef;
      }
      else {
        val = Alloc<value::Str>(s);
      }
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* assoc_val = static_cast<value::BashAssoc*>(UP_val);
      key = this->arith_ev->EvalWordToString(anode, location::TokenForArith(anode));
      vtest_place->index = Alloc<a_index::Str>(key);
      s = bash_impl::BashAssoc_GetElement(assoc_val, key);
      if (s == nullptr) {
        val = value::Undef;
      }
      else {
        val = Alloc<value::Str>(s);
      }
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, S_xCq, Alloc<loc::WordPart>(part));
    }
  }
  return val;
}

void AbstractWordEvaluator::_EvalDoubleQuoted(List<syntax_asdl::word_part_t*>* parts, List<runtime_asdl::part_value_t*>* part_vals) {
  runtime_asdl::Piece* v = nullptr;
  StackRoot _root0(&parts);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&v);

  if (len(parts) == 0) {
    v = Alloc<Piece>(S_Aoo, true, false);
    part_vals->append(v);
    return ;
  }
  for (ListIter<syntax_asdl::word_part_t*> it(parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* p = it.Value();
    StackRoot _for(&p  );
    this->_EvalWordPart(p, part_vals, QUOTED);
  }
}

BigStr* AbstractWordEvaluator::EvalDoubleQuotedToString(syntax_asdl::DoubleQuoted* dq_part) {
  List<runtime_asdl::part_value_t*>* part_vals = nullptr;
  StackRoot _root0(&dq_part);
  StackRoot _root1(&part_vals);

  part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
  this->_EvalDoubleQuoted(dq_part->parts, part_vals);
  return this->_ConcatPartVals(part_vals, dq_part->left);
}

value::Str* AbstractWordEvaluator::_DecayArray(value::BashArray* val) {
  BigStr* sep = nullptr;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&sep);
  StackRoot _root2(&tmp);

  sep = this->splitter->GetJoinChar();
  tmp = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(bash_impl::BashArray_GetValues(val)); !it.Done(); it.Next()) {
    BigStr* s = it.Value();
    if (s != nullptr) {
      tmp->append(s);
    }
  }
  return Alloc<value::Str>(sep->join(tmp));
}

value_asdl::value_t* AbstractWordEvaluator::_ProcessUndef(value_asdl::value_t* val, syntax_asdl::Token* name_tok, runtime_asdl::VarSubState* vsub_state) {
  syntax_asdl::Token* array_tok = nullptr;
  BigStr* tok_str = nullptr;
  BigStr* name = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&name_tok);
  StackRoot _root2(&vsub_state);
  StackRoot _root3(&array_tok);
  StackRoot _root4(&tok_str);
  StackRoot _root5(&name);

  if (val->tag() != value_e::Undef) {
    return val;
  }
  if (vsub_state->array_ref != nullptr) {
    array_tok = vsub_state->array_ref;
    if (this->exec_opts->nounset()) {
      e_die(StrFormat("Undefined array %r", lexer::TokenVal(array_tok)), array_tok);
    }
    else {
      return Alloc<value::BashArray>(Alloc<List<BigStr*>>());
    }
  }
  else {
    if (this->exec_opts->nounset()) {
      tok_str = lexer::TokenVal(name_tok);
      name = tok_str->startswith(S_Czx) ? tok_str->slice(1) : tok_str;
      e_die(StrFormat("Undefined variable %r", name), name_tok);
    }
    else {
      return Alloc<value::Str>(S_Aoo);
    }
  }
}

value_asdl::value_t* AbstractWordEvaluator::_EvalBracketOp(value_asdl::value_t* val, syntax_asdl::BracedVarSub* part, bool quoted, runtime_asdl::VarSubState* vsub_state, runtime_asdl::VTestPlace* vtest_place) {
  BigStr* var_name = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&part);
  StackRoot _root2(&vsub_state);
  StackRoot _root3(&vtest_place);
  StackRoot _root4(&var_name);

  if (part->bracket_op) {
    switch (part->bracket_op->tag()) {
      case bracket_op_e::WholeArray: {
        val = this->_WholeArray(val, part, quoted, vsub_state);
      }
        break;
      case bracket_op_e::ArrayIndex: {
        val = this->_ArrayIndex(val, part, vtest_place);
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  else {
    var_name = vtest_place->name;
    if ((var_name != nullptr and (val->tag() == value_e::BashArray || val->tag() == value_e::SparseArray || val->tag() == value_e::BashAssoc))) {
      if (ShouldArrayDecay(var_name, this->exec_opts, !(part->prefix_op or part->suffix_op))) {
        val = DecayArray(val);
      }
      else {
        e_die(StrFormat("Array %r can't be referred to as a scalar (without @ or *)", var_name), Alloc<loc::WordPart>(part));
      }
    }
  }
  return val;
}

value_asdl::value_t* AbstractWordEvaluator::_VarRefValue(syntax_asdl::BracedVarSub* part, bool quoted, runtime_asdl::VarSubState* vsub_state, runtime_asdl::VTestPlace* vtest_place) {
  value_asdl::value_t* val = nullptr;
  int var_num;
  StackRoot _root0(&part);
  StackRoot _root1(&vsub_state);
  StackRoot _root2(&vtest_place);
  StackRoot _root3(&val);

  if (part->name_tok->id == Id::VSub_Name) {
    vtest_place->name = part->var_name;
    val = this->mem->GetValue(part->var_name);
  }
  else {
    if (part->name_tok->id == Id::VSub_Number) {
      var_num = to_int(part->var_name);
      val = this->_EvalVarNum(var_num);
    }
    else {
      val = this->_EvalSpecialVar(part->name_tok->id, quoted, vsub_state);
    }
  }
  vsub_state->h_value = val;
  if (this->exec_opts->eval_unsafe_arith()) {
    val = this->_EvalBracketOp(val, part, quoted, vsub_state, vtest_place);
  }
  else {
    {  // with
      state::ctx_Option ctx{this->mutable_opts, NewList<int>(std::initializer_list<int>{option_i::_allow_command_sub}), false};

      val = this->_EvalBracketOp(val, part, quoted, vsub_state, vtest_place);
    }
  }
  return val;
}

void AbstractWordEvaluator::_EvalBracedVarSub(syntax_asdl::BracedVarSub* part, List<runtime_asdl::part_value_t*>* part_vals, bool quoted) {
  BigStr* var_name = nullptr;
  runtime_asdl::VTestPlace* vtest_place = nullptr;
  runtime_asdl::VarSubState* vsub_state = nullptr;
  syntax_asdl::Token* nullary_op = nullptr;
  List<BigStr*>* names = nullptr;
  BigStr* sep = nullptr;
  value_asdl::value_t* val = nullptr;
  int var_num;
  syntax_asdl::suffix_op_t* suffix_op_ = nullptr;
  syntax_asdl::suffix_op_t* UP_op = nullptr;
  int n;
  bool quoted2;
  syntax_asdl::suffix_op_t* op = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  runtime_asdl::part_value_t* part_val = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&var_name);
  StackRoot _root3(&vtest_place);
  StackRoot _root4(&vsub_state);
  StackRoot _root5(&nullary_op);
  StackRoot _root6(&names);
  StackRoot _root7(&sep);
  StackRoot _root8(&val);
  StackRoot _root9(&suffix_op_);
  StackRoot _root10(&UP_op);
  StackRoot _root11(&op);
  StackRoot _root12(&UP_val);
  StackRoot _root13(&part_val);

  var_name = nullptr;
  vtest_place = Alloc<VTestPlace>(var_name, nullptr);
  vsub_state = VarSubState::CreateNull();
  if (part->name_tok->id == Id::VSub_Name) {
    if ((part->prefix_op != nullptr and (part->bracket_op == nullptr and (part->suffix_op != nullptr and part->suffix_op->tag() == suffix_op_e::Nullary)))) {
      nullary_op = static_cast<Token*>(part->suffix_op);
      if (consts::GetKind(nullary_op->id) == Kind::VOp3) {
        names = this->mem->VarNamesStartingWith(part->var_name);
        names->sort();
        if ((quoted and nullary_op->id == Id::VOp3_At)) {
          part_vals->append(Alloc<part_value::Array>(names));
        }
        else {
          sep = this->splitter->GetJoinChar();
          part_vals->append(Alloc<Piece>(sep->join(names), quoted, true));
        }
        return ;
      }
    }
    var_name = part->var_name;
    vtest_place->name = var_name;
    val = this->mem->GetValue(var_name);
  }
  else {
    if (part->name_tok->id == Id::VSub_Number) {
      var_num = to_int(part->var_name);
      val = this->_EvalVarNum(var_num);
    }
    else {
      val = this->_EvalSpecialVar(part->name_tok->id, quoted, vsub_state);
    }
  }
  suffix_op_ = part->suffix_op;
  if (suffix_op_) {
    UP_op = suffix_op_;
  }
  vsub_state->h_value = val;
  val = this->_EvalBracketOp(val, part, quoted, vsub_state, vtest_place);
  if (part->prefix_op) {
    if (part->prefix_op->id == Id::VSub_Pound) {
      val = this->_ProcessUndef(val, part->name_tok, vsub_state);
      n = this->_Count(val, part->name_tok);
      part_vals->append(Alloc<Piece>(str(n), quoted, false));
      return ;
    }
    else {
      if (part->prefix_op->id == Id::VSub_Bang) {
        if ((part->bracket_op and (part->bracket_op->tag() == bracket_op_e::WholeArray and !suffix_op_))) {
          val = this->_ProcessUndef(val, part->name_tok, vsub_state);
          val = this->_Keys(val, part->name_tok);
        }
        else {
          vtest_place->name = nullptr;
          vtest_place->index = nullptr;
          val = this->_EvalVarRef(val, part->name_tok, quoted, vsub_state, vtest_place);
        }
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  quoted2 = false;
  if (suffix_op_) {
    op = suffix_op_;
    switch (suffix_op_->tag()) {
      case suffix_op_e::Nullary: {
        Token* op = static_cast<Token*>(UP_op);
        Tuple2<value_asdl::value_t*, bool> tup7 = this->_Nullary(val, op, var_name, part->name_tok, vsub_state);
        val = tup7.at0();
        quoted2 = tup7.at1();
      }
        break;
      case suffix_op_e::Unary: {
        suffix_op::Unary* op = static_cast<suffix_op::Unary*>(UP_op);
        if (consts::GetKind(op->op->id) == Kind::VTest) {
          if (this->_ApplyTestOp(val, op, quoted, part_vals, vtest_place, part->name_tok, vsub_state)) {
            return ;
          }
        }
        else {
          val = this->_ProcessUndef(val, part->name_tok, vsub_state);
          val = this->_ApplyUnarySuffixOp(val, op);
        }
      }
        break;
      case suffix_op_e::PatSub: {
        suffix_op::PatSub* op = static_cast<suffix_op::PatSub*>(UP_op);
        val = this->_ProcessUndef(val, part->name_tok, vsub_state);
        val = this->_PatSub(val, op);
      }
        break;
      case suffix_op_e::Slice: {
        suffix_op::Slice* op = static_cast<suffix_op::Slice*>(UP_op);
        val = this->_ProcessUndef(val, part->name_tok, vsub_state);
        val = this->_Slice(val, op, var_name, part);
      }
        break;
      case suffix_op_e::Static: {
        suffix_op::Static* op = static_cast<suffix_op::Static*>(UP_op);
        e_die(S_mnf, op->tok);
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  else {
    val = this->_ProcessUndef(val, part->name_tok, vsub_state);
  }
  UP_val = val;
  if (val->tag() == value_e::BashArray) {
    value::BashArray* array_val = static_cast<value::BashArray*>(UP_val);
    if (vsub_state->join_array) {
      val = this->_DecayArray(array_val);
    }
    else {
      val = array_val;
    }
  }
  part_val = _ValueToPartValue(val, (quoted or quoted2), part);
  part_vals->append(part_val);
}

BigStr* AbstractWordEvaluator::_ConcatPartVals(List<runtime_asdl::part_value_t*>* part_vals, syntax_asdl::loc_t* location) {
  List<BigStr*>* strs = nullptr;
  runtime_asdl::part_value_t* UP_part_val = nullptr;
  BigStr* s = nullptr;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&part_vals);
  StackRoot _root1(&location);
  StackRoot _root2(&strs);
  StackRoot _root3(&UP_part_val);
  StackRoot _root4(&s);
  StackRoot _root5(&tmp);

  strs = Alloc<List<BigStr*>>();
  for (ListIter<runtime_asdl::part_value_t*> it(part_vals); !it.Done(); it.Next()) {
    runtime_asdl::part_value_t* part_val = it.Value();
    StackRoot _for(&part_val  );
    UP_part_val = part_val;
    switch (part_val->tag()) {
      case part_value_e::String: {
        Piece* part_val = static_cast<Piece*>(UP_part_val);
        s = part_val->s;
      }
        break;
      case part_value_e::Array: {
        part_value::Array* part_val = static_cast<part_value::Array*>(UP_part_val);
        if (this->exec_opts->strict_array()) {
          e_die(S_qul_1, location);
        }
        else {
          tmp = Alloc<List<BigStr*>>();
          for (ListIter<BigStr*> it(part_val->strs); !it.Done(); it.Next()) {
            BigStr* s = it.Value();
            if (s != nullptr) {
              tmp->append(s);
            }
          }
          s = S_yfw->join(tmp);
        }
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
    strs->append(s);
  }
  return S_Aoo->join(strs);
}

BigStr* AbstractWordEvaluator::EvalBracedVarSubToString(syntax_asdl::BracedVarSub* part) {
  List<runtime_asdl::part_value_t*>* part_vals = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&part_vals);

  part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
  this->_EvalBracedVarSub(part, part_vals, false);
  return this->_ConcatPartVals(part_vals, part->left);
}

void AbstractWordEvaluator::_EvalSimpleVarSub(syntax_asdl::SimpleVarSub* part, List<runtime_asdl::part_value_t*>* part_vals, bool quoted) {
  syntax_asdl::Token* token = nullptr;
  runtime_asdl::VarSubState* vsub_state = nullptr;
  BigStr* var_name = nullptr;
  value_asdl::value_t* val = nullptr;
  int var_num;
  value_asdl::value_t* UP_val = nullptr;
  runtime_asdl::part_value_t* v = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&token);
  StackRoot _root3(&vsub_state);
  StackRoot _root4(&var_name);
  StackRoot _root5(&val);
  StackRoot _root6(&UP_val);
  StackRoot _root7(&v);

  token = part->tok;
  vsub_state = VarSubState::CreateNull();
  if (token->id == Id::VSub_DollarName) {
    var_name = lexer::LazyStr(token);
    val = this->mem->GetValue(var_name);
    if ((val->tag() == value_e::BashArray || val->tag() == value_e::SparseArray || val->tag() == value_e::BashAssoc)) {
      if (ShouldArrayDecay(var_name, this->exec_opts)) {
        val = DecayArray(val);
      }
      else {
        e_die(StrFormat("Array %r can't be referred to as a scalar (without @ or *)", var_name), token);
      }
    }
  }
  else {
    if (token->id == Id::VSub_Number) {
      var_num = to_int(lexer::LazyStr(token));
      val = this->_EvalVarNum(var_num);
    }
    else {
      val = this->_EvalSpecialVar(token->id, quoted, vsub_state);
    }
  }
  val = this->_ProcessUndef(val, token, vsub_state);
  UP_val = val;
  if (val->tag() == value_e::BashArray) {
    value::BashArray* array_val = static_cast<value::BashArray*>(UP_val);
    if (vsub_state->join_array) {
      val = this->_DecayArray(array_val);
    }
    else {
      val = array_val;
    }
  }
  v = _ValueToPartValue(val, quoted, part);
  part_vals->append(v);
}

BigStr* AbstractWordEvaluator::EvalSimpleVarSubToString(syntax_asdl::SimpleVarSub* node) {
  List<runtime_asdl::part_value_t*>* part_vals = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&part_vals);

  part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
  this->_EvalSimpleVarSub(node, part_vals, false);
  return this->_ConcatPartVals(part_vals, node->tok);
}

void AbstractWordEvaluator::_EvalExtGlob(word_part::ExtGlob* part, List<runtime_asdl::part_value_t*>* part_vals) {
  syntax_asdl::Token* op = nullptr;
  BigStr* op_str = nullptr;
  int i;
  StackRoot _root0(&part);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&op);
  StackRoot _root3(&op_str);

  op = part->op;
  if (op->id == Id::ExtGlob_Comma) {
    op_str = S_xsa;
  }
  else {
    op_str = lexer::LazyStr(op);
  }
  part_vals->append(Alloc<Piece>(op_str, false, false));
  i = 0;
  for (ListIter<syntax_asdl::CompoundWord*> it(part->arms); !it.Done(); it.Next(), ++i) {
    syntax_asdl::CompoundWord* w = it.Value();
    StackRoot _for(&w  );
    if (i != 0) {
      part_vals->append(Alloc<Piece>(S_Ebn, false, false));
    }
    this->_EvalWordToParts(w, part_vals, EXTGLOB_NESTED);
  }
  part_vals->append(Alloc<Piece>(S_hxb, false, false));
}

void AbstractWordEvaluator::_TranslateExtGlob(List<runtime_asdl::part_value_t*>* part_vals, syntax_asdl::CompoundWord* w, List<BigStr*>* glob_parts, List<BigStr*>* fnmatch_parts) {
  int i;
  runtime_asdl::part_value_t* UP_part_val = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&part_vals);
  StackRoot _root1(&w);
  StackRoot _root2(&glob_parts);
  StackRoot _root3(&fnmatch_parts);
  StackRoot _root4(&UP_part_val);
  StackRoot _root5(&s);

  i = 0;
  for (ListIter<runtime_asdl::part_value_t*> it(part_vals); !it.Done(); it.Next(), ++i) {
    runtime_asdl::part_value_t* part_val = it.Value();
    StackRoot _for(&part_val  );
    UP_part_val = part_val;
    switch (part_val->tag()) {
      case part_value_e::String: {
        Piece* part_val = static_cast<Piece*>(UP_part_val);
        if ((part_val->quoted and !this->exec_opts->noglob())) {
          s = glob_::GlobEscape(part_val->s);
        }
        else {
          s = part_val->s;
        }
        glob_parts->append(s);
        fnmatch_parts->append(s);
      }
        break;
      case part_value_e::Array: {
        e_die(S_cli, w);
      }
        break;
      case part_value_e::ExtGlob: {
        part_value::ExtGlob* part_val = static_cast<part_value::ExtGlob*>(UP_part_val);
        this->_TranslateExtGlob(part_val->part_vals, w, Alloc<List<BigStr*>>(), fnmatch_parts);
        glob_parts->append(S_Fgw);
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
}

void AbstractWordEvaluator::_EvalWordPart(syntax_asdl::word_part_t* part, List<runtime_asdl::part_value_t*>* part_vals, int flags) {
  bool quoted;
  bool is_subst;
  syntax_asdl::word_part_t* UP_part = nullptr;
  runtime_asdl::Piece* v = nullptr;
  int id_;
  runtime_asdl::part_value_t* sv = nullptr;
  BigStr* s = nullptr;
  mops::BigInt num;
  List<runtime_asdl::part_value_t*>* part_vals2 = nullptr;
  value_asdl::value_t* val = nullptr;
  List<BigStr*>* strs = nullptr;
  runtime_asdl::part_value_t* part_val = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&UP_part);
  StackRoot _root3(&v);
  StackRoot _root4(&sv);
  StackRoot _root5(&s);
  StackRoot _root6(&part_vals2);
  StackRoot _root7(&val);
  StackRoot _root8(&strs);
  StackRoot _root9(&part_val);

  quoted = to_bool((flags & QUOTED));
  is_subst = to_bool((flags & IS_SUBST));
  UP_part = part;
  switch (part->tag()) {
    case word_part_e::ShArrayLiteral: {
      ShArrayLiteral* part = static_cast<ShArrayLiteral*>(UP_part);
      e_die(S_Arg, Alloc<loc::WordPart>(part));
    }
      break;
    case word_part_e::BashAssocLiteral: {
      word_part::BashAssocLiteral* part = static_cast<word_part::BashAssocLiteral*>(UP_part);
      e_die(S_aya, Alloc<loc::WordPart>(part));
    }
      break;
    case word_part_e::Literal: {
      Token* part = static_cast<Token*>(UP_part);
      v = Alloc<Piece>(lexer::LazyStr(part), quoted, is_subst);
      part_vals->append(v);
    }
      break;
    case word_part_e::EscapedLiteral: {
      word_part::EscapedLiteral* part = static_cast<word_part::EscapedLiteral*>(UP_part);
      v = Alloc<Piece>(part->ch, true, false);
      part_vals->append(v);
    }
      break;
    case word_part_e::SingleQuoted: {
      SingleQuoted* part = static_cast<SingleQuoted*>(UP_part);
      v = Alloc<Piece>(part->sval, true, false);
      part_vals->append(v);
    }
      break;
    case word_part_e::DoubleQuoted: {
      DoubleQuoted* part = static_cast<DoubleQuoted*>(UP_part);
      this->_EvalDoubleQuoted(part->parts, part_vals);
    }
      break;
    case word_part_e::CommandSub: {
      CommandSub* part = static_cast<CommandSub*>(UP_part);
      id_ = part->left_token->id;
      if ((id_ == Id::Left_DollarParen || id_ == Id::Left_AtParen || id_ == Id::Left_Backtick)) {
        sv = this->_EvalCommandSub(part, quoted);
      }
      else {
        if ((id_ == Id::Left_ProcSubIn || id_ == Id::Left_ProcSubOut)) {
          sv = this->_EvalProcessSub(part);
        }
        else {
          assert(0);  // AssertionError
        }
      }
      part_vals->append(sv);
    }
      break;
    case word_part_e::SimpleVarSub: {
      SimpleVarSub* part = static_cast<SimpleVarSub*>(UP_part);
      this->_EvalSimpleVarSub(part, part_vals, quoted);
    }
      break;
    case word_part_e::BracedVarSub: {
      BracedVarSub* part = static_cast<BracedVarSub*>(UP_part);
      this->_EvalBracedVarSub(part, part_vals, quoted);
    }
      break;
    case word_part_e::TildeSub: {
      word_part::TildeSub* part = static_cast<word_part::TildeSub*>(UP_part);
      s = this->tilde_ev->Eval(part);
      v = Alloc<Piece>(s, true, false);
      part_vals->append(v);
    }
      break;
    case word_part_e::ArithSub: {
      word_part::ArithSub* part = static_cast<word_part::ArithSub*>(UP_part);
      num = this->arith_ev->EvalToBigInt(part->anode);
      v = Alloc<Piece>(mops::ToStr(num), quoted, !quoted);
      part_vals->append(v);
    }
      break;
    case word_part_e::ExtGlob: {
      word_part::ExtGlob* part = static_cast<word_part::ExtGlob*>(UP_part);
      part_vals2 = Alloc<List<runtime_asdl::part_value_t*>>();
      this->_EvalExtGlob(part, part_vals2);
      part_vals->append(Alloc<part_value::ExtGlob>(part_vals2));
    }
      break;
    case word_part_e::BashRegexGroup: {
      word_part::BashRegexGroup* part = static_cast<word_part::BashRegexGroup*>(UP_part);
      part_vals->append(Alloc<Piece>(S_ijB, false, false));
      if (part->child) {
        this->_EvalWordToParts(part->child, part_vals, 0);
      }
      part_vals->append(Alloc<Piece>(S_hxb, false, false));
    }
      break;
    case word_part_e::Splice: {
      word_part::Splice* part = static_cast<word_part::Splice*>(UP_part);
      val = this->mem->GetValue(part->var_name);
      strs = this->expr_ev->SpliceValue(val, part);
      part_vals->append(Alloc<part_value::Array>(strs));
    }
      break;
    case word_part_e::ExprSub: {
      word_part::ExprSub* part = static_cast<word_part::ExprSub*>(UP_part);
      part_val = this->expr_ev->EvalExprSub(part);
      part_vals->append(part_val);
    }
      break;
    case word_part_e::ZshVarSub: {
      word_part::ZshVarSub* part = static_cast<word_part::ZshVarSub*>(UP_part);
      e_die(S_bvg, part->left);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void AbstractWordEvaluator::_EvalRhsWordToParts(syntax_asdl::rhs_word_t* w, List<runtime_asdl::part_value_t*>* part_vals, int eval_flags) {
  bool quoted;
  syntax_asdl::rhs_word_t* UP_w = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&UP_w);

  quoted = to_bool((eval_flags & QUOTED));
  UP_w = w;
  switch (w->tag()) {
    case rhs_word_e::Empty: {
      part_vals->append(Alloc<Piece>(S_Aoo, quoted, !quoted));
    }
      break;
    case rhs_word_e::Compound: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_w);
      this->_EvalWordToParts(w, part_vals, eval_flags);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void AbstractWordEvaluator::_EvalWordToParts(syntax_asdl::CompoundWord* w, List<runtime_asdl::part_value_t*>* part_vals, int eval_flags) {
  List<runtime_asdl::part_value_t*>* word_part_vals = nullptr;
  bool has_extglob;
  List<BigStr*>* glob_parts = nullptr;
  List<BigStr*>* fnmatch_parts = nullptr;
  BigStr* glob_pat = nullptr;
  BigStr* fnmatch_pat = nullptr;
  List<BigStr*>* results = nullptr;
  int n;
  StackRoot _root0(&w);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&word_part_vals);
  StackRoot _root3(&glob_parts);
  StackRoot _root4(&fnmatch_parts);
  StackRoot _root5(&glob_pat);
  StackRoot _root6(&fnmatch_pat);
  StackRoot _root7(&results);

  word_part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
  has_extglob = false;
  for (ListIter<syntax_asdl::word_part_t*> it(w->parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* p = it.Value();
    StackRoot _for(&p  );
    if (p->tag() == word_part_e::ExtGlob) {
      has_extglob = true;
    }
    this->_EvalWordPart(p, word_part_vals, eval_flags);
  }
  if (has_extglob) {
    if (to_bool((eval_flags & EXTGLOB_FILES))) {
      glob_parts = Alloc<List<BigStr*>>();
      fnmatch_parts = Alloc<List<BigStr*>>();
      this->_TranslateExtGlob(word_part_vals, w, glob_parts, fnmatch_parts);
      glob_pat = S_Aoo->join(glob_parts);
      fnmatch_pat = S_Aoo->join(fnmatch_parts);
      results = Alloc<List<BigStr*>>();
      n = this->globber->ExpandExtended(glob_pat, fnmatch_pat, results);
      if (n < 0) {
        throw Alloc<error::FailGlob>(StrFormat("Extended glob %r matched no files", fnmatch_pat), w);
      }
      part_vals->append(Alloc<part_value::Array>(results));
    }
    else {
      if (to_bool((eval_flags & EXTGLOB_NESTED))) {
        part_vals->extend(word_part_vals);
      }
      else {
        e_die(S_hzl, w);
      }
    }
  }
  else {
    part_vals->extend(word_part_vals);
  }
}

void AbstractWordEvaluator::_PartValsToString(List<runtime_asdl::part_value_t*>* part_vals, syntax_asdl::CompoundWord* w, int eval_flags, List<BigStr*>* strs) {
  runtime_asdl::part_value_t* UP_part_val = nullptr;
  BigStr* s = nullptr;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&part_vals);
  StackRoot _root1(&w);
  StackRoot _root2(&strs);
  StackRoot _root3(&UP_part_val);
  StackRoot _root4(&s);
  StackRoot _root5(&tmp);

  for (ListIter<runtime_asdl::part_value_t*> it(part_vals); !it.Done(); it.Next()) {
    runtime_asdl::part_value_t* part_val = it.Value();
    StackRoot _for(&part_val  );
    UP_part_val = part_val;
    switch (part_val->tag()) {
      case part_value_e::String: {
        Piece* part_val = static_cast<Piece*>(UP_part_val);
        s = part_val->s;
        if (part_val->quoted) {
          if ((eval_flags & QUOTE_FNMATCH)) {
            s = glob_::GlobEscape(s);
          }
          else {
            if ((eval_flags & QUOTE_ERE)) {
              s = glob_::ExtendedRegexEscape(s);
            }
          }
        }
        strs->append(s);
      }
        break;
      case part_value_e::Array: {
        part_value::Array* part_val = static_cast<part_value::Array*>(UP_part_val);
        if (this->exec_opts->strict_array()) {
          e_die(S_Emv, w);
        }
        else {
          tmp = Alloc<List<BigStr*>>();
          for (ListIter<BigStr*> it(part_val->strs); !it.Done(); it.Next()) {
            BigStr* s = it.Value();
            if (s != nullptr) {
              tmp->append(s);
            }
          }
          s = S_yfw->join(tmp);
          strs->append(s);
        }
      }
        break;
      case part_value_e::ExtGlob: {
        part_value::ExtGlob* part_val = static_cast<part_value::ExtGlob*>(UP_part_val);
        if (!to_bool((eval_flags & QUOTE_FNMATCH))) {
          e_die(S_idc_1, w);
        }
        this->_PartValsToString(part_val->part_vals, w, eval_flags, strs);
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
}

value::Str* AbstractWordEvaluator::EvalWordToString(syntax_asdl::word_t* UP_w, int eval_flags) {
  BigStr* fast_str = nullptr;
  List<runtime_asdl::part_value_t*>* part_vals = nullptr;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&UP_w);
  StackRoot _root1(&fast_str);
  StackRoot _root2(&part_vals);
  StackRoot _root3(&strs);

  CompoundWord* w = static_cast<CompoundWord*>(UP_w);
  if (eval_flags == 0) {
    fast_str = word_::FastStrEval(w);
    if (fast_str != nullptr) {
      return Alloc<value::Str>(fast_str);
    }
  }
  part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
  for (ListIter<syntax_asdl::word_part_t*> it(w->parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* p = it.Value();
    StackRoot _for(&p  );
    this->_EvalWordPart(p, part_vals, 0);
  }
  strs = Alloc<List<BigStr*>>();
  this->_PartValsToString(part_vals, w, eval_flags, strs);
  return Alloc<value::Str>(S_Aoo->join(strs));
}

Tuple2<value::Str*, bool> AbstractWordEvaluator::EvalWordToPattern(syntax_asdl::rhs_word_t* UP_w) {
  bool has_extglob;
  List<runtime_asdl::part_value_t*>* part_vals = nullptr;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&UP_w);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&strs);

  if (UP_w->tag() == rhs_word_e::Empty) {
    return Tuple2<value::Str*, bool>(Alloc<value::Str>(S_Aoo), false);
  }
  CompoundWord* w = static_cast<CompoundWord*>(UP_w);
  has_extglob = false;
  part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
  for (ListIter<syntax_asdl::word_part_t*> it(w->parts); !it.Done(); it.Next()) {
    syntax_asdl::word_part_t* p = it.Value();
    StackRoot _for(&p  );
    this->_EvalWordPart(p, part_vals, 0);
    if (p->tag() == word_part_e::ExtGlob) {
      has_extglob = true;
    }
  }
  strs = Alloc<List<BigStr*>>();
  this->_PartValsToString(part_vals, w, QUOTE_FNMATCH, strs);
  return Tuple2<value::Str*, bool>(Alloc<value::Str>(S_Aoo->join(strs)), has_extglob);
}

value::Str* AbstractWordEvaluator::EvalForPlugin(syntax_asdl::CompoundWord* w) {
  value::Str* val = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&val);

  {  // with
    state::ctx_Registers ctx{this->mem};

    try {
      val = this->EvalWordToString(w);
    }
    catch (error::FatalRuntime* e) {
      val = Alloc<value::Str>(StrFormat("<Runtime error: %s>", e->UserErrorString()));
    }
    catch (IOError_OSError* e) {
      val = Alloc<value::Str>(StrFormat("<I/O error: %s>", pyutil::strerror(e)));
    }
    catch (KeyboardInterrupt*) {
      val = Alloc<value::Str>(S_uur);
    }
  }
  return val;
}

value_asdl::value_t* AbstractWordEvaluator::EvalRhsWord(syntax_asdl::rhs_word_t* UP_w) {
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::word_part_t* UP_part0 = nullptr;
  int tag;
  List<syntax_asdl::word_t*>* array_words = nullptr;
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  List<BigStr*>* strs = nullptr;
  Dict<BigStr*, BigStr*>* d = nullptr;
  value::Str* k = nullptr;
  value::Str* v = nullptr;
  StackRoot _root0(&UP_w);
  StackRoot _root1(&part0);
  StackRoot _root2(&UP_part0);
  StackRoot _root3(&array_words);
  StackRoot _root4(&words);
  StackRoot _root5(&strs);
  StackRoot _root6(&d);
  StackRoot _root7(&k);
  StackRoot _root8(&v);

  if (UP_w->tag() == rhs_word_e::Empty) {
    return Alloc<value::Str>(S_Aoo);
  }
  CompoundWord* w = static_cast<CompoundWord*>(UP_w);
  if (len(w->parts) == 1) {
    part0 = w->parts->at(0);
    UP_part0 = part0;
    tag = part0->tag();
    if (tag == word_part_e::ShArrayLiteral) {
      ShArrayLiteral* part0 = static_cast<ShArrayLiteral*>(UP_part0);
      array_words = part0->words;
      words = braces::BraceExpandWords(array_words);
      strs = this->EvalWordSequence(words);
      return Alloc<value::BashArray>(strs);
    }
    if (tag == word_part_e::BashAssocLiteral) {
      word_part::BashAssocLiteral* part0 = static_cast<word_part::BashAssocLiteral*>(UP_part0);
      d = Alloc<Dict<BigStr*, BigStr*>>();
      for (ListIter<syntax_asdl::AssocPair*> it(part0->pairs); !it.Done(); it.Next()) {
        syntax_asdl::AssocPair* pair = it.Value();
        StackRoot _for(&pair      );
        k = this->EvalWordToString(pair->key);
        v = this->EvalWordToString(pair->value);
        d->set(k->s, v->s);
      }
      return Alloc<value::BashAssoc>(d);
    }
  }
  return this->EvalWordToString(w);
}

void AbstractWordEvaluator::_EvalWordFrame(List<runtime_asdl::Piece*>* frame, List<BigStr*>* argv) {
  bool all_empty;
  bool all_quoted;
  bool any_quoted;
  List<BigStr*>* tmp = nullptr;
  BigStr* a = nullptr;
  bool will_glob;
  List<BigStr*>* frags = nullptr;
  BigStr* frag = nullptr;
  BigStr* flat = nullptr;
  List<BigStr*>* args = nullptr;
  int n;
  StackRoot _root0(&frame);
  StackRoot _root1(&argv);
  StackRoot _root2(&tmp);
  StackRoot _root3(&a);
  StackRoot _root4(&frags);
  StackRoot _root5(&frag);
  StackRoot _root6(&flat);
  StackRoot _root7(&args);

  all_empty = true;
  all_quoted = true;
  any_quoted = false;
  for (ListIter<runtime_asdl::Piece*> it(frame); !it.Done(); it.Next()) {
    runtime_asdl::Piece* piece = it.Value();
    StackRoot _for(&piece  );
    if (len(piece->s)) {
      all_empty = false;
    }
    if (piece->quoted) {
      any_quoted = true;
    }
    else {
      all_quoted = false;
    }
  }
  if ((all_empty and !any_quoted)) {
    return ;
  }
  if (all_quoted) {
    tmp = Alloc<List<BigStr*>>();
    for (ListIter<runtime_asdl::Piece*> it(frame); !it.Done(); it.Next()) {
      runtime_asdl::Piece* piece = it.Value();
      tmp->append(piece->s);
    }
    a = S_Aoo->join(tmp);
    argv->append(a);
    return ;
  }
  will_glob = !this->exec_opts->noglob();
  frags = Alloc<List<BigStr*>>();
  for (ListIter<runtime_asdl::Piece*> it(frame); !it.Done(); it.Next()) {
    runtime_asdl::Piece* piece = it.Value();
    StackRoot _for(&piece  );
    if ((will_glob and piece->quoted)) {
      frag = glob_::GlobEscape(piece->s);
    }
    else {
      frag = _BackslashEscape(piece->s);
    }
    if (piece->do_split) {
      frag = _BackslashEscape(frag);
    }
    else {
      frag = this->splitter->Escape(frag);
    }
    frags->append(frag);
  }
  flat = S_Aoo->join(frags);
  args = this->splitter->SplitForWordEval(flat);
  if ((len(args) == 0 and any_quoted)) {
    argv->append(S_Aoo);
    return ;
  }
  for (ListIter<BigStr*> it(args); !it.Done(); it.Next()) {
    BigStr* a = it.Value();
    StackRoot _for(&a  );
    if (glob_::LooksLikeGlob(a)) {
      n = this->globber->Expand(a, argv);
      if (n < 0) {
        throw Alloc<error::FailGlob>(StrFormat("Pattern %r matched no files", a), loc::Missing);
      }
    }
    else {
      argv->append(glob_::GlobUnescape(a));
    }
  }
}

List<BigStr*>* AbstractWordEvaluator::_EvalWordToArgv(syntax_asdl::CompoundWord* w) {
  List<runtime_asdl::part_value_t*>* part_vals = nullptr;
  List<List<runtime_asdl::Piece*>*>* frames = nullptr;
  List<BigStr*>* argv = nullptr;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&part_vals);
  StackRoot _root2(&frames);
  StackRoot _root3(&argv);
  StackRoot _root4(&tmp);

  part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
  this->_EvalWordToParts(w, part_vals, 0);
  frames = _MakeWordFrames(part_vals);
  argv = Alloc<List<BigStr*>>();
  for (ListIter<List<runtime_asdl::Piece*>*> it(frames); !it.Done(); it.Next()) {
    List<runtime_asdl::Piece*>* frame = it.Value();
    StackRoot _for(&frame  );
    if (len(frame)) {
      tmp = Alloc<List<BigStr*>>();
      for (ListIter<runtime_asdl::Piece*> it(frame); !it.Done(); it.Next()) {
        runtime_asdl::Piece* piece = it.Value();
        tmp->append(piece->s);
      }
      argv->append(S_Aoo->join(tmp));
    }
  }
  return argv;
}

cmd_value::Assign* AbstractWordEvaluator::_EvalAssignBuiltin(int builtin_id, BigStr* arg0, List<syntax_asdl::CompoundWord*>* words, int meta_offset) {
  bool eval_to_pairs;
  bool started_pairs;
  List<BigStr*>* flags = nullptr;
  List<syntax_asdl::CompoundWord*>* flag_locs = nullptr;
  List<runtime_asdl::AssignArg*>* assign_args = nullptr;
  int n;
  syntax_asdl::CompoundWord* w = nullptr;
  syntax_asdl::Token* left_token = nullptr;
  syntax_asdl::Token* close_token = nullptr;
  int part_offset;
  BigStr* var_name = nullptr;
  bool append;
  syntax_asdl::rhs_word_t* rhs = nullptr;
  syntax_asdl::CompoundWord* tmp = nullptr;
  value_asdl::value_t* right = nullptr;
  runtime_asdl::AssignArg* arg2 = nullptr;
  List<BigStr*>* argv = nullptr;
  StackRoot _root0(&arg0);
  StackRoot _root1(&words);
  StackRoot _root2(&flags);
  StackRoot _root3(&flag_locs);
  StackRoot _root4(&assign_args);
  StackRoot _root5(&w);
  StackRoot _root6(&left_token);
  StackRoot _root7(&close_token);
  StackRoot _root8(&var_name);
  StackRoot _root9(&rhs);
  StackRoot _root10(&tmp);
  StackRoot _root11(&right);
  StackRoot _root12(&arg2);
  StackRoot _root13(&argv);

  eval_to_pairs = true;
  started_pairs = false;
  flags = NewList<BigStr*>(std::initializer_list<BigStr*>{arg0});
  flag_locs = NewList<syntax_asdl::CompoundWord*>(std::initializer_list<syntax_asdl::CompoundWord*>{words->at(0)});
  assign_args = Alloc<List<runtime_asdl::AssignArg*>>();
  n = len(words);
  for (int i = (meta_offset + 1); i < n; ++i) {
    w = words->at(i);
    if (word_::IsVarLike(w)) {
      started_pairs = true;
    }
    if (started_pairs) {
      Tuple3<syntax_asdl::Token*, syntax_asdl::Token*, int> tup8 = word_::DetectShAssignment(w);
      left_token = tup8.at0();
      close_token = tup8.at1();
      part_offset = tup8.at2();
      if (left_token) {
        if (left_token->id != Id::Lit_VarLike) {
          e_die(S_lgF, w);
        }
        if (lexer::IsPlusEquals(left_token)) {
          var_name = lexer::TokenSliceRight(left_token, -2);
          append = true;
        }
        else {
          var_name = lexer::TokenSliceRight(left_token, -1);
          append = false;
        }
        if (part_offset == len(w->parts)) {
          rhs = rhs_word::Empty;
        }
        else {
          tmp = Alloc<CompoundWord>(w->parts->slice(part_offset));
          word_::TildeDetectAssign(tmp);
          rhs = tmp;
        }
        {  // with
          state::ctx_AssignBuiltin ctx{this->mutable_opts};

          right = this->EvalRhsWord(rhs);
        }
        arg2 = Alloc<AssignArg>(var_name, right, append, w);
        assign_args->append(arg2);
      }
      else {
        argv = this->_EvalWordToArgv(w);
        for (ListIter<BigStr*> it(argv); !it.Done(); it.Next()) {
          BigStr* arg = it.Value();
          StackRoot _for(&arg        );
          arg2 = _SplitAssignArg(arg, w);
          assign_args->append(arg2);
        }
      }
    }
    else {
      argv = this->_EvalWordToArgv(w);
      for (ListIter<BigStr*> it(argv); !it.Done(); it.Next()) {
        BigStr* arg = it.Value();
        StackRoot _for(&arg      );
        if ((arg->startswith(S_Bjq) or arg->startswith(S_jnE))) {
          flags->append(arg);
          flag_locs->append(w);
          if ((str_contains(arg, S_ksc) or str_contains(arg, S_gFh))) {
            eval_to_pairs = false;
          }
        }
        else {
          if (eval_to_pairs) {
            arg2 = _SplitAssignArg(arg, w);
            assign_args->append(arg2);
            started_pairs = true;
          }
          else {
            flags->append(arg);
          }
        }
      }
    }
  }
  return Alloc<cmd_value::Assign>(builtin_id, flags, flag_locs, assign_args);
}

cmd_value::Assign* AbstractWordEvaluator::_DetectAssignBuiltinStr(BigStr* arg0, List<syntax_asdl::CompoundWord*>* words, int meta_offset) {
  int builtin_id;
  StackRoot _root0(&arg0);
  StackRoot _root1(&words);

  builtin_id = consts::LookupAssignBuiltin(arg0);
  if (builtin_id != consts::NO_INDEX) {
    return this->_EvalAssignBuiltin(builtin_id, arg0, words, meta_offset);
  }
  return nullptr;
}

cmd_value::Assign* AbstractWordEvaluator::_DetectAssignBuiltin(runtime_asdl::part_value_t* val0, List<syntax_asdl::CompoundWord*>* words, int meta_offset) {
  runtime_asdl::part_value_t* UP_val0 = nullptr;
  StackRoot _root0(&val0);
  StackRoot _root1(&words);
  StackRoot _root2(&UP_val0);

  UP_val0 = val0;
  if (val0->tag() == part_value_e::String) {
    Piece* val0 = static_cast<Piece*>(UP_val0);
    if (!val0->quoted) {
      return this->_DetectAssignBuiltinStr(val0->s, words, meta_offset);
    }
  }
  return nullptr;
}

runtime_asdl::cmd_value_t* AbstractWordEvaluator::SimpleEvalWordSequence2(List<syntax_asdl::CompoundWord*>* words, bool is_last_cmd, bool allow_assign) {
  List<BigStr*>* strs = nullptr;
  List<syntax_asdl::CompoundWord*>* locs = nullptr;
  int meta_offset;
  int i;
  List<BigStr*>* strs0 = nullptr;
  cmd_value::Assign* cmd_val = nullptr;
  value::Str* val = nullptr;
  int num_appended;
  List<runtime_asdl::part_value_t*>* part_vals = nullptr;
  List<List<runtime_asdl::Piece*>*>* frames = nullptr;
  List<BigStr*>* tmp = nullptr;
  StackRoot _root0(&words);
  StackRoot _root1(&strs);
  StackRoot _root2(&locs);
  StackRoot _root3(&strs0);
  StackRoot _root4(&cmd_val);
  StackRoot _root5(&val);
  StackRoot _root6(&part_vals);
  StackRoot _root7(&frames);
  StackRoot _root8(&tmp);

  strs = Alloc<List<BigStr*>>();
  locs = Alloc<List<syntax_asdl::CompoundWord*>>();
  meta_offset = 0;
  i = 0;
  for (ListIter<syntax_asdl::CompoundWord*> it(words); !it.Done(); it.Next(), ++i) {
    syntax_asdl::CompoundWord* w = it.Value();
    StackRoot _for(&w  );
    if ((i == meta_offset and allow_assign)) {
      strs0 = this->_EvalWordToArgv(w);
      if (len(strs0) == 1) {
        cmd_val = this->_DetectAssignBuiltinStr(strs0->at(0), words, meta_offset);
        if (cmd_val) {
          return cmd_val;
        }
      }
      strs->extend(strs0);
      for (ListIter<BigStr*> it(strs0); !it.Done(); it.Next()) {
        BigStr* _ = it.Value();
        StackRoot _for(&_      );
        locs->append(w);
      }
      continue;
    }
    if (glob_::LooksLikeStaticGlob(w)) {
      val = this->EvalWordToString(w);
      num_appended = this->globber->Expand(val->s, strs);
      if (num_appended < 0) {
        throw Alloc<error::FailGlob>(StrFormat("Pattern %r matched no files", val->s), w);
      }
      for (int _ = 0; _ < num_appended; ++_) {
        locs->append(w);
      }
      continue;
    }
    part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
    this->_EvalWordToParts(w, part_vals, 0);
    frames = _MakeWordFrames(part_vals);
    for (ListIter<List<runtime_asdl::Piece*>*> it(frames); !it.Done(); it.Next()) {
      List<runtime_asdl::Piece*>* frame = it.Value();
      StackRoot _for(&frame    );
      if (len(frame)) {
        tmp = Alloc<List<BigStr*>>();
        for (ListIter<runtime_asdl::Piece*> it(frame); !it.Done(); it.Next()) {
          runtime_asdl::Piece* piece = it.Value();
          tmp->append(piece->s);
        }
        strs->append(S_Aoo->join(tmp));
        locs->append(w);
      }
    }
  }
  return Alloc<cmd_value::Argv>(strs, locs, is_last_cmd, nullptr, nullptr);
}

runtime_asdl::cmd_value_t* AbstractWordEvaluator::EvalWordSequence2(List<syntax_asdl::CompoundWord*>* words, bool is_last_cmd, bool allow_assign) {
  List<BigStr*>* strs = nullptr;
  List<syntax_asdl::CompoundWord*>* locs = nullptr;
  int meta_offset;
  int n;
  int i;
  BigStr* fast_str = nullptr;
  cmd_value::Assign* cmd_val = nullptr;
  List<runtime_asdl::part_value_t*>* part_vals = nullptr;
  List<List<runtime_asdl::Piece*>*>* frames = nullptr;
  int n_next;
  StackRoot _root0(&words);
  StackRoot _root1(&strs);
  StackRoot _root2(&locs);
  StackRoot _root3(&fast_str);
  StackRoot _root4(&cmd_val);
  StackRoot _root5(&part_vals);
  StackRoot _root6(&frames);

  if (this->exec_opts->simple_word_eval()) {
    return this->SimpleEvalWordSequence2(words, is_last_cmd, allow_assign);
  }
  strs = Alloc<List<BigStr*>>();
  locs = Alloc<List<syntax_asdl::CompoundWord*>>();
  meta_offset = 0;
  n = 0;
  i = 0;
  for (ListIter<syntax_asdl::CompoundWord*> it(words); !it.Done(); it.Next(), ++i) {
    syntax_asdl::CompoundWord* w = it.Value();
    StackRoot _for(&w  );
    fast_str = word_::FastStrEval(w);
    if (fast_str != nullptr) {
      strs->append(fast_str);
      locs->append(w);
      if ((allow_assign and i == meta_offset)) {
        cmd_val = this->_DetectAssignBuiltinStr(fast_str, words, meta_offset);
        if (cmd_val) {
          return cmd_val;
        }
      }
      if ((i <= meta_offset and _DetectMetaBuiltinStr(fast_str))) {
        meta_offset += 1;
      }
      n = len(strs);
      continue;
    }
    part_vals = Alloc<List<runtime_asdl::part_value_t*>>();
    this->_EvalWordToParts(w, part_vals, EXTGLOB_FILES);
    if (len(part_vals) == 1) {
      if ((allow_assign and i == meta_offset)) {
        cmd_val = this->_DetectAssignBuiltin(part_vals->at(0), words, meta_offset);
        if (cmd_val) {
          return cmd_val;
        }
      }
      if ((i <= meta_offset and _DetectMetaBuiltin(part_vals->at(0)))) {
        meta_offset += 1;
      }
    }
    frames = _MakeWordFrames(part_vals);
    for (ListIter<List<runtime_asdl::Piece*>*> it(frames); !it.Done(); it.Next()) {
      List<runtime_asdl::Piece*>* frame = it.Value();
      StackRoot _for(&frame    );
      this->_EvalWordFrame(frame, strs);
    }
    n_next = len(strs);
    for (int _ = 0; _ < (n_next - n); ++_) {
      locs->append(w);
    }
    n = n_next;
  }
  return Alloc<cmd_value::Argv>(strs, locs, is_last_cmd, nullptr, nullptr);
}

List<BigStr*>* AbstractWordEvaluator::EvalWordSequence(List<syntax_asdl::CompoundWord*>* words) {
  runtime_asdl::cmd_value_t* cmd_val = nullptr;
  StackRoot _root0(&words);
  StackRoot _root1(&cmd_val);

  cmd_val = this->EvalWordSequence2(words, false);
  return static_cast<cmd_value::Argv*>(cmd_val)->argv;
}

NormalWordEvaluator::NormalWordEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, word_eval::TildeEvaluator* tilde_ev, split::SplitContext* splitter, ui::ErrorFormatter* errfmt) : ::word_eval::AbstractWordEvaluator(mem, exec_opts, mutable_opts, tilde_ev, splitter, errfmt) {
  this->shell_ex = nullptr;
}

void NormalWordEvaluator::CheckCircularDeps() {
}

runtime_asdl::part_value_t* NormalWordEvaluator::_EvalCommandSub(syntax_asdl::CommandSub* cs_part, bool quoted) {
  BigStr* stdout_str = nullptr;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&cs_part);
  StackRoot _root1(&stdout_str);
  StackRoot _root2(&strs);

  stdout_str = this->shell_ex->RunCommandSub(cs_part);
  if (cs_part->left_token->id == Id::Left_AtParen) {
    try {
      strs = j8::SplitJ8Lines(stdout_str);
    }
    catch (error::Decode* e) {
      throw Alloc<error::Structured>(4, e->Message(), cs_part->left_token);
    }
    return Alloc<part_value::Array>(strs);
  }
  else {
    return Alloc<Piece>(stdout_str, quoted, !quoted);
  }
}

runtime_asdl::Piece* NormalWordEvaluator::_EvalProcessSub(syntax_asdl::CommandSub* cs_part) {
  BigStr* dev_path = nullptr;
  StackRoot _root0(&cs_part);
  StackRoot _root1(&dev_path);

  dev_path = this->shell_ex->RunProcessSub(cs_part);
  return Alloc<Piece>(dev_path, true, false);
}
BigStr* _DUMMY = S_FwA;

CompletionWordEvaluator::CompletionWordEvaluator(state::Mem* mem, optview::Exec* exec_opts, state::MutableOpts* mutable_opts, word_eval::TildeEvaluator* tilde_ev, split::SplitContext* splitter, ui::ErrorFormatter* errfmt) : ::word_eval::AbstractWordEvaluator(mem, exec_opts, mutable_opts, tilde_ev, splitter, errfmt) {
}

void CompletionWordEvaluator::CheckCircularDeps() {
}

runtime_asdl::part_value_t* CompletionWordEvaluator::_EvalCommandSub(syntax_asdl::CommandSub* cs_part, bool quoted) {
  StackRoot _root0(&cs_part);

  if (cs_part->left_token->id == Id::Left_AtParen) {
    return Alloc<part_value::Array>(NewList<BigStr*>(std::initializer_list<BigStr*>{_DUMMY}));
  }
  else {
    return Alloc<Piece>(_DUMMY, quoted, !quoted);
  }
}

runtime_asdl::Piece* CompletionWordEvaluator::_EvalProcessSub(syntax_asdl::CommandSub* cs_part) {
  StackRoot _root0(&cs_part);

  return Alloc<Piece>(S_Chb, true, false);
}

}  // define namespace word_eval

namespace word_parse {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using id_kind_asdl::Id_str;
using id_kind_asdl::Kind;
using types_asdl::lex_mode_t;
using types_asdl::lex_mode_e;
using syntax_asdl::BoolParamBox;
using syntax_asdl::Token;
using syntax_asdl::SimpleVarSub;
using syntax_asdl::loc;
using syntax_asdl::source;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::SingleQuoted;
using syntax_asdl::BracedVarSub;
using syntax_asdl::CommandSub;
using syntax_asdl::ShArrayLiteral;
using syntax_asdl::AssocPair;
using syntax_asdl::bracket_op;
using syntax_asdl::bracket_op_t;
using syntax_asdl::suffix_op;
using syntax_asdl::suffix_op_t;
using syntax_asdl::rhs_word;
using syntax_asdl::rhs_word_e;
using syntax_asdl::rhs_word_t;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::CompoundWord;
using syntax_asdl::word_part;
using syntax_asdl::word_part_t;
using syntax_asdl::y_lhs_e;
using syntax_asdl::arith_expr_t;
using syntax_asdl::command;
using syntax_asdl::expr;
using syntax_asdl::expr_e;
using syntax_asdl::expr_t;
using syntax_asdl::pat_t;
using syntax_asdl::ArgList;
using syntax_asdl::Proc;
using syntax_asdl::Func;
using syntax_asdl::Subscript;
using syntax_asdl::Attribute;
using syntax_asdl::arith_expr;
using error::p_die;
GLOBAL_LIST(KINDS_THAT_END_WORDS, id_kind_asdl::Kind_t, 4, {Kind::Eof COMMA Kind::WS COMMA Kind::Op COMMA Kind::Right});

WordEmitter::WordEmitter() {
  ;  // pass
}

syntax_asdl::word_t* WordEmitter::ReadWord(types_asdl::lex_mode_t lex_mode) {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

WordParser::WordParser(parse_lib::ParseContext* parse_ctx, lexer::Lexer* lexer, reader::_Reader* line_reader) {
  this->parse_ctx = parse_ctx;
  this->lexer = lexer;
  this->line_reader = line_reader;
  this->arena = line_reader->arena;
  this->parse_opts = parse_ctx->parse_opts;
  this->a_parser = Alloc<tdop::TdopParser>(arith_parse::Spec(), this, this->parse_opts);
  this->Reset();
}

void WordParser::Init(types_asdl::lex_mode_t lex_mode) {
  this->next_lex_mode = lex_mode;
}

void WordParser::Reset() {
  this->cur_token = nullptr;
  this->token_kind = Kind::Undefined;
  this->token_type = Id::Undefined_Tok;
  this->next_lex_mode = lex_mode_e::ShCommand;
  this->emit_doc_token = false;
  this->multiline = false;
  this->newline_state = 0;
  this->returned_newline = false;
  this->buffered_word = nullptr;
}

void WordParser::_GetToken() {
  bool is_fake;
  types_asdl::lex_mode_t real_mode;
  if (this->next_lex_mode == lex_mode_e::Undefined) {
    return ;
  }
  is_fake = this->next_lex_mode == lex_mode_e::BashRegexFakeInner;
  real_mode = is_fake ? lex_mode_e::BashRegex : this->next_lex_mode;
  this->cur_token = this->lexer->Read(real_mode);
  if ((is_fake and (this->cur_token->id == Id::WS_Space || this->cur_token->id == Id::BashRegex_AllowedInParens))) {
    this->cur_token->id = Id::Lit_Chars;
  }
  this->token_type = this->cur_token->id;
  this->token_kind = consts::GetKind(this->token_type);
  if (this->token_type == Id::Op_Newline) {
    this->newline_state += 1;
  }
  else {
    if (this->token_kind != Kind::WS) {
      this->newline_state = 0;
    }
  }
  this->parse_ctx->trail->AppendToken(this->cur_token);
  this->next_lex_mode = lex_mode_e::Undefined;
}

void WordParser::_SetNext(types_asdl::lex_mode_t lex_mode) {
  this->next_lex_mode = lex_mode;
}

syntax_asdl::rhs_word_t* WordParser::_ReadVarOpArg(types_asdl::lex_mode_t arg_lex_mode) {
  syntax_asdl::CompoundWord* w = nullptr;
  StackRoot _root0(&w);

  this->_SetNext(arg_lex_mode);
  this->_GetToken();
  w = this->_ReadVarOpArg2(arg_lex_mode, Id::Undefined_Tok, true);
  if ((len(w->parts) == 0 and arg_lex_mode == lex_mode_e::VSub_ArgDQ)) {
    return rhs_word::Empty;
  }
  return w;
}

syntax_asdl::CompoundWord* WordParser::_ReadVarOpArg2(types_asdl::lex_mode_t arg_lex_mode, int eof_type, bool empty_ok) {
  syntax_asdl::CompoundWord* w = nullptr;
  syntax_asdl::CompoundWord* tilde = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&tilde);

  w = this->_ReadCompoundWord3(arg_lex_mode, eof_type, empty_ok);
  tilde = word_::TildeDetect(w);
  if (tilde) {
    w = tilde;
  }
  return w;
}

suffix_op::Slice* WordParser::_ReadSliceVarOp() {
  int cur_id;
  syntax_asdl::arith_expr_t* begin = nullptr;
  syntax_asdl::arith_expr_t* no_length = nullptr;
  syntax_asdl::Token* colon_tok = nullptr;
  syntax_asdl::arith_expr_t* length = nullptr;
  StackRoot _root0(&begin);
  StackRoot _root1(&no_length);
  StackRoot _root2(&colon_tok);
  StackRoot _root3(&length);

  this->_NextNonSpace();
  cur_id = this->token_type;
  if ((cur_id == Id::Arith_RBrace || cur_id == Id::Arith_Colon)) {
    begin = arith_expr::EmptyZero;
  }
  else {
    begin = this->a_parser->Parse();
    cur_id = this->a_parser->CurrentId();
  }
  if (cur_id == Id::Arith_RBrace) {
    no_length = nullptr;
    return Alloc<suffix_op::Slice>(begin, no_length);
  }
  else {
    if (cur_id == Id::Arith_Colon) {
      colon_tok = this->cur_token;
      this->_NextNonSpace();
      if (this->token_type == Id::Arith_RBrace) {
        if (this->parse_opts->strict_parse_slice()) {
          p_die(S_npc, colon_tok);
        }
        length = arith_expr::EmptyZero;
      }
      else {
        length = this->_ReadArithExpr(Id::Arith_RBrace);
      }
      return Alloc<suffix_op::Slice>(begin, length);
    }
    else {
      p_die(S_jod, this->cur_token);
    }
  }
  assert(0);  // AssertionError
}

suffix_op::PatSub* WordParser::_ReadPatSubVarOp() {
  syntax_asdl::Token* slash_tok = nullptr;
  int replace_mode;
  syntax_asdl::CompoundWord* pat = nullptr;
  bool empty_ok;
  syntax_asdl::rhs_word_t* replace = nullptr;
  StackRoot _root0(&slash_tok);
  StackRoot _root1(&pat);
  StackRoot _root2(&replace);

  slash_tok = this->cur_token;
  replace_mode = Id::Undefined_Tok;
  this->_SetNext(lex_mode_e::VSub_ArgUnquoted);
  this->_GetToken();
  if (this->token_type == Id::Right_DollarBrace) {
    pat = Alloc<CompoundWord>(Alloc<List<syntax_asdl::word_part_t*>>());
    return Alloc<suffix_op::PatSub>(pat, rhs_word::Empty, replace_mode, slash_tok);
  }
  if ((this->token_type == Id::Lit_Slash || this->token_type == Id::Lit_Pound || this->token_type == Id::Lit_Percent)) {
    replace_mode = this->token_type;
    this->_SetNext(lex_mode_e::VSub_ArgUnquoted);
  }
  empty_ok = replace_mode != Id::Lit_Slash;
  pat = this->_ReadVarOpArg2(lex_mode_e::VSub_ArgUnquoted, Id::Lit_Slash, empty_ok);
  if (this->token_type == Id::Lit_Slash) {
    replace = this->_ReadVarOpArg(lex_mode_e::VSub_ArgUnquoted);
  }
  else {
    replace = rhs_word::Empty;
  }
  this->_GetToken();
  if (this->token_type != Id::Right_DollarBrace) {
    p_die(StrFormat("Expected } after replacement string, got %s", ui::PrettyId(this->token_type)), this->cur_token);
  }
  return Alloc<suffix_op::PatSub>(pat, replace, replace_mode, slash_tok);
}

syntax_asdl::bracket_op_t* WordParser::_ReadSubscript() {
  int next_id;
  syntax_asdl::bracket_op_t* op = nullptr;
  syntax_asdl::arith_expr_t* anode = nullptr;
  StackRoot _root0(&op);
  StackRoot _root1(&anode);

  next_id = this->lexer->LookPastSpace(lex_mode_e::Arith);
  if ((next_id == Id::Lit_At || next_id == Id::Arith_Star)) {
    op = Alloc<bracket_op::WholeArray>(next_id);
    this->_SetNext(lex_mode_e::Arith);
    this->_GetToken();
    this->_SetNext(lex_mode_e::Arith);
    this->_GetToken();
  }
  else {
    this->_SetNext(lex_mode_e::Arith);
    anode = this->_ReadArithExpr(Id::Arith_RBracket);
    op = Alloc<bracket_op::ArrayIndex>(anode);
  }
  if (this->token_type != Id::Arith_RBracket) {
    p_die(S_sxv, this->cur_token);
  }
  this->_SetNext(lex_mode_e::VSub_2);
  this->_GetToken();
  return op;
}

syntax_asdl::BracedVarSub* WordParser::_ParseVarOf() {
  syntax_asdl::Token* name_token = nullptr;
  syntax_asdl::bracket_op_t* bracket_op = nullptr;
  syntax_asdl::BracedVarSub* part = nullptr;
  StackRoot _root0(&name_token);
  StackRoot _root1(&bracket_op);
  StackRoot _root2(&part);

  this->_GetToken();
  name_token = this->cur_token;
  this->_SetNext(lex_mode_e::VSub_2);
  this->_GetToken();
  if (this->token_type == Id::VOp2_LBracket) {
    bracket_op = this->_ReadSubscript();
  }
  else {
    bracket_op = nullptr;
  }
  part = BracedVarSub::CreateNull();
  part->name_tok = name_token;
  part->var_name = lexer::TokenVal(name_token);
  part->bracket_op = bracket_op;
  return part;
}

syntax_asdl::BracedVarSub* WordParser::_ParseVarExpr(types_asdl::lex_mode_t arg_lex_mode, bool allow_query) {
  syntax_asdl::BracedVarSub* part = nullptr;
  id_kind_asdl::Kind_t op_kind;
  syntax_asdl::Token* tok = nullptr;
  syntax_asdl::rhs_word_t* arg_word = nullptr;
  syntax_asdl::rhs_word_t* UP_arg_word = nullptr;
  bool ok;
  BigStr* arg = nullptr;
  bool quoted;
  syntax_asdl::suffix_op_t* patsub_op = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&tok);
  StackRoot _root2(&arg_word);
  StackRoot _root3(&UP_arg_word);
  StackRoot _root4(&arg);
  StackRoot _root5(&patsub_op);

  part = this->_ParseVarOf();
  this->_GetToken();
  if (this->token_type == Id::Right_DollarBrace) {
    return part;
  }
  op_kind = this->token_kind;
  if (op_kind == Kind::VTest) {
    tok = this->cur_token;
    arg_word = this->_ReadVarOpArg(arg_lex_mode);
    if (this->token_type != Id::Right_DollarBrace) {
      p_die(S_fpg, this->cur_token);
    }
    part->suffix_op = Alloc<suffix_op::Unary>(tok, arg_word);
  }
  else {
    if (op_kind == Kind::VOpYsh) {
      tok = this->cur_token;
      arg_word = this->_ReadVarOpArg(arg_lex_mode);
      if (this->token_type != Id::Right_DollarBrace) {
        p_die(S_fpg, this->cur_token);
      }
      UP_arg_word = arg_word;
      switch (arg_word->tag()) {
        case rhs_word_e::Empty: {
          ;  // pass
        }
          break;
        case rhs_word_e::Compound: {
          CompoundWord* arg_word = static_cast<CompoundWord*>(UP_arg_word);
          Tuple3<bool, BigStr*, bool> tup0 = word_::StaticEval(arg_word);
          ok = tup0.at0();
          arg = tup0.at1();
          quoted = tup0.at2();
          if ((!ok or quoted)) {
            p_die(S_Fwi, Alloc<loc::Word>(arg_word));
          }
        }
          break;
      }
      part->suffix_op = Alloc<suffix_op::Static>(tok, arg);
    }
    else {
      if (op_kind == Kind::VOp0) {
        part->suffix_op = this->cur_token;
        this->_SetNext(lex_mode_e::VSub_2);
        this->_GetToken();
      }
      else {
        if (op_kind == Kind::VOp1) {
          tok = this->cur_token;
          arg_word = this->_ReadVarOpArg(lex_mode_e::VSub_ArgUnquoted);
          if (this->token_type != Id::Right_DollarBrace) {
            p_die(S_fpg, this->cur_token);
          }
          part->suffix_op = Alloc<suffix_op::Unary>(tok, arg_word);
        }
        else {
          if (op_kind == Kind::VOp2) {
            if (this->token_type == Id::VOp2_Slash) {
              patsub_op = this->_ReadPatSubVarOp();
              part->suffix_op = patsub_op;
            }
            else {
              if (this->token_type == Id::VOp2_Colon) {
                part->suffix_op = this->_ReadSliceVarOp();
                if (this->token_type != Id::Arith_RBrace) {
                  p_die(S_fpg, this->cur_token);
                }
              }
              else {
                p_die(StrFormat("Unexpected token in ${} (%s)", S_Duk), this->cur_token);
              }
            }
          }
          else {
            if (op_kind == Kind::VOp3) {
              if (allow_query) {
                part->suffix_op = this->cur_token;
                this->_SetNext(lex_mode_e::VSub_2);
                this->_GetToken();
              }
              else {
                p_die(StrFormat("Unexpected token in ${} (%s)", S_hgF), this->cur_token);
              }
            }
          }
        }
      }
    }
  }
  if ((this->token_type != Id::Right_DollarBrace && this->token_type != Id::Arith_RBrace)) {
    p_die(S_fpg, this->cur_token);
  }
  return part;
}

word_part::ZshVarSub* WordParser::_ReadZshVarSub(syntax_asdl::Token* left_token) {
  syntax_asdl::CompoundWord* w = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&w);

  this->_SetNext(lex_mode_e::VSub_Zsh);
  w = this->_ReadCompoundWord3(lex_mode_e::VSub_Zsh, Id::Right_DollarBrace, true);
  this->_GetToken();
  return Alloc<word_part::ZshVarSub>(left_token, w, this->cur_token);
}

Tuple2<syntax_asdl::BracedVarSub*, syntax_asdl::Token*> WordParser::ReadBracedVarSub(syntax_asdl::Token* left_token) {
  syntax_asdl::BracedVarSub* part = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&part);
  StackRoot _root2(&last_token);

  part = this->_ReadBracedVarSub(left_token, false);
  last_token = this->cur_token;
  return Tuple2<syntax_asdl::BracedVarSub*, syntax_asdl::Token*>(part, last_token);
}

syntax_asdl::BracedVarSub* WordParser::_ReadBracedVarSub(syntax_asdl::Token* left_token, bool d_quoted) {
  types_asdl::lex_mode_t arg_lex_mode;
  int ty;
  syntax_asdl::Token* first_tok = nullptr;
  int next_id;
  syntax_asdl::BracedVarSub* part = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&first_tok);
  StackRoot _root2(&part);

  if (d_quoted) {
    arg_lex_mode = lex_mode_e::VSub_ArgDQ;
  }
  else {
    arg_lex_mode = lex_mode_e::VSub_ArgUnquoted;
  }
  this->_SetNext(lex_mode_e::VSub_1);
  this->_GetToken();
  ty = this->token_type;
  first_tok = this->cur_token;
  if (ty == Id::VSub_Pound) {
    next_id = this->lexer->LookPastSpace(lex_mode_e::VSub_1);
    if ((next_id != Id::Unknown_Tok && next_id != Id::Right_DollarBrace)) {
      this->_SetNext(lex_mode_e::VSub_1);
      part = this->_ParseVarOf();
      this->_GetToken();
      if (this->token_type != Id::Right_DollarBrace) {
        p_die(S_mpl, this->cur_token);
      }
      part->prefix_op = first_tok;
    }
    else {
      part = this->_ParseVarExpr(arg_lex_mode);
    }
  }
  else {
    if (ty == Id::VSub_Bang) {
      next_id = this->lexer->LookPastSpace(lex_mode_e::VSub_1);
      if ((next_id != Id::Unknown_Tok && next_id != Id::Right_DollarBrace)) {
        this->_SetNext(lex_mode_e::VSub_1);
        part = this->_ParseVarExpr(arg_lex_mode, true);
        part->prefix_op = first_tok;
      }
      else {
        part = this->_ParseVarExpr(arg_lex_mode);
      }
    }
    else {
      if (ty == Id::VSub_Dot) {
        p_die(S_hzv, this->cur_token);
      }
      else {
        if (this->token_kind == Kind::VSub) {
          part = this->_ParseVarExpr(arg_lex_mode);
        }
        else {
          p_die(S_tBm, this->cur_token);
        }
      }
    }
  }
  part->left = left_token;
  part->right = this->cur_token;
  return part;
}

syntax_asdl::SingleQuoted* WordParser::_ReadSingleQuoted(syntax_asdl::Token* left_token, types_asdl::lex_mode_t lex_mode) {
  List<syntax_asdl::Token*>* tokens = nullptr;
  syntax_asdl::Token* right_quote = nullptr;
  BigStr* sval = nullptr;
  syntax_asdl::SingleQuoted* node = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&tokens);
  StackRoot _root2(&right_quote);
  StackRoot _root3(&sval);
  StackRoot _root4(&node);

  tokens = Alloc<List<syntax_asdl::Token*>>();
  right_quote = this->ReadSingleQuoted(lex_mode, left_token, tokens, false);
  sval = word_compile::EvalSingleQuoted(left_token->id, tokens);
  node = Alloc<SingleQuoted>(left_token, sval, right_quote);
  return node;
}

syntax_asdl::Token* WordParser::ReadSingleQuoted(types_asdl::lex_mode_t lex_mode, syntax_asdl::Token* left_token, List<syntax_asdl::Token*>* out_tokens, bool is_ysh_expr) {
  List<syntax_asdl::Token*>* tokens = nullptr;
  bool no_backslashes;
  int expected_end_tokens;
  int num_end_tokens;
  syntax_asdl::Token* tok = nullptr;
  bool is_u_string;
  StackRoot _root0(&left_token);
  StackRoot _root1(&out_tokens);
  StackRoot _root2(&tokens);
  StackRoot _root3(&tok);

  tokens = Alloc<List<syntax_asdl::Token*>>();
  no_backslashes = (is_ysh_expr and left_token->id == Id::Left_SingleQuote);
  expected_end_tokens = (left_token->id == Id::Left_TSingleQuote || left_token->id == Id::Left_RTSingleQuote || left_token->id == Id::Left_UTSingleQuote || left_token->id == Id::Left_BTSingleQuote) ? 3 : 1;
  num_end_tokens = 0;
  while (num_end_tokens < expected_end_tokens) {
    this->_SetNext(lex_mode);
    this->_GetToken();
    if ((this->token_kind == Kind::Lit || this->token_kind == Kind::Char)) {
      tok = this->cur_token;
      if ((no_backslashes and lexer::TokenContains(tok, S_iyu))) {
        p_die(S_mrm, tok);
      }
      if (is_ysh_expr) {
        if (this->token_type == Id::Char_Octal3) {
          p_die(S_jmF, tok);
        }
        if ((this->token_type == Id::Char_Hex and this->cur_token->length != 4)) {
          p_die(S_dDj, tok);
        }
      }
      tokens->append(tok);
    }
    else {
      if (this->token_kind == Kind::Unknown) {
        tok = this->cur_token;
        if ((is_ysh_expr or !this->parse_opts->parse_backslash())) {
          p_die(S_vla, tok);
        }
        tokens->append(tok);
      }
      else {
        if (this->token_kind == Kind::Eof) {
          p_die(S_acC, left_token);
        }
        else {
          if (this->token_kind == Kind::Right) {
            num_end_tokens += 1;
            tokens->append(this->cur_token);
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
    }
    if (this->token_kind != Kind::Right) {
      num_end_tokens = 0;
    }
  }
  if (expected_end_tokens == 1) {
    tokens->pop();
  }
  else {
    if (expected_end_tokens == 3) {
      tokens->pop();
      tokens->pop();
      tokens->pop();
    }
  }
  if ((left_token->id == Id::Left_TSingleQuote || left_token->id == Id::Left_RTSingleQuote || left_token->id == Id::Left_UTSingleQuote || left_token->id == Id::Left_BTSingleQuote)) {
    word_compile::RemoveLeadingSpaceSQ(tokens);
  }
  is_u_string = (left_token->id == Id::Left_USingleQuote || left_token->id == Id::Left_UTSingleQuote);
  for (ListIter<syntax_asdl::Token*> it(tokens); !it.Done(); it.Next()) {
    syntax_asdl::Token* tok = it.Value();
    StackRoot _for(&tok  );
    if ((is_u_string and tok->id == Id::Char_YHex)) {
      p_die(StrFormat("%s escapes not allowed in u'' strings", lexer::TokenVal(tok)), tok);
    }
  }
  out_tokens->extend(tokens);
  return this->cur_token;
}

syntax_asdl::word_part_t* WordParser::_ReadDoubleQuotedLeftParts() {
  if ((this->token_type == Id::Left_DollarParen || this->token_type == Id::Left_Backtick)) {
    return this->_ReadCommandSub(this->token_type, true);
  }
  if (this->token_type == Id::Left_DollarBrace) {
    return this->_ReadBracedVarSub(this->cur_token, true);
  }
  if (this->token_type == Id::Left_DollarDParen) {
    return this->_ReadArithSub();
  }
  if (this->token_type == Id::Left_DollarBracket) {
    return this->_ReadExprSub(lex_mode_e::DQ);
  }
  assert(0);  // AssertionError
}

syntax_asdl::CompoundWord* WordParser::_ReadYshSingleQuoted(int left_id) {
  types_asdl::lex_mode_t lexer_mode;
  int triple_left_id;
  syntax_asdl::Token* left_tok = nullptr;
  syntax_asdl::SingleQuoted* sq_part = nullptr;
  StackRoot _root0(&left_tok);
  StackRoot _root1(&sq_part);

  if (left_id == Id::Left_RSingleQuote) {
    lexer_mode = lex_mode_e::SQ_Raw;
    triple_left_id = Id::Left_RTSingleQuote;
  }
  else {
    if (left_id == Id::Left_USingleQuote) {
      lexer_mode = lex_mode_e::J8_Str;
      triple_left_id = Id::Left_UTSingleQuote;
    }
    else {
      if (left_id == Id::Left_BSingleQuote) {
        lexer_mode = lex_mode_e::J8_Str;
        triple_left_id = Id::Left_BTSingleQuote;
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  left_tok = this->cur_token;
  left_tok->id = left_id;
  sq_part = this->_ReadSingleQuoted(left_tok, lexer_mode);
  if ((len(sq_part->sval) == 0 and str_equals(this->lexer->ByteLookAhead(), S_Bfw))) {
    this->_SetNext(lex_mode_e::ShCommand);
    this->_GetToken();
    left_tok = this->cur_token;
    left_tok->id = triple_left_id;
    sq_part = this->_ReadSingleQuoted(left_tok, lexer_mode);
  }
  this->_SetNext(lex_mode_e::ShCommand);
  this->_GetToken();
  if (!list_contains(KINDS_THAT_END_WORDS, this->token_kind)) {
    p_die(S_wxA, this->cur_token);
  }
  return Alloc<CompoundWord>(NewList<syntax_asdl::word_part_t*>(std::initializer_list<syntax_asdl::word_part_t*>{sq_part}));
}

syntax_asdl::word_part_t* WordParser::_ReadUnquotedLeftParts(syntax_asdl::BoolParamBox* triple_out) {
  syntax_asdl::DoubleQuoted* dq_part = nullptr;
  syntax_asdl::Token* left_dq_token = nullptr;
  types_asdl::lex_mode_t lexer_mode;
  int triple_left_id;
  syntax_asdl::SingleQuoted* sq_part = nullptr;
  syntax_asdl::Token* left_sq_token = nullptr;
  StackRoot _root0(&triple_out);
  StackRoot _root1(&dq_part);
  StackRoot _root2(&left_dq_token);
  StackRoot _root3(&sq_part);
  StackRoot _root4(&left_sq_token);

  if ((this->token_type == Id::Left_DoubleQuote || this->token_type == Id::Left_DollarDoubleQuote)) {
    dq_part = this->_ReadDoubleQuoted(this->cur_token);
    if ((triple_out and (len(dq_part->parts) == 0 and str_equals(this->lexer->ByteLookAhead(), S_krt)))) {
      this->_SetNext(lex_mode_e::ShCommand);
      this->_GetToken();
      left_dq_token = this->cur_token;
      left_dq_token->id = Id::Left_TDoubleQuote;
      triple_out->b = true;
      return this->_ReadDoubleQuoted(left_dq_token);
    }
    return dq_part;
  }
  if ((this->token_type == Id::Left_SingleQuote || this->token_type == Id::Left_RSingleQuote || this->token_type == Id::Left_DollarSingleQuote)) {
    if (this->token_type == Id::Left_SingleQuote) {
      lexer_mode = lex_mode_e::SQ_Raw;
      triple_left_id = Id::Left_TSingleQuote;
    }
    else {
      if (this->token_type == Id::Left_RSingleQuote) {
        lexer_mode = lex_mode_e::SQ_Raw;
        triple_left_id = Id::Left_RTSingleQuote;
      }
      else {
        lexer_mode = lex_mode_e::SQ_C;
        triple_left_id = Id::Undefined_Tok;
      }
    }
    sq_part = this->_ReadSingleQuoted(this->cur_token, lexer_mode);
    if ((triple_left_id != Id::Undefined_Tok and (triple_out != nullptr and (len(sq_part->sval) == 0 and str_equals(this->lexer->ByteLookAhead(), S_Bfw))))) {
      this->_SetNext(lex_mode_e::ShCommand);
      this->_GetToken();
      left_sq_token = this->cur_token;
      left_sq_token->id = triple_left_id;
      triple_out->b = true;
      return this->_ReadSingleQuoted(left_sq_token, lexer_mode);
    }
    return sq_part;
  }
  if ((this->token_type == Id::Left_DollarParen || this->token_type == Id::Left_Backtick || this->token_type == Id::Left_ProcSubIn || this->token_type == Id::Left_ProcSubOut)) {
    return this->_ReadCommandSub(this->token_type, false);
  }
  if (this->token_type == Id::Left_DollarBrace) {
    return this->_ReadBracedVarSub(this->cur_token, false);
  }
  if (this->token_type == Id::Left_DollarDParen) {
    return this->_ReadArithSub();
  }
  if (this->token_type == Id::Left_DollarBracket) {
    return this->_ReadExprSub(lex_mode_e::ShCommand);
  }
  if (this->token_type == Id::Left_DollarBraceZsh) {
    return this->_ReadZshVarSub(this->cur_token);
  }
  assert(0);  // AssertionError
}

word_part::ExtGlob* WordParser::_ReadExtGlob() {
  syntax_asdl::Token* left_token = nullptr;
  syntax_asdl::Token* right_token = nullptr;
  List<syntax_asdl::CompoundWord*>* arms = nullptr;
  bool read_word;
  syntax_asdl::CompoundWord* w = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&right_token);
  StackRoot _root2(&arms);
  StackRoot _root3(&w);

  left_token = this->cur_token;
  right_token = nullptr;
  arms = Alloc<List<syntax_asdl::CompoundWord*>>();
  this->lexer->PushHint(Id::Op_RParen, Id::Right_ExtGlob);
  this->_SetNext(lex_mode_e::ExtGlob);
  read_word = false;
  while (true) {
    this->_GetToken();
    if (this->token_type == Id::Right_ExtGlob) {
      if (!read_word) {
        arms->append(Alloc<CompoundWord>(Alloc<List<syntax_asdl::word_part_t*>>()));
      }
      right_token = this->cur_token;
      break;
    }
    else {
      if (this->token_type == Id::Op_Pipe) {
        if (!read_word) {
          arms->append(Alloc<CompoundWord>(Alloc<List<syntax_asdl::word_part_t*>>()));
        }
        read_word = false;
        this->_SetNext(lex_mode_e::ExtGlob);
      }
      else {
        if ((this->token_kind == Kind::Lit || this->token_kind == Kind::Left || this->token_kind == Kind::VSub || this->token_kind == Kind::ExtGlob)) {
          w = this->_ReadCompoundWord(lex_mode_e::ExtGlob);
          arms->append(w);
          read_word = true;
        }
        else {
          if (this->token_kind == Kind::Eof) {
            p_die(S_ilx, left_token);
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
    }
  }
  return Alloc<word_part::ExtGlob>(left_token, arms, right_token);
}

word_part::BashRegexGroup* WordParser::_ReadBashRegexGroup() {
  syntax_asdl::Token* left_token = nullptr;
  List<syntax_asdl::CompoundWord*>* arms = nullptr;
  syntax_asdl::CompoundWord* w = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&arms);
  StackRoot _root2(&w);

  left_token = this->cur_token;
  arms = Alloc<List<syntax_asdl::CompoundWord*>>();
  this->lexer->PushHint(Id::Op_RParen, Id::Right_BashRegexGroup);
  this->_SetNext(lex_mode_e::BashRegexFakeInner);
  this->_GetToken();
  if (this->token_type == Id::Right_BashRegexGroup) {
    return Alloc<word_part::BashRegexGroup>(left_token, nullptr, this->cur_token);
  }
  if ((this->token_kind == Kind::Lit || this->token_kind == Kind::Left || this->token_kind == Kind::VSub || this->token_kind == Kind::BashRegex)) {
    w = this->_ReadCompoundWord(lex_mode_e::BashRegexFakeInner);
    arms->append(w);
    this->_GetToken();
    if (this->token_type != Id::Right_BashRegexGroup) {
      p_die(S_paD, this->cur_token);
    }
    return Alloc<word_part::BashRegexGroup>(left_token, w, this->cur_token);
  }
  p_die(S_see, this->cur_token);
}

void WordParser::_ReadLikeDQ(syntax_asdl::Token* left_token, bool is_ysh_expr, List<syntax_asdl::word_part_t*>* out_parts) {
  int expected_end_tokens;
  int num_end_tokens;
  syntax_asdl::Token* tok = nullptr;
  BigStr* ch = nullptr;
  syntax_asdl::word_part_t* part = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&out_parts);
  StackRoot _root2(&tok);
  StackRoot _root3(&ch);
  StackRoot _root4(&part);

  if (left_token) {
    if ((left_token->id == Id::Left_TDoubleQuote || left_token->id == Id::Left_DollarTDoubleQuote)) {
      expected_end_tokens = 3;
    }
    else {
      expected_end_tokens = 1;
    }
  }
  else {
    expected_end_tokens = 1000;
  }
  num_end_tokens = 0;
  while (num_end_tokens < expected_end_tokens) {
    this->_SetNext(lex_mode_e::DQ);
    this->_GetToken();
    if (this->token_kind == Kind::Lit) {
      if (this->token_type == Id::Lit_EscapedChar) {
        tok = this->cur_token;
        ch = lexer::TokenSliceLeft(tok, 1);
        part = Alloc<word_part::EscapedLiteral>(tok, ch);
      }
      else {
        if (this->token_type == Id::Lit_BadBackslash) {
          if ((is_ysh_expr or !this->parse_opts->parse_backslash())) {
            p_die(S_Bpn, this->cur_token);
          }
        }
        else {
          if (this->token_type == Id::Lit_Dollar) {
            if ((is_ysh_expr or !this->parse_opts->parse_dollar())) {
              p_die(S_oex, this->cur_token);
            }
          }
        }
        part = this->cur_token;
      }
      out_parts->append(part);
    }
    else {
      if (this->token_kind == Kind::Left) {
        if ((this->token_type == Id::Left_Backtick and is_ysh_expr)) {
          p_die(S_bio, this->cur_token);
        }
        part = this->_ReadDoubleQuotedLeftParts();
        out_parts->append(part);
      }
      else {
        if (this->token_kind == Kind::VSub) {
          tok = this->cur_token;
          part = Alloc<SimpleVarSub>(tok);
          out_parts->append(part);
        }
        else {
          if (this->token_kind == Kind::Right) {
            if (left_token) {
              num_end_tokens += 1;
            }
            out_parts->append(this->cur_token);
          }
          else {
            if (this->token_kind == Kind::Eof) {
              if (left_token) {
                p_die(S_fip, left_token);
              }
              else {
                break;
              }
            }
            else {
              assert(0);  // AssertionError
            }
          }
        }
      }
    }
    if (this->token_kind != Kind::Right) {
      num_end_tokens = 0;
    }
  }
  if (expected_end_tokens == 1) {
    out_parts->pop();
  }
  else {
    if (expected_end_tokens == 3) {
      out_parts->pop();
      out_parts->pop();
      out_parts->pop();
    }
  }
  if ((left_token and (left_token->id == Id::Left_TDoubleQuote || left_token->id == Id::Left_DollarTDoubleQuote))) {
    word_compile::RemoveLeadingSpaceDQ(out_parts);
  }
}

syntax_asdl::DoubleQuoted* WordParser::_ReadDoubleQuoted(syntax_asdl::Token* left_token) {
  List<syntax_asdl::word_part_t*>* parts = nullptr;
  syntax_asdl::Token* right_quote = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&parts);
  StackRoot _root2(&right_quote);

  parts = Alloc<List<syntax_asdl::word_part_t*>>();
  this->_ReadLikeDQ(left_token, false, parts);
  right_quote = this->cur_token;
  return Alloc<DoubleQuoted>(left_token, parts, right_quote);
}

syntax_asdl::Token* WordParser::ReadDoubleQuoted(syntax_asdl::Token* left_token, List<syntax_asdl::word_part_t*>* parts) {
  StackRoot _root0(&left_token);
  StackRoot _root1(&parts);

  this->_ReadLikeDQ(left_token, true, parts);
  return this->cur_token;
}

syntax_asdl::CommandSub* WordParser::_ReadCommandSub(int left_id, bool d_quoted) {
  syntax_asdl::Token* left_token = nullptr;
  int right_id;
  cmd_parse::CommandParser* c_parser = nullptr;
  syntax_asdl::command_t* node = nullptr;
  syntax_asdl::Token* right_token = nullptr;
  List<BigStr*>* parts = nullptr;
  BigStr* code_str = nullptr;
  alloc::Arena* arena = nullptr;
  reader::FileLineReader* line_reader = nullptr;
  source::Reparsed* src = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&c_parser);
  StackRoot _root2(&node);
  StackRoot _root3(&right_token);
  StackRoot _root4(&parts);
  StackRoot _root5(&code_str);
  StackRoot _root6(&arena);
  StackRoot _root7(&line_reader);
  StackRoot _root8(&src);

  left_token = this->cur_token;
  if ((left_id == Id::Left_DollarParen || left_id == Id::Left_AtParen || left_id == Id::Left_ProcSubIn || left_id == Id::Left_ProcSubOut)) {
    this->_SetNext(lex_mode_e::ShCommand);
    right_id = Id::Eof_RParen;
    this->lexer->PushHint(Id::Op_RParen, right_id);
    c_parser = this->parse_ctx->MakeParserForCommandSub(this->line_reader, this->lexer, right_id);
    node = c_parser->ParseCommandSub();
    right_token = c_parser->w_parser->cur_token;
  }
  else {
    if ((left_id == Id::Left_Backtick and this->parse_ctx->do_lossless)) {
      right_id = Id::Eof_Backtick;
      this->lexer->PushHint(Id::Left_Backtick, right_id);
      c_parser = this->parse_ctx->MakeParserForCommandSub(this->line_reader, this->lexer, right_id);
      node = c_parser->ParseCommandSub();
      right_token = c_parser->w_parser->cur_token;
    }
    else {
      if (left_id == Id::Left_Backtick) {
        if (!this->parse_opts->parse_backticks()) {
          p_die(S_Aeo, left_token);
        }
        this->_SetNext(lex_mode_e::Backtick);
        parts = Alloc<List<BigStr*>>();
        while (true) {
          this->_GetToken();
          if (this->token_type == Id::Backtick_Quoted) {
            parts->append(lexer::TokenSliceLeft(this->cur_token, 1));
          }
          else {
            if (this->token_type == Id::Backtick_DoubleQuote) {
              if (d_quoted) {
                parts->append(lexer::TokenSliceLeft(this->cur_token, 1));
              }
              else {
                parts->append(lexer::TokenVal(this->cur_token));
              }
            }
            else {
              if (this->token_type == Id::Backtick_Other) {
                parts->append(lexer::TokenVal(this->cur_token));
              }
              else {
                if (this->token_type == Id::Backtick_Right) {
                  break;
                }
                else {
                  if (this->token_type == Id::Eof_Real) {
                    p_die(S_edt, left_token);
                  }
                  else {
                    assert(0);  // AssertionError
                  }
                }
              }
            }
          }
          this->_SetNext(lex_mode_e::Backtick);
        }
        right_token = this->cur_token;
        code_str = S_Aoo->join(parts);
        arena = this->parse_ctx->arena;
        line_reader = reader::StringLineReader(code_str, arena);
        c_parser = this->parse_ctx->MakeOshParser(line_reader);
        src = Alloc<source::Reparsed>(S_vlc, left_token, right_token);
        {  // with
          alloc::ctx_SourceCode ctx{arena, src};

          node = c_parser->ParseCommandSub();
        }
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  return Alloc<CommandSub>(left_token, node, right_token);
}

word_part::ExprSub* WordParser::_ReadExprSub(types_asdl::lex_mode_t lex_mode) {
  syntax_asdl::Token* left_token = nullptr;
  syntax_asdl::expr_t* enode = nullptr;
  syntax_asdl::Token* right_token = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&enode);
  StackRoot _root2(&right_token);

  left_token = this->cur_token;
  this->_SetNext(lex_mode_e::Expr);
  Tuple2<syntax_asdl::expr_t*, syntax_asdl::Token*> tup1 = this->parse_ctx->ParseYshExpr(this->lexer, grammar_nt::ysh_expr_sub);
  enode = tup1.at0();
  right_token = tup1.at1();
  this->_SetNext(lex_mode);
  return Alloc<word_part::ExprSub>(left_token, enode, right_token);
}

command::VarDecl* WordParser::ParseVarDecl(syntax_asdl::Token* kw_token) {
  command::VarDecl* enode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&kw_token);
  StackRoot _root1(&enode);
  StackRoot _root2(&last_token);

  this->_SetNext(lex_mode_e::Expr);
  Tuple2<command::VarDecl*, syntax_asdl::Token*> tup2 = this->parse_ctx->ParseVarDecl(kw_token, this->lexer);
  enode = tup2.at0();
  last_token = tup2.at1();
  if (last_token->id == Id::Op_RBrace) {
    last_token->id = Id::Lit_RBrace;
  }
  this->buffered_word = last_token;
  this->_SetNext(lex_mode_e::ShCommand);
  return enode;
}

command::Mutation* WordParser::ParseMutation(syntax_asdl::Token* kw_token, cmd_parse::VarChecker* var_checker) {
  command::Mutation* enode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  syntax_asdl::y_lhs_t* UP_lhs = nullptr;
  expr::Var* v = nullptr;
  StackRoot _root0(&kw_token);
  StackRoot _root1(&var_checker);
  StackRoot _root2(&enode);
  StackRoot _root3(&last_token);
  StackRoot _root4(&UP_lhs);
  StackRoot _root5(&v);

  this->_SetNext(lex_mode_e::Expr);
  Tuple2<command::Mutation*, syntax_asdl::Token*> tup3 = this->parse_ctx->ParseMutation(kw_token, this->lexer);
  enode = tup3.at0();
  last_token = tup3.at1();
  if (last_token->id == Id::Op_RBrace) {
    last_token->id = Id::Lit_RBrace;
  }
  for (ListIter<syntax_asdl::y_lhs_t*> it(enode->lhs); !it.Done(); it.Next()) {
    syntax_asdl::y_lhs_t* lhs = it.Value();
    StackRoot _for(&lhs  );
    UP_lhs = lhs;
    switch (lhs->tag()) {
      case y_lhs_e::Var: {
        Token* lhs = static_cast<Token*>(UP_lhs);
        var_checker->Check(kw_token->id, lexer::LazyStr(lhs), lhs);
      }
        break;
      case y_lhs_e::Subscript: {
        Subscript* lhs = static_cast<Subscript*>(UP_lhs);
        if (lhs->obj->tag() == expr_e::Var) {
          v = static_cast<expr::Var*>(lhs->obj);
          var_checker->Check(kw_token->id, v->name, v->left);
        }
      }
        break;
      case y_lhs_e::Attribute: {
        Attribute* lhs = static_cast<Attribute*>(UP_lhs);
        if (lhs->obj->tag() == expr_e::Var) {
          v = static_cast<expr::Var*>(lhs->obj);
          var_checker->Check(kw_token->id, v->name, v->left);
        }
      }
        break;
    }
  }
  this->buffered_word = last_token;
  this->_SetNext(lex_mode_e::ShCommand);
  return enode;
}

syntax_asdl::expr_t* WordParser::ParseBareDecl() {
  syntax_asdl::expr_t* enode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&enode);
  StackRoot _root1(&last_token);

  this->_SetNext(lex_mode_e::Expr);
  this->_GetToken();
  Tuple2<syntax_asdl::expr_t*, syntax_asdl::Token*> tup4 = this->parse_ctx->ParseYshExpr(this->lexer, grammar_nt::command_expr);
  enode = tup4.at0();
  last_token = tup4.at1();
  if (last_token->id == Id::Op_RBrace) {
    last_token->id = Id::Lit_RBrace;
  }
  this->buffered_word = last_token;
  this->_SetNext(lex_mode_e::ShCommand);
  return enode;
}

syntax_asdl::expr_t* WordParser::ParseYshExprForCommand() {
  syntax_asdl::expr_t* enode = nullptr;
  StackRoot _root0(&enode);

  if (this->token_type == Id::Op_LParen) {
    this->lexer->MaybeUnreadOne();
  }
  Tuple2<syntax_asdl::expr_t*, syntax_asdl::Token*> tup5 = this->parse_ctx->ParseYshExpr(this->lexer, grammar_nt::ysh_expr);
  enode = tup5.at0();
  this->_SetNext(lex_mode_e::ShCommand);
  return enode;
}

syntax_asdl::expr_t* WordParser::ParseCommandExpr() {
  syntax_asdl::expr_t* enode = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&enode);
  StackRoot _root1(&last_token);

  Tuple2<syntax_asdl::expr_t*, syntax_asdl::Token*> tup6 = this->parse_ctx->ParseYshExpr(this->lexer, grammar_nt::command_expr);
  enode = tup6.at0();
  last_token = tup6.at1();
  if (last_token->id != Id::Eof_Real) {
    this->lexer->MaybeUnreadOne();
  }
  return enode;
}

void WordParser::ParseProc(syntax_asdl::Proc* node) {
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&last_token);

  this->_SetNext(lex_mode_e::ShCommand);
  this->_GetToken();
  if (this->token_type != Id::Lit_Chars) {
    p_die(StrFormat("Invalid proc name %s", ui::PrettyToken(this->cur_token)), this->cur_token);
  }
  node->name = this->cur_token;
  last_token = this->parse_ctx->ParseProc(this->lexer, node);
  last_token->id = Id::Lit_LBrace;
  this->buffered_word = last_token;
  this->_SetNext(lex_mode_e::ShCommand);
}

void WordParser::ParseFunc(syntax_asdl::Func* node) {
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&last_token);

  last_token = this->parse_ctx->ParseFunc(this->lexer, node);
  last_token->id = Id::Lit_LBrace;
  this->buffered_word = last_token;
  this->_SetNext(lex_mode_e::ShCommand);
}

Tuple2<syntax_asdl::pat_t*, syntax_asdl::Token*> WordParser::ParseYshCasePattern() {
  syntax_asdl::pat_t* pat = nullptr;
  syntax_asdl::Token* left_tok = nullptr;
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&pat);
  StackRoot _root1(&left_tok);
  StackRoot _root2(&last_token);

  Tuple3<syntax_asdl::pat_t*, syntax_asdl::Token*, syntax_asdl::Token*> tup7 = this->parse_ctx->ParseYshCasePattern(this->lexer);
  pat = tup7.at0();
  left_tok = tup7.at1();
  last_token = tup7.at2();
  if (last_token->id == Id::Op_LBrace) {
    last_token->id = Id::Lit_LBrace;
  }
  this->buffered_word = last_token;
  return Tuple2<syntax_asdl::pat_t*, syntax_asdl::Token*>(pat, left_tok);
}

int WordParser::NewlineOkForYshCase() {
  int next_id;
  id_kind_asdl::Kind_t next_kind;
  while (true) {
    next_id = this->lexer->LookAheadOne(lex_mode_e::Expr);
    if (next_id == Id::Unknown_Tok) {
      if (!this->lexer->MoveToNextLine()) {
        break;
      }
      continue;
    }
    next_kind = consts::GetKind(next_id);
    if ((next_id != Id::Op_Newline and next_kind != Kind::Ignored)) {
      break;
    }
    this->lexer->Read(lex_mode_e::Expr);
  }
  if ((next_id == Id::Op_RBrace || next_id == Id::Op_LParen || next_id == Id::Arith_Slash)) {
    this->_SetNext(lex_mode_e::Expr);
  }
  else {
    this->_SetNext(lex_mode_e::ShCommand);
    this->_GetToken();
  }
  return next_id;
}

syntax_asdl::arith_expr_t* WordParser::_ReadArithExpr(int end_id) {
  syntax_asdl::arith_expr_t* anode = nullptr;
  int cur_id;
  StackRoot _root0(&anode);

  anode = this->a_parser->Parse();
  cur_id = this->a_parser->CurrentId();
  if ((end_id != Id::Undefined_Tok and cur_id != end_id)) {
    p_die(StrFormat("Unexpected token after arithmetic expression (%s != %s)", ui::PrettyId(cur_id), ui::PrettyId(end_id)), Alloc<loc::Word>(this->a_parser->cur_word));
  }
  return anode;
}

word_part::ArithSub* WordParser::_ReadArithSub() {
  syntax_asdl::Token* left_tok = nullptr;
  syntax_asdl::arith_expr_t* anode = nullptr;
  syntax_asdl::Token* right_tok = nullptr;
  StackRoot _root0(&left_tok);
  StackRoot _root1(&anode);
  StackRoot _root2(&right_tok);

  left_tok = this->cur_token;
  this->lexer->PushHint(Id::Op_RParen, Id::Right_DollarDParen);
  anode = arith_expr::EmptyZero;
  this->_NextNonSpace();
  if (this->token_type != Id::Arith_RParen) {
    anode = this->_ReadArithExpr(Id::Arith_RParen);
  }
  this->_SetNext(lex_mode_e::ShCommand);
  this->_GetToken();
  if (this->token_type != Id::Right_DollarDParen) {
    p_die(S_fsD, this->cur_token);
  }
  right_tok = this->cur_token;
  return Alloc<word_part::ArithSub>(left_tok, anode, right_tok);
}

Tuple2<syntax_asdl::arith_expr_t*, syntax_asdl::Token*> WordParser::ReadDParen() {
  syntax_asdl::arith_expr_t* anode = nullptr;
  syntax_asdl::Token* right = nullptr;
  StackRoot _root0(&anode);
  StackRoot _root1(&right);

  anode = arith_expr::EmptyZero;
  this->lexer->PushHint(Id::Op_RParen, Id::Op_DRightParen);
  this->_NextNonSpace();
  if (this->token_type != Id::Arith_RParen) {
    anode = this->_ReadArithExpr(Id::Arith_RParen);
  }
  this->_SetNext(lex_mode_e::ShCommand);
  this->_GetToken();
  right = this->cur_token;
  if (right->id != Id::Op_DRightParen) {
    p_die(S_Fgl, right);
  }
  this->_SetNext(lex_mode_e::ShCommand);
  return Tuple2<syntax_asdl::arith_expr_t*, syntax_asdl::Token*>(anode, right);
}

void WordParser::_NextNonSpace() {
  while (true) {
    this->_SetNext(lex_mode_e::Arith);
    this->_GetToken();
    if ((this->token_kind != Kind::Ignored && this->token_kind != Kind::WS)) {
      break;
    }
  }
}

command::ForExpr* WordParser::ReadForExpression() {
  int cur_id;
  syntax_asdl::arith_expr_t* init_node = nullptr;
  syntax_asdl::arith_expr_t* cond_node = nullptr;
  syntax_asdl::arith_expr_t* update_node = nullptr;
  command::ForExpr* node = nullptr;
  StackRoot _root0(&init_node);
  StackRoot _root1(&cond_node);
  StackRoot _root2(&update_node);
  StackRoot _root3(&node);

  this->_NextNonSpace();
  cur_id = this->token_type;
  if (cur_id == Id::Arith_Semi) {
    init_node = arith_expr::EmptyZero;
  }
  else {
    init_node = this->a_parser->Parse();
    cur_id = this->a_parser->CurrentId();
  }
  this->_NextNonSpace();
  if (cur_id != Id::Arith_Semi) {
    p_die(S_lun, Alloc<loc::Word>(this->a_parser->cur_word));
  }
  this->_GetToken();
  cur_id = this->token_type;
  if (cur_id == Id::Arith_Semi) {
    cond_node = arith_expr::EmptyOne;
  }
  else {
    cond_node = this->a_parser->Parse();
    cur_id = this->a_parser->CurrentId();
  }
  if (cur_id != Id::Arith_Semi) {
    p_die(S_lun, Alloc<loc::Word>(this->a_parser->cur_word));
  }
  this->_NextNonSpace();
  if (this->token_type == Id::Arith_RParen) {
    update_node = arith_expr::EmptyZero;
  }
  else {
    update_node = this->_ReadArithExpr(Id::Arith_RParen);
  }
  this->_NextNonSpace();
  if (this->token_type != Id::Arith_RParen) {
    p_die(S_nfD, this->cur_token);
  }
  this->_SetNext(lex_mode_e::ShCommand);
  node = command::ForExpr::CreateNull();
  node->init = init_node;
  node->cond = cond_node;
  node->update = update_node;
  return node;
}

syntax_asdl::word_part_t* WordParser::_ReadArrayLiteral() {
  syntax_asdl::Token* left_token = nullptr;
  syntax_asdl::Token* right_token = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  bool done;
  syntax_asdl::word_t* w = nullptr;
  syntax_asdl::Token* tok = nullptr;
  List<syntax_asdl::word_t*>* no_words = nullptr;
  syntax_asdl::ShArrayLiteral* node = nullptr;
  List<syntax_asdl::AssocPair*>* pairs = nullptr;
  syntax_asdl::AssocPair* pair = nullptr;
  int n;
  syntax_asdl::CompoundWord* w2 = nullptr;
  List<syntax_asdl::word_t*>* words2 = nullptr;
  List<syntax_asdl::word_t*>* words3 = nullptr;
  StackRoot _root0(&left_token);
  StackRoot _root1(&right_token);
  StackRoot _root2(&w_parser);
  StackRoot _root3(&words);
  StackRoot _root4(&w);
  StackRoot _root5(&tok);
  StackRoot _root6(&no_words);
  StackRoot _root7(&node);
  StackRoot _root8(&pairs);
  StackRoot _root9(&pair);
  StackRoot _root10(&w2);
  StackRoot _root11(&words2);
  StackRoot _root12(&words3);

  this->_SetNext(lex_mode_e::ShCommand);
  this->_GetToken();
  if (this->cur_token->id != Id::Op_LParen) {
    p_die(S_oCF, this->cur_token);
  }
  left_token = this->cur_token;
  right_token = nullptr;
  w_parser = this->parse_ctx->MakeWordParser(this->lexer, this->line_reader);
  words = Alloc<List<syntax_asdl::CompoundWord*>>();
  done = false;
  while (!done) {
    w = w_parser->ReadWord(lex_mode_e::ShCommand);
    switch (w->tag()) {
      case word_e::Operator: {
        tok = static_cast<Token*>(w);
        if (tok->id == Id::Right_ShArrayLiteral) {
          right_token = tok;
          done = true;
        }
        else {
          if (tok->id == Id::Op_Newline) {
            continue;
          }
          else {
            p_die(S_Ayd, Alloc<loc::Word>(w));
          }
        }
      }
        break;
      case word_e::Compound: {
        words->append(static_cast<CompoundWord*>(w));
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  if (len(words) == 0) {
    no_words = Alloc<List<syntax_asdl::word_t*>>();
    node = Alloc<ShArrayLiteral>(left_token, no_words, right_token);
    return node;
  }
  pairs = Alloc<List<syntax_asdl::AssocPair*>>();
  pair = word_::DetectAssocPair(words->at(0));
  if (pair) {
    pairs->append(pair);
    n = len(words);
    for (int i = 1; i < n; ++i) {
      w2 = words->at(i);
      pair = word_::DetectAssocPair(w2);
      if (!pair) {
        p_die(S_ntr, Alloc<loc::Word>(w2));
      }
      pairs->append(pair);
    }
    return Alloc<word_part::BashAssocLiteral>(left_token, pairs, right_token);
  }
  words2 = braces::BraceDetectAll(words);
  words3 = word_::TildeDetectAll(words2);
  return Alloc<ShArrayLiteral>(left_token, words3, right_token);
}

syntax_asdl::ArgList* WordParser::ParseProcCallArgs(int start_symbol) {
  syntax_asdl::ArgList* arg_list = nullptr;
  StackRoot _root0(&arg_list);

  this->lexer->MaybeUnreadOne();
  arg_list = ArgList::CreateNull(true);
  arg_list->left = this->cur_token;
  this->parse_ctx->ParseProcCallArgs(this->lexer, arg_list, start_symbol);
  return arg_list;
}

bool WordParser::_MaybeReadWordPart(bool is_first, types_asdl::lex_mode_t lex_mode, List<syntax_asdl::word_part_t*>* parts) {
  bool done;
  syntax_asdl::Token* tok = nullptr;
  BigStr* ch = nullptr;
  syntax_asdl::word_part_t* part = nullptr;
  int next_id;
  syntax_asdl::word_part_t* part2 = nullptr;
  syntax_asdl::Token* splice_tok = nullptr;
  StackRoot _root0(&parts);
  StackRoot _root1(&tok);
  StackRoot _root2(&ch);
  StackRoot _root3(&part);
  StackRoot _root4(&part2);
  StackRoot _root5(&splice_tok);

  done = false;
  if (this->token_type == Id::Lit_EscapedChar) {
    tok = this->cur_token;
    ch = lexer::TokenSliceLeft(tok, 1);
    if (!this->parse_opts->parse_backslash()) {
      if (!pyutil::IsValidCharEscape(ch)) {
        p_die(S_dEh, this->cur_token);
      }
    }
    part = Alloc<word_part::EscapedLiteral>(this->cur_token, ch);
  }
  else {
    part = this->cur_token;
  }
  if ((is_first and this->token_type == Id::Lit_VarLike)) {
    parts->append(part);
    next_id = this->lexer->LookPastSpace(lex_mode);
    if (next_id == Id::Op_LParen) {
      this->lexer->PushHint(Id::Op_RParen, Id::Right_ShArrayLiteral);
      part2 = this->_ReadArrayLiteral();
      parts->append(part2);
      this->_SetNext(lex_mode);
      this->_GetToken();
      if (!list_contains(KINDS_THAT_END_WORDS, this->token_kind)) {
        p_die(S_bbn, this->cur_token);
      }
      done = true;
    }
  }
  else {
    if ((is_first and (this->parse_opts->parse_at() and this->token_type == Id::Lit_Splice))) {
      splice_tok = this->cur_token;
      part2 = Alloc<word_part::Splice>(splice_tok, lexer::TokenSliceLeft(splice_tok, 1));
      parts->append(part2);
      this->_SetNext(lex_mode);
      this->_GetToken();
      if (!list_contains(KINDS_THAT_END_WORDS, this->token_kind)) {
        p_die(S_evz, this->cur_token);
      }
      done = true;
    }
    else {
      if ((is_first and (this->parse_opts->parse_at() and this->token_type == Id::Lit_AtLBracket))) {
        part2 = this->_ReadExprSub(lex_mode_e::DQ);
        parts->append(part2);
        this->_SetNext(lex_mode);
        this->_GetToken();
        if (!list_contains(KINDS_THAT_END_WORDS, this->token_kind)) {
          p_die(S_AAk, this->cur_token);
        }
        done = true;
      }
      else {
        if ((is_first and (this->parse_opts->parse_at() and this->token_type == Id::Lit_AtLBraceDot))) {
          p_die(S_rsr, this->cur_token);
        }
        else {
          if ((is_first and (this->parse_opts->parse_at_all() and this->token_type == Id::Lit_At))) {
            p_die(S_oFq, this->cur_token);
          }
          else {
            parts->append(part);
          }
        }
      }
    }
  }
  return done;
}

syntax_asdl::CompoundWord* WordParser::_ReadCompoundWord(types_asdl::lex_mode_t lex_mode) {
  return this->_ReadCompoundWord3(lex_mode, Id::Undefined_Tok, true);
}

syntax_asdl::CompoundWord* WordParser::_ReadCompoundWord3(types_asdl::lex_mode_t lex_mode, int eof_type, bool empty_ok) {
  syntax_asdl::CompoundWord* w = nullptr;
  int num_parts;
  int brace_count;
  bool done;
  syntax_asdl::BoolParamBox* is_triple_quoted = nullptr;
  bool allow_done;
  BigStr* next_byte = nullptr;
  syntax_asdl::Token* vsub_token = nullptr;
  syntax_asdl::word_part_t* part = nullptr;
  syntax_asdl::CommandSub* cs_part = nullptr;
  bool try_triple_quote;
  StackRoot _root0(&w);
  StackRoot _root1(&is_triple_quoted);
  StackRoot _root2(&next_byte);
  StackRoot _root3(&vsub_token);
  StackRoot _root4(&part);
  StackRoot _root5(&cs_part);

  w = Alloc<CompoundWord>(Alloc<List<syntax_asdl::word_part_t*>>());
  num_parts = 0;
  brace_count = 0;
  done = false;
  is_triple_quoted = nullptr;
  while (!done) {
    this->_GetToken();
    allow_done = (empty_ok or num_parts != 0);
    if ((allow_done and this->token_type == eof_type)) {
      done = true;
    }
    else {
      if ((this->token_kind == Kind::Lit || this->token_kind == Kind::History || this->token_kind == Kind::KW || this->token_kind == Kind::ControlFlow || this->token_kind == Kind::BoolUnary || this->token_kind == Kind::BoolBinary)) {
        if (this->token_type == Id::Lit_LBrace) {
          brace_count += 1;
        }
        else {
          if (this->token_type == Id::Lit_RBrace) {
            brace_count -= 1;
          }
          else {
            if (this->token_type == Id::Lit_Dollar) {
              if (!this->parse_opts->parse_dollar()) {
                if ((num_parts == 0 and lex_mode == lex_mode_e::ShCommand)) {
                  next_byte = this->lexer->ByteLookAhead();
                  if (str_equals(next_byte, S_ckc)) {
                    ;  // pass
                  }
                }
                p_die(S_oex, this->cur_token);
              }
            }
          }
        }
        done = this->_MaybeReadWordPart(num_parts == 0, lex_mode, w->parts);
      }
      else {
        if (this->token_kind == Kind::VSub) {
          vsub_token = this->cur_token;
          part = Alloc<SimpleVarSub>(vsub_token);
          w->parts->append(part);
        }
        else {
          if (this->token_kind == Kind::ExtGlob) {
            if ((this->parse_opts->parse_at() and (this->token_type == Id::ExtGlob_At and num_parts == 0))) {
              cs_part = this->_ReadCommandSub(Id::Left_AtParen, false);
              cs_part->left_token->id = Id::Left_AtParen;
              part = cs_part;
              this->_GetToken();
              if (!list_contains(KINDS_THAT_END_WORDS, this->token_kind)) {
                p_die(S_rip, this->cur_token);
              }
              done = true;
            }
            else {
              if (HAVE_FNM_EXTMATCH == 0) {
                p_die(S_eez, this->cur_token);
              }
              part = this->_ReadExtGlob();
            }
            w->parts->append(part);
          }
          else {
            if (this->token_kind == Kind::BashRegex) {
              if (this->token_type == Id::BashRegex_LParen) {
                part = this->_ReadBashRegexGroup();
                w->parts->append(part);
              }
              else {
                p_die(S_qDr, this->cur_token);
              }
            }
            else {
              if (this->token_kind == Kind::Left) {
                try_triple_quote = (this->parse_opts->parse_triple_quote() and (lex_mode == lex_mode_e::ShCommand and num_parts == 0));
                if (try_triple_quote) {
                  is_triple_quoted = Alloc<BoolParamBox>(false);
                }
                part = this->_ReadUnquotedLeftParts(is_triple_quoted);
                w->parts->append(part);
              }
              else {
                if (this->token_kind == Kind::Right) {
                  if (this->token_type == Id::Right_DoubleQuote) {
                    ;  // pass
                  }
                  else {
                    if (this->token_type == Id::Right_Subshell) {
                      if (this->lexer->MaybeUnreadOne()) {
                        this->lexer->PushHint(Id::Op_RParen, Id::Right_Subshell);
                        this->_SetNext(lex_mode);
                      }
                      done = true;
                    }
                    else {
                      done = true;
                    }
                  }
                }
                else {
                  if (this->token_kind == Kind::Ignored) {
                    done = true;
                  }
                  else {
                    if ((this->token_type == Id::Op_RParen || this->token_type == Id::Eof_RParen)) {
                      if (this->lexer->MaybeUnreadOne()) {
                        if (this->token_type == Id::Eof_RParen) {
                          this->lexer->PushHint(Id::Op_RParen, Id::Eof_RParen);
                        }
                        this->_SetNext(lex_mode);
                      }
                    }
                    done = true;
                  }
                }
              }
            }
          }
        }
      }
    }
    if (!done) {
      this->_SetNext(lex_mode);
      num_parts += 1;
    }
  }
  if ((this->parse_opts->parse_brace() and (num_parts > 1 and brace_count != 0))) {
    p_die(S_tvz, Alloc<loc::Word>(w));
  }
  if ((is_triple_quoted and (is_triple_quoted->b and num_parts > 1))) {
    p_die(S_imw, Alloc<loc::WordPart>(w->parts->at(-1)));
  }
  return w;
}

syntax_asdl::word_t* WordParser::_ReadArithWord() {
  this->_GetToken();
  if (this->token_kind == Kind::Unknown) {
    p_die(StrFormat("Unexpected token while parsing arithmetic: %r", lexer::TokenVal(this->cur_token)), this->cur_token);
  }
  else {
    if (this->token_kind == Kind::Eof) {
      return this->cur_token;
    }
    else {
      if (this->token_kind == Kind::Ignored) {
        this->_SetNext(lex_mode_e::Arith);
        return nullptr;
      }
      else {
        if ((this->token_kind == Kind::Arith || this->token_kind == Kind::Right)) {
          this->_SetNext(lex_mode_e::Arith);
          return this->cur_token;
        }
        else {
          if ((this->token_kind == Kind::Lit || this->token_kind == Kind::Left || this->token_kind == Kind::VSub)) {
            return this->_ReadCompoundWord(lex_mode_e::Arith);
          }
          else {
            assert(0);  // AssertionError
          }
        }
      }
    }
  }
}

syntax_asdl::word_t* WordParser::_ReadWord(types_asdl::lex_mode_t word_mode) {
  types_asdl::lex_mode_t lex_mode;
  syntax_asdl::Token* bracket_word = nullptr;
  syntax_asdl::Token* tok = nullptr;
  int left_id;
  StackRoot _root0(&bracket_word);
  StackRoot _root1(&tok);

  if (word_mode == lex_mode_e::ShCommandFakeBrack) {
    lex_mode = lex_mode_e::ShCommand;
  }
  else {
    lex_mode = word_mode;
  }
  this->_GetToken();
  if (this->token_kind == Kind::Eof) {
    return this->cur_token;
  }
  else {
    if ((this->token_kind == Kind::Op || this->token_kind == Kind::Redir || this->token_kind == Kind::Arith)) {
      this->_SetNext(lex_mode);
      if (this->token_type == Id::Op_Newline) {
        if (this->multiline) {
          if (this->newline_state > 1) {
            p_die(S_gba, this->cur_token);
          }
          return nullptr;
        }
        if (this->returned_newline) {
          return nullptr;
        }
      }
      return this->cur_token;
    }
    else {
      if (this->token_kind == Kind::Right) {
        if ((this->token_type != Id::Right_Subshell && this->token_type != Id::Right_ShFunction && this->token_type != Id::Right_CasePat && this->token_type != Id::Right_ShArrayLiteral)) {
          assert(0);  // AssertionError
        }
        this->_SetNext(lex_mode);
        return this->cur_token;
      }
      else {
        if ((this->token_kind == Kind::Ignored || this->token_kind == Kind::WS)) {
          this->_SetNext(lex_mode);
          return nullptr;
        }
        else {
          if ((word_mode == lex_mode_e::ShCommandFakeBrack and (this->parse_opts->parse_bracket() and this->token_type == Id::Lit_LBracket))) {
            bracket_word = this->cur_token;
            bracket_word->id = Id::Op_LBracket;
            this->_SetNext(lex_mode);
            return bracket_word;
          }
          if (this->token_type == Id::Lit_Pound) {
            this->_SetNext(lex_mode_e::Comment);
            this->_GetToken();
            return nullptr;
          }
          else {
            if (this->token_type == Id::Lit_TPound) {
              this->_SetNext(lex_mode_e::Comment);
              this->_GetToken();
              if ((this->token_type == Id::Ignored_Comment and this->emit_doc_token)) {
                return this->cur_token;
              }
              return nullptr;
            }
            else {
              if ((this->token_type == Id::Lit_Chars and this->lexer->LookAheadOne(lex_mode_e::ShCommand) == Id::Left_SingleQuote)) {
                tok = this->cur_token;
                if (this->parse_opts->parse_ysh_string()) {
                  if (lexer::TokenEquals(tok, S_nAr_1)) {
                    left_id = Id::Left_RSingleQuote;
                  }
                  else {
                    if (lexer::TokenEquals(tok, S_rsz)) {
                      left_id = Id::Left_USingleQuote;
                    }
                    else {
                      if (lexer::TokenEquals(tok, S_jFv)) {
                        left_id = Id::Left_BSingleQuote;
                      }
                      else {
                        left_id = Id::Undefined_Tok;
                      }
                    }
                  }
                  if (left_id != Id::Undefined_Tok) {
                    this->_SetNext(lex_mode_e::ShCommand);
                    this->_GetToken();
                    return this->_ReadYshSingleQuoted(left_id);
                  }
                }
              }
              return this->_ReadCompoundWord(lex_mode);
            }
          }
        }
      }
    }
  }
}

syntax_asdl::BracedVarSub* WordParser::ParseVarRef() {
  syntax_asdl::BracedVarSub* part = nullptr;
  StackRoot _root0(&part);

  this->_SetNext(lex_mode_e::VSub_1);
  this->_GetToken();
  if (this->token_kind != Kind::VSub) {
    p_die(S_smu, this->cur_token);
  }
  part = this->_ParseVarOf();
  part->left = part->name_tok;
  part->right = part->name_tok;
  this->_GetToken();
  if (this->token_type != Id::Eof_Real) {
    p_die(S_txD, this->cur_token);
  }
  return part;
}

int WordParser::LookPastSpace() {
  int id_;
  if (this->cur_token->id == Id::WS_Space) {
    id_ = this->lexer->LookPastSpace(lex_mode_e::ShCommand);
  }
  else {
    id_ = this->cur_token->id;
  }
  return id_;
}

bool WordParser::LookAheadFuncParens() {
  if (this->cur_token->id == Id::Op_LParen) {
    return this->lexer->LookAheadFuncParens(1);
  }
  else {
    if (this->cur_token->id == Id::WS_Space) {
      return this->lexer->LookAheadFuncParens(0);
    }
    else {
      return false;
    }
  }
}

syntax_asdl::word_t* WordParser::ReadWord(types_asdl::lex_mode_t word_mode) {
  syntax_asdl::word_t* w = nullptr;
  StackRoot _root0(&w);

  if (this->buffered_word) {
    w = this->buffered_word;
    this->buffered_word = nullptr;
  }
  else {
    while (true) {
      w = this->_ReadWord(word_mode);
      if (w != nullptr) {
        break;
      }
    }
  }
  this->returned_newline = word_::CommandId(w) == Id::Op_Newline;
  return w;
}

syntax_asdl::word_t* WordParser::ReadArithWord() {
  syntax_asdl::word_t* w = nullptr;
  StackRoot _root0(&w);

  while (true) {
    w = this->_ReadArithWord();
    if (w != nullptr) {
      break;
    }
  }
  return w;
}

void WordParser::ReadHereDocBody(List<syntax_asdl::word_part_t*>* parts) {
  StackRoot _root0(&parts);

  this->_ReadLikeDQ(nullptr, false, parts);
}

syntax_asdl::CompoundWord* WordParser::ReadForPlugin() {
  syntax_asdl::CompoundWord* w = nullptr;
  StackRoot _root0(&w);

  w = Alloc<CompoundWord>(Alloc<List<syntax_asdl::word_part_t*>>());
  this->_ReadLikeDQ(nullptr, false, w->parts);
  return w;
}

void WordParser::EmitDocToken(bool b) {
  this->emit_doc_token = b;
}

void WordParser::Multiline(bool b) {
  this->multiline = b;
}

}  // define namespace word_parse

namespace parse {  // define

using pnode::PNode;
using pnode::PNodeAllocator;
int NT_OFFSET = 256;

ParseError::ParseError(BigStr* msg, int type_, syntax_asdl::Token* tok) {
  this->msg = msg;
  this->type = type_;
  this->tok = tok;
}

_StackItem::_StackItem(Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* dfa, int state, pnode::PNode* node) {
  this->dfa = dfa;
  this->state = state;
  this->node = node;
}

Parser::Parser(grammar::Grammar* grammar) {
  this->grammar = grammar;
  this->rootnode = nullptr;
  this->stack = Alloc<List<parse::_StackItem*>>();
  this->pnode_alloc = nullptr;
}

void Parser::setup(int start, pnode::PNodeAllocator* pnode_alloc) {
  pnode::PNode* newnode = nullptr;
  StackRoot _root0(&pnode_alloc);
  StackRoot _root1(&newnode);

  this->pnode_alloc = pnode_alloc;
  newnode = this->pnode_alloc->NewPNode(start, nullptr);
  this->stack = NewList<parse::_StackItem*>(std::initializer_list<parse::_StackItem*>{Alloc<_StackItem>(this->grammar->dfas->at(start), 0, newnode)});
  this->rootnode = nullptr;
}

bool Parser::addtoken(int typ, syntax_asdl::Token* opaque, int ilabel) {
  parse::_StackItem* top = nullptr;
  List<List<Tuple2<int, int>*>*>* states = nullptr;
  int state;
  List<Tuple2<int, int>*>* arcs = nullptr;
  bool found;
  int t;
  int s0;
  int s1;
  Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* itsdfa = nullptr;
  Dict<int, int>* itsfirst = nullptr;
  bool found2;
  StackRoot _root0(&opaque);
  StackRoot _root1(&top);
  StackRoot _root2(&states);
  StackRoot _root3(&arcs);
  StackRoot _root4(&itsdfa);
  StackRoot _root5(&itsfirst);

  while (true) {
    top = this->stack->at(-1);
    Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* tup0 = top->dfa;
    states = tup0->at0();
    state = top->state;
    arcs = states->at(state);
    found = false;
    for (ListIter<Tuple2<int, int>*> it(arcs); !it.Done(); it.Next()) {
      Tuple2<int, int>* tup1 = it.Value();
      int ilab = tup1->at0();
      int newstate = tup1->at1();
      t = this->grammar->labels->at(ilab);
      if (ilabel == ilab) {
        this->shift(typ, opaque, newstate);
        state = newstate;
        while (true) {
          if (len(states->at(state)) != 1) {
            break;
          }
          Tuple2<int, int>* tup2 = states->at(state)->at(0);
          s0 = tup2->at0();
          s1 = tup2->at1();
          if ((s0 != 0 or s1 != state)) {
            break;
          }
          this->pop();
          if (len(this->stack) == 0) {
            return true;
          }
          top = this->stack->at(-1);
          Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* tup3 = top->dfa;
          states = tup3->at0();
          state = top->state;
        }
        return false;
      }
      else {
        if (t >= NT_OFFSET) {
          itsdfa = this->grammar->dfas->at(t);
          Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* tup4 = itsdfa;
          itsfirst = tup4->at1();
          if (dict_contains(itsfirst, ilabel)) {
            this->push(t, opaque, this->grammar->dfas->at(t), newstate);
            found = true;
            break;
          }
        }
      }
    }
    if (!found) {
      found2 = false;
      for (ListIter<Tuple2<int, int>*> it(arcs); !it.Done(); it.Next()) {
        Tuple2<int, int>* tup5 = it.Value();
        int left = tup5->at0();
        int right = tup5->at1();
        if ((left == 0 and right == state)) {
          this->pop();
          if (len(this->stack) == 0) {
            throw Alloc<ParseError>(S_vwh, typ, opaque);
          }
          found2 = true;
        }
      }
      if (!found2) {
        throw Alloc<ParseError>(S_trA, typ, opaque);
      }
    }
  }
}

void Parser::shift(int typ, syntax_asdl::Token* opaque, int newstate) {
  parse::_StackItem* top = nullptr;
  pnode::PNode* newnode = nullptr;
  StackRoot _root0(&opaque);
  StackRoot _root1(&top);
  StackRoot _root2(&newnode);

  top = this->stack->at(-1);
  newnode = this->pnode_alloc->NewPNode(typ, opaque);
  top->node->AddChild(newnode);
  this->stack->at(-1)->state = newstate;
}

void Parser::push(int typ, syntax_asdl::Token* opaque, Tuple2<List<List<Tuple2<int, int>*>*>*, Dict<int, int>*>* newdfa, int newstate) {
  parse::_StackItem* top = nullptr;
  pnode::PNode* newnode = nullptr;
  StackRoot _root0(&opaque);
  StackRoot _root1(&newdfa);
  StackRoot _root2(&top);
  StackRoot _root3(&newnode);

  top = this->stack->at(-1);
  newnode = this->pnode_alloc->NewPNode(typ, opaque);
  this->stack->at(-1)->state = newstate;
  this->stack->append(Alloc<_StackItem>(newdfa, 0, newnode));
}

void Parser::pop() {
  parse::_StackItem* top = nullptr;
  pnode::PNode* newnode = nullptr;
  parse::_StackItem* top2 = nullptr;
  StackRoot _root0(&top);
  StackRoot _root1(&newnode);
  StackRoot _root2(&top2);

  top = this->stack->pop();
  newnode = top->node;
  if (len(this->stack)) {
    top2 = this->stack->at(-1);
    top2->node->AddChild(newnode);
  }
  else {
    this->rootnode = newnode;
  }
}

}  // define namespace parse

namespace os_path {  // define

BigStr* extsep = S_Aru;
BigStr* sep = S_ckc;

BigStr* join(BigStr* s1, BigStr* s2) {
  StackRoot _root0(&s1);
  StackRoot _root1(&s2);

  if ((s2->startswith(S_ckc) or len(s1) == 0)) {
    return s2;
  }
  if (s1->endswith(S_ckc)) {
    return str_concat(s1, s2);
  }
  return StrFormat("%s/%s", s1, s2);
}

Tuple2<BigStr*, BigStr*> split(BigStr* p) {
  int i;
  BigStr* head = nullptr;
  BigStr* tail = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&head);
  StackRoot _root2(&tail);

  i = (p->rfind(S_ckc) + 1);
  head = p->slice(0, i);
  tail = p->slice(i);
  head = rstrip_slashes(head);
  return Tuple2<BigStr*, BigStr*>(head, tail);
}

Tuple2<BigStr*, BigStr*> _splitext(BigStr* p, BigStr* sep, BigStr* extsep) {
  int sepIndex;
  int dotIndex;
  int filenameIndex;
  StackRoot _root0(&p);
  StackRoot _root1(&sep);
  StackRoot _root2(&extsep);

  sepIndex = p->rfind(sep);
  dotIndex = p->rfind(extsep);
  if (dotIndex > sepIndex) {
    filenameIndex = (sepIndex + 1);
    while (filenameIndex < dotIndex) {
      if (!(str_equals(p->at(filenameIndex), extsep))) {
        return Tuple2<BigStr*, BigStr*>(p->slice(0, dotIndex), p->slice(dotIndex));
      }
      filenameIndex += 1;
    }
  }
  return Tuple2<BigStr*, BigStr*>(p, S_Aoo);
}

Tuple2<BigStr*, BigStr*> splitext(BigStr* p) {
  StackRoot _root0(&p);

  return _splitext(p, sep, extsep);
}

BigStr* basename(BigStr* p) {
  int i;
  StackRoot _root0(&p);

  i = (p->rfind(S_ckc) + 1);
  return p->slice(i);
}

BigStr* dirname(BigStr* p) {
  int i;
  BigStr* head = nullptr;
  StackRoot _root0(&p);
  StackRoot _root1(&head);

  i = (p->rfind(S_ckc) + 1);
  head = p->slice(0, i);
  head = rstrip_slashes(head);
  return head;
}

BigStr* normpath(BigStr* path) {
  BigStr* slash = nullptr;
  BigStr* dot = nullptr;
  int initial_slashes;
  List<BigStr*>* comps = nullptr;
  List<BigStr*>* new_comps = nullptr;
  StackRoot _root0(&path);
  StackRoot _root1(&slash);
  StackRoot _root2(&dot);
  StackRoot _root3(&comps);
  StackRoot _root4(&new_comps);

  slash = S_ckc;
  dot = S_Aru;
  if (str_equals(path, S_Aoo)) {
    return dot;
  }
  initial_slashes = path->startswith(S_ckc);
  if ((initial_slashes and (path->startswith(S_lFp) and !path->startswith(S_gEs)))) {
    initial_slashes = 2;
  }
  comps = path->split(S_ckc);
  new_comps = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(comps); !it.Done(); it.Next()) {
    BigStr* comp = it.Value();
    StackRoot _for(&comp  );
    if ((len(comp) == 0 or str_equals(comp, S_Aru))) {
      continue;
    }
    if ((!(str_equals(comp, S_Dmc)) or ((initial_slashes == 0 and len(new_comps) == 0) or (len(new_comps) and str_equals(new_comps->at(-1), S_Dmc))))) {
      new_comps->append(comp);
    }
    else {
      if (len(new_comps)) {
        new_comps->pop();
      }
    }
  }
  comps = new_comps;
  path = slash->join(comps);
  if (initial_slashes) {
    path = str_concat(str_repeat(slash, initial_slashes), path);
  }
  return len(path) ? path : dot;
}

bool isabs(BigStr* s) {
  StackRoot _root0(&s);

  return s->startswith(S_ckc);
}

BigStr* abspath(BigStr* path) {
  BigStr* cwd = nullptr;
  StackRoot _root0(&path);
  StackRoot _root1(&cwd);

  if (!isabs(path)) {
    cwd = posix::getcwd();
    path = join(cwd, path);
  }
  return normpath(path);
}

}  // define namespace os_path

namespace fmt {  // define


void Format(alloc::Arena* arena, syntax_asdl::command_t* node) {
  ysh_ify::Cursor* cursor = nullptr;
  StackRoot _root0(&arena);
  StackRoot _root1(&node);
  StackRoot _root2(&cursor);

  cursor = Alloc<ysh_ify::Cursor>(arena, mylib::Stdout());
  cursor->PrintUntilEnd();
}

}  // define namespace fmt

namespace ysh_ify {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_str;
using runtime_asdl::word_style_e;
using runtime_asdl::word_style_t;
using syntax_asdl::loc;
using syntax_asdl::CompoundWord;
using syntax_asdl::Token;
using syntax_asdl::SimpleVarSub;
using syntax_asdl::BracedVarSub;
using syntax_asdl::CommandSub;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::SingleQuoted;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::word_part;
using syntax_asdl::word_part_e;
using syntax_asdl::word_part_t;
using syntax_asdl::rhs_word_e;
using syntax_asdl::rhs_word_t;
using syntax_asdl::sh_lhs;
using syntax_asdl::sh_lhs_e;
using syntax_asdl::command;
using syntax_asdl::command_e;
using syntax_asdl::BraceGroup;
using syntax_asdl::for_iter_e;
using syntax_asdl::case_arg_e;
using syntax_asdl::case_arg;
using syntax_asdl::condition_e;
using syntax_asdl::redir_param;
using syntax_asdl::redir_param_e;
using syntax_asdl::Redir;
using syntax_asdl::List_of_command;
using error::p_die;
using mylib::print_stderr;

Cursor::Cursor(alloc::Arena* arena, mylib::Writer* f) {
  this->arena = arena;
  this->f = f;
  this->next_span_id = 0;
}

void Cursor::_PrintUntilSpid(int until_span_id) {
  syntax_asdl::Token* span = nullptr;
  int start_index;
  int end_index;
  BigStr* piece = nullptr;
  StackRoot _root0(&span);
  StackRoot _root1(&piece);

  if (until_span_id == runtime::NO_SPID) {
  }
  for (int span_id = this->next_span_id; span_id < until_span_id; ++span_id) {
    span = this->arena->GetToken(span_id);
    if (span->line == nullptr) {
      continue;
    }
    start_index = span->id == Id::Lit_CharsWithoutPrefix ? 0 : span->col;
    end_index = (span->col + span->length);
    piece = span->line->content->slice(start_index, end_index);
    this->f->write(piece);
  }
  this->next_span_id = until_span_id;
}

void Cursor::_SkipUntilSpid(int next_span_id) {
  if ((next_span_id == runtime::NO_SPID or next_span_id == (runtime::NO_SPID + 1))) {
  }
  this->next_span_id = next_span_id;
}

void Cursor::SkipUntil(syntax_asdl::Token* tok) {
  int span_id;
  StackRoot _root0(&tok);

  span_id = this->arena->GetSpanId(tok);
  this->_SkipUntilSpid(span_id);
}

void Cursor::SkipPast(syntax_asdl::Token* tok) {
  int span_id;
  StackRoot _root0(&tok);

  span_id = this->arena->GetSpanId(tok);
  this->_SkipUntilSpid((span_id + 1));
}

void Cursor::PrintUntil(syntax_asdl::Token* tok) {
  int span_id;
  StackRoot _root0(&tok);

  span_id = this->arena->GetSpanId(tok);
  this->_PrintUntilSpid(span_id);
}

void Cursor::PrintIncluding(syntax_asdl::Token* tok) {
  int span_id;
  StackRoot _root0(&tok);

  span_id = this->arena->GetSpanId(tok);
  this->_PrintUntilSpid((span_id + 1));
}

void Cursor::PrintUntilEnd() {
  this->_PrintUntilSpid(this->arena->LastSpanId());
}

void LosslessCat(alloc::Arena* arena) {
  ysh_ify::Cursor* cursor = nullptr;
  StackRoot _root0(&arena);
  StackRoot _root1(&cursor);

  cursor = Alloc<Cursor>(arena, mylib::Stdout());
  cursor->PrintUntilEnd();
}

void PrintTokens(alloc::Arena* arena) {
  int i;
  BigStr* piece = nullptr;
  StackRoot _root0(&arena);
  StackRoot _root1(&piece);

  if (len(arena->tokens) == 1) {
    print(S_dyg);
    print(StrFormat("%s", arena->tokens->at(0)));
    return ;
  }
  i = 0;
  for (ListIter<syntax_asdl::Token*> it(arena->tokens); !it.Done(); it.Next(), ++i) {
    syntax_asdl::Token* tok = it.Value();
    StackRoot _for(&tok  );
    piece = tok->line->content->slice(tok->col, (tok->col + tok->length));
    print(StrFormat("%5d %-20s %r", i, Id_str(tok->id, false), piece));
  }
  print_stderr(StrFormat("(%d tokens)", len(arena->tokens)));
}

void Ysh_ify(alloc::Arena* arena, syntax_asdl::command_t* node) {
  ysh_ify::Cursor* cursor = nullptr;
  ysh_ify::YshPrinter* fixer = nullptr;
  StackRoot _root0(&arena);
  StackRoot _root1(&node);
  StackRoot _root2(&cursor);
  StackRoot _root3(&fixer);

  cursor = Alloc<Cursor>(arena, mylib::Stdout());
  fixer = Alloc<YshPrinter>(cursor, arena, mylib::Stdout());
  fixer->DoCommand(node, nullptr, true);
  fixer->End();
}

runtime_asdl::word_style_t _GetRhsStyle(syntax_asdl::rhs_word_t* w) {
  syntax_asdl::rhs_word_t* UP_w = nullptr;
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::word_part_t* UP_part0 = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&UP_w);
  StackRoot _root2(&part0);
  StackRoot _root3(&UP_part0);

  UP_w = w;
  switch (w->tag()) {
    case rhs_word_e::Empty: {
      return word_style_e::SQ;
    }
      break;
    case rhs_word_e::Compound: {
      CompoundWord* w = static_cast<CompoundWord*>(UP_w);
      if (len(w->parts) == 0) {
        assert(0);  // AssertionError
      }
      else {
        if (len(w->parts) == 1) {
          part0 = w->parts->at(0);
          UP_part0 = part0;
          switch (part0->tag()) {
            case word_part_e::TildeSub: {
              return word_style_e::Expr;
            }
              break;
            case word_part_e::Literal: {
              return word_style_e::SQ;
            }
              break;
            case word_part_e::SimpleVarSub: {
              return word_style_e::DQ;
            }
              break;
            case word_part_e::BracedVarSub: 
            case word_part_e::CommandSub: 
            case word_part_e::ArithSub: {
              return word_style_e::Unquoted;
            }
              break;
            case word_part_e::DoubleQuoted: {
              DoubleQuoted* part0 = static_cast<DoubleQuoted*>(UP_part0);
              return word_style_e::DQ;
            }
              break;
          }
        }
        else {
          return word_style_e::DQ;
        }
      }
    }
      break;
  }
  return word_style_e::SQ;
}

YshPrinter::YshPrinter(ysh_ify::Cursor* cursor, alloc::Arena* arena, mylib::Writer* f) {
  this->cursor = cursor;
  this->arena = arena;
  this->f = f;
}

void YshPrinter::_DebugSpid(int spid) {
  syntax_asdl::Token* span = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&span);
  StackRoot _root1(&s);

  span = this->arena->GetToken(spid);
  s = span->line->content->slice(span->col, (span->col + span->length));
  print_stderr(StrFormat("SPID %d = %r", spid, s));
}

void YshPrinter::End() {
  this->cursor->PrintUntilEnd();
}

void YshPrinter::DoRedirect(syntax_asdl::Redir* node, Dict<BigStr*, bool>* local_symbols) {
  int op_id;
  redir_param::HereDoc* here_doc = nullptr;
  syntax_asdl::word_t* here_begin = nullptr;
  bool ok;
  BigStr* delimiter = nullptr;
  bool delim_quoted;
  syntax_asdl::Token* delim_end_tok = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&local_symbols);
  StackRoot _root2(&here_doc);
  StackRoot _root3(&here_begin);
  StackRoot _root4(&delimiter);
  StackRoot _root5(&delim_end_tok);

  op_id = node->op->id;
  this->cursor->PrintUntil(node->op);
  if (node->arg->tag() == redir_param_e::HereDoc) {
    here_doc = static_cast<redir_param::HereDoc*>(node->arg);
    here_begin = here_doc->here_begin;
    Tuple3<bool, BigStr*, bool> tup0 = word_::StaticEval(here_begin);
    ok = tup0.at0();
    delimiter = tup0.at1();
    delim_quoted = tup0.at2();
    if (!ok) {
      p_die(S_xco, Alloc<loc::Word>(here_begin));
    }
    this->f->write(S_iDd);
    if (delim_quoted) {
      this->f->write(S_Brw);
    }
    else {
      this->f->write(S_eyD);
    }
    delim_end_tok = location::RightTokenForWord(here_begin);
    this->cursor->SkipPast(delim_end_tok);
    for (ListIter<syntax_asdl::word_part_t*> it(here_doc->stdin_parts); !it.Done(); it.Next()) {
      syntax_asdl::word_part_t* part = it.Value();
      StackRoot _for(&part    );
      this->DoWordPart(part, local_symbols);
    }
    this->cursor->SkipPast(here_doc->here_end_tok);
    if (delim_quoted) {
      this->f->write(S_oyy);
    }
    else {
      this->f->write(S_epy);
    }
  }
  else {
    ;  // pass
  }
}

void YshPrinter::DoShAssignment(command::ShAssignment* node, bool at_top_level, Dict<BigStr*, bool>* local_symbols) {
  bool has_rhs;
  bool defined_locally;
  syntax_asdl::sh_lhs_t* lhs0 = nullptr;
  List<bool>* has_array_index = nullptr;
  int n;
  int i;
  syntax_asdl::sh_lhs_t* lhs = nullptr;
  syntax_asdl::sh_lhs_t* UP_lhs = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&local_symbols);
  StackRoot _root2(&lhs0);
  StackRoot _root3(&has_array_index);
  StackRoot _root4(&lhs);
  StackRoot _root5(&UP_lhs);

  has_rhs = false;
  defined_locally = false;
  if (true) {
    this->cursor->PrintUntil(node->pairs->at(0)->left);
    if (local_symbols != nullptr) {
      lhs0 = node->pairs->at(0)->lhs;
    }
    has_array_index = Alloc<List<bool>>();
    for (ListIter<syntax_asdl::AssignPair*> it(node->pairs); !it.Done(); it.Next()) {
      syntax_asdl::AssignPair* pair = it.Value();
      has_array_index->append(pair->lhs->tag() == sh_lhs_e::UnparsedIndex);
    }
    if (at_top_level) {
      this->f->write(S_scw);
    }
    else {
      if (defined_locally) {
        this->f->write(S_scp);
      }
      else {
        this->f->write(S_scw);
      }
    }
  }
  n = len(node->pairs);
  i = 0;
  for (ListIter<syntax_asdl::AssignPair*> it(node->pairs); !it.Done(); it.Next(), ++i) {
    syntax_asdl::AssignPair* pair = it.Value();
    StackRoot _for(&pair  );
    lhs = pair->lhs;
    UP_lhs = lhs;
    switch (lhs->tag()) {
      case sh_lhs_e::Name: {
        sh_lhs::Name* lhs = static_cast<sh_lhs::Name*>(UP_lhs);
        this->cursor->PrintUntil(pair->left);
        this->cursor->SkipPast(pair->left);
        this->f->write(lhs->name);
        this->f->write(S_ryc);
        if (pair->rhs->tag() == rhs_word_e::Empty) {
          this->f->write(S_wvB);
        }
        else {
          this->DoRhsWord(pair->rhs, local_symbols);
        }
      }
        break;
      case sh_lhs_e::UnparsedIndex: {
        ;  // pass
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
    if (i != (n - 1)) {
      this->f->write(S_Cce);
    }
  }
}

void YshPrinter::_DoSimple(command::Simple* node, Dict<BigStr*, bool>* local_symbols) {
  syntax_asdl::word_t* first_word = nullptr;
  bool ok;
  BigStr* val = nullptr;
  bool quoted;
  syntax_asdl::Token* word0_tok = nullptr;
  syntax_asdl::word_t* word2 = nullptr;
  syntax_asdl::word_t* last_word = nullptr;
  syntax_asdl::Token* tok2 = nullptr;
  syntax_asdl::Token* rbrack_tok = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&local_symbols);
  StackRoot _root2(&first_word);
  StackRoot _root3(&val);
  StackRoot _root4(&word0_tok);
  StackRoot _root5(&word2);
  StackRoot _root6(&last_word);
  StackRoot _root7(&tok2);
  StackRoot _root8(&rbrack_tok);

  if (len(node->more_env)) {
    for (ListIter<syntax_asdl::EnvPair*> it(node->more_env); !it.Done(); it.Next()) {
      syntax_asdl::EnvPair* pair = it.Value();
      StackRoot _for(&pair    );
      this->DoRhsWord(pair->val, local_symbols);
    }
  }
  if (len(node->words)) {
    first_word = node->words->at(0);
    Tuple3<bool, BigStr*, bool> tup1 = word_::StaticEval(first_word);
    ok = tup1.at0();
    val = tup1.at1();
    quoted = tup1.at2();
    word0_tok = location::LeftTokenForWord(first_word);
    if ((ok and !quoted)) {
      if ((str_equals(val, S_Eax) and len(node->words) >= 3)) {
        word2 = node->words->at(-2);
        last_word = node->words->at(-1);
        Tuple3<bool, BigStr*, bool> tup2 = word_::StaticEval(last_word);
        ok = tup2.at0();
        val = tup2.at1();
        quoted = tup2.at2();
        if ((ok and (!quoted and str_equals(val, S_pcD)))) {
          this->cursor->PrintUntil(word0_tok);
          this->cursor->SkipPast(word0_tok);
          this->f->write(S_jvs);
          for (ListIter<syntax_asdl::word_t*> it(node->words->slice(1, -1)); !it.Done(); it.Next()) {
            syntax_asdl::word_t* w = it.Value();
            StackRoot _for(&w          );
            this->DoWordInCommand(w, local_symbols);
          }
          tok2 = location::RightTokenForWord(word2);
          rbrack_tok = location::LeftTokenForWord(last_word);
          this->cursor->PrintIncluding(tok2);
          this->cursor->SkipPast(rbrack_tok);
          return ;
        }
        else {
          throw Alloc<RuntimeError>(S_CCx);
        }
      }
      else {
        if (str_equals(val, S_Aru)) {
          this->cursor->PrintUntil(word0_tok);
          this->cursor->SkipPast(word0_tok);
          this->f->write(S_cmd);
          return ;
        }
      }
    }
  }
  for (ListIter<syntax_asdl::word_t*> it(node->words); !it.Done(); it.Next()) {
    syntax_asdl::word_t* w = it.Value();
    StackRoot _for(&w  );
    this->DoWordInCommand(w, local_symbols);
  }
}

void YshPrinter::DoCommand(syntax_asdl::command_t* node, Dict<BigStr*, bool>* local_symbols, bool at_top_level) {
  syntax_asdl::command_t* UP_node = nullptr;
  Dict<BigStr*, bool>* new_local_symbols = nullptr;
  syntax_asdl::command_t* UP_body = nullptr;
  syntax_asdl::for_iter_t* UP_iterable = nullptr;
  syntax_asdl::Token* body_tok = nullptr;
  syntax_asdl::List_of_command* commands = nullptr;
  command::Sentence* sentence = nullptr;
  int i;
  syntax_asdl::Token* elif_tok = nullptr;
  syntax_asdl::Token* then_tok = nullptr;
  syntax_asdl::condition_t* cond = nullptr;
  syntax_asdl::word_t* to_match = nullptr;
  syntax_asdl::SimpleVarSub* var_part = nullptr;
  syntax_asdl::CompoundWord* w = nullptr;
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::DoubleQuoted* dq_part = nullptr;
  syntax_asdl::word_part_t* dq_part0 = nullptr;
  bool missing_last_dsemi;
  StackRoot _root0(&node);
  StackRoot _root1(&local_symbols);
  StackRoot _root2(&UP_node);
  StackRoot _root3(&new_local_symbols);
  StackRoot _root4(&UP_body);
  StackRoot _root5(&UP_iterable);
  StackRoot _root6(&body_tok);
  StackRoot _root7(&commands);
  StackRoot _root8(&sentence);
  StackRoot _root9(&elif_tok);
  StackRoot _root10(&then_tok);
  StackRoot _root11(&cond);
  StackRoot _root12(&to_match);
  StackRoot _root13(&var_part);
  StackRoot _root14(&w);
  StackRoot _root15(&part0);
  StackRoot _root16(&dq_part);
  StackRoot _root17(&dq_part0);

  UP_node = node;
  switch (node->tag()) {
    case command_e::CommandList: {
      command::CommandList* node = static_cast<command::CommandList*>(UP_node);
      for (ListIter<syntax_asdl::command_t*> it(node->children); !it.Done(); it.Next()) {
        syntax_asdl::command_t* child = it.Value();
        StackRoot _for(&child      );
        this->DoCommand(child, local_symbols, at_top_level);
      }
    }
      break;
    case command_e::Redirect: {
      command::Redirect* node = static_cast<command::Redirect*>(UP_node);
      this->DoCommand(node->child, local_symbols, at_top_level);
      for (ListIter<syntax_asdl::Redir*> it(node->redirects); !it.Done(); it.Next()) {
        syntax_asdl::Redir* r = it.Value();
        StackRoot _for(&r      );
        this->DoRedirect(r, local_symbols);
      }
    }
      break;
    case command_e::Simple: {
      command::Simple* node = static_cast<command::Simple*>(UP_node);
      this->_DoSimple(node, local_symbols);
    }
      break;
    case command_e::ShAssignment: {
      command::ShAssignment* node = static_cast<command::ShAssignment*>(UP_node);
      this->DoShAssignment(node, at_top_level, local_symbols);
    }
      break;
    case command_e::Pipeline: {
      command::Pipeline* node = static_cast<command::Pipeline*>(UP_node);
      for (ListIter<syntax_asdl::command_t*> it(node->children); !it.Done(); it.Next()) {
        syntax_asdl::command_t* child = it.Value();
        StackRoot _for(&child      );
        this->DoCommand(child, local_symbols);
      }
    }
      break;
    case command_e::AndOr: {
      command::AndOr* node = static_cast<command::AndOr*>(UP_node);
      for (ListIter<syntax_asdl::command_t*> it(node->children); !it.Done(); it.Next()) {
        syntax_asdl::command_t* child = it.Value();
        StackRoot _for(&child      );
        this->DoCommand(child, local_symbols);
      }
    }
      break;
    case command_e::Sentence: {
      command::Sentence* node = static_cast<command::Sentence*>(UP_node);
      this->DoCommand(node->child, local_symbols);
    }
      break;
    case command_e::BraceGroup: {
      BraceGroup* node = static_cast<BraceGroup*>(UP_node);
      this->cursor->PrintUntil(node->left);
      this->cursor->SkipPast(node->left);
      this->f->write(S_rrt);
      for (ListIter<syntax_asdl::command_t*> it(node->children); !it.Done(); it.Next()) {
        syntax_asdl::command_t* child = it.Value();
        StackRoot _for(&child      );
        this->DoCommand(child, local_symbols);
      }
    }
      break;
    case command_e::Subshell: {
      command::Subshell* node = static_cast<command::Subshell*>(UP_node);
      this->cursor->PrintUntil(node->left);
      this->cursor->SkipPast(node->left);
      this->f->write(S_ozu);
      this->DoCommand(node->child, local_symbols);
      this->cursor->PrintUntil(node->right);
      this->cursor->SkipPast(node->right);
      this->f->write(S_cEn);
    }
      break;
    case command_e::ShFunction: {
      command::ShFunction* node = static_cast<command::ShFunction*>(UP_node);
      new_local_symbols = Alloc<Dict<BigStr*, bool>>();
      if (node->keyword) {
        this->cursor->PrintUntil(node->keyword);
      }
      else {
        this->cursor->PrintUntil(node->name_tok);
      }
      this->f->write(StrFormat("proc %s ", node->name));
      UP_body = node->body;
      switch (UP_body->tag()) {
        case command_e::BraceGroup: {
          BraceGroup* body = static_cast<BraceGroup*>(UP_body);
          this->cursor->SkipUntil(body->left);
          for (ListIter<syntax_asdl::command_t*> it(body->children); !it.Done(); it.Next()) {
            syntax_asdl::command_t* child = it.Value();
            StackRoot _for(&child          );
            this->DoCommand(child, new_local_symbols);
          }
        }
          break;
        default: {
          ;  // pass
        }
      }
    }
      break;
    case command_e::DoGroup: {
      command::DoGroup* node = static_cast<command::DoGroup*>(UP_node);
      this->cursor->PrintUntil(node->left);
      this->cursor->SkipPast(node->left);
      this->f->write(S_ato);
      for (ListIter<syntax_asdl::command_t*> it(node->children); !it.Done(); it.Next()) {
        syntax_asdl::command_t* child = it.Value();
        StackRoot _for(&child      );
        this->DoCommand(child, local_symbols);
      }
      this->cursor->PrintUntil(node->right);
      this->cursor->SkipPast(node->right);
      this->f->write(S_cEn);
    }
      break;
    case command_e::ForEach: {
      command::ForEach* node = static_cast<command::ForEach*>(UP_node);
      UP_iterable = node->iterable;
      switch (node->iterable->tag()) {
        case for_iter_e::Args: {
          this->f->write(StrFormat("for %s in @ARGV ", node->iter_names->at(0)));
          body_tok = location::TokenForCommand(node->body);
          this->cursor->SkipUntil(body_tok);
        }
          break;
        case for_iter_e::Words: {
          ;  // pass
        }
          break;
        case for_iter_e::YshExpr: {
          ;  // pass
        }
          break;
      }
      if (node->semi_tok != nullptr) {
        this->cursor->PrintUntil(node->semi_tok);
        this->cursor->SkipPast(node->semi_tok);
      }
      this->DoCommand(node->body, local_symbols);
    }
      break;
    case command_e::WhileUntil: {
      command::WhileUntil* node = static_cast<command::WhileUntil*>(UP_node);
      if (node->keyword->id == Id::KW_Until) {
        this->cursor->PrintUntil(node->keyword);
        this->cursor->SkipPast(node->keyword);
        this->f->write(S_pii);
      }
      if (node->cond->tag() == condition_e::Shell) {
        commands = static_cast<List_of_command*>(node->cond);
        if ((len(commands) == 1 and commands->at(0)->tag() == command_e::Sentence)) {
          sentence = static_cast<command::Sentence*>(commands->at(0));
          this->DoCommand(sentence->child, local_symbols);
          this->cursor->SkipPast(sentence->terminator);
        }
      }
      this->DoCommand(node->body, local_symbols);
    }
      break;
    case command_e::If: {
      command::If* node = static_cast<command::If*>(UP_node);
      i = 0;
      for (ListIter<syntax_asdl::IfArm*> it(node->arms); !it.Done(); it.Next(), ++i) {
        syntax_asdl::IfArm* arm = it.Value();
        StackRoot _for(&arm      );
        elif_tok = arm->keyword;
        then_tok = arm->then_tok;
        if (i != 0) {
          this->cursor->PrintUntil(elif_tok);
          this->f->write(S_ior);
        }
        cond = arm->cond;
        if (cond->tag() == condition_e::Shell) {
          commands = static_cast<List_of_command*>(cond);
          if ((len(commands) == 1 and commands->at(0)->tag() == command_e::Sentence)) {
            sentence = static_cast<command::Sentence*>(commands->at(0));
            this->DoCommand(sentence, local_symbols);
            this->cursor->PrintUntil(sentence->terminator);
            this->cursor->SkipPast(sentence->terminator);
          }
          else {
            for (ListIter<syntax_asdl::command_t*> it(commands); !it.Done(); it.Next()) {
              syntax_asdl::command_t* child = it.Value();
              StackRoot _for(&child            );
              this->DoCommand(child, local_symbols);
            }
          }
        }
        this->cursor->PrintUntil(then_tok);
        this->cursor->SkipPast(then_tok);
        this->f->write(S_ato);
        for (ListIter<syntax_asdl::command_t*> it(arm->action); !it.Done(); it.Next()) {
          syntax_asdl::command_t* child = it.Value();
          StackRoot _for(&child        );
          this->DoCommand(child, local_symbols);
        }
      }
      if (len(node->else_action)) {
        this->cursor->PrintUntil(node->else_kw);
        this->f->write(S_ior);
        this->cursor->PrintIncluding(node->else_kw);
        this->f->write(S_iCo);
        for (ListIter<syntax_asdl::command_t*> it(node->else_action); !it.Done(); it.Next()) {
          syntax_asdl::command_t* child = it.Value();
          StackRoot _for(&child        );
          this->DoCommand(child, local_symbols);
        }
      }
      this->cursor->PrintUntil(node->fi_kw);
      this->cursor->SkipPast(node->fi_kw);
      this->f->write(S_cEn);
    }
      break;
    case command_e::Case: {
      command::Case* node = static_cast<command::Case*>(UP_node);
      to_match = nullptr;
      switch (node->to_match->tag()) {
        case case_arg_e::YshExpr: {
          return ;
        }
          break;
        case case_arg_e::Word: {
          to_match = static_cast<case_arg::Word*>(node->to_match)->w;
        }
          break;
        default: {
          assert(0);  // AssertionError
        }
      }
      this->cursor->PrintIncluding(node->case_kw);
      var_part = nullptr;
      switch (to_match->tag()) {
        case word_e::Compound: {
          w = static_cast<CompoundWord*>(to_match);
          part0 = w->parts->at(0);
          switch (part0->tag()) {
            case word_part_e::SimpleVarSub: {
              var_part = static_cast<SimpleVarSub*>(part0);
            }
              break;
            case word_part_e::DoubleQuoted: {
              dq_part = static_cast<DoubleQuoted*>(part0);
              if (len(dq_part->parts) == 1) {
                dq_part0 = dq_part->parts->at(0);
                switch (dq_part0->tag()) {
                  case word_part_e::SimpleVarSub: {
                    var_part = static_cast<SimpleVarSub*>(dq_part0);
                  }
                    break;
                }
              }
            }
              break;
          }
        }
          break;
      }
      if (var_part) {
        this->f->write(S_sge);
        this->f->write(lexer::LazyStr(var_part->tok));
        this->f->write(S_Ezk);
      }
      this->cursor->SkipPast(node->arms_start);
      this->f->write(S_ato);
      missing_last_dsemi = false;
      for (ListIter<syntax_asdl::CaseArm*> it(node->arms); !it.Done(); it.Next()) {
        syntax_asdl::CaseArm* case_arm = it.Value();
        StackRoot _for(&case_arm      );
        this->cursor->PrintUntil(case_arm->middle);
        this->f->write(S_iCo);
        this->cursor->SkipPast(case_arm->middle);
        for (ListIter<syntax_asdl::command_t*> it(case_arm->action); !it.Done(); it.Next()) {
          syntax_asdl::command_t* child = it.Value();
          StackRoot _for(&child        );
          this->DoCommand(child, local_symbols);
        }
        if (case_arm->right) {
          this->cursor->PrintUntil(case_arm->right);
          this->f->write(S_cEn);
          this->cursor->SkipPast(case_arm->right);
        }
        else {
          missing_last_dsemi = true;
        }
      }
      this->cursor->PrintUntil(node->arms_end);
      if (missing_last_dsemi) {
        this->f->write(S_tve);
      }
      this->cursor->SkipPast(node->arms_end);
      this->f->write(S_cEn);
    }
      break;
    case command_e::TimeBlock: {
      command::TimeBlock* node = static_cast<command::TimeBlock*>(UP_node);
      this->DoCommand(node->pipeline, local_symbols);
    }
      break;
    case command_e::DParen: {
      command::DParen* node = static_cast<command::DParen*>(UP_node);
      ;  // pass
    }
      break;
    case command_e::DBracket: {
      command::DBracket* node = static_cast<command::DBracket*>(UP_node);
      ;  // pass
    }
      break;
    default: {
      ;  // pass
    }
  }
}

void YshPrinter::DoRhsWord(syntax_asdl::rhs_word_t* node, Dict<BigStr*, bool>* local_symbols) {
  syntax_asdl::rhs_word_t* UP_node = nullptr;
  runtime_asdl::word_style_t style;
  StackRoot _root0(&node);
  StackRoot _root1(&local_symbols);
  StackRoot _root2(&UP_node);

  UP_node = node;
  switch (node->tag()) {
    case rhs_word_e::Empty: {
      this->f->write(S_wvB);
    }
      break;
    case rhs_word_e::Compound: {
      CompoundWord* node = static_cast<CompoundWord*>(UP_node);
      style = _GetRhsStyle(node);
      if (style == word_style_e::SQ) {
        this->f->write(S_Bfw);
        this->DoWordInCommand(node, local_symbols);
        this->f->write(S_Bfw);
      }
      else {
        if (style == word_style_e::DQ) {
          this->f->write(S_krt);
          this->DoWordInCommand(node, local_symbols);
          this->f->write(S_krt);
        }
        else {
          if (word_::IsVarSub(node)) {
            ;  // pass
          }
          this->DoWordInCommand(node, local_symbols);
        }
      }
    }
      break;
  }
}

void YshPrinter::DoWordInCommand(syntax_asdl::word_t* node, Dict<BigStr*, bool>* local_symbols) {
  syntax_asdl::word_t* UP_node = nullptr;
  syntax_asdl::DoubleQuoted* dq_part = nullptr;
  syntax_asdl::word_part_t* part0 = nullptr;
  syntax_asdl::SimpleVarSub* vsub_part = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&local_symbols);
  StackRoot _root2(&UP_node);
  StackRoot _root3(&dq_part);
  StackRoot _root4(&part0);
  StackRoot _root5(&vsub_part);

  UP_node = node;
  switch (node->tag()) {
    case word_e::Compound: {
      CompoundWord* node = static_cast<CompoundWord*>(UP_node);
      if ((len(node->parts) == 1 and node->parts->at(0)->tag() == word_part_e::DoubleQuoted)) {
        dq_part = static_cast<DoubleQuoted*>(node->parts->at(0));
        if (len(dq_part->parts) == 1) {
          part0 = dq_part->parts->at(0);
          if (part0->tag() == word_part_e::SimpleVarSub) {
            vsub_part = static_cast<SimpleVarSub*>(dq_part->parts->at(0));
            if (vsub_part->tok->id == Id::VSub_At) {
              this->cursor->PrintUntil(dq_part->left);
              this->cursor->SkipPast(dq_part->right);
              this->f->write(S_Fyz);
              return ;
            }
            if ((vsub_part->tok->id == Id::VSub_Number || vsub_part->tok->id == Id::VSub_DollarName)) {
              this->cursor->PrintUntil(dq_part->left);
              this->cursor->SkipPast(dq_part->right);
              this->f->write(lexer::TokenVal(vsub_part->tok));
              return ;
            }
          }
          else {
            if (part0->tag() == word_part_e::BracedVarSub) {
              this->cursor->PrintUntil(dq_part->left);
              this->cursor->SkipPast(dq_part->left);
              this->DoWordPart(part0, local_symbols);
              this->cursor->SkipPast(dq_part->right);
              return ;
            }
            else {
              if (part0->tag() == word_part_e::CommandSub) {
                this->cursor->PrintUntil(dq_part->left);
                this->cursor->SkipPast(dq_part->left);
                this->DoWordPart(part0, local_symbols);
                this->cursor->SkipPast(dq_part->right);
                return ;
              }
            }
          }
        }
      }
      for (ListIter<syntax_asdl::word_part_t*> it(node->parts); !it.Done(); it.Next()) {
        syntax_asdl::word_part_t* part = it.Value();
        StackRoot _for(&part      );
        this->DoWordPart(part, local_symbols);
      }
    }
      break;
    case word_e::BracedTree: {
      ;  // pass
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void YshPrinter::DoWordPart(syntax_asdl::word_part_t* node, Dict<BigStr*, bool>* local_symbols, bool quoted) {
  syntax_asdl::Token* left_tok = nullptr;
  syntax_asdl::word_part_t* UP_node = nullptr;
  syntax_asdl::Token* t = nullptr;
  BigStr* val = nullptr;
  int op_id;
  StackRoot _root0(&node);
  StackRoot _root1(&local_symbols);
  StackRoot _root2(&left_tok);
  StackRoot _root3(&UP_node);
  StackRoot _root4(&t);
  StackRoot _root5(&val);

  left_tok = location::LeftTokenForWordPart(node);
  if (left_tok) {
    this->cursor->PrintUntil(left_tok);
  }
  UP_node = node;
  switch (node->tag()) {
    case word_part_e::ShArrayLiteral: 
    case word_part_e::BashAssocLiteral: 
    case word_part_e::TildeSub: 
    case word_part_e::ExtGlob: {
      ;  // pass
    }
      break;
    case word_part_e::EscapedLiteral: {
      word_part::EscapedLiteral* node = static_cast<word_part::EscapedLiteral*>(UP_node);
      if (quoted) {
        ;  // pass
      }
      else {
        t = node->token;
        val = lexer::TokenSliceLeft(t, 1);
        if (!(str_equals(val, S_nfs))) {
          this->cursor->PrintUntil(t);
          this->cursor->SkipPast(t);
          this->f->write(StrFormat("'%s'", val));
        }
      }
    }
      break;
    case word_part_e::Literal: {
      Token* node = static_cast<Token*>(UP_node);
      this->cursor->PrintIncluding(node);
    }
      break;
    case word_part_e::SingleQuoted: {
      SingleQuoted* node = static_cast<SingleQuoted*>(UP_node);
      this->cursor->PrintUntil(node->right);
    }
      break;
    case word_part_e::DoubleQuoted: {
      DoubleQuoted* node = static_cast<DoubleQuoted*>(UP_node);
      for (ListIter<syntax_asdl::word_part_t*> it(node->parts); !it.Done(); it.Next()) {
        syntax_asdl::word_part_t* part = it.Value();
        StackRoot _for(&part      );
        this->DoWordPart(part, local_symbols, true);
      }
    }
      break;
    case word_part_e::SimpleVarSub: {
      SimpleVarSub* node = static_cast<SimpleVarSub*>(UP_node);
      op_id = node->tok->id;
      if (op_id == Id::VSub_DollarName) {
        this->cursor->PrintIncluding(node->tok);
      }
      else {
        if (op_id == Id::VSub_Number) {
          this->cursor->PrintIncluding(node->tok);
        }
        else {
          if (op_id == Id::VSub_At) {
            this->f->write(S_jrh);
            this->cursor->SkipPast(node->tok);
          }
          else {
            if (op_id == Id::VSub_Star) {
              this->f->write(S_jrh);
              this->cursor->SkipPast(node->tok);
            }
            else {
              if (op_id == Id::VSub_Pound) {
                this->f->write(S_igC);
                this->cursor->SkipPast(node->tok);
              }
              else {
                ;  // pass
              }
            }
          }
        }
      }
    }
      break;
    case word_part_e::BracedVarSub: {
      BracedVarSub* node = static_cast<BracedVarSub*>(UP_node);
      this->cursor->PrintUntil(node->left);
      if (node->bracket_op) {
        ;  // pass
      }
      if (node->prefix_op) {
        ;  // pass
      }
      if (node->suffix_op) {
        ;  // pass
      }
      op_id = node->name_tok->id;
      if (op_id == Id::VSub_QMark) {
        this->cursor->PrintIncluding(node->name_tok);
      }
      this->cursor->PrintIncluding(node->right);
    }
      break;
    case word_part_e::CommandSub: {
      CommandSub* node = static_cast<CommandSub*>(UP_node);
      if (node->left_token->id == Id::Left_Backtick) {
        this->cursor->PrintUntil(node->left_token);
        this->f->write(S_eaw);
        this->cursor->SkipPast(node->left_token);
        this->DoCommand(node->child, local_symbols);
        this->cursor->SkipPast(node->right);
        this->f->write(S_hxb);
      }
      else {
        this->cursor->PrintIncluding(node->right);
      }
    }
      break;
    default: {
      ;  // pass
    }
  }
}

}  // define namespace ysh_ify

namespace expr_eval {  // define

using id_kind_asdl::Id;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::re;
using syntax_asdl::re_e;
using syntax_asdl::re_t;
using syntax_asdl::Token;
using syntax_asdl::SimpleVarSub;
using syntax_asdl::word_part;
using syntax_asdl::SingleQuoted;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::BracedVarSub;
using syntax_asdl::ShArrayLiteral;
using syntax_asdl::CommandSub;
using syntax_asdl::expr;
using syntax_asdl::expr_e;
using syntax_asdl::expr_t;
using syntax_asdl::y_lhs_e;
using syntax_asdl::y_lhs_t;
using syntax_asdl::Attribute;
using syntax_asdl::Subscript;
using syntax_asdl::class_literal_term;
using syntax_asdl::class_literal_term_e;
using syntax_asdl::class_literal_term_t;
using syntax_asdl::char_class_term_t;
using syntax_asdl::PosixClass;
using syntax_asdl::PerlClass;
using syntax_asdl::CharCode;
using syntax_asdl::CharRange;
using syntax_asdl::ArgList;
using syntax_asdl::Eggex;
using runtime_asdl::coerced_e;
using runtime_asdl::coerced_t;
using runtime_asdl::scope_e;
using runtime_asdl::scope_t;
using runtime_asdl::part_value;
using runtime_asdl::part_value_t;
using runtime_asdl::Piece;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::y_lvalue;
using value_asdl::y_lvalue_e;
using value_asdl::y_lvalue_t;
using value_asdl::IntBox;
using value_asdl::LeftName;
using value_asdl::Obj;
using value_asdl::cmd_frag;
using error::e_die;
using error::e_die_status;
using mylib::print_stderr;

value_asdl::value_t* LookupVar(state::Mem* mem, BigStr* var_name, runtime_asdl::scope_t which_scopes, syntax_asdl::loc_t* var_loc) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&var_name);
  StackRoot _root2(&var_loc);
  StackRoot _root3(&val);

  val = mem->GetValue(var_name, which_scopes);
  if (val->tag() == value_e::Undef) {
    e_die(StrFormat("Undefined variable %r", var_name), var_loc);
  }
  return val;
}

mops::BigInt _ConvertToInt(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_val = nullptr;
  BigStr* s = nullptr;
  bool ok;
  mops::BigInt i;
  StackRoot _root0(&val);
  StackRoot _root1(&msg);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_val);
  StackRoot _root4(&s);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Int: {
      value::Int* val = static_cast<value::Int*>(UP_val);
      return val->i;
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      if (match::LooksLikeYshInt(val->s)) {
        s = val->s->replace(S_tci, S_Aoo);
        Tuple2<bool, mops::BigInt> tup0 = mops::FromStr2(s);
        ok = tup0.at0();
        i = tup0.at1();
        if (!ok) {
          e_die(StrFormat("Integer too big: %s", s), blame_loc);
        }
        return i;
      }
    }
      break;
  }
  throw Alloc<error::TypeErr>(val, msg, blame_loc);
}

Tuple3<runtime_asdl::coerced_t, mops::BigInt, double> _ConvertToNumber(value_asdl::value_t* val) {
  value_asdl::value_t* UP_val = nullptr;
  BigStr* s = nullptr;
  bool ok;
  mops::BigInt i;
  StackRoot _root0(&val);
  StackRoot _root1(&UP_val);
  StackRoot _root2(&s);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Int: {
      value::Int* val = static_cast<value::Int*>(UP_val);
      return Tuple3<runtime_asdl::coerced_t, mops::BigInt, double>(coerced_e::Int, val->i, -1.0);
    }
      break;
    case value_e::Float: {
      value::Float* val = static_cast<value::Float*>(UP_val);
      return Tuple3<runtime_asdl::coerced_t, mops::BigInt, double>(coerced_e::Float, mops::MINUS_ONE, val->f);
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      if (match::LooksLikeYshInt(val->s)) {
        s = val->s->replace(S_tci, S_Aoo);
        Tuple2<bool, mops::BigInt> tup1 = mops::FromStr2(s);
        ok = tup1.at0();
        i = tup1.at1();
        if (!ok) {
          e_die(StrFormat("Integer too big: %s", s), loc::Missing);
        }
        return Tuple3<runtime_asdl::coerced_t, mops::BigInt, double>(coerced_e::Int, i, -1.0);
      }
      if (match::LooksLikeYshFloat(val->s)) {
        s = val->s->replace(S_tci, S_Aoo);
        return Tuple3<runtime_asdl::coerced_t, mops::BigInt, double>(coerced_e::Float, mops::MINUS_ONE, to_float(s));
      }
    }
      break;
  }
  return Tuple3<runtime_asdl::coerced_t, mops::BigInt, double>(coerced_e::Neither, mops::MINUS_ONE, -1.0);
}

Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double> _ConvertForBinaryOp(value_asdl::value_t* left, value_asdl::value_t* right) {
  runtime_asdl::coerced_t c1;
  mops::BigInt i1;
  double f1;
  runtime_asdl::coerced_t c2;
  mops::BigInt i2;
  double f2;
  mops::BigInt nope;
  StackRoot _root0(&left);
  StackRoot _root1(&right);

  Tuple3<runtime_asdl::coerced_t, mops::BigInt, double> tup2 = _ConvertToNumber(left);
  c1 = tup2.at0();
  i1 = tup2.at1();
  f1 = tup2.at2();
  Tuple3<runtime_asdl::coerced_t, mops::BigInt, double> tup3 = _ConvertToNumber(right);
  c2 = tup3.at0();
  i2 = tup3.at1();
  f2 = tup3.at2();
  nope = mops::MINUS_ONE;
  if ((c1 == coerced_e::Int and c2 == coerced_e::Int)) {
    return Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double>(coerced_e::Int, i1, i2, -1.0, -1.0);
  }
  else {
    if ((c1 == coerced_e::Int and c2 == coerced_e::Float)) {
      return Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double>(coerced_e::Float, nope, nope, mops::ToFloat(i1), f2);
    }
    else {
      if ((c1 == coerced_e::Float and c2 == coerced_e::Int)) {
        return Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double>(coerced_e::Float, nope, nope, f1, mops::ToFloat(i2));
      }
      else {
        if ((c1 == coerced_e::Float and c2 == coerced_e::Float)) {
          return Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double>(coerced_e::Float, nope, nope, f1, f2);
        }
        else {
          return Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double>(coerced_e::Neither, nope, nope, -1.0, -1.0);
        }
      }
    }
  }
}

ExprEvaluator::ExprEvaluator(state::Mem* mem, state::MutableOpts* mutable_opts, Dict<int, Dict<BigStr*, vm::_Callable*>*>* methods, split::SplitContext* splitter, ui::ErrorFormatter* errfmt) {
  this->shell_ex = nullptr;
  this->cmd_ev = nullptr;
  this->word_ev = nullptr;
  this->mem = mem;
  this->mutable_opts = mutable_opts;
  this->methods = methods;
  this->splitter = splitter;
  this->errfmt = errfmt;
}

void ExprEvaluator::CheckCircularDeps() {
}

value_asdl::value_t* ExprEvaluator::_LookupVar(BigStr* name, syntax_asdl::loc_t* var_loc) {
  StackRoot _root0(&name);
  StackRoot _root1(&var_loc);

  return LookupVar(this->mem, name, scope_e::LocalOrGlobal, var_loc);
}

void ExprEvaluator::EvalAugmented(value_asdl::y_lvalue_t* lval, value_asdl::value_t* rhs_val, syntax_asdl::Token* op, runtime_asdl::scope_t which_scopes) {
  value_asdl::y_lvalue_t* UP_lval = nullptr;
  value_asdl::value_t* lhs_val = nullptr;
  value_asdl::value_t* new_val = nullptr;
  value_asdl::value_t* obj = nullptr;
  value_asdl::value_t* UP_obj = nullptr;
  value_asdl::value_t* lhs_val_ = nullptr;
  mops::BigInt i1;
  int index;
  BigStr* key = nullptr;
  value_asdl::value_t* new_val_ = nullptr;
  StackRoot _root0(&lval);
  StackRoot _root1(&rhs_val);
  StackRoot _root2(&op);
  StackRoot _root3(&UP_lval);
  StackRoot _root4(&lhs_val);
  StackRoot _root5(&new_val);
  StackRoot _root6(&obj);
  StackRoot _root7(&UP_obj);
  StackRoot _root8(&lhs_val_);
  StackRoot _root9(&key);
  StackRoot _root10(&new_val_);

  UP_lval = lval;
  switch (lval->tag()) {
    case y_lvalue_e::Local: {
      LeftName* lval = static_cast<LeftName*>(UP_lval);
      lhs_val = this->_LookupVar(lval->name, lval->blame_loc);
      if ((op->id == Id::Arith_PlusEqual || op->id == Id::Arith_MinusEqual || op->id == Id::Arith_StarEqual || op->id == Id::Arith_SlashEqual)) {
        new_val = this->_ArithIntFloat(lhs_val, rhs_val, op);
      }
      else {
        new_val = this->_ArithIntOnly(lhs_val, rhs_val, op);
      }
      this->mem->SetNamed(lval, new_val, which_scopes);
    }
      break;
    case y_lvalue_e::Container: {
      y_lvalue::Container* lval = static_cast<y_lvalue::Container*>(UP_lval);
      obj = lval->obj;
      UP_obj = obj;
      lhs_val_ = nullptr;
      switch (obj->tag()) {
        case value_e::List: {
          value::List* obj = static_cast<value::List*>(UP_obj);
          i1 = _ConvertToInt(lval->index, S_cfe, loc::Missing);
          index = mops::BigTruncate(i1);
          try {
            lhs_val_ = obj->items->at(index);
          }
          catch (IndexError*) {
            throw Alloc<error::Expr>(StrFormat("List index out of range: %d", index), loc::Missing);
          }
        }
          break;
        case value_e::Dict: {
          value::Dict* obj = static_cast<value::Dict*>(UP_obj);
          index = -1;
          key = val_ops::ToStr(lval->index, S_ldu, loc::Missing);
          try {
            lhs_val_ = obj->d->at(key);
          }
          catch (KeyError*) {
            throw Alloc<error::Expr>(StrFormat("Dict key not found: %r", key), loc::Missing);
          }
        }
          break;
        case value_e::Obj: {
          Obj* obj = static_cast<Obj*>(UP_obj);
          index = -1;
          key = val_ops::ToStr(lval->index, S_uny, loc::Missing);
          try {
            lhs_val_ = obj->d->at(key);
          }
          catch (KeyError*) {
            throw Alloc<error::Expr>(StrFormat("Obj attribute not found: %r", key), loc::Missing);
          }
        }
          break;
        default: {
          throw Alloc<error::TypeErr>(obj, S_Bww, loc::Missing);
        }
      }
      if ((op->id == Id::Arith_PlusEqual || op->id == Id::Arith_MinusEqual || op->id == Id::Arith_StarEqual || op->id == Id::Arith_SlashEqual)) {
        new_val_ = this->_ArithIntFloat(lhs_val_, rhs_val, op);
      }
      else {
        new_val_ = this->_ArithIntOnly(lhs_val_, rhs_val, op);
      }
      switch (obj->tag()) {
        case value_e::List: {
          value::List* obj = static_cast<value::List*>(UP_obj);
          obj->items->set(index, new_val_);
        }
          break;
        case value_e::Dict: {
          value::Dict* obj = static_cast<value::Dict*>(UP_obj);
          obj->d->set(key, new_val_);
        }
          break;
        case value_e::Obj: {
          Obj* obj = static_cast<Obj*>(UP_obj);
          obj->d->set(key, new_val_);
        }
          break;
        default: {
          assert(0);  // AssertionError
        }
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

value_asdl::value_t* ExprEvaluator::_EvalLeftLocalOrGlobal(syntax_asdl::expr_t* lhs, runtime_asdl::scope_t which_scopes) {
  syntax_asdl::expr_t* UP_lhs = nullptr;
  value_asdl::value_t* obj = nullptr;
  value_asdl::value_t* index = nullptr;
  StackRoot _root0(&lhs);
  StackRoot _root1(&UP_lhs);
  StackRoot _root2(&obj);
  StackRoot _root3(&index);

  UP_lhs = lhs;
  switch (lhs->tag()) {
    case expr_e::Var: {
      expr::Var* lhs = static_cast<expr::Var*>(UP_lhs);
      return LookupVar(this->mem, lhs->name, which_scopes, lhs->left);
    }
      break;
    case expr_e::Subscript: {
      Subscript* lhs = static_cast<Subscript*>(UP_lhs);
      obj = this->_EvalLeftLocalOrGlobal(lhs->obj, which_scopes);
      index = this->_EvalExpr(lhs->index);
      return this->_EvalSubscript(obj, index, lhs->left);
    }
      break;
    case expr_e::Attribute: {
      Attribute* lhs = static_cast<Attribute*>(UP_lhs);
      obj = this->_EvalLeftLocalOrGlobal(lhs->obj, which_scopes);
      return this->_EvalDot(lhs, obj);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

value_asdl::y_lvalue_t* ExprEvaluator::_EvalLhsExpr(syntax_asdl::y_lhs_t* lhs, runtime_asdl::scope_t which_scopes) {
  syntax_asdl::y_lhs_t* UP_lhs = nullptr;
  value_asdl::value_t* lval = nullptr;
  value_asdl::value_t* index = nullptr;
  value::Str* attr = nullptr;
  StackRoot _root0(&lhs);
  StackRoot _root1(&UP_lhs);
  StackRoot _root2(&lval);
  StackRoot _root3(&index);
  StackRoot _root4(&attr);

  UP_lhs = lhs;
  switch (lhs->tag()) {
    case y_lhs_e::Var: {
      Token* lhs = static_cast<Token*>(UP_lhs);
      return Alloc<LeftName>(lexer::LazyStr(lhs), lhs);
    }
      break;
    case y_lhs_e::Subscript: {
      Subscript* lhs = static_cast<Subscript*>(UP_lhs);
      lval = this->_EvalLeftLocalOrGlobal(lhs->obj, which_scopes);
      index = this->_EvalExpr(lhs->index);
      return Alloc<y_lvalue::Container>(lval, index);
    }
      break;
    case y_lhs_e::Attribute: {
      Attribute* lhs = static_cast<Attribute*>(UP_lhs);
      lval = this->_EvalLeftLocalOrGlobal(lhs->obj, which_scopes);
      attr = Alloc<value::Str>(lhs->attr_name);
      return Alloc<y_lvalue::Container>(lval, attr);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

value_asdl::value_t* ExprEvaluator::EvalExprClosure(value::Expr* expr_val, syntax_asdl::loc_t* blame_loc) {
  StackRoot _root0(&expr_val);
  StackRoot _root1(&blame_loc);

  {  // with
    state::ctx_EnclosedFrame ctx{this->mem, expr_val->captured_frame, expr_val->module_frame, nullptr};

    return this->EvalExpr(expr_val->e, blame_loc);
  }
}

value_asdl::value_t* ExprEvaluator::EvalExpr(syntax_asdl::expr_t* node, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&val);

  this->mem->SetLocationForExpr(blame_loc);
  {  // with
    state::ctx_YshExpr ctx{this->mutable_opts};

    val = this->_EvalExpr(node);
  }
  return val;
}

value_asdl::y_lvalue_t* ExprEvaluator::EvalLhsExpr(syntax_asdl::y_lhs_t* lhs, runtime_asdl::scope_t which_scopes) {
  value_asdl::y_lvalue_t* lval = nullptr;
  StackRoot _root0(&lhs);
  StackRoot _root1(&lval);

  {  // with
    state::ctx_YshExpr ctx{this->mutable_opts};

    lval = this->_EvalLhsExpr(lhs, which_scopes);
  }
  return lval;
}

runtime_asdl::part_value_t* ExprEvaluator::EvalExprSub(word_part::ExprSub* part) {
  value_asdl::value_t* val = nullptr;
  BigStr* s = nullptr;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&part);
  StackRoot _root1(&val);
  StackRoot _root2(&s);
  StackRoot _root3(&strs);

  val = this->EvalExpr(part->child, part->left);
  switch (part->left->id) {
    case Id::Left_DollarBracket: {
      s = val_ops::Stringify(val, Alloc<loc::WordPart>(part), S_wjw);
      return Alloc<Piece>(s, false, false);
    }
      break;
    case Id::Lit_AtLBracket: {
      strs = val_ops::ToShellArray(val, Alloc<loc::WordPart>(part), S_fcy);
      return Alloc<part_value::Array>(strs);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

value_asdl::value_t* ExprEvaluator::PluginCall(value::Func* func_val, List<value_asdl::value_t*>* pos_args) {
  Dict<BigStr*, value_asdl::value_t*>* named_args = nullptr;
  syntax_asdl::ArgList* arg_list = nullptr;
  typed_args::Reader* rd = nullptr;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&func_val);
  StackRoot _root1(&pos_args);
  StackRoot _root2(&named_args);
  StackRoot _root3(&arg_list);
  StackRoot _root4(&rd);
  StackRoot _root5(&val);

  {  // with
    state::ctx_YshExpr ctx{this->mutable_opts};

    {  // with
      state::ctx_Registers ctx{this->mem};

      named_args = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      arg_list = ArgList::CreateNull();
      rd = Alloc<typed_args::Reader>(pos_args, named_args, nullptr, arg_list);
      try {
        val = func_proc::CallUserFunc(func_val, rd, this->mem, this->cmd_ev);
      }
      catch (error::FatalRuntime* e) {
        val = Alloc<value::Str>(StrFormat("<Runtime error: %s>", e->UserErrorString()));
      }
      catch (IOError_OSError* e) {
        val = Alloc<value::Str>(StrFormat("<I/O error: %s>", pyutil::strerror(e)));
      }
      catch (KeyboardInterrupt*) {
        val = Alloc<value::Str>(S_uur);
      }
    }
  }
  return val;
}

value_asdl::value_t* ExprEvaluator::CallConvertFunc(value_asdl::value_t* func_val, value_asdl::value_t* arg, syntax_asdl::Token* convert_tok, syntax_asdl::loc_t* call_loc) {
  List<value_asdl::value_t*>* pos_args = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_args = nullptr;
  syntax_asdl::ArgList* arg_list = nullptr;
  typed_args::Reader* rd = nullptr;
  value_asdl::value_t* val = nullptr;
  BigStr* func_name = nullptr;
  StackRoot _root0(&func_val);
  StackRoot _root1(&arg);
  StackRoot _root2(&convert_tok);
  StackRoot _root3(&call_loc);
  StackRoot _root4(&pos_args);
  StackRoot _root5(&named_args);
  StackRoot _root6(&arg_list);
  StackRoot _root7(&rd);
  StackRoot _root8(&val);
  StackRoot _root9(&func_name);

  {  // with
    state::ctx_YshExpr ctx{this->mutable_opts};

    pos_args = NewList<value_asdl::value_t*>(std::initializer_list<value_asdl::value_t*>{arg});
    named_args = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
    arg_list = ArgList::CreateNull();
    rd = Alloc<typed_args::Reader>(pos_args, named_args, nullptr, arg_list);
    rd->SetFallbackLocation(convert_tok);
    try {
      val = this->_CallFunc(func_val, rd);
    }
    catch (error::FatalRuntime* e) {
      func_name = lexer::TokenVal(convert_tok);
      this->errfmt->Print_(StrFormat("Fatal error calling Eggex conversion func %r from this Match accessor", func_name), call_loc);
      print_stderr(S_Aoo);
      throw;
    }
  }
  return val;
}

value_asdl::value_t* ExprEvaluator::_CallMetaMethod(value_asdl::value_t* func_val, List<value_asdl::value_t*>* pos_args, syntax_asdl::loc_t* blame_loc) {
  Dict<BigStr*, value_asdl::value_t*>* named_args = nullptr;
  syntax_asdl::ArgList* arg_list = nullptr;
  typed_args::Reader* rd = nullptr;
  StackRoot _root0(&func_val);
  StackRoot _root1(&pos_args);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&named_args);
  StackRoot _root4(&arg_list);
  StackRoot _root5(&rd);

  named_args = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  arg_list = ArgList::CreateNull();
  rd = Alloc<typed_args::Reader>(pos_args, named_args, nullptr, arg_list);
  rd->SetFallbackLocation(blame_loc);
  return this->_CallFunc(func_val, rd);
}

List<BigStr*>* ExprEvaluator::SpliceValue(value_asdl::value_t* val, word_part::Splice* part) {
  StackRoot _root0(&val);
  StackRoot _root1(&part);

  return val_ops::ToShellArray(val, Alloc<loc::WordPart>(part), S_etk);
}

value_asdl::value_t* ExprEvaluator::_EvalConst(expr::Const* node) {
  StackRoot _root0(&node);

  return node->val;
}

value_asdl::value_t* ExprEvaluator::_EvalUnary(expr::Unary* node) {
  value_asdl::value_t* val = nullptr;
  runtime_asdl::coerced_t c1;
  mops::BigInt i1;
  double f1;
  mops::BigInt i;
  bool b;
  StackRoot _root0(&node);
  StackRoot _root1(&val);

  val = this->_EvalExpr(node->child);
  switch (node->op->id) {
    case Id::Arith_Minus: {
      Tuple3<runtime_asdl::coerced_t, mops::BigInt, double> tup4 = _ConvertToNumber(val);
      c1 = tup4.at0();
      i1 = tup4.at1();
      f1 = tup4.at2();
      if (c1 == coerced_e::Int) {
        return Alloc<value::Int>(mops::Negate(i1));
      }
      if (c1 == coerced_e::Float) {
        return Alloc<value::Float>(-f1);
      }
      throw Alloc<error::TypeErr>(val, S_vvs, node->op);
    }
      break;
    case Id::Arith_Tilde: {
      i = _ConvertToInt(val, S_erg, node->op);
      return Alloc<value::Int>(mops::BitNot(i));
    }
      break;
    case Id::Expr_Not: {
      b = val_ops::ToBool(val);
      return Alloc<value::Bool>(b ? false : true);
    }
      break;
    case Id::Arith_Amp: {
      FAIL(kNotImplemented);  // Python NotImplementedError
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

value_asdl::value_t* ExprEvaluator::_ArithIntFloat(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::Token* op) {
  runtime_asdl::coerced_t c;
  mops::BigInt i1;
  mops::BigInt i2;
  double f1;
  double f2;
  int op_id;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&op);

  Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double> tup5 = _ConvertForBinaryOp(left, right);
  c = tup5.at0();
  i1 = tup5.at1();
  i2 = tup5.at2();
  f1 = tup5.at3();
  f2 = tup5.at4();
  op_id = op->id;
  if (c == coerced_e::Int) {
    switch (op_id) {
      case Id::Arith_Plus: 
      case Id::Arith_PlusEqual: {
        return Alloc<value::Int>(mops::Add(i1, i2));
      }
        break;
      case Id::Arith_Minus: 
      case Id::Arith_MinusEqual: {
        return Alloc<value::Int>(mops::Sub(i1, i2));
      }
        break;
      case Id::Arith_Star: 
      case Id::Arith_StarEqual: {
        return Alloc<value::Int>(mops::Mul(i1, i2));
      }
        break;
      case Id::Arith_Slash: 
      case Id::Arith_SlashEqual: {
        if (mops::Equal(i2, mops::ZERO)) {
          throw Alloc<error::Expr>(S_Bdr, op);
        }
        return Alloc<value::Float>((mops::ToFloat(i1) / mops::ToFloat(i2)));
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  else {
    if (c == coerced_e::Float) {
      switch (op_id) {
        case Id::Arith_Plus: 
        case Id::Arith_PlusEqual: {
          return Alloc<value::Float>((f1 + f2));
        }
          break;
        case Id::Arith_Minus: 
        case Id::Arith_MinusEqual: {
          return Alloc<value::Float>((f1 - f2));
        }
          break;
        case Id::Arith_Star: 
        case Id::Arith_StarEqual: {
          return Alloc<value::Float>((f1 * f2));
        }
          break;
        case Id::Arith_Slash: 
        case Id::Arith_SlashEqual: {
          if (f2 == 0.0) {
            throw Alloc<error::Expr>(S_Bdr, op);
          }
          return Alloc<value::Float>((f1 / f2));
        }
          break;
        default: {
          assert(0);  // AssertionError
        }
      }
    }
    else {
      throw Alloc<error::TypeErrVerbose>(StrFormat("Binary operator expected numbers, got %s and %s (OILS-ERR-201)", ui::ValType(left), ui::ValType(right)), op);
    }
  }
}

value_asdl::value_t* ExprEvaluator::_ArithIntOnly(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::Token* op) {
  mops::BigInt i1;
  mops::BigInt i2;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&op);

  i1 = _ConvertToInt(left, S_avA_1, op);
  i2 = _ConvertToInt(right, S_Epo, op);
  switch (op->id) {
    case Id::Arith_Percent: 
    case Id::Arith_PercentEqual: {
      if (mops::Equal(i2, mops::ZERO)) {
        throw Alloc<error::Expr>(S_Bdr, op);
      }
      if (mops::Greater(mops::ZERO, i2)) {
        throw Alloc<error::Expr>(S_tcf, op);
      }
      return Alloc<value::Int>(mops::Rem(i1, i2));
    }
      break;
    case Id::Expr_DSlash: 
    case Id::Expr_DSlashEqual: {
      if (mops::Equal(i2, mops::ZERO)) {
        throw Alloc<error::Expr>(S_Bdr, op);
      }
      return Alloc<value::Int>(mops::Div(i1, i2));
    }
      break;
    case Id::Arith_DStar: 
    case Id::Expr_DStarEqual: {
      if (mops::Greater(mops::ZERO, i2)) {
        throw Alloc<error::Expr>(S_abr, op);
      }
      return Alloc<value::Int>(num::Exponent(i1, i2));
    }
      break;
    case Id::Arith_Amp: 
    case Id::Arith_AmpEqual: {
      return Alloc<value::Int>(mops::BitAnd(i1, i2));
    }
      break;
    case Id::Arith_Pipe: 
    case Id::Arith_PipeEqual: {
      return Alloc<value::Int>(mops::BitOr(i1, i2));
    }
      break;
    case Id::Arith_Caret: 
    case Id::Arith_CaretEqual: {
      return Alloc<value::Int>(mops::BitXor(i1, i2));
    }
      break;
    case Id::Arith_DGreat: 
    case Id::Arith_DGreatEqual: {
      if (mops::Greater(mops::ZERO, i2)) {
        throw Alloc<error::Expr>(S_tDc, op);
      }
      return Alloc<value::Int>(mops::RShift(i1, i2));
    }
      break;
    case Id::Arith_DLess: 
    case Id::Arith_DLessEqual: {
      if (mops::Greater(mops::ZERO, i2)) {
        throw Alloc<error::Expr>(S_Clv, op);
      }
      return Alloc<value::Int>(mops::LShift(i1, i2));
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

value_asdl::value_t* ExprEvaluator::_Concat(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::Token* op) {
  value_asdl::value_t* UP_left = nullptr;
  value_asdl::value_t* UP_right = nullptr;
  List<value_asdl::value_t*>* c = nullptr;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&op);
  StackRoot _root3(&UP_left);
  StackRoot _root4(&UP_right);
  StackRoot _root5(&c);

  UP_left = left;
  UP_right = right;
  if ((left->tag() == value_e::Str and right->tag() == value_e::Str)) {
    value::Str* left = static_cast<value::Str*>(UP_left);
    value::Str* right = static_cast<value::Str*>(UP_right);
    return Alloc<value::Str>(str_concat(left->s, right->s));
  }
  else {
    if ((left->tag() == value_e::List and right->tag() == value_e::List)) {
      value::List* left = static_cast<value::List*>(UP_left);
      value::List* right = static_cast<value::List*>(UP_right);
      c = list(left->items);
      c->extend(right->items);
      return Alloc<value::List>(c);
    }
    else {
      throw Alloc<error::TypeErrVerbose>(StrFormat("Expected Str ++ Str or List ++ List, got %s ++ %s", ui::ValType(left), ui::ValType(right)), op);
    }
  }
}

value_asdl::value_t* ExprEvaluator::_EvalBinary(expr::Binary* node) {
  value_asdl::value_t* left = nullptr;
  value_asdl::value_t* right = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&left);
  StackRoot _root2(&right);

  left = this->_EvalExpr(node->left);
  switch (node->op->id) {
    case Id::Expr_And: {
      if (val_ops::ToBool(left)) {
        return this->_EvalExpr(node->right);
      }
      else {
        return left;
      }
    }
      break;
    case Id::Expr_Or: {
      if (val_ops::ToBool(left)) {
        return left;
      }
      else {
        return this->_EvalExpr(node->right);
      }
    }
      break;
  }
  right = this->_EvalExpr(node->right);
  switch (node->op->id) {
    case Id::Arith_DPlus: {
      return this->_Concat(left, right, node->op);
    }
      break;
    case Id::Arith_Plus: 
    case Id::Arith_Minus: 
    case Id::Arith_Star: 
    case Id::Arith_Slash: {
      return this->_ArithIntFloat(left, right, node->op);
    }
      break;
    default: {
      return this->_ArithIntOnly(left, right, node->op);
    }
  }
}

bool ExprEvaluator::_CompareNumeric(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::Token* op) {
  runtime_asdl::coerced_t c;
  mops::BigInt i1;
  mops::BigInt i2;
  double f1;
  double f2;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&op);

  Tuple5<runtime_asdl::coerced_t, mops::BigInt, mops::BigInt, double, double> tup6 = _ConvertForBinaryOp(left, right);
  c = tup6.at0();
  i1 = tup6.at1();
  i2 = tup6.at2();
  f1 = tup6.at3();
  f2 = tup6.at4();
  if (c == coerced_e::Int) {
    switch (op->id) {
      case Id::Arith_Less: {
        return mops::Greater(i2, i1);
      }
        break;
      case Id::Arith_Great: {
        return mops::Greater(i1, i2);
      }
        break;
      case Id::Arith_LessEqual: {
        return (mops::Greater(i2, i1) or mops::Equal(i1, i2));
      }
        break;
      case Id::Arith_GreatEqual: {
        return (mops::Greater(i1, i2) or mops::Equal(i1, i2));
      }
        break;
      default: {
        assert(0);  // AssertionError
      }
    }
  }
  else {
    if (c == coerced_e::Float) {
      switch (op->id) {
        case Id::Arith_Less: {
          return f1 < f2;
        }
          break;
        case Id::Arith_Great: {
          return f1 > f2;
        }
          break;
        case Id::Arith_LessEqual: {
          return f1 <= f2;
        }
          break;
        case Id::Arith_GreatEqual: {
          return f1 >= f2;
        }
          break;
        default: {
          assert(0);  // AssertionError
        }
      }
    }
    else {
      throw Alloc<error::TypeErrVerbose>(StrFormat("Comparison operator expected numbers, got %s and %s", ui::ValType(left), ui::ValType(right)), op);
    }
  }
}

value_asdl::value_t* ExprEvaluator::_EvalCompare(expr::Compare* node) {
  value_asdl::value_t* left = nullptr;
  bool result;
  int i;
  syntax_asdl::expr_t* right_expr = nullptr;
  value_asdl::value_t* right = nullptr;
  value_asdl::value_t* UP_left = nullptr;
  value_asdl::value_t* UP_right = nullptr;
  BigStr* left2 = nullptr;
  bool lb;
  bool ok;
  mops::BigInt left_i;
  bool eq;
  StackRoot _root0(&node);
  StackRoot _root1(&left);
  StackRoot _root2(&right_expr);
  StackRoot _root3(&right);
  StackRoot _root4(&UP_left);
  StackRoot _root5(&UP_right);
  StackRoot _root6(&left2);

  left = this->_EvalExpr(node->left);
  result = true;
  i = 0;
  for (ListIter<syntax_asdl::Token*> it(node->ops); !it.Done(); it.Next(), ++i) {
    syntax_asdl::Token* op = it.Value();
    StackRoot _for(&op  );
    right_expr = node->comparators->at(i);
    right = this->_EvalExpr(right_expr);
    if ((op->id == Id::Arith_Less || op->id == Id::Arith_Great || op->id == Id::Arith_LessEqual || op->id == Id::Arith_GreatEqual)) {
      result = this->_CompareNumeric(left, right, op);
    }
    else {
      if (op->id == Id::Expr_TEqual) {
        result = val_ops::ExactlyEqual(left, right, op);
      }
      else {
        if (op->id == Id::Expr_NotDEqual) {
          result = !val_ops::ExactlyEqual(left, right, op);
        }
        else {
          if (op->id == Id::Expr_In) {
            result = val_ops::Contains(left, right);
          }
          else {
            if (op->id == Id::Node_NotIn) {
              result = !val_ops::Contains(left, right);
            }
            else {
              if (op->id == Id::Expr_Is) {
                result = left == right;
              }
              else {
                if (op->id == Id::Node_IsNot) {
                  result = left != right;
                }
                else {
                  if (op->id == Id::Expr_DTilde) {
                    if (left->tag() != value_e::Str) {
                      throw Alloc<error::TypeErrVerbose>(S_hdl, op);
                    }
                    if (right->tag() != value_e::Str) {
                      throw Alloc<error::TypeErrVerbose>(S_fEm, op);
                    }
                    UP_left = left;
                    UP_right = right;
                    value::Str* left = static_cast<value::Str*>(UP_left);
                    value::Str* right = static_cast<value::Str*>(UP_right);
                    return Alloc<value::Bool>(libc::fnmatch(right->s, left->s));
                  }
                  else {
                    if (op->id == Id::Expr_NotDTilde) {
                      if (left->tag() != value_e::Str) {
                        throw Alloc<error::TypeErrVerbose>(S_hdl, op);
                      }
                      if (right->tag() != value_e::Str) {
                        throw Alloc<error::TypeErrVerbose>(S_fEm, op);
                      }
                      UP_left = left;
                      UP_right = right;
                      value::Str* left = static_cast<value::Str*>(UP_left);
                      value::Str* right = static_cast<value::Str*>(UP_right);
                      return Alloc<value::Bool>(!libc::fnmatch(right->s, left->s));
                    }
                    else {
                      if (op->id == Id::Expr_TildeDEqual) {
                        UP_left = left;
                        if (left->tag() != value_e::Str) {
                          e_die(S_uEa, op);
                        }
                        value::Str* left = static_cast<value::Str*>(UP_left);
                        left2 = left->s->strip();
                        UP_right = right;
                        switch (right->tag()) {
                          case value_e::Str: {
                            value::Str* right = static_cast<value::Str*>(UP_right);
                            return Alloc<value::Bool>(str_equals(left2, right->s));
                          }
                            break;
                          case value_e::Bool: {
                            value::Bool* right = static_cast<value::Bool*>(UP_right);
                            left2 = left2->lower();
                            lb = false;
                            if (str_equals(left2, S_FsF)) {
                              lb = true;
                            }
                            else {
                              if (str_equals(left2, S_Ctn)) {
                                lb = false;
                              }
                              else {
                                return Alloc<value::Bool>(false);
                              }
                            }
                            return Alloc<value::Bool>(lb == right->b);
                          }
                            break;
                          case value_e::Int: {
                            value::Int* right = static_cast<value::Int*>(UP_right);
                            if (!match::LooksLikeYshInt(left2)) {
                              return Alloc<value::Bool>(false);
                            }
                            left2 = left2->replace(S_tci, S_Aoo);
                            Tuple2<bool, mops::BigInt> tup7 = mops::FromStr2(left2);
                            ok = tup7.at0();
                            left_i = tup7.at1();
                            if (!ok) {
                              e_die(StrFormat("Integer too big: %s", left2), op);
                            }
                            eq = mops::Equal(left_i, right->i);
                            return Alloc<value::Bool>(eq);
                          }
                            break;
                        }
                        e_die(S_rEw, op);
                      }
                      else {
                        try {
                          if (op->id == Id::Arith_Tilde) {
                            result = val_ops::MatchRegex(left, right, this->mem);
                          }
                          else {
                            if (op->id == Id::Expr_NotTilde) {
                              result = !val_ops::MatchRegex(left, right, nullptr);
                            }
                            else {
                              assert(0);  // AssertionError
                            }
                          }
                        }
                        catch (ValueError* e) {
                          e_die_status(2, e->message, op);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    if (!result) {
      return Alloc<value::Bool>(result);
    }
    left = right;
  }
  return Alloc<value::Bool>(result);
}

value_asdl::value_t* ExprEvaluator::_CallFunc(value_asdl::value_t* to_call, typed_args::Reader* rd) {
  value_asdl::value_t* UP_to_call = nullptr;
  vm::_Callable* f = nullptr;
  StackRoot _root0(&to_call);
  StackRoot _root1(&rd);
  StackRoot _root2(&UP_to_call);
  StackRoot _root3(&f);

  UP_to_call = to_call;
  switch (to_call->tag()) {
    case value_e::Func: {
      value::Func* to_call = static_cast<value::Func*>(UP_to_call);
      return func_proc::CallUserFunc(to_call, rd, this->mem, this->cmd_ev);
    }
      break;
    case value_e::BuiltinFunc: {
      value::BuiltinFunc* to_call = static_cast<value::BuiltinFunc*>(UP_to_call);
      f = static_cast<vm::_Callable*>(to_call->callable);
      return f->Call(rd);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

value_asdl::value_t* ExprEvaluator::_EvalFuncCall(expr::FuncCall* node) {
  value_asdl::value_t* func = nullptr;
  value_asdl::value_t* UP_func = nullptr;
  value_asdl::value_t* to_call = nullptr;
  List<value_asdl::value_t*>* pos_args = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_args = nullptr;
  typed_args::Reader* rd = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&func);
  StackRoot _root2(&UP_func);
  StackRoot _root3(&to_call);
  StackRoot _root4(&pos_args);
  StackRoot _root5(&named_args);
  StackRoot _root6(&rd);

  func = this->_EvalExpr(node->func);
  UP_func = func;
  switch (func->tag()) {
    case value_e::Func: 
    case value_e::BuiltinFunc: {
      to_call = func;
      Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*> tup8 = func_proc::_EvalArgList(this, node->args);
      pos_args = tup8.at0();
      named_args = tup8.at1();
      rd = Alloc<typed_args::Reader>(pos_args, named_args, nullptr, node->args);
    }
      break;
    case value_e::BoundFunc: {
      value::BoundFunc* func = static_cast<value::BoundFunc*>(UP_func);
      to_call = func->func;
      Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*> tup9 = func_proc::_EvalArgList(this, node->args, func->me);
      pos_args = tup9.at0();
      named_args = tup9.at1();
      rd = Alloc<typed_args::Reader>(pos_args, named_args, nullptr, node->args, true);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(func, S_tbs, node->args->left);
    }
  }
  return this->_CallFunc(to_call, rd);
}

value_asdl::value_t* ExprEvaluator::_EvalSubscript(value_asdl::value_t* obj, value_asdl::value_t* index, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_obj = nullptr;
  value_asdl::value_t* UP_index = nullptr;
  int lower;
  int upper;
  int i;
  mops::BigInt big_i;
  value_asdl::value_t* index_method = nullptr;
  List<value_asdl::value_t*>* pos_args = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&index);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_obj);
  StackRoot _root4(&UP_index);
  StackRoot _root5(&index_method);
  StackRoot _root6(&pos_args);

  UP_obj = obj;
  UP_index = index;
  switch (obj->tag()) {
    case value_e::Str: {
      value::Str* obj = static_cast<value::Str*>(UP_obj);
      switch (index->tag()) {
        case value_e::Slice: {
          value::Slice* index = static_cast<value::Slice*>(UP_index);
          lower = index->lower ? index->lower->i : 0;
          upper = index->upper ? index->upper->i : len(obj->s);
          return Alloc<value::Str>(obj->s->slice(lower, upper));
        }
          break;
        case value_e::Int: {
          value::Int* index = static_cast<value::Int*>(UP_index);
          i = mops::BigTruncate(index->i);
          try {
            return Alloc<value::Str>(obj->s->at(i));
          }
          catch (IndexError*) {
            throw Alloc<error::Expr>(S_tvw, blame_loc);
          }
        }
          break;
        default: {
          throw Alloc<error::TypeErr>(index, S_sAl, blame_loc);
        }
      }
    }
      break;
    case value_e::List: {
      value::List* obj = static_cast<value::List*>(UP_obj);
      big_i = mops::ZERO;
      switch (index->tag()) {
        case value_e::Slice: {
          value::Slice* index = static_cast<value::Slice*>(UP_index);
          lower = index->lower ? index->lower->i : 0;
          upper = index->upper ? index->upper->i : len(obj->items);
          return Alloc<value::List>(obj->items->slice(lower, upper));
        }
          break;
        case value_e::Int: {
          value::Int* index = static_cast<value::Int*>(UP_index);
          big_i = index->i;
        }
          break;
        case value_e::Str: {
          value::Str* index = static_cast<value::Str*>(UP_index);
          big_i = _ConvertToInt(index, S_dAC, blame_loc);
        }
          break;
        default: {
          throw Alloc<error::TypeErr>(index, S_jFB, blame_loc);
        }
      }
      i = mops::BigTruncate(big_i);
      try {
        return obj->items->at(i);
      }
      catch (IndexError*) {
        throw Alloc<error::Expr>(StrFormat("List index out of range: %d", i), blame_loc);
      }
    }
      break;
    case value_e::Dict: {
      value::Dict* obj = static_cast<value::Dict*>(UP_obj);
      if (index->tag() != value_e::Str) {
        throw Alloc<error::TypeErr>(index, S_xnB, blame_loc);
      }
      value::Str* index = static_cast<value::Str*>(UP_index);
      try {
        return obj->d->at(index->s);
      }
      catch (KeyError*) {
        throw Alloc<error::Expr>(StrFormat("Dict entry not found: %r", index->s), blame_loc);
      }
    }
      break;
    case value_e::Obj: {
      Obj* obj = static_cast<Obj*>(UP_obj);
      index_method = val_ops::IndexMetaMethod(obj);
      if (index_method != nullptr) {
        pos_args = NewList<value_asdl::value_t*>(std::initializer_list<value_asdl::value_t*>{obj, index});
        return this->_CallMetaMethod(index_method, pos_args, blame_loc);
      }
    }
      break;
  }
  throw Alloc<error::TypeErr>(obj, S_lct, blame_loc);
}

value_asdl::value_t* ExprEvaluator::_ChainedLookup(value_asdl::Obj* obj, value_asdl::Obj* current, BigStr* attr_name) {
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&current);
  StackRoot _root2(&attr_name);
  StackRoot _root3(&val);

  val = current->d->get(attr_name);
  if (val != nullptr) {
    if ((val->tag() == value_e::Func || val->tag() == value_e::BuiltinFunc)) {
      return Alloc<value::BoundFunc>(obj, val);
    }
    else {
      return val;
    }
  }
  if (current->prototype != nullptr) {
    return this->_ChainedLookup(obj, current->prototype, attr_name);
  }
  return nullptr;
}

value_asdl::value_t* ExprEvaluator::_EvalDot(syntax_asdl::Attribute* node, value_asdl::value_t* val) {
  value_asdl::value_t* UP_val = nullptr;
  BigStr* attr_name = nullptr;
  value_asdl::value_t* result = nullptr;
  Dict<BigStr*, vm::_Callable*>* type_methods = nullptr;
  BigStr* name = nullptr;
  vm::_Callable* vm_callable = nullptr;
  value::BuiltinFunc* func_val = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&val);
  StackRoot _root2(&UP_val);
  StackRoot _root3(&attr_name);
  StackRoot _root4(&result);
  StackRoot _root5(&type_methods);
  StackRoot _root6(&name);
  StackRoot _root7(&vm_callable);
  StackRoot _root8(&func_val);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Dict: {
      value::Dict* val = static_cast<value::Dict*>(UP_val);
      attr_name = node->attr_name;
      result = val->d->get(attr_name);
      if (result != nullptr) {
        return result;
      }
      throw Alloc<error::Expr>(StrFormat("Dict entry %r not found", attr_name), node->op);
    }
      break;
    case value_e::Obj: {
      Obj* obj = static_cast<Obj*>(UP_val);
      attr_name = node->attr_name;
      result = obj->d->get(attr_name);
      if (result != nullptr) {
        return result;
      }
      if (obj->prototype != nullptr) {
        result = this->_ChainedLookup(obj, obj->prototype, attr_name);
        if (result != nullptr) {
          return result;
        }
      }
      throw Alloc<error::Expr>(StrFormat("Attribute %r not found on Obj", attr_name), node->op);
    }
      break;
    default: {
      type_methods = this->methods->get(val->tag());
      name = node->attr_name;
      vm_callable = type_methods != nullptr ? type_methods->get(name) : nullptr;
      if (vm_callable) {
        func_val = Alloc<value::BuiltinFunc>(vm_callable);
        return Alloc<value::BoundFunc>(val, func_val);
      }
      throw Alloc<error::TypeErrVerbose>(StrFormat("Method %r not found on builtin type %s", name, ui::ValType(val)), node->attr);
    }
  }
  assert(0);  // AssertionError
}

value_asdl::value_t* ExprEvaluator::_EvalRArrow(syntax_asdl::Attribute* node, value_asdl::value_t* val) {
  BigStr* mut_name = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  value_asdl::value_t* result = nullptr;
  Dict<BigStr*, vm::_Callable*>* type_methods = nullptr;
  vm::_Callable* vm_callable = nullptr;
  value::BuiltinFunc* func_val = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&val);
  StackRoot _root2(&mut_name);
  StackRoot _root3(&UP_val);
  StackRoot _root4(&result);
  StackRoot _root5(&type_methods);
  StackRoot _root6(&vm_callable);
  StackRoot _root7(&func_val);

  mut_name = str_concat(S_qiD, node->attr_name);
  UP_val = val;
  switch (val->tag()) {
    case value_e::Obj: {
      Obj* obj = static_cast<Obj*>(UP_val);
      if (obj->prototype != nullptr) {
        result = this->_ChainedLookup(obj, obj->prototype, mut_name);
        if (result != nullptr) {
          return result;
        }
      }
      throw Alloc<error::Expr>(StrFormat("Mutating method %r not found on Obj prototype chain", mut_name), node->attr);
    }
      break;
    default: {
      type_methods = this->methods->get(val->tag());
      vm_callable = type_methods != nullptr ? type_methods->get(mut_name) : nullptr;
      if (vm_callable) {
        func_val = Alloc<value::BuiltinFunc>(vm_callable);
        return Alloc<value::BoundFunc>(val, func_val);
      }
      throw Alloc<error::TypeErrVerbose>(StrFormat("Mutating method %r not found on builtin type %s", mut_name, ui::ValType(val)), node->attr);
    }
  }
  assert(0);  // AssertionError
}

value_asdl::value_t* ExprEvaluator::_EvalAttribute(syntax_asdl::Attribute* node) {
  value_asdl::value_t* val = nullptr;
  BigStr* name = nullptr;
  Dict<BigStr*, vm::_Callable*>* type_methods = nullptr;
  vm::_Callable* vm_callable = nullptr;
  value::BuiltinFunc* func_val = nullptr;
  value_asdl::value_t* val2 = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&val);
  StackRoot _root2(&name);
  StackRoot _root3(&type_methods);
  StackRoot _root4(&vm_callable);
  StackRoot _root5(&func_val);
  StackRoot _root6(&val2);

  val = this->_EvalExpr(node->obj);
  switch (node->op->id) {
    case Id::Expr_Dot: {
      return this->_EvalDot(node, val);
    }
      break;
    case Id::Expr_RArrow: {
      return this->_EvalRArrow(node, val);
    }
      break;
    case Id::Expr_RDArrow: {
      name = node->attr_name;
      type_methods = this->methods->get(val->tag());
      vm_callable = type_methods != nullptr ? type_methods->get(name) : nullptr;
      if (vm_callable) {
        func_val = Alloc<value::BuiltinFunc>(vm_callable);
        return Alloc<value::BoundFunc>(val, func_val);
      }
      val2 = this->_LookupVar(name, node->attr);
      switch (val2->tag()) {
        case value_e::Func: 
        case value_e::BuiltinFunc: {
          return Alloc<value::BoundFunc>(val, val2);
        }
          break;
        default: {
          throw Alloc<error::TypeErr>(val2, S_snq, node->attr);
        }
      }
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

value_asdl::value_t* ExprEvaluator::_EvalExpr(syntax_asdl::expr_t* node) {
  syntax_asdl::expr_t* UP_node = nullptr;
  Dict<BigStr*, runtime_asdl::Cell*>* frame = nullptr;
  int id_;
  BigStr* stdout_str = nullptr;
  List<BigStr*>* strs = nullptr;
  List<value_asdl::value_t*>* items = nullptr;
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  value_asdl::IntBox* lower = nullptr;
  value_asdl::IntBox* upper = nullptr;
  mops::BigInt i1;
  mops::BigInt i2;
  bool b;
  List<value_asdl::value_t*>* kvals = nullptr;
  List<value_asdl::value_t*>* values = nullptr;
  int i;
  value::Str* key = nullptr;
  value_asdl::value_t* v = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* d = nullptr;
  BigStr* k = nullptr;
  value_asdl::value_t* obj = nullptr;
  value_asdl::value_t* index = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);
  StackRoot _root2(&frame);
  StackRoot _root3(&stdout_str);
  StackRoot _root4(&strs);
  StackRoot _root5(&items);
  StackRoot _root6(&words);
  StackRoot _root7(&lower);
  StackRoot _root8(&upper);
  StackRoot _root9(&kvals);
  StackRoot _root10(&values);
  StackRoot _root11(&key);
  StackRoot _root12(&v);
  StackRoot _root13(&d);
  StackRoot _root14(&k);
  StackRoot _root15(&obj);
  StackRoot _root16(&index);

  UP_node = node;
  switch (node->tag()) {
    case expr_e::Const: {
      expr::Const* node = static_cast<expr::Const*>(UP_node);
      return this->_EvalConst(node);
    }
      break;
    case expr_e::Var: {
      expr::Var* node = static_cast<expr::Var*>(UP_node);
      return this->_LookupVar(node->name, node->left);
    }
      break;
    case expr_e::Place: {
      expr::Place* node = static_cast<expr::Place*>(UP_node);
      frame = this->mem->CurrentFrame();
      return Alloc<value::Place>(Alloc<LeftName>(node->var_name, node->blame_tok), frame);
    }
      break;
    case expr_e::CommandSub: {
      CommandSub* node = static_cast<CommandSub*>(UP_node);
      id_ = node->left_token->id;
      if (id_ == Id::Left_CaretParen) {
        return Alloc<value::Command>(Alloc<cmd_frag::Expr>(node->child), this->mem->CurrentFrame(), this->mem->GlobalFrame());
      }
      else {
        stdout_str = this->shell_ex->RunCommandSub(node);
        if (id_ == Id::Left_AtParen) {
          try {
            strs = j8::SplitJ8Lines(stdout_str);
          }
          catch (error::Decode* e) {
            throw Alloc<error::Structured>(4, e->Message(), node->left_token);
          }
          items = Alloc<List<value_asdl::value_t*>>();
          for (ListIter<BigStr*> it(strs); !it.Done(); it.Next()) {
            BigStr* s = it.Value();
            items->append(Alloc<value::Str>(s));
          }
          return Alloc<value::List>(items);
        }
        else {
          return Alloc<value::Str>(stdout_str);
        }
      }
    }
      break;
    case expr_e::ShArrayLiteral: {
      ShArrayLiteral* node = static_cast<ShArrayLiteral*>(UP_node);
      words = braces::BraceExpandWords(node->words);
      strs = this->word_ev->EvalWordSequence(words);
      items = Alloc<List<value_asdl::value_t*>>();
      for (ListIter<BigStr*> it(strs); !it.Done(); it.Next()) {
        BigStr* s = it.Value();
        items->append(Alloc<value::Str>(s));
      }
      return Alloc<value::List>(items);
    }
      break;
    case expr_e::DoubleQuoted: {
      DoubleQuoted* node = static_cast<DoubleQuoted*>(UP_node);
      return Alloc<value::Str>(this->word_ev->EvalDoubleQuotedToString(node));
    }
      break;
    case expr_e::SingleQuoted: {
      SingleQuoted* node = static_cast<SingleQuoted*>(UP_node);
      return Alloc<value::Str>(node->sval);
    }
      break;
    case expr_e::BracedVarSub: {
      BracedVarSub* node = static_cast<BracedVarSub*>(UP_node);
      return Alloc<value::Str>(this->word_ev->EvalBracedVarSubToString(node));
    }
      break;
    case expr_e::SimpleVarSub: {
      SimpleVarSub* node = static_cast<SimpleVarSub*>(UP_node);
      return Alloc<value::Str>(this->word_ev->EvalSimpleVarSubToString(node));
    }
      break;
    case expr_e::Unary: {
      expr::Unary* node = static_cast<expr::Unary*>(UP_node);
      return this->_EvalUnary(node);
    }
      break;
    case expr_e::Binary: {
      expr::Binary* node = static_cast<expr::Binary*>(UP_node);
      return this->_EvalBinary(node);
    }
      break;
    case expr_e::Slice: {
      expr::Slice* node = static_cast<expr::Slice*>(UP_node);
      lower = nullptr;
      upper = nullptr;
      if (node->lower) {
        i1 = _ConvertToInt(this->_EvalExpr(node->lower), S_ljh, node->op);
        lower = Alloc<IntBox>(mops::BigTruncate(i1));
      }
      if (node->upper) {
        i1 = _ConvertToInt(this->_EvalExpr(node->upper), S_vEo, node->op);
        upper = Alloc<IntBox>(mops::BigTruncate(i1));
      }
      return Alloc<value::Slice>(lower, upper);
    }
      break;
    case expr_e::Range: {
      expr::Range* node = static_cast<expr::Range*>(UP_node);
      i1 = _ConvertToInt(this->_EvalExpr(node->lower), S_tkz, node->op);
      i2 = _ConvertToInt(this->_EvalExpr(node->upper), S_rgA, node->op);
      if (node->op->id == Id::Expr_DDotEqual) {
        i2 = mops::Add(i2, mops::ONE);
      }
      return Alloc<value::Range>(mops::BigTruncate(i1), mops::BigTruncate(i2));
    }
      break;
    case expr_e::Compare: {
      expr::Compare* node = static_cast<expr::Compare*>(UP_node);
      return this->_EvalCompare(node);
    }
      break;
    case expr_e::IfExp: {
      expr::IfExp* node = static_cast<expr::IfExp*>(UP_node);
      b = val_ops::ToBool(this->_EvalExpr(node->test));
      if (b) {
        return this->_EvalExpr(node->body);
      }
      else {
        return this->_EvalExpr(node->orelse);
      }
    }
      break;
    case expr_e::List: {
      expr::List* node = static_cast<expr::List*>(UP_node);
      items = Alloc<List<value_asdl::value_t*>>();
      for (ListIter<syntax_asdl::expr_t*> it(node->elts); !it.Done(); it.Next()) {
        syntax_asdl::expr_t* e = it.Value();
        items->append(this->_EvalExpr(e));
      }
      return Alloc<value::List>(items);
    }
      break;
    case expr_e::Tuple: {
      expr::Tuple* node = static_cast<expr::Tuple*>(UP_node);
      items = Alloc<List<value_asdl::value_t*>>();
      for (ListIter<syntax_asdl::expr_t*> it(node->elts); !it.Done(); it.Next()) {
        syntax_asdl::expr_t* e = it.Value();
        items->append(this->_EvalExpr(e));
      }
      return Alloc<value::List>(items);
    }
      break;
    case expr_e::Dict: {
      expr::Dict* node = static_cast<expr::Dict*>(UP_node);
      kvals = Alloc<List<value_asdl::value_t*>>();
      for (ListIter<syntax_asdl::expr_t*> it(node->keys); !it.Done(); it.Next()) {
        syntax_asdl::expr_t* e = it.Value();
        kvals->append(this->_EvalExpr(e));
      }
      values = Alloc<List<value_asdl::value_t*>>();
      i = 0;
      for (ListIter<syntax_asdl::expr_t*> it(node->values); !it.Done(); it.Next(), ++i) {
        syntax_asdl::expr_t* value_expr = it.Value();
        StackRoot _for(&value_expr      );
        if (value_expr->tag() == expr_e::Implicit) {
          key = static_cast<value::Str*>(kvals->at(i));
          v = this->_LookupVar(key->s, loc::Missing);
        }
        else {
          v = this->_EvalExpr(value_expr);
        }
        values->append(v);
      }
      d = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
      i = 0;
      for (ListIter<value_asdl::value_t*> it(kvals); !it.Done(); it.Next(), ++i) {
        value_asdl::value_t* kval = it.Value();
        StackRoot _for(&kval      );
        k = val_ops::ToStr(kval, S_mbB, loc::Missing);
        d->set(k, values->at(i));
      }
      return Alloc<value::Dict>(d);
    }
      break;
    case expr_e::ListComp: {
      e_die_status(2, S_hon);
    }
      break;
    case expr_e::GeneratorExp: {
      e_die_status(2, S_fam);
    }
      break;
    case expr_e::Literal: {
      expr::Literal* node = static_cast<expr::Literal*>(UP_node);
      return Alloc<value::Expr>(node->inner, this->mem->CurrentFrame(), this->mem->GlobalFrame());
    }
      break;
    case expr_e::Lambda: {
      e_die_status(2, S_Fpe);
    }
      break;
    case expr_e::FuncCall: {
      expr::FuncCall* node = static_cast<expr::FuncCall*>(UP_node);
      return this->_EvalFuncCall(node);
    }
      break;
    case expr_e::Subscript: {
      Subscript* node = static_cast<Subscript*>(UP_node);
      obj = this->_EvalExpr(node->obj);
      index = this->_EvalExpr(node->index);
      return this->_EvalSubscript(obj, index, node->left);
    }
      break;
    case expr_e::Attribute: {
      Attribute* node = static_cast<Attribute*>(UP_node);
      return this->_EvalAttribute(node);
    }
      break;
    case expr_e::Eggex: {
      Eggex* node = static_cast<Eggex*>(UP_node);
      return this->EvalEggex(node);
    }
      break;
    default: {
      FAIL(kNotImplemented);  // Python NotImplementedError
    }
  }
}

value::Eggex* ExprEvaluator::EvalEggex(syntax_asdl::Eggex* node) {
  expr_eval::EggexEvaluator* ev = nullptr;
  syntax_asdl::re_t* spliced = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&ev);
  StackRoot _root2(&spliced);

  ev = Alloc<EggexEvaluator>(this->mem, node->canonical_flags);
  spliced = ev->EvalE(node->regex);
  return Alloc<value::Eggex>(spliced, node->canonical_flags, ev->convert_funcs, ev->convert_toks, nullptr, Alloc<List<BigStr*>>());
}

EggexEvaluator::EggexEvaluator(state::Mem* mem, BigStr* canonical_flags) {
  this->mem = mem;
  this->canonical_flags = canonical_flags;
  this->convert_funcs = Alloc<List<value_asdl::value_t*>>();
  this->convert_toks = Alloc<List<syntax_asdl::Token*>>();
}

value_asdl::value_t* EggexEvaluator::_LookupVar(BigStr* name, syntax_asdl::loc_t* var_loc) {
  StackRoot _root0(&name);
  StackRoot _root1(&var_loc);

  return LookupVar(this->mem, name, scope_e::LocalOrGlobal, var_loc);
}

void EggexEvaluator::_EvalClassLiteralTerm(syntax_asdl::class_literal_term_t* term, List<syntax_asdl::char_class_term_t*>* out) {
  syntax_asdl::class_literal_term_t* UP_term = nullptr;
  BigStr* s = nullptr;
  syntax_asdl::Token* char_code_tok = nullptr;
  value_asdl::value_t* val = nullptr;
  int char_int;
  StackRoot _root0(&term);
  StackRoot _root1(&out);
  StackRoot _root2(&UP_term);
  StackRoot _root3(&s);
  StackRoot _root4(&char_code_tok);
  StackRoot _root5(&val);

  UP_term = term;
  s = nullptr;
  char_code_tok = nullptr;
  switch (term->tag()) {
    case class_literal_term_e::CharCode: {
      CharCode* term = static_cast<CharCode*>(UP_term);
      out->append(term);
      return ;
    }
      break;
    case class_literal_term_e::CharRange: {
      CharRange* term = static_cast<CharRange*>(UP_term);
      out->append(term);
      return ;
    }
      break;
    case class_literal_term_e::PosixClass: {
      PosixClass* term = static_cast<PosixClass*>(UP_term);
      out->append(term);
      return ;
    }
      break;
    case class_literal_term_e::PerlClass: {
      PerlClass* term = static_cast<PerlClass*>(UP_term);
      out->append(term);
      return ;
    }
      break;
    case class_literal_term_e::SingleQuoted: {
      SingleQuoted* term = static_cast<SingleQuoted*>(UP_term);
      s = term->sval;
      char_code_tok = term->left;
    }
      break;
    case class_literal_term_e::Splice: {
      class_literal_term::Splice* term = static_cast<class_literal_term::Splice*>(UP_term);
      val = this->_LookupVar(term->var_name, term->name);
      s = val_ops::ToStr(val, S_iBE, term->name);
      char_code_tok = term->name;
    }
      break;
  }
  for (StrIter it(s); !it.Done(); it.Next()) {
    BigStr* ch = it.Value();
    StackRoot _for(&ch  );
    char_int = ord(ch);
    if (char_int >= 128) {
      e_die(StrFormat("Use unquoted char literal for byte %d, which is >= 128 (avoid confusing a set of bytes with a sequence)", char_int), char_code_tok);
    }
    out->append(Alloc<CharCode>(char_code_tok, char_int, false));
  }
}

syntax_asdl::re_t* EggexEvaluator::EvalE(syntax_asdl::re_t* node) {
  syntax_asdl::re_t* UP_node = nullptr;
  List<syntax_asdl::re_t*>* new_children = nullptr;
  value_asdl::value_t* convert_func = nullptr;
  syntax_asdl::Token* convert_tok = nullptr;
  BigStr* func_name = nullptr;
  value_asdl::value_t* func_val = nullptr;
  List<syntax_asdl::char_class_term_t*>* new_terms = nullptr;
  BigStr* s = nullptr;
  value_asdl::value_t* val = nullptr;
  value_asdl::value_t* UP_val = nullptr;
  syntax_asdl::re_t* to_splice = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&UP_node);
  StackRoot _root2(&new_children);
  StackRoot _root3(&convert_func);
  StackRoot _root4(&convert_tok);
  StackRoot _root5(&func_name);
  StackRoot _root6(&func_val);
  StackRoot _root7(&new_terms);
  StackRoot _root8(&s);
  StackRoot _root9(&val);
  StackRoot _root10(&UP_val);
  StackRoot _root11(&to_splice);

  UP_node = node;
  switch (node->tag()) {
    case re_e::Seq: {
      re::Seq* node = static_cast<re::Seq*>(UP_node);
      new_children = Alloc<List<syntax_asdl::re_t*>>();
      for (ListIter<syntax_asdl::re_t*> it(node->children); !it.Done(); it.Next()) {
        syntax_asdl::re_t* child = it.Value();
        new_children->append(this->EvalE(child));
      }
      return Alloc<re::Seq>(new_children);
    }
      break;
    case re_e::Alt: {
      re::Alt* node = static_cast<re::Alt*>(UP_node);
      new_children = Alloc<List<syntax_asdl::re_t*>>();
      for (ListIter<syntax_asdl::re_t*> it(node->children); !it.Done(); it.Next()) {
        syntax_asdl::re_t* child = it.Value();
        new_children->append(this->EvalE(child));
      }
      return Alloc<re::Alt>(new_children);
    }
      break;
    case re_e::Repeat: {
      re::Repeat* node = static_cast<re::Repeat*>(UP_node);
      return Alloc<re::Repeat>(this->EvalE(node->child), node->op);
    }
      break;
    case re_e::Group: {
      re::Group* node = static_cast<re::Group*>(UP_node);
      this->convert_funcs->append(nullptr);
      this->convert_toks->append(nullptr);
      return Alloc<re::Group>(this->EvalE(node->child));
    }
      break;
    case re_e::Capture: {
      re::Capture* node = static_cast<re::Capture*>(UP_node);
      convert_func = nullptr;
      convert_tok = nullptr;
      if (node->func_name) {
        func_name = lexer::LazyStr(node->func_name);
        func_val = this->mem->GetValue(func_name);
        switch (func_val->tag()) {
          case value_e::Func: 
          case value_e::BuiltinFunc: {
            convert_func = func_val;
            convert_tok = node->func_name;
          }
            break;
          default: {
            throw Alloc<error::TypeErr>(func_val, StrFormat("Expected %r to be a func", func_name), node->func_name);
          }
        }
      }
      this->convert_funcs->append(convert_func);
      this->convert_toks->append(convert_tok);
      return Alloc<re::Capture>(this->EvalE(node->child), node->name, node->func_name);
    }
      break;
    case re_e::CharClassLiteral: {
      re::CharClassLiteral* node = static_cast<re::CharClassLiteral*>(UP_node);
      new_terms = Alloc<List<syntax_asdl::char_class_term_t*>>();
      for (ListIter<syntax_asdl::class_literal_term_t*> it(node->terms); !it.Done(); it.Next()) {
        syntax_asdl::class_literal_term_t* t = it.Value();
        StackRoot _for(&t      );
        this->_EvalClassLiteralTerm(t, new_terms);
      }
      return Alloc<re::CharClass>(node->negated, new_terms);
    }
      break;
    case re_e::SingleQuoted: {
      SingleQuoted* node = static_cast<SingleQuoted*>(UP_node);
      s = node->sval;
      return Alloc<re::LiteralChars>(node->left, s);
    }
      break;
    case re_e::Splice: {
      re::Splice* node = static_cast<re::Splice*>(UP_node);
      val = this->_LookupVar(node->var_name, node->name);
      UP_val = val;
      switch (val->tag()) {
        case value_e::Str: {
          value::Str* val = static_cast<value::Str*>(UP_val);
          to_splice = Alloc<re::LiteralChars>(node->name, val->s);
        }
          break;
        case value_e::Eggex: {
          value::Eggex* val = static_cast<value::Eggex*>(UP_val);
          this->convert_funcs->extend(val->convert_funcs);
          this->convert_toks->extend(val->convert_toks);
          to_splice = val->spliced;
          if (!(str_equals(val->canonical_flags, this->canonical_flags))) {
            e_die(StrFormat("Expected eggex flags %r, but got %r", this->canonical_flags, val->canonical_flags), node->name);
          }
        }
          break;
        default: {
          throw Alloc<error::TypeErr>(val, S_rDB, node->name);
        }
      }
      return to_splice;
    }
      break;
    default: {
      return node;
    }
  }
}

}  // define namespace expr_eval

namespace expr_parse {  // define

using syntax_asdl::loc;
using syntax_asdl::Token;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::SingleQuoted;
using syntax_asdl::CommandSub;
using syntax_asdl::ShArrayLiteral;
using syntax_asdl::CompoundWord;
using syntax_asdl::word_part_t;
using syntax_asdl::word_e;
using id_kind_asdl::Id;
using id_kind_asdl::Kind;
using id_kind_asdl::Id_str;
using types_asdl::lex_mode_e;
using error::p_die;
using pnode::PNodeAllocator;

int _Classify(grammar::Grammar* gr, syntax_asdl::Token* tok) {
  int id_;
  BigStr* type_str = nullptr;
  StackRoot _root0(&gr);
  StackRoot _root1(&tok);
  StackRoot _root2(&type_str);

  id_ = tok->id;
  if (dict_contains(gr->tokens, id_)) {
    return gr->tokens->at(id_);
  }
  if (id_ == Id::Unknown_DEqual) {
    p_die(S_qbb, tok);
  }
  if (id_ == Id::Unknown_DAmp) {
    p_die(S_hkt, tok);
  }
  if (id_ == Id::Unknown_DPipe) {
    p_die(S_yww, tok);
  }
  if (id_ == Id::Unknown_DDot) {
    p_die(S_bkb, tok);
  }
  if (id_ == Id::Unknown_Tok) {
    type_str = S_Aoo;
  }
  else {
    type_str = StrFormat(" (%s)", ui::PrettyId(tok->id));
  }
  p_die(StrFormat("Unexpected token in expression mode%s", type_str), tok);
}
GLOBAL_DICT(_OTHER_BALANCE, int, int, 4, {Id::Op_LParen COMMA Id::Op_RParen COMMA Id::Op_LBracket COMMA Id::Op_RBracket}, {1 COMMA -1 COMMA 1 COMMA -1});

syntax_asdl::Token* _PushYshTokens(parse_lib::ParseContext* parse_ctx, grammar::Grammar* gr, parse::Parser* p, lexer::Lexer* lex) {
  syntax_asdl::Token* last_token = nullptr;
  bool prev_was_newline;
  int balance;
  syntax_asdl::Token* tok = nullptr;
  int ilabel;
  syntax_asdl::Token* left_tok = nullptr;
  reader::DisallowedLineReader* line_reader = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  List<syntax_asdl::CompoundWord*>* words = nullptr;
  syntax_asdl::Token* close_tok = nullptr;
  bool done;
  syntax_asdl::word_t* w = nullptr;
  List<syntax_asdl::word_t*>* words2 = nullptr;
  List<syntax_asdl::word_t*>* words3 = nullptr;
  int typ;
  syntax_asdl::ShArrayLiteral* lit_part = nullptr;
  syntax_asdl::Token* opaque = nullptr;
  syntax_asdl::Token* left_token = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  syntax_asdl::command_t* node = nullptr;
  syntax_asdl::Token* right_token = nullptr;
  syntax_asdl::CommandSub* cs_part = nullptr;
  List<syntax_asdl::word_part_t*>* parts = nullptr;
  syntax_asdl::DoubleQuoted* expr_dq_part = nullptr;
  syntax_asdl::BracedVarSub* part = nullptr;
  types_asdl::lex_mode_t sq_mode;
  List<syntax_asdl::Token*>* tokens = nullptr;
  BigStr* sval = nullptr;
  syntax_asdl::SingleQuoted* sq_part = nullptr;
  StackRoot _root0(&parse_ctx);
  StackRoot _root1(&gr);
  StackRoot _root2(&p);
  StackRoot _root3(&lex);
  StackRoot _root4(&last_token);
  StackRoot _root5(&tok);
  StackRoot _root6(&left_tok);
  StackRoot _root7(&line_reader);
  StackRoot _root8(&w_parser);
  StackRoot _root9(&words);
  StackRoot _root10(&close_tok);
  StackRoot _root11(&w);
  StackRoot _root12(&words2);
  StackRoot _root13(&words3);
  StackRoot _root14(&lit_part);
  StackRoot _root15(&opaque);
  StackRoot _root16(&left_token);
  StackRoot _root17(&c_parser);
  StackRoot _root18(&node);
  StackRoot _root19(&right_token);
  StackRoot _root20(&cs_part);
  StackRoot _root21(&parts);
  StackRoot _root22(&expr_dq_part);
  StackRoot _root23(&part);
  StackRoot _root24(&tokens);
  StackRoot _root25(&sval);
  StackRoot _root26(&sq_part);

  last_token = nullptr;
  prev_was_newline = false;
  balance = 0;
  while (true) {
    if (last_token) {
      tok = last_token;
      last_token = nullptr;
    }
    else {
      tok = lex->Read(lex_mode_e::Expr);
    }
    if (consts::GetKind(tok->id) == Kind::Ignored) {
      continue;
    }
    if (tok->id == Id::Op_Newline) {
      if (balance > 0) {
        continue;
      }
      if (prev_was_newline) {
        continue;
      }
      prev_was_newline = true;
    }
    else {
      prev_was_newline = false;
    }
    balance += _OTHER_BALANCE->get(tok->id, 0);
    if (tok->id == Id::Op_LParen) {
      lex->PushHint(Id::Op_RParen, Id::Op_RParen);
    }
    ilabel = _Classify(gr, tok);
    if (p->addtoken(tok->id, tok, ilabel)) {
      return tok;
    }
    if ((tok->id == Id::Left_ColonPipe || tok->id == Id::Left_PercentParen)) {
      left_tok = tok;
      if (tok->id == Id::Left_PercentParen) {
        lex->PushHint(Id::Op_RParen, Id::Right_ShArrayLiteral);
      }
      line_reader = Alloc<reader::DisallowedLineReader>(parse_ctx->arena, tok);
      w_parser = parse_ctx->MakeWordParser(lex, line_reader);
      words = Alloc<List<syntax_asdl::CompoundWord*>>();
      close_tok = nullptr;
      done = false;
      while (!done) {
        w = w_parser->ReadWord(lex_mode_e::ShCommand);
        switch (w->tag()) {
          case word_e::Operator: {
            tok = reinterpret_cast<Token*>(w);
            if (tok->id == Id::Right_ShArrayLiteral) {
              if (left_tok->id != Id::Left_PercentParen) {
                p_die(S_ine, left_tok);
              }
              close_tok = tok;
              done = true;
            }
            else {
              if (tok->id == Id::Op_Pipe) {
                if (left_tok->id != Id::Left_ColonPipe) {
                  p_die(S_ine, left_tok);
                }
                close_tok = tok;
                done = true;
              }
              else {
                if (tok->id == Id::Op_Newline) {
                  continue;
                }
                else {
                  p_die(S_Ayd, Alloc<loc::Word>(w));
                }
              }
            }
          }
            break;
          case word_e::Compound: {
            words->append(static_cast<CompoundWord*>(w));
          }
            break;
          default: {
            assert(0);  // AssertionError
          }
        }
      }
      words2 = braces::BraceDetectAll(words);
      words3 = word_::TildeDetectAll(words2);
      typ = Id::Expr_CastedDummy;
      lit_part = Alloc<ShArrayLiteral>(left_tok, words3, close_tok);
      opaque = reinterpret_cast<Token*>(lit_part);
      done = p->addtoken(typ, opaque, gr->tokens->at(typ));
      ilabel = _Classify(gr, close_tok);
      done = p->addtoken(tok->id, close_tok, ilabel);
      continue;
    }
    if ((tok->id == Id::Left_DollarParen || tok->id == Id::Left_AtParen || tok->id == Id::Left_CaretParen)) {
      left_token = tok;
      lex->PushHint(Id::Op_RParen, Id::Eof_RParen);
      line_reader = Alloc<reader::DisallowedLineReader>(parse_ctx->arena, tok);
      c_parser = parse_ctx->MakeParserForCommandSub(line_reader, lex, Id::Eof_RParen);
      node = c_parser->ParseCommandSub();
      right_token = c_parser->w_parser->cur_token;
      cs_part = Alloc<CommandSub>(left_token, node, right_token);
      typ = Id::Expr_CastedDummy;
      opaque = reinterpret_cast<Token*>(cs_part);
      done = p->addtoken(typ, opaque, gr->tokens->at(typ));
      ilabel = _Classify(gr, right_token);
      done = p->addtoken(right_token->id, right_token, ilabel);
      continue;
    }
    if ((tok->id == Id::Left_DoubleQuote || tok->id == Id::Left_DollarDoubleQuote || tok->id == Id::Left_TDoubleQuote || tok->id == Id::Left_DollarTDoubleQuote || tok->id == Id::Left_CaretDoubleQuote)) {
      left_token = tok;
      line_reader = Alloc<reader::DisallowedLineReader>(parse_ctx->arena, tok);
      w_parser = parse_ctx->MakeWordParser(lex, line_reader);
      parts = Alloc<List<syntax_asdl::word_part_t*>>();
      last_token = w_parser->ReadDoubleQuoted(left_token, parts);
      expr_dq_part = Alloc<DoubleQuoted>(left_token, parts, last_token);
      typ = Id::Expr_CastedDummy;
      opaque = reinterpret_cast<Token*>(expr_dq_part);
      done = p->addtoken(typ, opaque, gr->tokens->at(typ));
      continue;
    }
    if (tok->id == Id::Left_DollarBrace) {
      left_token = tok;
      line_reader = Alloc<reader::DisallowedLineReader>(parse_ctx->arena, tok);
      w_parser = parse_ctx->MakeWordParser(lex, line_reader);
      Tuple2<syntax_asdl::BracedVarSub*, syntax_asdl::Token*> tup0 = w_parser->ReadBracedVarSub(left_token);
      part = tup0.at0();
      last_token = tup0.at1();
      typ = Id::Expr_CastedDummy;
      opaque = reinterpret_cast<Token*>(part);
      done = p->addtoken(typ, opaque, gr->tokens->at(typ));
      continue;
    }
    if ((tok->id == Id::Left_SingleQuote || tok->id == Id::Left_TSingleQuote || tok->id == Id::Left_RSingleQuote || tok->id == Id::Left_RTSingleQuote || tok->id == Id::Left_USingleQuote || tok->id == Id::Left_UTSingleQuote || tok->id == Id::Left_BSingleQuote || tok->id == Id::Left_BTSingleQuote || tok->id == Id::Left_DollarSingleQuote)) {
      if (tok->id == Id::Left_DollarSingleQuote) {
        sq_mode = lex_mode_e::SQ_C;
      }
      else {
        if ((tok->id == Id::Left_USingleQuote || tok->id == Id::Left_UTSingleQuote || tok->id == Id::Left_BSingleQuote || tok->id == Id::Left_BTSingleQuote)) {
          sq_mode = lex_mode_e::J8_Str;
        }
        else {
          sq_mode = lex_mode_e::SQ_Raw;
        }
      }
      left_token = tok;
      line_reader = Alloc<reader::DisallowedLineReader>(parse_ctx->arena, tok);
      w_parser = parse_ctx->MakeWordParser(lex, line_reader);
      tokens = Alloc<List<syntax_asdl::Token*>>();
      last_token = w_parser->ReadSingleQuoted(sq_mode, left_token, tokens, true);
      sval = word_compile::EvalSingleQuoted(left_token->id, tokens);
      sq_part = Alloc<SingleQuoted>(left_token, sval, last_token);
      typ = Id::Expr_CastedDummy;
      opaque = reinterpret_cast<Token*>(sq_part);
      done = p->addtoken(typ, opaque, gr->tokens->at(typ));
      continue;
    }
  }
}

ExprParser::ExprParser(parse_lib::ParseContext* parse_ctx, grammar::Grammar* gr) {
  this->parse_ctx = parse_ctx;
  this->gr = gr;
  this->push_parser = Alloc<parse::Parser>(gr);
  this->pnode_alloc = nullptr;
}

Tuple2<pnode::PNode*, syntax_asdl::Token*> ExprParser::Parse(lexer::Lexer* lexer, int start_symbol) {
  syntax_asdl::Token* last_token = nullptr;
  StackRoot _root0(&lexer);
  StackRoot _root1(&last_token);

  this->push_parser->setup(start_symbol, this->pnode_alloc);
  try {
    last_token = _PushYshTokens(this->parse_ctx, this->gr, this->push_parser, lexer);
  }
  catch (parse::ParseError* e) {
    p_die(StrFormat("Syntax error in expression (near %s)", ui::PrettyId(e->tok->id)), e->tok);
  }
  return Tuple2<pnode::PNode*, syntax_asdl::Token*>(this->push_parser->rootnode, last_token);
}

ctx_PNodeAllocator::ctx_PNodeAllocator(expr_parse::ExprParser* ep) {
  gHeap.PushRoot(reinterpret_cast<RawObject**>(&(this->expr_parser)));
  this->expr_parser = ep;
  this->expr_parser->pnode_alloc = Alloc<PNodeAllocator>();
}

ctx_PNodeAllocator::~ctx_PNodeAllocator() {
  this->expr_parser->pnode_alloc->Clear();
  this->expr_parser->pnode_alloc = nullptr;
  gHeap.PopRoot();
}

}  // define namespace expr_parse

namespace expr_to_ast {  // define

using id_kind_asdl::Id;
using id_kind_asdl::Id_t;
using id_kind_asdl::Id_str;
using id_kind_asdl::Kind;
using syntax_asdl::Token;
using syntax_asdl::SimpleVarSub;
using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::DoubleQuoted;
using syntax_asdl::SingleQuoted;
using syntax_asdl::BracedVarSub;
using syntax_asdl::CommandSub;
using syntax_asdl::ShArrayLiteral;
using syntax_asdl::command;
using syntax_asdl::expr;
using syntax_asdl::expr_e;
using syntax_asdl::expr_t;
using syntax_asdl::expr_context_e;
using syntax_asdl::re;
using syntax_asdl::re_t;
using syntax_asdl::re_repeat;
using syntax_asdl::re_repeat_t;
using syntax_asdl::class_literal_term;
using syntax_asdl::class_literal_term_t;
using syntax_asdl::PosixClass;
using syntax_asdl::PerlClass;
using syntax_asdl::NameType;
using syntax_asdl::y_lhs_t;
using syntax_asdl::Comprehension;
using syntax_asdl::Subscript;
using syntax_asdl::Attribute;
using syntax_asdl::proc_sig;
using syntax_asdl::proc_sig_t;
using syntax_asdl::Param;
using syntax_asdl::RestParam;
using syntax_asdl::ParamGroup;
using syntax_asdl::NamedArg;
using syntax_asdl::ArgList;
using syntax_asdl::pat;
using syntax_asdl::pat_t;
using syntax_asdl::TypeExpr;
using syntax_asdl::Func;
using syntax_asdl::Eggex;
using syntax_asdl::EggexFlag;
using syntax_asdl::CharCode;
using syntax_asdl::CharRange;
using value_asdl::value;
using value_asdl::value_t;
using error::p_die;
GLOBAL_DICT(PERL_CLASSES, BigStr*, BigStr*, 4, {S_Crn COMMA S_pfC COMMA S_Cbp COMMA S_anC}, {S_Crn COMMA S_pfC COMMA S_pfC COMMA S_anC});
GLOBAL_LIST(POSIX_CLASSES, BigStr*, 12, {S_gja COMMA S_mij COMMA S_urB COMMA S_iya COMMA S_EvD COMMA S_Coo COMMA S_nld COMMA S_fgo COMMA S_bdb COMMA S_jji COMMA S_syu COMMA S_dgp});
BigStr* RANGE_POINT_TOO_LONG = S_Fza;
BigStr* POS_ARG_MISPLACED = S_Fpz;
int NT_OFFSET = 256;

Transformer::Transformer(grammar::Grammar* gr) {
  this->number2symbol = gr->number2symbol;
}

syntax_asdl::expr_t* Transformer::_LeftAssoc(pnode::PNode* p_node) {
  int i;
  int n;
  syntax_asdl::expr_t* left = nullptr;
  pnode::PNode* op = nullptr;
  syntax_asdl::expr_t* right = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&left);
  StackRoot _root2(&op);
  StackRoot _root3(&right);

  i = 1;
  n = p_node->NumChildren();
  left = this->Expr(p_node->GetChild(0));
  while (i < n) {
    op = p_node->GetChild(i);
    right = this->Expr(p_node->GetChild((i + 1)));
    left = Alloc<expr::Binary>(op->tok, left, right);
    i += 2;
  }
  return left;
}

syntax_asdl::expr_t* Transformer::_Trailer(syntax_asdl::expr_t* base, pnode::PNode* p_trailer) {
  syntax_asdl::Token* tok0 = nullptr;
  int typ0;
  syntax_asdl::Token* lparen = nullptr;
  syntax_asdl::Token* rparen = nullptr;
  syntax_asdl::ArgList* arglist = nullptr;
  pnode::PNode* p = nullptr;
  pnode::PNode* p_args = nullptr;
  int n;
  syntax_asdl::expr_t* subscript = nullptr;
  List<syntax_asdl::expr_t*>* slices = nullptr;
  syntax_asdl::Token* comma_tok = nullptr;
  syntax_asdl::Token* attr = nullptr;
  StackRoot _root0(&base);
  StackRoot _root1(&p_trailer);
  StackRoot _root2(&tok0);
  StackRoot _root3(&lparen);
  StackRoot _root4(&rparen);
  StackRoot _root5(&arglist);
  StackRoot _root6(&p);
  StackRoot _root7(&p_args);
  StackRoot _root8(&subscript);
  StackRoot _root9(&slices);
  StackRoot _root10(&comma_tok);
  StackRoot _root11(&attr);

  tok0 = p_trailer->GetChild(0)->tok;
  typ0 = p_trailer->GetChild(0)->typ;
  if (typ0 == Id::Op_LParen) {
    lparen = tok0;
    rparen = p_trailer->GetChild(-1)->tok;
    arglist = Alloc<ArgList>(lparen, Alloc<List<syntax_asdl::expr_t*>>(), nullptr, Alloc<List<syntax_asdl::NamedArg*>>(), nullptr, nullptr, rparen);
    if (p_trailer->NumChildren() == 2) {
      return Alloc<expr::FuncCall>(base, arglist);
    }
    p = p_trailer->GetChild(1);
    this->_ArgList(p, arglist);
    return Alloc<expr::FuncCall>(base, arglist);
  }
  if (typ0 == Id::Op_LBracket) {
    p_args = p_trailer->GetChild(1);
    n = p_args->NumChildren();
    if (n == 1) {
      subscript = this->_Subscript(p_args->GetChild(0));
    }
    else {
      slices = Alloc<List<syntax_asdl::expr_t*>>();
      for (int i = 0; i < n; i += 2) {
        slices->append(this->_Subscript(p_args->GetChild(i)));
      }
      comma_tok = p_args->GetChild(1)->tok;
      subscript = Alloc<expr::Tuple>(comma_tok, slices, expr_context_e::Store);
    }
    return Alloc<Subscript>(tok0, base, subscript);
  }
  if ((typ0 == Id::Expr_Dot || typ0 == Id::Expr_RArrow || typ0 == Id::Expr_RDArrow)) {
    attr = p_trailer->GetChild(1)->tok;
    return Alloc<Attribute>(base, tok0, attr, lexer::TokenVal(attr), expr_context_e::Store);
  }
  assert(0);  // AssertionError
}

Tuple2<syntax_asdl::expr_t*, syntax_asdl::expr_t*> Transformer::_DictPair(pnode::PNode* p_node) {
  int typ;
  syntax_asdl::expr_t* key = nullptr;
  syntax_asdl::expr_t* val = nullptr;
  syntax_asdl::Token* tok0 = nullptr;
  int id_;
  value::Str* key_str = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&key);
  StackRoot _root2(&val);
  StackRoot _root3(&tok0);
  StackRoot _root4(&key_str);

  typ = p_node->GetChild(0)->typ;
  if ((typ == grammar_nt::sq_string || typ == grammar_nt::dq_string)) {
    key = this->Expr(p_node->GetChild(0));
    val = this->Expr(p_node->GetChild(2));
    return Tuple2<syntax_asdl::expr_t*, syntax_asdl::expr_t*>(key, val);
  }
  tok0 = p_node->GetChild(0)->tok;
  id_ = tok0->id;
  if (id_ == Id::Expr_Name) {
    key_str = Alloc<value::Str>(lexer::TokenVal(tok0));
    key = Alloc<expr::Const>(tok0, key_str);
    if (p_node->NumChildren() >= 3) {
      val = this->Expr(p_node->GetChild(2));
    }
    else {
      val = expr::Implicit;
    }
  }
  if (id_ == Id::Op_LBracket) {
    key = this->Expr(p_node->GetChild(1));
    val = this->Expr(p_node->GetChild(4));
    return Tuple2<syntax_asdl::expr_t*, syntax_asdl::expr_t*>(key, val);
  }
  return Tuple2<syntax_asdl::expr_t*, syntax_asdl::expr_t*>(key, val);
}

expr::Dict* Transformer::_Dict(pnode::PNode* parent, pnode::PNode* p_node) {
  List<syntax_asdl::expr_t*>* keys = nullptr;
  List<syntax_asdl::expr_t*>* values = nullptr;
  int n;
  syntax_asdl::expr_t* key = nullptr;
  syntax_asdl::expr_t* val = nullptr;
  StackRoot _root0(&parent);
  StackRoot _root1(&p_node);
  StackRoot _root2(&keys);
  StackRoot _root3(&values);
  StackRoot _root4(&key);
  StackRoot _root5(&val);

  if (p_node->typ == Id::Op_RBrace) {
    return Alloc<expr::Dict>(parent->tok, Alloc<List<syntax_asdl::expr_t*>>(), Alloc<List<syntax_asdl::expr_t*>>());
  }
  keys = Alloc<List<syntax_asdl::expr_t*>>();
  values = Alloc<List<syntax_asdl::expr_t*>>();
  n = p_node->NumChildren();
  for (int i = 0; i < n; i += 2) {
    Tuple2<syntax_asdl::expr_t*, syntax_asdl::expr_t*> tup0 = this->_DictPair(p_node->GetChild(i));
    key = tup0.at0();
    val = tup0.at1();
    keys->append(key);
    values->append(val);
  }
  return Alloc<expr::Dict>(parent->tok, keys, values);
}

syntax_asdl::expr_t* Transformer::_Tuple(pnode::PNode* parent) {
  int n;
  List<syntax_asdl::expr_t*>* elts = nullptr;
  pnode::PNode* p_node = nullptr;
  StackRoot _root0(&parent);
  StackRoot _root1(&elts);
  StackRoot _root2(&p_node);

  n = parent->NumChildren();
  if (n == 1) {
    return this->Expr(parent->GetChild(0));
  }
  if (n == 2) {
    p_die(S_iFj, parent->GetChild(1)->tok);
  }
  elts = Alloc<List<syntax_asdl::expr_t*>>();
  for (int i = 0; i < n; i += 2) {
    p_node = parent->GetChild(i);
    elts->append(this->Expr(p_node));
  }
  return Alloc<expr::Tuple>(parent->tok, elts, expr_context_e::Store);
}

syntax_asdl::expr_t* Transformer::_TestlistComp(pnode::PNode* parent, pnode::PNode* p_node, int id0) {
  int n;
  syntax_asdl::expr_t* elt = nullptr;
  syntax_asdl::Comprehension* comp = nullptr;
  List<syntax_asdl::expr_t*>* elts = nullptr;
  StackRoot _root0(&parent);
  StackRoot _root1(&p_node);
  StackRoot _root2(&elt);
  StackRoot _root3(&comp);
  StackRoot _root4(&elts);

  n = p_node->NumChildren();
  if ((n > 1 and p_node->GetChild(1)->typ == grammar_nt::comp_for)) {
    elt = this->Expr(p_node->GetChild(0));
    comp = this->_CompFor(p_node->GetChild(1));
    if (id0 == Id::Op_LParen) {
      return Alloc<expr::GeneratorExp>(elt, NewList<syntax_asdl::Comprehension*>(std::initializer_list<syntax_asdl::Comprehension*>{comp}));
    }
    if (id0 == Id::Op_LBracket) {
      return Alloc<expr::ListComp>(parent->tok, elt, NewList<syntax_asdl::Comprehension*>(std::initializer_list<syntax_asdl::Comprehension*>{comp}));
    }
    assert(0);  // AssertionError
  }
  if (id0 == Id::Op_LParen) {
    if (n == 1) {
      return this->Expr(p_node->GetChild(0));
    }
    if (p_node->GetChild(1)->typ == Id::Arith_Comma) {
      return this->_Tuple(p_node);
    }
    assert(0);  // AssertionError
  }
  if (id0 == Id::Op_LBracket) {
    elts = Alloc<List<syntax_asdl::expr_t*>>();
    for (int i = 0; i < n; i += 2) {
      elts->append(this->Expr(p_node->GetChild(i)));
    }
    return Alloc<expr::List>(parent->tok, elts, expr_context_e::Store);
  }
  assert(0);  // AssertionError
}

syntax_asdl::expr_t* Transformer::_Atom(pnode::PNode* parent) {
  syntax_asdl::Token* tok = nullptr;
  int id_;
  int n;
  syntax_asdl::expr_t* child = nullptr;
  int i;
  syntax_asdl::Token* name_tok = nullptr;
  StackRoot _root0(&parent);
  StackRoot _root1(&tok);
  StackRoot _root2(&child);
  StackRoot _root3(&name_tok);

  tok = parent->GetChild(0)->tok;
  id_ = tok->id;
  n = parent->NumChildren();
  if (id_ == Id::Op_LParen) {
    if (n == 2) {
      return Alloc<expr::Tuple>(tok, Alloc<List<syntax_asdl::expr_t*>>(), expr_context_e::Store);
    }
    return this->_TestlistComp(parent, parent->GetChild(1), id_);
  }
  if (id_ == Id::Op_LBracket) {
    if (n == 2) {
      return Alloc<expr::List>(tok, Alloc<List<syntax_asdl::expr_t*>>(), expr_context_e::Store);
    }
    return this->_TestlistComp(parent, parent->GetChild(1), id_);
  }
  if (id_ == Id::Left_CaretBracket) {
    child = this->Expr(parent->GetChild(1));
    return Alloc<expr::Literal>(child);
  }
  if (id_ == Id::Op_LBrace) {
    i = 1;
    if (parent->GetChild(i)->typ == Id::Op_Newline) {
      i += 1;
    }
    return this->_Dict(parent, parent->GetChild(i));
  }
  if (id_ == Id::Arith_Amp) {
    n = parent->NumChildren();
    if (n >= 3) {
      p_die(S_ynk, parent->GetChild(2)->tok);
    }
    name_tok = parent->GetChild(1)->tok;
    return Alloc<expr::Place>(name_tok, lexer::TokenVal(name_tok), Alloc<List<syntax_asdl::place_op_t*>>());
  }
  if (id_ == Id::Expr_Func) {
    return Alloc<expr::Lambda>(Alloc<List<syntax_asdl::NameType*>>(), expr::Implicit);
  }
  if (id_ == Id::Expr_DecInt) {
    p_die(S_rBg, parent->GetChild(1)->tok);
  }
  if (id_ == Id::Expr_Float) {
    p_die(S_zxo, parent->GetChild(1)->tok);
  }
  assert(0);  // AssertionError
}

syntax_asdl::NameType* Transformer::_NameType(pnode::PNode* p_node) {
  syntax_asdl::Token* name_tok = nullptr;
  syntax_asdl::TypeExpr* typ = nullptr;
  int n;
  StackRoot _root0(&p_node);
  StackRoot _root1(&name_tok);
  StackRoot _root2(&typ);

  name_tok = p_node->GetChild(0)->tok;
  typ = nullptr;
  n = p_node->NumChildren();
  if (n == 2) {
    typ = this->_TypeExpr(p_node->GetChild(1));
  }
  if (n == 3) {
    typ = this->_TypeExpr(p_node->GetChild(2));
  }
  return Alloc<NameType>(name_tok, lexer::TokenVal(name_tok), typ);
}

List<syntax_asdl::NameType*>* Transformer::_NameTypeList(pnode::PNode* p_node) {
  List<syntax_asdl::NameType*>* results = nullptr;
  int n;
  StackRoot _root0(&p_node);
  StackRoot _root1(&results);

  results = Alloc<List<syntax_asdl::NameType*>>();
  n = p_node->NumChildren();
  for (int i = 0; i < n; i += 2) {
    results->append(this->_NameType(p_node->GetChild(i)));
  }
  return results;
}

syntax_asdl::Comprehension* Transformer::_CompFor(pnode::PNode* p_node) {
  List<syntax_asdl::NameType*>* lhs = nullptr;
  syntax_asdl::expr_t* iterable = nullptr;
  syntax_asdl::expr_t* cond = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&lhs);
  StackRoot _root2(&iterable);
  StackRoot _root3(&cond);

  lhs = this->_NameTypeList(p_node->GetChild(1));
  iterable = this->Expr(p_node->GetChild(3));
  if (p_node->NumChildren() >= 6) {
    cond = this->Expr(p_node->GetChild(5));
  }
  else {
    cond = nullptr;
  }
  return Alloc<Comprehension>(lhs, iterable, cond);
}

syntax_asdl::expr_t* Transformer::_CompareChain(pnode::PNode* parent) {
  List<syntax_asdl::Token*>* cmp_ops = nullptr;
  List<syntax_asdl::expr_t*>* comparators = nullptr;
  syntax_asdl::expr_t* left = nullptr;
  int i;
  int n;
  pnode::PNode* p = nullptr;
  syntax_asdl::Token* op = nullptr;
  StackRoot _root0(&parent);
  StackRoot _root1(&cmp_ops);
  StackRoot _root2(&comparators);
  StackRoot _root3(&left);
  StackRoot _root4(&p);
  StackRoot _root5(&op);

  cmp_ops = Alloc<List<syntax_asdl::Token*>>();
  comparators = Alloc<List<syntax_asdl::expr_t*>>();
  left = this->Expr(parent->GetChild(0));
  i = 1;
  n = parent->NumChildren();
  while (i < n) {
    p = parent->GetChild(i);
    op = p->GetChild(0)->tok;
    if (p->NumChildren() == 2) {
      if (op->id == Id::Expr_Not) {
        op->id = Id::Node_NotIn;
      }
      else {
        if (op->id == Id::Expr_Is) {
          op->id = Id::Node_IsNot;
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
    else {
      ;  // pass
    }
    cmp_ops->append(op);
    i += 1;
    comparators->append(this->Expr(parent->GetChild(i)));
    i += 1;
  }
  return Alloc<expr::Compare>(left, cmp_ops, comparators);
}

syntax_asdl::expr_t* Transformer::_Subscript(pnode::PNode* parent) {
  int typ0;
  int n;
  syntax_asdl::expr_t* lower = nullptr;
  syntax_asdl::Token* op_tok = nullptr;
  syntax_asdl::expr_t* upper = nullptr;
  StackRoot _root0(&parent);
  StackRoot _root1(&lower);
  StackRoot _root2(&op_tok);
  StackRoot _root3(&upper);

  typ0 = parent->GetChild(0)->typ;
  n = parent->NumChildren();
  if (typ0 == grammar_nt::expr) {
    if (n == 3) {
      lower = this->Expr(parent->GetChild(0));
      op_tok = parent->GetChild(1)->tok;
      upper = this->Expr(parent->GetChild(2));
    }
    else {
      if (n == 2) {
        lower = this->Expr(parent->GetChild(0));
        op_tok = parent->GetChild(1)->tok;
        upper = nullptr;
      }
      else {
        return this->Expr(parent->GetChild(0));
      }
    }
  }
  else {
    lower = nullptr;
    if (n == 1) {
      op_tok = parent->GetChild(0)->tok;
      upper = nullptr;
    }
    else {
      op_tok = parent->GetChild(0)->tok;
      upper = this->Expr(parent->GetChild(1));
    }
  }
  return Alloc<expr::Slice>(lower, op_tok, upper);
}

syntax_asdl::expr_t* Transformer::Expr(pnode::PNode* pnode) {
  int typ;
  syntax_asdl::expr_t* test = nullptr;
  syntax_asdl::expr_t* body = nullptr;
  syntax_asdl::expr_t* orelse = nullptr;
  int n;
  List<syntax_asdl::NameType*>* params = nullptr;
  syntax_asdl::Token* op_tok = nullptr;
  pnode::PNode* op = nullptr;
  pnode::PNode* e = nullptr;
  syntax_asdl::expr_t* node = nullptr;
  int i;
  syntax_asdl::expr_t* factor = nullptr;
  syntax_asdl::DoubleQuoted* dq = nullptr;
  syntax_asdl::Token* tok = nullptr;
  BigStr* bare = nullptr;
  BigStr* tok_str = nullptr;
  BigStr* c_under = nullptr;
  bool ok;
  mops::BigInt big_int;
  value_asdl::value_t* cval = nullptr;
  BigStr* s = nullptr;
  BigStr* hex_str = nullptr;
  int code_point;
  StackRoot _root0(&pnode);
  StackRoot _root1(&test);
  StackRoot _root2(&body);
  StackRoot _root3(&orelse);
  StackRoot _root4(&params);
  StackRoot _root5(&op_tok);
  StackRoot _root6(&op);
  StackRoot _root7(&e);
  StackRoot _root8(&node);
  StackRoot _root9(&factor);
  StackRoot _root10(&dq);
  StackRoot _root11(&tok);
  StackRoot _root12(&bare);
  StackRoot _root13(&tok_str);
  StackRoot _root14(&c_under);
  StackRoot _root15(&cval);
  StackRoot _root16(&s);
  StackRoot _root17(&hex_str);

  typ = pnode->typ;
  if (typ == grammar_nt::ysh_expr) {
    return this->Expr(pnode->GetChild(1));
  }
  if (typ == grammar_nt::command_expr) {
    return this->Expr(pnode->GetChild(0));
  }
  if (typ == grammar_nt::atom) {
    if (pnode->NumChildren() == 1) {
      return this->Expr(pnode->GetChild(0));
    }
    return this->_Atom(pnode);
  }
  if (typ == grammar_nt::testlist) {
    return this->_Tuple(pnode);
  }
  if (typ == grammar_nt::test) {
    if (pnode->NumChildren() == 1) {
      return this->Expr(pnode->GetChild(0));
    }
    test = this->Expr(pnode->GetChild(2));
    body = this->Expr(pnode->GetChild(0));
    orelse = this->Expr(pnode->GetChild(4));
    return Alloc<expr::IfExp>(test, body, orelse);
  }
  if (typ == grammar_nt::lambdef) {
    n = pnode->NumChildren();
    if (n == 4) {
      params = this->_NameTypeList(pnode->GetChild(1));
    }
    else {
      params = Alloc<List<syntax_asdl::NameType*>>();
    }
    body = this->Expr(pnode->GetChild((n - 1)));
    return Alloc<expr::Lambda>(params, body);
  }
  if (typ == grammar_nt::or_test) {
    return this->_LeftAssoc(pnode);
  }
  if (typ == grammar_nt::and_test) {
    return this->_LeftAssoc(pnode);
  }
  if (typ == grammar_nt::not_test) {
    if (pnode->NumChildren() == 1) {
      return this->Expr(pnode->GetChild(0));
    }
    op_tok = pnode->GetChild(0)->tok;
    return Alloc<expr::Unary>(op_tok, this->Expr(pnode->GetChild(1)));
  }
  else {
    if (typ == grammar_nt::comparison) {
      if (pnode->NumChildren() == 1) {
        return this->Expr(pnode->GetChild(0));
      }
      return this->_CompareChain(pnode);
    }
    else {
      if (typ == grammar_nt::range_expr) {
        n = pnode->NumChildren();
        if (n == 1) {
          return this->Expr(pnode->GetChild(0));
        }
        if (n == 3) {
          return Alloc<expr::Range>(this->Expr(pnode->GetChild(0)), pnode->GetChild(1)->tok, this->Expr(pnode->GetChild(2)));
        }
        assert(0);  // AssertionError
      }
      else {
        if (typ == grammar_nt::expr) {
          return this->_LeftAssoc(pnode);
        }
      }
    }
  }
  if (typ == grammar_nt::xor_expr) {
    return this->_LeftAssoc(pnode);
  }
  if (typ == grammar_nt::and_expr) {
    return this->_LeftAssoc(pnode);
  }
  else {
    if (typ == grammar_nt::shift_expr) {
      return this->_LeftAssoc(pnode);
    }
    else {
      if (typ == grammar_nt::arith_expr) {
        return this->_LeftAssoc(pnode);
      }
      else {
        if (typ == grammar_nt::term) {
          return this->_LeftAssoc(pnode);
        }
        else {
          if (typ == grammar_nt::factor) {
            if (pnode->NumChildren() == 1) {
              return this->Expr(pnode->GetChild(0));
            }
            op = pnode->GetChild(0);
            e = pnode->GetChild(1);
            return Alloc<expr::Unary>(op->tok, this->Expr(e));
          }
          else {
            if (typ == grammar_nt::power) {
              node = this->Expr(pnode->GetChild(0));
              if (pnode->NumChildren() == 1) {
                return node;
              }
              n = pnode->NumChildren();
              i = 1;
              while ((i < n and pnode->GetChild(i)->typ == grammar_nt::trailer)) {
                node = this->_Trailer(node, pnode->GetChild(i));
                i += 1;
              }
              if (i != n) {
                op_tok = pnode->GetChild(i)->tok;
                factor = this->Expr(pnode->GetChild((i + 1)));
                node = Alloc<expr::Binary>(op_tok, node, factor);
              }
              return node;
            }
            else {
              if (typ == grammar_nt::eggex) {
                return this->_Eggex(pnode);
              }
              else {
                if (typ == grammar_nt::ysh_expr_sub) {
                  return this->Expr(pnode->GetChild(0));
                }
                else {
                  if (typ == grammar_nt::sh_array_literal) {
                    return reinterpret_cast<ShArrayLiteral*>(pnode->GetChild(1)->tok);
                  }
                  else {
                    if (typ == grammar_nt::old_sh_array_literal) {
                      return reinterpret_cast<ShArrayLiteral*>(pnode->GetChild(1)->tok);
                    }
                    else {
                      if (typ == grammar_nt::sh_command_sub) {
                        return reinterpret_cast<CommandSub*>(pnode->GetChild(1)->tok);
                      }
                      else {
                        if (typ == grammar_nt::braced_var_sub) {
                          return reinterpret_cast<BracedVarSub*>(pnode->GetChild(1)->tok);
                        }
                        else {
                          if (typ == grammar_nt::dq_string) {
                            dq = reinterpret_cast<DoubleQuoted*>(pnode->GetChild(1)->tok);
                            if (pnode->GetChild(0)->typ == Id::Left_CaretDoubleQuote) {
                              return Alloc<expr::Literal>(dq);
                            }
                            return dq;
                          }
                          else {
                            if (typ == grammar_nt::sq_string) {
                              return reinterpret_cast<SingleQuoted*>(pnode->GetChild(1)->tok);
                            }
                            else {
                              if (typ == grammar_nt::simple_var_sub) {
                                tok = pnode->GetChild(0)->tok;
                                if (tok->id == Id::VSub_DollarName) {
                                  bare = lexer::TokenSliceLeft(tok, 1);
                                  p_die(StrFormat("In expressions, remove $ and use `%s`, or sometimes \"$%s\"", bare, bare), tok);
                                }
                                return Alloc<SimpleVarSub>(tok);
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  tok = pnode->tok;
  if (typ == Id::Expr_Name) {
    return Alloc<expr::Var>(tok, lexer::TokenVal(tok));
  }
  tok_str = lexer::TokenVal(tok);
  c_under = tok_str->replace(S_tci, S_Aoo);
  if (typ == Id::Expr_DecInt) {
    Tuple2<bool, mops::BigInt> tup1 = mops::FromStr2(c_under);
    ok = tup1.at0();
    big_int = tup1.at1();
    if (!ok) {
      p_die(S_jgg, tok);
    }
    cval = Alloc<value::Int>(big_int);
  }
  else {
    if (typ == Id::Expr_BinInt) {
      Tuple2<bool, mops::BigInt> tup2 = mops::FromStr2(c_under->slice(2), 2);
      ok = tup2.at0();
      big_int = tup2.at1();
      if (!ok) {
        p_die(S_DCt, tok);
      }
      cval = Alloc<value::Int>(big_int);
    }
    else {
      if (typ == Id::Expr_OctInt) {
        Tuple2<bool, mops::BigInt> tup3 = mops::FromStr2(c_under->slice(2), 8);
        ok = tup3.at0();
        big_int = tup3.at1();
        if (!ok) {
          p_die(S_rDF, tok);
        }
        cval = Alloc<value::Int>(big_int);
      }
      else {
        if (typ == Id::Expr_HexInt) {
          Tuple2<bool, mops::BigInt> tup4 = mops::FromStr2(c_under->slice(2), 16);
          ok = tup4.at0();
          big_int = tup4.at1();
          if (!ok) {
            p_die(S_uDt, tok);
          }
          cval = Alloc<value::Int>(big_int);
        }
        else {
          if (typ == Id::Expr_Float) {
            cval = Alloc<value::Float>(to_float(c_under));
          }
          else {
            if (typ == Id::Expr_Null) {
              cval = value::Null;
            }
            else {
              if (typ == Id::Expr_True) {
                cval = Alloc<value::Bool>(true);
              }
              else {
                if (typ == Id::Expr_False) {
                  cval = Alloc<value::Bool>(false);
                }
                else {
                  if (typ == Id::Char_OneChar) {
                    s = consts::LookupCharC(lexer::TokenSliceLeft(tok, 1));
                    cval = Alloc<value::Str>(s);
                  }
                  else {
                    if (typ == Id::Char_YHex) {
                      hex_str = lexer::TokenSliceLeft(tok, 2);
                      s = chr(to_int(hex_str, 16));
                      cval = Alloc<value::Str>(s);
                    }
                    else {
                      if (typ == Id::Char_UBraced) {
                        hex_str = lexer::TokenSlice(tok, 3, -1);
                        code_point = to_int(hex_str, 16);
                        s = j8::Utf8Encode(code_point);
                        cval = Alloc<value::Str>(s);
                      }
                      else {
                        assert(0);  // AssertionError
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  return Alloc<expr::Const>(tok, cval);
}

void Transformer::_CheckLhs(syntax_asdl::expr_t* lhs) {
  syntax_asdl::expr_t* UP_lhs = nullptr;
  StackRoot _root0(&lhs);
  StackRoot _root1(&UP_lhs);

  UP_lhs = lhs;
  switch (lhs->tag()) {
    case expr_e::Var: {
      ;  // pass
    }
      break;
    case expr_e::Subscript: {
      Subscript* lhs = static_cast<Subscript*>(UP_lhs);
      this->_CheckLhs(lhs->obj);
    }
      break;
    case expr_e::Attribute: {
      Attribute* lhs = static_cast<Attribute*>(UP_lhs);
      this->_CheckLhs(lhs->obj);
    }
      break;
    default: {
      p_die(S_juC, location::TokenForExpr(lhs));
    }
  }
}

List<syntax_asdl::y_lhs_t*>* Transformer::_LhsExprList(pnode::PNode* p_node) {
  List<syntax_asdl::y_lhs_t*>* lhs_list = nullptr;
  int n;
  pnode::PNode* p = nullptr;
  syntax_asdl::expr_t* e = nullptr;
  syntax_asdl::expr_t* UP_e = nullptr;
  syntax_asdl::loc_t* blame = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&lhs_list);
  StackRoot _root2(&p);
  StackRoot _root3(&e);
  StackRoot _root4(&UP_e);
  StackRoot _root5(&blame);

  lhs_list = Alloc<List<syntax_asdl::y_lhs_t*>>();
  n = p_node->NumChildren();
  for (int i = 0; i < n; i += 2) {
    p = p_node->GetChild(i);
    e = this->Expr(p);
    UP_e = e;
    switch (e->tag()) {
      case expr_e::Var: {
        expr::Var* e = static_cast<expr::Var*>(UP_e);
        lhs_list->append(e->left);
      }
        break;
      case expr_e::Subscript: {
        Subscript* e = static_cast<Subscript*>(UP_e);
        this->_CheckLhs(e);
        lhs_list->append(e);
      }
        break;
      case expr_e::Attribute: {
        Attribute* e = static_cast<Attribute*>(UP_e);
        this->_CheckLhs(e);
        if (e->op->id != Id::Expr_Dot) {
          p_die(S_rcx, e->op);
        }
        lhs_list->append(e);
      }
        break;
      default: {
        ;  // pass
        if (p->tok) {
          blame = p->tok;
        }
        else {
          blame = loc::Missing;
        }
        p_die(S_gzm, blame);
      }
    }
  }
  return lhs_list;
}

command::VarDecl* Transformer::MakeVarDecl(pnode::PNode* p_node) {
  List<syntax_asdl::NameType*>* lhs = nullptr;
  int n;
  syntax_asdl::expr_t* rhs = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&lhs);
  StackRoot _root2(&rhs);

  lhs = this->_NameTypeList(p_node->GetChild(0));
  n = p_node->NumChildren();
  if (n >= 3) {
    rhs = this->Expr(p_node->GetChild(2));
  }
  else {
    rhs = nullptr;
  }
  return Alloc<command::VarDecl>(nullptr, lhs, rhs);
}

command::Mutation* Transformer::MakeMutation(pnode::PNode* p_node) {
  List<syntax_asdl::y_lhs_t*>* lhs_list = nullptr;
  syntax_asdl::Token* op_tok = nullptr;
  syntax_asdl::expr_t* rhs = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&lhs_list);
  StackRoot _root2(&op_tok);
  StackRoot _root3(&rhs);

  lhs_list = this->_LhsExprList(p_node->GetChild(0));
  op_tok = p_node->GetChild(1)->tok;
  if ((len(lhs_list) > 1 and op_tok->id != Id::Arith_Equal)) {
    p_die(S_Dtp, op_tok);
  }
  rhs = this->Expr(p_node->GetChild(2));
  return Alloc<command::Mutation>(nullptr, lhs_list, op_tok, rhs);
}

syntax_asdl::EggexFlag* Transformer::_EggexFlag(pnode::PNode* p_node) {
  int n;
  StackRoot _root0(&p_node);

  n = p_node->NumChildren();
  if (n == 1) {
    return Alloc<EggexFlag>(false, p_node->GetChild(0)->tok);
  }
  else {
    if (n == 2) {
      return Alloc<EggexFlag>(true, p_node->GetChild(1)->tok);
    }
    else {
      assert(0);  // AssertionError
    }
  }
}

syntax_asdl::Eggex* Transformer::_Eggex(pnode::PNode* p_node) {
  syntax_asdl::Token* left = nullptr;
  syntax_asdl::re_t* regex = nullptr;
  List<syntax_asdl::EggexFlag*>* flags = nullptr;
  syntax_asdl::Token* trans_pref = nullptr;
  int i;
  pnode::PNode* current = nullptr;
  BigStr* canonical_flags = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&left);
  StackRoot _root2(&regex);
  StackRoot _root3(&flags);
  StackRoot _root4(&trans_pref);
  StackRoot _root5(&current);
  StackRoot _root6(&canonical_flags);

  left = p_node->GetChild(0)->tok;
  regex = this->_Regex(p_node->GetChild(1));
  flags = Alloc<List<syntax_asdl::EggexFlag*>>();
  trans_pref = nullptr;
  i = 2;
  current = p_node->GetChild(i);
  if (current->typ == Id::Op_Semi) {
    i += 1;
    while (true) {
      current = p_node->GetChild(i);
      if (current->typ != grammar_nt::re_flag) {
        break;
      }
      flags->append(this->_EggexFlag(current));
      i += 1;
    }
    if (current->typ == Id::Op_Semi) {
      i += 1;
      trans_pref = p_node->GetChild(i)->tok;
    }
  }
  if ((trans_pref == nullptr or str_equals(lexer::TokenVal(trans_pref), S_ith))) {
    canonical_flags = regex_translate::CanonicalFlags(flags);
  }
  else {
    canonical_flags = nullptr;
  }
  return Alloc<Eggex>(left, regex, flags, trans_pref, canonical_flags);
}

syntax_asdl::pat_t* Transformer::YshCasePattern(pnode::PNode* pnode) {
  pnode::PNode* pattern = nullptr;
  int typ;
  List<syntax_asdl::expr_t*>* exprs = nullptr;
  pnode::PNode* child = nullptr;
  syntax_asdl::expr_t* expr = nullptr;
  StackRoot _root0(&pnode);
  StackRoot _root1(&pattern);
  StackRoot _root2(&exprs);
  StackRoot _root3(&child);
  StackRoot _root4(&expr);

  pattern = pnode->GetChild(0);
  typ = pattern->typ;
  if (typ == Id::Op_LParen) {
    pattern = pnode->GetChild(1);
    typ = pattern->typ;
    if (typ == grammar_nt::pat_else) {
      return pat::Else;
    }
    if (typ == grammar_nt::pat_exprs) {
      exprs = Alloc<List<syntax_asdl::expr_t*>>();
      for (int i = 0; i < pattern->NumChildren(); ++i) {
        child = pattern->GetChild(i);
        if (child->typ == grammar_nt::expr) {
          expr = this->Expr(child);
          exprs->append(expr);
        }
      }
      return Alloc<pat::YshExprs>(exprs);
    }
  }
  if (typ == grammar_nt::eggex) {
    return this->_Eggex(pattern);
  }
  assert(0);  // AssertionError
}

syntax_asdl::expr_t* Transformer::_BlockArg(pnode::PNode* p_node) {
  int n;
  pnode::PNode* child = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&child);

  n = p_node->NumChildren();
  if (n == 1) {
    child = p_node->GetChild(0);
    return this->Expr(child);
  }
  p_die(S_ago, p_node->tok);
}

void Transformer::_Argument(pnode::PNode* p_node, bool after_semi, syntax_asdl::ArgList* arglist) {
  List<syntax_asdl::expr_t*>* pos_args = nullptr;
  List<syntax_asdl::NamedArg*>* named_args = nullptr;
  int n;
  pnode::PNode* child = nullptr;
  syntax_asdl::expr_t* arg = nullptr;
  syntax_asdl::Token* tok0 = nullptr;
  expr::Spread* spread_expr = nullptr;
  syntax_asdl::expr_t* elt = nullptr;
  syntax_asdl::Comprehension* comp = nullptr;
  syntax_asdl::NamedArg* n1 = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&arglist);
  StackRoot _root2(&pos_args);
  StackRoot _root3(&named_args);
  StackRoot _root4(&child);
  StackRoot _root5(&arg);
  StackRoot _root6(&tok0);
  StackRoot _root7(&spread_expr);
  StackRoot _root8(&elt);
  StackRoot _root9(&comp);
  StackRoot _root10(&n1);

  pos_args = arglist->pos_args;
  named_args = arglist->named_args;
  n = p_node->NumChildren();
  if (n == 1) {
    child = p_node->GetChild(0);
    if (after_semi) {
      p_die(POS_ARG_MISPLACED, child->tok);
    }
    arg = this->Expr(child);
    pos_args->append(arg);
    return ;
  }
  if (n == 2) {
    tok0 = p_node->GetChild(0)->tok;
    if (tok0->id == Id::Expr_Ellipsis) {
      spread_expr = Alloc<expr::Spread>(tok0, this->Expr(p_node->GetChild(1)));
      if (after_semi) {
        named_args->append(Alloc<NamedArg>(nullptr, spread_expr));
      }
      else {
        pos_args->append(spread_expr);
      }
      return ;
    }
    if (p_node->GetChild(1)->typ == grammar_nt::comp_for) {
      child = p_node->GetChild(0);
      if (after_semi) {
        p_die(POS_ARG_MISPLACED, child->tok);
      }
      elt = this->Expr(child);
      comp = this->_CompFor(p_node->GetChild(1));
      arg = Alloc<expr::GeneratorExp>(elt, NewList<syntax_asdl::Comprehension*>(std::initializer_list<syntax_asdl::Comprehension*>{comp}));
      pos_args->append(arg);
      return ;
    }
    assert(0);  // AssertionError
  }
  if (n == 3) {
    n1 = Alloc<NamedArg>(p_node->GetChild(0)->tok, this->Expr(p_node->GetChild(2)));
    named_args->append(n1);
    return ;
  }
  assert(0);  // AssertionError
}

void Transformer::_ArgGroup(pnode::PNode* p_node, bool after_semi, syntax_asdl::ArgList* arglist) {
  pnode::PNode* p_child = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&arglist);
  StackRoot _root2(&p_child);

  for (int i = 0; i < p_node->NumChildren(); ++i) {
    p_child = p_node->GetChild(i);
    if (p_child->typ == grammar_nt::argument) {
      this->_Argument(p_child, after_semi, arglist);
    }
  }
}

void Transformer::_ArgList(pnode::PNode* p_node, syntax_asdl::ArgList* arglist) {
  int n;
  int i;
  pnode::PNode* child = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&arglist);
  StackRoot _root2(&child);

  n = p_node->NumChildren();
  if (n == 0) {
    return ;
  }
  i = 0;
  if (i >= n) {
    return ;
  }
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::arg_group) {
    this->_ArgGroup(child, false, arglist);
    i += 1;
  }
  if (i >= n) {
    return ;
  }
  child = p_node->GetChild(i);
  if (child->typ == Id::Op_Semi) {
    arglist->semi_tok = child->tok;
    i += 1;
  }
  if (i >= n) {
    return ;
  }
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::arg_group) {
    this->_ArgGroup(child, true, arglist);
    i += 1;
  }
  if (i >= n) {
    return ;
  }
  child = p_node->GetChild(i);
  if (child->typ == Id::Op_Semi) {
    arglist->semi_tok2 = child->tok;
    i += 1;
  }
  if (i >= n) {
    return ;
  }
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::argument) {
    arglist->block_expr = this->_BlockArg(child);
    i += 1;
  }
}

void Transformer::ProcCallArgs(pnode::PNode* pnode, syntax_asdl::ArgList* arglist) {
  int n;
  pnode::PNode* child1 = nullptr;
  StackRoot _root0(&pnode);
  StackRoot _root1(&arglist);
  StackRoot _root2(&child1);

  n = pnode->NumChildren();
  if (n == 2) {
    return ;
  }
  if (n == 3) {
    child1 = pnode->GetChild(1);
    this->_ArgList(child1, arglist);
    return ;
  }
  assert(0);  // AssertionError
}

syntax_asdl::TypeExpr* Transformer::_TypeExpr(pnode::PNode* pnode) {
  syntax_asdl::TypeExpr* ty = nullptr;
  int n;
  int i;
  syntax_asdl::TypeExpr* p = nullptr;
  StackRoot _root0(&pnode);
  StackRoot _root1(&ty);
  StackRoot _root2(&p);

  ty = TypeExpr::CreateNull();
  ty->tok = pnode->GetChild(0)->tok;
  ty->name = lexer::TokenVal(ty->tok);
  n = pnode->NumChildren();
  if (n == 1) {
    return ty;
  }
  ty->params = Alloc<List<syntax_asdl::TypeExpr*>>();
  i = 2;
  while (i < n) {
    p = this->_TypeExpr(pnode->GetChild(i));
    ty->params->append(p);
    i += 2;
  }
  return ty;
}

syntax_asdl::Param* Transformer::_Param(pnode::PNode* pnode) {
  syntax_asdl::Token* name_tok = nullptr;
  int n;
  syntax_asdl::expr_t* default_val = nullptr;
  syntax_asdl::TypeExpr* type_ = nullptr;
  StackRoot _root0(&pnode);
  StackRoot _root1(&name_tok);
  StackRoot _root2(&default_val);
  StackRoot _root3(&type_);

  name_tok = pnode->GetChild(0)->tok;
  n = pnode->NumChildren();
  default_val = nullptr;
  type_ = nullptr;
  if (n == 1) {
    ;  // pass
  }
  else {
    if (n == 2) {
      type_ = this->_TypeExpr(pnode->GetChild(1));
    }
    else {
      if (n == 3) {
        default_val = this->Expr(pnode->GetChild(2));
      }
      else {
        if (n == 4) {
          type_ = this->_TypeExpr(pnode->GetChild(1));
          default_val = this->Expr(pnode->GetChild(3));
        }
      }
    }
  }
  return Alloc<Param>(name_tok, lexer::TokenVal(name_tok), type_, default_val);
}

syntax_asdl::ParamGroup* Transformer::_ParamGroup(pnode::PNode* p_node) {
  List<syntax_asdl::Param*>* params = nullptr;
  syntax_asdl::RestParam* rest_of = nullptr;
  int n;
  int i;
  pnode::PNode* child = nullptr;
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&params);
  StackRoot _root2(&rest_of);
  StackRoot _root3(&child);
  StackRoot _root4(&tok);

  params = Alloc<List<syntax_asdl::Param*>>();
  rest_of = nullptr;
  n = p_node->NumChildren();
  i = 0;
  while (i < n) {
    child = p_node->GetChild(i);
    if (child->typ == grammar_nt::param) {
      params->append(this->_Param(child));
    }
    else {
      if (child->typ == Id::Expr_Ellipsis) {
        tok = p_node->GetChild((i + 1))->tok;
        rest_of = Alloc<RestParam>(tok, lexer::TokenVal(tok));
      }
    }
    i += 2;
  }
  return Alloc<ParamGroup>(params, rest_of);
}

syntax_asdl::proc_sig_t* Transformer::Proc(pnode::PNode* p_node) {
  int n;
  proc_sig::Closed* sig = nullptr;
  int i;
  pnode::PNode* child = nullptr;
  syntax_asdl::ParamGroup* group = nullptr;
  List<syntax_asdl::Param*>* params = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&sig);
  StackRoot _root2(&child);
  StackRoot _root3(&group);
  StackRoot _root4(&params);

  n = p_node->NumChildren();
  if (n == 1) {
    return proc_sig::Open;
  }
  if (n == 3) {
    sig = proc_sig::Closed::CreateNull(true);
  }
  sig = proc_sig::Closed::CreateNull(true);
  i = 1;
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::param_group) {
    sig->word = this->_ParamGroup(p_node->GetChild(i));
    for (ListIter<syntax_asdl::Param*> it(sig->word->params); !it.Done(); it.Next()) {
      syntax_asdl::Param* word = it.Value();
      StackRoot _for(&word    );
      if (word->type) {
        if ((!str_equals(word->type->name, S_DsF) && !str_equals(word->type->name, S_ioh))) {
          p_die(S_Bej, word->type->tok);
        }
        if (word->type->params != nullptr) {
          p_die(S_noa, word->type->tok);
        }
      }
    }
    i += 2;
  }
  else {
    i += 1;
  }
  if (i >= n) {
    return sig;
  }
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::param_group) {
    sig->positional = this->_ParamGroup(p_node->GetChild(i));
    i += 2;
  }
  else {
    i += 1;
  }
  if (i >= n) {
    return sig;
  }
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::param_group) {
    sig->named = this->_ParamGroup(p_node->GetChild(i));
    i += 2;
  }
  else {
    i += 1;
  }
  if (i >= n) {
    return sig;
  }
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::param_group) {
    group = this->_ParamGroup(p_node->GetChild(i));
    params = group->params;
    if (len(params) > 1) {
      p_die(S_sxs, params->at(1)->blame_tok);
    }
    if (group->rest_of) {
      p_die(S_Cab, group->rest_of->blame_tok);
    }
    if (len(params) == 1) {
      if (params->at(0)->type) {
        if (!(str_equals(params->at(0)->type->name, S_jma))) {
          p_die(S_stA, params->at(0)->type->tok);
        }
        if (params->at(0)->type->params != nullptr) {
          p_die(S_noa, params->at(0)->type->tok);
        }
      }
      sig->block_param = params->at(0);
    }
  }
  return sig;
}

void Transformer::YshFunc(pnode::PNode* p_node, syntax_asdl::Func* out) {
  int n;
  int i;
  pnode::PNode* child = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&out);
  StackRoot _root2(&child);

  out->name = p_node->GetChild(0)->tok;
  n = p_node->NumChildren();
  i = 2;
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::param_group) {
    out->positional = this->_ParamGroup(child);
    i += 2;
  }
  else {
    i += 1;
  }
  if (i >= n) {
    return ;
  }
  child = p_node->GetChild(i);
  if (child->typ == grammar_nt::param_group) {
    out->named = this->_ParamGroup(child);
  }
}

syntax_asdl::CharCode* Transformer::_RangeCharSingleQuoted(pnode::PNode* p_node) {
  pnode::PNode* child0 = nullptr;
  syntax_asdl::SingleQuoted* sq_part = nullptr;
  int n;
  StackRoot _root0(&p_node);
  StackRoot _root1(&child0);
  StackRoot _root2(&sq_part);

  child0 = p_node->GetChild(0);
  if (child0->typ == grammar_nt::sq_string) {
    sq_part = reinterpret_cast<SingleQuoted*>(child0->GetChild(1)->tok);
    n = len(sq_part->sval);
    if (n == 0) {
      p_die(S_lBE, Alloc<loc::WordPart>(sq_part));
    }
    else {
      if (n == 1) {
        return Alloc<CharCode>(sq_part->left, ord(sq_part->sval->at(0)), false);
      }
      else {
        p_die(RANGE_POINT_TOO_LONG, Alloc<loc::WordPart>(sq_part));
      }
    }
  }
  return nullptr;
}

syntax_asdl::Token* Transformer::_OtherRangeToken(pnode::PNode* p_node) {
  pnode::PNode* child0 = nullptr;
  syntax_asdl::Token* tok = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&child0);
  StackRoot _root2(&tok);

  child0 = p_node->GetChild(0);
  if (child0->typ == grammar_nt::char_literal) {
    tok = child0->GetChild(0)->tok;
    return tok;
  }
  tok = p_node->tok;
  if (tok->length != 1) {
    p_die(RANGE_POINT_TOO_LONG, tok);
  }
  return tok;
}

syntax_asdl::class_literal_term_t* Transformer::_NonRangeChars(pnode::PNode* p_node) {
  pnode::PNode* child0 = nullptr;
  int typ0;
  StackRoot _root0(&p_node);
  StackRoot _root1(&child0);

  child0 = p_node->GetChild(0);
  typ0 = p_node->GetChild(0)->typ;
  if (typ0 == grammar_nt::sq_string) {
    return reinterpret_cast<SingleQuoted*>(child0->GetChild(1)->tok);
  }
  if (typ0 == grammar_nt::char_literal) {
    return word_compile::EvalCharLiteralForRegex(child0->tok);
  }
  if (typ0 == Id::Expr_Name) {
    return this->_NameInClass(nullptr, child0->tok);
  }
  assert(0);  // AssertionError
}

syntax_asdl::class_literal_term_t* Transformer::_ClassLiteralTerm(pnode::PNode* p_node) {
  int typ0;
  int n;
  pnode::PNode* left = nullptr;
  pnode::PNode* right = nullptr;
  syntax_asdl::CharCode* code1 = nullptr;
  syntax_asdl::Token* tok1 = nullptr;
  syntax_asdl::CharCode* code2 = nullptr;
  syntax_asdl::Token* tok2 = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&left);
  StackRoot _root2(&right);
  StackRoot _root3(&code1);
  StackRoot _root4(&tok1);
  StackRoot _root5(&code2);
  StackRoot _root6(&tok2);

  typ0 = p_node->GetChild(0)->typ;
  if (typ0 == grammar_nt::range_char) {
    n = p_node->NumChildren();
    if (n == 1) {
      return this->_NonRangeChars(p_node->GetChild(0));
    }
    if (n == 3) {
      left = p_node->GetChild(0);
      right = p_node->GetChild(2);
      code1 = this->_RangeCharSingleQuoted(left);
      if (code1 == nullptr) {
        tok1 = this->_OtherRangeToken(left);
        code1 = word_compile::EvalCharLiteralForRegex(tok1);
      }
      code2 = this->_RangeCharSingleQuoted(right);
      if (code2 == nullptr) {
        tok2 = this->_OtherRangeToken(right);
        code2 = word_compile::EvalCharLiteralForRegex(tok2);
      }
      return Alloc<CharRange>(code1, code2);
    }
    assert(0);  // AssertionError
  }
  if (typ0 == Id::Expr_At) {
    tok1 = p_node->GetChild(1)->tok;
    return Alloc<class_literal_term::Splice>(tok1, lexer::TokenVal(tok1));
  }
  if (typ0 == Id::Expr_Bang) {
    return this->_NameInClass(p_node->GetChild(0)->tok, p_node->GetChild(1)->tok);
  }
  p_die(S_dlq, p_node->GetChild(0)->tok);
}

List<syntax_asdl::class_literal_term_t*>* Transformer::_ClassLiteral(pnode::PNode* p_node) {
  List<syntax_asdl::class_literal_term_t*>* terms = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&terms);

  terms = Alloc<List<syntax_asdl::class_literal_term_t*>>();
  for (int i = 1; i < (p_node->NumChildren() - 1); ++i) {
    terms->append(this->_ClassLiteralTerm(p_node->GetChild(i)));
  }
  return terms;
}

syntax_asdl::re_t* Transformer::_NameInRegex(syntax_asdl::Token* negated_tok, syntax_asdl::Token* tok) {
  BigStr* tok_str = nullptr;
  BigStr* perl = nullptr;
  StackRoot _root0(&negated_tok);
  StackRoot _root1(&tok);
  StackRoot _root2(&tok_str);
  StackRoot _root3(&perl);

  tok_str = lexer::TokenVal(tok);
  if (str_equals(tok_str, S_urc)) {
    if (negated_tok) {
      p_die(S_kfs, tok);
    }
    return Alloc<re::Primitive>(tok, Id::Eggex_Dot);
  }
  if (list_contains(POSIX_CLASSES, tok_str)) {
    return Alloc<PosixClass>(negated_tok, tok_str);
  }
  perl = PERL_CLASSES->get(tok_str);
  if (perl != nullptr) {
    return Alloc<PerlClass>(negated_tok, perl);
  }
  if (tok_str->at(0)->isupper()) {
    return Alloc<re::Splice>(tok, lexer::TokenVal(tok));
  }
  p_die(StrFormat("%r isn't a character class", tok_str), tok);
}

syntax_asdl::class_literal_term_t* Transformer::_NameInClass(syntax_asdl::Token* negated_tok, syntax_asdl::Token* tok) {
  BigStr* tok_str = nullptr;
  BigStr* perl = nullptr;
  StackRoot _root0(&negated_tok);
  StackRoot _root1(&tok);
  StackRoot _root2(&tok_str);
  StackRoot _root3(&perl);

  tok_str = lexer::TokenVal(tok);
  if (len(tok_str) == 1) {
    if (negated_tok) {
      p_die(S_kfs, tok);
    }
    return word_compile::EvalCharLiteralForRegex(tok);
  }
  if (list_contains(POSIX_CLASSES, tok_str)) {
    return Alloc<PosixClass>(negated_tok, tok_str);
  }
  perl = PERL_CLASSES->get(tok_str);
  if (perl != nullptr) {
    return Alloc<PerlClass>(negated_tok, perl);
  }
  p_die(StrFormat("%r isn't a character class", tok_str), tok);
}

syntax_asdl::re_t* Transformer::_ReAtom(pnode::PNode* p_atom) {
  pnode::PNode* child0 = nullptr;
  int typ0;
  syntax_asdl::Token* tok0 = nullptr;
  BigStr* s = nullptr;
  BigStr* tok_str = nullptr;
  syntax_asdl::Token* tok1 = nullptr;
  int n;
  pnode::PNode* child1 = nullptr;
  syntax_asdl::re_t* regex = nullptr;
  syntax_asdl::Token* as_name = nullptr;
  syntax_asdl::Token* func_name = nullptr;
  int i;
  int typ;
  StackRoot _root0(&p_atom);
  StackRoot _root1(&child0);
  StackRoot _root2(&tok0);
  StackRoot _root3(&s);
  StackRoot _root4(&tok_str);
  StackRoot _root5(&tok1);
  StackRoot _root6(&child1);
  StackRoot _root7(&regex);
  StackRoot _root8(&as_name);
  StackRoot _root9(&func_name);

  child0 = p_atom->GetChild(0);
  typ0 = p_atom->GetChild(0)->typ;
  tok0 = p_atom->GetChild(0)->tok;
  if (typ0 == grammar_nt::class_literal) {
    return Alloc<re::CharClassLiteral>(false, this->_ClassLiteral(child0));
  }
  if (typ0 == grammar_nt::sq_string) {
    return reinterpret_cast<SingleQuoted*>(child0->GetChild(1)->tok);
  }
  if (typ0 == grammar_nt::char_literal) {
    s = word_compile::EvalCStringToken(tok0->id, lexer::TokenVal(tok0));
    return Alloc<re::LiteralChars>(tok0, s);
  }
  if (typ0 == Id::Expr_Dot) {
    return Alloc<re::Primitive>(tok0, Id::Eggex_Dot);
  }
  if (typ0 == Id::Arith_Caret) {
    return Alloc<re::Primitive>(tok0, Id::Eggex_Start);
  }
  if (typ0 == Id::Expr_Dollar) {
    return Alloc<re::Primitive>(tok0, Id::Eggex_End);
  }
  if (typ0 == Id::Expr_Name) {
    return this->_NameInRegex(nullptr, tok0);
  }
  if (typ0 == Id::Expr_Symbol) {
    tok_str = lexer::TokenVal(tok0);
    if (str_equals(tok_str, S_DwB)) {
      return Alloc<re::Primitive>(tok0, Id::Eggex_Start);
    }
    if (str_equals(tok_str, S_Ctu)) {
      return Alloc<re::Primitive>(tok0, Id::Eggex_End);
    }
    p_die(StrFormat("Unexpected token %r in regex", tok_str), tok0);
  }
  if (typ0 == Id::Expr_At) {
    tok1 = p_atom->GetChild(1)->tok;
    return Alloc<re::Splice>(tok0, lexer::TokenVal(tok1));
  }
  if (typ0 == Id::Expr_Bang) {
    n = p_atom->NumChildren();
    if (n == 2) {
      child1 = p_atom->GetChild(1);
      if (child1->typ == grammar_nt::class_literal) {
        return Alloc<re::CharClassLiteral>(true, this->_ClassLiteral(child1));
      }
      else {
        return this->_NameInRegex(tok0, p_atom->GetChild(1)->tok);
      }
    }
    else {
      p_die(S_Fkp, p_atom->GetChild(1)->tok);
    }
  }
  if (typ0 == Id::Op_LParen) {
    return Alloc<re::Group>(this->_Regex(p_atom->GetChild(1)));
  }
  if (typ0 == Id::Arith_Less) {
    n = p_atom->NumChildren();
    regex = this->_Regex(p_atom->GetChild(2));
    as_name = nullptr;
    func_name = nullptr;
    i = 3;
    typ = p_atom->GetChild(i)->typ;
    if (typ == Id::Expr_As) {
      as_name = p_atom->GetChild((i + 1))->tok;
      i += 2;
    }
    typ = p_atom->GetChild(i)->typ;
    if (typ == Id::Arith_Colon) {
      func_name = p_atom->GetChild((i + 1))->tok;
    }
    return Alloc<re::Capture>(regex, as_name, func_name);
  }
  assert(0);  // AssertionError
}

syntax_asdl::re_repeat_t* Transformer::_RepeatOp(pnode::PNode* p_repeat) {
  syntax_asdl::Token* tok = nullptr;
  int id_;
  pnode::PNode* child1 = nullptr;
  int n;
  syntax_asdl::Token* left = nullptr;
  syntax_asdl::Token* right = nullptr;
  StackRoot _root0(&p_repeat);
  StackRoot _root1(&tok);
  StackRoot _root2(&child1);
  StackRoot _root3(&left);
  StackRoot _root4(&right);

  tok = p_repeat->GetChild(0)->tok;
  id_ = tok->id;
  if ((id_ == Id::Arith_Plus || id_ == Id::Arith_Star || id_ == Id::Arith_QMark)) {
    return tok;
  }
  if (id_ == Id::Op_LBrace) {
    child1 = p_repeat->GetChild(1);
    if (child1->typ != grammar_nt::repeat_range) {
      p_die(S_aBC, child1->tok);
    }
    n = child1->NumChildren();
    if (n == 1) {
      tok = child1->GetChild(0)->tok;
      return tok;
    }
    if (n == 2) {
      if (child1->GetChild(0)->typ == Id::Expr_DecInt) {
        left = child1->GetChild(0)->tok;
        return Alloc<re_repeat::Range>(left, lexer::TokenVal(left), S_Aoo, nullptr);
      }
      else {
        right = child1->GetChild(1)->tok;
        return Alloc<re_repeat::Range>(nullptr, S_Aoo, lexer::TokenVal(right), right);
      }
    }
    if (n == 3) {
      left = child1->GetChild(0)->tok;
      right = child1->GetChild(2)->tok;
      return Alloc<re_repeat::Range>(left, lexer::TokenVal(left), lexer::TokenVal(right), right);
    }
    assert(0);  // AssertionError
  }
  assert(0);  // AssertionError
}

syntax_asdl::re_t* Transformer::_ReAlt(pnode::PNode* p_node) {
  int i;
  int n;
  List<syntax_asdl::re_t*>* seq = nullptr;
  syntax_asdl::re_t* r = nullptr;
  syntax_asdl::re_repeat_t* repeat_op = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&seq);
  StackRoot _root2(&r);
  StackRoot _root3(&repeat_op);

  i = 0;
  n = p_node->NumChildren();
  seq = Alloc<List<syntax_asdl::re_t*>>();
  while (i < n) {
    r = this->_ReAtom(p_node->GetChild(i));
    i += 1;
    if ((i < n and p_node->GetChild(i)->typ == grammar_nt::repeat_op)) {
      repeat_op = this->_RepeatOp(p_node->GetChild(i));
      r = Alloc<re::Repeat>(r, repeat_op);
      i += 1;
    }
    seq->append(r);
  }
  if (len(seq) == 1) {
    return seq->at(0);
  }
  else {
    return Alloc<re::Seq>(seq);
  }
}

syntax_asdl::re_t* Transformer::_Regex(pnode::PNode* p_node) {
  int n;
  List<syntax_asdl::re_t*>* alts = nullptr;
  pnode::PNode* c = nullptr;
  StackRoot _root0(&p_node);
  StackRoot _root1(&alts);
  StackRoot _root2(&c);

  n = p_node->NumChildren();
  alts = Alloc<List<syntax_asdl::re_t*>>();
  for (int i = 0; i < n; i += 2) {
    c = p_node->GetChild(i);
    alts->append(this->_ReAlt(c));
  }
  if (len(alts) == 1) {
    return alts->at(0);
  }
  else {
    return Alloc<re::Alt>(alts);
  }
}

}  // define namespace expr_to_ast

namespace func_proc {  // define

using id_kind_asdl::Id;
using runtime_asdl::cmd_value;
using runtime_asdl::ProcArgs;
using runtime_asdl::Cell;
using syntax_asdl::proc_sig;
using syntax_asdl::proc_sig_e;
using syntax_asdl::Param;
using syntax_asdl::ParamGroup;
using syntax_asdl::NamedArg;
using syntax_asdl::Func;
using syntax_asdl::loc;
using syntax_asdl::ArgList;
using syntax_asdl::expr;
using syntax_asdl::expr_e;
using syntax_asdl::expr_t;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::ProcDefaults;
using value_asdl::LeftName;
using error::e_die;

void _DisallowMutableDefault(value_asdl::value_t* val, syntax_asdl::loc_t* blame_loc) {
  StackRoot _root0(&val);
  StackRoot _root1(&blame_loc);

  if ((val->tag() == value_e::List || val->tag() == value_e::Dict)) {
    throw Alloc<error::TypeErr>(val, S_BCt, blame_loc);
  }
}

List<value_asdl::value_t*>* _EvalPosDefaults(expr_eval::ExprEvaluator* expr_ev, List<syntax_asdl::Param*>* pos_params) {
  value_asdl::value_t* no_val = nullptr;
  List<value_asdl::value_t*>* pos_defaults = nullptr;
  int i;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&expr_ev);
  StackRoot _root1(&pos_params);
  StackRoot _root2(&no_val);
  StackRoot _root3(&pos_defaults);
  StackRoot _root4(&val);

  no_val = nullptr;
  pos_defaults = list_repeat(no_val, len(pos_params));
  i = 0;
  for (ListIter<syntax_asdl::Param*> it(pos_params); !it.Done(); it.Next(), ++i) {
    syntax_asdl::Param* p = it.Value();
    StackRoot _for(&p  );
    if (p->default_val) {
      val = expr_ev->EvalExpr(p->default_val, p->blame_tok);
      _DisallowMutableDefault(val, p->blame_tok);
      pos_defaults->set(i, val);
    }
  }
  return pos_defaults;
}

Dict<BigStr*, value_asdl::value_t*>* _EvalNamedDefaults(expr_eval::ExprEvaluator* expr_ev, List<syntax_asdl::Param*>* named_params) {
  Dict<BigStr*, value_asdl::value_t*>* named_defaults = nullptr;
  int i;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&expr_ev);
  StackRoot _root1(&named_params);
  StackRoot _root2(&named_defaults);
  StackRoot _root3(&val);

  named_defaults = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  i = 0;
  for (ListIter<syntax_asdl::Param*> it(named_params); !it.Done(); it.Next(), ++i) {
    syntax_asdl::Param* p = it.Value();
    StackRoot _for(&p  );
    if (p->default_val) {
      val = expr_ev->EvalExpr(p->default_val, p->blame_tok);
      _DisallowMutableDefault(val, p->blame_tok);
      named_defaults->set(p->name, val);
    }
  }
  return named_defaults;
}

Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*> EvalFuncDefaults(expr_eval::ExprEvaluator* expr_ev, syntax_asdl::Func* func) {
  List<value_asdl::value_t*>* pos_defaults = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_defaults = nullptr;
  StackRoot _root0(&expr_ev);
  StackRoot _root1(&func);
  StackRoot _root2(&pos_defaults);
  StackRoot _root3(&named_defaults);

  if (func->positional) {
    pos_defaults = _EvalPosDefaults(expr_ev, func->positional->params);
  }
  else {
    pos_defaults = nullptr;
  }
  if (func->named) {
    named_defaults = _EvalNamedDefaults(expr_ev, func->named->params);
  }
  else {
    named_defaults = nullptr;
  }
  return Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*>(pos_defaults, named_defaults);
}

value_asdl::ProcDefaults* EvalProcDefaults(expr_eval::ExprEvaluator* expr_ev, proc_sig::Closed* sig) {
  value_asdl::value_t* no_val = nullptr;
  List<value_asdl::value_t*>* word_defaults = nullptr;
  int i;
  value_asdl::value_t* val = nullptr;
  List<value_asdl::value_t*>* pos_defaults = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_defaults = nullptr;
  syntax_asdl::expr_t* exp = nullptr;
  value_asdl::value_t* block_default = nullptr;
  StackRoot _root0(&expr_ev);
  StackRoot _root1(&sig);
  StackRoot _root2(&no_val);
  StackRoot _root3(&word_defaults);
  StackRoot _root4(&val);
  StackRoot _root5(&pos_defaults);
  StackRoot _root6(&named_defaults);
  StackRoot _root7(&exp);
  StackRoot _root8(&block_default);

  no_val = nullptr;
  if (sig->word) {
    word_defaults = list_repeat(no_val, len(sig->word->params));
    i = 0;
    for (ListIter<syntax_asdl::Param*> it(sig->word->params); !it.Done(); it.Next(), ++i) {
      syntax_asdl::Param* p = it.Value();
      StackRoot _for(&p    );
      if (p->default_val) {
        val = expr_ev->EvalExpr(p->default_val, p->blame_tok);
        if (val->tag() != value_e::Str) {
          throw Alloc<error::TypeErr>(val, S_kyn, p->blame_tok);
        }
        word_defaults->set(i, val);
      }
    }
  }
  else {
    word_defaults = nullptr;
  }
  if (sig->positional) {
    pos_defaults = _EvalPosDefaults(expr_ev, sig->positional->params);
  }
  else {
    pos_defaults = nullptr;
  }
  if (sig->named) {
    named_defaults = _EvalNamedDefaults(expr_ev, sig->named->params);
  }
  else {
    named_defaults = nullptr;
  }
  if (sig->block_param) {
    exp = sig->block_param->default_val;
    if (exp) {
      block_default = expr_ev->EvalExpr(exp, sig->block_param->blame_tok);
      if ((block_default->tag() != value_e::Null && block_default->tag() != value_e::Command)) {
        throw Alloc<error::TypeErr>(block_default, S_jhk, sig->block_param->blame_tok);
      }
    }
    else {
      block_default = nullptr;
    }
  }
  else {
    block_default = nullptr;
  }
  return Alloc<ProcDefaults>(word_defaults, pos_defaults, named_defaults, block_default);
}

void _EvalPosArgs(expr_eval::ExprEvaluator* expr_ev, List<syntax_asdl::expr_t*>* exprs, List<value_asdl::value_t*>* pos_args) {
  syntax_asdl::expr_t* UP_e = nullptr;
  value_asdl::value_t* val = nullptr;
  StackRoot _root0(&expr_ev);
  StackRoot _root1(&exprs);
  StackRoot _root2(&pos_args);
  StackRoot _root3(&UP_e);
  StackRoot _root4(&val);

  for (ListIter<syntax_asdl::expr_t*> it(exprs); !it.Done(); it.Next()) {
    syntax_asdl::expr_t* e = it.Value();
    StackRoot _for(&e  );
    UP_e = e;
    if (e->tag() == expr_e::Spread) {
      expr::Spread* e = static_cast<expr::Spread*>(UP_e);
      val = expr_ev->_EvalExpr(e->child);
      if (val->tag() != value_e::List) {
        throw Alloc<error::TypeErr>(val, S_yys, e->left);
      }
      pos_args->extend(static_cast<value::List*>(val)->items);
    }
    else {
      pos_args->append(expr_ev->_EvalExpr(e));
    }
  }
}

Dict<BigStr*, value_asdl::value_t*>* _EvalNamedArgs(expr_eval::ExprEvaluator* expr_ev, List<syntax_asdl::NamedArg*>* named_exprs) {
  Dict<BigStr*, value_asdl::value_t*>* named_args = nullptr;
  syntax_asdl::expr_t* val_expr = nullptr;
  syntax_asdl::expr_t* UP_val_expr = nullptr;
  value_asdl::value_t* val = nullptr;
  BigStr* name = nullptr;
  StackRoot _root0(&expr_ev);
  StackRoot _root1(&named_exprs);
  StackRoot _root2(&named_args);
  StackRoot _root3(&val_expr);
  StackRoot _root4(&UP_val_expr);
  StackRoot _root5(&val);
  StackRoot _root6(&name);

  named_args = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  for (ListIter<syntax_asdl::NamedArg*> it(named_exprs); !it.Done(); it.Next()) {
    syntax_asdl::NamedArg* n = it.Value();
    StackRoot _for(&n  );
    val_expr = n->value;
    UP_val_expr = val_expr;
    if (val_expr->tag() == expr_e::Spread) {
      expr::Spread* val_expr = static_cast<expr::Spread*>(UP_val_expr);
      val = expr_ev->_EvalExpr(val_expr->child);
      if (val->tag() != value_e::Dict) {
        throw Alloc<error::TypeErr>(val, S_fFz, val_expr->left);
      }
      named_args->update(static_cast<value::Dict*>(val)->d);
    }
    else {
      val = expr_ev->EvalExpr(n->value, n->name);
      name = lexer::TokenVal(n->name);
      named_args->set(name, val);
    }
  }
  return named_args;
}

Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*> _EvalArgList(expr_eval::ExprEvaluator* expr_ev, syntax_asdl::ArgList* args, value_asdl::value_t* self_val) {
  List<value_asdl::value_t*>* pos_args = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_args = nullptr;
  StackRoot _root0(&expr_ev);
  StackRoot _root1(&args);
  StackRoot _root2(&self_val);
  StackRoot _root3(&pos_args);
  StackRoot _root4(&named_args);

  pos_args = Alloc<List<value_asdl::value_t*>>();
  if (self_val) {
    pos_args->append(self_val);
  }
  _EvalPosArgs(expr_ev, args->pos_args, pos_args);
  named_args = nullptr;
  if (args->named_args != nullptr) {
    named_args = _EvalNamedArgs(expr_ev, args->named_args);
  }
  return Tuple2<List<value_asdl::value_t*>*, Dict<BigStr*, value_asdl::value_t*>*>(pos_args, named_args);
}

void EvalTypedArgsToProc(expr_eval::ExprEvaluator* expr_ev, Dict<BigStr*, runtime_asdl::Cell*>* current_frame, Dict<BigStr*, runtime_asdl::Cell*>* module_frame, state::MutableOpts* mutable_opts, command::Simple* node, runtime_asdl::ProcArgs* proc_args) {
  syntax_asdl::ArgList* ty = nullptr;
  List<syntax_asdl::NamedArg*>* n1 = nullptr;
  BigStr* name = nullptr;
  StackRoot _root0(&expr_ev);
  StackRoot _root1(&current_frame);
  StackRoot _root2(&module_frame);
  StackRoot _root3(&mutable_opts);
  StackRoot _root4(&node);
  StackRoot _root5(&proc_args);
  StackRoot _root6(&ty);
  StackRoot _root7(&n1);
  StackRoot _root8(&name);

  proc_args->typed_args = node->typed_args;
  proc_args->pos_args = Alloc<List<value_asdl::value_t*>>();
  ty = node->typed_args;
  if (ty) {
    if (ty->left->id == Id::Op_LBracket) {
      for (ListIter<syntax_asdl::expr_t*> it(ty->pos_args); !it.Done(); it.Next()) {
        syntax_asdl::expr_t* exp = it.Value();
        StackRoot _for(&exp      );
        proc_args->pos_args->append(Alloc<value::Expr>(exp, current_frame, module_frame));
      }
      n1 = ty->named_args;
      if (n1 != nullptr) {
        proc_args->named_args = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
        for (ListIter<syntax_asdl::NamedArg*> it(n1); !it.Done(); it.Next()) {
          syntax_asdl::NamedArg* named_arg = it.Value();
          StackRoot _for(&named_arg        );
          name = lexer::TokenVal(named_arg->name);
          proc_args->named_args->set(name, Alloc<value::Expr>(named_arg->value, current_frame, module_frame));
        }
      }
    }
    else {
      {  // with
        state::ctx_YshExpr ctx{mutable_opts};

        _EvalPosArgs(expr_ev, ty->pos_args, proc_args->pos_args);
        if (ty->named_args != nullptr) {
          proc_args->named_args = _EvalNamedArgs(expr_ev, ty->named_args);
        }
      }
    }
    if ((ty->block_expr and node->block)) {
      e_die(S_hjj, node->block->brace_group->left);
    }
    if (ty->block_expr) {
      proc_args->block_arg = expr_ev->EvalExpr(ty->block_expr, ty->left);
    }
  }
  if (node->block) {
    proc_args->block_arg = Alloc<value::Command>(node->block, current_frame, module_frame);
    if (!proc_args->typed_args) {
      proc_args->typed_args = ArgList::CreateNull();
      proc_args->typed_args->left = node->block->brace_group->left;
      proc_args->typed_args->right = node->block->brace_group->right;
    }
  }
}

void _BindWords(BigStr* proc_name, syntax_asdl::ParamGroup* group, List<value_asdl::value_t*>* defaults, cmd_value::Argv* cmd_val, state::Mem* mem, syntax_asdl::loc_t* blame_loc) {
  List<BigStr*>* argv = nullptr;
  int num_args;
  int i;
  value_asdl::value_t* val = nullptr;
  int num_params;
  syntax_asdl::RestParam* rest = nullptr;
  value_asdl::LeftName* lval = nullptr;
  List<value_asdl::value_t*>* items = nullptr;
  value::List* rest_val = nullptr;
  syntax_asdl::loc_t* extra_loc = nullptr;
  StackRoot _root0(&proc_name);
  StackRoot _root1(&group);
  StackRoot _root2(&defaults);
  StackRoot _root3(&cmd_val);
  StackRoot _root4(&mem);
  StackRoot _root5(&blame_loc);
  StackRoot _root6(&argv);
  StackRoot _root7(&val);
  StackRoot _root8(&rest);
  StackRoot _root9(&lval);
  StackRoot _root10(&items);
  StackRoot _root11(&rest_val);
  StackRoot _root12(&extra_loc);

  argv = cmd_val->argv->slice(1);
  num_args = len(argv);
  i = 0;
  for (ListIter<syntax_asdl::Param*> it(group->params); !it.Done(); it.Next(), ++i) {
    syntax_asdl::Param* p = it.Value();
    StackRoot _for(&p  );
    if (i < num_args) {
      val = Alloc<value::Str>(argv->at(i));
    }
    else {
      val = defaults->at(i);
      if (val == nullptr) {
        throw Alloc<error::Expr>(StrFormat("proc %r wasn't passed word param %r", proc_name, p->name), blame_loc);
      }
    }
    mem->SetLocalName(Alloc<LeftName>(p->name, p->blame_tok), val);
  }
  num_params = len(group->params);
  rest = group->rest_of;
  if (rest) {
    lval = Alloc<LeftName>(rest->name, rest->blame_tok);
    items = Alloc<List<value_asdl::value_t*>>();
    for (ListIter<BigStr*> it(argv->slice(num_params)); !it.Done(); it.Next()) {
      BigStr* s = it.Value();
      items->append(Alloc<value::Str>(s));
    }
    rest_val = Alloc<value::List>(items);
    mem->SetLocalName(lval, rest_val);
  }
  else {
    if (num_args > num_params) {
      if (len(cmd_val->arg_locs)) {
        extra_loc = cmd_val->arg_locs->at((num_params + 1));
      }
      else {
        extra_loc = loc::Missing;
      }
      throw Alloc<error::Expr>(StrFormat("proc %r takes %d words, but got %d", proc_name, num_params, num_args), extra_loc);
    }
  }
}

void _BindTyped(BigStr* code_name, syntax_asdl::ParamGroup* group, List<value_asdl::value_t*>* defaults, List<value_asdl::value_t*>* pos_args, state::Mem* mem, syntax_asdl::loc_t* blame_loc) {
  int num_args;
  int num_params;
  int i;
  value_asdl::value_t* val = nullptr;
  syntax_asdl::RestParam* rest = nullptr;
  value_asdl::LeftName* lval = nullptr;
  value::List* rest_val = nullptr;
  StackRoot _root0(&code_name);
  StackRoot _root1(&group);
  StackRoot _root2(&defaults);
  StackRoot _root3(&pos_args);
  StackRoot _root4(&mem);
  StackRoot _root5(&blame_loc);
  StackRoot _root6(&val);
  StackRoot _root7(&rest);
  StackRoot _root8(&lval);
  StackRoot _root9(&rest_val);

  if (pos_args == nullptr) {
    pos_args = Alloc<List<value_asdl::value_t*>>();
  }
  num_args = len(pos_args);
  num_params = 0;
  i = 0;
  if (group) {
    for (ListIter<syntax_asdl::Param*> it(group->params); !it.Done(); it.Next()) {
      syntax_asdl::Param* p = it.Value();
      StackRoot _for(&p    );
      if (i < num_args) {
        val = pos_args->at(i);
      }
      else {
        val = defaults->at(i);
        if (val == nullptr) {
          throw Alloc<error::Expr>(StrFormat("%r wasn't passed typed param %r", code_name, p->name), blame_loc);
        }
      }
      mem->SetLocalName(Alloc<LeftName>(p->name, p->blame_tok), val);
      i += 1;
    }
    num_params += len(group->params);
  }
  if (group) {
    rest = group->rest_of;
    if (rest) {
      lval = Alloc<LeftName>(rest->name, rest->blame_tok);
      rest_val = Alloc<value::List>(pos_args->slice(num_params));
      mem->SetLocalName(lval, rest_val);
    }
    else {
      if (num_args > num_params) {
        throw Alloc<error::Expr>(StrFormat("%r takes %d typed args, but got %d", code_name, num_params, num_args), blame_loc);
      }
    }
  }
}

void _BindNamed(BigStr* code_name, syntax_asdl::ParamGroup* group, Dict<BigStr*, value_asdl::value_t*>* defaults, Dict<BigStr*, value_asdl::value_t*>* named_args, state::Mem* mem, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* val = nullptr;
  syntax_asdl::RestParam* rest = nullptr;
  value_asdl::LeftName* lval = nullptr;
  int num_args;
  int num_params;
  StackRoot _root0(&code_name);
  StackRoot _root1(&group);
  StackRoot _root2(&defaults);
  StackRoot _root3(&named_args);
  StackRoot _root4(&mem);
  StackRoot _root5(&blame_loc);
  StackRoot _root6(&val);
  StackRoot _root7(&rest);
  StackRoot _root8(&lval);

  if (named_args == nullptr) {
    named_args = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  }
  for (ListIter<syntax_asdl::Param*> it(group->params); !it.Done(); it.Next()) {
    syntax_asdl::Param* p = it.Value();
    StackRoot _for(&p  );
    val = named_args->get(p->name);
    if (val == nullptr) {
      val = defaults->get(p->name);
    }
    if (val == nullptr) {
      throw Alloc<error::Expr>(StrFormat("%r wasn't passed named param %r", code_name, p->name), blame_loc);
    }
    mem->SetLocalName(Alloc<LeftName>(p->name, p->blame_tok), val);
    mylib::dict_erase(named_args, p->name);
  }
  rest = group->rest_of;
  if (rest) {
    lval = Alloc<LeftName>(rest->name, rest->blame_tok);
    mem->SetLocalName(lval, Alloc<value::Dict>(named_args));
  }
  else {
    num_args = len(named_args);
    num_params = len(group->params);
    if (num_args > num_params) {
      throw Alloc<error::Expr>(StrFormat("%r takes %d named args, but got %d", code_name, num_params, num_args), blame_loc);
    }
  }
}

void _BindFuncArgs(value::Func* func, typed_args::Reader* rd, state::Mem* mem) {
  syntax_asdl::Func* node = nullptr;
  syntax_asdl::Token* blame_loc = nullptr;
  int num_pos;
  syntax_asdl::Token* semi = nullptr;
  int num_named;
  StackRoot _root0(&func);
  StackRoot _root1(&rd);
  StackRoot _root2(&mem);
  StackRoot _root3(&node);
  StackRoot _root4(&blame_loc);
  StackRoot _root5(&semi);

  node = func->parsed;
  blame_loc = rd->LeftParenToken();
  if (node->positional) {
    _BindTyped(func->name, node->positional, func->pos_defaults, rd->pos_args, mem, blame_loc);
  }
  else {
    if (rd->pos_args != nullptr) {
      num_pos = len(rd->pos_args);
      if (num_pos != 0) {
        throw Alloc<error::Expr>(StrFormat("Func %r takes no positional args, but got %d", func->name, num_pos), blame_loc);
      }
    }
  }
  semi = rd->arg_list->semi_tok;
  if (semi != nullptr) {
    blame_loc = semi;
  }
  if (node->named) {
    _BindNamed(func->name, node->named, func->named_defaults, rd->named_args, mem, blame_loc);
  }
  else {
    if (rd->named_args != nullptr) {
      num_named = len(rd->named_args);
      if (num_named != 0) {
        throw Alloc<error::Expr>(StrFormat("Func %r takes no named args, but got %d", func->name, num_named), blame_loc);
      }
    }
  }
}

void BindProcArgs(value::Proc* proc, cmd_value::Argv* cmd_val, state::Mem* mem) {
  runtime_asdl::ProcArgs* proc_args = nullptr;
  syntax_asdl::proc_sig_t* UP_sig = nullptr;
  syntax_asdl::loc_t* blame_loc = nullptr;
  int num_word;
  List<value_asdl::value_t*>* pos_args = nullptr;
  int num_pos;
  syntax_asdl::Token* semi = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* named_args = nullptr;
  int num_named;
  syntax_asdl::Param* block_param = nullptr;
  value_asdl::value_t* block_arg = nullptr;
  StackRoot _root0(&proc);
  StackRoot _root1(&cmd_val);
  StackRoot _root2(&mem);
  StackRoot _root3(&proc_args);
  StackRoot _root4(&UP_sig);
  StackRoot _root5(&blame_loc);
  StackRoot _root6(&pos_args);
  StackRoot _root7(&semi);
  StackRoot _root8(&named_args);
  StackRoot _root9(&block_param);
  StackRoot _root10(&block_arg);

  proc_args = cmd_val->proc_args;
  UP_sig = proc->sig;
  if (UP_sig->tag() != proc_sig_e::Closed) {
    return ;
  }
  proc_sig::Closed* sig = static_cast<proc_sig::Closed*>(UP_sig);
  blame_loc = loc::Missing;
  if (len(cmd_val->arg_locs) > 0) {
    blame_loc = cmd_val->arg_locs->at(0);
  }
  if (sig->word) {
    _BindWords(proc->name, sig->word, proc->defaults->for_word, cmd_val, mem, blame_loc);
  }
  else {
    num_word = len(cmd_val->argv);
    if (num_word != 1) {
      throw Alloc<error::Expr>(StrFormat("Proc %r takes no word args, but got %d", proc->name, (num_word - 1)), blame_loc);
    }
  }
  if ((proc_args and proc_args->typed_args)) {
    blame_loc = proc_args->typed_args->left;
  }
  if (cmd_val->self_obj) {
    pos_args = NewList<value_asdl::value_t*>(std::initializer_list<value_asdl::value_t*>{cmd_val->self_obj});
    if (proc_args) {
      pos_args->extend(proc_args->pos_args);
    }
  }
  else {
    if (proc_args) {
      pos_args = proc_args->pos_args;
    }
    else {
      pos_args = Alloc<List<value_asdl::value_t*>>();
    }
  }
  if (sig->positional) {
    _BindTyped(proc->name, sig->positional, proc->defaults->for_typed, pos_args, mem, blame_loc);
  }
  else {
    if (cmd_val->self_obj != nullptr) {
      throw Alloc<error::Expr>(StrFormat("Using proc %r as __invoke__ requires a 'self' param", proc->name), blame_loc);
    }
    if (pos_args != nullptr) {
      num_pos = len(pos_args);
      if (num_pos != 0) {
        throw Alloc<error::Expr>(StrFormat("Proc %r takes no typed args, but got %d", proc->name, num_pos), blame_loc);
      }
    }
  }
  if ((proc_args and proc_args->typed_args)) {
    semi = proc_args->typed_args->semi_tok;
    if (semi != nullptr) {
      blame_loc = semi;
    }
  }
  named_args = proc_args ? proc_args->named_args : nullptr;
  if (sig->named) {
    _BindNamed(proc->name, sig->named, proc->defaults->for_named, named_args, mem, blame_loc);
  }
  else {
    if (named_args != nullptr) {
      num_named = len(named_args);
      if (num_named != 0) {
        throw Alloc<error::Expr>(StrFormat("Proc %r takes no named args, but got %d", proc->name, num_named), blame_loc);
      }
    }
  }
  if ((proc_args and proc_args->typed_args)) {
    semi = proc_args->typed_args->semi_tok2;
    if (semi != nullptr) {
      blame_loc = semi;
    }
  }
  block_param = sig->block_param;
  block_arg = proc_args ? proc_args->block_arg : nullptr;
  if (block_param) {
    if (block_arg == nullptr) {
      block_arg = proc->defaults->for_block;
    }
    if (block_arg == nullptr) {
      throw Alloc<error::Expr>(StrFormat("%r wasn't passed block param %r", proc->name, block_param->name), blame_loc);
    }
    mem->SetLocalName(Alloc<LeftName>(block_param->name, block_param->blame_tok), block_arg);
  }
  else {
    if (block_arg != nullptr) {
      throw Alloc<error::Expr>(StrFormat("Proc %r doesn't accept a block argument", proc->name), blame_loc);
    }
  }
}

value_asdl::value_t* CallUserFunc(value::Func* func, typed_args::Reader* rd, state::Mem* mem, cmd_eval::CommandEvaluator* cmd_ev) {
  StackRoot _root0(&func);
  StackRoot _root1(&rd);
  StackRoot _root2(&mem);
  StackRoot _root3(&cmd_ev);

  {  // with
    state::ctx_FuncCall ctx{mem, func};

    _BindFuncArgs(func, rd, mem);
    try {
      cmd_ev->_Execute(func->parsed->body);
      return value::Null;
    }
    catch (vm::ValueControlFlow* e) {
      return e->value;
    }
    catch (vm::IntControlFlow* e) {
      assert(0);  // AssertionError
    }
  }
  assert(0);  // AssertionError
}

}  // define namespace func_proc

namespace regex_translate {  // define

using syntax_asdl::PosixClass;
using syntax_asdl::PerlClass;
using syntax_asdl::CharCode;
using syntax_asdl::CharRange;
using syntax_asdl::char_class_term_e;
using syntax_asdl::char_class_term_t;
using syntax_asdl::re;
using syntax_asdl::re_e;
using syntax_asdl::re_repeat;
using syntax_asdl::re_repeat_e;
using syntax_asdl::EggexFlag;
using syntax_asdl::Token;
using id_kind_asdl::Id;
using value_asdl::value;
using error::e_die;
using error::p_die;
GLOBAL_DICT(PERL_CLASS, BigStr*, BigStr*, 3, {S_Crn COMMA S_pfC COMMA S_anC}, {S_Bro COMMA S_eEf COMMA S_khq});
int CH_RBRACKET = 93;
int CH_BACKSLASH = 92;
int CH_CARET = 94;
int CH_HYPHEN = 45;
int FLAG_RBRACKET = 1;
int FLAG_BACKSLASH = 2;
int FLAG_CARET = 4;
int FLAG_HYPHEN = 8;

void _CharCodeToEre(syntax_asdl::CharCode* term, List<BigStr*>* parts, List<int>* special_char_flags) {
  int char_int;
  int mask;
  StackRoot _root0(&term);
  StackRoot _root1(&parts);
  StackRoot _root2(&special_char_flags);

  char_int = term->i;
  if ((char_int >= 128 and term->u_braced)) {
    e_die(StrFormat("ERE can't express char code %d", char_int), term->blame_tok);
  }
  mask = special_char_flags->at(0);
  if (char_int == CH_HYPHEN) {
    mask |= FLAG_HYPHEN;
  }
  else {
    if (char_int == CH_CARET) {
      mask |= FLAG_CARET;
    }
    else {
      if (char_int == CH_RBRACKET) {
        mask |= FLAG_RBRACKET;
      }
      else {
        if (char_int == CH_BACKSLASH) {
          mask |= FLAG_BACKSLASH;
        }
        else {
          parts->append(chr(char_int));
        }
      }
    }
  }
  special_char_flags->set(0, mask);
}

void _CharClassTermToEre(syntax_asdl::char_class_term_t* term, List<BigStr*>* parts, List<int>* special_char_flags) {
  syntax_asdl::char_class_term_t* UP_term = nullptr;
  List<int>* range_no_special = nullptr;
  BigStr* n = nullptr;
  BigStr* chars = nullptr;
  BigStr* pat = nullptr;
  StackRoot _root0(&term);
  StackRoot _root1(&parts);
  StackRoot _root2(&special_char_flags);
  StackRoot _root3(&UP_term);
  StackRoot _root4(&range_no_special);
  StackRoot _root5(&n);
  StackRoot _root6(&chars);
  StackRoot _root7(&pat);

  UP_term = term;
  switch (term->tag()) {
    case char_class_term_e::CharRange: {
      CharRange* term = static_cast<CharRange*>(UP_term);
      range_no_special = NewList<int>(std::initializer_list<int>{0});
      _CharCodeToEre(term->start, parts, range_no_special);
      if (range_no_special->at(0) != 0) {
        e_die(StrFormat("Can't use char %d as start of range in ERE syntax", term->start->i), term->start->blame_tok);
      }
      parts->append(S_Bjq);
      _CharCodeToEre(term->end, parts, range_no_special);
      if (range_no_special->at(0) != 0) {
        e_die(StrFormat("Can't use char %d as end of range in ERE syntax", term->end->i), term->end->blame_tok);
      }
    }
      break;
    case char_class_term_e::CharCode: {
      CharCode* term = static_cast<CharCode*>(UP_term);
      _CharCodeToEre(term, parts, special_char_flags);
    }
      break;
    case char_class_term_e::PerlClass: {
      PerlClass* term = static_cast<PerlClass*>(UP_term);
      n = term->name;
      chars = PERL_CLASS->at(term->name);
      if (term->negated) {
        e_die(S_rqm, term->negated);
      }
      else {
        pat = StrFormat("%s", chars);
      }
      parts->append(pat);
    }
      break;
    case char_class_term_e::PosixClass: {
      PosixClass* term = static_cast<PosixClass*>(UP_term);
      n = term->name;
      if (term->negated) {
        e_die(S_xil, term->negated);
      }
      else {
        pat = StrFormat("[:%s:]", n);
      }
      parts->append(pat);
    }
      break;
    default: {
      assert(0);  // AssertionError
    }
  }
}

void _AsPosixEre(syntax_asdl::re_t* node, List<BigStr*>* parts, List<BigStr*>* capture_names) {
  syntax_asdl::re_t* UP_node = nullptr;
  int tag;
  int i;
  re::LiteralChars* child = nullptr;
  syntax_asdl::re_repeat_t* op = nullptr;
  int op_tag;
  syntax_asdl::re_repeat_t* UP_op = nullptr;
  BigStr* capture_str = nullptr;
  BigStr* n = nullptr;
  BigStr* chars = nullptr;
  BigStr* pat = nullptr;
  List<int>* special_char_flags = nullptr;
  List<BigStr*>* non_special_parts = nullptr;
  StackRoot _root0(&node);
  StackRoot _root1(&parts);
  StackRoot _root2(&capture_names);
  StackRoot _root3(&UP_node);
  StackRoot _root4(&child);
  StackRoot _root5(&op);
  StackRoot _root6(&UP_op);
  StackRoot _root7(&capture_str);
  StackRoot _root8(&n);
  StackRoot _root9(&chars);
  StackRoot _root10(&pat);
  StackRoot _root11(&special_char_flags);
  StackRoot _root12(&non_special_parts);

  UP_node = node;
  tag = node->tag();
  if (tag == re_e::Primitive) {
    re::Primitive* node = static_cast<re::Primitive*>(UP_node);
    if (node->id == Id::Eggex_Dot) {
      parts->append(S_Aru);
    }
    else {
      if (node->id == Id::Eggex_Start) {
        parts->append(S_EAB);
      }
      else {
        if (node->id == Id::Eggex_End) {
          parts->append(S_Czx);
        }
        else {
          assert(0);  // AssertionError
        }
      }
    }
    return ;
  }
  if (tag == re_e::LiteralChars) {
    re::LiteralChars* node = static_cast<re::LiteralChars*>(UP_node);
    parts->append(glob_::ExtendedRegexEscape(node->s));
    return ;
  }
  if (tag == re_e::Seq) {
    re::Seq* node = static_cast<re::Seq*>(UP_node);
    for (ListIter<syntax_asdl::re_t*> it(node->children); !it.Done(); it.Next()) {
      syntax_asdl::re_t* c = it.Value();
      StackRoot _for(&c    );
      _AsPosixEre(c, parts, capture_names);
    }
    return ;
  }
  if (tag == re_e::Alt) {
    re::Alt* node = static_cast<re::Alt*>(UP_node);
    i = 0;
    for (ListIter<syntax_asdl::re_t*> it(node->children); !it.Done(); it.Next(), ++i) {
      syntax_asdl::re_t* c = it.Value();
      StackRoot _for(&c    );
      if (i != 0) {
        parts->append(S_Ebn);
      }
      _AsPosixEre(c, parts, capture_names);
    }
    return ;
  }
  if (tag == re_e::Repeat) {
    re::Repeat* node = static_cast<re::Repeat*>(UP_node);
    if (node->child->tag() == re_e::LiteralChars) {
      child = static_cast<re::LiteralChars*>(node->child);
      if (len(child->s) > 1) {
        e_die(S_gik, child->blame_tok);
      }
    }
    _AsPosixEre(node->child, parts, capture_names);
    op = node->op;
    op_tag = op->tag();
    UP_op = op;
    if (op_tag == re_repeat_e::Op) {
      Token* op = static_cast<Token*>(UP_op);
      switch (op->id) {
        case Id::Arith_Plus: {
          parts->append(S_jnE);
        }
          break;
        case Id::Arith_Star: {
          parts->append(S_Fgw);
        }
          break;
        case Id::Arith_QMark: {
          parts->append(S_BAk);
        }
          break;
        case Id::Expr_DecInt: {
          parts->append(StrFormat("{%s}", lexer::LazyStr(op)));
        }
          break;
        default: {
          assert(0);  // AssertionError
        }
      }
      return ;
    }
    if (op_tag == re_repeat_e::Range) {
      re_repeat::Range* op = static_cast<re_repeat::Range*>(UP_op);
      parts->append(StrFormat("{%s,%s}", op->lower, op->upper));
      return ;
    }
    FAIL(kNotImplemented);  // Python NotImplementedError
  }
  if (tag == re_e::Group) {
    re::Group* node = static_cast<re::Group*>(UP_node);
    capture_names->append(nullptr);
    parts->append(S_ijB);
    _AsPosixEre(node->child, parts, capture_names);
    parts->append(S_hxb);
    return ;
  }
  if (tag == re_e::Capture) {
    re::Capture* node = static_cast<re::Capture*>(UP_node);
    capture_str = node->name ? lexer::TokenVal(node->name) : nullptr;
    capture_names->append(capture_str);
    parts->append(S_ijB);
    _AsPosixEre(node->child, parts, capture_names);
    parts->append(S_hxb);
    return ;
  }
  if (tag == re_e::PerlClass) {
    PerlClass* node = static_cast<PerlClass*>(UP_node);
    n = node->name;
    chars = PERL_CLASS->at(node->name);
    if (node->negated) {
      pat = StrFormat("[^%s]", chars);
    }
    else {
      pat = StrFormat("[%s]", chars);
    }
    parts->append(pat);
    return ;
  }
  if (tag == re_e::PosixClass) {
    PosixClass* node = static_cast<PosixClass*>(UP_node);
    n = node->name;
    if (node->negated) {
      pat = StrFormat("[^[:%s:]]", n);
    }
    else {
      pat = StrFormat("[[:%s:]]", n);
    }
    parts->append(pat);
    return ;
  }
  if (tag == re_e::CharClass) {
    re::CharClass* node = static_cast<re::CharClass*>(UP_node);
    special_char_flags = NewList<int>(std::initializer_list<int>{0});
    non_special_parts = Alloc<List<BigStr*>>();
    for (ListIter<syntax_asdl::char_class_term_t*> it(node->terms); !it.Done(); it.Next()) {
      syntax_asdl::char_class_term_t* term = it.Value();
      StackRoot _for(&term    );
      _CharClassTermToEre(term, non_special_parts, special_char_flags);
    }
    parts->append(S_Eax);
    if (node->negated) {
      parts->append(S_EAB);
    }
    if ((special_char_flags->at(0) & FLAG_RBRACKET)) {
      parts->append(S_pcD);
    }
    parts->extend(non_special_parts);
    if ((special_char_flags->at(0) & FLAG_BACKSLASH)) {
      parts->append(S_Eef);
    }
    if ((special_char_flags->at(0) & FLAG_CARET)) {
      parts->append(S_EAB);
    }
    if ((special_char_flags->at(0) & FLAG_HYPHEN)) {
      parts->append(S_Bjq);
    }
    parts->append(S_pcD);
    return ;
  }
  FAIL(kNotImplemented);  // Python NotImplementedError
}

BigStr* AsPosixEre(value::Eggex* eggex) {
  List<BigStr*>* parts = nullptr;
  StackRoot _root0(&eggex);
  StackRoot _root1(&parts);

  if (eggex->as_ere != nullptr) {
    return eggex->as_ere;
  }
  parts = Alloc<List<BigStr*>>();
  _AsPosixEre(eggex->spliced, parts, eggex->capture_names);
  eggex->as_ere = S_Aoo->join(parts);
  return eggex->as_ere;
}

BigStr* CanonicalFlags(List<syntax_asdl::EggexFlag*>* flags) {
  List<BigStr*>* letters = nullptr;
  BigStr* flag_name = nullptr;
  StackRoot _root0(&flags);
  StackRoot _root1(&letters);
  StackRoot _root2(&flag_name);

  letters = Alloc<List<BigStr*>>();
  for (ListIter<syntax_asdl::EggexFlag*> it(flags); !it.Done(); it.Next()) {
    syntax_asdl::EggexFlag* flag = it.Value();
    StackRoot _for(&flag  );
    if (flag->negated) {
      p_die(S_dwl, flag->flag);
    }
    flag_name = lexer::TokenVal(flag->flag);
    if ((str_equals(flag_name, S_eil) || str_equals(flag_name, S_fdv))) {
      letters->append(S_eil);
    }
    else {
      if (str_equals(flag_name, S_ABj)) {
        letters->append(S_rob);
      }
      else {
        p_die(StrFormat("Invalid regex flag %r", flag_name), flag->flag);
      }
    }
  }
  letters->sort();
  return S_Aoo->join(letters);
}

int LibcFlags(BigStr* canonical_flags) {
  int libc_flags;
  StackRoot _root0(&canonical_flags);

  if (canonical_flags == nullptr) {
    return 0;
  }
  libc_flags = 0;
  for (StrIter it(canonical_flags); !it.Done(); it.Next()) {
    BigStr* ch = it.Value();
    StackRoot _for(&ch  );
    if (str_equals(ch, S_eil)) {
      libc_flags |= REG_ICASE;
    }
    else {
      if (str_equals(ch, S_rob)) {
        libc_flags |= REG_NEWLINE;
      }
      else {
        assert(0);  // AssertionError
      }
    }
  }
  return libc_flags;
}

}  // define namespace regex_translate

namespace val_ops {  // define

using syntax_asdl::loc;
using syntax_asdl::loc_t;
using syntax_asdl::command_t;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::eggex_ops;
using value_asdl::eggex_ops_t;
using value_asdl::regex_match;
using value_asdl::RegexMatch;
using value_asdl::Obj;
using error::e_die;

int ToInt(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&msg);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_val);

  UP_val = val;
  if (val->tag() == value_e::Int) {
    value::Int* val = static_cast<value::Int*>(UP_val);
    return mops::BigTruncate(val->i);
  }
  throw Alloc<error::TypeErr>(val, msg, blame_loc);
}

double ToFloat(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&msg);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_val);

  UP_val = val;
  if (val->tag() == value_e::Float) {
    value::Float* val = static_cast<value::Float*>(UP_val);
    return val->f;
  }
  throw Alloc<error::TypeErr>(val, msg, blame_loc);
}

BigStr* ToStr(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&msg);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_val);

  UP_val = val;
  if (val->tag() == value_e::Str) {
    value::Str* val = static_cast<value::Str*>(UP_val);
    return val->s;
  }
  throw Alloc<error::TypeErr>(val, msg, blame_loc);
}

List<value_asdl::value_t*>* ToList(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&msg);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_val);

  UP_val = val;
  if (val->tag() == value_e::List) {
    value::List* val = static_cast<value::List*>(UP_val);
    return val->items;
  }
  throw Alloc<error::TypeErr>(val, msg, blame_loc);
}

Dict<BigStr*, value_asdl::value_t*>* ToDict(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&msg);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_val);

  UP_val = val;
  if (val->tag() == value_e::Dict) {
    value::Dict* val = static_cast<value::Dict*>(UP_val);
    return val->d;
  }
  throw Alloc<error::TypeErr>(val, msg, blame_loc);
}

syntax_asdl::command_t* ToCommandFrag(value_asdl::value_t* val, BigStr* msg, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&msg);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_val);

  UP_val = val;
  if (val->tag() == value_e::CommandFrag) {
    value::CommandFrag* val = static_cast<value::CommandFrag*>(UP_val);
    return val->c;
  }
  throw Alloc<error::TypeErr>(val, msg, blame_loc);
}

BigStr* Stringify(value_asdl::value_t* val, syntax_asdl::loc_t* blame_loc, BigStr* op_desc) {
  value_asdl::value_t* UP_val = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&op_desc);
  StackRoot _root3(&UP_val);
  StackRoot _root4(&s);

  if (blame_loc == nullptr) {
    blame_loc = loc::Missing;
  }
  UP_val = val;
  switch (val->tag()) {
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      return val->s;
    }
      break;
    case value_e::Null: {
      s = S_lbA;
    }
      break;
    case value_e::Bool: {
      value::Bool* val = static_cast<value::Bool*>(UP_val);
      s = val->b ? S_FsF : S_Ctn;
    }
      break;
    case value_e::Int: {
      value::Int* val = static_cast<value::Int*>(UP_val);
      s = mops::ToStr(val->i);
    }
      break;
    case value_e::Float: {
      value::Float* val = static_cast<value::Float*>(UP_val);
      s = str(val->f);
    }
      break;
    case value_e::Eggex: {
      value::Eggex* val = static_cast<value::Eggex*>(UP_val);
      s = regex_translate::AsPosixEre(val);
    }
      break;
    default: {
      ;  // pass
      if (val->tag() == value_e::List) {
        throw Alloc<error::TypeErrVerbose>(StrFormat("%sgot a List, which can't be stringified (OILS-ERR-203)", op_desc), blame_loc);
      }
      throw Alloc<error::TypeErr>(val, StrFormat("%sexpected one of (Null Bool Int Float Str Eggex)", op_desc), blame_loc);
    }
  }
  return s;
}

List<BigStr*>* ToShellArray(value_asdl::value_t* val, syntax_asdl::loc_t* blame_loc, BigStr* prefix) {
  value_asdl::value_t* UP_val = nullptr;
  List<BigStr*>* strs = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&blame_loc);
  StackRoot _root2(&prefix);
  StackRoot _root3(&UP_val);
  StackRoot _root4(&strs);

  UP_val = val;
  switch (val->tag()) {
    case value_e::List: {
      value::List* val = static_cast<value::List*>(UP_val);
      strs = Alloc<List<BigStr*>>();
      for (ListIter<value_asdl::value_t*> it(val->items); !it.Done(); it.Next()) {
        value_asdl::value_t* item = it.Value();
        StackRoot _for(&item      );
        strs->append(Stringify(item, blame_loc, prefix));
      }
    }
      break;
    case value_e::BashArray: {
      value::BashArray* array_val = static_cast<value::BashArray*>(UP_val);
      strs = bash_impl::BashArray_GetValues(array_val);
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* sparse_val = static_cast<value::SparseArray*>(UP_val);
      strs = bash_impl::SparseArray_GetValues(sparse_val);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(val, StrFormat("%sexpected List", prefix), blame_loc);
    }
  }
  return strs;
}

Iterator::Iterator() {
  this->i = 0;
}

int Iterator::Index() {
  return this->i;
}

void Iterator::Next() {
  this->i += 1;
}

value_asdl::value_t* Iterator::FirstValue() {
  FAIL(kNotImplemented);  // Python NotImplementedError
}

value_asdl::value_t* Iterator::SecondValue() {
  assert(0);  // AssertionError
}

StdinIterator::StdinIterator(syntax_asdl::loc_t* blame_loc) : ::val_ops::Iterator() {
  this->blame_loc = blame_loc;
  this->f = mylib::Stdin();
}

value_asdl::value_t* StdinIterator::FirstValue() {
  BigStr* line = nullptr;
  StackRoot _root0(&line);

  try {
    line = this->f->readline();
  }
  catch (IOError_OSError* e) {
    if (e->errno_ == EINTR) {
      return value::Interrupted;
    }
    else {
      e_die(StrFormat("I/O error in for <> loop: %s", posix::strerror(e->errno_)), this->blame_loc);
    }
  }
  if (len(line) == 0) {
    return nullptr;
  }
  else {
    if (line->endswith(S_nfs)) {
      line = line->slice(0, -1);
    }
  }
  return Alloc<value::Str>(line);
}

ArrayIter::ArrayIter(List<BigStr*>* strs) : ::val_ops::Iterator() {
  this->strs = strs;
  this->n = len(strs);
}

value_asdl::value_t* ArrayIter::FirstValue() {
  if (this->i == this->n) {
    return nullptr;
  }
  return Alloc<value::Str>(this->strs->at(this->i));
}

RangeIterator::RangeIterator(value::Range* val) : ::val_ops::Iterator() {
  this->val = val;
}

value_asdl::value_t* RangeIterator::FirstValue() {
  if ((this->val->lower + this->i) >= this->val->upper) {
    return nullptr;
  }
  return Alloc<value::Int>(mops::IntWiden((this->val->lower + this->i)));
}

ListIterator::ListIterator(value::List* val) : ::val_ops::Iterator() {
  this->val = val;
  this->n = len(val->items);
}

value_asdl::value_t* ListIterator::FirstValue() {
  if (this->i == this->n) {
    return nullptr;
  }
  return this->val->items->at(this->i);
}

DictIterator::DictIterator(value::Dict* val) : ::val_ops::Iterator() {
  this->keys = val->d->keys();
  this->values = val->d->values();
  this->n = len(val->d);
}

value_asdl::value_t* DictIterator::FirstValue() {
  if (this->i == this->n) {
    return nullptr;
  }
  return Alloc<value::Str>(this->keys->at(this->i));
}

value_asdl::value_t* DictIterator::SecondValue() {
  return this->values->at(this->i);
}

bool ToBool(value_asdl::value_t* val) {
  value_asdl::value_t* UP_val = nullptr;
  StackRoot _root0(&val);
  StackRoot _root1(&UP_val);

  UP_val = val;
  switch (val->tag()) {
    case value_e::Undef: {
      return false;
    }
      break;
    case value_e::Null: {
      return false;
    }
      break;
    case value_e::Str: {
      value::Str* val = static_cast<value::Str*>(UP_val);
      return len(val->s) != 0;
    }
      break;
    case value_e::BashArray: {
      value::BashArray* val = static_cast<value::BashArray*>(UP_val);
      return !bash_impl::BashArray_IsEmpty(val);
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* val = static_cast<value::BashAssoc*>(UP_val);
      return !bash_impl::BashAssoc_IsEmpty(val);
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* val = static_cast<value::SparseArray*>(UP_val);
      return !bash_impl::SparseArray_IsEmpty(val);
    }
      break;
    case value_e::Bool: {
      value::Bool* val = static_cast<value::Bool*>(UP_val);
      return val->b;
    }
      break;
    case value_e::Int: {
      value::Int* val = static_cast<value::Int*>(UP_val);
      return !mops::Equal(val->i, mops::BigInt(0));
    }
      break;
    case value_e::Float: {
      value::Float* val = static_cast<value::Float*>(UP_val);
      return val->f != 0.0;
    }
      break;
    case value_e::List: {
      value::List* val = static_cast<value::List*>(UP_val);
      return len(val->items) > 0;
    }
      break;
    case value_e::Dict: {
      value::Dict* val = static_cast<value::Dict*>(UP_val);
      return len(val->d) > 0;
    }
      break;
    default: {
      return true;
    }
  }
}

bool ExactlyEqual(value_asdl::value_t* left, value_asdl::value_t* right, syntax_asdl::loc_t* blame_loc) {
  value_asdl::value_t* UP_left = nullptr;
  value_asdl::value_t* UP_right = nullptr;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&blame_loc);
  StackRoot _root3(&UP_left);
  StackRoot _root4(&UP_right);

  if ((left->tag() == value_e::Float or right->tag() == value_e::Float)) {
    throw Alloc<error::TypeErrVerbose>(S_ilD, blame_loc);
  }
  if (left->tag() != right->tag()) {
    return false;
  }
  UP_left = left;
  UP_right = right;
  switch (left->tag()) {
    case value_e::Undef: {
      return true;
    }
      break;
    case value_e::Null: {
      return true;
    }
      break;
    case value_e::Bool: {
      value::Bool* left = static_cast<value::Bool*>(UP_left);
      value::Bool* right = static_cast<value::Bool*>(UP_right);
      return left->b == right->b;
    }
      break;
    case value_e::Int: {
      value::Int* left = static_cast<value::Int*>(UP_left);
      value::Int* right = static_cast<value::Int*>(UP_right);
      return mops::Equal(left->i, right->i);
    }
      break;
    case value_e::Float: {
      assert(0);  // AssertionError
    }
      break;
    case value_e::Str: {
      value::Str* left = static_cast<value::Str*>(UP_left);
      value::Str* right = static_cast<value::Str*>(UP_right);
      return str_equals(left->s, right->s);
    }
      break;
    case value_e::BashArray: {
      value::BashArray* left = static_cast<value::BashArray*>(UP_left);
      value::BashArray* right = static_cast<value::BashArray*>(UP_right);
      return bash_impl::BashArray_Equals(left, right);
    }
      break;
    case value_e::SparseArray: {
      value::SparseArray* left = static_cast<value::SparseArray*>(UP_left);
      value::SparseArray* right = static_cast<value::SparseArray*>(UP_right);
      return bash_impl::SparseArray_Equals(left, right);
    }
      break;
    case value_e::List: {
      value::List* left = static_cast<value::List*>(UP_left);
      value::List* right = static_cast<value::List*>(UP_right);
      if (len(left->items) != len(right->items)) {
        return false;
      }
      for (int i = 0; i < len(left->items); ++i) {
        if (!ExactlyEqual(left->items->at(i), right->items->at(i), blame_loc)) {
          return false;
        }
      }
      return true;
    }
      break;
    case value_e::BashAssoc: {
      value::BashAssoc* left = static_cast<value::BashAssoc*>(UP_left);
      value::BashAssoc* right = static_cast<value::BashAssoc*>(UP_right);
      return bash_impl::BashAssoc_Equals(left, right);
    }
      break;
    case value_e::Dict: {
      value::Dict* left = static_cast<value::Dict*>(UP_left);
      value::Dict* right = static_cast<value::Dict*>(UP_right);
      if (len(left->d) != len(right->d)) {
        return false;
      }
      for (DictIter<BigStr*, value_asdl::value_t*> it(left->d); !it.Done(); it.Next()) {
        BigStr* k = it.Key();
        StackRoot _for(&k      );
        if ((!dict_contains(right->d, k) or !ExactlyEqual(right->d->at(k), left->d->at(k), blame_loc))) {
          return false;
        }
      }
      return true;
    }
      break;
  }
  throw Alloc<error::TypeErrVerbose>(StrFormat("Can't compare two values of type %s", ui::ValType(left)), blame_loc);
}

bool Contains(value_asdl::value_t* needle, value_asdl::value_t* haystack) {
  value_asdl::value_t* UP_haystack = nullptr;
  BigStr* s = nullptr;
  StackRoot _root0(&needle);
  StackRoot _root1(&haystack);
  StackRoot _root2(&UP_haystack);
  StackRoot _root3(&s);

  UP_haystack = haystack;
  switch (haystack->tag()) {
    case value_e::Dict: {
      value::Dict* haystack = static_cast<value::Dict*>(UP_haystack);
      s = ToStr(needle, S_lnF, loc::Missing);
      return dict_contains(haystack->d, s);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(haystack, S_idc, loc::Missing);
    }
  }
  return false;
}

bool MatchRegex(value_asdl::value_t* left, value_asdl::value_t* right, state::Mem* mem) {
  value_asdl::value_t* UP_right = nullptr;
  BigStr* right_s = nullptr;
  int regex_flags;
  value_asdl::eggex_ops_t* capture = nullptr;
  value_asdl::value_t* UP_left = nullptr;
  BigStr* left_s = nullptr;
  List<int>* indices = nullptr;
  StackRoot _root0(&left);
  StackRoot _root1(&right);
  StackRoot _root2(&mem);
  StackRoot _root3(&UP_right);
  StackRoot _root4(&right_s);
  StackRoot _root5(&capture);
  StackRoot _root6(&UP_left);
  StackRoot _root7(&left_s);
  StackRoot _root8(&indices);

  UP_right = right;
  switch (right->tag()) {
    case value_e::Str: {
      value::Str* right = static_cast<value::Str*>(UP_right);
      right_s = right->s;
      regex_flags = 0;
      capture = eggex_ops::No;
    }
      break;
    case value_e::Eggex: {
      value::Eggex* right = static_cast<value::Eggex*>(UP_right);
      right_s = regex_translate::AsPosixEre(right);
      regex_flags = regex_translate::LibcFlags(right->canonical_flags);
      capture = Alloc<eggex_ops::Yes>(right->convert_funcs, right->convert_toks, right->capture_names);
    }
      break;
    default: {
      throw Alloc<error::TypeErr>(right, S_nAf, loc::Missing);
    }
  }
  UP_left = left;
  left_s = nullptr;
  switch (left->tag()) {
    case value_e::Str: {
      value::Str* left = static_cast<value::Str*>(UP_left);
      left_s = left->s;
    }
      break;
    default: {
      throw Alloc<error::TypeErrVerbose>(S_Atu, loc::Missing);
    }
  }
  indices = libc::regex_search(right_s, regex_flags, left_s, 0);
  if (indices != nullptr) {
    if (mem) {
      mem->SetRegexMatch(Alloc<RegexMatch>(left_s, indices, capture));
    }
    return true;
  }
  else {
    if (mem) {
      mem->SetRegexMatch(regex_match::No);
    }
    return false;
  }
}

value_asdl::value_t* IndexMetaMethod(value_asdl::Obj* obj) {
  value_asdl::value_t* index_val = nullptr;
  StackRoot _root0(&obj);
  StackRoot _root1(&index_val);

  if (!obj->prototype) {
    return nullptr;
  }
  index_val = obj->prototype->d->get(S_opF);
  if (!index_val) {
    return nullptr;
  }
  if ((index_val->tag() != value_e::BuiltinFunc && index_val->tag() != value_e::Func)) {
    return nullptr;
  }
  return index_val;
}

}  // define namespace val_ops

namespace bracket_osh {  // define

using id_kind_asdl::Id;
using syntax_asdl::loc;
using syntax_asdl::word;
using syntax_asdl::word_e;
using syntax_asdl::word_t;
using syntax_asdl::bool_expr;
using types_asdl::lex_mode_e;
using value_asdl::value;
using error::e_usage;
using error::p_die;

_StringWordEmitter::_StringWordEmitter(cmd_value::Argv* cmd_val) {
  this->cmd_val = cmd_val;
  this->i = 0;
  this->n = len(cmd_val->argv);
}

word::String* _StringWordEmitter::ReadWord(types_asdl::lex_mode_t unused_lex_mode) {
  word::String* w = nullptr;
  BigStr* s = nullptr;
  syntax_asdl::CompoundWord* arg_loc = nullptr;
  int id_;
  StackRoot _root0(&w);
  StackRoot _root1(&s);
  StackRoot _root2(&arg_loc);

  if (this->i == this->n) {
    w = Alloc<word::String>(Id::Eof_Real, S_Aoo, nullptr);
    return w;
  }
  s = this->cmd_val->argv->at(this->i);
  arg_loc = this->cmd_val->arg_locs->at(this->i);
  this->i += 1;
  id_ = match::BracketUnary(s);
  if (id_ == Id::Undefined_Tok) {
    id_ = match::BracketBinary(s);
  }
  if (id_ == Id::Undefined_Tok) {
    id_ = match::BracketOther(s);
  }
  if (id_ == Id::Undefined_Tok) {
    id_ = Id::Word_Compound;
  }
  return Alloc<word::String>(id_, s, arg_loc);
}

word::String* _StringWordEmitter::Read() {
  return this->ReadWord(lex_mode_e::ShCommand);
}

BigStr* _StringWordEmitter::Peek(int offset) {
  return this->cmd_val->argv->at((this->i + offset));
}

void _StringWordEmitter::Rewind(int offset) {
  this->i -= offset;
}

_WordEvaluator::_WordEvaluator() : ::word_eval::StringWordEvaluator() {
}

value::Str* _WordEvaluator::EvalWordToString(syntax_asdl::word_t* w, int eval_flags) {
  word::String* string_word = nullptr;
  StackRoot _root0(&w);
  StackRoot _root1(&string_word);

  string_word = static_cast<word::String*>(w);
  return Alloc<value::Str>(string_word->s);
}

syntax_asdl::bool_expr_t* _TwoArgs(bracket_osh::_StringWordEmitter* w_parser) {
  word::String* w0 = nullptr;
  word::String* w1 = nullptr;
  BigStr* s0 = nullptr;
  int unary_id;
  StackRoot _root0(&w_parser);
  StackRoot _root1(&w0);
  StackRoot _root2(&w1);
  StackRoot _root3(&s0);

  w0 = w_parser->Read();
  w1 = w_parser->Read();
  s0 = w0->s;
  if (str_equals(s0, S_kao)) {
    return Alloc<bool_expr::LogicalNot>(Alloc<bool_expr::WordTest>(w1));
  }
  unary_id = Id::Undefined_Tok;
  if (w0->s->startswith(S_gpk)) {
    if (str_equals(s0, S_pgr)) {
      unary_id = Id::BoolUnary_d;
    }
    else {
      if (str_equals(s0, S_rxp)) {
        unary_id = Id::BoolUnary_e;
      }
      else {
        if (str_equals(s0, S_ugu)) {
          unary_id = Id::BoolUnary_f;
        }
        else {
          if (str_equals(s0, S_ukB)) {
            unary_id = Id::BoolUnary_L;
          }
          else {
            if (str_equals(s0, S_bAm)) {
              unary_id = Id::BoolUnary_true;
            }
            else {
              if (str_equals(s0, S_cyo)) {
                unary_id = Id::BoolUnary_false;
              }
            }
          }
        }
      }
    }
  }
  if (unary_id == Id::Undefined_Tok) {
    unary_id = match::BracketUnary(w0->s);
  }
  if (unary_id == Id::Undefined_Tok) {
    p_die(StrFormat("Expected unary operator, got %r (2 args)", w0->s), Alloc<loc::Word>(w0));
  }
  return Alloc<bool_expr::Unary>(unary_id, w1);
}

syntax_asdl::bool_expr_t* _ThreeArgs(bracket_osh::_StringWordEmitter* w_parser) {
  word::String* w0 = nullptr;
  word::String* w1 = nullptr;
  word::String* w2 = nullptr;
  int binary_id;
  syntax_asdl::bool_expr_t* child = nullptr;
  StackRoot _root0(&w_parser);
  StackRoot _root1(&w0);
  StackRoot _root2(&w1);
  StackRoot _root3(&w2);
  StackRoot _root4(&child);

  w0 = w_parser->Read();
  w1 = w_parser->Read();
  w2 = w_parser->Read();
  binary_id = match::BracketBinary(w1->s);
  if (binary_id != Id::Undefined_Tok) {
    return Alloc<bool_expr::Binary>(binary_id, w0, w2);
  }
  if (str_equals(w1->s, S_nlm)) {
    return Alloc<bool_expr::LogicalAnd>(Alloc<bool_expr::WordTest>(w0), Alloc<bool_expr::WordTest>(w2));
  }
  if (str_equals(w1->s, S_rba)) {
    return Alloc<bool_expr::LogicalOr>(Alloc<bool_expr::WordTest>(w0), Alloc<bool_expr::WordTest>(w2));
  }
  if (str_equals(w0->s, S_kao)) {
    w_parser->Rewind(2);
    child = _TwoArgs(w_parser);
    return Alloc<bool_expr::LogicalNot>(child);
  }
  if ((str_equals(w0->s, S_ijB) and str_equals(w2->s, S_hxb))) {
    return Alloc<bool_expr::WordTest>(w1);
  }
  p_die(StrFormat("Expected binary operator, got %r (3 args)", w1->s), Alloc<loc::Word>(w1));
}

Test::Test(bool need_right_bracket, optview::Exec* exec_opts, state::Mem* mem, ui::ErrorFormatter* errfmt) {
  this->need_right_bracket = need_right_bracket;
  this->exec_opts = exec_opts;
  this->mem = mem;
  this->errfmt = errfmt;
}

int Test::Run(cmd_value::Argv* cmd_val) {
  List<BigStr*>* strs = nullptr;
  bracket_osh::_StringWordEmitter* w_parser = nullptr;
  bool_parse::BoolParser* b_parser = nullptr;
  syntax_asdl::bool_expr_t* bool_node = nullptr;
  int n;
  word::String* w = nullptr;
  BigStr* a0 = nullptr;
  syntax_asdl::bool_expr_t* child = nullptr;
  bracket_osh::_WordEvaluator* word_ev = nullptr;
  sh_expr_eval::BoolEvaluator* bool_ev = nullptr;
  bool b;
  int status;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&strs);
  StackRoot _root2(&w_parser);
  StackRoot _root3(&b_parser);
  StackRoot _root4(&bool_node);
  StackRoot _root5(&w);
  StackRoot _root6(&a0);
  StackRoot _root7(&child);
  StackRoot _root8(&word_ev);
  StackRoot _root9(&bool_ev);

  typed_args::DoesNotAccept(cmd_val->proc_args);
  if (this->need_right_bracket) {
    if (this->exec_opts->simple_test_builtin()) {
      e_usage(S_Ayw, loc::Missing);
    }
    strs = cmd_val->argv;
    if ((len(strs) == 0 or !(str_equals(strs->at(-1), S_pcD)))) {
      this->errfmt->Print_(S_uCh, cmd_val->arg_locs->at(0));
      return 2;
    }
    cmd_val->argv->pop();
    cmd_val->arg_locs->pop();
  }
  w_parser = Alloc<_StringWordEmitter>(cmd_val);
  w_parser->Read();
  b_parser = Alloc<bool_parse::BoolParser>(w_parser);
  bool_node = nullptr;
  n = (len(cmd_val->argv) - 1);
  if ((this->exec_opts->simple_test_builtin() and n > 3)) {
    e_usage(S_Ajx, loc::Missing);
  }
  try {
    if (n == 0) {
      return 1;
    }
    else {
      if (n == 1) {
        w = w_parser->Read();
        bool_node = Alloc<bool_expr::WordTest>(w);
      }
      else {
        if (n == 2) {
          bool_node = _TwoArgs(w_parser);
        }
        else {
          if (n == 3) {
            bool_node = _ThreeArgs(w_parser);
          }
        }
      }
    }
    if (n == 4) {
      a0 = w_parser->Peek(0);
      if (str_equals(a0, S_kao)) {
        w_parser->Read();
        child = _ThreeArgs(w_parser);
        bool_node = Alloc<bool_expr::LogicalNot>(child);
      }
      else {
        if ((str_equals(a0, S_ijB) and str_equals(w_parser->Peek(3), S_hxb))) {
          w_parser->Read();
          bool_node = _TwoArgs(w_parser);
        }
        else {
          ;  // pass
        }
      }
    }
    if (bool_node == nullptr) {
      bool_node = b_parser->ParseForBuiltin();
    }
  }
  catch (error::Parse* e) {
    this->errfmt->PrettyPrintError(e, S_AuE);
    return 2;
  }
  word_ev = Alloc<_WordEvaluator>();
  bool_ev = Alloc<sh_expr_eval::BoolEvaluator>(this->mem, this->exec_opts, nullptr, nullptr, this->errfmt, true);
  bool_ev->word_ev = word_ev;
  bool_ev->CheckCircularDeps();
  try {
    b = bool_ev->EvalB(bool_node);
  }
  catch (error::_ErrorWithLocation* e) {
    this->errfmt->PrettyPrintError(e, S_AuE);
    return 2;
  }
  status = b ? 0 : 1;
  return status;
}

}  // define namespace bracket_osh

namespace completion_osh {  // define

using syntax_asdl::loc;
using value_asdl::value;
using value_asdl::value_e;
using mylib::print_stderr;

_FixedWordsAction::_FixedWordsAction(List<BigStr*>* d) {
  this->d = d;
}

void _FixedWordsAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  StackRoot _root0(&comp);

  for (ListIter<BigStr*> it(sorted(this->d)); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    if (name->startswith(comp->to_complete)) {
      YIELD->append(name);
    }
  }
}

void _FixedWordsAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_Ekf);
}

_DynamicProcDictAction::_DynamicProcDictAction(state::Procs* d) {
  this->d = d;
}

void _DynamicProcDictAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  StackRoot _root0(&comp);

  for (ListIter<BigStr*> it(this->d->InvokableNames()); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    if (name->startswith(comp->to_complete)) {
      YIELD->append(name);
    }
  }
}

void _DynamicProcDictAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_koi);
}

_DynamicStrDictAction::_DynamicStrDictAction(Dict<BigStr*, BigStr*>* d) {
  this->d = d;
}

void _DynamicStrDictAction::Matches(completion::Api* comp, List<BigStr*>* YIELD) {
  StackRoot _root0(&comp);

  for (ListIter<BigStr*> it(sorted(this->d)); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    if (name->startswith(comp->to_complete)) {
      YIELD->append(name);
    }
  }
}

void _DynamicStrDictAction::Print(mylib::BufWriter* f) {
  StackRoot _root0(&f);

  f->write(S_mdp);
}

SpecBuilder::SpecBuilder(cmd_eval::CommandEvaluator* cmd_ev, parse_lib::ParseContext* parse_ctx, word_eval::NormalWordEvaluator* word_ev, split::SplitContext* splitter, completion::Lookup* comp_lookup, Dict<BigStr*, BigStr*>* help_data, ui::ErrorFormatter* errfmt) {
  this->cmd_ev = cmd_ev;
  this->parse_ctx = parse_ctx;
  this->word_ev = word_ev;
  this->splitter = splitter;
  this->comp_lookup = comp_lookup;
  this->help_data = help_data;
  this->topic_list = nullptr;
  this->errfmt = errfmt;
}

completion::UserSpec* SpecBuilder::Build(List<BigStr*>* argv, args::_Attributes* attrs, Dict<BigStr*, bool>* base_opts) {
  cmd_eval::CommandEvaluator* cmd_ev = nullptr;
  arg_types::compgen* arg = nullptr;
  List<completion::CompletionAction*>* actions = nullptr;
  BigStr* func_name = nullptr;
  value::Proc* func = nullptr;
  BigStr* command = nullptr;
  completion::CompletionAction* a = nullptr;
  word_parse::WordParser* w_parser = nullptr;
  syntax_asdl::CompoundWord* arg_word = nullptr;
  List<completion::CompletionAction*>* extra_actions = nullptr;
  List<completion::CompletionAction*>* else_actions = nullptr;
  completion::_Predicate* p = nullptr;
  BigStr* filter_pat = nullptr;
  BigStr* prefix = nullptr;
  BigStr* suffix = nullptr;
  StackRoot _root0(&argv);
  StackRoot _root1(&attrs);
  StackRoot _root2(&base_opts);
  StackRoot _root3(&cmd_ev);
  StackRoot _root4(&arg);
  StackRoot _root5(&actions);
  StackRoot _root6(&func_name);
  StackRoot _root7(&func);
  StackRoot _root8(&command);
  StackRoot _root9(&a);
  StackRoot _root10(&w_parser);
  StackRoot _root11(&arg_word);
  StackRoot _root12(&extra_actions);
  StackRoot _root13(&else_actions);
  StackRoot _root14(&p);
  StackRoot _root15(&filter_pat);
  StackRoot _root16(&prefix);
  StackRoot _root17(&suffix);

  cmd_ev = this->cmd_ev;
  arg = Alloc<arg_types::compgen>(attrs->attrs);
  actions = Alloc<List<completion::CompletionAction*>>();
  if (arg->F != nullptr) {
    func_name = arg->F;
    func = cmd_ev->procs->GetShellFunc(func_name);
    if (func == nullptr) {
      throw Alloc<error::Usage>(StrFormat("shell function %r not found", func_name), loc::Missing);
    }
    actions->append(Alloc<completion::ShellFuncAction>(cmd_ev, func, this->comp_lookup));
  }
  if (arg->C != nullptr) {
    command = arg->C;
    actions->append(Alloc<completion::CommandAction>(cmd_ev, command));
    print_stderr(S_gxD);
  }
  for (ListIter<BigStr*> it(attrs->actions); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    if (str_equals(name, S_nwn)) {
      a = Alloc<_DynamicStrDictAction>(this->parse_ctx->aliases);
    }
    else {
      if (str_equals(name, S_kAh)) {
        a = Alloc<_FixedWordsAction>(NewList<BigStr*>(std::initializer_list<BigStr*>{S_FbA}));
      }
      else {
        if (str_equals(name, S_utc)) {
          a = Alloc<_FixedWordsAction>(consts::BUILTIN_NAMES);
        }
        else {
          if (str_equals(name, S_zij)) {
            actions->append(Alloc<_FixedWordsAction>(consts::BUILTIN_NAMES));
            actions->append(Alloc<_DynamicStrDictAction>(this->parse_ctx->aliases));
            actions->append(Alloc<_DynamicProcDictAction>(cmd_ev->procs));
            actions->append(Alloc<_FixedWordsAction>(consts::OSH_KEYWORD_NAMES));
            actions->append(Alloc<completion::FileSystemAction>(false, true, false));
            a = Alloc<completion::ExternalCommandAction>(cmd_ev->mem);
          }
          else {
            if (str_equals(name, S_nmo)) {
              a = Alloc<completion::FileSystemAction>(true, false, false);
            }
            else {
              if (str_equals(name, S_rkC)) {
                a = Alloc<completion::ExportedVarsAction>(cmd_ev->mem);
              }
              else {
                if (str_equals(name, S_xeh)) {
                  a = Alloc<completion::FileSystemAction>(false, false, false);
                }
                else {
                  if (str_equals(name, S_cgg)) {
                    a = Alloc<_DynamicProcDictAction>(cmd_ev->procs);
                  }
                  else {
                    if (str_equals(name, S_orw)) {
                      a = Alloc<_FixedWordsAction>(NewList<BigStr*>(std::initializer_list<BigStr*>{S_gbr}));
                    }
                    else {
                      if (str_equals(name, S_evo)) {
                        a = Alloc<_FixedWordsAction>(consts::OSH_KEYWORD_NAMES);
                      }
                      else {
                        if (str_equals(name, S_sqx)) {
                          a = Alloc<completion::UsersAction>();
                        }
                        else {
                          if (str_equals(name, S_unB)) {
                            a = Alloc<completion::VariablesAction>(cmd_ev->mem);
                          }
                          else {
                            if (str_equals(name, S_yqg)) {
                              if (this->topic_list == nullptr) {
                                this->topic_list = this->help_data->keys();
                              }
                              a = Alloc<_FixedWordsAction>(this->topic_list);
                            }
                            else {
                              if (str_equals(name, S_uem)) {
                                a = Alloc<_FixedWordsAction>(consts::SET_OPTION_NAMES);
                              }
                              else {
                                if (str_equals(name, S_ene)) {
                                  a = Alloc<_FixedWordsAction>(consts::SHOPT_OPTION_NAMES);
                                }
                                else {
                                  if (str_equals(name, S_wzk)) {
                                    a = Alloc<_FixedWordsAction>(NewList<BigStr*>(std::initializer_list<BigStr*>{S_afz}));
                                  }
                                  else {
                                    if (str_equals(name, S_AAt)) {
                                      a = Alloc<_FixedWordsAction>(NewList<BigStr*>(std::initializer_list<BigStr*>{S_gbr}));
                                    }
                                    else {
                                      assert(0);  // AssertionError
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    actions->append(a);
  }
  if (arg->W != nullptr) {
    w_parser = this->parse_ctx->MakeWordParserForPlugin(arg->W);
    try {
      arg_word = w_parser->ReadForPlugin();
    }
    catch (error::Parse* e) {
      this->errfmt->PrettyPrintError(e);
      throw;
    }
    a = Alloc<completion::DynamicWordsAction>(this->word_ev, this->splitter, arg_word, this->errfmt);
    actions->append(a);
  }
  extra_actions = Alloc<List<completion::CompletionAction*>>();
  if (base_opts->get(S_ccq, false)) {
    extra_actions->append(Alloc<completion::FileSystemAction>(true, false, false));
  }
  else_actions = Alloc<List<completion::CompletionAction*>>();
  if (base_opts->get(S_vlb, false)) {
    else_actions->append(Alloc<completion::FileSystemAction>(false, false, false));
  }
  if (base_opts->get(S_aml, false)) {
    else_actions->append(Alloc<completion::FileSystemAction>(true, false, false));
  }
  if ((len(actions) == 0 and len(else_actions) == 0)) {
    throw Alloc<error::Usage>(StrFormat("No actions defined in completion: %s", S_yfw->join(argv)), loc::Missing);
  }
  p = Alloc<completion::DefaultPredicate>();
  if (arg->X != nullptr) {
    filter_pat = arg->X;
    if (filter_pat->startswith(S_kao)) {
      p = Alloc<completion::GlobPredicate>(false, filter_pat->slice(1));
    }
    else {
      p = Alloc<completion::GlobPredicate>(true, filter_pat);
    }
  }
  prefix = arg->P;
  if (prefix == nullptr) {
    prefix = S_Aoo;
  }
  suffix = arg->S;
  if (suffix == nullptr) {
    suffix = S_Aoo;
  }
  return Alloc<completion::UserSpec>(actions, extra_actions, else_actions, p, prefix, suffix);
}

Complete::Complete(completion_osh::SpecBuilder* spec_builder, completion::Lookup* comp_lookup) {
  this->spec_builder = spec_builder;
  this->comp_lookup = comp_lookup;
}

int Complete::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  args::_Attributes* attrs = nullptr;
  arg_types::complete* arg = nullptr;
  List<BigStr*>* commands = nullptr;
  Dict<BigStr*, bool>* base_opts = nullptr;
  completion::UserSpec* user_spec = nullptr;
  List<BigStr*>* patterns = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&attrs);
  StackRoot _root3(&arg);
  StackRoot _root4(&commands);
  StackRoot _root5(&base_opts);
  StackRoot _root6(&user_spec);
  StackRoot _root7(&patterns);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  attrs = flag_util::ParseMore(S_hyn, arg_r);
  arg = Alloc<arg_types::complete>(attrs->attrs);
  commands = arg_r->Rest();
  if (arg->D) {
    commands->append(S_jaj);
  }
  if (arg->E) {
    commands->append(S_lgv);
  }
  if (len(commands) == 0) {
    if (len(cmd_val->argv) == 1) {
      this->comp_lookup->PrintSpecs();
      return 0;
    }
    else {
      throw Alloc<error::Usage>(S_mrk, loc::Missing);
    }
  }
  base_opts = dict(attrs->opt_changes);
  try {
    user_spec = this->spec_builder->Build(cmd_val->argv, attrs, base_opts);
  }
  catch (error::Parse* e) {
    return 2;
  }
  for (ListIter<BigStr*> it(commands); !it.Done(); it.Next()) {
    BigStr* command = it.Value();
    StackRoot _for(&command  );
    this->comp_lookup->RegisterName(command, base_opts, user_spec);
  }
  patterns = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(patterns); !it.Done(); it.Next()) {
    BigStr* pat = it.Value();
    StackRoot _for(&pat  );
    this->comp_lookup->RegisterGlob(pat, base_opts, user_spec);
  }
  return 0;
}

CompGen::CompGen(completion_osh::SpecBuilder* spec_builder) {
  this->spec_builder = spec_builder;
}

int CompGen::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  args::_Attributes* arg = nullptr;
  BigStr* to_complete = nullptr;
  bool matched;
  Dict<BigStr*, bool>* base_opts = nullptr;
  completion::UserSpec* user_spec = nullptr;
  completion::Api* comp = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&arg);
  StackRoot _root3(&to_complete);
  StackRoot _root4(&base_opts);
  StackRoot _root5(&user_spec);
  StackRoot _root6(&comp);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  arg = flag_util::ParseMore(S_pqd, arg_r);
  if (arg_r->AtEnd()) {
    to_complete = S_Aoo;
  }
  else {
    to_complete = arg_r->Peek();
    arg_r->Next();
  }
  matched = false;
  base_opts = dict(arg->opt_changes);
  try {
    user_spec = this->spec_builder->Build(cmd_val->argv, arg, base_opts);
  }
  catch (error::Parse* e) {
    return 2;
  }
  matched = false;
  comp = Alloc<completion::Api>(S_Aoo, 0, 0);
  comp->Update(S_pqd, to_complete, S_Aoo, -1, nullptr);
  try {
    List<Tuple2<BigStr*, runtime_asdl::comp_action_t>*> YIELD_for_0;
    user_spec->AllMatches(comp, &YIELD_for_0);
    for (ListIter<Tuple2<BigStr*, runtime_asdl::comp_action_t>*> it(&YIELD_for_0); !it.Done(); it.Next()) {
      Tuple2<BigStr*, runtime_asdl::comp_action_t>* tup1 = it.Value();
      BigStr* m = tup1->at0();
      StackRoot _unpack_0(&m);
      matched = true;
      print(m);
    }
  }
  catch (error::FatalRuntime*) {
    return 1;
  }
  return matched ? 0 : 1;
}

CompOpt::CompOpt(completion::OptionState* comp_state, ui::ErrorFormatter* errfmt) {
  this->comp_state = comp_state;
  this->errfmt = errfmt;
}

int CompOpt::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  args::_Attributes* arg = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&arg);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  arg = flag_util::ParseMore(S_ore, arg_r);
  if (!this->comp_state->currently_completing) {
    this->errfmt->Print_(S_sbp);
    return 1;
  }
  this->comp_state->dynamic_opts->update(arg->opt_changes);
  return 0;
}

CompAdjust::CompAdjust(state::Mem* mem) {
  this->mem = mem;
}

int CompAdjust::Run(cmd_value::Argv* cmd_val) {
  args::Reader* arg_r = nullptr;
  args::_Attributes* attrs = nullptr;
  arg_types::compadjust* arg = nullptr;
  List<BigStr*>* var_names = nullptr;
  value_asdl::value_t* val = nullptr;
  List<BigStr*>* comp_argv = nullptr;
  List<BigStr*>* break_chars = nullptr;
  BigStr* omit_chars = nullptr;
  List<BigStr*>* adjusted_argv = nullptr;
  int n;
  BigStr* cur = nullptr;
  BigStr* prev = nullptr;
  BigStr* split = nullptr;
  StackRoot _root0(&cmd_val);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&attrs);
  StackRoot _root3(&arg);
  StackRoot _root4(&var_names);
  StackRoot _root5(&val);
  StackRoot _root6(&comp_argv);
  StackRoot _root7(&break_chars);
  StackRoot _root8(&omit_chars);
  StackRoot _root9(&adjusted_argv);
  StackRoot _root10(&cur);
  StackRoot _root11(&prev);
  StackRoot _root12(&split);

  arg_r = Alloc<args::Reader>(cmd_val->argv, cmd_val->arg_locs);
  arg_r->Next();
  attrs = flag_util::ParseMore(S_qxt, arg_r);
  arg = Alloc<arg_types::compadjust>(attrs->attrs);
  var_names = arg_r->Rest();
  for (ListIter<BigStr*> it(var_names); !it.Done(); it.Next()) {
    BigStr* name = it.Value();
    StackRoot _for(&name  );
    if (!list_contains(NewList<BigStr*>(std::initializer_list<BigStr*>{S_CgA, S_fmh, S_pho, S_CBu}), name)) {
      throw Alloc<error::Usage>(StrFormat("Invalid output variable name %r", name), loc::Missing);
    }
  }
  val = this->mem->GetValue(S_FiD);
  if (val->tag() == value_e::BashArray) {
    comp_argv = static_cast<value::BashArray*>(val)->strs;
  }
  else {
    if (val->tag() == value_e::SparseArray) {
      comp_argv = bash_impl::SparseArray_GetValues(static_cast<value::SparseArray*>(val));
    }
    else {
      throw Alloc<error::Usage>(S_uox, loc::Missing);
    }
  }
  break_chars = NewList<BigStr*>(std::initializer_list<BigStr*>{S_fyj, S_bby});
  if (arg->s) {
    break_chars->remove(S_bby);
  }
  omit_chars = arg->n;
  if (omit_chars == nullptr) {
    omit_chars = S_Aoo;
  }
  for (StrIter it(omit_chars); !it.Done(); it.Next()) {
    BigStr* c = it.Value();
    StackRoot _for(&c  );
    if (list_contains(break_chars, c)) {
      break_chars->remove(c);
    }
  }
  adjusted_argv = Alloc<List<BigStr*>>();
  for (ListIter<BigStr*> it(comp_argv); !it.Done(); it.Next()) {
    BigStr* a = it.Value();
    StackRoot _for(&a  );
    if (a != nullptr) {
      completion::AdjustArg(a, break_chars, adjusted_argv);
    }
  }
  if (list_contains(var_names, S_pho)) {
    state::BuiltinSetArray(this->mem, S_pho, adjusted_argv);
  }
  n = len(adjusted_argv);
  cur = n < 1 ? S_Aoo : adjusted_argv->at(-1);
  prev = n < 2 ? S_Aoo : adjusted_argv->at(-2);
  if (arg->s) {
    if ((cur->startswith(S_gpk) and str_contains(cur, S_bby))) {
      Tuple2<BigStr*, BigStr*> tup2 = mylib::split_once(cur, S_bby);
      prev = tup2.at0();
      cur = tup2.at1();
      split = S_FsF;
    }
    else {
      split = S_Ctn;
    }
    state::BuiltinSetString(this->mem, S_umv, split);
  }
  if (list_contains(var_names, S_CgA)) {
    state::BuiltinSetString(this->mem, S_CgA, cur);
  }
  if (list_contains(var_names, S_fmh)) {
    state::BuiltinSetString(this->mem, S_fmh, prev);
  }
  if (list_contains(var_names, S_CBu)) {
    state::BuiltinSetString(this->mem, S_CBu, str((n - 1)));
  }
  return 0;
}

}  // define namespace completion_osh

namespace shell {  // define

using option_asdl::option_i;
using option_asdl::builtin_i;
using syntax_asdl::loc;
using syntax_asdl::source;
using syntax_asdl::source_t;
using syntax_asdl::IntParamBox;
using syntax_asdl::debug_frame;
using syntax_asdl::debug_frame_t;
using value_asdl::value;
using value_asdl::value_e;
using value_asdl::value_t;
using value_asdl::value_str;
using value_asdl::Obj;
using mylib::print_stderr;

void _InitDefaultCompletions(cmd_eval::CommandEvaluator* cmd_ev, completion_osh::Complete* complete_builtin, completion::Lookup* comp_lookup) {
  StackRoot _root0(&cmd_ev);
  StackRoot _root1(&complete_builtin);
  StackRoot _root2(&comp_lookup);

  complete_builtin->Run(cmd_eval::MakeBuiltinArgv(NewList<BigStr*>(std::initializer_list<BigStr*>{S_Fha, S_Fyt, S_zij})));
  complete_builtin->Run(cmd_eval::MakeBuiltinArgv(NewList<BigStr*>(std::initializer_list<BigStr*>{S_nvg, S_Fyt, S_xeh})));
}

void _CompletionDemo(completion::Lookup* comp_lookup) {
  completion::TestAction* A1 = nullptr;
  List<BigStr*>* l = nullptr;
  completion::TestAction* A2 = nullptr;
  completion::UserSpec* C1 = nullptr;
  StackRoot _root0(&comp_lookup);
  StackRoot _root1(&A1);
  StackRoot _root2(&l);
  StackRoot _root3(&A2);
  StackRoot _root4(&C1);

  A1 = Alloc<completion::TestAction>(NewList<BigStr*>(std::initializer_list<BigStr*>{S_paw, S_lqB, S_sdC}), 0.0);
  l = Alloc<List<BigStr*>>();
  for (int i = 0; i < 5; ++i) {
    l->append(StrFormat("m%d", i));
  }
  A2 = Alloc<completion::TestAction>(l, 0.1);
  C1 = Alloc<completion::UserSpec>(NewList<completion::CompletionAction*>(std::initializer_list<completion::CompletionAction*>{A1, A2}), Alloc<List<completion::CompletionAction*>>(), Alloc<List<completion::CompletionAction*>>(), Alloc<completion::DefaultPredicate>(), S_Aoo, S_Aoo);
  comp_lookup->RegisterName(S_vfo, Alloc<Dict<BigStr*, bool>>(), C1);
}

void SourceStartupFile(process::FdState* fd_state, BigStr* rc_path, BigStr* lang, parse_lib::ParseContext* parse_ctx, cmd_eval::CommandEvaluator* cmd_ev, ui::ErrorFormatter* errfmt) {
  mylib::LineReader* f = nullptr;
  alloc::Arena* arena = nullptr;
  reader::FileLineReader* rc_line_reader = nullptr;
  cmd_parse::CommandParser* rc_c_parser = nullptr;
  int unused;
  (void)unused;
  StackRoot _root0(&fd_state);
  StackRoot _root1(&rc_path);
  StackRoot _root2(&lang);
  StackRoot _root3(&parse_ctx);
  StackRoot _root4(&cmd_ev);
  StackRoot _root5(&errfmt);
  StackRoot _root6(&f);
  StackRoot _root7(&arena);
  StackRoot _root8(&rc_line_reader);
  StackRoot _root9(&rc_c_parser);

  try {
    f = fd_state->Open(rc_path);
  }
  catch (IOError_OSError* e) {
    if (e->errno_ != ENOENT) {
      throw;
    }
    return ;
  }
  arena = parse_ctx->arena;
  rc_line_reader = Alloc<reader::FileLineReader>(f, arena);
  rc_c_parser = parse_ctx->MakeOshParser(rc_line_reader);
  {  // with
    alloc::ctx_SourceCode ctx{arena, Alloc<source::MainFile>(rc_path)};

    unused = main_loop::Batch(cmd_ev, rc_c_parser, errfmt);
  }
  f->close();
}

ShellOptHook::ShellOptHook(py_readline::Readline* readline) {
  this->readline = readline;
}

bool ShellOptHook::OnChange(List<bool>* opt0_array, BigStr* opt_name, bool b) {
  StackRoot _root0(&opt0_array);
  StackRoot _root1(&opt_name);

  if ((str_equals(opt_name, S_dmp) or str_equals(opt_name, S_lcm))) {
    if (this->readline) {
      this->readline->parse_and_bind(str_concat(S_akf, opt_name));
    }
    else {
      print_stderr(StrFormat("Warning: Can't set option %r because shell wasn't compiled with GNU readline", opt_name));
      return false;
    }
    if (str_equals(opt_name, S_dmp)) {
      opt0_array->set(option_i::emacs, !b);
    }
    else {
      if (str_equals(opt_name, S_lcm)) {
        opt0_array->set(option_i::vi, !b);
      }
    }
  }
  return true;
}

void _AddBuiltinFunc(state::Mem* mem, BigStr* name, vm::_Callable* func) {
  StackRoot _root0(&mem);
  StackRoot _root1(&name);
  StackRoot _root2(&func);

  mem->AddBuiltin(name, Alloc<value::BuiltinFunc>(func));
}

Dict<int, vm::_AssignBuiltin*>* InitAssignmentBuiltins(state::Mem* mem, state::Procs* procs, optview::Exec* exec_opts, ui::ErrorFormatter* errfmt) {
  Dict<int, vm::_AssignBuiltin*>* assign_b = nullptr;
  assign_osh::NewVar* new_var = nullptr;
  StackRoot _root0(&mem);
  StackRoot _root1(&procs);
  StackRoot _root2(&exec_opts);
  StackRoot _root3(&errfmt);
  StackRoot _root4(&assign_b);
  StackRoot _root5(&new_var);

  assign_b = Alloc<Dict<int, vm::_AssignBuiltin*>>();
  new_var = Alloc<assign_osh::NewVar>(mem, procs, exec_opts, errfmt);
  assign_b->set(builtin_i::declare, new_var);
  assign_b->set(builtin_i::typeset, new_var);
  assign_b->set(builtin_i::local, new_var);
  assign_b->set(builtin_i::export_, Alloc<assign_osh::Export>(mem, errfmt));
  assign_b->set(builtin_i::readonly, Alloc<assign_osh::Readonly>(mem, errfmt));
  return assign_b;
}

int Main(BigStr* lang, args::Reader* arg_r, Dict<BigStr*, BigStr*>* environ, bool login_shell, pyutil::_ResourceLoader* loader, py_readline::Readline* readline) {
  BigStr* argv0 = nullptr;
  args::_Attributes* attrs = nullptr;
  arg_types::main* flag = nullptr;
  alloc::Arena* arena = nullptr;
  ui::ErrorFormatter* errfmt = nullptr;
  List<BigStr*>* paths = nullptr;
  int status;
  BigStr* contents = nullptr;
  List<syntax_asdl::debug_frame_t*>* debug_stack = nullptr;
  BigStr* dollar0 = nullptr;
  debug_frame::Main* frame0 = nullptr;
  BigStr* script_name = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* env_dict = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* defaults = nullptr;
  state::Mem* mem = nullptr;
  shell::ShellOptHook* opt_hook = nullptr;
  optview::Parse* parse_opts = nullptr;
  optview::Exec* exec_opts = nullptr;
  state::MutableOpts* mutable_opts = nullptr;
  BigStr* version_str = nullptr;
  Dict<BigStr*, BigStr*>* aliases = nullptr;
  grammar::Grammar* ysh_grammar = nullptr;
  bool do_lossless;
  parse_lib::ParseContext* parse_ctx = nullptr;
  alloc::Arena* comp_arena = nullptr;
  parse_lib::Trail* trail1 = nullptr;
  parse_lib::ParseContext* comp_ctx = nullptr;
  alloc::Arena* hist_arena = nullptr;
  parse_lib::Trail* trail2 = nullptr;
  parse_lib::ParseContext* hist_ctx = nullptr;
  cmd_eval::Deps* cmd_deps = nullptr;
  process::JobControl* job_control = nullptr;
  process::JobList* job_list = nullptr;
  process::FdState* fd_state = nullptr;
  int my_pid;
  BigStr* debug_path = nullptr;
  BigStr* debug_dir = nullptr;
  util::_DebugFile* debug_f = nullptr;
  util::_DebugFile* trace_f = nullptr;
  BigStr* trace_dir = nullptr;
  BigStr* dumps = nullptr;
  BigStr* streams = nullptr;
  dev::MultiTracer* multi_trace = nullptr;
  dev::Tracer* tracer = nullptr;
  iolib::SignalSafe* signal_safe = nullptr;
  trap_osh::TrapState* trap_state = nullptr;
  process::Waiter* waiter = nullptr;
  double now;
  BigStr* iso_stamp = nullptr;
  mylib::BufWriter* argv_buf = nullptr;
  BigStr* interp = nullptr;
  executor::SearchPath* search_path = nullptr;
  process::ExternalProgram* ext_prog = nullptr;
  split::SplitContext* splitter = nullptr;
  glob_::Globber* globber = nullptr;
  BigStr* crash_dump_dir = nullptr;
  completion::Lookup* comp_lookup = nullptr;
  completion::OptionState* compopt_state = nullptr;
  comp_ui::State* comp_ui_state = nullptr;
  comp_ui::PromptState* prompt_state = nullptr;
  word_eval::TildeEvaluator* tilde_ev = nullptr;
  BigStr* home_dir = nullptr;
  sh_init::ShellFiles* sh_files = nullptr;
  state::Procs* procs = nullptr;
  Dict<int, vm::_Builtin*>* builtins = nullptr;
  Dict<int, Dict<BigStr*, vm::_Callable*>*>* methods = nullptr;
  hay_ysh::HayState* hay_state = nullptr;
  executor::ShellExecutor* shell_ex = nullptr;
  sh_expr_eval::ArithEvaluator* arith_ev = nullptr;
  sh_expr_eval::BoolEvaluator* bool_ev = nullptr;
  expr_eval::ExprEvaluator* expr_ev = nullptr;
  word_eval::NormalWordEvaluator* word_ev = nullptr;
  Dict<int, vm::_AssignBuiltin*>* assign_b = nullptr;
  cmd_eval::CommandEvaluator* cmd_ev = nullptr;
  prompt::Evaluator* prompt_ev = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* io_methods = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* io_props = nullptr;
  value_asdl::Obj* io_obj = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* vm_methods = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* vm_props = nullptr;
  value_asdl::Obj* vm_obj = nullptr;
  method_type::Index__* i_func = nullptr;
  Dict<BigStr*, value_asdl::value_t*>* type_m = nullptr;
  value_asdl::Obj* type_obj_methods = nullptr;
  BigStr* type_name = nullptr;
  value_asdl::Obj* type_obj = nullptr;
  int tag;
  Dict<BigStr*, value_asdl::value_t*>* type_props = nullptr;
  sh_expr_eval::UnsafeArith* unsafe_arith = nullptr;
  Dict<int, vm::_Builtin*>* b = nullptr;
  Dict<BigStr*, BigStr*>* help_data = nullptr;
  module_ysh::ModuleInvoke* module_invoke = nullptr;
  meta_oils::ShellFile* source_builtin = nullptr;
  Dict<BigStr*, bool>* guards = nullptr;
  pure_osh::Boolean* true_ = nullptr;
  io_ysh::RunBlock* redir_builtin = nullptr;
  io_osh::MapFile* mapfile = nullptr;
  dirs_osh::DirStack* dir_stack = nullptr;
  completion_osh::SpecBuilder* spec_builder = nullptr;
  completion_osh::Complete* complete_builtin = nullptr;
  word_eval::CompletionWordEvaluator* comp_ev = nullptr;
  completion::RootCompleter* root_comp = nullptr;
  func_hay::ParseHay* parse_hay = nullptr;
  func_hay::EvalHay* eval_hay = nullptr;
  func_hay::HayFunc* hay_func = nullptr;
  func_eggex::MatchFunc* g = nullptr;
  history::Evaluator* hist_ev = nullptr;
  syntax_asdl::source_t* src = nullptr;
  reader::_Reader* line_reader = nullptr;
  mylib::LineReader* stdin_ = nullptr;
  mylib::LineReader* f = nullptr;
  int location_start_line;
  BigStr* config_dir = nullptr;
  List<BigStr*>* rc_paths = nullptr;
  BigStr* rc_path = nullptr;
  BigStr* rc_dir = nullptr;
  main_loop::Headless* loop = nullptr;
  syntax_asdl::IntParamBox* mut_status = nullptr;
  cmd_parse::CommandParser* c_parser = nullptr;
  int term_width;
  comp_ui::_IDisplay* display = nullptr;
  prompt::UserPlugin* prompt_plugin = nullptr;
  BigStr* hist_file = nullptr;
  BigStr* tool_name = nullptr;
  syntax_asdl::command_t* node = nullptr;
  StackRoot _root0(&lang);
  StackRoot _root1(&arg_r);
  StackRoot _root2(&environ);
  StackRoot _root3(&loader);
  StackRoot _root4(&readline);
  StackRoot _root5(&argv0);
  StackRoot _root6(&attrs);
  StackRoot _root7(&flag);
  StackRoot _root8(&arena);
  StackRoot _root9(&errfmt);
  StackRoot _root10(&paths);
  StackRoot _root11(&contents);
  StackRoot _root12(&debug_stack);
  StackRoot _root13(&dollar0);
  StackRoot _root14(&frame0);
  StackRoot _root15(&script_name);
  StackRoot _root16(&env_dict);
  StackRoot _root17(&defaults);
  StackRoot _root18(&mem);
  StackRoot _root19(&opt_hook);
  StackRoot _root20(&parse_opts);
  StackRoot _root21(&exec_opts);
  StackRoot _root22(&mutable_opts);
  StackRoot _root23(&version_str);
  StackRoot _root24(&aliases);
  StackRoot _root25(&ysh_grammar);
  StackRoot _root26(&parse_ctx);
  StackRoot _root27(&comp_arena);
  StackRoot _root28(&trail1);
  StackRoot _root29(&comp_ctx);
  StackRoot _root30(&hist_arena);
  StackRoot _root31(&trail2);
  StackRoot _root32(&hist_ctx);
  StackRoot _root33(&cmd_deps);
  StackRoot _root34(&job_control);
  StackRoot _root35(&job_list);
  StackRoot _root36(&fd_state);
  StackRoot _root37(&debug_path);
  StackRoot _root38(&debug_dir);
  StackRoot _root39(&debug_f);
  StackRoot _root40(&trace_f);
  StackRoot _root41(&trace_dir);
  StackRoot _root42(&dumps);
  StackRoot _root43(&streams);
  StackRoot _root44(&multi_trace);
  StackRoot _root45(&tracer);
  StackRoot _root46(&signal_safe);
  StackRoot _root47(&trap_state);
  StackRoot _root48(&waiter);
  StackRoot _root49(&iso_stamp);
  StackRoot _root50(&argv_buf);
  StackRoot _root51(&interp);
  StackRoot _root52(&search_path);
  StackRoot _root53(&ext_prog);
  StackRoot _root54(&splitter);
  StackRoot _root55(&globber);
  StackRoot _root56(&crash_dump_dir);
  StackRoot _root57(&comp_lookup);
  StackRoot _root58(&compopt_state);
  StackRoot _root59(&comp_ui_state);
  StackRoot _root60(&prompt_state);
  StackRoot _root61(&tilde_ev);
  StackRoot _root62(&home_dir);
  StackRoot _root63(&sh_files);
  StackRoot _root64(&procs);
  StackRoot _root65(&builtins);
  StackRoot _root66(&methods);
  StackRoot _root67(&hay_state);
  StackRoot _root68(&shell_ex);
  StackRoot _root69(&arith_ev);
  StackRoot _root70(&bool_ev);
  StackRoot _root71(&expr_ev);
  StackRoot _root72(&word_ev);
  StackRoot _root73(&assign_b);
  StackRoot _root74(&cmd_ev);
  StackRoot _root75(&prompt_ev);
  StackRoot _root76(&io_methods);
  StackRoot _root77(&io_props);
  StackRoot _root78(&io_obj);
  StackRoot _root79(&vm_methods);
  StackRoot _root80(&vm_props);
  StackRoot _root81(&vm_obj);
  StackRoot _root82(&i_func);
  StackRoot _root83(&type_m);
  StackRoot _root84(&type_obj_methods);
  StackRoot _root85(&type_name);
  StackRoot _root86(&type_obj);
  StackRoot _root87(&type_props);
  StackRoot _root88(&unsafe_arith);
  StackRoot _root89(&b);
  StackRoot _root90(&help_data);
  StackRoot _root91(&module_invoke);
  StackRoot _root92(&source_builtin);
  StackRoot _root93(&guards);
  StackRoot _root94(&true_);
  StackRoot _root95(&redir_builtin);
  StackRoot _root96(&mapfile);
  StackRoot _root97(&dir_stack);
  StackRoot _root98(&spec_builder);
  StackRoot _root99(&complete_builtin);
  StackRoot _root100(&comp_ev);
  StackRoot _root101(&root_comp);
  StackRoot _root102(&parse_hay);
  StackRoot _root103(&eval_hay);
  StackRoot _root104(&hay_func);
  StackRoot _root105(&g);
  StackRoot _root106(&hist_ev);
  StackRoot _root107(&src);
  StackRoot _root108(&line_reader);
  StackRoot _root109(&stdin_);
  StackRoot _root110(&f);
  StackRoot _root111(&config_dir);
  StackRoot _root112(&rc_paths);
  StackRoot _root113(&rc_path);
  StackRoot _root114(&rc_dir);
  StackRoot _root115(&loop);
  StackRoot _root116(&mut_status);
  StackRoot _root117(&c_parser);
  StackRoot _root118(&display);
  StackRoot _root119(&prompt_plugin);
  StackRoot _root120(&hist_file);
  StackRoot _root121(&tool_name);
  StackRoot _root122(&node);

  argv0 = arg_r->Peek();
  arg_r->Next();
  try {
    attrs = flag_util::ParseMore(S_sDc_1, arg_r);
  }
  catch (error::Usage* e) {
    print_stderr(StrFormat("%s usage error: %s", lang, e->msg));
    return 2;
  }
  flag = Alloc<arg_types::main>(attrs->attrs);
  arena = Alloc<alloc::Arena>();
  errfmt = Alloc<ui::ErrorFormatter>();
  if (flag->help) {
    util::HelpFlag(loader, StrFormat("%s-usage", lang), mylib::Stdout());
    return 0;
  }
  if (flag->version) {
    util::VersionFlag(loader, mylib::Stdout());
    return 0;
  }
  if (maybe_str_equals(flag->tool, S_jaz)) {
    paths = arg_r->Rest();
    status = 0;
    for (ListIter<BigStr*> it(paths); !it.Done(); it.Next()) {
      BigStr* p = it.Value();
      StackRoot _for(&p    );
      try {
        contents = loader->Get(p);
        print(contents);
      }
      catch (IOError_OSError*) {
        print_stderr(StrFormat("cat-em: %r not found", p));
        status = 1;
      }
    }
    return status;
  }
  debug_stack = Alloc<List<syntax_asdl::debug_frame_t*>>();
  if (arg_r->AtEnd()) {
    dollar0 = argv0;
  }
  else {
    dollar0 = arg_r->Peek();
    frame0 = Alloc<debug_frame::Main>(dollar0);
    debug_stack->append(frame0);
  }
  script_name = arg_r->Peek();
  arg_r->Next();
  env_dict = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  defaults = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  mem = Alloc<state::Mem>(dollar0, arg_r->Rest(), arena, debug_stack, env_dict, defaults);
  opt_hook = Alloc<ShellOptHook>(readline);
  Tuple3<optview::Parse*, optview::Exec*, state::MutableOpts*> tup0 = state::MakeOpts(mem, environ, opt_hook);
  parse_opts = tup0.at0();
  exec_opts = tup0.at1();
  mutable_opts = tup0.at2();
  mem->exec_opts = exec_opts;
  mutable_opts->Init();
  if (str_equals(lang, S_Awp)) {
    mutable_opts->SetAnyOption(S_CpF, true);
  }
  pure_osh::SetOptionsFromFlags(mutable_opts, attrs->opt_changes, attrs->shopt_changes);
  version_str = pyutil::GetVersion(loader);
  sh_init::InitBuiltins(mem, version_str, defaults);
  sh_init::InitDefaultVars(mem);
  sh_init::CopyVarsFromEnv(exec_opts, environ, mem);
  sh_init::InitVarsAfterEnv(mem);
  if (attrs->show_options) {
    pure_osh::ShowOptions(mutable_opts, Alloc<List<BigStr*>>());
    return 0;
  }
  aliases = Alloc<Dict<BigStr*, BigStr*>>();
  ysh_grammar = pyutil::LoadYshGrammar(loader);
  if ((flag->do_lossless and !exec_opts->noexec())) {
    throw Alloc<error::Usage>(S_Arv, loc::Missing);
  }
  do_lossless = len(flag->tool) ? true : flag->do_lossless;
  parse_ctx = Alloc<parse_lib::ParseContext>(arena, parse_opts, aliases, ysh_grammar, do_lossless);
  comp_arena = Alloc<alloc::Arena>();
  comp_arena->PushSource(Alloc<source::Unused>(S_kgx));
  trail1 = Alloc<parse_lib::Trail>();
  comp_ctx = Alloc<parse_lib::ParseContext>(comp_arena, parse_opts, aliases, ysh_grammar, true);
  comp_ctx->Init_Trail(trail1);
  hist_arena = Alloc<alloc::Arena>();
  hist_arena->PushSource(Alloc<source::Unused>(S_gBD));
  trail2 = Alloc<parse_lib::Trail>();
  hist_ctx = Alloc<parse_lib::ParseContext>(hist_arena, parse_opts, aliases, ysh_grammar);
  hist_ctx->Init_Trail(trail2);
  cmd_deps = Alloc<cmd_eval::Deps>();
  cmd_deps->mutable_opts = mutable_opts;
  job_control = Alloc<process::JobControl>();
  job_list = Alloc<process::JobList>();
  fd_state = Alloc<process::FdState>(errfmt, job_control, job_list, mem, nullptr, nullptr, exec_opts);
  my_pid = posix::getpid();
  debug_path = S_Aoo;
  debug_dir = environ->get(S_aBF);
  if (flag->debug_file != nullptr) {
    debug_path = flag->debug_file;
  }
  else {
    if (debug_dir != nullptr) {
      debug_path = os_path::join(debug_dir, StrFormat("%d-osh.log", my_pid));
    }
  }
  if (len(debug_path)) {
    try {
      debug_f = Alloc<util::DebugFile>(fd_state->OpenForWrite(debug_path));
    }
    catch (IOError_OSError* e) {
      print_stderr(StrFormat("%s: Couldn't open %r: %s", lang, debug_path, posix::strerror(e->errno_)));
      return 2;
    }
  }
  else {
    debug_f = Alloc<util::NullDebugFile>();
  }
  if (flag->xtrace_to_debug_file) {
    trace_f = debug_f;
  }
  else {
    trace_f = Alloc<util::DebugFile>(mylib::Stderr());
  }
  trace_dir = environ->get(S_jam, S_Aoo);
  dumps = environ->get(S_flz, S_Aoo);
  streams = environ->get(S_ayE, S_Aoo);
  multi_trace = Alloc<dev::MultiTracer>(my_pid, trace_dir, dumps, streams, fd_state);
  tracer = Alloc<dev::Tracer>(parse_ctx, exec_opts, mutable_opts, mem, trace_f, multi_trace);
  fd_state->tracer = tracer;
  signal_safe = iolib::InitSignalSafe();
  trap_state = Alloc<trap_osh::TrapState>(signal_safe);
  waiter = Alloc<process::Waiter>(job_list, exec_opts, signal_safe, tracer);
  fd_state->waiter = waiter;
  cmd_deps->debug_f = debug_f;
  now = time_::time();
  iso_stamp = time_::strftime(S_qAp, time_::localtime(now));
  argv_buf = Alloc<mylib::BufWriter>();
  dev::PrintShellArgv(arg_r->argv, argv_buf);
  debug_f->writeln(StrFormat("%s [%d] Oils started with argv %s", iso_stamp, my_pid, argv_buf->getvalue()));
  if (len(debug_path)) {
    debug_f->writeln(StrFormat("Writing logs to %r", debug_path));
  }
  interp = environ->get(S_dDv, S_Aoo);
  search_path = Alloc<executor::SearchPath>(mem, exec_opts);
  ext_prog = Alloc<process::ExternalProgram>(interp, fd_state, errfmt, debug_f);
  splitter = Alloc<split::SplitContext>(mem);
  globber = Alloc<glob_::Globber>(exec_opts);
  crash_dump_dir = environ->get(S_kyd, S_Aoo);
  cmd_deps->dumper = Alloc<dev::CrashDumper>(crash_dump_dir, fd_state);
  comp_lookup = Alloc<completion::Lookup>();
  compopt_state = Alloc<completion::OptionState>();
  comp_ui_state = Alloc<comp_ui::State>();
  prompt_state = Alloc<comp_ui::PromptState>();
  tilde_ev = Alloc<word_eval::TildeEvaluator>(mem, exec_opts);
  home_dir = tilde_ev->GetMyHomeDir();
  if (home_dir == nullptr) {
    print_stderr(StrFormat("%s: Failed to get home dir from $HOME or getpwuid()", lang));
    return 1;
  }
  sh_files = Alloc<sh_init::ShellFiles>(lang, home_dir, mem, flag);
  procs = Alloc<state::Procs>(mem);
  builtins = Alloc<Dict<int, vm::_Builtin*>>();
  methods = Alloc<Dict<int, Dict<BigStr*, vm::_Callable*>*>>();
  hay_state = Alloc<hay_ysh::HayState>();
  shell_ex = Alloc<executor::ShellExecutor>(mem, exec_opts, mutable_opts, procs, hay_state, builtins, search_path, ext_prog, waiter, tracer, job_control, job_list, fd_state, trap_state, errfmt);
  arith_ev = Alloc<sh_expr_eval::ArithEvaluator>(mem, exec_opts, mutable_opts, parse_ctx, errfmt);
  bool_ev = Alloc<sh_expr_eval::BoolEvaluator>(mem, exec_opts, mutable_opts, parse_ctx, errfmt);
  expr_ev = Alloc<expr_eval::ExprEvaluator>(mem, mutable_opts, methods, splitter, errfmt);
  word_ev = Alloc<word_eval::NormalWordEvaluator>(mem, exec_opts, mutable_opts, tilde_ev, splitter, errfmt);
  assign_b = InitAssignmentBuiltins(mem, procs, exec_opts, errfmt);
  cmd_ev = Alloc<cmd_eval::CommandEvaluator>(mem, exec_opts, errfmt, procs, assign_b, arena, cmd_deps, trap_state, signal_safe);
  prompt_ev = Alloc<prompt::Evaluator>(lang, version_str, parse_ctx, mem);
  io_methods = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  io_methods->set(S_bEz, Alloc<value::BuiltinFunc>(Alloc<method_io::PromptVal>(prompt_ev)));
  io_methods->set(S_naE_1, Alloc<value::BuiltinFunc>(Alloc<method_io::Eval>(mem, cmd_ev, method_io::EVAL_NULL)));
  io_methods->set(S_ohv, Alloc<value::BuiltinFunc>(Alloc<method_io::Eval>(mem, cmd_ev, method_io::EVAL_DICT)));
  io_methods->set(S_usa, Alloc<value::BuiltinFunc>(Alloc<method_io::EvalInFrame>(mem, cmd_ev)));
  io_methods->set(S_hqr, Alloc<value::BuiltinFunc>(Alloc<method_io::EvalExpr>(expr_ev)));
  io_methods->set(S_gDo, Alloc<value::BuiltinFunc>(Alloc<method_io::CaptureStdout>(mem, shell_ex)));
  io_methods->set(S_rtt_1, Alloc<value::BuiltinFunc>(Alloc<method_io::Time>()));
  io_methods->set(S_Eoc, Alloc<value::BuiltinFunc>(Alloc<method_io::Strftime>()));
  io_methods->set(S_fvi, Alloc<value::BuiltinFunc>(Alloc<method_io::Glob>()));
  io_props = Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_EoC}, std::initializer_list<value_asdl::value_t*>{value::Stdin});
  io_obj = Alloc<Obj>(Alloc<Obj>(nullptr, io_methods), io_props);
  vm_methods = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  vm_methods->set(S_wvg, Alloc<value::BuiltinFunc>(Alloc<func_reflect::GetFrame>(mem)));
  vm_methods->set(S_huA, Alloc<value::BuiltinFunc>(Alloc<func_reflect::Id>()));
  vm_props = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  vm_obj = Alloc<Obj>(Alloc<Obj>(nullptr, vm_methods), vm_props);
  i_func = Alloc<method_type::Index__>();
  type_m = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  type_m->set(S_opF, Alloc<value::BuiltinFunc>(i_func));
  type_obj_methods = Alloc<Obj>(nullptr, type_m);
  for (ListIter<int> it(NewList<int>(std::initializer_list<int>{value_e::Bool, value_e::Int, value_e::Float, value_e::Str, value_e::List, value_e::Dict})); !it.Done(); it.Next()) {
    int tag = it.Value();
    type_name = value_str(tag, false);
    type_obj = Alloc<Obj>(type_obj_methods, Alloc<Dict<BigStr*, value_asdl::value_t*>>(std::initializer_list<BigStr*>{S_klA}, std::initializer_list<value_asdl::value_t*>{Alloc<value::Str>(type_name)}));
    mem->AddBuiltin(type_name, type_obj);
  }
  tag = value_e::Obj;
  type_name = value_str(tag, false);
  type_props = Alloc<Dict<BigStr*, value_asdl::value_t*>>();
  type_props->set(S_klA, Alloc<value::Str>(type_name));
  type_props->set(S_cwj, Alloc<value::BuiltinFunc>(Alloc<func_misc::Obj_call>()));
  type_obj = Alloc<Obj>(type_obj_methods, type_props);
  mem->AddBuiltin(type_name, type_obj);
  vm::InitCircularDeps(arith_ev, bool_ev, expr_ev, word_ev, cmd_ev, shell_ex, prompt_ev, io_obj, tracer);
  unsafe_arith = Alloc<sh_expr_eval::UnsafeArith>(mem, exec_opts, mutable_opts, parse_ctx, arith_ev, errfmt);
  vm::InitUnsafeArith(mem, word_ev, unsafe_arith);
  b = builtins;
  // if not PYTHON
  {
    help_data = help_meta::TopicMetadata();
  }
  // endif MYCPP
  b->set(builtin_i::help, Alloc<misc_osh::Help>(lang, loader, help_data, errfmt));
  b->set(builtin_i::set, Alloc<pure_osh::Set>(mutable_opts, mem));
  b->set(builtin_i::shopt, Alloc<pure_osh::Shopt>(exec_opts, mutable_opts, cmd_ev, mem, environ));
  b->set(builtin_i::hash, Alloc<pure_osh::Hash>(search_path));
  b->set(builtin_i::trap, Alloc<trap_osh::Trap>(trap_state, parse_ctx, tracer, errfmt));
  b->set(builtin_i::shvar, Alloc<pure_ysh::Shvar>(mem, search_path, cmd_ev));
  b->set(builtin_i::ctx, Alloc<pure_ysh::Ctx>(mem, cmd_ev));
  b->set(builtin_i::push_registers, Alloc<pure_ysh::PushRegisters>(mem, cmd_ev));
  b->set(builtin_i::hay, Alloc<hay_ysh::Hay>(hay_state, mutable_opts, mem, cmd_ev));
  b->set(builtin_i::haynode, Alloc<hay_ysh::HayNode_>(hay_state, mem, cmd_ev));
  b->set(builtin_i::type, Alloc<meta_oils::Type>(procs, aliases, search_path, errfmt));
  b->set(builtin_i::builtin, Alloc<meta_oils::Builtin>(shell_ex, errfmt));
  b->set(builtin_i::command, Alloc<meta_oils::Command>(shell_ex, procs, aliases, search_path));
  b->set(builtin_i::runproc, Alloc<meta_oils::RunProc>(shell_ex, procs, errfmt));
  b->set(builtin_i::invoke, Alloc<meta_oils::Invoke>(shell_ex, procs, errfmt));
  b->set(builtin_i::extern_, Alloc<meta_oils::Extern>(shell_ex, procs, errfmt));
  module_invoke = Alloc<module_ysh::ModuleInvoke>(cmd_ev, tracer, errfmt);
  b->set(builtin_i::use, Alloc<meta_oils::ShellFile>(parse_ctx, search_path, cmd_ev, fd_state, tracer, errfmt, loader, module_invoke));
  source_builtin = Alloc<meta_oils::ShellFile>(parse_ctx, search_path, cmd_ev, fd_state, tracer, errfmt, loader);
  b->set(builtin_i::source, source_builtin);
  b->set(builtin_i::dot, source_builtin);
  b->set(builtin_i::eval, Alloc<meta_oils::Eval>(parse_ctx, exec_opts, cmd_ev, tracer, errfmt, mem));
  guards = Alloc<Dict<BigStr*, bool>>();
  b->set(builtin_i::source_guard, Alloc<module_ysh::SourceGuard>(guards, exec_opts, errfmt));
  b->set(builtin_i::is_main, Alloc<module_ysh::IsMain>(mem));
  b->set(builtin_i::error, Alloc<error_ysh::Error>());
  b->set(builtin_i::failed, Alloc<error_ysh::Failed>(mem));
  b->set(builtin_i::boolstatus, Alloc<error_ysh::BoolStatus>(shell_ex, errfmt));
  b->set(builtin_i::try_, Alloc<error_ysh::Try>(mutable_opts, mem, cmd_ev, shell_ex, errfmt));
  b->set(builtin_i::assert_, Alloc<error_ysh::Assert>(expr_ev, errfmt));
  true_ = Alloc<pure_osh::Boolean>(0);
  b->set(builtin_i::colon, true_);
  b->set(builtin_i::true_, true_);
  b->set(builtin_i::false_, Alloc<pure_osh::Boolean>(1));
  b->set(builtin_i::alias, Alloc<pure_osh::Alias>(aliases, errfmt));
  b->set(builtin_i::unalias, Alloc<pure_osh::UnAlias>(aliases, errfmt));
  b->set(builtin_i::getopts, Alloc<pure_osh::GetOpts>(mem, errfmt));
  b->set(builtin_i::shift, Alloc<assign_osh::Shift>(mem));
  b->set(builtin_i::unset, Alloc<assign_osh::Unset>(mem, procs, unsafe_arith, errfmt));
  b->set(builtin_i::append, Alloc<pure_ysh::Append>(mem, errfmt));
  b->set(builtin_i::test, Alloc<bracket_osh::Test>(false, exec_opts, mem, errfmt));
  b->set(builtin_i::bracket, Alloc<bracket_osh::Test>(true, exec_opts, mem, errfmt));
  b->set(builtin_i::echo, Alloc<io_osh::Echo>(exec_opts));
  b->set(builtin_i::printf, Alloc<printf_osh::Printf>(mem, parse_ctx, unsafe_arith, errfmt));
  b->set(builtin_i::write, Alloc<io_ysh::Write>(mem, errfmt));
  redir_builtin = Alloc<io_ysh::RunBlock>(mem, cmd_ev);
  b->set(builtin_i::redir, redir_builtin);
  b->set(builtin_i::fopen, redir_builtin);
  b->set(builtin_i::pp, Alloc<io_ysh::Pp>(expr_ev, mem, errfmt, procs, arena));
  b->set(builtin_i::cat, Alloc<io_osh::Cat>());
  b->set(builtin_i::read, Alloc<read_osh::Read>(splitter, mem, parse_ctx, cmd_ev, errfmt));
  mapfile = Alloc<io_osh::MapFile>(mem, errfmt, cmd_ev);
  b->set(builtin_i::mapfile, mapfile);
  b->set(builtin_i::readarray, mapfile);
  dir_stack = Alloc<dirs_osh::DirStack>();
  b->set(builtin_i::cd, Alloc<dirs_osh::Cd>(mem, dir_stack, cmd_ev, errfmt));
  b->set(builtin_i::pushd, Alloc<dirs_osh::Pushd>(mem, dir_stack, errfmt));
  b->set(builtin_i::popd, Alloc<dirs_osh::Popd>(mem, dir_stack, errfmt));
  b->set(builtin_i::dirs, Alloc<dirs_osh::Dirs>(mem, dir_stack, errfmt));
  b->set(builtin_i::pwd, Alloc<dirs_osh::Pwd>(mem, errfmt));
  b->set(builtin_i::times, Alloc<misc_osh::Times>());
  b->set(builtin_i::json, Alloc<json_ysh::Json>(mem, errfmt, false));
  b->set(builtin_i::json8, Alloc<json_ysh::Json>(mem, errfmt, true));
  b->set(builtin_i::exec_, Alloc<process_osh::Exec>(mem, ext_prog, fd_state, search_path, errfmt));
  b->set(builtin_i::umask, Alloc<process_osh::Umask>());
  b->set(builtin_i::ulimit, Alloc<process_osh::Ulimit>());
  b->set(builtin_i::wait, Alloc<process_osh::Wait>(waiter, job_list, mem, tracer, errfmt));
  b->set(builtin_i::jobs, Alloc<process_osh::Jobs>(job_list));
  b->set(builtin_i::fg, Alloc<process_osh::Fg>(job_control, job_list, waiter));
  b->set(builtin_i::bg, Alloc<process_osh::Bg>(job_list));
  b->set(builtin_i::fork, Alloc<process_osh::Fork>(shell_ex));
  b->set(builtin_i::forkwait, Alloc<process_osh::ForkWait>(shell_ex));
  b->set(builtin_i::bind, Alloc<readline_osh::Bind>(readline, errfmt));
  b->set(builtin_i::history, Alloc<readline_osh::History>(readline, sh_files, errfmt, mylib::Stdout()));
  spec_builder = Alloc<completion_osh::SpecBuilder>(cmd_ev, parse_ctx, word_ev, splitter, comp_lookup, help_data, errfmt);
  complete_builtin = Alloc<completion_osh::Complete>(spec_builder, comp_lookup);
  b->set(builtin_i::complete, complete_builtin);
  b->set(builtin_i::compgen, Alloc<completion_osh::CompGen>(spec_builder));
  b->set(builtin_i::compopt, Alloc<completion_osh::CompOpt>(compopt_state, errfmt));
  b->set(builtin_i::compadjust, Alloc<completion_osh::CompAdjust>(mem));
  comp_ev = Alloc<word_eval::CompletionWordEvaluator>(mem, exec_opts, mutable_opts, tilde_ev, splitter, errfmt);
  comp_ev->arith_ev = arith_ev;
  comp_ev->expr_ev = expr_ev;
  comp_ev->prompt_ev = prompt_ev;
  comp_ev->CheckCircularDeps();
  root_comp = Alloc<completion::RootCompleter>(comp_ev, mem, comp_lookup, compopt_state, comp_ui_state, comp_ctx, debug_f);
  b->set(builtin_i::compexport, Alloc<completion_ysh::CompExport>(root_comp));
  methods->set(value_e::Str, Alloc<Dict<BigStr*, vm::_Callable*>>(std::initializer_list<BigStr*>{S_lfz, S_vdA, S_Adn, S_Cjp, S_EsB, S_fgo, S_urB, S_umv, S_Egw, S_Cbl, S_vjw, S_omo, S_fig}, std::initializer_list<vm::_Callable*>{Alloc<method_str::HasAffix>(method_str::START), Alloc<method_str::HasAffix>(method_str::END), Alloc<method_str::Trim>((method_str::START | method_str::END)), Alloc<method_str::Trim>(method_str::START), Alloc<method_str::Trim>(method_str::END), Alloc<method_str::Upper>(), Alloc<method_str::Lower>(), Alloc<method_str::Split>(), nullptr, Alloc<method_str::Replace>(mem, expr_ev), Alloc<method_str::SearchMatch>(method_str::SEARCH), Alloc<method_str::SearchMatch>(method_str::LEFT_MATCH), nullptr}));
  methods->set(value_e::Dict, Alloc<Dict<BigStr*, vm::_Callable*>>(std::initializer_list<BigStr*>{S_bFs, S_hFC, S_gzE, S_ylo, S_zcr, S_eyc}, std::initializer_list<vm::_Callable*>{Alloc<method_dict::Erase>(), nullptr, nullptr, Alloc<method_dict::Get>(), Alloc<method_dict::Keys>(), Alloc<method_dict::Values>()}));
  methods->set(value_e::List, Alloc<Dict<BigStr*, vm::_Callable*>>(std::initializer_list<BigStr*>{S_zvj, S_ywz, S_trz, S_qCf, S_yhj, S_yEv, S_zkE, S_dnv, S_zfa, S_eov}, std::initializer_list<vm::_Callable*>{Alloc<method_list::Reverse>(), Alloc<method_list::Append>(), Alloc<method_list::Clear>(), Alloc<method_list::Extend>(), Alloc<method_list::Pop>(), nullptr, nullptr, Alloc<method_list::IndexOf>(), Alloc<method_list::LastIndexOf>(), Alloc<func_misc::Join>()}));
  methods->set(value_e::Match, Alloc<Dict<BigStr*, vm::_Callable*>>(std::initializer_list<BigStr*>{S_elk, S_lra_1, S_Ate}, std::initializer_list<vm::_Callable*>{Alloc<func_eggex::MatchMethod>(func_eggex::G, expr_ev), Alloc<func_eggex::MatchMethod>(func_eggex::S, nullptr), Alloc<func_eggex::MatchMethod>(func_eggex::E, nullptr)}));
  methods->set(value_e::Place, Alloc<Dict<BigStr*, vm::_Callable*>>(std::initializer_list<BigStr*>{S_CEu}, std::initializer_list<vm::_Callable*>{Alloc<method_other::SetValue>(mem)}));
  methods->set(value_e::CommandFrag, Alloc<Dict<BigStr*, vm::_Callable*>>(std::initializer_list<BigStr*>{S_rkC}, std::initializer_list<vm::_Callable*>{nullptr}));
  parse_hay = Alloc<func_hay::ParseHay>(fd_state, parse_ctx, mem, errfmt);
  eval_hay = Alloc<func_hay::EvalHay>(hay_state, mutable_opts, mem, cmd_ev);
  hay_func = Alloc<func_hay::HayFunc>(hay_state);
  _AddBuiltinFunc(mem, S_tEt, parse_hay);
  _AddBuiltinFunc(mem, S_mul, eval_hay);
  _AddBuiltinFunc(mem, S_jcn, hay_func);
  _AddBuiltinFunc(mem, S_fDC, Alloc<func_misc::Len>());
  _AddBuiltinFunc(mem, S_qEi, Alloc<func_misc::Type>());
  g = Alloc<func_eggex::MatchFunc>(func_eggex::G, expr_ev, mem);
  _AddBuiltinFunc(mem, S_dfx, g);
  _AddBuiltinFunc(mem, S_asc, g);
  _AddBuiltinFunc(mem, S_Dvd, Alloc<func_eggex::MatchFunc>(func_eggex::S, nullptr, mem));
  _AddBuiltinFunc(mem, S_iii, Alloc<func_eggex::MatchFunc>(func_eggex::E, nullptr, mem));
  _AddBuiltinFunc(mem, S_AdB, Alloc<func_reflect::ParseCommand>(parse_ctx, mem, errfmt));
  _AddBuiltinFunc(mem, S_upi, Alloc<func_reflect::ParseExpr>(parse_ctx, errfmt));
  _AddBuiltinFunc(mem, S_Cko, Alloc<func_reflect::Shvar_get>(mem));
  _AddBuiltinFunc(mem, S_Aeu, Alloc<func_reflect::GetVar>(mem));
  _AddBuiltinFunc(mem, S_riF, Alloc<func_reflect::SetVar>(mem));
  _AddBuiltinFunc(mem, S_ksw, Alloc<func_reflect::BindFrame>());
  _AddBuiltinFunc(mem, S_iza, Alloc<func_misc::Object>());
  _AddBuiltinFunc(mem, S_hjl, Alloc<func_misc::Prototype>());
  _AddBuiltinFunc(mem, S_apg, Alloc<func_misc::PropView>());
  _AddBuiltinFunc(mem, S_Clj, Alloc<func_misc::Prototype>());
  _AddBuiltinFunc(mem, S_zob, Alloc<func_misc::PropView>());
  _AddBuiltinFunc(mem, S_qko, Alloc<func_misc::Bool>());
  _AddBuiltinFunc(mem, S_gcE, Alloc<func_misc::Int>());
  _AddBuiltinFunc(mem, S_itx, Alloc<func_misc::Float>());
  _AddBuiltinFunc(mem, S_urq, Alloc<func_misc::Str_>());
  _AddBuiltinFunc(mem, S_yrn, Alloc<func_misc::List_>());
  _AddBuiltinFunc(mem, S_wlA, Alloc<func_misc::DictFunc>());
  _AddBuiltinFunc(mem, S_ylo, Alloc<method_dict::Get>());
  _AddBuiltinFunc(mem, S_zcr, Alloc<method_dict::Keys>());
  _AddBuiltinFunc(mem, S_eyc, Alloc<method_dict::Values>());
  _AddBuiltinFunc(mem, S_ubi, Alloc<func_misc::Runes>());
  _AddBuiltinFunc(mem, S_cCw, Alloc<func_misc::EncodeRunes>());
  _AddBuiltinFunc(mem, S_AoD, Alloc<func_misc::Bytes>());
  _AddBuiltinFunc(mem, S_kvt, Alloc<func_misc::EncodeBytes>());
  _AddBuiltinFunc(mem, S_umv, Alloc<func_misc::Split>(splitter));
  _AddBuiltinFunc(mem, S_jvC, Alloc<func_misc::Split>(splitter));
  _AddBuiltinFunc(mem, S_FCw, Alloc<func_misc::FloatsEqual>());
  _AddBuiltinFunc(mem, S_eov, Alloc<func_misc::Join>());
  _AddBuiltinFunc(mem, S_rpn, Alloc<func_misc::Maybe>());
  _AddBuiltinFunc(mem, S_fvi, Alloc<func_misc::Glob>(globber));
  _AddBuiltinFunc(mem, S_qwA, Alloc<func_misc::ToJson8>(true));
  _AddBuiltinFunc(mem, S_Bsg, Alloc<func_misc::ToJson8>(false));
  _AddBuiltinFunc(mem, S_qqd, Alloc<func_misc::FromJson8>(true));
  _AddBuiltinFunc(mem, S_fnB, Alloc<func_misc::FromJson8>(false));
  _AddBuiltinFunc(mem, S_oCh, Alloc<func_misc::BashArrayToSparse>());
  _AddBuiltinFunc(mem, S_rtn, Alloc<func_misc::SparseOp>());
  mem->AddBuiltin(S_Akj, io_obj);
  mem->AddBuiltin(S_kaF, vm_obj);
  mem->AddBuiltin(S_dba, Alloc<value::BuiltinProc>(module_invoke));
  hist_ev = Alloc<history::Evaluator>(readline, hist_ctx, debug_f);
  if (flag->c != nullptr) {
    src = source::CFlag;
    line_reader = reader::StringLineReader(flag->c, arena);
    if (flag->i) {
      mutable_opts->set_interactive();
    }
  }
  else {
    if (flag->i) {
      src = Alloc<source::Stdin>(S_Fem);
      line_reader = Alloc<reader::InteractiveLineReader>(arena, prompt_ev, hist_ev, readline, prompt_state);
      mutable_opts->set_interactive();
    }
    else {
      if (script_name == nullptr) {
        if (flag->headless) {
          src = source::Headless;
          line_reader = nullptr;
        }
        else {
          stdin_ = mylib::Stdin();
          if ((len(flag->tool) == 0 and stdin_->isatty())) {
            src = source::Interactive;
            line_reader = Alloc<reader::InteractiveLineReader>(arena, prompt_ev, hist_ev, readline, prompt_state);
            mutable_opts->set_interactive();
          }
          else {
            src = Alloc<source::Stdin>(S_Aoo);
            line_reader = Alloc<reader::FileLineReader>(stdin_, arena);
          }
        }
      }
      else {
        src = Alloc<source::MainFile>(script_name);
        try {
          f = fd_state->Open(script_name);
        }
        catch (IOError_OSError* e) {
          print_stderr(StrFormat("%s: Couldn't open %r: %s", lang, script_name, posix::strerror(e->errno_)));
          return 1;
        }
        line_reader = Alloc<reader::FileLineReader>(f, arena);
      }
    }
  }
  if (flag->location_str != nullptr) {
    src = Alloc<source::Synthetic>(flag->location_str);
    location_start_line = mops::BigTruncate(flag->location_start_line);
    if (location_start_line != -1) {
      line_reader->SetLineOffset(location_start_line);
    }
  }
  arena->PushSource(src);
  config_dir = S_rCp;
  rc_paths = Alloc<List<BigStr*>>();
  if ((flag->headless or exec_opts->interactive())) {
    if (flag->norc) {
      if (flag->rcfile != nullptr) {
        print_stderr(StrFormat("%s warning: --rcfile ignored with --norc", lang));
      }
      if (flag->rcdir != nullptr) {
        print_stderr(StrFormat("%s warning: --rcdir ignored with --norc", lang));
      }
    }
    else {
      rc_path = flag->rcfile;
      if (rc_path == nullptr) {
        rc_paths->append(os_path::join(home_dir, StrFormat("%s/%src", config_dir, lang)));
      }
      else {
        rc_paths->append(rc_path);
      }
      rc_dir = flag->rcdir;
      if (rc_dir == nullptr) {
        rc_dir = os_path::join(home_dir, StrFormat("%s/%src.d", config_dir, lang));
      }
      rc_paths->extend(libc::glob(os_path::join(rc_dir, S_Fgw), 0));
    }
  }
  _InitDefaultCompletions(cmd_ev, complete_builtin, comp_lookup);
  if (flag->headless) {
    sh_init::InitInteractive(mem, sh_files, lang);
    mutable_opts->set_redefine_const();
    mutable_opts->set_redefine_source();
    for (ListIter<BigStr*> it(rc_paths); !it.Done(); it.Next()) {
      BigStr* rc_path = it.Value();
      StackRoot _for(&rc_path    );
      {  // with
        state::ctx_ThisDir ctx{mem, rc_path};

        try {
          SourceStartupFile(fd_state, rc_path, lang, parse_ctx, cmd_ev, errfmt);
        }
        catch (util::UserExit* e) {
          return e->status;
        }
      }
    }
    loop = Alloc<main_loop::Headless>(cmd_ev, parse_ctx, errfmt);
    try {
      status = loop->Loop();
    }
    catch (util::UserExit* e) {
      status = e->status;
    }
    mut_status = Alloc<IntParamBox>(status);
    cmd_ev->RunTrapsOnExit(mut_status);
    status = mut_status->i;
    return status;
  }
  c_parser = parse_ctx->MakeOshParser(line_reader);
  if (exec_opts->interactive()) {
    sh_init::InitInteractive(mem, sh_files, lang);
    mutable_opts->set_emacs();
    mutable_opts->set_redefine_const();
    mutable_opts->set_redefine_source();
    if (readline) {
      term_width = 0;
      if (maybe_str_equals(flag->completion_display, S_zkk)) {
        try {
          term_width = libc::get_terminal_width();
        }
        catch (IOError_OSError*) {
          ;  // pass
        }
      }
      if (term_width != 0) {
        display = Alloc<comp_ui::NiceDisplay>(term_width, comp_ui_state, prompt_state, debug_f, readline, signal_safe);
      }
      else {
        display = Alloc<comp_ui::MinimalDisplay>(comp_ui_state, prompt_state, debug_f);
      }
      comp_ui::InitReadline(readline, sh_files->HistoryFile(), root_comp, display, debug_f);
      if (flag->completion_demo) {
        _CompletionDemo(comp_lookup);
      }
    }
    else {
      display = Alloc<comp_ui::MinimalDisplay>(comp_ui_state, prompt_state, debug_f);
    }
    process::InitInteractiveShell(signal_safe);
    {  // with
      process::ctx_TerminalControl ctx{job_control, errfmt};

      for (ListIter<BigStr*> it(rc_paths); !it.Done(); it.Next()) {
        BigStr* rc_path = it.Value();
        StackRoot _for(&rc_path      );
        {  // with
          state::ctx_ThisDir ctx{mem, rc_path};

          try {
            SourceStartupFile(fd_state, rc_path, lang, parse_ctx, cmd_ev, errfmt);
          }
          catch (util::UserExit* e) {
            return e->status;
          }
        }
      }
      line_reader->Reset();
      prompt_plugin = Alloc<prompt::UserPlugin>(mem, parse_ctx, cmd_ev, errfmt);
      try {
        status = main_loop::Interactive(flag, cmd_ev, c_parser, display, prompt_plugin, waiter, errfmt);
      }
      catch (util::UserExit* e) {
        status = e->status;
      }
      mut_status = Alloc<IntParamBox>(status);
      cmd_ev->RunTrapsOnExit(mut_status);
      status = mut_status->i;
    }
    if (readline) {
      hist_file = sh_files->HistoryFile();
      if (hist_file != nullptr) {
        try {
          readline->write_history_file(hist_file);
        }
        catch (IOError_OSError*) {
          ;  // pass
        }
      }
    }
    return status;
  }
  if (flag->rcfile != nullptr) {
    print_stderr(StrFormat("%s warning: --rcfile ignored in non-interactive shell", lang));
  }
  if (flag->rcdir != nullptr) {
    print_stderr(StrFormat("%s warning: --rcdir ignored in non-interactive shell", lang));
  }
  tool_name = exec_opts->noexec() ? S_xcB : flag->tool;
  if (len(tool_name)) {
    if (!(maybe_str_equals(tool_name, S_xcB))) {
      arena->SaveTokens();
    }
    try {
      node = main_loop::ParseWholeFile(c_parser);
    }
    catch (error::Parse* e) {
      errfmt->PrettyPrintError(e);
      return 2;
    }
    if (maybe_str_equals(tool_name, S_xcB)) {
      ui::PrintAst(node, flag);
    }
    else {
      if (maybe_str_equals(tool_name, S_tje)) {
        ysh_ify::PrintTokens(arena);
      }
      else {
        if (maybe_str_equals(tool_name, S_brz)) {
          ysh_ify::LosslessCat(arena);
        }
        else {
          if (maybe_str_equals(tool_name, S_hex)) {
            fmt::Format(arena, node);
          }
          else {
            if (maybe_str_equals(tool_name, S_jvs)) {
              assert(0);  // AssertionError
            }
            else {
              if (maybe_str_equals(tool_name, S_Cha)) {
                ysh_ify::Ysh_ify(arena, node);
              }
              else {
                if (maybe_str_equals(tool_name, S_Fuj)) {
                }
                else {
                  assert(0);  // AssertionError
                }
              }
            }
          }
        }
      }
    }
    return 0;
  }
  {  // with
    state::ctx_ThisDir ctx{mem, script_name};

    try {
      status = main_loop::Batch(cmd_ev, c_parser, errfmt, cmd_eval::IsMainProgram);
    }
    catch (util::UserExit* e) {
      status = e->status;
    }
    catch (KeyboardInterrupt*) {
      status = 130;
    }
  }
  mut_status = Alloc<IntParamBox>(status);
  cmd_ev->RunTrapsOnExit(mut_status);
  multi_trace->WriteDumps();
  return mut_status->i;
}

}  // define namespace shell

int main(int argc, char **argv) {
  mylib::InitCppOnly();  // Initializes gHeap

  auto* args = Alloc<List<BigStr*>>();
  for (int i = 0; i < argc; ++i) {
    args->append(StrFromC(argv[i]));
  }

  int status = oils_for_unix::main(args);

  gHeap.ProcessExit();

  return status;
}