bundle.react.js 1.1 MB

12
  1. !function e(n,t,o){function a(i,s){if(!t[i]){if(!n[i]){var l="function"==typeof require&&require;if(!s&&l)return l(i,!0);if(r)return r(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var u=t[i]={exports:{}};n[i][0].call(u.exports,function(e){var t=n[i][1][e];return a(t||e)},u,u.exports,e,n,t,o)}return t[i].exports}for(var r="function"==typeof require&&require,i=0;i<o.length;i++)a(o[i]);return a}({1:[function(e,n,t){"use strict";n.exports=function(e){if(e)throw e}},{}],2:[function(e,n,t){n.exports={AElig:"Æ",AMP:"&",Aacute:"Á",Acirc:"Â",Agrave:"À",Aring:"Å",Atilde:"Ã",Auml:"Ä",COPY:"©",Ccedil:"Ç",ETH:"Ð",Eacute:"É",Ecirc:"Ê",Egrave:"È",Euml:"Ë",GT:">",Iacute:"Í",Icirc:"Î",Igrave:"Ì",Iuml:"Ï",LT:"<",Ntilde:"Ñ",Oacute:"Ó",Ocirc:"Ô",Ograve:"Ò",Oslash:"Ø",Otilde:"Õ",Ouml:"Ö",QUOT:'"',REG:"®",THORN:"Þ",Uacute:"Ú",Ucirc:"Û",Ugrave:"Ù",Uuml:"Ü",Yacute:"Ý",aacute:"á",acirc:"â",acute:"´",aelig:"æ",agrave:"à",amp:"&",aring:"å",atilde:"ã",auml:"ä",brvbar:"¦",ccedil:"ç",cedil:"¸",cent:"¢",copy:"©",curren:"¤",deg:"°",divide:"÷",eacute:"é",ecirc:"ê",egrave:"è",eth:"ð",euml:"ë",frac12:"½",frac14:"¼",frac34:"¾",gt:">",iacute:"í",icirc:"î",iexcl:"¡",igrave:"ì",iquest:"¿",iuml:"ï",laquo:"«",lt:"<",macr:"¯",micro:"µ",middot:"·",nbsp:" ",not:"¬",ntilde:"ñ",oacute:"ó",ocirc:"ô",ograve:"ò",ordf:"ª",ordm:"º",oslash:"ø",otilde:"õ",ouml:"ö",para:"¶",plusmn:"±",pound:"£",quot:'"',raquo:"»",reg:"®",sect:"§",shy:"­",sup1:"¹",sup2:"²",sup3:"³",szlig:"ß",thorn:"þ",times:"×",uacute:"ú",ucirc:"û",ugrave:"ù",uml:"¨",uuml:"ü",yacute:"ý",yen:"¥",yuml:"ÿ"}},{}],3:[function(e,n,t){n.exports={AEli:"Æ",AElig:"Æ",AM:"&",AMP:"&",Aacut:"Á",Aacute:"Á",Abreve:"Ă",Acir:"Â",Acirc:"Â",Acy:"А",Afr:"𝔄",Agrav:"À",Agrave:"À",Alpha:"Α",Amacr:"Ā",And:"⩓",Aogon:"Ą",Aopf:"𝔸",ApplyFunction:"⁡",Arin:"Å",Aring:"Å",Ascr:"𝒜",Assign:"≔",Atild:"Ã",Atilde:"Ã",Aum:"Ä",Auml:"Ä",Backslash:"∖",Barv:"⫧",Barwed:"⌆",Bcy:"Б",Because:"∵",Bernoullis:"ℬ",Beta:"Β",Bfr:"𝔅",Bopf:"𝔹",Breve:"˘",Bscr:"ℬ",Bumpeq:"≎",CHcy:"Ч",COP:"©",COPY:"©",Cacute:"Ć",Cap:"⋒",CapitalDifferentialD:"ⅅ",Cayleys:"ℭ",Ccaron:"Č",Ccedi:"Ç",Ccedil:"Ç",Ccirc:"Ĉ",Cconint:"∰",Cdot:"Ċ",Cedilla:"¸",CenterDot:"·",Cfr:"ℭ",Chi:"Χ",CircleDot:"⊙",CircleMinus:"⊖",CirclePlus:"⊕",CircleTimes:"⊗",ClockwiseContourIntegral:"∲",CloseCurlyDoubleQuote:"”",CloseCurlyQuote:"’",Colon:"∷",Colone:"⩴",Congruent:"≡",Conint:"∯",ContourIntegral:"∮",Copf:"ℂ",Coproduct:"∐",CounterClockwiseContourIntegral:"∳",Cross:"⨯",Cscr:"𝒞",Cup:"⋓",CupCap:"≍",DD:"ⅅ",DDotrahd:"⤑",DJcy:"Ђ",DScy:"Ѕ",DZcy:"Џ",Dagger:"‡",Darr:"↡",Dashv:"⫤",Dcaron:"Ď",Dcy:"Д",Del:"∇",Delta:"Δ",Dfr:"𝔇",DiacriticalAcute:"´",DiacriticalDot:"˙",DiacriticalDoubleAcute:"˝",DiacriticalGrave:"`",DiacriticalTilde:"˜",Diamond:"⋄",DifferentialD:"ⅆ",Dopf:"𝔻",Dot:"¨",DotDot:"⃜",DotEqual:"≐",DoubleContourIntegral:"∯",DoubleDot:"¨",DoubleDownArrow:"⇓",DoubleLeftArrow:"⇐",DoubleLeftRightArrow:"⇔",DoubleLeftTee:"⫤",DoubleLongLeftArrow:"⟸",DoubleLongLeftRightArrow:"⟺",DoubleLongRightArrow:"⟹",DoubleRightArrow:"⇒",DoubleRightTee:"⊨",DoubleUpArrow:"⇑",DoubleUpDownArrow:"⇕",DoubleVerticalBar:"∥",DownArrow:"↓",DownArrowBar:"⤓",DownArrowUpArrow:"⇵",DownBreve:"̑",DownLeftRightVector:"⥐",DownLeftTeeVector:"⥞",DownLeftVector:"↽",DownLeftVectorBar:"⥖",DownRightTeeVector:"⥟",DownRightVector:"⇁",DownRightVectorBar:"⥗",DownTee:"⊤",DownTeeArrow:"↧",Downarrow:"⇓",Dscr:"𝒟",Dstrok:"Đ",ENG:"Ŋ",ET:"Ð",ETH:"Ð",Eacut:"É",Eacute:"É",Ecaron:"Ě",Ecir:"Ê",Ecirc:"Ê",Ecy:"Э",Edot:"Ė",Efr:"𝔈",Egrav:"È",Egrave:"È",Element:"∈",Emacr:"Ē",EmptySmallSquare:"◻",EmptyVerySmallSquare:"▫",Eogon:"Ę",Eopf:"𝔼",Epsilon:"Ε",Equal:"⩵",EqualTilde:"≂",Equilibrium:"⇌",Escr:"ℰ",Esim:"⩳",Eta:"Η",Eum:"Ë",Euml:"Ë",Exists:"∃",ExponentialE:"ⅇ",Fcy:"Ф",Ffr:"𝔉",FilledSmallSquare:"◼",FilledVerySmallSquare:"▪",Fopf:"𝔽",ForAll:"∀",Fouriertrf:"ℱ",Fscr:"ℱ",GJcy:"Ѓ",G:">",GT:">",Gamma:"Γ",Gammad:"Ϝ",Gbreve:"Ğ",Gcedil:"Ģ",Gcirc:"Ĝ",Gcy:"Г",Gdot:"Ġ",Gfr:"𝔊",Gg:"⋙",Gopf:"𝔾",GreaterEqual:"≥",GreaterEqualLess:"⋛",GreaterFullEqual:"≧",GreaterGreater:"⪢",GreaterLess:"≷",GreaterSlantEqual:"⩾",GreaterTilde:"≳",Gscr:"𝒢",Gt:"≫",HARDcy:"Ъ",Hacek:"ˇ",Hat:"^",Hcirc:"Ĥ",Hfr:"ℌ",HilbertSpace:"ℋ",Hopf:"ℍ",HorizontalLine:"─",Hscr:"ℋ",Hstrok:"Ħ",HumpDownHump:"≎",HumpEqual:"≏",IEcy:"Е",IJlig:"IJ",IOcy:"Ё",Iacut:"Í",Iacute:"Í",Icir:"Î",Icirc:"Î",Icy:"И",Idot:"İ",Ifr:"ℑ",Igrav:"Ì",Igrave:"Ì",Im:"ℑ",Imacr:"Ī",ImaginaryI:"ⅈ",Implies:"⇒",Int:"∬",Integral:"∫",Intersection:"⋂",InvisibleComma:"⁣",InvisibleTimes:"⁢",Iogon:"Į",Iopf:"𝕀",Iota:"Ι",Iscr:"ℐ",Itilde:"Ĩ",Iukcy:"І",Ium:"Ï",Iuml:"Ï",Jcirc:"Ĵ",Jcy:"Й",Jfr:"𝔍",Jopf:"𝕁",Jscr:"𝒥",Jsercy:"Ј",Jukcy:"Є",KHcy:"Х",KJcy:"Ќ",Kappa:"Κ",Kcedil:"Ķ",Kcy:"К",Kfr:"𝔎",Kopf:"𝕂",Kscr:"𝒦",LJcy:"Љ",L:"<",LT:"<",Lacute:"Ĺ",Lambda:"Λ",Lang:"⟪",Laplacetrf:"ℒ",Larr:"↞",Lcaron:"Ľ",Lcedil:"Ļ",Lcy:"Л",LeftAngleBracket:"⟨",LeftArrow:"←",LeftArrowBar:"⇤",LeftArrowRightArrow:"⇆",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVector:"⇃",LeftDownVectorBar:"⥙",LeftFloor:"⌊",LeftRightArrow:"↔",LeftRightVector:"⥎",LeftTee:"⊣",LeftTeeArrow:"↤",LeftTeeVector:"⥚",LeftTriangle:"⊲",LeftTriangleBar:"⧏",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVector:"↿",LeftUpVectorBar:"⥘",LeftVector:"↼",LeftVectorBar:"⥒",Leftarrow:"⇐",Leftrightarrow:"⇔",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",LessLess:"⪡",LessSlantEqual:"⩽",LessTilde:"≲",Lfr:"𝔏",Ll:"⋘",Lleftarrow:"⇚",Lmidot:"Ŀ",LongLeftArrow:"⟵",LongLeftRightArrow:"⟷",LongRightArrow:"⟶",Longleftarrow:"⟸",Longleftrightarrow:"⟺",Longrightarrow:"⟹",Lopf:"𝕃",LowerLeftArrow:"↙",LowerRightArrow:"↘",Lscr:"ℒ",Lsh:"↰",Lstrok:"Ł",Lt:"≪",Map:"⤅",Mcy:"М",MediumSpace:" ",Mellintrf:"ℳ",Mfr:"𝔐",MinusPlus:"∓",Mopf:"𝕄",Mscr:"ℳ",Mu:"Μ",NJcy:"Њ",Nacute:"Ń",Ncaron:"Ň",Ncedil:"Ņ",Ncy:"Н",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:"\n",Nfr:"𝔑",NoBreak:"⁠",NonBreakingSpace:" ",Nopf:"ℕ",Not:"⫬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"≏̸",NotLeftTriangle:"⋪",NotLeftTriangleBar:"⧏̸",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"⋠",NotReverseElement:"∌",NotRightTriangle:"⋫",NotRightTriangleBar:"⧐̸",NotRightTriangleEqual:"⋭",NotSquareSubset:"⊏̸",NotSquareSubsetEqual:"⋢",NotSquareSuperset:"⊐̸",NotSquareSupersetEqual:"⋣",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"⊁",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"⋡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"≁",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",Nscr:"𝒩",Ntild:"Ñ",Ntilde:"Ñ",Nu:"Ν",OElig:"Œ",Oacut:"Ó",Oacute:"Ó",Ocir:"Ô",Ocirc:"Ô",Ocy:"О",Odblac:"Ő",Ofr:"𝔒",Ograv:"Ò",Ograve:"Ò",Omacr:"Ō",Omega:"Ω",Omicron:"Ο",Oopf:"𝕆",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",Or:"⩔",Oscr:"𝒪",Oslas:"Ø",Oslash:"Ø",Otild:"Õ",Otilde:"Õ",Otimes:"⨷",Oum:"Ö",Ouml:"Ö",OverBar:"‾",OverBrace:"⏞",OverBracket:"⎴",OverParenthesis:"⏜",PartialD:"∂",Pcy:"П",Pfr:"𝔓",Phi:"Φ",Pi:"Π",PlusMinus:"±",Poincareplane:"ℌ",Popf:"ℙ",Pr:"⪻",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",Prime:"″",Product:"∏",Proportion:"∷",Proportional:"∝",Pscr:"𝒫",Psi:"Ψ",QUO:'"',QUOT:'"',Qfr:"𝔔",Qopf:"ℚ",Qscr:"𝒬",RBarr:"⤐",RE:"®",REG:"®",Racute:"Ŕ",Rang:"⟫",Rarr:"↠",Rarrtl:"⤖",Rcaron:"Ř",Rcedil:"Ŗ",Rcy:"Р",Re:"ℜ",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",Rfr:"ℜ",Rho:"Ρ",RightAngleBracket:"⟩",RightArrow:"→",RightArrowBar:"⇥",RightArrowLeftArrow:"⇄",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"⥝",RightDownVector:"⇂",RightDownVectorBar:"⥕",RightFloor:"⌋",RightTee:"⊢",RightTeeArrow:"↦",RightTeeVector:"⥛",RightTriangle:"⊳",RightTriangleBar:"⧐",RightTriangleEqual:"⊵",RightUpDownVector:"⥏",RightUpTeeVector:"⥜",RightUpVector:"↾",RightUpVectorBar:"⥔",RightVector:"⇀",RightVectorBar:"⥓",Rightarrow:"⇒",Ropf:"ℝ",RoundImplies:"⥰",Rrightarrow:"⇛",Rscr:"ℛ",Rsh:"↱",RuleDelayed:"⧴",SHCHcy:"Щ",SHcy:"Ш",SOFTcy:"Ь",Sacute:"Ś",Sc:"⪼",Scaron:"Š",Scedil:"Ş",Scirc:"Ŝ",Scy:"С",Sfr:"𝔖",ShortDownArrow:"↓",ShortLeftArrow:"←",ShortRightArrow:"→",ShortUpArrow:"↑",Sigma:"Σ",SmallCircle:"∘",Sopf:"𝕊",Sqrt:"√",Square:"□",SquareIntersection:"⊓",SquareSubset:"⊏",SquareSubsetEqual:"⊑",SquareSuperset:"⊐",SquareSupersetEqual:"⊒",SquareUnion:"⊔",Sscr:"𝒮",Star:"⋆",Sub:"⋐",Subset:"⋐",SubsetEqual:"⊆",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",SuchThat:"∋",Sum:"∑",Sup:"⋑",Superset:"⊃",SupersetEqual:"⊇",Supset:"⋑",THOR:"Þ",THORN:"Þ",TRADE:"™",TSHcy:"Ћ",TScy:"Ц",Tab:"\t",Tau:"Τ",Tcaron:"Ť",Tcedil:"Ţ",Tcy:"Т",Tfr:"𝔗",Therefore:"∴",Theta:"Θ",ThickSpace:"  ",ThinSpace:" ",Tilde:"∼",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",Topf:"𝕋",TripleDot:"⃛",Tscr:"𝒯",Tstrok:"Ŧ",Uacut:"Ú",Uacute:"Ú",Uarr:"↟",Uarrocir:"⥉",Ubrcy:"Ў",Ubreve:"Ŭ",Ucir:"Û",Ucirc:"Û",Ucy:"У",Udblac:"Ű",Ufr:"𝔘",Ugrav:"Ù",Ugrave:"Ù",Umacr:"Ū",UnderBar:"_",UnderBrace:"⏟",UnderBracket:"⎵",UnderParenthesis:"⏝",Union:"⋃",UnionPlus:"⊎",Uogon:"Ų",Uopf:"𝕌",UpArrow:"↑",UpArrowBar:"⤒",UpArrowDownArrow:"⇅",UpDownArrow:"↕",UpEquilibrium:"⥮",UpTee:"⊥",UpTeeArrow:"↥",Uparrow:"⇑",Updownarrow:"⇕",UpperLeftArrow:"↖",UpperRightArrow:"↗",Upsi:"ϒ",Upsilon:"Υ",Uring:"Ů",Uscr:"𝒰",Utilde:"Ũ",Uum:"Ü",Uuml:"Ü",VDash:"⊫",Vbar:"⫫",Vcy:"В",Vdash:"⊩",Vdashl:"⫦",Vee:"⋁",Verbar:"‖",Vert:"‖",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"❘",VerticalTilde:"≀",VeryThinSpace:" ",Vfr:"𝔙",Vopf:"𝕍",Vscr:"𝒱",Vvdash:"⊪",Wcirc:"Ŵ",Wedge:"⋀",Wfr:"𝔚",Wopf:"𝕎",Wscr:"𝒲",Xfr:"𝔛",Xi:"Ξ",Xopf:"𝕏",Xscr:"𝒳",YAcy:"Я",YIcy:"Ї",YUcy:"Ю",Yacut:"Ý",Yacute:"Ý",Ycirc:"Ŷ",Ycy:"Ы",Yfr:"𝔜",Yopf:"𝕐",Yscr:"𝒴",Yuml:"Ÿ",ZHcy:"Ж",Zacute:"Ź",Zcaron:"Ž",Zcy:"З",Zdot:"Ż",ZeroWidthSpace:"​",Zeta:"Ζ",Zfr:"ℨ",Zopf:"ℤ",Zscr:"𝒵",aacut:"á",aacute:"á",abreve:"ă",ac:"∾",acE:"∾̳",acd:"∿",acir:"â",acirc:"â",acut:"´",acute:"´",acy:"а",aeli:"æ",aelig:"æ",af:"⁡",afr:"𝔞",agrav:"à",agrave:"à",alefsym:"ℵ",aleph:"ℵ",alpha:"α",amacr:"ā",amalg:"⨿",am:"&",amp:"&",and:"∧",andand:"⩕",andd:"⩜",andslope:"⩘",andv:"⩚",ang:"∠",ange:"⦤",angle:"∠",angmsd:"∡",angmsdaa:"⦨",angmsdab:"⦩",angmsdac:"⦪",angmsdad:"⦫",angmsdae:"⦬",angmsdaf:"⦭",angmsdag:"⦮",angmsdah:"⦯",angrt:"∟",angrtvb:"⊾",angrtvbd:"⦝",angsph:"∢",angst:"Å",angzarr:"⍼",aogon:"ą",aopf:"𝕒",ap:"≈",apE:"⩰",apacir:"⩯",ape:"≊",apid:"≋",apos:"'",approx:"≈",approxeq:"≊",arin:"å",aring:"å",ascr:"𝒶",ast:"*",asymp:"≈",asympeq:"≍",atild:"ã",atilde:"ã",aum:"ä",auml:"ä",awconint:"∳",awint:"⨑",bNot:"⫭",backcong:"≌",backepsilon:"϶",backprime:"‵",backsim:"∽",backsimeq:"⋍",barvee:"⊽",barwed:"⌅",barwedge:"⌅",bbrk:"⎵",bbrktbrk:"⎶",bcong:"≌",bcy:"б",bdquo:"„",becaus:"∵",because:"∵",bemptyv:"⦰",bepsi:"϶",bernou:"ℬ",beta:"β",beth:"ℶ",between:"≬",bfr:"𝔟",bigcap:"⋂",bigcirc:"◯",bigcup:"⋃",bigodot:"⨀",bigoplus:"⨁",bigotimes:"⨂",bigsqcup:"⨆",bigstar:"★",bigtriangledown:"▽",bigtriangleup:"△",biguplus:"⨄",bigvee:"⋁",bigwedge:"⋀",bkarow:"⤍",blacklozenge:"⧫",blacksquare:"▪",blacktriangle:"▴",blacktriangledown:"▾",blacktriangleleft:"◂",blacktriangleright:"▸",blank:"␣",blk12:"▒",blk14:"░",blk34:"▓",block:"█",bne:"=⃥",bnequiv:"≡⃥",bnot:"⌐",bopf:"𝕓",bot:"⊥",bottom:"⊥",bowtie:"⋈",boxDL:"╗",boxDR:"╔",boxDl:"╖",boxDr:"╓",boxH:"═",boxHD:"╦",boxHU:"╩",boxHd:"╤",boxHu:"╧",boxUL:"╝",boxUR:"╚",boxUl:"╜",boxUr:"╙",boxV:"║",boxVH:"╬",boxVL:"╣",boxVR:"╠",boxVh:"╫",boxVl:"╢",boxVr:"╟",boxbox:"⧉",boxdL:"╕",boxdR:"╒",boxdl:"┐",boxdr:"┌",boxh:"─",boxhD:"╥",boxhU:"╨",boxhd:"┬",boxhu:"┴",boxminus:"⊟",boxplus:"⊞",boxtimes:"⊠",boxuL:"╛",boxuR:"╘",boxul:"┘",boxur:"└",boxv:"│",boxvH:"╪",boxvL:"╡",boxvR:"╞",boxvh:"┼",boxvl:"┤",boxvr:"├",bprime:"‵",breve:"˘",brvba:"¦",brvbar:"¦",bscr:"𝒷",bsemi:"⁏",bsim:"∽",bsime:"⋍",bsol:"\\",bsolb:"⧅",bsolhsub:"⟈",bull:"•",bullet:"•",bump:"≎",bumpE:"⪮",bumpe:"≏",bumpeq:"≏",cacute:"ć",cap:"∩",capand:"⩄",capbrcup:"⩉",capcap:"⩋",capcup:"⩇",capdot:"⩀",caps:"∩︀",caret:"⁁",caron:"ˇ",ccaps:"⩍",ccaron:"č",ccedi:"ç",ccedil:"ç",ccirc:"ĉ",ccups:"⩌",ccupssm:"⩐",cdot:"ċ",cedi:"¸",cedil:"¸",cemptyv:"⦲",cen:"¢",cent:"¢",centerdot:"·",cfr:"𝔠",chcy:"ч",check:"✓",checkmark:"✓",chi:"χ",cir:"○",cirE:"⧃",circ:"ˆ",circeq:"≗",circlearrowleft:"↺",circlearrowright:"↻",circledR:"®",circledS:"Ⓢ",circledast:"⊛",circledcirc:"⊚",circleddash:"⊝",cire:"≗",cirfnint:"⨐",cirmid:"⫯",cirscir:"⧂",clubs:"♣",clubsuit:"♣",colon:":",colone:"≔",coloneq:"≔",comma:",",commat:"@",comp:"∁",compfn:"∘",complement:"∁",complexes:"ℂ",cong:"≅",congdot:"⩭",conint:"∮",copf:"𝕔",coprod:"∐",cop:"©",copy:"©",copysr:"℗",crarr:"↵",cross:"✗",cscr:"𝒸",csub:"⫏",csube:"⫑",csup:"⫐",csupe:"⫒",ctdot:"⋯",cudarrl:"⤸",cudarrr:"⤵",cuepr:"⋞",cuesc:"⋟",cularr:"↶",cularrp:"⤽",cup:"∪",cupbrcap:"⩈",cupcap:"⩆",cupcup:"⩊",cupdot:"⊍",cupor:"⩅",cups:"∪︀",curarr:"↷",curarrm:"⤼",curlyeqprec:"⋞",curlyeqsucc:"⋟",curlyvee:"⋎",curlywedge:"⋏",curre:"¤",curren:"¤",curvearrowleft:"↶",curvearrowright:"↷",cuvee:"⋎",cuwed:"⋏",cwconint:"∲",cwint:"∱",cylcty:"⌭",dArr:"⇓",dHar:"⥥",dagger:"†",daleth:"ℸ",darr:"↓",dash:"‐",dashv:"⊣",dbkarow:"⤏",dblac:"˝",dcaron:"ď",dcy:"д",dd:"ⅆ",ddagger:"‡",ddarr:"⇊",ddotseq:"⩷",de:"°",deg:"°",delta:"δ",demptyv:"⦱",dfisht:"⥿",dfr:"𝔡",dharl:"⇃",dharr:"⇂",diam:"⋄",diamond:"⋄",diamondsuit:"♦",diams:"♦",die:"¨",digamma:"ϝ",disin:"⋲",div:"÷",divid:"÷",divide:"÷",divideontimes:"⋇",divonx:"⋇",djcy:"ђ",dlcorn:"⌞",dlcrop:"⌍",dollar:"$",dopf:"𝕕",dot:"˙",doteq:"≐",doteqdot:"≑",dotminus:"∸",dotplus:"∔",dotsquare:"⊡",doublebarwedge:"⌆",downarrow:"↓",downdownarrows:"⇊",downharpoonleft:"⇃",downharpoonright:"⇂",drbkarow:"⤐",drcorn:"⌟",drcrop:"⌌",dscr:"𝒹",dscy:"ѕ",dsol:"⧶",dstrok:"đ",dtdot:"⋱",dtri:"▿",dtrif:"▾",duarr:"⇵",duhar:"⥯",dwangle:"⦦",dzcy:"џ",dzigrarr:"⟿",eDDot:"⩷",eDot:"≑",eacut:"é",eacute:"é",easter:"⩮",ecaron:"ě",ecir:"ê",ecirc:"ê",ecolon:"≕",ecy:"э",edot:"ė",ee:"ⅇ",efDot:"≒",efr:"𝔢",eg:"⪚",egrav:"è",egrave:"è",egs:"⪖",egsdot:"⪘",el:"⪙",elinters:"⏧",ell:"ℓ",els:"⪕",elsdot:"⪗",emacr:"ē",empty:"∅",emptyset:"∅",emptyv:"∅",emsp13:" ",emsp14:" ",emsp:" ",eng:"ŋ",ensp:" ",eogon:"ę",eopf:"𝕖",epar:"⋕",eparsl:"⧣",eplus:"⩱",epsi:"ε",epsilon:"ε",epsiv:"ϵ",eqcirc:"≖",eqcolon:"≕",eqsim:"≂",eqslantgtr:"⪖",eqslantless:"⪕",equals:"=",equest:"≟",equiv:"≡",equivDD:"⩸",eqvparsl:"⧥",erDot:"≓",erarr:"⥱",escr:"ℯ",esdot:"≐",esim:"≂",eta:"η",et:"ð",eth:"ð",eum:"ë",euml:"ë",euro:"€",excl:"!",exist:"∃",expectation:"ℰ",exponentiale:"ⅇ",fallingdotseq:"≒",fcy:"ф",female:"♀",ffilig:"ffi",fflig:"ff",ffllig:"ffl",ffr:"𝔣",filig:"fi",fjlig:"fj",flat:"♭",fllig:"fl",fltns:"▱",fnof:"ƒ",fopf:"𝕗",forall:"∀",fork:"⋔",forkv:"⫙",fpartint:"⨍",frac1:"¼",frac12:"½",frac13:"⅓",frac14:"¼",frac15:"⅕",frac16:"⅙",frac18:"⅛",frac23:"⅔",frac25:"⅖",frac3:"¾",frac34:"¾",frac35:"⅗",frac38:"⅜",frac45:"⅘",frac56:"⅚",frac58:"⅝",frac78:"⅞",frasl:"⁄",frown:"⌢",fscr:"𝒻",gE:"≧",gEl:"⪌",gacute:"ǵ",gamma:"γ",gammad:"ϝ",gap:"⪆",gbreve:"ğ",gcirc:"ĝ",gcy:"г",gdot:"ġ",ge:"≥",gel:"⋛",geq:"≥",geqq:"≧",geqslant:"⩾",ges:"⩾",gescc:"⪩",gesdot:"⪀",gesdoto:"⪂",gesdotol:"⪄",gesl:"⋛︀",gesles:"⪔",gfr:"𝔤",gg:"≫",ggg:"⋙",gimel:"ℷ",gjcy:"ѓ",gl:"≷",glE:"⪒",gla:"⪥",glj:"⪤",gnE:"≩",gnap:"⪊",gnapprox:"⪊",gne:"⪈",gneq:"⪈",gneqq:"≩",gnsim:"⋧",gopf:"𝕘",grave:"`",gscr:"ℊ",gsim:"≳",gsime:"⪎",gsiml:"⪐",g:">",gt:">",gtcc:"⪧",gtcir:"⩺",gtdot:"⋗",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"⋗",gtreqless:"⋛",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",hArr:"⇔",hairsp:" ",half:"½",hamilt:"ℋ",hardcy:"ъ",harr:"↔",harrcir:"⥈",harrw:"↭",hbar:"ℏ",hcirc:"ĥ",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",hfr:"𝔥",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",hopf:"𝕙",horbar:"―",hscr:"𝒽",hslash:"ℏ",hstrok:"ħ",hybull:"⁃",hyphen:"‐",iacut:"í",iacute:"í",ic:"⁣",icir:"î",icirc:"î",icy:"и",iecy:"е",iexc:"¡",iexcl:"¡",iff:"⇔",ifr:"𝔦",igrav:"ì",igrave:"ì",ii:"ⅈ",iiiint:"⨌",iiint:"∭",iinfin:"⧜",iiota:"℩",ijlig:"ij",imacr:"ī",image:"ℑ",imagline:"ℐ",imagpart:"ℑ",imath:"ı",imof:"⊷",imped:"Ƶ",in:"∈",incare:"℅",infin:"∞",infintie:"⧝",inodot:"ı",int:"∫",intcal:"⊺",integers:"ℤ",intercal:"⊺",intlarhk:"⨗",intprod:"⨼",iocy:"ё",iogon:"į",iopf:"𝕚",iota:"ι",iprod:"⨼",iques:"¿",iquest:"¿",iscr:"𝒾",isin:"∈",isinE:"⋹",isindot:"⋵",isins:"⋴",isinsv:"⋳",isinv:"∈",it:"⁢",itilde:"ĩ",iukcy:"і",ium:"ï",iuml:"ï",jcirc:"ĵ",jcy:"й",jfr:"𝔧",jmath:"ȷ",jopf:"𝕛",jscr:"𝒿",jsercy:"ј",jukcy:"є",kappa:"κ",kappav:"ϰ",kcedil:"ķ",kcy:"к",kfr:"𝔨",kgreen:"ĸ",khcy:"х",kjcy:"ќ",kopf:"𝕜",kscr:"𝓀",lAarr:"⇚",lArr:"⇐",lAtail:"⤛",lBarr:"⤎",lE:"≦",lEg:"⪋",lHar:"⥢",lacute:"ĺ",laemptyv:"⦴",lagran:"ℒ",lambda:"λ",lang:"⟨",langd:"⦑",langle:"⟨",lap:"⪅",laqu:"«",laquo:"«",larr:"←",larrb:"⇤",larrbfs:"⤟",larrfs:"⤝",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",lat:"⪫",latail:"⤙",late:"⪭",lates:"⪭︀",lbarr:"⤌",lbbrk:"❲",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"⦏",lbrkslu:"⦍",lcaron:"ľ",lcedil:"ļ",lceil:"⌈",lcub:"{",lcy:"л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",le:"≤",leftarrow:"←",leftarrowtail:"↢",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",leftrightarrow:"↔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",leftthreetimes:"⋋",leg:"⋚",leq:"≤",leqq:"≦",leqslant:"⩽",les:"⩽",lescc:"⪨",lesdot:"⩿",lesdoto:"⪁",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"⋖",lesseqgtr:"⋚",lesseqqgtr:"⪋",lessgtr:"≶",lesssim:"≲",lfisht:"⥼",lfloor:"⌊",lfr:"𝔩",lg:"≶",lgE:"⪑",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"▄",ljcy:"љ",ll:"≪",llarr:"⇇",llcorner:"⌞",llhard:"⥫",lltri:"◺",lmidot:"ŀ",lmoust:"⎰",lmoustache:"⎰",lnE:"≨",lnap:"⪉",lnapprox:"⪉",lne:"⪇",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",longleftarrow:"⟵",longleftrightarrow:"⟷",longmapsto:"⟼",longrightarrow:"⟶",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",lopf:"𝕝",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",loz:"◊",lozenge:"◊",lozf:"⧫",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",lscr:"𝓁",lsh:"↰",lsim:"≲",lsime:"⪍",lsimg:"⪏",lsqb:"[",lsquo:"‘",lsquor:"‚",lstrok:"ł",l:"<",lt:"<",ltcc:"⪦",ltcir:"⩹",ltdot:"⋖",lthree:"⋋",ltimes:"⋉",ltlarr:"⥶",ltquest:"⩻",ltrPar:"⦖",ltri:"◃",ltrie:"⊴",ltrif:"◂",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",mDDot:"∺",mac:"¯",macr:"¯",male:"♂",malt:"✠",maltese:"✠",map:"↦",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"▮",mcomma:"⨩",mcy:"м",mdash:"—",measuredangle:"∡",mfr:"𝔪",mho:"℧",micr:"µ",micro:"µ",mid:"∣",midast:"*",midcir:"⫰",middo:"·",middot:"·",minus:"−",minusb:"⊟",minusd:"∸",minusdu:"⨪",mlcp:"⫛",mldr:"…",mnplus:"∓",models:"⊧",mopf:"𝕞",mp:"∓",mscr:"𝓂",mstpos:"∾",mu:"μ",multimap:"⊸",mumap:"⊸",nGg:"⋙̸",nGt:"≫⃒",nGtv:"≫̸",nLeftarrow:"⇍",nLeftrightarrow:"⇎",nLl:"⋘̸",nLt:"≪⃒",nLtv:"≪̸",nRightarrow:"⇏",nVDash:"⊯",nVdash:"⊮",nabla:"∇",nacute:"ń",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natur:"♮",natural:"♮",naturals:"ℕ",nbs:" ",nbsp:" ",nbump:"≎̸",nbumpe:"≏̸",ncap:"⩃",ncaron:"ň",ncedil:"ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"⩂",ncy:"н",ndash:"–",ne:"≠",neArr:"⇗",nearhk:"⤤",nearr:"↗",nearrow:"↗",nedot:"≐̸",nequiv:"≢",nesear:"⤨",nesim:"≂̸",nexist:"∄",nexists:"∄",nfr:"𝔫",ngE:"≧̸",nge:"≱",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",ngsim:"≵",ngt:"≯",ngtr:"≯",nhArr:"⇎",nharr:"↮",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",njcy:"њ",nlArr:"⇍",nlE:"≦̸",nlarr:"↚",nldr:"‥",nle:"≰",nleftarrow:"↚",nleftrightarrow:"↮",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nlsim:"≴",nlt:"≮",nltri:"⋪",nltrie:"⋬",nmid:"∤",nopf:"𝕟",no:"¬",not:"¬",notin:"∉",notinE:"⋹̸",notindot:"⋵̸",notinva:"∉",notinvb:"⋷",notinvc:"⋶",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",npar:"∦",nparallel:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"⋠",npre:"⪯̸",nprec:"⊀",npreceq:"⪯̸",nrArr:"⇏",nrarr:"↛",nrarrc:"⤳̸",nrarrw:"↝̸",nrightarrow:"↛",nrtri:"⋫",nrtrie:"⋭",nsc:"⊁",nsccue:"⋡",nsce:"⪰̸",nscr:"𝓃",nshortmid:"∤",nshortparallel:"∦",nsim:"≁",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"⋢",nsqsupe:"⋣",nsub:"⊄",nsubE:"⫅̸",nsube:"⊈",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"⊁",nsucceq:"⪰̸",nsup:"⊅",nsupE:"⫆̸",nsupe:"⊉",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",ntild:"ñ",ntilde:"ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"⋫",ntrianglerighteq:"⋭",nu:"ν",num:"#",numero:"№",numsp:" ",nvDash:"⊭",nvHarr:"⤄",nvap:"≍⃒",nvdash:"⊬",nvge:"≥⃒",nvgt:">⃒",nvinfin:"⧞",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwArr:"⇖",nwarhk:"⤣",nwarr:"↖",nwarrow:"↖",nwnear:"⤧",oS:"Ⓢ",oacut:"ó",oacute:"ó",oast:"⊛",ocir:"ô",ocirc:"ô",ocy:"о",odash:"⊝",odblac:"ő",odiv:"⨸",odot:"⊙",odsold:"⦼",oelig:"œ",ofcir:"⦿",ofr:"𝔬",ogon:"˛",ograv:"ò",ograve:"ò",ogt:"⧁",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"⧀",omacr:"ō",omega:"ω",omicron:"ο",omid:"⦶",ominus:"⊖",oopf:"𝕠",opar:"⦷",operp:"⦹",oplus:"⊕",or:"∨",orarr:"↻",ord:"º",order:"ℴ",orderof:"ℴ",ordf:"ª",ordm:"º",origof:"⊶",oror:"⩖",orslope:"⩗",orv:"⩛",oscr:"ℴ",oslas:"ø",oslash:"ø",osol:"⊘",otild:"õ",otilde:"õ",otimes:"⊗",otimesas:"⨶",oum:"ö",ouml:"ö",ovbar:"⌽",par:"¶",para:"¶",parallel:"∥",parsim:"⫳",parsl:"⫽",part:"∂",pcy:"п",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",pfr:"𝔭",phi:"φ",phiv:"ϕ",phmmat:"ℳ",phone:"☎",pi:"π",pitchfork:"⋔",piv:"ϖ",planck:"ℏ",planckh:"ℎ",plankv:"ℏ",plus:"+",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plusdo:"∔",plusdu:"⨥",pluse:"⩲",plusm:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",pointint:"⨕",popf:"𝕡",poun:"£",pound:"£",pr:"≺",prE:"⪳",prap:"⪷",prcue:"≼",pre:"⪯",prec:"≺",precapprox:"⪷",preccurlyeq:"≼",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",precsim:"≾",prime:"′",primes:"ℙ",prnE:"⪵",prnap:"⪹",prnsim:"⋨",prod:"∏",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"∝",propto:"∝",prsim:"≾",prurel:"⊰",pscr:"𝓅",psi:"ψ",puncsp:" ",qfr:"𝔮",qint:"⨌",qopf:"𝕢",qprime:"⁗",qscr:"𝓆",quaternions:"ℍ",quatint:"⨖",quest:"?",questeq:"≟",quo:'"',quot:'"',rAarr:"⇛",rArr:"⇒",rAtail:"⤜",rBarr:"⤏",rHar:"⥤",race:"∽̱",racute:"ŕ",radic:"√",raemptyv:"⦳",rang:"⟩",rangd:"⦒",range:"⦥",rangle:"⟩",raqu:"»",raquo:"»",rarr:"→",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",rarrtl:"↣",rarrw:"↝",ratail:"⤚",ratio:"∶",rationals:"ℚ",rbarr:"⤍",rbbrk:"❳",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"⦐",rcaron:"ř",rcedil:"ŗ",rceil:"⌉",rcub:"}",rcy:"р",rdca:"⤷",rdldhar:"⥩",rdquo:"”",rdquor:"”",rdsh:"↳",real:"ℜ",realine:"ℛ",realpart:"ℜ",reals:"ℝ",rect:"▭",re:"®",reg:"®",rfisht:"⥽",rfloor:"⌋",rfr:"𝔯",rhard:"⇁",rharu:"⇀",rharul:"⥬",rho:"ρ",rhov:"ϱ",rightarrow:"→",rightarrowtail:"↣",rightharpoondown:"⇁",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"↝",rightthreetimes:"⋌",ring:"˚",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"‏",rmoust:"⎱",rmoustache:"⎱",rnmid:"⫮",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",ropf:"𝕣",roplus:"⨮",rotimes:"⨵",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",rsaquo:"›",rscr:"𝓇",rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"▹",rtrie:"⊵",rtrif:"▸",rtriltri:"⧎",ruluhar:"⥨",rx:"℞",sacute:"ś",sbquo:"‚",sc:"≻",scE:"⪴",scap:"⪸",scaron:"š",sccue:"≽",sce:"⪰",scedil:"ş",scirc:"ŝ",scnE:"⪶",scnap:"⪺",scnsim:"⋩",scpolint:"⨓",scsim:"≿",scy:"с",sdot:"⋅",sdotb:"⊡",sdote:"⩦",seArr:"⇘",searhk:"⤥",searr:"↘",searrow:"↘",sec:"§",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",sfr:"𝔰",sfrown:"⌢",sharp:"♯",shchcy:"щ",shcy:"ш",shortmid:"∣",shortparallel:"∥",sh:"­",shy:"­",sigma:"σ",sigmaf:"ς",sigmav:"ς",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"⪝",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"←",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",softcy:"ь",sol:"/",solb:"⧄",solbar:"⌿",sopf:"𝕤",spades:"♠",spadesuit:"♠",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",sqsub:"⊏",sqsube:"⊑",sqsubset:"⊏",sqsubseteq:"⊑",sqsup:"⊐",sqsupe:"⊒",sqsupset:"⊐",sqsupseteq:"⊒",squ:"□",square:"□",squarf:"▪",squf:"▪",srarr:"→",sscr:"𝓈",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",star:"☆",starf:"★",straightepsilon:"ϵ",straightphi:"ϕ",strns:"¯",sub:"⊂",subE:"⫅",subdot:"⪽",sube:"⊆",subedot:"⫃",submult:"⫁",subnE:"⫋",subne:"⊊",subplus:"⪿",subrarr:"⥹",subset:"⊂",subseteq:"⊆",subseteqq:"⫅",subsetneq:"⊊",subsetneqq:"⫋",subsim:"⫇",subsub:"⫕",subsup:"⫓",succ:"≻",succapprox:"⪸",succcurlyeq:"≽",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"⋩",succsim:"≿",sum:"∑",sung:"♪",sup:"⊃",sup1:"¹",sup2:"²",sup3:"³",supE:"⫆",supdot:"⪾",supdsub:"⫘",supe:"⊇",supedot:"⫄",suphsol:"⟉",suphsub:"⫗",suplarr:"⥻",supmult:"⫂",supnE:"⫌",supne:"⊋",supplus:"⫀",supset:"⊃",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"⫔",supsup:"⫖",swArr:"⇙",swarhk:"⤦",swarr:"↙",swarrow:"↙",swnwar:"⤪",szli:"ß",szlig:"ß",target:"⌖",tau:"τ",tbrk:"⎴",tcaron:"ť",tcedil:"ţ",tcy:"т",tdot:"⃛",telrec:"⌕",tfr:"𝔱",there4:"∴",therefore:"∴",theta:"θ",thetasym:"ϑ",thetav:"ϑ",thickapprox:"≈",thicksim:"∼",thinsp:" ",thkap:"≈",thksim:"∼",thor:"þ",thorn:"þ",tilde:"˜",time:"×",times:"×",timesb:"⊠",timesbar:"⨱",timesd:"⨰",tint:"∭",toea:"⤨",top:"⊤",topbot:"⌶",topcir:"⫱",topf:"𝕥",topfork:"⫚",tosa:"⤩",tprime:"‴",trade:"™",triangle:"▵",triangledown:"▿",triangleleft:"◃",trianglelefteq:"⊴",triangleq:"≜",triangleright:"▹",trianglerighteq:"⊵",tridot:"◬",trie:"≜",triminus:"⨺",triplus:"⨹",trisb:"⧍",tritime:"⨻",trpezium:"⏢",tscr:"𝓉",tscy:"ц",tshcy:"ћ",tstrok:"ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",uArr:"⇑",uHar:"⥣",uacut:"ú",uacute:"ú",uarr:"↑",ubrcy:"ў",ubreve:"ŭ",ucir:"û",ucirc:"û",ucy:"у",udarr:"⇅",udblac:"ű",udhar:"⥮",ufisht:"⥾",ufr:"𝔲",ugrav:"ù",ugrave:"ù",uharl:"↿",uharr:"↾",uhblk:"▀",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"⌏",ultri:"◸",umacr:"ū",um:"¨",uml:"¨",uogon:"ų",uopf:"𝕦",uparrow:"↑",updownarrow:"↕",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",upsi:"υ",upsih:"ϒ",upsilon:"υ",upuparrows:"⇈",urcorn:"⌝",urcorner:"⌝",urcrop:"⌎",uring:"ů",urtri:"◹",uscr:"𝓊",utdot:"⋰",utilde:"ũ",utri:"▵",utrif:"▴",uuarr:"⇈",uum:"ü",uuml:"ü",uwangle:"⦧",vArr:"⇕",vBar:"⫨",vBarv:"⫩",vDash:"⊨",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"ϕ",varpi:"ϖ",varpropto:"∝",varr:"↕",varrho:"ϱ",varsigma:"ς",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"ϑ",vartriangleleft:"⊲",vartriangleright:"⊳",vcy:"в",vdash:"⊢",vee:"∨",veebar:"⊻",veeeq:"≚",vellip:"⋮",verbar:"|",vert:"|",vfr:"𝔳",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",vopf:"𝕧",vprop:"∝",vrtri:"⊳",vscr:"𝓋",vsubnE:"⫋︀",vsubne:"⊊︀",vsupnE:"⫌︀",vsupne:"⊋︀",vzigzag:"⦚",wcirc:"ŵ",wedbar:"⩟",wedge:"∧",wedgeq:"≙",weierp:"℘",wfr:"𝔴",wopf:"𝕨",wp:"℘",wr:"≀",wreath:"≀",wscr:"𝓌",xcap:"⋂",xcirc:"◯",xcup:"⋃",xdtri:"▽",xfr:"𝔵",xhArr:"⟺",xharr:"⟷",xi:"ξ",xlArr:"⟸",xlarr:"⟵",xmap:"⟼",xnis:"⋻",xodot:"⨀",xopf:"𝕩",xoplus:"⨁",xotime:"⨂",xrArr:"⟹",xrarr:"⟶",xscr:"𝓍",xsqcup:"⨆",xuplus:"⨄",xutri:"△",xvee:"⋁",xwedge:"⋀",yacut:"ý",yacute:"ý",yacy:"я",ycirc:"ŷ",ycy:"ы",ye:"¥",yen:"¥",yfr:"𝔶",yicy:"ї",yopf:"𝕪",yscr:"𝓎",yucy:"ю",yum:"ÿ",yuml:"ÿ",zacute:"ź",zcaron:"ž",zcy:"з",zdot:"ż",zeetrf:"ℨ",zeta:"ζ",zfr:"𝔷",zhcy:"ж",zigrarr:"⇝",zopf:"𝕫",zscr:"𝓏",zwj:"‍",zwnj:"‌"}},{}],4:[function(e,n,t){n.exports={0:"�",128:"€",130:"‚",131:"ƒ",132:"„",133:"…",134:"†",135:"‡",136:"ˆ",137:"‰",138:"Š",139:"‹",140:"Œ",142:"Ž",145:"‘",146:"’",147:"“",148:"”",149:"•",150:"–",151:"—",152:"˜",153:"™",154:"š",155:"›",156:"œ",158:"ž",159:"Ÿ"}},{}],5:[function(e,n,t){"use strict";n.exports=function(e){return String(e).replace(/\s+/g," ")}},{}],6:[function(e,n,t){"use strict";var o=Object.prototype.hasOwnProperty,a=Object.prototype.toString,r=function(e){return"function"==typeof Array.isArray?Array.isArray(e):"[object Array]"===a.call(e)},i=function(e){if(!e||"[object Object]"!==a.call(e))return!1;var n,t=o.call(e,"constructor"),r=e.constructor&&e.constructor.prototype&&o.call(e.constructor.prototype,"isPrototypeOf");if(e.constructor&&!t&&!r)return!1;for(n in e);return void 0===n||o.call(e,n)};n.exports=function e(){var n,t,o,a,s,l,c=arguments[0],u=1,h=arguments.length,d=!1;for("boolean"==typeof c&&(d=c,c=arguments[1]||{},u=2),(null==c||"object"!=typeof c&&"function"!=typeof c)&&(c={});u<h;++u)if(null!=(n=arguments[u]))for(t in n)o=c[t],c!==(a=n[t])&&(d&&a&&(i(a)||(s=r(a)))?(s?(s=!1,l=o&&r(o)?o:[]):l=o&&i(o)?o:{},c[t]=e(d,l,a)):void 0!==a&&(c[t]=a));return c}},{}],7:[function(e,n,t){(function(t){"use strict";var o=e("./emptyFunction"),a={listen:function(e,n,t){return e.addEventListener?(e.addEventListener(n,t,!1),{remove:function(){e.removeEventListener(n,t,!1)}}):e.attachEvent?(e.attachEvent("on"+n,t),{remove:function(){e.detachEvent("on"+n,t)}}):void 0},capture:function(e,n,a){return e.addEventListener?(e.addEventListener(n,a,!0),{remove:function(){e.removeEventListener(n,a,!0)}}):("production"!==t.env.NODE_ENV&&console.error("Attempted to listen to events during the capture phase on a browser that does not support the capture phase. Your application will not receive some events."),{remove:o})},registerDefault:function(){}};n.exports=a}).call(this,e("_process"))},{"./emptyFunction":12,_process:48}],8:[function(e,n,t){"use strict";var o=!("undefined"==typeof window||!window.document||!window.document.createElement),a={canUseDOM:o,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:o&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:o&&!!window.screen,isInWorker:!o};n.exports=a},{}],9:[function(e,n,t){"use strict";var o=/-(.)/g;n.exports=function(e){return e.replace(o,function(e,n){return n.toUpperCase()})}},{}],10:[function(e,n,t){"use strict";var o=e("./camelize"),a=/^-ms-/;n.exports=function(e){return o(e.replace(a,"ms-"))}},{"./camelize":9}],11:[function(e,n,t){"use strict";var o=e("./isTextNode");n.exports=function e(n,t){return!(!n||!t)&&(n===t||!o(n)&&(o(t)?e(n,t.parentNode):"contains"in n?n.contains(t):!!n.compareDocumentPosition&&!!(16&n.compareDocumentPosition(t))))}},{"./isTextNode":20}],12:[function(e,n,t){"use strict";function o(e){return function(){return e}}var a=function(){};a.thatReturns=o,a.thatReturnsFalse=o(!1),a.thatReturnsTrue=o(!0),a.thatReturnsNull=o(null),a.thatReturnsThis=function(){return this},a.thatReturnsArgument=function(e){return e},n.exports=a},{}],13:[function(e,n,t){(function(e){"use strict";var t={};"production"!==e.env.NODE_ENV&&Object.freeze(t),n.exports=t}).call(this,e("_process"))},{_process:48}],14:[function(e,n,t){"use strict";n.exports=function(e){try{e.focus()}catch(e){}}},{}],15:[function(e,n,t){"use strict";n.exports=function(e){if(void 0===(e=e||("undefined"!=typeof document?document:void 0)))return null;try{return e.activeElement||e.body}catch(n){return e.body}}},{}],16:[function(e,n,t){"use strict";var o=/([A-Z])/g;n.exports=function(e){return e.replace(o,"-$1").toLowerCase()}},{}],17:[function(e,n,t){"use strict";var o=e("./hyphenate"),a=/^ms-/;n.exports=function(e){return o(e).replace(a,"-ms-")}},{"./hyphenate":16}],18:[function(e,n,t){(function(e){"use strict";var t=function(e){};"production"!==e.env.NODE_ENV&&(t=function(e){if(void 0===e)throw new Error("invariant requires an error message argument")}),n.exports=function(e,n,o,a,r,i,s,l){if(t(n),!e){var c;if(void 0===n)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[o,a,r,i,s,l],h=0;(c=new Error(n.replace(/%s/g,function(){return u[h++]}))).name="Invariant Violation"}throw c.framesToPop=1,c}}}).call(this,e("_process"))},{_process:48}],19:[function(e,n,t){"use strict";n.exports=function(e){var n=(e?e.ownerDocument||e:document).defaultView||window;return!(!e||!("function"==typeof n.Node?e instanceof n.Node:"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName))}},{}],20:[function(e,n,t){"use strict";var o=e("./isNode");n.exports=function(e){return o(e)&&3==e.nodeType}},{"./isNode":19}],21:[function(e,n,t){"use strict";var o=Object.prototype.hasOwnProperty;function a(e,n){return e===n?0!==e||0!==n||1/e==1/n:e!=e&&n!=n}n.exports=function(e,n){if(a(e,n))return!0;if("object"!=typeof e||null===e||"object"!=typeof n||null===n)return!1;var t=Object.keys(e),r=Object.keys(n);if(t.length!==r.length)return!1;for(var i=0;i<t.length;i++)if(!o.call(n,t[i])||!a(e[t[i]],n[t[i]]))return!1;return!0}},{}],22:[function(e,n,t){(function(t){"use strict";var o=e("./emptyFunction");if("production"!==t.env.NODE_ENV){o=function(e,n){if(void 0===n)throw new Error("`warning(condition, format, ...args)` requires a warning message argument");if(0!==n.indexOf("Failed Composite propType: ")&&!e){for(var t=arguments.length,o=Array(t>2?t-2:0),a=2;a<t;a++)o[a-2]=arguments[a];(function(e){for(var n=arguments.length,t=Array(n>1?n-1:0),o=1;o<n;o++)t[o-1]=arguments[o];var a=0,r="Warning: "+e.replace(/%s/g,function(){return t[a++]});"undefined"!=typeof console&&console.error(r);try{throw new Error(r)}catch(e){}}).apply(void 0,[n].concat(o))}}}n.exports=o}).call(this,e("_process"))},{"./emptyFunction":12,_process:48}],23:[function(e,n,t){"use strict";t.__esModule=!0;t.canUseDOM=!("undefined"==typeof window||!window.document||!window.document.createElement),t.addEventListener=function(e,n,t){return e.addEventListener?e.addEventListener(n,t,!1):e.attachEvent("on"+n,t)},t.removeEventListener=function(e,n,t){return e.removeEventListener?e.removeEventListener(n,t,!1):e.detachEvent("on"+n,t)},t.getConfirmation=function(e,n){return n(window.confirm(e))},t.supportsHistory=function(){var e=window.navigator.userAgent;return(-1===e.indexOf("Android 2.")&&-1===e.indexOf("Android 4.0")||-1===e.indexOf("Mobile Safari")||-1!==e.indexOf("Chrome")||-1!==e.indexOf("Windows Phone"))&&(window.history&&"pushState"in window.history)},t.supportsPopStateOnHashChange=function(){return-1===window.navigator.userAgent.indexOf("Trident")},t.supportsGoWithoutReloadUsingHash=function(){return-1===window.navigator.userAgent.indexOf("Firefox")},t.isExtraneousPopstateEvent=function(e){return void 0===e.state&&-1===navigator.userAgent.indexOf("CriOS")}},{}],24:[function(e,n,t){"use strict";t.__esModule=!0,t.locationsAreEqual=t.createLocation=void 0;var o=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},a=s(e("resolve-pathname")),r=s(e("value-equal")),i=e("./PathUtils");function s(e){return e&&e.__esModule?e:{default:e}}t.createLocation=function(e,n,t,r){var s=void 0;"string"==typeof e?(s=(0,i.parsePath)(e)).state=n:(void 0===(s=o({},e)).pathname&&(s.pathname=""),s.search?"?"!==s.search.charAt(0)&&(s.search="?"+s.search):s.search="",s.hash?"#"!==s.hash.charAt(0)&&(s.hash="#"+s.hash):s.hash="",void 0!==n&&void 0===s.state&&(s.state=n));try{s.pathname=decodeURI(s.pathname)}catch(e){throw e instanceof URIError?new URIError('Pathname "'+s.pathname+'" could not be decoded. This is likely caused by an invalid percent-encoding.'):e}return t&&(s.key=t),r?s.pathname?"/"!==s.pathname.charAt(0)&&(s.pathname=(0,a.default)(s.pathname,r.pathname)):s.pathname=r.pathname:s.pathname||(s.pathname="/"),s},t.locationsAreEqual=function(e,n){return e.pathname===n.pathname&&e.search===n.search&&e.hash===n.hash&&e.key===n.key&&(0,r.default)(e.state,n.state)}},{"./PathUtils":25,"resolve-pathname":141,"value-equal":152}],25:[function(e,n,t){"use strict";t.__esModule=!0;t.addLeadingSlash=function(e){return"/"===e.charAt(0)?e:"/"+e},t.stripLeadingSlash=function(e){return"/"===e.charAt(0)?e.substr(1):e};var o=t.hasBasename=function(e,n){return new RegExp("^"+n+"(\\/|\\?|#|$)","i").test(e)};t.stripBasename=function(e,n){return o(e,n)?e.substr(n.length):e},t.stripTrailingSlash=function(e){return"/"===e.charAt(e.length-1)?e.slice(0,-1):e},t.parsePath=function(e){var n=e||"/",t="",o="",a=n.indexOf("#");-1!==a&&(o=n.substr(a),n=n.substr(0,a));var r=n.indexOf("?");return-1!==r&&(t=n.substr(r),n=n.substr(0,r)),{pathname:n,search:"?"===t?"":t,hash:"#"===o?"":o}},t.createPath=function(e){var n=e.pathname,t=e.search,o=e.hash,a=n||"/";return t&&"?"!==t&&(a+="?"===t.charAt(0)?t:"?"+t),o&&"#"!==o&&(a+="#"===o.charAt(0)?o:"#"+o),a}},{}],26:[function(e,n,t){"use strict";t.__esModule=!0;var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},r=h(e("warning")),i=h(e("invariant")),s=e("./LocationUtils"),l=e("./PathUtils"),c=h(e("./createTransitionManager")),u=e("./DOMUtils");function h(e){return e&&e.__esModule?e:{default:e}}var d="popstate",p="hashchange",f=function(){try{return window.history.state||{}}catch(e){return{}}};t.default=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(0,i.default)(u.canUseDOM,"Browser history needs a DOM");var n=window.history,t=(0,u.supportsHistory)(),h=!(0,u.supportsPopStateOnHashChange)(),m=e.forceRefresh,g=void 0!==m&&m,y=e.getUserConfirmation,b=void 0===y?u.getConfirmation:y,w=e.keyLength,v=void 0===w?6:w,k=e.basename?(0,l.stripTrailingSlash)((0,l.addLeadingSlash)(e.basename)):"",T=function(e){var n=e||{},t=n.key,o=n.state,a=window.location,i=a.pathname+a.search+a.hash;return(0,r.default)(!k||(0,l.hasBasename)(i,k),'You are attempting to use a basename on a page whose URL path does not begin with the basename. Expected path "'+i+'" to begin with "'+k+'".'),k&&(i=(0,l.stripBasename)(i,k)),(0,s.createLocation)(i,o,t)},I=function(){return Math.random().toString(36).substr(2,v)},E=(0,c.default)(),x=function(e){a(W,e),W.length=n.length,E.notifyListeners(W.location,W.action)},S=function(e){(0,u.isExtraneousPopstateEvent)(e)||C(T(e.state))},L=function(){C(T(f()))},N=!1,C=function(e){N?(N=!1,x()):E.confirmTransitionTo(e,"POP",b,function(n){n?x({action:"POP",location:e}):A(e)})},A=function(e){var n=W.location,t=O.indexOf(n.key);-1===t&&(t=0);var o=O.indexOf(e.key);-1===o&&(o=0);var a=t-o;a&&(N=!0,D(a))},P=T(f()),O=[P.key],R=function(e){return k+(0,l.createPath)(e)},D=function(e){n.go(e)},M=0,F=function(e){1===(M+=e)?((0,u.addEventListener)(window,d,S),h&&(0,u.addEventListener)(window,p,L)):0===M&&((0,u.removeEventListener)(window,d,S),h&&(0,u.removeEventListener)(window,p,L))},B=!1,W={length:n.length,action:"POP",location:P,createHref:R,push:function(e,a){(0,r.default)(!("object"===(void 0===e?"undefined":o(e))&&void 0!==e.state&&void 0!==a),"You should avoid providing a 2nd state argument to push when the 1st argument is a location-like object that already has state; it is ignored");var i=(0,s.createLocation)(e,a,I(),W.location);E.confirmTransitionTo(i,"PUSH",b,function(e){if(e){var o=R(i),a=i.key,s=i.state;if(t)if(n.pushState({key:a,state:s},null,o),g)window.location.href=o;else{var l=O.indexOf(W.location.key),c=O.slice(0,-1===l?0:l+1);c.push(i.key),O=c,x({action:"PUSH",location:i})}else(0,r.default)(void 0===s,"Browser history cannot push state in browsers that do not support HTML5 history"),window.location.href=o}})},replace:function(e,a){(0,r.default)(!("object"===(void 0===e?"undefined":o(e))&&void 0!==e.state&&void 0!==a),"You should avoid providing a 2nd state argument to replace when the 1st argument is a location-like object that already has state; it is ignored");var i="REPLACE",l=(0,s.createLocation)(e,a,I(),W.location);E.confirmTransitionTo(l,i,b,function(e){if(e){var o=R(l),a=l.key,s=l.state;if(t)if(n.replaceState({key:a,state:s},null,o),g)window.location.replace(o);else{var c=O.indexOf(W.location.key);-1!==c&&(O[c]=l.key),x({action:i,location:l})}else(0,r.default)(void 0===s,"Browser history cannot replace state in browsers that do not support HTML5 history"),window.location.replace(o)}})},go:D,goBack:function(){return D(-1)},goForward:function(){return D(1)},block:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],n=E.setPrompt(e);return B||(F(1),B=!0),function(){return B&&(B=!1,F(-1)),n()}},listen:function(e){var n=E.appendListener(e);return F(1),function(){F(-1),n()}}};return W}},{"./DOMUtils":23,"./LocationUtils":24,"./PathUtils":25,"./createTransitionManager":29,invariant:33,warning:157}],27:[function(e,n,t){"use strict";t.__esModule=!0;var o=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},a=u(e("warning")),r=u(e("invariant")),i=e("./LocationUtils"),s=e("./PathUtils"),l=u(e("./createTransitionManager")),c=e("./DOMUtils");function u(e){return e&&e.__esModule?e:{default:e}}var h="hashchange",d={hashbang:{encodePath:function(e){return"!"===e.charAt(0)?e:"!/"+(0,s.stripLeadingSlash)(e)},decodePath:function(e){return"!"===e.charAt(0)?e.substr(1):e}},noslash:{encodePath:s.stripLeadingSlash,decodePath:s.addLeadingSlash},slash:{encodePath:s.addLeadingSlash,decodePath:s.addLeadingSlash}},p=function(){var e=window.location.href,n=e.indexOf("#");return-1===n?"":e.substring(n+1)},f=function(e){var n=window.location.href.indexOf("#");window.location.replace(window.location.href.slice(0,n>=0?n:0)+"#"+e)};t.default=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(0,r.default)(c.canUseDOM,"Hash history needs a DOM");var n=window.history,t=(0,c.supportsGoWithoutReloadUsingHash)(),u=e.getUserConfirmation,m=void 0===u?c.getConfirmation:u,g=e.hashType,y=void 0===g?"slash":g,b=e.basename?(0,s.stripTrailingSlash)((0,s.addLeadingSlash)(e.basename)):"",w=d[y],v=w.encodePath,k=w.decodePath,T=function(){var e=k(p());return(0,a.default)(!b||(0,s.hasBasename)(e,b),'You are attempting to use a basename on a page whose URL path does not begin with the basename. Expected path "'+e+'" to begin with "'+b+'".'),b&&(e=(0,s.stripBasename)(e,b)),(0,i.createLocation)(e)},I=(0,l.default)(),E=function(e){o(W,e),W.length=n.length,I.notifyListeners(W.location,W.action)},x=!1,S=null,L=function(){var e=p(),n=v(e);if(e!==n)f(n);else{var t=T(),o=W.location;if(!x&&(0,i.locationsAreEqual)(o,t))return;if(S===(0,s.createPath)(t))return;S=null,N(t)}},N=function(e){x?(x=!1,E()):I.confirmTransitionTo(e,"POP",m,function(n){n?E({action:"POP",location:e}):C(e)})},C=function(e){var n=W.location,t=R.lastIndexOf((0,s.createPath)(n));-1===t&&(t=0);var o=R.lastIndexOf((0,s.createPath)(e));-1===o&&(o=0);var a=t-o;a&&(x=!0,D(a))},A=p(),P=v(A);A!==P&&f(P);var O=T(),R=[(0,s.createPath)(O)],D=function(e){(0,a.default)(t,"Hash history go(n) causes a full page reload in this browser"),n.go(e)},M=0,F=function(e){1===(M+=e)?(0,c.addEventListener)(window,h,L):0===M&&(0,c.removeEventListener)(window,h,L)},B=!1,W={length:n.length,action:"POP",location:O,createHref:function(e){return"#"+v(b+(0,s.createPath)(e))},push:function(e,n){(0,a.default)(void 0===n,"Hash history cannot push state; it is ignored");var t=(0,i.createLocation)(e,void 0,void 0,W.location);I.confirmTransitionTo(t,"PUSH",m,function(e){if(e){var n,o=(0,s.createPath)(t),r=v(b+o);if(p()!==r){S=o,n=r,window.location.hash=n;var i=R.lastIndexOf((0,s.createPath)(W.location)),l=R.slice(0,-1===i?0:i+1);l.push(o),R=l,E({action:"PUSH",location:t})}else(0,a.default)(!1,"Hash history cannot PUSH the same path; a new entry will not be added to the history stack"),E()}})},replace:function(e,n){(0,a.default)(void 0===n,"Hash history cannot replace state; it is ignored");var t="REPLACE",o=(0,i.createLocation)(e,void 0,void 0,W.location);I.confirmTransitionTo(o,t,m,function(e){if(e){var n=(0,s.createPath)(o),a=v(b+n);p()!==a&&(S=n,f(a));var r=R.indexOf((0,s.createPath)(W.location));-1!==r&&(R[r]=n),E({action:t,location:o})}})},go:D,goBack:function(){return D(-1)},goForward:function(){return D(1)},block:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],n=I.setPrompt(e);return B||(F(1),B=!0),function(){return B&&(B=!1,F(-1)),n()}},listen:function(e){var n=I.appendListener(e);return F(1),function(){F(-1),n()}}};return W}},{"./DOMUtils":23,"./LocationUtils":24,"./PathUtils":25,"./createTransitionManager":29,invariant:33,warning:157}],28:[function(e,n,t){"use strict";t.__esModule=!0;var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},r=c(e("warning")),i=e("./PathUtils"),s=e("./LocationUtils"),l=c(e("./createTransitionManager"));function c(e){return e&&e.__esModule?e:{default:e}}var u=function(e,n,t){return Math.min(Math.max(e,n),t)};t.default=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.getUserConfirmation,t=e.initialEntries,c=void 0===t?["/"]:t,h=e.initialIndex,d=void 0===h?0:h,p=e.keyLength,f=void 0===p?6:p,m=(0,l.default)(),g=function(e){a(T,e),T.length=T.entries.length,m.notifyListeners(T.location,T.action)},y=function(){return Math.random().toString(36).substr(2,f)},b=u(d,0,c.length-1),w=c.map(function(e){return"string"==typeof e?(0,s.createLocation)(e,void 0,y()):(0,s.createLocation)(e,void 0,e.key||y())}),v=i.createPath,k=function(e){var t=u(T.index+e,0,T.entries.length-1),o=T.entries[t];m.confirmTransitionTo(o,"POP",n,function(e){e?g({action:"POP",location:o,index:t}):g()})},T={length:w.length,action:"POP",location:w[b],index:b,entries:w,createHref:v,push:function(e,t){(0,r.default)(!("object"===(void 0===e?"undefined":o(e))&&void 0!==e.state&&void 0!==t),"You should avoid providing a 2nd state argument to push when the 1st argument is a location-like object that already has state; it is ignored");var a=(0,s.createLocation)(e,t,y(),T.location);m.confirmTransitionTo(a,"PUSH",n,function(e){if(e){var n=T.index+1,t=T.entries.slice(0);t.length>n?t.splice(n,t.length-n,a):t.push(a),g({action:"PUSH",location:a,index:n,entries:t})}})},replace:function(e,t){(0,r.default)(!("object"===(void 0===e?"undefined":o(e))&&void 0!==e.state&&void 0!==t),"You should avoid providing a 2nd state argument to replace when the 1st argument is a location-like object that already has state; it is ignored");var a="REPLACE",i=(0,s.createLocation)(e,t,y(),T.location);m.confirmTransitionTo(i,a,n,function(e){e&&(T.entries[T.index]=i,g({action:a,location:i}))})},go:k,goBack:function(){return k(-1)},goForward:function(){return k(1)},canGo:function(e){var n=T.index+e;return n>=0&&n<T.entries.length},block:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];return m.setPrompt(e)},listen:function(e){return m.appendListener(e)}};return T}},{"./LocationUtils":24,"./PathUtils":25,"./createTransitionManager":29,warning:157}],29:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("warning"),r=(o=a)&&o.__esModule?o:{default:o};t.default=function(){var e=null,n=[];return{setPrompt:function(n){return(0,r.default)(null==e,"A history supports only one prompt at a time"),e=n,function(){e===n&&(e=null)}},confirmTransitionTo:function(n,t,o,a){if(null!=e){var i="function"==typeof e?e(n,t):e;"string"==typeof i?"function"==typeof o?o(i,a):((0,r.default)(!1,"A history needs a getUserConfirmation function in order to use a prompt message"),a(!0)):a(!1!==i)}else a(!0)},appendListener:function(e){var t=!0,o=function(){t&&e.apply(void 0,arguments)};return n.push(o),function(){t=!1,n=n.filter(function(e){return e!==o})}},notifyListeners:function(){for(var e=arguments.length,t=Array(e),o=0;o<e;o++)t[o]=arguments[o];n.forEach(function(e){return e.apply(void 0,t)})}}}},{warning:157}],30:[function(e,n,t){"use strict";t.__esModule=!0,t.createPath=t.parsePath=t.locationsAreEqual=t.createLocation=t.createMemoryHistory=t.createHashHistory=t.createBrowserHistory=void 0;var o=e("./LocationUtils");Object.defineProperty(t,"createLocation",{enumerable:!0,get:function(){return o.createLocation}}),Object.defineProperty(t,"locationsAreEqual",{enumerable:!0,get:function(){return o.locationsAreEqual}});var a=e("./PathUtils");Object.defineProperty(t,"parsePath",{enumerable:!0,get:function(){return a.parsePath}}),Object.defineProperty(t,"createPath",{enumerable:!0,get:function(){return a.createPath}});var r=l(e("./createBrowserHistory")),i=l(e("./createHashHistory")),s=l(e("./createMemoryHistory"));function l(e){return e&&e.__esModule?e:{default:e}}t.createBrowserHistory=r.default,t.createHashHistory=i.default,t.createMemoryHistory=s.default},{"./LocationUtils":24,"./PathUtils":25,"./createBrowserHistory":26,"./createHashHistory":27,"./createMemoryHistory":28}],31:[function(e,n,t){"use strict";var o={childContextTypes:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,mixins:!0,propTypes:!0,type:!0},a={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},r=Object.defineProperty,i=Object.getOwnPropertyNames,s=Object.getOwnPropertySymbols,l=Object.getOwnPropertyDescriptor,c=Object.getPrototypeOf,u=c&&c(Object);n.exports=function e(n,t,h){if("string"!=typeof t){if(u){var d=c(t);d&&d!==u&&e(n,d,h)}var p=i(t);s&&(p=p.concat(s(t)));for(var f=0;f<p.length;++f){var m=p[f];if(!(o[m]||a[m]||h&&h[m])){var g=l(t,m);try{r(n,m,g)}catch(e){}}}return n}return n}},{}],32:[function(e,n,t){"function"==typeof Object.create?n.exports=function(e,n){e.super_=n,e.prototype=Object.create(n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:n.exports=function(e,n){e.super_=n;var t=function(){};t.prototype=n.prototype,e.prototype=new t,e.prototype.constructor=e}},{}],33:[function(e,n,t){(function(e){"use strict";n.exports=function(n,t,o,a,r,i,s,l){if("production"!==e.env.NODE_ENV&&void 0===t)throw new Error("invariant requires an error message argument");if(!n){var c;if(void 0===t)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var u=[o,a,r,i,s,l],h=0;(c=new Error(t.replace(/%s/g,function(){return u[h++]}))).name="Invariant Violation"}throw c.framesToPop=1,c}}}).call(this,e("_process"))},{_process:48}],34:[function(e,n,t){"use strict";n.exports=function(e){var n="string"==typeof e?e.charCodeAt(0):e;return n>=97&&n<=122||n>=65&&n<=90}},{}],35:[function(e,n,t){"use strict";var o=e("is-alphabetical"),a=e("is-decimal");n.exports=function(e){return o(e)||a(e)}},{"is-alphabetical":34,"is-decimal":37}],36:[function(e,n,t){function o(e){return!!e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}n.exports=function(e){return null!=e&&(o(e)||"function"==typeof(n=e).readFloatLE&&"function"==typeof n.slice&&o(n.slice(0,0))||!!e._isBuffer);var n}},{}],37:[function(e,n,t){"use strict";n.exports=function(e){var n="string"==typeof e?e.charCodeAt(0):e;return n>=48&&n<=57}},{}],38:[function(e,n,t){"use strict";n.exports=function(e){var n="string"==typeof e?e.charCodeAt(0):e;return n>=97&&n<=102||n>=65&&n<=70||n>=48&&n<=57}},{}],39:[function(e,n,t){"use strict";var o=Object.prototype.toString;n.exports=function(e){var n;return"[object Object]"===o.call(e)&&(null===(n=Object.getPrototypeOf(e))||n===Object.getPrototypeOf({}))}},{}],40:[function(e,n,t){"use strict";n.exports=function(e){return a.test("number"==typeof e?o(e):e.charAt(0))};var o=String.fromCharCode,a=/\s/},{}],41:[function(e,n,t){"use strict";n.exports=function(e){return a.test("number"==typeof e?o(e):e.charAt(0))};var o=String.fromCharCode,a=/\w/},{}],42:[function(e,n,t){"use strict";n.exports=i;var o=["\\","`","*","{","}","[","]","(",")","#","+","-",".","!","_",">"],a=o.concat(["~","|"]),r=a.concat(["\n",'"',"$","%","&","'",",","/",":",";","<","=","?","@","^"]);function i(e){var n=e||{};return n.commonmark?r:n.gfm?a:o}i.default=o,i.gfm=a,i.commonmark=r},{}],43:[function(e,n,t){"use strict";var o=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;n.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var n={},t=0;t<10;t++)n["_"+String.fromCharCode(t)]=t;if("0123456789"!==Object.getOwnPropertyNames(n).map(function(e){return n[e]}).join(""))return!1;var o={};return"abcdefghijklmnopqrst".split("").forEach(function(e){o[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},o)).join("")}catch(e){return!1}}()?Object.assign:function(e,n){for(var t,i,s=function(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),l=1;l<arguments.length;l++){for(var c in t=Object(arguments[l]))a.call(t,c)&&(s[c]=t[c]);if(o){i=o(t);for(var u=0;u<i.length;u++)r.call(t,i[u])&&(s[i[u]]=t[i[u]])}}return s}},{}],44:[function(e,n,t){"use strict";var o=e("character-entities"),a=e("character-entities-legacy"),r=e("character-reference-invalid"),i=e("is-decimal"),s=e("is-hexadecimal"),l=e("is-alphanumerical");n.exports=function(e,n){var t,i,s={};n||(n={});for(i in x)t=n[i],s[i]=null===t||void 0===t?x[i]:t;(s.position.indent||s.position.start)&&(s.indent=s.position.indent||[],s.position=s.position.start);return function(e,n){var t,i,s,x,W,j,_,U,V,H,q,K,Y,X,Q,$,J,Z,ee=n.additional,ne=n.nonTerminated,te=n.text,oe=n.reference,ae=n.warning,re=n.textContext,ie=n.referenceContext,se=n.warningContext,le=n.position,ce=n.indent||[],ue=e.length,he=0,de=-1,pe=le.column||1,fe=le.line||1,me=I,ge=[];Q=be(),_=ae?function(e,n){var t=be();t.column+=n,t.offset+=n,ae.call(se,z[e],t,e)}:h,he--,ue++;for(;++he<ue;)if(x===y&&(pe=ce[de]||1),(x=we(he))!==f)x===y&&(fe++,de++,pe=0),x?(me+=x,pe++):ve();else{if((j=we(he+1))===E||j===y||j===p||j===v||j===k||j===f||j===I||ee&&j===ee){me+=x,pe++;continue}for(q=K=he+1,Z=K,j!==m?Y=S:(Z=++q,(j=we(Z))===b||j===w?(Y=L,Z=++q):Y=N),t=I,H=I,s=I,X=A[Y],Z--;++Z<ue&&(j=we(Z),X(j));)s+=j,Y===S&&c.call(a,s)&&(t=s,H=a[s]);(i=we(Z)===g)&&(Z++,Y===S&&c.call(o,s)&&(t=s,H=o[s])),J=1+Z-K,(i||ne)&&(s?Y===S?(i&&!H?_(M,1):(t!==s&&(Z=q+t.length,J=1+Z-q,i=!1),i||(U=t?P:R,n.attribute?(j=we(Z))===T?(_(U,J),H=null):l(j)?H=null:_(U,J):_(U,J))),W=H):(i||_(O,J),W=parseInt(s,C[Y]),(ye=W)>=55296&&ye<=57343||ye>1114111?(_(B,J),W=d):W in r?(_(F,J),W=r[W]):(V=I,G(W)&&_(F,J),W>65535&&(V+=u((W-=65536)>>>10|55296),W=56320|1023&W),W=V+u(W))):Y!==S&&_(D,J)),W?(ve(),Q=be(),he=Z-1,pe+=Z-K+1,ge.push(W),($=be()).offset++,oe&&oe.call(ie,W,{start:Q,end:$},e.slice(K-1,Z)),Q=$):(s=e.slice(K-1,Z),me+=s,pe+=s.length,he=Z-1)}var ye;return ge.join(I);function be(){return{line:fe,column:pe,offset:he+(le.offset||0)}}function we(n){return e.charAt(n)}function ve(){me&&(ge.push(me),te&&te.call(re,me,{start:Q,end:be()}),me=I)}}(e,s)};var c={}.hasOwnProperty,u=String.fromCharCode,h=Function.prototype,d="�",p="\f",f="&",m="#",g=";",y="\n",b="x",w="X",v=" ",k="<",T="=",I="",E="\t",x={warning:null,reference:null,text:null,warningContext:null,referenceContext:null,textContext:null,position:{},additional:null,attribute:!1,nonTerminated:!0},S="named",L="hexadecimal",N="decimal",C={};C[L]=16,C[N]=10;var A={};A[S]=l,A[N]=i,A[L]=s;var P=1,O=2,R=3,D=4,M=5,F=6,B=7,W="Numeric character references",j="Named character references",_=" must be terminated by a semicolon",U=" cannot be empty",z={};function G(e){return e>=1&&e<=8||11===e||e>=13&&e<=31||e>=127&&e<=159||e>=64976&&e<=65007||65535==(65535&e)||65534==(65535&e)}z[P]=j+_,z[O]=W+_,z[R]=j+U,z[D]=W+U,z[M]=j+" must be known",z[F]=W+" cannot be disallowed",z[B]=W+" cannot be outside the permissible Unicode range"},{"character-entities":3,"character-entities-legacy":2,"character-reference-invalid":4,"is-alphanumerical":35,"is-decimal":37,"is-hexadecimal":38}],45:[function(e,n,t){(function(e){function n(e,n){for(var t=0,o=e.length-1;o>=0;o--){var a=e[o];"."===a?e.splice(o,1):".."===a?(e.splice(o,1),t++):t&&(e.splice(o,1),t--)}if(n)for(;t--;t)e.unshift("..");return e}var o=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/,a=function(e){return o.exec(e).slice(1)};function r(e,n){if(e.filter)return e.filter(n);for(var t=[],o=0;o<e.length;o++)n(e[o],o,e)&&t.push(e[o]);return t}t.resolve=function(){for(var t="",o=!1,a=arguments.length-1;a>=-1&&!o;a--){var i=a>=0?arguments[a]:e.cwd();if("string"!=typeof i)throw new TypeError("Arguments to path.resolve must be strings");i&&(t=i+"/"+t,o="/"===i.charAt(0))}return t=n(r(t.split("/"),function(e){return!!e}),!o).join("/"),(o?"/":"")+t||"."},t.normalize=function(e){var o=t.isAbsolute(e),a="/"===i(e,-1);return(e=n(r(e.split("/"),function(e){return!!e}),!o).join("/"))||o||(e="."),e&&a&&(e+="/"),(o?"/":"")+e},t.isAbsolute=function(e){return"/"===e.charAt(0)},t.join=function(){var e=Array.prototype.slice.call(arguments,0);return t.normalize(r(e,function(e,n){if("string"!=typeof e)throw new TypeError("Arguments to path.join must be strings");return e}).join("/"))},t.relative=function(e,n){function o(e){for(var n=0;n<e.length&&""===e[n];n++);for(var t=e.length-1;t>=0&&""===e[t];t--);return n>t?[]:e.slice(n,t-n+1)}e=t.resolve(e).substr(1),n=t.resolve(n).substr(1);for(var a=o(e.split("/")),r=o(n.split("/")),i=Math.min(a.length,r.length),s=i,l=0;l<i;l++)if(a[l]!==r[l]){s=l;break}var c=[];for(l=s;l<a.length;l++)c.push("..");return(c=c.concat(r.slice(s))).join("/")},t.sep="/",t.delimiter=":",t.dirname=function(e){var n=a(e),t=n[0],o=n[1];return t||o?(o&&(o=o.substr(0,o.length-1)),t+o):"."},t.basename=function(e,n){var t=a(e)[2];return n&&t.substr(-1*n.length)===n&&(t=t.substr(0,t.length-n.length)),t},t.extname=function(e){return a(e)[3]};var i="b"==="ab".substr(-1)?function(e,n,t){return e.substr(n,t)}:function(e,n,t){return n<0&&(n=e.length+n),e.substr(n,t)}}).call(this,e("_process"))},{_process:48}],46:[function(e,n,t){var o=e("isarray");n.exports=d,n.exports.parse=r,n.exports.compile=function(e,n){return s(r(e,n))},n.exports.tokensToFunction=s,n.exports.tokensToRegExp=h;var a=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function r(e,n){for(var t,o,r=[],i=0,s=0,c="",u=n&&n.delimiter||"/";null!=(t=a.exec(e));){var h=t[0],d=t[1],p=t.index;if(c+=e.slice(s,p),s=p+h.length,d)c+=d[1];else{var f=e[s],m=t[2],g=t[3],y=t[4],b=t[5],w=t[6],v=t[7];c&&(r.push(c),c="");var k=null!=m&&null!=f&&f!==m,T="+"===w||"*"===w,I="?"===w||"*"===w,E=t[2]||u,x=y||b;r.push({name:g||i++,prefix:m||"",delimiter:E,optional:I,repeat:T,partial:k,asterisk:!!v,pattern:x?(o=x,o.replace(/([=!:$\/()])/g,"\\$1")):v?".*":"[^"+l(E)+"]+?"})}}return s<e.length&&(c+=e.substr(s)),c&&r.push(c),r}function i(e){return encodeURI(e).replace(/[\/?#]/g,function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()})}function s(e){for(var n=new Array(e.length),t=0;t<e.length;t++)"object"==typeof e[t]&&(n[t]=new RegExp("^(?:"+e[t].pattern+")$"));return function(t,a){for(var r="",s=t||{},l=(a||{}).pretty?i:encodeURIComponent,c=0;c<e.length;c++){var u=e[c];if("string"!=typeof u){var h,d=s[u.name];if(null==d){if(u.optional){u.partial&&(r+=u.prefix);continue}throw new TypeError('Expected "'+u.name+'" to be defined')}if(o(d)){if(!u.repeat)throw new TypeError('Expected "'+u.name+'" to not repeat, but received `'+JSON.stringify(d)+"`");if(0===d.length){if(u.optional)continue;throw new TypeError('Expected "'+u.name+'" to not be empty')}for(var p=0;p<d.length;p++){if(h=l(d[p]),!n[c].test(h))throw new TypeError('Expected all "'+u.name+'" to match "'+u.pattern+'", but received `'+JSON.stringify(h)+"`");r+=(0===p?u.prefix:u.delimiter)+h}}else{if(h=u.asterisk?encodeURI(d).replace(/[?#]/g,function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()}):l(d),!n[c].test(h))throw new TypeError('Expected "'+u.name+'" to match "'+u.pattern+'", but received "'+h+'"');r+=u.prefix+h}}else r+=u}return r}}function l(e){return e.replace(/([.+*?=^!:${}()[\]|\/\\])/g,"\\$1")}function c(e,n){return e.keys=n,e}function u(e){return e.sensitive?"":"i"}function h(e,n,t){o(n)||(t=n||t,n=[]);for(var a=(t=t||{}).strict,r=!1!==t.end,i="",s=0;s<e.length;s++){var h=e[s];if("string"==typeof h)i+=l(h);else{var d=l(h.prefix),p="(?:"+h.pattern+")";n.push(h),h.repeat&&(p+="(?:"+d+p+")*"),i+=p=h.optional?h.partial?d+"("+p+")?":"(?:"+d+"("+p+"))?":d+"("+p+")"}}var f=l(t.delimiter||"/"),m=i.slice(-f.length)===f;return a||(i=(m?i.slice(0,-f.length):i)+"(?:"+f+"(?=$))?"),i+=r?"$":a&&m?"":"(?="+f+"|$)",c(new RegExp("^"+i,u(t)),n)}function d(e,n,t){return o(n)||(t=n||t,n=[]),t=t||{},e instanceof RegExp?function(e,n){var t=e.source.match(/\((?!\?)/g);if(t)for(var o=0;o<t.length;o++)n.push({name:o,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,asterisk:!1,pattern:null});return c(e,n)}(e,n):o(e)?function(e,n,t){for(var o=[],a=0;a<e.length;a++)o.push(d(e[a],n,t).source);return c(new RegExp("(?:"+o.join("|")+")",u(t)),n)}(e,n,t):(a=n,h(r(e,i=t),a,i));var a,i}},{isarray:47}],47:[function(e,n,t){n.exports=Array.isArray||function(e){return"[object Array]"==Object.prototype.toString.call(e)}},{}],48:[function(e,n,t){var o,a,r=n.exports={};function i(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function l(e){if(o===setTimeout)return setTimeout(e,0);if((o===i||!o)&&setTimeout)return o=setTimeout,setTimeout(e,0);try{return o(e,0)}catch(n){try{return o.call(null,e,0)}catch(n){return o.call(this,e,0)}}}!function(){try{o="function"==typeof setTimeout?setTimeout:i}catch(e){o=i}try{a="function"==typeof clearTimeout?clearTimeout:s}catch(e){a=s}}();var c,u=[],h=!1,d=-1;function p(){h&&c&&(h=!1,c.length?u=c.concat(u):d=-1,u.length&&f())}function f(){if(!h){var e=l(p);h=!0;for(var n=u.length;n;){for(c=u,u=[];++d<n;)c&&c[d].run();d=-1,n=u.length}c=null,h=!1,function(e){if(a===clearTimeout)return clearTimeout(e);if((a===s||!a)&&clearTimeout)return a=clearTimeout,clearTimeout(e);try{a(e)}catch(n){try{return a.call(null,e)}catch(n){return a.call(this,e)}}}(e)}}function m(e,n){this.fun=e,this.array=n}function g(){}r.nextTick=function(e){var n=new Array(arguments.length-1);if(arguments.length>1)for(var t=1;t<arguments.length;t++)n[t-1]=arguments[t];u.push(new m(e,n)),1!==u.length||h||l(f)},m.prototype.run=function(){this.fun.apply(null,this.array)},r.title="browser",r.browser=!0,r.env={},r.argv=[],r.version="",r.versions={},r.on=g,r.addListener=g,r.once=g,r.off=g,r.removeListener=g,r.removeAllListeners=g,r.emit=g,r.prependListener=g,r.prependOnceListener=g,r.listeners=function(e){return[]},r.binding=function(e){throw new Error("process.binding is not supported")},r.cwd=function(){return"/"},r.chdir=function(e){throw new Error("process.chdir is not supported")},r.umask=function(){return 0}},{}],49:[function(e,n,t){(function(t){"use strict";if("production"!==t.env.NODE_ENV)var o=e("fbjs/lib/invariant"),a=e("fbjs/lib/warning"),r=e("./lib/ReactPropTypesSecret"),i={};n.exports=function(e,n,s,l,c){if("production"!==t.env.NODE_ENV)for(var u in e)if(e.hasOwnProperty(u)){var h;try{o("function"==typeof e[u],"%s: %s type `%s` is invalid; it must be a function, usually from the `prop-types` package, but received `%s`.",l||"React class",s,u,typeof e[u]),h=e[u](n,u,l,s,null,r)}catch(e){h=e}if(a(!h||h instanceof Error,"%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",l||"React class",s,u,typeof h),h instanceof Error&&!(h.message in i)){i[h.message]=!0;var d=c?c():"";a(!1,"Failed %s type: %s%s",s,h.message,null!=d?d:"")}}}}).call(this,e("_process"))},{"./lib/ReactPropTypesSecret":53,_process:48,"fbjs/lib/invariant":18,"fbjs/lib/warning":22}],50:[function(e,n,t){"use strict";var o=e("fbjs/lib/emptyFunction"),a=e("fbjs/lib/invariant"),r=e("./lib/ReactPropTypesSecret");n.exports=function(){function e(e,n,t,o,i,s){s!==r&&a(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")}function n(){return e}e.isRequired=e;var t={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:n,element:e,instanceOf:n,node:e,objectOf:n,oneOf:n,oneOfType:n,shape:n,exact:n};return t.checkPropTypes=o,t.PropTypes=t,t}},{"./lib/ReactPropTypesSecret":53,"fbjs/lib/emptyFunction":12,"fbjs/lib/invariant":18}],51:[function(e,n,t){(function(t){"use strict";var o=e("fbjs/lib/emptyFunction"),a=e("fbjs/lib/invariant"),r=e("fbjs/lib/warning"),i=e("object-assign"),s=e("./lib/ReactPropTypesSecret"),l=e("./checkPropTypes");n.exports=function(e,n){var c="function"==typeof Symbol&&Symbol.iterator,u="@@iterator";var h="<<anonymous>>",d={array:g("array"),bool:g("boolean"),func:g("function"),number:g("number"),object:g("object"),string:g("string"),symbol:g("symbol"),any:m(o.thatReturnsNull),arrayOf:function(e){return m(function(n,t,o,a,r){if("function"!=typeof e)return new f("Property `"+r+"` of component `"+o+"` has invalid PropType notation inside arrayOf.");var i=n[t];if(!Array.isArray(i)){var l=b(i);return new f("Invalid "+a+" `"+r+"` of type `"+l+"` supplied to `"+o+"`, expected an array.")}for(var c=0;c<i.length;c++){var u=e(i,c,o,a,r+"["+c+"]",s);if(u instanceof Error)return u}return null})},element:function(){return m(function(n,t,o,a,r){var i=n[t];if(!e(i)){var s=b(i);return new f("Invalid "+a+" `"+r+"` of type `"+s+"` supplied to `"+o+"`, expected a single ReactElement.")}return null})}(),instanceOf:function(e){return m(function(n,t,o,a,r){if(!(n[t]instanceof e)){var i=e.name||h,s=function(e){if(!e.constructor||!e.constructor.name)return h;return e.constructor.name}(n[t]);return new f("Invalid "+a+" `"+r+"` of type `"+s+"` supplied to `"+o+"`, expected instance of `"+i+"`.")}return null})},node:function(){return m(function(e,n,t,o,a){if(!y(e[n]))return new f("Invalid "+o+" `"+a+"` supplied to `"+t+"`, expected a ReactNode.");return null})}(),objectOf:function(e){return m(function(n,t,o,a,r){if("function"!=typeof e)return new f("Property `"+r+"` of component `"+o+"` has invalid PropType notation inside objectOf.");var i=n[t],l=b(i);if("object"!==l)return new f("Invalid "+a+" `"+r+"` of type `"+l+"` supplied to `"+o+"`, expected an object.");for(var c in i)if(i.hasOwnProperty(c)){var u=e(i,c,o,a,r+"."+c,s);if(u instanceof Error)return u}return null})},oneOf:function(e){if(!Array.isArray(e))return"production"!==t.env.NODE_ENV&&r(!1,"Invalid argument supplied to oneOf, expected an instance of array."),o.thatReturnsNull;return m(function(n,t,o,a,r){for(var i=n[t],s=0;s<e.length;s++)if(p(i,e[s]))return null;var l=JSON.stringify(e);return new f("Invalid "+a+" `"+r+"` of value `"+i+"` supplied to `"+o+"`, expected one of "+l+".")})},oneOfType:function(e){if(!Array.isArray(e))return"production"!==t.env.NODE_ENV&&r(!1,"Invalid argument supplied to oneOfType, expected an instance of array."),o.thatReturnsNull;for(var n=0;n<e.length;n++){var a=e[n];if("function"!=typeof a)return r(!1,"Invalid argument supplied to oneOfType. Expected an array of check functions, but received %s at index %s.",v(a),n),o.thatReturnsNull}return m(function(n,t,o,a,r){for(var i=0;i<e.length;i++){var l=e[i];if(null==l(n,t,o,a,r,s))return null}return new f("Invalid "+a+" `"+r+"` supplied to `"+o+"`.")})},shape:function(e){return m(function(n,t,o,a,r){var i=n[t],l=b(i);if("object"!==l)return new f("Invalid "+a+" `"+r+"` of type `"+l+"` supplied to `"+o+"`, expected `object`.");for(var c in e){var u=e[c];if(u){var h=u(i,c,o,a,r+"."+c,s);if(h)return h}}return null})},exact:function(e){return m(function(n,t,o,a,r){var l=n[t],c=b(l);if("object"!==c)return new f("Invalid "+a+" `"+r+"` of type `"+c+"` supplied to `"+o+"`, expected `object`.");var u=i({},n[t],e);for(var h in u){var d=e[h];if(!d)return new f("Invalid "+a+" `"+r+"` key `"+h+"` supplied to `"+o+"`.\nBad object: "+JSON.stringify(n[t],null," ")+"\nValid keys: "+JSON.stringify(Object.keys(e),null," "));var p=d(l,h,o,a,r+"."+h,s);if(p)return p}return null})}};function p(e,n){return e===n?0!==e||1/e==1/n:e!=e&&n!=n}function f(e){this.message=e,this.stack=""}function m(e){if("production"!==t.env.NODE_ENV)var o={},i=0;function l(l,c,u,d,p,m,g){if(d=d||h,m=m||u,g!==s)if(n)a(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types");else if("production"!==t.env.NODE_ENV&&"undefined"!=typeof console){var y=d+":"+u;!o[y]&&i<3&&(r(!1,"You are manually calling a React.PropTypes validation function for the `%s` prop on `%s`. This is deprecated and will throw in the standalone `prop-types` package. You may be seeing this warning due to a third-party PropTypes library. See https://fb.me/react-warning-dont-call-proptypes for details.",m,d),o[y]=!0,i++)}return null==c[u]?l?null===c[u]?new f("The "+p+" `"+m+"` is marked as required in `"+d+"`, but its value is `null`."):new f("The "+p+" `"+m+"` is marked as required in `"+d+"`, but its value is `undefined`."):null:e(c,u,d,p,m)}var c=l.bind(null,!1);return c.isRequired=l.bind(null,!0),c}function g(e){return m(function(n,t,o,a,r,i){var s=n[t];return b(s)!==e?new f("Invalid "+a+" `"+r+"` of type `"+w(s)+"` supplied to `"+o+"`, expected `"+e+"`."):null})}function y(n){switch(typeof n){case"number":case"string":case"undefined":return!0;case"boolean":return!n;case"object":if(Array.isArray(n))return n.every(y);if(null===n||e(n))return!0;var t=function(e){var n=e&&(c&&e[c]||e[u]);if("function"==typeof n)return n}(n);if(!t)return!1;var o,a=t.call(n);if(t!==n.entries){for(;!(o=a.next()).done;)if(!y(o.value))return!1}else for(;!(o=a.next()).done;){var r=o.value;if(r&&!y(r[1]))return!1}return!0;default:return!1}}function b(e){var n,t=typeof e;return Array.isArray(e)?"array":e instanceof RegExp?"object":(n=e,"symbol"===t||"Symbol"===n["@@toStringTag"]||"function"==typeof Symbol&&n instanceof Symbol?"symbol":t)}function w(e){if(void 0===e||null===e)return""+e;var n=b(e);if("object"===n){if(e instanceof Date)return"date";if(e instanceof RegExp)return"regexp"}return n}function v(e){var n=w(e);switch(n){case"array":case"object":return"an "+n;case"boolean":case"date":case"regexp":return"a "+n;default:return n}}return f.prototype=Error.prototype,d.checkPropTypes=l,d.PropTypes=d,d}}).call(this,e("_process"))},{"./checkPropTypes":49,"./lib/ReactPropTypesSecret":53,_process:48,"fbjs/lib/emptyFunction":12,"fbjs/lib/invariant":18,"fbjs/lib/warning":22,"object-assign":43}],52:[function(e,n,t){(function(t){if("production"!==t.env.NODE_ENV){var o="function"==typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103;n.exports=e("./factoryWithTypeCheckers")(function(e){return"object"==typeof e&&null!==e&&e.$$typeof===o},!0)}else n.exports=e("./factoryWithThrowingShims")()}).call(this,e("_process"))},{"./factoryWithThrowingShims":50,"./factoryWithTypeCheckers":51,_process:48}],53:[function(e,n,t){"use strict";n.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},{}],54:[function(e,n,t){(function(t){"use strict";"production"!==t.env.NODE_ENV&&function(){var t=e("react"),o=e("fbjs/lib/invariant"),a=e("fbjs/lib/warning"),r=e("fbjs/lib/ExecutionEnvironment"),i=e("object-assign"),s=e("fbjs/lib/emptyFunction"),l=e("fbjs/lib/EventListener"),c=e("fbjs/lib/getActiveElement"),u=e("fbjs/lib/shallowEqual"),h=e("fbjs/lib/containsNode"),d=e("fbjs/lib/focusNode"),p=e("fbjs/lib/emptyObject"),f=e("prop-types/checkPropTypes"),m=e("fbjs/lib/hyphenateStyleName"),g=e("fbjs/lib/camelizeStyleName");t||o(!1,"ReactDOM was loaded before React. Make sure you load the React package before loading ReactDOM.");var y={children:!0,dangerouslySetInnerHTML:!0,defaultValue:!0,defaultChecked:!0,innerHTML:!0,suppressContentEditableWarning:!0,suppressHydrationWarning:!0,style:!0};function b(e,n){return(e&n)===n}var w={MUST_USE_PROPERTY:1,HAS_BOOLEAN_VALUE:4,HAS_NUMERIC_VALUE:8,HAS_POSITIVE_NUMERIC_VALUE:24,HAS_OVERLOADED_BOOLEAN_VALUE:32,HAS_STRING_BOOLEAN_VALUE:64,injectDOMPropertyConfig:function(e){var n=w,t=e.Properties||{},a=e.DOMAttributeNamespaces||{},r=e.DOMAttributeNames||{},i=e.DOMMutationMethods||{};for(var s in t){I.hasOwnProperty(s)&&o(!1,"injectDOMPropertyConfig(...): You're trying to inject DOM property '%s' which has already been injected. You may be accidentally injecting the same DOM property config twice, or you may be injecting two configs that have conflicting property names.",s);var l=s.toLowerCase(),c=t[s],u={attributeName:l,attributeNamespace:null,propertyName:s,mutationMethod:null,mustUseProperty:b(c,n.MUST_USE_PROPERTY),hasBooleanValue:b(c,n.HAS_BOOLEAN_VALUE),hasNumericValue:b(c,n.HAS_NUMERIC_VALUE),hasPositiveNumericValue:b(c,n.HAS_POSITIVE_NUMERIC_VALUE),hasOverloadedBooleanValue:b(c,n.HAS_OVERLOADED_BOOLEAN_VALUE),hasStringBooleanValue:b(c,n.HAS_STRING_BOOLEAN_VALUE)};if(u.hasBooleanValue+u.hasNumericValue+u.hasOverloadedBooleanValue<=1||o(!1,"DOMProperty: Value can be one of boolean, overloaded boolean, or numeric value, but not a combination: %s",s),r.hasOwnProperty(s)){var h=r[s];u.attributeName=h}a.hasOwnProperty(s)&&(u.attributeNamespace=a[s]),i.hasOwnProperty(s)&&(u.mutationMethod=i[s]),I[s]=u}}},v=":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD",k=v+"\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040",T="data-reactroot",I={};function E(e,n){if(L(e))return!1;if(e.length>2&&("o"===e[0]||"O"===e[0])&&("n"===e[1]||"N"===e[1]))return!1;if(null===n)return!0;switch(typeof n){case"boolean":return S(e);case"undefined":case"number":case"string":case"object":return!0;default:return!1}}function x(e){return I.hasOwnProperty(e)?I[e]:null}function S(e){if(L(e))return!0;var n=x(e);if(n)return n.hasBooleanValue||n.hasStringBooleanValue||n.hasOverloadedBooleanValue;var t=e.toLowerCase().slice(0,5);return"data-"===t||"aria-"===t}function L(e){return y.hasOwnProperty(e)}var N=w,C=N.MUST_USE_PROPERTY,A=N.HAS_BOOLEAN_VALUE,P=N.HAS_NUMERIC_VALUE,O=N.HAS_POSITIVE_NUMERIC_VALUE,R=N.HAS_OVERLOADED_BOOLEAN_VALUE,D=N.HAS_STRING_BOOLEAN_VALUE,M={Properties:{allowFullScreen:A,async:A,autoFocus:A,autoPlay:A,capture:R,checked:C|A,cols:O,contentEditable:D,controls:A,default:A,defer:A,disabled:A,download:R,draggable:D,formNoValidate:A,hidden:A,loop:A,multiple:C|A,muted:C|A,noValidate:A,open:A,playsInline:A,readOnly:A,required:A,reversed:A,rows:O,rowSpan:P,scoped:A,seamless:A,selected:C|A,size:O,start:P,span:O,spellCheck:D,style:0,tabIndex:0,itemScope:A,acceptCharset:0,className:0,htmlFor:0,httpEquiv:0,value:D},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMMutationMethods:{value:function(e,n){if(null==n)return e.removeAttribute("value");"number"!==e.type||!1===e.hasAttribute("value")?e.setAttribute("value",""+n):e.validity&&!e.validity.badInput&&e.ownerDocument.activeElement!==e&&e.setAttribute("value",""+n)}}},F=N.HAS_STRING_BOOLEAN_VALUE,B="http://www.w3.org/1999/xlink",W="http://www.w3.org/XML/1998/namespace",j={Properties:{autoReverse:F,externalResourcesRequired:F,preserveAlpha:F},DOMAttributeNames:{autoReverse:"autoReverse",externalResourcesRequired:"externalResourcesRequired",preserveAlpha:"preserveAlpha"},DOMAttributeNamespaces:{xlinkActuate:B,xlinkArcrole:B,xlinkHref:B,xlinkRole:B,xlinkShow:B,xlinkTitle:B,xlinkType:B,xmlBase:W,xmlLang:W,xmlSpace:W}},_=/[\-\:]([a-z])/g,U=function(e){return e[1].toUpperCase()};["accent-height","alignment-baseline","arabic-form","baseline-shift","cap-height","clip-path","clip-rule","color-interpolation","color-interpolation-filters","color-profile","color-rendering","dominant-baseline","enable-background","fill-opacity","fill-rule","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","glyph-name","glyph-orientation-horizontal","glyph-orientation-vertical","horiz-adv-x","horiz-origin-x","image-rendering","letter-spacing","lighting-color","marker-end","marker-mid","marker-start","overline-position","overline-thickness","paint-order","panose-1","pointer-events","rendering-intent","shape-rendering","stop-color","stop-opacity","strikethrough-position","strikethrough-thickness","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-anchor","text-decoration","text-rendering","underline-position","underline-thickness","unicode-bidi","unicode-range","units-per-em","v-alphabetic","v-hanging","v-ideographic","v-mathematical","vector-effect","vert-adv-y","vert-origin-x","vert-origin-y","word-spacing","writing-mode","x-height","xlink:actuate","xlink:arcrole","xlink:href","xlink:role","xlink:show","xlink:title","xlink:type","xml:base","xmlns:xlink","xml:lang","xml:space"].forEach(function(e){var n=e.replace(_,U);j.Properties[n]=0,j.DOMAttributeNames[n]=e}),N.injectDOMPropertyConfig(M),N.injectDOMPropertyConfig(j);var z={_caughtError:null,_hasCaughtError:!1,_rethrowError:null,_hasRethrowError:!1,injection:{injectErrorUtils:function(e){"function"!=typeof e.invokeGuardedCallback&&o(!1,"Injected invokeGuardedCallback() must be a function."),G=e.invokeGuardedCallback}},invokeGuardedCallback:function(e,n,t,o,a,r,i,s,l){G.apply(z,arguments)},invokeGuardedCallbackAndCatchFirstError:function(e,n,t,o,a,r,i,s,l){if(z.invokeGuardedCallback.apply(this,arguments),z.hasCaughtError()){var c=z.clearCaughtError();z._hasRethrowError||(z._hasRethrowError=!0,z._rethrowError=c)}},rethrowCaughtError:function(){return H.apply(z,arguments)},hasCaughtError:function(){return z._hasCaughtError},clearCaughtError:function(){if(z._hasCaughtError){var e=z._caughtError;return z._caughtError=null,z._hasCaughtError=!1,e}o(!1,"clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue.")}},G=function(e,n,t,o,a,r,i,s,l){z._hasCaughtError=!1,z._caughtError=null;var c=Array.prototype.slice.call(arguments,3);try{n.apply(t,c)}catch(e){z._caughtError=e,z._hasCaughtError=!0}};if("undefined"!=typeof window&&"function"==typeof window.dispatchEvent&&"undefined"!=typeof document&&"function"==typeof document.createEvent){var V=document.createElement("react");G=function(e,n,t,o,a,r,i,s,l){var c=!0,u=Array.prototype.slice.call(arguments,3);var h=void 0,d=!1,p=!1;function f(e){h=e.error,d=!0,null===h&&0===e.colno&&0===e.lineno&&(p=!0)}var m="react-"+(e||"invokeguardedcallback");window.addEventListener("error",f),V.addEventListener(m,function e(){V.removeEventListener(m,e,!1),n.apply(t,u),c=!1},!1);var g=document.createEvent("Event");g.initEvent(m,!1,!1),V.dispatchEvent(g),c?(d?p&&(h=new Error("A cross-origin error was thrown. React doesn't have access to the actual error object in development. See https://fb.me/react-crossorigin-error for more information.")):h=new Error("An error was thrown inside one of your components, but React doesn't know what it was. This is likely due to browser flakiness. React does its best to preserve the \"Pause on exceptions\" behavior of the DevTools, which requires some DEV-mode only tricks. It's possible that these don't work in your browser. Try triggering the error in production mode, or switching to a modern browser. If you suspect that this is actually an issue with React, please file an issue."),z._hasCaughtError=!0,z._caughtError=h):(z._hasCaughtError=!1,z._caughtError=null),window.removeEventListener("error",f)}}var H=function(){if(z._hasRethrowError){var e=z._rethrowError;throw z._rethrowError=null,z._hasRethrowError=!1,e}},q=null,K={};function Y(){if(q)for(var e in K){var n=K[e],t=q.indexOf(e);if(t>-1||o(!1,"EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `%s`.",e),!$[t]){n.extractEvents||o(!1,"EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `%s` does not.",e),$[t]=n;var a=n.eventTypes;for(var r in a)X(a[r],n,r)||o(!1,"EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.",r,e)}}}function X(e,n,t){J.hasOwnProperty(t)&&o(!1,"EventPluginHub: More than one plugin attempted to publish the same event name, `%s`.",t),J[t]=e;var a=e.phasedRegistrationNames;if(a){for(var r in a){if(a.hasOwnProperty(r))Q(a[r],n,t)}return!0}return!!e.registrationName&&(Q(e.registrationName,n,t),!0)}function Q(e,n,t){Z[e]&&o(!1,"EventPluginHub: More than one plugin attempted to publish the same registration name, `%s`.",e),Z[e]=n,ee[e]=n.eventTypes[t].dependencies;var a=e.toLowerCase();ne[a]=e,"onDoubleClick"===e&&(ne.ondblclick=e)}var $=[],J={},Z={},ee={},ne={};function te(e){q&&o(!1,"EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React."),q=Array.prototype.slice.call(e),Y()}function oe(e){var n=!1;for(var t in e)if(e.hasOwnProperty(t)){var a=e[t];K.hasOwnProperty(t)&&K[t]===a||(K[t]&&o(!1,"EventPluginRegistry: Cannot inject two different event plugins using the same name, `%s`.",t),K[t]=a,n=!0)}n&&Y()}var ae,re=Object.freeze({plugins:$,eventNameDispatchConfigs:J,registrationNameModules:Z,registrationNameDependencies:ee,possibleRegistrationNames:ne,injectEventPluginOrder:te,injectEventPluginsByName:oe}),ie=null,se=null,le=null,ce=function(e){ie=e.getFiberCurrentPropsFromNode,se=e.getInstanceFromNode,le=e.getNodeFromInstance,a(le&&se,"EventPluginUtils.injection.injectComponentTree(...): Injected module is missing getNodeFromInstance or getInstanceFromNode.")};function ue(e,n,t,o){var a=e.type||"unknown-event";e.currentTarget=le(o),z.invokeGuardedCallbackAndCatchFirstError(a,t,void 0,e),e.currentTarget=null}function he(e,n){return null==n&&o(!1,"accumulateInto(...): Accumulated items must not be null or undefined."),null==e?n:Array.isArray(e)?Array.isArray(n)?(e.push.apply(e,n),e):(e.push(n),e):Array.isArray(n)?[e].concat(n):[e,n]}function de(e,n,t){Array.isArray(e)?e.forEach(n,t):e&&n.call(t,e)}ae=function(e){var n=e._dispatchListeners,t=e._dispatchInstances,o=Array.isArray(n),r=o?n.length:n?1:0,i=Array.isArray(t),s=i?t.length:t?1:0;a(i===o&&s===r,"EventPluginUtils: Invalid `event`.")};var pe=null,fe=function(e,n){e&&(!function(e,n){var t=e._dispatchListeners,o=e._dispatchInstances;if(ae(e),Array.isArray(t))for(var a=0;a<t.length&&!e.isPropagationStopped();a++)ue(e,0,t[a],o[a]);else t&&ue(e,0,t,o);e._dispatchListeners=null,e._dispatchInstances=null}(e),e.isPersistent()||e.constructor.release(e))},me=function(e){return fe(e)},ge=function(e){return fe(e)};var ye={injectEventPluginOrder:te,injectEventPluginsByName:oe};function be(e,n){var t,a=e.stateNode;if(!a)return null;var r=ie(a);return r?(t=r[n],function(e,n,t){switch(e){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":return!(!t.disabled||(o=n,"button"!==o&&"input"!==o&&"select"!==o&&"textarea"!==o));default:return!1}var o}(n,e.type,r)?null:(t&&"function"!=typeof t&&o(!1,"Expected `%s` listener to be a function, instead got a value of `%s` type.",n,typeof t),t)):null}function we(e,n,t,o){for(var a,r=0;r<$.length;r++){var i=$[r];if(i){var s=i.extractEvents(e,n,t,o);s&&(a=he(a,s))}}return a}function ve(e){e&&(pe=he(pe,e))}function ke(e){var n=pe;pe=null,n&&(de(n,e?me:ge),pe&&o(!1,"processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented."),z.rethrowCaughtError())}var Te=Object.freeze({injection:ye,getListener:be,extractEvents:we,enqueueEvents:ve,processEventQueue:ke}),Ie=0,Ee=1,xe=2,Se=3,Le=4,Ne=5,Ce=6,Ae=7,Pe=8,Oe=9,Re=10,De=Math.random().toString(36).slice(2),Me="__reactInternalInstance$"+De,Fe="__reactEventHandlers$"+De;function Be(e,n){n[Me]=e}function We(e){if(e[Me])return e[Me];for(var n=[];!e[Me];){if(n.push(e),!e.parentNode)return null;e=e.parentNode}var t=void 0,o=e[Me];if(o.tag===Ne||o.tag===Ce)return o;for(;e&&(o=e[Me]);e=n.pop())t=o;return t}function je(e){var n=e[Me];return n&&(n.tag===Ne||n.tag===Ce)?n:null}function _e(e){if(e.tag===Ne||e.tag===Ce)return e.stateNode;o(!1,"getNodeFromInstance: Invalid argument.")}function Ue(e){return e[Fe]||null}function ze(e,n){e[Fe]=n}var Ge=Object.freeze({precacheFiberNode:Be,getClosestInstanceFromNode:We,getInstanceFromNode:je,getNodeFromInstance:_e,getFiberCurrentPropsFromNode:Ue,updateFiberProps:ze});function Ve(e){do{e=e.return}while(e&&e.tag!==Ne);return e||null}function He(e,n,t){for(var o,a=[];e;)a.push(e),e=Ve(e);for(o=a.length;o-- >0;)n(a[o],"captured",t);for(o=0;o<a.length;o++)n(a[o],"bubbled",t)}function qe(e,n,t,o,a){for(var r=e&&n?function(e,n){for(var t=0,o=e;o;o=Ve(o))t++;for(var a=0,r=n;r;r=Ve(r))a++;for(;t-a>0;)e=Ve(e),t--;for(;a-t>0;)n=Ve(n),a--;for(var i=t;i--;){if(e===n||e===n.alternate)return e;e=Ve(e),n=Ve(n)}return null}(e,n):null,i=[];e&&e!==r;){var s=e.alternate;if(null!==s&&s===r)break;i.push(e),e=Ve(e)}for(var l=[];n&&n!==r;){var c=n.alternate;if(null!==c&&c===r)break;l.push(n),n=Ve(n)}for(var u=0;u<i.length;u++)t(i[u],"bubbled",o);for(var h=l.length;h-- >0;)t(l[h],"captured",a)}function Ke(e,n,t){a(e,"Dispatching inst must not be null");var o,r=(o=n,be(e,t.dispatchConfig.phasedRegistrationNames[o]));r&&(t._dispatchListeners=he(t._dispatchListeners,r),t._dispatchInstances=he(t._dispatchInstances,e))}function Ye(e){e&&e.dispatchConfig.phasedRegistrationNames&&He(e._targetInst,Ke,e)}function Xe(e){if(e&&e.dispatchConfig.phasedRegistrationNames){var n=e._targetInst;He(n?Ve(n):null,Ke,e)}}function Qe(e,n,t){if(e&&t&&t.dispatchConfig.registrationName){var o=be(e,t.dispatchConfig.registrationName);o&&(t._dispatchListeners=he(t._dispatchListeners,o),t._dispatchInstances=he(t._dispatchInstances,e))}}function $e(e){e&&e.dispatchConfig.registrationName&&Qe(e._targetInst,0,e)}function Je(e){de(e,Ye)}function Ze(e,n,t,o){qe(t,o,Qe,e,n)}var en=Object.freeze({accumulateTwoPhaseDispatches:Je,accumulateTwoPhaseDispatchesSkipTarget:function(e){de(e,Xe)},accumulateEnterLeaveDispatches:Ze,accumulateDirectDispatches:function(e){de(e,$e)}}),nn=null;function tn(){return!nn&&r.canUseDOM&&(nn="textContent"in document.documentElement?"textContent":"innerText"),nn}var on={_root:null,_startText:null,_fallbackText:null};function an(){if(on._fallbackText)return on._fallbackText;var e,n,t=on._startText,o=t.length,a=rn(),r=a.length;for(e=0;e<o&&t[e]===a[e];e++);var i=o-e;for(n=1;n<=i&&t[o-n]===a[r-n];n++);var s=n>1?1-n:void 0;return on._fallbackText=a.slice(e,s),on._fallbackText}function rn(){return"value"in on._root?on._root.value:on._root[tn()]}var sn=!1,ln="function"==typeof Proxy,cn=10,un=["dispatchConfig","_targetInst","nativeEvent","isDefaultPrevented","isPropagationStopped","_dispatchListeners","_dispatchInstances"],hn={type:null,target:null,currentTarget:s.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};function dn(e,n,t,o){delete this.nativeEvent,delete this.preventDefault,delete this.stopPropagation,this.dispatchConfig=e,this._targetInst=n,this.nativeEvent=t;var a=this.constructor.Interface;for(var r in a)if(a.hasOwnProperty(r)){delete this[r];var i=a[r];i?this[r]=i(t):"target"===r?this.target=o:this[r]=t[r]}var l=null!=t.defaultPrevented?t.defaultPrevented:!1===t.returnValue;return this.isDefaultPrevented=l?s.thatReturnsTrue:s.thatReturnsFalse,this.isPropagationStopped=s.thatReturnsFalse,this}function pn(e,n){var t="function"==typeof n;return{configurable:!0,set:function(e){return o(t?"setting the method":"setting the property","This is effectively a no-op"),e},get:function(){return o(t?"accessing the method":"accessing the property",t?"This is a no-op function":"This is set to null"),n}};function o(n,t){a(!1,"This synthetic event is reused for performance reasons. If you're seeing this, you're %s `%s` on a released/nullified synthetic event. %s. If you must keep the original synthetic event around, use event.persist(). See https://fb.me/react-event-pooling for more information.",n,e,t)}}function fn(e,n,t,o){if(this.eventPool.length){var a=this.eventPool.pop();return this.call(a,e,n,t,o),a}return new this(e,n,t,o)}function mn(e){e instanceof this||o(!1,"Trying to release an event instance into a pool of a different type."),e.destructor(),this.eventPool.length<cn&&this.eventPool.push(e)}function gn(e){e.eventPool=[],e.getPooled=fn,e.release=mn}i(dn.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=s.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=s.thatReturnsTrue)},persist:function(){this.isPersistent=s.thatReturnsTrue},isPersistent:s.thatReturnsFalse,destructor:function(){var e=this.constructor.Interface;for(var n in e)Object.defineProperty(this,n,pn(n,e[n]));for(var t=0;t<un.length;t++)this[un[t]]=null;Object.defineProperty(this,"nativeEvent",pn("nativeEvent",null)),Object.defineProperty(this,"preventDefault",pn("preventDefault",s)),Object.defineProperty(this,"stopPropagation",pn("stopPropagation",s))}}),dn.Interface=hn,dn.augmentClass=function(e,n){var t=function(){};t.prototype=this.prototype;var o=new t;i(o,e.prototype),e.prototype=o,e.prototype.constructor=e,e.Interface=i({},this.Interface,n),e.augmentClass=this.augmentClass,gn(e)},ln&&(dn=new Proxy(dn,{construct:function(e,n){return this.apply(e,Object.create(e.prototype),n)},apply:function(e,n,t){return new Proxy(e.apply(n,t),{set:function(e,n,t){return"isPersistent"===n||e.constructor.Interface.hasOwnProperty(n)||-1!==un.indexOf(n)||(a(sn||e.isPersistent(),"This synthetic event is reused for performance reasons. If you're seeing this, you're adding a new property in the synthetic event object. The property is never released. See https://fb.me/react-event-pooling for more information."),sn=!0),e[n]=t,!0}})}})),gn(dn);var yn=dn;function bn(e,n,t,o){return yn.call(this,e,n,t,o)}yn.augmentClass(bn,{data:null});function wn(e,n,t,o){return yn.call(this,e,n,t,o)}yn.augmentClass(wn,{data:null});var vn=[9,13,27,32],kn=229,Tn=r.canUseDOM&&"CompositionEvent"in window,In=null;r.canUseDOM&&"documentMode"in document&&(In=document.documentMode);var En,xn=r.canUseDOM&&"TextEvent"in window&&!In&&!("object"==typeof(En=window.opera)&&"function"==typeof En.version&&parseInt(En.version(),10)<=12),Sn=r.canUseDOM&&(!Tn||In&&In>8&&In<=11);var Ln=32,Nn=String.fromCharCode(Ln),Cn={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["topCompositionEnd","topKeyPress","topTextInput","topPaste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:["topBlur","topCompositionEnd","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:["topBlur","topCompositionStart","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:["topBlur","topCompositionUpdate","topKeyDown","topKeyPress","topKeyUp","topMouseDown"]}},An=!1;function Pn(e,n){switch(e){case"topKeyUp":return-1!==vn.indexOf(n.keyCode);case"topKeyDown":return n.keyCode!==kn;case"topKeyPress":case"topMouseDown":case"topBlur":return!0;default:return!1}}function On(e){var n=e.detail;return"object"==typeof n&&"data"in n?n.data:null}var Rn=!1;function Dn(e,n,t,o){var a,r,i;if(Tn?a=function(e){switch(e){case"topCompositionStart":return Cn.compositionStart;case"topCompositionEnd":return Cn.compositionEnd;case"topCompositionUpdate":return Cn.compositionUpdate}}(e):Rn?Pn(e,t)&&(a=Cn.compositionEnd):"topKeyDown"===e&&t.keyCode===kn&&(a=Cn.compositionStart),!a)return null;Sn&&(Rn||a!==Cn.compositionStart?a===Cn.compositionEnd&&Rn&&(r=an()):(i=o,on._root=i,on._startText=rn(),Rn=!0));var s=bn.getPooled(a,n,t,o);if(r)s.data=r;else{var l=On(t);null!==l&&(s.data=l)}return Je(s),s}function Mn(e,n){if(Rn){if("topCompositionEnd"===e||!Tn&&Pn(e,n)){var t=an();return on._root=null,on._startText=null,on._fallbackText=null,Rn=!1,t}return null}switch(e){case"topPaste":return null;case"topKeyPress":if(!((o=n).ctrlKey||o.altKey||o.metaKey)||o.ctrlKey&&o.altKey){if(n.char&&n.char.length>1)return n.char;if(n.which)return String.fromCharCode(n.which)}return null;case"topCompositionEnd":return Sn?null:n.data;default:return null}var o}function Fn(e,n,t,o){var a;if(!(a=xn?function(e,n){switch(e){case"topCompositionEnd":return On(n);case"topKeyPress":return n.which!==Ln?null:(An=!0,Nn);case"topTextInput":var t=n.data;return t===Nn&&An?null:t;default:return null}}(e,t):Mn(e,t)))return null;var r=wn.getPooled(Cn.beforeInput,n,t,o);return r.data=a,Je(r),r}var Bn={eventTypes:Cn,extractEvents:function(e,n,t,o){return[Dn(e,n,t,o),Fn(e,n,t,o)]}},Wn=null,jn=null,_n=null;function Un(e){var n=se(e);if(n){Wn&&"function"==typeof Wn.restoreControlledState||o(!1,"Fiber needs to be injected to handle a fiber target for controlled events. This error is likely caused by a bug in React. Please file an issue.");var t=ie(n.stateNode);Wn.restoreControlledState(n.stateNode,n.type,t)}}var zn={injectFiberControlledHostComponent:function(e){Wn=e}};function Gn(e){jn?_n?_n.push(e):_n=[e]:jn=e}function Vn(){if(jn){var e=jn,n=_n;if(jn=null,_n=null,Un(e),n)for(var t=0;t<n.length;t++)Un(n[t])}}var Hn=Object.freeze({injection:zn,enqueueStateRestore:Gn,restoreStateIfNeeded:Vn}),qn=function(e,n){return e(n)},Kn=!1;function Yn(e,n){if(Kn)return qn(e,n);Kn=!0;try{return qn(e,n)}finally{Kn=!1,Vn()}}var Xn={injectFiberBatchedUpdates:function(e){qn=e}},Qn={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function $n(e){var n=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===n?!!Qn[e.type]:"textarea"===n}var Jn,Zn=1,et=3,nt=8,tt=9,ot=11;function at(e){var n=e.target||e.srcElement||window;return n.correspondingUseElement&&(n=n.correspondingUseElement),n.nodeType===et?n.parentNode:n}function rt(e,n){if(!r.canUseDOM||n&&!("addEventListener"in document))return!1;var t="on"+e,o=t in document;if(!o){var a=document.createElement("div");a.setAttribute(t,"return;"),o="function"==typeof a[t]}return!o&&Jn&&"wheel"===e&&(o=document.implementation.hasFeature("Events.wheel","3.0")),o}function it(e){var n=e.type,t=e.nodeName;return t&&"input"===t.toLowerCase()&&("checkbox"===n||"radio"===n)}function st(e){return e._valueTracker}function lt(e){st(e)||(e._valueTracker=function(e){var n=it(e)?"checked":"value",t=Object.getOwnPropertyDescriptor(e.constructor.prototype,n),o=""+e[n];if(!e.hasOwnProperty(n)&&"function"==typeof t.get&&"function"==typeof t.set)return Object.defineProperty(e,n,{enumerable:t.enumerable,configurable:!0,get:function(){return t.get.call(this)},set:function(e){o=""+e,t.set.call(this,e)}}),{getValue:function(){return o},setValue:function(e){o=""+e},stopTracking:function(){e._valueTracker=null,delete e[n]}}}(e))}function ct(e){if(!e)return!1;var n=st(e);if(!n)return!0;var t,o,a=n.getValue(),r=(o="",(t=e)?o=it(t)?t.checked?"true":"false":t.value:o);return r!==a&&(n.setValue(r),!0)}r.canUseDOM&&(Jn=document.implementation&&document.implementation.hasFeature&&!0!==document.implementation.hasFeature("",""));var ut={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:["topBlur","topChange","topClick","topFocus","topInput","topKeyDown","topKeyUp","topSelectionChange"]}};function ht(e,n,t){var o=yn.getPooled(ut.change,e,n,t);return o.type="change",Gn(t),Je(o),o}var dt=null,pt=null;function ft(e){ve(e),ke(!1)}function mt(e){if(ct(_e(e)))return e}function gt(e,n){if("topChange"===e)return n}var yt=!1;function bt(){dt&&(dt.detachEvent("onpropertychange",wt),dt=null,pt=null)}function wt(e){var n;"value"===e.propertyName&&(mt(pt)&&Yn(ft,ht(pt,n=e,at(n))))}function vt(e,n,t){"topFocus"===e?(bt(),pt=t,(dt=n).attachEvent("onpropertychange",wt)):"topBlur"===e&&bt()}function kt(e,n){if("topSelectionChange"===e||"topKeyUp"===e||"topKeyDown"===e)return mt(pt)}function Tt(e,n){if("topClick"===e)return mt(n)}function It(e,n){if("topInput"===e||"topChange"===e)return mt(n)}r.canUseDOM&&(yt=rt("input")&&(!document.documentMode||document.documentMode>9));var Et={eventTypes:ut,_isInputEventSupported:yt,extractEvents:function(e,n,t,o){var a,r,i,s,l,c,u=n?_e(n):window;if("select"===(c=(l=u).nodeName&&l.nodeName.toLowerCase())||"input"===c&&"file"===l.type?a=gt:$n(u)?yt?a=It:(a=kt,r=vt):!(s=(i=u).nodeName)||"input"!==s.toLowerCase()||"checkbox"!==i.type&&"radio"!==i.type||(a=Tt),a){var h=a(e,n);if(h)return ht(h,t,o)}r&&r(e,u,n),"topBlur"===e&&function(e,n){if(null!=e){var t=e._wrapperState||n._wrapperState;if(t&&t.controlled&&"number"===n.type){var o=""+n.value;n.getAttribute("value")!==o&&n.setAttribute("value",o)}}}(n,u)}};function xt(e,n,t,o){return yn.call(this,e,n,t,o)}yn.augmentClass(xt,{view:null,detail:null});var St={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function Lt(e){var n=this.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var t=St[e];return!!t&&!!n[t]}function Nt(e){return Lt}var Ct={screenX:null,screenY:null,clientX:null,clientY:null,pageX:null,pageY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:Nt,button:null,buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)}};function At(e,n,t,o){return xt.call(this,e,n,t,o)}xt.augmentClass(At,Ct);var Pt={mouseEnter:{registrationName:"onMouseEnter",dependencies:["topMouseOut","topMouseOver"]},mouseLeave:{registrationName:"onMouseLeave",dependencies:["topMouseOut","topMouseOver"]}},Ot={eventTypes:Pt,extractEvents:function(e,n,t,o){if("topMouseOver"===e&&(t.relatedTarget||t.fromElement))return null;if("topMouseOut"!==e&&"topMouseOver"!==e)return null;var a,r,i;if(o.window===o)a=o;else{var s=o.ownerDocument;a=s?s.defaultView||s.parentWindow:window}if("topMouseOut"===e){r=n;var l=t.relatedTarget||t.toElement;i=l?We(l):null}else r=null,i=n;if(r===i)return null;var c=null==r?a:_e(r),u=null==i?a:_e(i),h=At.getPooled(Pt.mouseLeave,r,t,o);h.type="mouseleave",h.target=c,h.relatedTarget=u;var d=At.getPooled(Pt.mouseEnter,i,t,o);return d.type="mouseenter",d.target=u,d.relatedTarget=c,Ze(h,d,r,i),[h,d]}};function Rt(e){return e._reactInternalFiber}var Dt=t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,Mt=Dt.ReactCurrentOwner,Ft=Dt.ReactDebugCurrentFrame;function Bt(e){var n=e.type;return"string"==typeof n?n:"function"==typeof n?n.displayName||n.name:null}var Wt=0,jt=1,_t=2,Ut=4,zt=6,Gt=8,Vt=16,Ht=32,qt=64,Kt=128,Yt=1,Xt=2,Qt=3;function $t(e){var n=e;if(e.alternate)for(;n.return;)n=n.return;else{if((n.effectTag&_t)!==Wt)return Yt;for(;n.return;)if(((n=n.return).effectTag&_t)!==Wt)return Yt}return n.tag===Se?Xt:Qt}function Jt(e){return $t(e)===Xt}function Zt(e){var n=Mt.current;if(null!==n&&n.tag===xe){var t=n,o=t.stateNode;a(o._warnedAboutRefsInRender,"%s is accessing isMounted inside its render() function. render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.",Bt(t)||"A component"),o._warnedAboutRefsInRender=!0}var r=Rt(e);return!!r&&$t(r)===Xt}function eo(e){$t(e)!==Xt&&o(!1,"Unable to find node on an unmounted component.")}function no(e){var n=e.alternate;if(!n){var t=$t(e);return t===Qt&&o(!1,"Unable to find node on an unmounted component."),t===Yt?null:e}for(var a=e,r=n;;){var i=a.return,s=i?i.alternate:null;if(!i||!s)break;if(i.child===s.child){for(var l=i.child;l;){if(l===a)return eo(i),e;if(l===r)return eo(i),n;l=l.sibling}o(!1,"Unable to find node on an unmounted component.")}if(a.return!==r.return)a=i,r=s;else{for(var c=!1,u=i.child;u;){if(u===a){c=!0,a=i,r=s;break}if(u===r){c=!0,r=i,a=s;break}u=u.sibling}if(!c){for(u=s.child;u;){if(u===a){c=!0,a=s,r=i;break}if(u===r){c=!0,r=s,a=i;break}u=u.sibling}c||o(!1,"Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue.")}}a.alternate!==r&&o(!1,"Return fibers should always be each others' alternates. This error is likely caused by a bug in React. Please file an issue.")}return a.tag!==Se&&o(!1,"Unable to find node on an unmounted component."),a.stateNode.current===a?e:n}var to=10,oo=[];function ao(e){for(;e.return;)e=e.return;return e.tag!==Se?null:e.stateNode.containerInfo}function ro(e){var n=e.targetInst,t=n;do{if(!t){e.ancestors.push(t);break}var o=ao(t);if(!o)break;e.ancestors.push(t),t=We(o)}while(t);for(var a=0;a<e.ancestors.length;a++)n=e.ancestors[a],so(e.topLevelType,n,e.nativeEvent,at(e.nativeEvent))}var io=!0,so=void 0;function lo(e){so=e}function co(e){io=!!e}function uo(){return io}function ho(e,n,t){return t?l.listen(t,n,fo.bind(null,e)):null}function po(e,n,t){return t?l.capture(t,n,fo.bind(null,e)):null}function fo(e,n){if(io){var t=We(at(n));null===t||"number"!=typeof t.tag||Jt(t)||(t=null);var o,a=function(e,n,t){if(oo.length){var o=oo.pop();return o.topLevelType=e,o.nativeEvent=n,o.targetInst=t,o}return{topLevelType:e,nativeEvent:n,targetInst:t,ancestors:[]}}(e,n,t);try{Yn(ro,a)}finally{(o=a).topLevelType=null,o.nativeEvent=null,o.targetInst=null,o.ancestors.length=0,oo.length<to&&oo.push(o)}}}var mo=Object.freeze({get _enabled(){return io},get _handleTopLevel(){return so},setHandleTopLevel:lo,setEnabled:co,isEnabled:uo,trapBubbledEvent:ho,trapCapturedEvent:po,dispatchEvent:fo});function go(e,n){var t={};return t[e.toLowerCase()]=n.toLowerCase(),t["Webkit"+e]="webkit"+n,t["Moz"+e]="moz"+n,t["ms"+e]="MS"+n,t["O"+e]="o"+n.toLowerCase(),t}var yo={animationend:go("Animation","AnimationEnd"),animationiteration:go("Animation","AnimationIteration"),animationstart:go("Animation","AnimationStart"),transitionend:go("Transition","TransitionEnd")},bo={},wo={};function vo(e){if(bo[e])return bo[e];if(!yo[e])return e;var n=yo[e];for(var t in n)if(n.hasOwnProperty(t)&&t in wo)return bo[e]=n[t];return""}r.canUseDOM&&(wo=document.createElement("div").style,"AnimationEvent"in window||(delete yo.animationend.animation,delete yo.animationiteration.animation,delete yo.animationstart.animation),"TransitionEvent"in window||delete yo.transitionend.transition);var ko={topLevelTypes:{topAbort:"abort",topAnimationEnd:vo("animationend")||"animationend",topAnimationIteration:vo("animationiteration")||"animationiteration",topAnimationStart:vo("animationstart")||"animationstart",topBlur:"blur",topCancel:"cancel",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topChange:"change",topClick:"click",topClose:"close",topCompositionEnd:"compositionend",topCompositionStart:"compositionstart",topCompositionUpdate:"compositionupdate",topContextMenu:"contextmenu",topCopy:"copy",topCut:"cut",topDoubleClick:"dblclick",topDrag:"drag",topDragEnd:"dragend",topDragEnter:"dragenter",topDragExit:"dragexit",topDragLeave:"dragleave",topDragOver:"dragover",topDragStart:"dragstart",topDrop:"drop",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topFocus:"focus",topInput:"input",topKeyDown:"keydown",topKeyPress:"keypress",topKeyUp:"keyup",topLoadedData:"loadeddata",topLoad:"load",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topMouseDown:"mousedown",topMouseMove:"mousemove",topMouseOut:"mouseout",topMouseOver:"mouseover",topMouseUp:"mouseup",topPaste:"paste",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topScroll:"scroll",topSeeked:"seeked",topSeeking:"seeking",topSelectionChange:"selectionchange",topStalled:"stalled",topSuspend:"suspend",topTextInput:"textInput",topTimeUpdate:"timeupdate",topToggle:"toggle",topTouchCancel:"touchcancel",topTouchEnd:"touchend",topTouchMove:"touchmove",topTouchStart:"touchstart",topTransitionEnd:vo("transitionend")||"transitionend",topVolumeChange:"volumechange",topWaiting:"waiting",topWheel:"wheel"}}.topLevelTypes,To={},Io=0,Eo="_reactListenersID"+(""+Math.random()).slice(2);function xo(e){return Object.prototype.hasOwnProperty.call(e,Eo)||(e[Eo]=Io++,To[e[Eo]]={}),To[e[Eo]]}function So(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function Lo(e){for(;e;){if(e.nextSibling)return e.nextSibling;e=e.parentNode}}function No(e,n){for(var t=So(e),o=0,a=0;t;){if(t.nodeType===et){if(a=o+t.textContent.length,o<=n&&a>=n)return{node:t,offset:n-o};o=a}t=So(Lo(t))}}function Co(e){var n=window.getSelection&&window.getSelection();if(!n||0===n.rangeCount)return null;var t=n.anchorNode,o=n.anchorOffset,a=n.focusNode,r=n.focusOffset;try{t.nodeType,a.nodeType}catch(e){return null}return function(e,n,t,o,a){var r=0,i=-1,s=-1,l=0,c=0,u=e,h=null;e:for(;;){for(var d=null;u!==n||0!==t&&u.nodeType!==et||(i=r+t),u!==o||0!==a&&u.nodeType!==et||(s=r+a),u.nodeType===et&&(r+=u.nodeValue.length),null!==(d=u.firstChild);)h=u,u=d;for(;;){if(u===e)break e;if(h===n&&++l===t&&(i=r),h===o&&++c===a&&(s=r),null!==(d=u.nextSibling))break;h=(u=h).parentNode}u=d}if(-1===i||-1===s)return null;return{start:i,end:s}}(e,t,o,a,r)}function Ao(e){var n=e&&e.nodeName&&e.nodeName.toLowerCase();return n&&("input"===n&&"text"===e.type||"textarea"===n||"true"===e.contentEditable)}function Po(){var e=c();return{focusedElem:e,selectionRange:Ao(e)?function(e){var n=void 0;n="selectionStart"in e?{start:e.selectionStart,end:e.selectionEnd}:Co(e);return n||{start:0,end:0}}(e):null}}function Oo(e){var n,t=c(),o=e.focusedElem,a=e.selectionRange;if(t!==o&&(n=o,h(document.documentElement,n))){Ao(o)&&function(e,n){var t=n.start,o=n.end;void 0===o&&(o=t);"selectionStart"in e?(e.selectionStart=t,e.selectionEnd=Math.min(o,e.value.length)):function(e,n){if(window.getSelection){var t=window.getSelection(),o=e[tn()].length,a=Math.min(n.start,o),r=void 0===n.end?a:Math.min(n.end,o);if(!t.extend&&a>r){var i=r;r=a,a=i}var s=No(e,a),l=No(e,r);if(s&&l){if(1===t.rangeCount&&t.anchorNode===s.node&&t.anchorOffset===s.offset&&t.focusNode===l.node&&t.focusOffset===l.offset)return;var c=document.createRange();c.setStart(s.node,s.offset),t.removeAllRanges(),a>r?(t.addRange(c),t.extend(l.node,l.offset)):(c.setEnd(l.node,l.offset),t.addRange(c))}}}(e,n)}(o,a);for(var r=[],i=o;i=i.parentNode;)i.nodeType===Zn&&r.push({element:i,left:i.scrollLeft,top:i.scrollTop});d(o);for(var s=0;s<r.length;s++){var l=r[s];l.element.scrollLeft=l.left,l.element.scrollTop=l.top}}}var Ro=r.canUseDOM&&"documentMode"in document&&document.documentMode<=11,Do={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},dependencies:["topBlur","topContextMenu","topFocus","topKeyDown","topKeyUp","topMouseDown","topMouseUp","topSelectionChange"]}},Mo=null,Fo=null,Bo=null,Wo=!1;function jo(e,n){if(Wo||null==Mo||Mo!==c())return null;var t=function(e){if("selectionStart"in e&&Ao(e))return{start:e.selectionStart,end:e.selectionEnd};if(window.getSelection){var n=window.getSelection();return{anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}}}(Mo);if(!Bo||!u(Bo,t)){Bo=t;var o=yn.getPooled(Do.select,Fo,e,n);return o.type="select",o.target=Mo,Je(o),o}return null}var _o={eventTypes:Do,extractEvents:function(e,n,t,o){var a=o.window===o?o.document:o.nodeType===tt?o:o.ownerDocument;if(!a||!function(e,n){for(var t=xo(n),o=ee[e],a=0;a<o.length;a++){var r=o[a];if(!t.hasOwnProperty(r)||!t[r])return!1}return!0}("onSelect",a))return null;var r=n?_e(n):window;switch(e){case"topFocus":($n(r)||"true"===r.contentEditable)&&(Mo=r,Fo=n,Bo=null);break;case"topBlur":Mo=null,Fo=null,Bo=null;break;case"topMouseDown":Wo=!0;break;case"topContextMenu":case"topMouseUp":return Wo=!1,jo(t,o);case"topSelectionChange":if(Ro)break;case"topKeyDown":case"topKeyUp":return jo(t,o)}return null}};function Uo(e,n,t,o){return yn.call(this,e,n,t,o)}yn.augmentClass(Uo,{animationName:null,elapsedTime:null,pseudoElement:null});var zo={clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}};function Go(e,n,t,o){return yn.call(this,e,n,t,o)}yn.augmentClass(Go,zo);function Vo(e,n,t,o){return xt.call(this,e,n,t,o)}function Ho(e){var n,t=e.keyCode;return"charCode"in e?0===(n=e.charCode)&&13===t&&(n=13):n=t,n>=32||13===n?n:0}xt.augmentClass(Vo,{relatedTarget:null});var qo={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},Ko={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"};var Yo={key:function(e){if(e.key){var n=qo[e.key]||e.key;if("Unidentified"!==n)return n}if("keypress"===e.type){var t=Ho(e);return 13===t?"Enter":String.fromCharCode(t)}return"keydown"===e.type||"keyup"===e.type?Ko[e.keyCode]||"Unidentified":""},location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:Nt,charCode:function(e){return"keypress"===e.type?Ho(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?Ho(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}};function Xo(e,n,t,o){return xt.call(this,e,n,t,o)}xt.augmentClass(Xo,Yo);function Qo(e,n,t,o){return At.call(this,e,n,t,o)}At.augmentClass(Qo,{dataTransfer:null});var $o={touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:Nt};function Jo(e,n,t,o){return xt.call(this,e,n,t,o)}xt.augmentClass(Jo,$o);function Zo(e,n,t,o){return yn.call(this,e,n,t,o)}yn.augmentClass(Zo,{propertyName:null,elapsedTime:null,pseudoElement:null});function ea(e,n,t,o){return At.call(this,e,n,t,o)}At.augmentClass(ea,{deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:null,deltaMode:null});var na={},ta={};["abort","animationEnd","animationIteration","animationStart","blur","cancel","canPlay","canPlayThrough","click","close","contextMenu","copy","cut","doubleClick","drag","dragEnd","dragEnter","dragExit","dragLeave","dragOver","dragStart","drop","durationChange","emptied","encrypted","ended","error","focus","input","invalid","keyDown","keyPress","keyUp","load","loadedData","loadedMetadata","loadStart","mouseDown","mouseMove","mouseOut","mouseOver","mouseUp","paste","pause","play","playing","progress","rateChange","reset","scroll","seeked","seeking","stalled","submit","suspend","timeUpdate","toggle","touchCancel","touchEnd","touchMove","touchStart","transitionEnd","volumeChange","waiting","wheel"].forEach(function(e){var n=e[0].toUpperCase()+e.slice(1),t="on"+n,o="top"+n,a={phasedRegistrationNames:{bubbled:t,captured:t+"Capture"},dependencies:[o]};na[e]=a,ta[o]=a});var oa=["topAbort","topCancel","topCanPlay","topCanPlayThrough","topClose","topDurationChange","topEmptied","topEncrypted","topEnded","topError","topInput","topInvalid","topLoad","topLoadedData","topLoadedMetadata","topLoadStart","topPause","topPlay","topPlaying","topProgress","topRateChange","topReset","topSeeked","topSeeking","topStalled","topSubmit","topSuspend","topTimeUpdate","topToggle","topVolumeChange","topWaiting"],aa={eventTypes:na,extractEvents:function(e,n,t,o){var r,i=ta[e];if(!i)return null;switch(e){case"topKeyPress":if(0===Ho(t))return null;case"topKeyDown":case"topKeyUp":r=Xo;break;case"topBlur":case"topFocus":r=Vo;break;case"topClick":if(2===t.button)return null;case"topDoubleClick":case"topMouseDown":case"topMouseMove":case"topMouseUp":case"topMouseOut":case"topMouseOver":case"topContextMenu":r=At;break;case"topDrag":case"topDragEnd":case"topDragEnter":case"topDragExit":case"topDragLeave":case"topDragOver":case"topDragStart":case"topDrop":r=Qo;break;case"topTouchCancel":case"topTouchEnd":case"topTouchMove":case"topTouchStart":r=Jo;break;case"topAnimationEnd":case"topAnimationIteration":case"topAnimationStart":r=Uo;break;case"topTransitionEnd":r=Zo;break;case"topScroll":r=xt;break;case"topWheel":r=ea;break;case"topCopy":case"topCut":case"topPaste":r=Go;break;default:-1===oa.indexOf(e)&&a(!1,"SimpleEventPlugin: Unhandled event type, `%s`. This warning is likely caused by a bug in React. Please file an issue.",e),r=yn}var s=r.getPooled(i,n,t,o);return Je(s),s}};lo(function(e,n,t,o){ve(we(e,n,t,o)),ke(!1)}),ye.injectEventPluginOrder(["ResponderEventPlugin","SimpleEventPlugin","TapEventPlugin","EnterLeaveEventPlugin","ChangeEventPlugin","SelectEventPlugin","BeforeInputEventPlugin"]),ce(Ge),ye.injectEventPluginsByName({SimpleEventPlugin:aa,EnterLeaveEventPlugin:Ot,ChangeEventPlugin:Et,SelectEventPlugin:_o,BeforeInputEventPlugin:Bn});var ra=!0,ia=!0,sa=!0,la=!1,ca=!1,ua=[],ha=[],da=-1;function pa(e){return{current:e}}function fa(e,n){da<0?a(!1,"Unexpected pop."):(n!==ha[da]&&a(!1,"Unexpected Fiber popped."),e.current=ua[da],ua[da]=null,ha[da]=null,da--)}function ma(e,n,t){ua[++da]=e.current,ha[da]=t,e.current=n}var ga=function(e,n,t){return"\n in "+(e||"Unknown")+(n?" (at "+n.fileName.replace(/^.*[\\\/]/,"")+":"+n.lineNumber+")":t?" (created by "+t+")":"")};function ya(e){switch(e.tag){case Ie:case Ee:case xe:case Ne:var n=e._debugOwner,t=e._debugSource,o=Bt(e),a=null;return n&&(a=Bt(n)),ga(o,t,a);default:return""}}function ba(e){var n="",t=e;do{n+=ya(t),t=t.return}while(t);return n}function wa(){var e=va.current;return null===e?null:ba(e)}var va={current:null,phase:null,resetCurrentFiber:function(){Ft.getCurrentStack=null,va.current=null,va.phase=null},setCurrentFiber:function(e){Ft.getCurrentStack=wa,va.current=e,va.phase=null},setCurrentPhase:function(e){va.phase=e},getCurrentFiberOwnerName:function(){var e=va.current;if(null===e)return null;var n=e._debugOwner;return null!==n&&void 0!==n?Bt(n):null},getCurrentFiberStackAddendum:wa},ka="undefined"!=typeof performance&&"function"==typeof performance.mark&&"function"==typeof performance.clearMarks&&"function"==typeof performance.measure&&"function"==typeof performance.clearMeasures,Ta=null,Ia=null,Ea=null,xa=!1,Sa=!1,La=!1,Na=0,Ca=0,Aa=!1,Pa=new Set,Oa=function(e){return"⚛ "+e},Ra=function(e){performance.mark(Oa(e))},Da=function(e,n,t){var o,a=Oa(n),r=((o=t)?"⛔ ":"⚛ ")+e+(o?" Warning: "+o:"");try{performance.measure(r,a)}catch(e){}performance.clearMarks(a),performance.clearMeasures(r)},Ma=function(e,n){return e+" (#"+n+")"},Fa=function(e,n,t){return null===t?e+" ["+(n?"update":"mount")+"]":e+"."+t},Ba=function(e,n){var t=Bt(e)||"Unknown",o=e._debugID,a=null!==e.alternate,r=Fa(t,a,n);if(xa&&Pa.has(r))return!1;Pa.add(r);var i=Ma(r,o);return Ra(i),!0},Wa=function(e,n){var t,o=Bt(e)||"Unknown",a=e._debugID,r=null!==e.alternate,i=Fa(o,r,n),s=Ma(i,a);t=s,performance.clearMarks(Oa(t))},ja=function(e,n,t){var o=Bt(e)||"Unknown",a=e._debugID,r=null!==e.alternate,i=Fa(o,r,n),s=Ma(i,a);Da(i,s,t)},_a=function(e){switch(e.tag){case Se:case Ne:case Ce:case Le:case Oe:case Re:return!0;default:return!1}},Ua=function(){null!==Ia&&null!==Ea&&Wa(Ea,Ia),Ea=null,Ia=null,La=!1},za=function(){for(var e=Ta;e;)e._debugIsCurrentlyTiming&&ja(e,null,null),e=e.return},Ga=function(e){null!==e.return&&Ga(e.return),e._debugIsCurrentlyTiming&&Ba(e,null)},Va=function(){null!==Ta&&Ga(Ta)};function Ha(){ia&&Ca++}function qa(e){if(ia){if(!ka||_a(e))return;if(Ta=e,!Ba(e,null))return;e._debugIsCurrentlyTiming=!0}}function Ka(e){if(ia){if(!ka||_a(e))return;e._debugIsCurrentlyTiming=!1,Wa(e,null)}}function Ya(e){if(ia){if(!ka||_a(e))return;if(Ta=e.return,!e._debugIsCurrentlyTiming)return;e._debugIsCurrentlyTiming=!1,ja(e,null,null)}}function Xa(e){if(ia){if(!ka||_a(e))return;if(Ta=e.return,!e._debugIsCurrentlyTiming)return;e._debugIsCurrentlyTiming=!1;ja(e,null,"An error was thrown inside this error boundary")}}function Qa(e,n){if(ia){if(!ka)return;if(Ua(),!Ba(e,n))return;Ea=e,Ia=n}}function $a(){if(ia){if(!ka)return;if(null!==Ia&&null!==Ea)ja(Ea,Ia,La?"Scheduled a cascading update":null);Ia=null,Ea=null}}var Ja={},Za=pa(p),er=pa(!1),nr=p;function tr(e){return ir(e)?nr:Za.current}function or(e,n,t){var o=e.stateNode;o.__reactInternalMemoizedUnmaskedChildContext=n,o.__reactInternalMemoizedMaskedChildContext=t}function ar(e,n){var t=e.type.contextTypes;if(!t)return p;var o=e.stateNode;if(o&&o.__reactInternalMemoizedUnmaskedChildContext===n)return o.__reactInternalMemoizedMaskedChildContext;var a={};for(var r in t)a[r]=n[r];var i=Bt(e)||"Unknown";return f(t,a,"context",i,va.getCurrentFiberStackAddendum),o&&or(e,n,a),a}function rr(){return er.current}function ir(e){return e.tag===xe&&null!=e.type.childContextTypes}function sr(e){ir(e)&&(fa(er,e),fa(Za,e))}function lr(e,n,t){null!=Za.cursor&&o(!1,"Unexpected context found on stack. This error is likely caused by a bug in React. Please file an issue."),ma(Za,n,e),ma(er,t,e)}function cr(e,n){var t=e.stateNode,r=e.type.childContextTypes;if("function"!=typeof t.getChildContext){var s=Bt(e)||"Unknown";return Ja[s]||(Ja[s]=!0,a(!1,"%s.childContextTypes is specified but there is no getChildContext() method on the instance. You can either define getChildContext() on %s or remove childContextTypes from it.",s,s)),n}var l;for(var c in va.setCurrentPhase("getChildContext"),Qa(e,"getChildContext"),l=t.getChildContext(),$a(),va.setCurrentPhase(null),l)c in r||o(!1,'%s.getChildContext(): key "%s" is not defined in childContextTypes.',Bt(e)||"Unknown",c);var u=Bt(e)||"Unknown";return f(r,l,"child context",u,va.getCurrentFiberStackAddendum),i({},n,l)}function ur(e){if(!ir(e))return!1;var n=e.stateNode,t=n&&n.__reactInternalMemoizedMergedChildContext||p;return nr=Za.current,ma(Za,t,e),ma(er,er.current,e),!0}function hr(e,n){var t=e.stateNode;if(t||o(!1,"Expected to have an instance by this point. This error is likely caused by a bug in React. Please file an issue."),n){var a=cr(e,nr);t.__reactInternalMemoizedMergedChildContext=a,fa(er,e),fa(Za,e),ma(Za,a,e),ma(er,n,e)}else fa(er,e),ma(er,n,e)}var dr=0,pr=1,fr=2147483647,mr=10,gr=2;function yr(e){return(e/mr|0)+gr}var br=0,wr=1,vr=!1;try{Object.preventExtensions({})}catch(e){vr=!0}var kr=1;var Tr=function(e,n,t){return new function(e,n,t){this.tag=e,this.key=n,this.type=null,this.stateNode=null,this.return=null,this.child=null,this.sibling=null,this.index=0,this.ref=null,this.pendingProps=null,this.memoizedProps=null,this.updateQueue=null,this.memoizedState=null,this.internalContextTag=t,this.effectTag=Wt,this.nextEffect=null,this.firstEffect=null,this.lastEffect=null,this.expirationTime=dr,this.alternate=null,this._debugID=kr++,this._debugSource=null,this._debugOwner=null,this._debugIsCurrentlyTiming=!1,vr||"function"!=typeof Object.preventExtensions||Object.preventExtensions(this)}(e,n,t)};function Ir(e,n,t){var o=e.alternate;return null===o?((o=Tr(e.tag,e.key,e.internalContextTag)).type=e.type,o.stateNode=e.stateNode,o._debugID=e._debugID,o._debugSource=e._debugSource,o._debugOwner=e._debugOwner,o.alternate=e,e.alternate=o):(o.effectTag=Wt,o.nextEffect=null,o.firstEffect=null,o.lastEffect=null),o.expirationTime=t,o.pendingProps=n,o.child=e.child,o.memoizedProps=e.memoizedProps,o.memoizedState=e.memoizedState,o.updateQueue=e.updateQueue,o.sibling=e.sibling,o.index=e.index,o.ref=e.ref,o}function Er(e,n,t){var a;a=e._owner;var r,i=void 0,s=e.type,l=e.key;if("function"==typeof s)(i=(r=s).prototype&&r.prototype.isReactComponent?Tr(xe,l,n):Tr(Ie,l,n)).type=s,i.pendingProps=e.props;else if("string"==typeof s)(i=Tr(Ne,l,n)).type=s,i.pendingProps=e.props;else if("object"==typeof s&&null!==s&&"number"==typeof s.tag)(i=s).pendingProps=e.props;else{var c="";(void 0===s||"object"==typeof s&&null!==s&&0===Object.keys(s).length)&&(c+=" You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.");var u=a?Bt(a):null;u&&(c+="\n\nCheck the render method of `"+u+"`."),o(!1,"Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",null==s?s:typeof s,c)}return i._debugSource=e._source,i._debugOwner=e._owner,i.expirationTime=t,i}function xr(e,n,t,o){var a=Tr(Re,o,n);return a.pendingProps=e,a.expirationTime=t,a}function Sr(e,n,t){var o=Tr(Ce,null,n);return o.pendingProps=e,o.expirationTime=t,o}function Lr(e,n,t){var o=Tr(Ae,e.key,n);return o.type=e.handler,o.pendingProps=e,o.expirationTime=t,o}function Nr(e,n,t){var o=Tr(Oe,null,n);return o.expirationTime=t,o}function Cr(e,n,t){var o=Tr(Le,e.key,n);return o.pendingProps=e.children||[],o.expirationTime=t,o.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},o}function Ar(e,n){var t=Tr(Se,null,br),o={current:t,containerInfo:e,pendingChildren:null,remainingExpirationTime:dr,isReadyForCommit:!1,finishedWork:null,context:null,pendingContext:null,hydrate:n,nextScheduledRoot:null};return t.stateNode=o,o}var Pr=null,Or=null,Rr=!1;function Dr(e){return function(n){try{return e(n)}catch(e){Rr||(Rr=!0,a(!1,"React DevTools encountered an error: %s",e))}}}function Mr(e){"function"==typeof Pr&&Pr(e)}function Fr(e){"function"==typeof Or&&Or(e)}var Br=!1;function Wr(e){var n={baseState:e,expirationTime:dr,first:null,last:null,callbackList:null,hasForceUpdate:!1,isInitialized:!1,isProcessing:!1};return n}function jr(e,n){null===e.last?e.first=e.last=n:(e.last.next=n,e.last=n),(e.expirationTime===dr||e.expirationTime>n.expirationTime)&&(e.expirationTime=n.expirationTime)}function _r(e,n){var t=e.alternate,o=e.updateQueue;null===o&&(o=e.updateQueue=Wr(null));var r=void 0;if(null!==t?null===(r=t.updateQueue)&&(r=t.updateQueue=Wr(null)):r=null,r=r!==o?r:null,(o.isProcessing||null!==r&&r.isProcessing)&&!Br&&(a(!1,"An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using componentDidUpdate or a callback."),Br=!0),null!==r){if(null===o.last||null===r.last)return jr(o,n),void jr(r,n);jr(o,n),r.last=n}else jr(o,n)}function Ur(e,n,t,o){var a=e.partialState;if("function"==typeof a){var r=a;return ca&&r.call(n,t,o),r.call(n,t,o)}return a}function zr(e,n,t,o,a,r){if(null!==e&&e.updateQueue===t){var s=t;t=n.updateQueue={baseState:s.baseState,expirationTime:s.expirationTime,first:s.first,last:s.last,isInitialized:s.isInitialized,callbackList:null,hasForceUpdate:!1}}t.isProcessing=!0,t.expirationTime=dr;var l=void 0;t.isInitialized?l=t.baseState:(l=t.baseState=n.memoizedState,t.isInitialized=!0);for(var c=!0,u=t.first,h=!1;null!==u;){var d=u.expirationTime;if(d>r){var p=t.expirationTime;(p===dr||p>d)&&(t.expirationTime=d),h||(h=!0,t.baseState=l),u=u.next}else{h||(t.first=u.next,null===t.first&&(t.last=null));var f=void 0;if(u.isReplace?(l=Ur(u,o,l,a),c=!0):(f=Ur(u,o,l,a))&&(l=c?i({},l,f):i(l,f),c=!1),u.isForced&&(t.hasForceUpdate=!0),null!==u.callback){var m=t.callbackList;null===m&&(m=t.callbackList=[]),m.push(u)}u=u.next}}return null!==t.callbackList?n.effectTag|=Ht:null!==t.first||t.hasForceUpdate||(n.updateQueue=null),h||(h=!0,t.baseState=l),t.isProcessing=!1,l}function Gr(e,n){var t=e.callbackList;if(null!==t){e.callbackList=null;for(var a=0;a<t.length;a++){var r=t[a],i=r.callback;r.callback=null,"function"!=typeof i&&o(!1,"Invalid argument passed as callback. Expected a function. Instead received: %s",i),i.call(n)}}}var Vr={},Hr=Array.isArray,qr={},Kr=function(e,n){a(null===e||"function"==typeof e,"%s(...): Expected the last optional `callback` argument to be a function. Instead received: %s.",n,e)};Object.defineProperty(Vr,"_processChildContext",{enumerable:!1,value:function(){o(!1,"_processChildContext is not available in React 16+. This likely means you have multiple copies of React and are attempting to nest a React 15 tree inside a React 16 tree using unstable_renderSubtreeIntoContainer, which isn't supported. Try to make sure you have only one copy of React (and ideally, switch to ReactDOM.createPortal).")}}),Object.freeze(Vr);var Yr=function(e,n,t,r){var i={isMounted:Zt,enqueueSetState:function(t,o,a){var r=Rt(t);Kr(a=void 0===a?null:a,"setState");var i=n(r);_r(r,{expirationTime:i,partialState:o,callback:a,isReplace:!1,isForced:!1,nextCallback:null,next:null}),e(r,i)},enqueueReplaceState:function(t,o,a){var r=Rt(t);Kr(a=void 0===a?null:a,"replaceState");var i=n(r);_r(r,{expirationTime:i,partialState:o,callback:a,isReplace:!0,isForced:!1,nextCallback:null,next:null}),e(r,i)},enqueueForceUpdate:function(t,o){var a=Rt(t);Kr(o=void 0===o?null:o,"forceUpdate");var r=n(a);_r(a,{expirationTime:r,partialState:null,callback:o,isReplace:!1,isForced:!0,nextCallback:null,next:null}),e(a,r)}};function s(e,n){var t;n.updater=i,e.stateNode=n,t=e,n._reactInternalFiber=t,n._reactInternalInstance=Vr}return{adoptClassInstance:s,constructClassInstance:function(e,n){var t,o=e.type,a=tr(e),r=(t=e).tag===xe&&null!=t.type.contextTypes,i=r?ar(e,a):p,l=new o(n,i);return s(e,l),r&&or(e,a,i),l},mountClassInstance:function(e,n){var t=e.alternate;!function(e){var n=e.stateNode,t=e.type,o=Bt(e);n.render||(t.prototype&&"function"==typeof t.prototype.render?a(!1,"%s(...): No `render` method found on the returned component instance: did you accidentally return an object from the constructor?",o):a(!1,"%s(...): No `render` method found on the returned component instance: you may have forgotten to define `render`.",o));var r=!n.getInitialState||n.getInitialState.isReactClassApproved||n.state;a(r,"getInitialState was defined on %s, a plain JavaScript class. This is only supported for classes created using React.createClass. Did you mean to define a state property instead?",o);var i=!n.getDefaultProps||n.getDefaultProps.isReactClassApproved;a(i,"getDefaultProps was defined on %s, a plain JavaScript class. This is only supported for classes created using React.createClass. Use a static property to define defaultProps instead.",o);var s=!n.propTypes;a(s,"propTypes was defined as an instance property on %s. Use a static property to define propTypes instead.",o);var l=!n.contextTypes;a(l,"contextTypes was defined as an instance property on %s. Use a static property to define contextTypes instead.",o);var c="function"!=typeof n.componentShouldUpdate;a(c,"%s has a method called componentShouldUpdate(). Did you mean shouldComponentUpdate()? The name is phrased as a question because the function is expected to return a value.",o),t.prototype&&t.prototype.isPureReactComponent&&void 0!==n.shouldComponentUpdate&&a(!1,"%s has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.",Bt(e)||"A pure component");var u="function"!=typeof n.componentDidUnmount;a(u,"%s has a method called componentDidUnmount(). But there is no such lifecycle method. Did you mean componentWillUnmount()?",o);var h="function"!=typeof n.componentDidReceiveProps;a(h,"%s has a method called componentDidReceiveProps(). But there is no such lifecycle method. If you meant to update the state in response to changing props, use componentWillReceiveProps(). If you meant to fetch data or run side-effects or mutations after React has updated the UI, use componentDidUpdate().",o);var d="function"!=typeof n.componentWillRecieveProps;a(d,"%s has a method called componentWillRecieveProps(). Did you mean componentWillReceiveProps()?",o);var p=n.props!==e.pendingProps;a(void 0===n.props||!p,"%s(...): When calling super() in `%s`, make sure to pass up the same props that your component's constructor was passed.",o,o);var f=!n.defaultProps;a(f,"Setting defaultProps as an instance property on %s is not supported and will be ignored. Instead, define defaultProps as a static property on %s.",o,o);var m=n.state;m&&("object"!=typeof m||Hr(m))&&a(!1,"%s.state: must be set to an object or null",Bt(e)),"function"==typeof n.getChildContext&&a("object"==typeof e.type.childContextTypes,"%s.getChildContext(): childContextTypes must be defined in order to use getChildContext().",Bt(e))}(e);var r=e.stateNode,s=r.state||null,l=e.pendingProps;l||o(!1,"There must be pending props for an initial mount. This error is likely caused by a bug in React. Please file an issue.");var c=tr(e);if(r.props=l,r.state=e.memoizedState=s,r.refs=p,r.context=ar(e,c),ra&&null!=e.type&&null!=e.type.prototype&&!0===e.type.prototype.unstable_isAsyncReactComponent&&(e.internalContextTag|=wr),"function"==typeof r.componentWillMount){!function(e,n){Qa(e,"componentWillMount");var t=n.state;n.componentWillMount(),$a(),ca&&n.componentWillMount(),t!==n.state&&(a(!1,"%s.componentWillMount(): Assigning directly to this.state is deprecated (except inside a component's constructor). Use setState instead.",Bt(e)),i.enqueueReplaceState(n,n.state,null))}(e,r);var u=e.updateQueue;null!==u&&(r.state=zr(t,e,u,r,l,n))}"function"==typeof r.componentDidMount&&(e.effectTag|=Ut)},updateClassInstance:function(e,n,s){var l,c,h=n.stateNode;l=n,(c=h).props=l.memoizedProps,c.state=l.memoizedState;var d=n.memoizedProps,p=n.pendingProps;p||null==(p=d)&&o(!1,"There should always be pending or memoized props. This error is likely caused by a bug in React. Please file an issue.");var f=h.context,m=ar(n,tr(n));"function"!=typeof h.componentWillReceiveProps||d===p&&f===m||function(e,n,t,o){Qa(e,"componentWillReceiveProps");var r=n.state;if(n.componentWillReceiveProps(t,o),$a(),ca&&n.componentWillReceiveProps(t,o),n.state!==r){var s=Bt(e)||"Component";qr[s]||(a(!1,"%s.componentWillReceiveProps(): Assigning directly to this.state is deprecated (except inside a component's constructor). Use setState instead.",s),qr[s]=!0),i.enqueueReplaceState(n,n.state,null)}}(n,h,p,m);var g=n.memoizedState,y=void 0;if(y=null!==n.updateQueue?zr(e,n,n.updateQueue,h,p,s):g,!(d!==p||g!==y||rr()||null!==n.updateQueue&&n.updateQueue.hasForceUpdate))return"function"==typeof h.componentDidUpdate&&(d===e.memoizedProps&&g===e.memoizedState||(n.effectTag|=Ut)),!1;var b=function(e,n,t,o,r,i){if(null===n||null!==e.updateQueue&&e.updateQueue.hasForceUpdate)return!0;var s=e.stateNode,l=e.type;if("function"==typeof s.shouldComponentUpdate){Qa(e,"shouldComponentUpdate");var c=s.shouldComponentUpdate(t,r,i);return $a(),ca&&s.shouldComponentUpdate(t,r,i),a(void 0!==c,"%s.shouldComponentUpdate(): Returned undefined instead of a boolean value. Make sure to return true or false.",Bt(e)||"Unknown"),c}return!(l.prototype&&l.prototype.isPureReactComponent&&u(n,t)&&u(o,r))}(n,d,p,g,y,m);return b?("function"==typeof h.componentWillUpdate&&(Qa(n,"componentWillUpdate"),h.componentWillUpdate(p,y,m),$a(),ca&&h.componentWillUpdate(p,y,m)),"function"==typeof h.componentDidUpdate&&(n.effectTag|=Ut)):("function"==typeof h.componentDidUpdate&&(d===e.memoizedProps&&g===e.memoizedState||(n.effectTag|=Ut)),t(n,p),r(n,y)),h.props=p,h.state=y,h.context=m,b}}},Xr="function"==typeof Symbol&&Symbol.for,Qr=Xr?Symbol.for("react.element"):60103,$r=Xr?Symbol.for("react.call"):60104,Jr=Xr?Symbol.for("react.return"):60105,Zr=Xr?Symbol.for("react.portal"):60106,ei=Xr?Symbol.for("react.fragment"):60107,ni="function"==typeof Symbol&&Symbol.iterator,ti="@@iterator";function oi(e){if(null===e||void 0===e)return null;var n=ni&&e[ni]||e[ti];return"function"==typeof n?n:null}var ai=va.getCurrentFiberStackAddendum,ri=!1,ii={},si={},li=function(e){if(null!==e&&"object"==typeof e&&e._store&&!e._store.validated&&null==e.key){"object"!=typeof e._store&&o(!1,"React Component in warnForMissingKey should have a _store. This error is likely caused by a bug in React. Please file an issue."),e._store.validated=!0;var n='Each child in an array or iterator should have a unique "key" prop. See https://fb.me/react-warning-keys for more information.'+(ai()||"");ii[n]||(ii[n]=!0,a(!1,'Each child in an array or iterator should have a unique "key" prop. See https://fb.me/react-warning-keys for more information.%s',ai()))}},ci=Array.isArray;function ui(e,n){var t=n.ref;if(null!==t&&"function"!=typeof t){if(n._owner){var a=n._owner,r=void 0;if(a){var i=a;i.tag!==xe&&o(!1,"Stateless function components cannot have refs."),r=i.stateNode}r||o(!1,"Missing owner for string ref %s. This error is likely caused by a bug in React. Please file an issue.",t);var s=""+t;if(null!==e&&null!==e.ref&&e.ref._stringRef===s)return e.ref;var l=function(e){var n=r.refs===p?r.refs={}:r.refs;null===e?delete n[s]:n[s]=e};return l._stringRef=s,l}"string"!=typeof t&&o(!1,"Expected ref to be a function or a string."),n._owner||o(!1,"Element ref was specified as a string (%s) but no owner was set. You may have multiple copies of React loaded. (details: https://fb.me/react-refs-must-have-owner).",t)}return t}function hi(e,n){if("textarea"!==e.type){var t;t=" If you meant to render a collection of children, use an array instead."+(ai()||""),o(!1,"Objects are not valid as a React child (found: %s).%s","[object Object]"===Object.prototype.toString.call(n)?"object with keys {"+Object.keys(n).join(", ")+"}":n,t)}}function di(){var e="Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it."+(ai()||"");si[e]||(si[e]=!0,a(!1,"Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.%s",ai()||""))}function pi(e){function n(n,t){if(e){var o=n.lastEffect;null!==o?(o.nextEffect=t,n.lastEffect=t):n.firstEffect=n.lastEffect=t,t.nextEffect=null,t.effectTag=Gt}}function t(t,o){if(!e)return null;for(var a=o;null!==a;)n(t,a),a=a.sibling;return null}function r(e,n){for(var t=new Map,o=n;null!==o;)null!==o.key?t.set(o.key,o):t.set(o.index,o),o=o.sibling;return t}function i(e,n,t){var o=Ir(e,n,t);return o.index=0,o.sibling=null,o}function s(n,t,o){if(n.index=o,!e)return t;var a=n.alternate;if(null!==a){var r=a.index;return r<t?(n.effectTag=_t,t):r}return n.effectTag=_t,t}function l(n){return e&&null===n.alternate&&(n.effectTag=_t),n}function c(e,n,t,o){if(null===n||n.tag!==Ce){var a=Sr(t,e.internalContextTag,o);return a.return=e,a}var r=i(n,t,o);return r.return=e,r}function u(e,n,t,o){if(null!==n&&n.type===t.type){var a=i(n,t.props,o);return a.ref=ui(n,t),a.return=e,a._debugSource=t._source,a._debugOwner=t._owner,a}var r=Er(t,e.internalContextTag,o);return r.ref=ui(n,t),r.return=e,r}function h(e,n,t,o){if(null===n||n.tag!==Ae){var a=Lr(t,e.internalContextTag,o);return a.return=e,a}var r=i(n,t,o);return r.return=e,r}function d(e,n,t,o){if(null===n||n.tag!==Oe){var a=Nr(0,e.internalContextTag,o);return a.type=t.value,a.return=e,a}var r=i(n,null,o);return r.type=t.value,r.return=e,r}function p(e,n,t,o){if(null===n||n.tag!==Le||n.stateNode.containerInfo!==t.containerInfo||n.stateNode.implementation!==t.implementation){var a=Cr(t,e.internalContextTag,o);return a.return=e,a}var r=i(n,t.children||[],o);return r.return=e,r}function f(e,n,t,o,a){if(null===n||n.tag!==Re){var r=xr(t,e.internalContextTag,o,a);return r.return=e,r}var s=i(n,t,o);return s.return=e,s}function m(e,n,t){if("string"==typeof n||"number"==typeof n){var o=Sr(""+n,e.internalContextTag,t);return o.return=e,o}if("object"==typeof n&&null!==n){switch(n.$$typeof){case Qr:if(n.type===ei){var a=xr(n.props.children,e.internalContextTag,t,n.key);return a.return=e,a}var r=Er(n,e.internalContextTag,t);return r.ref=ui(null,n),r.return=e,r;case $r:var i=Lr(n,e.internalContextTag,t);return i.return=e,i;case Jr:var s=Nr(0,e.internalContextTag,t);return s.type=n.value,s.return=e,s;case Zr:var l=Cr(n,e.internalContextTag,t);return l.return=e,l}if(ci(n)||oi(n)){var c=xr(n,e.internalContextTag,t,null);return c.return=e,c}hi(e,n)}return"function"==typeof n&&di(),null}function g(e,n,t,o){var a=null!==n?n.key:null;if("string"==typeof t||"number"==typeof t)return null!==a?null:c(e,n,""+t,o);if("object"==typeof t&&null!==t){switch(t.$$typeof){case Qr:return t.key===a?t.type===ei?f(e,n,t.props.children,o,a):u(e,n,t,o):null;case $r:return t.key===a?h(e,n,t,o):null;case Jr:return null===a?d(e,n,t,o):null;case Zr:return t.key===a?p(e,n,t,o):null}if(ci(t)||oi(t))return null!==a?null:f(e,n,t,o,null);hi(e,t)}return"function"==typeof t&&di(),null}function y(e,n,t,o,a){if("string"==typeof o||"number"==typeof o)return c(n,e.get(t)||null,""+o,a);if("object"==typeof o&&null!==o){switch(o.$$typeof){case Qr:var r=e.get(null===o.key?t:o.key)||null;return o.type===ei?f(n,r,o.props.children,a,o.key):u(n,r,o,a);case $r:return h(n,e.get(null===o.key?t:o.key)||null,o,a);case Jr:return d(n,e.get(t)||null,o,a);case Zr:return p(n,e.get(null===o.key?t:o.key)||null,o,a)}if(ci(o)||oi(o))return f(n,e.get(t)||null,o,a,null);hi(n,o)}return"function"==typeof o&&di(),null}function b(e,n){if("object"!=typeof e||null===e)return n;switch(e.$$typeof){case Qr:case $r:case Zr:li(e);var t=e.key;if("string"!=typeof t)break;if(null===n){(n=new Set).add(t);break}if(!n.has(t)){n.add(t);break}a(!1,"Encountered two children with the same key, `%s`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.%s",t,ai())}return n}return function(c,u,h,d){"object"==typeof h&&null!==h&&h.type===ei&&null===h.key&&(h=h.props.children);var p="object"==typeof h&&null!==h;if(p)switch(h.$$typeof){case Qr:return l(function(e,o,a,r){for(var s=a.key,l=o;null!==l;){if(l.key===s){if(l.tag===Re?a.type===ei:l.type===a.type){t(e,l.sibling);var c=i(l,a.type===ei?a.props.children:a.props,r);return c.ref=ui(l,a),c.return=e,c._debugSource=a._source,c._debugOwner=a._owner,c}t(e,l);break}n(e,l),l=l.sibling}if(a.type===ei){var u=xr(a.props.children,e.internalContextTag,r,a.key);return u.return=e,u}var h=Er(a,e.internalContextTag,r);return h.ref=ui(o,a),h.return=e,h}(c,u,h,d));case $r:return l(function(e,o,a,r){for(var s=a.key,l=o;null!==l;){if(l.key===s){if(l.tag===Ae){t(e,l.sibling);var c=i(l,a,r);return c.return=e,c}t(e,l);break}n(e,l),l=l.sibling}var u=Lr(a,e.internalContextTag,r);return u.return=e,u}(c,u,h,d));case Jr:return l(function(e,n,o,a){var r=n;if(null!==r){if(r.tag===Oe){t(e,r.sibling);var s=i(r,null,a);return s.type=o.value,s.return=e,s}t(e,r)}var l=Nr(0,e.internalContextTag,a);return l.type=o.value,l.return=e,l}(c,u,h,d));case Zr:return l(function(e,o,a,r){for(var s=a.key,l=o;null!==l;){if(l.key===s){if(l.tag===Le&&l.stateNode.containerInfo===a.containerInfo&&l.stateNode.implementation===a.implementation){t(e,l.sibling);var c=i(l,a.children||[],r);return c.return=e,c}t(e,l);break}n(e,l),l=l.sibling}var u=Cr(a,e.internalContextTag,r);return u.return=e,u}(c,u,h,d))}if("string"==typeof h||"number"==typeof h)return l(function(e,n,o,a){if(null!==n&&n.tag===Ce){t(e,n.sibling);var r=i(n,o,a);return r.return=e,r}t(e,n);var s=Sr(o,e.internalContextTag,a);return s.return=e,s}(c,u,""+h,d));if(ci(h))return function(o,a,i,l){for(var c=null,u=0;u<i.length;u++)c=b(i[u],c);for(var h=null,d=null,p=a,f=0,w=0,v=null;null!==p&&w<i.length;w++){p.index>w?(v=p,p=null):v=p.sibling;var k=g(o,p,i[w],l);if(null===k){null===p&&(p=v);break}e&&p&&null===k.alternate&&n(o,p),f=s(k,f,w),null===d?h=k:d.sibling=k,d=k,p=v}if(w===i.length)return t(o,p),h;if(null===p){for(;w<i.length;w++){var T=m(o,i[w],l);T&&(f=s(T,f,w),null===d?h=T:d.sibling=T,d=T)}return h}for(var I=r(0,p);w<i.length;w++){var E=y(I,o,w,i[w],l);E&&(e&&null!==E.alternate&&I.delete(null===E.key?w:E.key),f=s(E,f,w),null===d?h=E:d.sibling=E,d=E)}return e&&I.forEach(function(e){return n(o,e)}),h}(c,u,h,d);if(oi(h))return function(i,l,c,u){var h=oi(c);"function"!=typeof h&&o(!1,"An object is not an iterable. This error is likely caused by a bug in React. Please file an issue."),"function"==typeof c.entries&&c.entries===h&&(a(ri,"Using Maps as children is unsupported and will likely yield unexpected results. Convert it to a sequence/iterable of keyed ReactElements instead.%s",ai()),ri=!0);var d=h.call(c);if(d)for(var p=null,f=d.next();!f.done;f=d.next())p=b(f.value,p);var w=h.call(c);null==w&&o(!1,"An iterable object provided no iterator.");for(var v=null,k=null,T=l,I=0,E=0,x=null,S=w.next();null!==T&&!S.done;E++,S=w.next()){T.index>E?(x=T,T=null):x=T.sibling;var L=g(i,T,S.value,u);if(null===L){T||(T=x);break}e&&T&&null===L.alternate&&n(i,T),I=s(L,I,E),null===k?v=L:k.sibling=L,k=L,T=x}if(S.done)return t(i,T),v;if(null===T){for(;!S.done;E++,S=w.next()){var N=m(i,S.value,u);null!==N&&(I=s(N,I,E),null===k?v=N:k.sibling=N,k=N)}return v}for(var C=r(0,T);!S.done;E++,S=w.next()){var A=y(C,i,E,S.value,u);null!==A&&(e&&null!==A.alternate&&C.delete(null===A.key?E:A.key),I=s(A,I,E),null===k?v=A:k.sibling=A,k=A)}return e&&C.forEach(function(e){return n(i,e)}),v}(c,u,h,d);if(p&&hi(c,h),"function"==typeof h&&di(),void 0===h)switch(c.tag){case xe:if(c.stateNode.render._isMockFunction)break;case Ee:var f=c.type;o(!1,"%s(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.",f.displayName||f.name||"Component")}return t(c,u)}}var fi=pi(!0),mi=pi(!1);var gi={},yi=function(e,n,t,r,i){var s=e.shouldSetTextContent,l=e.useSyncScheduling,c=e.shouldDeprioritizeSubtree,u=n.pushHostContext,h=n.pushHostContainer,d=t.enterHydrationState,p=t.resetHydrationState,f=t.tryToClaimNextHydratableInstance,m=Yr(r,i,L,N),g=m.adoptClassInstance,y=m.constructClassInstance,b=m.mountClassInstance,w=m.updateClassInstance;function v(e,n,t){k(e,n,t,n.expirationTime)}function k(e,n,t,o){n.child=null===e?mi(n,null,t,o):fi(n,e.child,t,o)}function T(e,n){var t=n.ref;null===t||e&&e.ref===t||(n.effectTag|=Kt)}function I(e,n,t,o){if(T(e,n),!t)return o&&hr(n,!1),x(e,n);var a=n.stateNode;Mt.current=n;var r;return va.setCurrentPhase("render"),r=a.render(),ca&&a.render(),va.setCurrentPhase(null),n.effectTag|=jt,v(e,n,r),N(n,a.state),L(n,a.props),o&&hr(n,!0),n.child}function E(e){var n=e.stateNode;n.pendingContext?lr(e,n.pendingContext,n.pendingContext!==n.context):n.context&&lr(e,n.context,!1),h(e,n.containerInfo)}function x(e,n){return Ka(n),function(e,n){if(null!==e&&n.child!==e.child&&o(!1,"Resuming work not yet implemented."),null!==n.child){var t=n.child,a=Ir(t,t.pendingProps,t.expirationTime);for(n.child=a,a.return=n;null!==t.sibling;)t=t.sibling,(a=a.sibling=Ir(t,t.pendingProps,t.expirationTime)).return=n;a.sibling=null}}(e,n),n.child}function S(e,n){switch(Ka(n),n.tag){case Se:E(n);break;case xe:ur(n);break;case Le:h(n,n.stateNode.containerInfo)}return null}function L(e,n){e.memoizedProps=n}function N(e,n){e.memoizedState=n}return{beginWork:function(e,n,t){if(n.expirationTime===dr||n.expirationTime>t)return S(0,n);switch(n.tag){case Ie:return function(e,n,t){null!==e&&o(!1,"An indeterminate component should never have mounted. This error is likely caused by a bug in React. Please file an issue.");var r,i=n.type,s=n.pendingProps,l=ar(n,tr(n));if(i.prototype&&"function"==typeof i.prototype.render){var c=Bt(n);a(!1,"The <%s /> component appears to have a render method, but doesn't extend React.Component. This is likely to cause errors. Change %s to extend React.Component instead.",c,c)}if(Mt.current=n,r=i(s,l),n.effectTag|=jt,"object"==typeof r&&null!==r&&"function"==typeof r.render){n.tag=xe;var u=ur(n);return g(n,r),b(n,t),I(e,n,!0,u)}n.tag=Ee;var h=n.type;if(h&&a(!h.childContextTypes,"%s(...): childContextTypes cannot be defined on a functional component.",h.displayName||h.name||"Component"),null!==n.ref){var d="",p=va.getCurrentFiberOwnerName();p&&(d+="\n\nCheck the render method of `"+p+"`.");var f=p||n._debugID||"",m=n._debugSource;m&&(f=m.fileName+":"+m.lineNumber),gi[f]||(gi[f]=!0,a(!1,"Stateless function components cannot be given refs. Attempts to access this ref will fail.%s%s",d,va.getCurrentFiberStackAddendum()))}return v(e,n,r),L(n,s),n.child}(e,n,t);case Ee:return function(e,n){var t=n.type,o=n.pendingProps,a=n.memoizedProps;if(rr())null===o&&(o=a);else if(null===o||a===o)return x(e,n);var r,i=ar(n,tr(n));return Mt.current=n,va.setCurrentPhase("render"),r=t(o,i),va.setCurrentPhase(null),n.effectTag|=jt,v(e,n,r),L(n,o),n.child}(e,n);case xe:return r=e,m=t,k=ur(i=n),C=void 0,null===r?i.stateNode?o(!1,"Resuming work not yet implemented."):(y(i,i.pendingProps),b(i,m),C=!0):C=w(r,i,m),I(r,i,C,k);case Se:return function(e,n,t){E(n);var o=n.updateQueue;if(null!==o){var a=n.memoizedState,r=zr(e,n,o,null,null,t);if(a===r)return p(),x(e,n);var i=r.element,s=n.stateNode;return(null===e||null===e.child)&&s.hydrate&&d(n)?(n.effectTag|=_t,n.child=mi(n,null,i,t)):(p(),v(e,n,i)),N(n,r),n.child}return p(),x(e,n)}(e,n,t);case Ne:return function(e,n,t){u(n),null===e&&f(n);var a=n.type,r=n.memoizedProps,i=n.pendingProps;null===i&&null===(i=r)&&o(!1,"We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue.");var h=null!==e?e.memoizedProps:null;if(rr());else if(null===i||r===i)return x(e,n);var d=i.children;return s(a,i)?d=null:h&&s(a,h)&&(n.effectTag|=Vt),T(e,n),t!==fr&&!l&&c(a,i)?(n.expirationTime=fr,null):(v(e,n,d),L(n,i),n.child)}(e,n,t);case Ce:return function(e,n){null===e&&f(n);var t=n.pendingProps;return null===t&&(t=n.memoizedProps),L(n,t),null}(e,n);case Pe:n.tag=Ae;case Ae:return function(e,n,t){var a=n.pendingProps;rr()?null===a&&null===(a=e&&e.memoizedProps)&&o(!1,"We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue."):null!==a&&n.memoizedProps!==a||(a=n.memoizedProps);var r=a.children;return n.stateNode=null===e?mi(n,n.stateNode,r,t):fi(n,n.stateNode,r,t),L(n,a),n.stateNode}(e,n,t);case Oe:return null;case Le:return function(e,n,t){h(n,n.stateNode.containerInfo);var a=n.pendingProps;if(rr())null===a&&null==(a=e&&e.memoizedProps)&&o(!1,"We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue.");else if(null===a||n.memoizedProps===a)return x(e,n);return null===e?(n.child=fi(n,null,a,t),L(n,a)):(v(e,n,a),L(n,a)),n.child}(e,n,t);case Re:return function(e,n){var t=n.pendingProps;if(rr())null===t&&(t=n.memoizedProps);else if(null===t||n.memoizedProps===t)return x(e,n);return v(e,n,t),L(n,t),n.child}(e,n);default:o(!1,"Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.")}var r,i,m,k,C},beginFailedWork:function(e,n,t){switch(n.tag){case xe:ur(n);break;case Se:E(n);break;default:o(!1,"Invalid type of work. This error is likely caused by a bug in React. Please file an issue.")}if(n.effectTag|=qt,null===e?n.child=null:n.child!==e.child&&(n.child=e.child),n.expirationTime===dr||n.expirationTime>t)return S(0,n);if(n.firstEffect=null,n.lastEffect=null,k(e,n,null,t),n.tag===xe){var a=n.stateNode;n.memoizedProps=a.props,n.memoizedState=a.state}return n.child}}},bi=function(e,n,t){var a=e.createInstance,r=e.createTextInstance,i=e.appendInitialChild,s=e.finalizeInitialChildren,l=e.prepareUpdate,c=e.mutation,u=e.persistence,h=n.getRootHostContainer,d=n.popHostContext,p=n.getHostContext,f=n.popHostContainer,m=t.prepareToHydrateHostInstance,g=t.prepareToHydrateHostTextInstance,y=t.popHydrationState;function b(e){e.effectTag|=Ut}function w(e){e.effectTag|=Kt}function v(e,n,t){var a=n.memoizedProps;a||o(!1,"Should be resolved by now. This error is likely caused by a bug in React. Please file an issue."),n.tag=Pe;var r=[];!function(e,n){var t=n.stateNode;for(t&&(t.return=n);null!==t;){if(t.tag===Ne||t.tag===Ce||t.tag===Le)o(!1,"A call cannot have host component children.");else if(t.tag===Oe)e.push(t.type);else if(null!==t.child){t.child.return=t,t=t.child;continue}for(;null===t.sibling;){if(null===t.return||t.return===n)return;t=t.return}t.sibling.return=t.return,t=t.sibling}}(r,n);var i=(0,a.handler)(a.props,r),s=null!==e?e.child:null;return n.child=fi(n,s,i,t),n.child}function k(e,n){for(var t=n.child;null!==t;){if(t.tag===Ne||t.tag===Ce)i(e,t.stateNode);else if(t.tag===Le);else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===n)return;for(;null===t.sibling;){if(null===t.return||t.return===n)return;t=t.return}t.sibling.return=t.return,t=t.sibling}}var T=void 0,I=void 0,E=void 0;if(c)sa?(T=function(e){},I=function(e,n,t,o,a,r,i){n.updateQueue=t,t&&b(n)},E=function(e,n,t,o){t!==o&&b(n)}):o(!1,"Mutating reconciler is disabled.");else if(u)if(la){var x=u.cloneInstance,S=u.createContainerChildSet,L=u.appendChildToContainerChildSet,N=u.finalizeContainerChildren;T=function(e){var n=e.stateNode;if(null===e.firstEffect);else{var t=n.containerInfo,o=S(t);N(t,o)&&b(e),n.pendingChildren=o,function(e,n){for(var t=n.child;null!==t;){if(t.tag===Ne||t.tag===Ce)L(e,t.stateNode);else if(t.tag===Le);else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===n)return;for(;null===t.sibling;){if(null===t.return||t.return===n)return;t=t.return}t.sibling.return=t.return,t=t.sibling}}(o,e),b(e)}},I=function(e,n,t,o,a,r,i){var l=null===n.firstEffect,c=e.stateNode;if(l&&null===t)n.stateNode=c;else{var u=n.stateNode,h=x(c,t,o,a,r,n,l,u);s(h,o,r,i)&&b(n),n.stateNode=h,l?b(n):k(h,n)}},E=function(e,n,t,o){if(t!==o){var a=h(),i=p();n.stateNode=r(o,a,i,n),b(n)}}}else o(!1,"Persistent reconciler is disabled.");else o(!1,"Noop reconciler is disabled.");return{completeWork:function(e,n,t){var i,c=n.pendingProps;switch(null===c?c=n.memoizedProps:n.expirationTime===fr&&t!==fr||(n.pendingProps=null),n.tag){case Ee:return null;case xe:return sr(n),null;case Se:f(n),fa(er,i=n),fa(Za,i);var u=n.stateNode;return u.pendingContext&&(u.context=u.pendingContext,u.pendingContext=null),null!==e&&null!==e.child||(y(n),n.effectTag&=~_t),T(n),null;case Ne:d(n);var x=h(),S=n.type;if(null!==e&&null!=n.stateNode){var L=e.memoizedProps,N=n.stateNode,C=p(),A=l(N,S,L,c,x,C);I(e,n,A,S,L,c,x),e.ref!==n.ref&&w(n)}else{if(!c)return null===n.stateNode&&o(!1,"We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."),null;var P=p();if(y(n))m(n,x,P)&&b(n);else{var O=a(S,c,x,P,n);k(O,n),s(O,S,c,x)&&b(n),n.stateNode=O}null!==n.ref&&w(n)}return null;case Ce:var R=c;if(e&&null!=n.stateNode){var D=e.memoizedProps;E(e,n,D,R)}else{if("string"!=typeof R)return null===n.stateNode&&o(!1,"We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."),null;var M=h(),F=p();y(n)?g(n)&&b(n):n.stateNode=r(R,M,F,n)}return null;case Ae:return v(e,n,t);case Pe:return n.tag=Ae,null;case Oe:case Re:return null;case Le:return f(n),T(n),null;case Ie:o(!1,"An indeterminate component should have become determinate before completing. This error is likely caused by a bug in React. Please file an issue.");default:o(!1,"Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.")}}}},wi=z.invokeGuardedCallback,vi=z.hasCaughtError,ki=z.clearCaughtError,Ti=function(e,n){var t=e.getPublicInstance,a=e.mutation,r=e.persistence,i=function(e,n){Qa(e,"componentWillUnmount"),n.props=e.memoizedProps,n.state=e.memoizedState,n.componentWillUnmount(),$a()};function s(e){var t=e.ref;if(null!==t&&(wi(null,t,null,null),vi())){var o=ki();n(e,o)}}function l(e,n){switch(n.tag){case xe:var t=n.stateNode;if(n.effectTag&Ut)if(null===e)Qa(n,"componentDidMount"),t.props=n.memoizedProps,t.state=n.memoizedState,t.componentDidMount(),$a();else{var a=e.memoizedProps,r=e.memoizedState;Qa(n,"componentDidUpdate"),t.props=n.memoizedProps,t.state=n.memoizedState,t.componentDidUpdate(a,r),$a()}var i=n.updateQueue;return void(null!==i&&Gr(i,t));case Se:var s=n.updateQueue;if(null!==s)Gr(s,null!==n.child?n.child.stateNode:null);return;case Ne:var l=n.stateNode;if(null===e&&n.effectTag&Ut){var c=n.type,u=n.memoizedProps;b(l,c,u,n)}return;case Ce:case Le:return;default:o(!1,"This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.")}}function c(e){var n=e.ref;if(null!==n){var o=e.stateNode;switch(e.tag){case Ne:n(t(o));break;default:n(o)}}}function u(e){var n=e.ref;null!==n&&n(null)}function h(e){switch(Fr(e),e.tag){case xe:s(e);var t=e.stateNode;return void("function"==typeof t.componentWillUnmount&&function(e,t){if(wi(null,i,null,e,t),vi()){var o=ki();n(e,o)}}(e,t));case Ne:return void s(e);case Ae:return void d(e.stateNode);case Le:return void(sa&&a?C(e):la&&r&&y(e))}}function d(e){for(var n=e;;)if(h(n),null===n.child||a&&n.tag===Le){if(n===e)return;for(;null===n.sibling;){if(null===n.return||n.return===e)return;n=n.return}n.sibling.return=n.return,n=n.sibling}else n.child.return=n,n=n.child}function p(e){e.return=null,e.child=null,e.alternate&&(e.alternate.child=null,e.alternate.return=null)}if(!a){var f=void 0;if(r){var m=r.replaceContainerChildren,g=r.createContainerChildSet,y=function(e){var n=e.stateNode.containerInfo,t=g(n);m(n,t)};f=function(e){switch(e.tag){case xe:case Ne:case Ce:return;case Se:case Le:var n=e.stateNode,t=n.containerInfo,a=n.pendingChildren;return void m(t,a);default:o(!1,"This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.")}}}else f=function(e){};if(la)return{commitResetTextContent:function(e){},commitPlacement:function(e){},commitDeletion:function(e){d(e),p(e)},commitWork:function(e,n){f(n)},commitLifeCycles:l,commitAttachRef:c,commitDetachRef:u};o(!1,r?"Persistent reconciler is disabled.":"Noop reconciler is disabled.")}var b=a.commitMount,w=a.commitUpdate,v=a.resetTextContent,k=a.commitTextUpdate,T=a.appendChild,I=a.appendChildToContainer,E=a.insertBefore,x=a.insertInContainerBefore,S=a.removeChild,L=a.removeChildFromContainer;function N(e){return e.tag===Ne||e.tag===Se||e.tag===Le}function C(e){for(var n=e,t=!1,a=void 0,r=void 0;;){if(!t){var i=n.return;e:for(;;){switch(null===i&&o(!1,"Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue."),i.tag){case Ne:a=i.stateNode,r=!1;break e;case Se:case Le:a=i.stateNode.containerInfo,r=!0;break e}i=i.return}t=!0}if(n.tag===Ne||n.tag===Ce)d(n),r?L(a,n.stateNode):S(a,n.stateNode);else if(n.tag===Le){if(a=n.stateNode.containerInfo,null!==n.child){n.child.return=n,n=n.child;continue}}else if(h(n),null!==n.child){n.child.return=n,n=n.child;continue}if(n===e)return;for(;null===n.sibling;){if(null===n.return||n.return===e)return;(n=n.return).tag===Le&&(t=!1)}n.sibling.return=n.return,n=n.sibling}}if(sa)return{commitResetTextContent:function(e){v(e.stateNode)},commitPlacement:function(e){var n=function(e){for(var n=e.return;null!==n;){if(N(n))return n;n=n.return}o(!1,"Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue.")}(e),t=void 0,a=void 0;switch(n.tag){case Ne:t=n.stateNode,a=!1;break;case Se:case Le:t=n.stateNode.containerInfo,a=!0;break;default:o(!1,"Invalid host parent fiber. This error is likely caused by a bug in React. Please file an issue.")}n.effectTag&Vt&&(v(t),n.effectTag&=~Vt);for(var r=function(e){var n=e;e:for(;;){for(;null===n.sibling;){if(null===n.return||N(n.return))return null;n=n.return}for(n.sibling.return=n.return,n=n.sibling;n.tag!==Ne&&n.tag!==Ce;){if(n.effectTag&_t)continue e;if(null===n.child||n.tag===Le)continue e;n.child.return=n,n=n.child}if(!(n.effectTag&_t))return n.stateNode}}(e),i=e;;){if(i.tag===Ne||i.tag===Ce)r?a?x(t,i.stateNode,r):E(t,i.stateNode,r):a?I(t,i.stateNode):T(t,i.stateNode);else if(i.tag===Le);else if(null!==i.child){i.child.return=i,i=i.child;continue}if(i===e)return;for(;null===i.sibling;){if(null===i.return||i.return===e)return;i=i.return}i.sibling.return=i.return,i=i.sibling}},commitDeletion:function(e){C(e),p(e)},commitWork:function(e,n){switch(n.tag){case xe:return;case Ne:var t=n.stateNode;if(null!=t){var a=n.memoizedProps,r=null!==e?e.memoizedProps:a,i=n.type,s=n.updateQueue;n.updateQueue=null,null!==s&&w(t,s,i,r,a,n)}return;case Ce:null===n.stateNode&&o(!1,"This should have a text node initialized. This error is likely caused by a bug in React. Please file an issue.");var l=n.stateNode,c=n.memoizedProps,u=null!==e?e.memoizedProps:c;return void k(l,u,c);case Se:return;default:o(!1,"This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.")}},commitLifeCycles:l,commitAttachRef:c,commitDetachRef:u};o(!1,"Mutating reconciler is disabled.")},Ii={},Ei=function(e){var n=e.shouldSetTextContent,t=e.hydration;if(!t)return{enterHydrationState:function(){return!1},resetHydrationState:function(){},tryToClaimNextHydratableInstance:function(){},prepareToHydrateHostInstance:function(){o(!1,"Expected prepareToHydrateHostInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.")},prepareToHydrateHostTextInstance:function(){o(!1,"Expected prepareToHydrateHostTextInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.")},popHydrationState:function(e){return!1}};var a=t.canHydrateInstance,r=t.canHydrateTextInstance,i=t.getNextHydratableSibling,s=t.getFirstHydratableChild,l=t.hydrateInstance,c=t.hydrateTextInstance,u=t.didNotMatchHydratedContainerTextInstance,h=t.didNotMatchHydratedTextInstance,d=t.didNotHydrateContainerInstance,p=t.didNotHydrateInstance,f=t.didNotFindHydratableContainerInstance,m=t.didNotFindHydratableContainerTextInstance,g=t.didNotFindHydratableInstance,y=t.didNotFindHydratableTextInstance,b=null,w=null,v=!1;function k(e,n){switch(e.tag){case Se:d(e.stateNode.containerInfo,n);break;case Ne:p(e.type,e.memoizedProps,e.stateNode,n)}var t,o=((t=Tr(Ne,null,br)).type="DELETED",t);o.stateNode=n,o.return=e,o.effectTag=Gt,null!==e.lastEffect?(e.lastEffect.nextEffect=o,e.lastEffect=o):e.firstEffect=e.lastEffect=o}function T(e,n){switch(n.effectTag|=_t,e.tag){case Se:var t=e.stateNode.containerInfo;switch(n.tag){case Ne:var o=n.type,a=n.pendingProps;f(t,o,a);break;case Ce:var r=n.pendingProps;m(t,r)}break;case Ne:var i=e.type,s=e.memoizedProps,l=e.stateNode;switch(n.tag){case Ne:var c=n.type,u=n.pendingProps;g(i,s,l,c,u);break;case Ce:var h=n.pendingProps;y(i,s,l,h)}break;default:return}}function I(e,n){switch(e.tag){case Ne:var t=e.type,o=e.pendingProps,i=a(n,t,o);return null!==i&&(e.stateNode=i,!0);case Ce:var s=e.pendingProps,l=r(n,s);return null!==l&&(e.stateNode=l,!0);default:return!1}}function E(e){for(var n=e.return;null!==n&&n.tag!==Ne&&n.tag!==Se;)n=n.return;b=n}return{enterHydrationState:function(e){var n=e.stateNode.containerInfo;return w=s(n),b=e,v=!0,!0},resetHydrationState:function(){b=null,w=null,v=!1},tryToClaimNextHydratableInstance:function(e){if(v){var n=w;if(!n)return T(b,e),v=!1,void(b=e);if(!I(e,n)){if(!(n=i(n))||!I(e,n))return T(b,e),v=!1,void(b=e);k(b,w)}b=e,w=s(n)}},prepareToHydrateHostInstance:function(e,n,t){var o=e.stateNode,a=l(o,e.type,e.memoizedProps,n,t,e);return e.updateQueue=a,null!==a},prepareToHydrateHostTextInstance:function(e){var n=e.stateNode,t=e.memoizedProps,o=c(n,t,e);if(o){var a=b;if(null!==a)switch(a.tag){case Se:var r=a.stateNode.containerInfo;u(r,n,t);break;case Ne:var i=a.type,s=a.memoizedProps,l=a.stateNode;h(i,s,l,n,t)}}return o},popHydrationState:function(e){if(e!==b)return!1;if(!v)return E(e),v=!0,!1;var t=e.type;if(e.tag!==Ne||"head"!==t&&"body"!==t&&!n(t,e.memoizedProps))for(var o=w;o;)k(e,o),o=i(o);return E(e),w=b?i(e.stateNode):null,!0}}},xi={debugTool:null},Si=function(e){return!0};var Li=z.invokeGuardedCallback,Ni=z.hasCaughtError,Ci=z.clearCaughtError,Ai=!1,Pi=!1,Oi={},Ri=function(e){var n=Bt(e)||"ReactClass";Oi[n]||(a(!1,"Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.\n\nPlease check the code for the %s component.",n),Oi[n]=!0)},Di=function(e){switch(va.phase){case"getChildContext":if(Pi)return;a(!1,"setState(...): Cannot call setState() inside getChildContext()"),Pi=!0;break;case"render":if(Ai)return;a(!1,"Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`."),Ai=!0}},Mi=function(e){var n=function(e){var n=e.getChildHostContext,t=e.getRootHostContext,a=pa(Ii),r=pa(Ii),i=pa(Ii);function s(e){return e===Ii&&o(!1,"Expected host context to exist. This error is likely caused by a bug in React. Please file an issue."),e}return{getHostContext:function(){return s(a.current)},getRootHostContainer:function(){return s(i.current)},popHostContainer:function(e){fa(a,e),fa(r,e),fa(i,e)},popHostContext:function(e){r.current===e&&(fa(a,e),fa(r,e))},pushHostContainer:function(e,n){ma(i,n,e);var o=t(n);ma(r,e,e),ma(a,o,e)},pushHostContext:function(e){var t=s(i.current),o=s(a.current),l=n(o,e.type,t);o!==l&&(ma(r,e,e),ma(a,l,e))},resetHostContainer:function(){a.current=Ii,i.current=Ii}}}(e),t=Ei(e),a=n.popHostContainer,r=n.popHostContext,i=n.resetHostContainer,s=yi(e,n,t,re,ae),l=s.beginWork,c=s.beginFailedWork,u=bi(e,n,t).completeWork,h=Ti(e,Z),d=h.commitResetTextContent,f=h.commitPlacement,m=h.commitDeletion,g=h.commitWork,y=h.commitLifeCycles,b=h.commitAttachRef,w=h.commitDetachRef,v=e.now,k=e.scheduleDeferredCallback,T=e.cancelDeferredCallback,I=e.useSyncScheduling,E=e.prepareForCommit,x=e.resetAfterCommit,S=v(),L=yr(0),N=dr,C=!1,A=null,P=null,O=dr,R=null,D=null,M=null,F=null,B=null,W=!1,j=!1,_=!1,U=null;function z(){!function(){for(;da>-1;)ua[da]=null,ha[da]=null,da--}(),nr=p,Za.current=p,er.current=!1,i()}function G(){for(;null!==R;){va.setCurrentFiber(R),Ha();var e=R.effectTag;if(e&Vt&&d(R),e&Kt){var n=R.alternate;null!==n&&w(n)}switch(e&~(Ht|qt|Vt|Kt|jt)){case _t:f(R),R.effectTag&=~_t;break;case zt:f(R),R.effectTag&=~_t;var t=R.alternate;g(t,R);break;case Ut:var o=R.alternate;g(o,R);break;case Gt:_=!0,m(R),_=!1}R=R.nextEffect}va.resetCurrentFiber()}function V(){for(;null!==R;){var e=R.effectTag;if(e&(Ut|Ht)){Ha();var n=R.alternate;y(n,R)}e&Kt&&(Ha(),b(R)),e&qt&&(Ha(),te(R));var t=R.nextEffect;R.nextEffect=null,R=t}}function H(e){C=!0,j=!0,function(){if(ia){if(!ka)return;xa=!0,Sa=!1,Pa.clear(),Ra("(Committing Changes)")}}();var n=e.stateNode;n.current===e&&o(!1,"Cannot commit the same tree as before. This is probably a bug related to the return field. This error is likely caused by a bug in React. Please file an issue."),n.isReadyForCommit=!1,Mt.current=null;var t=void 0;for(e.effectTag>jt?null!==e.lastEffect?(e.lastEffect.nextEffect=e,t=e.firstEffect):t=e:t=e.firstEffect,E(),R=t,function(){if(ia){if(!ka)return;Ca=0,Ra("(Committing Host Effects)")}}();null!==R;){var a=!1,r=void 0;Li(null,G,null),Ni()&&(a=!0,r=Ci()),a&&(null===R&&o(!1,"Should have next effect. This error is likely caused by a bug in React. Please file an issue."),Z(R,r),null!==R&&(R=R.nextEffect))}for(!function(){if(ia){if(!ka)return;var e=Ca;Ca=0,Da("(Committing Host Effects: "+e+" Total)","(Committing Host Effects)",null)}}(),x(),n.current=e,R=t,function(){if(ia){if(!ka)return;Ca=0,Ra("(Calling Lifecycle Methods)")}}();null!==R;){var i=!1,s=void 0;Li(null,V,null),Ni()&&(i=!0,s=Ci()),i&&(null===R&&o(!1,"Should have next effect. This error is likely caused by a bug in React. Please file an issue."),Z(R,s),null!==R&&(R=R.nextEffect))}if(j=!1,C=!1,function(){if(ia){if(!ka)return;var e=Ca;Ca=0,Da("(Calling Lifecycle Methods: "+e+" Total)","(Calling Lifecycle Methods)",null)}}(),function(){if(ia){if(!ka)return;var e=null;Sa?e="Lifecycle hook scheduled a cascading update":Na>0&&(e="Caused by a cascading update in earlier commit"),Sa=!1,Na++,xa=!1,Pa.clear(),Da("(Committing Changes)","(Committing Changes)",e)}}(),Mr(e.stateNode),xi.debugTool&&xi.debugTool.onCommitWork(e),F&&(F.forEach(le),F=null),null!==B){var l=B;B=null,Be(l)}var c=n.current.expirationTime;return c===dr&&(D=null,M=null),c}function q(e,n){if(n===fr||e.expirationTime!==fr){for(var t=function(e){if(e.tag!==xe&&e.tag!==Se)return dr;var n=e.updateQueue;return null===n?dr:n.expirationTime}(e),o=e.child;null!==o;)o.expirationTime!==dr&&(t===dr||t>o.expirationTime)&&(t=o.expirationTime),o=o.sibling;e.expirationTime=t}}function K(e){for(;;){var n=e.alternate;va.setCurrentFiber(e);var t=u(n,e,O);va.resetCurrentFiber();var o=e.return,a=e.sibling;if(q(e,O),null!==t)return Ya(e),xi.debugTool&&xi.debugTool.onCompleteWork(e),t;if(null!==o)null===o.firstEffect&&(o.firstEffect=e.firstEffect),null!==e.lastEffect&&(null!==o.lastEffect&&(o.lastEffect.nextEffect=e.firstEffect),o.lastEffect=e.lastEffect),e.effectTag>jt&&(null!==o.lastEffect?o.lastEffect.nextEffect=e:o.firstEffect=e,o.lastEffect=e);if(Ya(e),xi.debugTool&&xi.debugTool.onCompleteWork(e),null!==a)return a;if(null===o)return e.stateNode.isReadyForCommit=!0,null;e=o}return null}function Y(e){var n=e.alternate;qa(e),va.setCurrentFiber(e);var t=l(n,e,O);return va.resetCurrentFiber(),xi.debugTool&&xi.debugTool.onBeginWork(e),null===t&&(t=K(e)),Mt.current=null,t}function X(e){var n=e.alternate;qa(e),va.setCurrentFiber(e);var t=c(n,e,O);return va.resetCurrentFiber(),xi.debugTool&&xi.debugTool.onBeginWork(e),null===t&&(t=K(e)),Mt.current=null,t}function Q(e){if(null===D){if(!(O===dr||O>e))if(O<=L)for(;null!==A;)A=Y(A);else for(;null!==A&&!Fe();)A=Y(A)}else!function(e){if(O===dr||O>e)return;if(O<=L)for(;null!==A;)A=ee(A)?X(A):Y(A);else for(;null!==A&&!Fe();)A=ee(A)?X(A):Y(A)}(e)}function $(e,n,t,o){!function(e,n){var t=e;for(;null!==t;){switch(t.tag){case xe:sr(t);break;case Ne:r(t);break;case Se:case Le:a(t)}if(t===n||t.alternate===n){Xa(t);break}Ya(t),t=t.return}}(n,t),A=X(t),Q(o)}function J(e,n){C&&o(!1,"renderRoot was called recursively. This error is likely caused by a bug in React. Please file an issue."),C=!0,e.isReadyForCommit=!1,e===P&&n===O&&null!==A||(z(),O=n,A=Ir((P=e).current,null,n)),function(e){if(ia){if(Ta=e,!ka)return;Na=0,Ra("(React Tree Reconciliation)"),Va()}}(A);var t=!1,a=null;for(Li(null,Q,null,n),Ni()&&(t=!0,a=Ci());t;){if(W){B=a;break}var r=A;if(null!==r){var i=Z(r,a);if(null===i&&o(!1,"Should have found an error boundary. This error is likely caused by a bug in React. Please file an issue."),!W){if(t=!1,a=null,Li(null,$,null,e,r,i,n),!Ni())break;t=!0,a=Ci()}}else W=!0}var s=B;return function(e){if(ia){if(!ka)return;var n=null;null!==e?n=e.tag===Se?"A top-level update interrupted the previous render":"An update to "+(Bt(e)||"Unknown")+" interrupted the previous render":Na>1&&(n="There were cascading updates"),Na=0,za(),Da("(React Tree Reconciliation)","(React Tree Reconciliation)",n)}}(U),U=null,C=!1,W=!1,B=null,null!==s&&Be(s),e.isReadyForCommit?e.current.alternate:null}function Z(e,n){Mt.current=null,va.resetCurrentFiber();var t=null,o=!1,a=!1,r=null;if(e.tag===Se)t=e,ne(e)&&(W=!0);else for(var i=e.return;null!==i&&null===t;){if(i.tag===xe)"function"==typeof i.stateNode.componentDidCatch&&(o=!0,r=Bt(i),t=i,a=!0);else i.tag===Se&&(t=i);if(ne(i)){if(_)return null;if(null!==F&&(F.has(i)||null!==i.alternate&&F.has(i.alternate)))return null;t=null,a=!1}i=i.return}if(null!==t){null===M&&(M=new Set),M.add(t);var s=ba(e),l=Bt(e);null===D&&(D=new Map);var c={componentName:l,componentStack:s,error:n,errorBoundary:o?t.stateNode:null,errorBoundaryFound:o,errorBoundaryName:r,willRetry:a};D.set(t,c);try{!function(e){if(!1!==Si(e)){var n=e.error;if(!n||!n.suppressReactErrorLogging){var t=e.componentName,o=e.componentStack,a=e.errorBoundaryName,r=e.errorBoundaryFound,i=e.willRetry,s=(t?"The above error occurred in the <"+t+"> component:":"The above error occurred in one of your React components:")+o+"\n\n"+(r&&a?i?"React will try to recreate this component tree from scratch using the error boundary you provided, "+a+".":"This error was initially handled by the error boundary "+a+".\nRecreating the tree from scratch failed so React will unmount the tree.":"Consider adding an error boundary to your tree to customize error handling behavior.\nVisit https://fb.me/react-error-boundaries to learn more about error boundaries.");console.error(s)}}}(c)}catch(e){e&&e.suppressReactErrorLogging||console.error(e)}return j?(null===F&&(F=new Set),F.add(t)):le(t),t}return null===B&&(B=n),null}function ee(e){return null!==D&&(D.has(e)||null!==e.alternate&&D.has(e.alternate))}function ne(e){return null!==M&&(M.has(e)||null!==e.alternate&&M.has(e.alternate))}function te(e){var n=void 0;switch(null!==D&&(n=D.get(e),D.delete(e),null==n&&null!==e.alternate&&(e=e.alternate,n=D.get(e),D.delete(e))),null==n&&o(!1,"No error for given unit of work. This error is likely caused by a bug in React. Please file an issue."),e.tag){case xe:var t=e.stateNode,a={componentStack:n.componentStack};return void t.componentDidCatch(n.error,a);case Se:return void(null===B&&(B=n.error));default:o(!1,"Invalid type of work. This error is likely caused by a bug in React. Please file an issue.")}}function oe(){var e,n=ce();return(1+((n+1e3/mr)/(e=200/mr)|0))*e}function ae(e){return N!==dr?N:C?j?pr:O:!I||e.internalContextTag&wr?oe():pr}function re(e,n){return se(e,n,!1)}function ie(e,n,t){!C&&e===P&&t<O&&(null!==A&&(U=n),P=null,A=null,O=dr)}function se(e,n,t){if(ia&&(xa&&(Sa=!0),null!==Ia&&"componentWillMount"!==Ia&&"componentWillReceiveProps"!==Ia&&(La=!0)),!t&&e.tag===xe){var o=e.stateNode;Di(o)}for(var a=e;null!==a;){if((a.expirationTime===dr||a.expirationTime>n)&&(a.expirationTime=n),null!==a.alternate&&(a.alternate.expirationTime===dr||a.alternate.expirationTime>n)&&(a.alternate.expirationTime=n),null===a.return){if(a.tag!==Se)return void(t||e.tag!==xe||Ri(e));var r=a.stateNode;ie(r,e,n),Pe(r,n),ie(r,e,n)}a=a.return}}function le(e){se(e,pr,!0)}function ce(){var e=v()-S;return L=yr(e)}var ue=null,he=null,de=dr,pe=-1,fe=!1,me=null,ge=dr,ye=!1,be=!1,we=null,ve=null,ke=!1,Te=!1,Ie=1e3,Ee=0,Ce=1;function Ae(e){if(de!==dr){if(e>de)return;T(pe)}else ia&&ka&&!Aa&&(Aa=!0,Ra("(Waiting for async callback...)"));var n=v()-S,t=(e-gr)*mr;de=e,pe=k(Re,{timeout:t-n})}function Pe(e,n){if(Ee>Ie&&o(!1,"Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."),null===e.nextScheduledRoot)e.remainingExpirationTime=n,null===he?(ue=he=e,e.nextScheduledRoot=e):(he.nextScheduledRoot=e,(he=e).nextScheduledRoot=ue);else{var t=e.remainingExpirationTime;(t===dr||n<t)&&(e.remainingExpirationTime=n)}fe||(ke?Te&&Me(me=e,ge=pr):n===pr?De(pr,null):Ae(n))}function Oe(){var e=dr,n=null;if(null!==he)for(var t=he,a=ue;null!==a;){var r=a.remainingExpirationTime;if(r===dr){if((null===t||null===he)&&o(!1,"Should have a previous and last root. This error is likely caused by a bug in React. Please file an issue."),a===a.nextScheduledRoot){a.nextScheduledRoot=null,ue=he=null;break}if(a===ue){var i=a.nextScheduledRoot;ue=i,he.nextScheduledRoot=i,a.nextScheduledRoot=null}else{if(a===he){(he=t).nextScheduledRoot=ue,a.nextScheduledRoot=null;break}t.nextScheduledRoot=a.nextScheduledRoot,a.nextScheduledRoot=null}a=t.nextScheduledRoot}else{if((e===dr||r<e)&&(e=r,n=a),a===he)break;t=a,a=a.nextScheduledRoot}}null!==me&&me===n?Ee++:Ee=0,me=n,ge=e}function Re(e){De(dr,e)}function De(e,n){if(ve=n,Oe(),ia&&null!==ve){var t=ge<ce();ia&&ka&&(Aa=!1,Da("(Waiting for async callback...)","(Waiting for async callback...)",t?"React was blocked by main thread":null))}for(;null!==me&&ge!==dr&&(e===dr||ge<=e)&&!ye;)Me(me,ge),Oe();if(null!==ve&&(de=dr,pe=-1),ge!==dr&&Ae(ge),ve=null,ye=!1,Ee=0,be){var o=we;throw we=null,be=!1,o}}function Me(e,n){if(fe&&o(!1,"performWorkOnRoot was called recursively. This error is likely caused by a bug in React. Please file an issue."),fe=!0,n<=ce()){var t=e.finishedWork;null!==t?(e.finishedWork=null,e.remainingExpirationTime=H(t)):(e.finishedWork=null,null!==(t=J(e,n))&&(e.remainingExpirationTime=H(t)))}else{var a=e.finishedWork;null!==a?(e.finishedWork=null,e.remainingExpirationTime=H(a)):(e.finishedWork=null,null!==(a=J(e,n))&&(Fe()?e.finishedWork=a:e.remainingExpirationTime=H(a)))}fe=!1}function Fe(){return null!==ve&&(!(ve.timeRemaining()>Ce)&&(ye=!0,!0))}function Be(e){null===me&&o(!1,"Should be working on a root. This error is likely caused by a bug in React. Please file an issue."),me.remainingExpirationTime=dr,be||(be=!0,we=e)}return{computeAsyncExpiration:oe,computeExpirationForFiber:ae,scheduleWork:re,batchedUpdates:function(e,n){var t=ke;ke=!0;try{return e(n)}finally{(ke=t)||fe||De(pr,null)}},unbatchedUpdates:function(e){if(ke&&!Te){Te=!0;try{return e()}finally{Te=!1}}return e()},flushSync:function(e){var n=ke;ke=!0;try{return function(e){var n=N;N=pr;try{return e()}finally{N=n}}(e)}finally{ke=n,fe&&o(!1,"flushSync was called from inside a lifecycle method. It cannot be called when React is already rendering."),De(pr,null)}},deferredUpdates:function(e){var n=N;N=oe();try{return e()}finally{N=n}}}},Fi=!1;function Bi(e){if(!e)return p;var n=Rt(e),t=function(e){Jt(e)&&e.tag===xe||o(!1,"Expected subtree parent to be a mounted class component. This error is likely caused by a bug in React. Please file an issue.");for(var n=e;n.tag!==Se;){if(ir(n))return n.stateNode.__reactInternalMemoizedMergedChildContext;var t=n.return;t||o(!1,"Found unexpected detached subtree parent. This error is likely caused by a bug in React. Please file an issue."),n=t}return n.stateNode.context}(n);return ir(n)?cr(n,t):t}var Wi=function(e){var n=e.getPublicInstance,t=Mi(e),o=t.computeAsyncExpiration,r=t.computeExpirationForFiber,s=t.scheduleWork,l=t.batchedUpdates,c=t.unbatchedUpdates,u=t.flushSync;function h(e){var n=function(e){var n=no(e);if(!n)return null;for(var t=n;;){if(t.tag===Ne||t.tag===Ce)return t;if(t.child)t.child.return=t,t=t.child;else{if(t===n)return null;for(;!t.sibling;){if(!t.return||t.return===n)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}}return null}(e);return null===n?null:n.stateNode}return{createContainer:function(e,n){return Ar(e,n)},updateContainer:function(e,n,t,i){var l=n.current;xi.debugTool&&(null===l.alternate?xi.debugTool.onMountContainer(n):null===e?xi.debugTool.onUnmountContainer(n):xi.debugTool.onUpdateContainer(n));var c=Bi(t);null===n.context?n.context=c:n.pendingContext=c,function(e,n,t){"render"!==va.phase||null===va.current||Fi||(Fi=!0,a(!1,"Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate.\n\nCheck the render method of %s.",Bt(va.current)||"Unknown")),a(null===(t=void 0===t?null:t)||"function"==typeof t,"render(...): Expected the last optional `callback` argument to be a function. Instead received: %s.",t);var i=void 0;_r(e,{expirationTime:i=ra&&null!=n&&null!=n.type&&null!=n.type.prototype&&!0===n.type.prototype.unstable_isAsyncReactComponent?o():r(e),partialState:{element:n},callback:t,isReplace:!1,isForced:!1,nextCallback:null,next:null}),s(e,i)}(l,e,i)},batchedUpdates:l,unbatchedUpdates:c,deferredUpdates:t.deferredUpdates,flushSync:u,getPublicRootInstance:function(e){var t=e.current;if(!t.child)return null;switch(t.child.tag){case Ne:return n(t.child.stateNode);default:return t.child.stateNode}},findHostInstance:h,findHostInstanceWithNoPortals:function(e){var n=function(e){var n=no(e);if(!n)return null;for(var t=n;;){if(t.tag===Ne||t.tag===Ce)return t;if(t.child&&t.tag!==Le)t.child.return=t,t=t.child;else{if(t===n)return null;for(;!t.sibling;){if(!t.return||t.return===n)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}}return null}(e);return null===n?null:n.stateNode},injectIntoDevTools:function(e){var n=e.findFiberByHostInstance;return function(e){if("undefined"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var n=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(n.isDisabled)return!0;if(!n.supportsFiber)return a(!1,"The installed version of React DevTools is too old and will not work with the current version of React. Please update React DevTools. https://fb.me/react-devtools"),!0;try{var t=n.inject(e);Pr=Dr(function(e){return n.onCommitFiberRoot(t,e)}),Or=Dr(function(e){return n.onCommitFiberUnmount(t,e)})}catch(e){a(!1,"React DevTools encountered an error: %s.",e)}return!0}(i({},e,{findHostInstanceByFiber:function(e){return h(e)},findFiberByHostInstance:function(e){return n?n(e):null}}))}}},ji=Object.freeze({default:Wi}),_i=ji&&Wi||ji,Ui=_i.default?_i.default:_i;r.canUseDOM&&"function"!=typeof requestAnimationFrame&&a(!1,"React depends on requestAnimationFrame. Make sure that you load a polyfill in older browsers. http://fb.me/react-polyfills");var zi="object"==typeof performance&&"function"==typeof performance.now,Gi=void 0;Gi=zi?function(){return performance.now()}:function(){return Date.now()};var Vi=void 0,Hi=void 0;if(r.canUseDOM)if("function"!=typeof requestIdleCallback||"function"!=typeof cancelIdleCallback){var qi,Ki=null,Yi=!1,Xi=-1,Qi=!1,$i=0,Ji=33,Zi=33;qi=zi?{didTimeout:!1,timeRemaining:function(){var e=$i-performance.now();return e>0?e:0}}:{didTimeout:!1,timeRemaining:function(){var e=$i-Date.now();return e>0?e:0}};var es="__reactIdleCallback$"+Math.random().toString(36).slice(2);window.addEventListener("message",function(e){if(e.source===window&&e.data===es){Yi=!1;var n=Gi();if($i-n<=0){if(!(-1!==Xi&&Xi<=n))return void(Qi||(Qi=!0,requestAnimationFrame(ns)));qi.didTimeout=!0}else qi.didTimeout=!1;Xi=-1;var t=Ki;Ki=null,null!==t&&t(qi)}},!1);var ns=function(e){Qi=!1;var n=e-$i+Zi;n<Zi&&Ji<Zi?(n<8&&(n=8),Zi=n<Ji?Ji:n):Ji=n,$i=e+Zi,Yi||(Yi=!0,window.postMessage(es,"*"))};Vi=function(e,n){return Ki=e,null!=n&&"number"==typeof n.timeout&&(Xi=Gi()+n.timeout),Qi||(Qi=!0,requestAnimationFrame(ns)),0},Hi=function(){Ki=null,Yi=!1,Xi=-1}}else Vi=window.requestIdleCallback,Hi=window.cancelIdleCallback;else Vi=function(e){return setTimeout(function(){e({timeRemaining:function(){return 1/0}})})},Hi=function(e){clearTimeout(e)};var ts=function(e,n){if(void 0===n)throw new Error("`warning(condition, format, ...args)` requires a warning message argument");if(!e){for(var t=arguments.length,o=Array(t>2?t-2:0),a=2;a<t;a++)o[a-2]=arguments[a];(function(e){for(var n=arguments.length,t=Array(n>1?n-1:0),o=1;o<n;o++)t[o-1]=arguments[o];var a=0,r="Warning: "+e.replace(/%s/g,function(){return t[a++]});"undefined"!=typeof console&&console.warn(r);try{throw new Error(r)}catch(e){}}).apply(void 0,[n].concat(o))}},os=new RegExp("^["+v+"]["+k+"]*$"),as={},rs={};function is(e){return!!rs.hasOwnProperty(e)||!as.hasOwnProperty(e)&&(os.test(e)?(rs[e]=!0,!0):(as[e]=!0,a(!1,"Invalid attribute name: `%s`",e),!1))}function ss(e,n){return null==n||e.hasBooleanValue&&!n||e.hasNumericValue&&isNaN(n)||e.hasPositiveNumericValue&&n<1||e.hasOverloadedBooleanValue&&!1===n}function ls(e,n,t){var o=x(n);if(o){if(o.mutationMethod||o.mustUseProperty)return e[o.propertyName];var a=o.attributeName,r=null;if(o.hasOverloadedBooleanValue){if(e.hasAttribute(a)){var i=e.getAttribute(a);return""===i||(ss(o,t)?i:i===""+t?t:i)}}else if(e.hasAttribute(a)){if(ss(o,t))return e.getAttribute(a);if(o.hasBooleanValue)return t;r=e.getAttribute(a)}return ss(o,t)?null===r?t:r:r===""+t?t:r}}function cs(e,n,t){if(is(n)){if(!e.hasAttribute(n))return void 0===t?void 0:null;var o=e.getAttribute(n);return o===""+t?t:o}}function us(e,n,t){var o=x(n);if(o&&E(n,t)){var a=o.mutationMethod;if(a)a(e,t);else{if(ss(o,t))return void ds(e,n);if(o.mustUseProperty)e[o.propertyName]=t;else{var r=o.attributeName,i=o.attributeNamespace;i?e.setAttributeNS(i,r,""+t):o.hasBooleanValue||o.hasOverloadedBooleanValue&&!0===t?e.setAttribute(r,""):e.setAttribute(r,""+t)}}}else hs(e,n,E(n,t)?t:null)}function hs(e,n,t){is(n)&&(null==t?e.removeAttribute(n):e.setAttribute(n,""+t))}function ds(e,n){var t=x(n);if(t){var o=t.mutationMethod;if(o)o(e,void 0);else if(t.mustUseProperty){var a=t.propertyName;t.hasBooleanValue?e[a]=!1:e[a]=""}else e.removeAttribute(t.attributeName)}else e.removeAttribute(n)}var ps={checkPropTypes:null},fs={button:!0,checkbox:!0,image:!0,hidden:!0,radio:!0,reset:!0,submit:!0},ms={value:function(e,n,t){return!e[n]||fs[e.type]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.")},checked:function(e,n,t){return!e[n]||e.onChange||e.readOnly||e.disabled?null:new Error("You provided a `checked` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultChecked`. Otherwise, set either `onChange` or `readOnly`.")}};ps.checkPropTypes=function(e,n,t){f(ms,n,"prop",e,t)};var gs=va.getCurrentFiberOwnerName,ys=va.getCurrentFiberStackAddendum,bs=!1,ws=!1,vs=!1,ks=!1;function Ts(e){return"checkbox"===e.type||"radio"===e.type?null!=e.checked:null!=e.value}function Is(e,n){var t=e,o=n.value,a=n.checked;return i({type:void 0,step:void 0,min:void 0,max:void 0},n,{defaultChecked:void 0,defaultValue:void 0,value:null!=o?o:t._wrapperState.initialValue,checked:null!=a?a:t._wrapperState.initialChecked})}function Es(e,n){ps.checkPropTypes("input",n,ys),void 0===n.checked||void 0===n.defaultChecked||ws||(a(!1,"%s contains an input of type %s with both checked and defaultChecked props. Input elements must be either controlled or uncontrolled (specify either the checked prop, or the defaultChecked prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props. More info: https://fb.me/react-controlled-components",gs()||"A component",n.type),ws=!0),void 0===n.value||void 0===n.defaultValue||bs||(a(!1,"%s contains an input of type %s with both value and defaultValue props. Input elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props. More info: https://fb.me/react-controlled-components",gs()||"A component",n.type),bs=!0);var t=n.defaultValue;e._wrapperState={initialChecked:null!=n.checked?n.checked:n.defaultChecked,initialValue:null!=n.value?n.value:t,controlled:Ts(n)}}function xs(e,n){var t=e,o=n.checked;null!=o&&us(t,"checked",o)}function Ss(e,n){var t=e,o=Ts(n);t._wrapperState.controlled||!o||ks||(a(!1,"A component is changing an uncontrolled input of type %s to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s",n.type,ys()),ks=!0),!t._wrapperState.controlled||o||vs||(a(!1,"A component is changing a controlled input of type %s to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s",n.type,ys()),vs=!0),xs(e,n);var r=n.value;if(null!=r)if(0===r&&""===t.value)t.value="0";else if("number"===n.type){var i=parseFloat(t.value)||0;(r!=i||r==i&&t.value!=r)&&(t.value=""+r)}else t.value!==""+r&&(t.value=""+r);else null==n.value&&null!=n.defaultValue&&t.defaultValue!==""+n.defaultValue&&(t.defaultValue=""+n.defaultValue),null==n.checked&&null!=n.defaultChecked&&(t.defaultChecked=!!n.defaultChecked)}function Ls(e,n){var t=e;switch(n.type){case"submit":case"reset":break;case"color":case"date":case"datetime":case"datetime-local":case"month":case"time":case"week":t.value="",t.value=t.defaultValue;break;default:t.value=t.value}var o=t.name;""!==o&&(t.name=""),t.defaultChecked=!t.defaultChecked,t.defaultChecked=!t.defaultChecked,""!==o&&(t.name=o)}function Ns(e,n){var t=e;Ss(t,n),function(e,n){var t=n.name;if("radio"===n.type&&null!=t){for(var a=e;a.parentNode;)a=a.parentNode;for(var r=a.querySelectorAll("input[name="+JSON.stringify(""+t)+'][type="radio"]'),i=0;i<r.length;i++){var s=r[i];if(s!==e&&s.form===e.form){var l=Ue(s);l||o(!1,"ReactDOMInput: Mixing React and non-React radio inputs with the same `name` is not supported."),ct(s),Ss(s,l)}}}}(t,n)}function Cs(e,n){a(null==n.selected,"Use the `defaultValue` or `value` props on <select> instead of setting `selected` on <option>.")}function As(e,n){var o,a,r=i({children:void 0},n),s=(o=n.children,a="",t.Children.forEach(o,function(e){null!=e&&("string"!=typeof e&&"number"!=typeof e||(a+=e))}),a);return s&&(r.children=s),r}var Ps=va.getCurrentFiberOwnerName,Os=va.getCurrentFiberStackAddendum,Rs=!1;function Ds(){var e=Ps();return e?"\n\nCheck the render method of `"+e+"`.":""}var Ms=["value","defaultValue"];function Fs(e,n,t,o){var a=e.options;if(n){for(var r=t,i={},s=0;s<r.length;s++)i["$"+r[s]]=!0;for(var l=0;l<a.length;l++){var c=i.hasOwnProperty("$"+a[l].value);a[l].selected!==c&&(a[l].selected=c),c&&o&&(a[l].defaultSelected=!0)}}else{for(var u=""+t,h=null,d=0;d<a.length;d++){if(a[d].value===u)return a[d].selected=!0,void(o&&(a[d].defaultSelected=!0));null!==h||a[d].disabled||(h=a[d])}null!==h&&(h.selected=!0)}}function Bs(e,n){return i({},n,{value:void 0})}function Ws(e,n){var t=e;!function(e){ps.checkPropTypes("select",e,Os);for(var n=0;n<Ms.length;n++){var t=Ms[n];if(null!=e[t]){var o=Array.isArray(e[t]);e.multiple&&!o?a(!1,"The `%s` prop supplied to <select> must be an array if `multiple` is true.%s",t,Ds()):!e.multiple&&o&&a(!1,"The `%s` prop supplied to <select> must be a scalar value if `multiple` is false.%s",t,Ds())}}}(n);var o=n.value;t._wrapperState={initialValue:null!=o?o:n.defaultValue,wasMultiple:!!n.multiple},void 0===n.value||void 0===n.defaultValue||Rs||(a(!1,"Select elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled select element and remove one of these props. More info: https://fb.me/react-controlled-components"),Rs=!0)}var js=va.getCurrentFiberStackAddendum,_s=!1;function Us(e,n){var t=e;return null!=n.dangerouslySetInnerHTML&&o(!1,"`dangerouslySetInnerHTML` does not make sense on <textarea>."),i({},n,{value:void 0,defaultValue:void 0,children:""+t._wrapperState.initialValue})}function zs(e,n){var t=e;ps.checkPropTypes("textarea",n,js),void 0===n.value||void 0===n.defaultValue||_s||(a(!1,"Textarea elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled textarea and remove one of these props. More info: https://fb.me/react-controlled-components"),_s=!0);var r=n.value;if(null==r){var i=n.defaultValue,s=n.children;null!=s&&(a(!1,"Use the `defaultValue` or `value` props instead of setting children on <textarea>."),null!=i&&o(!1,"If you supply `defaultValue` on a <textarea>, do not pass children."),Array.isArray(s)&&(s.length<=1||o(!1,"<textarea> can only have at most one child."),s=s[0]),i=""+s),null==i&&(i=""),r=i}t._wrapperState={initialValue:""+r}}function Gs(e,n){var t=e,o=n.value;if(null!=o){var a=""+o;a!==t.value&&(t.value=a),null==n.defaultValue&&(t.defaultValue=a)}null!=n.defaultValue&&(t.defaultValue=n.defaultValue)}function Vs(e,n){var t=e,o=t.textContent;o===t._wrapperState.initialValue&&(t.value=o)}var Hs="http://www.w3.org/1999/xhtml",qs="http://www.w3.org/1998/Math/MathML",Ks="http://www.w3.org/2000/svg",Ys={html:Hs,mathml:qs,svg:Ks};function Xs(e){switch(e){case"svg":return Ks;case"math":return qs;default:return Hs}}function Qs(e,n){return null==e||e===Hs?Xs(n):e===Ks&&"foreignObject"===n?Hs:e}var $s,Js=void 0,Zs=($s=function(e,n){if(e.namespaceURI!==Ys.svg||"innerHTML"in e)e.innerHTML=n;else{(Js=Js||document.createElement("div")).innerHTML="<svg>"+n+"</svg>";for(var t=Js.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}},"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,n,t,o){MSApp.execUnsafeLocalFunction(function(){return $s(e,n,t,o)})}:$s),el=function(e,n){if(n){var t=e.firstChild;if(t&&t===e.lastChild&&t.nodeType===et)return void(t.nodeValue=n)}e.textContent=n},nl={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0};var tl=["Webkit","ms","Moz","O"];function ol(e,n,t){return null==n||"boolean"==typeof n||""===n?"":t||"number"!=typeof n||0===n||nl.hasOwnProperty(e)&&nl[e]?(""+n).trim():n+"px"}Object.keys(nl).forEach(function(e){tl.forEach(function(n){var t,o;nl[(t=n,o=e,t+o.charAt(0).toUpperCase()+o.substring(1))]=nl[e]})});var al=/^(?:webkit|moz|o)[A-Z]/,rl=/;\s*$/,il={},sl={},ll=!1,cl=!1,ul=function(e,n,t){var o,r,i,s,l,c,u;e.indexOf("-")>-1?(c=e,u=t,il.hasOwnProperty(c)&&il[c]||(il[c]=!0,a(!1,"Unsupported style property %s. Did you mean %s?%s",c,g(c),u()))):al.test(e)?(s=e,l=t,il.hasOwnProperty(s)&&il[s]||(il[s]=!0,a(!1,"Unsupported vendor-prefixed style property %s. Did you mean %s?%s",s,s.charAt(0).toUpperCase()+s.slice(1),l()))):rl.test(n)&&(o=e,r=n,i=t,sl.hasOwnProperty(r)&&sl[r]||(sl[r]=!0,a(!1,'Style property values shouldn\'t contain a semicolon. Try "%s: %s" instead.%s',o,r.replace(rl,""),i()))),"number"==typeof n&&(isNaN(n)?ll||(ll=!0,a(!1,"`NaN` is an invalid value for the `%s` css style property.%s",e,t())):isFinite(n)||cl||(cl=!0,a(!1,"`Infinity` is an invalid value for the `%s` css style property.%s",e,t())))};function hl(e){var n="",t="";for(var o in e)if(e.hasOwnProperty(o)){var a=e[o];if(null!=a){var r=0===o.indexOf("--");n+=t+m(o)+":",n+=ol(o,a,r),t=";"}}return n||null}function dl(e,n,t){var o=e.style;for(var a in n)if(n.hasOwnProperty(a)){var r=0===a.indexOf("--");r||ul(a,n[a],t);var i=ol(a,n[a],r);"float"===a&&(a="cssFloat"),r?o.setProperty(a,i):o[a]=i}}var pl=i({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}),fl="__html";function ml(e,n,t){n&&(pl[e]&&(null!=n.children||null!=n.dangerouslySetInnerHTML)&&o(!1,"%s is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.%s",e,t()),null!=n.dangerouslySetInnerHTML&&(null!=n.children&&o(!1,"Can only set one of `children` or `props.dangerouslySetInnerHTML`."),"object"==typeof n.dangerouslySetInnerHTML&&fl in n.dangerouslySetInnerHTML||o(!1,"`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit https://fb.me/react-invariant-dangerously-set-inner-html for more information.")),a(n.suppressContentEditableWarning||!n.contentEditable||null==n.children,"A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.%s",t()),null!=n.style&&"object"!=typeof n.style&&o(!1,"The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + 'em'}} when using JSX.%s",t()))}function gl(e,n){if(-1===e.indexOf("-"))return"string"==typeof n.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var yl={"aria-current":0,"aria-details":0,"aria-disabled":0,"aria-hidden":0,"aria-invalid":0,"aria-keyshortcuts":0,"aria-label":0,"aria-roledescription":0,"aria-autocomplete":0,"aria-checked":0,"aria-expanded":0,"aria-haspopup":0,"aria-level":0,"aria-modal":0,"aria-multiline":0,"aria-multiselectable":0,"aria-orientation":0,"aria-placeholder":0,"aria-pressed":0,"aria-readonly":0,"aria-required":0,"aria-selected":0,"aria-sort":0,"aria-valuemax":0,"aria-valuemin":0,"aria-valuenow":0,"aria-valuetext":0,"aria-atomic":0,"aria-busy":0,"aria-live":0,"aria-relevant":0,"aria-dropeffect":0,"aria-grabbed":0,"aria-activedescendant":0,"aria-colcount":0,"aria-colindex":0,"aria-colspan":0,"aria-controls":0,"aria-describedby":0,"aria-errormessage":0,"aria-flowto":0,"aria-labelledby":0,"aria-owns":0,"aria-posinset":0,"aria-rowcount":0,"aria-rowindex":0,"aria-rowspan":0,"aria-setsize":0},bl={},wl=new RegExp("^(aria)-["+k+"]*$"),vl=new RegExp("^(aria)[A-Z]["+k+"]*$"),kl=Object.prototype.hasOwnProperty;function Tl(){var e=Ft.getStackAddendum();return null!=e?e:""}function Il(e,n){if(kl.call(bl,n)&&bl[n])return!0;if(vl.test(n)){var t="aria-"+n.slice(4).toLowerCase(),o=yl.hasOwnProperty(t)?t:null;if(null==o)return a(!1,"Invalid ARIA attribute `%s`. ARIA attributes follow the pattern aria-* and must be lowercase.%s",n,Tl()),bl[n]=!0,!0;if(n!==o)return a(!1,"Invalid ARIA attribute `%s`. Did you mean `%s`?%s",n,o,Tl()),bl[n]=!0,!0}if(wl.test(n)){var r=n.toLowerCase(),i=yl.hasOwnProperty(r)?r:null;if(null==i)return bl[n]=!0,!1;if(n!==i)return a(!1,"Unknown ARIA attribute `%s`. Did you mean `%s`?%s",n,i,Tl()),bl[n]=!0,!0}return!0}function El(e,n){gl(e,n)||function(e,n){var t=[];for(var o in n)Il(0,o)||t.push(o);var r=t.map(function(e){return"`"+e+"`"}).join(", ");1===t.length?a(!1,"Invalid aria prop %s on <%s> tag. For details, see https://fb.me/invalid-aria-prop%s",r,e,Tl()):t.length>1&&a(!1,"Invalid aria props %s on <%s> tag. For details, see https://fb.me/invalid-aria-prop%s",r,e,Tl())}(e,n)}var xl=!1;function Sl(){var e=Ft.getStackAddendum();return null!=e?e:""}var Ll={accept:"accept",acceptcharset:"acceptCharset","accept-charset":"acceptCharset",accesskey:"accessKey",action:"action",allowfullscreen:"allowFullScreen",alt:"alt",as:"as",async:"async",autocapitalize:"autoCapitalize",autocomplete:"autoComplete",autocorrect:"autoCorrect",autofocus:"autoFocus",autoplay:"autoPlay",autosave:"autoSave",capture:"capture",cellpadding:"cellPadding",cellspacing:"cellSpacing",challenge:"challenge",charset:"charSet",checked:"checked",children:"children",cite:"cite",class:"className",classid:"classID",classname:"className",cols:"cols",colspan:"colSpan",content:"content",contenteditable:"contentEditable",contextmenu:"contextMenu",controls:"controls",controlslist:"controlsList",coords:"coords",crossorigin:"crossOrigin",dangerouslysetinnerhtml:"dangerouslySetInnerHTML",data:"data",datetime:"dateTime",default:"default",defaultchecked:"defaultChecked",defaultvalue:"defaultValue",defer:"defer",dir:"dir",disabled:"disabled",download:"download",draggable:"draggable",enctype:"encType",for:"htmlFor",form:"form",formmethod:"formMethod",formaction:"formAction",formenctype:"formEncType",formnovalidate:"formNoValidate",formtarget:"formTarget",frameborder:"frameBorder",headers:"headers",height:"height",hidden:"hidden",high:"high",href:"href",hreflang:"hrefLang",htmlfor:"htmlFor",httpequiv:"httpEquiv","http-equiv":"httpEquiv",icon:"icon",id:"id",innerhtml:"innerHTML",inputmode:"inputMode",integrity:"integrity",is:"is",itemid:"itemID",itemprop:"itemProp",itemref:"itemRef",itemscope:"itemScope",itemtype:"itemType",keyparams:"keyParams",keytype:"keyType",kind:"kind",label:"label",lang:"lang",list:"list",loop:"loop",low:"low",manifest:"manifest",marginwidth:"marginWidth",marginheight:"marginHeight",max:"max",maxlength:"maxLength",media:"media",mediagroup:"mediaGroup",method:"method",min:"min",minlength:"minLength",multiple:"multiple",muted:"muted",name:"name",nonce:"nonce",novalidate:"noValidate",open:"open",optimum:"optimum",pattern:"pattern",placeholder:"placeholder",playsinline:"playsInline",poster:"poster",preload:"preload",profile:"profile",radiogroup:"radioGroup",readonly:"readOnly",referrerpolicy:"referrerPolicy",rel:"rel",required:"required",reversed:"reversed",role:"role",rows:"rows",rowspan:"rowSpan",sandbox:"sandbox",scope:"scope",scoped:"scoped",scrolling:"scrolling",seamless:"seamless",selected:"selected",shape:"shape",size:"size",sizes:"sizes",span:"span",spellcheck:"spellCheck",src:"src",srcdoc:"srcDoc",srclang:"srcLang",srcset:"srcSet",start:"start",step:"step",style:"style",summary:"summary",tabindex:"tabIndex",target:"target",title:"title",type:"type",usemap:"useMap",value:"value",width:"width",wmode:"wmode",wrap:"wrap",about:"about",accentheight:"accentHeight","accent-height":"accentHeight",accumulate:"accumulate",additive:"additive",alignmentbaseline:"alignmentBaseline","alignment-baseline":"alignmentBaseline",allowreorder:"allowReorder",alphabetic:"alphabetic",amplitude:"amplitude",arabicform:"arabicForm","arabic-form":"arabicForm",ascent:"ascent",attributename:"attributeName",attributetype:"attributeType",autoreverse:"autoReverse",azimuth:"azimuth",basefrequency:"baseFrequency",baselineshift:"baselineShift","baseline-shift":"baselineShift",baseprofile:"baseProfile",bbox:"bbox",begin:"begin",bias:"bias",by:"by",calcmode:"calcMode",capheight:"capHeight","cap-height":"capHeight",clip:"clip",clippath:"clipPath","clip-path":"clipPath",clippathunits:"clipPathUnits",cliprule:"clipRule","clip-rule":"clipRule",color:"color",colorinterpolation:"colorInterpolation","color-interpolation":"colorInterpolation",colorinterpolationfilters:"colorInterpolationFilters","color-interpolation-filters":"colorInterpolationFilters",colorprofile:"colorProfile","color-profile":"colorProfile",colorrendering:"colorRendering","color-rendering":"colorRendering",contentscripttype:"contentScriptType",contentstyletype:"contentStyleType",cursor:"cursor",cx:"cx",cy:"cy",d:"d",datatype:"datatype",decelerate:"decelerate",descent:"descent",diffuseconstant:"diffuseConstant",direction:"direction",display:"display",divisor:"divisor",dominantbaseline:"dominantBaseline","dominant-baseline":"dominantBaseline",dur:"dur",dx:"dx",dy:"dy",edgemode:"edgeMode",elevation:"elevation",enablebackground:"enableBackground","enable-background":"enableBackground",end:"end",exponent:"exponent",externalresourcesrequired:"externalResourcesRequired",fill:"fill",fillopacity:"fillOpacity","fill-opacity":"fillOpacity",fillrule:"fillRule","fill-rule":"fillRule",filter:"filter",filterres:"filterRes",filterunits:"filterUnits",floodopacity:"floodOpacity","flood-opacity":"floodOpacity",floodcolor:"floodColor","flood-color":"floodColor",focusable:"focusable",fontfamily:"fontFamily","font-family":"fontFamily",fontsize:"fontSize","font-size":"fontSize",fontsizeadjust:"fontSizeAdjust","font-size-adjust":"fontSizeAdjust",fontstretch:"fontStretch","font-stretch":"fontStretch",fontstyle:"fontStyle","font-style":"fontStyle",fontvariant:"fontVariant","font-variant":"fontVariant",fontweight:"fontWeight","font-weight":"fontWeight",format:"format",from:"from",fx:"fx",fy:"fy",g1:"g1",g2:"g2",glyphname:"glyphName","glyph-name":"glyphName",glyphorientationhorizontal:"glyphOrientationHorizontal","glyph-orientation-horizontal":"glyphOrientationHorizontal",glyphorientationvertical:"glyphOrientationVertical","glyph-orientation-vertical":"glyphOrientationVertical",glyphref:"glyphRef",gradienttransform:"gradientTransform",gradientunits:"gradientUnits",hanging:"hanging",horizadvx:"horizAdvX","horiz-adv-x":"horizAdvX",horizoriginx:"horizOriginX","horiz-origin-x":"horizOriginX",ideographic:"ideographic",imagerendering:"imageRendering","image-rendering":"imageRendering",in2:"in2",in:"in",inlist:"inlist",intercept:"intercept",k1:"k1",k2:"k2",k3:"k3",k4:"k4",k:"k",kernelmatrix:"kernelMatrix",kernelunitlength:"kernelUnitLength",kerning:"kerning",keypoints:"keyPoints",keysplines:"keySplines",keytimes:"keyTimes",lengthadjust:"lengthAdjust",letterspacing:"letterSpacing","letter-spacing":"letterSpacing",lightingcolor:"lightingColor","lighting-color":"lightingColor",limitingconeangle:"limitingConeAngle",local:"local",markerend:"markerEnd","marker-end":"markerEnd",markerheight:"markerHeight",markermid:"markerMid","marker-mid":"markerMid",markerstart:"markerStart","marker-start":"markerStart",markerunits:"markerUnits",markerwidth:"markerWidth",mask:"mask",maskcontentunits:"maskContentUnits",maskunits:"maskUnits",mathematical:"mathematical",mode:"mode",numoctaves:"numOctaves",offset:"offset",opacity:"opacity",operator:"operator",order:"order",orient:"orient",orientation:"orientation",origin:"origin",overflow:"overflow",overlineposition:"overlinePosition","overline-position":"overlinePosition",overlinethickness:"overlineThickness","overline-thickness":"overlineThickness",paintorder:"paintOrder","paint-order":"paintOrder",panose1:"panose1","panose-1":"panose1",pathlength:"pathLength",patterncontentunits:"patternContentUnits",patterntransform:"patternTransform",patternunits:"patternUnits",pointerevents:"pointerEvents","pointer-events":"pointerEvents",points:"points",pointsatx:"pointsAtX",pointsaty:"pointsAtY",pointsatz:"pointsAtZ",prefix:"prefix",preservealpha:"preserveAlpha",preserveaspectratio:"preserveAspectRatio",primitiveunits:"primitiveUnits",property:"property",r:"r",radius:"radius",refx:"refX",refy:"refY",renderingintent:"renderingIntent","rendering-intent":"renderingIntent",repeatcount:"repeatCount",repeatdur:"repeatDur",requiredextensions:"requiredExtensions",requiredfeatures:"requiredFeatures",resource:"resource",restart:"restart",result:"result",results:"results",rotate:"rotate",rx:"rx",ry:"ry",scale:"scale",security:"security",seed:"seed",shaperendering:"shapeRendering","shape-rendering":"shapeRendering",slope:"slope",spacing:"spacing",specularconstant:"specularConstant",specularexponent:"specularExponent",speed:"speed",spreadmethod:"spreadMethod",startoffset:"startOffset",stddeviation:"stdDeviation",stemh:"stemh",stemv:"stemv",stitchtiles:"stitchTiles",stopcolor:"stopColor","stop-color":"stopColor",stopopacity:"stopOpacity","stop-opacity":"stopOpacity",strikethroughposition:"strikethroughPosition","strikethrough-position":"strikethroughPosition",strikethroughthickness:"strikethroughThickness","strikethrough-thickness":"strikethroughThickness",string:"string",stroke:"stroke",strokedasharray:"strokeDasharray","stroke-dasharray":"strokeDasharray",strokedashoffset:"strokeDashoffset","stroke-dashoffset":"strokeDashoffset",strokelinecap:"strokeLinecap","stroke-linecap":"strokeLinecap",strokelinejoin:"strokeLinejoin","stroke-linejoin":"strokeLinejoin",strokemiterlimit:"strokeMiterlimit","stroke-miterlimit":"strokeMiterlimit",strokewidth:"strokeWidth","stroke-width":"strokeWidth",strokeopacity:"strokeOpacity","stroke-opacity":"strokeOpacity",suppresscontenteditablewarning:"suppressContentEditableWarning",suppresshydrationwarning:"suppressHydrationWarning",surfacescale:"surfaceScale",systemlanguage:"systemLanguage",tablevalues:"tableValues",targetx:"targetX",targety:"targetY",textanchor:"textAnchor","text-anchor":"textAnchor",textdecoration:"textDecoration","text-decoration":"textDecoration",textlength:"textLength",textrendering:"textRendering","text-rendering":"textRendering",to:"to",transform:"transform",typeof:"typeof",u1:"u1",u2:"u2",underlineposition:"underlinePosition","underline-position":"underlinePosition",underlinethickness:"underlineThickness","underline-thickness":"underlineThickness",unicode:"unicode",unicodebidi:"unicodeBidi","unicode-bidi":"unicodeBidi",unicoderange:"unicodeRange","unicode-range":"unicodeRange",unitsperem:"unitsPerEm","units-per-em":"unitsPerEm",unselectable:"unselectable",valphabetic:"vAlphabetic","v-alphabetic":"vAlphabetic",values:"values",vectoreffect:"vectorEffect","vector-effect":"vectorEffect",version:"version",vertadvy:"vertAdvY","vert-adv-y":"vertAdvY",vertoriginx:"vertOriginX","vert-origin-x":"vertOriginX",vertoriginy:"vertOriginY","vert-origin-y":"vertOriginY",vhanging:"vHanging","v-hanging":"vHanging",videographic:"vIdeographic","v-ideographic":"vIdeographic",viewbox:"viewBox",viewtarget:"viewTarget",visibility:"visibility",vmathematical:"vMathematical","v-mathematical":"vMathematical",vocab:"vocab",widths:"widths",wordspacing:"wordSpacing","word-spacing":"wordSpacing",writingmode:"writingMode","writing-mode":"writingMode",x1:"x1",x2:"x2",x:"x",xchannelselector:"xChannelSelector",xheight:"xHeight","x-height":"xHeight",xlinkactuate:"xlinkActuate","xlink:actuate":"xlinkActuate",xlinkarcrole:"xlinkArcrole","xlink:arcrole":"xlinkArcrole",xlinkhref:"xlinkHref","xlink:href":"xlinkHref",xlinkrole:"xlinkRole","xlink:role":"xlinkRole",xlinkshow:"xlinkShow","xlink:show":"xlinkShow",xlinktitle:"xlinkTitle","xlink:title":"xlinkTitle",xlinktype:"xlinkType","xlink:type":"xlinkType",xmlbase:"xmlBase","xml:base":"xmlBase",xmllang:"xmlLang","xml:lang":"xmlLang",xmlns:"xmlns","xml:space":"xmlSpace",xmlnsxlink:"xmlnsXlink","xmlns:xlink":"xmlnsXlink",xmlspace:"xmlSpace",y1:"y1",y2:"y2",y:"y",ychannelselector:"yChannelSelector",z:"z",zoomandpan:"zoomAndPan"};function Nl(){var e=Ft.getStackAddendum();return null!=e?e:""}var Cl={},Al=Object.prototype.hasOwnProperty,Pl=/^on./,Ol=/^on[^A-Z]/,Rl=new RegExp("^(aria)-["+k+"]*$"),Dl=new RegExp("^(aria)[A-Z]["+k+"]*$"),Ml=function(e,n,t,o){if(Al.call(Cl,n)&&Cl[n])return!0;var r=n.toLowerCase();if("onfocusin"===r||"onfocusout"===r)return a(!1,"React uses onFocus and onBlur instead of onFocusIn and onFocusOut. All React events are normalized to bubble, so onFocusIn and onFocusOut are not needed/supported by React."),Cl[n]=!0,!0;if(o){if(Z.hasOwnProperty(n))return!0;var i=ne.hasOwnProperty(r)?ne[r]:null;if(null!=i)return a(!1,"Invalid event handler property `%s`. Did you mean `%s`?%s",n,i,Nl()),Cl[n]=!0,!0;if(Pl.test(n))return a(!1,"Unknown event handler property `%s`. It will be ignored.%s",n,Nl()),Cl[n]=!0,!0}else if(Pl.test(n))return Ol.test(n)&&a(!1,"Invalid event handler property `%s`. React events use the camelCase naming convention, for example `onClick`.%s",n,Nl()),Cl[n]=!0,!0;if(Rl.test(n)||Dl.test(n))return!0;if("innerhtml"===r)return a(!1,"Directly setting property `innerHTML` is not permitted. For more information, lookup documentation on `dangerouslySetInnerHTML`."),Cl[n]=!0,!0;if("aria"===r)return a(!1,"The `aria` attribute is reserved for future use in React. Pass individual `aria-` attributes instead."),Cl[n]=!0,!0;if("is"===r&&null!==t&&void 0!==t&&"string"!=typeof t)return a(!1,"Received a `%s` for a string attribute `is`. If this is expected, cast the value to a string.%s",typeof t,Nl()),Cl[n]=!0,!0;if("number"==typeof t&&isNaN(t))return a(!1,"Received NaN for the `%s` attribute. If this is expected, cast the value to a string.%s",n,Nl()),Cl[n]=!0,!0;var s=L(n);if(Ll.hasOwnProperty(r)){var l=Ll[r];if(l!==n)return a(!1,"Invalid DOM property `%s`. Did you mean `%s`?%s",n,l,Nl()),Cl[n]=!0,!0}else if(!s&&n!==r)return a(!1,"React does not recognize the `%s` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `%s` instead. If you accidentally passed it from a parent component, remove it from the DOM element.%s",n,r,Nl()),Cl[n]=!0,!0;return"boolean"!=typeof t||S(n)?!!s||(!!E(n,t)||(Cl[n]=!0,!1)):(t?a(!1,'Received `%s` for a non-boolean attribute `%s`.\n\nIf you want to write it to the DOM, pass a string instead: %s="%s" or %s={value.toString()}.%s',t,n,n,t,n,Nl()):a(!1,'Received `%s` for a non-boolean attribute `%s`.\n\nIf you want to write it to the DOM, pass a string instead: %s="%s" or %s={value.toString()}.\n\nIf you used to conditionally omit it with %s={condition && value}, pass %s={condition ? value : undefined} instead.%s',t,n,n,t,n,n,n,Nl()),Cl[n]=!0,!0)},Fl=function(e,n,t){var o=[];for(var r in n){Ml(0,r,n[r],t)||o.push(r)}var i=o.map(function(e){return"`"+e+"`"}).join(", ");1===o.length?a(!1,"Invalid value for prop %s on <%s> tag. Either remove it from the element, or pass a string or number value to keep it in the DOM. For details, see https://fb.me/react-attribute-behavior%s",i,e,Nl()):o.length>1&&a(!1,"Invalid values for props %s on <%s> tag. Either remove them from the element, or pass a string or number value to keep them in the DOM. For details, see https://fb.me/react-attribute-behavior%s",i,e,Nl())};var Bl=va.getCurrentFiberOwnerName,Wl=va.getCurrentFiberStackAddendum,jl=!1,_l=!1,Ul="dangerouslySetInnerHTML",zl="suppressContentEditableWarning",Gl="suppressHydrationWarning",Vl="autoFocus",Hl="children",ql="style",Kl="__html",Yl=Ys.html,Xl=s.thatReturns("");Xl=Wl;var Ql={time:!0,dialog:!0},$l=function(e,n){var t,o,r,i,s;El(e,n),o=n,"input"!==(t=e)&&"textarea"!==t&&"select"!==t||null==o||null!==o.value||xl||(xl=!0,"select"===t&&o.multiple?a(!1,"`value` prop on `%s` should not be null. Consider using an empty array when `multiple` is set to `true` to clear the component or `undefined` for uncontrolled components.%s",t,Sl()):a(!1,"`value` prop on `%s` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components.%s",t,Sl())),s=!0,gl(r=e,i=n)||Fl(r,i,s)},Jl=/\r\n?/g,Zl=/\u0000|\uFFFD/g,ec=function(e){return("string"==typeof e?e:""+e).replace(Jl,"\n").replace(Zl,"")},nc=function(e,n){if(!jl){var t=ec(n),o=ec(e);o!==t&&(jl=!0,a(!1,'Text content did not match. Server: "%s" Client: "%s"',o,t))}},tc=function(e,n,t){if(!jl){var o=ec(t),r=ec(n);r!==o&&(jl=!0,a(!1,"Prop `%s` did not match. Server: %s Client: %s",e,JSON.stringify(r),JSON.stringify(o)))}},oc=function(e){if(!jl){jl=!0;var n=[];e.forEach(function(e){n.push(e)}),a(!1,"Extra attributes from the server: %s",n)}},ac=function(e,n){!1===n?a(!1,"Expected `%s` listener to be a function, instead got `false`.\n\nIf you used to conditionally omit it with %s={condition && value}, pass %s={condition ? value : undefined} instead.%s",e,e,e,Wl()):a(!1,"Expected `%s` listener to be a function, instead got a value of `%s` type.%s",e,typeof n,Wl())},rc=function(e,n){var t=e.namespaceURI===Yl?e.ownerDocument.createElement(e.tagName):e.ownerDocument.createElementNS(e.namespaceURI,e.tagName);return t.innerHTML=n,t.innerHTML};function ic(e,n){!function(e,n){for(var t=n,o=xo(t),a=ee[e],r=0;r<a.length;r++){var i=a[r];o.hasOwnProperty(i)&&o[i]||("topScroll"===i?po("topScroll","scroll",t):"topFocus"===i||"topBlur"===i?(po("topFocus","focus",t),po("topBlur","blur",t),o.topBlur=!0,o.topFocus=!0):"topCancel"===i?(rt("cancel",!0)&&po("topCancel","cancel",t),o.topCancel=!0):"topClose"===i?(rt("close",!0)&&po("topClose","close",t),o.topClose=!0):ko.hasOwnProperty(i)&&ho(i,ko[i],t),o[i]=!0)}}(n,e.nodeType===tt||e.nodeType===ot?e:e.ownerDocument)}function sc(e){return e.nodeType===tt?e:e.ownerDocument}var lc={topAbort:"abort",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topSeeked:"seeked",topSeeking:"seeking",topStalled:"stalled",topSuspend:"suspend",topTimeUpdate:"timeupdate",topVolumeChange:"volumechange",topWaiting:"waiting"};function cc(e){e.onclick=s}function uc(e,n,t,o){var r,i=sc(t),s=o;if(s===Yl&&(s=Xs(e)),s===Yl){var l=gl(e,n);if(a(l||e===e.toLowerCase(),"<%s /> is using uppercase HTML. Always use lowercase HTML tags in React.",e),"script"===e){var c=i.createElement("div");c.innerHTML="<script><\/script>";var u=c.firstChild;r=c.removeChild(u)}else r="string"==typeof n.is?i.createElement(e,{is:n.is}):i.createElement(e)}else r=i.createElementNS(s,e);return s===Yl&&(l||"[object HTMLUnknownElement]"!==Object.prototype.toString.call(r)||Object.prototype.hasOwnProperty.call(Ql,e)||(Ql[e]=!0,a(!1,"The tag <%s> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.",e))),r}function hc(e,n){return sc(n).createTextNode(e)}function dc(e,n,t,o){var r,i,s,l=gl(n,t);switch($l(n,t),l&&!_l&&e.shadyRoot&&(a(!1,"%s is using shady DOM. Using shady DOM with React can cause things to break subtly.",Bl()||"A component"),_l=!0),n){case"iframe":case"object":ho("topLoad","load",e),r=t;break;case"video":case"audio":for(var c in lc)lc.hasOwnProperty(c)&&ho(c,lc[c],e);r=t;break;case"source":ho("topError","error",e),r=t;break;case"img":case"image":ho("topError","error",e),ho("topLoad","load",e),r=t;break;case"form":ho("topReset","reset",e),ho("topSubmit","submit",e),r=t;break;case"details":ho("topToggle","toggle",e),r=t;break;case"input":Es(e,t),r=Is(e,t),ho("topInvalid","invalid",e),ic(o,"onChange");break;case"option":Cs(0,t),r=As(0,t);break;case"select":Ws(e,t),r=Bs(0,t),ho("topInvalid","invalid",e),ic(o,"onChange");break;case"textarea":zs(e,t),r=Us(e,t),ho("topInvalid","invalid",e),ic(o,"onChange");break;default:r=t}switch(ml(n,r,Xl),function(e,n,t,o,a){for(var r in o)if(o.hasOwnProperty(r)){var i=o[r];if(r===ql)i&&Object.freeze(i),dl(n,i,Xl);else if(r===Ul){var s=i?i[Kl]:void 0;null!=s&&Zs(n,s)}else r===Hl?"string"==typeof i?("textarea"!==e||""!==i)&&el(n,i):"number"==typeof i&&el(n,""+i):r===zl||r===Gl||r===Vl||(Z.hasOwnProperty(r)?null!=i&&("function"!=typeof i&&ac(r,i),ic(t,r)):a?hs(n,r,i):null!=i&&us(n,r,i))}}(n,e,o,r,l),n){case"input":lt(e),Ls(e,t);break;case"textarea":lt(e),Vs(e);break;case"option":i=e,null!=(s=t).value&&i.setAttribute("value",s.value);break;case"select":!function(e,n){var t=e;t.multiple=!!n.multiple;var o=n.value;null!=o?Fs(t,!!n.multiple,o,!1):null!=n.defaultValue&&Fs(t,!!n.multiple,n.defaultValue,!0)}(e,t);break;default:"function"==typeof r.onClick&&cc(e)}}function pc(e,n,t,o,a){$l(n,o);var r,i,s,l,c=null;switch(n){case"input":r=Is(e,t),i=Is(e,o),c=[];break;case"option":r=As(0,t),i=As(0,o),c=[];break;case"select":r=Bs(0,t),i=Bs(0,o),c=[];break;case"textarea":r=Us(e,t),i=Us(e,o),c=[];break;default:i=o,"function"!=typeof(r=t).onClick&&"function"==typeof i.onClick&&cc(e)}ml(n,i,Xl);var u=null;for(s in r)if(!i.hasOwnProperty(s)&&r.hasOwnProperty(s)&&null!=r[s])if(s===ql){var h=r[s];for(l in h)h.hasOwnProperty(l)&&(u||(u={}),u[l]="")}else s===Ul||s===Hl||s===zl||s===Gl||s===Vl||(Z.hasOwnProperty(s)?c||(c=[]):(c=c||[]).push(s,null));for(s in i){var d=i[s],p=null!=r?r[s]:void 0;if(i.hasOwnProperty(s)&&d!==p&&(null!=d||null!=p))if(s===ql)if(d&&Object.freeze(d),p){for(l in p)!p.hasOwnProperty(l)||d&&d.hasOwnProperty(l)||(u||(u={}),u[l]="");for(l in d)d.hasOwnProperty(l)&&p[l]!==d[l]&&(u||(u={}),u[l]=d[l])}else u||(c||(c=[]),c.push(s,u)),u=d;else if(s===Ul){var f=d?d[Kl]:void 0,m=p?p[Kl]:void 0;null!=f&&m!==f&&(c=c||[]).push(s,""+f)}else s===Hl?p===d||"string"!=typeof d&&"number"!=typeof d||(c=c||[]).push(s,""+d):s===zl||s===Gl||(Z.hasOwnProperty(s)?(null!=d&&("function"!=typeof d&&ac(s,d),ic(a,s)),c||p===d||(c=[])):(c=c||[]).push(s,d))}return u&&(c=c||[]).push(ql,u),c}function fc(e,n,t,o,a){"input"===t&&"radio"===a.type&&null!=a.name&&xs(e,a);gl(t,o);switch(function(e,n,t,o){for(var a=0;a<n.length;a+=2){var r=n[a],i=n[a+1];r===ql?dl(e,i,Xl):r===Ul?Zs(e,i):r===Hl?el(e,i):o?null!=i?hs(e,r,i):(s=r,e.removeAttribute(s)):null!=i?us(e,r,i):ds(e,r)}var s}(e,n,0,gl(t,a)),t){case"input":Ss(e,a);break;case"textarea":Gs(e,a);break;case"select":!function(e,n){var t=e;t._wrapperState.initialValue=void 0;var o=t._wrapperState.wasMultiple;t._wrapperState.wasMultiple=!!n.multiple;var a=n.value;null!=a?Fs(t,!!n.multiple,a,!1):o!==!!n.multiple&&(null!=n.defaultValue?Fs(t,!!n.multiple,n.defaultValue,!0):Fs(t,!!n.multiple,n.multiple?[]:"",!1))}(e,a)}}function mc(e,n,t,o,r){var i=!0===t[Gl],s=gl(n,t);switch($l(n,t),s&&!_l&&e.shadyRoot&&(a(!1,"%s is using shady DOM. Using shady DOM with React can cause things to break subtly.",Bl()||"A component"),_l=!0),n){case"iframe":case"object":ho("topLoad","load",e);break;case"video":case"audio":for(var l in lc)lc.hasOwnProperty(l)&&ho(l,lc[l],e);break;case"source":ho("topError","error",e);break;case"img":case"image":ho("topError","error",e),ho("topLoad","load",e);break;case"form":ho("topReset","reset",e),ho("topSubmit","submit",e);break;case"details":ho("topToggle","toggle",e);break;case"input":Es(e,t),ho("topInvalid","invalid",e),ic(r,"onChange");break;case"option":Cs(0,t);break;case"select":Ws(e,t),ho("topInvalid","invalid",e),ic(r,"onChange");break;case"textarea":zs(e,t),ho("topInvalid","invalid",e),ic(r,"onChange")}ml(n,t,Xl);for(var c=new Set,u=e.attributes,h=0;h<u.length;h++){switch(u[h].name.toLowerCase()){case"data-reactroot":case"value":case"checked":case"selected":break;default:c.add(u[h].name)}}var d=null;for(var p in t)if(t.hasOwnProperty(p)){var f=t[p];if(p===Hl)"string"==typeof f?e.textContent!==f&&(i||nc(e.textContent,f),d=[Hl,f]):"number"==typeof f&&e.textContent!==""+f&&(i||nc(e.textContent,f),d=[Hl,""+f]);else if(Z.hasOwnProperty(p))null!=f&&("function"!=typeof f&&ac(p,f),ic(r,p));else{var m,g;if(i);else if(p===zl||p===Gl||"value"===p||"checked"===p||"selected"===p);else if(p===Ul){var y=f&&f[Kl]||"",b=e.innerHTML,w=rc(e,y);w!==b&&tc(p,b,w)}else if(p===ql){c.delete(p);var v=hl(f);v!==(m=e.getAttribute("style"))&&tc(p,m,v)}else if(s)c.delete(p.toLowerCase()),f!==(m=cs(e,p,f))&&tc(p,m,f);else if(E(p,f)){if(g=x(p))c.delete(g.attributeName),m=ls(e,p,f);else{var k=o;k===Yl&&(k=Xs(n)),k===Yl?c.delete(p.toLowerCase()):c.delete(p),m=cs(e,p,f)}f!==m&&tc(p,m,f)}}}switch(c.size>0&&!i&&oc(c),n){case"input":lt(e),Ls(e,t);break;case"textarea":lt(e),Vs(e);break;case"select":case"option":break;default:"function"==typeof t.onClick&&cc(e)}return d}function gc(e,n){return e.nodeValue!==n}function yc(e,n){nc(e.nodeValue,n)}function bc(e,n){jl||(jl=!0,a(!1,"Did not expect server HTML to contain a <%s> in <%s>.",n.nodeName.toLowerCase(),e.nodeName.toLowerCase()))}function wc(e,n){jl||(jl=!0,a(!1,'Did not expect server HTML to contain the text node "%s" in <%s>.',n.nodeValue,e.nodeName.toLowerCase()))}function vc(e,n,t){jl||(jl=!0,a(!1,"Expected server HTML to contain a matching <%s> in <%s>.",n,e.nodeName.toLowerCase()))}function kc(e,n){""!==n&&(jl||(jl=!0,a(!1,'Expected server HTML to contain a matching text node for "%s" in <%s>.',n,e.nodeName.toLowerCase())))}var Tc,Ic=Object.freeze({createElement:uc,createTextNode:hc,setInitialProperties:dc,diffProperties:pc,updateProperties:fc,diffHydratedProperties:mc,diffHydratedText:gc,warnForUnmatchedText:yc,warnForDeletedHydratableElement:bc,warnForDeletedHydratableText:wc,warnForInsertedHydratedElement:vc,warnForInsertedHydratedText:kc,restoreControlledState:function(e,n,t){switch(n){case"input":return void Ns(e,t);case"textarea":return void Gs(e,t);case"select":return a=e,void(null!=(r=(o=t).value)&&Fs(a,!!o.multiple,r,!1))}var o,a,r}}),Ec=va.getCurrentFiberStackAddendum,xc=["address","applet","area","article","aside","base","basefont","bgsound","blockquote","body","br","button","caption","center","col","colgroup","dd","details","dir","div","dl","dt","embed","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","iframe","img","input","isindex","li","link","listing","main","marquee","menu","menuitem","meta","nav","noembed","noframes","noscript","object","ol","p","param","plaintext","pre","script","section","select","source","style","summary","table","tbody","td","template","textarea","tfoot","th","thead","title","tr","track","ul","wbr","xmp"],Sc=["applet","caption","html","table","td","th","marquee","object","template","foreignObject","desc","title"],Lc=Sc.concat(["button"]),Nc=["dd","dt","li","option","optgroup","p","rp","rt"],Cc={current:null,formTag:null,aTagInScope:null,buttonTagInScope:null,nobrTagInScope:null,pTagInButtonScope:null,listItemTagAutoclosing:null,dlItemTagAutoclosing:null},Ac=function(e,n){switch(n){case"select":return"option"===e||"optgroup"===e||"#text"===e;case"optgroup":return"option"===e||"#text"===e;case"option":return"#text"===e;case"tr":return"th"===e||"td"===e||"style"===e||"script"===e||"template"===e;case"tbody":case"thead":case"tfoot":return"tr"===e||"style"===e||"script"===e||"template"===e;case"colgroup":return"col"===e||"template"===e;case"table":return"caption"===e||"colgroup"===e||"tbody"===e||"tfoot"===e||"thead"===e||"style"===e||"script"===e||"template"===e;case"head":return"base"===e||"basefont"===e||"bgsound"===e||"link"===e||"meta"===e||"title"===e||"noscript"===e||"noframes"===e||"style"===e||"script"===e||"template"===e;case"html":return"head"===e||"body"===e;case"#document":return"html"===e}switch(e){case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":return"h1"!==n&&"h2"!==n&&"h3"!==n&&"h4"!==n&&"h5"!==n&&"h6"!==n;case"rp":case"rt":return-1===Nc.indexOf(n);case"body":case"caption":case"col":case"colgroup":case"frame":case"head":case"html":case"tbody":case"td":case"tfoot":case"th":case"thead":case"tr":return null==n}return!0},Pc=function(e,n){switch(e){case"address":case"article":case"aside":case"blockquote":case"center":case"details":case"dialog":case"dir":case"div":case"dl":case"fieldset":case"figcaption":case"figure":case"footer":case"header":case"hgroup":case"main":case"menu":case"nav":case"ol":case"p":case"section":case"summary":case"ul":case"pre":case"listing":case"table":case"hr":case"xmp":case"h1":case"h2":case"h3":case"h4":case"h5":case"h6":return n.pTagInButtonScope;case"form":return n.formTag||n.pTagInButtonScope;case"li":return n.listItemTagAutoclosing;case"dd":case"dt":return n.dlItemTagAutoclosing;case"button":return n.buttonTagInScope;case"a":return n.aTagInScope;case"nobr":return n.nobrTagInScope}return null},Oc={};(Tc=function(e,n,t){var o=(t=t||Cc).current,r=o&&o.tag;null!=n&&(a(null==e,"validateDOMNesting: when childText is passed, childTag should be null"),e="#text");var i=Ac(e,r)?null:o,s=i?null:Pc(e,t),l=i||s;if(l){var c=l.tag,u=Ec(),h=!!i+"|"+e+"|"+c+"|"+u;if(!Oc[h]){Oc[h]=!0;var d=e,p="";if("#text"===e?/\S/.test(n)?d="Text nodes":(d="Whitespace text nodes",p=" Make sure you don't have any extra whitespace between tags on each line of your source code."):d="<"+e+">",i){var f="";"table"===c&&"tr"===e&&(f+=" Add a <tbody> to your code to match the DOM tree generated by the browser."),a(!1,"validateDOMNesting(...): %s cannot appear as a child of <%s>.%s%s%s",d,c,p,f,u)}else a(!1,"validateDOMNesting(...): %s cannot appear as a descendant of <%s>.%s",d,c,u)}}}).updatedAncestorInfo=function(e,n,t){var o=i({},e||Cc),a={tag:n,instance:t};return-1!==Sc.indexOf(n)&&(o.aTagInScope=null,o.buttonTagInScope=null,o.nobrTagInScope=null),-1!==Lc.indexOf(n)&&(o.pTagInButtonScope=null),-1!==xc.indexOf(n)&&"address"!==n&&"div"!==n&&"p"!==n&&(o.listItemTagAutoclosing=null,o.dlItemTagAutoclosing=null),o.current=a,"form"===n&&(o.formTag=a),"a"===n&&(o.aTagInScope=a),"button"===n&&(o.buttonTagInScope=a),"nobr"===n&&(o.nobrTagInScope=a),"p"===n&&(o.pTagInButtonScope=a),"li"===n&&(o.listItemTagAutoclosing=a),"dd"!==n&&"dt"!==n||(o.dlItemTagAutoclosing=a),o},Tc.isTagValidInContext=function(e,n){var t=(n=n||Cc).current,o=t&&t.tag;return Ac(e,o)&&!Pc(e,n)};var Rc=Tc,Dc=uc,Mc=hc,Fc=dc,Bc=pc,Wc=fc,jc=mc,_c=gc,Uc=yc,zc=bc,Gc=wc,Vc=vc,Hc=kc,qc=Rc.updatedAncestorInfo,Kc=Be,Yc=ze,Xc="suppressHydrationWarning";"function"==typeof Map&&null!=Map.prototype&&"function"==typeof Map.prototype.forEach&&"function"==typeof Set&&null!=Set.prototype&&"function"==typeof Set.prototype.clear&&"function"==typeof Set.prototype.forEach||a(!1,"React depends on Map and Set built-in types. Make sure that you load a polyfill in older browsers. http://fb.me/react-polyfills"),zn.injectFiberControlledHostComponent(Ic);var Qc=null,$c=null;function Jc(e){return!(!e||e.nodeType!==Zn&&e.nodeType!==tt&&e.nodeType!==ot&&(e.nodeType!==nt||" react-mount-point-unstable "!==e.nodeValue))}function Zc(e){return e?e.nodeType===tt?e.documentElement:e.firstChild:null}var eu=Ui({getRootHostContext:function(e){var n=void 0,t=void 0,o=e.nodeType;switch(o){case tt:case ot:n=o===tt?"#document":"#fragment";var a=e.documentElement;t=a?a.namespaceURI:Qs(null,"");break;default:var r=o===nt?e.parentNode:e;t=Qs(r.namespaceURI||null,n=r.tagName)}var i=n.toLowerCase();return{namespace:t,ancestorInfo:qc(null,i,null)}},getChildHostContext:function(e,n){var t=e;return{namespace:Qs(t.namespace,n),ancestorInfo:qc(t.ancestorInfo,n,null)}},getPublicInstance:function(e){return e},prepareForCommit:function(){Qc=uo(),$c=Po(),co(!1)},resetAfterCommit:function(){Oo($c),$c=null,co(Qc),Qc=null},createInstance:function(e,n,t,o,a){var r,i=o;if(Rc(e,null,i.ancestorInfo),"string"==typeof n.children||"number"==typeof n.children){var s=""+n.children,l=qc(i.ancestorInfo,e,null);Rc(null,s,l)}r=i.namespace;var c=Dc(e,n,t,r);return Kc(a,c),Yc(c,n),c},appendInitialChild:function(e,n){e.appendChild(n)},finalizeInitialChildren:function(e,n,t,o){return Fc(e,n,t,o),function(e,n){switch(e){case"button":case"input":case"select":case"textarea":return!!n.autoFocus}return!1}(n,t)},prepareUpdate:function(e,n,t,o,a,r){var i=r;if(typeof o.children!=typeof t.children&&("string"==typeof o.children||"number"==typeof o.children)){var s=""+o.children,l=qc(i.ancestorInfo,n,null);Rc(null,s,l)}return Bc(e,n,t,o,a)},shouldSetTextContent:function(e,n){return"textarea"===e||"string"==typeof n.children||"number"==typeof n.children||"object"==typeof n.dangerouslySetInnerHTML&&null!==n.dangerouslySetInnerHTML&&"string"==typeof n.dangerouslySetInnerHTML.__html},shouldDeprioritizeSubtree:function(e,n){return!!n.hidden},createTextInstance:function(e,n,t,o){Rc(null,e,t.ancestorInfo);var a=Mc(e,n);return Kc(o,a),a},now:Gi,mutation:{commitMount:function(e,n,t,o){e.focus()},commitUpdate:function(e,n,t,o,a,r){Yc(e,a),Wc(e,n,t,o,a)},resetTextContent:function(e){e.textContent=""},commitTextUpdate:function(e,n,t){e.nodeValue=t},appendChild:function(e,n){e.appendChild(n)},appendChildToContainer:function(e,n){e.nodeType===nt?e.parentNode.insertBefore(n,e):e.appendChild(n)},insertBefore:function(e,n,t){e.insertBefore(n,t)},insertInContainerBefore:function(e,n,t){e.nodeType===nt?e.parentNode.insertBefore(n,t):e.insertBefore(n,t)},removeChild:function(e,n){e.removeChild(n)},removeChildFromContainer:function(e,n){e.nodeType===nt?e.parentNode.removeChild(n):e.removeChild(n)}},hydration:{canHydrateInstance:function(e,n,t){return e.nodeType!==Zn||n.toLowerCase()!==e.nodeName.toLowerCase()?null:e},canHydrateTextInstance:function(e,n){return""===n||e.nodeType!==et?null:e},getNextHydratableSibling:function(e){for(var n=e.nextSibling;n&&n.nodeType!==Zn&&n.nodeType!==et;)n=n.nextSibling;return n},getFirstHydratableChild:function(e){for(var n=e.firstChild;n&&n.nodeType!==Zn&&n.nodeType!==et;)n=n.nextSibling;return n},hydrateInstance:function(e,n,t,o,a,r){Kc(r,e),Yc(e,t);var i;return i=a.namespace,jc(e,n,t,i,o)},hydrateTextInstance:function(e,n,t){return Kc(t,e),_c(e,n)},didNotMatchHydratedContainerTextInstance:function(e,n,t){Uc(n,t)},didNotMatchHydratedTextInstance:function(e,n,t,o,a){!0!==n[Xc]&&Uc(o,a)},didNotHydrateContainerInstance:function(e,n){1===n.nodeType?zc(e,n):Gc(e,n)},didNotHydrateInstance:function(e,n,t,o){!0!==n[Xc]&&(1===o.nodeType?zc(t,o):Gc(t,o))},didNotFindHydratableContainerInstance:function(e,n,t){Vc(e,n,t)},didNotFindHydratableContainerTextInstance:function(e,n){Hc(e,n)},didNotFindHydratableInstance:function(e,n,t,o,a){!0!==n[Xc]&&Vc(t,o,a)},didNotFindHydratableTextInstance:function(e,n,t,o){!0!==n[Xc]&&Hc(t,o)}},scheduleDeferredCallback:Vi,cancelDeferredCallback:Hi,useSyncScheduling:!0});Xn.injectFiberBatchedUpdates(eu.batchedUpdates);var nu=!1;function tu(e,n,t,r,i){if(Jc(t)||o(!1,"Target container is not a DOM element."),t._reactRootContainer&&t.nodeType!==nt){var s=eu.findHostInstanceWithNoPortals(t._reactRootContainer.current);s&&a(s.parentNode===t,"render(...): It looks like the React-rendered content of this container was removed without using React. This is not supported and will cause errors. Instead, call ReactDOM.unmountComponentAtNode to empty a container.")}var l=!!t._reactRootContainer,c=Zc(t),u=!(!c||!je(c));a(!u||l,"render(...): Replacing React-rendered children with a new root component. If you intended to update the children of this node, you should instead have the existing children update their state and render the new components instead of calling ReactDOM.render."),a(t.nodeType!==Zn||!t.tagName||"BODY"!==t.tagName.toUpperCase(),"render(): Rendering components directly into document.body is discouraged, since its children are often manipulated by third-party scripts and browser extensions. This may lead to subtle reconciliation issues. Try rendering into a container element created for your app.");var h,d=t._reactRootContainer;if(d)eu.updateContainer(n,d,e,i);else{var p=r||!(!(h=Zc(t))||h.nodeType!==Zn||!h.hasAttribute(T));if(!p)for(var f=!1,m=void 0;m=t.lastChild;)!f&&m.nodeType===Zn&&m.hasAttribute(T)&&(f=!0,a(!1,"render(): Target node has markup rendered by React, but there are unrelated nodes as well. This is most commonly caused by white-space inserted around server-rendered markup.")),t.removeChild(m);!p||r||nu||(nu=!0,ts(!1,"render(): Calling ReactDOM.render() to hydrate server-rendered markup will stop working in React v17. Replace the ReactDOM.render() call with ReactDOM.hydrate() if you want React to attach to the server HTML."));var g=eu.createContainer(t,p);d=t._reactRootContainer=g,eu.unbatchedUpdates(function(){eu.updateContainer(n,g,e,i)})}return eu.getPublicRootInstance(d)}function ou(e,n){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;return Jc(n)||o(!1,"Target container is not a DOM element."),function(e,n,t){var o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:Zr,key:null==o?null:""+o,children:e,containerInfo:n,implementation:t}}(e,n,null,t)}function au(e,n){var t=eu.createContainer(e,n);this._reactRootContainer=t}au.prototype.render=function(e,n){var t=this._reactRootContainer;eu.updateContainer(e,t,null,n)},au.prototype.unmount=function(e){var n=this._reactRootContainer;eu.updateContainer(null,n,null,e)};var ru={createPortal:ou,findDOMNode:function(e){var n=Mt.current;if(null!==n){var t=n.stateNode._warnedAboutRefsInRender;a(t,"%s is accessing findDOMNode inside its render(). render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.",Bt(n)||"A component"),n.stateNode._warnedAboutRefsInRender=!0}if(null==e)return null;if(e.nodeType===Zn)return e;var r=Rt(e);if(r)return eu.findHostInstance(r);"function"==typeof e.render?o(!1,"Unable to find node on an unmounted component."):o(!1,"Element appears to be neither ReactComponent nor DOMNode. Keys: %s",Object.keys(e))},hydrate:function(e,n,t){return tu(null,e,n,!0,t)},render:function(e,n,t){return tu(null,e,n,!1,t)},unstable_renderSubtreeIntoContainer:function(e,n,t,a){return(null==e||void 0===e._reactInternalFiber)&&o(!1,"parentComponent must be a valid React Component"),tu(e,n,t,!1,a)},unmountComponentAtNode:function(e){if(Jc(e)||o(!1,"unmountComponentAtNode(...): Target container is not a DOM element."),e._reactRootContainer){var n=Zc(e),t=n&&!je(n);return a(!t,"unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React."),eu.unbatchedUpdates(function(){tu(null,null,e,!1,function(){e._reactRootContainer=null})}),!0}var r=Zc(e),i=!(!r||!je(r)),s=1===e.nodeType&&Jc(e.parentNode)&&!!e.parentNode._reactRootContainer;return a(!i,"unmountComponentAtNode(): The node you're attempting to unmount was rendered by React and is not a top-level container. %s",s?"You may have accidentally passed in a React root node instead of its container.":"Instead, have the parent component update its state and rerender in order to remove this component."),!1},unstable_createPortal:ou,unstable_batchedUpdates:Yn,unstable_deferredUpdates:eu.deferredUpdates,flushSync:eu.flushSync,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{EventPluginHub:Te,EventPluginRegistry:re,EventPropagators:en,ReactControlledComponent:Hn,ReactDOMComponentTree:Ge,ReactDOMEventListener:mo}};if(!eu.injectIntoDevTools({findFiberByHostInstance:We,bundleType:1,version:"16.2.0",rendererPackageName:"react-dom"})&&r.canUseDOM&&window.top===window.self&&(navigator.userAgent.indexOf("Chrome")>-1&&-1===navigator.userAgent.indexOf("Edge")||navigator.userAgent.indexOf("Firefox")>-1)){var iu=window.location.protocol;/^(https?|file):$/.test(iu)&&console.info("%cDownload the React DevTools for a better development experience: https://fb.me/react-devtools"+("file:"===iu?"\nYou might need to use a local HTTP server (instead of file://): https://fb.me/react-devtools-faq":""),"font-weight:bold")}var su=Object.freeze({default:ru}),lu=su&&ru||su,cu=lu.default?lu.default:lu;n.exports=cu}()}).call(this,e("_process"))},{_process:48,"fbjs/lib/EventListener":7,"fbjs/lib/ExecutionEnvironment":8,"fbjs/lib/camelizeStyleName":10,"fbjs/lib/containsNode":11,"fbjs/lib/emptyFunction":12,"fbjs/lib/emptyObject":13,"fbjs/lib/focusNode":14,"fbjs/lib/getActiveElement":15,"fbjs/lib/hyphenateStyleName":17,"fbjs/lib/invariant":18,"fbjs/lib/shallowEqual":21,"fbjs/lib/warning":22,"object-assign":43,"prop-types/checkPropTypes":49,react:90}],55:[function(e,n,t){"use strict";var o=e("react"),a=e("fbjs/lib/ExecutionEnvironment"),r=e("object-assign"),i=e("fbjs/lib/emptyFunction"),s=e("fbjs/lib/EventListener"),l=e("fbjs/lib/getActiveElement"),c=e("fbjs/lib/shallowEqual"),u=e("fbjs/lib/containsNode"),h=e("fbjs/lib/focusNode"),d=e("fbjs/lib/emptyObject");function p(e){for(var n=arguments.length-1,t="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,o=0;o<n;o++)t+="&args[]="+encodeURIComponent(arguments[o+1]);throw(n=Error(t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.")).name="Invariant Violation",n.framesToPop=1,n}o||p("227");var f={children:!0,dangerouslySetInnerHTML:!0,defaultValue:!0,defaultChecked:!0,innerHTML:!0,suppressContentEditableWarning:!0,suppressHydrationWarning:!0,style:!0};function m(e,n){return(e&n)===n}var g={MUST_USE_PROPERTY:1,HAS_BOOLEAN_VALUE:4,HAS_NUMERIC_VALUE:8,HAS_POSITIVE_NUMERIC_VALUE:24,HAS_OVERLOADED_BOOLEAN_VALUE:32,HAS_STRING_BOOLEAN_VALUE:64,injectDOMPropertyConfig:function(e){var n=g,t=e.Properties||{},o=e.DOMAttributeNamespaces||{},a=e.DOMAttributeNames||{};for(var r in e=e.DOMMutationMethods||{},t){y.hasOwnProperty(r)&&p("48",r);var i=r.toLowerCase(),s=t[r];1>=(i={attributeName:i,attributeNamespace:null,propertyName:r,mutationMethod:null,mustUseProperty:m(s,n.MUST_USE_PROPERTY),hasBooleanValue:m(s,n.HAS_BOOLEAN_VALUE),hasNumericValue:m(s,n.HAS_NUMERIC_VALUE),hasPositiveNumericValue:m(s,n.HAS_POSITIVE_NUMERIC_VALUE),hasOverloadedBooleanValue:m(s,n.HAS_OVERLOADED_BOOLEAN_VALUE),hasStringBooleanValue:m(s,n.HAS_STRING_BOOLEAN_VALUE)}).hasBooleanValue+i.hasNumericValue+i.hasOverloadedBooleanValue||p("50",r),a.hasOwnProperty(r)&&(i.attributeName=a[r]),o.hasOwnProperty(r)&&(i.attributeNamespace=o[r]),e.hasOwnProperty(r)&&(i.mutationMethod=e[r]),y[r]=i}}},y={};function b(e,n){if(f.hasOwnProperty(e)||2<e.length&&("o"===e[0]||"O"===e[0])&&("n"===e[1]||"N"===e[1]))return!1;if(null===n)return!0;switch(typeof n){case"boolean":return f.hasOwnProperty(e)?e=!0:(n=w(e))?e=n.hasBooleanValue||n.hasStringBooleanValue||n.hasOverloadedBooleanValue:e="data-"===(e=e.toLowerCase().slice(0,5))||"aria-"===e,e;case"undefined":case"number":case"string":case"object":return!0;default:return!1}}function w(e){return y.hasOwnProperty(e)?y[e]:null}var v=g,k=v.MUST_USE_PROPERTY,T=v.HAS_BOOLEAN_VALUE,I=v.HAS_NUMERIC_VALUE,E=v.HAS_POSITIVE_NUMERIC_VALUE,x=v.HAS_OVERLOADED_BOOLEAN_VALUE,S=v.HAS_STRING_BOOLEAN_VALUE,L={Properties:{allowFullScreen:T,async:T,autoFocus:T,autoPlay:T,capture:x,checked:k|T,cols:E,contentEditable:S,controls:T,default:T,defer:T,disabled:T,download:x,draggable:S,formNoValidate:T,hidden:T,loop:T,multiple:k|T,muted:k|T,noValidate:T,open:T,playsInline:T,readOnly:T,required:T,reversed:T,rows:E,rowSpan:I,scoped:T,seamless:T,selected:k|T,size:E,start:I,span:E,spellCheck:S,style:0,tabIndex:0,itemScope:T,acceptCharset:0,className:0,htmlFor:0,httpEquiv:0,value:S},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMMutationMethods:{value:function(e,n){if(null==n)return e.removeAttribute("value");"number"!==e.type||!1===e.hasAttribute("value")?e.setAttribute("value",""+n):e.validity&&!e.validity.badInput&&e.ownerDocument.activeElement!==e&&e.setAttribute("value",""+n)}}},N=v.HAS_STRING_BOOLEAN_VALUE,C="http://www.w3.org/1999/xlink",A="http://www.w3.org/XML/1998/namespace",P={Properties:{autoReverse:N,externalResourcesRequired:N,preserveAlpha:N},DOMAttributeNames:{autoReverse:"autoReverse",externalResourcesRequired:"externalResourcesRequired",preserveAlpha:"preserveAlpha"},DOMAttributeNamespaces:{xlinkActuate:C,xlinkArcrole:C,xlinkHref:C,xlinkRole:C,xlinkShow:C,xlinkTitle:C,xlinkType:C,xmlBase:A,xmlLang:A,xmlSpace:A}},O=/[\-\:]([a-z])/g;function R(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode x-height xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xmlns:xlink xml:lang xml:space".split(" ").forEach(function(e){var n=e.replace(O,R);P.Properties[n]=0,P.DOMAttributeNames[n]=e}),v.injectDOMPropertyConfig(L),v.injectDOMPropertyConfig(P);var D={_caughtError:null,_hasCaughtError:!1,_rethrowError:null,_hasRethrowError:!1,injection:{injectErrorUtils:function(e){"function"!=typeof e.invokeGuardedCallback&&p("197"),M=e.invokeGuardedCallback}},invokeGuardedCallback:function(e,n,t,o,a,r,i,s,l){M.apply(D,arguments)},invokeGuardedCallbackAndCatchFirstError:function(e,n,t,o,a,r,i,s,l){if(D.invokeGuardedCallback.apply(this,arguments),D.hasCaughtError()){var c=D.clearCaughtError();D._hasRethrowError||(D._hasRethrowError=!0,D._rethrowError=c)}},rethrowCaughtError:function(){return function(){if(D._hasRethrowError){var e=D._rethrowError;throw D._rethrowError=null,D._hasRethrowError=!1,e}}.apply(D,arguments)},hasCaughtError:function(){return D._hasCaughtError},clearCaughtError:function(){if(D._hasCaughtError){var e=D._caughtError;return D._caughtError=null,D._hasCaughtError=!1,e}p("198")}};function M(e,n,t,o,a,r,i,s,l){D._hasCaughtError=!1,D._caughtError=null;var c=Array.prototype.slice.call(arguments,3);try{n.apply(t,c)}catch(e){D._caughtError=e,D._hasCaughtError=!0}}var F=null,B={};function W(){if(F)for(var e in B){var n=B[e],t=F.indexOf(e);if(-1<t||p("96",e),!_[t])for(var o in n.extractEvents||p("97",e),_[t]=n,t=n.eventTypes){var a=void 0,r=t[o],i=n,s=o;U.hasOwnProperty(s)&&p("99",s),U[s]=r;var l=r.phasedRegistrationNames;if(l){for(a in l)l.hasOwnProperty(a)&&j(l[a],i,s);a=!0}else r.registrationName?(j(r.registrationName,i,s),a=!0):a=!1;a||p("98",o,e)}}}function j(e,n,t){z[e]&&p("100",e),z[e]=n,G[e]=n.eventTypes[t].dependencies}var _=[],U={},z={},G={};function V(e){F&&p("101"),F=Array.prototype.slice.call(e),W()}function H(e){var n,t=!1;for(n in e)if(e.hasOwnProperty(n)){var o=e[n];B.hasOwnProperty(n)&&B[n]===o||(B[n]&&p("102",n),B[n]=o,t=!0)}t&&W()}var q=Object.freeze({plugins:_,eventNameDispatchConfigs:U,registrationNameModules:z,registrationNameDependencies:G,possibleRegistrationNames:null,injectEventPluginOrder:V,injectEventPluginsByName:H}),K=null,Y=null,X=null;function Q(e,n,t,o){n=e.type||"unknown-event",e.currentTarget=X(o),D.invokeGuardedCallbackAndCatchFirstError(n,t,void 0,e),e.currentTarget=null}function $(e,n){return null==n&&p("30"),null==e?n:Array.isArray(e)?Array.isArray(n)?(e.push.apply(e,n),e):(e.push(n),e):Array.isArray(n)?[e].concat(n):[e,n]}function J(e,n,t){Array.isArray(e)?e.forEach(n,t):e&&n.call(t,e)}var Z=null;function ee(e,n){if(e){var t=e._dispatchListeners,o=e._dispatchInstances;if(Array.isArray(t))for(var a=0;a<t.length&&!e.isPropagationStopped();a++)Q(e,n,t[a],o[a]);else t&&Q(e,n,t,o);e._dispatchListeners=null,e._dispatchInstances=null,e.isPersistent()||e.constructor.release(e)}}function ne(e){return ee(e,!0)}function te(e){return ee(e,!1)}var oe={injectEventPluginOrder:V,injectEventPluginsByName:H};function ae(e,n){var t=e.stateNode;if(!t)return null;var o=K(t);if(!o)return null;t=o[n];e:switch(n){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":(o=!o.disabled)||(o=!("button"===(e=e.type)||"input"===e||"select"===e||"textarea"===e)),e=!o;break e;default:e=!1}return e?null:(t&&"function"!=typeof t&&p("231",n,typeof t),t)}function re(e,n,t,o){for(var a,r=0;r<_.length;r++){var i=_[r];i&&(i=i.extractEvents(e,n,t,o))&&(a=$(a,i))}return a}function ie(e){e&&(Z=$(Z,e))}function se(e){var n=Z;Z=null,n&&(J(n,e?ne:te),Z&&p("95"),D.rethrowCaughtError())}var le=Object.freeze({injection:oe,getListener:ae,extractEvents:re,enqueueEvents:ie,processEventQueue:se}),ce=Math.random().toString(36).slice(2),ue="__reactInternalInstance$"+ce,he="__reactEventHandlers$"+ce;function de(e){if(e[ue])return e[ue];for(var n=[];!e[ue];){if(n.push(e),!e.parentNode)return null;e=e.parentNode}var t=void 0,o=e[ue];if(5===o.tag||6===o.tag)return o;for(;e&&(o=e[ue]);e=n.pop())t=o;return t}function pe(e){if(5===e.tag||6===e.tag)return e.stateNode;p("33")}function fe(e){return e[he]||null}var me=Object.freeze({precacheFiberNode:function(e,n){n[ue]=e},getClosestInstanceFromNode:de,getInstanceFromNode:function(e){return!(e=e[ue])||5!==e.tag&&6!==e.tag?null:e},getNodeFromInstance:pe,getFiberCurrentPropsFromNode:fe,updateFiberProps:function(e,n){e[he]=n}});function ge(e){do{e=e.return}while(e&&5!==e.tag);return e||null}function ye(e,n,t){for(var o=[];e;)o.push(e),e=ge(e);for(e=o.length;0<e--;)n(o[e],"captured",t);for(e=0;e<o.length;e++)n(o[e],"bubbled",t)}function be(e,n,t){(n=ae(e,t.dispatchConfig.phasedRegistrationNames[n]))&&(t._dispatchListeners=$(t._dispatchListeners,n),t._dispatchInstances=$(t._dispatchInstances,e))}function we(e){e&&e.dispatchConfig.phasedRegistrationNames&&ye(e._targetInst,be,e)}function ve(e){if(e&&e.dispatchConfig.phasedRegistrationNames){var n=e._targetInst;ye(n=n?ge(n):null,be,e)}}function ke(e,n,t){e&&t&&t.dispatchConfig.registrationName&&(n=ae(e,t.dispatchConfig.registrationName))&&(t._dispatchListeners=$(t._dispatchListeners,n),t._dispatchInstances=$(t._dispatchInstances,e))}function Te(e){e&&e.dispatchConfig.registrationName&&ke(e._targetInst,null,e)}function Ie(e){J(e,we)}function Ee(e,n,t,o){if(t&&o)e:{for(var a=t,r=o,i=0,s=a;s;s=ge(s))i++;s=0;for(var l=r;l;l=ge(l))s++;for(;0<i-s;)a=ge(a),i--;for(;0<s-i;)r=ge(r),s--;for(;i--;){if(a===r||a===r.alternate)break e;a=ge(a),r=ge(r)}a=null}else a=null;for(r=a,a=[];t&&t!==r&&(null===(i=t.alternate)||i!==r);)a.push(t),t=ge(t);for(t=[];o&&o!==r&&(null===(i=o.alternate)||i!==r);)t.push(o),o=ge(o);for(o=0;o<a.length;o++)ke(a[o],"bubbled",e);for(e=t.length;0<e--;)ke(t[e],"captured",n)}var xe=Object.freeze({accumulateTwoPhaseDispatches:Ie,accumulateTwoPhaseDispatchesSkipTarget:function(e){J(e,ve)},accumulateEnterLeaveDispatches:Ee,accumulateDirectDispatches:function(e){J(e,Te)}}),Se=null;function Le(){return!Se&&a.canUseDOM&&(Se="textContent"in document.documentElement?"textContent":"innerText"),Se}var Ne={_root:null,_startText:null,_fallbackText:null};function Ce(){if(Ne._fallbackText)return Ne._fallbackText;var e,n,t=Ne._startText,o=t.length,a=Ae(),r=a.length;for(e=0;e<o&&t[e]===a[e];e++);var i=o-e;for(n=1;n<=i&&t[o-n]===a[r-n];n++);return Ne._fallbackText=a.slice(e,1<n?1-n:void 0),Ne._fallbackText}function Ae(){return"value"in Ne._root?Ne._root.value:Ne._root[Le()]}var Pe="dispatchConfig _targetInst nativeEvent isDefaultPrevented isPropagationStopped _dispatchListeners _dispatchInstances".split(" "),Oe={type:null,target:null,currentTarget:i.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};function Re(e,n,t,o){for(var a in this.dispatchConfig=e,this._targetInst=n,this.nativeEvent=t,e=this.constructor.Interface)e.hasOwnProperty(a)&&((n=e[a])?this[a]=n(t):"target"===a?this.target=o:this[a]=t[a]);return this.isDefaultPrevented=(null!=t.defaultPrevented?t.defaultPrevented:!1===t.returnValue)?i.thatReturnsTrue:i.thatReturnsFalse,this.isPropagationStopped=i.thatReturnsFalse,this}function De(e,n,t,o){if(this.eventPool.length){var a=this.eventPool.pop();return this.call(a,e,n,t,o),a}return new this(e,n,t,o)}function Me(e){e instanceof this||p("223"),e.destructor(),10>this.eventPool.length&&this.eventPool.push(e)}function Fe(e){e.eventPool=[],e.getPooled=De,e.release=Me}function Be(e,n,t,o){return Re.call(this,e,n,t,o)}function We(e,n,t,o){return Re.call(this,e,n,t,o)}r(Re.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=i.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=i.thatReturnsTrue)},persist:function(){this.isPersistent=i.thatReturnsTrue},isPersistent:i.thatReturnsFalse,destructor:function(){var e,n=this.constructor.Interface;for(e in n)this[e]=null;for(n=0;n<Pe.length;n++)this[Pe[n]]=null}}),Re.Interface=Oe,Re.augmentClass=function(e,n){function t(){}t.prototype=this.prototype;var o=new t;r(o,e.prototype),e.prototype=o,e.prototype.constructor=e,e.Interface=r({},this.Interface,n),e.augmentClass=this.augmentClass,Fe(e)},Fe(Re),Re.augmentClass(Be,{data:null}),Re.augmentClass(We,{data:null});var je,_e=[9,13,27,32],Ue=a.canUseDOM&&"CompositionEvent"in window,ze=null;if(a.canUseDOM&&"documentMode"in document&&(ze=document.documentMode),je=a.canUseDOM&&"TextEvent"in window&&!ze){var Ge=window.opera;je=!("object"==typeof Ge&&"function"==typeof Ge.version&&12>=parseInt(Ge.version(),10))}var Ve=je,He=a.canUseDOM&&(!Ue||ze&&8<ze&&11>=ze),qe=String.fromCharCode(32),Ke={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["topCompositionEnd","topKeyPress","topTextInput","topPaste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"topBlur topCompositionEnd topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"topBlur topCompositionStart topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"topBlur topCompositionUpdate topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")}},Ye=!1;function Xe(e,n){switch(e){case"topKeyUp":return-1!==_e.indexOf(n.keyCode);case"topKeyDown":return 229!==n.keyCode;case"topKeyPress":case"topMouseDown":case"topBlur":return!0;default:return!1}}function Qe(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var $e=!1;var Je={eventTypes:Ke,extractEvents:function(e,n,t,o){var a;if(Ue)e:{switch(e){case"topCompositionStart":var r=Ke.compositionStart;break e;case"topCompositionEnd":r=Ke.compositionEnd;break e;case"topCompositionUpdate":r=Ke.compositionUpdate;break e}r=void 0}else $e?Xe(e,t)&&(r=Ke.compositionEnd):"topKeyDown"===e&&229===t.keyCode&&(r=Ke.compositionStart);return r?(He&&($e||r!==Ke.compositionStart?r===Ke.compositionEnd&&$e&&(a=Ce()):(Ne._root=o,Ne._startText=Ae(),$e=!0)),r=Be.getPooled(r,n,t,o),a?r.data=a:null!==(a=Qe(t))&&(r.data=a),Ie(r),a=r):a=null,(e=Ve?function(e,n){switch(e){case"topCompositionEnd":return Qe(n);case"topKeyPress":return 32!==n.which?null:(Ye=!0,qe);case"topTextInput":return(e=n.data)===qe&&Ye?null:e;default:return null}}(e,t):function(e,n){if($e)return"topCompositionEnd"===e||!Ue&&Xe(e,n)?(e=Ce(),Ne._root=null,Ne._startText=null,Ne._fallbackText=null,$e=!1,e):null;switch(e){case"topPaste":return null;case"topKeyPress":if(!(n.ctrlKey||n.altKey||n.metaKey)||n.ctrlKey&&n.altKey){if(n.char&&1<n.char.length)return n.char;if(n.which)return String.fromCharCode(n.which)}return null;case"topCompositionEnd":return He?null:n.data;default:return null}}(e,t))?((n=We.getPooled(Ke.beforeInput,n,t,o)).data=e,Ie(n)):n=null,[a,n]}},Ze=null,en=null,nn=null;function tn(e){if(e=Y(e)){Ze&&"function"==typeof Ze.restoreControlledState||p("194");var n=K(e.stateNode);Ze.restoreControlledState(e.stateNode,e.type,n)}}var on={injectFiberControlledHostComponent:function(e){Ze=e}};function an(e){en?nn?nn.push(e):nn=[e]:en=e}function rn(){if(en){var e=en,n=nn;if(nn=en=null,tn(e),n)for(e=0;e<n.length;e++)tn(n[e])}}var sn=Object.freeze({injection:on,enqueueStateRestore:an,restoreStateIfNeeded:rn});function ln(e,n){return e(n)}var cn=!1;function un(e,n){if(cn)return ln(e,n);cn=!0;try{return ln(e,n)}finally{cn=!1,rn()}}var hn,dn={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function pn(e){var n=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===n?!!dn[e.type]:"textarea"===n}function fn(e){return(e=e.target||e.srcElement||window).correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}function mn(e,n){if(!a.canUseDOM||n&&!("addEventListener"in document))return!1;var t=(n="on"+e)in document;return t||((t=document.createElement("div")).setAttribute(n,"return;"),t="function"==typeof t[n]),!t&&hn&&"wheel"===e&&(t=document.implementation.hasFeature("Events.wheel","3.0")),t}function gn(e){var n=e.type;return(e=e.nodeName)&&"input"===e.toLowerCase()&&("checkbox"===n||"radio"===n)}function yn(e){e._valueTracker||(e._valueTracker=function(e){var n=gn(e)?"checked":"value",t=Object.getOwnPropertyDescriptor(e.constructor.prototype,n),o=""+e[n];if(!e.hasOwnProperty(n)&&"function"==typeof t.get&&"function"==typeof t.set)return Object.defineProperty(e,n,{enumerable:t.enumerable,configurable:!0,get:function(){return t.get.call(this)},set:function(e){o=""+e,t.set.call(this,e)}}),{getValue:function(){return o},setValue:function(e){o=""+e},stopTracking:function(){e._valueTracker=null,delete e[n]}}}(e))}function bn(e){if(!e)return!1;var n=e._valueTracker;if(!n)return!0;var t=n.getValue(),o="";return e&&(o=gn(e)?e.checked?"true":"false":e.value),(e=o)!==t&&(n.setValue(e),!0)}a.canUseDOM&&(hn=document.implementation&&document.implementation.hasFeature&&!0!==document.implementation.hasFeature("",""));var wn={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:"topBlur topChange topClick topFocus topInput topKeyDown topKeyUp topSelectionChange".split(" ")}};function vn(e,n,t){return(e=Re.getPooled(wn.change,e,n,t)).type="change",an(t),Ie(e),e}var kn=null,Tn=null;function In(e){ie(e),se(!1)}function En(e){if(bn(pe(e)))return e}function xn(e,n){if("topChange"===e)return n}var Sn=!1;function Ln(){kn&&(kn.detachEvent("onpropertychange",Nn),Tn=kn=null)}function Nn(e){"value"===e.propertyName&&En(Tn)&&un(In,e=vn(Tn,e,fn(e)))}function Cn(e,n,t){"topFocus"===e?(Ln(),Tn=t,(kn=n).attachEvent("onpropertychange",Nn)):"topBlur"===e&&Ln()}function An(e){if("topSelectionChange"===e||"topKeyUp"===e||"topKeyDown"===e)return En(Tn)}function Pn(e,n){if("topClick"===e)return En(n)}function On(e,n){if("topInput"===e||"topChange"===e)return En(n)}a.canUseDOM&&(Sn=mn("input")&&(!document.documentMode||9<document.documentMode));var Rn={eventTypes:wn,_isInputEventSupported:Sn,extractEvents:function(e,n,t,o){var a=n?pe(n):window,r=a.nodeName&&a.nodeName.toLowerCase();if("select"===r||"input"===r&&"file"===a.type)var i=xn;else if(pn(a))if(Sn)i=On;else{i=An;var s=Cn}else!(r=a.nodeName)||"input"!==r.toLowerCase()||"checkbox"!==a.type&&"radio"!==a.type||(i=Pn);if(i&&(i=i(e,n)))return vn(i,t,o);s&&s(e,a,n),"topBlur"===e&&null!=n&&(e=n._wrapperState||a._wrapperState)&&e.controlled&&"number"===a.type&&(e=""+a.value,a.getAttribute("value")!==e&&a.setAttribute("value",e))}};function Dn(e,n,t,o){return Re.call(this,e,n,t,o)}Re.augmentClass(Dn,{view:null,detail:null});var Mn={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function Fn(e){var n=this.nativeEvent;return n.getModifierState?n.getModifierState(e):!!(e=Mn[e])&&!!n[e]}function Bn(){return Fn}function Wn(e,n,t,o){return Re.call(this,e,n,t,o)}Dn.augmentClass(Wn,{screenX:null,screenY:null,clientX:null,clientY:null,pageX:null,pageY:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,getModifierState:Bn,button:null,buttons:null,relatedTarget:function(e){return e.relatedTarget||(e.fromElement===e.srcElement?e.toElement:e.fromElement)}});var jn={mouseEnter:{registrationName:"onMouseEnter",dependencies:["topMouseOut","topMouseOver"]},mouseLeave:{registrationName:"onMouseLeave",dependencies:["topMouseOut","topMouseOver"]}},_n={eventTypes:jn,extractEvents:function(e,n,t,o){if("topMouseOver"===e&&(t.relatedTarget||t.fromElement)||"topMouseOut"!==e&&"topMouseOver"!==e)return null;var a=o.window===o?o:(a=o.ownerDocument)?a.defaultView||a.parentWindow:window;if("topMouseOut"===e?(e=n,n=(n=t.relatedTarget||t.toElement)?de(n):null):e=null,e===n)return null;var r=null==e?a:pe(e);a=null==n?a:pe(n);var i=Wn.getPooled(jn.mouseLeave,e,t,o);return i.type="mouseleave",i.target=r,i.relatedTarget=a,(t=Wn.getPooled(jn.mouseEnter,n,t,o)).type="mouseenter",t.target=a,t.relatedTarget=r,Ee(i,t,e,n),[i,t]}},Un=o.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner;function zn(e){return"string"==typeof(e=e.type)?e:"function"==typeof e?e.displayName||e.name:null}function Gn(e){var n=e;if(e.alternate)for(;n.return;)n=n.return;else{if(0!=(2&n.effectTag))return 1;for(;n.return;)if(0!=(2&(n=n.return).effectTag))return 1}return 3===n.tag?2:3}function Vn(e){return!!(e=e._reactInternalFiber)&&2===Gn(e)}function Hn(e){2!==Gn(e)&&p("188")}function qn(e){var n=e.alternate;if(!n)return 3===(n=Gn(e))&&p("188"),1===n?null:e;for(var t=e,o=n;;){var a=t.return,r=a?a.alternate:null;if(!a||!r)break;if(a.child===r.child){for(var i=a.child;i;){if(i===t)return Hn(a),e;if(i===o)return Hn(a),n;i=i.sibling}p("188")}if(t.return!==o.return)t=a,o=r;else{i=!1;for(var s=a.child;s;){if(s===t){i=!0,t=a,o=r;break}if(s===o){i=!0,o=a,t=r;break}s=s.sibling}if(!i){for(s=r.child;s;){if(s===t){i=!0,t=r,o=a;break}if(s===o){i=!0,o=r,t=a;break}s=s.sibling}i||p("189")}}t.alternate!==o&&p("190")}return 3!==t.tag&&p("188"),t.stateNode.current===t?e:n}var Kn=[];function Yn(e){var n=e.targetInst;do{if(!n){e.ancestors.push(n);break}var t;for(t=n;t.return;)t=t.return;if(!(t=3!==t.tag?null:t.stateNode.containerInfo))break;e.ancestors.push(n),n=de(t)}while(n);for(t=0;t<e.ancestors.length;t++)n=e.ancestors[t],Qn(e.topLevelType,n,e.nativeEvent,fn(e.nativeEvent))}var Xn=!0,Qn=void 0;function $n(e){Xn=!!e}function Jn(e,n,t){return t?s.listen(t,n,et.bind(null,e)):null}function Zn(e,n,t){return t?s.capture(t,n,et.bind(null,e)):null}function et(e,n){if(Xn){var t=fn(n);if(null===(t=de(t))||"number"!=typeof t.tag||2===Gn(t)||(t=null),Kn.length){var o=Kn.pop();o.topLevelType=e,o.nativeEvent=n,o.targetInst=t,e=o}else e={topLevelType:e,nativeEvent:n,targetInst:t,ancestors:[]};try{un(Yn,e)}finally{e.topLevelType=null,e.nativeEvent=null,e.targetInst=null,e.ancestors.length=0,10>Kn.length&&Kn.push(e)}}}var nt=Object.freeze({get _enabled(){return Xn},get _handleTopLevel(){return Qn},setHandleTopLevel:function(e){Qn=e},setEnabled:$n,isEnabled:function(){return Xn},trapBubbledEvent:Jn,trapCapturedEvent:Zn,dispatchEvent:et});function tt(e,n){var t={};return t[e.toLowerCase()]=n.toLowerCase(),t["Webkit"+e]="webkit"+n,t["Moz"+e]="moz"+n,t["ms"+e]="MS"+n,t["O"+e]="o"+n.toLowerCase(),t}var ot={animationend:tt("Animation","AnimationEnd"),animationiteration:tt("Animation","AnimationIteration"),animationstart:tt("Animation","AnimationStart"),transitionend:tt("Transition","TransitionEnd")},at={},rt={};function it(e){if(at[e])return at[e];if(!ot[e])return e;var n,t=ot[e];for(n in t)if(t.hasOwnProperty(n)&&n in rt)return at[e]=t[n];return""}a.canUseDOM&&(rt=document.createElement("div").style,"AnimationEvent"in window||(delete ot.animationend.animation,delete ot.animationiteration.animation,delete ot.animationstart.animation),"TransitionEvent"in window||delete ot.transitionend.transition);var st={topAbort:"abort",topAnimationEnd:it("animationend")||"animationend",topAnimationIteration:it("animationiteration")||"animationiteration",topAnimationStart:it("animationstart")||"animationstart",topBlur:"blur",topCancel:"cancel",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topChange:"change",topClick:"click",topClose:"close",topCompositionEnd:"compositionend",topCompositionStart:"compositionstart",topCompositionUpdate:"compositionupdate",topContextMenu:"contextmenu",topCopy:"copy",topCut:"cut",topDoubleClick:"dblclick",topDrag:"drag",topDragEnd:"dragend",topDragEnter:"dragenter",topDragExit:"dragexit",topDragLeave:"dragleave",topDragOver:"dragover",topDragStart:"dragstart",topDrop:"drop",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topFocus:"focus",topInput:"input",topKeyDown:"keydown",topKeyPress:"keypress",topKeyUp:"keyup",topLoadedData:"loadeddata",topLoad:"load",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topMouseDown:"mousedown",topMouseMove:"mousemove",topMouseOut:"mouseout",topMouseOver:"mouseover",topMouseUp:"mouseup",topPaste:"paste",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topScroll:"scroll",topSeeked:"seeked",topSeeking:"seeking",topSelectionChange:"selectionchange",topStalled:"stalled",topSuspend:"suspend",topTextInput:"textInput",topTimeUpdate:"timeupdate",topToggle:"toggle",topTouchCancel:"touchcancel",topTouchEnd:"touchend",topTouchMove:"touchmove",topTouchStart:"touchstart",topTransitionEnd:it("transitionend")||"transitionend",topVolumeChange:"volumechange",topWaiting:"waiting",topWheel:"wheel"},lt={},ct=0,ut="_reactListenersID"+(""+Math.random()).slice(2);function ht(e){return Object.prototype.hasOwnProperty.call(e,ut)||(e[ut]=ct++,lt[e[ut]]={}),lt[e[ut]]}function dt(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function pt(e,n){var t,o=dt(e);for(e=0;o;){if(3===o.nodeType){if(t=e+o.textContent.length,e<=n&&t>=n)return{node:o,offset:n-e};e=t}e:{for(;o;){if(o.nextSibling){o=o.nextSibling;break e}o=o.parentNode}o=void 0}o=dt(o)}}function ft(e){var n=e&&e.nodeName&&e.nodeName.toLowerCase();return n&&("input"===n&&"text"===e.type||"textarea"===n||"true"===e.contentEditable)}var mt=a.canUseDOM&&"documentMode"in document&&11>=document.documentMode,gt={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},dependencies:"topBlur topContextMenu topFocus topKeyDown topKeyUp topMouseDown topMouseUp topSelectionChange".split(" ")}},yt=null,bt=null,wt=null,vt=!1;function kt(e,n){if(vt||null==yt||yt!==l())return null;var t=yt;return"selectionStart"in t&&ft(t)?t={start:t.selectionStart,end:t.selectionEnd}:window.getSelection?t={anchorNode:(t=window.getSelection()).anchorNode,anchorOffset:t.anchorOffset,focusNode:t.focusNode,focusOffset:t.focusOffset}:t=void 0,wt&&c(wt,t)?null:(wt=t,(e=Re.getPooled(gt.select,bt,e,n)).type="select",e.target=yt,Ie(e),e)}var Tt={eventTypes:gt,extractEvents:function(e,n,t,o){var a,r=o.window===o?o.document:9===o.nodeType?o:o.ownerDocument;if(!(a=!r)){e:{r=ht(r),a=G.onSelect;for(var i=0;i<a.length;i++){var s=a[i];if(!r.hasOwnProperty(s)||!r[s]){r=!1;break e}}r=!0}a=!r}if(a)return null;switch(r=n?pe(n):window,e){case"topFocus":(pn(r)||"true"===r.contentEditable)&&(yt=r,bt=n,wt=null);break;case"topBlur":wt=bt=yt=null;break;case"topMouseDown":vt=!0;break;case"topContextMenu":case"topMouseUp":return vt=!1,kt(t,o);case"topSelectionChange":if(mt)break;case"topKeyDown":case"topKeyUp":return kt(t,o)}return null}};function It(e,n,t,o){return Re.call(this,e,n,t,o)}function Et(e,n,t,o){return Re.call(this,e,n,t,o)}function xt(e,n,t,o){return Re.call(this,e,n,t,o)}function St(e){var n=e.keyCode;return"charCode"in e?0===(e=e.charCode)&&13===n&&(e=13):e=n,32<=e||13===e?e:0}Re.augmentClass(It,{animationName:null,elapsedTime:null,pseudoElement:null}),Re.augmentClass(Et,{clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}}),Dn.augmentClass(xt,{relatedTarget:null});var Lt={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},Nt={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"};function Ct(e,n,t,o){return Re.call(this,e,n,t,o)}function At(e,n,t,o){return Re.call(this,e,n,t,o)}function Pt(e,n,t,o){return Re.call(this,e,n,t,o)}function Ot(e,n,t,o){return Re.call(this,e,n,t,o)}function Rt(e,n,t,o){return Re.call(this,e,n,t,o)}Dn.augmentClass(Ct,{key:function(e){if(e.key){var n=Lt[e.key]||e.key;if("Unidentified"!==n)return n}return"keypress"===e.type?13===(e=St(e))?"Enter":String.fromCharCode(e):"keydown"===e.type||"keyup"===e.type?Nt[e.keyCode]||"Unidentified":""},location:null,ctrlKey:null,shiftKey:null,altKey:null,metaKey:null,repeat:null,locale:null,getModifierState:Bn,charCode:function(e){return"keypress"===e.type?St(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?St(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}}),Wn.augmentClass(At,{dataTransfer:null}),Dn.augmentClass(Pt,{touches:null,targetTouches:null,changedTouches:null,altKey:null,metaKey:null,ctrlKey:null,shiftKey:null,getModifierState:Bn}),Re.augmentClass(Ot,{propertyName:null,elapsedTime:null,pseudoElement:null}),Wn.augmentClass(Rt,{deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:null,deltaMode:null});var Dt={},Mt={};"abort animationEnd animationIteration animationStart blur cancel canPlay canPlayThrough click close contextMenu copy cut doubleClick drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error focus input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing progress rateChange reset scroll seeked seeking stalled submit suspend timeUpdate toggle touchCancel touchEnd touchMove touchStart transitionEnd volumeChange waiting wheel".split(" ").forEach(function(e){var n=e[0].toUpperCase()+e.slice(1),t="on"+n;t={phasedRegistrationNames:{bubbled:t,captured:t+"Capture"},dependencies:[n="top"+n]},Dt[e]=t,Mt[n]=t});var Ft={eventTypes:Dt,extractEvents:function(e,n,t,o){var a=Mt[e];if(!a)return null;switch(e){case"topKeyPress":if(0===St(t))return null;case"topKeyDown":case"topKeyUp":e=Ct;break;case"topBlur":case"topFocus":e=xt;break;case"topClick":if(2===t.button)return null;case"topDoubleClick":case"topMouseDown":case"topMouseMove":case"topMouseUp":case"topMouseOut":case"topMouseOver":case"topContextMenu":e=Wn;break;case"topDrag":case"topDragEnd":case"topDragEnter":case"topDragExit":case"topDragLeave":case"topDragOver":case"topDragStart":case"topDrop":e=At;break;case"topTouchCancel":case"topTouchEnd":case"topTouchMove":case"topTouchStart":e=Pt;break;case"topAnimationEnd":case"topAnimationIteration":case"topAnimationStart":e=It;break;case"topTransitionEnd":e=Ot;break;case"topScroll":e=Dn;break;case"topWheel":e=Rt;break;case"topCopy":case"topCut":case"topPaste":e=Et;break;default:e=Re}return Ie(n=e.getPooled(a,n,t,o)),n}};Qn=function(e,n,t,o){ie(e=re(e,n,t,o)),se(!1)},oe.injectEventPluginOrder("ResponderEventPlugin SimpleEventPlugin TapEventPlugin EnterLeaveEventPlugin ChangeEventPlugin SelectEventPlugin BeforeInputEventPlugin".split(" ")),K=me.getFiberCurrentPropsFromNode,Y=me.getInstanceFromNode,X=me.getNodeFromInstance,oe.injectEventPluginsByName({SimpleEventPlugin:Ft,EnterLeaveEventPlugin:_n,ChangeEventPlugin:Rn,SelectEventPlugin:Tt,BeforeInputEventPlugin:Je});var Bt=[],Wt=-1;function jt(e){0>Wt||(e.current=Bt[Wt],Bt[Wt]=null,Wt--)}function _t(e,n){Bt[++Wt]=e.current,e.current=n}new Set;var Ut={current:d},zt={current:!1},Gt=d;function Vt(e){return qt(e)?Gt:Ut.current}function Ht(e,n){var t=e.type.contextTypes;if(!t)return d;var o=e.stateNode;if(o&&o.__reactInternalMemoizedUnmaskedChildContext===n)return o.__reactInternalMemoizedMaskedChildContext;var a,r={};for(a in t)r[a]=n[a];return o&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=n,e.__reactInternalMemoizedMaskedChildContext=r),r}function qt(e){return 2===e.tag&&null!=e.type.childContextTypes}function Kt(e){qt(e)&&(jt(zt),jt(Ut))}function Yt(e,n,t){null!=Ut.cursor&&p("168"),_t(Ut,n),_t(zt,t)}function Xt(e,n){var t=e.stateNode,o=e.type.childContextTypes;if("function"!=typeof t.getChildContext)return n;for(var a in t=t.getChildContext())a in o||p("108",zn(e)||"Unknown",a);return r({},n,t)}function Qt(e){if(!qt(e))return!1;var n=e.stateNode;return n=n&&n.__reactInternalMemoizedMergedChildContext||d,Gt=Ut.current,_t(Ut,n),_t(zt,zt.current),!0}function $t(e,n){var t=e.stateNode;if(t||p("169"),n){var o=Xt(e,Gt);t.__reactInternalMemoizedMergedChildContext=o,jt(zt),jt(Ut),_t(Ut,o)}else jt(zt);_t(zt,n)}function Jt(e,n,t){this.tag=e,this.key=n,this.stateNode=this.type=null,this.sibling=this.child=this.return=null,this.index=0,this.memoizedState=this.updateQueue=this.memoizedProps=this.pendingProps=this.ref=null,this.internalContextTag=t,this.effectTag=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.expirationTime=0,this.alternate=null}function Zt(e,n,t){var o=e.alternate;return null===o?((o=new Jt(e.tag,e.key,e.internalContextTag)).type=e.type,o.stateNode=e.stateNode,o.alternate=e,e.alternate=o):(o.effectTag=0,o.nextEffect=null,o.firstEffect=null,o.lastEffect=null),o.expirationTime=t,o.pendingProps=n,o.child=e.child,o.memoizedProps=e.memoizedProps,o.memoizedState=e.memoizedState,o.updateQueue=e.updateQueue,o.sibling=e.sibling,o.index=e.index,o.ref=e.ref,o}function eo(e,n,t){var o=void 0,a=e.type,r=e.key;return"function"==typeof a?((o=a.prototype&&a.prototype.isReactComponent?new Jt(2,r,n):new Jt(0,r,n)).type=a,o.pendingProps=e.props):"string"==typeof a?((o=new Jt(5,r,n)).type=a,o.pendingProps=e.props):"object"==typeof a&&null!==a&&"number"==typeof a.tag?(o=a).pendingProps=e.props:p("130",null==a?a:typeof a,""),o.expirationTime=t,o}function no(e,n,t,o){return(n=new Jt(10,o,n)).pendingProps=e,n.expirationTime=t,n}function to(e,n,t){return(n=new Jt(6,null,n)).pendingProps=e,n.expirationTime=t,n}function oo(e,n,t){return(n=new Jt(7,e.key,n)).type=e.handler,n.pendingProps=e,n.expirationTime=t,n}function ao(e,n,t){return(e=new Jt(9,null,n)).expirationTime=t,e}function ro(e,n,t){return(n=new Jt(4,e.key,n)).pendingProps=e.children||[],n.expirationTime=t,n.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},n}var io=null,so=null;function lo(e){return function(n){try{return e(n)}catch(e){}}}function co(e){"function"==typeof io&&io(e)}function uo(e){"function"==typeof so&&so(e)}function ho(e){return{baseState:e,expirationTime:0,first:null,last:null,callbackList:null,hasForceUpdate:!1,isInitialized:!1}}function po(e,n){null===e.last?e.first=e.last=n:(e.last.next=n,e.last=n),(0===e.expirationTime||e.expirationTime>n.expirationTime)&&(e.expirationTime=n.expirationTime)}function fo(e,n){var t=e.alternate,o=e.updateQueue;null===o&&(o=e.updateQueue=ho(null)),null!==t?null===(e=t.updateQueue)&&(e=t.updateQueue=ho(null)):e=null,null===(e=e!==o?e:null)?po(o,n):null===o.last||null===e.last?(po(o,n),po(e,n)):(po(o,n),e.last=n)}function mo(e,n,t,o){return"function"==typeof(e=e.partialState)?e.call(n,t,o):e}function go(e,n,t,o,a,i){null!==e&&e.updateQueue===t&&(t=n.updateQueue={baseState:t.baseState,expirationTime:t.expirationTime,first:t.first,last:t.last,isInitialized:t.isInitialized,callbackList:null,hasForceUpdate:!1}),t.expirationTime=0,t.isInitialized?e=t.baseState:(e=t.baseState=n.memoizedState,t.isInitialized=!0);for(var s=!0,l=t.first,c=!1;null!==l;){var u=l.expirationTime;if(u>i){var h=t.expirationTime;(0===h||h>u)&&(t.expirationTime=u),c||(c=!0,t.baseState=e)}else c||(t.first=l.next,null===t.first&&(t.last=null)),l.isReplace?(e=mo(l,o,e,a),s=!0):(u=mo(l,o,e,a))&&(e=s?r({},e,u):r(e,u),s=!1),l.isForced&&(t.hasForceUpdate=!0),null!==l.callback&&(null===(u=t.callbackList)&&(u=t.callbackList=[]),u.push(l));l=l.next}return null!==t.callbackList?n.effectTag|=32:null!==t.first||t.hasForceUpdate||(n.updateQueue=null),c||(t.baseState=e),e}function yo(e,n){var t=e.callbackList;if(null!==t)for(e.callbackList=null,e=0;e<t.length;e++){var o=t[e],a=o.callback;o.callback=null,"function"!=typeof a&&p("191",a),a.call(n)}}var bo="function"==typeof Symbol&&Symbol.for,wo=bo?Symbol.for("react.element"):60103,vo=bo?Symbol.for("react.call"):60104,ko=bo?Symbol.for("react.return"):60105,To=bo?Symbol.for("react.portal"):60106,Io=bo?Symbol.for("react.fragment"):60107,Eo="function"==typeof Symbol&&Symbol.iterator;function xo(e){return null===e||void 0===e?null:"function"==typeof(e=Eo&&e[Eo]||e["@@iterator"])?e:null}var So=Array.isArray;function Lo(e,n){var t=n.ref;if(null!==t&&"function"!=typeof t){if(n._owner){var o=void 0;(n=n._owner)&&(2!==n.tag&&p("110"),o=n.stateNode),o||p("147",t);var a=""+t;return null!==e&&null!==e.ref&&e.ref._stringRef===a?e.ref:((e=function(e){var n=o.refs===d?o.refs={}:o.refs;null===e?delete n[a]:n[a]=e})._stringRef=a,e)}"string"!=typeof t&&p("148"),n._owner||p("149",t)}return t}function No(e,n){"textarea"!==e.type&&p("31","[object Object]"===Object.prototype.toString.call(n)?"object with keys {"+Object.keys(n).join(", ")+"}":n,"")}function Co(e){function n(n,t){if(e){var o=n.lastEffect;null!==o?(o.nextEffect=t,n.lastEffect=t):n.firstEffect=n.lastEffect=t,t.nextEffect=null,t.effectTag=8}}function t(t,o){if(!e)return null;for(;null!==o;)n(t,o),o=o.sibling;return null}function o(e,n){for(e=new Map;null!==n;)null!==n.key?e.set(n.key,n):e.set(n.index,n),n=n.sibling;return e}function a(e,n,t){return(e=Zt(e,n,t)).index=0,e.sibling=null,e}function r(n,t,o){return n.index=o,e?null!==(o=n.alternate)?(o=o.index)<t?(n.effectTag=2,t):o:(n.effectTag=2,t):t}function i(n){return e&&null===n.alternate&&(n.effectTag=2),n}function s(e,n,t,o){return null===n||6!==n.tag?((n=to(t,e.internalContextTag,o)).return=e,n):((n=a(n,t,o)).return=e,n)}function l(e,n,t,o){return null!==n&&n.type===t.type?((o=a(n,t.props,o)).ref=Lo(n,t),o.return=e,o):((o=eo(t,e.internalContextTag,o)).ref=Lo(n,t),o.return=e,o)}function c(e,n,t,o){return null===n||7!==n.tag?((n=oo(t,e.internalContextTag,o)).return=e,n):((n=a(n,t,o)).return=e,n)}function u(e,n,t,o){return null===n||9!==n.tag?((n=ao(t,e.internalContextTag,o)).type=t.value,n.return=e,n):((n=a(n,null,o)).type=t.value,n.return=e,n)}function h(e,n,t,o){return null===n||4!==n.tag||n.stateNode.containerInfo!==t.containerInfo||n.stateNode.implementation!==t.implementation?((n=ro(t,e.internalContextTag,o)).return=e,n):((n=a(n,t.children||[],o)).return=e,n)}function d(e,n,t,o,r){return null===n||10!==n.tag?((n=no(t,e.internalContextTag,o,r)).return=e,n):((n=a(n,t,o)).return=e,n)}function f(e,n,t){if("string"==typeof n||"number"==typeof n)return(n=to(""+n,e.internalContextTag,t)).return=e,n;if("object"==typeof n&&null!==n){switch(n.$$typeof){case wo:return n.type===Io?((n=no(n.props.children,e.internalContextTag,t,n.key)).return=e,n):((t=eo(n,e.internalContextTag,t)).ref=Lo(null,n),t.return=e,t);case vo:return(n=oo(n,e.internalContextTag,t)).return=e,n;case ko:return(t=ao(n,e.internalContextTag,t)).type=n.value,t.return=e,t;case To:return(n=ro(n,e.internalContextTag,t)).return=e,n}if(So(n)||xo(n))return(n=no(n,e.internalContextTag,t,null)).return=e,n;No(e,n)}return null}function m(e,n,t,o){var a=null!==n?n.key:null;if("string"==typeof t||"number"==typeof t)return null!==a?null:s(e,n,""+t,o);if("object"==typeof t&&null!==t){switch(t.$$typeof){case wo:return t.key===a?t.type===Io?d(e,n,t.props.children,o,a):l(e,n,t,o):null;case vo:return t.key===a?c(e,n,t,o):null;case ko:return null===a?u(e,n,t,o):null;case To:return t.key===a?h(e,n,t,o):null}if(So(t)||xo(t))return null!==a?null:d(e,n,t,o,null);No(e,t)}return null}function g(e,n,t,o,a){if("string"==typeof o||"number"==typeof o)return s(n,e=e.get(t)||null,""+o,a);if("object"==typeof o&&null!==o){switch(o.$$typeof){case wo:return e=e.get(null===o.key?t:o.key)||null,o.type===Io?d(n,e,o.props.children,a,o.key):l(n,e,o,a);case vo:return c(n,e=e.get(null===o.key?t:o.key)||null,o,a);case ko:return u(n,e=e.get(t)||null,o,a);case To:return h(n,e=e.get(null===o.key?t:o.key)||null,o,a)}if(So(o)||xo(o))return d(n,e=e.get(t)||null,o,a,null);No(n,o)}return null}return function(s,l,c,u){"object"==typeof c&&null!==c&&c.type===Io&&null===c.key&&(c=c.props.children);var h="object"==typeof c&&null!==c;if(h)switch(c.$$typeof){case wo:e:{var d=c.key;for(h=l;null!==h;){if(h.key===d){if(10===h.tag?c.type===Io:h.type===c.type){t(s,h.sibling),(l=a(h,c.type===Io?c.props.children:c.props,u)).ref=Lo(h,c),l.return=s,s=l;break e}t(s,h);break}n(s,h),h=h.sibling}c.type===Io?((l=no(c.props.children,s.internalContextTag,u,c.key)).return=s,s=l):((u=eo(c,s.internalContextTag,u)).ref=Lo(l,c),u.return=s,s=u)}return i(s);case vo:e:{for(h=c.key;null!==l;){if(l.key===h){if(7===l.tag){t(s,l.sibling),(l=a(l,c,u)).return=s,s=l;break e}t(s,l);break}n(s,l),l=l.sibling}(l=oo(c,s.internalContextTag,u)).return=s,s=l}return i(s);case ko:e:{if(null!==l){if(9===l.tag){t(s,l.sibling),(l=a(l,null,u)).type=c.value,l.return=s,s=l;break e}t(s,l)}(l=ao(c,s.internalContextTag,u)).type=c.value,l.return=s,s=l}return i(s);case To:e:{for(h=c.key;null!==l;){if(l.key===h){if(4===l.tag&&l.stateNode.containerInfo===c.containerInfo&&l.stateNode.implementation===c.implementation){t(s,l.sibling),(l=a(l,c.children||[],u)).return=s,s=l;break e}t(s,l);break}n(s,l),l=l.sibling}(l=ro(c,s.internalContextTag,u)).return=s,s=l}return i(s)}if("string"==typeof c||"number"==typeof c)return c=""+c,null!==l&&6===l.tag?(t(s,l.sibling),l=a(l,c,u)):(t(s,l),l=to(c,s.internalContextTag,u)),l.return=s,i(s=l);if(So(c))return function(a,i,s,l){for(var c=null,u=null,h=i,d=i=0,p=null;null!==h&&d<s.length;d++){h.index>d?(p=h,h=null):p=h.sibling;var y=m(a,h,s[d],l);if(null===y){null===h&&(h=p);break}e&&h&&null===y.alternate&&n(a,h),i=r(y,i,d),null===u?c=y:u.sibling=y,u=y,h=p}if(d===s.length)return t(a,h),c;if(null===h){for(;d<s.length;d++)(h=f(a,s[d],l))&&(i=r(h,i,d),null===u?c=h:u.sibling=h,u=h);return c}for(h=o(a,h);d<s.length;d++)(p=g(h,a,d,s[d],l))&&(e&&null!==p.alternate&&h.delete(null===p.key?d:p.key),i=r(p,i,d),null===u?c=p:u.sibling=p,u=p);return e&&h.forEach(function(e){return n(a,e)}),c}(s,l,c,u);if(xo(c))return function(a,i,s,l){var c=xo(s);"function"!=typeof c&&p("150"),null==(s=c.call(s))&&p("151");for(var u=c=null,h=i,d=i=0,y=null,b=s.next();null!==h&&!b.done;d++,b=s.next()){h.index>d?(y=h,h=null):y=h.sibling;var w=m(a,h,b.value,l);if(null===w){h||(h=y);break}e&&h&&null===w.alternate&&n(a,h),i=r(w,i,d),null===u?c=w:u.sibling=w,u=w,h=y}if(b.done)return t(a,h),c;if(null===h){for(;!b.done;d++,b=s.next())null!==(b=f(a,b.value,l))&&(i=r(b,i,d),null===u?c=b:u.sibling=b,u=b);return c}for(h=o(a,h);!b.done;d++,b=s.next())null!==(b=g(h,a,d,b.value,l))&&(e&&null!==b.alternate&&h.delete(null===b.key?d:b.key),i=r(b,i,d),null===u?c=b:u.sibling=b,u=b);return e&&h.forEach(function(e){return n(a,e)}),c}(s,l,c,u);if(h&&No(s,c),void 0===c)switch(s.tag){case 2:case 1:p("152",(u=s.type).displayName||u.name||"Component")}return t(s,l)}}var Ao=Co(!0),Po=Co(!1);function Oo(e,n,t,o,a){function r(e,n,t){var o=n.expirationTime;n.child=null===e?Po(n,null,t,o):Ao(n,e.child,t,o)}function i(e,n){var t=n.ref;null===t||e&&e.ref===t||(n.effectTag|=128)}function s(e,n,t,o){if(i(e,n),!t)return o&&$t(n,!1),u(e,n);t=n.stateNode,Un.current=n;var a=t.render();return n.effectTag|=1,r(e,n,a),n.memoizedState=t.state,n.memoizedProps=t.props,o&&$t(n,!0),n.child}function l(e){var n=e.stateNode;n.pendingContext?Yt(0,n.pendingContext,n.pendingContext!==n.context):n.context&&Yt(0,n.context,!1),b(e,n.containerInfo)}function u(e,n){if(null!==e&&n.child!==e.child&&p("153"),null!==n.child){var t=Zt(e=n.child,e.pendingProps,e.expirationTime);for(n.child=t,t.return=n;null!==e.sibling;)e=e.sibling,(t=t.sibling=Zt(e,e.pendingProps,e.expirationTime)).return=n;t.sibling=null}return n.child}function h(e,n){switch(n.tag){case 3:l(n);break;case 2:Qt(n);break;case 4:b(n,n.stateNode.containerInfo)}return null}var f=e.shouldSetTextContent,m=e.useSyncScheduling,g=e.shouldDeprioritizeSubtree,y=n.pushHostContext,b=n.pushHostContainer,w=t.enterHydrationState,v=t.resetHydrationState,k=t.tryToClaimNextHydratableInstance,T=(e=function(e,n,t,o){function a(e,n){n.updater=r,e.stateNode=n,n._reactInternalFiber=e}var r={isMounted:Vn,enqueueSetState:function(t,o,a){t=t._reactInternalFiber,a=void 0===a?null:a;var r=n(t);fo(t,{expirationTime:r,partialState:o,callback:a,isReplace:!1,isForced:!1,nextCallback:null,next:null}),e(t,r)},enqueueReplaceState:function(t,o,a){t=t._reactInternalFiber,a=void 0===a?null:a;var r=n(t);fo(t,{expirationTime:r,partialState:o,callback:a,isReplace:!0,isForced:!1,nextCallback:null,next:null}),e(t,r)},enqueueForceUpdate:function(t,o){t=t._reactInternalFiber,o=void 0===o?null:o;var a=n(t);fo(t,{expirationTime:a,partialState:null,callback:o,isReplace:!1,isForced:!0,nextCallback:null,next:null}),e(t,a)}};return{adoptClassInstance:a,constructClassInstance:function(e,n){var t=e.type,o=Vt(e),r=2===e.tag&&null!=e.type.contextTypes,i=r?Ht(e,o):d;return a(e,n=new t(n,i)),r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=o,e.__reactInternalMemoizedMaskedChildContext=i),n},mountClassInstance:function(e,n){var t=e.alternate,o=e.stateNode,a=o.state||null,i=e.pendingProps;i||p("158");var s=Vt(e);o.props=i,o.state=e.memoizedState=a,o.refs=d,o.context=Ht(e,s),null!=e.type&&null!=e.type.prototype&&!0===e.type.prototype.unstable_isAsyncReactComponent&&(e.internalContextTag|=1),"function"==typeof o.componentWillMount&&(a=o.state,o.componentWillMount(),a!==o.state&&r.enqueueReplaceState(o,o.state,null),null!==(a=e.updateQueue)&&(o.state=go(t,e,a,o,i,n))),"function"==typeof o.componentDidMount&&(e.effectTag|=4)},updateClassInstance:function(e,n,a){var i=n.stateNode;i.props=n.memoizedProps,i.state=n.memoizedState;var s=n.memoizedProps,l=n.pendingProps;l||null==(l=s)&&p("159");var u=i.context,h=Vt(n);if(h=Ht(n,h),"function"!=typeof i.componentWillReceiveProps||s===l&&u===h||(u=i.state,i.componentWillReceiveProps(l,h),i.state!==u&&r.enqueueReplaceState(i,i.state,null)),u=n.memoizedState,a=null!==n.updateQueue?go(e,n,n.updateQueue,i,l,a):u,!(s!==l||u!==a||zt.current||null!==n.updateQueue&&n.updateQueue.hasForceUpdate))return"function"!=typeof i.componentDidUpdate||s===e.memoizedProps&&u===e.memoizedState||(n.effectTag|=4),!1;var d=l;if(null===s||null!==n.updateQueue&&n.updateQueue.hasForceUpdate)d=!0;else{var f=n.stateNode,m=n.type;d="function"==typeof f.shouldComponentUpdate?f.shouldComponentUpdate(d,a,h):!(m.prototype&&m.prototype.isPureReactComponent&&c(s,d)&&c(u,a))}return d?("function"==typeof i.componentWillUpdate&&i.componentWillUpdate(l,a,h),"function"==typeof i.componentDidUpdate&&(n.effectTag|=4)):("function"!=typeof i.componentDidUpdate||s===e.memoizedProps&&u===e.memoizedState||(n.effectTag|=4),t(n,l),o(n,a)),i.props=l,i.state=a,i.context=h,d}}}(o,a,function(e,n){e.memoizedProps=n},function(e,n){e.memoizedState=n})).adoptClassInstance,I=e.constructClassInstance,E=e.mountClassInstance,x=e.updateClassInstance;return{beginWork:function(e,n,t){if(0===n.expirationTime||n.expirationTime>t)return h(0,n);switch(n.tag){case 0:null!==e&&p("155");var o=n.type,a=n.pendingProps,c=Vt(n);return o=o(a,c=Ht(n,c)),n.effectTag|=1,"object"==typeof o&&null!==o&&"function"==typeof o.render?(n.tag=2,a=Qt(n),T(n,o),E(n,t),n=s(e,n,!0,a)):(n.tag=1,r(e,n,o),n.memoizedProps=a,n=n.child),n;case 1:e:{if(a=n.type,t=n.pendingProps,o=n.memoizedProps,zt.current)null===t&&(t=o);else if(null===t||o===t){n=u(e,n);break e}a=a(t,o=Ht(n,o=Vt(n))),n.effectTag|=1,r(e,n,a),n.memoizedProps=t,n=n.child}return n;case 2:return a=Qt(n),o=void 0,null===e?n.stateNode?p("153"):(I(n,n.pendingProps),E(n,t),o=!0):o=x(e,n,t),s(e,n,o,a);case 3:return l(n),null!==(a=n.updateQueue)?(o=n.memoizedState)===(a=go(e,n,a,null,null,t))?(v(),n=u(e,n)):(o=a.element,c=n.stateNode,(null===e||null===e.child)&&c.hydrate&&w(n)?(n.effectTag|=2,n.child=Po(n,null,o,t)):(v(),r(e,n,o)),n.memoizedState=a,n=n.child):(v(),n=u(e,n)),n;case 5:y(n),null===e&&k(n),a=n.type;var d=n.memoizedProps;return null===(o=n.pendingProps)&&(null===(o=d)&&p("154")),c=null!==e?e.memoizedProps:null,zt.current||null!==o&&d!==o?(d=o.children,f(a,o)?d=null:c&&f(a,c)&&(n.effectTag|=16),i(e,n),2147483647!==t&&!m&&g(a,o)?(n.expirationTime=2147483647,n=null):(r(e,n,d),n.memoizedProps=o,n=n.child)):n=u(e,n),n;case 6:return null===e&&k(n),null===(e=n.pendingProps)&&(e=n.memoizedProps),n.memoizedProps=e,null;case 8:n.tag=7;case 7:return a=n.pendingProps,zt.current?null===a&&(null===(a=e&&e.memoizedProps)&&p("154")):null!==a&&n.memoizedProps!==a||(a=n.memoizedProps),o=a.children,n.stateNode=null===e?Po(n,n.stateNode,o,t):Ao(n,n.stateNode,o,t),n.memoizedProps=a,n.stateNode;case 9:return null;case 4:e:{if(b(n,n.stateNode.containerInfo),a=n.pendingProps,zt.current)null===a&&(null==(a=e&&e.memoizedProps)&&p("154"));else if(null===a||n.memoizedProps===a){n=u(e,n);break e}null===e?n.child=Ao(n,null,a,t):r(e,n,a),n.memoizedProps=a,n=n.child}return n;case 10:e:{if(t=n.pendingProps,zt.current)null===t&&(t=n.memoizedProps);else if(null===t||n.memoizedProps===t){n=u(e,n);break e}r(e,n,t),n.memoizedProps=t,n=n.child}return n;default:p("156")}},beginFailedWork:function(e,n,t){switch(n.tag){case 2:Qt(n);break;case 3:l(n);break;default:p("157")}return n.effectTag|=64,null===e?n.child=null:n.child!==e.child&&(n.child=e.child),0===n.expirationTime||n.expirationTime>t?h(0,n):(n.firstEffect=null,n.lastEffect=null,n.child=null===e?Po(n,null,null,t):Ao(n,e.child,null,t),2===n.tag&&(e=n.stateNode,n.memoizedProps=e.props,n.memoizedState=e.state),n.child)}}}var Ro={};function Do(e){function n(e){ie=Q=!0;var n=e.stateNode;if(n.current===e&&p("177"),n.isReadyForCommit=!1,Un.current=null,1<e.effectTag)if(null!==e.lastEffect){e.lastEffect.nextEffect=e;var t=e.firstEffect}else t=e;else t=e.firstEffect;for(H(),ee=t;null!==ee;){var o=!1,a=void 0;try{for(;null!==ee;){var r=ee.effectTag;if(16&r&&D(ee),128&r){var i=ee.alternate;null!==i&&_(i)}switch(-242&r){case 2:M(ee),ee.effectTag&=-3;break;case 6:M(ee),ee.effectTag&=-3,B(ee.alternate,ee);break;case 4:B(ee.alternate,ee);break;case 8:se=!0,F(ee),se=!1}ee=ee.nextEffect}}catch(e){o=!0,a=e}o&&(null===ee&&p("178"),s(ee,a),null!==ee&&(ee=ee.nextEffect))}for(q(),n.current=e,ee=t;null!==ee;){t=!1,o=void 0;try{for(;null!==ee;){var l=ee.effectTag;if(36&l&&W(ee.alternate,ee),128&l&&j(ee),64&l)switch(a=ee,r=void 0,null!==ne&&(r=ne.get(a),ne.delete(a),null==r&&null!==a.alternate&&(a=a.alternate,r=ne.get(a),ne.delete(a))),null==r&&p("184"),a.tag){case 2:a.stateNode.componentDidCatch(r.error,{componentStack:r.componentStack});break;case 3:null===ae&&(ae=r.error);break;default:p("157")}var c=ee.nextEffect;ee.nextEffect=null,ee=c}}catch(e){t=!0,o=e}t&&(null===ee&&p("178"),s(ee,o),null!==ee&&(ee=ee.nextEffect))}return Q=ie=!1,co(e.stateNode),oe&&(oe.forEach(g),oe=null),null!==ae&&(e=ae,ae=null,E(e)),0===(n=n.current.expirationTime)&&(te=ne=null),n}function t(e){for(;;){var n=R(e.alternate,e,Z),t=e.return,o=e.sibling,a=e;if(2147483647===Z||2147483647!==a.expirationTime){if(2!==a.tag&&3!==a.tag)var r=0;else r=null===(r=a.updateQueue)?0:r.expirationTime;for(var i=a.child;null!==i;)0!==i.expirationTime&&(0===r||r>i.expirationTime)&&(r=i.expirationTime),i=i.sibling;a.expirationTime=r}if(null!==n)return n;if(null!==t&&(null===t.firstEffect&&(t.firstEffect=e.firstEffect),null!==e.lastEffect&&(null!==t.lastEffect&&(t.lastEffect.nextEffect=e.firstEffect),t.lastEffect=e.lastEffect),1<e.effectTag&&(null!==t.lastEffect?t.lastEffect.nextEffect=e:t.firstEffect=e,t.lastEffect=e)),null!==o)return o;if(null===t){e.stateNode.isReadyForCommit=!0;break}e=t}return null}function o(e){var n=P(e.alternate,e,Z);return null===n&&(n=t(e)),Un.current=null,n}function a(e){var n=O(e.alternate,e,Z);return null===n&&(n=t(e)),Un.current=null,n}function r(e){if(null!==ne){if(!(0===Z||Z>e))if(Z<=Y)for(;null!==$;)$=l($)?a($):o($);else for(;null!==$&&!I();)$=l($)?a($):o($)}else if(!(0===Z||Z>e))if(Z<=Y)for(;null!==$;)$=o($);else for(;null!==$&&!I();)$=o($)}function i(e,n){if(Q&&p("243"),Q=!0,e.isReadyForCommit=!1,e!==J||n!==Z||null===$){for(;-1<Wt;)Bt[Wt]=null,Wt--;Gt=d,Ut.current=d,zt.current=!1,C(),Z=n,$=Zt((J=e).current,null,n)}var t=!1,o=null;try{r(n)}catch(e){t=!0,o=e}for(;t;){if(re){ae=o;break}var i=$;if(null===i)re=!0;else{var l=s(i,o);if(null===l&&p("183"),!re){try{for(o=n,l=t=l;null!==i;){switch(i.tag){case 2:Kt(i);break;case 5:N(i);break;case 3:L(i);break;case 4:L(i)}if(i===l||i.alternate===l)break;i=i.return}$=a(t),r(o)}catch(e){t=!0,o=e;continue}break}}}return n=ae,re=Q=!1,ae=null,null!==n&&E(n),e.isReadyForCommit?e.current.alternate:null}function s(e,n){var t=Un.current=null,o=!1,a=!1,r=null;if(3===e.tag)t=e,c(e)&&(re=!0);else for(var i=e.return;null!==i&&null===t;){if(2===i.tag?"function"==typeof i.stateNode.componentDidCatch&&(o=!0,r=zn(i),t=i,a=!0):3===i.tag&&(t=i),c(i)){if(se||null!==oe&&(oe.has(i)||null!==i.alternate&&oe.has(i.alternate)))return null;t=null,a=!1}i=i.return}if(null!==t){null===te&&(te=new Set),te.add(t);var s="";i=e;do{e:switch(i.tag){case 0:case 1:case 2:case 5:var l=i._debugOwner,u=i._debugSource,h=zn(i),d=null;l&&(d=zn(l)),l=u,h="\n in "+(h||"Unknown")+(l?" (at "+l.fileName.replace(/^.*[\\\/]/,"")+":"+l.lineNumber+")":d?" (created by "+d+")":"");break e;default:h=""}s+=h,i=i.return}while(i);i=s,e=zn(e),null===ne&&(ne=new Map),n={componentName:e,componentStack:i,error:n,errorBoundary:o?t.stateNode:null,errorBoundaryFound:o,errorBoundaryName:r,willRetry:a},ne.set(t,n);try{var p=n.error;p&&p.suppressReactErrorLogging||console.error(p)}catch(e){e&&e.suppressReactErrorLogging||console.error(e)}return ie?(null===oe&&(oe=new Set),oe.add(t)):g(t),t}return null===ae&&(ae=n),null}function l(e){return null!==ne&&(ne.has(e)||null!==e.alternate&&ne.has(e.alternate))}function c(e){return null!==te&&(te.has(e)||null!==e.alternate&&te.has(e.alternate))}function u(){return 20*(1+((y()+100)/20|0))}function h(e){return 0!==X?X:Q?ie?1:Z:!V||1&e.internalContextTag?u():1}function f(e,n){return m(e,n)}function m(e,n){for(;null!==e;){if((0===e.expirationTime||e.expirationTime>n)&&(e.expirationTime=n),null!==e.alternate&&(0===e.alternate.expirationTime||e.alternate.expirationTime>n)&&(e.alternate.expirationTime=n),null===e.return){if(3!==e.tag)break;var t=e.stateNode;!Q&&t===J&&n<Z&&($=J=null,Z=0);var o=t,a=n;if(Te>ke&&p("185"),null===o.nextScheduledRoot)o.remainingExpirationTime=a,null===ce?(le=ce=o,o.nextScheduledRoot=o):(ce=ce.nextScheduledRoot=o).nextScheduledRoot=le;else{var r=o.remainingExpirationTime;(0===r||a<r)&&(o.remainingExpirationTime=a)}de||(we?ve&&T(pe=o,fe=1):1===a?k(1,null):b(a)),!Q&&t===J&&n<Z&&($=J=null,Z=0)}e=e.return}}function g(e){m(e,1)}function y(){return Y=2+((U()-K)/10|0)}function b(e){if(0!==ue){if(e>ue)return;G(he)}var n=U()-K;ue=e,he=z(v,{timeout:10*(e-2)-n})}function w(){var e=0,n=null;if(null!==ce)for(var t=ce,o=le;null!==o;){var a=o.remainingExpirationTime;if(0===a){if((null===t||null===ce)&&p("244"),o===o.nextScheduledRoot){le=ce=o.nextScheduledRoot=null;break}if(o===le)le=a=o.nextScheduledRoot,ce.nextScheduledRoot=a,o.nextScheduledRoot=null;else{if(o===ce){(ce=t).nextScheduledRoot=le,o.nextScheduledRoot=null;break}t.nextScheduledRoot=o.nextScheduledRoot,o.nextScheduledRoot=null}o=t.nextScheduledRoot}else{if((0===e||a<e)&&(e=a,n=o),o===ce)break;t=o,o=o.nextScheduledRoot}}null!==(t=pe)&&t===n?Te++:Te=0,pe=n,fe=e}function v(e){k(0,e)}function k(e,n){for(be=n,w();null!==pe&&0!==fe&&(0===e||fe<=e)&&!me;)T(pe,fe),w();if(null!==be&&(ue=0,he=-1),0!==fe&&b(fe),be=null,me=!1,Te=0,ge)throw e=ye,ye=null,ge=!1,e}function T(e,t){if(de&&p("245"),de=!0,t<=y()){var o=e.finishedWork;null!==o?(e.finishedWork=null,e.remainingExpirationTime=n(o)):(e.finishedWork=null,null!==(o=i(e,t))&&(e.remainingExpirationTime=n(o)))}else null!==(o=e.finishedWork)?(e.finishedWork=null,e.remainingExpirationTime=n(o)):(e.finishedWork=null,null!==(o=i(e,t))&&(I()?e.finishedWork=o:e.remainingExpirationTime=n(o)));de=!1}function I(){return!(null===be||be.timeRemaining()>Ie)&&(me=!0)}function E(e){null===pe&&p("246"),pe.remainingExpirationTime=0,ge||(ge=!0,ye=e)}var x=function(e){function n(e){return e===Ro&&p("174"),e}var t=e.getChildHostContext,o=e.getRootHostContext,a={current:Ro},r={current:Ro},i={current:Ro};return{getHostContext:function(){return n(a.current)},getRootHostContainer:function(){return n(i.current)},popHostContainer:function(e){jt(a),jt(r),jt(i)},popHostContext:function(e){r.current===e&&(jt(a),jt(r))},pushHostContainer:function(e,n){_t(i,n),n=o(n),_t(r,e),_t(a,n)},pushHostContext:function(e){var o=n(i.current),s=n(a.current);s!==(o=t(s,e.type,o))&&(_t(r,e),_t(a,o))},resetHostContainer:function(){a.current=Ro,i.current=Ro}}}(e),S=function(e){function n(e,n){var t=new Jt(5,null,0);t.type="DELETED",t.stateNode=n,t.return=e,t.effectTag=8,null!==e.lastEffect?(e.lastEffect.nextEffect=t,e.lastEffect=t):e.firstEffect=e.lastEffect=t}function t(e,n){switch(e.tag){case 5:return null!==(n=r(n,e.type,e.pendingProps))&&(e.stateNode=n,!0);case 6:return null!==(n=i(n,e.pendingProps))&&(e.stateNode=n,!0);default:return!1}}function o(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag;)e=e.return;h=e}var a=e.shouldSetTextContent;if(!(e=e.hydration))return{enterHydrationState:function(){return!1},resetHydrationState:function(){},tryToClaimNextHydratableInstance:function(){},prepareToHydrateHostInstance:function(){p("175")},prepareToHydrateHostTextInstance:function(){p("176")},popHydrationState:function(){return!1}};var r=e.canHydrateInstance,i=e.canHydrateTextInstance,s=e.getNextHydratableSibling,l=e.getFirstHydratableChild,c=e.hydrateInstance,u=e.hydrateTextInstance,h=null,d=null,f=!1;return{enterHydrationState:function(e){return d=l(e.stateNode.containerInfo),h=e,f=!0},resetHydrationState:function(){d=h=null,f=!1},tryToClaimNextHydratableInstance:function(e){if(f){var o=d;if(o){if(!t(e,o)){if(!(o=s(o))||!t(e,o))return e.effectTag|=2,f=!1,void(h=e);n(h,d)}h=e,d=l(o)}else e.effectTag|=2,f=!1,h=e}},prepareToHydrateHostInstance:function(e,n,t){return n=c(e.stateNode,e.type,e.memoizedProps,n,t,e),e.updateQueue=n,null!==n},prepareToHydrateHostTextInstance:function(e){return u(e.stateNode,e.memoizedProps,e)},popHydrationState:function(e){if(e!==h)return!1;if(!f)return o(e),f=!0,!1;var t=e.type;if(5!==e.tag||"head"!==t&&"body"!==t&&!a(t,e.memoizedProps))for(t=d;t;)n(e,t),t=s(t);return o(e),d=h?s(e.stateNode):null,!0}}}(e),L=x.popHostContainer,N=x.popHostContext,C=x.resetHostContainer,A=Oo(e,x,S,f,h),P=A.beginWork,O=A.beginFailedWork,R=function(e,n,t){function o(e){e.effectTag|=4}var a=e.createInstance,r=e.createTextInstance,i=e.appendInitialChild,s=e.finalizeInitialChildren,l=e.prepareUpdate,c=e.persistence,u=n.getRootHostContainer,h=n.popHostContext,d=n.getHostContext,f=n.popHostContainer,m=t.prepareToHydrateHostInstance,g=t.prepareToHydrateHostTextInstance,y=t.popHydrationState,b=void 0,w=void 0,v=void 0;return e.mutation?(b=function(){},w=function(e,n,t){(n.updateQueue=t)&&o(n)},v=function(e,n,t,a){t!==a&&o(n)}):p(c?"235":"236"),{completeWork:function(e,n,t){var c=n.pendingProps;switch(null===c?c=n.memoizedProps:2147483647===n.expirationTime&&2147483647!==t||(n.pendingProps=null),n.tag){case 1:return null;case 2:return Kt(n),null;case 3:return f(n),jt(zt),jt(Ut),(c=n.stateNode).pendingContext&&(c.context=c.pendingContext,c.pendingContext=null),null!==e&&null!==e.child||(y(n),n.effectTag&=-3),b(n),null;case 5:h(n),t=u();var k=n.type;if(null!==e&&null!=n.stateNode){var T=e.memoizedProps,I=n.stateNode,E=d();I=l(I,k,T,c,t,E),w(e,n,I,k,T,c,t),e.ref!==n.ref&&(n.effectTag|=128)}else{if(!c)return null===n.stateNode&&p("166"),null;if(e=d(),y(n))m(n,t,e)&&o(n);else{e=a(k,c,t,e,n);e:for(T=n.child;null!==T;){if(5===T.tag||6===T.tag)i(e,T.stateNode);else if(4!==T.tag&&null!==T.child){T.child.return=T,T=T.child;continue}if(T===n)break;for(;null===T.sibling;){if(null===T.return||T.return===n)break e;T=T.return}T.sibling.return=T.return,T=T.sibling}s(e,k,c,t)&&o(n),n.stateNode=e}null!==n.ref&&(n.effectTag|=128)}return null;case 6:if(e&&null!=n.stateNode)v(e,n,e.memoizedProps,c);else{if("string"!=typeof c)return null===n.stateNode&&p("166"),null;e=u(),t=d(),y(n)?g(n)&&o(n):n.stateNode=r(c,e,t,n)}return null;case 7:(c=n.memoizedProps)||p("165"),n.tag=8,k=[];e:for((T=n.stateNode)&&(T.return=n);null!==T;){if(5===T.tag||6===T.tag||4===T.tag)p("247");else if(9===T.tag)k.push(T.type);else if(null!==T.child){T.child.return=T,T=T.child;continue}for(;null===T.sibling;){if(null===T.return||T.return===n)break e;T=T.return}T.sibling.return=T.return,T=T.sibling}return c=(T=c.handler)(c.props,k),n.child=Ao(n,null!==e?e.child:null,c,t),n.child;case 8:return n.tag=7,null;case 9:case 10:return null;case 4:return f(n),b(n),null;case 0:p("167");default:p("156")}}}}(e,x,S).completeWork,D=(x=function(e,n){function t(e){var t=e.ref;if(null!==t)try{t(null)}catch(t){n(e,t)}}function o(e){switch(uo(e),e.tag){case 2:t(e);var o=e.stateNode;if("function"==typeof o.componentWillUnmount)try{o.props=e.memoizedProps,o.state=e.memoizedState,o.componentWillUnmount()}catch(t){n(e,t)}break;case 5:t(e);break;case 7:a(e.stateNode);break;case 4:l&&i(e)}}function a(e){for(var n=e;;)if(o(n),null===n.child||l&&4===n.tag){if(n===e)break;for(;null===n.sibling;){if(null===n.return||n.return===e)return;n=n.return}n.sibling.return=n.return,n=n.sibling}else n.child.return=n,n=n.child}function r(e){return 5===e.tag||3===e.tag||4===e.tag}function i(e){for(var n=e,t=!1,r=void 0,i=void 0;;){if(!t){t=n.return;e:for(;;){switch(null===t&&p("160"),t.tag){case 5:r=t.stateNode,i=!1;break e;case 3:case 4:r=t.stateNode.containerInfo,i=!0;break e}t=t.return}t=!0}if(5===n.tag||6===n.tag)a(n),i?w(r,n.stateNode):b(r,n.stateNode);else if(4===n.tag?r=n.stateNode.containerInfo:o(n),null!==n.child){n.child.return=n,n=n.child;continue}if(n===e)break;for(;null===n.sibling;){if(null===n.return||n.return===e)return;4===(n=n.return).tag&&(t=!1)}n.sibling.return=n.return,n=n.sibling}}var s=e.getPublicInstance,l=e.mutation;e=e.persistence,l||p(e?"235":"236");var c=l.commitMount,u=l.commitUpdate,h=l.resetTextContent,d=l.commitTextUpdate,f=l.appendChild,m=l.appendChildToContainer,g=l.insertBefore,y=l.insertInContainerBefore,b=l.removeChild,w=l.removeChildFromContainer;return{commitResetTextContent:function(e){h(e.stateNode)},commitPlacement:function(e){e:{for(var n=e.return;null!==n;){if(r(n)){var t=n;break e}n=n.return}p("160"),t=void 0}var o=n=void 0;switch(t.tag){case 5:n=t.stateNode,o=!1;break;case 3:case 4:n=t.stateNode.containerInfo,o=!0;break;default:p("161")}16&t.effectTag&&(h(n),t.effectTag&=-17);e:n:for(t=e;;){for(;null===t.sibling;){if(null===t.return||r(t.return)){t=null;break e}t=t.return}for(t.sibling.return=t.return,t=t.sibling;5!==t.tag&&6!==t.tag;){if(2&t.effectTag)continue n;if(null===t.child||4===t.tag)continue n;t.child.return=t,t=t.child}if(!(2&t.effectTag)){t=t.stateNode;break e}}for(var a=e;;){if(5===a.tag||6===a.tag)t?o?y(n,a.stateNode,t):g(n,a.stateNode,t):o?m(n,a.stateNode):f(n,a.stateNode);else if(4!==a.tag&&null!==a.child){a.child.return=a,a=a.child;continue}if(a===e)break;for(;null===a.sibling;){if(null===a.return||a.return===e)return;a=a.return}a.sibling.return=a.return,a=a.sibling}},commitDeletion:function(e){i(e),e.return=null,e.child=null,e.alternate&&(e.alternate.child=null,e.alternate.return=null)},commitWork:function(e,n){switch(n.tag){case 2:break;case 5:var t=n.stateNode;if(null!=t){var o=n.memoizedProps;e=null!==e?e.memoizedProps:o;var a=n.type,r=n.updateQueue;n.updateQueue=null,null!==r&&u(t,r,a,e,o,n)}break;case 6:null===n.stateNode&&p("162"),t=n.memoizedProps,d(n.stateNode,null!==e?e.memoizedProps:t,t);break;case 3:break;default:p("163")}},commitLifeCycles:function(e,n){switch(n.tag){case 2:var t=n.stateNode;if(4&n.effectTag)if(null===e)t.props=n.memoizedProps,t.state=n.memoizedState,t.componentDidMount();else{var o=e.memoizedProps;e=e.memoizedState,t.props=n.memoizedProps,t.state=n.memoizedState,t.componentDidUpdate(o,e)}null!==(n=n.updateQueue)&&yo(n,t);break;case 3:null!==(t=n.updateQueue)&&yo(t,null!==n.child?n.child.stateNode:null);break;case 5:t=n.stateNode,null===e&&4&n.effectTag&&c(t,n.type,n.memoizedProps,n);break;case 6:case 4:break;default:p("163")}},commitAttachRef:function(e){var n=e.ref;if(null!==n){var t=e.stateNode;switch(e.tag){case 5:n(s(t));break;default:n(t)}}},commitDetachRef:function(e){null!==(e=e.ref)&&e(null)}}}(e,s)).commitResetTextContent,M=x.commitPlacement,F=x.commitDeletion,B=x.commitWork,W=x.commitLifeCycles,j=x.commitAttachRef,_=x.commitDetachRef,U=e.now,z=e.scheduleDeferredCallback,G=e.cancelDeferredCallback,V=e.useSyncScheduling,H=e.prepareForCommit,q=e.resetAfterCommit,K=U(),Y=2,X=0,Q=!1,$=null,J=null,Z=0,ee=null,ne=null,te=null,oe=null,ae=null,re=!1,ie=!1,se=!1,le=null,ce=null,ue=0,he=-1,de=!1,pe=null,fe=0,me=!1,ge=!1,ye=null,be=null,we=!1,ve=!1,ke=1e3,Te=0,Ie=1;return{computeAsyncExpiration:u,computeExpirationForFiber:h,scheduleWork:f,batchedUpdates:function(e,n){var t=we;we=!0;try{return e(n)}finally{(we=t)||de||k(1,null)}},unbatchedUpdates:function(e){if(we&&!ve){ve=!0;try{return e()}finally{ve=!1}}return e()},flushSync:function(e){var n=we;we=!0;try{e:{var t=X;X=1;try{var o=e();break e}finally{X=t}o=void 0}return o}finally{we=n,de&&p("187"),k(1,null)}},deferredUpdates:function(e){var n=X;X=u();try{return e()}finally{X=n}}}}function Mo(e){function n(e){return null===(e=function(e){if(!(e=qn(e)))return null;for(var n=e;;){if(5===n.tag||6===n.tag)return n;if(n.child)n.child.return=n,n=n.child;else{if(n===e)break;for(;!n.sibling;){if(!n.return||n.return===e)return null;n=n.return}n.sibling.return=n.return,n=n.sibling}}return null}(e))?null:e.stateNode}var t=e.getPublicInstance,o=(e=Do(e)).computeAsyncExpiration,a=e.computeExpirationForFiber,i=e.scheduleWork;return{createContainer:function(e,n){var t=new Jt(3,null,0);return e={current:t,containerInfo:e,pendingChildren:null,remainingExpirationTime:0,isReadyForCommit:!1,finishedWork:null,context:null,pendingContext:null,hydrate:n,nextScheduledRoot:null},t.stateNode=e},updateContainer:function(e,n,t,r){var s=n.current;if(t){var l;t=t._reactInternalFiber;e:{for(2===Gn(t)&&2===t.tag||p("170"),l=t;3!==l.tag;){if(qt(l)){l=l.stateNode.__reactInternalMemoizedMergedChildContext;break e}(l=l.return)||p("171")}l=l.stateNode.context}t=qt(t)?Xt(t,l):l}else t=d;null===n.context?n.context=t:n.pendingContext=t,n=void 0===(n=r)?null:n,fo(s,{expirationTime:r=null!=e&&null!=e.type&&null!=e.type.prototype&&!0===e.type.prototype.unstable_isAsyncReactComponent?o():a(s),partialState:{element:e},callback:n,isReplace:!1,isForced:!1,nextCallback:null,next:null}),i(s,r)},batchedUpdates:e.batchedUpdates,unbatchedUpdates:e.unbatchedUpdates,deferredUpdates:e.deferredUpdates,flushSync:e.flushSync,getPublicRootInstance:function(e){if(!(e=e.current).child)return null;switch(e.child.tag){case 5:return t(e.child.stateNode);default:return e.child.stateNode}},findHostInstance:n,findHostInstanceWithNoPortals:function(e){return null===(e=function(e){if(!(e=qn(e)))return null;for(var n=e;;){if(5===n.tag||6===n.tag)return n;if(n.child&&4!==n.tag)n.child.return=n,n=n.child;else{if(n===e)break;for(;!n.sibling;){if(!n.return||n.return===e)return null;n=n.return}n.sibling.return=n.return,n=n.sibling}}return null}(e))?null:e.stateNode},injectIntoDevTools:function(e){var t=e.findFiberByHostInstance;return function(e){if("undefined"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var n=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(n.isDisabled||!n.supportsFiber)return!0;try{var t=n.inject(e);io=lo(function(e){return n.onCommitFiberRoot(t,e)}),so=lo(function(e){return n.onCommitFiberUnmount(t,e)})}catch(e){}return!0}(r({},e,{findHostInstanceByFiber:function(e){return n(e)},findFiberByHostInstance:function(e){return t?t(e):null}}))}}}var Fo=Object.freeze({default:Mo}),Bo=Fo&&Mo||Fo,Wo=Bo.default?Bo.default:Bo;var jo="object"==typeof performance&&"function"==typeof performance.now,_o=void 0;_o=jo?function(){return performance.now()}:function(){return Date.now()};var Uo=void 0,zo=void 0;if(a.canUseDOM)if("function"!=typeof requestIdleCallback||"function"!=typeof cancelIdleCallback){var Go,Vo=null,Ho=!1,qo=-1,Ko=!1,Yo=0,Xo=33,Qo=33;Go=jo?{didTimeout:!1,timeRemaining:function(){var e=Yo-performance.now();return 0<e?e:0}}:{didTimeout:!1,timeRemaining:function(){var e=Yo-Date.now();return 0<e?e:0}};var $o="__reactIdleCallback$"+Math.random().toString(36).slice(2);window.addEventListener("message",function(e){if(e.source===window&&e.data===$o){if(Ho=!1,e=_o(),0>=Yo-e){if(!(-1!==qo&&qo<=e))return void(Ko||(Ko=!0,requestAnimationFrame(Jo)));Go.didTimeout=!0}else Go.didTimeout=!1;qo=-1,e=Vo,Vo=null,null!==e&&e(Go)}},!1);var Jo=function(e){Ko=!1;var n=e-Yo+Qo;n<Qo&&Xo<Qo?(8>n&&(n=8),Qo=n<Xo?Xo:n):Xo=n,Yo=e+Qo,Ho||(Ho=!0,window.postMessage($o,"*"))};Uo=function(e,n){return Vo=e,null!=n&&"number"==typeof n.timeout&&(qo=_o()+n.timeout),Ko||(Ko=!0,requestAnimationFrame(Jo)),0},zo=function(){Vo=null,Ho=!1,qo=-1}}else Uo=window.requestIdleCallback,zo=window.cancelIdleCallback;else Uo=function(e){return setTimeout(function(){e({timeRemaining:function(){return 1/0}})})},zo=function(e){clearTimeout(e)};var Zo=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ea={},na={};function ta(e,n,t){var o=w(n);if(o&&b(n,t)){var a=o.mutationMethod;a?a(e,t):null==t||o.hasBooleanValue&&!t||o.hasNumericValue&&isNaN(t)||o.hasPositiveNumericValue&&1>t||o.hasOverloadedBooleanValue&&!1===t?aa(e,n):o.mustUseProperty?e[o.propertyName]=t:(n=o.attributeName,(a=o.attributeNamespace)?e.setAttributeNS(a,n,""+t):o.hasBooleanValue||o.hasOverloadedBooleanValue&&!0===t?e.setAttribute(n,""):e.setAttribute(n,""+t))}else oa(e,n,b(n,t)?t:null)}function oa(e,n,t){var o;o=n,(na.hasOwnProperty(o)||!ea.hasOwnProperty(o)&&(Zo.test(o)?na[o]=!0:(ea[o]=!0,0)))&&(null==t?e.removeAttribute(n):e.setAttribute(n,""+t))}function aa(e,n){var t=w(n);t?(n=t.mutationMethod)?n(e,void 0):t.mustUseProperty?e[t.propertyName]=!t.hasBooleanValue&&"":e.removeAttribute(t.attributeName):e.removeAttribute(n)}function ra(e,n){var t=n.value,o=n.checked;return r({type:void 0,step:void 0,min:void 0,max:void 0},n,{defaultChecked:void 0,defaultValue:void 0,value:null!=t?t:e._wrapperState.initialValue,checked:null!=o?o:e._wrapperState.initialChecked})}function ia(e,n){var t=n.defaultValue;e._wrapperState={initialChecked:null!=n.checked?n.checked:n.defaultChecked,initialValue:null!=n.value?n.value:t,controlled:"checkbox"===n.type||"radio"===n.type?null!=n.checked:null!=n.value}}function sa(e,n){null!=(n=n.checked)&&ta(e,"checked",n)}function la(e,n){sa(e,n);var t=n.value;null!=t?0===t&&""===e.value?e.value="0":"number"===n.type?(t!=(n=parseFloat(e.value)||0)||t==n&&e.value!=t)&&(e.value=""+t):e.value!==""+t&&(e.value=""+t):(null==n.value&&null!=n.defaultValue&&e.defaultValue!==""+n.defaultValue&&(e.defaultValue=""+n.defaultValue),null==n.checked&&null!=n.defaultChecked&&(e.defaultChecked=!!n.defaultChecked))}function ca(e,n){switch(n.type){case"submit":case"reset":break;case"color":case"date":case"datetime":case"datetime-local":case"month":case"time":case"week":e.value="",e.value=e.defaultValue;break;default:e.value=e.value}""!==(n=e.name)&&(e.name=""),e.defaultChecked=!e.defaultChecked,e.defaultChecked=!e.defaultChecked,""!==n&&(e.name=n)}function ua(e,n){var t,a;return e=r({children:void 0},n),t=n.children,a="",o.Children.forEach(t,function(e){null==e||"string"!=typeof e&&"number"!=typeof e||(a+=e)}),(n=a)&&(e.children=n),e}function ha(e,n,t,o){if(e=e.options,n){n={};for(var a=0;a<t.length;a++)n["$"+t[a]]=!0;for(t=0;t<e.length;t++)a=n.hasOwnProperty("$"+e[t].value),e[t].selected!==a&&(e[t].selected=a),a&&o&&(e[t].defaultSelected=!0)}else{for(t=""+t,n=null,a=0;a<e.length;a++){if(e[a].value===t)return e[a].selected=!0,void(o&&(e[a].defaultSelected=!0));null!==n||e[a].disabled||(n=e[a])}null!==n&&(n.selected=!0)}}function da(e,n){var t=n.value;e._wrapperState={initialValue:null!=t?t:n.defaultValue,wasMultiple:!!n.multiple}}function pa(e,n){return null!=n.dangerouslySetInnerHTML&&p("91"),r({},n,{value:void 0,defaultValue:void 0,children:""+e._wrapperState.initialValue})}function fa(e,n){var t=n.value;null==t&&(t=n.defaultValue,null!=(n=n.children)&&(null!=t&&p("92"),Array.isArray(n)&&(1>=n.length||p("93"),n=n[0]),t=""+n),null==t&&(t="")),e._wrapperState={initialValue:""+t}}function ma(e,n){var t=n.value;null!=t&&((t=""+t)!==e.value&&(e.value=t),null==n.defaultValue&&(e.defaultValue=t)),null!=n.defaultValue&&(e.defaultValue=n.defaultValue)}function ga(e){var n=e.textContent;n===e._wrapperState.initialValue&&(e.value=n)}var ya="http://www.w3.org/1999/xhtml",ba="http://www.w3.org/2000/svg";function wa(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function va(e,n){return null==e||"http://www.w3.org/1999/xhtml"===e?wa(n):"http://www.w3.org/2000/svg"===e&&"foreignObject"===n?"http://www.w3.org/1999/xhtml":e}var ka,Ta=void 0,Ia=(ka=function(e,n){if(e.namespaceURI!==ba||"innerHTML"in e)e.innerHTML=n;else{for((Ta=Ta||document.createElement("div")).innerHTML="<svg>"+n+"</svg>",n=Ta.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;n.firstChild;)e.appendChild(n.firstChild)}},"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,n,t,o){MSApp.execUnsafeLocalFunction(function(){return ka(e,n)})}:ka);function Ea(e,n){if(n){var t=e.firstChild;if(t&&t===e.lastChild&&3===t.nodeType)return void(t.nodeValue=n)}e.textContent=n}var xa={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Sa=["Webkit","ms","Moz","O"];function La(e,n){for(var t in e=e.style,n)if(n.hasOwnProperty(t)){var o=0===t.indexOf("--"),a=t,r=n[t];a=null==r||"boolean"==typeof r||""===r?"":o||"number"!=typeof r||0===r||xa.hasOwnProperty(a)&&xa[a]?(""+r).trim():r+"px","float"===t&&(t="cssFloat"),o?e.setProperty(t,a):e[t]=a}}Object.keys(xa).forEach(function(e){Sa.forEach(function(n){n=n+e.charAt(0).toUpperCase()+e.substring(1),xa[n]=xa[e]})});var Na=r({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Ca(e,n,t){n&&(Na[e]&&(null!=n.children||null!=n.dangerouslySetInnerHTML)&&p("137",e,t()),null!=n.dangerouslySetInnerHTML&&(null!=n.children&&p("60"),"object"==typeof n.dangerouslySetInnerHTML&&"__html"in n.dangerouslySetInnerHTML||p("61")),null!=n.style&&"object"!=typeof n.style&&p("62",t()))}function Aa(e,n){if(-1===e.indexOf("-"))return"string"==typeof n.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Pa=ya,Oa=i.thatReturns("");function Ra(e,n){var t=ht(e=9===e.nodeType||11===e.nodeType?e:e.ownerDocument);n=G[n];for(var o=0;o<n.length;o++){var a=n[o];t.hasOwnProperty(a)&&t[a]||("topScroll"===a?Zn("topScroll","scroll",e):"topFocus"===a||"topBlur"===a?(Zn("topFocus","focus",e),Zn("topBlur","blur",e),t.topBlur=!0,t.topFocus=!0):"topCancel"===a?(mn("cancel",!0)&&Zn("topCancel","cancel",e),t.topCancel=!0):"topClose"===a?(mn("close",!0)&&Zn("topClose","close",e),t.topClose=!0):st.hasOwnProperty(a)&&Jn(a,st[a],e),t[a]=!0)}}var Da={topAbort:"abort",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topSeeked:"seeked",topSeeking:"seeking",topStalled:"stalled",topSuspend:"suspend",topTimeUpdate:"timeupdate",topVolumeChange:"volumechange",topWaiting:"waiting"};function Ma(e,n,t,o){return t=9===t.nodeType?t:t.ownerDocument,o===Pa&&(o=wa(e)),o===Pa?"script"===e?((e=t.createElement("div")).innerHTML="<script><\/script>",e=e.removeChild(e.firstChild)):e="string"==typeof n.is?t.createElement(e,{is:n.is}):t.createElement(e):e=t.createElementNS(o,e),e}function Fa(e,n){return(9===n.nodeType?n:n.ownerDocument).createTextNode(e)}function Ba(e,n,t,o){var a=Aa(n,t);switch(n){case"iframe":case"object":Jn("topLoad","load",e);var s=t;break;case"video":case"audio":for(s in Da)Da.hasOwnProperty(s)&&Jn(s,Da[s],e);s=t;break;case"source":Jn("topError","error",e),s=t;break;case"img":case"image":Jn("topError","error",e),Jn("topLoad","load",e),s=t;break;case"form":Jn("topReset","reset",e),Jn("topSubmit","submit",e),s=t;break;case"details":Jn("topToggle","toggle",e),s=t;break;case"input":ia(e,t),s=ra(e,t),Jn("topInvalid","invalid",e),Ra(o,"onChange");break;case"option":s=ua(e,t);break;case"select":da(e,t),s=r({},t,{value:void 0}),Jn("topInvalid","invalid",e),Ra(o,"onChange");break;case"textarea":fa(e,t),s=pa(e,t),Jn("topInvalid","invalid",e),Ra(o,"onChange");break;default:s=t}Ca(n,s,Oa);var l,c=s;for(l in c)if(c.hasOwnProperty(l)){var u=c[l];"style"===l?La(e,u):"dangerouslySetInnerHTML"===l?null!=(u=u?u.__html:void 0)&&Ia(e,u):"children"===l?"string"==typeof u?("textarea"!==n||""!==u)&&Ea(e,u):"number"==typeof u&&Ea(e,""+u):"suppressContentEditableWarning"!==l&&"suppressHydrationWarning"!==l&&"autoFocus"!==l&&(z.hasOwnProperty(l)?null!=u&&Ra(o,l):a?oa(e,l,u):null!=u&&ta(e,l,u))}switch(n){case"input":yn(e),ca(e,t);break;case"textarea":yn(e),ga(e);break;case"option":null!=t.value&&e.setAttribute("value",t.value);break;case"select":e.multiple=!!t.multiple,null!=(n=t.value)?ha(e,!!t.multiple,n,!1):null!=t.defaultValue&&ha(e,!!t.multiple,t.defaultValue,!0);break;default:"function"==typeof s.onClick&&(e.onclick=i)}}function Wa(e,n,t,o,a){var s,l,c=null;switch(n){case"input":t=ra(e,t),o=ra(e,o),c=[];break;case"option":t=ua(e,t),o=ua(e,o),c=[];break;case"select":t=r({},t,{value:void 0}),o=r({},o,{value:void 0}),c=[];break;case"textarea":t=pa(e,t),o=pa(e,o),c=[];break;default:"function"!=typeof t.onClick&&"function"==typeof o.onClick&&(e.onclick=i)}for(s in Ca(n,o,Oa),e=null,t)if(!o.hasOwnProperty(s)&&t.hasOwnProperty(s)&&null!=t[s])if("style"===s)for(l in n=t[s])n.hasOwnProperty(l)&&(e||(e={}),e[l]="");else"dangerouslySetInnerHTML"!==s&&"children"!==s&&"suppressContentEditableWarning"!==s&&"suppressHydrationWarning"!==s&&"autoFocus"!==s&&(z.hasOwnProperty(s)?c||(c=[]):(c=c||[]).push(s,null));for(s in o){var u=o[s];if(n=null!=t?t[s]:void 0,o.hasOwnProperty(s)&&u!==n&&(null!=u||null!=n))if("style"===s)if(n){for(l in n)!n.hasOwnProperty(l)||u&&u.hasOwnProperty(l)||(e||(e={}),e[l]="");for(l in u)u.hasOwnProperty(l)&&n[l]!==u[l]&&(e||(e={}),e[l]=u[l])}else e||(c||(c=[]),c.push(s,e)),e=u;else"dangerouslySetInnerHTML"===s?(u=u?u.__html:void 0,n=n?n.__html:void 0,null!=u&&n!==u&&(c=c||[]).push(s,""+u)):"children"===s?n===u||"string"!=typeof u&&"number"!=typeof u||(c=c||[]).push(s,""+u):"suppressContentEditableWarning"!==s&&"suppressHydrationWarning"!==s&&(z.hasOwnProperty(s)?(null!=u&&Ra(a,s),c||n===u||(c=[])):(c=c||[]).push(s,u))}return e&&(c=c||[]).push("style",e),c}function ja(e,n,t,o,a){"input"===t&&"radio"===a.type&&null!=a.name&&sa(e,a),Aa(t,o),o=Aa(t,a);for(var r=0;r<n.length;r+=2){var i=n[r],s=n[r+1];"style"===i?La(e,s):"dangerouslySetInnerHTML"===i?Ia(e,s):"children"===i?Ea(e,s):o?null!=s?oa(e,i,s):e.removeAttribute(i):null!=s?ta(e,i,s):aa(e,i)}switch(t){case"input":la(e,a);break;case"textarea":ma(e,a);break;case"select":e._wrapperState.initialValue=void 0,n=e._wrapperState.wasMultiple,e._wrapperState.wasMultiple=!!a.multiple,null!=(t=a.value)?ha(e,!!a.multiple,t,!1):n!==!!a.multiple&&(null!=a.defaultValue?ha(e,!!a.multiple,a.defaultValue,!0):ha(e,!!a.multiple,a.multiple?[]:"",!1))}}function _a(e,n,t,o,a){switch(n){case"iframe":case"object":Jn("topLoad","load",e);break;case"video":case"audio":for(var r in Da)Da.hasOwnProperty(r)&&Jn(r,Da[r],e);break;case"source":Jn("topError","error",e);break;case"img":case"image":Jn("topError","error",e),Jn("topLoad","load",e);break;case"form":Jn("topReset","reset",e),Jn("topSubmit","submit",e);break;case"details":Jn("topToggle","toggle",e);break;case"input":ia(e,t),Jn("topInvalid","invalid",e),Ra(a,"onChange");break;case"select":da(e,t),Jn("topInvalid","invalid",e),Ra(a,"onChange");break;case"textarea":fa(e,t),Jn("topInvalid","invalid",e),Ra(a,"onChange")}for(var s in Ca(n,t,Oa),o=null,t)t.hasOwnProperty(s)&&(r=t[s],"children"===s?"string"==typeof r?e.textContent!==r&&(o=["children",r]):"number"==typeof r&&e.textContent!==""+r&&(o=["children",""+r]):z.hasOwnProperty(s)&&null!=r&&Ra(a,s));switch(n){case"input":yn(e),ca(e,t);break;case"textarea":yn(e),ga(e);break;case"select":case"option":break;default:"function"==typeof t.onClick&&(e.onclick=i)}return o}function Ua(e,n){return e.nodeValue!==n}var za=Object.freeze({createElement:Ma,createTextNode:Fa,setInitialProperties:Ba,diffProperties:Wa,updateProperties:ja,diffHydratedProperties:_a,diffHydratedText:Ua,warnForUnmatchedText:function(){},warnForDeletedHydratableElement:function(){},warnForDeletedHydratableText:function(){},warnForInsertedHydratedElement:function(){},warnForInsertedHydratedText:function(){},restoreControlledState:function(e,n,t){switch(n){case"input":if(la(e,t),n=t.name,"radio"===t.type&&null!=n){for(t=e;t.parentNode;)t=t.parentNode;for(t=t.querySelectorAll("input[name="+JSON.stringify(""+n)+'][type="radio"]'),n=0;n<t.length;n++){var o=t[n];if(o!==e&&o.form===e.form){var a=fe(o);a||p("90"),bn(o),la(o,a)}}}break;case"textarea":ma(e,t);break;case"select":null!=(n=t.value)&&ha(e,!!t.multiple,n,!1)}}});on.injectFiberControlledHostComponent(za);var Ga=null,Va=null;function Ha(e){return!(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType&&(8!==e.nodeType||" react-mount-point-unstable "!==e.nodeValue))}var qa=Wo({getRootHostContext:function(e){var n=e.nodeType;switch(n){case 9:case 11:e=(e=e.documentElement)?e.namespaceURI:va(null,"");break;default:e=va(e=(n=8===n?e.parentNode:e).namespaceURI||null,n=n.tagName)}return e},getChildHostContext:function(e,n){return va(e,n)},getPublicInstance:function(e){return e},prepareForCommit:function(){Ga=Xn;var e=l();if(ft(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{var t=window.getSelection&&window.getSelection();if(t&&0!==t.rangeCount){n=t.anchorNode;var o=t.anchorOffset,a=t.focusNode;t=t.focusOffset;try{n.nodeType,a.nodeType}catch(e){n=null;break e}var r=0,i=-1,s=-1,c=0,u=0,h=e,d=null;n:for(;;){for(var p;h!==n||0!==o&&3!==h.nodeType||(i=r+o),h!==a||0!==t&&3!==h.nodeType||(s=r+t),3===h.nodeType&&(r+=h.nodeValue.length),null!==(p=h.firstChild);)d=h,h=p;for(;;){if(h===e)break n;if(d===n&&++c===o&&(i=r),d===a&&++u===t&&(s=r),null!==(p=h.nextSibling))break;d=(h=d).parentNode}h=p}n=-1===i||-1===s?null:{start:i,end:s}}else n=null}n=n||{start:0,end:0}}else n=null;Va={focusedElem:e,selectionRange:n},$n(!1)},resetAfterCommit:function(){var e=Va,n=l(),t=e.focusedElem,o=e.selectionRange;if(n!==t&&u(document.documentElement,t)){if(ft(t))if(n=o.start,void 0===(e=o.end)&&(e=n),"selectionStart"in t)t.selectionStart=n,t.selectionEnd=Math.min(e,t.value.length);else if(window.getSelection){n=window.getSelection();var a=t[Le()].length;e=Math.min(o.start,a),o=void 0===o.end?e:Math.min(o.end,a),!n.extend&&e>o&&(a=o,o=e,e=a),a=pt(t,e);var r=pt(t,o);if(a&&r&&(1!==n.rangeCount||n.anchorNode!==a.node||n.anchorOffset!==a.offset||n.focusNode!==r.node||n.focusOffset!==r.offset)){var i=document.createRange();i.setStart(a.node,a.offset),n.removeAllRanges(),e>o?(n.addRange(i),n.extend(r.node,r.offset)):(i.setEnd(r.node,r.offset),n.addRange(i))}}for(n=[],e=t;e=e.parentNode;)1===e.nodeType&&n.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(h(t),t=0;t<n.length;t++)(e=n[t]).element.scrollLeft=e.left,e.element.scrollTop=e.top}Va=null,$n(Ga),Ga=null},createInstance:function(e,n,t,o,a){return(e=Ma(e,n,t,o))[ue]=a,e[he]=n,e},appendInitialChild:function(e,n){e.appendChild(n)},finalizeInitialChildren:function(e,n,t,o){Ba(e,n,t,o);e:{switch(n){case"button":case"input":case"select":case"textarea":e=!!t.autoFocus;break e}e=!1}return e},prepareUpdate:function(e,n,t,o,a){return Wa(e,n,t,o,a)},shouldSetTextContent:function(e,n){return"textarea"===e||"string"==typeof n.children||"number"==typeof n.children||"object"==typeof n.dangerouslySetInnerHTML&&null!==n.dangerouslySetInnerHTML&&"string"==typeof n.dangerouslySetInnerHTML.__html},shouldDeprioritizeSubtree:function(e,n){return!!n.hidden},createTextInstance:function(e,n,t,o){return(e=Fa(e,n))[ue]=o,e},now:_o,mutation:{commitMount:function(e){e.focus()},commitUpdate:function(e,n,t,o,a){e[he]=a,ja(e,n,t,o,a)},resetTextContent:function(e){e.textContent=""},commitTextUpdate:function(e,n,t){e.nodeValue=t},appendChild:function(e,n){e.appendChild(n)},appendChildToContainer:function(e,n){8===e.nodeType?e.parentNode.insertBefore(n,e):e.appendChild(n)},insertBefore:function(e,n,t){e.insertBefore(n,t)},insertInContainerBefore:function(e,n,t){8===e.nodeType?e.parentNode.insertBefore(n,t):e.insertBefore(n,t)},removeChild:function(e,n){e.removeChild(n)},removeChildFromContainer:function(e,n){8===e.nodeType?e.parentNode.removeChild(n):e.removeChild(n)}},hydration:{canHydrateInstance:function(e,n){return 1!==e.nodeType||n.toLowerCase()!==e.nodeName.toLowerCase()?null:e},canHydrateTextInstance:function(e,n){return""===n||3!==e.nodeType?null:e},getNextHydratableSibling:function(e){for(e=e.nextSibling;e&&1!==e.nodeType&&3!==e.nodeType;)e=e.nextSibling;return e},getFirstHydratableChild:function(e){for(e=e.firstChild;e&&1!==e.nodeType&&3!==e.nodeType;)e=e.nextSibling;return e},hydrateInstance:function(e,n,t,o,a,r){return e[ue]=r,e[he]=t,_a(e,n,t,a,o)},hydrateTextInstance:function(e,n,t){return e[ue]=t,Ua(e,n)},didNotMatchHydratedContainerTextInstance:function(){},didNotMatchHydratedTextInstance:function(){},didNotHydrateContainerInstance:function(){},didNotHydrateInstance:function(){},didNotFindHydratableContainerInstance:function(){},didNotFindHydratableContainerTextInstance:function(){},didNotFindHydratableInstance:function(){},didNotFindHydratableTextInstance:function(){}},scheduleDeferredCallback:Uo,cancelDeferredCallback:zo,useSyncScheduling:!0});function Ka(e,n,t,o,a){Ha(t)||p("200");var r,i=t._reactRootContainer;if(i)qa.updateContainer(n,i,e,a);else{if(!(o=o||!(!(r=(r=t)?9===r.nodeType?r.documentElement:r.firstChild:null)||1!==r.nodeType||!r.hasAttribute("data-reactroot"))))for(i=void 0;i=t.lastChild;)t.removeChild(i);var s=qa.createContainer(t,o);i=t._reactRootContainer=s,qa.unbatchedUpdates(function(){qa.updateContainer(n,s,e,a)})}return qa.getPublicRootInstance(i)}function Ya(e,n){var t=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;return Ha(n)||p("200"),function(e,n,t){var o=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:To,key:null==o?null:""+o,children:e,containerInfo:n,implementation:t}}(e,n,null,t)}function Xa(e,n){this._reactRootContainer=qa.createContainer(e,n)}ln=qa.batchedUpdates,Xa.prototype.render=function(e,n){qa.updateContainer(e,this._reactRootContainer,null,n)},Xa.prototype.unmount=function(e){qa.updateContainer(null,this._reactRootContainer,null,e)};var Qa={createPortal:Ya,findDOMNode:function(e){if(null==e)return null;if(1===e.nodeType)return e;var n=e._reactInternalFiber;if(n)return qa.findHostInstance(n);"function"==typeof e.render?p("188"):p("213",Object.keys(e))},hydrate:function(e,n,t){return Ka(null,e,n,!0,t)},render:function(e,n,t){return Ka(null,e,n,!1,t)},unstable_renderSubtreeIntoContainer:function(e,n,t,o){return(null==e||void 0===e._reactInternalFiber)&&p("38"),Ka(e,n,t,!1,o)},unmountComponentAtNode:function(e){return Ha(e)||p("40"),!!e._reactRootContainer&&(qa.unbatchedUpdates(function(){Ka(null,null,e,!1,function(){e._reactRootContainer=null})}),!0)},unstable_createPortal:Ya,unstable_batchedUpdates:un,unstable_deferredUpdates:qa.deferredUpdates,flushSync:qa.flushSync,__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{EventPluginHub:le,EventPluginRegistry:q,EventPropagators:xe,ReactControlledComponent:sn,ReactDOMComponentTree:me,ReactDOMEventListener:nt}};qa.injectIntoDevTools({findFiberByHostInstance:de,bundleType:0,version:"16.2.0",rendererPackageName:"react-dom"});var $a=Object.freeze({default:Qa}),Ja=$a&&Qa||$a;n.exports=Ja.default?Ja.default:Ja},{"fbjs/lib/EventListener":7,"fbjs/lib/ExecutionEnvironment":8,"fbjs/lib/containsNode":11,"fbjs/lib/emptyFunction":12,"fbjs/lib/emptyObject":13,"fbjs/lib/focusNode":14,"fbjs/lib/getActiveElement":15,"fbjs/lib/shallowEqual":21,"object-assign":43,react:90}],56:[function(e,n,t){(function(t){"use strict";"production"===t.env.NODE_ENV?(!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE){if("production"!==t.env.NODE_ENV)throw new Error("^_^");try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(e){console.error(e)}}}(),n.exports=e("./cjs/react-dom.production.min.js")):n.exports=e("./cjs/react-dom.development.js")}).call(this,e("_process"))},{"./cjs/react-dom.development.js":54,"./cjs/react-dom.production.min.js":55,_process:48}],57:[function(e,n,t){"use strict";var o=e("react"),a=e("xtend");function r(e,n){var t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0;if("text"===e.type)return e.value;var l=n.renderers[e.type];if("function"!=typeof l&&"string"!=typeof l)throw new Error("Renderer for type `"+e.type+"` not defined or is not renderable");var c=e.position.start,u=[e.type,c.line,c.column].join("-"),h=function(e,n,t,o,s,l){var c={key:n};t.sourcePos&&e.position&&(c["data-sourcepos"]=[(u=e.position).start.line,":",u.start.column,"-",u.end.line,":",u.end.column].map(String).join(""));var u;var h=e.identifier?t.definitions[e.identifier]||{}:null;switch(e.type){case"root":i(c,{className:t.className});break;case"heading":c.level=e.depth;break;case"list":c.start=e.start,c.ordered=e.ordered,c.tight=!e.loose;break;case"listItem":c.checked=e.checked,c.tight=!e.loose,c.children=(c.tight?(d=e,d.children.reduce(function(e,n){return e.concat("paragraph"===n.type?n.children||[]:[n])},[])):e.children).map(function(n,o){return r(n,t,{node:e,props:c},o)});break;case"definition":i(c,{identifier:e.identifier,title:e.title,url:e.url});break;case"code":i(c,{language:e.lang});break;case"inlineCode":c.children=e.value,c.inline=!0;break;case"link":i(c,{title:e.title||void 0,href:t.transformLinkUri?t.transformLinkUri(e.url,e.children,e.title):e.url});break;case"image":i(c,{alt:e.alt||void 0,title:e.title||void 0,src:t.transformImageUri?t.transformImageUri(e.url,e.children,e.title,e.alt):e.url});break;case"linkReference":i(c,a(h,{href:t.transformLinkUri?t.transformLinkUri(h.href):h.href}));break;case"imageReference":i(c,{src:t.transformImageUri&&h.href?t.transformImageUri(h.href,e.children,h.title,e.alt):h.href,title:h.title||void 0,alt:e.alt||void 0});break;case"table":case"tableHead":case"tableBody":c.columnAlignment=e.align;break;case"tableRow":c.isHeader="tableHead"===s.node.type,c.columnAlignment=s.props.columnAlignment;break;case"tableCell":i(c,{isHeader:s.props.isHeader,align:s.props.columnAlignment[l]});break;case"virtualHtml":c.tag=e.tag;break;case"html":c.isBlock=e.position.start.line!==e.position.end.line,c.escapeHtml=t.escapeHtml,c.skipHtml=t.skipHtml}var d;"string"!=typeof o&&e.value&&(c.value=e.value);return c}(e,u,n,l,t,s);return o.createElement(l,h,h.children||e.children&&e.children.map(function(t,o){return r(t,n,{node:e,props:h},o)})||void 0)}function i(e,n){for(var t in n)void 0!==n[t]&&(e[t]=n[t])}n.exports=r},{react:90,xtend:160}],58:[function(e,n,t){"use strict";n.exports=function e(n){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return(n.children||[]).reduce(function(n,t){return"definition"===t.type&&(n[t.identifier]={href:t.url,title:t.title}),e(t,n)},t)}},{}],59:[function(e,n,t){"use strict";var o=e("unist-util-visit");function a(e,n,t,o){if("remove"===o)t.children.splice(n,1);else if("unwrap"===o){var a=[n,1].concat(e.children);Array.prototype.splice.apply(t.children,a)}}t.ofType=function(e,n){return function(n){return e.forEach(function(e){return o(n,e,t,!0)}),n};function t(e,t,o){o&&a(e,t,o,n)}},t.ifNotMatch=function(e,n){return function(e){return o(e,t,!0),e};function t(t,o,r){r&&!e(t,o,r)&&a(t,o,r,n)}}},{"unist-util-visit":151}],60:[function(e,n,t){"use strict";var o=e("unist-util-visit"),a="virtualHtml",r=/^<(area|base|br|col|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)\s*\/?>$/i,i=/^<(\/?)([a-z]+)\s*>$/;n.exports=function(e){var n=void 0,t=void 0;return o(e,"html",function(e,o,s){t!==s&&(n=[],t=s);var l,c=!!(l=e.value.match(r))&&l[1];if(c)return s.children.splice(o,1,{type:a,tag:c,position:e.position}),!0;var u,h,d=!!(h=(u=e).value.match(i))&&{tag:h[2],opening:!h[1],node:u};if(!d)return!0;var p,f,m,g,y,b,w=function(e,n){var t=e.length;for(;t--;)if(e[t].tag===n)return e.splice(t,1)[0];return!1}(n,d.tag);return w?s.children.splice(o,0,(p=d,f=w,g=(m=s).children.indexOf(p.node),y=m.children.indexOf(f.node),b=m.children.splice(g,y-g+1).slice(1,-1),{type:a,children:b,tag:p.tag,position:{start:p.node.position.start,end:f.node.position.end,indent:[]}})):d.opening||n.push(d),!0},!0),e}},{"unist-util-visit":151}],61:[function(e,n,t){"use strict";var o=e("xtend"),a=e("unified"),r=e("remark-parse"),i=e("prop-types"),s=e("./plugins/naive-html"),l=e("./plugins/disallow-node"),c=e("./ast-to-react"),u=e("./wrap-table-rows"),h=e("./get-definitions"),d=e("./uriTransformer"),p=e("./renderers"),f=Object.keys(p),m=function(e){var n=e.source||e.children||"";if(e.allowedTypes&&e.disallowedTypes)throw new Error("Only one of `allowedTypes` and `disallowedTypes` should be defined");var t=o(p,e.renderers),i=[r].concat(e.plugins||[]).reduce(g,a()).parse(n),d=o(e,{renderers:t,definitions:h(i)}),m=function(e){var n=[u],t=e.disallowedTypes;e.allowedTypes&&(t=f.filter(function(n){return"root"!==n&&-1===e.allowedTypes.indexOf(n)}));var o=e.unwrapDisallowed?"unwrap":"remove";t&&t.length>0&&n.push(l.ofType(t,o));e.allowNode&&n.push(l.ifNotMatch(e.allowNode,o));e.escapeHtml||e.skipHtml||n.push(s);return e.astPlugins?n.concat(e.astPlugins):n}(e).reduce(function(e,n){return n(e,d)},i);return c(m,d)};function g(e,n){return Array.isArray(n)?e.use(n[0],n[1]):e.use(n)}m.defaultProps={renderers:{},escapeHtml:!0,skipHtml:!1,transformLinkUri:d},m.propTypes={className:i.string,source:i.string,children:i.string,sourcePos:i.bool,escapeHtml:i.bool,skipHtml:i.bool,allowNode:i.func,allowedTypes:i.arrayOf(i.oneOf(f)),disallowedTypes:i.arrayOf(i.oneOf(f)),transformLinkUri:i.oneOfType([i.func,i.bool]),transformImageUri:i.func,astPlugins:i.arrayOf(i.func),unwrapDisallowed:i.bool,renderers:i.object},m.types=f,m.renderers=p,m.uriTransformer=d,n.exports=m},{"./ast-to-react":57,"./get-definitions":58,"./plugins/disallow-node":59,"./plugins/naive-html":60,"./renderers":62,"./uriTransformer":63,"./wrap-table-rows":64,"prop-types":52,"remark-parse":91,unified:147,xtend:160}],62:[function(e,n,t){"use strict";var o=e("xtend"),a=e("react").createElement;function r(e,n){return a(e,i(n),n.children)}function i(e){return e["data-sourcepos"]?{"data-sourcepos":e["data-sourcepos"]}:{}}n.exports={root:"div",break:"br",paragraph:"p",emphasis:"em",strong:"strong",thematicBreak:"hr",blockquote:"blockquote",delete:"del",link:"a",image:"img",linkReference:"a",imageReference:"img",table:r.bind(null,"table"),tableHead:r.bind(null,"thead"),tableBody:r.bind(null,"tbody"),tableRow:r.bind(null,"tr"),tableCell:function(e){var n=e.align?{textAlign:e.align}:void 0,t=i(e);return a(e.isHeader?"th":"td",n?o({style:n},t):t,e.children)},list:function(e){var n=i(e);null!==e.start&&1!==e.start&&(n.start=e.start.toString());return a(e.ordered?"ol":"ul",n,e.children)},listItem:function(e){var n=null;if(null!==e.checked){var t=e.checked;n=a("input",{type:"checkbox",checked:t,readOnly:!0})}return a("li",i(e),n,e.children)},definition:function(){return null},heading:function(e){return a("h"+e.level,i(e),e.children)},inlineCode:function(e){return a("code",i(e),e.children)},code:function(e){var n=e.language&&"language-"+e.language,t=a("code",n?{className:n}:null,e.value);return a("pre",i(e),t)},html:function(e){if(e.skipHtml)return null;var n=e.isBlock?"div":"span";if(e.escapeHtml)return a(n,null,e.value);var t={dangerouslySetInnerHTML:{__html:e.value}};return a(n,t)},virtualHtml:function(e){return a(e.tag,i(e),e.children)}}},{react:90,xtend:160}],63:[function(e,n,t){"use strict";var o=["http","https","mailto","tel"];n.exports=function(e){var n=(e||"").trim(),t=n.charAt(0);if("#"===t||"/"===t)return n;var a=n.indexOf(":");if(-1===a)return n;for(var r=o.length,i=-1;++i<r;){var s=o[i];if(a===s.length&&n.slice(0,s.length)===s)return n}return-1!==(i=n.indexOf("?"))&&a>i?n:-1!==(i=n.indexOf("#"))&&a>i?n:"javascript:void(0)"}},{}],64:[function(e,n,t){"use strict";var o=e("unist-util-visit");function a(e){var n=e.children;e.children=[{type:"tableHead",align:e.align,children:[n[0]],position:n[0].position}],n.length>1&&e.children.push({type:"tableBody",align:e.align,children:n.slice(1),position:{start:n[1].position.start,end:n[n.length-1].position.end}})}n.exports=function(e){return o(e,"table",a),e}},{"unist-util-visit":151}],65:[function(e,n,t){"use strict";t.__esModule=!0;var o=l(e("warning")),a=l(e("react")),r=l(e("prop-types")),i=l(e("history/createBrowserHistory")),s=l(e("./Router"));function l(e){return e&&e.__esModule?e:{default:e}}function c(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}var u=function(e){function n(){var t,o;!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n);for(var a=arguments.length,r=Array(a),s=0;s<a;s++)r[s]=arguments[s];return t=o=c(this,e.call.apply(e,[this].concat(r))),o.history=(0,i.default)(o.props),c(o,t)}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.componentWillMount=function(){(0,o.default)(!this.props.history,"<BrowserRouter> ignores the history prop. To use a custom history, use `import { Router }` instead of `import { BrowserRouter as Router }`.")},n.prototype.render=function(){return a.default.createElement(s.default,{history:this.history,children:this.props.children})},n}(a.default.Component);u.propTypes={basename:r.default.string,forceRefresh:r.default.bool,getUserConfirmation:r.default.func,keyLength:r.default.number,children:r.default.node},t.default=u},{"./Router":73,"history/createBrowserHistory":26,"prop-types":52,react:90,warning:157}],66:[function(e,n,t){"use strict";t.__esModule=!0;var o=l(e("warning")),a=l(e("react")),r=l(e("prop-types")),i=l(e("history/createHashHistory")),s=l(e("./Router"));function l(e){return e&&e.__esModule?e:{default:e}}function c(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}var u=function(e){function n(){var t,o;!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n);for(var a=arguments.length,r=Array(a),s=0;s<a;s++)r[s]=arguments[s];return t=o=c(this,e.call.apply(e,[this].concat(r))),o.history=(0,i.default)(o.props),c(o,t)}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.componentWillMount=function(){(0,o.default)(!this.props.history,"<HashRouter> ignores the history prop. To use a custom history, use `import { Router }` instead of `import { HashRouter as Router }`.")},n.prototype.render=function(){return a.default.createElement(s.default,{history:this.history,children:this.props.children})},n}(a.default.Component);u.propTypes={basename:r.default.string,getUserConfirmation:r.default.func,hashType:r.default.oneOf(["hashbang","noslash","slash"]),children:r.default.node},t.default=u},{"./Router":73,"history/createHashHistory":27,"prop-types":52,react:90,warning:157}],67:[function(e,n,t){"use strict";t.__esModule=!0;var o=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},a=s(e("react")),r=s(e("prop-types")),i=s(e("invariant"));function s(e){return e&&e.__esModule?e:{default:e}}function l(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}var c=function(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)},u=function(e){function n(){var t,o;!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n);for(var a=arguments.length,r=Array(a),i=0;i<a;i++)r[i]=arguments[i];return t=o=l(this,e.call.apply(e,[this].concat(r))),o.handleClick=function(e){if(o.props.onClick&&o.props.onClick(e),!e.defaultPrevented&&0===e.button&&!o.props.target&&!c(e)){e.preventDefault();var n=o.context.router.history,t=o.props,a=t.replace,r=t.to;a?n.replace(r):n.push(r)}},l(o,t)}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.render=function(){var e=this.props,n=(e.replace,e.to),t=e.innerRef,r=function(e,n){var t={};for(var o in e)n.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o]);return t}(e,["replace","to","innerRef"]);(0,i.default)(this.context.router,"You should not use <Link> outside a <Router>");var s=this.context.router.history.createHref("string"==typeof n?{pathname:n}:n);return a.default.createElement("a",o({},r,{onClick:this.handleClick,href:s,ref:t}))},n}(a.default.Component);u.propTypes={onClick:r.default.func,target:r.default.string,replace:r.default.bool,to:r.default.oneOfType([r.default.string,r.default.object]).isRequired,innerRef:r.default.oneOfType([r.default.string,r.default.func])},u.defaultProps={replace:!1},u.contextTypes={router:r.default.shape({history:r.default.shape({push:r.default.func.isRequired,replace:r.default.func.isRequired,createHref:r.default.func.isRequired}).isRequired}).isRequired},t.default=u},{invariant:33,"prop-types":52,react:90}],68:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/MemoryRouter"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/MemoryRouter":79}],69:[function(e,n,t){"use strict";t.__esModule=!0;var o=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r=c(e("react")),i=c(e("prop-types")),s=c(e("./Route")),l=c(e("./Link"));function c(e){return e&&e.__esModule?e:{default:e}}var u=function(e){var n=e.to,t=e.exact,i=e.strict,c=e.location,u=e.activeClassName,h=e.className,d=e.activeStyle,p=e.style,f=e.isActive,m=e.ariaCurrent,g=function(e,n){var t={};for(var o in e)n.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o]);return t}(e,["to","exact","strict","location","activeClassName","className","activeStyle","style","isActive","ariaCurrent"]);return r.default.createElement(s.default,{path:"object"===(void 0===n?"undefined":a(n))?n.pathname:n,exact:t,strict:i,location:c,children:function(e){var t=e.location,a=e.match,i=!!(f?f(a,t):a);return r.default.createElement(l.default,o({to:n,className:i?[h,u].filter(function(e){return e}).join(" "):h,style:i?o({},p,d):p,"aria-current":i&&m},g))}})};u.propTypes={to:l.default.propTypes.to,exact:i.default.bool,strict:i.default.bool,location:i.default.object,activeClassName:i.default.string,className:i.default.string,activeStyle:i.default.object,style:i.default.object,isActive:i.default.func,ariaCurrent:i.default.oneOf(["page","step","location","true"])},u.defaultProps={activeClassName:"active",ariaCurrent:"true"},t.default=u},{"./Link":67,"./Route":72,"prop-types":52,react:90}],70:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/Prompt"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/Prompt":80}],71:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/Redirect"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/Redirect":81}],72:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/Route"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/Route":82}],73:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/Router"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/Router":83}],74:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/StaticRouter"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/StaticRouter":84}],75:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/Switch"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/Switch":85}],76:[function(e,n,t){"use strict";t.__esModule=!0,t.withRouter=t.matchPath=t.Switch=t.StaticRouter=t.Router=t.Route=t.Redirect=t.Prompt=t.NavLink=t.MemoryRouter=t.Link=t.HashRouter=t.BrowserRouter=void 0;var o=g(e("./BrowserRouter")),a=g(e("./HashRouter")),r=g(e("./Link")),i=g(e("./MemoryRouter")),s=g(e("./NavLink")),l=g(e("./Prompt")),c=g(e("./Redirect")),u=g(e("./Route")),h=g(e("./Router")),d=g(e("./StaticRouter")),p=g(e("./Switch")),f=g(e("./matchPath")),m=g(e("./withRouter"));function g(e){return e&&e.__esModule?e:{default:e}}t.BrowserRouter=o.default,t.HashRouter=a.default,t.Link=r.default,t.MemoryRouter=i.default,t.NavLink=s.default,t.Prompt=l.default,t.Redirect=c.default,t.Route=u.default,t.Router=h.default,t.StaticRouter=d.default,t.Switch=p.default,t.matchPath=f.default,t.withRouter=m.default},{"./BrowserRouter":65,"./HashRouter":66,"./Link":67,"./MemoryRouter":68,"./NavLink":69,"./Prompt":70,"./Redirect":71,"./Route":72,"./Router":73,"./StaticRouter":74,"./Switch":75,"./matchPath":77,"./withRouter":78}],77:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/matchPath"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/matchPath":86}],78:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("react-router/withRouter"),r=(o=a)&&o.__esModule?o:{default:o};t.default=r.default},{"react-router/withRouter":87}],79:[function(e,n,t){"use strict";t.__esModule=!0;var o=l(e("warning")),a=l(e("react")),r=l(e("prop-types")),i=l(e("history/createMemoryHistory")),s=l(e("./Router"));function l(e){return e&&e.__esModule?e:{default:e}}function c(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}var u=function(e){function n(){var t,o;!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n);for(var a=arguments.length,r=Array(a),s=0;s<a;s++)r[s]=arguments[s];return t=o=c(this,e.call.apply(e,[this].concat(r))),o.history=(0,i.default)(o.props),c(o,t)}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.componentWillMount=function(){(0,o.default)(!this.props.history,"<MemoryRouter> ignores the history prop. To use a custom history, use `import { Router }` instead of `import { MemoryRouter as Router }`.")},n.prototype.render=function(){return a.default.createElement(s.default,{history:this.history,children:this.props.children})},n}(a.default.Component);u.propTypes={initialEntries:r.default.array,initialIndex:r.default.number,getUserConfirmation:r.default.func,keyLength:r.default.number,children:r.default.node},t.default=u},{"./Router":83,"history/createMemoryHistory":28,"prop-types":52,react:90,warning:157}],80:[function(e,n,t){"use strict";t.__esModule=!0;var o=i(e("react")),a=i(e("prop-types")),r=i(e("invariant"));function i(e){return e&&e.__esModule?e:{default:e}}var s=function(e){function n(){return function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n),function(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}(this,e.apply(this,arguments))}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.enable=function(e){this.unblock&&this.unblock(),this.unblock=this.context.router.history.block(e)},n.prototype.disable=function(){this.unblock&&(this.unblock(),this.unblock=null)},n.prototype.componentWillMount=function(){(0,r.default)(this.context.router,"You should not use <Prompt> outside a <Router>"),this.props.when&&this.enable(this.props.message)},n.prototype.componentWillReceiveProps=function(e){e.when?this.props.when&&this.props.message===e.message||this.enable(e.message):this.disable()},n.prototype.componentWillUnmount=function(){this.disable()},n.prototype.render=function(){return null},n}(o.default.Component);s.propTypes={when:a.default.bool,message:a.default.oneOfType([a.default.func,a.default.string]).isRequired},s.defaultProps={when:!0},s.contextTypes={router:a.default.shape({history:a.default.shape({block:a.default.func.isRequired}).isRequired}).isRequired},t.default=s},{invariant:33,"prop-types":52,react:90}],81:[function(e,n,t){"use strict";t.__esModule=!0;var o=l(e("react")),a=l(e("prop-types")),r=l(e("warning")),i=l(e("invariant")),s=e("history");function l(e){return e&&e.__esModule?e:{default:e}}var c=function(e){function n(){return function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n),function(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}(this,e.apply(this,arguments))}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.isStatic=function(){return this.context.router&&this.context.router.staticContext},n.prototype.componentWillMount=function(){(0,i.default)(this.context.router,"You should not use <Redirect> outside a <Router>"),this.isStatic()&&this.perform()},n.prototype.componentDidMount=function(){this.isStatic()||this.perform()},n.prototype.componentDidUpdate=function(e){var n=(0,s.createLocation)(e.to),t=(0,s.createLocation)(this.props.to);(0,s.locationsAreEqual)(n,t)?(0,r.default)(!1,"You tried to redirect to the same route you're currently on: \""+t.pathname+t.search+'"'):this.perform()},n.prototype.perform=function(){var e=this.context.router.history,n=this.props,t=n.push,o=n.to;t?e.push(o):e.replace(o)},n.prototype.render=function(){return null},n}(o.default.Component);c.propTypes={push:a.default.bool,from:a.default.string,to:a.default.oneOfType([a.default.string,a.default.object]).isRequired},c.defaultProps={push:!1},c.contextTypes={router:a.default.shape({history:a.default.shape({push:a.default.func.isRequired,replace:a.default.func.isRequired}).isRequired,staticContext:a.default.object}).isRequired},t.default=c},{history:30,invariant:33,"prop-types":52,react:90,warning:157}],82:[function(e,n,t){"use strict";t.__esModule=!0;var o=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},a=c(e("warning")),r=c(e("invariant")),i=c(e("react")),s=c(e("prop-types")),l=c(e("./matchPath"));function c(e){return e&&e.__esModule?e:{default:e}}function u(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}var h=function(e){return 0===i.default.Children.count(e)},d=function(e){function n(){var t,o;!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n);for(var a=arguments.length,r=Array(a),i=0;i<a;i++)r[i]=arguments[i];return t=o=u(this,e.call.apply(e,[this].concat(r))),o.state={match:o.computeMatch(o.props,o.context.router)},u(o,t)}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.getChildContext=function(){return{router:o({},this.context.router,{route:{location:this.props.location||this.context.router.route.location,match:this.state.match}})}},n.prototype.computeMatch=function(e,n){var t=e.computedMatch,o=e.location,a=e.path,i=e.strict,s=e.exact,c=e.sensitive;if(t)return t;(0,r.default)(n,"You should not use <Route> or withRouter() outside a <Router>");var u=n.route,h=(o||u.location).pathname;return a?(0,l.default)(h,{path:a,strict:i,exact:s,sensitive:c}):u.match},n.prototype.componentWillMount=function(){(0,a.default)(!(this.props.component&&this.props.render),"You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored"),(0,a.default)(!(this.props.component&&this.props.children&&!h(this.props.children)),"You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored"),(0,a.default)(!(this.props.render&&this.props.children&&!h(this.props.children)),"You should not use <Route render> and <Route children> in the same route; <Route children> will be ignored")},n.prototype.componentWillReceiveProps=function(e,n){(0,a.default)(!(e.location&&!this.props.location),'<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'),(0,a.default)(!(!e.location&&this.props.location),'<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.'),this.setState({match:this.computeMatch(e,n.router)})},n.prototype.render=function(){var e=this.state.match,n=this.props,t=n.children,o=n.component,a=n.render,r=this.context.router,s=r.history,l=r.route,c=r.staticContext,u={match:e,location:this.props.location||l.location,history:s,staticContext:c};return o?e?i.default.createElement(o,u):null:a?e?a(u):null:t?"function"==typeof t?t(u):h(t)?null:i.default.Children.only(t):null},n}(i.default.Component);d.propTypes={computedMatch:s.default.object,path:s.default.string,exact:s.default.bool,strict:s.default.bool,sensitive:s.default.bool,component:s.default.func,render:s.default.func,children:s.default.oneOfType([s.default.func,s.default.node]),location:s.default.object},d.contextTypes={router:s.default.shape({history:s.default.object.isRequired,route:s.default.object.isRequired,staticContext:s.default.object})},d.childContextTypes={router:s.default.object.isRequired},t.default=d},{"./matchPath":86,invariant:33,"prop-types":52,react:90,warning:157}],83:[function(e,n,t){"use strict";t.__esModule=!0;var o=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},a=l(e("warning")),r=l(e("invariant")),i=l(e("react")),s=l(e("prop-types"));function l(e){return e&&e.__esModule?e:{default:e}}function c(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}var u=function(e){function n(){var t,o;!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n);for(var a=arguments.length,r=Array(a),i=0;i<a;i++)r[i]=arguments[i];return t=o=c(this,e.call.apply(e,[this].concat(r))),o.state={match:o.computeMatch(o.props.history.location.pathname)},c(o,t)}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.getChildContext=function(){return{router:o({},this.context.router,{history:this.props.history,route:{location:this.props.history.location,match:this.state.match}})}},n.prototype.computeMatch=function(e){return{path:"/",url:"/",params:{},isExact:"/"===e}},n.prototype.componentWillMount=function(){var e=this,n=this.props,t=n.children,o=n.history;(0,r.default)(null==t||1===i.default.Children.count(t),"A <Router> may have only one child element"),this.unlisten=o.listen(function(){e.setState({match:e.computeMatch(o.location.pathname)})})},n.prototype.componentWillReceiveProps=function(e){(0,a.default)(this.props.history===e.history,"You cannot change <Router history>")},n.prototype.componentWillUnmount=function(){this.unlisten()},n.prototype.render=function(){var e=this.props.children;return e?i.default.Children.only(e):null},n}(i.default.Component);u.propTypes={history:s.default.object.isRequired,children:s.default.node},u.contextTypes={router:s.default.object},u.childContextTypes={router:s.default.object.isRequired},t.default=u},{invariant:33,"prop-types":52,react:90,warning:157}],84:[function(e,n,t){"use strict";t.__esModule=!0;var o=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},a=u(e("warning")),r=u(e("invariant")),i=u(e("react")),s=u(e("prop-types")),l=e("history/PathUtils"),c=u(e("./Router"));function u(e){return e&&e.__esModule?e:{default:e}}function h(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}var d=function(e,n){return e?o({},n,{pathname:(0,l.addLeadingSlash)(e)+n.pathname}):n},p=function(e){return"string"==typeof e?(0,l.parsePath)(e):(t=(n=e).pathname,o=void 0===t?"/":t,a=n.search,r=void 0===a?"":a,i=n.hash,s=void 0===i?"":i,{pathname:o,search:"?"===r?"":r,hash:"#"===s?"":s});var n,t,o,a,r,i,s},f=function(e){return"string"==typeof e?e:(0,l.createPath)(e)},m=function(e){return function(){(0,r.default)(!1,"You cannot %s with <StaticRouter>",e)}},g=function(){},y=function(e){function n(){var t,o;!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n);for(var a=arguments.length,r=Array(a),i=0;i<a;i++)r[i]=arguments[i];return t=o=h(this,e.call.apply(e,[this].concat(r))),o.createHref=function(e){return(0,l.addLeadingSlash)(o.props.basename+f(e))},o.handlePush=function(e){var n=o.props,t=n.basename,a=n.context;a.action="PUSH",a.location=d(t,p(e)),a.url=f(a.location)},o.handleReplace=function(e){var n=o.props,t=n.basename,a=n.context;a.action="REPLACE",a.location=d(t,p(e)),a.url=f(a.location)},o.handleListen=function(){return g},o.handleBlock=function(){return g},h(o,t)}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.getChildContext=function(){return{router:{staticContext:this.props.context}}},n.prototype.componentWillMount=function(){(0,a.default)(!this.props.history,"<StaticRouter> ignores the history prop. To use a custom history, use `import { Router }` instead of `import { StaticRouter as Router }`.")},n.prototype.render=function(){var e=this.props,n=e.basename,t=(e.context,e.location),a=function(e,n){var t={};for(var o in e)n.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o]);return t}(e,["basename","context","location"]),r={createHref:this.createHref,action:"POP",location:function(e,n){if(!e)return n;var t=(0,l.addLeadingSlash)(e);return 0!==n.pathname.indexOf(t)?n:o({},n,{pathname:n.pathname.substr(t.length)})}(n,p(t)),push:this.handlePush,replace:this.handleReplace,go:m("go"),goBack:m("goBack"),goForward:m("goForward"),listen:this.handleListen,block:this.handleBlock};return i.default.createElement(c.default,o({},a,{history:r}))},n}(i.default.Component);y.propTypes={basename:s.default.string,context:s.default.object.isRequired,location:s.default.oneOfType([s.default.string,s.default.object])},y.defaultProps={basename:"",location:"/"},y.childContextTypes={router:s.default.object.isRequired},t.default=y},{"./Router":83,"history/PathUtils":25,invariant:33,"prop-types":52,react:90,warning:157}],85:[function(e,n,t){"use strict";t.__esModule=!0;var o=l(e("react")),a=l(e("prop-types")),r=l(e("warning")),i=l(e("invariant")),s=l(e("./matchPath"));function l(e){return e&&e.__esModule?e:{default:e}}var c=function(e){function n(){return function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n),function(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}(this,e.apply(this,arguments))}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,e),n.prototype.componentWillMount=function(){(0,i.default)(this.context.router,"You should not use <Switch> outside a <Router>")},n.prototype.componentWillReceiveProps=function(e){(0,r.default)(!(e.location&&!this.props.location),'<Switch> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'),(0,r.default)(!(!e.location&&this.props.location),'<Switch> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.')},n.prototype.render=function(){var e=this.context.router.route,n=this.props.children,t=this.props.location||e.location,a=void 0,r=void 0;return o.default.Children.forEach(n,function(n){if(o.default.isValidElement(n)){var i=n.props,l=i.path,c=i.exact,u=i.strict,h=i.sensitive,d=i.from,p=l||d;null==a&&(r=n,a=p?(0,s.default)(t.pathname,{path:p,exact:c,strict:u,sensitive:h}):e.match)}}),a?o.default.cloneElement(r,{location:t,computedMatch:a}):null},n}(o.default.Component);c.contextTypes={router:a.default.shape({route:a.default.object.isRequired}).isRequired},c.propTypes={children:a.default.node,location:a.default.object},t.default=c},{"./matchPath":86,invariant:33,"prop-types":52,react:90,warning:157}],86:[function(e,n,t){"use strict";t.__esModule=!0;var o,a=e("path-to-regexp"),r=(o=a)&&o.__esModule?o:{default:o};var i={},s=0;t.default=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"string"==typeof n&&(n={path:n});var t=n,o=t.path,a=void 0===o?"/":o,l=t.exact,c=void 0!==l&&l,u=t.strict,h=void 0!==u&&u,d=t.sensitive,p=function(e,n){var t=""+n.end+n.strict+n.sensitive,o=i[t]||(i[t]={});if(o[e])return o[e];var a=[],l={re:(0,r.default)(e,a,n),keys:a};return s<1e4&&(o[e]=l,s++),l}(a,{end:c,strict:h,sensitive:void 0!==d&&d}),f=p.re,m=p.keys,g=f.exec(e);if(!g)return null;var y=g[0],b=g.slice(1),w=e===y;return c&&!w?null:{path:a,url:"/"===a&&""===y?"/":y,isExact:w,params:m.reduce(function(e,n,t){return e[n.name]=b[t],e},{})}}},{"path-to-regexp":46}],87:[function(e,n,t){"use strict";t.__esModule=!0;var o=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e},a=l(e("react")),r=l(e("prop-types")),i=l(e("hoist-non-react-statics")),s=l(e("./Route"));function l(e){return e&&e.__esModule?e:{default:e}}t.default=function(e){var n=function(n){var t=n.wrappedComponentRef,r=function(e,n){var t={};for(var o in e)n.indexOf(o)>=0||Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o]);return t}(n,["wrappedComponentRef"]);return a.default.createElement(s.default,{render:function(n){return a.default.createElement(e,o({},r,n,{ref:t}))}})};return n.displayName="withRouter("+(e.displayName||e.name)+")",n.WrappedComponent=e,n.propTypes={wrappedComponentRef:r.default.func},(0,i.default)(n,e)}},{"./Route":82,"hoist-non-react-statics":31,"prop-types":52,react:90}],88:[function(e,n,t){(function(t){"use strict";"production"!==t.env.NODE_ENV&&function(){var t=e("object-assign"),o=e("fbjs/lib/emptyObject"),a=e("fbjs/lib/invariant"),r=e("fbjs/lib/warning"),i=e("fbjs/lib/emptyFunction"),s=e("prop-types/checkPropTypes"),l="function"==typeof Symbol&&Symbol.for,c=l?Symbol.for("react.element"):60103,u=l?Symbol.for("react.call"):60104,h=l?Symbol.for("react.return"):60105,d=l?Symbol.for("react.portal"):60106,p=l?Symbol.for("react.fragment"):60107,f="function"==typeof Symbol&&Symbol.iterator,m="@@iterator";function g(e){if(null===e||void 0===e)return null;var n=f&&e[f]||e[m];return"function"==typeof n?n:null}var y=function(e,n){if(void 0===n)throw new Error("`warning(condition, format, ...args)` requires a warning message argument");if(!e){for(var t=arguments.length,o=Array(t>2?t-2:0),a=2;a<t;a++)o[a-2]=arguments[a];(function(e){for(var n=arguments.length,t=Array(n>1?n-1:0),o=1;o<n;o++)t[o-1]=arguments[o];var a=0,r="Warning: "+e.replace(/%s/g,function(){return t[a++]});"undefined"!=typeof console&&console.warn(r);try{throw new Error(r)}catch(e){}}).apply(void 0,[n].concat(o))}},b={};function w(e,n){var t=e.constructor,o=t&&(t.displayName||t.name)||"ReactClass",a=o+"."+n;b[a]||(r(!1,"%s(...): Can only update a mounted or mounting component. This usually means you called %s() on an unmounted component. This is a no-op.\n\nPlease check the code for the %s component.",n,n,o),b[a]=!0)}var v={isMounted:function(e){return!1},enqueueForceUpdate:function(e,n,t){w(e,"forceUpdate")},enqueueReplaceState:function(e,n,t,o){w(e,"replaceState")},enqueueSetState:function(e,n,t,o){w(e,"setState")}};function k(e,n,t){this.props=e,this.context=n,this.refs=o,this.updater=t||v}k.prototype.isReactComponent={},k.prototype.setState=function(e,n){"object"!=typeof e&&"function"!=typeof e&&null!=e&&a(!1,"setState(...): takes an object of state variables to update or a function which returns an object of state variables."),this.updater.enqueueSetState(this,e,n,"setState")},k.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};var T={isMounted:["isMounted","Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],replaceState:["replaceState","Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]},I=function(e,n){Object.defineProperty(k.prototype,e,{get:function(){y(!1,"%s(...) is deprecated in plain JavaScript React classes. %s",n[0],n[1])}})};for(var E in T)T.hasOwnProperty(E)&&I(E,T[E]);function x(e,n,t){this.props=e,this.context=n,this.refs=o,this.updater=t||v}function S(){}S.prototype=k.prototype;var L=x.prototype=new S;function N(e,n,t){this.props=e,this.context=n,this.refs=o,this.updater=t||v}L.constructor=x,t(L,k.prototype),L.isPureReactComponent=!0;var C=N.prototype=new S;C.constructor=N,t(C,k.prototype),C.unstable_isAsyncReactComponent=!0,C.render=function(){return this.props.children};var A,P,O={current:null},R=Object.prototype.hasOwnProperty,D={key:!0,ref:!0,__self:!0,__source:!0};function M(e){if(R.call(e,"ref")){var n=Object.getOwnPropertyDescriptor(e,"ref").get;if(n&&n.isReactWarning)return!1}return void 0!==e.ref}function F(e){if(R.call(e,"key")){var n=Object.getOwnPropertyDescriptor(e,"key").get;if(n&&n.isReactWarning)return!1}return void 0!==e.key}var B=function(e,n,t,o,a,r,i){var s={$$typeof:c,type:e,key:n,ref:t,props:i,_owner:r,_store:{}};return Object.defineProperty(s._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:!1}),Object.defineProperty(s,"_self",{configurable:!1,enumerable:!1,writable:!1,value:o}),Object.defineProperty(s,"_source",{configurable:!1,enumerable:!1,writable:!1,value:a}),Object.freeze&&(Object.freeze(s.props),Object.freeze(s)),s};function W(e,n,t){var o,a={},i=null,s=null,l=null,u=null;if(null!=n)for(o in M(n)&&(s=n.ref),F(n)&&(i=""+n.key),l=void 0===n.__self?null:n.__self,u=void 0===n.__source?null:n.__source,n)R.call(n,o)&&!D.hasOwnProperty(o)&&(a[o]=n[o]);var h,d,p,f,m,g,y=arguments.length-2;if(1===y)a.children=t;else if(y>1){for(var b=Array(y),w=0;w<y;w++)b[w]=arguments[w+2];Object.freeze&&Object.freeze(b),a.children=b}if(e&&e.defaultProps){var v=e.defaultProps;for(o in v)void 0===a[o]&&(a[o]=v[o])}if((i||s)&&(void 0===a.$$typeof||a.$$typeof!==c)){var k="function"==typeof e?e.displayName||e.name||"Unknown":e;i&&(f=a,m=k,(g=function(){A||(A=!0,r(!1,"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://fb.me/react-special-props)",m))}).isReactWarning=!0,Object.defineProperty(f,"key",{get:g,configurable:!0})),s&&(h=a,d=k,(p=function(){P||(P=!0,r(!1,"%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://fb.me/react-special-props)",d))}).isReactWarning=!0,Object.defineProperty(h,"ref",{get:p,configurable:!0}))}return B(e,i,s,l,u,O.current,a)}function j(e){return"object"==typeof e&&null!==e&&e.$$typeof===c}var _={getCurrentStack:null,getStackAddendum:function(){var e=_.getCurrentStack;return e?e():null}},U=".",z=":";var G=!1,V=/\/+/g;function H(e){return(""+e).replace(V,"$&/")}var q=10,K=[];function Y(e,n,t,o){if(K.length){var a=K.pop();return a.result=e,a.keyPrefix=n,a.func=t,a.context=o,a.count=0,a}return{result:e,keyPrefix:n,func:t,context:o,count:0}}function X(e){e.result=null,e.keyPrefix=null,e.func=null,e.context=null,e.count=0,K.length<q&&K.push(e)}function Q(e,n,t){return null==e?0:function e(n,t,o,i){var s=typeof n;"undefined"!==s&&"boolean"!==s||(n=null);var l,p=!1;if(null===n)p=!0;else switch(s){case"string":case"number":p=!0;break;case"object":switch(n.$$typeof){case c:case u:case h:case d:p=!0}}if(p)return o(i,n,""===t?U+$(n,0):t),1;var f=0,m=""===t?U:t+z;if(Array.isArray(n))for(var y=0;y<n.length;y++)f+=e(l=n[y],m+$(l,y),o,i);else{var b=g(n);if("function"==typeof b){b===n.entries&&(r(G,"Using Maps as children is unsupported and will likely yield unexpected results. Convert it to a sequence/iterable of keyed ReactElements instead.%s",_.getStackAddendum()),G=!0);for(var w,v=b.call(n),k=0;!(w=v.next()).done;)f+=e(l=w.value,m+$(l,k++),o,i)}else if("object"===s){var T;T=" If you meant to render a collection of children, use an array instead."+_.getStackAddendum();var I=""+n;a(!1,"Objects are not valid as a React child (found: %s).%s","[object Object]"===I?"object with keys {"+Object.keys(n).join(", ")+"}":I,T)}}return f}(e,"",n,t)}function $(e,n){return"object"==typeof e&&null!==e&&null!=e.key?(t=e.key,o={"=":"=0",":":"=2"},"$"+(""+t).replace(/[=:]/g,function(e){return o[e]})):n.toString(36);var t,o}function J(e,n,t){var o=e.func,a=e.context;o.call(a,n,e.count++)}function Z(e,n,t){var o,a,r=e.result,s=e.keyPrefix,l=e.func,c=e.context,u=l.call(c,n,e.count++);Array.isArray(u)?ee(u,r,t,i.thatReturnsArgument):null!=u&&(j(u)&&(o=u,a=s+(!u.key||n&&n.key===u.key?"":H(u.key)+"/")+t,u=B(o.type,a,o.ref,o._self,o._source,o._owner,o.props)),r.push(u))}function ee(e,n,t,o,a){var r="";null!=t&&(r=H(t)+"/");var i=Y(n,r,o,a);Q(e,Z,i),X(i)}function ne(e){var n=e.type;return"string"==typeof n?n:"function"==typeof n?n.displayName||n.name:null}var te=null,oe=!1,ae=function(){var e,n,t,o,a="";if(te){var r=null==(o=te)?"#empty":"string"==typeof o||"number"==typeof o?"#text":"string"==typeof o.type?o.type:o.type===p?"React.Fragment":o.type.displayName||o.type.name||"Unknown",i=te._owner;a+=(e=r,n=te._source,t=i&&ne(i),"\n in "+(e||"Unknown")+(n?" (at "+n.fileName.replace(/^.*[\\\/]/,"")+":"+n.lineNumber+")":t?" (created by "+t+")":""))}return a+=_.getStackAddendum()||""},re=new Map([["children",!0],["key",!0]]);function ie(){if(O.current){var e=ne(O.current);if(e)return"\n\nCheck the render method of `"+e+"`."}return""}var se={};function le(e,n){if(e._store&&!e._store.validated&&null==e.key){e._store.validated=!0;var t=function(e){var n=ie();if(!n){var t="string"==typeof e?e:e.displayName||e.name;t&&(n="\n\nCheck the top-level render call using <"+t+">.")}return n}(n);if(!se[t]){se[t]=!0;var o="";e&&e._owner&&e._owner!==O.current&&(o=" It was passed a child from "+ne(e._owner)+"."),te=e,r(!1,'Each child in an array or iterator should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.%s',t,o,ae()),te=null}}}function ce(e,n){if("object"==typeof e)if(Array.isArray(e))for(var t=0;t<e.length;t++){var o=e[t];j(o)&&le(o,n)}else if(j(e))e._store&&(e._store.validated=!0);else if(e){var a=g(e);if("function"==typeof a&&a!==e.entries)for(var r,i=a.call(e);!(r=i.next()).done;)j(r.value)&&le(r.value,n)}}function ue(e){var n=e.type;if("function"==typeof n){var t=n.displayName||n.name,o=n.propTypes;o?(te=e,s(o,e.props,"prop",t,ae),te=null):void 0===n.PropTypes||oe||(oe=!0,r(!1,"Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?",t||"Unknown")),"function"==typeof n.getDefaultProps&&r(n.getDefaultProps.isReactClassApproved,"getDefaultProps is only used on classic React.createClass definitions. Use a static property named `defaultProps` instead.")}}function he(e,n,t){var o="string"==typeof e||"function"==typeof e||"symbol"==typeof e||"number"==typeof e;if(!o){var a="";(void 0===e||"object"==typeof e&&null!==e&&0===Object.keys(e).length)&&(a+=" You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.");var i=function(e){if(null!==e&&void 0!==e&&void 0!==e.__source){var n=e.__source;return"\n\nCheck your code at "+n.fileName.replace(/^.*[\\\/]/,"")+":"+n.lineNumber+"."}return""}(n);a+=i||ie(),a+=ae()||"",r(!1,"React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",null==e?e:typeof e,a)}var s=W.apply(this,arguments);if(null==s)return s;if(o)for(var l=2;l<arguments.length;l++)ce(arguments[l],e);return"symbol"==typeof e&&e===p?function(e){te=e;var n=!0,t=!1,o=void 0;try{for(var a,i=Object.keys(e.props)[Symbol.iterator]();!(n=(a=i.next()).done);n=!0){var s=a.value;if(!re.has(s)){r(!1,"Invalid prop `%s` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.%s",s,ae());break}}}catch(e){t=!0,o=e}finally{try{!n&&i.return&&i.return()}finally{if(t)throw o}}null!==e.ref&&r(!1,"Invalid attribute `ref` supplied to `React.Fragment`.%s",ae()),te=null}(s):ue(s),s}var de={Children:{map:function(e,n,t){if(null==e)return e;var o=[];return ee(e,o,null,n,t),o},forEach:function(e,n,t){if(null==e)return e;var o=Y(null,null,n,t);Q(e,J,o),X(o)},count:function(e,n){return Q(e,i.thatReturnsNull,null)},toArray:function(e){var n=[];return ee(e,n,null,i.thatReturnsArgument),n},only:function(e){return j(e)||a(!1,"React.Children.only expected to receive a single React element child."),e}},Component:k,PureComponent:x,unstable_AsyncComponent:N,Fragment:p,createElement:he,cloneElement:function(e,n,o){for(var a=function(e,n,o){var a,r,i=t({},e.props),s=e.key,l=e.ref,c=e._self,u=e._source,h=e._owner;if(null!=n)for(a in M(n)&&(l=n.ref,h=O.current),F(n)&&(s=""+n.key),e.type&&e.type.defaultProps&&(r=e.type.defaultProps),n)R.call(n,a)&&!D.hasOwnProperty(a)&&(void 0===n[a]&&void 0!==r?i[a]=r[a]:i[a]=n[a]);var d=arguments.length-2;if(1===d)i.children=o;else if(d>1){for(var p=Array(d),f=0;f<d;f++)p[f]=arguments[f+2];i.children=p}return B(e.type,s,l,c,u,h,i)}.apply(this,arguments),r=2;r<arguments.length;r++)ce(arguments[r],a.type);return ue(a),a},createFactory:function(e){var n=he.bind(null,e);return n.type=e,Object.defineProperty(n,"type",{enumerable:!1,get:function(){return y(!1,"Factory.type is deprecated. Access the class directly before passing it to createFactory."),Object.defineProperty(this,"type",{value:e}),e}}),n},isValidElement:j,version:"16.2.0",__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentOwner:O,assign:t}};t(de.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,{ReactDebugCurrentFrame:_,ReactComponentTreeHook:{}});var pe=Object.freeze({default:de}),fe=pe&&de||pe,me=fe.default?fe.default:fe;n.exports=me}()}).call(this,e("_process"))},{_process:48,"fbjs/lib/emptyFunction":12,"fbjs/lib/emptyObject":13,"fbjs/lib/invariant":18,"fbjs/lib/warning":22,"object-assign":43,"prop-types/checkPropTypes":49}],89:[function(e,n,t){"use strict";var o=e("object-assign"),a=e("fbjs/lib/emptyObject"),r=e("fbjs/lib/emptyFunction"),i="function"==typeof Symbol&&Symbol.for,s=i?Symbol.for("react.element"):60103,l=i?Symbol.for("react.call"):60104,c=i?Symbol.for("react.return"):60105,u=i?Symbol.for("react.portal"):60106,h=i?Symbol.for("react.fragment"):60107,d="function"==typeof Symbol&&Symbol.iterator;function p(e){for(var n=arguments.length-1,t="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,o=0;o<n;o++)t+="&args[]="+encodeURIComponent(arguments[o+1]);throw(n=Error(t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings.")).name="Invariant Violation",n.framesToPop=1,n}var f={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}};function m(e,n,t){this.props=e,this.context=n,this.refs=a,this.updater=t||f}function g(e,n,t){this.props=e,this.context=n,this.refs=a,this.updater=t||f}function y(){}m.prototype.isReactComponent={},m.prototype.setState=function(e,n){"object"!=typeof e&&"function"!=typeof e&&null!=e&&p("85"),this.updater.enqueueSetState(this,e,n,"setState")},m.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},y.prototype=m.prototype;var b=g.prototype=new y;function w(e,n,t){this.props=e,this.context=n,this.refs=a,this.updater=t||f}b.constructor=g,o(b,m.prototype),b.isPureReactComponent=!0;var v=w.prototype=new y;v.constructor=w,o(v,m.prototype),v.unstable_isAsyncReactComponent=!0,v.render=function(){return this.props.children};var k={current:null},T=Object.prototype.hasOwnProperty,I={key:!0,ref:!0,__self:!0,__source:!0};function E(e,n,t){var o,a={},r=null,i=null;if(null!=n)for(o in void 0!==n.ref&&(i=n.ref),void 0!==n.key&&(r=""+n.key),n)T.call(n,o)&&!I.hasOwnProperty(o)&&(a[o]=n[o]);var l=arguments.length-2;if(1===l)a.children=t;else if(1<l){for(var c=Array(l),u=0;u<l;u++)c[u]=arguments[u+2];a.children=c}if(e&&e.defaultProps)for(o in l=e.defaultProps)void 0===a[o]&&(a[o]=l[o]);return{$$typeof:s,type:e,key:r,ref:i,props:a,_owner:k.current}}function x(e){return"object"==typeof e&&null!==e&&e.$$typeof===s}var S=/\/+/g,L=[];function N(e,n,t,o){if(L.length){var a=L.pop();return a.result=e,a.keyPrefix=n,a.func=t,a.context=o,a.count=0,a}return{result:e,keyPrefix:n,func:t,context:o,count:0}}function C(e){e.result=null,e.keyPrefix=null,e.func=null,e.context=null,e.count=0,10>L.length&&L.push(e)}function A(e,n,t,o){var a=typeof e;"undefined"!==a&&"boolean"!==a||(e=null);var r=!1;if(null===e)r=!0;else switch(a){case"string":case"number":r=!0;break;case"object":switch(e.$$typeof){case s:case l:case c:case u:r=!0}}if(r)return t(o,e,""===n?"."+P(e,0):n),1;if(r=0,n=""===n?".":n+":",Array.isArray(e))for(var i=0;i<e.length;i++){var h=n+P(a=e[i],i);r+=A(a,h,t,o)}else if(null===e||void 0===e?h=null:h="function"==typeof(h=d&&e[d]||e["@@iterator"])?h:null,"function"==typeof h)for(e=h.call(e),i=0;!(a=e.next()).done;)r+=A(a=a.value,h=n+P(a,i++),t,o);else"object"===a&&p("31","[object Object]"===(t=""+e)?"object with keys {"+Object.keys(e).join(", ")+"}":t,"");return r}function P(e,n){return"object"==typeof e&&null!==e&&null!=e.key?(t=e.key,o={"=":"=0",":":"=2"},"$"+(""+t).replace(/[=:]/g,function(e){return o[e]})):n.toString(36);var t,o}function O(e,n){e.func.call(e.context,n,e.count++)}function R(e,n,t){var o=e.result,a=e.keyPrefix;e=e.func.call(e.context,n,e.count++),Array.isArray(e)?D(e,o,t,r.thatReturnsArgument):null!=e&&(x(e)&&(n=a+(!e.key||n&&n.key===e.key?"":(""+e.key).replace(S,"$&/")+"/")+t,e={$$typeof:s,type:e.type,key:n,ref:e.ref,props:e.props,_owner:e._owner}),o.push(e))}function D(e,n,t,o,a){var r="";null!=t&&(r=(""+t).replace(S,"$&/")+"/"),n=N(n,r,o,a),null==e||A(e,"",R,n),C(n)}var M={Children:{map:function(e,n,t){if(null==e)return e;var o=[];return D(e,o,null,n,t),o},forEach:function(e,n,t){if(null==e)return e;n=N(null,null,n,t),null==e||A(e,"",O,n),C(n)},count:function(e){return null==e?0:A(e,"",r.thatReturnsNull,null)},toArray:function(e){var n=[];return D(e,n,null,r.thatReturnsArgument),n},only:function(e){return x(e)||p("143"),e}},Component:m,PureComponent:g,unstable_AsyncComponent:w,Fragment:h,createElement:E,cloneElement:function(e,n,t){var a=o({},e.props),r=e.key,i=e.ref,l=e._owner;if(null!=n){if(void 0!==n.ref&&(i=n.ref,l=k.current),void 0!==n.key&&(r=""+n.key),e.type&&e.type.defaultProps)var c=e.type.defaultProps;for(u in n)T.call(n,u)&&!I.hasOwnProperty(u)&&(a[u]=void 0===n[u]&&void 0!==c?c[u]:n[u])}var u=arguments.length-2;if(1===u)a.children=t;else if(1<u){c=Array(u);for(var h=0;h<u;h++)c[h]=arguments[h+2];a.children=c}return{$$typeof:s,type:e.type,key:r,ref:i,props:a,_owner:l}},createFactory:function(e){var n=E.bind(null,e);return n.type=e,n},isValidElement:x,version:"16.2.0",__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:{ReactCurrentOwner:k,assign:o}},F=Object.freeze({default:M}),B=F&&M||F;n.exports=B.default?B.default:B},{"fbjs/lib/emptyFunction":12,"fbjs/lib/emptyObject":13,"object-assign":43}],90:[function(e,n,t){(function(t){"use strict";"production"===t.env.NODE_ENV?n.exports=e("./cjs/react.production.min.js"):n.exports=e("./cjs/react.development.js")}).call(this,e("_process"))},{"./cjs/react.development.js":88,"./cjs/react.production.min.js":89,_process:48}],91:[function(e,n,t){"use strict";var o=e("unherit"),a=e("xtend"),r=e("./lib/parser.js");function i(e){var n=o(r);n.prototype.options=a(n.prototype.options,this.data("settings"),e),this.Parser=n}n.exports=i,i.Parser=r},{"./lib/parser.js":105,unherit:146,xtend:160}],92:[function(e,n,t){n.exports=["address","article","aside","base","basefont","blockquote","body","caption","center","col","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","iframe","legend","li","link","main","menu","menuitem","meta","nav","noframes","ol","optgroup","option","p","param","pre","section","source","title","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"]},{}],93:[function(e,n,t){"use strict";var o=e("parse-entities");n.exports=function(e){return a.raw=function(e,a){return o(e,{position:n(a),warning:t})},a;function n(n){for(var t=e.offset,o=n.line,a=[];++o&&o in t;)a.push((t[o]||0)+1);return{start:n,indent:a}}function t(n,t,o){3!==o&&e.file.message(n,t)}function a(a,r,i){o(a,{position:n(r),warning:t,text:i,reference:i,textContext:e,referenceContext:e})}}},{"parse-entities":44}],94:[function(e,n,t){"use strict";n.exports={position:!0,gfm:!0,commonmark:!1,footnotes:!1,pedantic:!1,blocks:e("./block-elements.json")}},{"./block-elements.json":92}],95:[function(e,n,t){"use strict";n.exports=function(e,n){var t=e.indexOf("\n",n);for(;t>n&&" "===e.charAt(t-1);)t--;return t}},{}],96:[function(e,n,t){"use strict";n.exports=function(e,n){return e.indexOf("`",n)}},{}],97:[function(e,n,t){"use strict";n.exports=function(e,n){return e.indexOf("~~",n)}},{}],98:[function(e,n,t){"use strict";n.exports=function(e,n){var t=e.indexOf("*",n),o=e.indexOf("_",n);if(-1===o)return t;if(-1===t)return o;return o<t?o:t}},{}],99:[function(e,n,t){"use strict";n.exports=function(e,n){return e.indexOf("\\",n)}},{}],100:[function(e,n,t){"use strict";n.exports=function(e,n){var t=e.indexOf("[",n),o=e.indexOf("![",n);if(-1===o)return t;return t<o?t:o}},{}],101:[function(e,n,t){"use strict";n.exports=function(e,n){var t=e.indexOf("**",n),o=e.indexOf("__",n);if(-1===o)return t;if(-1===t)return o;return o<t?o:t}},{}],102:[function(e,n,t){"use strict";n.exports=function(e,n){return e.indexOf("<",n)}},{}],103:[function(e,n,t){"use strict";n.exports=function(e,n){var t,a=o.length,r=-1,i=-1;if(!this.options.gfm)return-1;for(;++r<a;)-1!==(t=e.indexOf(o[r],n))&&(t<i||-1===i)&&(i=t);return i};var o=["https://","http://","mailto:"]},{}],104:[function(e,n,t){"use strict";var o=e("xtend"),a=e("unist-util-remove-position");n.exports=function(){var e,n=String(this.file),t={line:1,column:1,offset:0},s=o(t);65279===(n=n.replace(i,r)).charCodeAt(0)&&(n=n.slice(1),s.column++,s.offset++);e={type:"root",children:this.tokenizeBlock(n,s),position:{start:t,end:this.eof||o(t)}},this.options.position||a(e,!0);return e};var r="\n",i=/\r\n|\r/g},{"unist-util-remove-position":149,xtend:160}],105:[function(e,n,t){"use strict";var o=e("xtend"),a=e("state-toggle"),r=e("vfile-location"),i=e("./unescape"),s=e("./decode"),l=e("./tokenizer");function c(e,n){this.file=n,this.offset={},this.options=o(this.options),this.setOptions({}),this.inList=!1,this.inBlock=!1,this.inLink=!1,this.atStart=!0,this.toOffset=r(n).toOffset,this.unescape=i(this,"escape"),this.decode=s(this)}n.exports=c;var u=c.prototype;function h(e){var n,t=[];for(n in e)t.push(n);return t}u.setOptions=e("./set-options"),u.parse=e("./parse"),u.options=e("./defaults"),u.exitStart=a("atStart",!0),u.enterList=a("inList",!1),u.enterLink=a("inLink",!1),u.enterBlock=a("inBlock",!1),u.interruptParagraph=[["thematicBreak"],["atxHeading"],["fencedCode"],["blockquote"],["html"],["setextHeading",{commonmark:!1}],["definition",{commonmark:!1}],["footnote",{commonmark:!1}]],u.interruptList=[["fencedCode",{pedantic:!1}],["thematicBreak",{pedantic:!1}],["definition",{commonmark:!1}],["footnote",{commonmark:!1}]],u.interruptBlockquote=[["indentedCode",{commonmark:!0}],["fencedCode",{commonmark:!0}],["atxHeading",{commonmark:!0}],["setextHeading",{commonmark:!0}],["thematicBreak",{commonmark:!0}],["html",{commonmark:!0}],["list",{commonmark:!0}],["definition",{commonmark:!1}],["footnote",{commonmark:!1}]],u.blockTokenizers={newline:e("./tokenize/newline"),indentedCode:e("./tokenize/code-indented"),fencedCode:e("./tokenize/code-fenced"),blockquote:e("./tokenize/blockquote"),atxHeading:e("./tokenize/heading-atx"),thematicBreak:e("./tokenize/thematic-break"),list:e("./tokenize/list"),setextHeading:e("./tokenize/heading-setext"),html:e("./tokenize/html-block"),footnote:e("./tokenize/footnote-definition"),definition:e("./tokenize/definition"),table:e("./tokenize/table"),paragraph:e("./tokenize/paragraph")},u.inlineTokenizers={escape:e("./tokenize/escape"),autoLink:e("./tokenize/auto-link"),url:e("./tokenize/url"),html:e("./tokenize/html-inline"),link:e("./tokenize/link"),reference:e("./tokenize/reference"),strong:e("./tokenize/strong"),emphasis:e("./tokenize/emphasis"),deletion:e("./tokenize/delete"),code:e("./tokenize/code-inline"),break:e("./tokenize/break"),text:e("./tokenize/text")},u.blockMethods=h(u.blockTokenizers),u.inlineMethods=h(u.inlineTokenizers),u.tokenizeBlock=l("block"),u.tokenizeInline=l("inline"),u.tokenizeFactory=l},{"./decode":93,"./defaults":94,"./parse":104,"./set-options":106,"./tokenize/auto-link":107,"./tokenize/blockquote":108,"./tokenize/break":109,"./tokenize/code-fenced":110,"./tokenize/code-indented":111,"./tokenize/code-inline":112,"./tokenize/definition":113,"./tokenize/delete":114,"./tokenize/emphasis":115,"./tokenize/escape":116,"./tokenize/footnote-definition":117,"./tokenize/heading-atx":118,"./tokenize/heading-setext":119,"./tokenize/html-block":120,"./tokenize/html-inline":121,"./tokenize/link":122,"./tokenize/list":123,"./tokenize/newline":124,"./tokenize/paragraph":125,"./tokenize/reference":126,"./tokenize/strong":127,"./tokenize/table":128,"./tokenize/text":129,"./tokenize/thematic-break":130,"./tokenize/url":131,"./tokenizer":132,"./unescape":133,"state-toggle":142,"vfile-location":153,xtend:160}],106:[function(e,n,t){"use strict";var o=e("xtend"),a=e("markdown-escapes"),r=e("./defaults");n.exports=function(e){var n,t,i=this.options;if(null==e)e={};else{if("object"!=typeof e)throw new Error("Invalid value `"+e+"` for setting `options`");e=o(e)}for(n in r){if(null==(t=e[n])&&(t=i[n]),"blocks"!==n&&"boolean"!=typeof t||"blocks"===n&&"object"!=typeof t)throw new Error("Invalid value `"+t+"` for setting `options."+n+"`");e[n]=t}return this.options=e,this.escape=a(e),this}},{"./defaults":94,"markdown-escapes":42,xtend:160}],107:[function(e,n,t){"use strict";var o=e("is-whitespace-character"),a=e("parse-entities"),r=e("../locate/tag");n.exports=d,d.locator=r,d.notInLink=!0;var i="<",s=">",l="@",c="/",u="mailto:",h=u.length;function d(e,n,t){var r,d,p,f,m,g,y,b,w,v,k;if(n.charAt(0)===i){for(this,r="",d=n.length,p=0,f="",g=!1,y="",p++,r=i;p<d&&(m=n.charAt(p),!(o(m)||m===s||m===l||":"===m&&n.charAt(p+1)===c));)f+=m,p++;if(f){if(y+=f,f="",y+=m=n.charAt(p),p++,m===l)g=!0;else{if(":"!==m||n.charAt(p+1)!==c)return;y+=c,p++}for(;p<d&&(m=n.charAt(p),!o(m)&&m!==s);)f+=m,p++;if(m=n.charAt(p),f&&m===s)return!!t||(w=y+=f,r+=y+m,(b=e.now()).column++,b.offset++,g&&(y.slice(0,h).toLowerCase()===u?(w=w.substr(h),b.column+=h,b.offset+=h):y=u+y),v=this.inlineTokenizers.escape,this.inlineTokenizers.escape=null,k=this.enterLink(),w=this.tokenizeInline(w,b),this.inlineTokenizers.escape=v,k(),e(r)({type:"link",title:null,url:a(y),children:w}))}}}},{"../locate/tag":102,"is-whitespace-character":40,"parse-entities":44}],108:[function(e,n,t){"use strict";var o=e("trim"),a=e("../util/interrupt");n.exports=function(e,n,t){var c,u,h,d,p,f,m,g,y,b=this.offset,w=this.blockTokenizers,v=this.interruptBlockquote,k=e.now(),T=k.line,I=n.length,E=[],x=[],S=[],L=0;for(;L<I&&((u=n.charAt(L))===s||u===i);)L++;if(n.charAt(L)!==l)return;if(t)return!0;L=0;for(;L<I;){for(d=n.indexOf(r,L),m=L,g=!1,-1===d&&(d=I);L<I&&((u=n.charAt(L))===s||u===i);)L++;if(n.charAt(L)===l?(L++,g=!0,n.charAt(L)===s&&L++):L=m,p=n.slice(L,d),!g&&!o(p)){L=m;break}if(!g&&(h=n.slice(L),a(v,w,this,[e,h,!0])))break;f=m===L?p:n.slice(m,d),S.push(L-m),E.push(f),x.push(p),L=d+1}L=-1,I=S.length,c=e(E.join(r));for(;++L<I;)b[T]=(b[T]||0)+S[L],T++;return y=this.enterBlock(),x=this.tokenizeBlock(x.join(r),k),y(),c({type:"blockquote",children:x})};var r="\n",i="\t",s=" ",l=">"},{"../util/interrupt":136,trim:144}],109:[function(e,n,t){"use strict";var o=e("../locate/break");n.exports=r,r.locator=o;var a=2;function r(e,n,t){for(var o,r=n.length,i=-1,s="";++i<r;){if("\n"===(o=n.charAt(i))){if(i<a)return;return!!t||e(s+=o)({type:"break"})}if(" "!==o)return;s+=o}}},{"../locate/break":95}],110:[function(e,n,t){"use strict";var o=e("trim-trailing-lines");n.exports=function(e,n,t){var h,d,p,f,m,g,y,b,w,v,k,T=this.options,I=n.length+1,E=0,x="";if(!T.gfm)return;for(;E<I&&((p=n.charAt(E))===i||p===r);)x+=p,E++;if(v=E,(p=n.charAt(E))!==s&&p!==l)return;E++,d=p,h=1,x+=p;for(;E<I&&(p=n.charAt(E))===d;)x+=p,h++,E++;if(h<c)return;for(;E<I&&((p=n.charAt(E))===i||p===r);)x+=p,E++;f="",m="";for(;E<I&&(p=n.charAt(E))!==a&&p!==s&&p!==l;)p===i||p===r?m+=p:(f+=m+p,m=""),E++;if((p=n.charAt(E))&&p!==a)return;if(t)return!0;(k=e.now()).column+=x.length,k.offset+=x.length,x+=f,f=this.decode.raw(this.unescape(f),k),m&&(x+=m);m="",b="",w="",g="",y="";for(;E<I;)if(p=n.charAt(E),g+=b,y+=w,b="",w="",p===a){for(g?(b+=p,w+=p):x+=p,m="",E++;E<I&&(p=n.charAt(E))===i;)m+=p,E++;if(b+=m,w+=m.slice(v),!(m.length>=u)){for(m="";E<I&&(p=n.charAt(E))===d;)m+=p,E++;if(b+=m,w+=m,!(m.length<h)){for(m="";E<I&&((p=n.charAt(E))===i||p===r);)b+=p,w+=p,E++;if(!p||p===a)break}}}else g+=p,w+=p,E++;return e(x+=g+b)({type:"code",lang:f||null,value:o(y)})};var a="\n",r="\t",i=" ",s="~",l="`",c=3,u=4},{"trim-trailing-lines":143}],111:[function(e,n,t){"use strict";var o=e("repeat-string"),a=e("trim-trailing-lines");n.exports=function(e,n,t){var o,c,u,h=-1,d=n.length,p="",f="",m="",g="";for(;++h<d;)if(o=n.charAt(h),u)if(u=!1,p+=m,f+=g,m="",g="",o===r)m=o,g=o;else for(p+=o,f+=o;++h<d;){if(!(o=n.charAt(h))||o===r){g=o,m=o;break}p+=o,f+=o}else if(o===s&&n.charAt(h+1)===o&&n.charAt(h+2)===o&&n.charAt(h+3)===o)m+=l,h+=3,u=!0;else if(o===i)m+=o,u=!0;else{for(c="";o===i||o===s;)c+=o,o=n.charAt(++h);if(o!==r)break;m+=c+o,g+=o}if(f)return!!t||e(p)({type:"code",lang:null,value:a(f)})};var r="\n",i="\t",s=" ",l=o(s,4)},{"repeat-string":139,"trim-trailing-lines":143}],112:[function(e,n,t){"use strict";var o=e("is-whitespace-character"),a=e("../locate/code-inline");n.exports=i,i.locator=a;var r="`";function i(e,n,t){for(var a,i,s,l,c,u,h,d,p=n.length,f=0,m="",g="";f<p&&n.charAt(f)===r;)m+=r,f++;if(m){for(c=m,l=f,m="",d=n.charAt(f),s=0;f<p;){if(u=d,d=n.charAt(f+1),u===r?(s++,g+=u):(s=0,m+=u),s&&d!==r){if(s===l){c+=m+g,h=!0;break}m+=g,g=""}f++}if(!h){if(l%2!=0)return;m=""}if(t)return!0;for(a="",i="",p=m.length,f=-1;++f<p;)u=m.charAt(f),o(u)?i+=u:(i&&(a&&(a+=i),i=""),a+=u);return e(c)({type:"inlineCode",value:a})}}},{"../locate/code-inline":96,"is-whitespace-character":40}],113:[function(e,n,t){"use strict";var o=e("is-whitespace-character"),a=e("../util/normalize");n.exports=b,b.notInList=!0,b.notInBlock=!0;var r='"',i="'",s="\\",l="\n",c="\t",u=" ",h="[",d="]",p="(",f=")",m=":",g="<",y=">";function b(e,n,t){for(var y,b,v,k,T,I,E,x,S=this.options.commonmark,L=0,N=n.length,C="";L<N&&((k=n.charAt(L))===u||k===c);)C+=k,L++;if((k=n.charAt(L))===h){for(L++,C+=k,v="";L<N&&(k=n.charAt(L))!==d;)k===s&&(v+=k,L++,k=n.charAt(L)),v+=k,L++;if(v&&n.charAt(L)===d&&n.charAt(L+1)===m){for(I=v,L=(C+=v+d+m).length,v="";L<N&&((k=n.charAt(L))===c||k===u||k===l);)C+=k,L++;if(v="",y=C,(k=n.charAt(L))===g){for(L++;L<N&&w(k=n.charAt(L));)v+=k,L++;if((k=n.charAt(L))===w.delimiter)C+=g+v+k,L++;else{if(S)return;L-=v.length+1,v=""}}if(!v){for(;L<N&&(k=n.charAt(L),(A=k)!==h&&A!==d&&!o(A));)v+=k,L++;C+=v}var A;if(v){for(E=v,v="";L<N&&((k=n.charAt(L))===c||k===u||k===l);)v+=k,L++;if(T=null,(k=n.charAt(L))===r?T=r:k===i?T=i:k===p&&(T=f),T){if(!v)return;for(L=(C+=v+k).length,v="";L<N&&(k=n.charAt(L))!==T;){if(k===l){if(L++,(k=n.charAt(L))===l||k===T)return;v+=l}v+=k,L++}if((k=n.charAt(L))!==T)return;b=C,C+=v+k,L++,x=v,v=""}else v="",L=C.length;for(;L<N&&((k=n.charAt(L))===c||k===u);)C+=k,L++;return(k=n.charAt(L))&&k!==l?void 0:!!t||(y=e(y).test().end,E=this.decode.raw(this.unescape(E),y),x&&(b=e(b).test().end,x=this.decode.raw(this.unescape(x),b)),e(C)({type:"definition",identifier:a(I),title:x||null,url:E}))}}}}function w(e){return e!==y&&e!==h&&e!==d}w.delimiter=y},{"../util/normalize":137,"is-whitespace-character":40}],114:[function(e,n,t){"use strict";var o=e("is-whitespace-character"),a=e("../locate/delete");n.exports=s,s.locator=a;var r="~",i="~~";function s(e,n,t){var a,s,l,c="",u="",h="",d="";if(this.options.gfm&&n.charAt(0)===r&&n.charAt(1)===r&&!o(n.charAt(2)))for(a=1,s=n.length,(l=e.now()).column+=2,l.offset+=2;++a<s;){if(!((c=n.charAt(a))!==r||u!==r||h&&o(h)))return!!t||e(i+d+i)({type:"delete",children:this.tokenizeInline(d,l)});d+=u,h=u,u=c}}},{"../locate/delete":97,"is-whitespace-character":40}],115:[function(e,n,t){"use strict";var o=e("trim"),a=e("is-word-character"),r=e("is-whitespace-character"),i=e("../locate/emphasis");n.exports=c,c.locator=i;var s="*",l="_";function c(e,n,t){var i,c,u,h,d,p,f,m=0,g=n.charAt(m);if(!(g!==s&&g!==l||(c=this.options.pedantic,d=g,u=g,p=n.length,m++,h="",g="",c&&r(n.charAt(m)))))for(;m<p;){if(f=g,!((g=n.charAt(m))!==u||c&&r(f))){if((g=n.charAt(++m))!==u){if(!o(h)||f===u)return;if(!c&&u===l&&a(g)){h+=u;continue}return!!t||((i=e.now()).column++,i.offset++,e(d+h+u)({type:"emphasis",children:this.tokenizeInline(h,i)}))}h+=u}c||"\\"!==g||(h+=g,g=n.charAt(++m)),h+=g,m++}}},{"../locate/emphasis":98,"is-whitespace-character":40,"is-word-character":41,trim:144}],116:[function(e,n,t){"use strict";var o=e("../locate/escape");function a(e,n,t){var o,a;if("\\"===n.charAt(0)&&(o=n.charAt(1),-1!==this.escape.indexOf(o)))return!!t||(a="\n"===o?{type:"break"}:{type:"text",value:o},e("\\"+o)(a))}n.exports=a,a.locator=o},{"../locate/escape":99}],117:[function(e,n,t){"use strict";var o=e("is-whitespace-character"),a=e("../util/normalize");n.exports=f,f.notInList=!0,f.notInBlock=!0;var r="\\",i="\n",s="\t",l=" ",c="[",u="]",h="^",d=":",p=/^( {4}|\t)?/gm;function f(e,n,t){var f,m,g,y,b,w,v,k,T,I,E,x,S=this.offset;if(this.options.footnotes){for(f=0,m=n.length,g="",y=e.now(),b=y.line;f<m&&(T=n.charAt(f),o(T));)g+=T,f++;if(n.charAt(f)===c&&n.charAt(f+1)===h){for(f=(g+=c+h).length,v="";f<m&&(T=n.charAt(f))!==u;)T===r&&(v+=T,f++,T=n.charAt(f)),v+=T,f++;if(v&&n.charAt(f)===u&&n.charAt(f+1)===d){if(t)return!0;for(I=a(v),f=(g+=v+u+d).length;f<m&&((T=n.charAt(f))===s||T===l);)g+=T,f++;for(y.column+=g.length,y.offset+=g.length,v="",w="",k="";f<m;){if((T=n.charAt(f))===i){for(k=T,f++;f<m&&(T=n.charAt(f))===i;)k+=T,f++;for(v+=k,k="";f<m&&(T=n.charAt(f))===l;)k+=T,f++;if(0===k.length)break;v+=k}v&&(w+=v,v=""),w+=T,f++}return g+=w,w=w.replace(p,function(e){return S[b]=(S[b]||0)+e.length,b++,""}),E=e(g),x=this.enterBlock(),w=this.tokenizeBlock(w,y),x(),E({type:"footnoteDefinition",identifier:I,children:w})}}}}},{"../util/normalize":137,"is-whitespace-character":40}],118:[function(e,n,t){"use strict";n.exports=function(e,n,t){var l,c,u,h=this.options,d=n.length+1,p=-1,f=e.now(),m="",g="";for(;++p<d;){if((l=n.charAt(p))!==r&&l!==a){p--;break}m+=l}u=0;for(;++p<=d;){if((l=n.charAt(p))!==i){p--;break}m+=l,u++}if(u>s)return;if(!u||!h.pedantic&&n.charAt(p+1)===i)return;d=n.length+1,c="";for(;++p<d;){if((l=n.charAt(p))!==r&&l!==a){p--;break}c+=l}if(!h.pedantic&&0===c.length&&l&&l!==o)return;if(t)return!0;m+=c,c="",g="";for(;++p<d&&(l=n.charAt(p))&&l!==o;)if(l===r||l===a||l===i){for(;l===r||l===a;)c+=l,l=n.charAt(++p);for(;l===i;)c+=l,l=n.charAt(++p);for(;l===r||l===a;)c+=l,l=n.charAt(++p);p--}else g+=c+l,c="";return f.column+=m.length,f.offset+=m.length,e(m+=g+c)({type:"heading",depth:u,children:this.tokenizeInline(g,f)})};var o="\n",a="\t",r=" ",i="#",s=6},{}],119:[function(e,n,t){"use strict";n.exports=function(e,n,t){var l,c,u,h,d,p=e.now(),f=n.length,m=-1,g="";for(;++m<f;){if((u=n.charAt(m))!==r||m>=i){m--;break}g+=u}l="",c="";for(;++m<f;){if((u=n.charAt(m))===o){m--;break}u===r||u===a?c+=u:(l+=c+u,c="")}if(p.column+=g.length,p.offset+=g.length,g+=l+c,u=n.charAt(++m),h=n.charAt(++m),u!==o||!s[h])return;g+=u,c=h,d=s[h];for(;++m<f;){if((u=n.charAt(m))!==h){if(u!==o)return;m--;break}c+=u}if(t)return!0;return e(g+c)({type:"heading",depth:d,children:this.tokenizeInline(l,p)})};var o="\n",a="\t",r=" ",i=3,s={};s["="]=1,s["-"]=2},{}],120:[function(e,n,t){"use strict";var o=e("../util/html").openCloseTag;n.exports=function(e,n,t){var l,c,u,h,d,p,f,m=this.options.blocks,g=n.length,y=0,b=[[/^<(script|pre|style)(?=(\s|>|$))/i,/<\/(script|pre|style)>/i,!0],[/^<!--/,/-->/,!0],[/^<\?/,/\?>/,!0],[/^<![A-Za-z]/,/>/,!0],[/^<!\[CDATA\[/,/\]\]>/,!0],[new RegExp("^</?("+m.join("|")+")(?=(\\s|/?>|$))","i"),/^$/,!0],[new RegExp(o.source+"\\s*$"),/^$/,!1]];for(;y<g&&((h=n.charAt(y))===a||h===r);)y++;if(n.charAt(y)!==s)return;l=-1===(l=n.indexOf(i,y+1))?g:l,c=n.slice(y,l),u=-1,d=b.length;for(;++u<d;)if(b[u][0].test(c)){p=b[u];break}if(!p)return;if(t)return p[2];if(y=l,!p[1].test(c))for(;y<g;){if(l=-1===(l=n.indexOf(i,y+1))?g:l,c=n.slice(y+1,l),p[1].test(c)){c&&(y=l);break}y=l}return f=n.slice(0,y),e(f)({type:"html",value:f})};var a="\t",r=" ",i="\n",s="<"},{"../util/html":135}],121:[function(e,n,t){"use strict";var o=e("is-alphabetical"),a=e("../locate/tag"),r=e("../util/html").tag;n.exports=l,l.locator=a;var i=/^<a /i,s=/^<\/a>/i;function l(e,n,t){var a,l,c=n.length;if(!("<"!==n.charAt(0)||c<3)&&(a=n.charAt(1),(o(a)||"?"===a||"!"===a||"/"===a)&&(l=n.match(r))))return!!t||(l=l[0],!this.inLink&&i.test(l)?this.inLink=!0:this.inLink&&s.test(l)&&(this.inLink=!1),e(l)({type:"html",value:l}))}},{"../locate/tag":102,"../util/html":135,"is-alphabetical":34}],122:[function(e,n,t){"use strict";var o=e("is-whitespace-character"),a=e("../locate/link");n.exports=g,g.locator=a;var r={}.hasOwnProperty,i="\\",s="[",l="]",c="(",u=")",h="<",d=">",p="`",f={'"':'"',"'":"'"},m={};function g(e,n,t){var a,g,y,b,w,v,k,T,I,E,x,S,L,N,C,A,P,O,R,D=this,M="",F=0,B=n.charAt(0),W=D.options.pedantic,j=D.options.commonmark,_=D.options.gfm;if("!"===B&&(I=!0,M=B,B=n.charAt(++F)),B===s&&(I||!D.inLink)){for(M+=B,C="",F++,S=n.length,N=0,(P=e.now()).column+=F,P.offset+=F;F<S;){if(v=B=n.charAt(F),B===p){for(g=1;n.charAt(F+1)===p;)v+=B,F++,g++;y?g>=y&&(y=0):y=g}else if(B===i)F++,v+=n.charAt(F);else if(y&&!_||B!==s){if((!y||_)&&B===l){if(!N){if(!W)for(;F<S&&(B=n.charAt(F+1),o(B));)v+=B,F++;if(n.charAt(F+1)!==c)return;v+=c,a=!0,F++;break}N--}}else N++;C+=v,v="",F++}if(a){for(E=C,M+=C+v,F++;F<S&&(B=n.charAt(F),o(B));)M+=B,F++;if(B=n.charAt(F),T=j?m:f,C="",b=M,B===h){for(F++,b+=h;F<S&&(B=n.charAt(F))!==d;){if(j&&"\n"===B)return;C+=B,F++}if(n.charAt(F)!==d)return;M+=h+C+d,A=C,F++}else{for(B=null,v="";F<S&&(B=n.charAt(F),!v||!r.call(T,B));){if(o(B)){if(!W)break;v+=B}else{if(B===c)N++;else if(B===u){if(0===N)break;N--}C+=v,v="",B===i&&(C+=i,B=n.charAt(++F)),C+=B}F++}A=C,F=(M+=C).length}for(C="";F<S&&(B=n.charAt(F),o(B));)C+=B,F++;if(B=n.charAt(F),M+=C,C&&r.call(T,B))if(F++,M+=B,C="",x=T[B],w=M,j){for(;F<S&&(B=n.charAt(F))!==x;)B===i&&(C+=i,B=n.charAt(++F)),F++,C+=B;if((B=n.charAt(F))!==x)return;for(L=C,M+=C+B,F++;F<S&&(B=n.charAt(F),o(B));)M+=B,F++}else for(v="";F<S;){if((B=n.charAt(F))===x)k&&(C+=x+v,v=""),k=!0;else if(k){if(B===u){M+=C+x+v,L=C;break}o(B)?v+=B:(C+=x+v+B,v="",k=!1)}else C+=B;F++}if(n.charAt(F)===u)return!!t||(M+=u,A=D.decode.raw(D.unescape(A),e(b).test().end),L&&(w=e(w).test().end,L=D.decode.raw(D.unescape(L),w)),R={type:I?"image":"link",title:L||null,url:A},I?R.alt=D.decode.raw(D.unescape(E),P)||null:(O=D.enterLink(),R.children=D.tokenizeInline(E,P),O()),e(M)(R))}}}m['"']='"',m["'"]="'",m[c]=u},{"../locate/link":100,"is-whitespace-character":40}],123:[function(e,n,t){"use strict";var o=e("trim"),a=e("repeat-string"),r=e("is-decimal"),i=e("../util/get-indentation"),s=e("../util/remove-indentation"),l=e("../util/interrupt");n.exports=function(e,n,t){var a,i,s,m,y,b,w,v,k,S,L,N,C,A,P,O,R,D,M,F,B,W,j,_,U=this.options.commonmark,z=this.options.pedantic,G=this.blockTokenizers,V=this.interruptList,H=0,q=n.length,K=null,Y=0;for(;H<q;){if((m=n.charAt(H))===f)Y+=g-Y%g;else{if(m!==d)break;Y++}H++}if(Y>=g)return;if(m=n.charAt(H),a=U?E:I,!0===T[m])y=m,s=!1;else{for(s=!0,i="";H<q&&(m=n.charAt(H),r(m));)i+=m,H++;if(m=n.charAt(H),!i||!0!==a[m])return;K=parseInt(i,10),y=m}if((m=n.charAt(++H))!==d&&m!==f)return;if(t)return!0;H=0,A=[],P=[],O=[];for(;H<q;){for(b=n.indexOf(p,H),w=H,v=!1,_=!1,-1===b&&(b=q),j=H+g,Y=0;H<q;){if((m=n.charAt(H))===f)Y+=g-Y%g;else{if(m!==d)break;Y++}H++}if(Y>=g&&(_=!0),R&&Y>=R.indent&&(_=!0),m=n.charAt(H),k=null,!_){if(!0===T[m])k=m,H++,Y++;else{for(i="";H<q&&(m=n.charAt(H),r(m));)i+=m,H++;m=n.charAt(H),H++,i&&!0===a[m]&&(k=m,Y+=i.length+1)}if(k)if((m=n.charAt(H))===f)Y+=g-Y%g,H++;else if(m===d){for(j=H+g;H<j&&n.charAt(H)===d;)H++,Y++;H===j&&n.charAt(H)===d&&(H-=g-1,Y-=g-1)}else m!==p&&""!==m&&(k=null)}if(k){if(!z&&y!==k)break;v=!0}else U||_||n.charAt(w)!==d?U&&R&&(_=Y>=R.indent||Y>g):_=!0,v=!1,H=w;if(L=n.slice(w,b),S=w===H?L:n.slice(H,b),(k===c||k===u||k===h)&&G.thematicBreak.call(this,e,L,!0))break;if(N=C,C=!o(S).length,_&&R)R.value=R.value.concat(O,L),P=P.concat(O,L),O=[];else if(v)0!==O.length&&(R.value.push(""),R.trail=O.concat()),R={value:[L],indent:Y,trail:[]},A.push(R),P=P.concat(O,L),O=[];else if(C){if(N)break;O.push(L)}else{if(N)break;if(l(V,G,this,[e,L,!0]))break;R.value=R.value.concat(O,L),P=P.concat(O,L),O=[]}H=b+1}B=e(P.join(p)).reset({type:"list",ordered:s,start:K,loose:null,children:[]}),D=this.enterList(),M=this.enterBlock(),F=!1,H=-1,q=A.length;for(;++H<q;)R=A[H].value.join(p),W=e.now(),(R=e(R)(x(this,R,W),B)).loose&&(F=!0),R=A[H].trail.join(p),H!==q-1&&(R+=p),e(R);return D(),M(),B.loose=F,B};var c="*",u="_",h="-",d=" ",p="\n",f="\t",m="x",g=4,y=/\n\n(?!\s*$)/,b=/^\[([ \t]|x|X)][ \t]/,w=/^([ \t]*)([*+-]|\d+[.)])( {1,4}(?! )| |\t|$|(?=\n))([^\n]*)/,v=/^([ \t]*)([*+-]|\d+[.)])([ \t]+)/,k=/^( {1,4}|\t)?/gm,T={};T[c]=!0,T["+"]=!0,T[h]=!0;var I={".":!0},E={};function x(e,n,t){var o,a,r=e.offset,i=null;return n=(e.options.pedantic?S:L).apply(null,arguments),e.options.gfm&&(o=n.match(b))&&(a=o[0].length,i=o[1].toLowerCase()===m,r[t.line]+=a,n=n.slice(a)),{type:"listItem",loose:y.test(n)||n.charAt(n.length-1)===p,checked:i,children:e.tokenizeBlock(n,t)}}function S(e,n,t){var o=e.offset,a=t.line;return n=n.replace(v,r),a=t.line,n.replace(k,r);function r(e){return o[a]=(o[a]||0)+e.length,a++,""}}function L(e,n,t){var o,r,l,c,u,h,f,m=e.offset,g=t.line;for(c=(n=n.replace(w,function(e,n,t,i,s){r=n+t+i,l=s,Number(t)<10&&r.length%2==1&&(t=d+t);return(o=n+a(d,t.length)+i)+l})).split(p),(u=s(n,i(o).indent).split(p))[0]=l,m[g]=(m[g]||0)+r.length,g++,h=0,f=c.length;++h<f;)m[g]=(m[g]||0)+c[h].length-u[h].length,g++;return u.join(p)}E["."]=!0,E[")"]=!0},{"../util/get-indentation":134,"../util/interrupt":136,"../util/remove-indentation":138,"is-decimal":37,"repeat-string":139,trim:144}],124:[function(e,n,t){"use strict";var o=e("is-whitespace-character");n.exports=function(e,n,t){var a,r,i,s,l=n.charAt(0);if("\n"!==l)return;if(t)return!0;s=1,a=n.length,r=l,i="";for(;s<a&&(l=n.charAt(s),o(l));)i+=l,"\n"===l&&(r+=i,i=""),s++;e(r)}},{"is-whitespace-character":40}],125:[function(e,n,t){"use strict";var o=e("trim"),a=e("is-decimal"),r=e("trim-trailing-lines"),i=e("../util/interrupt");n.exports=function(e,n,t){var h,d,p,f,m,g=this.options,y=g.commonmark,b=g.gfm,w=this.blockTokenizers,v=this.interruptParagraph,k=n.indexOf(s),T=n.length;for(;k<T;){if(-1===k){k=T;break}if(n.charAt(k+1)===s)break;if(y){for(f=0,h=k+1;h<T;){if((p=n.charAt(h))===l){f=u;break}if(p!==c)break;f++,h++}if(f>=u){k=n.indexOf(s,k+1);continue}}if(d=n.slice(k+1),i(v,w,this,[e,d,!0]))break;if(w.list.call(this,e,d,!0)&&(this.inList||y||b&&!a(o.left(d).charAt(0))))break;if(h=k,-1!==(k=n.indexOf(s,k+1))&&""===o(n.slice(h,k))){k=h;break}}if(d=n.slice(0,k),""===o(d))return e(d),null;if(t)return!0;return m=e.now(),d=r(d),e(d)({type:"paragraph",children:this.tokenizeInline(d,m)})};var s="\n",l="\t",c=" ",u=4},{"../util/interrupt":136,"is-decimal":37,trim:144,"trim-trailing-lines":143}],126:[function(e,n,t){"use strict";var o=e("is-whitespace-character"),a=e("../locate/link"),r=e("../util/normalize");n.exports=g,g.locator=a;var i="link",s="image",l="footnote",c="shortcut",u="collapsed",h="full",d="^",p="\\",f="[",m="]";function g(e,n,t){var a,g,y,b,w,v,k,T,I=n.charAt(0),E=0,x=n.length,S="",L="",N=i,C=c;if("!"===I&&(N=s,L=I,I=n.charAt(++E)),I===f){for(E++,L+=I,v="",this.options.footnotes&&N===i&&n.charAt(E)===d&&(L+=d,E++,N=l),T=0;E<x;){if((I=n.charAt(E))===f)k=!0,T++;else if(I===m){if(!T)break;T--}I===p&&(v+=p,I=n.charAt(++E)),v+=I,E++}if(S=v,a=v,(I=n.charAt(E))===m){for(E++,S+=I,v="";E<x&&(I=n.charAt(E),o(I));)v+=I,E++;if(I=n.charAt(E),N!==l&&I===f){for(g="",v+=I,E++;E<x&&(I=n.charAt(E))!==f&&I!==m;)I===p&&(g+=p,I=n.charAt(++E)),g+=I,E++;(I=n.charAt(E))===m?(C=g?h:u,v+=g+I,E++):g="",S+=v,v=""}else{if(!a)return;g=a}if(C===h||!k)return S=L+S,N===i&&this.inLink?null:!!t||(N===l&&-1!==a.indexOf(" ")?e(S)({type:"footnote",children:this.tokenizeInline(a,e.now())}):((y=e.now()).column+=L.length,y.offset+=L.length,b={type:N+"Reference",identifier:r(g=C===h?g:a)},N!==i&&N!==s||(b.referenceType=C),N===i?(w=this.enterLink(),b.children=this.tokenizeInline(a,y),w()):N===s&&(b.alt=this.decode.raw(this.unescape(a),y)||null),e(S)(b)))}}}},{"../locate/link":100,"../util/normalize":137,"is-whitespace-character":40}],127:[function(e,n,t){"use strict";var o=e("trim"),a=e("is-whitespace-character"),r=e("../locate/strong");n.exports=l,l.locator=r;var i="*",s="_";function l(e,n,t){var r,l,c,u,h,d,p,f=0,m=n.charAt(f);if(!(m!==i&&m!==s||n.charAt(++f)!==m||(l=this.options.pedantic,h=(c=m)+c,d=n.length,f++,u="",m="",l&&a(n.charAt(f)))))for(;f<d;){if(p=m,!((m=n.charAt(f))!==c||n.charAt(f+1)!==c||l&&a(p))&&(m=n.charAt(f+2))!==c){if(!o(u))return;return!!t||((r=e.now()).column+=2,r.offset+=2,e(h+u+h)({type:"strong",children:this.tokenizeInline(u,r)}))}l||"\\"!==m||(u+=m,m=n.charAt(++f)),u+=m,f++}}},{"../locate/strong":101,"is-whitespace-character":40,trim:144}],128:[function(e,n,t){"use strict";var o=e("is-whitespace-character");n.exports=function(e,n,t){var b,w,v,k,T,I,E,x,S,L,N,C,A,P,O,R,D,M,F,B,W,j,_,U;if(!this.options.gfm)return;b=0,M=0,I=n.length+1,E=[];for(;b<I;){if(j=n.indexOf(u,b),_=n.indexOf(s,b+1),-1===j&&(j=n.length),-1===_||_>j){if(M<p)return;break}E.push(n.slice(b,j)),M++,b=j+1}k=E.join(u),w=E.splice(1,1)[0]||[],b=0,I=w.length,M--,v=!1,N=[];for(;b<I;){if((S=w.charAt(b))===s){if(L=null,!1===v){if(!1===U)return}else N.push(v),v=!1;U=!1}else if(S===i)L=!0,v=v||y;else if(S===l)v=v===f?m:L&&v===y?g:f;else if(!o(S))return;b++}!1!==v&&N.push(v);if(N.length<d)return;if(t)return!0;D=-1,B=[],W=e(k).reset({type:"table",align:N,children:B});for(;++D<M;){for(F=E[D],T={type:"tableRow",children:[]},D&&e(u),e(F).reset(T,W),I=F.length+1,b=0,x="",C="",A=!0,P=null,O=null;b<I;)if((S=F.charAt(b))!==h&&S!==c){if(""===S||S===s)if(A)e(S);else{if(S&&O){x+=S,b++;continue}!C&&!S||A||(k=C,x.length>1&&(S?(k+=x.slice(0,x.length-1),x=x.charAt(x.length-1)):(k+=x,x="")),R=e.now(),e(k)({type:"tableCell",children:this.tokenizeInline(C,R)},T)),e(x+S),x="",C=""}else if(x&&(C+=x,x=""),C+=S,S===a&&b!==I-2&&(C+=F.charAt(b+1),b++),S===r){for(P=1;F.charAt(b+1)===S;)C+=S,b++,P++;O?P>=O&&(O=0):O=P}A=!1,b++}else C?x+=S:e(S),b++;D||e(u+w)}return W};var a="\\",r="`",i="-",s="|",l=":",c=" ",u="\n",h="\t",d=1,p=2,f="left",m="center",g="right",y=null},{"is-whitespace-character":40}],129:[function(e,n,t){"use strict";n.exports=function(e,n,t){var o,a,r,i,s,l,c,u,h,d;if(t)return!0;o=this.inlineMethods,i=o.length,a=this.inlineTokenizers,r=-1,h=n.length;for(;++r<i;)"text"!==(u=o[r])&&a[u]&&((c=a[u].locator)||e.file.fail("Missing locator: `"+u+"`"),-1!==(l=c.call(this,n,1))&&l<h&&(h=l));s=n.slice(0,h),d=e.now(),this.decode(s,d,function(n,t,o){e(o||n)({type:"text",value:n})})}},{}],130:[function(e,n,t){"use strict";n.exports=function(e,n,t){var u,h,d,p,f=-1,m=n.length+1,g="";for(;++f<m&&((u=n.charAt(f))===a||u===r);)g+=u;if(u!==i&&u!==l&&u!==s)return;h=u,g+=u,d=1,p="";for(;++f<m;)if((u=n.charAt(f))===h)d++,g+=p+h,p="";else{if(u!==r)return d>=c&&(!u||u===o)?(g+=p,!!t||e(g)({type:"thematicBreak"})):void 0;p+=u}};var o="\n",a="\t",r=" ",i="*",s="_",l="-",c=3},{}],131:[function(e,n,t){"use strict";var o=e("parse-entities"),a=e("is-whitespace-character"),r=e("../locate/url");n.exports=m,m.locator=r,m.notInLink=!0;var i="[",s="]",l="(",c=")",u="<",h="@",d="mailto:",p=["http://","https://",d],f=p.length;function m(e,n,t){var r,m,g,y,b,w,v,k,T,I,E,x;if(this.options.gfm){for(r="",y=-1,k=f;++y<k;)if(w=p[y],(v=n.slice(0,w.length)).toLowerCase()===w){r=v;break}if(r){for(y=r.length,k=n.length,T="",I=0;y<k&&(g=n.charAt(y),!a(g)&&g!==u)&&("."!==g&&","!==g&&":"!==g&&";"!==g&&'"'!==g&&"'"!==g&&")"!==g&&"]"!==g||(E=n.charAt(y+1))&&!a(E))&&(g!==l&&g!==i||I++,g!==c&&g!==s||!(--I<0));)T+=g,y++;if(T){if(m=r+=T,w===d){if(-1===(b=T.indexOf(h))||b===k-1)return;m=m.substr(d.length)}return!!t||(x=this.enterLink(),m=this.tokenizeInline(m,e.now()),x(),e(r)({type:"link",title:null,url:o(r),children:m}))}}}}},{"../locate/url":103,"is-whitespace-character":40,"parse-entities":44}],132:[function(e,n,t){"use strict";n.exports=function(e){return function(n,t){var r,i,s,l,c,u,h=this,d=h.offset,p=[],f=h[e+"Methods"],m=h[e+"Tokenizers"],g=t.line,y=t.column;if(!n)return p;k.now=w,k.file=h.file,b("");for(;n;){for(r=-1,i=f.length,c=!1;++r<i&&(l=f[r],!(s=m[l])||s.onlyAtStart&&!h.atStart||s.notInList&&h.inList||s.notInBlock&&h.inBlock||s.notInLink&&h.inLink||(u=n.length,s.apply(h,[k,n]),!(c=u!==n.length))););c||h.file.fail(new Error("Infinite loop"),k.now())}return h.eof=w(),p;function b(e){for(var n=-1,t=e.indexOf("\n");-1!==t;)g++,n=t,t=e.indexOf("\n",t+1);-1===n?y+=e.length:y=e.length-n,g in d&&(-1!==n?y+=d[g]:y<=d[g]&&(y=d[g]+1))}function w(){var e={line:g,column:y};return e.offset=h.toOffset(e),e}function v(){var e=w();return function(n,t){var o=n.position,a=o?o.start:e,r=[],i=o&&o.end.line,s=e.line;if(n.position=new function(e){this.start=e,this.end=w()}(a),o&&t&&o.indent){if(r=o.indent,i<s){for(;++i<s;)r.push((d[i]||0)+1);r.push(e.column)}t=r.concat(t)}return n.position.indent=t||[],n}}function k(e){var t,r,i,s=(t=[],r=g+1,function(){for(var e=g+1;r<e;)t.push((d[r]||0)+1),r++;return t}),l=v(),c=w();return i=e,n.substring(0,i.length)!==i&&h.file.fail(new Error("Incorrectly eaten value: please report this warning on http://git.io/vg5Ft"),w()),u.reset=f,f.test=m,u.test=m,n=n.substring(e.length),b(e),s=s(),u;function u(e,n){return l(function(e,n){var t=n?n.children:p,r=t[t.length-1];r&&e.type===r.type&&e.type in o&&a(r)&&a(e)&&(e=o[e.type].call(h,r,e));e!==r&&t.push(e);h.atStart&&0!==p.length&&h.exitStart();return e}(l(e),n),s)}function f(){var t=u.apply(null,arguments);return g=c.line,y=c.column,n=e+n,t}function m(){var t=l({});return g=c.line,y=c.column,n=e+n,t.position}}}};var o={text:function(e,n){return e.value+=n.value,e},blockquote:function(e,n){if(this.options.commonmark)return n;return e.children=e.children.concat(n.children),e}};function a(e){var n,t;return"text"!==e.type||!e.position||(n=e.position.start,t=e.position.end,n.line!==t.line||t.column-n.column===e.value.length)}},{}],133:[function(e,n,t){"use strict";n.exports=function(e,n){return function(t){var o,a=0,r=t.indexOf("\\"),i=e[n],s=[];for(;-1!==r;)s.push(t.slice(a,r)),a=r+1,(o=t.charAt(a))&&-1!==i.indexOf(o)||s.push("\\"),r=t.indexOf("\\",a);return s.push(t.slice(a)),s.join("")}}},{}],134:[function(e,n,t){"use strict";n.exports=function(e){var n,t=0,a=0,r=e.charAt(t),i={};for(;r in o;)n=o[r],a+=n,n>1&&(a=Math.floor(a/n)*n),i[a]=t,r=e.charAt(++t);return{indent:a,stops:i}};var o={" ":1,"\t":4}},{}],135:[function(e,n,t){"use strict";var o="<[A-Za-z][A-Za-z0-9\\-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9:._-]*(?:\\s*=\\s*(?:[^\"'=<>`\\u0000-\\u0020]+|'[^']*'|\"[^\"]*\"))?)*\\s*\\/?>",a="<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>";t.openCloseTag=new RegExp("^(?:"+o+"|"+a+")"),t.tag=new RegExp("^(?:"+o+"|"+a+"|\x3c!----\x3e|\x3c!--(?:-?[^>-])(?:-?[^-])*--\x3e|<[?].*?[?]>|<![A-Za-z]+\\s+[^>]*>|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>)")},{}],136:[function(e,n,t){"use strict";n.exports=function(e,n,t,o){var a,r,i,s,l,c,u=["pedantic","commonmark"],h=u.length,d=e.length,p=-1;for(;++p<d;){for(a=e[p],r=a[1]||{},i=a[0],s=-1,c=!1;++s<h;)if(void 0!==r[l=u[s]]&&r[l]!==t.options[l]){c=!0;break}if(!c&&n[i].apply(t,o))return!0}return!1}},{}],137:[function(e,n,t){"use strict";var o=e("collapse-white-space");n.exports=function(e){return o(e).toLowerCase()}},{"collapse-white-space":5}],138:[function(e,n,t){"use strict";var o=e("trim"),a=e("repeat-string"),r=e("./get-indentation");n.exports=function(e,n){var t,c,u,h,d=e.split(s),p=d.length+1,f=1/0,m=[];d.unshift(a(i,n)+"!");for(;p--;)if(c=r(d[p]),m[p]=c.stops,0!==o(d[p]).length){if(!c.indent){f=1/0;break}c.indent>0&&c.indent<f&&(f=c.indent)}if(f!==1/0)for(p=d.length;p--;){for(u=m[p],t=f;t&&!(t in u);)t--;h=0!==o(d[p]).length&&f&&t!==f?l:"",d[p]=h+d[p].slice(t in u?u[t]+1:0)}return d.shift(),d.join(s)};var i=" ",s="\n",l="\t"},{"./get-indentation":134,"repeat-string":139,trim:144}],139:[function(e,n,t){"use strict";var o,a="";n.exports=function(e,n){if("string"!=typeof e)throw new TypeError("expected a string");if(1===n)return e;if(2===n)return e+e;var t=e.length*n;if(o!==e||void 0===o)o=e,a="";else if(a.length>=t)return a.substr(0,t);for(;t>a.length&&n>1;)1&n&&(a+=e),n>>=1,e+=e;return a=(a+=e).substr(0,t)}},{}],140:[function(e,n,t){"use strict";var o=e("path");n.exports=function(e,n){if("string"!=typeof e)return e;if(0===e.length)return e;var t=o.basename(e,o.extname(e))+n;return o.join(o.dirname(e),t)}},{path:45}],141:[function(e,n,t){"use strict";function o(e){return"/"===e.charAt(0)}function a(e,n){for(var t=n,o=t+1,a=e.length;o<a;t+=1,o+=1)e[t]=e[o];e.pop()}t.__esModule=!0,t.default=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",t=e&&e.split("/")||[],r=n&&n.split("/")||[],i=e&&o(e),s=n&&o(n),l=i||s;if(e&&o(e)?r=t:t.length&&(r.pop(),r=r.concat(t)),!r.length)return"/";var c=void 0;if(r.length){var u=r[r.length-1];c="."===u||".."===u||""===u}else c=!1;for(var h=0,d=r.length;d>=0;d--){var p=r[d];"."===p?a(r,d):".."===p?(a(r,d),h++):h&&(a(r,d),h--)}if(!l)for(;h--;h)r.unshift("..");!l||""===r[0]||r[0]&&o(r[0])||r.unshift("");var f=r.join("/");return c&&"/"!==f.substr(-1)&&(f+="/"),f},n.exports=t.default},{}],142:[function(e,n,t){"use strict";n.exports=function(e,n,t){return function(){var o=t||this,a=o[e];return o[e]=!n,function(){o[e]=a}}}},{}],143:[function(e,n,t){"use strict";n.exports=function(e){var n=String(e),t=n.length;for(;n.charAt(--t)===o;);return n.slice(0,t+1)};var o="\n"},{}],144:[function(e,n,t){(t=n.exports=function(e){return e.replace(/^\s*|\s*$/g,"")}).left=function(e){return e.replace(/^\s*/,"")},t.right=function(e){return e.replace(/\s*$/,"")}},{}],145:[function(e,n,t){"use strict";n.exports=function(){var e=[],n={};return n.run=function(){var n=-1,t=o.call(arguments,0,-1),a=arguments[arguments.length-1];if("function"!=typeof a)throw new Error("Expected function as last argument, not "+a);(function r(i){var s=e[++n],l=o.call(arguments,0),c=l.slice(1),u=t.length,h=-1;if(i)a(i);else{for(;++h<u;)null!==c[h]&&void 0!==c[h]||(c[h]=t[h]);t=c,s?function(e,n){var t;return function(){var n,i=o.call(arguments,0),s=e.length>i.length;s&&i.push(a);try{n=e.apply(null,i)}catch(e){if(s&&t)throw e;return a(e)}s||(n&&"function"==typeof n.then?n.then(r,a):n instanceof Error?a(n):r(n))};function a(){t||(t=!0,n.apply(null,arguments))}function r(e){a(null,e)}}(s,r).apply(null,t):a.apply(null,[null].concat(t))}}).apply(null,[null].concat(t))},n.use=function(t){if("function"!=typeof t)throw new Error("Expected `fn` to be a function, not "+t);return e.push(t),n},n};var o=[].slice},{}],146:[function(e,n,t){"use strict";var o=e("xtend"),a=e("inherits");n.exports=function(e){var n,t,r;for(t in a(s,e),a(i,s),n=s.prototype)(r=n[t])&&"object"==typeof r&&(n[t]="concat"in r?r.concat():o(r));return s;function i(n){return e.apply(this,n)}function s(){return this instanceof s?e.apply(this,arguments):new i(arguments)}}},{inherits:32,xtend:160}],147:[function(e,n,t){"use strict";var o=e("extend"),a=e("bail"),r=e("vfile"),i=e("trough"),s=e("x-is-string"),l=e("x-is-function"),c=e("is-plain-obj");n.exports=function e(){var n=[];var t=i();var w={};var v=!1;var k=-1;T.data=function(e,n){if(s(e))return 2===arguments.length?(g("data",v),w[e]=n,T):h.call(w,e)&&w[e]||null;if(e)return g("data",v),w=e,T;return w};T.freeze=I;T.attachers=n;T.use=function(e){var t;if(g("use",v),null===e||void 0===e);else if(l(e))s.apply(null,arguments);else{if("object"!=typeof e)throw new Error("Expected usable value, not `"+e+"`");"length"in e?i(e):a(e)}t&&(w.settings=o(w.settings||{},t));return T;function a(e){i(e.plugins),e.settings&&(t=o(t||{},e.settings))}function r(e){if(l(e))s(e);else{if("object"!=typeof e)throw new Error("Expected usable value, not `"+e+"`");"length"in e?s.apply(null,e):a(e)}}function i(e){var n,t;if(null===e||void 0===e);else{if(!("object"==typeof e&&"length"in e))throw new Error("Expected a list of plugins, not `"+e+"`");for(n=e.length,t=-1;++t<n;)r(e[t])}}function s(e,t){var a=function(e){var t,o=n.length,a=-1;for(;++a<o;)if((t=n[a])[0]===e)return t}(e);a?(c(a[1])&&c(t)&&(t=o(a[1],t)),a[1]=t):n.push(u.call(arguments))}};T.parse=function(e){var n,t=r(e);if(I(),f("parse",n=T.Parser),p(n))return new n(String(t),t).parse();return n(String(t),t)};T.stringify=function(e,n){var t,o=r(n);if(I(),m("stringify",t=T.Compiler),y(e),p(t))return new t(e,o).compile();return t(e,o)};T.run=E;T.runSync=function(e,n){var t,o=!1;return E(e,n,function(e,n){o=!0,a(e),t=n}),b("runSync","run",o),t};T.process=x;T.processSync=function(e){var n,t=!1;return I(),f("processSync",T.Parser),m("processSync",T.Compiler),x(n=r(e),function(e){t=!0,a(e)}),b("processSync","process",t),n};return T;function T(){for(var t=e(),a=n.length,r=-1;++r<a;)t.use.apply(null,n[r]);return t.data(o(!0,{},w)),t}function I(){var e,o,a,r;if(v)return T;for(;++k<n.length;)e=n[k],o=e[0],a=e[1],r=null,!1!==a&&(!0===a&&(e[1]=void 0),r=o.apply(T,e.slice(1)),l(r)&&t.use(r));return v=!0,k=1/0,T}function E(e,n,o){if(y(e),I(),!o&&l(n)&&(o=n,n=null),!o)return new Promise(a);function a(a,i){t.run(e,r(n),function(n,t,r){t=t||e,n?i(n):a?a(t):o(null,t,r)})}a(null,o)}function x(e,n){if(I(),f("process",T.Parser),m("process",T.Compiler),!n)return new Promise(t);function t(t,o){var a=r(e);d.run(T,{file:a},function(e){e?o(e):t?t(a):n(null,a)})}t(null,n)}}().freeze();var u=[].slice,h={}.hasOwnProperty,d=i().use(function(e,n){n.tree=e.parse(n.file)}).use(function(e,n,t){e.run(n.tree,n.file,function(e,o,a){e?t(e):(n.tree=o,n.file=a,t())})}).use(function(e,n){n.file.contents=e.stringify(n.tree,n.file)});function p(e){return l(e)&&function(e){var n;for(n in e)return!0;return!1}(e.prototype)}function f(e,n){if(!l(n))throw new Error("Cannot `"+e+"` without `Parser`")}function m(e,n){if(!l(n))throw new Error("Cannot `"+e+"` without `Compiler`")}function g(e,n){if(n)throw new Error("Cannot invoke `"+e+"` on a frozen processor.\nCreate a new processor first, by invoking it: use `processor()` instead of `processor`.")}function y(e){if(!e||!s(e.type))throw new Error("Expected node, got `"+e+"`")}function b(e,n,t){if(!t)throw new Error("`"+e+"` finished async. Use `"+n+"` instead")}},{bail:1,extend:6,"is-plain-obj":39,trough:145,vfile:156,"x-is-function":158,"x-is-string":159}],148:[function(e,n,t){"use strict";function o(e){if("string"==typeof e)return function(e){return function(n){return Boolean(n&&n.type===e)}}(e);if(null===e||void 0===e)return a;if("object"==typeof e)return("length"in e?function(e){var n=function(e){var n=[],t=e.length,a=-1;for(;++a<t;)n[a]=o(e[a]);return n}(e),t=n.length;return function(){var e=-1;for(;++e<t;)if(n[e].apply(this,arguments))return!0;return!1}}:function(e){return function(n){var t;for(t in e)if(n[t]!==e[t])return!1;return!0}})(e);if("function"==typeof e)return e;throw new Error("Expected function, string, or object as test")}function a(){return!0}n.exports=function e(n,t,a,r,i){var s=null!==r&&void 0!==r;var l=null!==a&&void 0!==a;var c=o(n);if(l&&("number"!=typeof a||a<0||a===1/0))throw new Error("Expected positive finite index or child node");if(s&&(!e(null,r)||!r.children))throw new Error("Expected parent node");if(!t||!t.type||"string"!=typeof t.type)return!1;if(s!==l)throw new Error("Expected both parent and index");return Boolean(c.call(i,t,a,r))}},{}],149:[function(e,n,t){"use strict";var o=e("unist-util-visit");function a(e){delete e.position}function r(e){e.position=void 0}n.exports=function(e,n){return o(e,n?a:r),e}},{"unist-util-visit":151}],150:[function(e,n,t){"use strict";var o={}.hasOwnProperty;function a(e){return e&&"object"==typeof e||(e={}),i(e.line)+":"+i(e.column)}function r(e){return e&&"object"==typeof e||(e={}),a(e.start)+"-"+a(e.end)}function i(e){return e&&"number"==typeof e?e:1}n.exports=function(e){if(!e||"object"!=typeof e)return null;if(o.call(e,"position")||o.call(e,"type"))return r(e.position);if(o.call(e,"start")||o.call(e,"end"))return r(e);if(o.call(e,"line")||o.call(e,"column"))return a(e);return null}},{}],151:[function(e,n,t){"use strict";n.exports=s;var o=e("unist-util-is"),a=!0,r="skip",i=!1;function s(e,n,t,s){function l(e,c,u){var h;return c=c||(u?0:null),n&&e.type!==n&&!o(n,e,c,u||null)||(h=t(e,c,u||null)),h===i?h:e.children&&h!==r&&function(e,n){var t,o,r=s?-1:1,c=(s?e.length:-1)+r;for(;c>-1&&c<e.length;){if(t=e[c],(o=t&&l(t,c,n))===i)return o;c="number"==typeof o?o:c+r}return a}(e.children,e)===i?i:h}"function"==typeof n&&"function"!=typeof t&&(s=t,t=n,n=null),l(e)}s.CONTINUE=a,s.SKIP=r,s.EXIT=i},{"unist-util-is":148}],152:[function(e,n,t){"use strict";t.__esModule=!0;var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};t.default=function e(n,t){if(n===t)return!0;if(null==n||null==t)return!1;if(Array.isArray(n))return Array.isArray(t)&&n.length===t.length&&n.every(function(n,o){return e(n,t[o])});var a=void 0===n?"undefined":o(n);if(a!==(void 0===t?"undefined":o(t)))return!1;if("object"===a){var r=n.valueOf(),i=t.valueOf();if(r!==n||i!==t)return e(r,i);var s=Object.keys(n),l=Object.keys(t);return s.length===l.length&&s.every(function(o){return e(n[o],t[o])})}return!1},n.exports=t.default},{}],153:[function(e,n,t){"use strict";n.exports=function(e){var n=function(e){var n=[],t=e.indexOf("\n");for(;-1!==t;)n.push(t+1),t=e.indexOf("\n",t+1);return n.push(e.length+1),n}(String(e));return{toPosition:function(e){return function(n){var t=-1,o=e.length;if(n<0)return{};for(;++t<o;)if(e[t]>n)return{line:t+1,column:n-(e[t-1]||0)+1,offset:n};return{}}}(n),toOffset:function(e){return function(n){var t=n&&n.line,o=n&&n.column;if(!isNaN(t)&&!isNaN(o)&&t-1 in e)return(e[t-2]||0)+o-1||0;return-1}}(n)}}},{}],154:[function(e,n,t){"use strict";var o=e("unist-util-stringify-position");function a(){}n.exports=i,a.prototype=Error.prototype,i.prototype=new a;var r=i.prototype;function i(e,n,t){var a,r,i;"string"==typeof n&&(t=n,n=null),a=function(e){var n,t=[null,null];"string"==typeof e&&(-1===(n=e.indexOf(":"))?t[1]=e:(t[0]=e.slice(0,n),t[1]=e.slice(n+1)));return t}(t),r=o(n)||"1:1",i={start:{line:null,column:null},end:{line:null,column:null}},n&&n.position&&(n=n.position),n&&(n.start?(i=n,n=n.start):i.start=n),e.stack&&(this.stack=e.stack,e=e.message),this.message=e,this.name=r,this.reason=e,this.line=n?n.line:null,this.column=n?n.column:null,this.location=i,this.source=a[0],this.ruleId=a[1]}r.file="",r.name="",r.reason="",r.message="",r.stack="",r.fatal=null,r.column=null,r.line=null},{"unist-util-stringify-position":150}],155:[function(e,n,t){(function(t){"use strict";var o=e("path"),a=e("replace-ext"),r=e("is-buffer");n.exports=c;var i={}.hasOwnProperty,s=c.prototype;s.toString=function(e){var n=this.contents||"";return r(n)?n.toString(e):String(n)};var l=["history","path","basename","stem","extname","dirname"];function c(e){var n,o,a;if(e){if("string"==typeof e||r(e))e={contents:e};else if("message"in e&&"messages"in e)return e}else e={};if(!(this instanceof c))return new c(e);for(this.data={},this.messages=[],this.history=[],this.cwd=t.cwd(),o=-1,a=l.length;++o<a;)n=l[o],i.call(e,n)&&(this[n]=e[n]);for(n in e)-1===l.indexOf(n)&&(this[n]=e[n])}function u(e,n){if(-1!==e.indexOf(o.sep))throw new Error("`"+n+"` cannot be a path: did not expect `"+o.sep+"`")}function h(e,n){if(!e)throw new Error("`"+n+"` cannot be empty")}function d(e,n){if(!e)throw new Error("Setting `"+n+"` requires `path` to be set too")}Object.defineProperty(s,"path",{get:function(){return this.history[this.history.length-1]},set:function(e){h(e,"path"),e!==this.path&&this.history.push(e)}}),Object.defineProperty(s,"dirname",{get:function(){return"string"==typeof this.path?o.dirname(this.path):void 0},set:function(e){d(this.path,"dirname"),this.path=o.join(e||"",this.basename)}}),Object.defineProperty(s,"basename",{get:function(){return"string"==typeof this.path?o.basename(this.path):void 0},set:function(e){h(e,"basename"),u(e,"basename"),this.path=o.join(this.dirname||"",e)}}),Object.defineProperty(s,"extname",{get:function(){return"string"==typeof this.path?o.extname(this.path):void 0},set:function(e){var n=e||"";if(u(n,"extname"),d(this.path,"extname"),n){if("."!==n.charAt(0))throw new Error("`extname` must start with `.`");if(-1!==n.indexOf(".",1))throw new Error("`extname` cannot contain multiple dots")}this.path=a(this.path,n)}}),Object.defineProperty(s,"stem",{get:function(){return"string"==typeof this.path?o.basename(this.path,this.extname):void 0},set:function(e){h(e,"stem"),u(e,"stem"),this.path=o.join(this.dirname||"",e+(this.extname||""))}})}).call(this,e("_process"))},{_process:48,"is-buffer":36,path:45,"replace-ext":140}],156:[function(e,n,t){"use strict";var o=e("vfile-message"),a=e("./core.js");n.exports=a;var r=a.prototype;function i(e,n,t){var a=this.path,r=new o(e,n,t);return a&&(r.name=a+":"+r.name,r.file=a),r.fatal=!1,this.messages.push(r),r}r.message=i,r.info=function(){var e=this.message.apply(this,arguments);return e.fatal=null,e},r.fail=function(){var e=this.message.apply(this,arguments);throw e.fatal=!0,e},r.warn=i},{"./core.js":155,"vfile-message":154}],157:[function(e,n,t){(function(e){"use strict";var t=function(){};"production"!==e.env.NODE_ENV&&(t=function(e,n,t){var o=arguments.length;t=new Array(o>2?o-2:0);for(var a=2;a<o;a++)t[a-2]=arguments[a];if(void 0===n)throw new Error("`warning(condition, format, ...args)` requires a warning message argument");if(n.length<10||/^[s\W]*$/.test(n))throw new Error("The warning format should be able to uniquely identify this warning. Please, use a more descriptive format than: "+n);if(!e){var r=0,i="Warning: "+n.replace(/%s/g,function(){return t[r++]});"undefined"!=typeof console&&console.error(i);try{throw new Error(i)}catch(e){}}}),n.exports=t}).call(this,e("_process"))},{_process:48}],158:[function(e,n,t){n.exports=function(e){return"[object Function]"===Object.prototype.toString.call(e)}},{}],159:[function(e,n,t){var o=Object.prototype.toString;n.exports=function(e){return"[object String]"===o.call(e)}},{}],160:[function(e,n,t){n.exports=function(){for(var e={},n=0;n<arguments.length;n++){var t=arguments[n];for(var a in t)o.call(t,a)&&(e[a]=t[a])}return e};var o=Object.prototype.hasOwnProperty},{}],161:[function(e,n,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,n){for(var t=0;t<n.length;t++){var o=n[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(n,t,o){return t&&e(n.prototype,t),o&&e(n,o),n}}(),a=i(e("react")),r=i(e("react-markdown"));function i(e){return e&&e.__esModule?e:{default:e}}var s=function(e){function n(){return function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n),function(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}(this,(n.__proto__||Object.getPrototypeOf(n)).apply(this,arguments))}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,a.default.Component),o(n,[{key:"render",value:function(){return a.default.createElement("div",{class:"about"},a.default.createElement(r.default,{source:"# About\n\nThis web playground is based on [Let's Build a Compiler, by Jack Crenshaw](https://compilers.iecc.com/crenshaw/)."}))}}]),n}();t.default=s},{react:90,"react-markdown":61}],162:[function(e,n,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,n){for(var t=0;t<n.length;t++){var o=n[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(n,t,o){return t&&e(n.prototype,t),o&&e(n,o),n}}(),a=l(e("react")),r=l(e("react-markdown")),i=e("react-router-dom"),s=e("../resources/const");function l(e){return e&&e.__esModule?e:{default:e}}var c=e("../resources/markdown.json");var u=function(e){function n(){return function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n),function(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}(this,(n.__proto__||Object.getPrototypeOf(n)).apply(this,arguments))}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,a.default.Component),o(n,[{key:"render",value:function(){return a.default.createElement("div",{className:"page"},a.default.createElement("div",{className:"breadcrumb-wrapper"},a.default.createElement("span",{className:"breadcrumb light-blue-text"},a.default.createElement("i",{className:"material-icons"},"home")),a.default.createElement("span",{className:"breadcrumb grey-text"},"Home")),a.default.createElement("h4",{className:"header orange-text"},"Let' build a Compiler"),a.default.createElement("h6",{className:"header"},"By Jack W. Crenshaw, Ph.D"),a.default.createElement(r.default,{source:s.intro}),a.default.createElement("ul",null,c.map(function(e,n){return a.default.createElement("li",{key:n},a.default.createElement(i.Link,{to:e.path},e.title))})))}}]),n}();t.default=u},{"../resources/const":166,"../resources/markdown.json":167,react:90,"react-markdown":61,"react-router-dom":76}],163:[function(e,n,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o,a=function(){function e(e,n){for(var t=0;t<n.length;t++){var o=n[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(n,t,o){return t&&e(n.prototype,t),o&&e(n,o),n}}(),r=e("react"),i=(o=r)&&o.__esModule?o:{default:o},s=e("react-router-dom");var l=function(e){function n(){return function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n),function(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}(this,(n.__proto__||Object.getPrototypeOf(n)).apply(this,arguments))}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,i.default.Component),a(n,[{key:"render",value:function(){return i.default.createElement("div",{className:"navbar-fixed"},i.default.createElement("nav",{className:"light-blue lighten-1",role:"navigation"},i.default.createElement("div",{className:"nav-wrapper"},i.default.createElement(s.Link,{id:"logo-container",to:"/",className:"brand-logo"},i.default.createElement("span",{className:"icon-library"})),i.default.createElement("ul",{className:"right hide-on-med-and-down"},i.default.createElement("li",null,i.default.createElement(s.Link,{to:"/about"},"About"))),i.default.createElement("ul",{id:"nav-mobile",className:"side-nav"},i.default.createElement("li",null,i.default.createElement(s.Link,{to:"/about"},"About"))),i.default.createElement("a",{href:"#","data-activates":"nav-mobile",className:"button-collapse"},i.default.createElement("i",{className:"material-icons"},"menu")))))}}]),n}();t.default=l},{react:90,"react-router-dom":76}],164:[function(e,n,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,n){for(var t=0;t<n.length;t++){var o=n[t];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(n,t,o){return t&&e(n.prototype,t),o&&e(n,o),n}}(),a=s(e("react")),r=s(e("react-markdown")),i=e("react-router-dom");function s(e){return e&&e.__esModule?e:{default:e}}var l=function(e){function n(e){!function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}(this,n);var t=function(e,n){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!n||"object"!=typeof n&&"function"!=typeof n?e:n}(this,(n.__proto__||Object.getPrototypeOf(n)).call(this,e));return t.onResize=t.onResize.bind(t),t}return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Super expression must either be null or a function, not "+typeof n);e.prototype=Object.create(n&&n.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),n&&(Object.setPrototypeOf?Object.setPrototypeOf(e,n):e.__proto__=n)}(n,a.default.Component),o(n,[{key:"componentDidMount",value:function(){Prism.highlightAll(),this.div=$(".page"),this.div.find("ul").addClass("browser-default"),this.onResize(),$(window).on("resize",this.onResize)}},{key:"onResize",value:function(){$(".main").outerHeight()+$("nav").height()<=$(window).height()?$(".pagination-wrapper").addClass("abs-bottom"):$(".pagination-wrapper").removeClass("abs-bottom")}},{key:"render",value:function(){var e=this.props.page,n=e.title,t=e.content,o=this.props.section;return a.default.createElement("div",{className:"page"},a.default.createElement("div",{className:"breadcrumb-wrapper"},a.default.createElement(i.Link,{to:"/",className:"breadcrumb light-blue-text"},a.default.createElement("i",{className:"material-icons"},"home")),a.default.createElement(i.Link,{to:o.path,className:"breadcrumb light-blue-text"},o.title),a.default.createElement("span",{className:"breadcrumb grey-text"},n)),a.default.createElement("h4",{className:"header orange-text"},n),a.default.createElement(r.default,{source:t,escapeHtml:!1}))}}]),n}();t.default=l},{react:90,"react-markdown":61,"react-router-dom":76}],165:[function(e,n,t){"use strict";var o=u(e("react")),a=u(e("react-dom")),r=(u(e("react-markdown")),e("react-router-dom")),i=u(e("./components/About")),s=u(e("./components/Home")),l=u(e("./components/Navbar")),c=u(e("./components/Page"));function u(e){return e&&e.__esModule?e:{default:e}}var h=e("./resources/markdown.json"),d=$("#left-panel"),p=$(window);function f(e){var n=h[e];return function(){return o.default.createElement("div",null,o.default.createElement("div",{className:"breadcrumb-wrapper"},o.default.createElement(r.Link,{to:"/",className:"breadcrumb light-blue-text"},o.default.createElement("i",{className:"material-icons"},"home")),o.default.createElement("span",{className:"breadcrumb grey-text"},n.title)),o.default.createElement("h4",{className:"header orange-text"},n.title),(e=n,o.default.createElement("ul",null,e.items.map(function(n,t){return o.default.createElement("li",{key:t},o.default.createElement(r.Link,{to:e.path+n.path},n.title))}))));var e}}function m(e,n){var t=h[e],a=t.items[n];return function(){return o.default.createElement("div",null,o.default.createElement(c.default,{page:a,section:t}),(i=n,s=(e=t).items.length-1,l=0===i?e.path:e.path+e.items[i-1].path,u=i===s?e.path:e.path+e.items[i+1].path,o.default.createElement("div",{className:"pagination-wrapper"},o.default.createElement("ul",{className:"pagination"},o.default.createElement("li",{className:0===i?"disabled":"waves-effect"},o.default.createElement(r.Link,{to:l},o.default.createElement("i",{className:"material-icons"},"chevron_left"))),e.items.map(function(n,t){return o.default.createElement("li",{key:t,className:i===t?"active orange":"waves-effect"},o.default.createElement(r.Link,{to:e.path+n.path},t+1))}),o.default.createElement("li",{className:i===s?"disabled":"waves-effect"},o.default.createElement(r.Link,{to:u},o.default.createElement("i",{className:"material-icons"},"chevron_right")))))));var e,i,s,l,u}}p.on("resize",function(){d.css("max-height",p.height())}),a.default.render(o.default.createElement(r.HashRouter,null,o.default.createElement("div",{id:"react-wrapper"},o.default.createElement(l.default,null),o.default.createElement("div",{className:"main"},o.default.createElement(r.Switch,null,o.default.createElement(r.Route,{exact:!0,path:"/",component:s.default}),o.default.createElement(r.Route,{exact:!0,path:"/a-propos",component:i.default}),h.map(function(e,n){return[{key:n,path:e.path,component:f(n)}].concat(e.items.map(function(t,o){return{key:n+"-"+o,path:e.path+t.path,component:m(n,o)}})).map(function(e){return o.default.createElement(r.Route,{key:e.key,exact:!0,path:e.path,component:e.component})})}))))),document.getElementById("left-panel"))},{"./components/About":161,"./components/Home":162,"./components/Navbar":163,"./components/Page":164,"./resources/markdown.json":167,react:90,"react-dom":56,"react-markdown":61,"react-router-dom":76}],166:[function(e,n,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.intro="\nThis web playground is based on [Let's Build a Compiler, by Jack Crenshaw](https://compilers.iecc.com/crenshaw/).\n\nAs of August 2019, this web-based version is *Work in progress*.\n"},{}],167:[function(e,n,t){n.exports=[{title:"Part I: INTRODUCTION - 24 July 1988",path:"/part-i-introduction-24-july-1988",items:[{title:"INTRODUCTION",path:"/introduction",content:"This series of articles is a tutorial on the theory and practice\nof developing language parsers and compilers. Before we are\nfinished, we will have covered every aspect of compiler\nconstruction, designed a new programming language, and built a\nworking compiler.\n\nThough I am not a computer scientist by education (my Ph.D. is in\na different field, Physics), I have been interested in compilers\nfor many years. I have bought and tried to digest the contents\nof virtually every book on the subject ever written. I don't\nmind telling you that it was slow going. Compiler texts are\nwritten for Computer Science majors, and are tough sledding for\nthe rest of us. But over the years a bit of it began to seep in.\nWhat really caused it to jell was when I began to branch off on\nmy own and begin to try things on my own computer. Now I plan to\nshare with you what I have learned. At the end of this series\nyou will by no means be a computer scientist, nor will you know\nall the esoterics of compiler theory. I intend to completely\nignore the more theoretical aspects of the subject. What you\n_WILL_ know is all the practical aspects that one needs to know\nto build a working system.\n\nThis is a \"learn-by-doing\" series. In the course of the series I\nwill be performing experiments on a computer. You will be\nexpected to follow along, repeating the experiments that I do,\nand performing some on your own. I will be using Turbo Pascal\n4.0 on a PC clone. I will periodically insert examples written\nin TP. These will be executable code, which you will be expected\nto copy into your own computer and run. If you don't have a copy\nof Turbo, you will be severely limited in how well you will be\nable to follow what's going on. If you don't have a copy, I urge\nyou to get one. After all, it's an excellent product, good for\nmany other uses!\n\nSome articles on compilers show you examples, or show you (as in\nthe case of Small-C) a finished product, which you can then copy\nand use without a whole lot of understanding of how it works. I\nhope to do much more than that. I hope to teach you HOW the\nthings get done, so that you can go off on your own and not only\nreproduce what I have done, but improve on it.\n \nThis is admittedly an ambitious undertaking, and it won't be done\nin one page. I expect to do it in the course of a number of\narticles. Each article will cover a single aspect of compiler\ntheory, and will pretty much stand alone. If all you're\ninterested in at a given time is one aspect, then you need to\nlook only at that one article. Each article will be uploaded as\nit is complete, so you will have to wait for the last one before\nyou can consider yourself finished. Please be patient.\n\n\n\nThe average text on compiler theory covers a lot of ground that\nwe won't be covering here. The typical sequence is:\n\n- An introductory chapter describing what a compiler is.\n- A chapter or two on syntax equations, using Backus-Naur Form\n (BNF).\n- A chapter or two on lexical scanning, with emphasis on\n deterministic and non-deterministic finite automata.\n- Several chapters on parsing theory, beginning with top-down\n recursive descent, and ending with LALR parsers.\n- A chapter on intermediate languages, with emphasis on P-code\n and similar reverse polish representations.\n- Many chapters on alternative ways to handle subroutines and\n parameter passing, type declarations, and such.\n- A chapter toward the end on code generation, usually for some\n imaginary CPU with a simple instruction set. Most readers\n (and in fact, most college classes) never make it this far.\n- A final chapter or two on optimization. This chapter often\n goes unread, too.\n\n\nI'll be taking a much different approach in this series. To\nbegin with, I won't dwell long on options. I'll be giving you\n_A_ way that works. If you want to explore options, well and\ngood ... I encourage you to do so ... but I'll be sticking to\nwhat I know. I also will skip over most of the theory that puts\npeople to sleep. Don't get me wrong: I don't belittle the\ntheory, and it's vitally important when it comes to dealing with\nthe more tricky parts of a given language. But I believe in\nputting first things first. Here we'll be dealing with the 95%\nof compiler techniques that don't need a lot of theory to handle.\n\nI also will discuss only one approach to parsing: top-down,\nrecursive descent parsing, which is the _ONLY_ technique that's\nat all amenable to hand-crafting a compiler. The other\napproaches are only useful if you have a tool like YACC, and also\ndon't care how much memory space the final product uses.\n \nI also take a page from the work of Ron Cain, the author of the\noriginal Small C. Whereas almost all other compiler authors have\nhistorically used an intermediate language like P-code and\ndivided the compiler into two parts (a front end that produces\nP-code, and a back end that processes P-code to produce\nexecutable object code), Ron showed us that it is a\nstraightforward matter to make a compiler directly produce\nexecutable object code, in the form of assembler language\nstatements. The code will _NOT_ be the world's tightest code ...\nproducing optimized code is a much more difficult job. But it\nwill work, and work reasonably well. Just so that I don't leave\nyou with the impression that our end product will be worthless, I\n_DO_ intend to show you how to \"soup up\" the compiler with some\noptimization.\n\n\n\nFinally, I'll be using some tricks that I've found to be most\nhelpful in letting me understand what's going on without wading\nthrough a lot of boiler plate. Chief among these is the use of\nsingle-character tokens, with no embedded spaces, for the early\ndesign work. I figure that if I can get a parser to recognize\nand deal with I-T-L, I can get it to do the same with IF-THEN-\nELSE. And I can. In the second \"lesson,\" I'll show you just\nhow easy it is to extend a simple parser to handle tokens of\narbitrary length. As another trick, I completely ignore file\nI/O, figuring that if I can read source from the keyboard and\noutput object to the screen, I can also do it from/to disk files.\nExperience has proven that once a translator is working\ncorrectly, it's a straightforward matter to redirect the I/O to\nfiles. The last trick is that I make no attempt to do error\ncorrection/recovery. The programs we'll be building will\nRECOGNIZE errors, and will not CRASH, but they will simply stop\non the first error ... just like good ol' Turbo does. There will\nbe other tricks that you'll see as you go. Most of them can't be\nfound in any compiler textbook, but they work.\n\nA word about style and efficiency. As you will see, I tend to\nwrite programs in _VERY_ small, easily understood pieces. None\nof the procedures we'll be working with will be more than about\n15-20 lines long. I'm a fervent devotee of the KISS (Keep It\nSimple, Sidney) school of software development. I try to never\ndo something tricky or complex, when something simple will do.\nInefficient? Perhaps, but you'll like the results. As Brian\nKernighan has said, FIRST make it run, THEN make it run fast.\nIf, later on, you want to go back and tighten up the code in one\nof our products, you'll be able to do so, since the code will be\nquite understandable. If you do so, however, I urge you to wait\nuntil the program is doing everything you want it to.\n\nI also have a tendency to delay building a module until I\ndiscover that I need it. Trying to anticipate every possible\nfuture contingency can drive you crazy, and you'll generally\nguess wrong anyway. In this modern day of screen editors and\nfast compilers, I don't hesitate to change a module when I feel I\nneed a more powerful one. Until then, I'll write only what I\nneed.\n\nOne final caveat: One of the principles we'll be sticking to here\nis that we don't fool around with P-code or imaginary CPUs, but\nthat we will start out on day one producing working, executable\nobject code, at least in the form of assembler language source.\nHowever, you may not like my choice of assembler language ...\nit's 68000 code, which is what works on my system (under SK*DOS).\nI think you'll find, though, that the translation to any other\nCPU such as the 80x86 will be quite obvious, though, so I don't\nsee a problem here. In fact, I hope someone out there who knows\nthe '86 language better than I do will offer us the equivalent\nobject code fragments as we need them."},{title:"THE CRADLE",path:"/the-cradle",content:"Every program needs some boiler plate ... I/O routines, error\nmessage routines, etc. The programs we develop here will be no\nexceptions. I've tried to hold this stuff to an absolute\nminimum, however, so that we can concentrate on the important\nstuff without losing it among the trees. The code given below\nrepresents about the minimum that we need to get anything done.\nIt consists of some I/O routines, an error-handling routine and a\nskeleton, null main program. I call it our cradle. As we\ndevelop other routines, we'll add them to the cradle, and add the\ncalls to them as we need to. Make a copy of the cradle and save\nit, because we'll be using it more than once.\n\nThere are many different ways to organize the scanning activities\nof a parser. In Unix systems, authors tend to use getc and\nungetc. I've had very good luck with the approach shown here,\nwhich is to use a single, global, lookahead character. Part of\nthe initialization procedure (the only part, so far!) serves to\n\"prime the pump\" by reading the first character from the input\nstream. No other special techniques are required with Turbo 4.0\n... each successive call to GetChar will read the next character\nin the stream.\n\n\n```delphi\n\n{--------------------------------------------------------------}\nprogram Cradle;\n\n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look: char; { Lookahead Character }\n \n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n\nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n if Look = x then GetChar\n else Expected('''' + x + '''');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := upcase(c) in ['A'..'Z'];\nend;\n \n\n{--------------------------------------------------------------}\n\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: char;\nbegin\n if not IsAlpha(Look) then Expected('Name');\n GetName := UpCase(Look);\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: char;\nbegin\n if not IsDigit(Look) then Expected('Integer');\n GetNum := Look;\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n\nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nbegin\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\nend.\n{--------------------------------------------------------------}\n```\n\nThat's it for this introduction. Copy the code above into TP and\ncompile it. Make sure that it compiles and runs correctly. Then\nproceed to the first lesson, which is on expression parsing."}]},{title:"Part II: EXPRESSION PARSING - 24 July 1988",path:"/part-ii-expression-parsing-24-july-1988",items:[{title:"Introduction",path:"/introduction",content:"If you've read the introduction document to this series, you will\nalready know what we're about. You will also have copied the\ncradle software into your Turbo Pascal system, and have compiled\nit. So you should be ready to go.\n\n\nThe purpose of this article is for us to learn how to parse and\ntranslate mathematical expressions. What we would like to see as\noutput is a series of assembler-language statements that perform\nthe desired actions. For purposes of definition, an expression\nis the right-hand side of an equation, as in `x = 2*y + 3/(4*z)`\n\nIn the early going, I'll be taking things in _VERY_ small steps.\nThat's so that the beginners among you won't get totally lost.\nThere are also some very good lessons to be learned early on,\nthat will serve us well later. For the more experienced readers:\nbear with me. We'll get rolling soon enough."},{title:"SINGLE DIGITS",path:"/single-digits",content:"In keeping with the whole theme of this series (KISS, remember?),\nlet's start with the absolutely most simple case we can think of.\nThat, to me, is an expression consisting of a single digit.\n\nBefore starting to code, make sure you have a baseline copy of\nthe \"cradle\" that I gave last time. We'll be using it again for\nother experiments. Then add this code:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Expression }\n\nprocedure Expression;\nbegin\n EmitLn('MOVE #' + GetNum + ',D0')\nend;\n{---------------------------------------------------------------}\n```\n\nAnd add the line \"Expression;\" to the main program so that it\nreads:\n \n```delphi\n{---------------------------------------------------------------}\nbegin\n Init;\n Expression;\nend.\n{---------------------------------------------------------------}\n```\n\nNow run the program. Try any single-digit number as input. You\nshould get a single line of assembler-language output. Now try\nany other character as input, and you'll see that the parser\nproperly reports an error.\n\n\nCONGRATULATIONS! You have just written a working translator!\n\nOK, I grant you that it's pretty limited. But don't brush it off\ntoo lightly. This little \"compiler\" does, on a very limited\nscale, exactly what any larger compiler does: it correctly\nrecognizes legal statements in the input \"language\" that we have\ndefined for it, and it produces correct, executable assembler\ncode, suitable for assembling into object format. Just as\nimportantly, it correctly recognizes statements that are NOT\nlegal, and gives a meaningful error message. Who could ask for\nmore? As we expand our parser, we'd better make sure those two\ncharacteristics always hold true.\n\nThere are some other features of this tiny program worth\nmentioning. First, you can see that we don't separate code\ngeneration from parsing ... as soon as the parser knows what we\nwant done, it generates the object code directly. In a real\ncompiler, of course, the reads in GetChar would be from a disk\nfile, and the writes to another disk file, but this way is much\neasier to deal with while we're experimenting.\n\nAlso note that an expression must leave a result somewhere. I've\nchosen the 68000 register DO. I could have made some other\nchoices, but this one makes sense."},{title:"BINARY EXPRESSIONS",path:"/binary-expressions",content:"Now that we have that under our belt, let's branch out a bit.\nAdmittedly, an \"expression\" consisting of only one character is\nnot going to meet our needs for long, so let's see what we can do\nto extend it. Suppose we want to handle expressions of the form:\n\n`1+2` or `4-3` or, in general, `<term> +/- <term>`\n\n(That's a bit of Backus-Naur Form, or BNF.)\n \nTo do this we need a procedure that recognizes a term and leaves\nits result somewhere, and another that recognizes and\ndistinguishes between a '+' and a '-' and generates the\nappropriate code. But if Expression is going to leave its result\nin DO, where should Term leave its result? Answer: the same\nplace. We're going to have to save the first result of Term\nsomewhere before we get the next one.\n\nOK, basically what we want to do is have procedure Term do what\nExpression was doing before. So just RENAME procedure Expression\nas Term, and enter the following new version of Expression:\n\n\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n Term;\n EmitLn('MOVE D0,D1');\n case Look of\n '+': Add;\n '-': Subtract;\n else Expected('Addop');\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNext, just above Expression enter these two procedures:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Match('+');\n Term;\n EmitLn('ADD D1,D0');\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n EmitLn('SUB D1,D0');\nend;\n{-------------------------------------------------------------}\n``` \n\nWhen you're finished with that, the order of the routines should\nbe:\n\n- Term (The OLD Expression)\n- Add\n- Subtract\n- Expression\n\nNow run the program. Try any combination you can think of of two\nsingle digits, separated by a `'+'` or a `'-'`. You should get a\nseries of four assembler-language instructions out of each run.\nNow try some expressions with deliberate errors in them. Does\nthe parser catch the errors?\n\nTake a look at the object code generated. There are two\nobservations we can make. First, the code generated is NOT what\nwe would write ourselves. The sequence\n\n```asm\nMOVE #n,D0\nMOVE D0,D1\n```\nis inefficient. If we were writing this code by hand, we would\nprobably just load the data directly to D1.\n\nThere is a message here: code generated by our parser is less\nefficient than the code we would write by hand. Get used to it.\nThat's going to be true throughout this series. It's true of all\ncompilers to some extent. Computer scientists have devoted whole\nlifetimes to the issue of code optimization, and there are indeed\nthings that can be done to improve the quality of code output.\nSome compilers do quite well, but there is a heavy price to pay\nin complexity, and it's a losing battle anyway ... there will\nprobably never come a time when a good assembler-language pro-\ngrammer can't out-program a compiler. Before this session is\nover, I'll briefly mention some ways that we can do a little op-\ntimization, just to show you that we can indeed improve things\nwithout too much trouble. But remember, we're here to learn, not\nto see how tight we can make the object code. For now, and\nreally throughout this series of articles, we'll studiously\nignore optimization and concentrate on getting out code that\nworks.\n\nSpeaking of which: ours DOESN'T! The code is _WRONG_! As things\nare working now, the subtraction process subtracts D1 (which has\nthe FIRST argument in it) from D0 (which has the second). That's\nthe wrong way, so we end up with the wrong sign for the result.\nSo let's fix up procedure Subtract with a sign-changer, so that\nit reads\n\n```delphi\n{-------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n EmitLn('SUB D1,D0');\n EmitLn('NEG D0');\nend;\n{-------------------------------------------------------------}\n```\n\nNow our code is even less efficient, but at least it gives the\nright answer! Unfortunately, the rules that give the meaning of\nmath expressions require that the terms in an expression come out\nin an inconvenient order for us. Again, this is just one of\nthose facts of life you learn to live with. This one will come\nback to haunt us when we get to division.\n\nOK, at this point we have a parser that can recognize the sum or\ndifference of two digits. Earlier, we could only recognize a\nsingle digit. But real expressions can have either form (or an\ninfinity of others). For kicks, go back and run the program with\nthe single input line '1'.\n\nDidn't work, did it? And why should it? We just finished\ntelling our parser that the only kinds of expressions that are\nlegal are those with two terms. We must rewrite procedure\nExpression to be a lot more broadminded, and this is where things\nstart to take the shape of a real parser."},{title:"GENERAL EXPRESSIONS",path:"/general-expressions",content:"In the REAL world, an expression can consist of one or more\nterms, separated by \"addops\" ('+' or '-'). In BNF, this is\nwritten `<expression> ::= <term> [<addop> <term>]*`\n\n\nWe can accomodate this definition of an expression with the\naddition of a simple loop to procedure Expression:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n Term;\n while Look in ['+', '-'] do begin\n EmitLn('MOVE D0,D1');\n case Look of\n '+': Add;\n '-': Subtract;\n else Expected('Addop');\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNOW we're getting somewhere! This version handles any number of\nterms, and it only cost us two extra lines of code. As we go on,\nyou'll discover that this is characteristic of top-down parsers\n... it only takes a few lines of code to accomodate extensions to\nthe language. That's what makes our incremental approach\npossible. Notice, too, how well the code of procedure Expression\nmatches the BNF definition. That, too, is characteristic of the\nmethod. As you get proficient in the approach, you'll find that\nyou can turn BNF into parser code just about as fast as you can\ntype!\n\nOK, compile the new version of our parser, and give it a try. As\nusual, verify that the \"compiler\" can handle any legal\nexpression, and will give a meaningful error message for an\nillegal one. Neat, eh? You might note that in our test version,\nany error message comes out sort of buried in whatever code had\nalready been generated. But remember, that's just because we are\nusing the CRT as our \"output file\" for this series of\nexperiments. In a production version, the two outputs would be\nseparated ... one to the output file, and one to the screen.\n\n\nUSING THE STACK\n\nAt this point I'm going to violate my rule that we don't\nintroduce any complexity until it's absolutely necessary, long\nenough to point out a problem with the code we're generating. As\nthings stand now, the parser uses D0 for the \"primary\" register,\nand D1 as a place to store the partial sum. That works fine for\nnow, because as long as we deal with only the \"addops\" '+' and\n'-', any new term can be added in as soon as it is found. But in\ngeneral that isn't true. Consider, for example, the expression\n\n `1+(2-(3+(4-5)))`\n \nIf we put the '1' in D1, where do we put the `'2'`? Since a\ngeneral expression can have any degree of complexity, we're going\nto run out of registers fast!\n\nFortunately, there's a simple solution. Like every modern\nmicroprocessor, the 68000 has a stack, which is the perfect place\nto save a variable number of items. So instead of moving the term\nin D0 to D1, let's just push it onto the stack. For the benefit\nof those unfamiliar with 68000 assembler language, a push is\nwritten `-(SP)` and a pop, `(SP)+` .\n\n\nSo let's change the EmitLn in Expression to read:\n```delphi\n EmitLn('MOVE D0,-(SP)');\n```\nand the two lines in Add and Subtract to\n```delphi\n EmitLn('ADD (SP)+,D0')\n```\nand\n```delphi\n EmitLn('SUB (SP)+,D0'),\n```\nrespectively. Now try the parser again and make sure we haven't\nbroken it.\n\nOnce again, the generated code is less efficient than before, but\nit's a necessary step, as you'll see."},{title:"MULTIPLICATION AND DIVISION",path:"/multiplication-and-division",content:"Now let's get down to some REALLY serious business. As you all\nknow, there are other math operators than \"addops\" ...\nexpressions can also have multiply and divide operations. You\nalso know that there is an implied operator PRECEDENCE, or\nhierarchy, associated with expressions, so that in an expression\nlike `2 + 3 * 4`, we know that we're supposed to multiply FIRST, then add. (See\nwhy we needed the stack?)\n\nIn the early days of compiler technology, people used some rather\ncomplex techniques to insure that the operator precedence rules\nwere obeyed. It turns out, though, that none of this is\nnecessary ... the rules can be accommodated quite nicely by our\ntop-down parsing technique. Up till now, the only form that\nwe've considered for a term is that of a single decimal digit.\n\nMore generally, we can define a term as a PRODUCT of FACTORS;\ni.e. `<term> ::= <factor> [ <mulop> <factor ]*`\n\nWhat is a `factor`? For now, it's what a term used to be ... a\nsingle digit.\n\nNotice the symmetry: a term has the same form as an expression.\nAs a matter of fact, we can add to our parser with a little\njudicious copying and renaming. But to avoid confusion, the\nlisting below is the complete set of parsing routines. (Note the\nway we handle the reversal of operands in Divide.)\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Factor;\nbegin\n EmitLn('MOVE #' + GetNum + ',D0')\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nprocedure Multiply;\nbegin\n Match('*');\n Factor;\n EmitLn('MULS (SP)+,D0');\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nprocedure Divide;\nbegin\n Match('/');\n Factor;\n EmitLn('MOVE (SP)+,D1');\n EmitLn('DIVS D1,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nprocedure Term;\nbegin\n Factor;\n while Look in ['*', '/'] do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '*': Multiply;\n '/': Divide;\n else Expected('Mulop');\n end;\n end;\nend;\n\n\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Match('+');\n Term;\n EmitLn('ADD (SP)+,D0');\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n EmitLn('SUB (SP)+,D0');\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n Term;\n while Look in ['+', '-'] do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '+': Add;\n '-': Subtract;\n else Expected('Addop');\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nHot dog! A NEARLY functional parser/translator, in only 55 lines\nof Pascal! The output is starting to look really useful, if you\ncontinue to overlook the inefficiency, which I hope you will.\nRemember, we're not trying to produce tight code here."},{title:"PARENTHESES",path:"/parentheses",content:"We can wrap up this part of the parser with the addition of\nparentheses with math expressions. As you know, parentheses are\na mechanism to force a desired operator precedence. So, for\nexample, in the expression `2*(3+4)` ,\n\nthe parentheses force the addition before the multiply. Much\nmore importantly, though, parentheses give us a mechanism for\ndefining expressions of any degree of complexity, as in\n\n `(1+2)/((3+4)+(5-6))`\n\nThe key to incorporating parentheses into our parser is to\nrealize that no matter how complicated an expression enclosed by\nparentheses may be, to the rest of the world it looks like a\nsimple factor. That is, one of the forms for a factor is:\n\n `<factor> ::= (<expression>)`\n\nThis is where the recursion comes in. An expression can contain a\nfactor which contains another expression which contains a factor,\netc., ad infinitum.\n\nComplicated or not, we can take care of this by adding just a few\nlines of Pascal to procedure Factor:\n \n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Expression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n Expression;\n Match(')');\n end\n else\n EmitLn('MOVE #' + GetNum + ',D0');\nend;\n{--------------------------------------------------------------}\n```\n\nNote again how easily we can extend the parser, and how well the\nPascal code matches the BNF syntax.\n\nAs usual, compile the new version and make sure that it correctly\nparses legal sentences, and flags illegal ones with an error\nmessage."},{title:"UNARY MINUS",path:"/unary-minus",content:"At this point, we have a parser that can handle just about any\nexpression, right? OK, try this input sentence: `-1`\n\nWOOPS! It doesn't work, does it? Procedure Expression expects\neverything to start with an integer, so it coughs up the leading\nminus sign. You'll find that +3 won't work either, nor will\nsomething like `-(3-2)` .\n\nThere are a couple of ways to fix the problem. The easiest\n(although not necessarily the best) way is to stick an imaginary\nleading zero in front of expressions of this type, so that -3\nbecomes 0-3. We can easily patch this into our existing version\nof Expression:\n\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n if IsAddop(Look) then\n EmitLn('CLR D0')\n else\n Term;\n while IsAddop(Look) do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '+': Add;\n '-': Subtract;\n else Expected('Addop');\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nI TOLD you that making changes was easy! This time it cost us\nonly three new lines of Pascal. Note the new reference to\nfunction IsAddop. Since the test for an addop appeared twice, I\nchose to embed it in the new function. The form of IsAddop\nshould be apparent from that for IsAlpha. Here it is:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n{--------------------------------------------------------------}\n```\n\nOK, make these changes to the program and recompile. You should\nalso include IsAddop in your baseline copy of the cradle. We'll\nbe needing it again later. Now try the input -1 again. Wow!\nThe efficiency of the code is pretty poor ... six lines of code\njust for loading a simple constant ... but at least it's correct.\nRemember, we're not trying to replace Turbo Pascal here.\n\nAt this point we're just about finished with the structure of our\nexpression parser. This version of the program should correctly\nparse and compile just about any expression you care to throw at\nit. It's still limited in that we can only handle factors\ninvolving single decimal digits. But I hope that by now you're\nstarting to get the message that we can accomodate further\nextensions with just some minor changes to the parser. You\nprobably won't be surprised to hear that a variable or even a\nfunction call is just another kind of a factor.\n \nIn the next session, I'll show you just how easy it is to extend\nour parser to take care of these things too, and I'll also show\nyou just how easily we can accomodate multicharacter numbers and\nvariable names. So you see, we're not far at all from a truly\nuseful parser."},{title:"A WORD ABOUT OPTIMIZATION",path:"/a-word-about-optimization",content:"Earlier in this session, I promised to give you some hints as to\nhow we can improve the quality of the generated code. As I said,\nthe production of tight code is not the main purpose of this\nseries of articles. But you need to at least know that we aren't\njust wasting our time here ... that we can indeed modify the\nparser further to make it produce better code, without throwing\naway everything we've done to date. As usual, it turns out that\nSOME optimization is not that difficult to do ... it simply takes\nsome extra code in the parser.\n\nThere are two basic approaches we can take:\n\n - Try to fix up the code after it's generated\n\n This is the concept of \"peephole\" optimization. The general\n idea it that we know what combinations of instructions the\n compiler is going to generate, and we also know which ones\n are pretty bad (such as the code for -1, above). So all we\n do is to scan the produced code, looking for those\n combinations, and replacing them by better ones. It's sort\n of a macro expansion, in reverse, and a fairly\n straightforward exercise in pattern-matching. The only\n complication, really, is that there may be a LOT of such\n combinations to look for. It's called peephole optimization\n simply because it only looks at a small group of instructions\n at a time. Peephole optimization can have a dramatic effect\n on the quality of the code, with little change to the\n structure of the compiler itself. There is a price to pay,\n though, in both the speed, size, and complexity of the\n compiler. Looking for all those combinations calls for a lot\n of IF tests, each one of which is a source of error. And, of\n course, it takes time.\n\n In the classical implementation of a peephole optimizer,\n it's done as a second pass to the compiler. The output code\n is written to disk, and then the optimizer reads and\n processes the disk file again. As a matter of fact, you can\n see that the optimizer could even be a separate PROGRAM from\n the compiler proper. Since the optimizer only looks at the\n code through a small \"window\" of instructions (hence the\n name), a better implementation would be to simply buffer up a\n few lines of output, and scan the buffer after each EmitLn.\n\n - Try to generate better code in the first place\n \n This approach calls for us to look for special cases BEFORE\n we Emit them. As a trivial example, we should be able to\n identify a constant zero, and Emit a CLR instead of a load,\n or even do nothing at all, as in an add of zero, for example.\n Closer to home, if we had chosen to recognize the unary minus\n in Factor instead of in Expression, we could treat constants\n like -1 as ordinary constants, rather then generating them\n from positive ones. None of these things are difficult to\n deal with ... they only add extra tests in the code, which is\n why I haven't included them in our program. The way I see\n it, once we get to the point that we have a working compiler,\n generating useful code that executes, we can always go back\n and tweak the thing to tighten up the code produced. That's\n why there are Release 2.0's in the world.\n\nThere IS one more type of optimization worth mentioning, that\nseems to promise pretty tight code without too much hassle. It's\nmy \"invention\" in the sense that I haven't seen it suggested in\nprint anywhere, though I have no illusions that it's original\nwith me.\n\nThis is to avoid such a heavy use of the stack, by making better\nuse of the CPU registers. Remember back when we were doing only\naddition and subtraction, that we used registers D0 and D1,\nrather than the stack? It worked, because with only those two\noperations, the \"stack\" never needs more than two entries.\n\nWell, the 68000 has eight data registers. Why not use them as a\nprivately managed stack? The key is to recognize that, at any\npoint in its processing, the parser KNOWS how many items are on\nthe stack, so it can indeed manage it properly. We can define a\nprivate \"stack pointer\" that keeps track of which stack level\nwe're at, and addresses the corresponding register. Procedure\nFactor, for example, would not cause data to be loaded into\nregister D0, but into whatever the current \"top-of-stack\"\nregister happened to be.\n\nWhat we're doing in effect is to replace the CPU's RAM stack with\na locally managed stack made up of registers. For most\nexpressions, the stack level will never exceed eight, so we'll\nget pretty good code out. Of course, we also have to deal with\nthose odd cases where the stack level DOES exceed eight, but\nthat's no problem either. We simply let the stack spill over\ninto the CPU stack. For levels beyond eight, the code is no\nworse than what we're generating now, and for levels less than\neight, it's considerably better.\n\nFor the record, I have implemented this concept, just to make\nsure it works before I mentioned it to you. It does. In\npractice, it turns out that you can't really use all eight levels\n... you need at least one register free to reverse the operand\norder for division (sure wish the 68000 had an XTHL, like the\n8080!). For expressions that include function calls, we would\nalso need a register reserved for them. Still, there is a nice\nimprovement in code size for most expressions.\n\nSo, you see, getting better code isn't that difficult, but it\ndoes add complexity to the our translator ... complexity we can\ndo without at this point. For that reason, I STRONGLY suggest\nthat we continue to ignore efficiency issues for the rest of this\nseries, secure in the knowledge that we can indeed improve the\ncode quality without throwing away what we've done.\n\nNext lesson, I'll show you how to deal with variables factors and\nfunction calls. I'll also show you just how easy it is to handle\nmulticharacter tokens and embedded white space."}]},{title:"Part III: MORE EXPRESSIONS - 4 Aug 1988",path:"/part-iii-more-expressions-4-aug-1988",items:[{title:"INTRODUCTION ",path:"/introduction",content:"In the last installment, we examined the techniques used to parse\nand translate a general math expression. We ended up with a\nsimple parser that could handle arbitrarily complex expressions,\nwith two restrictions:\n\n - No variables were allowed, only numeric factors\n\n - The numeric factors were limited to single digits\n\nIn this installment, we'll get rid of those restrictions. We'll\nalso extend what we've done to include assignment statements\nfunction calls and. Remember, though, that the second\nrestriction was mainly self-imposed ... a choice of convenience\non our part, to make life easier and to let us concentrate on the\nfundamental concepts. As you'll see in a bit, it's an easy\nrestriction to get rid of, so don't get too hung up about it.\nWe'll use the trick when it serves us to do so, confident that we\ncan discard it when we're ready to."},{title:"VARIABLES",path:"/variables",content:"Most expressions that we see in practice involve variables, such\nas `b * b + 4 * a * c`\n\nNo parser is much good without being able to deal with them.\nFortunately, it's also quite easy to do.\n\nRemember that in our parser as it currently stands, there are two\nkinds of factors allowed: integer constants and expressions\nwithin parentheses. In BNF notation,\n\n `<factor> ::= <number> | (<expression>)`\n\nThe '|' stands for \"or\", meaning of course that either form is a\nlegal form for a factor. Remember, too, that we had no trouble\nknowing which was which ... the lookahead character is a left\nparen '(' in one case, and a digit in the other.\n \nIt probably won't come as too much of a surprise that a variable\nis just another kind of factor. So we extend the BNF above to\nread:\n\n\n `<factor> ::= <number> | (<expression>) | <variable>`\n\n\nAgain, there is no ambiguity: if the lookahead character is a\nletter, we have a variable; if a digit, we have a number. Back\nwhen we translated the number, we just issued code to load the\nnumber, as immediate data, into D0. Now we do the same, only we\nload a variable.\n\nA minor complication in the code generation arises from the fact\nthat most 68000 operating systems, including the SK*DOS that I'm\nusing, require the code to be written in \"position-independent\"\nform, which basically means that everything is PC-relative. The\nformat for a load in this language is `MOVE X(PC),D0`\n\nwhere X is, of course, the variable name. Armed with that, let's\nmodify the current version of Factor to read:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Expression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n EmitLn('MOVE ' + GetName + '(PC),D0')\n else\n EmitLn('MOVE #' + GetNum + ',D0');\nend;\n{--------------------------------------------------------------}\n```\n\nI've remarked before how easy it is to add extensions to the\nparser, because of the way it's structured. You can see that\nthis still holds true here. This time it cost us all of two\nextra lines of code. Notice, too, how the if-else-else structure\nexactly parallels the BNF syntax equation.\n\nOK, compile and test this new version of the parser. That didn't\nhurt too badly, did it?\n "},{title:"FUNCTIONS",path:"/functions",content:"There is only one other common kind of factor supported by most\nlanguages: the function call. It's really too early for us to\ndeal with functions well, because we haven't yet addressed the\nissue of parameter passing. What's more, a \"real\" language would\ninclude a mechanism to support more than one type, one of which\nshould be a function type. We haven't gotten there yet, either.\nBut I'd still like to deal with functions now for a couple of\nreasons. First, it lets us finally wrap up the parser in\nsomething very close to its final form, and second, it brings up\na new issue which is very much worth talking about.\n\nUp till now, we've been able to write what is called a\n\"predictive parser.\" That means that at any point, we can know\nby looking at the current lookahead character exactly what to do\nnext. That isn't the case when we add functions. Every language\nhas some naming rules for what constitutes a legal identifier.\nFor the present, ours is simply that it is one of the letters\n'a'..'z'. The problem is that a variable name and a function\nname obey the same rules. So how can we tell which is which?\nOne way is to require that they each be declared before they are\nused. Pascal takes that approach. The other is that we might\nrequire a function to be followed by a (possibly empty) parameter\nlist. That's the rule used in C.\n\nSince we don't yet have a mechanism for declaring types, let's\nuse the C rule for now. Since we also don't have a mechanism to\ndeal with parameters, we can only handle empty lists, so our\nfunction calls will have the form `x()` .\n\nSince we're not dealing with parameter lists yet, there is\nnothing to do but to call the function, so we need only to issue\na BSR (call) instead of a MOVE.\n\nNow that there are two possibilities for the \"If IsAlpha\" branch\nof the test in Factor, let's treat them in a separate procedure.\nModify Factor to read:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Expression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n Ident\n else\n EmitLn('MOVE #' + GetNum + ',D0');\nend;\n{--------------------------------------------------------------}\n```\n\nand insert before it the new procedure\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Identifier }\n\nprocedure Ident;\nvar Name: char;\nbegin\n Name := GetName;\n if Look = '(' then begin\n Match('(');\n Match(')');\n EmitLn('BSR ' + Name);\n end\n else\n EmitLn('MOVE ' + Name + '(PC),D0')\nend;\n{---------------------------------------------------------------}\n```\n\nOK, compile and test this version. Does it parse all legal\nexpressions? Does it correctly flag badly formed ones?\n\nThe important thing to notice is that even though we no longer\nhave a predictive parser, there is little or no complication\nadded with the recursive descent approach that we're using. At\nthe point where Factor finds an identifier (letter), it doesn't\nknow whether it's a variable name or a function name, nor does it\nreally care. It simply passes it on to Ident and leaves it up to\nthat procedure to figure it out. Ident, in turn, simply tucks\naway the identifier and then reads one more character to decide\nwhich kind of identifier it's dealing with.\n\nKeep this approach in mind. It's a very powerful concept, and it\nshould be used whenever you encounter an ambiguous situation\nrequiring further lookahead. Even if you had to look several\ntokens ahead, the principle would still work."},{title:"MORE ON ERROR HANDLING",path:"/more-on-error-handling",content:"As long as we're talking philosophy, there's another important\nissue to point out: error handling. Notice that although the\nparser correctly rejects (almost) every malformed expression we\ncan throw at it, with a meaningful error message, we haven't\nreally had to do much work to make that happen. In fact, in the\nwhole parser per se (from Ident through Expression) there are\nonly two calls to the error routine, Expected. Even those aren't\nnecessary ... if you'll look again in Term and Expression, you'll\nsee that those statements can't be reached. I put them in early\non as a bit of insurance, but they're no longer needed. Why\ndon't you delete them now?\n\nSo how did we get this nice error handling virtually for free?\nIt's simply that I've carefully avoided reading a character\ndirectly using GetChar. Instead, I've relied on the error\nhandling in GetName, GetNum, and Match to do all the error\nchecking for me. Astute readers will notice that some of the\ncalls to Match (for example, the ones in Add and Subtract) are\nalso unnecessary ... we already know what the character is by the\ntime we get there ... but it maintains a certain symmetry to\nleave them in, and the general rule to always use Match instead\nof GetChar is a good one.\n\nI mentioned an \"almost\" above. There is a case where our error\nhandling leaves a bit to be desired. So far we haven't told our\nparser what and end-of-line looks like, or what to do with\nembedded white space. So a space character (or any other\ncharacter not part of the recognized character set) simply causes\nthe parser to terminate, ignoring the unrecognized characters.\n\nIt could be argued that this is reasonable behavior at this\npoint. In a \"real\" compiler, there is usually another statement\nfollowing the one we're working on, so any characters not treated\nas part of our expression will either be used for or rejected as\npart of the next one.\n\nBut it's also a very easy thing to fix up, even if it's only\ntemporary. All we have to do is assert that the expression\nshould end with an end-of-line , i.e., a carriage return.\n\nTo see what I'm talking about, try the input line\n\n `1+2 <space> 3+4`\n\nSee how the space was treated as a terminator? Now, to make the\ncompiler properly flag this, add the line\n\n```delphi\n if Look <> CR then Expected('Newline');\n```\nin the main program, just after the call to Expression. That\ncatches anything left over in the input stream. Don't forget to\ndefine CR in the const statement:\n\n```delphi\n CR = ^M;\n```\n\nAs usual, recompile the program and verify that it does what it's\nsupposed to."},{title:"ASSIGNMENT STATEMENTS",path:"/assignment-statements",content:"OK, at this point we have a parser that works very nicely. I'd\nlike to point out that we got it using only 88 lines of\nexecutable code, not counting what was in the cradle. The\ncompiled object file is a whopping 4752 bytes. Not bad,\nconsidering we weren't trying very hard to save either source\ncode or object size. We just stuck to the KISS principle.\n\nOf course, parsing an expression is not much good without having\nsomething to do with it afterwards. Expressions USUALLY (but not\nalways) appear in assignment statements, in the form\n\n `<Ident> = <Expression>` \n\nWe're only a breath away from being able to parse an assignment\nstatement, so let's take that last step. Just after procedure\nExpression, add the following new procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := GetName;\n Match('=');\n Expression;\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)')\nend;\n{--------------------------------------------------------------}\n```\n\nNote again that the code exactly parallels the BNF. And notice\nfurther that the error checking was painless, handled by GetName\nand Match.\n\nThe reason for the two lines of assembler has to do with a\npeculiarity in the 68000, which requires this kind of construct\nfor PC-relative code.\n\nNow change the call to Expression, in the main program, to one to\nAssignment. That's all there is to it.\n\nSon of a gun! We are actually compiling assignment statements.\nIf those were the only kind of statements in a language, all we'd\nhave to do is put this in a loop and we'd have a full-fledged\ncompiler!\n\nWell, of course they're not the only kind. There are also little\nitems like control statements (IFs and loops), procedures,\ndeclarations, etc. But cheer up. The arithmetic expressions\nthat we've been dealing with are among the most challenging in a\nlanguage. Compared to what we've already done, control\nstatements will be easy. I'll be covering them in the fifth\ninstallment. And the other statements will all fall in line, as\nlong as we remember to KISS."},{title:"MULTI-CHARACTER TOKENS",path:"/multi-character-tokens",content:"Throughout this series, I've been carefully restricting\neverything we do to single-character tokens, all the while\nassuring you that it wouldn't be difficult to extend to multi-\ncharacter ones. I don't know if you believed me or not ... I\nwouldn't really blame you if you were a bit skeptical. I'll\ncontinue to use that approach in the sessions which follow,\nbecause it helps keep complexity away. But I'd like to back up\nthose assurances, and wrap up this portion of the parser, by\nshowing you just how easy that extension really is. In the\nprocess, we'll also provide for embedded white space. Before you\nmake the next few changes, though, save the current version of\nthe parser away under another name. I have some more uses for it\nin the next installment, and we'll be working with the single-\ncharacter version.\n\nMost compilers separate out the handling of the input stream into\na separate module called the lexical scanner. The idea is that\nthe scanner deals with all the character-by-character input, and\nreturns the separate units (tokens) of the stream. There may\ncome a time when we'll want to do something like that, too, but\nfor now there is no need. We can handle the multi-character\ntokens that we need by very slight and very local modifications\nto GetName and GetNum.\n\nThe usual definition of an identifier is that the first character\nmust be a letter, but the rest can be alphanumeric (letters or\nnumbers). To deal with this, we need one other recognizer\nfunction\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize an Alphanumeric }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n{--------------------------------------------------------------}\n```\n\nAdd this function to your parser. I put mine just after IsDigit.\nWhile you're at it, might as well include it as a permanent\nmember of Cradle, too.\n \nNow, we need to modify function GetName to return a string\ninstead of a character:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: string;\nvar Token: string;\nbegin\n Token := '';\n if not IsAlpha(Look) then Expected('Name');\n while IsAlNum(Look) do begin\n Token := Token + UpCase(Look);\n GetChar;\n end;\n GetName := Token;\nend;\n{--------------------------------------------------------------}\n```\n\nSimilarly, modify GetNum to read:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: string;\nvar Value: string;\nbegin\n Value := '';\n if not IsDigit(Look) then Expected('Integer');\n while IsDigit(Look) do begin\n Value := Value + Look;\n GetChar;\n end;\n GetNum := Value;\nend;\n{--------------------------------------------------------------}\n\n````\n\nAmazingly enough, that is virtually all the changes required to\nthe parser! The local variable Name in procedures Ident and\nAssignment was originally declared as \"char\", and must now be\ndeclared string[8]. (Clearly, we could make the string length\nlonger if we chose, but most assemblers limit the length anyhow.)\nMake this change, and then recompile and test. _NOW_ do you\nbelieve that it's a simple change?"},{title:"WHITE SPACE",path:"/white-space",content:"Before we leave this parser for awhile, let's address the issue\nof white space. As it stands now, the parser will barf (or\nsimply terminate) on a single space character embedded anywhere\nin the input stream. That's pretty unfriendly behavior. So\nlet's \"productionize\" the thing a bit by eliminating this last\nrestriction.\n\nThe key to easy handling of white space is to come up with a\nsimple rule for how the parser should treat the input stream, and\nto enforce that rule everywhere. Up till now, because white\nspace wasn't permitted, we've been able to assume that after each\nparsing action, the lookahead character Look contains the next\nmeaningful character, so we could test it immediately. Our\ndesign was based upon this principle.\n\nIt still sounds like a good rule to me, so that's the one we'll\nuse. This means that every routine that advances the input\nstream must skip over white space, and leave the next non-white\ncharacter in Look. Fortunately, because we've been careful to\nuse GetName, GetNum, and Match for most of our input processing,\nit is only those three routines (plus Init) that we need to\nmodify.\n\nNot surprisingly, we start with yet another new recognizer\nroutine:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB];\nend;\n{--------------------------------------------------------------}\n```\n\nWe also need a routine that will eat white-space characters,\nuntil it finds a non-white one:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n{--------------------------------------------------------------}\n```\n\nNow, add calls to SkipWhite to Match, GetName, and GetNum as\nshown below:\n\n```delphi\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n if Look <> x then Expected('''' + x + '''')\n else begin\n GetChar;\n SkipWhite;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: string;\nvar Token: string;\nbegin\n Token := '';\n if not IsAlpha(Look) then Expected('Name');\n while IsAlNum(Look) do begin\n Token := Token + UpCase(Look);\n GetChar;\n end;\n GetName := Token;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: string;\nvar Value: string;\nbegin\n Value := '';\n if not IsDigit(Look) then Expected('Integer');\n while IsDigit(Look) do begin\n Value := Value + Look;\n GetChar;\n end;\n GetNum := Value;\n SkipWhite;\nend;\n{--------------------------------------------------------------}\n```\n(Note that I rearranged Match a bit, without changing the\nfunctionality.)\n\nFinally, we need to skip over leading blanks where we \"prime the\npump\" in Init:\n\n```delphi \n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nbegin\n GetChar;\n SkipWhite;\nend;\n{--------------------------------------------------------------}\n```\n\nMake these changes and recompile the program. You will find that\nyou will have to move Match below SkipWhite, to avoid an error\nmessage from the Pascal compiler. Test the program as always to\nmake sure it works properly.\n\nSince we've made quite a few changes during this session, I'm\nreproducing the entire parser below:\n\n```delphi\n{--------------------------------------------------------------}\nprogram parse;\n\n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n CR = ^M;\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look: char; { Lookahead Character }\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n \nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Alphanumeric }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize White Space }\n \nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB];\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n if Look <> x then Expected('''' + x + '''')\n else begin\n GetChar;\n SkipWhite;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: string;\nvar Token: string;\nbegin\n Token := '';\n if not IsAlpha(Look) then Expected('Name');\n while IsAlNum(Look) do begin\n Token := Token + UpCase(Look);\n GetChar;\n end;\n GetName := Token;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: string;\nvar Value: string;\nbegin\n Value := '';\n if not IsDigit(Look) then Expected('Integer');\n while IsDigit(Look) do begin\n Value := Value + Look;\n GetChar;\n end;\n GetNum := Value;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n\nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Identifier }\n\nprocedure Ident;\nvar Name: string[8];\nbegin\n Name:= GetName;\n if Look = '(' then begin\n Match('(');\n Match(')');\n EmitLn('BSR ' + Name);\n end\n else\n EmitLn('MOVE ' + Name + '(PC),D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Expression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n Ident\n else\n EmitLn('MOVE #' + GetNum + ',D0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nprocedure Multiply;\nbegin\n Match('*');\n Factor;\n EmitLn('MULS (SP)+,D0');\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nprocedure Divide;\nbegin\n Match('/');\n Factor;\n EmitLn('MOVE (SP)+,D1');\n EmitLn('EXS.L D0');\n EmitLn('DIVS D1,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nprocedure Term;\nbegin\n Factor;\n while Look in ['*', '/'] do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '*': Multiply;\n '/': Divide;\n end;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Match('+');\n Term;\n EmitLn('ADD (SP)+,D0');\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n EmitLn('SUB (SP)+,D0');\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n if IsAddop(Look) then\n EmitLn('CLR D0')\n else\n Term;\n while IsAddop(Look) do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '+': Add;\n '-': Subtract;\n end;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: string[8];\nbegin\n Name := GetName;\n Match('=');\n Expression;\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)')\nend;\n\n\n{--------------------------------------------------------------}\n{ Initialize }\n \nprocedure Init;\nbegin\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n Assignment;\n If Look <> CR then Expected('NewLine');\nend.\n{--------------------------------------------------------------}\n```\n\nNow the parser is complete. It's got every feature we can put in\na one-line \"compiler.\" Tuck it away in a safe place. Next time\nwe'll move on to a new subject, but we'll still be talking about\nexpressions for quite awhile. Next installment, I plan to talk a\nbit about interpreters as opposed to compilers, and show you how\nthe structure of the parser changes a bit as we change what sort\nof action has to be taken. The information we pick up there will\nserve us in good stead later on, even if you have no interest in\ninterpreters. See you next time."}]},{title:"Part IV: INTERPRETERS - 24 July 1988",path:"/part-iv-interpreters-24-july-1988",items:[{title:"INTRODUCTION",path:"/introduction",content:"In the first three installments of this series, we've looked at\nparsing and compiling math expressions, and worked our way grad-\nually and methodically from dealing with very simple one-term,\none-character \"expressions\" up through more general ones, finally\narriving at a very complete parser that could parse and translate\ncomplete assignment statements, with multi-character tokens,\nembedded white space, and function calls. This time, I'm going\nto walk you through the process one more time, only with the goal\nof interpreting rather than compiling object code.\n\nSince this is a series on compilers, why should we bother with\ninterpreters? Simply because I want you to see how the nature of\nthe parser changes as we change the goals. I also want to unify\nthe concepts of the two types of translators, so that you can see\nnot only the differences, but also the similarities.\n\nConsider the assignment statement\n\n `x = 2 * y + 3`\n\nIn a compiler, we want the target CPU to execute this assignment\nat EXECUTION time. The translator itself doesn't do any arith-\nmetic ... it only issues the object code that will cause the CPU\nto do it when the code is executed. For the example above, the\ncompiler would issue code to compute the expression and store the\nresults in variable x.\n\nFor an interpreter, on the other hand, no object code is gen-\nerated. Instead, the arithmetic is computed immediately, as the\nparsing is going on. For the example, by the time parsing of the\nstatement is complete, x will have a new value.\n\nThe approach we've been taking in this whole series is called\n\"syntax-driven translation.\" As you are aware by now, the struc-\nture of the parser is very closely tied to the syntax of the\nproductions we parse. We have built Pascal procedures that rec-\nognize every language construct. Associated with each of these\nconstructs (and procedures) is a corresponding \"action,\" which\ndoes whatever makes sense to do once a construct has been\nrecognized. In our compiler so far, every action involves\nemitting object code, to be executed later at execution time. In\nan interpreter, every action involves something to be done im-\nmediately.\n\nWhat I'd like you to see here is that the layout ... the struc-\nture ... of the parser doesn't change. It's only the actions\nthat change. So if you can write an interpreter for a given\nlanguage, you can also write a compiler, and vice versa. Yet, as\nyou will see, there ARE differences, and significant ones.\nBecause the actions are different, the procedures that do the\nrecognizing end up being written differently. Specifically, in\nthe interpreter the recognizing procedures end up being coded as\nFUNCTIONS that return numeric values to their callers. None of\nthe parsing routines for our compiler did that.\n\nOur compiler, in fact, is what we might call a \"pure\" compiler.\nEach time a construct is recognized, the object code is emitted\nIMMEDIATELY. (That's one reason the code is not very efficient.)\nThe interpreter we'll be building here is a pure interpreter, in\nthe sense that there is no translation, such as \"tokenizing,\"\nperformed on the source code. These represent the two extremes\nof translation. In the real world, translators are rarely so\npure, but tend to have bits of each technique.\n\nI can think of several examples. I've already mentioned one:\nmost interpreters, such as Microsoft BASIC, for example, trans-\nlate the source code (tokenize it) into an intermediate form so\nthat it'll be easier to parse real time.\n\nAnother example is an assembler. The purpose of an assembler, of\ncourse, is to produce object code, and it normally does that on a\none-to-one basis: one object instruction per line of source code.\nBut almost every assembler also permits expressions as arguments.\nIn this case, the expressions are always constant expressions,\nand so the assembler isn't supposed to issue object code for\nthem. Rather, it \"interprets\" the expressions and computes the\ncorresponding constant result, which is what it actually emits as\nobject code.\n\nAs a matter of fact, we could use a bit of that ourselves. The\ntranslator we built in the previous installment will dutifully\nspit out object code for complicated expressions, even though\nevery term in the expression is a constant. In that case it\nwould be far better if the translator behaved a bit more like an\ninterpreter, and just computed the equivalent constant result.\n\nThere is a concept in compiler theory called \"lazy\" translation.\nThe idea is that you typically don't just emit code at every\naction. In fact, at the extreme you don't emit anything at all,\nuntil you absolutely have to. To accomplish this, the actions\nassociated with the parsing routines typically don't just emit\ncode. Sometimes they do, but often they simply return in-\nformation back to the caller. Armed with such information, the\ncaller can then make a better choice of what to do.\n\nFor example, given the statement\n\n `x = x + 3 - 2 - (5 - 4)` ,\n\nour compiler will dutifully spit out a stream of 18 instructions\nto load each parameter into registers, perform the arithmetic,\nand store the result. A lazier evaluation would recognize that\nthe arithmetic involving constants can be evaluated at compile\ntime, and would reduce the expression to\n\n `x = x + 0` .\n\nAn even lazier evaluation would then be smart enough to figure\nout that this is equivalent to\n\n `x = x` ,\n\nwhich calls for no action at all. We could reduce 18 in-\nstructions to zero!\n\nNote that there is no chance of optimizing this way in our trans-\nlator as it stands, because every action takes place immediately.\n\nLazy expression evaluation can produce significantly better\nobject code than we have been able to so far. I warn you,\nthough: it complicates the parser code considerably, because each\nroutine now has to make decisions as to whether to emit object\ncode or not. Lazy evaluation is certainly not named that because\nit's easier on the compiler writer!\n\nSince we're operating mainly on the KISS principle here, I won't\ngo into much more depth on this subject. I just want you to be\naware that you can get some code optimization by combining the\ntechniques of compiling and interpreting. In particular, you\nshould know that the parsing routines in a smarter translator\nwill generally return things to their caller, and sometimes\nexpect things as well. That's the main reason for going over\ninterpretation in this installment."},{title:"THE INTERPRETER",path:"/the-interpreter",content:"OK, now that you know WHY we're going into all this, let's do it.\nJust to give you practice, we're going to start over with a bare\ncradle and build up the translator all over again. This time, of\ncourse, we can go a bit faster.\n\nSince we're now going to do arithmetic, the first thing we need\nto do is to change function GetNum, which up till now has always\nreturned a character (or string). Now, it's better for it to\nreturn an integer. MAKE A COPY of the cradle (for goodness's\nsake, don't change the version in Cradle itself!!) and modify\nGetNum as follows:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: integer;\nbegin\n if not IsDigit(Look) then Expected('Integer');\n GetNum := Ord(Look) - Ord('0');\n GetChar;\nend;\n{--------------------------------------------------------------}\n```\n\nNow, write the following version of Expression:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nfunction Expression: integer;\nbegin\n Expression := GetNum;\nend;\n{--------------------------------------------------------------}\n```\n\nFinally, insert the statement\n\n\n `Writeln(Expression);`\n\n\nat the end of the main program. Now compile and test.\n\nAll this program does is to \"parse\" and translate a single\ninteger \"expression.\" As always, you should make sure that it\ndoes that with the digits 0..9, and gives an error message for\nanything else. Shouldn't take you very long!\n\nOK, now let's extend this to include addops. Change Expression\nto read:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nfunction Expression: integer;\nvar Value: integer;\nbegin\n if IsAddop(Look) then\n Value := 0\n else\n Value := GetNum;\n while IsAddop(Look) do begin\n case Look of\n '+': begin\n Match('+');\n Value := Value + GetNum;\n end;\n '-': begin\n Match('-');\n Value := Value - GetNum;\n end;\n end;\n end;\n Expression := Value;\nend;\n{--------------------------------------------------------------}\n```\n\nThe structure of Expression, of course, parallels what we did\nbefore, so we shouldn't have too much trouble debugging it.\nThere's been a SIGNIFICANT development, though, hasn't there?\nProcedures Add and Subtract went away! The reason is that the\naction to be taken requires BOTH arguments of the operation. I\ncould have chosen to retain the procedures and pass into them the\nvalue of the expression to date, which is Value. But it seemed\ncleaner to me to keep Value as strictly a local variable, which\nmeant that the code for Add and Subtract had to be moved in line.\nThis result suggests that, while the structure we had developed\nwas nice and clean for our simple-minded translation scheme, it\nprobably wouldn't do for use with lazy evaluation. That's a\nlittle tidbit we'll probably want to keep in mind for later.\n\nOK, did the translator work? Then let's take the next step.\nIt's not hard to figure out what procedure Term should now look\nlike. Change every call to GetNum in function Expression to a\ncall to Term, and then enter the following form for Term:\n\n\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nfunction Term: integer;\nvar Value: integer;\nbegin\n Value := GetNum;\n while Look in ['*', '/'] do begin\n case Look of\n '*': begin\n Match('*');\n Value := Value * GetNum;\n end;\n '/': begin\n Match('/');\n Value := Value div GetNum;\n end;\n end;\n end;\n Term := Value;\nend;\n{--------------------------------------------------------------}\n```\nNow, try it out. Don't forget two things: first, we're dealing\nwith integer division, so, for example, 1/3 should come out zero.\nSecond, even though we can output multi-digit results, our input\nis still restricted to single digits.\n\nThat seems like a silly restriction at this point, since we have\nalready seen how easily function GetNum can be extended. So\nlet's go ahead and fix it right now. The new version is\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: integer;\nvar Value: integer;\nbegin\n Value := 0;\n if not IsDigit(Look) then Expected('Integer');\n while IsDigit(Look) do begin\n Value := 10 * Value + Ord(Look) - Ord('0');\n GetChar;\n end;\n GetNum := Value;\nend;\n{--------------------------------------------------------------}\n```\n\nIf you've compiled and tested this version of the interpreter,\nthe next step is to install function Factor, complete with pa-\nrenthesized expressions. We'll hold off a bit longer on the\nvariable names. First, change the references to GetNum, in\nfunction Term, so that they call Factor instead. Now code the\nfollowing version of Factor:\n\n\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nfunction Expression: integer; Forward;\n\nfunction Factor: integer;\nbegin\n if Look = '(' then begin\n Match('(');\n Factor := Expression;\n Match(')');\n end\n else\n Factor := GetNum;\nend;\n{---------------------------------------------------------------}\n```\n\nThat was pretty easy, huh? We're rapidly closing in on a useful\ninterpreter."},{title:"A LITTLE PHILOSOPHY",path:"/a-little-philosophy",content:"Before going any further, there's something I'd like to call to\nyour attention. It's a concept that we've been making use of in\nall these sessions, but I haven't explicitly mentioned it up till\nnow. I think it's time, because it's a concept so useful, and so\npowerful, that it makes all the difference between a parser\nthat's trivially easy, and one that's too complex to deal with.\n\nIn the early days of compiler technology, people had a terrible\ntime figuring out how to deal with things like operator prece-\ndence ... the way that multiply and divide operators take\nprecedence over add and subtract, etc. I remember a colleague of\nsome thirty years ago, and how excited he was to find out how to\ndo it. The technique used involved building two stacks, upon\nwhich you pushed each operator or operand. Associated with each\noperator was a precedence level, and the rules required that you\nonly actually performed an operation (\"reducing\" the stack) if\nthe precedence level showing on top of the stack was correct. To\nmake life more interesting, an operator like ')' had different\nprecedence levels, depending upon whether or not it was already\non the stack. You had to give it one value before you put it on\nthe stack, and another to decide when to take it off. Just for\nthe experience, I worked all of this out for myself a few years\nago, and I can tell you that it's very tricky.\n\nWe haven't had to do anything like that. In fact, by now the\nparsing of an arithmetic statement should seem like child's play.\nHow did we get so lucky? And where did the precedence stacks go?\n\nA similar thing is going on in our interpreter above. You just\nKNOW that in order for it to do the computation of arithmetic\nstatements (as opposed to the parsing of them), there have to be\nnumbers pushed onto a stack somewhere. But where is the stack?\n\nFinally, in compiler textbooks, there are a number of places\nwhere stacks and other structures are discussed. In the other\nleading parsing method (LR), an explicit stack is used. In fact,\nthe technique is very much like the old way of doing arithmetic\nexpressions. Another concept is that of a parse tree. Authors\nlike to draw diagrams of the tokens in a statement, connected\ninto a tree with operators at the internal nodes. Again, where\nare the trees and stacks in our technique? We haven't seen any.\nThe answer in all cases is that the structures are implicit, not\nexplicit. In any computer language, there is a stack involved\nevery time you call a subroutine. Whenever a subroutine is\ncalled, the return address is pushed onto the CPU stack. At the\nend of the subroutine, the address is popped back off and control\nis transferred there. In a recursive language such as Pascal,\nthere can also be local data pushed onto the stack, and it, too,\nreturns when it's needed.\n\nFor example, function Expression contains a local parameter\ncalled Value, which it fills by a call to Term. Suppose, in its\nnext call to Term for the second argument, that Term calls\nFactor, which recursively calls Expression again. That \"in-\nstance\" of Expression gets another value for its copy of Value.\nWhat happens to the first Value? Answer: it's still on the\nstack, and will be there again when we return from our call\nsequence.\n\nIn other words, the reason things look so simple is that we've\nbeen making maximum use of the resources of the language. The\nhierarchy levels and the parse trees are there, all right, but\nthey're hidden within the structure of the parser, and they're\ntaken care of by the order with which the various procedures are\ncalled. Now that you've seen how we do it, it's probably hard to\nimagine doing it any other way. But I can tell you that it took\na lot of years for compiler writers to get that smart. The early\ncompilers were too complex too imagine. Funny how things get\neasier with a little practice.\n\nThe reason I've brought all this up is as both a lesson and a\nwarning. The lesson: things can be easy when you do them right.\nThe warning: take a look at what you're doing. If, as you branch\nout on your own, you begin to find a real need for a separate\nstack or tree structure, it may be time to ask yourself if you're\nlooking at things the right way. Maybe you just aren't using the\nfacilities of the language as well as you could be.\n\n\nThe next step is to add variable names. Now, though, we have a\nslight problem. For the compiler, we had no problem in dealing\nwith variable names ... we just issued the names to the assembler\nand let the rest of the program take care of allocating storage\nfor them. Here, on the other hand, we need to be able to fetch\nthe values of the variables and return them as the return values\nof Factor. We need a storage mechanism for these variables.\n\nBack in the early days of personal computing, Tiny BASIC lived.\nIt had a grand total of 26 possible variables: one for each\nletter of the alphabet. This fits nicely with our concept of\nsingle-character tokens, so we'll try the same trick. In the\nbeginning of your interpreter, just after the declaration of\nvariable Look, insert the line:\n\n `Table: Array['A'..'Z'] of integer;`\n\nWe also need to initialize the array, so add this procedure:\n\n\n\n```delphi\n{---------------------------------------------------------------}\n{ Initialize the Variable Area }\n\nprocedure InitTable;\nvar i: char;\nbegin\n for i := 'A' to 'Z' do\n Table[i] := 0;\nend;\n{---------------------------------------------------------------}\n```\n\nYou must also insert a call to InitTable, in procedure Init.\nDON'T FORGET to do that, or the results may surprise you!\n\nNow that we have an array of variables, we can modify Factor to\nuse it. Since we don't have a way (so far) to set the variables,\nFactor will always return zero values for them, but let's go\nahead and extend it anyway. Here's the new version:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nfunction Expression: integer; Forward;\n\nfunction Factor: integer;\nbegin\n if Look = '(' then begin\n Match('(');\n Factor := Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n Factor := Table[GetName]\n else\n Factor := GetNum;\nend;\n{---------------------------------------------------------------}\n```\n\nAs always, compile and test this version of the program. Even\nthough all the variables are now zeros, at least we can correctly\nparse the complete expressions, as well as catch any badly formed\nexpressions.\n\nI suppose you realize the next step: we need to do an assignment\nstatement so we can put something INTO the variables. For now,\nlet's stick to one-liners, though we will soon be handling\nmultiple statements.\n\nThe assignment statement parallels what we did before:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n \n\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := GetName;\n Match('=');\n Table[Name] := Expression;\nend;\n{--------------------------------------------------------------}\n```\n\nTo test this, I added a temporary write statement in the main\nprogram, to print out the value of A. Then I tested it with\nvarious assignments to it.\n\nOf course, an interpretive language that can only accept a single\nline of program is not of much value. So we're going to want to\nhandle multiple statements. This merely means putting a loop\naround the call to Assignment. So let's do that now. But what\nshould be the loop exit criterion? Glad you asked, because it\nbrings up a point we've been able to ignore up till now.\n\nOne of the most tricky things to handle in any translator is to\ndetermine when to bail out of a given construct and go look for\nsomething else. This hasn't been a problem for us so far because\nwe've only allowed for a single kind of construct ... either an\nexpression or an assignment statement. When we start adding\nloops and different kinds of statements, you'll find that we have\nto be very careful that things terminate properly. If we put our\ninterpreter in a loop, we need a way to quit. Terminating on a\nnewline is no good, because that's what sends us back for another\nline. We could always let an unrecognized character take us out,\nbut that would cause every run to end in an error message, which\ncertainly seems uncool.\n\nWhat we need is a termination character. I vote for Pascal's\nending period ('.'). A minor complication is that Turbo ends\nevery normal line with TWO characters, the carriage return (CR)\nand line feed (LF). At the end of each line, we need to eat\nthese characters before processing the next one. A natural way\nto do this would be with procedure Match, except that Match's\nerror message prints the character, which of course for the CR\nand/or LF won't look so great. What we need is a special proce-\ndure for this, which we'll no doubt be using over and over. Here\nit is:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Skip Over a Newline }\n\nprocedure NewLine;\nbegin\n if Look = CR then begin\n GetChar;\n if Look = LF then\n GetChar;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nInsert this procedure at any convenient spot ... I put mine just\nafter Match. Now, rewrite the main program to look like this:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n repeat\n Assignment;\n NewLine;\n until Look = '.';\nend.\n{--------------------------------------------------------------}\n```\n\nNote that the test for a CR is now gone, and that there are also\nno error tests within NewLine itself. That's OK, though ...\nwhatever is left over in terms of bogus characters will be caught\nat the beginning of the next assignment statement.\n\nWell, we now have a functioning interpreter. It doesn't do us a\nlot of good, however, since we have no way to read data in or\nwrite it out. Sure would help to have some I/O!\n\nLet's wrap this session up, then, by adding the I/O routines.\nSince we're sticking to single-character tokens, I'll use '?' to\nstand for a read statement, and '!' for a write, with the char-\nacter immediately following them to be used as a one-token\n\"parameter list.\" Here are the routines:\n\n```delphi\n{--------------------------------------------------------------}\n{ Input Routine }\n\nprocedure Input;\nbegin\n Match('?');\n Read(Table[GetName]);\nend;\n\n\n{--------------------------------------------------------------}\n{ Output Routine }\n\nprocedure Output;\nbegin\n Match('!');\n WriteLn(Table[GetName]);\nend;\n{--------------------------------------------------------------}\n```\nThey aren't very fancy, I admit ... no prompt character on input,\nfor example ... but they get the job done.\n\nThe corresponding changes in the main program are shown below.\nNote that we use the usual trick of a case statement based upon\nthe current lookahead character, to decide what to do.\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n repeat\n case Look of\n '?': Input;\n '!': Output;\n else Assignment;\n end;\n NewLine;\n until Look = '.';\nend.\n{--------------------------------------------------------------}\n```\n\nYou have now completed a real, working interpreter. It's pretty\nsparse, but it works just like the \"big boys.\" It includes three\nkinds of program statements (and can tell the difference!), 26\nvariables, and I/O statements. The only things that it lacks,\nreally, are control statements, subroutines, and some kind of\nprogram editing function. The program editing part, I'm going to\npass on. After all, we're not here to build a product, but to\nlearn things. The control statements, we'll cover in the next\ninstallment, and the subroutines soon after. I'm anxious to get\non with that, so we'll leave the interpreter as it stands.\n\nI hope that by now you're convinced that the limitation of sin-\ngle-character names and the processing of white space are easily\ntaken care of, as we did in the last session. This time, if\nyou'd like to play around with these extensions, be my guest ...\nthey're \"left as an exercise for the student.\" See you next\ntime."}]},{title:"Part V: CONTROL CONSTRUCTS - 19 August 1988",path:"/part-v-control-constructs-19-august-1988",items:[{title:"INTRODUCTION",path:"/introduction",content:"In the first four installments of this series, we've been\nconcentrating on the parsing of math expressions and assignment\nstatements. In this installment, we'll take off on a new and\nexciting tangent: that of parsing and translating control\nconstructs such as IF statements.\n\nThis subject is dear to my heart, because it represents a turning\npoint for me. I had been playing with the parsing of\nexpressions, just as we have done in this series, but I still\nfelt that I was a LONG way from being able to handle a complete\nlanguage. After all, REAL languages have branches and loops and\nsubroutines and all that. Perhaps you've shared some of the same\nthoughts. Awhile back, though, I had to produce control\nconstructs for a structured assembler preprocessor I was writing.\nImagine my surprise to discover that it was far easier than the\nexpression parsing I had already been through. I remember\nthinking, \"Hey! This is EASY!\" After we've finished this session,\nI'll bet you'll be thinking so, too."},{title:"THE PLAN",path:"/the-plan",content:"In what follows, we'll be starting over again with a bare cradle,\nand as we've done twice before now, we'll build things up one at\na time. We'll also be retaining the concept of single-character\ntokens that has served us so well to date. This means that the\n\"code\" will look a little funny, with 'i' for IF, 'w' for WHILE,\netc. But it helps us get the concepts down pat without fussing\nover lexical scanning. Fear not ... eventually we'll see\nsomething looking like \"real\" code.\n\nI also don't want to have us get bogged down in dealing with\nstatements other than branches, such as the assignment statements\nwe've been working on. We've already demonstrated that we can\nhandle them, so there's no point carrying them around as excess\nbaggage during this exercise. So what I'll do instead is to use\nan anonymous statement, \"other\", to take the place of the non-\ncontrol statements and serve as a place-holder for them. We have\nto generate some kind of object code for them (we're back into\ncompiling, not interpretation), so for want of anything else I'll\njust echo the character input.\n\nOK, then, starting with yet another copy of the cradle, let's\ndefine the procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate an \"Other\" }\n\nprocedure Other;\nbegin\n EmitLn(GetName);\nend;\n{--------------------------------------------------------------}\n```\n\nNow include a call to it in the main program, thus:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n Other;\nend.\n{--------------------------------------------------------------}\n```\n\nRun the program and see what you get. Not very exciting, is it?\nBut hang in there, it's a start, and things will get better.\n\nThe first thing we need is the ability to deal with more than one\nstatement, since a single-line branch is pretty limited. We did\nthat in the last session on interpreting, but this time let's get\na little more formal. Consider the following BNF:\n```\n <program> ::= <block> END\n\n <block> ::= [ <statement> ]*\n```\nThis says that, for our purposes here, a program is defined as a\nblock, followed by an END statement. A block, in turn, consists\nof zero or more statements. We only have one kind of statement,\nso far.\n\nWhat signals the end of a block? It's simply any construct that\nisn't an \"other\" statement. For now, that means only the END\nstatement.\n\nArmed with these ideas, we can proceed to build up our parser.\nThe code for a program (we have to call it DoProgram, or Pascal\nwill complain, is:\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Program }\n\nprocedure DoProgram;\nbegin\n Block;\n if Look <> 'e' then Expected('End');\n EmitLn('END')\nend;\n{--------------------------------------------------------------}\n```\n\nNotice that I've arranged to emit an \"END\" command to the\nassembler, which sort of punctuates the output code, and makes\nsense considering that we're parsing a complete program here.\n\nThe code for Block is:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block;\nbegin\n while not(Look in ['e']) do begin\n Other;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\n(From the form of the procedure, you just KNOW we're going to be\nadding to it in a bit!)\n\nOK, enter these routines into your program. Replace the call to\nBlock in the main program, by a call to DoProgram. Now try it\nand see how it works. Well, it's still not much, but we're\ngetting closer."},{title:"SOME GROUNDWORK",path:"/some-groundwork",content:"Before we begin to define the various control constructs, we need\nto lay a bit more groundwork. First, a word of warning: I won't\nbe using the same syntax for these constructs as you're familiar\nwith from Pascal or C. For example, the Pascal syntax for an IF\nis:\n\n\n `IF <condition> THEN <statement>`\n\n\n(where the statement, of course, may be compound).\n\nThe C version is similar:\n\n\n `IF ( <condition> ) <statement>`\n\n\nInstead, I'll be using something that looks more like Ada:\n\n\n `IF <condition> <block> ENDIF`\n\n\nIn other words, the IF construct has a specific termination\nsymbol. This avoids the dangling-else of Pascal and C and also\nprecludes the need for the brackets {} or begin-end. The syntax\nI'm showing you here, in fact, is that of the language KISS that\nI'll be detailing in later installments. The other constructs\nwill also be slightly different. That shouldn't be a real\nproblem for you. Once you see how it's done, you'll realize that\nit really doesn't matter so much which specific syntax is\ninvolved. Once the syntax is defined, turning it into code is\nstraightforward.\n\nNow, all of the constructs we'll be dealing with here involve\ntransfer of control, which at the assembler-language level means\nconditional and/or unconditional branches. For example, the\nsimple IF statement\n\n\n `IF <condition> A ENDIF B ....`\n\nmust get translated into\n```\n Branch if NOT condition to L\n A\n L: B\n ...\n```\n\nIt's clear, then, that we're going to need some more procedures\nto help us deal with these branches. I've defined two of them\nbelow. Procedure NewLabel generates unique labels. This is done\nvia the simple expedient of calling every label 'Lnn', where nn\nis a label number starting from zero. Procedure PostLabel just\noutputs the labels at the proper place.\n\nHere are the two routines:\n\n```delphi\n{--------------------------------------------------------------}\n{ Generate a Unique Label }\n\nfunction NewLabel: string;\nvar S: string;\nbegin\n Str(LCount, S);\n NewLabel := 'L' + S;\n Inc(LCount);\nend;\n\n\n{--------------------------------------------------------------}\n{ Post a Label To Output }\n\nprocedure PostLabel(L: string);\nbegin\n WriteLn(L, ':');\nend;\n{--------------------------------------------------------------}\n```\n\nNotice that we've added a new global variable, LCount, so you\nneed to change the VAR declarations at the top of the program to\nlook like this:\n\n```delphi\nvar Look : char; { Lookahead Character }\n Lcount: integer; { Label Counter }\n```\n\nAlso, add the following extra initialization to Init:\n\n```delphi\n LCount := 0;\n```\n(DON'T forget that, or your labels can look really strange!)\n\n\nAt this point I'd also like to show you a new kind of notation.\nIf you compare the form of the IF statement above with the as-\nsembler code that must be produced, you can see that there are\ncertain actions associated with each of the keywords in the\nstatement:\n\n\n - IF: First, get the condition and issue the code for it.\n Then, create a unique label and emit a branch if false.\n\n - ENDIF: Emit the label.\n\n\nThese actions can be shown very concisely if we write the syntax\nthis way:\n \n```\n IF\n <condition> { Condition;\n L = NewLabel;\n Emit(Branch False to L); }\n <block>\n ENDIF { PostLabel(L) }\n```\n\nThis is an example of syntax-directed translation. We've been\ndoing it all along ... we've just never written it down this way\nbefore. The stuff in curly brackets represents the ACTIONS to be\ntaken. The nice part about this representation is that it not\nonly shows what we have to recognize, but also the actions we\nhave to perform, and in which order. Once we have this syntax,\nthe code almost writes itself.\n\nAbout the only thing left to do is to be a bit more specific\nabout what we mean by \"Branch if false.\"\n\nI'm assuming that there will be code executed for `<condition>`\nthat will perform Boolean algebra and compute some result. It\nshould also set the condition flags corresponding to that result.\nNow, the usual convention for a Boolean variable is to let 0000\nrepresent \"false,\" and anything else (some use FFFF, some 0001)\nrepresent \"true.\"\n\nOn the 68000 the condition flags are set whenever any data is\nmoved or calculated. If the data is a 0000 (corresponding to a\nfalse condition, remember), the zero flag will be set. The code\nfor \"Branch on zero\" is BEQ. So for our purposes here,\n\n```\n BEQ <=> Branch if false\n BNE <=> Branch if true\n```\n\nIt's the nature of the beast that most of the branches we see\nwill be BEQ's ... we'll be branching AROUND the code that's\nsupposed to be executed when the condition is true."},{title:"THE IF STATEMENT",path:"/the-if-statement",content:"With that bit of explanation out of the way, we're finally ready\nto begin coding the IF-statement parser. In fact, we've almost\nalready done it! As usual, I'll be using our single-character\napproach, with the character 'i' for IF, and 'e' for ENDIF (as\nwell as END ... that dual nature causes no confusion). I'll\nalso, for now, skip completely the character for the branch con-\ndition, which we still have to define.\n\nThe code for DoIf is:\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block; Forward;\n\n\nprocedure DoIf;\nvar L: string;\nbegin\n Match('i');\n L := NewLabel;\n Condition;\n EmitLn('BEQ ' + L);\n Block;\n Match('e');\n PostLabel(L);\nend;\n{--------------------------------------------------------------}\n```\n\nAdd this routine to your program, and change Block to reference\nit as follows:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block;\nbegin\n while not(Look in ['e']) do begin\n case Look of\n 'i': DoIf;\n 'o': Other;\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNotice the reference to procedure Condition. Eventually, we'll\nwrite a routine that can parse and translate any Boolean con-\ndition we care to give it. But that's a whole installment by\nitself (the next one, in fact). For now, let's just make it a\ndummy that emits some text. Write the following routine:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Boolean Condition }\n{ This version is a dummy }\n\nProcedure Condition;\nbegin\n EmitLn('<condition>');\nend;\n{--------------------------------------------------------------}\n```\n\nInsert this procedure in your program just before DoIf. Now run\nthe program. Try a string like\n\n `aibece`\n\nAs you can see, the parser seems to recognize the construct and\ninserts the object code at the right places. Now try a set of\nnested IF's, like\n\n `aibicedefe`\n\nIt's starting to look real, eh?\n\nNow that we have the general idea (and the tools such as the\nnotation and the procedures NewLabel and PostLabel), it's a piece\nof cake to extend the parser to include other constructs. The\nfirst (and also one of the trickiest) is to add the ELSE clause\nto IF. The BNF is\n\n\n `IF <condition> <block> [ ELSE <block>] ENDIF`\n\n\nThe tricky part arises simply because there is an optional part,\nwhich doesn't occur in the other constructs.\n\nThe corresponding output code should be\n\n```\n <condition>\n BEQ L1\n <block>\n BRA L2\n L1: <block>\n L2: ...\n```\n\nThis leads us to the following syntax-directed translation:\n```\n\n IF\n <condition> { L1 = NewLabel;\n L2 = NewLabel;\n Emit(BEQ L1) }\n <block>\n ELSE { Emit(BRA L2);\n PostLabel(L1) }\n <block>\n ENDIF { PostLabel(L2) }\n\n```\nComparing this with the case for an ELSE-less IF gives us a clue\nas to how to handle both situations. The code below does it.\n(Note that I use an 'l' for the ELSE, since 'e' is otherwise\noccupied):\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure DoIf;\nvar L1, L2: string;\nbegin\n Match('i');\n Condition;\n L1 := NewLabel;\n L2 := L1;\n EmitLn('BEQ ' + L1);\n Block;\n if Look = 'l' then begin\n Match('l');\n L2 := NewLabel;\n EmitLn('BRA ' + L2);\n PostLabel(L1);\n Block;\n end;\n Match('e');\n PostLabel(L2);\nend;\n{--------------------------------------------------------------}\n```\n\nThere you have it. A complete IF parser/translator, in 19 lines\nof code.\n\nGive it a try now. Try something like\n\n `aiblcede`\n\nDid it work? Now, just to be sure we haven't broken the ELSE-\nless case, try\n\n `aibece`\n\nNow try some nested IF's. Try anything you like, including some\nbadly formed statements. Just remember that 'e' is not a legal\n\"other\" statement."},{title:"THE WHILE STATEMENT",path:"/the-while-statement",content:"The next type of statement should be easy, since we already have\nthe process down pat. The syntax I've chosen for the WHILE\nstatement is\n\n\n `WHILE <condition> <block> ENDWHILE`\n\n\nI know, I know, we don't REALLY need separate kinds of ter-\nminators for each construct ... you can see that by the fact that\nin our one-character version, 'e' is used for all of them. But I\nalso remember MANY debugging sessions in Pascal, trying to track\ndown a wayward END that the compiler obviously thought I meant to\nput somewhere else. It's been my experience that specific and\nunique keywords, although they add to the vocabulary of the\nlanguage, give a bit of error-checking that is worth the extra\nwork for the compiler writer.\n\nNow, consider what the WHILE should be translated into. It\nshould be:\n\n```\n L1: <condition>\n BEQ L2\n <block>\n BRA L1\n L2:\n```\n\n\n\nAs before, comparing the two representations gives us the actions\nneeded at each point.\n\n```\n WHILE { L1 = NewLabel;\n PostLabel(L1) }\n <condition> { Emit(BEQ L2) }\n <block>\n ENDWHILE { Emit(BRA L1);\n PostLabel(L2) }\n```\n\nThe code follows immediately from the syntax:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a WHILE Statement }\n\nprocedure DoWhile;\nvar L1, L2: string;\nbegin\n Match('w');\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n Condition;\n EmitLn('BEQ ' + L2);\n Block;\n Match('e');\n EmitLn('BRA ' + L1);\n PostLabel(L2);\nend;\n{--------------------------------------------------------------}\n```\n\nSince we've got a new statement, we have to add a call to it\nwithin procedure Block:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block;\nbegin\n while not(Look in ['e', 'l']) do begin\n case Look of\n 'i': DoIf;\n 'w': DoWhile;\n else Other;\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNo other changes are necessary.\n\nOK, try the new program. Note that this time, the <condition>\ncode is INSIDE the upper label, which is just where we wanted it.\nTry some nested loops. Try some loops within IF's, and some IF's\nwithin loops. If you get a bit confused as to what you should\ntype, don't be discouraged: you write bugs in other languages,\ntoo, don't you? It'll look a lot more meaningful when we get\nfull keywords.\n\nI hope by now that you're beginning to get the idea that this\nreally IS easy. All we have to do to accomodate a new construct\nis to work out the syntax-directed translation of it. The code\nalmost falls out from there, and it doesn't affect any of the\nother routines. Once you've gotten the feel of the thing, you'll\nsee that you can add new constructs about as fast as you can\ndream them up."},{title:"THE LOOP STATEMENT",path:"/the-loop-statement",content:"We could stop right here, and have a language that works. It's\nbeen shown many times that a high-order language with only two\nconstructs, the IF and the WHILE, is sufficient to write struc-\ntured code. But we're on a roll now, so let's richen up the\nrepertoire a bit.\n\nThis construct is even easier, since it has no condition test at\nall ... it's an infinite loop. What's the point of such a loop?\nNot much, by itself, but later on we're going to add a BREAK\ncommand, that will give us a way out. This makes the language\nconsiderably richer than Pascal, which has no break, and also\navoids the funny WHILE(1) or WHILE TRUE of C and Pascal.\n\nThe syntax is simply\n\n `LOOP <block> ENDLOOP`\n\nand the syntax-directed translation is:\n\n```\n LOOP { L = NewLabel;\n PostLabel(L) }\n <block>\n ENDLOOP { Emit(BRA L }\n```\n\nThe corresponding code is shown below. Since I've already used\n'l' for the ELSE, I've used the last letter, 'p', as the\n\"keyword\" this time.\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a LOOP Statement }\n\nprocedure DoLoop;\nvar L: string;\nbegin\n Match('p');\n L := NewLabel;\n PostLabel(L);\n Block;\n Match('e');\n EmitLn('BRA ' + L);\nend;\n{--------------------------------------------------------------}\n``` \n\nWhen you insert this routine, don't forget to add a line in Block\nto call it."},{title:"REPEAT-UNTIL",path:"/repeat-until",content:"Here's one construct that I lifted right from Pascal. The syntax\nis\n\n\n `REPEAT <block> UNTIL <condition>` ,\n\n\nand the syntax-directed translation is:\n\n```\n REPEAT { L = NewLabel;\n PostLabel(L) }\n <block>\n UNTIL\n <condition> { Emit(BEQ L) }\n```\n\nAs usual, the code falls out pretty easily:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a REPEAT Statement }\n\nprocedure DoRepeat;\nvar L: string;\nbegin\n Match('r');\n L := NewLabel;\n PostLabel(L);\n Block;\n Match('u');\n Condition;\n EmitLn('BEQ ' + L);\nend;\n{--------------------------------------------------------------}\n```\n\nAs before, we have to add the call to DoRepeat within Block.\nThis time, there's a difference, though. I decided to use 'r'\nfor REPEAT (naturally), but I also decided to use 'u' for UNTIL.\nThis means that the 'u' must be added to the set of characters in\nthe while-test. These are the characters that signal an exit\nfrom the current block ... the \"follow\" characters, in compiler\njargon.\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block;\nbegin\n while not(Look in ['e', 'l', 'u']) do begin\n case Look of\n 'i': DoIf;\n 'w': DoWhile;\n 'p': DoLoop;\n 'r': DoRepeat;\n else Other;\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```"},{title:"THE FOR LOOP",path:"/the-for-loop",content:"The FOR loop is a very handy one to have around, but it's a bear\nto translate. That's not so much because the construct itself is\nhard ... it's only a loop after all ... but simply because it's\nhard to implement in assembler language. Once the code is\nfigured out, the translation is straightforward enough.\n\nC fans love the FOR-loop of that language (and, in fact, it's\neasier to code), but I've chosen instead a syntax very much like\nthe one from good ol' BASIC:\n\n```\n FOR <ident> = <expr1> TO <expr2> <block> ENDFOR\n```\n\nThe translation of a FOR loop can be just about as difficult as\nyou choose to make it, depending upon the way you decide to\ndefine the rules as to how to handle the limits. Does expr2 get\nevaluated every time through the loop, for example, or is it\ntreated as a constant limit? Do you always go through the loop\nat least once, as in FORTRAN, or not? It gets simpler if you\nadopt the point of view that the construct is equivalent to:\n\n```\n <ident> = <expr1>\n TEMP = <expr2>\n WHILE <ident> <= TEMP\n <block>\n ENDWHILE\n```\n\nNotice that with this definition of the loop, `<block>` will not be\nexecuted at all if `<expr1>` is initially larger than `<expr2>`.\n \nThe 68000 code needed to do this is trickier than anything we've\ndone so far. I had a couple of tries at it, putting both the\ncounter and the upper limit on the stack, both in registers,\netc. I finally arrived at a hybrid arrangement, in which the\nloop counter is in memory (so that it can be accessed within the\nloop), and the upper limit is on the stack. The translated code\ncame out like this:\n\n```\n <ident> get name of loop counter\n <expr1> get initial value\n LEA <ident>(PC),A0 address the loop counter\n SUBQ #1,D0 predecrement it\n MOVE D0,(A0) save it\n <expr1> get upper limit\n MOVE D0,-(SP) save it on stack\n\n L1: LEA <ident>(PC),A0 address loop counter\n MOVE (A0),D0 fetch it to D0\n ADDQ #1,D0 bump the counter\n MOVE D0,(A0) save new value\n CMP (SP),D0 check for range\n BLE L2 skip out if D0 > (SP)\n <block>\n BRA L1 loop for next pass\n L2: ADDQ #2,SP clean up the stack\n```\n\nWow! That seems like a lot of code ... the line containing\n`<block>` seems to almost get lost. But that's the best I could do\nwith it. I guess it helps to keep in mind that it's really only\nsixteen words, after all. If anyone else can optimize this\nbetter, please let me know.\n\nStill, the parser routine is pretty easy now that we have the\ncode:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a FOR Statement }\n\nprocedure DoFor;\nvar L1, L2: string;\n Name: char;\nbegin\n Match('f');\n L1 := NewLabel;\n L2 := NewLabel;\n Name := GetName;\n Match('=');\n Expression;\n EmitLn('SUBQ #1,D0');\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)');\n Expression;\n EmitLn('MOVE D0,-(SP)');\n PostLabel(L1);\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE (A0),D0');\n EmitLn('ADDQ #1,D0');\n EmitLn('MOVE D0,(A0)');\n EmitLn('CMP (SP),D0');\n EmitLn('BGT ' + L2);\n Block;\n Match('e');\n EmitLn('BRA ' + L1);\n PostLabel(L2);\n EmitLn('ADDQ #2,SP');\nend;\n{--------------------------------------------------------------}\n```\n\nSince we don't have expressions in this parser, I used the same\ntrick as for Condition, and wrote the routine\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate an Expression }\n{ This version is a dummy }\n\nProcedure Expression;\nbegin\n EmitLn('<expr>');\nend;\n{--------------------------------------------------------------}\n```\n\nGive it a try. Once again, don't forget to add the call in\nBlock. Since we don't have any input for the dummy version of\nExpression, a typical input line would look something like\n\n `afi=bece`\n\nWell, it DOES generate a lot of code, doesn't it? But at least\nit's the RIGHT code."},{title:"THE DO STATEMENT",path:"/the-do-statement",content:"All this made me wish for a simpler version of the FOR loop. The\nreason for all the code above is the need to have the loop\ncounter accessible as a variable within the loop. If all we need\nis a counting loop to make us go through something a specified\nnumber of times, but don't need access to the counter itself,\nthere is a much easier solution. The 68000 has a \"decrement and\nbranch nonzero\" instruction built in which is ideal for counting.\nFor good measure, let's add this construct, too. This will be\nthe last of our loop structures.\n \nThe syntax and its translation is:\n\n```\n DO\n <expr> { Emit(SUBQ #1,D0);\n L = NewLabel;\n PostLabel(L);\n Emit(MOVE D0,-(SP) }\n <block>\n ENDDO { Emit(MOVE (SP)+,D0;\n Emit(DBRA D0,L) }\n\n```\nThat's quite a bit simpler! The loop will execute `<expr>` times.\nHere's the code:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a DO Statement }\n\nprocedure Dodo;\nvar L: string;\nbegin\n Match('d');\n L := NewLabel;\n Expression;\n EmitLn('SUBQ #1,D0');\n PostLabel(L);\n EmitLn('MOVE D0,-(SP)');\n Block;\n EmitLn('MOVE (SP)+,D0');\n EmitLn('DBRA D0,' + L);\nend;\n{--------------------------------------------------------------}\n```\n\nI think you'll have to agree, that's a whole lot simpler than the\nclassical FOR. Still, each construct has its place."},{title:"THE BREAK STATEMENT",path:"/the-break-statement",content:"Earlier I promised you a BREAK statement to accompany LOOP. This\nis one I'm sort of proud of. On the face of it a BREAK seems\nreally tricky. My first approach was to just use it as an extra\nterminator to Block, and split all the loops into two parts, just\nas I did with the ELSE half of an IF. That turns out not to\nwork, though, because the BREAK statement is almost certainly not\ngoing to show up at the same level as the loop itself. The most\nlikely place for a BREAK is right after an IF, which would cause\nit to exit to the IF construct, not the enclosing loop. WRONG.\nThe BREAK has to exit the inner LOOP, even if it's nested down\ninto several levels of IFs.\n \nMy next thought was that I would just store away, in some global\nvariable, the ending label of the innermost loop. That doesn't\nwork either, because there may be a break from an inner loop\nfollowed by a break from an outer one. Storing the label for the\ninner loop would clobber the label for the outer one. So the\nglobal variable turned into a stack. Things were starting to get\nmessy.\n\nThen I decided to take my own advice. Remember in the last\nsession when I pointed out how well the implicit stack of a\nrecursive descent parser was serving our needs? I said that if\nyou begin to see the need for an external stack you might be\ndoing something wrong. Well, I was. It is indeed possible to\nlet the recursion built into our parser take care of everything,\nand the solution is so simple that it's surprising.\n\nThe secret is to note that every BREAK statement has to occur\nwithin a block ... there's no place else for it to be. So all we\nhave to do is to pass into Block the exit address of the\ninnermost loop. Then it can pass the address to the routine that\ntranslates the break instruction. Since an IF statement doesn't\nchange the loop level, procedure DoIf doesn't need to do anything\nexcept pass the label into ITS blocks (both of them). Since\nloops DO change the level, each loop construct simply ignores\nwhatever label is above it and passes its own exit label along.\n\nAll this is easier to show you than it is to describe. I'll\ndemonstrate with the easiest loop, which is LOOP:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a LOOP Statement }\n\nprocedure DoLoop;\nvar L1, L2: string;\nbegin\n Match('p');\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n Block(L2);\n Match('e');\n EmitLn('BRA ' + L1);\n PostLabel(L2);\nend;\n{--------------------------------------------------------------}\n```\n\nNotice that DoLoop now has TWO labels, not just one. The second\nis to give the BREAK instruction a target to jump to. If there\nis no BREAK within the loop, we've wasted a label and cluttered\nup things a bit, but there's no harm done.\n\nNote also that Block now has a parameter, which for loops will\nalways be the exit address. The new version of Block is:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block(L: string);\nbegin\n while not(Look in ['e', 'l', 'u']) do begin\n case Look of\n 'i': DoIf(L);\n 'w': DoWhile;\n 'p': DoLoop;\n 'r': DoRepeat;\n 'f': DoFor;\n 'd': DoDo;\n 'b': DoBreak(L);\n else Other;\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nAgain, notice that all Block does with the label is to pass it\ninto DoIf and DoBreak. The loop constructs don't need it,\nbecause they are going to pass their own label anyway.\n\nThe new version of DoIf is:\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block(L: string); Forward;\n\n\nprocedure DoIf(L: string);\nvar L1, L2: string;\nbegin\n Match('i');\n Condition;\n L1 := NewLabel;\n L2 := L1;\n EmitLn('BEQ ' + L1);\n Block(L);\n if Look = 'l' then begin\n Match('l');\n L2 := NewLabel;\n EmitLn('BRA ' + L2);\n PostLabel(L1);\n Block(L);\n end;\n Match('e');\n PostLabel(L2);\nend;\n{--------------------------------------------------------------}\n```\n\nHere, the only thing that changes is the addition of the\nparameter to procedure Block. An IF statement doesn't change the\nloop nesting level, so DoIf just passes the label along. No\nmatter how many levels of IF nesting we have, the same label will\nbe used.\n\nNow, remember that DoProgram also calls Block, so it now needs to\npass it a label. An attempt to exit the outermost block is an\nerror, so DoProgram passes a null label which is caught by\nDoBreak:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate a BREAK }\n\nprocedure DoBreak(L: string);\nbegin\n Match('b');\n if L <> '' then\n EmitLn('BRA ' + L)\n else Abort('No loop to break from');\nend;\n\n\n{--------------------------------------------------------------}\n\n{ Parse and Translate a Program }\n\nprocedure DoProgram;\nbegin\n Block('');\n if Look <> 'e' then Expected('End');\n EmitLn('END')\nend;\n{--------------------------------------------------------------}\n```\n\nThat ALMOST takes care of everything. Give it a try, see if you\ncan \"break\" it `<pun>`. Careful, though. By this time we've used\nso many letters, it's hard to think of characters that aren't now\nrepresenting reserved words. Remember: before you try the\nprogram, you're going to have to edit every occurence of Block in\nthe other loop constructs to include the new parameter. Do it\njust like I did for LOOP.\n\nI said ALMOST above. There is one slight problem: if you take a\nhard look at the code generated for DO, you'll see that if you\nbreak out of this loop, the value of the loop counter is still\nleft on the stack. We're going to have to fix that! A shame ...\nthat was one of our smaller routines, but it can't be helped.\nHere's a version that doesn't have the problem:\n\n\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a DO Statement }\n\nprocedure Dodo;\nvar L1, L2: string;\nbegin\n Match('d');\n L1 := NewLabel;\n L2 := NewLabel;\n Expression;\n EmitLn('SUBQ #1,D0');\n PostLabel(L1);\n EmitLn('MOVE D0,-(SP)');\n Block(L2);\n EmitLn('MOVE (SP)+,D0');\n EmitLn('DBRA D0,' + L1);\n EmitLn('SUBQ #2,SP');\n PostLabel(L2);\n EmitLn('ADDQ #2,SP');\nend;\n{--------------------------------------------------------------}\n```\n\nThe two extra instructions, the SUBQ and ADDQ, take care of\nleaving the stack in the right shape.\n "},{title:"CONCLUSION",path:"/conclusion",content:"At this point we have created a number of control constructs ...\na richer set, really, than that provided by almost any other pro-\ngramming language. And, except for the FOR loop, it was pretty\neasy to do. Even that one was tricky only because it's tricky in\nassembler language.\n\nI'll conclude this session here. To wrap the thing up with a red\nribbon, we really should have a go at having real keywords\ninstead of these mickey-mouse single-character things. You've\nalready seen that the extension to multi-character words is not\ndifficult, but in this case it will make a big difference in the\nappearance of our input code. I'll save that little bit for the\nnext installment. In that installment we'll also address Boolean\nexpressions, so we can get rid of the dummy version of Condition\nthat we've used here. See you then.\n\nFor reference purposes, here is the completed parser for this\nsession:\n\n\n\n```delphi\n{--------------------------------------------------------------}\nprogram Branch;\n\n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n CR = ^M;\n\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look : char; { Lookahead Character }\n Lcount: integer; { Label Counter }\n\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n\n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n\nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n if Look = x then GetChar\n else Expected('''' + x + '''');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n \n\n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB];\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: char;\nbegin\n if not IsAlpha(Look) then Expected('Name');\n GetName := UpCase(Look);\n GetChar;\nend;\n\n\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: char;\nbegin\n if not IsDigit(Look) then Expected('Integer');\n GetNum := Look;\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Generate a Unique Label }\n\nfunction NewLabel: string;\nvar S: string;\nbegin\n Str(LCount, S);\n NewLabel := 'L' + S;\n Inc(LCount);\nend;\n\n\n{--------------------------------------------------------------}\n{ Post a Label To Output }\n\nprocedure PostLabel(L: string);\nbegin\n WriteLn(L, ':');\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n\nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n{--------------------------------------------------------------}\n\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Boolean Condition }\n\nprocedure Condition;\nbegin\n EmitLn('<condition>');\nend;\n\n \n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Math Expression }\n\nprocedure Expression;\nbegin\n EmitLn('<expr>');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block(L: string); Forward;\n\n\nprocedure DoIf(L: string);\nvar L1, L2: string;\nbegin\n Match('i');\n Condition;\n L1 := NewLabel;\n L2 := L1;\n EmitLn('BEQ ' + L1);\n Block(L);\n if Look = 'l' then begin\n Match('l');\n L2 := NewLabel;\n EmitLn('BRA ' + L2);\n PostLabel(L1);\n Block(L);\n end;\n Match('e');\n PostLabel(L2);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a WHILE Statement }\n\nprocedure DoWhile;\nvar L1, L2: string;\nbegin\n Match('w');\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n Condition;\n EmitLn('BEQ ' + L2);\n Block(L2);\n Match('e');\n EmitLn('BRA ' + L1);\n PostLabel(L2);\nend;\n \n\n{--------------------------------------------------------------}\n{ Parse and Translate a LOOP Statement }\n\nprocedure DoLoop;\nvar L1, L2: string;\nbegin\n Match('p');\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n Block(L2);\n Match('e');\n EmitLn('BRA ' + L1);\n PostLabel(L2);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a REPEAT Statement }\n\nprocedure DoRepeat;\nvar L1, L2: string;\nbegin\n Match('r');\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n Block(L2);\n Match('u');\n Condition;\n EmitLn('BEQ ' + L1);\n PostLabel(L2);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a FOR Statement }\n\nprocedure DoFor;\nvar L1, L2: string;\n Name: char;\nbegin\n Match('f');\n L1 := NewLabel;\n L2 := NewLabel;\n Name := GetName;\n Match('=');\n Expression;\n EmitLn('SUBQ #1,D0');\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)');\n Expression;\n EmitLn('MOVE D0,-(SP)');\n PostLabel(L1);\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE (A0),D0');\n EmitLn('ADDQ #1,D0');\n EmitLn('MOVE D0,(A0)');\n EmitLn('CMP (SP),D0');\n EmitLn('BGT ' + L2);\n Block(L2);\n Match('e');\n EmitLn('BRA ' + L1);\n PostLabel(L2);\n EmitLn('ADDQ #2,SP');\nend;\n\n\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a DO Statement }\n\nprocedure Dodo;\nvar L1, L2: string;\nbegin\n Match('d');\n L1 := NewLabel;\n L2 := NewLabel;\n Expression;\n EmitLn('SUBQ #1,D0');\n PostLabel(L1);\n EmitLn('MOVE D0,-(SP)');\n Block(L2);\n EmitLn('MOVE (SP)+,D0');\n EmitLn('DBRA D0,' + L1);\n EmitLn('SUBQ #2,SP');\n PostLabel(L2);\n EmitLn('ADDQ #2,SP');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a BREAK }\n\nprocedure DoBreak(L: string);\nbegin\n Match('b');\n EmitLn('BRA ' + L);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an \"Other\" }\n\nprocedure Other;\nbegin\n EmitLn(GetName);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block(L: string);\nbegin\n while not(Look in ['e', 'l', 'u']) do begin\n case Look of\n 'i': DoIf(L);\n 'w': DoWhile;\n 'p': DoLoop;\n 'r': DoRepeat;\n 'f': DoFor;\n 'd': DoDo;\n 'b': DoBreak(L);\n else Other;\n end;\n end;\nend;\n\n\n\n\n{--------------------------------------------------------------}\n\n{ Parse and Translate a Program }\n\nprocedure DoProgram;\nbegin\n Block('');\n if Look <> 'e' then Expected('End');\n EmitLn('END')\nend;\n\n\n{--------------------------------------------------------------}\n\n{ Initialize }\n\nprocedure Init;\nbegin\n LCount := 0;\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n DoProgram;\nend.\n{--------------------------------------------------------------}\n```"}]},{title:"Part VI: BOOLEAN EXPRESSIONS - 31 August 1988",path:"/part-vi-boolean-expressions-31-august-1988",items:[{title:"INTRODUCTION",path:"/introduction",content:"In Part V of this series, we took a look at control constructs,\nand developed parsing routines to translate them into object\ncode. We ended up with a nice, relatively rich set of\nconstructs.\n\nAs we left the parser, though, there was one big hole in our\ncapabilities: we did not address the issue of the branch\ncondition. To fill the void, I introduced to you a dummy parse\nroutine called Condition, which only served as a place-keeper for\nthe real thing.\n\nOne of the things we'll do in this session is to plug that hole\nby expanding Condition into a true parser/translator."},{title:"THE PLAN",path:"/the-plan",content:"We're going to approach this installment a bit differently than\nany of the others. In those other installments, we started out\nimmediately with experiments using the Pascal compiler, building\nup the parsers from very rudimentary beginnings to their final\nforms, without spending much time in planning beforehand. That's\ncalled coding without specs, and it's usually frowned upon. We\ncould get away with it before because the rules of arithmetic are\npretty well established ... we know what a '+' sign is supposed\nto mean without having to discuss it at length. The same is true\nfor branches and loops. But the ways in which programming\nlanguages implement logic vary quite a bit from language to\nlanguage. So before we begin serious coding, we'd better first\nmake up our minds what it is we want. And the way to do that is\nat the level of the BNF syntax rules (the GRAMMAR)."},{title:"THE GRAMMAR",path:"/the-grammar",content:"For some time now, we've been implementing BNF syntax equations\nfor arithmetic expressions, without ever actually writing them\ndown all in one place. It's time that we did so. They are:\n\n```\n <expression> ::= <unary op> <term> [<addop> <term>]*\n <term> ::= <factor> [<mulop> factor]*\n <factor> ::= <integer> | <variable> | ( <expression> )\n```\n(Remember, the nice thing about this grammar is that it enforces\nthe operator precedence hierarchy that we normally expect for\nalgebra.)\n\nActually, while we're on the subject, I'd like to amend this\ngrammar a bit right now. The way we've handled the unary minus\nis a bit awkward. I've found that it's better to write the\ngrammar this way:\n\n```\n <expression> ::= <term> [<addop> <term>]*\n <term> ::= <signed factor> [<mulop> factor]*\n <signed factor> ::= [<addop>] <factor>\n <factor> ::= <integer> | <variable> | (<expression>)\n\n```\nThis puts the job of handling the unary minus onto Factor, which\nis where it really belongs.\n\nThis doesn't mean that you have to go back and recode the\nprograms you've already written, although you're free to do so if\nyou like. But I will be using the new syntax from now on.\n\nNow, it probably won't come as a shock to you to learn that we\ncan define an analogous grammar for Boolean algebra. A typical\nset or rules is:\n\n```\n <b-expression>::= <b-term> [<orop> <b-term>]*\n <b-term> ::= <not-factor> [AND <not-factor>]*\n <not-factor> ::= [NOT] <b-factor>\n <b-factor> ::= <b-literal> | <b-variable> | (<b-expression>)\n```\n\nNotice that in this grammar, the operator AND is analogous to\n'*', and OR (and exclusive OR) to '+'. The NOT operator is\nanalogous to a unary minus. This hierarchy is not absolutely\nstandard ... some languages, notably Ada, treat all logical\noperators as having the same precedence level ... but it seems\nnatural.\n\nNotice also the slight difference between the way the NOT and the\nunary minus are handled. In algebra, the unary minus is\nconsidered to go with the whole term, and so never appears but\nonce in a given term. So an expression like\n\n `a * -b`\n\nor worse yet,\n `a - -b`\n\nis not allowed. In Boolean algebra, though, the expression\n\n `a AND NOT b`\n\nmakes perfect sense, and the syntax shown allows for that."},{title:"RELOPS",path:"/relops",content:"OK, assuming that you're willing to accept the grammar I've shown\nhere, we now have syntax rules for both arithmetic and Boolean\nalgebra. The sticky part comes in when we have to combine the\ntwo. Why do we have to do that? Well, the whole subject came up\nbecause of the need to process the \"predicates\" (conditions)\nassociated with control statements such as the IF. The predicate\nis required to have a Boolean value; that is, it must evaluate to\neither TRUE or FALSE. The branch is then taken or not taken,\ndepending on that value. What we expect to see going on in\nprocedure Condition, then, is the evaluation of a Boolean\nexpression.\n\nBut there's more to it than that. A pure Boolean expression can\nindeed be the predicate of a control statement ... things like\n\n\n `IF a AND NOT b THEN ....`\n\n\nBut more often, we see Boolean algebra show up in such things as\n\n\n `IF (x >= 0) and (x <= 100) THEN ...`\n\n\nHere, the two terms in parens are Boolean expressions, but the\nindividual terms being compared: x, 0, and 100, are NUMERIC in\nnature. The RELATIONAL OPERATORS >= and <= are the catalysts by\nwhich the Boolean and the arithmetic ingredients get merged\ntogether.\n\nNow, in the example above, the terms being compared are just\nthat: terms. However, in general each side can be a math\nexpression. So we can define a RELATION to be:\n\n\n `<relation> ::= <expression> <relop> <expression>` ,\n\n\nwhere the expressions we're talking about here are the old\nnumeric type, and the relops are any of the usual symbols\n\n\n `=, <> (or !=), <, >, <=, and >=`\n\n\nIf you think about it a bit, you'll agree that, since this kind\nof predicate has a single Boolean value, TRUE or FALSE, as its\nresult, it is really just another kind of factor. So we can\nexpand the definition of a Boolean factor above to read:\n\n```\n <b-factor> ::= <b-literal>\n | <b-variable>\n | (<b-expression>)\n | <relation>\n```\n\nTHAT's the connection! The relops and the relation they define\nserve to wed the two kinds of algebra. It is worth noting that\nthis implies a hierarchy where the arithmetic expression has a\nHIGHER precedence that a Boolean factor, and therefore than all\nthe Boolean operators. If you write out the precedence levels\nfor all the operators, you arrive at the following list:\n\n --------------------------------------------\n Level Syntax Element Operator\n --------------------------------------------\n 0 factor literal, variable\n 1 signed factor unary minus\n 2 term *, /\n 3 expression +, -\n 4 b-factor literal, variable, relop\n 5 not-factor NOT\n 6 b-term AND\n 7 b-expression OR, XOR\n\n\nIf we're willing to accept that many precedence levels, this\n\n\ngrammar seems reasonable. Unfortunately, it won't work! The\ngrammar may be great in theory, but it's no good at all in the\npractice of a top-down parser. To see the problem, consider the\ncode fragment:\n\n\n `IF ((((((A + B + C) < 0 ) AND ....`\n\n\nWhen the parser is parsing this code, it knows after it sees the\nIF token that a Boolean expression is supposed to be next. So it\ncan set up to begin evaluating such an expression. But the first\nexpression in the example is an ARITHMETIC expression, A + B + C.\nWhat's worse, at the point that the parser has read this much of\nthe input line:\n\n\n `IF ((((((A` ,\n\n\nit still has no way of knowing which kind of expression it's\ndealing with. That won't do, because we must have different\nrecognizers for the two cases. The situation can be handled\nwithout changing any of our definitions, but only if we're\nwilling to accept an arbitrary amount of backtracking to work our\nway out of bad guesses. No compiler writer in his right mind\nwould agree to that.\n\nWhat's going on here is that the beauty and elegance of BNF\ngrammar has met face to face with the realities of compiler\ntechnology.\n\nTo deal with this situation, compiler writers have had to make\ncompromises so that a single parser can handle the grammar\nwithout backtracking."},{title:"FIXING THE GRAMMAR",path:"/fixing-the-grammar",content:"The problem that we've encountered comes up because our\ndefinitions of both arithmetic and Boolean factors permit the use\nof parenthesized expressions. Since the definitions are\nrecursive, we can end up with any number of levels of\nparentheses, and the parser can't know which kind of expression\nit's dealing with.\n\nThe solution is simple, although it ends up causing profound\nchanges to our grammar. We can only allow parentheses in one\nkind of factor. The way to do that varies considerably from\nlanguage to language. This is one place where there is NO\nagreement or convention to help us.\n\nWhen Niklaus Wirth designed Pascal, the desire was to limit the\nnumber of levels of precedence (fewer parse routines, after all).\nSo the OR and exclusive OR operators are treated just like an\nAddop and processed at the level of a math expression.\nSimilarly, the AND is treated like a Mulop and processed with\nTerm. The precedence levels are\n\n\n Level Syntax Element Operator\n\n 0 factor literal, variable\n 1 signed factor unary minus, NOT\n 2 term *, /, AND\n 3 expression +, -, OR\n\n\nNotice that there is only ONE set of syntax rules, applying to\nboth kinds of operators. According to this grammar, then,\nexpressions like\n\n `x + (y AND NOT z) DIV 3`\n\nare perfectly legal. And, in fact, they ARE ... as far as the\nparser is concerned. Pascal doesn't allow the mixing of\narithmetic and Boolean variables, and things like this are caught\nat the SEMANTIC level, when it comes time to generate code for\nthem, rather than at the syntax level.\n\nThe authors of C took a diametrically opposite approach: they\ntreat the operators as different, and have something much more\nakin to our seven levels of precedence. In fact, in C there are\nno fewer than 17 levels! That's because C also has the operators\n'=', '+=' and its kin, '<<', '>>', '++', '--', etc. Ironically,\nalthough in C the arithmetic and Boolean operators are treated\nseparately, the variables are NOT ... there are no Boolean or\nlogical variables in C, so a Boolean test can be made on any\ninteger value.\n\nWe'll do something that's sort of in-between. I'm tempted to\nstick mostly with the Pascal approach, since that seems the\nsimplest from an implementation point of view, but it results in\nsome funnies that I never liked very much, such as the fact that,\nin the expression\n\n `IF (c >= 'A') and (c <= 'Z') then ...`\n\nthe parens above are REQUIRED. I never understood why before,\nand neither my compiler nor any human ever explained it very\nwell, either. But now, we can all see that the 'and' operator,\nhaving the precedence of a multiply, has a higher one than the\nrelational operators, so without the parens the expression is\nequivalent to\n\n `IF c >= ('A' and c) <= 'Z' then`\n\nwhich doesn't make sense.\n\nIn any case, I've elected to separate the operators into\ndifferent levels, although not as many as in C.\n\n```\n <b-expression> ::= <b-term> [<orop> <b-term>]*\n <b-term> ::= <not-factor> [AND <not-factor>]*\n <not-factor> ::= [NOT] <b-factor>\n <b-factor> ::= <b-literal> | <b-variable> | <relation>\n <relation> ::= | <expression> [<relop> <expression]\n <expression> ::= <term> [<addop> <term>]*\n <term> ::= <signed factor> [<mulop> factor]*\n <signed factor>::= [<addop>] <factor>\n <factor> ::= <integer> | <variable> | (<b-expression>)\n```\n\nThis grammar results in the same set of seven levels that I\nshowed earlier. Really, it's almost the same grammar ... I just\nremoved the option of parenthesized b-expressions as a possible\nb-factor, and added the relation as a legal form of b-factor.\n\nThere is one subtle but crucial difference, which is what makes\nthe whole thing work. Notice the square brackets in the\ndefinition of a relation. This means that the relop and the\nsecond expression are OPTIONAL.\n\nA strange consequence of this grammar (and one shared by C) is\nthat EVERY expression is potentially a Boolean expression. The\nparser will always be looking for a Boolean expression, but will\n\"settle\" for an arithmetic one. To be honest, that's going to\nslow down the parser, because it has to wade through more layers\nof procedure calls. That's one reason why Pascal compilers tend\nto compile faster than C compilers. If it's raw speed you want,\nstick with the Pascal syntax."},{title:"THE PARSER",path:"/the-parser",content:"Now that we've gotten through the decision-making process, we can\npress on with development of a parser. You've done this with me\nseveral times now, so you know the drill: we begin with a fresh\ncopy of the cradle, and begin adding procedures one by one. So\nlet's do it.\n\nWe begin, as we did in the arithmetic case, by dealing only with\nBoolean literals rather than variables. This gives us a new kind\nof input token, so we're also going to need a new recognizer, and\na new procedure to read instances of that token type. Let's\nstart by defining the two new procedures:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize a Boolean Literal }\n\nfunction IsBoolean(c: char): Boolean;\nbegin\n IsBoolean := UpCase(c) in ['T', 'F'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Boolean Literal }\n\nfunction GetBoolean: Boolean;\nvar c: char;\nbegin\n if not IsBoolean(Look) then Expected('Boolean Literal');\n GetBoolean := UpCase(Look) = 'T';\n GetChar;\nend;\n{--------------------------------------------------------------}\n```\n\nType these routines into your program. You can test them by\nadding into the main program the print statement\n\n```delphi\n WriteLn(GetBoolean);\n\n```\n\n\nOK, compile the program and test it. As usual, it's not very\nimpressive so far, but it soon will be.\n\nNow, when we were dealing with numeric data we had to arrange to\ngenerate code to load the values into D0. We need to do the same\nfor Boolean data. The usual way to encode Boolean variables is\nto let 0 stand for FALSE, and some other value for TRUE. Many\nlanguages, such as C, use an integer 1 to represent it. But I\nprefer FFFF hex (or -1), because a bitwise NOT also becomes a\nBoolean NOT. So now we need to emit the right assembler code to\nload those values. The first cut at the Boolean expression\nparser (BoolExpression, of course) is:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Expression }\n\nprocedure BoolExpression;\nbegin\n if not IsBoolean(Look) then Expected('Boolean Literal');\n if GetBoolean then\n EmitLn('MOVE #-1,D0')\n else\n EmitLn('CLR D0');\nend;\n{---------------------------------------------------------------}\n```\n\nAdd this procedure to your parser, and call it from the main\nprogram (replacing the print statement you had just put there).\nAs you can see, we still don't have much of a parser, but the\noutput code is starting to look more realistic.\n\nNext, of course, we have to expand the definition of a Boolean\nexpression. We already have the BNF rule:\n\n```\n <b-expression> ::= <b-term> [<orop> <b-term>]*\n```\n\nI prefer the Pascal versions of the \"orops\", OR and XOR. But\nsince we are keeping to single-character tokens here, I'll encode\nthose with '|' and '~'. The next version of BoolExpression is\nalmost a direct copy of the arithmetic procedure Expression:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate a Boolean OR }\n\nprocedure BoolOr;\nbegin\n Match('|');\n BoolTerm;\n EmitLn('OR (SP)+,D0');\nend;\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Exclusive Or }\n\nprocedure BoolXor;\nbegin\n Match('~');\n BoolTerm;\n EmitLn('EOR (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Expression }\n\nprocedure BoolExpression;\nbegin\n BoolTerm;\n while IsOrOp(Look) do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '|': BoolOr;\n '~': BoolXor;\n end;\n end;\nend;\n{---------------------------------------------------------------}\n```\n\nNote the new recognizer IsOrOp, which is also a copy, this time\nof IsAddOp:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize a Boolean Orop }\n\nfunction IsOrop(c: char): Boolean;\nbegin\n IsOrop := c in ['|', '~'];\nend;\n{--------------------------------------------------------------}\n```\nOK, rename the old version of BoolExpression to BoolTerm, then\nenter the code above. Compile and test this version. At this\npoint, the output code is starting to look pretty good. Of\ncourse, it doesn't make much sense to do a lot of Boolean algebra\non constant values, but we'll soon be expanding the types of\nBooleans we deal with.\n\nYou've probably already guessed what the next step is: The\nBoolean version of Term.\n\nRename the current procedure BoolTerm to NotFactor, and enter the\nfollowing new version of BoolTerm. Note that is is much simpler\nthan the numeric version, since there is no equivalent of\ndivision.\n\n\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Term }\n\nprocedure BoolTerm;\nbegin\n NotFactor;\n while Look = '&' do begin\n EmitLn('MOVE D0,-(SP)');\n Match('&');\n NotFactor;\n EmitLn('AND (SP)+,D0');\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNow, we're almost home. We are translating complex Boolean\nexpressions, although only for constant values. The next step is\nto allow for the NOT. Write the following procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Boolean Factor with NOT }\n\nprocedure NotFactor;\nbegin\n if Look = '!' then begin\n Match('!');\n BoolFactor;\n EmitLn('EOR #-1,D0');\n end\n else\n BoolFactor;\nend;\n{--------------------------------------------------------------}\n```\nAnd rename the earlier procedure to BoolFactor. Now try that.\nAt this point the parser should be able to handle any Boolean\nexpression you care to throw at it. Does it? Does it trap badly\nformed expressions?\n\nIf you've been following what we did in the parser for math\nexpressions, you know that what we did next was to expand the\ndefinition of a factor to include variables and parens. We don't\nhave to do that for the Boolean factor, because those little\nitems get taken care of by the next step. It takes just a one\nline addition to BoolFactor to take care of relations:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Boolean Factor }\n\nprocedure BoolFactor;\nbegin\n if IsBoolean(Look) then\n if GetBoolean then\n EmitLn('MOVE #-1,D0')\n else\n EmitLn('CLR D0')\n else Relation;\nend;\n{--------------------------------------------------------------}\n```\n\nYou might be wondering when I'm going to provide for Boolean\nvariables and parenthesized Boolean expressions. The answer is,\nI'm NOT! Remember, we took those out of the grammar earlier.\nRight now all I'm doing is encoding the grammar we've already\nagreed upon. The compiler itself can't tell the difference\nbetween a Boolean variable or expression and an arithmetic one\n... all of those will be handled by Relation, either way.\n\n\nOf course, it would help to have some code for Relation. I don't\nfeel comfortable, though, adding any more code without first\nchecking out what we already have. So for now let's just write a\ndummy version of Relation that does nothing except eat the\ncurrent character, and write a little message:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Relation }\n\nprocedure Relation;\nbegin\n WriteLn('<Relation>');\n GetChar;\nend;\n{--------------------------------------------------------------}\n```\nOK, key in this code and give it a try. All the old things\nshould still work ... you should be able to generate the code for\nANDs, ORs, and NOTs. In addition, if you type any alphabetic\ncharacter you should get a little <Relation> place-holder, where\na Boolean factor should be. Did you get that? Fine, then let's\nmove on to the full-blown version of Relation.\n\nTo get that, though, there is a bit of groundwork that we must\nlay first. Recall that a relation has the form\n\n```\n <relation> ::= | <expression> [<relop> <expression]\n```\n\nSince we have a new kind of operator, we're also going to need a\nnew Boolean function to recognize it. That function is shown\nbelow. Because of the single-character limitation, I'm sticking\nto the four operators that can be encoded with such a character\n(the \"not equals\" is encoded by '#').\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize a Relop }\n\nfunction IsRelop(c: char): Boolean;\nbegin\n IsRelop := c in ['=', '#', '<', '>'];\nend;\n{--------------------------------------------------------------}\n\n```\nNow, recall that we're using a zero or a -1 in register D0 to\nrepresent a Boolean value, and also that the loop constructs\nexpect the flags to be set to correspond. In implementing all\nthis on the 68000, things get a a little bit tricky.\n\nSince the loop constructs operate only on the flags, it would be\nnice (and also quite efficient) just to set up those flags, and\n\n\nnot load anything into D0 at all. This would be fine for the\nloops and branches, but remember that the relation can be used\nANYWHERE a Boolean factor could be used. We may be storing its\nresult to a Boolean variable. Since we can't know at this point\nhow the result is going to be used, we must allow for BOTH cases.\n\nComparing numeric data is easy enough ... the 68000 has an\noperation for that ... but it sets the flags, not a value.\nWhat's more, the flags will always be set the same (zero if\nequal, etc.), while we need the zero flag set differently for the\neach of the different relops.\n\nThe solution is found in the 68000 instruction Scc, which sets a\nbyte value to 0000 or FFFF (funny how that works!) depending upon\nthe result of the specified condition. If we make the\ndestination byte to be D0, we get the Boolean value needed.\n\nUnfortunately, there's one final complication: unlike almost\nevery other instruction in the 68000 set, Scc does NOT reset the\ncondition flags to match the data being stored. So we have to do\none last step, which is to test D0 and set the flags to match it.\nIt must seem to be a trip around the moon to get what we want: we\nfirst perform the test, then test the flags to set data into D0,\nthen test D0 to set the flags again. It is sort of roundabout,\nbut it's the most straightforward way to get the flags right, and\nafter all it's only a couple of instructions.\n\nI might mention here that this area is, in my opinion, the one\nthat represents the biggest difference between the efficiency of\nhand-coded assembler language and compiler-generated code. We\nhave seen already that we lose efficiency in arithmetic\noperations, although later I plan to show you how to improve that\na bit. We've also seen that the control constructs themselves\ncan be done quite efficiently ... it's usually very difficult to\nimprove on the code generated for an IF or a WHILE. But\nvirtually every compiler I've ever seen generates terrible code,\ncompared to assembler, for the computation of a Boolean function,\nand particularly for relations. The reason is just what I've\nhinted at above. When I'm writing code in assembler, I go ahead\nand perform the test the most convenient way I can, and then set\nup the branch so that it goes the way it should. In effect, I\n\"tailor\" every branch to the situation. The compiler can't do\nthat (practically), and it also can't know that we don't want to\nstore the result of the test as a Boolean variable. So it must\ngenerate the code in a very strict order, and it often ends up\nloading the result as a Boolean that never gets used for\nanything.\n\nIn any case, we're now ready to look at the code for Relation.\nIt's shown below with its companion procedures:\n\n```delphi\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Equals\" }\n\nprocedure Equals;\nbegin\n Match('=');\n Expression;\n EmitLn('CMP (SP)+,D0');\n EmitLn('SEQ D0');\nend;\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Not Equals\" }\n\nprocedure NotEquals;\nbegin\n Match('#');\n Expression;\n EmitLn('CMP (SP)+,D0');\n EmitLn('SNE D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Less Than\" }\n\nprocedure Less;\nbegin\n Match('<');\n Expression;\n EmitLn('CMP (SP)+,D0');\n EmitLn('SGE D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Greater Than\" }\n\nprocedure Greater;\nbegin\n Match('>');\n Expression;\n EmitLn('CMP (SP)+,D0');\n EmitLn('SLE D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Relation }\n\nprocedure Relation;\nbegin\n Expression;\n if IsRelop(Look) then begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '=': Equals;\n '#': NotEquals;\n '<': Less;\n '>': Greater;\n end;\n EmitLn('TST D0');\n end;\nend;\n{---------------------------------------------------------------}\n```\nNow, that call to Expression looks familiar! Here is where the\neditor of your system comes in handy. We have already generated\ncode for Expression and its buddies in previous sessions. You\ncan copy them into your file now. Remember to use the single-\ncharacter versions. Just to be certain, I've duplicated the\narithmetic procedures below. If you're observant, you'll also\nsee that I've changed them a little to make them correspond to\nthe latest version of the syntax. This change is NOT necessary,\nso you may prefer to hold off on that until you're sure\n\n\neverything is working.\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Identifier }\n\nprocedure Ident;\nvar Name: char;\nbegin\n Name:= GetName;\n if Look = '(' then begin\n Match('(');\n Match(')');\n EmitLn('BSR ' + Name);\n end\n else\n EmitLn('MOVE ' + Name + '(PC),D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Expression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n Ident\n else\n EmitLn('MOVE #' + GetNum + ',D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate the First Math Factor }\n\n\nprocedure SignedFactor;\nbegin\n if Look = '+' then\n GetChar;\n if Look = '-' then begin\n GetChar;\n if IsDigit(Look) then\n EmitLn('MOVE #-' + GetNum + ',D0')\n else begin\n Factor;\n EmitLn('NEG D0');\n end;\n end\n else Factor;\nend;\n\n\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nprocedure Multiply;\nbegin\n Match('*');\n Factor;\n EmitLn('MULS (SP)+,D0');\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nprocedure Divide;\nbegin\n Match('/');\n Factor;\n EmitLn('MOVE (SP)+,D1');\n EmitLn('EXS.L D0');\n EmitLn('DIVS D1,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nprocedure Term;\nbegin\n SignedFactor;\n while Look in ['*', '/'] do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '*': Multiply;\n '/': Divide;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Match('+');\n Term;\n EmitLn('ADD (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n EmitLn('SUB (SP)+,D0');\n EmitLn('NEG D0');\nend;\n\n\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n Term;\n while IsAddop(Look) do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '+': Add;\n '-': Subtract;\n end;\n end;\nend;\n{---------------------------------------------------------------}\n```\n\nThere you have it ... a parser that can handle both arithmetic\nAND Boolean algebra, and things that combine the two through the\nuse of relops. I suggest you file away a copy of this parser in\na safe place for future reference, because in our next step we're\ngoing to be chopping it up."},{title:"MERGING WITH CONTROL CONSTRUCTS",path:"/merging-with-control-constructs",content:"At this point, let's go back to the file we had previously built\nthat parses control constructs. Remember those little dummy\nprocedures called Condition and Expression? Now you know what\ngoes in their places!\n\nI warn you, you're going to have to do some creative editing\nhere, so take your time and get it right. What you need to do is\nto copy all of the procedures from the logic parser, from Ident\nthrough BoolExpression, into the parser for control constructs.\nInsert them at the current location of Condition. Then delete\nthat procedure, as well as the dummy Expression. Next, change\nevery call to Condition to refer to BoolExpression instead.\nFinally, copy the procedures IsMulop, IsOrOp, IsRelop, IsBoolean,\nand GetBoolean into place. That should do it.\n\nCompile the resulting program and give it a try. Since we\nhaven't used this program in awhile, don't forget that we used\nsingle-character tokens for IF, WHILE, etc. Also don't forget\nthat any letter not a keyword just gets echoed as a block.\n\nTry `ia=bxlye`\n\nwhich stands for \"IF a=b X ELSE Y ENDIF\".\n\nWhat do you think? Did it work? Try some others."},{title:"ADDING ASSIGNMENTS",path:"/adding-assignments",content:"As long as we're this far, and we already have the routines for\nexpressions in place, we might as well replace the \"blocks\" with\nreal assignment statements. We've already done that before, so\nit won't be too hard. Before taking that step, though, we need\nto fix something else.\n\n\n\nWe're soon going to find that the one-line \"programs\" that we're\nhaving to write here will really cramp our style. At the moment\nwe have no cure for that, because our parser doesn't recognize\nthe end-of-line characters, the carriage return (CR) and the line\nfeed (LF). So before going any further let's plug that hole.\n\nThere are a couple of ways to deal with the CR/LFs. One (the\nC/Unix approach) is just to treat them as additional white space\ncharacters and ignore them. That's actually not such a bad\napproach, but it does sort of produce funny results for our\nparser as it stands now. If it were reading its input from a\nsource file as any self-respecting REAL compiler does, there\nwould be no problem. But we're reading input from the keyboard,\nand we're sort of conditioned to expect something to happen when\nwe hit the return key. It won't, if we just skip over the CR and\nLF (try it). So I'm going to use a different method here, which\nis NOT necessarily the best approach in the long run. Consider\nit a temporary kludge until we're further along.\n\nInstead of skipping the CR/LF, We'll let the parser go ahead and\ncatch them, then introduce a special procedure, analogous to\nSkipWhite, that skips them only in specified \"legal\" spots.\n\nHere's the procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip a CRLF }\n\nprocedure Fin;\nbegin\n if Look = CR then GetChar;\n if Look = LF then GetChar;\nend;\n\n{--------------------------------------------------------------}\n```\n\nNow, add two calls to Fin in procedure Block, like this:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block(L: string);\nbegin\n while not(Look in ['e', 'l', 'u']) do begin\n Fin;\n case Look of\n 'i': DoIf(L);\n 'w': DoWhile;\n 'p': DoLoop;\n 'r': DoRepeat;\n 'f': DoFor;\n 'd': DoDo;\n 'b': DoBreak(L);\n else Other;\n end;\n Fin;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\n\nNow, you'll find that you can use multiple-line \"programs.\" The\nonly restriction is that you can't separate an IF or WHILE token\nfrom its predicate.\n\nNow we're ready to include the assignment statements. Simply\nchange that call to Other in procedure Block to a call to\nAssignment, and add the following procedure, copied from one of\nour earlier programs. Note that Assignment now calls\nBoolExpression, so that we can assign Boolean variables.\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := GetName;\n Match('=');\n BoolExpression;\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)');\nend;\n{--------------------------------------------------------------}\n```\n\nWith that change, you should now be able to write reasonably\nrealistic-looking programs, subject only to our limitation on\nsingle-character tokens. My original intention was to get rid of\nthat limitation for you, too. However, that's going to require a\nfairly major change to what we've done so far. We need a true\nlexical scanner, and that requires some structural changes. They\nare not BIG changes that require us to throw away all of what\nwe've done so far ... with care, it can be done with very minimal\nchanges, in fact. But it does require that care.\n\nThis installment has already gotten pretty long, and it contains\nsome pretty heavy stuff, so I've decided to leave that step until\nnext time, when you've had a little more time to digest what\nwe've done and are ready to start fresh.\n\nIn the next installment, then, we'll build a lexical scanner and\neliminate the single-character barrier once and for all. We'll\nalso write our first complete compiler, based on what we've done\nin this session. See you then."}]},{title:"Part VII: LEXICAL SCANNING - 7 November 1988",path:"/part-vii-lexical-scanning-7-november-1988",items:[{title:"INTRODUCTION",path:"/introduction",content:"In the last installment, I left you with a compiler that would\nALMOST work, except that we were still limited to single-\ncharacter tokens. The purpose of this session is to get rid of\nthat restriction, once and for all. This means that we must deal\nwith the concept of the lexical scanner.\n\nMaybe I should mention why we need a lexical scanner at all ...\nafter all, we've been able to manage all right without one, up\ntill now, even when we provided for multi-character tokens.\n\nThe ONLY reason, really, has to do with keywords. It's a fact of\ncomputer life that the syntax for a keyword has the same form as\nthat for any other identifier. We can't tell until we get the\ncomplete word whether or not it IS a keyword. For example, the\nvariable IFILE and the keyword IF look just alike, until you get\nto the third character. In the examples to date, we were always\nable to make a decision based upon the first character of the\ntoken, but that's no longer possible when keywords are present.\nWe need to know that a given string is a keyword BEFORE we begin\nto process it. And that's why we need a scanner.\n\nIn the last session, I also promised that we would be able to\nprovide for normal tokens without making wholesale changes to\nwhat we have already done. I didn't lie ... we can, as you will\nsee later. But every time I set out to install these elements of\nthe software into the parser we have already built, I had bad\nfeelings about it. The whole thing felt entirely too much like a\nband-aid. I finally figured out what was causing the problem: I\nwas installing lexical scanning software without first explaining\nto you what scanning is all about, and what the alternatives are.\nUp till now, I have studiously avoided giving you a lot of\ntheory, and certainly not alternatives. I generally don't\nrespond well to the textbooks that give you twenty-five different\nways to do something, but no clue as to which way best fits your\nneeds. I've tried to avoid that pitfall by just showing you ONE\nmethod, that WORKS.\n\nBut this is an important area. While the lexical scanner is\nhardly the most exciting part of a compiler, it often has the\nmost profound effect on the general \"look & feel\" of the\nlanguage, since after all it's the part closest to the user. I\nhave a particular structure in mind for the scanner to be used\nwith KISS. It fits the look & feel that I want for that\nlanguage. But it may not work at all for the language YOU'RE\ncooking up, so in this one case I feel that it's important for\nyou to know your options.\n\nSo I'm going to depart, again, from my usual format. In this\nsession we'll be getting much deeper than usual into the basic\ntheory of languages and grammars. I'll also be talking about\nareas OTHER than compilers in which lexical scanning plays an\nimportant role. Finally, I will show you some alternatives for\nthe structure of the lexical scanner. Then, and only then, will\nwe get back to our parser from the last installment. Bear with\nme ... I think you'll find it's worth the wait. In fact, since\nscanners have many applications outside of compilers, you may\nwell find this to be the most useful session for you."},{title:"LEXICAL SCANNING",path:"/lexical-scanning",content:"Lexical scanning is the process of scanning the stream of input\ncharacters and separating it into strings called tokens. Most\ncompiler texts start here, and devote several chapters to\ndiscussing various ways to build scanners. This approach has its\nplace, but as you have already seen, there is a lot you can do\nwithout ever even addressing the issue, and in fact the scanner\nwe'll end up with here won't look much like what the texts\ndescribe. The reason? Compiler theory and, consequently, the\nprograms resulting from it, must deal with the most general kind\nof parsing rules. We don't. In the real world, it is possible\nto specify the language syntax in such a way that a pretty simple\nscanner will suffice. And as always, KISS is our motto.\n\nTypically, lexical scanning is done in a separate part of the\ncompiler, so that the parser per se sees only a stream of input\ntokens. Now, theoretically it is not necessary to separate this\nfunction from the rest of the parser. There is only one set of\nsyntax equations that define the whole language, so in theory we\ncould write the whole parser in one module.\n\nWhy the separation? The answer has both practical and\ntheoretical bases.\n\nIn 1956, Noam Chomsky defined the \"Chomsky Hierarchy\" of\ngrammars. They are:\n\n - Type 0: Unrestricted (e.g., English)\n - Type 1: Context-Sensitive\n - Type 2: Context-Free\n - Type 3: Regular\n\nA few features of the typical programming language (particularly\nthe older ones, such as FORTRAN) are Type 1, but for the most\npart all modern languages can be described using only the last\ntwo types, and those are all we'll be dealing with here.\n\nThe neat part about these two types is that there are very\nspecific ways to parse them. It has been shown that any regular\ngrammar can be parsed using a particular form of abstract machine\ncalled the state machine (finite automaton). We have already\nimplemented state machines in some of our recognizers.\n\nSimilarly, Type 2 (context-free) grammars can always be parsed\nusing a push-down automaton (a state machine augmented by a\nstack). We have also implemented these machines. Instead of\nimplementing a literal stack, we have relied on the built-in\nstack associated with recursive coding to do the job, and that in\nfact is the preferred approach for top-down parsing.\n\nNow, it happens that in real, practical grammars, the parts that\nqualify as regular expressions tend to be the lower-level parts,\nsuch as the definition of an identifier:\n\n `<ident> ::= <letter> [ <letter> | <digit> ]*`\n\nSince it takes a different kind of abstract machine to parse the\ntwo types of grammars, it makes sense to separate these lower-\nlevel functions into a separate module, the lexical scanner,\nwhich is built around the idea of a state machine. The idea is to\nuse the simplest parsing technique needed for the job.\n\nThere is another, more practical reason for separating scanner\nfrom parser. We like to think of the input source file as a\nstream of characters, which we process right to left without\nbacktracking. In practice that isn't possible. Almost every\nlanguage has certain keywords such as IF, WHILE, and END. As I\nmentioned earlier, we can't really know whether a given\ncharacter string is a keyword, until we've reached the end of it,\nas defined by a space or other delimiter. So in that sense, we\nMUST save the string long enough to find out whether we have a\nkeyword or not. That's a limited form of backtracking.\n\nSo the structure of a conventional compiler involves splitting up\nthe functions of the lower-level and higher-level parsing. The\nlexical scanner deals with things at the character level,\ncollecting characters into strings, etc., and passing them along\nto the parser proper as indivisible tokens. It's also considered\nnormal to let the scanner have the job of identifying keywords."},{title:"STATE MACHINES AND ALTERNATIVES",path:"/state-machines-and-alternatives",content:"I mentioned that the regular expressions can be parsed using a\nstate machine. In most compiler texts, and indeed in most\ncompilers as well, you will find this taken literally. There is\ntypically a real implementation of the state machine, with\nintegers used to define the current state, and a table of actions\nto take for each combination of current state and input\ncharacter. If you write a compiler front end using the popular\nUnix tools LEX and YACC, that's what you'll get. The output of\nLEX is a state machine implemented in C, plus a table of actions\ncorresponding to the input grammar given to LEX. The YACC output\nis similar ... a canned table-driven parser, plus the table\ncorresponding to the language syntax.\n\nThat is not the only choice, though. In our previous\ninstallments, you have seen over and over that it is possible to\nimplement parsers without dealing specifically with tables,\nstacks, or state variables. In fact, in Installment V I warned\nyou that if you find yourself needing these things you might be\ndoing something wrong, and not taking advantage of the power of\nPascal. There are basically two ways to define a state machine's\nstate: explicitly, with a state number or code, and implicitly,\nsimply by virtue of the fact that I'm at a certain place in the\ncode (if it's Tuesday, this must be Belgium). We've relied\nheavily on the implicit approaches before, and I think you'll\nfind that they work well here, too.\n\nIn practice, it may not even be necessary to HAVE a well-defined\nlexical scanner. This isn't our first experience at dealing with\nmulti-character tokens. In Installment III, we extended our\nparser to provide for them, and we didn't even NEED a lexical\nscanner. That was because in that narrow context, we could\nalways tell, just by looking at the single lookahead character,\nwhether we were dealing with a number, a variable, or an\noperator. In effect, we built a distributed lexical scanner,\nusing procedures GetName and GetNum.\n\nWith keywords present, we can't know anymore what we're dealing\nwith, until the entire token is read. This leads us to a more\nlocalized scanner; although, as you will see, the idea of a\ndistributed scanner still has its merits."},{title:"SOME EXPERIMENTS IN SCANNING",path:"/some-experiments-in-scanning",content:"Before getting back to our compiler, it will be useful to\nexperiment a bit with the general concepts.\n\nLet's begin with the two definitions most often seen in real\nprogramming languages:\n```\n <ident> ::= <letter> [ <letter> | <digit> ]*\n <number ::= [<digit>]+\n```\n(Remember, the '*' indicates zero or more occurences of the terms\nin brackets, and the '+', one or more.)\n\nWe have already dealt with similar items in Installment III.\nLet's begin (as usual) with a bare cradle. Not surprisingly, we\nare going to need a new recognizer:\n \n```delphi\n{--------------------------------------------------------------}\n{ Recognize an Alphanumeric Character }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n{--------------------------------------------------------------}\n```\n\nUsing this let's write the following two routines, which are very\nsimilar to those we've used before:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: string;\nvar x: string[8];\nbegin\n x := '';\n if not IsAlpha(Look) then Expected('Name');\n while IsAlNum(Look) do begin\n x := x + UpCase(Look);\n GetChar;\n end;\n GetName := x;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: string;\nvar x: string[16];\nbegin\n x := '';\n if not IsDigit(Look) then Expected('Integer');\n while IsDigit(Look) do begin\n x := x + Look;\n GetChar;\n end;\n GetNum := x;\nend;\n{--------------------------------------------------------------}\n```\n\n(Notice that this version of GetNum returns a string, not an\ninteger as before.)\n\nYou can easily verify that these routines work by calling them\nfrom the main program, as in\n```delphi\n WriteLn(GetName);\n```\nThis program will print any legal name typed in (maximum eight\ncharacters, since that's what we told GetName). It will reject\nanything else.\n\nTest the other routine similarly."},{title:"WHITE SPACE",path:"/white-space",content:"We also have dealt with embedded white space before, using the\ntwo routines IsWhite and SkipWhite. Make sure that these\nroutines are in your current version of the cradle, and add the\nthe line\n```delphi\n SkipWhite;\n```\nat the end of both GetName and GetNum.\n\nNow, let's define the new procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Lexical Scanner }\n\nFunction Scan: string;\nbegin\n if IsAlpha(Look) then\n Scan := GetName\n else if IsDigit(Look) then\n Scan := GetNum\n else begin\n Scan := Look;\n GetChar;\n end;\n SkipWhite;\nend;\n{--------------------------------------------------------------}\n```\n\nWe can call this from the new main program:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\n\nbegin\n Init;\n repeat\n Token := Scan;\n writeln(Token);\n until Token = CR;\nend.\n{--------------------------------------------------------------}\n```\n\n(You will have to add the declaration of the string Token at the\nbeginning of the program. Make it any convenient length, say 16\ncharacters.)\n\nNow, run the program. Note how the input string is, indeed,\nseparated into distinct tokens."},{title:"STATE MACHINES",path:"/state-machines",content:"For the record, a parse routine like GetName does indeed\nimplement a state machine. The state is implicit in the current\nposition in the code. A very useful trick for visualizing what's\ngoing on is the syntax diagram, or \"railroad-track\" diagram.\nIt's a little difficult to draw one in this medium, so I'll use\nthem very sparingly, but the figure below should give you the\nidea:\n\n```\n |-----\x3e Other---------------------------\x3e Error\n |\n Start -------\x3e Letter ---------------\x3e Other -----\x3e Finish\n ^ V\n | |\n |<----- Letter <---------|\n | |\n |<----- Digit <----------\n```\n\nAs you can see, this diagram shows how the logic flows as\ncharacters are read. Things begin, of course, in the start\nstate, and end when a character other than an alphanumeric is\nfound. If the first character is not alpha, an error occurs.\nOtherwise the machine will continue looping until the terminating\ndelimiter is found.\n\nNote that at any point in the flow, our position is entirely\ndependent on the past history of the input characters. At that\npoint, the action to be taken depends only on the current state,\nplus the current input character. That's what make this a state\nmachine.\n\nBecause of the difficulty of drawing railroad-track diagrams in\nthis medium, I'll continue to stick to syntax equations from now\non. But I highly recommend the diagrams to you for anything you\ndo that involves parsing. After a little practice you can begin\nto see how to write a parser directly from the diagrams.\nParallel paths get coded into guarded actions (guarded by IF's or\nCASE statements), serial paths into sequential calls. It's\nalmost like working from a schematic.\n\nWe didn't even discuss SkipWhite, which was introduced earlier,\nbut it also is a simple state machine, as is GetNum. So is their\nparent procedure, Scan. Little machines make big machines.\n\nThe neat thing that I'd like you to note is how painlessly this\nimplicit approach creates these state machines. I personally\nprefer it a lot over the table-driven approach. It also results\nis a small, tight, and fast scanner."},{title:"NEWLINES",path:"/newlines",content:"Moving right along, let's modify our scanner to handle more than\none line. As I mentioned last time, the most straightforward way\nto do this is to simply treat the newline characters, carriage\nreturn and line feed, as white space. This is, in fact, the way\nthe C standard library routine, iswhite, works. We didn't\nactually try this before. I'd like to do it now, so you can get\na feel for the results.\n\nTo do this, simply modify the single executable line of IsWhite\nto read:\n\n\n```delphi\n IsWhite := c in [' ', TAB, CR, LF];\n```\n\n\nWe need to give the main program a new stop condition, since it\nwill never see a CR. Let's just use:\n\n```delphi\n until Token = '.';\n\n```\nOK, compile this program and run it. Try a couple of lines,\nterminated by the period. I used:\n\n\n now is the time\n for all good men.\n\nHey, what happened? When I tried it, I didn't get the last\ntoken, the period. The program didn't halt. What's more, when I\npressed the 'enter' key a few times, I still didn't get the\nperiod.\n\nIf you're still stuck in your program, you'll find that typing a\nperiod on a new line will terminate it.\n\nWhat's going on here? The answer is that we're hanging up in\nSkipWhite. A quick look at that routine will show that as long\nas we're typing null lines, we're going to just continue to loop.\nAfter SkipWhite encounters an LF, it tries to execute a GetChar.\nBut since the input buffer is now empty, GetChar's read statement\ninsists on having another line. Procedure Scan gets the\nterminating period, all right, but it calls SkipWhite to clean\nup, and SkipWhite won't return until it gets a non-null line.\n\nThis kind of behavior is not quite as bad as it seems. In a real\ncompiler, we'd be reading from an input file instead of the\nconsole, and as long as we have some procedure for dealing with\nend-of-files, everything will come out OK. But for reading data\nfrom the console, the behavior is just too bizarre. The fact of\nthe matter is that the C/Unix convention is just not compatible\nwith the structure of our parser, which calls for a lookahead\ncharacter. The code that the Bell wizards have implemented\ndoesn't use that convention, which is why they need 'ungetc'.\n\nOK, let's fix the problem. To do that, we need to go back to the\nold definition of IsWhite (delete the CR and LF characters) and\nmake use of the procedure Fin that I introduced last time. If\nit's not in your current version of the cradle, put it there now.\n\nAlso, modify the main program to read:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\n\nbegin\n Init;\n repeat\n Token := Scan;\n writeln(Token);\n if Token = CR then Fin;\n until Token = '.';\nend.\n{--------------------------------------------------------------}\n```\n\nNote the \"guard\" test preceding the call to Fin. That's what\nmakes the whole thing work, and ensures that we don't try to read\na line ahead.\n \nTry the code now. I think you'll like it better.\n\nIf you refer to the code we did in the last installment, you'll\nfind that I quietly sprinkled calls to Fin throughout the code,\nwherever a line break was appropriate. This is one of those\nareas that really affects the look & feel that I mentioned. At\nthis point I would urge you to experiment with different\narrangements and see how you like them. If you want your\nlanguage to be truly free-field, then newlines should be\ntransparent. In this case, the best approach is to put the\nfollowing lines at the BEGINNING of Scan:\n\n```delphi\n\n while Look = CR do\n Fin;\n```\n\nIf, on the other hand, you want a line-oriented language like\nAssembler, BASIC, or FORTRAN (or even Ada... note that it has\ncomments terminated by newlines), then you'll need for Scan to\nreturn CR's as tokens. It must also eat the trailing LF. The\nbest way to do that is to use this line, again at the beginning\nof Scan:\n```delphi\n if Look = LF then Fin;\n```\n\nFor other conventions, you'll have to use other arrangements.\nIn my example of the last session, I allowed newlines only at\nspecific places, so I was somewhere in the middle ground. In the\nrest of these sessions, I'll be picking ways to handle newlines\nthat I happen to like, but I want you to know how to choose other\nways for yourselves."},{title:"OPERATORS",path:"/operators",content:"We could stop now and have a pretty useful scanner for our\npurposes. In the fragments of KISS that we've built so far, the\nonly tokens that have multiple characters are the identifiers and\nnumbers. All operators were single characters. The only\nexception I can think of is the relops <=, >=, and <>, but they\ncould be dealt with as special cases.\n\nStill, other languages have multi-character operators, such as\nthe ':=' of Pascal or the '++' and '>>' of C. So while we may\nnot need multi-character operators, it's nice to know how to get\nthem if necessary.\n\nNeedless to say, we can handle operators very much the same way\nas the other tokens. Let's start with a recognizer:\n \n```delphi\n{--------------------------------------------------------------}\n{ Recognize Any Operator }\n\nfunction IsOp(c: char): boolean;\nbegin\n IsOp := c in ['+', '-', '*', '/', '<', '>', ':', '='];\nend;\n{--------------------------------------------------------------}\n```\n\nIt's important to note that we DON'T have to include every\npossible operator in this list. For example, the paretheses\naren't included, nor is the terminating period. The current\nversion of Scan handles single-character operators just fine as\nit is. The list above includes only those characters that can\nappear in multi-character operators. (For specific languages, of\ncourse, the list can always be edited.)\n\nNow, let's modify Scan to read:\n\n```delphi\n{--------------------------------------------------------------}\n{ Lexical Scanner }\n\nFunction Scan: string;\nbegin\n while Look = CR do\n Fin;\n if IsAlpha(Look) then\n Scan := GetName\n else if IsDigit(Look) then\n Scan := GetNum\n else if IsOp(Look) then\n Scan := GetOp\n else begin\n Scan := Look;\n GetChar;\n end;\n SkipWhite;\nend;\n{--------------------------------------------------------------}\n```\n\nTry the program now. You will find that any code fragments you\ncare to throw at it will be neatly broken up into individual\ntokens."},{title:"LISTS, COMMAS AND COMMAND LINES",path:"/lists-commas-and-command-lines",content:"Before getting back to the main thrust of our study, I'd like to\nget on my soapbox for a moment.\n \nHow many times have you worked with a program or operating system\nthat had rigid rules about how you must separate items in a list?\n(Try, the last time you used MSDOS!) Some programs require\nspaces as delimiters, and some require commas. Worst of all,\nsome require both, in different places. Most are pretty\nunforgiving about violations of their rules.\n\nI think this is inexcusable. It's too easy to write a parser\nthat will handle both spaces and commas in a flexible way.\nConsider the following procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip Over a Comma }\n\nprocedure SkipComma;\nbegin\n SkipWhite;\n if Look = ',' then begin\n GetChar;\n SkipWhite;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nThis eight-line procedure will skip over a delimiter consisting\nof any number (including zero) of spaces, with zero or one comma\nembedded in the string.\n\nTEMPORARILY, change the call to SkipWhite in Scan to a call to\nSkipComma, and try inputting some lists. Works nicely, eh?\nDon't you wish more software authors knew about SkipComma?\n\nFor the record, I found that adding the equivalent of SkipComma\nto my Z80 assembler-language programs took all of 6 (six) extra\nbytes of code. Even in a 64K machine, that's not a very high\nprice to pay for user-friendliness!\n\nI think you can see where I'm going here. Even if you never\nwrite a line of a compiler code in your life, there are places in\nevery program where you can use the concepts of parsing. Any\nprogram that processes a command line needs them. In fact, if\nyou think about it for a bit, you'll have to conclude that any\ntime you write a program that processes user inputs, you're\ndefining a language. People communicate with languages, and the\nsyntax implicit in your program defines that language. The real\nquestion is: are you going to define it deliberately and\nexplicitly, or just let it turn out to be whatever the program\nends up parsing?\n\nI claim that you'll have a better, more user-friendly program if\nyou'll take the time to define the syntax explicitly. Write down\nthe syntax equations or draw the railroad-track diagrams, and\ncode the parser using the techniques I've shown you here. You'll\nend up with a better program, and it will be easier to write, to\nboot."},{title:"GETTING FANCY",path:"/getting-fancy",content:"OK, at this point we have a pretty nice lexical scanner that will\nbreak an input stream up into tokens. We could use it as it\nstands and have a servicable compiler. But there are some other\naspects of lexical scanning that we need to cover.\n\nThe main consideration is `<shudder>` efficiency. Remember when we\nwere dealing with single-character tokens, every test was a\ncomparison of a single character, Look, with a byte constant. We\nalso used the Case statement heavily.\n\nWith the multi-character tokens being returned by Scan, all those\ntests now become string comparisons. Much slower. And not only\nslower, but more awkward, since there is no string equivalent of\nthe Case statement in Pascal. It seems especially wasteful to\ntest for what used to be single characters ... the '=', '+', and\nother operators ... using string comparisons.\n\nUsing string comparison is not impossible ... Ron Cain used just\nthat approach in writing Small C. Since we're sticking to the\nKISS principle here, we would be truly justified in settling for\nthis approach. But then I would have failed to tell you about\none of the key approaches used in \"real\" compilers.\n\nYou have to remember: the lexical scanner is going to be called a\n_LOT_! Once for every token in the whole source program, in\nfact. Experiments have indicated that the average compiler\nspends anywhere from 20% to 40% of its time in the scanner\nroutines. If there were ever a place where efficiency deserves\nreal consideration, this is it.\n\nFor this reason, most compiler writers ask the lexical scanner to\ndo a little more work, by \"tokenizing\" the input stream. The\nidea is to match every token against a list of acceptable\nkeywords and operators, and return unique codes for each one\nrecognized. In the case of ordinary variable names or numbers,\nwe just return a code that says what kind of token they are, and\nsave the actual string somewhere else.\n\nOne of the first things we're going to need is a way to identify\nkeywords. We can always do it with successive IF tests, but it\nsurely would be nice if we had a general-purpose routine that\ncould compare a given string with a table of keywords. (By the\nway, we're also going to need such a routine later, for dealing\nwith symbol tables.) This usually presents a problem in Pascal,\nbecause standard Pascal doesn't allow for arrays of variable\nlengths. It's a real bother to have to declare a different\nsearch routine for every table. Standard Pascal also doesn't\nallow for initializing arrays, so you tend to see code like\n```delphi\n Table[1] := 'IF';\n Table[2] := 'ELSE';\n .\n .\n Table[n] := 'END';\n```\nwhich can get pretty old if there are many keywords.\n\nFortunately, Turbo Pascal 4.0 has extensions that eliminate both\nof these problems. Constant arrays can be declared using TP's\n\"typed constant\" facility, and the variable dimensions can be\nhandled with its C-like extensions for pointers.\n\nFirst, modify your declarations like this:\n\n```delphi\n{--------------------------------------------------------------}\n{ Type Declarations }\n\ntype Symbol = string[8];\n\n SymTab = array[1..1000] of Symbol;\n\n TabPtr = ^SymTab;\n{--------------------------------------------------------------}\n```\n\n(The dimension used in SymTab is not real ... no storage is\nallocated by the declaration itself, and the number need only be\n\"big enough.\")\n\nNow, just beneath those declarations, add the following:\n\n```delphi\n{--------------------------------------------------------------}\n{ Definition of Keywords and Token Types }\n\nconst KWlist: array [1..4] of Symbol =\n ('IF', 'ELSE', 'ENDIF', 'END');\n\n{--------------------------------------------------------------}\n```\n\nNext, insert the following new function:\n\n```delphi\n{--------------------------------------------------------------}\n{ Table Lookup }\n\n{ If the input string matches a table entry, return the entry\n index. If not, return a zero. }\n \nfunction Lookup(T: TabPtr; s: string; n: integer): integer;\nvar i: integer;\n found: boolean;\nbegin\n found := false;\n i := n;\n while (i > 0) and not found do\n if s = T^[i] then\n found := true\n else\n dec(i);\n Lookup := i;\nend;\n{--------------------------------------------------------------}\n```\n\nTo test it, you can temporarily change the main program as\nfollows:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\n\nbegin\n ReadLn(Token);\n WriteLn(Lookup(Addr(KWList), Token, 4));\nend.\n{--------------------------------------------------------------}\n```\n\nNotice how Lookup is called: The Addr function sets up a pointer\nto KWList, which gets passed to Lookup.\n\nOK, give this a try. Since we're bypassing Scan here, you'll\nhave to type the keywords in upper case to get any matches.\n\nNow that we can recognize keywords, the next thing is to arrange\nto return codes for them.\n\nSo what kind of code should we return? There are really only two\nreasonable choices. This seems like an ideal application for the\nPascal enumerated type. For example, you can define something\nlike\n```delphi\n SymType = (IfSym, ElseSym, EndifSym, EndSym, Ident, Number,\n Operator);\n```\nand arrange to return a variable of this type. Let's give it a\ntry. Insert the line above into your type definitions.\n\nNow, add the two variable declarations:\n \n```delphi\n Token: Symtype; { Current Token }\n Value: String[16]; { String Token of Look }\n```\n\nModify the scanner to read:\n\n```delphi\n{--------------------------------------------------------------}\n{ Lexical Scanner }\n\nprocedure Scan;\nvar k: integer;\nbegin\n while Look = CR do\n Fin;\n if IsAlpha(Look) then begin\n Value := GetName;\n k := Lookup(Addr(KWlist), Value, 4);\n if k = 0 then\n Token := Ident\n else\n Token := SymType(k - 1);\n end\n else if IsDigit(Look) then begin\n Value := GetNum;\n Token := Number;\n end\n else if IsOp(Look) then begin\n Value := GetOp;\n Token := Operator;\n end\n else begin\n Value := Look;\n Token := Operator;\n GetChar;\n end;\n SkipWhite;\nend;\n{--------------------------------------------------------------}\n```\n\n(Notice that Scan is now a procedure, not a function.)\n\n\nFinally, modify the main program to read:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n repeat\n Scan;\n case Token of\n Ident: write('Ident ');\n Number: Write('Number ');\n Operator: Write('Operator ');\n IfSym, ElseSym, EndifSym, EndSym: Write('Keyword ');\n end;\n Writeln(Value);\n until Token = EndSym;\nend.\n{--------------------------------------------------------------}\n```\n\nWhat we've done here is to replace the string Token used earlier\nwith an enumerated type. Scan returns the type in variable Token,\nand returns the string itself in the new variable Value.\n\nOK, compile this and give it a whirl. If everything goes right,\nyou should see that we are now recognizing keywords.\n\nWhat we have now is working right, and it was easy to generate\nfrom what we had earlier. However, it still seems a little\n\"busy\" to me. We can simplify things a bit by letting GetName,\nGetNum, GetOp, and Scan be procedures working with the global\nvariables Token and Value, thereby eliminating the local copies.\nIt also seems a little cleaner to move the table lookup into\nGetName. The new form for the four procedures is, then:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nprocedure GetName;\nvar k: integer;\nbegin\n Value := '';\n if not IsAlpha(Look) then Expected('Name');\n while IsAlNum(Look) do begin\n Value := Value + UpCase(Look);\n GetChar;\n end;\n k := Lookup(Addr(KWlist), Value, 4);\n if k = 0 then\n Token := Ident\n else\n Token := SymType(k-1);\nend;\n \n{--------------------------------------------------------------}\n{ Get a Number }\n\nprocedure GetNum;\nbegin\n Value := '';\n if not IsDigit(Look) then Expected('Integer');\n while IsDigit(Look) do begin\n Value := Value + Look;\n GetChar;\n end;\n Token := Number;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Operator }\n\nprocedure GetOp;\nbegin\n Value := '';\n if not IsOp(Look) then Expected('Operator');\n while IsOp(Look) do begin\n Value := Value + Look;\n GetChar;\n end;\n Token := Operator;\nend;\n\n\n{--------------------------------------------------------------}\n{ Lexical Scanner }\n\nprocedure Scan;\nvar k: integer;\nbegin\n while Look = CR do\n Fin;\n if IsAlpha(Look) then\n GetName\n else if IsDigit(Look) then\n GetNum\n else if IsOp(Look) then\n GetOp\n else begin\n Value := Look;\n Token := Operator;\n GetChar;\n end;\n SkipWhite;\nend;\n{--------------------------------------------------------------}\n``` "},{title:"RETURNING A CHARACTER",path:"/returning-a-character",content:"Essentially every scanner I've ever seen that was written in\nPascal used the mechanism of an enumerated type that I've just\ndescribed. It is certainly a workable mechanism, but it doesn't\nseem the simplest approach to me.\n\nFor one thing, the list of possible symbol types can get pretty\nlong. Here, I've used just one symbol, \"Operator,\" to stand for\nall of the operators, but I've seen other designs that actually\nreturn different codes for each one.\n\nThere is, of course, another simple type that can be returned as\na code: the character. Instead of returning the enumeration\nvalue 'Operator' for a '+' sign, what's wrong with just returning\nthe character itself? A character is just as good a variable for\nencoding the different token types, it can be used in case\nstatements easily, and it's sure a lot easier to type. What\ncould be simpler?\n\nBesides, we've already had experience with the idea of encoding\nkeywords as single characters. Our previous programs are already\nwritten that way, so using this approach will minimize the\nchanges to what we've already done.\n\nSome of you may feel that this idea of returning character codes\nis too mickey-mouse. I must admit it gets a little awkward for\nmulti-character operators like '<='. If you choose to stay with\nthe enumerated type, fine. For the rest, I'd like to show you\nhow to change what we've done above to support that approach.\n\nFirst, you can delete the SymType declaration now ... we won't be\nneeding that. And you can change the type of Token to char.\n\nNext, to replace SymType, add the following constant string:\n\n```delphi\n const KWcode: string[5] = 'xilee';\n```\n\n(I'll be encoding all idents with the single character 'x'.)\n\n\nLastly, modify Scan and its relatives as follows:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nprocedure GetName;\nbegin\n Value := '';\n if not IsAlpha(Look) then Expected('Name');\n while IsAlNum(Look) do begin\n Value := Value + UpCase(Look);\n GetChar;\n end;\n Token := KWcode[Lookup(Addr(KWlist), Value, 4) + 1];\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nprocedure GetNum;\nbegin\n Value := '';\n if not IsDigit(Look) then Expected('Integer');\n while IsDigit(Look) do begin\n Value := Value + Look;\n GetChar;\n end;\n Token := '#';\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Operator }\n\nprocedure GetOp;\nbegin\n Value := '';\n if not IsOp(Look) then Expected('Operator');\n while IsOp(Look) do begin\n Value := Value + Look;\n GetChar;\n end;\n if Length(Value) = 1 then\n Token := Value[1]\n else\n Token := '?';\nend;\n\n\n{--------------------------------------------------------------}\n{ Lexical Scanner }\n\nprocedure Scan;\nvar k: integer;\nbegin\n while Look = CR do\n Fin;\n if IsAlpha(Look) then\n GetName\n else if IsDigit(Look) then\n GetNum\n else if IsOp(Look) then begin\n GetOp\n else begin\n Value := Look;\n Token := '?';\n GetChar;\n end;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\n\nbegin\n Init;\n repeat\n Scan;\n case Token of\n 'x': write('Ident ');\n '#': Write('Number ');\n 'i', 'l', 'e': Write('Keyword ');\n else Write('Operator ');\n end;\n Writeln(Value);\n until Value = 'END';\nend.\n{--------------------------------------------------------------}\n```\n\nThis program should work the same as the previous version. A\nminor difference in structure, maybe, but it seems more\nstraightforward to me."},{title:"DISTRIBUTED vs CENTRALIZED SCANNERS",path:"/distributed-vs-centralized-scanners",content:"The structure for the lexical scanner that I've just shown you is\nvery conventional, and about 99% of all compilers use something\nvery close to it. This is not, however, the only possible\nstructure, or even always the best one.\n \nThe problem with the conventional approach is that the scanner\nhas no knowledge of context. For example, it can't distinguish\nbetween the assignment operator '=' and the relational operator\n'=' (perhaps that's why both C and Pascal use different strings\nfor the two). All the scanner can do is to pass the operator\nalong to the parser, which can hopefully tell from the context\nwhich operator is meant. Similarly, a keyword like 'IF' has no\nplace in the middle of a math expression, but if one happens to\nappear there, the scanner will see no problem with it, and will\nreturn it to the parser, properly encoded as an 'IF'.\n\nWith this kind of approach, we are not really using all the\ninformation at our disposal. In the middle of an expression, for\nexample, the parser \"knows\" that there is no need to look for\nkeywords, but it has no way of telling the scanner that. So the\nscanner continues to do so. This, of course, slows down the\ncompilation.\n\nIn real-world compilers, the designers often arrange for more\ninformation to be passed between parser and scanner, just to\navoid this kind of problem. But that can get awkward, and\ncertainly destroys a lot of the modularity of the structure.\n\nThe alternative is to seek some way to use the contextual\ninformation that comes from knowing where we are in the parser.\nThis leads us back to the notion of a distributed scanner, in\nwhich various portions of the scanner are called depending upon\nthe context.\n\nIn KISS, as in most languages, keywords ONLY appear at the\nbeginning of a statement. In places like expressions, they are\nnot allowed. Also, with one minor exception (the multi-character\nrelops) that is easily handled, all operators are single\ncharacters, which means that we don't need GetOp at all.\n\nSo it turns out that even with multi-character tokens, we can\nstill always tell from the current lookahead character exactly\nwhat kind of token is coming, except at the very beginning of a\nstatement.\n\nEven at that point, the ONLY kind of token we can accept is an\nidentifier. We need only to determine if that identifier is a\nkeyword or the target of an assignment statement.\n\nWe end up, then, still needing only GetName and GetNum, which are\nused very much as we've used them in earlier installments.\n\nIt may seem at first to you that this is a step backwards, and a\nrather primitive approach. In fact, it is an improvement over\nthe classical scanner, since we're using the scanning routines\nonly where they're really needed. In places where keywords are\nnot allowed, we don't slow things down by looking for them."},{title:"MERGING SCANNER AND PARSER",path:"/merging-scanner-and-parser",content:"Now that we've covered all of the theory and general aspects of\nlexical scanning that we'll be needing, I'm FINALLY ready to back\nup my claim that we can accomodate multi-character tokens with\nminimal change to our previous work. To keep things short and\nsimple I will restrict myself here to a subset of what we've done\nbefore; I'm allowing only one control construct (the IF) and no\nBoolean expressions. That's enough to demonstrate the parsing of\nboth keywords and expressions. The extension to the full set of\nconstructs should be pretty apparent from what we've already\ndone.\n\nAll the elements of the program to parse this subset, using\nsingle-character tokens, exist already in our previous programs.\nI built it by judicious copying of these files, but I wouldn't\ndare try to lead you through that process. Instead, to avoid any\nconfusion, the whole program is shown below:\n\n```delphi\n{--------------------------------------------------------------}\nprogram KISS;\n\n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n CR = ^M;\n LF = ^J;\n\n{--------------------------------------------------------------}\n{ Type Declarations }\n\ntype Symbol = string[8];\n\n SymTab = array[1..1000] of Symbol;\n\n TabPtr = ^SymTab;\n\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look : char; { Lookahead Character }\n Lcount: integer; { Label Counter }\n\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n \n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n\nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an AlphaNumeric Character }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n \n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Mulop }\n\nfunction IsMulop(c: char): boolean;\nbegin\n IsMulop := c in ['*', '/'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB];\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n if Look <> x then Expected('''' + x + '''');\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip a CRLF }\n\nprocedure Fin;\nbegin\n if Look = CR then GetChar;\n if Look = LF then GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: char;\nbegin\n while Look = CR do\n Fin;\n if not IsAlpha(Look) then Expected('Name');\n Getname := UpCase(Look);\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: char;\nbegin\n if not IsDigit(Look) then Expected('Integer');\n GetNum := Look;\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Generate a Unique Label }\n\nfunction NewLabel: string;\nvar S: string;\nbegin\n Str(LCount, S);\n NewLabel := 'L' + S;\n Inc(LCount);\nend;\n\n\n{--------------------------------------------------------------}\n{ Post a Label To Output }\n\nprocedure PostLabel(L: string);\nbegin\n WriteLn(L, ':');\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n \nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n{--------------------------------------------------------------}\n\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Identifier }\n\nprocedure Ident;\nvar Name: char;\nbegin\n Name := GetName;\n if Look = '(' then begin\n Match('(');\n Match(')');\n EmitLn('BSR ' + Name);\n end\n else\n EmitLn('MOVE ' + Name + '(PC),D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Expression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n Ident\n else\n EmitLn('MOVE #' + GetNum + ',D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate the First Math Factor }\n\n\nprocedure SignedFactor;\nvar s: boolean;\nbegin\n s := Look = '-';\n if IsAddop(Look) then begin\n GetChar;\n SkipWhite;\n end;\n Factor;\n if s then\n EmitLn('NEG D0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nprocedure Multiply;\nbegin\n Match('*');\n Factor;\n EmitLn('MULS (SP)+,D0');\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nprocedure Divide;\nbegin\n Match('/');\n Factor;\n EmitLn('MOVE (SP)+,D1');\n EmitLn('EXS.L D0');\n EmitLn('DIVS D1,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Completion of Term Processing (called by Term and FirstTerm }\n\nprocedure Term1;\nbegin\n while IsMulop(Look) do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '*': Multiply;\n '/': Divide;\n end;\n end;\nend;\n \n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nprocedure Term;\nbegin\n Factor;\n Term1;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term with Possible Leading Sign }\n\nprocedure FirstTerm;\nbegin\n SignedFactor;\n Term1;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Match('+');\n Term;\n EmitLn('ADD (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n EmitLn('SUB (SP)+,D0');\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n FirstTerm;\n while IsAddop(Look) do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '+': Add;\n '-': Subtract;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Condition }\n{ This version is a dummy }\n\nProcedure Condition;\nbegin\n EmitLn('Condition');\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block;\n Forward;\n\nprocedure DoIf;\nvar L1, L2: string;\nbegin\n Match('i');\n Condition;\n L1 := NewLabel;\n L2 := L1;\n EmitLn('BEQ ' + L1);\n Block;\n if Look = 'l' then begin\n Match('l');\n L2 := NewLabel;\n EmitLn('BRA ' + L2);\n PostLabel(L1);\n Block;\n end;\n PostLabel(L2);\n Match('e');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := GetName;\n Match('=');\n Expression;\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)');\nend;\n \n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block;\nbegin\n while not(Look in ['e', 'l']) do begin\n case Look of\n 'i': DoIf;\n CR: while Look = CR do\n Fin;\n else Assignment;\n end;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Program }\n\nprocedure DoProgram;\nbegin\n Block;\n if Look <> 'e' then Expected('END');\n EmitLn('END')\nend;\n\n\n{--------------------------------------------------------------}\n\n{ Initialize }\n\nprocedure Init;\nbegin\n LCount := 0;\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n DoProgram;\nend.\n{--------------------------------------------------------------}\n```\n\nA couple of comments:\n\n (1) The form for the expression parser, using FirstTerm, etc.,\n is a little different from what you've seen before. It's\n yet another variation on the same theme. Don't let it throw\n you ... the change is not required for what follows.\n\n (2) Note that, as usual, I had to add calls to Fin at strategic\n spots to allow for multiple lines.\n\nBefore we proceed to adding the scanner, first copy this file and\nverify that it does indeed parse things correctly. Don't forget\nthe \"codes\": 'i' for IF, 'l' for ELSE, and 'e' for END or ENDIF.\n\nIf the program works, then let's press on. In adding the scanner\nmodules to the program, it helps to have a systematic plan. In\nall the parsers we've written to date, we've stuck to a\nconvention that the current lookahead character should always be\na non-blank character. We preload the lookahead character in\nInit, and keep the \"pump primed\" after that. To keep the thing\nworking right at newlines, we had to modify this a bit and treat\nthe newline as a legal token.\n\nIn the multi-character version, the rule is similar: The current\nlookahead character should always be left at the BEGINNING of the\nnext token, or at a newline.\n\nThe multi-character version is shown next. To get it, I've made\nthe following changes:\n\n\n o Added the variables Token and Value, and the type definitions\n needed by Lookup.\n\n o Added the definitions of KWList and KWcode.\n\n o Added Lookup.\n\n o Replaced GetName and GetNum by their multi-character versions.\n (Note that the call to Lookup has been moved out of GetName,\n so that it will not be executed for calls within an\n expression.)\n\n o Created a new, vestigial Scan that calls GetName, then scans\n for keywords.\n\n o Created a new procedure, MatchString, that looks for a\n specific keyword. Note that, unlike Match, MatchString does\n NOT read the next keyword.\n\n o Modified Block to call Scan.\n\n o Changed the calls to Fin a bit. Fin is now called within\n GetName.\n\nHere is the program in its entirety:\n\n```delphi\n{--------------------------------------------------------------}\nprogram KISS;\n \n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n CR = ^M;\n LF = ^J;\n\n{--------------------------------------------------------------}\n{ Type Declarations }\n\ntype Symbol = string[8];\n\n SymTab = array[1..1000] of Symbol;\n\n TabPtr = ^SymTab;\n\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look : char; { Lookahead Character }\n Token : char; { Encoded Token }\n Value : string[16]; { Unencoded Token }\n Lcount: integer; { Label Counter }\n\n\n{--------------------------------------------------------------}\n{ Definition of Keywords and Token Types }\n\nconst KWlist: array [1..4] of Symbol =\n ('IF', 'ELSE', 'ENDIF', 'END');\n\nconst KWcode: string[5] = 'xilee';\n\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n\nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an AlphaNumeric Character }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Mulop }\n\nfunction IsMulop(c: char): boolean;\nbegin\n IsMulop := c in ['*', '/'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB];\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n if Look <> x then Expected('''' + x + '''');\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip a CRLF }\n\nprocedure Fin;\nbegin\n if Look = CR then GetChar;\n if Look = LF then GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Table Lookup }\n\nfunction Lookup(T: TabPtr; s: string; n: integer): integer;\nvar i: integer;\n found: boolean;\nbegin\n found := false;\n i := n;\n while (i > 0) and not found do\n if s = T^[i] then\n found := true\n else\n dec(i);\n Lookup := i;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nprocedure GetName;\nbegin\n while Look = CR do\n Fin;\n if not IsAlpha(Look) then Expected('Name');\n Value := '';\n while IsAlNum(Look) do begin\n Value := Value + UpCase(Look);\n GetChar;\n end;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nprocedure GetNum;\nbegin\n if not IsDigit(Look) then Expected('Integer');\n Value := '';\n while IsDigit(Look) do begin\n Value := Value + Look;\n GetChar;\n end;\n Token := '#';\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier and Scan it for Keywords }\n\nprocedure Scan;\nbegin\n GetName;\n Token := KWcode[Lookup(Addr(KWlist), Value, 4) + 1];\nend;\n \n\n{--------------------------------------------------------------}\n{ Match a Specific Input String }\n\nprocedure MatchString(x: string);\nbegin\n if Value <> x then Expected('''' + x + '''');\nend;\n\n\n{--------------------------------------------------------------}\n{ Generate a Unique Label }\n\nfunction NewLabel: string;\nvar S: string;\nbegin\n Str(LCount, S);\n NewLabel := 'L' + S;\n Inc(LCount);\nend;\n\n\n{--------------------------------------------------------------}\n{ Post a Label To Output }\n\nprocedure PostLabel(L: string);\nbegin\n WriteLn(L, ':');\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n\nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Identifier }\n\nprocedure Ident;\nbegin\n GetName;\n if Look = '(' then begin\n Match('(');\n Match(')');\n EmitLn('BSR ' + Value);\n end\n else\n EmitLn('MOVE ' + Value + '(PC),D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Expression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n Ident\n else begin\n GetNum;\n EmitLn('MOVE #' + Value + ',D0');\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate the First Math Factor }\n\nprocedure SignedFactor;\nvar s: boolean;\nbegin\n s := Look = '-';\n if IsAddop(Look) then begin\n GetChar;\n SkipWhite;\n end;\n Factor;\n if s then\n EmitLn('NEG D0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nprocedure Multiply;\nbegin\n Match('*');\n Factor;\n EmitLn('MULS (SP)+,D0');\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nprocedure Divide;\nbegin\n Match('/');\n Factor;\n EmitLn('MOVE (SP)+,D1');\n EmitLn('EXS.L D0');\n EmitLn('DIVS D1,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Completion of Term Processing (called by Term and FirstTerm }\n\nprocedure Term1;\nbegin\n while IsMulop(Look) do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '*': Multiply;\n '/': Divide;\n end;\n end;\nend;\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nprocedure Term;\nbegin\n Factor;\n Term1;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term with Possible Leading Sign }\n\nprocedure FirstTerm;\nbegin\n SignedFactor;\n Term1;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Match('+');\n Term;\n EmitLn('ADD (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n EmitLn('SUB (SP)+,D0');\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n FirstTerm;\n while IsAddop(Look) do begin\n EmitLn('MOVE D0,-(SP)');\n case Look of\n '+': Add;\n '-': Subtract;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Condition }\n{ This version is a dummy }\n\nProcedure Condition;\nbegin\n EmitLn('Condition');\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block; Forward;\n\n\nprocedure DoIf;\nvar L1, L2: string;\nbegin\n Condition;\n L1 := NewLabel;\n L2 := L1;\n EmitLn('BEQ ' + L1);\n Block;\n if Token = 'l' then begin\n L2 := NewLabel;\n EmitLn('BRA ' + L2);\n PostLabel(L1);\n Block;\n end;\n PostLabel(L2);\n MatchString('ENDIF');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: string;\nbegin\n Name := Value;\n Match('=');\n Expression;\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Statement Block }\n\nprocedure Block;\nbegin\n Scan;\n while not (Token in ['e', 'l']) do begin\n case Token of\n 'i': DoIf;\n else Assignment;\n end;\n Scan;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n\n{ Parse and Translate a Program }\n\nprocedure DoProgram;\nbegin\n Block;\n MatchString('END');\n EmitLn('END')\nend;\n\n\n{--------------------------------------------------------------}\n\n{ Initialize }\n\nprocedure Init;\nbegin\n LCount := 0;\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n DoProgram;\nend.\n{--------------------------------------------------------------}\n\n```\nCompare this program with its single-character counterpart. I\nthink you will agree that the differences are minor.\n\n\nCONCLUSION\n\nAt this point, you have learned how to parse and generate code\nfor expressions, Boolean expressions, and control structures.\nYou have now learned how to develop lexical scanners, and how to\nincorporate their elements into a translator. You have still not\nseen ALL the elements combined into one program, but on the basis\nof what we've done before you should find it a straightforward\nmatter to extend our earlier programs to include scanners.\n\nWe are very close to having all the elements that we need to\nbuild a real, functional compiler. There are still a few things\nmissing, notably procedure calls and type definitions. We will\ndeal with those in the next few sessions. Before doing so,\nhowever, I thought it would be fun to turn the translator above\ninto a true compiler. That's what we'll be doing in the next\ninstallment.\n\nUp till now, we've taken a rather bottom-up approach to parsing,\nbeginning with low-level constructs and working our way up. In\nthe next installment, I'll also be taking a look from the top\ndown, and we'll discuss how the structure of the translator is\naltered by changes in the language definition.\n\nSee you then."}]},{title:"Part VIII: A LITTLE PHILOSOPHY - 2 April 1989",path:"/part-viii-a-little-philosophy-2-april-1989",items:[{title:"INTRODUCTION",path:"/introduction",content:"This is going to be a different kind of session than the others\nin our series on parsing and compiler construction. For this\nsession, there won't be any experiments to do or code to write.\nThis once, I'd like to just talk with you for a while.\nMercifully, it will be a short session, and then we can take up\nwhere we left off, hopefully with renewed vigor.\n\nWhen I was in college, I found that I could always follow a\nprof's lecture a lot better if I knew where he was going with it.\nI'll bet you were the same.\n\nSo I thought maybe it's about time I told you where we're going\nwith this series: what's coming up in future installments, and in\ngeneral what all this is about. I'll also share some general\nthoughts concerning the usefulness of what we've been doing."},{title:"THE ROAD HOME",path:"/the-road-home",content:"So far, we've covered the parsing and translation of arithmetic\nexpressions, Boolean expressions, and combinations connected by\nrelational operators. We've also done the same for control\nconstructs. In all of this we've leaned heavily on the use of\ntop-down, recursive descent parsing, BNF definitions of the\nsyntax, and direct generation of assembly-language code. We also\nlearned the value of such tricks as single-character tokens to\nhelp us see the forest through the trees. In the last\ninstallment we dealt with lexical scanning, and I showed you\nsimple but powerful ways to remove the single-character barriers.\n\nThroughout the whole study, I've emphasized the KISS philosophy\n... Keep It Simple, Sidney ... and I hope by now you've realized\njust how simple this stuff can really be. While there are for\nsure areas of compiler theory that are truly intimidating, the\nultimate message of this series is that in practice you can just\npolitely sidestep many of these areas. If the language\ndefinition cooperates or, as in this series, if you can define\nthe language as you go, it's possible to write down the language\ndefinition in BNF with reasonable ease. And, as we've seen, you\ncan crank out parse procedures from the BNF just about as fast as\nyou can type.\n\nAs our compiler has taken form, it's gotten more parts, but each\npart is quite small and simple, and very much like all the\nothers.\n\nAt this point, we have many of the makings of a real, practical\ncompiler. As a matter of fact, we already have all we need to\nbuild a toy compiler for a language as powerful as, say, Tiny\nBASIC. In the next couple of installments, we'll go ahead and\ndefine that language.\n\nTo round out the series, we still have a few items to cover.\nThese include:\n\n - Procedure calls, with and without parameters\n\n - Local and global variables\n\n - Basic types, such as character and integer types\n\n - Arrays\n\n - Strings\n\n - User-defined types and structures\n\n - Tree-structured parsers and intermediate languages\n\n - Optimization\n\nThese will all be covered in future installments. When we're\nfinished, you'll have all the tools you need to design and build\nyour own languages, and the compilers to translate them.\n\nI can't design those languages for you, but I can make some\ncomments and recommendations. I've already sprinkled some\nthroughout past installments. You've seen, for example, the\ncontrol constructs I prefer.\n\nThese constructs are going to be part of the languages I build.\nI have three languages in mind at this point, two of which you\nwill see in installments to come:\n\nTINY - A minimal, but usable language on the order of Tiny\n BASIC or Tiny C. It won't be very practical, but it will\n have enough power to let you write and run real programs\n that do something worthwhile.\n\nKISS - The language I'm building for my own use. KISS is\n intended to be a systems programming language. It won't\n have strong typing or fancy data structures, but it will\n support most of the things I want to do with a higher-\n order language (HOL), except perhaps writing compilers.\n \nI've also been toying for years with the idea of a HOL-like\nassembler, with structured control constructs and HOL-like\nassignment statements. That, in fact, was the impetus behind my\noriginal foray into the jungles of compiler theory. This one may\nnever be built, simply because I've learned that it's actually\neasier to implement a language like KISS, that only uses a subset\nof the CPU instructions. As you know, assembly language can be\nbizarre and irregular in the extreme, and a language that maps\none-for-one onto it can be a real challenge. Still, I've always\nfelt that the syntax used in conventional assemblers is dumb ...\nwhy is\n\n `MOVE.L A,B`\n\nbetter, or easier to translate, than\n\n `B=A ?`\n\nI think it would be an interesting exercise to develop a\n\"compiler\" that would give the programmer complete access to and\ncontrol over the full complement of the CPU instruction set, and\nwould allow you to generate programs as efficient as assembly\nlanguage, without the pain of learning a set of mnemonics. Can\nit be done? I don't know. The real question may be, \"Will the\nresulting language be any easier to write than assembly\"? If\nnot, there's no point in it. I think that it can be done, but\nI'm not completely sure yet how the syntax should look.\n\nPerhaps you have some comments or suggestions on this one. I'd\nlove to hear them.\n\nYou probably won't be surprised to learn that I've already worked\nahead in most of the areas that we will cover. I have some good\nnews: Things never get much harder than they've been so far.\nIt's possible to build a complete, working compiler for a real\nlanguage, using nothing but the same kinds of techniques you've\nlearned so far. And THAT brings up some interesting questions."},{title:"WHY IS IT SO SIMPLE?",path:"/why-is-it-so-simple",content:"Before embarking on this series, I always thought that compilers\nwere just naturally complex computer programs ... the ultimate\nchallenge. Yet the things we have done here have usually turned\nout to be quite simple, sometimes even trivial.\n\nFor awhile, I thought is was simply because I hadn't yet gotten\ninto the meat of the subject. I had only covered the simple\nparts. I will freely admit to you that, even when I began the\nseries, I wasn't sure how far we would be able to go before\nthings got too complex to deal with in the ways we have so far.\nBut at this point I've already been down the road far enough to\nsee the end of it. Guess what?\n \n\n `THERE ARE NO HARD PARTS!`\n\n\nThen, I thought maybe it was because we were not generating very\ngood object code. Those of you who have been following the\nseries and trying sample compiles know that, while the code works\nand is rather foolproof, its efficiency is pretty awful. I\nfigured that if we were concentrating on turning out tight code,\nwe would soon find all that missing complexity.\n\nTo some extent, that one is true. In particular, my first few\nefforts at trying to improve efficiency introduced complexity at\nan alarming rate. But since then I've been tinkering around with\nsome simple optimizations and I've found some that result in very\nrespectable code quality, WITHOUT adding a lot of complexity.\n\nFinally, I thought that perhaps the saving grace was the \"toy\ncompiler\" nature of the study. I have made no pretense that we\nwere ever going to be able to build a compiler to compete with\nBorland and Microsoft. And yet, again, as I get deeper into this\nthing the differences are starting to fade away.\n\nJust to make sure you get the message here, let me state it flat\nout:\n\n USING THE TECHNIQUES WE'VE USED HERE, IT IS POSSIBLE TO\n BUILD A PRODUCTION-QUALITY, WORKING COMPILER WITHOUT ADDING\n A LOT OF COMPLEXITY TO WHAT WE'VE ALREADY DONE.\n\n\nSince the series began I've received some comments from you.\nMost of them echo my own thoughts: \"This is easy! Why do the\ntextbooks make it seem so hard?\" Good question.\n\nRecently, I've gone back and looked at some of those texts again,\nand even bought and read some new ones. Each time, I come away\nwith the same feeling: These guys have made it seem too hard.\n\nWhat's going on here? Why does the whole thing seem difficult in\nthe texts, but easy to us? Are we that much smarter than Aho,\nUllman, Brinch Hansen, and all the rest?\n\nHardly. But we are doing some things differently, and more and\nmore I'm starting to appreciate the value of our approach, and\nthe way that it simplifies things. Aside from the obvious\nshortcuts that I outlined in Part I, like single-character tokens\nand console I/O, we have made some implicit assumptions and done\nsome things differently from those who have designed compilers in\nthe past. As it turns out, our approach makes life a lot easier.\n\nSo why didn't all those other guys use it?\n\nYou have to remember the context of some of the earlier compiler\ndevelopment. These people were working with very small computers\nof limited capacity. Memory was very limited, the CPU\ninstruction set was minimal, and programs ran in batch mode\nrather than interactively. As it turns out, these caused some\nkey design decisions that have really complicated the designs.\nUntil recently, I hadn't realized how much of classical compiler\ndesign was driven by the available hardware.\n\nEven in cases where these limitations no longer apply, people\nhave tended to structure their programs in the same way, since\nthat is the way they were taught to do it.\n\nIn our case, we have started with a blank sheet of paper. There\nis a danger there, of course, that you will end up falling into\ntraps that other people have long since learned to avoid. But it\nalso has allowed us to take different approaches that, partly by\ndesign and partly by pure dumb luck, have allowed us to gain\nsimplicity.\n\nHere are the areas that I think have led to complexity in the\npast:\n\n - Limited RAM Forcing Multiple Passes\n\n I just read \"Brinch Hansen on Pascal Compilers\" (an\n excellent book, BTW). He developed a Pascal compiler for a\n PC, but he started the effort in 1981 with a 64K system, and\n so almost every design decision he made was aimed at making\n the compiler fit into RAM. To do this, his compiler has\n three passes, one of which is the lexical scanner. There is\n no way he could, for example, use the distributed scanner I\n introduced in the last installment, because the program\n structure wouldn't allow it. He also required not one but\n two intermediate languages, to provide the communication\n between phases.\n\n All the early compiler writers had to deal with this issue:\n Break the compiler up into enough parts so that it will fit\n in memory. When you have multiple passes, you need to add\n data structures to support the information that each pass\n leaves behind for the next. That adds complexity, and ends\n up driving the design. Lee's book, \"The Anatomy of a\n Compiler,\" mentions a FORTRAN compiler developed for an IBM\n 1401. It had no fewer than 63 separate passes! Needless to\n say, in a compiler like this the separation into phases\n would dominate the design.\n\n Even in situations where RAM is plentiful, people have\n tended to use the same techniques because that is what\n they're familiar with. It wasn't until Turbo Pascal came\n along that we found how simple a compiler could be if you\n started with different assumptions.\n\n\n - Batch Processing\n \n In the early days, batch processing was the only choice ...\n there was no interactive computing. Even today, compilers\n run in essentially batch mode.\n\n In a mainframe compiler as well as many micro compilers,\n considerable effort is expended on error recovery ... it can\n consume as much as 30-40% of the compiler and completely\n drive the design. The idea is to avoid halting on the first\n error, but rather to keep going at all costs, so that you\n can tell the programmer about as many errors in the whole\n program as possible.\n\n All of that harks back to the days of the early mainframes,\n where turnaround time was measured in hours or days, and it\n was important to squeeze every last ounce of information out\n of each run.\n\n In this series, I've been very careful to avoid the issue of\n error recovery, and instead our compiler simply halts with\n an error message on the first error. I will frankly admit\n that it was mostly because I wanted to take the easy way out\n and keep things simple. But this approach, pioneered by\n Borland in Turbo Pascal, also has a lot going for it anyway.\n Aside from keeping the compiler simple, it also fits very\n well with the idea of an interactive system. When\n compilation is fast, and especially when you have an editor\n such as Borland's that will take you right to the point of\n the error, then it makes a lot of sense to stop there, and\n just restart the compilation after the error is fixed.\n\n\n - Large Programs\n\n Early compilers were designed to handle large programs ...\n essentially infinite ones. In those days there was little\n choice; the idea of subroutine libraries and separate\n compilation were still in the future. Again, this\n assumption led to multi-pass designs and intermediate files\n to hold the results of partial processing.\n\n Brinch Hansen's stated goal was that the compiler should be\n able to compile itself. Again, because of his limited RAM,\n this drove him to a multi-pass design. He needed as little\n resident compiler code as possible, so that the necessary\n tables and other data structures would fit into RAM.\n\n I haven't stated this one yet, because there hasn't been a\n need ... we've always just read and written the data as\n streams, anyway. But for the record, my plan has always\n been that, in a production compiler, the source and object\n data should all coexist in RAM with the compiler, a la the\n early Turbo Pascals. That's why I've been careful to keep\n routines like GetChar and Emit as separate routines, in\n spite of their small size. It will be easy to change them\n to read to and write from memory.\n\n\n - Emphasis on Efficiency\n\n John Backus has stated that, when he and his colleagues\n developed the original FORTRAN compiler, they KNEW that they\n had to make it produce tight code. In those days, there was\n a strong sentiment against HOLs and in favor of assembly\n language, and efficiency was the reason. If FORTRAN didn't\n produce very good code by assembly standards, the users\n would simply refuse to use it. For the record, that FORTRAN\n compiler turned out to be one of the most efficient ever\n built, in terms of code quality. But it WAS complex!\n\n Today, we have CPU power and RAM size to spare, so code\n efficiency is not so much of an issue. By studiously\n ignoring this issue, we have indeed been able to Keep It\n Simple. Ironically, though, as I have said, I have found\n some optimizations that we can add to the basic compiler\n structure, without having to add a lot of complexity. So in\n this case we get to have our cake and eat it too: we will\n end up with reasonable code quality, anyway.\n\n\n - Limited Instruction Sets\n\n The early computers had primitive instruction sets. Things\n that we take for granted, such as stack operations and\n indirect addressing, came only with great difficulty.\n\n Example: In most compiler designs, there is a data structure\n called the literal pool. The compiler typically identifies\n all literals used in the program, and collects them into a\n single data structure. All references to the literals are\n done indirectly to this pool. At the end of the\n compilation, the compiler issues commands to set aside\n storage and initialize the literal pool.\n\n We haven't had to address that issue at all. When we want\n to load a literal, we just do it, in line, as in\n\n `MOVE #3,D0`\n\n There is something to be said for the use of a literal pool,\n particularly on a machine like the 8086 where data and code\n can be separated. Still, the whole thing adds a fairly\n large amount of complexity with little in return.\n\n Of course, without the stack we would be lost. In a micro,\n both subroutine calls and temporary storage depend heavily\n on the stack, and we have used it even more than necessary\n to ease expression parsing.\n\n\n - Desire for Generality\n\n Much of the content of the typical compiler text is taken up\n with issues we haven't addressed here at all ... things like\n automated translation of grammars, or generation of LALR\n parse tables. This is not simply because the authors want\n to impress you. There are good, practical reasons why the\n subjects are there.\n\n We have been concentrating on the use of a recursive-descent\n parser to parse a deterministic grammar, i.e., a grammar\n that is not ambiguous and, therefore, can be parsed with one\n level of lookahead. I haven't made much of this limitation,\n but the fact is that this represents a small subset of\n possible grammars. In fact, there is an infinite number of\n grammars that we can't parse using our techniques. The LR\n technique is a more powerful one, and can deal with grammars\n that we can't.\n\n In compiler theory, it's important to know how to deal with\n these other grammars, and how to transform them into\n grammars that are easier to deal with. For example, many\n (but not all) ambiguous grammars can be transformed into\n unambiguous ones. The way to do this is not always obvious,\n though, and so many people have devoted years to develop\n ways to transform them automatically.\n\n In practice, these issues turn out to be considerably less\n important. Modern languages tend to be designed to be easy\n to parse, anyway. That was a key motivation in the design\n of Pascal. Sure, there are pathological grammars that you\n would be hard pressed to write unambiguous BNF for, but in\n the real world the best answer is probably to avoid those\n grammars!\n\n In our case, of course, we have sneakily let the language\n evolve as we go, so we haven't painted ourselves into any\n corners here. You may not always have that luxury. Still,\n with a little care you should be able to keep the parser\n simple without having to resort to automatic translation of\n the grammar.\n\n\nWe have taken a vastly different approach in this series. We\nstarted with a clean sheet of paper, and developed techniques\nthat work in the context that we are in; that is, a single-user\nPC with rather ample CPU power and RAM space. We have limited\nourselves to reasonable grammars that are easy to parse, we have\nused the instruction set of the CPU to advantage, and we have not\nconcerned ourselves with efficiency. THAT's why it's been easy.\n\nDoes this mean that we are forever doomed to be able to build\nonly toy compilers? No, I don't think so. As I've said, we can\nadd certain optimizations without changing the compiler\nstructure. If we want to process large files, we can always add\nfile buffering to do that. These things do not affect the\noverall program design.\n\nAnd I think that's a key factor. By starting with small and\nlimited cases, we have been able to concentrate on a structure\nfor the compiler that is natural for the job. Since the\nstructure naturally fits the job, it is almost bound to be simple\nand transparent. Adding capability doesn't have to change that\nbasic structure. We can simply expand things like the file\nstructure or add an optimization layer. I guess my feeling is\nthat, back when resources were tight, the structures people ended\nup with were artificially warped to make them work under those\nconditions, and weren't optimum structures for the problem at\nhand."},{title:"CONCLUSION",path:"/conclusion",content:"Anyway, that's my arm-waving guess as to how we've been able to\nkeep things simple. We started with something simple and let it\nevolve naturally, without trying to force it into some\ntraditional mold.\n\nWe're going to press on with this. I've given you a list of the\nareas we'll be covering in future installments. With those\ninstallments, you should be able to build complete, working\ncompilers for just about any occasion, and build them simply. If\nyou REALLY want to build production-quality compilers, you'll be\nable to do that, too.\n\nFor those of you who are chafing at the bit for more parser code,\nI apologize for this digression. I just thought you'd like to\nhave things put into perspective a bit. Next time, we'll get\nback to the mainstream of the tutorial.\n\nSo far, we've only looked at pieces of compilers, and while we\nhave many of the makings of a complete language, we haven't\ntalked about how to put it all together. That will be the\nsubject of our next two installments. Then we'll press on into\nthe new subjects I listed at the beginning of this installment.\n\nSee you then."}]},{title:"Part IX: A TOP VIEW - 16 April 1989",path:"/part-ix-a-top-view-16-april-1989",items:[{title:"INTRODUCTION",path:"/introduction",content:"In the previous installments, we have learned many of the\ntechniques required to build a full-blown compiler. We've done\nboth assignment statements (with Boolean and arithmetic\nexpressions), relational operators, and control constructs. We\nstill haven't addressed procedure or function calls, but even so\nwe could conceivably construct a mini-language without them.\nI've always thought it would be fun to see just how small a\nlanguage one could build that would still be useful. We're\nALMOST in a position to do that now. The problem is: though we\nknow how to parse and translate the constructs, we still don't\nknow quite how to put them all together into a language.\n\nIn those earlier installments, the development of our programs\nhad a decidedly bottom-up flavor. In the case of expression\nparsing, for example, we began with the very lowest level\nconstructs, the individual constants and variables, and worked\nour way up to more complex expressions.\n\nMost people regard the top-down design approach as being better\nthan the bottom-up one. I do too, but the way we did it\ncertainly seemed natural enough for the kinds of things we were\nparsing.\n\nYou mustn't get the idea, though, that the incremental approach\nthat we've been using in all these tutorials is inherently\nbottom-up. In this installment I'd like to show you that the\napproach can work just as well when applied from the top down ...\nmaybe better. We'll consider languages such as C and Pascal, and\nsee how complete compilers can be built starting from the top.\n\nIn the next installment, we'll apply the same technique to build\na complete translator for a subset of the KISS language, which\nI'll be calling TINY. But one of my goals for this series is\nthat you will not only be able to see how a compiler for TINY or\nKISS works, but that you will also be able to design and build\ncompilers for your own languages. The C and Pascal examples will\nhelp. One thing I'd like you to see is that the natural\nstructure of the compiler depends very much on the language being\ntranslated, so the simplicity and ease of construction of the\ncompiler depends very much on letting the language set the\nprogram structure.\n \nIt's a bit much to produce a full C or Pascal compiler here, and\nwe won't try. But we can flesh out the top levels far enough so\nthat you can see how it goes.\n\nLet's get started."},{title:"THE TOP LEVEL",path:"/the-top-level",content:"One of the biggest mistakes people make in a top-down design is\nfailing to start at the true top. They think they know what the\noverall structure of the design should be, so they go ahead and\nwrite it down.\n\nWhenever I start a new design, I always like to do it at the\nabsolute beginning. In program design language (PDL), this top\nlevel looks something like:\n\n```delphi\n begin\n solve the problem\n end\n```\n\nOK, I grant you that this doesn't give much of a hint as to what\nthe next level is, but I like to write it down anyway, just to\ngive me that warm feeling that I am indeed starting at the top.\n\nFor our problem, the overall function of a compiler is to compile\na complete program. Any definition of the language, written in\nBNF, begins here. What does the top level BNF look like? Well,\nthat depends quite a bit on the language to be translated. Let's\ntake a look at Pascal."},{title:"THE STRUCTURE OF PASCAL",path:"/the-structure-of-pascal",content:"Most texts for Pascal include a BNF or \"railroad-track\"\ndefinition of the language. Here are the first few lines of one:\n```\n\n <program> ::= <program-header> <block> '.'\n\n <program-header> ::= PROGRAM <ident>\n\n <block> ::= <declarations> <statements>\n```\n\nWe can write recognizers to deal with each of these elements,\njust as we've done before. For each one, we'll use our familiar\nsingle-character tokens to represent the input, then flesh things\nout a little at a time. Let's begin with the first recognizer:\nthe program itself.\n \nTo translate this, we'll start with a fresh copy of the Cradle.\nSince we're back to single-character names, we'll just use a 'p'\nto stand for 'PROGRAM.'\n\nTo a fresh copy of the cradle, add the following code, and insert\na call to it from the main program:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate A Program }\n\nprocedure Prog;\nvar Name: char;\nbegin\n Match('p'); { Handles program header part }\n Name := GetName;\n Prolog(Name);\n Match('.');\n Epilog(Name);\nend;\n{--------------------------------------------------------------}\n\n```\nThe procedures Prolog and Epilog perform whatever is required to\nlet the program interface with the operating system, so that it\ncan execute as a program. Needless to say, this part will be\nVERY OS-dependent. Remember, I've been emitting code for a 68000\nrunning under the OS I use, which is SK*DOS. I realize most of\nyou are using PC's and would rather see something else, but I'm\nin this thing too deep to change now!\n\nAnyhow, SK*DOS is a particularly easy OS to interface to. Here\nis the code for Prolog and Epilog:\n\n```delphi\n{--------------------------------------------------------------}\n{ Write the Prolog }\n\nprocedure Prolog;\nbegin\n EmitLn('WARMST EQU $A01E');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write the Epilog }\n\nprocedure Epilog(Name: char);\nbegin\n EmitLn('DC WARMST');\n EmitLn('END ' + Name);\nend;\n{--------------------------------------------------------------}\n``` \nAs usual, add this code and try out the \"compiler.\" At this\npoint, there is only one legal input:\n\n\n px. (where x is any single letter, the program name)\n\n\nWell, as usual our first effort is rather unimpressive, but by\nnow I'm sure you know that things will get more interesting.\nThere is one important thing to note: THE OUTPUT IS A WORKING,\nCOMPLETE, AND EXECUTABLE PROGRAM (at least after it's assembled).\n\nThis is very important. The nice feature of the top-down\napproach is that at any stage you can compile a subset of the\ncomplete language and get a program that will run on the target\nmachine. From here on, then, we need only add features by\nfleshing out the language constructs. It's all very similar to\nwhat we've been doing all along, except that we're approaching it\nfrom the other end."},{title:"FLESHING IT OUT",path:"/fleshing-it-out",content:"To flesh out the compiler, we only have to deal with language\nfeatures one by one. I like to start with a stub procedure that\ndoes nothing, then add detail in incremental fashion. Let's\nbegin by processing a block, in accordance with its PDL above.\nWe can do this in two stages. First, add the null procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Pascal Block }\n\nprocedure DoBlock(Name: char);\nbegin\nend;\n{--------------------------------------------------------------}\n```\n\nand modify Prog to read:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate A Program }\n\nprocedure Prog;\nvar Name: char;\nbegin\n Match('p');\n Name := GetName;\n Prolog;\n DoBlock(Name);\n Match('.');\n Epilog(Name);\nend;\n{--------------------------------------------------------------}\n```\n\nThat certainly shouldn't change the behavior of the program, and\nit doesn't. But now the definition of Prog is complete, and we\ncan proceed to flesh out DoBlock. That's done right from its BNF\ndefinition:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Pascal Block }\n\nprocedure DoBlock(Name: char);\nbegin\n Declarations;\n PostLabel(Name);\n Statements;\nend;\n{--------------------------------------------------------------}\n```\n\nThe procedure PostLabel was defined in the installment on\nbranches. Copy it into your cradle.\n\nI probably need to explain the reason for inserting the label\nwhere I have. It has to do with the operation of SK*DOS. Unlike\nsome OS's, SK*DOS allows the entry point to the main program to\nbe anywhere in the program. All you have to do is to give that\npoint a name. The call to PostLabel puts that name just before\nthe first executable statement in the main program. How does\nSK*DOS know which of the many labels is the entry point, you ask?\nIt's the one that matches the END statement at the end of the\nprogram.\n\nOK, now we need stubs for the procedures Declarations and\nStatements. Make them null procedures as we did before.\n\nDoes the program still run the same? Then we can move on to the\nnext stage."},{title:"DECLARATIONS",path:"/declarations",content:"The BNF for Pascal declarations is:\n\n```\n <declarations> ::= ( <label list> |\n <constant list> |\n <type list> |\n <variable list> |\n <procedure> |\n <function> )*\n \n```\n(Note that I'm using the more liberal definition used by Turbo\nPascal. In the standard Pascal definition, each of these parts\nmust be in a specific order relative to the rest.)\n\nAs usual, let's let a single character represent each of these\ndeclaration types. The new form of Declarations is:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate the Declaration Part }\n\nprocedure Declarations;\nbegin\n while Look in ['l', 'c', 't', 'v', 'p', 'f'] do\n case Look of\n 'l': Labels;\n 'c': Constants;\n 't': Types;\n 'v': Variables;\n 'p': DoProcedure;\n 'f': DoFunction;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nOf course, we need stub procedures for each of these declaration\ntypes. This time, they can't quite be null procedures, since\notherwise we'll end up with an infinite While loop. At the very\nleast, each recognizer must eat the character that invokes it.\nInsert the following procedures:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process Label Statement }\n\nprocedure Labels;\nbegin\n Match('l');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process Const Statement }\n\nprocedure Constants;\nbegin\n Match('c');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process Type Statement }\nprocedure Types;\nbegin\n Match('t');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process Var Statement }\n\nprocedure Variables;\nbegin\n Match('v');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process Procedure Definition }\n\nprocedure DoProcedure;\nbegin\n Match('p');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process Function Definition }\n\nprocedure DoFunction;\nbegin\n Match('f');\nend;\n{--------------------------------------------------------------}\n```\n\nNow try out the compiler with a few representative inputs. You\ncan mix the declarations any way you like, as long as the last\ncharacter in the program is'.' to indicate the end of the\nprogram. Of course, none of the declarations actually declare\nanything, so you don't need (and can't use) any characters other\nthan those standing for the keywords.\n\nWe can flesh out the statement part in a similar way. The BNF\nfor it is:\n\n```\n <statements> ::= <compound statement>\n\n <compound statement> ::= BEGIN <statement>\n (';' <statement>) END\n\n```\nNote that statements can begin with any identifier except END.\nSo the first stub form of procedure Statements is:\n \n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate the Statement Part }\n\nprocedure Statements;\nbegin\n Match('b');\n while Look <> 'e' do\n GetChar;\n Match('e');\nend;\n{--------------------------------------------------------------}\n\n```\nAt this point the compiler will accept any number of\ndeclarations, followed by the BEGIN block of the main program.\nThis block itself can contain any characters at all (except an\nEND), but it must be present.\n\nThe simplest form of input is now\n\n `'pxbe.'`\n\nTry it. Also try some combinations of this. Make some\ndeliberate errors and see what happens.\n\nAt this point you should be beginning to see the drill. We begin\nwith a stub translator to process a program, then we flesh out\neach procedure in turn, based upon its BNF definition. Just as\nthe lower-level BNF definitions add detail and elaborate upon the\nhigher-level ones, the lower-level recognizers will parse more\ndetail of the input program. When the last stub has been\nexpanded, the compiler will be complete. That's top-down\ndesign/implementation in its purest form.\n\nYou might note that even though we've been adding procedures, the\noutput of the program hasn't changed. That's as it should be.\nAt these top levels there is no emitted code required. The\nrecognizers are functioning as just that: recognizers. They are\naccepting input sentences, catching bad ones, and channeling good\ninput to the right places, so they are doing their job. If we\nwere to pursue this a bit longer, code would start to appear.\n\nThe next step in our expansion should probably be procedure\nStatements. The Pascal definition is:\n\n```\n <statement> ::= <simple statement> | <structured statement>\n\n <simple statement> ::= <assignment> | <procedure call> | null\n\n <structured statement> ::= <compound statement> |\n <if statement> |\n <case statement> |\n <while statement> |\n <repeat statement> |\n <for statement> |\n <with statement>\n\n```\nThese are starting to look familiar. As a matter of fact, you\nhave already gone through the process of parsing and generating\ncode for both assignment statements and control structures. This\nis where the top level meets our bottom-up approach of previous\nsessions. The constructs will be a little different from those\nwe've been using for KISS, but the differences are nothing you\ncan't handle.\n\nI think you can get the picture now as to the procedure. We\nbegin with a complete BNF description of the language. Starting\nat the top level, we code up the recognizer for that BNF\nstatement, using stubs for the next-level recognizers. Then we\nflesh those lower-level statements out one by one.\n\nAs it happens, the definition of Pascal is very compatible with\nthe use of BNF, and BNF descriptions of the language abound.\nArmed with such a description, you will find it fairly\nstraightforward to continue the process we've begun.\n\nYou might have a go at fleshing a few of these constructs out,\njust to get a feel for it. I don't expect you to be able to\ncomplete a Pascal compiler here ... there are too many things\nsuch as procedures and types that we haven't addressed yet ...\nbut it might be helpful to try some of the more familiar ones.\nIt will do you good to see executable programs coming out the\nother end.\n\nIf I'm going to address those issues that we haven't covered yet,\nI'd rather do it in the context of KISS. We're not trying to\nbuild a complete Pascal compiler just yet, so I'm going to stop\nthe expansion of Pascal here. Let's take a look at a very\ndifferent language."},{title:"THE STRUCTURE OF C",path:"/the-structure-of-c",content:"The C language is quite another matter, as you'll see. Texts on\nC rarely include a BNF definition of the language. Probably\nthat's because the language is quite hard to write BNF for.\n\nOne reason I'm showing you these structures now is so that I can\nimpress upon you these two facts:\n\n (1) The definition of the language drives the structure of the\n compiler. What works for one language may be a disaster for\n another. It's a very bad idea to try to force a given\n structure upon the compiler. Rather, you should let the BNF\n drive the structure, as we have done here.\n \n (2) A language that is hard to write BNF for will probably be\n hard to write a compiler for, as well. C is a popular\n language, and it has a reputation for letting you do\n virtually anything that is possible to do. Despite the\n success of Small C, C is _NOT_ an easy language to parse.\n\n\nA C program has less structure than its Pascal counterpart. At\nthe top level, everything in C is a static declaration, either of\ndata or of a function. We can capture this thought like this:\n\n```\n <program> ::= ( <global declaration> )*\n\n <global declaration> ::= <data declaration> |\n <function>\n```\nIn Small C, functions can only have the default type int, which\nis not declared. This makes the input easy to parse: the first\ntoken is either \"int,\" \"char,\" or the name of a function. In\nSmall C, the preprocessor commands are also processed by the\ncompiler proper, so the syntax becomes:\n\n```\n <global declaration> ::= '#' <preprocessor command> |\n 'int' <data list> |\n 'char' <data list> |\n <ident> <function body> |\n```\n\nAlthough we're really more interested in full C here, I'll show\nyou the code corresponding to this top-level structure for Small\nC.\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate A Program }\n\nprocedure Prog;\nbegin\n while Look <> ^Z do begin\n case Look of\n '#': PreProc;\n 'i': IntDecl;\n 'c': CharDecl;\n else DoFunction(Int);\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\nNote that I've had to use a ^Z to indicate the end of the source.\nC has no keyword such as END or the '.' to otherwise indicate the\nend.\n \nWith full C, things aren't even this easy. The problem comes\nabout because in full C, functions can also have types. So when\nthe compiler sees a keyword like \"int,\" it still doesn't know\nwhether to expect a data declaration or a function definition.\nThings get more complicated since the next token may not be a\nname ... it may start with an '*' or '(', or combinations of the\ntwo.\n\nMore specifically, the BNF for full C begins with:\n\n```\n <program> ::= ( <top-level decl> )*\n\n <top-level decl> ::= <function def> | <data decl>\n\n <data decl> ::= [<class>] <type> <decl-list>\n\n <function def> ::= [<class>] [<type>] <function decl>\n```\n\nYou can now see the problem: The first two parts of the\ndeclarations for data and functions can be the same. Because of\nthe ambiguity in the grammar as written above, it's not a\nsuitable grammar for a recursive-descent parser. Can we\ntransform it into one that is suitable? Yes, with a little work.\nSuppose we write it this way:\n```\n\n <top-level decl> ::= [<class>] <decl>\n\n <decl> ::= <type> <typed decl> | <function decl>\n\n <typed decl> ::= <data list> | <function decl>\n\n```\nWe can build a parsing routine for the class and type\ndefinitions, and have them store away their findings and go on,\nwithout their ever having to \"know\" whether a function or a data\ndeclaration is being processed.\n\nTo begin, key in the following version of the main program:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n while Look <> ^Z do begin\n GetClass;\n GetType;\n TopDecl;\n end;\nend.\n\n{--------------------------------------------------------------}\n```\n\nFor the first round, just make the three procedures stubs that do\nnothing _BUT_ call GetChar.\n\nDoes this program work? Well, it would be hard put NOT to, since\nwe're not really asking it to do anything. It's been said that a\nC compiler will accept virtually any input without choking. It's\ncertainly true of THIS compiler, since in effect all it does is\nto eat input characters until it finds a ^Z.\n\nNext, let's make GetClass do something worthwhile. Declare the\nglobal variable\n\n```delphi\n var Class: char;\n```\n\nand change GetClass to do the following:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Storage Class Specifier }\n\nProcedure GetClass;\nbegin\n if Look in ['a', 'x', 's'] then begin\n Class := Look;\n GetChar;\n end\n else Class := 'a';\nend;\n{--------------------------------------------------------------}\n```\n\nHere, I've used three single characters to represent the three\nstorage classes \"auto,\" \"extern,\" and \"static.\" These are not\nthe only three possible classes ... there are also \"register\" and\n\"typedef,\" but this should give you the picture. Note that the\ndefault class is \"auto.\"\n\nWe can do a similar thing for types. Enter the following\nprocedure next:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Type Specifier }\n\nprocedure GetType;\nbegin\n Typ := ' ';\n if Look = 'u' then begin\n Sign := 'u';\n Typ := 'i';\n GetChar;\n end\n else Sign := 's';\n if Look in ['i', 'l', 'c'] then begin\n Typ := Look;\n GetChar;\n end;\nend;\n{--------------------------------------------------------------}\n```\nNote that you must add two more global variables, Sign and Typ.\n\nWith these two procedures in place, the compiler will process the\nclass and type definitions and store away their findings. We can\nnow process the rest of the declaration.\n\nWe are by no means out of the woods yet, because there are still\nmany complexities just in the definition of the type, before we\neven get to the actual data or function names. Let's pretend for\nthe moment that we have passed all those gates, and that the next\nthing in the input stream is a name. If the name is followed by\na left paren, we have a function declaration. If not, we have at\nleast one data item, and possibly a list, each element of which\ncan have an initializer.\n\nInsert the following version of TopDecl:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process a Top-Level Declaration }\n\nprocedure TopDecl;\nvar Name: char;\nbegin\n Name := Getname;\n if Look = '(' then\n DoFunc(Name)\n else\n DoData(Name);\nend;\n{--------------------------------------------------------------}\n```\n\n(Note that, since we have already read the name, we must pass it\nalong to the appropriate routine.)\n\nFinally, add the two procedures DoFunc and DoData:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process a Function Definition }\n\nprocedure DoFunc(n: char);\nbegin\n Match('(');\n Match(')');\n Match('{');\n Match('}');\n if Typ = ' ' then Typ := 'i';\n Writeln(Class, Sign, Typ, ' function ', n);\nend;\n\n{--------------------------------------------------------------}\n{ Process a Data Declaration }\n\nprocedure DoData(n: char);\nbegin\n if Typ = ' ' then Expected('Type declaration');\n Writeln(Class, Sign, Typ, ' data ', n);\n while Look = ',' do begin\n Match(',');\n n := GetName;\n WriteLn(Class, Sign, Typ, ' data ', n);\n end;\n Match(';');\nend;\n{--------------------------------------------------------------}\n```\n\nSince we're still a long way from producing executable code, I\ndecided to just have these two routines tell us what they found.\n\nOK, give this program a try. For data declarations, it's OK to\ngive a list separated by commas. We can't process initializers\nas yet. We also can't process argument lists for the functions,\nbut the \"(){}\" characters should be there.\n\nWe're still a _VERY_ long way from having a C compiler, but what\nwe have is starting to process the right kinds of inputs, and is\nrecognizing both good and bad inputs. In the process, the\nnatural structure of the compiler is starting to take form.\n\nCan we continue this until we have something that acts more like\na compiler. Of course we can. Should we? That's another matter.\nI don't know about you, but I'm beginning to get dizzy, and we've\nstill got a long way to go to even get past the data\ndeclarations.\n\nAt this point, I think you can see how the structure of the\ncompiler evolves from the language definition. The structures\nwe've seen for our two examples, Pascal and C, are as different\nas night and day. Pascal was designed at least partly to be easy\nto parse, and that's reflected in the compiler. In general, in\nPascal there is more structure and we have a better idea of what\nkinds of constructs to expect at any point. In C, on the other\nhand, the program is essentially a list of declarations,\nterminated only by the end of file.\n\nWe could pursue both of these structures much farther, but\nremember that our purpose here is not to build a Pascal or a C\ncompiler, but rather to study compilers in general. For those of\nyou who DO want to deal with Pascal or C, I hope I've given you\nenough of a start so that you can take it from here (although\nyou'll soon need some of the stuff we still haven't covered yet,\nsuch as typing and procedure calls). For the rest of you, stay\nwith me through the next installment. There, I'll be leading you\nthrough the development of a complete compiler for TINY, a subset\nof KISS.\n\nSee you then."}]},{title:'Part X: INTRODUCING "TINY" - 21 May 1989',path:"/part-x-introducing-tiny-21-may-1989",items:[{title:"INTRODUCTION",path:"/introduction",content:"In the last installment, I showed you the general idea for the\ntop-down development of a compiler. I gave you the first few\nsteps of the process for compilers for Pascal and C, but I\nstopped far short of pushing it through to completion. The\nreason was simple: if we're going to produce a real, functional\ncompiler for any language, I'd rather do it for KISS, the\nlanguage that I've been defining in this tutorial series.\n\nIn this installment, we're going to do just that, for a subset of\nKISS which I've chosen to call TINY.\n\nThe process will be essentially that outlined in Installment IX,\nexcept for one notable difference. In that installment, I\nsuggested that you begin with a full BNF description of the\nlanguage. That's fine for something like Pascal or C, for which\nthe language definition is firm. In the case of TINY, however,\nwe don't yet have a full description ... we seem to be defining\nthe language as we go. That's OK. In fact, it's preferable,\nsince we can tailor the language slightly as we go, to keep the\nparsing easy.\n\nSo in the development that follows, we'll actually be doing a\ntop-down development of BOTH the language and its compiler. The\nBNF description will grow along with the compiler.\n\nIn this process, there will be a number of decisions to be made,\neach of which will influence the BNF and therefore the nature of\nthe language. At each decision point I'll try to remember to\nexplain the decision and the rationale behind my choice. That\nway, if you happen to hold a different opinion and would prefer a\ndifferent option, you can choose it instead. You now have the\nbackground to do that. I guess the important thing to note is\nthat nothing we do here is cast in concrete. When YOU'RE\ndesigning YOUR language, you should feel free to do it YOUR way.\n\nMany of you may be asking at this point: Why bother starting over\nfrom scratch? We had a working subset of KISS as the outcome of\nInstallment VII (lexical scanning). Why not just extend it as\nneeded? The answer is threefold. First of all, I have been\nmaking a number of changes to further simplify the program ...\nchanges like encapsulating the code generation procedures, so\nthat we can convert to a different target machine more easily.\nSecond, I want you to see how the development can indeed be done\nfrom the top down as outlined in the last installment. Finally,\nwe both need the practice. Each time I go through this exercise,\nI get a little better at it, and you will, also."},{title:"GETTING STARTED",path:"/getting-started",content:"Many years ago there were languages called Tiny BASIC, Tiny\nPascal, and Tiny C, each of which was a subset of its parent full\nlanguage. Tiny BASIC, for example, had only single-character\nvariable names and global variables. It supported only a single\ndata type. Sound familiar? At this point we have almost all the\ntools we need to build a compiler like that.\n\nYet a language called Tiny-anything still carries some baggage\ninherited from its parent language. I've often wondered if this\nis a good idea. Granted, a language based upon some parent\nlanguage will have the advantage of familiarity, but there may\nalso be some peculiar syntax carried over from the parent that\nmay tend to add unnecessary complexity to the compiler. (Nowhere\nis this more true than in Small C.)\n\nI've wondered just how small and simple a compiler could be made\nand still be useful, if it were designed from the outset to be\nboth easy to use and to parse. Let's find out. This language\nwill just be called \"TINY,\" period. It's a subset of KISS, which\nI also haven't fully defined, so that at least makes us\nconsistent (!). I suppose you could call it TINY KISS. But that\nopens up a whole can of worms involving cuter and cuter (and\nperhaps more risque) names, so let's just stick with TINY.\n\nThe main limitations of TINY will be because of the things we\nhaven't yet covered, such as data types. Like its cousins Tiny C\nand Tiny BASIC, TINY will have only one data type, the 16-bit\ninteger. The first version we develop will also have no\nprocedure calls and will use single-character variable names,\nalthough as you will see we can remove these restrictions without\nmuch effort.\n\nThe language I have in mind will share some of the good features\nof Pascal, C, and Ada. Taking a lesson from the comparison of\nthe Pascal and C compilers in the previous installment, though,\nTINY will have a decided Pascal flavor. Wherever feasible, a\nlanguage structure will be bracketed by keywords or symbols, so\nthat the parser will know where it's going without having to\nguess.\n\nOne other ground rule: As we go, I'd like to keep the compiler\nproducing real, executable code. Even though it may not DO much\nat the beginning, it will at least do it correctly.\n\nFinally, I'll use a couple of Pascal restrictions that make\nsense: All data and procedures must be declared before they are\nused. That makes good sense, even though for now the only data\ntype we'll use is a word. This rule in turn means that the only\nreasonable place to put the executable code for the main program\nis at the end of the listing.\n\nThe top-level definition will be similar to Pascal:\n\n```\n <program> ::= PROGRAM <top-level decl> <main> '.'\n```\n\nAlready, we've reached a decision point. My first thought was to\nmake the main block optional. It doesn't seem to make sense to\nwrite a \"program\" with no main program, but it does make sense if\nwe're allowing for multiple modules, linked together. As a\nmatter of fact, I intend to allow for this in KISS. But then we\nbegin to open up a can of worms that I'd rather leave closed for\nnow. For example, the term \"PROGRAM\" really becomes a misnomer.\nThe MODULE of Modula-2 or the Unit of Turbo Pascal would be more\nappropriate. Second, what about scope rules? We'd need a\nconvention for dealing with name visibility across modules.\nBetter for now to just keep it simple and ignore the idea\naltogether.\n\nThere's also a decision in choosing to require the main program\nto be last. I toyed with the idea of making its position\noptional, as in C. The nature of SK*DOS, the OS I'm compiling\nfor, make this very easy to do. But this doesn't really make\nmuch sense in view of the Pascal-like requirement that all data\nand procedures be declared before they're referenced. Since the\nmain program can only call procedures that have already been\ndeclared, the only position that makes sense is at the end, a la\nPascal.\n\nGiven the BNF above, let's write a parser that just recognizes\nthe brackets:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Program }\n\nprocedure Prog;\nbegin\n Match('p');\n Header;\n Prolog;\n Match('.');\n Epilog;\nend;\n{--------------------------------------------------------------}\n```\n\nThe procedure Header just emits the startup code required by the\nassembler:\n \n```delphi\n{--------------------------------------------------------------}\n{ Write Header Info }\n\nprocedure Header;\nbegin\n WriteLn('WARMST', TAB, 'EQU $A01E');\nend;\n{--------------------------------------------------------------}\n```\n\nThe procedures Prolog and Epilog emit the code for identifying\nthe main program, and for returning to the OS:\n\n```delphi\n{--------------------------------------------------------------}\n{ Write the Prolog }\n\nprocedure Prolog;\nbegin\n PostLabel('MAIN');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write the Epilog }\n\nprocedure Epilog;\nbegin\n EmitLn('DC WARMST');\n EmitLn('END MAIN');\nend;\n{--------------------------------------------------------------}\n```\n\nThe main program just calls Prog, and then looks for a clean\nending:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n Prog;\n if Look <> CR then Abort('Unexpected data after ''.''');\nend.\n{--------------------------------------------------------------}\n```\n\nAt this point, TINY will accept only one input \"program,\" the\nnull program:\n\n\n PROGRAM . (or 'p.' in our shorthand.)\n\nNote, though, that the compiler DOES generate correct code for\nthis program. It will run, and do what you'd expect the null\nprogram to do, that is, nothing but return gracefully to the OS.\n\nAs a matter of interest, one of my favorite compiler benchmarks\nis to compile, link, and execute the null program in whatever\nlanguage is involved. You can learn a lot about the\nimplementation by measuring the overhead in time required to\ncompile what should be a trivial case. It's also interesting to\nmeasure the amount of code produced. In many compilers, the code\ncan be fairly large, because they always include the whole run-\ntime library whether they need it or not. Early versions of\nTurbo Pascal produced a 12K object file for this case. VAX C\ngenerates 50K!\n\nThe smallest null programs I've seen are those produced by\nModula-2 compilers, and they run about 200-800 bytes.\n\nIn the case of TINY, we HAVE no run-time library as yet, so the\nobject code is indeed tiny: two bytes. That's got to be a\nrecord, and it's likely to remain one since it is the minimum\nsize required by the OS.\n\nThe next step is to process the code for the main program. I'll\nuse the Pascal BEGIN-block:\n\n```\n <main> ::= BEGIN <block> END\n```\n\nHere, again, we have made a decision. We could have chosen to\nrequire a \"PROCEDURE MAIN\" sort of declaration, similar to C. I\nmust admit that this is not a bad idea at all ... I don't\nparticularly like the Pascal approach since I tend to have\ntrouble locating the main program in a Pascal listing. But the\nalternative is a little awkward, too, since you have to deal with\nthe error condition where the user omits the main program or\nmisspells its name. Here I'm taking the easy way out.\n\nAnother solution to the \"where is the main program\" problem might\nbe to require a name for the program, and then bracket the main\nby\n\n```\n BEGIN <name>\n END <name>\n```\n\nsimilar to the convention of Modula 2. This adds a bit of\n\"syntactic sugar\" to the language. Things like this are easy to\nadd or change to your liking, if the language is your own design.\n\nTo parse this definition of a main block, change procedure Prog\nto read:\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Program }\n\nprocedure Prog;\nbegin\n Match('p');\n Header;\n Main;\n Match('.');\nend;\n{--------------------------------------------------------------}\n```\n\nand add the new procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Main Program }\n\nprocedure Main;\nbegin\n Match('b');\n Prolog;\n Match('e');\n Epilog;\nend;\n{--------------------------------------------------------------}\n```\n\nNow, the only legal program is:\n\n```delphi\n PROGRAM BEGIN END . (or 'pbe.')\n```\n\nAren't we making progress??? Well, as usual it gets better. You\nmight try some deliberate errors here, like omitting the 'b' or\nthe 'e', and see what happens. As always, the compiler should\nflag all illegal inputs."},{title:"DECLARATIONS",path:"/declarations",content:"The obvious next step is to decide what we mean by a declaration.\nMy intent here is to have two kinds of declarations: variables\nand procedures/functions. At the top level, only global\ndeclarations are allowed, just as in C.\n\nFor now, there can only be variable declarations, identified by\nthe keyword VAR (abbreviated 'v'):\n\n```\n <top-level decls> ::= ( <data declaration> )*\n\n <data declaration> ::= VAR <var-list>\n```\n\nNote that since there is only one variable type, there is no need\nto declare the type. Later on, for full KISS, we can easily add\na type description.\n\nThe procedure Prog becomes:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Program }\n\nprocedure Prog;\nbegin\n Match('p');\n Header;\n TopDecls;\n Main;\n Match('.');\nend;\n{--------------------------------------------------------------}\n```\n\nNow, add the two new procedures:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process a Data Declaration }\n\nprocedure Decl;\nbegin\n Match('v');\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n while Look <> 'b' do\n case Look of\n 'v': Decl;\n else Abort('Unrecognized Keyword ''' + Look + '''');\n end;\nend;\n{--------------------------------------------------------------}\n\n```\nNote that at this point, Decl is just a stub. It generates no\ncode, and it doesn't process a list ... every variable must occur\nin a separate VAR statement.\n\nOK, now we can have any number of data declarations, each\nstarting with a 'v' for VAR, before the BEGIN-block. Try a few\ncases and see what happens."},{title:"DECLARATIONS AND SYMBOLS",path:"/declarations-and-symbols",content:"That looks pretty good, but we're still only generating the null\nprogram for output. A real compiler would issue assembler\ndirectives to allocate storage for the variables. It's about\ntime we actually produced some code.\n\nWith a little extra code, that's an easy thing to do from\nprocedure Decl. Modify it as follows:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Data Declaration }\n\nprocedure Decl;\nvar Name: char;\nbegin\n Match('v');\n Alloc(GetName);\nend;\n{--------------------------------------------------------------}\n```\n\nThe procedure Alloc just issues a command to the assembler to\nallocate storage:\n\n```delphi\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc(N: char);\nbegin\n WriteLn(N, ':', TAB, 'DC 0');\nend;\n{--------------------------------------------------------------}\n```\n\nGive this one a whirl. Try an input that declares some\nvariables, such as:\n\n `pvxvyvzbe.`\n\nSee how the storage is allocated? Simple, huh? Note also that\nthe entry point, \"MAIN,\" comes out in the right place.\n\nFor the record, a \"real\" compiler would also have a symbol table\nto record the variables being used. Normally, the symbol table\nis necessary to record the type of each variable. But since in\nthis case all variables have the same type, we don't need a\nsymbol table for that reason. As it turns out, we're going to\nfind a symbol necessary even without different types, but let's\npostpone that need until it arises.\n\nOf course, we haven't really parsed the correct syntax for a data\ndeclaration, since it involves a variable list. Our version only\npermits a single variable. That's easy to fix, too.\n\nThe BNF for `<var-list>` is\n\n```\n <var-list> ::= <ident> (, <ident>)*\n```\n\nAdding this syntax to Decl gives this new version:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Data Declaration }\n\nprocedure Decl;\nvar Name: char;\nbegin\n Match('v');\n Alloc(GetName);\n while Look = ',' do begin\n GetChar;\n Alloc(GetName);\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nOK, now compile this code and give it a try. Try a number of\nlines of VAR declarations, try a list of several variables on one\nline, and try combinations of the two. Does it work?"},{title:"INITIALIZERS",path:"/initializers",content:"As long as we're dealing with data declarations, one thing that's\nalways bothered me about Pascal is that it doesn't allow\ninitializing data items in the declaration. That feature is\nadmittedly sort of a frill, and it may be out of place in a\nlanguage that purports to be a minimal language. But it's also\nSO easy to add that it seems a shame not to do so. The BNF\nbecomes:\n\n```\n <var-list> ::= <var> ( <var> )*\n\n <var> ::= <ident> [ = <integer> ]\n```\nChange Alloc as follows:\n\n```delphi\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc(N: char);\nbegin\n Write(N, ':', TAB, 'DC ');\n if Look = '=' then begin\n Match('=');\n WriteLn(GetNum);\n end\n else\n WriteLn('0');\nend;\n{--------------------------------------------------------------}\n```\n\nThere you are: an initializer with six added lines of Pascal.\n\nOK, try this version of TINY and verify that you can, indeed,\ngive the variables initial values.\n\nBy golly, this thing is starting to look real! Of course, it\nstill doesn't DO anything, but it looks good, doesn't it?\n\nBefore leaving this section, I should point out that we've used\ntwo versions of function GetNum. One, the earlier one, returns a\ncharacter value, a single digit. The other accepts a multi-digit\ninteger and returns an integer value. Either one will work here,\nsince WriteLn will handle either type. But there's no reason to\nlimit ourselves to single-digit values here, so the correct\nversion to use is the one that returns an integer. Here it is:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: integer;\nvar Val: integer;\nbegin\n Val := 0;\n if not IsDigit(Look) then Expected('Integer');\n while IsDigit(Look) do begin\n Val := 10 * Val + Ord(Look) - Ord('0');\n GetChar;\n end;\n GetNum := Val;\nend;\n{--------------------------------------------------------------}\n```\nAs a matter of fact, strictly speaking we should allow for\nexpressions in the data field of the initializer, or at the very\nleast for negative values. For now, let's just allow for\nnegative values by changing the code for Alloc as follows:\n\n```delphi\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc(N: char);\nbegin\n if InTable(N) then Abort('Duplicate Variable Name ' + N);\n ST[N] := 'v';\n Write(N, ':', TAB, 'DC ');\n if Look = '=' then begin\n Match('=');\n If Look = '-' then begin\n Write(Look);\n Match('-');\n end;\n WriteLn(GetNum);\n end\n else\n WriteLn('0');\nend;\n{--------------------------------------------------------------}\n```\n\nNow you should be able to initialize variables with negative\nand/or multi-digit values."},{title:"THE SYMBOL TABLE",path:"/the-symbol-table",content:"There's one problem with the compiler as it stands so far: it\ndoesn't do anything to record a variable when we declare it. So\nthe compiler is perfectly content to allocate storage for several\nvariables with the same name. You can easily verify this with an\ninput like\n\n\n `pvavavabe.`\n\n\nHere we've declared the variable A three times. As you can see,\nthe compiler will cheerfully accept that, and generate three\nidentical labels. Not good.\n\nLater on, when we start referencing variables, the compiler will\nalso let us reference variables that don't exist. The assembler\nwill catch both of these error conditions, but it doesn't seem\nfriendly at all to pass such errors along to the assembler. The\ncompiler should catch such things at the source language level.\n\nSo even though we don't need a symbol table to record data types,\nwe ought to install one just to check for these two conditions.\nSince at this point we are still restricted to single-character\nvariable names, the symbol table can be trivial. To provide for\nit, first add the following declaration at the beginning of your\nprogram:\n\n```delphi\n var ST: array['A'..'Z'] of char;\n```\n\nand insert the following function:\n\n```delphi\n{--------------------------------------------------------------}\n{ Look for Symbol in Table }\n\nfunction InTable(n: char): Boolean;\nbegin\n InTable := ST[n] <> ' ';\nend;\n{--------------------------------------------------------------}\n```\n\nWe also need to initialize the table to all blanks. The\nfollowing lines in Init will do the job:\n\n```delphi\nvar i: char;\nbegin\n for i := 'A' to 'Z' do\n ST[i] := ' ';\n ...\n```\n\nFinally, insert the following two lines at the beginning of\nAlloc:\n\n```delphi\n if InTable(N) then Abort('Duplicate Variable Name ' + N);\n ST[N] := 'v';\n```\n\nThat should do it. The compiler will now catch duplicate\ndeclarations. Later, we can also use InTable when generating\nreferences to the variables."},{title:"EXECUTABLE STATEMENTS",path:"/executable-statements",content:"At this point, we can generate a null program that has some data\nvariables declared and possibly initialized. But so far we\nhaven't arranged to generate the first line of executable code.\n\nBelieve it or not, though, we almost have a usable language!\nWhat's missing is the executable code that must go into the main\nprogram. But that code is just assignment statements and control\nstatements ... all stuff we have done before. So it shouldn't\ntake us long to provide for them, as well.\n\nThe BNF definition given earlier for the main program included a\nstatement block, which we have so far ignored:\n\n```\n <main> ::= BEGIN <block> END\n```\n\nFor now, we can just consider a block to be a series of\nassignment statements:\n\n```\n <block> ::= (Assignment)*\n\n```\nLet's start things off by adding a parser for the block. We'll\nbegin with a stub for the assignment statement:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nbegin\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n while Look <> 'e' do\n Assignment;\nend;\n{--------------------------------------------------------------}\n```\n\nModify procedure Main to call Block as shown below:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Main Program }\n\nprocedure Main;\nbegin\n Match('b');\n Prolog;\n Block;\n Match('e');\n Epilog;\nend;\n{--------------------------------------------------------------}\n```\n\nThis version still won't generate any code for the \"assignment\nstatements\" ... all it does is to eat characters until it sees\nthe 'e' for 'END.' But it sets the stage for what is to follow.\n\nThe next step, of course, is to flesh out the code for an\nassignment statement. This is something we've done many times\nbefore, so I won't belabor it. This time, though, I'd like to\ndeal with the code generation a little differently. Up till now,\nwe've always just inserted the Emits that generate output code in\nline with the parsing routines. A little unstructured, perhaps,\nbut it seemed the most straightforward approach, and made it easy\nto see what kind of code would be emitted for each construct.\n\nHowever, I realize that most of you are using an 80x86 computer,\nso the 68000 code generated is of little use to you. Several of\nyou have asked me if the CPU-dependent code couldn't be collected\ninto one spot where it would be easier to retarget to another\nCPU. The answer, of course, is yes.\n\nTo accomplish this, insert the following \"code generation\"\nroutines:\n\n```delphi\n{---------------------------------------------------------------}\n{ Clear the Primary Register }\n\nprocedure Clear;\nbegin\n EmitLn('CLR D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Negate the Primary Register }\n\nprocedure Negate;\nbegin\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Load a Constant Value to Primary Register }\n\nprocedure LoadConst(n: integer);\nbegin\n Emit('MOVE #');\n WriteLn(n, ',D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Load a Variable to Primary Register }\n\nprocedure LoadVar(Name: char);\nbegin\n if not InTable(Name) then Undefined(Name);\n EmitLn('MOVE ' + Name + '(PC),D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Push Primary onto Stack }\n\nprocedure Push;\nbegin\n EmitLn('MOVE D0,-(SP)');\nend;\n\n\n{---------------------------------------------------------------}\n{ Add Top of Stack to Primary }\n\nprocedure PopAdd;\nbegin\n EmitLn('ADD (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Subtract Primary from Top of Stack }\n\nprocedure PopSub;\nbegin\n EmitLn('SUB (SP)+,D0');\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Multiply Top of Stack by Primary }\n\nprocedure PopMul;\nbegin\n EmitLn('MULS (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Divide Top of Stack by Primary }\n\nprocedure PopDiv;\nbegin\n EmitLn('MOVE (SP)+,D7');\n EmitLn('EXT.L D7');\n EmitLn('DIVS D0,D7');\n EmitLn('MOVE D7,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Store Primary to Variable }\n\nprocedure Store(Name: char);\nbegin\n if not InTable(Name) then Undefined(Name);\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)')\nend;\n{---------------------------------------------------------------}\n```\n\nThe nice part of this approach, of course, is that we can\nretarget the compiler to a new CPU simply by rewriting these\n\"code generator\" procedures. In addition, we will find later\nthat we can improve the code quality by tweaking these routines a\nbit, without having to modify the compiler proper.\n\nNote that both LoadVar and Store check the symbol table to make\nsure that the variable is defined. The error handler Undefined\nsimply calls Abort:\n\n```delphi\n{--------------------------------------------------------------}\n{ Report an Undefined Identifier }\n\nprocedure Undefined(n: string);\nbegin\n Abort('Undefined Identifier ' + n);\nend;\n{--------------------------------------------------------------}\n```\n\nOK, we are now finally ready to begin processing executable code.\nWe'll do that by replacing the stub version of procedure\nAssignment.\n\nWe've been down this road many times before, so this should all\nbe familiar to you. In fact, except for the changes associated\nwith the code generation, we could just copy the procedures from\nPart VII. Since we are making some changes, I won't just copy\nthem, but we will go a little faster than usual.\n\nThe BNF for the assignment statement is:\n```\n <assignment> ::= <ident> = <expression>\n\n <expression> ::= <first term> ( <addop> <term> )*\n\n <first term> ::= <first factor> <rest>\n\n <term> ::= <factor> <rest>\n\n <rest> ::= ( <mulop> <factor> )*\n\n <first factor> ::= [ <addop> ] <factor>\n\n <factor> ::= <var> | <number> | ( <expression> )\n```\n\nThis version of the BNF is also a bit different than we've used\nbefore ... yet another \"variation on the theme of an expression.\"\nThis particular version has what I consider to be the best\ntreatment of the unary minus. As you'll see later, it lets us\nhandle negative constant values efficiently. It's worth\nmentioning here that we have often seen the advantages of\n\"tweaking\" the BNF as we go, to help make the language easy to\nparse. What you're looking at here is a bit different: we've\ntweaked the BNF to make the CODE GENERATION more efficient!\nThat's a first for this series.\n\nAnyhow, the following code implements the BNF:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure Expression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n LoadVar(GetName)\n else\n LoadConst(GetNum);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Negative Factor }\n\nprocedure NegFactor;\nbegin\n Match('-');\n if IsDigit(Look) then\n LoadConst(-GetNum)\n else begin\n Factor;\n Negate;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Leading Factor }\n\nprocedure FirstFactor;\nbegin\n case Look of\n '+': begin\n Match('+');\n Factor;\n end;\n '-': NegFactor;\n else Factor;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nprocedure Multiply;\nbegin\n Match('*');\n Factor;\n PopMul;\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nprocedure Divide;\nbegin\n Match('/');\n Factor;\n PopDiv;\nend;\n\n\n{---------------------------------------------------------------}\n{ Common Code Used by Term and FirstTerm }\n\nprocedure Term1;\nbegin\n while IsMulop(Look) do begin\n Push;\n case Look of\n '*': Multiply;\n '/': Divide;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nprocedure Term;\nbegin\n Factor;\n Term1;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Leading Term }\n\nprocedure FirstTerm;\nbegin\n FirstFactor;\n Term1;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Match('+');\n Term;\n PopAdd;\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n PopSub;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n FirstTerm;\n while IsAddop(Look) do begin\n Push;\n case Look of\n '+': Add;\n '-': Subtract;\n end;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := GetName;\n Match('=');\n Expression;\n Store(Name);\nend;\n{--------------------------------------------------------------}\n```\n\nOK, if you've got all this code inserted, then compile it and\ncheck it out. You should be seeing reasonable-looking code,\nrepresenting a complete program that will assemble and execute.\nWe have a compiler!"},{title:"BOOLEANS",path:"/booleans",content:"The next step should also be familiar to you. We must add\nBoolean expressions and relational operations. Again, since\nwe've already dealt with them more than once, I won't elaborate\nmuch on them, except where they are different from what we've\ndone before. Again, we won't just copy from other files because\nI've changed a few things just a bit. Most of the changes just\ninvolve encapsulating the machine-dependent parts as we did for\nthe arithmetic operations. I've also modified procedure\nNotFactor somewhat, to parallel the structure of FirstFactor.\nFinally, I corrected an error in the object code for the\nrelational operators: The Scc instruction I used only sets the\nlow 8 bits of D0. We want all 16 bits set for a logical true, so\nI've added an instruction to sign-extend the low byte.\n\nTo begin, we're going to need some more recognizers:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize a Boolean Orop }\n\nfunction IsOrop(c: char): boolean;\nbegin\n IsOrop := c in ['|', '~'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Relop }\n\nfunction IsRelop(c: char): boolean;\nbegin\n IsRelop := c in ['=', '#', '<', '>'];\nend;\n{--------------------------------------------------------------}\n```\n\nAlso, we're going to need some more code generation routines:\n\n```delphi\n{---------------------------------------------------------------}\n{ Complement the Primary Register }\n\nprocedure NotIt;\nbegin\n EmitLn('NOT D0');\nend;\n{---------------------------------------------------------------}\n.\n.\n.\n{---------------------------------------------------------------}\n{ AND Top of Stack with Primary }\n\nprocedure PopAnd;\nbegin\n EmitLn('AND (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ OR Top of Stack with Primary }\n\nprocedure PopOr;\nbegin\n EmitLn('OR (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ XOR Top of Stack with Primary }\n\nprocedure PopXor;\nbegin\n EmitLn('EOR (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Compare Top of Stack with Primary }\n\nprocedure PopCompare;\nbegin\n EmitLn('CMP (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was = }\n\nprocedure SetEqual;\nbegin\n EmitLn('SEQ D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was != }\n\nprocedure SetNEqual;\nbegin\n EmitLn('SNE D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was > }\n\nprocedure SetGreater;\nbegin\n EmitLn('SLT D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was < }\n\nprocedure SetLess;\nbegin\n EmitLn('SGT D0');\n EmitLn('EXT D0');\nend;\n{---------------------------------------------------------------}\n```\nAll of this gives us the tools we need. The BNF for the Boolean\nexpressions is:\n\n```\n <bool-expr> ::= <bool-term> ( <orop> <bool-term> )*\n\n <bool-term> ::= <not-factor> ( <andop> <not-factor> )*\n\n <not-factor> ::= [ '!' ] <relation>\n\n <relation> ::= <expression> [ <relop> <expression> ]\n```\n\nSharp-eyed readers might note that this syntax does not include\nthe non-terminal \"bool-factor\" used in earlier versions. It was\nneeded then because I also allowed for the Boolean constants TRUE\nand FALSE. But remember that in TINY there is no distinction\nmade between Boolean and arithmetic types ... they can be freely\nintermixed. So there is really no need for these predefined\nvalues ... we can just use -1 and 0, respectively.\n\nIn C terminology, we could always use the defines:\n\n```\n #define TRUE -1\n #define FALSE 0\n```\n\n(That is, if TINY had a preprocessor.) Later on, when we allow\nfor declarations of constants, these two values will be\npredefined by the language.\n\nThe reason that I'm harping on this is that I've already tried\nthe alternative, which is to include TRUE and FALSE as keywords.\nThe problem with that approach is that it then requires lexical\nscanning for EVERY variable name in every expression. If you'll\nrecall, I pointed out in Installment VII that this slows the\ncompiler down considerably. As long as keywords can't be in\nexpressions, we need to do the scanning only at the beginning of\nevery new statement ... quite an improvement. So using the\nsyntax above not only simplifies the parsing, but speeds up the\nscanning as well.\n\nOK, given that we're all satisfied with the syntax above, the\ncorresponding code is shown below:\n\n```delphi\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Equals\" }\n\nprocedure Equals;\nbegin\n Match('=');\n Expression;\n PopCompare;\n SetEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Not Equals\" }\n\nprocedure NotEquals;\nbegin\n Match('#');\n Expression;\n PopCompare;\n SetNEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Less Than\" }\n\nprocedure Less;\nbegin\n Match('<');\n Expression;\n PopCompare;\n SetLess;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Greater Than\" }\n\nprocedure Greater;\nbegin\n Match('>');\n Expression;\n PopCompare;\n SetGreater;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Relation }\n\n\nprocedure Relation;\nbegin\n Expression;\n if IsRelop(Look) then begin\n Push;\n case Look of\n '=': Equals;\n '#': NotEquals;\n '<': Less;\n '>': Greater;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Factor with Leading NOT }\n\nprocedure NotFactor;\nbegin\n if Look = '!' then begin\n Match('!');\n Relation;\n NotIt;\n end\n else\n Relation;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Term }\n\nprocedure BoolTerm;\nbegin\n NotFactor;\n while Look = '&' do begin\n Push;\n Match('&');\n NotFactor;\n PopAnd;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Boolean OR }\n\nprocedure BoolOr;\nbegin\n Match('|');\n BoolTerm;\n PopOr;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Exclusive Or }\n\nprocedure BoolXor;\nbegin\n Match('~');\n BoolTerm;\n PopXor;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Expression }\n\nprocedure BoolExpression;\nbegin\n BoolTerm;\n while IsOrOp(Look) do begin\n Push;\n case Look of\n '|': BoolOr;\n '~': BoolXor;\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nTo tie it all together, don't forget to change the references to\nExpression in procedures Factor and Assignment so that they call\nBoolExpression instead.\n\nOK, if you've got all that typed in, compile it and give it a\nwhirl. First, make sure you can still parse an ordinary\narithmetic expression. Then, try a Boolean one. Finally, make\nsure that you can assign the results of relations. Try, for\nexample:\n\n `pvx,y,zbx=z>ye.`\n\nwhich stands for:\n```\n PROGRAM\n VAR X,Y,Z\n BEGIN\n X = Z > Y\n END.\n\n```\nSee how this assigns a Boolean value to X?"},{title:"CONTROL STRUCTURES",path:"/control-structures",content:"We're almost home. With Boolean expressions in place, it's a\nsimple matter to add control structures. For TINY, we'll only\nallow two kinds of them, the IF and the WHILE:\n\n```\n <if> ::= IF <bool-expression> <block> [ ELSE <block>] ENDIF\n\n <while> ::= WHILE <bool-expression> <block> ENDWHILE\n```\nOnce again, let me spell out the decisions implicit in this\nsyntax, which departs strongly from that of C or Pascal. In both\nof those languages, the \"body\" of an IF or WHILE is regarded as a\nsingle statement. If you intend to use a block of more than one\nstatement, you have to build a compound statement using BEGIN-END\n(in Pascal) or '{}' (in C). In TINY (and KISS) there is no such\nthing as a compound statement ... single or multiple they're all\njust blocks to these languages.\n\nIn KISS, all the control structures will have explicit and unique\nkeywords bracketing the statement block, so there can be no\nconfusion as to where things begin and end. This is the modern\napproach, used in such respected languages as Ada and Modula 2,\nand it completely eliminates the problem of the \"dangling else.\"\n\nNote that I could have chosen to use the same keyword END to end\nall the constructs, as is done in Pascal. (The closing '}' in C\nserves the same purpose.) But this has always led to confusion,\nwhich is why Pascal programmers tend to write things like\n\n\n `end { loop }`\n\nor `end { if }`\n\n\nAs I explained in Part V, using unique terminal keywords does\nincrease the size of the keyword list and therefore slows down\nthe scanning, but in this case it seems a small price to pay for\nthe added insurance. Better to find the errors at compile time\nrather than run time.\n\nOne last thought: The two constructs above each have the non-\nterminals\n\n\n `<bool-expression> and <block>`\n\n\njuxtaposed with no separating keyword. In Pascal we would expect\nthe keywords THEN and DO in these locations.\n\nI have no problem with leaving out these keywords, and the parser\nhas no trouble either, ON CONDITION that we make no errors in the\nbool-expression part. On the other hand, if we were to include\nthese extra keywords we would get yet one more level of insurance\nat very little cost, and I have no problem with that, either.\nUse your best judgment as to which way to go.\n\nOK, with that bit of explanation let's proceed. As usual, we're\ngoing to need some new code generation routines. These generate\nthe code for conditional and unconditional branches:\n```delphi\n{---------------------------------------------------------------}\n{ Branch Unconditional }\n\nprocedure Branch(L: string);\nbegin\n EmitLn('BRA ' + L);\nend;\n\n\n{---------------------------------------------------------------}\n{ Branch False }\n\nprocedure BranchFalse(L: string);\nbegin\n EmitLn('TST D0');\n EmitLn('BEQ ' + L);\nend;\n{--------------------------------------------------------------}\n```\n\nExcept for the encapsulation of the code generation, the code to\nparse the control constructs is the same as you've seen before:\n\n```delphi\n{---------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block; Forward;\n\n\nprocedure DoIf;\nvar L1, L2: string;\nbegin\n Match('i');\n BoolExpression;\n L1 := NewLabel;\n L2 := L1;\n BranchFalse(L1);\n Block;\n if Look = 'l' then begin\n Match('l');\n L2 := NewLabel;\n Branch(L2);\n PostLabel(L1);\n Block;\n end;\n PostLabel(L2);\n Match('e');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a WHILE Statement }\n\nprocedure DoWhile;\nvar L1, L2: string;\nbegin\n Match('w');\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n BoolExpression;\n BranchFalse(L2);\n Block;\n Match('e');\n Branch(L1);\n PostLabel(L2);\nend;\n{--------------------------------------------------------------}\n```\n\nTo tie everything together, we need only modify procedure Block\nto recognize the \"keywords\" for the IF and WHILE. As usual, we\nexpand the definition of a block like so:\n\n```\n <block> ::= ( <statement> )*\n```\n\nwhere\n\n```\n <statement> ::= <if> | <while> | <assignment>\n\n```\nThe corresponding code is:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n while not(Look in ['e', 'l']) do begin\n case Look of\n 'i': DoIf;\n 'w': DoWhile;\n else Assignment;\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nOK, add the routines I've given, compile and test them. You\nshould be able to parse the single-character versions of any of\nthe control constructs. It's looking pretty good!\n\nAs a matter of fact, except for the single-character limitation\nwe've got a virtually complete version of TINY. I call it, with\ntongue planted firmly in cheek, TINY Version 0.1."},{title:"LEXICAL SCANNING",path:"/lexical-scanning",content:"Of course, you know what's next: We have to convert the program\nso that it can deal with multi-character keywords, newlines, and\nwhitespace. We have just gone through all that in Part VII.\nWe'll use the distributed scanner technique that I showed you in\nthat installment. The actual implementation is a little\ndifferent because the way I'm handling newlines is different.\n\nTo begin with, let's simply allow for whitespace. This involves\nonly adding calls to SkipWhite at the end of the three routines,\nGetName, GetNum, and Match. A call to SkipWhite in Init primes\nthe pump in case there are leading spaces.\n\nNext, we need to deal with newlines. This is really a two-step\nprocess, since the treatment of the newlines with single-\ncharacter tokens is different from that for multi-character ones.\nWe can eliminate some work by doing both steps at once, but I\nfeel safer taking things one step at a time.\n\nInsert the new procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip Over an End-of-Line }\n\nprocedure NewLine;\nbegin\n while Look = CR do begin\n GetChar;\n if Look = LF then GetChar;\n SkipWhite;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNote that we have seen this procedure before in the form of\nProcedure Fin. I've changed the name since this new one seems\nmore descriptive of the actual function. I've also changed the\ncode to allow for multiple newlines and lines with nothing but\nwhite space.\n\nThe next step is to insert calls to NewLine wherever we decide a\nnewline is permissible. As I've pointed out before, this can be\nvery different in different languages. In TINY, I've decided to\nallow them virtually anywhere. This means that we need calls to\nNewLine at the BEGINNING (not the end, as with SkipWhite) of the\nprocedures GetName, GetNum, and Match.\n\nFor procedures that have while loops, such as TopDecl, we need a\ncall to NewLine at the beginning of the procedure AND at the\nbottom of each loop. That way, we can be assured that NewLine\nhas just been called at the beginning of each pass through the\nloop.\n\nIf you've got all this done, try the program out and verify that\nit will indeed handle white space and newlines.\n\nIf it does, then we're ready to deal with multi-character tokens\nand keywords. To begin, add the additional declarations (copied\nalmost verbatim from Part VII):\n\n```delphi\n{--------------------------------------------------------------}\n{ Type Declarations }\n\ntype Symbol = string[8];\n\n SymTab = array[1..1000] of Symbol;\n\n TabPtr = ^SymTab;\n\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look : char; { Lookahead Character }\n Token: char; { Encoded Token }\n Value: string[16]; { Unencoded Token }\n\n ST: Array['A'..'Z'] of char;\n\n{--------------------------------------------------------------}\n{ Definition of Keywords and Token Types }\n\nconst NKW = 9;\n NKW1 = 10;\n\nconst KWlist: array[1..NKW] of Symbol =\n ('IF', 'ELSE', 'ENDIF', 'WHILE', 'ENDWHILE',\n 'VAR', 'BEGIN', 'END', 'PROGRAM');\n\nconst KWcode: string[NKW1] = 'xilewevbep';\n{--------------------------------------------------------------}\n```\n\nNext, add the three procedures, also from Part VII:\n\n```delphi\n{--------------------------------------------------------------}\n{ Table Lookup }\n\nfunction Lookup(T: TabPtr; s: string; n: integer): integer;\nvar i: integer;\n found: Boolean;\nbegin\n found := false;\n i := n;\n while (i > 0) and not found do\n if s = T^[i] then\n found := true\n else\n dec(i);\n Lookup := i;\nend;\n{--------------------------------------------------------------}\n.\n.\n{--------------------------------------------------------------}\n{ Get an Identifier and Scan it for Keywords }\n\nprocedure Scan;\nbegin\n GetName;\n Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];\nend;\n{--------------------------------------------------------------}\n.\n.\n{--------------------------------------------------------------}\n{ Match a Specific Input String }\n\nprocedure MatchString(x: string);\nbegin\n if Value <> x then Expected('''' + x + '''');\nend;\n{--------------------------------------------------------------}\n```\n\nNow, we have to make a fairly large number of subtle changes to\nthe remaining procedures. First, we must change the function\nGetName to a procedure, again as we did in Part VII:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nprocedure GetName;\nbegin\n NewLine;\n if not IsAlpha(Look) then Expected('Name');\n Value := '';\n while IsAlNum(Look) do begin\n Value := Value + UpCase(Look);\n GetChar;\n end;\n SkipWhite;\nend;\n{--------------------------------------------------------------}\n```\n\nNote that this procedure leaves its result in the global string\nValue.\n\nNext, we have to change every reference to GetName to reflect its\nnew form. These occur in Factor, Assignment, and Decl:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure BoolExpression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n BoolExpression;\n Match(')');\n end\n else if IsAlpha(Look) then begin\n GetName;\n LoadVar(Value[1]);\n end\n else\n LoadConst(GetNum);\nend;\n{--------------------------------------------------------------}\n.\n.\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := Value[1];\n Match('=');\n BoolExpression;\n Store(Name);\nend;\n{---------------------------------------------------------------}\n.\n.\n{--------------------------------------------------------------}\n{ Parse and Translate a Data Declaration }\n\nprocedure Decl;\nbegin\n GetName;\n Alloc(Value[1]);\n while Look = ',' do begin\n Match(',');\n GetName;\n Alloc(Value[1]);\n end;\nend;\n{--------------------------------------------------------------}\n```\n\n(Note that we're still only allowing single-character variable\nnames, so we take the easy way out here and simply use the first\ncharacter of the string.)\n\nFinally, we must make the changes to use Token instead of Look as\nthe test character and to call Scan at the appropriate places.\nMostly, this involves deleting calls to Match, occasionally\nreplacing calls to Match by calls to MatchString, and Replacing\ncalls to NewLine by calls to Scan. Here are the affected\nroutines:\n```delphi\n{---------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block; Forward;\n\n\nprocedure DoIf;\nvar L1, L2: string;\nbegin\n BoolExpression;\n L1 := NewLabel;\n L2 := L1;\n BranchFalse(L1);\n Block;\n if Token = 'l' then begin\n L2 := NewLabel;\n Branch(L2);\n PostLabel(L1);\n Block;\n end;\n PostLabel(L2);\n MatchString('ENDIF');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a WHILE Statement }\n\nprocedure DoWhile;\nvar L1, L2: string;\nbegin\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n BoolExpression;\n BranchFalse(L2);\n Block;\n MatchString('ENDWHILE');\n Branch(L1);\n PostLabel(L2);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n Scan;\n while not(Token in ['e', 'l']) do begin\n case Token of\n 'i': DoIf;\n 'w': DoWhile;\n else Assignment;\n end;\n Scan;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n Scan;\n while Token <> 'b' do begin\n case Token of\n 'v': Decl;\n else Abort('Unrecognized Keyword ' + Value);\n end;\n Scan;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Main Program }\n\nprocedure Main;\nbegin\n MatchString('BEGIN');\n Prolog;\n Block;\n MatchString('END');\n Epilog;\nend;\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Program }\n\nprocedure Prog;\nbegin\n MatchString('PROGRAM');\n Header;\n TopDecls;\n Main;\n Match('.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nvar i: char;\nbegin\n for i := 'A' to 'Z' do\n ST[i] := ' ';\n GetChar;\n Scan;\nend;\n{--------------------------------------------------------------}\n```\n\nThat should do it. If all the changes got in correctly, you\nshould now be parsing programs that look like programs. (If you\ndidn't make it through all the changes, don't despair. A\ncomplete listing of the final form is given later.)\n\nDid it work? If so, then we're just about home. In fact, with a\nfew minor exceptions we've already got a compiler that's usable.\nThere are still a few areas that need improvement."},{title:"MULTI-CHARACTER VARIABLE NAMES",path:"/multi-character-variable-names",content:"One of those is the restriction that we still have, requiring\nsingle-character variable names. Now that we can handle multi-\ncharacter keywords, this one begins to look very much like an\narbitrary and unnecessary limitation. And indeed it is.\nBasically, its only virtue is that it permits a trivially simple\nimplementation of the symbol table. But that's just a\nconvenience to the compiler writers, and needs to be eliminated.\n\nWe've done this step before. This time, as usual, I'm doing it a\nlittle differently. I think the approach used here keeps things\njust about as simple as possible.\n\nThe natural way to implement a symbol table in Pascal is by\ndeclaring a record type, and making the symbol table an array of\nsuch records. Here, though, we don't really need a type field\nyet (there is only one kind of entry allowed so far), so we only\nneed an array of symbols. This has the advantage that we can use\nthe existing procedure Lookup to search the symbol table as well\nas the keyword list. As it turns out, even when we need more\nfields we can still use the same approach, simply by storing the\nother fields in separate arrays.\n\nOK, here are the changes that need to be made. First, add the\nnew typed constant:\n\n```delphi\n NEntry: integer = 0;\n```\n\nThen change the definition of the symbol table as follows:\n\n```delphi\nconst MaxEntry = 100;\n\nvar ST : array[1..MaxEntry] of Symbol;\n```\n\n(Note that ST is _NOT_ declared as a SymTab. That declaration is\na phony one to get Lookup to work. A SymTab would take up too\nmuch RAM space, and so one is never actually allocated.)\n\nNext, we need to replace InTable:\n\n```delphi\n{--------------------------------------------------------------}\n{ Look for Symbol in Table }\n\nfunction InTable(n: Symbol): Boolean;\nbegin\n InTable := Lookup(@ST, n, MaxEntry) <> 0;\nend;\n{--------------------------------------------------------------}\n\n```\nWe also need a new procedure, AddEntry, that adds a new entry to\nthe table:\n\n```delphi\n{--------------------------------------------------------------}\n{ Add a New Entry to Symbol Table }\n\nprocedure AddEntry(N: Symbol; T: char);\nbegin\n if InTable(N) then Abort('Duplicate Identifier ' + N);\n if NEntry = MaxEntry then Abort('Symbol Table Full');\n Inc(NEntry);\n ST[NEntry] := N;\n SType[NEntry] := T;\nend;\n{--------------------------------------------------------------}\n\n```\nThis procedure is called by Alloc:\n\n```delphi\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc(N: Symbol);\nbegin\n if InTable(N) then Abort('Duplicate Variable Name ' + N);\n AddEntry(N, 'v');\n.\n.\n.\n{--------------------------------------------------------------}\n```\n\nFinally, we must change all the routines that currently treat the\nvariable name as a single character. These include LoadVar and\nStore (just change the type from char to string), and Factor,\nAssignment, and Decl (just change Value[1] to Value).\n\nOne last thing: change procedure Init to clear the array as\nshown:\n\n```delphi\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nvar i: integer;\nbegin\n for i := 1 to MaxEntry do begin\n ST[i] := '';\n SType[i] := ' ';\n end;\n GetChar;\n Scan;\nend;\n{--------------------------------------------------------------}\n```\n\nThat should do it. Try it out and verify that you can, indeed,\nuse multi-character variable names."},{title:"MORE RELOPS",path:"/more-relops",content:"We still have one remaining single-character restriction: the one\non relops. Some of the relops are indeed single characters, but\nothers require two. These are `'<='` and `'>='`. I also prefer the\nPascal `'<>'` for \"not equals,\" instead of `'#'`.\n\nIf you'll recall, in Part VII I pointed out that the conventional\nway to deal with relops is to include them in the list of\nkeywords, and let the lexical scanner find them. But, again,\nthis requires scanning throughout the expression parsing process,\nwhereas so far we've been able to limit the use of the scanner to\nthe beginning of a statement.\n\nI mentioned then that we can still get away with this, since the\nmulti-character relops are so few and so limited in their usage.\nIt's easy to just treat them as special cases and handle them in\nan ad hoc manner.\n\nThe changes required affect only the code generation routines and\nprocedures Relation and friends. First, we're going to need two\nmore code generation routines:\n\n```delphi\n{---------------------------------------------------------------}\n{ Set D0 If Compare was <= }\n\nprocedure SetLessOrEqual;\nbegin\n EmitLn('SGE D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was >= }\n\nprocedure SetGreaterOrEqual;\nbegin\n EmitLn('SLE D0');\n EmitLn('EXT D0');\nend;\n{---------------------------------------------------------------}\n```\n\nThen, modify the relation parsing routines as shown below:\n\n```delphi\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Less Than or Equal\" }\n\nprocedure LessOrEqual;\nbegin\n Match('=');\n Expression;\n PopCompare;\n SetLessOrEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Not Equals\" }\n\nprocedure NotEqual;\nbegin\n Match('>');\n Expression;\n PopCompare;\n SetNEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Less Than\" }\n\nprocedure Less;\nbegin\n Match('<');\n case Look of\n '=': LessOrEqual;\n '>': NotEqual;\n else begin\n Expression;\n PopCompare;\n SetLess;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Greater Than\" }\n\nprocedure Greater;\nbegin\n Match('>');\n if Look = '=' then begin\n Match('=');\n Expression;\n PopCompare;\n SetGreaterOrEqual;\n end\n else begin\n Expression;\n PopCompare;\n SetGreater;\n end;\nend;\n{---------------------------------------------------------------}\n```\n\nThat's all it takes. Now you can process all the relops. Try\nit."},{title:"INPUT/OUTPUT",path:"/inputoutput",content:"We now have a complete, working language, except for one minor\nembarassment: we have no way to get data in or out. We need some\nI/O.\n\nNow, the convention these days, established in C and continued in\nAda and Modula 2, is to leave I/O statements out of the language\nitself, and just include them in the subroutine library. That\nwould be fine, except that so far we have no provision for\nsubroutines. Anyhow, with this approach you run into the problem\nof variable-length argument lists. In Pascal, the I/O statements\nare built into the language because they are the only ones for\nwhich the argument list can have a variable number of entries.\nIn C, we settle for kludges like scanf and printf, and must pass\nthe argument count to the called procedure. In Ada and Modula 2\nwe must use the awkward (and SLOW!) approach of a separate call\nfor each argument.\n\nSo I think I prefer the Pascal approach of building the I/O in,\neven though we don't need to.\n\nAs usual, for this we need some more code generation routines.\nThese turn out to be the easiest of all, because all we do is to\ncall library procedures to do the work:\n\n```delphi\n{---------------------------------------------------------------}\n{ Read Variable to Primary Register }\n\nprocedure ReadVar;\nbegin\n EmitLn('BSR READ');\n Store(Value);\nend;\n\n\n{---------------------------------------------------------------}\n{ Write Variable from Primary Register }\n\nprocedure WriteVar;\nbegin\n EmitLn('BSR WRITE');\nend;\n{--------------------------------------------------------------}\n```\n\nThe idea is that READ loads the value from input to the D0, and\nWRITE outputs it from there.\n\nThese two procedures represent our first encounter with a need\nfor library procedures ... the components of a Run Time Library\n(RTL). Of course, someone (namely us) has to write these\nroutines, but they're not part of the compiler itself. I won't\neven bother showing the routines here, since these are obviously\nvery much OS-dependent. I _WILL_ simply say that for SK*DOS,\nthey are particularly simple ... almost trivial. One reason I\nwon't show them here is that you can add all kinds of fanciness\nto the things, for example by prompting in READ for the inputs,\nand by giving the user a chance to reenter a bad input.\n\nBut that is really separate from compiler design, so for now I'll\njust assume that a library call TINYLIB.LIB exists. Since we now\nneed it loaded, we need to add a statement to include it in\nprocedure Header:\n\n```delphi\n{--------------------------------------------------------------}\n{ Write Header Info }\n\nprocedure Header;\nbegin\n\n WriteLn('WARMST', TAB, 'EQU $A01E');\n EmitLn('LIB TINYLIB');\nend;\n{--------------------------------------------------------------}\n```\nThat takes care of that part. Now, we also need to recognize the\nread and write commands. We can do this by adding two more\nkeywords to our list:\n\n```delphi\n{--------------------------------------------------------------}\n{ Definition of Keywords and Token Types }\n\nconst NKW = 11;\n NKW1 = 12;\n\nconst KWlist: array[1..NKW] of Symbol =\n ('IF', 'ELSE', 'ENDIF', 'WHILE', 'ENDWHILE',\n 'READ', 'WRITE', 'VAR', 'BEGIN', 'END',\n'PROGRAM');\n\nconst KWcode: string[NKW1] = 'xileweRWvbep';\n{--------------------------------------------------------------}\n```\n\n(Note how I'm using upper case codes here to avoid conflict with\nthe 'w' of WHILE.)\n\nNext, we need procedures for processing the read/write statement\nand its argument list:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process a Read Statement }\nprocedure DoRead;\nbegin\n Match('(');\n GetName;\n ReadVar;\n while Look = ',' do begin\n Match(',');\n GetName;\n ReadVar;\n end;\n Match(')');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process a Write Statement }\n\nprocedure DoWrite;\nbegin\n Match('(');\n Expression;\n WriteVar;\n while Look = ',' do begin\n Match(',');\n Expression;\n WriteVar;\n end;\n Match(')');\nend;\n{--------------------------------------------------------------}\n```\n\nFinally, we must expand procedure Block to handle the new\nstatement types:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n Scan;\n while not(Token in ['e', 'l']) do begin\n case Token of\n 'i': DoIf;\n 'w': DoWhile;\n 'R': DoRead;\n 'W': DoWrite;\n else Assignment;\n end;\n Scan;\n end;\nend;\n{--------------------------------------------------------------}\n```\nThat's all there is to it. _NOW_ we have a language!"},{title:"CONCLUSION",path:"/conclusion",content:"At this point we have TINY completely defined. It's not much ...\nactually a toy compiler. TINY has only one data type and no\nsubroutines ... but it's a complete, usable language. While\nyou're not likely to be able to write another compiler in it, or\ndo anything else very seriously, you could write programs to read\nsome input, perform calculations, and output the results. Not\ntoo bad for a toy.\n\nMost importantly, we have a firm base upon which to build further\nextensions. I know you'll be glad to hear this: this is the last\ntime I'll start over in building a parser ... from now on I\nintend to just add features to TINY until it becomes KISS. Oh,\nthere'll be other times we will need to try things out with new\ncopies of the Cradle, but once we've found out how to do those\nthings they'll be incorporated into TINY.\n\nWhat will those features be? Well, for starters we need\nsubroutines and functions. Then we need to be able to handle\ndifferent types, including arrays, strings, and other structures.\nThen we need to deal with the idea of pointers. All this will be\nupcoming in future installments.\n\nSee you then.\n\nFor references purposes, the complete listing of TINY Version 1.0\nis shown below:\n\n```delphi\n{--------------------------------------------------------------}\nprogram Tiny10;\n\n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n CR = ^M;\n LF = ^J;\n\n LCount: integer = 0;\n NEntry: integer = 0;\n\n\n{--------------------------------------------------------------}\n{ Type Declarations }\n\ntype Symbol = string[8];\n\n SymTab = array[1..1000] of Symbol;\n TabPtr = ^SymTab;\n\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look : char; { Lookahead Character }\n Token: char; { Encoded Token }\n Value: string[16]; { Unencoded Token }\n\n\nconst MaxEntry = 100;\n\nvar ST : array[1..MaxEntry] of Symbol;\n SType: array[1..MaxEntry] of char;\n\n\n{--------------------------------------------------------------}\n{ Definition of Keywords and Token Types }\n\nconst NKW = 11;\n NKW1 = 12;\n\nconst KWlist: array[1..NKW] of Symbol =\n ('IF', 'ELSE', 'ENDIF', 'WHILE', 'ENDWHILE',\n 'READ', 'WRITE', 'VAR', 'BEGIN', 'END',\n'PROGRAM');\n\nconst KWcode: string[NKW1] = 'xileweRWvbep';\n\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n\nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n{--------------------------------------------------------------}\n{ Report an Undefined Identifier }\n\nprocedure Undefined(n: string);\nbegin\n Abort('Undefined Identifier ' + n);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an AlphaNumeric Character }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Mulop }\n\nfunction IsMulop(c: char): boolean;\nbegin\n IsMulop := c in ['*', '/'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Boolean Orop }\n\nfunction IsOrop(c: char): boolean;\nbegin\n IsOrop := c in ['|', '~'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Relop }\n\nfunction IsRelop(c: char): boolean;\nbegin\n IsRelop := c in ['=', '#', '<', '>'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB];\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over an End-of-Line }\n\nprocedure NewLine;\nbegin\n while Look = CR do begin\n GetChar;\n if Look = LF then GetChar;\n SkipWhite;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n NewLine;\n if Look = x then GetChar\n else Expected('''' + x + '''');\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Table Lookup }\n\nfunction Lookup(T: TabPtr; s: string; n: integer): integer;\nvar i: integer;\n found: Boolean;\nbegin\n found := false;\n i := n;\n while (i > 0) and not found do\n if s = T^[i] then\n found := true\n else\n dec(i);\n Lookup := i;\nend;\n\n\n{--------------------------------------------------------------}\n{ Locate a Symbol in Table }\n{ Returns the index of the entry. Zero if not present. }\n\nfunction Locate(N: Symbol): integer;\nbegin\n Locate := Lookup(@ST, n, MaxEntry);\nend;\n\n\n{--------------------------------------------------------------}\n{ Look for Symbol in Table }\n\nfunction InTable(n: Symbol): Boolean;\nbegin\n InTable := Lookup(@ST, n, MaxEntry) <> 0;\nend;\n\n\n{--------------------------------------------------------------}\n{ Add a New Entry to Symbol Table }\n\nprocedure AddEntry(N: Symbol; T: char);\nbegin\n if InTable(N) then Abort('Duplicate Identifier ' + N);\n if NEntry = MaxEntry then Abort('Symbol Table Full');\n Inc(NEntry);\n ST[NEntry] := N;\n SType[NEntry] := T;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nprocedure GetName;\nbegin\n NewLine;\n if not IsAlpha(Look) then Expected('Name');\n Value := '';\n while IsAlNum(Look) do begin\n Value := Value + UpCase(Look);\n GetChar;\n end;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: integer;\nvar Val: integer;\nbegin\n NewLine;\n if not IsDigit(Look) then Expected('Integer');\n Val := 0;\n while IsDigit(Look) do begin\n Val := 10 * Val + Ord(Look) - Ord('0');\n GetChar;\n end;\n GetNum := Val;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier and Scan it for Keywords }\n\nprocedure Scan;\nbegin\n GetName;\n Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];\nend;\n\n\n{--------------------------------------------------------------}\n{ Match a Specific Input String }\n\nprocedure MatchString(x: string);\nbegin\n if Value <> x then Expected('''' + x + '''');\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n\nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n\n{--------------------------------------------------------------}\n{ Generate a Unique Label }\n\nfunction NewLabel: string;\nvar S: string;\nbegin\n Str(LCount, S);\n NewLabel := 'L' + S;\n Inc(LCount);\nend;\n\n\n{--------------------------------------------------------------}\n{ Post a Label To Output }\n\nprocedure PostLabel(L: string);\nbegin\n WriteLn(L, ':');\nend;\n\n\n{---------------------------------------------------------------}\n{ Clear the Primary Register }\n\nprocedure Clear;\nbegin\n EmitLn('CLR D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Negate the Primary Register }\n\nprocedure Negate;\nbegin\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Complement the Primary Register }\n\nprocedure NotIt;\nbegin\n EmitLn('NOT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Load a Constant Value to Primary Register }\n\nprocedure LoadConst(n: integer);\nbegin\n Emit('MOVE #');\n WriteLn(n, ',D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Load a Variable to Primary Register }\n\nprocedure LoadVar(Name: string);\nbegin\n if not InTable(Name) then Undefined(Name);\n EmitLn('MOVE ' + Name + '(PC),D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Push Primary onto Stack }\n\nprocedure Push;\nbegin\n EmitLn('MOVE D0,-(SP)');\nend;\n\n\n{---------------------------------------------------------------}\n{ Add Top of Stack to Primary }\n\nprocedure PopAdd;\nbegin\n EmitLn('ADD (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Subtract Primary from Top of Stack }\n\nprocedure PopSub;\nbegin\n EmitLn('SUB (SP)+,D0');\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Multiply Top of Stack by Primary }\n\nprocedure PopMul;\nbegin\n EmitLn('MULS (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Divide Top of Stack by Primary }\n\nprocedure PopDiv;\nbegin\n EmitLn('MOVE (SP)+,D7');\n EmitLn('EXT.L D7');\n EmitLn('DIVS D0,D7');\n EmitLn('MOVE D7,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ AND Top of Stack with Primary }\n\nprocedure PopAnd;\nbegin\n EmitLn('AND (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ OR Top of Stack with Primary }\n\nprocedure PopOr;\nbegin\n EmitLn('OR (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ XOR Top of Stack with Primary }\n\nprocedure PopXor;\nbegin\n EmitLn('EOR (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Compare Top of Stack with Primary }\n\nprocedure PopCompare;\nbegin\n EmitLn('CMP (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was = }\n\nprocedure SetEqual;\nbegin\n EmitLn('SEQ D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was != }\n\nprocedure SetNEqual;\nbegin\n EmitLn('SNE D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was > }\n\nprocedure SetGreater;\nbegin\n EmitLn('SLT D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was < }\n\nprocedure SetLess;\nbegin\n EmitLn('SGT D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was <= }\n\nprocedure SetLessOrEqual;\nbegin\n EmitLn('SGE D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was >= }\n\nprocedure SetGreaterOrEqual;\nbegin\n EmitLn('SLE D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Store Primary to Variable }\n\nprocedure Store(Name: string);\nbegin\n if not InTable(Name) then Undefined(Name);\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)')\nend;\n\n\n{---------------------------------------------------------------}\n{ Branch Unconditional }\n\nprocedure Branch(L: string);\nbegin\n EmitLn('BRA ' + L);\nend;\n\n\n{---------------------------------------------------------------}\n{ Branch False }\n\nprocedure BranchFalse(L: string);\nbegin\n EmitLn('TST D0');\n EmitLn('BEQ ' + L);\nend;\n\n\n{---------------------------------------------------------------}\n{ Read Variable to Primary Register }\n\nprocedure ReadVar;\nbegin\n EmitLn('BSR READ');\n Store(Value[1]);\nend;\n\n\n{ Write Variable from Primary Register }\n\nprocedure WriteVar;\nbegin\n EmitLn('BSR WRITE');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write Header Info }\n\nprocedure Header;\nbegin\n WriteLn('WARMST', TAB, 'EQU $A01E');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write the Prolog }\n\nprocedure Prolog;\nbegin\n PostLabel('MAIN');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write the Epilog }\n\nprocedure Epilog;\nbegin\n EmitLn('DC WARMST');\n EmitLn('END MAIN');\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure BoolExpression; Forward;\n\nprocedure Factor;\nbegin\n if Look = '(' then begin\n Match('(');\n BoolExpression;\n Match(')');\n end\n else if IsAlpha(Look) then begin\n GetName;\n LoadVar(Value);\n end\n else\n LoadConst(GetNum);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Negative Factor }\n\nprocedure NegFactor;\nbegin\n Match('-');\n if IsDigit(Look) then\n LoadConst(-GetNum)\n else begin\n Factor;\n Negate;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Leading Factor }\n\nprocedure FirstFactor;\nbegin\n case Look of\n '+': begin\n Match('+');\n Factor;\n end;\n '-': NegFactor;\n else Factor;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nprocedure Multiply;\nbegin\n Match('*');\n Factor;\n PopMul;\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nprocedure Divide;\nbegin\n Match('/');\n Factor;\n PopDiv;\nend;\n\n\n{---------------------------------------------------------------}\n{ Common Code Used by Term and FirstTerm }\n\nprocedure Term1;\nbegin\n while IsMulop(Look) do begin\n Push;\n case Look of\n '*': Multiply;\n '/': Divide;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nprocedure Term;\nbegin\n Factor;\n Term1;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Leading Term }\n\nprocedure FirstTerm;\nbegin\n FirstFactor;\n Term1;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Match('+');\n Term;\n PopAdd;\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Match('-');\n Term;\n PopSub;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n FirstTerm;\n while IsAddop(Look) do begin\n Push;\n case Look of\n '+': Add;\n '-': Subtract;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Equals\" }\n\nprocedure Equal;\nbegin\n Match('=');\n Expression;\n PopCompare;\n SetEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Less Than or Equal\" }\n\nprocedure LessOrEqual;\nbegin\n Match('=');\n Expression;\n PopCompare;\n SetLessOrEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Not Equals\" }\n\nprocedure NotEqual;\nbegin\n Match('>');\n Expression;\n PopCompare;\n SetNEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Less Than\" }\n\nprocedure Less;\nbegin\n Match('<');\n case Look of\n '=': LessOrEqual;\n '>': NotEqual;\n else begin\n Expression;\n PopCompare;\n SetLess;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Greater Than\" }\n\nprocedure Greater;\nbegin\n Match('>');\n if Look = '=' then begin\n Match('=');\n Expression;\n PopCompare;\n SetGreaterOrEqual;\n end\n else begin\n Expression;\n PopCompare;\n SetGreater;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Relation }\n\n\nprocedure Relation;\nbegin\n Expression;\n if IsRelop(Look) then begin\n Push;\n case Look of\n '=': Equal;\n '<': Less;\n '>': Greater;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Factor with Leading NOT }\n\nprocedure NotFactor;\nbegin\n if Look = '!' then begin\n Match('!');\n Relation;\n NotIt;\n end\n else\n Relation;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Term }\n\nprocedure BoolTerm;\nbegin\n NotFactor;\n while Look = '&' do begin\n Push;\n Match('&');\n NotFactor;\n PopAnd;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Boolean OR }\n\nprocedure BoolOr;\nbegin\n Match('|');\n BoolTerm;\n PopOr;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Exclusive Or }\n\nprocedure BoolXor;\nbegin\n Match('~');\n BoolTerm;\n PopXor;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Expression }\n\nprocedure BoolExpression;\nbegin\n BoolTerm;\n while IsOrOp(Look) do begin\n Push;\n case Look of\n '|': BoolOr;\n '~': BoolXor;\n end;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: string;\nbegin\n Name := Value;\n Match('=');\n BoolExpression;\n Store(Name);\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block; Forward;\n\n\nprocedure DoIf;\nvar L1, L2: string;\nbegin\n BoolExpression;\n L1 := NewLabel;\n L2 := L1;\n BranchFalse(L1);\n Block;\n if Token = 'l' then begin\n L2 := NewLabel;\n Branch(L2);\n PostLabel(L1);\n Block;\n end;\n PostLabel(L2);\n MatchString('ENDIF');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a WHILE Statement }\n\nprocedure DoWhile;\nvar L1, L2: string;\nbegin\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n BoolExpression;\n BranchFalse(L2);\n Block;\n MatchString('ENDWHILE');\n Branch(L1);\n PostLabel(L2);\nend;\n\n\n{--------------------------------------------------------------}\n{ Process a Read Statement }\n\nprocedure DoRead;\nbegin\n Match('(');\n GetName;\n ReadVar;\n while Look = ',' do begin\n Match(',');\n GetName;\n ReadVar;\n end;\n Match(')');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process a Write Statement }\n\nprocedure DoWrite;\nbegin\n Match('(');\n Expression;\n WriteVar;\n while Look = ',' do begin\n Match(',');\n Expression;\n WriteVar;\n end;\n Match(')');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n Scan;\n while not(Token in ['e', 'l']) do begin\n case Token of\n 'i': DoIf;\n 'w': DoWhile;\n 'R': DoRead;\n 'W': DoWrite;\n else Assignment;\n end;\n Scan;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc(N: Symbol);\nbegin\n if InTable(N) then Abort('Duplicate Variable Name ' + N);\n AddEntry(N, 'v');\n Write(N, ':', TAB, 'DC ');\n if Look = '=' then begin\n Match('=');\n If Look = '-' then begin\n Write(Look);\n Match('-');\n end;\n WriteLn(GetNum);\n end\n else\n WriteLn('0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Data Declaration }\n\nprocedure Decl;\nbegin\n GetName;\n Alloc(Value);\n while Look = ',' do begin\n Match(',');\n GetName;\n Alloc(Value);\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n Scan;\n while Token <> 'b' do begin\n case Token of\n 'v': Decl;\n else Abort('Unrecognized Keyword ' + Value);\n end;\n Scan;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Main Program }\n\nprocedure Main;\nbegin\n MatchString('BEGIN');\n Prolog;\n Block;\n MatchString('END');\n Epilog;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Program }\n\nprocedure Prog;\nbegin\n MatchString('PROGRAM');\n Header;\n TopDecls;\n Main;\n Match('.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nvar i: integer;\nbegin\n for i := 1 to MaxEntry do begin\n ST[i] := '';\n SType[i] := ' ';\n end;\n GetChar;\n Scan;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n Prog;\n if Look <> CR then Abort('Unexpected data after ''.''');\nend.\n{--------------------------------------------------------------}\n\n```"}]},{title:"Part XI: LEXICAL SCAN REVISITED - 3 June 1989",path:"/part-xi-lexical-scan-revisited-3-june-1989",items:[{title:"INTRODUCTION",path:"/introduction",content:"I've got some good news and some bad news. The bad news is that\nthis installment is not the one I promised last time. What's\nmore, the one after this one won't be, either.\n\nThe good news is the reason for this installment: I've found a\nway to simplify and improve the lexical scanning part of the\ncompiler. Let me explain."},{title:"BACKGROUND",path:"/background",content:"If you'll remember, we talked at length about the subject of\nlexical scanners in Part VII, and I left you with a design for a\ndistributed scanner that I felt was about as simple as I could\nmake it ... more than most that I've seen elsewhere. We used\nthat idea in Part X. The compiler structure that resulted was\nsimple, and it got the job done.\n\nRecently, though, I've begun to have problems, and they're the\nkind that send a message that you might be doing something wrong.\n\nThe whole thing came to a head when I tried to address the issue\nof semicolons. Several people have asked me about them, and\nwhether or not KISS will have them separating the statements. My\nintention has been NOT to use semicolons, simply because I don't\nlike them and, as you can see, they have not proved necessary.\n\nBut I know that many of you, like me, have gotten used to them,\nand so I set out to write a short installment to show you how\nthey could easily be added, if you were so inclined.\n\nWell, it turned out that they weren't easy to add at all. In\nfact it was darned difficult.\n\nI guess I should have realized that something was wrong, because\nof the issue of newlines. In the last couple of installments\nwe've addressed that issue, and I've shown you how to deal with\nnewlines with a procedure called, appropriately enough, NewLine.\nIn TINY Version 1.0, I sprinkled calls to this procedure in\nstrategic spots in the code.\n\nIt seems that every time I've addressed the issue of newlines,\nthough, I've found it to be tricky, and the resulting parser\nturned out to be quite fragile ... one addition or deletion here\nor there and things tended to go to pot. Looking back on it, I\nrealize that there was a message in this that I just wasn't\npaying attention to.\n\nWhen I tried to add semicolons on top of the newlines, that was\nthe last straw. I ended up with much too complex a solution. I\nbegan to realize that something fundamental had to change.\n\nSo, in a way this installment will cause us to backtrack a bit\nand revisit the issue of scanning all over again. Sorry about\nthat. That's the price you pay for watching me do this in real\ntime. But the new version is definitely an improvement, and will\nserve us well for what is to come.\n\nAs I said, the scanner we used in Part X was about as simple as\none can get. But anything can be improved. The new scanner is\nmore like the classical scanner, and not as simple as before.\nBut the overall compiler structure is even simpler than before.\nIt's also more robust, and easier to add to and/or modify. I\nthink that's worth the time spent in this digression. So in this\ninstallment, I'll be showing you the new structure. No doubt\nyou'll be happy to know that, while the changes affect many\nprocedures, they aren't very profound and so we lose very little\nof what's been done so far.\n\nIronically, the new scanner is much more conventional than the\nold one, and is very much like the more generic scanner I showed\nyou earlier in Part VII. Then I started trying to get clever,\nand I almost clevered myself clean out of business. You'd think\none day I'd learn: K-I-S-S!"},{title:"THE PROBLEM",path:"/the-problem",content:"The problem begins to show itself in procedure Block, which I've\nreproduced below:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n Scan;\n while not(Token in ['e', 'l']) do begin\n case Token of\n 'i': DoIf;\n 'w': DoWhile;\n 'R': DoRead;\n 'W': DoWrite;\n else Assignment;\n end;\n Scan;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nAs you can see, Block is oriented to individual program\nstatements. At each pass through the loop, we know that we are\nat the beginning of a statement. We exit the block when we have\nscanned an END or an ELSE.\n\nBut suppose that we see a semicolon instead. The procedure as\nit's shown above can't handle that, because procedure Scan only\nexpects and can only accept tokens that begin with a letter.\n\nI tinkered around for quite awhile to come up with a fix. I\nfound many possible approaches, but none were very satisfying. I\nfinally figured out the reason.\n\nRecall that when we started with our single-character parsers, we\nadopted a convention that the lookahead character would always be\nprefetched. That is, we would have the character that\ncorresponds to our current position in the input stream fetched\ninto the global character Look, so that we could examine it as\nmany times as needed. The rule we adopted was that EVERY\nrecognizer, if it found its target token, would advance Look to\nthe next character in the input stream.\n\nThat simple and fixed convention served us very well when we had\nsingle-character tokens, and it still does. It would make a lot\nof sense to apply the same rule to multi-character tokens.\n\nBut when we got into lexical scanning, I began to violate that\nsimple rule. The scanner of Part X did indeed advance to the\nnext token if it found an identifier or keyword, but it DIDN'T do\nthat if it found a carriage return, a whitespace character, or an\noperator.\n\nNow, that sort of mixed-mode operation gets us into deep trouble\nin procedure Block, because whether or not the input stream has\nbeen advanced depends upon the kind of token we encounter. If\nit's a keyword or the target of an assignment statement, the\n\"cursor,\" as defined by the contents of Look, has been advanced\nto the next token OR to the beginning of whitespace. If, on the\nother hand, the token is a semicolon, or if we have hit a\ncarriage return, the cursor has NOT advanced.\n\nNeedless to say, we can add enough logic to keep us on track.\nBut it's tricky, and makes the whole parser very fragile.\n\nThere's a much better way, and that's just to adopt that same\nrule that's worked so well before, to apply to TOKENS as well as\nsingle characters. In other words, we'll prefetch tokens just as\nwe've always done for characters. It seems so obvious once you\nthink about it that way.\n\nInterestingly enough, if we do things this way the problem that\nwe've had with newline characters goes away. We can just lump\nthem in as whitespace characters, which means that the handling\nof newlines becomes very trivial, and MUCH less prone to error\nthan we've had to deal with in the past."},{title:"THE SOLUTION",path:"/the-solution",content:"Let's begin to fix the problem by re-introducing the two\nprocedures:\n```delphi\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nprocedure GetName;\nbegin\n SkipWhite;\n if Not IsAlpha(Look) then Expected('Identifier');\n Token := 'x';\n Value := '';\n repeat\n Value := Value + UpCase(Look);\n GetChar;\n until not IsAlNum(Look);\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nprocedure GetNum;\nbegin\n SkipWhite;\n if not IsDigit(Look) then Expected('Number');\n Token := '#';\n Value := '';\n repeat\n Value := Value + Look;\n GetChar;\n until not IsDigit(Look);\nend;\n{--------------------------------------------------------------}\n```\n\nThese two procedures are functionally almost identical to the\nones I showed you in Part VII. They each fetch the current\ntoken, either an identifier or a number, into the global string\nValue. They also set the encoded version, Token, to the\nappropriate code. The input stream is left with Look containing\nthe first character NOT part of the token.\n\nWe can do the same thing for operators, even multi-character\noperators, with a procedure such as:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get an Operator }\n\nprocedure GetOp;\nbegin\n Token := Look;\n Value := '';\n repeat\n Value := Value + Look;\n GetChar;\n until IsAlpha(Look) or IsDigit(Look) or IsWhite(Look);\nend;\n{--------------------------------------------------------------}\n```\nNote that GetOp returns, as its encoded token, the FIRST\ncharacter of the operator. This is important, because it means\nthat we can now use that single character to drive the parser,\ninstead of the lookahead character.\n\nWe need to tie these procedures together into a single procedure\nthat can handle all three cases. The following procedure will\nread any one of the token types and always leave the input stream\nadvanced beyond it:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get the Next Input Token }\n\nprocedure Next;\nbegin\n SkipWhite;\n if IsAlpha(Look) then GetName\n else if IsDigit(Look) then GetNum\n else GetOp;\nend;\n{--------------------------------------------------------------}\n```\n\n***NOTE that here I have put SkipWhite BEFORE the calls rather\nthan after. This means that, in general, the variable Look will\nNOT have a meaningful value in it, and therefore we should NOT\nuse it as a test value for parsing, as we have been doing so far.\nThat's the big departure from our normal approach.\n\nNow, remember that before I was careful not to treat the carriage\nreturn (CR) and line feed (LF) characters as white space. This\nwas because, with SkipWhite called as the last thing in the\nscanner, the encounter with LF would trigger a read statement.\nIf we were on the last line of the program, we couldn't get out\nuntil we input another line with a non-white character. That's\nwhy I needed the second procedure, NewLine, to handle the CRLF's.\n\nBut now, with the call to SkipWhite coming first, that's exactly\nthe behavior we want. The compiler must know there's another\ntoken coming or it wouldn't be calling Next. In other words, it\nhasn't found the terminating END yet. So we're going to insist\non more data until we find something.\n\nAll this means that we can greatly simplify both the program and\nthe concepts, by treating CR and LF as whitespace characters, and\neliminating NewLine. You can do that simply by modifying the\nfunction IsWhite:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB, CR, LF];\nend;\n{--------------------------------------------------------------}\n```\n\nWe've already tried similar routines in Part VII, but you might\nas well try these new ones out. Add them to a copy of the Cradle\nand call Next with the following main program:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n repeat\n Next;\n WriteLn(Token, ' ', Value);\n until Token = '.';\nend.\n{--------------------------------------------------------------}\n```\n\nCompile it and verify that you can separate a program into a\nseries of tokens, and that you get the right encoding for each\ntoken.\n\nThis ALMOST works, but not quite. There are two potential\nproblems: First, in KISS/TINY almost all of our operators are\nsingle-character operators. The only exceptions are the relops\n>=, <=, and <>. It seems a shame to treat all operators as\nstrings and do a string compare, when only a single character\ncompare will almost always suffice. Second, and much more\nimportant, the thing doesn't WORK when two operators appear\ntogether, as in (a+b)*(c+d). Here the string following 'b' would\nbe interpreted as a single operator \")*(.\"\n\nIt's possible to fix that problem. For example, we could just\ngive GetOp a list of legal characters, and we could treat the\nparentheses as different operator types than the others. But\nthis begins to get messy.\n\nFortunately, there's a better way that solves all the problems.\nSince almost all the operators are single characters, let's just\ntreat them that way, and let GetOp get only one character at a\ntime. This not only simplifies GetOp, but also speeds things up\nquite a bit. We still have the problem of the relops, but we\nwere treating them as special cases anyway.\n\nSo here's the final version of GetOp:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get an Operator }\n\nprocedure GetOp;\nbegin\n SkipWhite;\n Token := Look;\n Value := Look;\n GetChar;\nend;\n{--------------------------------------------------------------}\n\n```\nNote that I still give the string Value a value. If you're truly\nconcerned about efficiency, you could leave this out. When we're\nexpecting an operator, we will only be testing Token anyhow, so\nthe value of the string won't matter. But to me it seems to be\ngood practice to give the thing a value just in case.\n\nTry this new version with some realistic-looking code. You\nshould be able to separate any program into its individual\ntokens, with the caveat that the two-character relops will scan\ninto two separate tokens. That's OK ... we'll parse them that\nway.\n\nNow, in Part VII the function of Next was combined with procedure\nScan, which also checked every identifier against a list of\nkeywords and encoded each one that was found. As I mentioned at\nthe time, the last thing we would want to do is to use such a\nprocedure in places where keywords should not appear, such as in\nexpressions. If we did that, the keyword list would be scanned\nfor every identifier appearing in the code. Not good.\n\nThe right way to deal with that is to simply separate the\nfunctions of fetching tokens and looking for keywords. The\nversion of Scan shown below does NOTHING but check for keywords.\nNotice that it operates on the current token and does NOT advance\nthe input stream.\n\n```delphi\n{--------------------------------------------------------------}\n{ Scan the Current Identifier for Keywords }\n\nprocedure Scan;\nbegin\n if Token = 'x' then\n Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];\nend;\n{--------------------------------------------------------------}\n```\n\nThere is one last detail. In the compiler there are a few places\nthat we must actually check the string value of the token.\nMainly, this is done to distinguish between the different END's,\nbut there are a couple of other places. (I should note in\npassing that we could always eliminate the need for matching END\ncharacters by encoding each one to a different character. Right\nnow we are definitely taking the lazy man's route.)\n\nThe following version of MatchString takes the place of the\ncharacter-oriented Match. Note that, like Match, it DOES advance\nthe input stream.\n\n```delphi\n{--------------------------------------------------------------}\n{ Match a Specific Input String }\n\nprocedure MatchString(x: string);\nbegin\n if Value <> x then Expected('''' + x + '''');\n Next;\nend;\n{--------------------------------------------------------------}\n```"},{title:"FIXING UP THE COMPILER",path:"/fixing-up-the-compiler",content:"Armed with these new scanner procedures, we can now begin to fix\nthe compiler to use them properly. The changes are all quite\nminor, but there are quite a few places where changes are\nnecessary. Rather than showing you each place, I will give you\nthe general idea and then just give the finished product.\n\n\nFirst of all, the code for procedure Block doesn't change, though\nits function does:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n Scan;\n while not(Token in ['e', 'l']) do begin\n case Token of\n 'i': DoIf;\n 'w': DoWhile;\n 'R': DoRead;\n 'W': DoWrite;\n else Assignment;\n end;\n Scan;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nRemember that the new version of Scan doesn't advance the input\nstream, it only scans for keywords. The input stream must be\nadvanced by each procedure that Block calls.\n\nIn general, we have to replace every test on Look with a similar\ntest on Token. For example:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Expression }\n\nprocedure BoolExpression;\nbegin\n BoolTerm;\n while IsOrOp(Token) do begin\n Push;\n case Token of\n '|': BoolOr;\n '~': BoolXor;\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nIn procedures like Add, we don't have to use Match anymore. We\nneed only call Next to advance the input stream:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Next;\n Term;\n PopAdd;\nend;\n{-------------------------------------------------------------}\n```\n\nControl structures are actually simpler. We just call Next to\nadvance over the control keywords:\n\n```delphi\n{---------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block; Forward;\n\nprocedure DoIf;\nvar L1, L2: string;\nbegin\n Next;\n BoolExpression;\n L1 := NewLabel;\n L2 := L1;\n BranchFalse(L1);\n Block;\n if Token = 'l' then begin\n Next;\n L2 := NewLabel;\n Branch(L2);\n PostLabel(L1);\n Block;\n end;\n PostLabel(L2);\n MatchString('ENDIF');\nend;\n{--------------------------------------------------------------}\n```\n\nThat's about the extent of the REQUIRED changes. In the listing\nof TINY Version 1.1 below, I've also made a number of other\n\"improvements\" that aren't really required. Let me explain them\nbriefly:\n\n (1) I've deleted the two procedures Prog and Main, and combined\n their functions into the main program. They didn't seem to\n add to program clarity ... in fact they seemed to just\n muddy things up a little.\n\n (2) I've deleted the keywords PROGRAM and BEGIN from the\n keyword list. Each one only occurs in one place, so it's\n not necessary to search for it.\n\n (3) Having been bitten by an overdose of cleverness, I've\n reminded myself that TINY is supposed to be a minimalist\n program. Therefore I've replaced the fancy handling of\n unary minus with the dumbest one I could think of. A giant\n step backwards in code quality, but a great simplification\n of the compiler. KISS is the right place to use the other\n version.\n\n (4) I've added some error-checking routines such as CheckTable\n and CheckDup, and replaced in-line code by calls to them.\n This cleans up a number of routines.\n\n (5) I've taken the error checking out of code generation\n routines like Store, and put it in the parser where it\n belongs. See Assignment, for example.\n\n (6) There was an error in InTable and Locate that caused them\n to search all locations instead of only those with valid\n data in them. They now search only valid cells. This\n allows us to eliminate the initialization of the symbol\n table, which was done in Init.\n\n (7) Procedure AddEntry now has two arguments, which helps to\n make things a bit more modular.\n\n (8) I've cleaned up the code for the relational operators by\n the addition of the new procedures CompareExpression and\n NextExpression.\n\n (9) I fixed an error in the Read routine ... the earlier value\n did not check for a valid variable name."},{title:"CONCLUSION",path:"/conclusion",content:"The resulting compiler for TINY is given below. Other than the\nremoval of the keyword PROGRAM, it parses the same language as\nbefore. It's just a bit cleaner, and more importantly it's\nconsiderably more robust. I feel good about it.\n\nThe next installment will be another digression: the discussion\nof semicolons and such that got me into this mess in the first\nplace. THEN we'll press on into procedures and types. Hang in\nthere with me. The addition of those features will go a long way\ntowards removing KISS from the \"toy language\" category. We're\ngetting very close to being able to write a serious compiler."},{title:"TINY VERSION 1.1",path:"/tiny-version-11",content:"```delphi\n{--------------------------------------------------------------}\nprogram Tiny11;\n\n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n CR = ^M;\n LF = ^J;\n\n LCount: integer = 0;\n NEntry: integer = 0;\n\n\n{--------------------------------------------------------------}\n{ Type Declarations }\n\ntype Symbol = string[8];\n\n SymTab = array[1..1000] of Symbol;\n\n TabPtr = ^SymTab;\n\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look : char; { Lookahead Character }\n Token: char; { Encoded Token }\n Value: string[16]; { Unencoded Token }\n\n\nconst MaxEntry = 100;\n\nvar ST : array[1..MaxEntry] of Symbol;\n SType: array[1..MaxEntry] of char;\n\n\n{--------------------------------------------------------------}\n{ Definition of Keywords and Token Types }\n\nconst NKW = 9;\n NKW1 = 10;\n\nconst KWlist: array[1..NKW] of Symbol =\n ('IF', 'ELSE', 'ENDIF', 'WHILE', 'ENDWHILE',\n 'READ', 'WRITE', 'VAR', 'END');\n\nconst KWcode: string[NKW1] = 'xileweRWve';\n\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n\nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n{--------------------------------------------------------------}\n{ Report an Undefined Identifier }\n\nprocedure Undefined(n: string);\nbegin\n Abort('Undefined Identifier ' + n);\nend;\n\n\n{--------------------------------------------------------------}\n{ Report a Duplicate Identifier }\n\nprocedure Duplicate(n: string);\nbegin\n Abort('Duplicate Identifier ' + n);\nend;\n\n\n{--------------------------------------------------------------}\n{ Check to Make Sure the Current Token is an Identifier }\n\nprocedure CheckIdent;\nbegin\n if Token <> 'x' then Expected('Identifier');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an AlphaNumeric Character }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Mulop }\n\nfunction IsMulop(c: char): boolean;\nbegin\n IsMulop := c in ['*', '/'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Boolean Orop }\n\nfunction IsOrop(c: char): boolean;\nbegin\n IsOrop := c in ['|', '~'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Relop }\n\nfunction IsRelop(c: char): boolean;\nbegin\n IsRelop := c in ['=', '#', '<', '>'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB, CR, LF];\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Table Lookup }\n\nfunction Lookup(T: TabPtr; s: string; n: integer): integer;\nvar i: integer;\n found: Boolean;\nbegin\n found := false;\n i := n;\n while (i > 0) and not found do\n if s = T^[i] then\n found := true\n else\n dec(i);\n Lookup := i;\nend;\n\n\n{--------------------------------------------------------------}\n{ Locate a Symbol in Table }\n{ Returns the index of the entry. Zero if not present. }\n\nfunction Locate(N: Symbol): integer;\nbegin\n Locate := Lookup(@ST, n, NEntry);\nend;\n\n\n{--------------------------------------------------------------}\n{ Look for Symbol in Table }\n\nfunction InTable(n: Symbol): Boolean;\nbegin\n InTable := Lookup(@ST, n, NEntry) <> 0;\nend;\n\n\n{--------------------------------------------------------------}\n{ Check to See if an Identifier is in the Symbol Table }\n{ Report an error if it's not. }\n\n\nprocedure CheckTable(N: Symbol);\nbegin\n if not InTable(N) then Undefined(N);\nend;\n\n\n{--------------------------------------------------------------}\n{ Check the Symbol Table for a Duplicate Identifier }\n{ Report an error if identifier is already in table. }\n\n\nprocedure CheckDup(N: Symbol);\nbegin\n if InTable(N) then Duplicate(N);\nend;\n\n\n{--------------------------------------------------------------}\n{ Add a New Entry to Symbol Table }\n\nprocedure AddEntry(N: Symbol; T: char);\nbegin\n CheckDup(N);\n if NEntry = MaxEntry then Abort('Symbol Table Full');\n Inc(NEntry);\n ST[NEntry] := N;\n SType[NEntry] := T;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nprocedure GetName;\nbegin\n SkipWhite;\n if Not IsAlpha(Look) then Expected('Identifier');\n Token := 'x';\n Value := '';\n repeat\n Value := Value + UpCase(Look);\n GetChar;\n until not IsAlNum(Look);\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nprocedure GetNum;\nbegin\n SkipWhite;\n if not IsDigit(Look) then Expected('Number');\n Token := '#';\n Value := '';\n repeat\n Value := Value + Look;\n GetChar;\n until not IsDigit(Look);\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Operator }\n\nprocedure GetOp;\nbegin\n SkipWhite;\n Token := Look;\n Value := Look;\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get the Next Input Token }\n\nprocedure Next;\nbegin\n SkipWhite;\n if IsAlpha(Look) then GetName\n else if IsDigit(Look) then GetNum\n else GetOp;\nend;\n\n\n{--------------------------------------------------------------}\n{ Scan the Current Identifier for Keywords }\n\nprocedure Scan;\nbegin\n if Token = 'x' then\n Token := KWcode[Lookup(Addr(KWlist), Value, NKW) + 1];\nend;\n\n\n{--------------------------------------------------------------}\n{ Match a Specific Input String }\n\nprocedure MatchString(x: string);\nbegin\n if Value <> x then Expected('''' + x + '''');\n Next;\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n\nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n\n{--------------------------------------------------------------}\n{ Generate a Unique Label }\n\nfunction NewLabel: string;\nvar S: string;\nbegin\n Str(LCount, S);\n NewLabel := 'L' + S;\n Inc(LCount);\nend;\n\n\n{--------------------------------------------------------------}\n{ Post a Label To Output }\n\nprocedure PostLabel(L: string);\nbegin\n WriteLn(L, ':');\nend;\n\n\n{---------------------------------------------------------------}\n{ Clear the Primary Register }\n\nprocedure Clear;\nbegin\n EmitLn('CLR D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Negate the Primary Register }\n\nprocedure Negate;\nbegin\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Complement the Primary Register }\n\nprocedure NotIt;\nbegin\n EmitLn('NOT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Load a Constant Value to Primary Register }\n\nprocedure LoadConst(n: string);\nbegin\n Emit('MOVE #');\n WriteLn(n, ',D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Load a Variable to Primary Register }\n\nprocedure LoadVar(Name: string);\nbegin\n if not InTable(Name) then Undefined(Name);\n EmitLn('MOVE ' + Name + '(PC),D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Push Primary onto Stack }\n\nprocedure Push;\nbegin\n EmitLn('MOVE D0,-(SP)');\nend;\n\n\n{---------------------------------------------------------------}\n{ Add Top of Stack to Primary }\n\nprocedure PopAdd;\nbegin\n EmitLn('ADD (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Subtract Primary from Top of Stack }\n\nprocedure PopSub;\nbegin\n EmitLn('SUB (SP)+,D0');\n EmitLn('NEG D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Multiply Top of Stack by Primary }\n\nprocedure PopMul;\nbegin\n EmitLn('MULS (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Divide Top of Stack by Primary }\n\nprocedure PopDiv;\nbegin\n EmitLn('MOVE (SP)+,D7');\n EmitLn('EXT.L D7');\n EmitLn('DIVS D0,D7');\n EmitLn('MOVE D7,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ AND Top of Stack with Primary }\n\nprocedure PopAnd;\nbegin\n EmitLn('AND (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ OR Top of Stack with Primary }\n\nprocedure PopOr;\nbegin\n EmitLn('OR (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ XOR Top of Stack with Primary }\n\nprocedure PopXor;\nbegin\n EmitLn('EOR (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Compare Top of Stack with Primary }\n\nprocedure PopCompare;\nbegin\n EmitLn('CMP (SP)+,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was = }\n\nprocedure SetEqual;\nbegin\n EmitLn('SEQ D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was != }\n\nprocedure SetNEqual;\nbegin\n EmitLn('SNE D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was > }\n\nprocedure SetGreater;\nbegin\n EmitLn('SLT D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was < }\n\nprocedure SetLess;\nbegin\n EmitLn('SGT D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was <= }\n\nprocedure SetLessOrEqual;\nbegin\n EmitLn('SGE D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Set D0 If Compare was >= }\n\nprocedure SetGreaterOrEqual;\nbegin\n EmitLn('SLE D0');\n EmitLn('EXT D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Store Primary to Variable }\n\nprocedure Store(Name: string);\nbegin\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)')\nend;\n\n\n{---------------------------------------------------------------}\n{ Branch Unconditional }\n\nprocedure Branch(L: string);\nbegin\n EmitLn('BRA ' + L);\nend;\n\n\n{---------------------------------------------------------------}\n{ Branch False }\n\nprocedure BranchFalse(L: string);\nbegin\n EmitLn('TST D0');\n EmitLn('BEQ ' + L);\nend;\n\n\n{---------------------------------------------------------------}\n{ Read Variable to Primary Register }\n\nprocedure ReadIt(Name: string);\nbegin\n EmitLn('BSR READ');\n Store(Name);\nend;\n\n\n{ Write from Primary Register }\n\nprocedure WriteIt;\nbegin\n EmitLn('BSR WRITE');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write Header Info }\n\nprocedure Header;\nbegin\n WriteLn('WARMST', TAB, 'EQU $A01E');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write the Prolog }\n\nprocedure Prolog;\nbegin\n PostLabel('MAIN');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write the Epilog }\n\nprocedure Epilog;\nbegin\n EmitLn('DC WARMST');\n EmitLn('END MAIN');\nend;\n\n\n{---------------------------------------------------------------}\n{ Allocate Storage for a Static Variable }\n\nprocedure Allocate(Name, Val: string);\nbegin\n WriteLn(Name, ':', TAB, 'DC ', Val);\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Factor }\n\nprocedure BoolExpression; Forward;\n\nprocedure Factor;\nbegin\n if Token = '(' then begin\n Next;\n BoolExpression;\n MatchString(')');\n end\n else begin\n if Token = 'x' then\n LoadVar(Value)\n else if Token = '#' then\n LoadConst(Value)\n else Expected('Math Factor');\n Next;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nprocedure Multiply;\nbegin\n Next;\n Factor;\n PopMul;\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nprocedure Divide;\nbegin\n Next;\n Factor;\n PopDiv;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nprocedure Term;\nbegin\n Factor;\n while IsMulop(Token) do begin\n Push;\n case Token of\n '*': Multiply;\n '/': Divide;\n end;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nprocedure Add;\nbegin\n Next;\n Term;\n PopAdd;\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nprocedure Subtract;\nbegin\n Next;\n Term;\n PopSub;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nbegin\n if IsAddop(Token) then\n Clear\n else\n Term;\n while IsAddop(Token) do begin\n Push;\n case Token of\n '+': Add;\n '-': Subtract;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Get Another Expression and Compare }\n\nprocedure CompareExpression;\nbegin\n Expression;\n PopCompare;\nend;\n\n\n{---------------------------------------------------------------}\n{ Get The Next Expression and Compare }\n\nprocedure NextExpression;\nbegin\n Next;\n CompareExpression;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Equals\" }\n\nprocedure Equal;\nbegin\n NextExpression;\n SetEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Less Than or Equal\" }\n\nprocedure LessOrEqual;\nbegin\n NextExpression;\n SetLessOrEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Not Equals\" }\n\nprocedure NotEqual;\nbegin\n NextExpression;\n SetNEqual;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Less Than\" }\n\nprocedure Less;\nbegin\n Next;\n case Token of\n '=': LessOrEqual;\n '>': NotEqual;\n else begin\n CompareExpression;\n SetLess;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate a Relational \"Greater Than\" }\n\nprocedure Greater;\nbegin\n Next;\n if Token = '=' then begin\n NextExpression;\n SetGreaterOrEqual;\n end\n else begin\n CompareExpression;\n SetGreater;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Relation }\n\n\nprocedure Relation;\nbegin\n Expression;\n if IsRelop(Token) then begin\n Push;\n case Token of\n '=': Equal;\n '<': Less;\n '>': Greater;\n end;\n end;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Factor with Leading NOT }\n\nprocedure NotFactor;\nbegin\n if Token = '!' then begin\n Next;\n Relation;\n NotIt;\n end\n else\n Relation;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Term }\n\nprocedure BoolTerm;\nbegin\n NotFactor;\n while Token = '&' do begin\n Push;\n Next;\n NotFactor;\n PopAnd;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Boolean OR }\n\nprocedure BoolOr;\nbegin\n Next;\n BoolTerm;\n PopOr;\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate an Exclusive Or }\n\nprocedure BoolXor;\nbegin\n Next;\n BoolTerm;\n PopXor;\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Boolean Expression }\n\nprocedure BoolExpression;\nbegin\n BoolTerm;\n while IsOrOp(Token) do begin\n Push;\n case Token of\n '|': BoolOr;\n '~': BoolXor;\n end;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: string;\nbegin\n CheckTable(Value);\n Name := Value;\n Next;\n MatchString('=');\n BoolExpression;\n Store(Name);\nend;\n\n\n{---------------------------------------------------------------}\n{ Recognize and Translate an IF Construct }\n\nprocedure Block; Forward;\n\nprocedure DoIf;\nvar L1, L2: string;\nbegin\n Next;\n BoolExpression;\n L1 := NewLabel;\n L2 := L1;\n BranchFalse(L1);\n Block;\n if Token = 'l' then begin\n Next;\n L2 := NewLabel;\n Branch(L2);\n PostLabel(L1);\n Block;\n end;\n PostLabel(L2);\n MatchString('ENDIF');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a WHILE Statement }\n\nprocedure DoWhile;\nvar L1, L2: string;\nbegin\n Next;\n L1 := NewLabel;\n L2 := NewLabel;\n PostLabel(L1);\n BoolExpression;\n BranchFalse(L2);\n Block;\n MatchString('ENDWHILE');\n Branch(L1);\n PostLabel(L2);\nend;\n\n\n{--------------------------------------------------------------}\n{ Read a Single Variable }\n\nprocedure ReadVar;\nbegin\n CheckIdent;\n CheckTable(Value);\n ReadIt(Value);\n Next;\nend;\n\n\n{--------------------------------------------------------------}\n{ Process a Read Statement }\n\nprocedure DoRead;\nbegin\n Next;\n MatchString('(');\n ReadVar;\n while Token = ',' do begin\n Next;\n ReadVar;\n end;\n MatchString(')');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process a Write Statement }\n\nprocedure DoWrite;\nbegin\n Next;\n MatchString('(');\n Expression;\n WriteIt;\n while Token = ',' do begin\n Next;\n Expression;\n WriteIt;\n end;\n MatchString(')');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n Scan;\n while not(Token in ['e', 'l']) do begin\n case Token of\n 'i': DoIf;\n 'w': DoWhile;\n 'R': DoRead;\n 'W': DoWrite;\n else Assignment;\n end;\n Scan;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc;\nbegin\n Next;\n if Token <> 'x' then Expected('Variable Name');\n CheckDup(Value);\n AddEntry(Value, 'v');\n Allocate(Value, '0');\n Next;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n Scan;\n while Token = 'v' do\n Alloc;\n while Token = ',' do\n Alloc;\nend;\n\n\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nbegin\n GetChar;\n Next;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n MatchString('PROGRAM');\n Header;\n TopDecls;\n MatchString('BEGIN');\n Prolog;\n Block;\n MatchString('END');\n Epilog;\nend.\n{--------------------------------------------------------------}\n```"}]},{title:"Part XII: MISCELLANY - 5 June 1989",path:"/part-xii-miscellany-5-june-1989",items:[{title:"INTRODUCTION",path:"/introduction",content:"This installment is another one of those excursions into side\nalleys that don't seem to fit into the mainstream of this\ntutorial series. As I mentioned last time, it was while I was\nwriting this installment that I realized some changes had to be\nmade to the compiler structure. So I had to digress from this\ndigression long enough to develop the new structure and show it\nto you.\n\nNow that that's behind us, I can tell you what I set out to in\nthe first place. This shouldn't take long, and then we can get\nback into the mainstream.\n\nSeveral people have asked me about things that other languages\nprovide, but so far I haven't addressed in this series. The two\nbiggies are semicolons and comments. Perhaps you've wondered\nabout them, too, and wondered how things would change if we had\nto deal with them. Just so you can proceed with what's to come,\nwithout being bothered by that nagging feeling that something is\nmissing, we'll address such issues here."},{title:"SEMICOLONS",path:"/semicolons",content:"Ever since the introduction of Algol, semicolons have been a part\nof almost every modern language. We've all used them to the\npoint that they are taken for granted. Yet I suspect that more\ncompilation errors have occurred due to misplaced or missing\nsemicolons than any other single cause. And if we had a penny\nfor every extra keystroke programmers have used to type the\nlittle rascals, we could pay off the national debt.\n\nHaving been brought up with FORTRAN, it took me a long time to\nget used to using semicolons, and to tell the truth I've never\nquite understood why they were necessary. Since I program in\nPascal, and since the use of semicolons in Pascal is particularly\ntricky, that one little character is still by far my biggest\nsource of errors.\n\nWhen I began developing KISS, I resolved to question EVERY\nconstruct in other languages, and to try to avoid the most common\nproblems that occur with them. That puts the semicolon very high\non my hit list.\n\nTo understand the role of the semicolon, you have to look at a\nlittle history.\n\nEarly programming languages were line-oriented. In FORTRAN, for\nexample, various parts of the statement had specific columns or\nfields that they had to appear in. Since some statements were\ntoo long for one line, the \"continuation card\" mechanism was\nprovided to let the compiler know that a given card was still\npart of the previous line. The mechanism survives to this day,\neven though punched cards are now things of the distant past.\n\nWhen other languages came along, they also adopted various\nmechanisms for dealing with multiple-line statements. BASIC is a\ngood example. It's important to recognize, though, that the\nFORTRAN mechanism was not so much required by the line\norientation of that language, as by the column-orientation. In\nthose versions of FORTRAN where free-form input is permitted,\nit's no longer needed.\n\nWhen the fathers of Algol introduced that language, they wanted\nto get away from line-oriented programs like FORTRAN and BASIC,\nand allow for free-form input. This included the possibility of\nstringing multiple statements on a single line, as in\n\n```\n a=b; c=d; e=e+1;\n\n```\nIn cases like this, the semicolon is almost REQUIRED. The same\nline, without the semicolons, just looks \"funny\":\n\n```\n a=b c= d e=e+1\n```\nI suspect that this is the major ... perhaps ONLY ... reason for\nsemicolons: to keep programs from looking funny.\n\nBut the idea of stringing multiple statements together on a\nsingle line is a dubious one at best. It's not very good\nprogramming style, and harks back to the days when it was\nconsidered improtant to conserve cards. In these days of CRT's\nand indented code, the clarity of programs is far better served\nby keeping statements separate. It's still nice to have the\nOPTION of multiple statements, but it seems a shame to keep\nprogrammers in slavery to the semicolon, just to keep that one\nrare case from \"looking funny.\"\n\nWhen I started in with KISS, I tried to keep an open mind. I\ndecided that I would use semicolons when it became necessary for\nthe parser, but not until then. I figured this would happen just\nabout the time I added the ability to spread statements over\nmultiple lines. But, as you can see, that never happened. The\nTINY compiler is perfectly happy to parse the most complicated\nstatement, spread over any number of lines, without semicolons.\n\nStill, there are people who have used semicolons for so long,\nthey feel naked without them. I'm one of them. Once I had KISS\ndefined sufficiently well, I began to write a few sample programs\nin the language. I discovered, somewhat to my horror, that I\nkept putting semicolons in anyway. So now I'm facing the\nprospect of a NEW rash of compiler errors, caused by UNWANTED\nsemicolons. Phooey!\n\nPerhaps more to the point, there are readers out there who are\ndesigning their own languages, which may include semicolons, or\nwho want to use the techniques of these tutorials to compile\nconventional languages like C. In either case, we need to be\nable to deal with semicolons."},{title:"SYNTACTIC SUGAR",path:"/syntactic-sugar",content:"This whole discussion brings up the issue of \"syntactic sugar\"\n... constructs that are added to a language, not because they are\nneeded, but because they help make the programs look right to the\nprogrammer. After all, it's nice to have a small, simple\ncompiler, but it would be of little use if the resulting\nlanguage were cryptic and hard to program. The language FORTH\ncomes to mind (a premature OUCH! for the barrage I know that\none's going to fetch me). If we can add features to the language\nthat make the programs easier to read and understand, and if\nthose features help keep the programmer from making errors, then\nwe should do so. Particularly if the constructs don't add much\nto the complexity of the language or its compiler.\n\nThe semicolon could be considered an example, but there are\nplenty of others, such as the 'THEN' in a IF-statement, the 'DO'\nin a WHILE-statement, and even the 'PROGRAM' statement, which I\ncame within a gnat's eyelash of leaving out of TINY. None of\nthese tokens add much to the syntax of the language ... the\ncompiler can figure out what's going on without them. But some\nfolks feel that they DO add to the readability of programs, and\nthat can be very important.\n\nThere are two schools of thought on this subject, which are well\nrepresented by two of our most popular languages, C and Pascal.\n\nTo the minimalists, all such sugar should be left out. They\nargue that it clutters up the language and adds to the number of\nkeystrokes programmers must type. Perhaps more importantly,\nevery extra token or keyword represents a trap laying in wait for\nthe inattentive programmer. If you leave out a token, misplace\nit, or misspell it, the compiler will get you. So these people\nargue that the best approach is to get rid of such things. These\nfolks tend to like C, which has a minimum of unnecessary keywords\nand punctuation.\n\nThose from the other school tend to like Pascal. They argue that\nhaving to type a few extra characters is a small price to pay for\nlegibility. After all, humans have to read the programs, too.\nTheir best argument is that each such construct is an opportunity\nto tell the compiler that you really mean for it to do what you\nsaid to. The sugary tokens serve as useful landmarks to help you\nfind your way.\n\nThe differences are well represented by the two languages. The\nmost oft-heard complaint about C is that it is too forgiving.\nWhen you make a mistake in C, the erroneous code is too often\nanother legal C construct. So the compiler just happily\ncontinues to compile, and leaves you to find the error during\ndebug. I guess that's why debuggers are so popular with C\nprogrammers.\n\nOn the other hand, if a Pascal program compiles, you can be\npretty sure that the program will do what you told it. If there\nis an error at run time, it's probably a design error.\n\nThe best example of useful sugar is the semicolon itself.\nConsider the code fragment:\n\n```\n a=1+(2*b+c) b...\n```\n\nSince there is no operator connecting the token 'b' with the rest\nof the statement, the compiler will conclude that the expression\nends with the ')', and the 'b' is the beginning of a new\nstatement. But suppose I have simply left out the intended\noperator, and I really want to say:\n\n```\n a=1+(2*b+c)*b...\n```\n\nIn this case the compiler will get an error, all right, but it\nwon't be very meaningful since it will be expecting an '=' sign\nafter the 'b' that really shouldn't be there.\n\nIf, on the other hand, I include a semicolon after the 'b', THEN\nthere can be no doubt where I intend the statement to end.\nSyntactic sugar, then, can serve a very useful purpose by\nproviding some additional insurance that we remain on track.\n\nI find myself somewhere in the middle of all this. I tend to\nfavor the Pascal-ers' view ... I'd much rather find my bugs at\ncompile time rather than run time. But I also hate to just throw\nverbosity in for no apparent reason, as in COBOL. So far I've\nconsistently left most of the Pascal sugar out of KISS/TINY. But\nI certainly have no strong feelings either way, and I also can\nsee the value of sprinkling a little sugar around just for the\nextra insurance that it brings. If you like this latter\napproach, things like that are easy to add. Just remember that,\nlike the semicolon, each item of sugar is something that can\npotentially cause a compile error by its omission."},{title:"DEALING WITH SEMICOLONS",path:"/dealing-with-semicolons",content:"There are two distinct ways in which semicolons are used in\npopular languages. In Pascal, the semicolon is regarded as an\nstatement SEPARATOR. No semicolon is required after the last\nstatement in a block. The syntax is:\n\n```\n <block> ::= <statement> ( ';' <statement>)*\n\n <statement> ::= <assignment> | <if> | <while> ... | null\n\n```\n(The null statement is IMPORTANT!)\n\nPascal also defines some semicolons in other places, such as\nafter the PROGRAM statement.\n\nIn C and Ada, on the other hand, the semicolon is considered a\nstatement TERMINATOR, and follows all statements (with some\nembarrassing and confusing exceptions). The syntax for this is\nsimply:\n\n```\n <block> ::= ( <statement> ';')*\n\n```\nOf the two syntaxes, the Pascal one seems on the face of it more\nrational, but experience has shown that it leads to some strange\ndifficulties. People get so used to typing a semicolon after\nevery statement that they tend to type one after the last\nstatement in a block, also. That usually doesn't cause any harm\n... it just gets treated as a null statement. Many Pascal\nprogrammers, including yours truly, do just that. But there is\none place you absolutely CANNOT type a semicolon, and that's\nright before an ELSE. This little gotcha has cost me many an\nextra compilation, particularly when the ELSE is added to\nexisting code. So the C/Ada choice turns out to be better.\nApparently Nicklaus Wirth thinks so, too: In his Modula 2, he\nabandoned the Pascal approach.\n\nGiven either of these two syntaxes, it's an easy matter (now that\nwe've reorganized the parser!) to add these features to our\nparser. Let's take the last case first, since it's simpler.\n\nTo begin, I've made things easy by introducing a new recognizer:\n\n```delphi\n{--------------------------------------------------------------}\n{ Match a Semicolon }\n\nprocedure Semi;\nbegin\n MatchString(';');\nend;\n{--------------------------------------------------------------}\n```\n\nThis procedure works very much like our old Match. It insists on\nfinding a semicolon as the next token. Having found it, it skips\nto the next one.\n\nSince a semicolon follows a statement, procedure Block is almost\nthe only one we need to change:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n Scan;\n while not(Token in ['e', 'l']) do begin\n case Token of\n 'i': DoIf;\n 'w': DoWhile;\n 'R': DoRead;\n 'W': DoWrite;\n 'x': Assignment;\n end;\n Semi;\n Scan;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNote carefully the subtle change in the case statement. The call\nto Assignment is now guarded by a test on Token. This is to\navoid calling Assignment when the token is a semicolon (which\ncould happen if the statement is null).\n\nSince declarations are also statements, we also need to add a\ncall to Semi within procedure TopDecls:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n Scan;\n while Token = 'v' do begin\n Alloc;\n while Token = ',' do\n Alloc;\n Semi;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nFinally, we need one for the PROGRAM statement:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n MatchString('PROGRAM');\n Semi;\n Header;\n TopDecls;\n MatchString('BEGIN');\n Prolog;\n Block;\n MatchString('END');\n Epilog;\nend.\n{--------------------------------------------------------------}\n```\n\nIt's as easy as that. Try it with a copy of TINY and see how you\nlike it.\n\nThe Pascal version is a little trickier, but it still only\nrequires minor changes, and those only to procedure Block. To\nkeep things as simple as possible, let's split the procedure into\ntwo parts. The following procedure handles just one statement:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Single Statement }\n\nprocedure Statement;\nbegin\n Scan;\n case Token of\n 'i': DoIf;\n 'w': DoWhile;\n 'R': DoRead;\n 'W': DoWrite;\n 'x': Assignment;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nUsing this procedure, we can now rewrite Block like this:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n Statement;\n while Token = ';' do begin\n Next;\n Statement;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nThat sure didn't hurt, did it? We can now parse semicolons in\nPascal-like fashion."},{title:"A COMPROMISE",path:"/a-compromise",content:"Now that we know how to deal with semicolons, does that mean that\nI'm going to put them in KISS/TINY? Well, yes and no. I like\nthe extra sugar and the security that comes with knowing for sure\nwhere the ends of statements are. But I haven't changed my\ndislike for the compilation errors associated with semicolons.\n\nSo I have what I think is a nice compromise: Make them OPTIONAL!\n\nConsider the following version of Semi:\n\n```delphi\n{--------------------------------------------------------------}\n{ Match a Semicolon }\n\nprocedure Semi;\nbegin\n if Token = ';' then Next;\nend;\n{--------------------------------------------------------------}\n```\n\nThis procedure will ACCEPT a semicolon whenever it is called, but\nit won't INSIST on one. That means that when you choose to use\nsemicolons, the compiler will use the extra information to help\nkeep itself on track. But if you omit one (or omit them all) the\ncompiler won't complain. The best of both worlds.\n\nPut this procedure in place in the first version of your program\n(the one for C/Ada syntax), and you have the makings of TINY\nVersion 1.2."},{title:"COMMENTS",path:"/comments",content:"Up until now I have carefully avoided the subject of comments.\nYou would think that this would be an easy subject ... after all,\nthe compiler doesn't have to deal with comments at all; it should\njust ignore them. Well, sometimes that's true.\n\nComments can be just about as easy or as difficult as you choose\nto make them. At one extreme, we can arrange things so that\ncomments are intercepted almost the instant they enter the\ncompiler. At the other, we can treat them as lexical elements.\nThings tend to get interesting when you consider things like\ncomment delimiters contained in quoted strings."},{title:"SINGLE-CHARACTER DELIMITERS",path:"/single-character-delimiters",content:"Here's an example. Suppose we assume the Turbo Pascal standard\nand use curly braces for comments. In this case we have single-\ncharacter delimiters, so our parsing is a little easier.\n\nOne approach is to strip the comments out the instant we\nencounter them in the input stream; that is, right in procedure\nGetChar. To do this, first change the name of GetChar to\nsomething else, say GetCharX. (For the record, this is going to\nbe a TEMPORARY change, so best not do this with your only copy of\nTINY. I assume you understand that you should always do these\nexperiments with a working copy.)\n\nNow, we're going to need a procedure to skip over comments. So\nkey in the following one:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip A Comment Field }\n\nprocedure SkipComment;\nbegin\n while Look <> '}' do\n GetCharX;\n GetCharX;\nend;\n{--------------------------------------------------------------}\n\n```\nClearly, what this procedure is going to do is to simply read and\ndiscard characters from the input stream, until it finds a right\ncurly brace. Then it reads one more character and returns it in\nLook.\n\nNow we can write a new version of GetChar that SkipComment to\nstrip out comments:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get Character from Input Stream }\n{ Skip Any Comments }\n\nprocedure GetChar;\nbegin\n GetCharX;\n if Look = '{' then SkipComment;\nend;\n{--------------------------------------------------------------}\n```\n\nCode this up and give it a try. You'll find that you can,\nindeed, bury comments anywhere you like. The comments never even\nget into the parser proper ... every call to GetChar just returns\nany character that's NOT part of a comment.\n\nAs a matter of fact, while this approach gets the job done, and\nmay even be perfectly satisfactory for you, it does its job a\nlittle TOO well. First of all, most programming languages\nspecify that a comment should be treated like a space, so that\ncomments aren't allowed to be embedded in, say, variable names.\nThis current version doesn't care WHERE you put comments.\n\nSecond, since the rest of the parser can't even receive a '{'\ncharacter, you will not be allowed to put one in a quoted string.\n\nBefore you turn up your nose at this simplistic solution, though,\nI should point out that as respected a compiler as Turbo Pascal\nalso won't allow a '{' in a quoted string. Try it. And as for\nembedding a comment in an identifier, I can't imagine why anyone\nwould want to do such a thing, anyway, so the question is moot.\nFor 99% of all applications, what I've just shown you will work\njust fine.\n\nBut, if you want to be picky about it and stick to the\nconventional treatment, then we need to move the interception\npoint downstream a little further.\n\nTo do this, first change GetChar back to the way it was and\nchange the name called in SkipComment. Then, let's add the left\nbrace as a possible whitespace character:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB, CR, LF, '{'];\nend;\n{--------------------------------------------------------------}\n```\n\nNow, we can deal with comments in procedure SkipWhite:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do begin\n if Look = '{' then\n SkipComment\n else\n GetChar;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNote that SkipWhite is written so that we will skip over any\ncombination of whitespace characters and comments, in one call.\n\nOK, give this one a try, too. You'll find that it will let a\ncomment serve to delimit tokens. It's worth mentioning that this\napproach also gives us the ability to handle curly braces within\nquoted strings, since within such strings we will not be testing\nfor or skipping over whitespace.\n\nThere's one last item to deal with: Nested comments. Some\nprogrammers like the idea of nesting comments, since it allows\nyou to comment out code during debugging. The code I've given\nhere won't allow that and, again, neither will Turbo Pascal.\n\nBut the fix is incredibly easy. All we need to do is to make\nSkipComment recursive:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip A Comment Field }\n\nprocedure SkipComment;\nbegin\n while Look <> '}' do begin\n GetChar;\n if Look = '{' then SkipComment;\n end;\n GetChar;\nend;\n{--------------------------------------------------------------}\n```\n\nThat does it. As sophisticated a comment-handler as you'll ever\nneed."},{title:"MULTI-CHARACTER DELIMITERS",path:"/multi-character-delimiters",content:"That's all well and good for cases where a comment is delimited\nby single characters, but what about the cases such as C or\nstandard Pascal, where two characters are required? Well, the\nprinciples are still the same, but we have to change our approach\nquite a bit. I'm sure it won't surprise you to learn that things\nget harder in this case.\n\nFor the multi-character situation, the easiest thing to do is to\nintercept the left delimiter back at the GetChar stage. We can\n\"tokenize\" it right there, replacing it by a single character.\n\nLet's assume we're using the C delimiters '/*' and '*/'. First,\nwe need to go back to the \"GetCharX' approach. In yet another\ncopy of your compiler, rename GetChar to GetCharX and then enter\nthe following new procedure GetChar:\n\n```delphi\n{--------------------------------------------------------------}\n{ Read New Character. Intercept '/*' }\n\nprocedure GetChar;\nbegin\n if TempChar <> ' ' then begin\n Look := TempChar;\n TempChar := ' ';\n end\n else begin\n GetCharX;\n if Look = '/' then begin\n Read(TempChar);\n if TempChar = '*' then begin\n Look := '{';\n TempChar := ' ';\n end;\n end;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nAs you can see, what this procedure does is to intercept every\noccurrence of '/'. It then examines the NEXT character in the\nstream. If the character is a '*', then we have found the\nbeginning of a comment, and GetChar will return a single\ncharacter replacement for it. (For simplicity, I'm using the\nsame '{' character as I did for Pascal. If you were writing a C\ncompiler, you'd no doubt want to pick some other character that's\nnot used elsewhere in C. Pick anything you like ... even $FF,\nanything that's unique.)\n\nIf the character following the '/' is NOT a '*', then GetChar\ntucks it away in the new global TempChar, and returns the '/'.\n\nNote that you need to declare this new variable and initialize it\nto ' '. I like to do things like that using the Turbo \"typed\nconstant\" construct:\n\n```delphi\n const TempChar: char = ' ';\n\n```\nNow we need a new version of SkipComment:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip A Comment Field }\n\nprocedure SkipComment;\nbegin\n repeat\n repeat\n GetCharX;\n until Look = '*';\n GetCharX;\n until Look = '/';\n GetChar;\nend;\n{--------------------------------------------------------------}\n```\n\nA few things to note: first of all, function IsWhite and\nprocedure SkipWhite don't need to be changed, since GetChar\nreturns the '{' token. If you change that token character, then\nof course you also need to change the character in those two\nroutines.\n\nSecond, note that SkipComment doesn't call GetChar in its loop,\nbut GetCharX. That means that the trailing '/' is not\nintercepted and is seen by SkipComment. Third, although GetChar\nis the procedure doing the work, we can still deal with the\ncomment characters embedded in a quoted string, by calling\nGetCharX instead of GetChar while we're within the string.\nFinally, note that we can again provide for nested comments by\nadding a single statement to SkipComment, just as we did before."},{title:"ONE-SIDED COMMENTS",path:"/one-sided-comments",content:"So far I've shown you how to deal with any kind of comment\ndelimited on the left and the right. That only leaves the one-\nsided comments like those in assembler language or in Ada, that\nare terminated by the end of the line. In a way, that case is\neasier. The only procedure that would need to be changed is\nSkipComment, which must now terminate at the newline characters:\n\n```delphi\n{--------------------------------------------------------------}\n{ Skip A Comment Field }\n\nprocedure SkipComment;\nbegin\n repeat\n GetCharX;\n until Look = CR;\n GetChar;\nend;\n{--------------------------------------------------------------}\n```\n\nIf the leading character is a single one, as in the ';' of\nassembly language, then we're essentially done. If it's a two-\ncharacter token, as in the '--' of Ada, we need only modify the\ntests within GetChar. Either way, it's an easier problem than\nthe balanced case."},{title:"CONCLUSION",path:"/conclusion",content:"At this point we now have the ability to deal with both comments\nand semicolons, as well as other kinds of syntactic sugar. I've\nshown you several ways to deal with each, depending upon the\nconvention desired. The only issue left is: which of these\nconventions should we use in KISS/TINY?\n\nFor the reasons that I've given as we went along, I'm choosing\nthe following:\n\n\n (1) Semicolons are TERMINATORS, not separators\n\n (2) Semicolons are OPTIONAL\n\n (3) Comments are delimited by curly braces\n\n (4) Comments MAY be nested\n\n\nPut the code corresponding to these cases into your copy of TINY.\nYou now have TINY Version 1.2.\n\nNow that we have disposed of these sideline issues, we can\nfinally get back into the mainstream. In the next installment,\nwe'll talk about procedures and parameter passing, and we'll add\nthese important features to TINY. See you then."}]},{title:"Part XIII: PROCEDURES - 27 August 1989",path:"/part-xiii-procedures-27-august-1989",items:[{title:"INTRODUCTION",path:"/introduction",content:"At last we get to the good part!\n\nAt this point we've studied almost all the basic features of\ncompilers and parsing. We have learned how to translate\narithmetic expressions, Boolean expressions, control constructs,\ndata declarations, and I/O statements. We have defined a\nlanguage, TINY 1.3, that embodies all these features, and we have\nwritten a rudimentary compiler that can translate them. By\nadding some file I/O we could indeed have a working compiler that\ncould produce executable object files from programs written in\nTINY. With such a compiler, we could write simple programs that\ncould read integer data, perform calculations with it, and output\nthe results.\n\nThat's nice, but what we have is still only a toy language. We\ncan't read or write even a single character of text, and we still\ndon't have procedures.\n\nIt's the features to be discussed in the next couple of\ninstallments that separate the men from the toys, so to speak.\n\"Real\" languages have more than one data type, and they support\nprocedure calls. More than any others, it's these two features\nthat give a language much of its character and personality. Once\nwe have provided for them, our languages, TINY and its\nsuccessors, will cease to become toys and will take on the\ncharacter of real languages, suitable for serious programming\njobs.\n\nFor several installments now, I've been promising you sessions on\nthese two important subjects. Each time, other issues came up\nthat required me to digress and deal with them. Finally, we've\nbeen able to put all those issues to rest and can get on with the\nmainstream of things. In this installment, I'll cover\nprocedures. Next time, we'll talk about the basic data types."},{title:"ONE LAST DIGRESSION",path:"/one-last-digression",content:"This has been an extraordinarily difficult installment for me to\nwrite. The reason has nothing to do with the subject itself ...\nI've known what I wanted to say for some time, and in fact I\npresented most of this at Software Development '89, back in\nFebruary. It has more to do with the approach. Let me explain.\n\nWhen I first began this series, I told you that we would use\nseveral \"tricks\" to make things easy, and to let us learn the\nconcepts without getting too bogged down in the details. Among\nthese tricks was the idea of looking at individual pieces of a\ncompiler at a time, i.e. performing experiments using the Cradle\nas a base. When we studied expressions, for example, we dealt\nwith only that part of compiler theory. When we studied control\nstructures, we wrote a different program, still based on the\nCradle, to do that part. We only incorporated these concepts into\na complete language fairly recently. These techniques have served\nus very well indeed, and led us to the development of a compiler\nfor TINY version 1.3.\n\nWhen I first began this session, I tried to build upon what we\nhad already done, and just add the new features to the existing\ncompiler. That turned out to be a little awkward and tricky ...\nmuch too much to suit me.\n\nI finally figured out why. In this series of experiments, I had\nabandoned the very useful techniques that had allowed us to get\nhere, and without meaning to I had switched over into a new\nmethod of working, that involved incremental changes to the full\nTINY compiler.\n\nYou need to understand that what we are doing here is a little\nunique. There have been a number of articles, such as the Small\nC articles by Cain and Hendrix, that presented finished compilers\nfor one language or another. This is different. In this series\nof tutorials, you are watching me design and implement both a\nlanguage and a compiler, in real time.\n\nIn the experiments that I've been doing in preparation for this\narticle, I was trying to inject the changes into the TINY\ncompiler in such a way that, at every step, we still had a real,\nworking compiler. In other words, I was attempting an\nincremental enhancement of the language and its compiler, while\nat the same time explaining to you what I was doing.\n\nThat's a tough act to pull off! I finally realized that it was\ndumb to try. Having gotten this far using the idea of small\nexperiments based on single-character tokens and simple,\nspecial-purpose programs, I had abandoned them in favor of\nworking with the full compiler. It wasn't working.\n\nSo we're going to go back to our roots, so to speak. In this\ninstallment and the next, I'll be using single-character tokens\nagain as we study the concepts of procedures, unfettered by the\nother baggage that we have accumulated in the previous sessions.\nAs a matter of fact, I won't even attempt, at the end of this\nsession, to merge the constructs into the TINY compiler. We'll\nsave that for later.\n\nAfter all this time, you don't need more buildup than that, so\nlet's waste no more time and dive right in."},{title:"THE BASICS",path:"/the-basics",content:"All modern CPU's provide direct support for procedure calls, and\nthe 68000 is no exception. For the 68000, the call is a BSR\n(PC-relative version) or JSR, and the return is RTS. All we have\nto do is to arrange for the compiler to issue these commands at\nthe proper place.\n\nActually, there are really THREE things we have to address. One\nof them is the call/return mechanism. The second is the\nmechanism for DEFINING the procedure in the first place. And,\nfinally, there is the issue of passing parameters to the called\nprocedure. None of these things are really very difficult, and\nwe can of course borrow heavily on what people have done in other\nlanguages ... there's no need to reinvent the wheel here. Of the\nthree issues, that of parameter passing will occupy most of our\nattention, simply because there are so many options available."},{title:"A BASIS FOR EXPERIMENTS",path:"/a-basis-for-experiments",content:"As always, we will need some software to serve as a basis for\nwhat we are doing. We don't need the full TINY compiler, but we\ndo need enough of a program so that some of the other constructs\nare present. Specifically, we need at least to be able to handle\nstatements of some sort, and data declarations.\n\nThe program shown below is that basis. It's a vestigial form of\nTINY, with single-character tokens. It has data declarations,\nbut only in their simplest form ... no lists or initializers. It\nhas assignment statements, but only of the kind\n```\n <ident> = <ident>\n```\nIn other words, the only legal expression is a single variable\nname. There are no control constructs ... the only legal\nstatement is the assignment.\n\nMost of the program is just the standard Cradle routines. I've\nshown the whole thing here, just to make sure we're all starting\nfrom the same point:\n\n```delphi\n{--------------------------------------------------------------}\nprogram Calls;\n\n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n CR = ^M;\n LF = ^J;\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look: char; { Lookahead Character }\n\nvar ST: Array['A'..'Z'] of char;\n\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n\nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report an Undefined Identifier }\n\nprocedure Undefined(n: string);\nbegin\n Abort('Undefined Identifier ' + n);\nend;\n\n\n{--------------------------------------------------------------}\n{ Report an Duplicate Identifier }\n\nprocedure Duplicate(n: string);\nbegin\n Abort('Duplicate Identifier ' + n);\nend;\n\n\n{--------------------------------------------------------------}\n{ Get Type of Symbol }\n\nfunction TypeOf(n: char): char;\nbegin\n TypeOf := ST[n];\nend;\n\n\n{--------------------------------------------------------------}\n{ Look for Symbol in Table }\n\nfunction InTable(n: char): Boolean;\nbegin\n InTable := ST[n] <> ' ';\nend;\n\n\n{--------------------------------------------------------------}\n{ Add a New Symbol to Table }\n\nprocedure AddEntry(Name, T: char);\nbegin\n if Intable(Name) then Duplicate(Name);\n ST[Name] := T;\nend;\n\n\n{--------------------------------------------------------------}\n{ Check an Entry to Make Sure It's a Variable }\n\nprocedure CheckVar(Name: char);\nbegin\n if not InTable(Name) then Undefined(Name);\n if TypeOf(Name) <> 'v' then Abort(Name + ' is not a\nvariable');\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := upcase(c) in ['A'..'Z'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an AlphaNumeric Character }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Mulop }\n\nfunction IsMulop(c: char): boolean;\nbegin\n IsMulop := c in ['*', '/'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Boolean Orop }\n\nfunction IsOrop(c: char): boolean;\nbegin\n IsOrop := c in ['|', '~'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Relop }\n\nfunction IsRelop(c: char): boolean;\nbegin\n IsRelop := c in ['=', '#', '<', '>'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB];\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over an End-of-Line }\n\nprocedure Fin;\nbegin\n if Look = CR then begin\n GetChar;\n if Look = LF then\n GetChar;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n if Look = x then GetChar\n else Expected('''' + x + '''');\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: char;\nbegin\n if not IsAlpha(Look) then Expected('Name');\n GetName := UpCase(Look);\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: char;\nbegin\n if not IsDigit(Look) then Expected('Integer');\n GetNum := Look;\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n\nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n\n{--------------------------------------------------------------}\n{ Post a Label To Output }\n\nprocedure PostLabel(L: string);\nbegin\n WriteLn(L, ':');\nend;\n\n\n{--------------------------------------------------------------}\n{ Load a Variable to the Primary Register }\n\nprocedure LoadVar(Name: char);\nbegin\n CheckVar(Name);\n EmitLn('MOVE ' + Name + '(PC),D0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Store the Primary Register }\n\nprocedure StoreVar(Name: char);\nbegin\n CheckVar(Name);\n EmitLn('LEA ' + Name + '(PC),A0');\n EmitLn('MOVE D0,(A0)')\nend;\n\n\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nvar i: char;\nbegin\n GetChar;\n SkipWhite;\n for i := 'A' to 'Z' do\n ST[i] := ' ';\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Expression }\n{ Vestigial Version }\n\nprocedure Expression;\nbegin\n LoadVar(GetName);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := GetName;\n Match('=');\n Expression;\n StoreVar(Name);\nend;\n\n\n{--------------------------------------------------------------}\n\n \n\n\n\n\n\n\n{ Parse and Translate a Block of Statements }\n\nprocedure DoBlock;\nbegin\n while not(Look in ['e']) do begin\n Assignment;\n Fin;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Begin-Block }\n\nprocedure BeginBlock;\nbegin\n Match('b');\n Fin;\n DoBlock;\n Match('e');\n Fin;\nend;\n\n\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc(N: char);\nbegin\n if InTable(N) then Duplicate(N);\n ST[N] := 'v';\n WriteLn(N, ':', TAB, 'DC 0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Data Declaration }\n\nprocedure Decl;\nvar Name: char;\nbegin\n Match('v');\n Alloc(GetName);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n while Look <> 'b' do begin\n case Look of\n 'v': Decl;\n else Abort('Unrecognized Keyword ' + Look);\n end;\n Fin;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n TopDecls;\n BeginBlock;\nend.\n{--------------------------------------------------------------}\n\n```\nNote that we DO have a symbol table, and there is logic to check\na variable name to make sure it's a legal one. It's also worth\nnoting that I have included the code you've seen before to\nprovide for white space and newlines. Finally, note that the\nmain program is delimited, as usual, by BEGIN-END brackets.\n\nOnce you've copied the program to Turbo, the first step is to\ncompile it and make sure it works. Give it a few declarations,\nand then a begin-block. Try something like:\n```\n va (for VAR A)\n vb (for VAR B)\n vc (for VAR C)\n b (for BEGIN)\n a=b\n b=c\n e. (for END.)\n```\n\nAs usual, you should also make some deliberate errors, and verify\nthat the program catches them correctly."},{title:"DECLARING A PROCEDURE",path:"/declaring-a-procedure",content:"If you're satisfied that our little program works, then it's time\nto deal with the procedures. Since we haven't talked about parameters yet, we'll begin by considering only procedures that\nhave no parameter lists.\n\nAs a start, let's consider a simple program with a procedure, and\nthink about the code we'd like to see generated for it:\n\n```delphi\n PROGRAM FOO;\n .\n .\n PROCEDURE BAR; BAR:\n BEGIN .\n . .\n . .\n END; RTS\n\n BEGIN { MAIN PROGRAM } MAIN:\n . .\n . .\n FOO; BSR BAR\n . .\n . .\n END. END MAIN\n\n```\nHere I've shown the high-order language constructs on the left,\nand the desired assembler code on the right. The first thing to\nnotice is that we certainly don't have much code to generate\nhere! For the great bulk of both the procedure and the main\nprogram, our existing constructs take care of the code to be\ngenerated.\n\nThe key to dealing with the body of the procedure is to recognize\nthat although a procedure may be quite long, declaring it is\nreally no different than declaring a variable. It's just one\nmore kind of declaration. We can write the BNF:\n\n```delphi\n <declaration> ::= <data decl> | <procedure>\n```\n\nThis means that it should be easy to modify TopDecl to deal with\nprocedures. What about the syntax of a procedure? Well, here's\na suggested syntax, which is essentially that of Pascal:\n\n```delphi\n <procedure> ::= PROCEDURE <ident> <begin-block>\n```\n\nThere is practically no code generation required, other than that\ngenerated within the begin-block. We need only emit a label at\nthe beginning of the procedure, and an RTS at the end.\n\nHere's the required code:\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Procedure Declaration }\n\nprocedure DoProc;\nvar N: char;\nbegin\n Match('p');\n N := GetName;\n Fin;\n if InTable(N) then Duplicate(N);\n ST[N] := 'p';\n PostLabel(N);\n BeginBlock;\n Return;\nend;\n{--------------------------------------------------------------}\n```\n\nNote that I've added a new code generation routine, Return, which\nmerely emits an RTS instruction. The creation of that routine is\n\"left as an exercise for the student.\"\n\nTo finish this version, add the following line within the Case\nstatement in DoBlock:\n\n```delphi\n 'p': DoProc;\n```\n\nI should mention that this structure for declarations, and the\nBNF that drives it, differs from standard Pascal. In the Jensen\n& Wirth definition of Pascal, variable declarations, in fact ALL\nkinds of declarations, must appear in a specific sequence, i.e.\nlabels, constants, types, variables, procedures, and main\nprogram. To follow such a scheme, we should separate the two\ndeclarations, and have code in the main program something like\n\n```delphi\n DoVars;\n DoProcs;\n DoMain;\n```\n\nHowever, most implementations of Pascal, including Turbo, don't\nrequire that order and let you freely mix up the various\ndeclarations, as long as you still don't try to refer to\nsomething before it's declared. Although it may be more\naesthetically pleasing to declare all the global variables at the\ntop of the program, it certainly doesn't do any HARM to allow\nthem to be sprinkled around. In fact, it may do some GOOD, in\nthe sense that it gives you the opportunity to do a little\nrudimentary information hiding. Variables that should be\naccessed only by the main program, for example, can be declared\njust before it and will thus be inaccessible by the procedures.\n\nOK, try this new version out. Note that we can declare as many\nprocedures as we choose (as long as we don't run out of single-\ncharacter names!), and the labels and RTS's all come out in the\nright places.\n\nIt's worth noting here that I do _NOT_ allow for nested\nprocedures. In TINY, all procedures must be declared at the\nglobal level, the same as in C. There has been quite a\ndiscussion about this point in the Computer Language Forum of\nCompuServe. It turns out that there is a significant penalty in\ncomplexity that must be paid for the luxury of nested procedures.\nWhat's more, this penalty gets paid at RUN TIME, because extra\ncode must be added and executed every time a procedure is called.\nI also happen to believe that nesting is not a good idea, simply\non the grounds that I have seen too many abuses of the feature.\nBefore going on to the next step, it's also worth noting that the\n\"main program\" as it stands is incomplete, since it doesn't have\nthe label and END statement. Let's fix that little oversight:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Main Program }\n\nprocedure DoMain;\nbegin\n Match('b');\n Fin;\n Prolog;\n DoBlock;\n Epilog;\nend;\n{--------------------------------------------------------------}\n.\n.\n.\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n TopDecls;\n DoMain;\nend.\n{--------------------------------------------------------------}\n```\n\nNote that DoProc and DoMain are not quite symmetrical. DoProc\nuses a call to BeginBlock, whereas DoMain cannot. That's because\na procedure is signaled by the keyword PROCEDURE (abbreviated by\na 'p' here), while the main program gets no keyword other than\nthe BEGIN itself.\n\nAnd _THAT_ brings up an interesting question: WHY?\n\nIf we look at the structure of C programs, we find that all\nfunctions are treated just alike, except that the main program\nhappens to be identified by its name, \"main.\" Since C functions\ncan appear in any order, the main program can also be anywhere in\nthe compilation unit.\n\nIn Pascal, on the other hand, all variables and procedures must\nbe declared before they're used, which means that there is no\npoint putting anything after the main program ... it could never\nbe accessed. The \"main program\" is not identified at all, other\nthan being that part of the code that comes after the global\nBEGIN. In other words, if it ain't anything else, it must be the\nmain program.\n\nThis causes no small amount of confusion for beginning\nprogrammers, and for big Pascal programs sometimes it's difficult\nto find the beginning of the main program at all. This leads to\nconventions such as identifying it in comments:\n\n```delphi\n BEGIN { of MAIN }\n\n```\nThis has always seemed to me to be a bit of a kludge. The\nquestion comes up: Why should the main program be treated so\nmuch differently than a procedure? In fact, now that we've\nrecognized that procedure declarations are just that ... part of\nthe global declarations ... isn't the main program just one more\ndeclaration, also?\n\nThe answer is yes, and by treating it that way, we can simplify\nthe code and make it considerably more orthogonal. I propose\nthat we use an explicit keyword, PROGRAM, to identify the main\nprogram (Note that this means that we can't start the file with\nit, as in Pascal). In this case, our BNF becomes:\n\n```\n <declaration> ::= <data decl> | <procedure> | <main program>\n\n\n <procedure> ::= PROCEDURE <ident> <begin-block>\n\n\n <main program> ::= PROGRAM <ident> <begin-block>\n```\n\nThe code also looks much better, at least in the sense that\nDoMain and DoProc look more alike:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Main Program }\n\nprocedure DoMain;\nvar N: char;\nbegin\n Match('P');\n N := GetName;\n Fin;\n if InTable(N) then Duplicate(N);\n Prolog;\n BeginBlock;\nend;\n{--------------------------------------------------------------}\n.\n.\n.\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n while Look <> '.' do begin\n case Look of\n 'v': Decl;\n 'p': DoProc;\n 'P': DoMain;\n else Abort('Unrecognized Keyword ' + Look);\n end;\n Fin;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n TopDecls;\n Epilog;\nend.\n{--------------------------------------------------------------}\n```\n\nSince the declaration of the main program is now within the loop\nof TopDecl, that does present some difficulties. How do we\nensure that it's the last thing in the file? And how do we ever\nexit from the loop? My answer for the second question, as you\ncan see, was to bring back our old friend the period. Once the\nparser sees that, we're done.\n\nTo answer the first question: it depends on how far we're\nwilling to go to protect the programmer from dumb mistakes. In\nthe code that I've shown, there's nothing to keep the programmer\nfrom adding code after the main program ... even another main\nprogram. The code will just not be accessible. However, we\nCOULD access it via a FORWARD statement, which we'll be providing\nlater. As a matter of fact, many assembler language programmers\nlike to use the area just after the program to declare large,\nuninitialized data blocks, so there may indeed be some value in\nnot requiring the main program to be last. We'll leave it as it\nis.\n\nIf we decide that we should give the programmer a little more\nhelp than that, it's pretty easy to add some logic to kick us out\nof the loop once the main program has been processed. Or we\ncould at least flag an error if someone tries to include two\nmains."},{title:"CALLING THE PROCEDURE",path:"/calling-the-procedure",content:"If you're satisfied that things are working, let's address the\nsecond half of the equation ... the call.\n\nConsider the BNF for a procedure call:\n\n```\n <proc_call> ::= <identifier>\n```\n\nfor an assignment statement, on the other hand, the BNF is:\n\n```\n <assignment> ::= <identifier> '=' <expression>\n\n```\nAt this point we seem to have a problem. The two BNF statements\nboth begin on the right-hand side with the token `<identifier>`.\nHow are we supposed to know, when we see the identifier, whether\nwe have a procedure call or an assignment statement? This looks\nlike a case where our parser ceases being predictive, and indeed\nthat's exactly the case. However, it turns out to be an easy\nproblem to fix, since all we have to do is to look at the type of\nthe identifier, as recorded in the symbol table. As we've\ndiscovered before, a minor local violation of the predictive\nparsing rule can be easily handled as a special case.\n\nHere's how to do it:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment(Name: char);\nbegin\n Match('=');\n Expression;\n StoreVar(Name);\nend;\n\n\n{--------------------------------------------------------------}\n{ Decide if a Statement is an Assignment or Procedure Call }\n\nprocedure AssignOrProc;\nvar Name: char;\nbegin\n Name := GetName;\n case TypeOf(Name) of\n ' ': Undefined(Name);\n 'v': Assignment(Name);\n 'p': CallProc(Name);\n else Abort('Identifier ' + Name +\n ' Cannot Be Used Here');\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure DoBlock;\nbegin\n while not(Look in ['e']) do begin\n AssignOrProc;\n Fin;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nAs you can see, procedure Block now calls AssignOrProc instead of\nAssignment. The function of this new procedure is to simply read\nthe identifier, determine its type, and then call whichever\nprocedure is appropriate for that type. Since the name has\nalready been read, we must pass it to the two procedures, and\nmodify Assignment to match. Procedure CallProc is a simple code\ngeneration routine:\n\n```delphi\n{--------------------------------------------------------------}\n{ Call a Procedure }\n\nprocedure CallProc(N: char);\nbegin\n EmitLn('BSR ' + N);\nend;\n{--------------------------------------------------------------}\n```\n\nWell, at this point we have a compiler that can deal with\nprocedures. It's worth noting that procedures can call\nprocedures to any depth. So even though we don't allow nested\nDECLARATIONS, there is certainly nothing to keep us from nesting\nCALLS, just as we would expect to do in any language. We're\ngetting there, and it wasn't too hard, was it?\n\nOf course, so far we can only deal with procedures that have no\nparameters. The procedures can only operate on the global\nvariables by their global names. So at this point we have the\nequivalent of BASIC's GOSUB construct. Not too bad ... after all\nlots of serious programs were written using GOSUBs, but we can do\nbetter, and we will. That's the next step."},{title:"PASSING PARAMETERS",path:"/passing-parameters",content:"Again, we all know the basic idea of passed parameters, but let's\nreview them just to be safe.\n\nIn general the procedure is given a parameter list, for example\n```\n PROCEDURE FOO(X, Y, Z)\n```\nIn the declaration of a procedure, the parameters are called\nformal parameters, and may be referred to in the body of the\nprocedure by those names. The names used for the formal\nparameters are really arbitrary. Only the position really\ncounts. In the example above, the name 'X' simply means \"the\nfirst parameter\" wherever it is used.\n\nWhen a procedure is called, the \"actual parameters\" passed to it\nare associated with the formal parameters, on a one-for-one\nbasis.\n\nThe BNF for the syntax looks something like this:\n\n```\n <procedure> ::= PROCEDURE <ident>\n '(' <param-list> ')' <begin-block>\n\n\n <param_list> ::= <parameter> ( ',' <parameter> )* | null\n```\nSimilarly, the procedure call looks like:\n\n```\n <proc call> ::= <ident> '(' <param-list> ')'\n\n```\nNote that there is already an implicit decision built into this\nsyntax. Some languages, such as Pascal and Ada, permit parameter\nlists to be optional. If there are no parameters, you simply\nleave off the parens completely. Other languages, like C and\nModula 2, require the parens even if the list is empty. Clearly,\nthe example we just finished corresponds to the former point of\nview. But to tell the truth I prefer the latter. For procedures\nalone, the decision would seem to favor the \"listless\" approach.\nThe statement\n\n```delphi\n Initialize; ,\n\n```\nstanding alone, can only mean a procedure call. In the parsers\nwe've been writing, we've made heavy use of parameterless\nprocedures, and it would seem a shame to have to write an empty\npair of parens for each case.\n\nBut later on we're going to be using functions, too. And since\nfunctions can appear in the same places as simple scalar\nidentifiers, you can't tell the difference between the two. You\nhave to go back to the declarations to find out. Some folks\nconsider this to be an advantage. Their argument is that an\nidentifier gets replaced by a value, and what do you care whether\nit's done by substitution or by a function? But we sometimes\n_DO_ care, because the function may be quite time-consuming. If,\nby writing a simple identifier into a given expression, we can\nincur a heavy run-time penalty, it seems to me we ought to be\nmade aware of it.\n\nAnyway, Niklaus Wirth designed both Pascal and Modula 2. I'll\ngive him the benefit of the doubt and assume that he had a good\nreason for changing the rules the second time around!\n\nNeedless to say, it's an easy thing to accomodate either point of\nview as we design a language, so this one is strictly a matter of\npersonal preference. Do it whichever way you like best.\n\nBefore we go any further, let's alter the translator to handle a\n(possibly empty) parameter list. For now we won't generate any\nextra code ... just parse the syntax. The code for processing\nthe declaration has very much the same form we've seen before\nwhen dealing with VAR-lists:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process the Formal Parameter List of a Procedure }\n\nprocedure FormalList;\nbegin\n Match('(');\n if Look <> ')' then begin\n FormalParam;\n while Look = ',' do begin\n Match(',');\n FormalParam;\n end;\n end;\n Match(')');\nend;\n{--------------------------------------------------------------}\n```\n\nProcedure DoProc needs to have a line added to call FormalList:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Procedure Declaration }\n\nprocedure DoProc;\nvar N: char;\nbegin\n Match('p');\n N := GetName;\n FormalList;\n Fin;\n if InTable(N) then Duplicate(N);\n ST[N] := 'p';\n PostLabel(N);\n BeginBlock;\n Return;\nend;\n{--------------------------------------------------------------}\n```\n\nFor now, the code for FormalParam is just a dummy one that simply\nskips the parameter name:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process a Formal Parameter }\n\nprocedure FormalParam;\nvar Name: char;\nbegin\n Name := GetName;\nend;\n{--------------------------------------------------------------}\n```\n\nFor the actual procedure call, there must be similar code to\nprocess the actual parameter list:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process an Actual Parameter }\n\nprocedure Param;\nvar Name: char;\nbegin\n Name := GetName;\nend;\n\n\n{--------------------------------------------------------------}\n{ Process the Parameter List for a Procedure Call }\n\nprocedure ParamList;\nbegin\n Match('(');\n if Look <> ')' then begin\n Param;\n while Look = ',' do begin\n Match(',');\n Param;\n end;\n end;\n Match(')');\nend;\n\n\n{--------------------------------------------------------------}\n{ Process a Procedure Call }\n\nprocedure CallProc(Name: char);\nbegin\n ParamList;\n Call(Name);\nend;\n{--------------------------------------------------------------}\n```\n\nNote here that `CallProc` is no longer just a simple code\ngeneration routine. It has some structure to it. To handle\nthis, I've renamed the code generation routine to just Call, and\ncalled it from within `CallProc`.\n\nOK, if you'll add all this code to your translator and try it\nout, you'll find that you can indeed parse the syntax properly.\nI'll note in passing that there is _NO_ checking to make sure\nthat the number (and, later, types) of formal and actual\nparameters match up. In a production compiler, we must of course\ndo this. We'll ignore the issue now if for no other reason than\nthat the structure of our symbol table doesn't currently give us\na place to store the necessary information. Later on, we'll have\na place for that data and we can deal with the issue then."},{title:"THE SEMANTICS OF PARAMETERS",path:"/the-semantics-of-parameters",content:"So far we've dealt with the SYNTAX of parameter passing, and\nwe've got the parsing mechanisms in place to handle it. Next, we\nhave to look at the SEMANTICS, i.e., the actions to be taken when\nwe encounter parameters. This brings us square up against the\nissue of the different ways parameters can be passed.\n\nThere is more than one way to pass a parameter, and the way we do\nit can have a profound effect on the character of the language.\nSo this is another of those areas where I can't just give you my\nsolution. Rather, it's important that we spend some time looking\nat the alternatives so that you can go another route if you\nchoose to.\n\nThere are two main ways parameters are passed:\n\n - By value\n - By reference (address)\n\nThe differences are best seen in the light of a little history.\n\nThe old FORTRAN compilers passed all parameters by reference. In\nother words, what was actually passed was the address of the\nparameter. This meant that the called subroutine was free to\neither read or write that parameter, as often as it chose to,\njust as though it were a global variable. This was actually\nquite an efficient way to do things, and it was pretty simple\nsince the same mechanism was used in all cases, with one\nexception that I'll get to shortly.\n\nThere were problems, though. Many people felt that this method\ncreated entirely too much coupling between the called subroutine\nand its caller. In effect, it gave the subroutine complete\naccess to all variables that appeared in the parameter list.\n\nMany times, we didn't want to actually change a parameter, but\nonly use it as an input. For example, we might pass an element\ncount to a subroutine, and wish we could then use that count\nwithin a DO-loop. To avoid changing the value in the calling\nprogram, we had to make a local copy of the input parameter, and\noperate only on the copy. Some FORTRAN programmers, in fact,\nmade it a practice to copy ALL parameters except those that were\nto be used as return values. Needless to say, all this copying\ndefeated a good bit of the efficiency associated with the\napproach.\n\nThere was, however, an even more insidious problem, which was not\nreally just the fault of the \"pass by reference\" convention, but\na bad convergence of several implementation decisions.\n\nSuppose we have a subroutine:\n\n```delphi\n SUBROUTINE FOO(X, Y, N)\n```\n\nwhere N is some kind of input count or flag. Many times, we'd\nlike to be able to pass a literal or even an expression in place\nof a variable, such as:\n\n```delphi\n CALL FOO(A, B, J + 1)\n```\n\nHere the third parameter is not a variable, and so it has no\naddress. The earliest FORTRAN compilers did not allow such\nthings, so we had to resort to subterfuges like:\n\n```delphi\n K = J + 1\n CALL FOO(A, B, K)\n```\n\nHere again, there was copying required, and the burden was on the\nprogrammer to do it. Not good.\n\nLater FORTRAN implementations got rid of this by allowing\nexpressions as parameters. What they did was to assign a\ncompiler-generated variable, store the value of the expression in\nthe variable, and then pass the address of the expression.\n\nSo far, so good. Even if the subroutine mistakenly altered the\nanonymous variable, who was to know or care? On the next call,\nit would be recalculated anyway.\n\nThe problem arose when someone decided to make things more\nefficient. They reasoned, rightly enough, that the most common\nkind of \"expression\" was a single integer value, as in:\n\n```delphi\n CALL FOO(A, B, 4)\n```\n\nIt seemed inefficient to go to the trouble of \"computing\" such an\ninteger and storing it in a temporary variable, just to pass it\nthrough the calling list. Since we had to pass the address of\nthe thing anyway, it seemed to make lots of sense to just pass\nthe address of the literal integer, 4 in the example above.\n\nTo make matters more interesting, most compilers, then and now,\nidentify all literals and store them separately in a \"literal\npool,\" so that we only have to store one value for each unique\nliteral. That combination of design decisions: passing\nexpressions, optimization for literals as a special case, and use\nof a literal pool, is what led to disaster.\n\nTo see how it works, imagine that we call subroutine FOO as in\nthe example above, passing it a literal 4. Actually, what gets\npassed is the address of the literal 4, which is stored in the\nliteral pool. This address corresponds to the formal parameter,\nK, in the subroutine itself.\n\nNow suppose that, unbeknownst to the programmer, subroutine FOO\nactually modifies K to be, say, -7. Suddenly, that literal 4 in\nthe literal pool gets CHANGED, to a -7. From then on, every\nexpression that uses a 4 and every subroutine that passes a 4\nwill be using the value of -7 instead! Needless to say, this can\nlead to some bizarre and difficult-to-find behavior. The whole\nthing gave the concept of pass-by-reference a bad name, although\nas we have seen, it was really a combination of design decisions\nthat led to the problem.\n\nIn spite of the problem, the FORTRAN approach had its good\npoints. Chief among them is the fact that we don't have to\nsupport multiple mechanisms. The same scheme, passing the\naddress of the argument, works for EVERY case, including arrays.\nSo the size of the compiler can be reduced.\n\nPartly because of the FORTRAN gotcha, and partly just because of\nthe reduced coupling involved, modern languages like C, Pascal,\nAda, and Modula 2 generally pass scalars by value.\n\nThis means that the value of the scalar is COPIED into a separate\nvalue used only for the call. Since the value passed is a copy,\nthe called procedure can use it as a local variable and modify it\nany way it likes. The value in the caller will not be changed.\n\nIt may seem at first that this is a bit inefficient, because of\nthe need to copy the parameter. But remember that we're going to\nhave to fetch SOME value to pass anyway, whether it be the\nparameter itself or an address for it. Inside the subroutine,\nusing pass-by-value is definitely more efficient, since we\neliminate one level of indirection. Finally, we saw earlier that\nwith FORTRAN, it was often necessary to make copies within the\nsubroutine anyway, so pass-by-value reduces the number of local\nvariables. All in all, pass-by-value is better.\n\nExcept for one small little detail: if all parameters are passed\nby value, there is no way for a called to procedure to return a\nresult to its caller! The parameter passed is NOT altered in the\ncaller, only in the called procedure. Clearly, that won't get\nthe job done.\n\nThere have been two answers to this problem, which are\nequivalent. In Pascal, Wirth provides for VAR parameters, which\nare passed-by-reference. What a VAR parameter is, in fact, is\nnone other than our old friend the FORTRAN parameter, with a new\nname and paint job for disguise. Wirth neatly gets around the\n\"changing a literal\" problem as well as the \"address of an\nexpression\" problem, by the simple expedient of allowing only a\nvariable to be the actual parameter. In other words, it's the\nsame restriction that the earliest FORTRANs imposed.\n\nC does the same thing, but explicitly. In C, _ALL_ parameters\nare passed by value. One kind of variable that C supports,\nhowever, is the pointer. So by passing a pointer by value, you\nin effect pass what it points to by reference. In some ways this\nworks even better yet, because even though you can change the\nvariable pointed to all you like, you still CAN'T change the\npointer itself. In a function such as strcpy, for example, where\nthe pointers are incremented as the string is copied, we are\nreally only incrementing copies of the pointers, so the values of\nthose pointers in the calling procedure still remain as they\nwere. To modify a pointer, you must pass a pointer to the\npointer.\n\nSince we are simply performing experiments here, we'll look at\nBOTH pass-by-value and pass-by-reference. That way, we'll be\nable to use either one as we need to. It's worth mentioning that\nit's going to be tough to use the C approach to pointers here,\nsince a pointer is a different type and we haven't studied types\nyet!"},{title:"PASS-BY-VALUE",path:"/pass-by-value",content:"Let's just try some simple-minded things and see where they lead\nus. Let's begin with the pass-by-value case. Consider the\nprocedure call:\n\n```delphi\n FOO(X, Y)\n```\n\nAlmost the only reasonable way to pass the data is through the\nCPU stack. So the code we'd like to see generated might look\nsomething like this:\n\n```asm\n MOVE X(PC),-(SP) ; Push X\n MOVE Y(PC),-(SP) ; Push Y\n BSR FOO ; Call FOO\n```\n\nThat certainly doesn't seem too complex!\n\nWhen the BSR is executed, the CPU pushes the return address onto\nthe stack and jumps to FOO. At this point the stack will look\nlike this:\n```\n .\n .\n Value of X (2 bytes)\n Value of Y (2 bytes)\n SP --\x3e Return Address (4 bytes)\n```\n\nSo the values of the parameters have addresses that are fixed\noffsets from the stack pointer. In this example, the addresses\nare:\n\n```\n X: 6(SP)\n Y: 4(SP)\n```\n\nNow consider what the called procedure might look like:\n\n```delphi\n PROCEDURE FOO(A, B)\n BEGIN\n A = B\n END\n```\n(Remember, the names of the formal parameters are arbitrary ...\nonly the positions count.)\n\nThe desired output code might look like:\n\n```asm\n FOO: MOVE 4(SP),D0\n MOVE D0,6(SP)\n RTS\n```\n\nNote that, in order to address the formal parameters, we're going\nto have to know which position they have in the parameter list.\nThis means some changes to the symbol table stuff. In fact, for\nour single-character case it's best to just create a new symbol\ntable for the formal parameters.\n\nLet's begin by declaring a new table:\n\n```delphi\n var Params: Array['A'..'Z'] of integer;\n```\n\nWe also will need to keep track of how many parameters a given\nprocedure has:\n\n```delphi\n var NumParams: integer;\n```\n\nAnd we need to initialize the new table. Now, remember that the\nformal parameter list will be different for each procedure that\nwe process, so we'll need to initialize that table anew for each\nprocedure. Here's the initializer:\n\n```delphi\n{--------------------------------------------------------------}\n{ Initialize Parameter Table to Null }\n\nprocedure ClearParams;\nvar i: char;\nbegin\n for i := 'A' to 'Z' do\n Params[i] := 0;\n NumParams := 0;\nend;\n{--------------------------------------------------------------}\n```\n\nWe'll put a call to this procedure in Init, and also at the end\nof DoProc:\n\n```delphi\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nvar i: char;\nbegin\n GetChar;\n SkipWhite;\n for i := 'A' to 'Z' do\n ST[i] := ' ';\n ClearParams;\nend;\n{--------------------------------------------------------------}\n.\n.\n.\n{--------------------------------------------------------------}\n{ Parse and Translate a Procedure Declaration }\n\nprocedure DoProc;\nvar N: char;\nbegin\n Match('p');\n N := GetName;\n FormalList;\n Fin;\n if InTable(N) then Duplicate(N);\n ST[N] := 'p';\n PostLabel(N);\n BeginBlock;\n Return;\n ClearParams;\nend;\n{--------------------------------------------------------------}\n```\n\nNote that the call within DoProc ensures that the table will be\nclear when we're in the main program.\n\n\nOK, now we need a few procedures to work with the table. The\nnext few functions are essentially copies of InTable, TypeOf,\netc.:\n\n```delphi\n{--------------------------------------------------------------}\n{ Find the Parameter Number }\n\nfunction ParamNumber(N: char): integer;\nbegin\n ParamNumber := Params[N];\nend;\n\n\n{--------------------------------------------------------------}\n{ See if an Identifier is a Parameter }\n\nfunction IsParam(N: char): boolean;\nbegin\n IsParam := Params[N] <> 0;\nend;\n\n\n{--------------------------------------------------------------}\n{ Add a New Parameter to Table }\n\nprocedure AddParam(Name: char);\nbegin\n if IsParam(Name) then Duplicate(Name);\n Inc(NumParams);\n Params[Name] := NumParams;\nend;\n{--------------------------------------------------------------}\n```\n\nFinally, we need some code generation routines:\n\n```delphi\n{--------------------------------------------------------------}\n{ Load a Parameter to the Primary Register }\n\nprocedure LoadParam(N: integer);\nvar Offset: integer;\nbegin\n Offset := 4 + 2 * (NumParams - N);\n Emit('MOVE ');\n WriteLn(Offset, '(SP),D0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Store a Parameter from the Primary Register }\n\nprocedure StoreParam(N: integer);\nvar Offset: integer;\nbegin\n Offset := 4 + 2 * (NumParams - N);\n Emit('MOVE D0,');\n WriteLn(Offset, '(SP)');\nend;\n\n\n{--------------------------------------------------------------}\n{ Push The Primary Register to the Stack }\n\nprocedure Push;\nbegin\n EmitLn('MOVE D0,-(SP)');\nend;\n{--------------------------------------------------------------}\n```\n\n( The last routine is one we've seen before, but it wasn't in\nthis vestigial version of the program.)\n\nWith those preliminaries in place, we're ready to deal with the\nsemantics of procedures with calling lists (remember, the code to\ndeal with the syntax is already in place).\n\nLet's begin by processing a formal parameter. All we have to do\nis to add each parameter to the parameter symbol table:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process a Formal Parameter }\n\nprocedure FormalParam;\nbegin\n AddParam(GetName);\nend;\n{--------------------------------------------------------------}\n```\n\nNow, what about dealing with a formal parameter when it appears\nin the body of the procedure? That takes a little more work. We\nmust first determine that it IS a formal parameter. To do this,\nI've written a modified version of TypeOf:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get Type of Symbol }\n\nfunction TypeOf(n: char): char;\nbegin\n if IsParam(n) then\n TypeOf := 'f'\n else\n TypeOf := ST[n];\nend;\n{--------------------------------------------------------------}\n```\n\n(Note that, since TypeOf now calls IsParam, it may need to be\nrelocated in your source.)\n\nWe also must modify AssignOrProc to deal with this new type:\n\n```delphi\n{--------------------------------------------------------------}\n{ Decide if a Statement is an Assignment or Procedure Call }\n\nprocedure AssignOrProc;\nvar Name: char;\nbegin\n Name := GetName;\n case TypeOf(Name) of\n ' ': Undefined(Name);\n 'v', 'f': Assignment(Name);\n 'p': CallProc(Name);\n else Abort('Identifier ' + Name + ' Cannot Be Used\nHere');\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nFinally, the code to process an assignment statement and an\nexpression must be extended:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate an Expression }\n{ Vestigial Version }\n\nprocedure Expression;\nvar Name: char;\nbegin\n Name := GetName;\n if IsParam(Name) then\n LoadParam(ParamNumber(Name))\n else\n LoadVar(Name);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment(Name: char);\nbegin\n Match('=');\n Expression;\n if IsParam(Name) then\n StoreParam(ParamNumber(Name))\n else\n StoreVar(Name);\nend;\n{--------------------------------------------------------------}\n```\n\nAs you can see, these procedures will treat every variable name\nencountered as either a formal parameter or a global variable,\ndepending on whether or not it appears in the parameter symbol\ntable. Remember that we are using only a vestigial form of\nExpression. In the final program, the change shown here will\nhave to be added to Factor, not Expression.\n\nThe rest is easy. We need only add the semantics to the actual\nprocedure call, which we can do with one new line of code:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process an Actual Parameter }\n\nprocedure Param;\nbegin\n Expression;\n Push;\nend;\n{--------------------------------------------------------------}\n```\n\nThat's it. Add these changes to your program and give it a try.\nTry declaring one or two procedures, each with a formal parameter\nlist. Then do some assignments, using combinations of global and\nformal parameters. You can call one procedure from within\nanother, but you cannot DECLARE a nested procedure. You can even\npass formal parameters from one procedure to another. If we had\nthe full syntax of the language here, you'd also be able to do\nthings like read or write formal parameters or use them in\ncomplicated expressions."},{title:"WHAT'S WRONG?",path:"/whats-wrong",content:"At this point, you might be thinking: Surely there's more to this\nthan a few pushes and pops. There must be more to passing\nparameters than this.\n\nYou'd be right. As a matter of fact, the code that we're\ngenerating here leaves a lot to be desired in several respects.\n\nThe most glaring oversight is that it's wrong! If you'll look\nback at the code for a procedure call, you'll see that the caller\npushes each actual parameter onto the stack before it calls the\nprocedure. The procedure USES that information, but it doesn't\nchange the stack pointer. That means that the stuff is still\nthere when we return. SOMEBODY needs to clean up the stack, or\nwe'll soon be in very hot water!\n\nFortunately, that's easily fixed. All we have to do is to\nincrement the stack pointer when we're finished.\n\nShould we do that in the calling program, or the called\nprocedure? Some folks let the called procedure clean up the\nstack, since that requires less code to be generated per call,\nand since the procedure, after all, knows how many parameters\nit's got. But that means that it must do something with the\nreturn address so as not to lose it.\n\nI prefer letting the caller clean up, so that the callee need\nonly execute a return. Also, it seems a bit more balanced, since\nthe caller is the one who \"messed up\" the stack in the first\nplace. But THAT means that the caller must remember how many\nitems it pushed. To make things easy, I've modified the\nprocedure ParamList to be a function instead of a procedure,\nreturning the number of bytes pushed:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process the Parameter List for a Procedure Call }\n\nfunction ParamList: integer;\nvar N: integer;\nbegin\n N := 0;\n Match('(');\n if Look <> ')' then begin\n Param;\n inc(N);\n while Look = ',' do begin\n Match(',');\n Param;\n inc(N);\n end;\n end;\n Match(')');\n ParamList := 2 * N;\nend;\n{--------------------------------------------------------------}\n```\n\nProcedure CallProc then uses this to clean up the stack:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process a Procedure Call }\n\nprocedure CallProc(Name: char);\nvar N: integer;\nbegin\n N := ParamList;\n Call(Name);\n CleanStack(N);\nend;\n{--------------------------------------------------------------}\n```\n\nHere I've created yet another code generation procedure:\n\n```delphi\n{--------------------------------------------------------------}\n{ Adjust the Stack Pointer Upwards by N Bytes }\n\nprocedure CleanStack(N: integer);\nbegin\n if N > 0 then begin\n Emit('ADD #');\n WriteLn(N, ',SP');\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nOK, if you'll add this code to your compiler, I think you'll find\nthat the stack is now under control.\n\nThe next problem has to do with our way of addressing relative to\nthe stack pointer. That works fine in our simple examples, since\nwith our rudimentary form of expressions nobody else is messing\nwith the stack. But consider a different example as simple as:\n\n```delphi\n PROCEDURE FOO(A, B)\n BEGIN\n A = A + B\n END\n```\n\nThe code generated by a simple-minded parser might be:\n\n```asm\n FOO: MOVE 6(SP),D0 ; Fetch A\n MOVE D0,-(SP) ; Push it\n MOVE 4(SP),D0 ; Fetch B\n ADD (SP)+,D0 ; Add A\n MOVE D0,6(SP) : Store A\n RTS\n```\n\nThis would be wrong. When we push the first argument onto the\nstack, the offsets for the two formal parameters are no longer 4\nand 6, but are 6 and 8. So the second fetch would fetch A again,\nnot B.\n\nThis is not the end of the world. I think you can see that all\nwe really have to do is to alter the offset every time we do a\npush, and that in fact is what's done if the CPU has no support\nfor other methods.\n\nFortunately, though, the 68000 does have such support.\nRecognizing that this CPU would be used a lot with high-order\nlanguage compilers, Motorola decided to add direct support for\nthis kind of thing.\n\nThe problem, as you can see, is that as the procedure executes,\nthe stack pointer bounces up and down, and so it becomes an\nawkward thing to use as a reference to access the formal\nparameters. The solution is to define some _OTHER_ register, and\nuse it instead. This register is typically set equal to the\noriginal stack pointer, and is called the frame pointer.\n\nThe 68000 instruction set LINK lets you declare such a frame\npointer, and sets it equal to the stack pointer, all in one\ninstruction. As a matter of fact, it does even more than that.\nSince this register may have been in use for something else in\nthe calling procedure, LINK also pushes the current value of that\nregister onto the stack. It can also add a value to the stack\npointer, to make room for local variables.\n\nThe complement of LINK is UNLK, which simply restores the stack\npointer and pops the old value back into the register.\n\nUsing these two instructions, the code for the previous example\nbecomes:\n\n```asm\n FOO: LINK A6,#0\n MOVE 10(A6),D0 ; Fetch A\n MOVE D0,-(SP) ; Push it\n MOVE 8(A6),D0 ; Fetch B\n ADD (SP)+,D0 ; Add A\n MOVE D0,10(A6) : Store A\n UNLK A6\n RTS\n```\n\nFixing the compiler to generate this code is a lot easier than it\nis to explain it. All we need to do is to modify the code\ngeneration created by DoProc. Since that makes the code a little\nmore than one line, I've created new procedures to deal with it,\nparalleling the Prolog and Epilog procedures called by DoMain:\n\n```delphi\n{--------------------------------------------------------------}\n{ Write the Prolog for a Procedure }\n\nprocedure ProcProlog(N: char);\nbegin\n PostLabel(N);\n EmitLn('LINK A6,#0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Write the Epilog for a Procedure }\n\nprocedure ProcEpilog;\nbegin\n EmitLn('UNLK A6');\n EmitLn('RTS');\nend;\n{--------------------------------------------------------------}\n```\n\nProcedure DoProc now just calls these:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Procedure Declaration }\n\nprocedure DoProc;\nvar N: char;\nbegin\n Match('p');\n N := GetName;\n FormalList;\n Fin;\n if InTable(N) then Duplicate(N);\n ST[N] := 'p';\n ProcProlog(N);\n BeginBlock;\n ProcEpilog;\n ClearParams;\nend;\n{--------------------------------------------------------------}\n```\n\nFinally, we need to change the references to SP in procedures\nLoadParam and StoreParam:\n\n```delphi\n{--------------------------------------------------------------}\n{ Load a Parameter to the Primary Register }\n\nprocedure LoadParam(N: integer);\nvar Offset: integer;\nbegin\n Offset := 8 + 2 * (NumParams - N);\n Emit('MOVE ');\n WriteLn(Offset, '(A6),D0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Store a Parameter from the Primary Register }\n\nprocedure StoreParam(N: integer);\nvar Offset: integer;\nbegin\n Offset := 8 + 2 * (NumParams - N);\n Emit('MOVE D0,');\n WriteLn(Offset, '(A6)');\nend;\n{--------------------------------------------------------------}\n\n```\n(Note that the Offset computation changes to allow for the extra\npush of A6.)\n\nThat's all it takes. Try this out and see how you like it.\n\nAt this point we are generating some relatively nice code for\nprocedures and procedure calls. Within the limitation that there\nare no local variables (yet) and that no procedure nesting is\nallowed, this code is just what we need.\n\nThere is still just one little small problem remaining:"},{title:"WE HAVE NO WAY TO RETURN RESULTS TO THE CALLER!",path:"/we-have-no-way-to-return-results-to-the-caller",content:"But that, of course, is not a limitation of the code we're\ngenerating, but one inherent in the call-by-value protocol.\nNotice that we CAN use formal parameters in any way inside the\nprocedure. We can calculate new values for them, use them as\nloop counters (if we had loops, that is!), etc. So the code is\ndoing what it's supposed to. To get over this last problem, we\nneed to look at the alternative protocol."},{title:"CALL-BY-REFERENCE",path:"/call-by-reference",content:"This one is easy, now that we have the mechanisms already in\nplace. We only have to make a few changes to the code\ngeneration. Instead of pushing a value onto the stack, we must\npush an address. As it turns out, the 68000 has an instruction,\nPEA, that does just that.\n\nWe'll be making a new version of the test program for this.\nBefore we do anything else,\n\n`>>>> MAKE A COPY <<<<` of the program as it now stands, because we'll be needing it\nagain later.\n\nLet's begin by looking at the code we'd like to see generated for\nthe new case. Using the same example as before, we need the call\n\n```delphi\n FOO(X, Y)\n```\n\nto be translated to:\n\n```delphi\n PEA X(PC) ; Push the address of X\n PEA Y(PC) ; Push Y the address of Y\n BSR FOO ; Call FOO\n```\n\nThat's a simple matter of a slight change to Param:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process an Actual Parameter }\n\nprocedure Param;\nbegin\n EmitLn('PEA ' + GetName + '(PC)');\nend;\n{--------------------------------------------------------------}\n```\n\n(Note that with pass-by-reference, we can't have expressions in\nthe calling list, so Param can just read the name directly.)\n\nAt the other end, the references to the formal parameters must be\ngiven one level of indirection:\n\n```asm\n FOO: LINK A6,#0\n MOVE.L 12(A6),A0 ; Fetch the address of A\n MOVE (A0),D0 ; Fetch A\n MOVE D0,-(SP) ; Push it\n MOVE.L 8(A6),A0 ; Fetch the address of B\n MOVE (A0),D0 ; Fetch B\n ADD (SP)+,D0 ; Add A\n MOVE.L 12(A6),A0 ; Fetch the address of A\n MOVE D0,(A0) : Store A\n UNLK A6\n RTS\n```\n\nAll of this can be handled by changes to LoadParam and\nStoreParam:\n\n```delphi\n{--------------------------------------------------------------}\n{ Load a Parameter to the Primary Register }\n\nprocedure LoadParam(N: integer);\nvar Offset: integer;\nbegin\n Offset := 8 + 4 * (NumParams - N);\n Emit('MOVE.L ');\n WriteLn(Offset, '(A6),A0');\n EmitLn('MOVE (A0),D0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Store a Parameter from the Primary Register }\n\nprocedure StoreParam(N: integer);\nvar Offset: integer;\nbegin\n Offset := 8 + 4 * (NumParams - N);\n Emit('MOVE.L ');\n WriteLn(Offset, '(A6),A0');\n EmitLn('MOVE D0,(A0)');\nend;\n{--------------------------------------------------------------}\n```\nTo get the count right, we must also change one line in\nParamList:\n\n```delphi\n ParamList := 4 * N;\n```\n\nThat should do it. Give it a try and see if it's generating\nreasonable-looking code. As you will see, the code is hardly\noptimal, since we reload the address register every time a\nparameter is needed. But that's consistent with our KISS\napproach here, of just being sure to generate code that works.\nWe'll just make a little note here, that here's yet another\ncandidate for optimization, and press on.\n\nNow we've learned to process parameters using pass-by-value and\npass-by-reference. In the real world, of course, we'd like to be\nable to deal with BOTH methods. We can't do that yet, though,\nbecause we have not yet had a session on types, and that has to\ncome first.\n\nIf we can only have ONE method, then of course it has to be the\ngood ol' FORTRAN method of pass-by-reference, since that's the\nonly way procedures can ever return values to their caller.\n\nThis, in fact, will be one of the differences between TINY and\nKISS. In the next version of TINY, we'll use pass-by-reference\nfor all parameters. KISS will support both methods."},{title:"LOCAL VARIABLES",path:"/local-variables",content:"So far, we've said nothing about local variables, and our\ndefinition of procedures doesn't allow for them. Needless to\nsay, that's a big gap in our language, and one that needs to be\ncorrected.\n\nHere again we are faced with a choice: Static or dynamic storage?\n\nIn those old FORTRAN programs, local variables were given static\nstorage just like global ones. That is, each local variable got\na name and allocated address, like any other variable, and was\nreferenced by that name.\n\nThat's easy for us to do, using the allocation mechanisms already\nin place. Remember, though, that local variables can have the\nsame names as global ones. We need to somehow deal with that by\nassigning unique names for these variables.\n\nThe characteristic of static storage, of course, is that the data\nsurvives a procedure call and return. When the procedure is\ncalled again, the data will still be there. That can be an\nadvantage in some applications. In the FORTRAN days we used to\ndo tricks like initialize a flag, so that you could tell when you\nwere entering a procedure for the first time and could do any\none-time initialization that needed to be done.\n\nOf course, the same \"feature\" is also what makes recursion\nimpossible with static storage. Any new call to a procedure will\noverwrite the data already in the local variables.\n\nThe alternative is dynamic storage, in which storage is allocated\non the stack just as for passed parameters. We also have the\nmechanisms already for doing this. In fact, the same routines\nthat deal with passed (by value) parameters on the stack can\neasily deal with local variables as well ... the code to be\ngenerated is the same. The purpose of the offset in the 68000\nLINK instruction is there just for that reason: we can use it to\nadjust the stack pointer to make room for locals. Dynamic\nstorage, of course, inherently supports recursion.\n\nWhen I first began planning TINY, I must admit to being\nprejudiced in favor of static storage. That's simply because\nthose old FORTRAN programs were pretty darned efficient ... the\nearly FORTRAN compilers produced a quality of code that's still\nrarely matched by modern compilers. Even today, a given program\nwritten in FORTRAN is likely to outperform the same program\nwritten in C or Pascal, sometimes by wide margins. (Whew! Am I\ngoing to hear about THAT statement!)\n\nI've always supposed that the reason had to do with the two main\ndifferences between FORTRAN implementations and the others:\nstatic storage and pass-by-reference. I know that dynamic\nstorage supports recursion, but it's always seemed to me a bit\npeculiar to be willing to accept slower code in the 95% of cases\nthat don't need recursion, just to get that feature when you need\nit. The idea is that, with static storage, you can use absolute\naddressing rather than indirect addressing, which should result\nin faster code.\n\nMore recently, though, several folks have pointed out to me that\nthere really is no performance penalty associated with dynamic\nstorage. With the 68000, for example, you shouldn't use absolute\naddressing anyway ... most operating systems require position\nindependent code. And the 68000 instruction\n```\n MOVE 8(A6),D0\n```\nhas exactly the same timing as\n```\n MOVE X(PC),D0.\n```\nSo I'm convinced, now, that there is no good reason NOT to use\ndynamic storage.\n\nSince this use of local variables fits so well into the scheme of\npass-by-value parameters, we'll use that version of the\ntranslator to illustrate it. (I _SURE_ hope you kept a copy!)\n\nThe general idea is to keep track of how many local parameters\nthere are. Then we use the integer in the LINK instruction to\nadjust the stack pointer downward to make room for them. Formal\nparameters are addressed as positive offsets from the frame\npointer, and locals as negative offsets. With a little bit of\nwork, the same procedures we've already created can take care of\nthe whole thing.\n\nLet's start by creating a new variable, Base:\n\n```delphi\n var Base: integer;\n```\nWe'll use this variable, instead of NumParams, to compute stack\noffsets. That means changing the two references to NumParams in\nLoadParam and StoreParam:\n\n```delphi\n{--------------------------------------------------------------}\n{ Load a Parameter to the Primary Register }\n\nprocedure LoadParam(N: integer);\nvar Offset: integer;\nbegin\n Offset := 8 + 2 * (Base - N);\n Emit('MOVE ');\n WriteLn(Offset, '(A6),D0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Store a Parameter from the Primary Register }\n\nprocedure StoreParam(N: integer);\nvar Offset: integer;\nbegin\n Offset := 8 + 2 * (Base - N);\n Emit('MOVE D0,');\n WriteLn(Offset, '(A6)');\nend;\n{--------------------------------------------------------------}\n```\n\nThe idea is that the value of Base will be frozen after we have\nprocessed the formal parameters, and won't increase further as\nthe new, local variables, are inserted in the symbol table. This\nis taken care of at the end of FormalList:\n\n```delphi\n{--------------------------------------------------------------}\n{ Process the Formal Parameter List of a Procedure }\n\nprocedure FormalList;\nbegin\n Match('(');\n if Look <> ')' then begin\n FormalParam;\n while Look = ',' do begin\n Match(',');\n FormalParam;\n end;\n end;\n Match(')');\n Fin;\n Base := NumParams;\n NumParams := NumParams + 4;\nend;\n{--------------------------------------------------------------}\n```\n\n(We add four words to make allowances for the return address and\nold frame pointer, which end up between the formal parameters and\nthe locals.)\n\nAbout all we need to do next is to install the semantics for\ndeclaring local variables into the parser. The routines are very\nsimilar to Decl and TopDecls:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Local Data Declaration }\n\nprocedure LocDecl;\nvar Name: char;\nbegin\n Match('v');\n AddParam(GetName);\n Fin;\nend;\n\n\n{--------------------------------------------------------------}\n\n\n{ Parse and Translate Local Declarations }\n\nfunction LocDecls: integer;\nvar n: integer;\nbegin\n n := 0;\n while Look = 'v' do begin\n LocDecl;\n inc(n);\n end;\n LocDecls := n;\nend;\n{--------------------------------------------------------------}\n```\n\nNote that LocDecls is a FUNCTION, returning the number of locals\nto DoProc.\n\nNext, we modify DoProc to use this information:\n\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Procedure Declaration }\n\nprocedure DoProc;\nvar N: char;\n k: integer;\nbegin\n Match('p');\n N := GetName;\n if InTable(N) then Duplicate(N);\n ST[N] := 'p';\n FormalList;\n k := LocDecls;\n ProcProlog(N, k);\n BeginBlock;\n ProcEpilog;\n ClearParams;\nend;\n{--------------------------------------------------------------}\n```\n\n(I've made a couple of changes here that weren't really\nnecessary. Aside from rearranging things a bit, I moved the call\nto Fin to within FormalList, and placed one inside LocDecls as\nwell. Don't forget to put one at the end of FormalList, so that\nwe're together here.)\n\nNote the change in the call to ProcProlog. The new argument is\nthe number of WORDS (not bytes) to allocate space for. Here's\nthe new version of ProcProlog:\n\n```delphi\n{--------------------------------------------------------------}\n{ Write the Prolog for a Procedure }\n\nprocedure ProcProlog(N: char; k: integer);\nbegin\n PostLabel(N);\n Emit('LINK A6,#');\n WriteLn(-2 * k)\nend;\n{--------------------------------------------------------------}\n```\n\nThat should do it. Add these changes and see how they work."},{title:"CONCLUSION",path:"/conclusion",content:"At this point you know how to compile procedure declarations and\nprocedure calls, with parameters passed by reference and by\nvalue. You can also handle local variables. As you can see, the\nhard part is not in providing the mechanisms, but in deciding\njust which mechanisms to use. Once we make these decisions, the\ncode to translate the constructs is really not that difficult.\nI didn't show you how to deal with the combination of local\nparameters and pass-by-reference parameters, but that's a\nstraightforward extension to what you've already seen. It just\ngets a little more messy, that's all, since we need to support\nboth mechanisms instead of just one at a time. I'd prefer to\nsave that one until after we've dealt with ways to handle\ndifferent variable types.\n\nThat will be the next installment, which will be coming soon to a\nForum near you. See you then."}]},{title:"Part XIV: TYPES - 26 May 1990",path:"/part-xiv-types-26-may-1990",items:[{title:"INTRODUCTION",path:"/introduction",content:"In the last installment (Part XIII: PROCEDURES) I mentioned that\nin that part and this one, we would cover the two features that\ntend to separate the toy language from a real, usable one. We\ncovered procedure calls in that installment. Many of you have\nbeen waiting patiently, since August '89, for me to drop the\nother shoe. Well, here it is.\n\nIn this installment, we'll talk about how to deal with different\ndata types. As I did in the last segment, I will NOT incorporate\nthese features directly into the TINY compiler at this time.\nInstead, I'll be using the same approach that has worked so well\nfor us in the past: using only fragments of the parser and\nsingle-character tokens. As usual, this allows us to get\ndirectly to the heart of the matter without having to wade\nthrough a lot of unnecessary code. Since the major problems in\ndealing with multiple types occur in the arithmetic operations,\nthat's where we'll concentrate our focus.\n\nA few words of warning: First, there are some types that I will\nNOT be covering in this installment. Here we will ONLY be\ntalking about the simple, predefined types. We won't even deal\nwith arrays, pointers or strings in this installment; I'll be\ncovering them in the next few.\n\nSecond, we also will not discuss user-defined types. That will\nnot come until much later, for the simple reason that I still\nhaven't convinced myself that user-defined types belong in a\nlanguage named KISS. In later installments, I do intend to cover\nat least the general concepts of user-defined types, records,\netc., just so that the series will be complete. But whether or\nnot they will be included as part of KISS is still an open issue.\nI am open to comments or suggestions on this question.\n\nFinally, I should warn you: what we are about to do CAN add\nconsiderable extra complication to both the parser and the\ngenerated code. Handling variables of different types is\nstraightforward enough. The complexity comes in when you add\nrules about conversion between types. In general, you can make\nthe compiler as simple or as complex as you choose to make it,\ndepending upon the way you define the type-conversion rules.\nEven if you decide not to allow ANY type conversions (as in Ada,\nfor example) the problem is still there, and is built into the\nmathematics. When you multiply two short numbers, for example,\nyou can get a long result.\n\nI've approached this problem very carefully, in an attempt to\nKeep It Simple. But we can't avoid the complexity entirely. As\nhas so often has happened, we end up having to trade code quality\nagainst complexity, and as usual I will tend to opt for the\nsimplest approach."},{title:"WHAT'S COMING NEXT?",path:"/whats-coming-next",content:"Before diving into the tutorial, I think you'd like to know where\nwe are going from here ... especially since it's been so long\nsince the last installment.\n\nI have not been idle in the meantime. What I've been doing is\nreorganizing the compiler itself into Turbo Units. One of the\nproblems I've encountered is that as we've covered new areas and\nthereby added features to the TINY compiler, it's been getting\nlonger and longer. I realized a couple of installments back that\nthis was causing trouble, and that's why I've gone back to using\nonly compiler fragments for the last installment and this one.\nThe problem is that it just seems dumb to have to reproduce the\ncode for, say, processing boolean exclusive OR's, when the\nsubject of the discussion is parameter passing.\n\nThe obvious way to have our cake and eat it, too, is to break up\nthe compiler into separately compilable modules, and of course\nthe Turbo Unit is an ideal vehicle for doing this. This allows\nus to hide some fairly complex code (such as the full arithmetic\nand boolean expression parsing) into a single unit, and just pull\nit in whenever it's needed. In that way, the only code I'll have\nto reproduce in these installments will be the code that actually\nrelates to the issue under discussion.\n\nI've also been toying with Turbo 5.5, which of course includes\nthe Borland object-oriented extensions to Pascal. I haven't\ndecided whether to make use of these features, for two reasons.\nFirst of all, many of you who have been following this series may\nstill not have 5.5, and I certainly don't want to force anyone to\nhave to go out and buy a new compiler just to complete the\nseries. Secondly, I'm not convinced that the O-O extensions have\nall that much value for this application. We've been having some\ndiscussions about that in CompuServe's CLM forum, and so far\nwe've not found any compelling reason to use O-O constructs.\nThis is another of those areas where I could use some feedback\nfrom you readers. Anyone want to vote for Turbo 5.5 and O-O?\n\nIn any case, after the next few installments in the series, the\nplan is to upload to you a complete set of Units, and complete\nfunctioning compilers as well. The plan, in fact, is to have\nTHREE compilers: One for a single-character version of TINY (to\nuse for our experiments), one for TINY and one for KISS. I've\npretty much isolated the differences between TINY and KISS, which\nare these:\n\n - TINY will support only two data types: The character and the\n 16-bit integer. I may also try to do something with\n strings, since without them a compiler would be pretty\n useless. KISS will support all the usual simple types,\n including arrays and even floating point.\n\n - TINY will only have two control constructs, the IF and the\n WHILE. KISS will support a very rich set of constructs,\n including one we haven't discussed here before ... the CASE.\n\n - KISS will support separately compilable modules.\n\nOne caveat: Since I still don't know much about 80x86 assembler\nlanguage, all these compiler modules will still be written to\nsupport 68000 code. However, for the programs I plan to upload,\nall the code generation has been carefully encapsulated into a\nsingle unit, so that any enterprising student should be able to\neasily retarget to any other processor. This task is \"left as an\nexercise for the student.\" I'll make an offer right here and\nnow: For the person who provides us the first robust retarget to\n80x86, I will be happy to discuss shared copyrights and royalties\nfrom the book that's upcoming.\n\nBut enough talk. Let's get on with the study of types. As I\nsaid earlier, we'll do this one as we did in the last\ninstallment: by performing experiments using single-character\ntokens."},{title:"THE SYMBOL TABLE",path:"/the-symbol-table",content:"It should be apparent that, if we're going to deal with variables\nof different types, we're going to need someplace to record what\nthose types are. The obvious vehicle for that is the symbol\ntable, and we've already used it that way to distinguish, for\nexample, between local and global variables, and between\nvariables and procedures.\n\nThe symbol table structure for single-character tokens is\nparticularly simple, and we've used it several times before. To\ndeal with it, we'll steal some procedures that we've used before.\n\nFirst, we need to declare the symbol table itself:\n\n```delphi\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look: char; { Lookahead Character }\n\n ST: Array['A'..'Z'] of char; { *** ADD THIS LINE ***}\n{--------------------------------------------------------------}\n```\n\nNext, we need to make sure it's initialized as part of procedure\nInit:\n\n```delphi\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nvar i: char;\nbegin\n for i := 'A' to 'Z' do\n ST[i] := '?';\n GetChar;\nend;\n{--------------------------------------------------------------}\n```\n\nWe don't really need the next procedure, but it will be helpful\nfor debugging. All it does is to dump the contents of the symbol\ntable:\n\n```delphi\n{--------------------------------------------------------------}\n{ Dump the Symbol Table }\n\nprocedure DumpTable;\nvar i: char;\nbegin\n for i := 'A' to 'Z' do\n WriteLn(i, ' ', ST[i]);\nend;\n{--------------------------------------------------------------}\n```\n\nIt really doesn't matter much where you put this procedure ... I\nplan to cluster all the symbol table routines together, so I put\nmine just after the error reporting procedures.\n\nIf you're the cautious type (as I am), you might want to begin\nwith a test program that does nothing but initializes, then dumps\nthe table. Just to be sure that we're all on the same wavelength\nhere, I'm reproducing the entire program below, complete with the\nnew procedures. Note that this version includes support for\nwhite space:\n\n```delphi\n{--------------------------------------------------------------}\nprogram Types;\n\n{--------------------------------------------------------------}\n{ Constant Declarations }\n\nconst TAB = ^I;\n CR = ^M;\n LF = ^J;\n\n{--------------------------------------------------------------}\n{ Variable Declarations }\n\nvar Look: char; { Lookahead Character }\n\n ST: Array['A'..'Z'] of char;\n\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n Read(Look);\nend;\n\n\n{--------------------------------------------------------------}\n{ Report an Error }\n\nprocedure Error(s: string);\nbegin\n WriteLn;\n WriteLn(^G, 'Error: ', s, '.');\nend;\n\n\n{--------------------------------------------------------------}\n{ Report Error and Halt }\n\nprocedure Abort(s: string);\nbegin\n Error(s);\n Halt;\nend;\n\n\n{--------------------------------------------------------------}\n{ Report What Was Expected }\n\nprocedure Expected(s: string);\nbegin\n Abort(s + ' Expected');\nend;\n\n\n{--------------------------------------------------------------}\n{ Dump the Symbol Table }\n\nprocedure DumpTable;\nvar i: char;\nbegin\n for i := 'A' to 'Z' do\n WriteLn(i, ' ', ST[i]);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n IsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Decimal Digit }\n\nfunction IsDigit(c: char): boolean;\nbegin\n IsDigit := c in ['0'..'9'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an AlphaNumeric Character }\n\nfunction IsAlNum(c: char): boolean;\nbegin\n IsAlNum := IsAlpha(c) or IsDigit(c);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize an Addop }\n\nfunction IsAddop(c: char): boolean;\nbegin\n IsAddop := c in ['+', '-'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Mulop }\n\nfunction IsMulop(c: char): boolean;\nbegin\n IsMulop := c in ['*', '/'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Boolean Orop }\n\nfunction IsOrop(c: char): boolean;\nbegin\n IsOrop := c in ['|', '~'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize a Relop }\n\nfunction IsRelop(c: char): boolean;\nbegin\n IsRelop := c in ['=', '#', '<', '>'];\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize White Space }\n\nfunction IsWhite(c: char): boolean;\nbegin\n IsWhite := c in [' ', TAB];\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over Leading White Space }\n\nprocedure SkipWhite;\nbegin\n while IsWhite(Look) do\n GetChar;\nend;\n\n\n{--------------------------------------------------------------}\n{ Skip Over an End-of-Line }\n\nprocedure Fin;\nbegin\n if Look = CR then begin\n GetChar;\n if Look = LF then\n GetChar;\n end;\nend;\n\n\n{--------------------------------------------------------------}\n{ Match a Specific Input Character }\n\nprocedure Match(x: char);\nbegin\n if Look = x then GetChar\n else Expected('''' + x + '''');\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: char;\nbegin\n if not IsAlpha(Look) then Expected('Name');\n GetName := UpCase(Look);\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: char;\nbegin\n if not IsDigit(Look) then Expected('Integer');\n GetNum := Look;\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab }\n\nprocedure Emit(s: string);\nbegin\n Write(TAB, s);\nend;\n\n\n{--------------------------------------------------------------}\n{ Output a String with Tab and CRLF }\n\nprocedure EmitLn(s: string);\nbegin\n Emit(s);\n WriteLn;\nend;\n\n\n{--------------------------------------------------------------}\n{ Initialize }\n\nprocedure Init;\nvar i: char;\nbegin\n for i := 'A' to 'Z' do\n ST[i] := '?';\n GetChar;\n SkipWhite;\nend;\n\n\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n DumpTable;\nend.\n{--------------------------------------------------------------}\n```\n\nOK, run this program. You should get a (very fast) printout of\nall the letters of the alphabet (potential identifiers), each\nfollowed by a question mark. Not very exciting, but it's a\nstart.\n\nOf course, in general we only want to see the types of the\nvariables that have been defined. We can eliminate the others by\nmodifying DumpTable with an IF test. Change the loop to read:\n\n```delphi\n for i := 'A' to 'Z' do\n if ST[i] <> '?' then\n WriteLn(i, ' ', ST[i]);\n```\n\nNow, run the program again. What did you get?\n\nWell, that's even more boring than before! There was no output\nat all, since at this point NONE of the names have been declared.\nWe can spice things up a bit by inserting some statements\ndeclaring some entries in the main program. Try these:\n\n```delphi\n ST['A'] := 'a';\n ST['P'] := 'b';\n ST['X'] := 'c';\n```\n\nThis time, when you run the program, you should get an output\nshowing that the symbol table is working right."},{title:"ADDING ENTRIES",path:"/adding-entries",content:"Of course, writing to the table directly is pretty poor practice,\nand not one that will help us much later. What we need is a\nprocedure to add entries to the table. At the same time, we know\nthat we're going to need to test the table, to make sure that we\naren't redeclaring a variable that's already in use (easy to do\nwith only 26 choices!). To handle all this, enter the following\nnew procedures:\n\n```delphi\n{--------------------------------------------------------------}\n{ Report Type of a Variable }\n\n\nfunction TypeOf(N: char): char;\nbegin\n TypeOf := ST[N];\nend;\n\n\n{--------------------------------------------------------------}\n{ Report if a Variable is in the Table }\n\n\nfunction InTable(N: char): boolean;\nbegin\n InTable := TypeOf(N) <> '?';\nend;\n\n\n{--------------------------------------------------------------}\n{ Check for a Duplicate Variable Name }\n\nprocedure CheckDup(N: char);\nbegin\n if InTable(N) then Abort('Duplicate Name ' + N);\nend;\n\n\n{--------------------------------------------------------------}\n{ Add Entry to Table }\n\nprocedure AddEntry(N, T: char);\nbegin\n CheckDup(N);\n ST[N] := T;\nend;\n{--------------------------------------------------------------}\n```\n\nNow change the three lines in the main program to read:\n\n```delphi\n AddEntry('A', 'a');\n AddEntry('P', 'b');\n AddEntry('X', 'c');\n``` \n\nand run the program again. Did it work? Then we have the symbol\ntable routines needed to support our work on types. In the next\nsection, we'll actually begin to use them."},{title:"ALLOCATING STORAGE",path:"/allocating-storage",content:"In other programs like this one, including the TINY compiler\nitself, we have already addressed the issue of declaring global\nvariables, and the code generated for them. Let's build a\nvestigial version of a \"compiler\" here, whose only function is to\nallow us declare variables. Remember, the syntax for a\ndeclaration is:\n\n```\n <data decl> ::= VAR <identifier>\n```\n\nAgain, we can lift a lot of the code from previous programs. The\nfollowing are stripped-down versions of those procedures. They\nare greatly simplified since I have eliminated niceties like\nvariable lists and initializers. In procedure Alloc, note that\nthe new call to AddEntry will also take care of checking for\nduplicate declarations:\n\n```delphi\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc(N: char);\nbegin\n AddEntry(N, 'v');\n WriteLn(N, ':', TAB, 'DC 0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Data Declaration }\n\nprocedure Decl;\nvar Name: char;\nbegin\n Match('v');\n Alloc(GetName);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n while Look <> '.' do begin\n case Look of\n 'v': Decl;\n else Abort('Unrecognized Keyword ' + Look);\n end;\n Fin;\n end;\nend;\n{--------------------------------------------------------------}\n\n```\nNow, in the main program, add a call to TopDecls and run the\nprogram. Try allocating a few variables, and note the resulting\ncode generated. This is old stuff for you, so the results should\nlook familiar. Note from the code for TopDecls that the program\nis ended by a terminating period.\n\nWhile you're at it, try declaring two variables with the same\nname, and verify that the parser catches the error."},{title:"DECLARING TYPES",path:"/declaring-types",content:"Allocating storage of different sizes is as easy as modifying\nprocedure TopDecls to recognize more than one keyword. There are\na number of decisions to be made here, in terms of what the\nsyntax should be, etc., but for now I'm going to duck all the\nissues and simply declare by executive fiat that our syntax will\nbe:\n\n```\n <data decl> ::= <typename> <identifier>\n```\nwhere:\n\n```\n <typename> ::= BYTE | WORD | LONG\n\n```\n(By an amazing coincidence, the first letters of these names\nhappen to be the same as the 68000 assembly code length\nspecifications, so this choice saves us a little work.)\n\nWe can create the code to take care of these declarations with\nonly slight modifications. In the routines below, note that I've\nseparated the code generation parts of Alloc from the logic\nparts. This is in keeping with our desire to encapsulate the\nmachine-dependent part of the compiler.\n\n```delphi\n{--------------------------------------------------------------}\n{ Generate Code for Allocation of a Variable }\n\nprocedure AllocVar(N, T: char);\nbegin\n WriteLn(N, ':', TAB, 'DC.', T, ' 0');\nend;\n\n\n{--------------------------------------------------------------}\n{ Allocate Storage for a Variable }\n\nprocedure Alloc(N, T: char);\nbegin\n AddEntry(N, T);\n AllocVar(N, T);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Data Declaration }\n\nprocedure Decl;\nvar Typ: char;\nbegin\n Typ := GetName;\n Alloc(GetName, Typ);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate Global Declarations }\n\nprocedure TopDecls;\nbegin\n while Look <> '.' do begin\n case Look of\n 'b', 'w', 'l': Decl;\n else Abort('Unrecognized Keyword ' + Look);\n end;\n Fin;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nMake the changes shown to these procedures, and give the thing a\ntry. Use the single characters 'b', 'w', and 'l' for the\nkeywords (they must be lower case, for now). You will see that\nin each case, we are allocating the proper storage size. Note\nfrom the dumped symbol table that the sizes are also recorded for\nlater use. What later use? Well, that's the subject of the rest\nof this installment."},{title:"ASSIGNMENTS",path:"/assignments",content:"Now that we can declare variables of different sizes, it stands\nto reason that we ought to be able to do something with them.\nFor our first trick, let's just try loading them into our working\nregister, D0. It makes sense to use the same idea we used for\nAlloc; that is, make a load procedure that can load more than one\nsize. We also want to continue to encapsulate the machine-\ndependent stuff. The load procedure looks like this:\n\n```delphi\n{---------------------------------------------------------------}\n{ Load a Variable to Primary Register }\n\nprocedure LoadVar(Name, Typ: char);\nbegin\n Move(Typ, Name + '(PC)', 'D0');\nend;\n{---------------------------------------------------------------}\n```\n\nOn the 68000, at least, it happens that many instructions turn\nout to be MOVE's. It turns out to be useful to create a separate\ncode generator just for these instructions, and then call it as\nneeded:\n\n```delphi\n{---------------------------------------------------------------}\n{ Generate a Move Instruction }\n\nprocedure Move(Size: char; Source, Dest: String);\nbegin\n EmitLn('MOVE.' + Size + ' ' + Source + ',' + Dest);\nend;\n{---------------------------------------------------------------}\n```\n\nNote that these two routines are strictly code generators; they\nhave no error-checking or other logic. To complete the picture,\nwe need one more layer of software that provides these functions.\n\nFirst of all, we need to make sure that the type we are dealing\nwith is a loadable type. This sounds like a job for another\nrecognizer:\n\n```delphi\n{--------------------------------------------------------------}\n{ Recognize a Legal Variable Type }\n\nfunction IsVarType(c: char): boolean;\nbegin\n IsVarType := c in ['B', 'W', 'L'];\nend;\n{--------------------------------------------------------------}\n```\n\nNext, it would be nice to have a routine that will fetch the type\nof a variable from the symbol table, while checking it to make\nsure it's valid:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Variable Type from the Symbol Table }\n\nfunction VarType(Name: char): char;\nvar Typ: char;\nbegin\n Typ := TypeOf(Name);\n if not IsVarType(Typ) then Abort('Identifier ' + Name +\n ' is not a variable');\n VarType := Typ;\nend;\n{--------------------------------------------------------------}\n```\n\nArmed with these tools, a procedure to cause a variable to be\nloaded becomes trivial:\n\n```delphi\n{--------------------------------------------------------------}\n{ Load a Variable to the Primary Register }\n\nprocedure Load(Name: char);\nbegin\n LoadVar(Name, VarType(Name));\nend;\n{--------------------------------------------------------------}\n```\n\n(NOTE to the concerned: I know, I know, all this is all very\ninefficient. In a production program, we probably would take\nsteps to avoid such deep nesting of procedure calls. Don't worry\nabout it. This is an EXERCISE, remember? It's more important to\nget it right and understand it, than it is to make it get the\nwrong answer, quickly. If you get your compiler completed and\nfind that you're unhappy with the speed, feel free to come back\nand hack the code to speed it up!)\n\nIt would be a good idea to test the program at this point. Since\nwe don't have a procedure for dealing with assignments yet, I\njust added the lines:\n\n```delphi\n Load('A');\n Load('B');\n Load('C');\n Load('X');\n```\n\nto the main program. Thus, after the declaration section is\ncomplete, they will be executed to generate code for the loads.\nYou can play around with this, and try different combinations of\ndeclarations to see how the errors are handled.\n\nI'm sure you won't be surprised to learn that storing variables\nis a lot like loading them. The necessary procedures are shown\nnext:\n\n```delphi\n{---------------------------------------------------------------}\n{ Store Primary to Variable }\n\nprocedure StoreVar(Name, Typ: char);\nbegin\n EmitLn('LEA ' + Name + '(PC),A0');\n Move(Typ, 'D0', '(A0)');\nend;\n\n\n{--------------------------------------------------------------}\n{ Store a Variable from the Primary Register }\n\nprocedure Store(Name: char);\nbegin\n StoreVar(Name, VarType(Name));\nend;\n{--------------------------------------------------------------}\n```\n\nYou can test this one the same way as the loads.\n\nNow, of course, it's a RATHER small step to use these to handle\nassignment statements. What we'll do is to create a special\nversion of procedure Block that supports only assignment\nstatements, and also a special version of Expression that only\nsupports single variables as legal expressions. Here they are:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nprocedure Expression;\nvar Name: char;\nbegin\n Load(GetName);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := GetName;\n Match('=');\n Expression;\n Store(Name);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Block of Statements }\n\nprocedure Block;\nbegin\n while Look <> '.' do begin\n Assignment;\n Fin;\n end;\nend;\n{--------------------------------------------------------------}\n```\n\n(It's worth noting that, if anything, the new procedures that\npermit us to manipulate types are, if anything, even simpler and\ncleaner than what we've seen before. This is mostly thanks to\nour efforts to encapsulate the code generator procedures.)\n\nThere is one small, nagging problem. Before, we used the Pascal\nterminating period to get us out of procedure TopDecls. This is\nnow the wrong character ... it's used to terminate Block. In\nprevious programs, we've used the BEGIN symbol (abbreviated 'b')\nto get us out. But that is now used as a type symbol.\n\nThe solution, while somewhat of a kludge, is easy enough. We'll\nuse an UPPER CASE 'B' to stand for the BEGIN. So change the\ncharacter in the WHILE loop within TopDecls, from '.' to 'B', and\neverything will be fine.\n\nNow, we can complete the task by changing the main program to\nread:\n\n```delphi\n{--------------------------------------------------------------}\n{ Main Program }\n\nbegin\n Init;\n TopDecls;\n Match('B');\n Fin;\n Block;\n DumpTable;\nend.\n{--------------------------------------------------------------}\n```\n\n(Note that I've had to sprinkle a few calls to Fin around to get\nus out of Newline troubles.)\n\nOK, run this program. Try the input:\n```\n\n ba { byte a } *** DON'T TYPE THE COMMENTS!!! ***\n wb { word b }\n lc { long c }\n B { begin }\n a=a\n a=b\n a=c\n b=a\n b=b\n b=c\n c=a\n c=b\n c=c\n .\n```\n\nFor each declaration, you should get code generated that\nallocates storage. For each assignment, you should get code that\nloads a variable of the correct size, and stores one, also of the\ncorrect size.\n\nThere's only one small little problem: The generated code is\nWRONG!\n\nLook at the code for a=c above. The code is:\n\n```delphi\n MOVE.L C(PC),D0\n LEA A(PC),A0\n MOVE.B D0,(A0)\n```\n\nThis code is correct. It will cause the lower eight bits of C to\nbe stored into A, which is a reasonable behavior. It's about all\nwe can expect to happen.\n\nBut now, look at the opposite case. For c=a, the code generated\nis:\n\n```\n MOVE.B A(PC),D0\n LEA C(PC),A0\n MOVE.L D0,(A0)\n```\n\nThis is `NOT` correct. It will cause the byte variable A to be\nstored into the lower eight bits of D0. According to the rules\nfor the 68000 processor, the upper 24 bits are unchanged. This\nmeans that when we store the entire 32 bits into C, whatever\ngarbage that was in those high bits will also get stored. Not\ngood.\n\nSo what we have run into here, early on, is the issue of TYPE\nCONVERSION, or COERCION.\n\nBefore we do anything with variables of different types, even if\nit's just to copy them, we have to face up to the issue. It is\nnot the most easy part of a compiler. Most of the bugs I have\nseen in production compilers have had to do with errors in type\nconversion for some obscure combination of arguments. As usual,\nthere is a tradeoff between compiler complexity and the potential\nquality of the generated code, and as usual, we will take the\npath that keeps the compiler simple. I think you'll find that,\nwith this approach, we can keep the potential complexity in check\nrather nicely."},{title:"THE COWARD'S WAY OUT",path:"/the-cowards-way-out",content:"Before we get into the details (and potential complexity) of type\nconversion, I'd like you to see that there is one super-simple\nway to solve the problem: simply promote every variable to a long\ninteger when we load it!\n\nThis takes the addition of only one line to LoadVar, although if\nwe are not going to COMPLETELY ignore efficiency, it should be\nguarded by an IF test. Here is the modified version:\n\n```delphi\n{---------------------------------------------------------------}\n{ Load a Variable to Primary Register }\n\nprocedure LoadVar(Name, Typ: char);\nbegin\n if Typ <> 'L' then\n EmitLn('CLR.L D0');\n Move(Typ, Name + '(PC)', 'D0');\nend;\n{---------------------------------------------------------------}\n```\n\n(Note that StoreVar needs no similar change.)\n\nIf you run some tests with this new version, you will find that\neverything works correctly now, albeit sometimes inefficiently.\nFor example, consider the case a=b (for the same declarations\nshown above). Now the generated code turns out to be:\n```\n CLR.L D0\n MOVE.W B(PC),D0\n LEA A(PC),A0\n MOVE.B D0,(A0)\n```\n\nIn this case, the CLR turns out not to be necessary, since the\nresult is going into a byte-sized variable. With a little bit of\nwork, we can do better. Still, this is not bad, and it typical\nof the kinds of inefficiencies that we've seen before in simple-\nminded compilers.\n\nI should point out that, by setting the high bits to zero, we are\nin effect treating the numbers as UNSIGNED integers. If we want\nto treat them as signed ones instead (the more likely case) we\nshould do a sign extension after the load, instead of a clear\nbefore it. Just to tie this part of the discussion up with a\nnice, red ribbon, let's change LoadVar as shown below:\n\n```delphi\n{---------------------------------------------------------------}\n{ Load a Variable to Primary Register }\n\nprocedure LoadVar(Name, Typ: char);\nbegin\n if Typ = 'B' then\n EmitLn('CLR.L D0');\n Move(Typ, Name + '(PC)', 'D0');\n if Typ = 'W' then\n EmitLn('EXT.L D0');\nend;\n{---------------------------------------------------------------}\n```\n\nWith this version, a byte is treated as unsigned (as in Pascal\nand C), while a word is treated as signed."},{title:"A MORE REASONABLE SOLUTION",path:"/a-more-reasonable-solution",content:"As we've seen, promoting every variable to long while it's in\nmemory solves the problem, but it can hardly be called efficient,\nand probably wouldn't be acceptable even for those of us who\nclaim be unconcerned about efficiency. It will mean that all\narithmetic operations will be done to 32-bit accuracy, which will\nDOUBLE the run time for most operations, and make it even worse\nfor multiplication and division. For those operations, we would\nneed to call subroutines to do them, even if the data were byte\nor word types. The whole thing is sort of a cop-out, too, since\nit ducks all the real issues.\n\nOK, so that solution's no good. Is there still a relatively easy\nway to get data conversion? Can we still Keep It Simple?\n\nYes, indeed. All we have to do is to make the conversion at the\nother end ... that is, we convert on the way _OUT_, when the data\nis stored, rather than on the way in.\n\nBut, remember, the storage part of the assignment is pretty much\nindependent of the data load, which is taken care of by procedure\nExpression. In general the expression may be arbitrarily\ncomplex, so how can procedure Assignment know what type of data\nis left in register D0?\n\nAgain, the answer is simple: We'll just _ASK_ procedure\nExpression! The answer can be returned as a function value.\n\nAll of this requires several procedures to be modified, but the\nmods, like the method, are quite simple. First of all, since we\naren't requiring LoadVar to do all the work of conversion, let's\ngo back to the simple version:\n\n```delphi\n{---------------------------------------------------------------}\n{ Load a Variable to Primary Register }\n\nprocedure LoadVar(Name, Typ: char);\nbegin\n Move(Typ, Name + '(PC)', 'D0');\nend;\n{--------------------------------------------------------------}\n```\n\nNext, let's add a new procedure that will convert from one type\nto another:\n\n```delphi\n{---------------------------------------------------------------}\n{ Convert a Data Item from One Type to Another }\n\n\nprocedure Convert(Source, Dest: char);\nbegin\n if Source <> Dest then begin\n if Source = 'B' then\n EmitLn('AND.W #$FF,D0');\n if Dest = 'L' then\n EmitLn('EXT.L D0');\n end;\nend;\n{--------------------------------------------------------------}\n```\n\nNext, we need to do the logic required to load and store a\nvariable of any type. Here are the routines for that:\n\n```delphi\n{---------------------------------------------------------------}\n{ Load a Variable to the Primary Register }\n\nfunction Load(Name: char): char;\nvar Typ : char;\nbegin\n Typ := VarType(Name);\n LoadVar(Name, Typ);\n Load := Typ;\nend;\n\n\n{--------------------------------------------------------------}\n{ Store a Variable from the Primary Register }\n\nprocedure Store(Name, T1: char);\nvar T2: char;\nbegin\n T2 := VarType(Name);\n Convert(T1, T2);\n StoreVar(Name, T2);\nend;\n{--------------------------------------------------------------}\n```\n\nNote that Load is a function, which not only emits the code for a\nload, but also returns the variable type. In this way, we always\nknow what type of data we are dealing with. When we execute a\nStore, we pass it the current type of the variable in D0. Since\nStore also knows the type of the destination variable, it can\nconvert as necessary.\n\nArmed with all these new routines, the implementation of our\nrudimentary assignment statement is essentially trivial.\nProcedure Expression now becomes a function, which returns its\ntype to procedure Assignment:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nfunction Expression: char;\nbegin\n Expression := Load(GetName);\nend;\n\n\n{--------------------------------------------------------------}\n{ Parse and Translate an Assignment Statement }\n\nprocedure Assignment;\nvar Name: char;\nbegin\n Name := GetName;\n Match('=');\n Store(Name, Expression);\nend;\n{--------------------------------------------------------------}\n```\nAgain, note how incredibly simple these two routines are. We've\nencapsulated all the type logic into Load and Store, and the\ntrick of passing the type around makes the rest of the work\nextremely easy. Of course, all of this is for our special,\ntrivial case of Expression. Naturally, for the general case it\nwill have to get more complex. But you're looking now at the\nFINAL version of procedure Assignment!\n\nAll this seems like a very simple and clean solution, and it is\nindeed. Compile this program and run the same test cases as\nbefore. You will see that all types of data are converted\nproperly, and there are few if any wasted instructions. Only the\nbyte-to-long conversion uses two instructions where one would do,\nand we could easily modify Convert to handle this case, too.\n\nAlthough we haven't considered unsigned variables in this case, I\nthink you can see that we could easily fix up procedure Convert\nto deal with these types as well. This is \"left as an exercise\nfor the student.\""},{title:"LITERAL ARGUMENTS",path:"/literal-arguments",content:"Sharp-eyed readers might have noticed, though, that we don't even\nhave a proper form of a simple factor yet, because we don't allow\nfor loading literal constants, only variables. Let's fix that\nnow.\n\nTo begin with, we'll need a GetNum function. We've seen several\nversions of this, some returning only a single character, some a\nstring, and some an integer. The one needed here will return a\nLongInt, so that it can handle anything we throw at it. Note\nthat no type information is returned here: GetNum doesn't concern\nitself with how the number will be used:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNum: LongInt;\nvar Val: LongInt;\nbegin\n if not IsDigit(Look) then Expected('Integer');\n Val := 0;\n while IsDigit(Look) do begin\n Val := 10 * Val + Ord(Look) - Ord('0');\n GetChar;\n end;\n GetNum := Val;\n SkipWhite;\nend;\n{---------------------------------------------------------------}\n```\n\nNow, when dealing with literal data, we have one little small\nproblem. With variables, we know what type things should be\nbecause they've been declared to be that type. We have no such\ntype information for literals. When the programmer says, \"-1,\"\ndoes that mean a byte, word, or longword version? We have no\nclue. The obvious thing to do would be to use the largest type\npossible, i.e. a longword. But that's a bad idea, because when\nwe get to more complex expressions, we'll find that it will cause\nevery expression involving literals to be promoted to long, as\nwell.\n\nA better approach is to select a type based upon the value of the\nliteral, as shown next:\n\n```delphi\n{--------------------------------------------------------------}\n{ Load a Constant to the Primary Register }\n\nfunction LoadNum(N: LongInt): char;\nvar Typ : char;\nbegin\n if abs(N) <= 127 then\n Typ := 'B'\n else if abs(N) <= 32767 then\n Typ := 'W'\n else Typ := 'L';\n LoadConst(N, Typ);\n LoadNum := Typ;\nend;\n{---------------------------------------------------------------}\n```\n\n(I know, I know, the number base isn't really symmetric. You can\nstore -128 in a single byte, and -32768 in a word. But that's\neasily fixed, and not worth the time or the added complexity to\nfool with it here. It's the thought that counts.)\n\nNote that LoadNum calls a new version of the code generator\nroutine LoadConst, which has an added argument to define the\ntype:\n\n```delphi\n{---------------------------------------------------------------}\n{ Load a Constant to the Primary Register }\n\nprocedure LoadConst(N: LongInt; Typ: char);\nvar temp:string;\nbegin\n Str(N, temp);\n Move(Typ, '#' + temp, 'D0');\nend;\n{--------------------------------------------------------------}\n```\n\nNow we can modify procedure Expression to accomodate the two\npossible kinds of factors:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nfunction Expression: char;\nbegin\n if IsAlpha(Look) then\n Expression := Load(GetName)\n else\n Expression := LoadNum(GetNum);\nend;\n{--------------------------------------------------------------}\n```\n\n(Wow, that sure didn't hurt too bad! Just a few extra lines do\nthe job.)\n\nOK, compile this code into your program and give it a try.\nYou'll see that it now works for either variables or constants as\nvalid expressions."},{title:"ADDITIVE EXPRESSIONS",path:"/additive-expressions",content:"If you've been following this series from the beginning, I'm sure\nyou know what's coming next: We'll expand the form for an\nexpression to handle first additive expressions, then\nmultiplicative, then general expressions with parentheses.\n\nThe nice part is that we already have a pattern for dealing with\nthese more complex expressions. All we have to do is to make\nsure that all the procedures called by Expression (Term, Factor,\netc.) always return a type identifier. If we do that, the\nprogram structure gets changed hardly at all.\n\nThe first step is easy: We can rename our existing function\nExpression to Term, as we've done so many times before, and\ncreate the new version of Expression:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate an Expression }\n\nfunction Expression: char;\nvar Typ: char;\nbegin\n if IsAddop(Look) then\n Typ := Unop\n else\n Typ := Term;\n while IsAddop(Look) do begin\n Push(Typ);\n case Look of\n '+': Typ := Add(Typ);\n '-': Typ := Subtract(Typ);\n end;\n end;\n Expression := Typ;\nend;\n{--------------------------------------------------------------}\n```\n\nNote in this routine how each procedure call has become a\nfunction call, and how the local variable Typ gets updated at\neach pass.\n\nNote also the new call to a function Unop, which lets us deal\nwith a leading unary minus. This change is not necessary ... we\ncould still use a form more like what we've done before. I've\nchosen to introduce UnOp as a separate routine because it will\nmake it easier, later, to produce somewhat better code than we've\nbeen doing. In other words, I'm looking ahead to optimization\nissues.\n\nFor this version, though, we'll retain the same dumb old code,\nwhich makes the new routine trivial:\n\n```delphi\n{---------------------------------------------------------------}\n{ Process a Term with Leading Unary Operator }\n\nfunction Unop: char;\nbegin\n Clear;\n Unop := 'W';\nend;\n{---------------------------------------------------------------}\n```\n\nProcedure Push is a code-generator routine, and now has a type\nargument:\n\n```delphi\n{---------------------------------------------------------------}\n{ Push Primary onto Stack }\n\nprocedure Push(Size: char);\nbegin\n Move(Size, 'D0', '-(SP)');\nend;\n{---------------------------------------------------------------}\n```\n\nNow, let's take a look at functions Add and Subtract. In the\nolder versions of these routines, we let them call code generator\nroutines PopAdd and PopSub. We'll continue to do that, which\nmakes the functions themselves extremely simple:\n\n```delphi\n{---------------------------------------------------------------}\n{ Recognize and Translate an Add }\n\nfunction Add(T1: char): char;\nbegin\n Match('+');\n Add := PopAdd(T1, Term);\nend;\n\n\n{-------------------------------------------------------------}\n{ Recognize and Translate a Subtract }\n\nfunction Subtract(T1: char): char;\nbegin\n Match('-');\n Subtract := PopSub(T1, Term);\nend;\n{---------------------------------------------------------------}\n```\n\nThe simplicity is deceptive, though, because what we've done is\nto defer all the logic to PopAdd and PopSub, which are no longer\njust code generation routines. They must also now take care of\nthe type conversions required.\n\nAnd just what conversion is that? Simple: Both arguments must be\nof the same size, and the result is also of that size. The\nsmaller of the two arguments must be \"promoted\" to the size of\nthe larger one.\n\nBut this presents a bit of a problem. If the argument to be\npromoted is the second argument (i.e. in the primary register\nD0), we are in great shape. If it's not, however, we're in a\nfix: we can't change the size of the information that's already\nbeen pushed onto the stack.\n\nThe solution is simple but a little painful: We must abandon that\nlovely \"pop the data and do something with it\" instructions\nthoughtfully provided by Motorola.\n\nThe alternative is to assign a secondary register, which I've\nchosen to be R7. (Why not R1? Because I have later plans for\nthe other registers.)\n\nThe first step in this new structure is to introduce a Pop\nprocedure analogous to the Push. This procedure will always Pop\nthe top element of the stack into D7:\n\n```delphi\n{---------------------------------------------------------------}\n{ Pop Stack into Secondary Register }\n\nprocedure Pop(Size: char);\nbegin\n Move(Size, '(SP)+', 'D7');\nend;\n{---------------------------------------------------------------}\n```\n\nThe general idea is that all the \"Pop-Op\" routines can call this\none. When this is done, we will then have both operands in\nregisters, so we can promote whichever one we need to. To deal\nwith this, procedure Convert needs another argument, the register\nname:\n\n```delphi\n{---------------------------------------------------------------}\n{ Convert a Data Item from One Type to Another }\n\nprocedure Convert(Source, Dest: char; Reg: String);\nbegin\n if Source <> Dest then begin\n if Source = 'B' then\n EmitLn('AND.W #$FF,' + Reg);\n if Dest = 'L' then\n EmitLn('EXT.L ' + Reg);\n end;\nend;\n{---------------------------------------------------------------}\n```\n\nThe next function does a conversion, but only if the current type\nT1 is smaller in size than the desired type T2. It is a\nfunction, returning the final type to let us know what it decided\nto do:\n\n```delphi\n{---------------------------------------------------------------}\n{ Promote the Size of a Register Value }\n\nfunction Promote(T1, T2: char; Reg: string): char;\nvar Typ: char;\nbegin\n Typ := T1;\n if T1 <> T2 then\n if (T1 = 'B') or ((T1 = 'W') and (T2 = 'L')) then begin\n Convert(T1, T2, Reg);\n Typ := T2;\n end;\n Promote := Typ;\nend;\n{---------------------------------------------------------------}\n```\n\nFinally, the following function forces the two registers to be of\nthe same type:\n\n```delphi\n{---------------------------------------------------------------}\n{ Force both Arguments to Same Type }\n\nfunction SameType(T1, T2: char): char;\nbegin\n T1 := Promote(T1, T2, 'D7');\n SameType := Promote(T2, T1, 'D0');\nend;\n{---------------------------------------------------------------}\n```\n\nThese new routines give us the ammunition we need to flesh out\nPopAdd and PopSub:\n\n```delphi\n{---------------------------------------------------------------}\n{ Generate Code to Add Primary to the Stack }\n\nfunction PopAdd(T1, T2: char): char;\nbegin\n Pop(T1);\n T2 := SameType(T1, T2);\n GenAdd(T2);\n PopAdd := T2;\nend;\n\n\n{---------------------------------------------------------------}\n{ Generate Code to Subtract Primary from the Stack }\n\nfunction PopSub(T1, T2: char): char;\nbegin\n Pop(T1);\n T2 := SameType(T1, T2);\n GenSub(T2);\n PopSub := T2;\nend;\n{---------------------------------------------------------------}\n```\n\nAfter all the buildup, the final results are almost\nanticlimactic. Once again, you can see that the logic is quite\nsimple. All the two routines do is to pop the top-of-stack into\nD7, force the two operands to be the same size, and then generate\nthe code.\n\nNote the new code generator routines GenAdd and GenSub. These\nare vestigial forms of the ORIGINAL PopAdd and PopSub. That is,\nthey are pure code generators, producing a register-to-register\nadd or subtract:\n\n```delphi\n{---------------------------------------------------------------}\n{ Add Top of Stack to Primary }\n\nprocedure GenAdd(Size: char);\nbegin\n EmitLn('ADD.' + Size + ' D7,D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Subtract Primary from Top of Stack }\n\nprocedure GenSub(Size: char);\nbegin\n EmitLn('SUB.' + Size + ' D7,D0');\n EmitLn('NEG.' + Size + ' D0');\nend;\n{---------------------------------------------------------------}\n```\n\nOK, I grant you: I've thrown a lot of routines at you since we\nlast tested the code. But you have to admit that each new\nroutine is pretty simple and transparent. If you (like me) don't\nlike to test so many new routines at once, that's OK. You can\nstub out routines like Convert, Promote, and SameType, since they\ndon't read any inputs. You won't get the correct code, of\ncourse, but things should work. Then flesh them out one at a\ntime.\n\nWhen testing the program, don't forget that you first have to\ndeclare some variables, and then start the \"body\" of the program\nwith an upper-case 'B' (for BEGIN). You should find that the\nparser will handle any additive expressions. Once all the\nconversion routines are in, you should see that the correct code\nis generated, with type conversions inserted where necessary.\nTry mixing up variables of different sizes, and also literals.\nMake sure that everything's working properly. As usual, it's a\ngood idea to try some erroneous expressions and see how the\ncompiler handles them."},{title:"WHY SO MANY PROCEDURES?",path:"/why-so-many-procedures",content:"At this point, you may think I've pretty much gone off the deep\nend in terms of deeply nested procedures. There is admittedly a\nlot of overhead here. But there's a method in my madness. As in\nthe case of UnOp, I'm looking ahead to the time when we're going\nto want better code generation. The way the code is organized,\nwe can achieve this without major modifications to the program.\nFor example, in cases where the value pushed onto the stack does\n_NOT_ have to be converted, it's still better to use the \"pop and\nadd\" instruction. If we choose to test for such cases, we can\nembed the extra tests into PopAdd and PopSub without changing\nanything else much."},{title:"MULTIPLICATIVE EXPRESSIONS",path:"/multiplicative-expressions",content:"The procedure for dealing with multiplicative operators is much\nthe same. In fact, at the first level, they are almost\nidentical, so I'll just show them here without much fanfare. The\nfirst one is our general form for Factor, which includes\nparenthetical subexpressions:\n\n```delphi\n{---------------------------------------------------------------}\n{ Parse and Translate a Factor }\n\nfunction Expression: char; Forward;\n\nfunction Factor: char;\nbegin\n if Look = '(' then begin\n Match('(');\n Factor := Expression;\n Match(')');\n end\n else if IsAlpha(Look) then\n Factor := Load(GetName)\n else\n Factor := LoadNum(GetNum);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Multiply }\n\nFunction Multiply(T1: char): char;\nbegin\n Match('*');\n Multiply := PopMul(T1, Factor);\nend;\n\n\n{--------------------------------------------------------------}\n{ Recognize and Translate a Divide }\n\nfunction Divide(T1: char): char;\nbegin\n Match('/');\n DIvide := PopDiv(T1, Factor);\nend;\n\n\n{---------------------------------------------------------------}\n{ Parse and Translate a Math Term }\n\nfunction Term: char;\nvar Typ: char;\nbegin\n Typ := Factor;\n while IsMulop(Look) do begin\n Push(Typ);\n case Look of\n '*': Typ := Multiply(Typ);\n '/': Typ := Divide(Typ);\n end;\n end;\n Term := Typ;\nend;\n{---------------------------------------------------------------}\n```\n\nThese routines parallel the additive ones almost exactly. As\nbefore, the complexity is encapsulated within PopMul and PopDiv.\nIf you'd like to test the program before we get into that, you\ncan build dummy versions of them, similar to PopAdd and PopSub.\nAgain, the code won't be correct at this point, but the parser\nshould handle expressions of arbitrary complexity."},{title:"MULTIPLICATION",path:"/multiplication",content:"Once you've convinced yourself that the parser itself is working\nproperly, we need to figure out what it will take to generate the\nright code. This is where things begin to get a little sticky,\nbecause the rules are more complex.\n\nLet's take the case of multiplication first. This operation is\nsimilar to the \"addops\" in that both operands should be of the\nsame size. It differs in two important respects:\n\n\n - The type of the product is typically not the same as that of\n the two operands. For the product of two words, we get a\n longword result.\n\n - The 68000 does not support a 32 x 32 multiply, so a call to\n a software routine is needed. This routine will become part\n of the run-time library.\n\n - It also does not support an 8 x 8 multiply, so all byte\n operands must be promoted to words.\n\n\nThe actions that we have to take are best shown in the following\ntable:\n```\n-----------------------------------------------------------------\n T1 --\x3e | | | |\n | | | |\n | | B | W | L |\n T2 V | | | |\n-----------------------------------------------------------------\n | | | |\n B | Convert D0 to W | Convert D0 to W | Convert D0 to L |\n | Convert D7 to W | | |\n | MULS | MULS | JSR MUL32 |\n | Result = W | Result = L | Result = L |\n | | | |\n-----------------------------------------------------------------\n | | | |\n W | Convert D7 to W | | Convert D0 to L |\n | MULS | MULS | JSR MUL32 |\n | Result = L | Result = L | Result = L |\n | | | |\n-----------------------------------------------------------------\n | | | |\n L | Convert D7 to L | Convert D7 to L | |\n | JSR MUL32 | JSR MUL32 | JSR MUL32 |\n | Result = L | Result = L | Result = L |\n | | | |\n-----------------------------------------------------------------\n```\nThis table shows the actions to be taken for each combination of\noperand types. There are three things to note: First, we assume\na library routine MUL32 which performs a 32 x 32 multiply,\nleaving a >> 32-bit << (not 64-bit) product. If there is any\noverflow in the process, we choose to ignore it and return only\nthe lower 32 bits.\n\nSecond, note that the table is symmetric ... the two operands\nenter in the same way. Finally, note that the product is ALWAYS\na longword, except when both operands are bytes. (It's worth\nnoting, in passing, that this means that many expressions will\nend up being longwords, whether we like it or not. Perhaps the\nidea of just promoting them all up front wasn't all that\noutrageous, after all!)\n\nNow, clearly, we are going to have to generate different code for\nthe 16-bit and 32-bit multiplies. This is best done by having\nseparate code generator routines for the two cases:\n\n```delphi\n{---------------------------------------------------------------}\n{ Multiply Top of Stack by Primary (Word) }\n\nprocedure GenMult;\nbegin\n EmitLn('MULS D7,D0')\nend;\n\n\n{---------------------------------------------------------------}\n{ Multiply Top of Stack by Primary (Long) }\n\nprocedure GenLongMult;\nbegin\n EmitLn('JSR MUL32');\nend;\n{---------------------------------------------------------------}\n```\n\nAn examination of the code below for PopMul should convince you\nthat the conditions in the table are met:\n\n```delphi\n{---------------------------------------------------------------}\n{ Generate Code to Multiply Primary by Stack }\n\nfunction PopMul(T1, T2: char): char;\nvar T: char;\nbegin\n Pop(T1);\n T := SameType(T1, T2);\n Convert(T, 'W', 'D7');\n Convert(T, 'W', 'D0');\n if T = 'L' then\n GenLongMult\n else\n GenMult;\n if T = 'B' then\n PopMul := 'W'\n else\n PopMul:= 'L';\nend;\n{---------------------------------------------------------------}\n```\n\nAs you can see, the routine starts off just like PopAdd. The two\narguments are forced to the same type. The two calls to Convert\ntake care of the case where both operands are bytes. The data\nthemselves are promoted to words, but the routine remembers the\ntype so as to assign the correct type to the result. Finally, we\ncall one of the two code generator routines, and then assign the\nresult type. Not too complicated, really.\n\nAt this point, I suggest that you go ahead and test the program.\nTry all combinations of operand sizes."},{title:"DIVISION",path:"/division",content:"The case of division is not nearly so symmetric. I also have\nsome bad news for you:\n\nAll modern 16-bit CPU's support integer divide. The\nmanufacturer's data sheet will describe this operation as a\n32 x 16-bit divide, meaning that you can divide a 32-bit dividend\nby a 16-bit divisor. Here's the bad news: `THEY'RE LYING TO YOU!!!`\n\n\nIf you don't believe it, try dividing any large 32-bit number\n(meaning that it has non-zero bits in the upper 16 bits) by the\ninteger 1. You are guaranteed to get an overflow exception.\n\nThe problem is that the instruction really requires that the\nresulting quotient fit into a 16-bit result. This won't happen\nUNLESS the divisor is sufficiently large. When any number is\ndivided by unity, the quotient will of course be the same as the\ndividend, which had better fit into a 16-bit word.\n\nSince the beginning of time (well, computers, anyway), CPU\narchitects have provided this little gotcha in the division\ncircuitry. It provides a certain amount of symmetry in things,\nsince it is sort of the inverse of the way a multiply works. But\nsince unity is a perfectly valid (and rather common) number to\nuse as a divisor, the division as implemented in hardware needs\nsome help from us programmers.\n\nThe implications are as follows:\n\n - The type of the quotient must always be the same as that of\n the dividend. It is independent of the divisor.\n\n - In spite of the fact that the CPU supports a longword\n dividend, the hardware-provided instruction can only be\n trusted for byte and word dividends. For longword\n dividends, we need another library routine that can return a\n long result.\n\n\n\nThis looks like a job for another table, to summarize the\nrequired actions:\n-----------------------------------------------------------------\n T1 --\x3e | | | |\n | | | |\n | | B | W | L |\n T2 V | | | |\n-----------------------------------------------------------------\n | | | |\n B | Convert D0 to W | Convert D0 to W | Convert D0 to L |\n | Convert D7 to L | Convert D7 to L | |\n | DIVS | DIVS | JSR DIV32 |\n | Result = B | Result = W | Result = L |\n | | | |\n-----------------------------------------------------------------\n | | | |\n W | Convert D7 to L | Convert D7 to L | Convert D0 to L |\n | DIVS | DIVS | JSR DIV32 |\n | Result = B | Result = W | Result = L |\n | | | |\n-----------------------------------------------------------------\n | | | |\n L | Convert D7 to L | Convert D7 to L | |\n | JSR DIV32 | JSR DIV32 | JSR DIV32 |\n | Result = B | Result = W | Result = L |\n | | | |\n-----------------------------------------------------------------\n\n\n(You may wonder why it's necessary to do a 32-bit division, when\nthe dividend is, say, only a byte in the first place. Since the\nnumber of bits in the result can only be as many as that in the\ndividend, why bother? The reason is that, if the divisor is a\nlongword, and there are any high bits set in it, the result of\nthe division must be zero. We might not get that if we only use\nthe lower word of the divisor.)\n\nThe following code provides the correct function for PopDiv:\n\n```delphi\n{---------------------------------------------------------------}\n{ Generate Code to Divide Stack by the Primary }\n\nfunction PopDiv(T1, T2: char): char;\nbegin\n Pop(T1);\n Convert(T1, 'L', 'D7');\n if (T1 = 'L') or (T2 = 'L') then begin\n Convert(T2, 'L', 'D0');\n GenLongDiv;\n PopDiv := 'L';\n end\n else begin\n Convert(T2, 'W', 'D0');\n GenDiv;\n PopDiv := T1;\n end;\nend;\n{---------------------------------------------------------------}\n```\n\nThe two code generation procedures are:\n\n```delphi\n{---------------------------------------------------------------}\n{ Divide Top of Stack by Primary (Word) }\n\nprocedure GenDiv;\nbegin\n EmitLn('DIVS D0,D7');\n Move('W', 'D7', 'D0');\nend;\n\n\n{---------------------------------------------------------------}\n{ Divide Top of Stack by Primary (Long) }\n\nprocedure GenLongDiv;\nbegin\n EmitLn('JSR DIV32');\nend;\n{---------------------------------------------------------------}\n\n```\n\nNote that we assume that DIV32 leaves the (longword) result in\n`D0`.\n\nOK, install the new procedures for division. At this point you\nshould be able to generate code for any kind of arithmetic\nexpression. Give it a whirl!"},{title:"BEGINNING TO WIND DOWN",path:"/beginning-to-wind-down",content:"At last, in this installment, we've learned how to deal with\nvariables (and literals) of different types. As you can see, it\nhasn't been too tough. In fact, in some ways most of the code\nlooks even more simple than it does in earlier programs. Only\nthe multiplication and division operators require a little\nthinking and planning.\n\nThe main concept that made things easy was that of converting\nprocedures such as Expression into functions that return the type\nof the result. Once this was done, we were able to retain the\nsame general structure of the compiler.\n\nI won't pretend that we've covered every single aspect of the\nissue. I conveniently ignored unsigned arithmetic. From what\nwe've done, I think you can see that to include them adds no new\nchallenges, just extra possibilities to test for.\n\nI've also ignored the logical operators And, Or, etc. It turns\nout that these are pretty easy to handle. All the logical\noperators are bitwise operations, so they are symmetric and\ntherefore work in the same fashion as PopAdd. There is one\ndifference, however: if it is necessary to extend the word\nlength for a logical variable, the extension should be done as an\nUNSIGNED number. Floating point numbers, again, are\nstraightforward to handle ... just a few more procedures to be\nadded to the run-time library, or perhaps instructions for a math\nchip.\n\nPerhaps more importantly, I have also skirted the issue of type\nCHECKING, as opposed to conversion. In other words, we've\nallowed for operations between variables of all combinations of\ntypes. In general this will not be true ... certainly you don't\nwant to add an integer, for example, to a string. Most languages\nalso don't allow you to mix up character and integer variables.\n\nAgain, there are really no new issues to be addressed in this\ncase. We are already checking the types of the two operands ...\nmuch of this checking gets done in procedures like SameType.\nIt's pretty straightforward to include a call to an error\nhandler, if the types of the two operands are incompatible.\n\nIn the general case, we can think of every single operator as\nbeing handled by a different procedure, depending upon the type\nof the two operands. This is straightforward, though tedious, to\nimplement simply by implementing a jump table with the operand\ntypes as indices. In Pascal, the equivalent operation would\ninvolve nested Case statements. Some of the called procedures\ncould then be simple error routines, while others could effect\nwhatever kind of conversion we need. As more types are added,\nthe number of procedures goes up by a square-law rule, but that's\nstill not an unreasonably large number of procedures.\n\nWhat we've done here is to collapse such a jump table into far\nfewer procedures, simply by making use of symmetry and other\nsimplifying rules."},{title:"TO COERCE OR NOT TO COERCE",path:"/to-coerce-or-not-to-coerce",content:"In case you haven't gotten this message yet, it sure appears that\nTINY and KISS will probably _NOT_ be strongly typed languages,\nsince I've allowed for automatic mixing and conversion of just\nabout any type. Which brings up the next issue: `Is this really what we want to do?` \n\nThe answer depends on what kind of language you want, and the way\nyou'd like it to behave. What we have not addressed is the issue\nof when to allow and when to deny the use of operations involving\ndifferent data types. In other words, what should be the\nSEMANTICS of our compiler? Do we want automatic type conversion\nfor all cases, for some cases, or not at all?\n\nLet's pause here to think about this a bit more. To do so, it\nwill help to look at a bit of history.\n\nFORTRAN II supported only two simple data types: Integer and\nReal. It allowed implicit type conversion between real and\ninteger types during assignment, but not within expressions. All\ndata items (including literal constants) on the right-hand side\nof an assignment statement had to be of the same type. That made\nthings pretty easy ... much simpler than what we've had to do\nhere.\n\nThis was changed in FORTRAN IV to support \"mixed-mode\"\narithmetic. If an expression had any real data items in it, they\nwere all converted to reals and the expression itself was real.\nTo round out the picture, functions were provided to explicitly\nconvert from one type to the other, so that you could force an\nexpression to end up as either type.\n\nThis led to two things: code that was easier to write, and code\nthat was less efficient. That's because sloppy programmers would\nwrite expressions with simple constants like 0 and 1 in them,\nwhich the compiler would dutifully compile to convert at\nexecution time. Still, the system worked pretty well, which\nwould tend to indicate that implicit type conversion is a Good\nThing.\n\nC is also a weakly typed language, though it supports a larger\nnumber of types. C won't complain if you try to add a character\nto an integer, for example. Partly, this is helped by the C\nconvention of promoting every char to integer when it is loaded,\nor passed through a parameter list. This simplifies the\nconversions quite a bit. In fact, in subset C compilers that\ndon't support long or float types, we end up back where we were\nin our earlier, simple-minded first try: every variable has the\nsame representation, once loaded into a register. Makes life\npretty easy!\n\nThe ultimate language in the direction of automatic type\nconversion is PL/I. This language supports a large number of\ndata types, and you can mix them all freely. If the implicit\nconversions of FORTRAN seemed good, then those of PL/I should\nhave been Heaven, but it turned out to be more like Hell! The\nproblem was that with so many data types, there had to be a large\nnumber of different conversions, AND a correspondingly large\nnumber of rules about how mixed operands should be converted.\nThese rules became so complex that no one could remember what\nthey were! A lot of the errors in PL/I programs had to do with\nunexpected and unwanted type conversions. Too much of a Good\nThing can be bad for you!\n\nPascal, on the other hand, is a language which is \"strongly\ntyped,\" which means that in general you can't mix types, even if\nthey differ only in _NAME_, and yet have the same base type!\nNiklaus Wirth made Pascal strongly typed to help keep programmers\nout of trouble, and the restrictions have indeed saved many a\nprogrammer from himself, because the compiler kept him from doing\nsomething dumb. Better to find the bug in compilation rather\nthan the debug phase. The same restrictions can also cause\nfrustration when you really WANT to mix types, and they tend to\ndrive an ex-C-programmer up the wall.\n\nEven so, Pascal does permit some implicit conversions. You can\nassign an integer to a real value. You can also mix integer and\nreal types in expressions of type Real. The integers will be\nautomatically coerced to real, just as in FORTRAN (and with the\nsame hidden cost in run-time overhead).\n\nYou can't, however, convert the other way, from real to integer,\nwithout applying an explicit conversion function, Trunc. The\ntheory here is that, since the numerical value of a real number\nis necessarily going to be changed by the conversion (the\nfractional part will be lost), you really shouldn't do it in\n\"secret.\"\n\nIn the spirit of strong typing, Pascal will not allow you to mix\nChar and Integer variables, without applying the explicit\ncoercion functions Chr and Ord.\n\nTurbo Pascal also includes the types Byte, Word, and LongInt.\nThe first two are basically the same as unsigned integers. In\nTurbo, these can be freely intermixed with variables of type\nInteger, and Turbo will automatically handle the conversion.\nThere are run-time checks, though, to keep you from overflowing\nor otherwise getting the wrong answer. Note that you still can't\nmix Byte and Char types, even though they are stored internally\nin the same representation.\n\nThe ultimate in a strongly-typed language is Ada, which allows\n_NO_ implicit type conversions at all, and also will not allow\nmixed-mode arithmetic. Jean Ichbiah's position is that\nconversions cost execution time, and you shouldn't be allowed to\nbuild in such cost in a hidden manner. By forcing the programmer\nto explicitly request a type conversion, you make it more\napparent that there could be a cost involved.\n\nI have been using another strongly-typed language, a delightful\nlittle language called Whimsical, by John Spray. Although\nWhimsical is intended as a systems programming language, it also\nrequires explicit conversion EVERY time. There are NEVER any\nautomatic conversions, even the ones supported by Pascal.\n\nThis approach does have certain advantages: The compiler never\nhas to guess what to do: the programmer always tells it precisely\nwhat he wants. As a result, there tends to be a more nearly\none-to-one correspondence between source code and compiled code,\nand John's compiler produces VERY tight code.\n\nOn the other hand, I sometimes find the explicit conversions to\nbe a pain. If I want, for example, to add one to a character, or\nAND it with a mask, there are a lot of conversions to make. If I\nget it wrong, the only error message is \"Types are not\ncompatible.\" As it happens, John's particular implementation of\nthe language in his compiler doesn't tell you exactly WHICH types\nare not compatible ... it only tells you which LINE the error is\nin.\n\nI must admit that most of my errors with this compiler tend to be\nerrors of this type, and I've spent a lot of time with the\nWhimsical compiler, trying to figure out just WHERE in the line\nI've offended it. The only real way to fix the error is to keep\ntrying things until something works.\n\nSo what should we do in TINY and KISS? For the first one, I have\nthe answer: TINY will support only the types Char and Integer,\nand we'll use the C trick of promoting Chars to Integers\ninternally. That means that the TINY compiler will be _MUCH_\nsimpler than what we've already done. Type conversion in\nexpressions is sort of moot, since none will be required! Since\nlongwords will not be supported, we also won't need the MUL32 and\nDIV32 run-time routines, nor the logic to figure out when to call\nthem. I _LIKE_ it!\n\nKISS, on the other hand, will support the type Long.\n\nShould it support both signed and unsigned arithmetic? For the\nsake of simplicity I'd rather not. It does add quite a bit to\nthe complexity of type conversions. Even Niklaus Wirth has\neliminated unsigned (Cardinal) numbers from his new language\nOberon, with the argument that 32-bit integers should be long\nenough for anybody, in either case.\n\nBut KISS is supposed to be a systems programming language, which\nmeans that we should be able to do whatever operations that can\nbe done in assembler. Since the 68000 supports both flavors of\nintegers, I guess KISS should, also. We've seen that logical\noperations need to be able to extend integers in an unsigned\nfashion, so the unsigned conversion procedures are required in\nany case."},{title:"CONCLUSION",path:"/conclusion",content:"That wraps up our session on type conversions. Sorry you had to\nwait so long for it, but hope you feel that it was worth the\nwait.\n\nIn the next few installments, we'll extend the simple types to\ninclude arrays and pointers, and we'll have a look at what to do\nabout strings. That should pretty well wrap up the mainstream\npart of the series. After that, I'll give you the new versions\nof the TINY and KISS compilers, and then we'll start to look at\noptimization issues.\n\nSee you then."}]},{title:"Part 15: BACK TO THE FUTURE - 5 March 1994",path:"/part-15-back-to-the-future-5-march-1994",items:[{title:"INTRODUCTION",path:"/introduction",content:"Can it really have been four years since I wrote installment \nfourteen of this series? Is it really possible that six long \nyears have passed since I began it? Funny how time flies when \nyou're having fun, isn't it? \n\nI won't spend a lot of time making excuses; only point out that \nthings happen, and priorities change. In the four years since \ninstallment fourteen, I've managed to get laid off, get divorced, \nhave a nervous breakdown, begin a new career as a writer, begin \nanother one as a consultant, move, work on two real-time systems, \nand raise fourteen baby birds, three pigeons, six possums, and a \nduck. For awhile there, the parsing of source code was not high \non my list of priorities. Neither was writing stuff for free, \ninstead of writing stuff for pay. But I do try to be faithful, \nand I do recognize and feel my responsibility to you, the reader, \nto finish what I've started. As the tortoise said in one of my \nson's old stories, I may be slow, but I'm sure. I'm sure that \nthere are people out there anxious to see the last reel of this \nfilm, and I intend to give it to them. So, if you're one of those \nwho's been waiting, more or less patiently, to see how this thing \ncomes out, thanks for your patience. I apologize for the delay. \nLet's move on."},{title:"NEW STARTS, OLD DIRECTIONS",path:"/new-starts-old-directions",content:"Like many other things, programming languages and programming \nstyles change with time. In 1994, it seems a little anachronistic \nto be programming in Turbo Pascal, when the rest of the world \nseems to have gone bananas over C++. It also seems a little \nstrange to be programming in a classical style when the rest of \nthe world has switched to object-oriented methods. Still, in \nspite of the four-year hiatus, it would be entirely too wrenching \na change, at this point, to switch to, say, C++ with object-\norientation . Anyway, Pascal is still not only a powerful \nprogramming language (more than ever, in fact), but it's a \nwonderful medium for teaching. C is a notoriously difficult \nlanguage to read ... it's often been accused, along with Forth, of \nbeing a \"write-only language.\" When I program in C++, I find \nmyself spending at least 50% of my time struggling with language \nsyntax rather than with concepts. A stray \"&\" or \"*\" can not only \nchange the functioning of the program, but its correctness as \nwell. By contrast, Pascal code is usually quite transparent and \neasy to read, even if you don't know the language. What you see is \nalmost always what you get, and we can concentrate on concepts \nrather than implementation details. I've said from the beginning \nthat the purpose of this tutorial series was not to generate the \nworld's fastest compiler, but to teach the fundamentals of \ncompiler technology, while spending the least amount of time \nwrestling with language syntax or other aspects of software \nimplementation. Finally, since a lot of what we do in this course \namounts to software experimentation, it's important to have a \ncompiler and associated environment that compiles quickly and with \nno fuss. In my opinion, by far the most significant time measure \nin software development is the speed of the edit/compile/test \ncycle. In this department, Turbo Pascal is king. The compilation \nspeed is blazing fast, and continues to get faster in every \nrelease (how do they keep doing that?). Despite vast improvements \nin C compilation speed over the years, even Borland's fastest \nC/C++ compiler is still no match for Turbo Pascal. Further, the \neditor built into their IDE, the make facility, and even their \nsuperb smart linker, all complement each other to produce a \nwonderful environment for quick turnaround. For all of these \nreasons, I intend to stick with Pascal for the duration of this \nseries. We'll be using Turbo Pascal for Windows, one of the \ncompilers provided Borland Pascal with Objects, version 7.0. If \nyou don't have this compiler, don't worry ... nothing we do here \nis going to count on your having the latest version. Using the \nWindows version helps me a lot, by allowing me to use the \nClipboard to copy code from the compiler's editor into these \ndocuments. It should also help you at least as much, copying the \ncode in the other direction. \n\nI've thought long and hard about whether or not to introduce \nobjects to our discussion. I'm a big advocate of object-oriented \nmethods for all uses, and such methods definitely have their place \nin compiler technology. In fact, I've written papers on just this \nsubject (Refs. 1-3). But the architecture of a compiler which is \nbased on object-oriented approaches is vastly different than that \nof the more classical compiler we've been building. Again, it \nwould seem to be entirely too much to change these horses in mid-\nstream. As I said, programming styles change. Who knows, it may \nbe another six years before we finish this thing, and if we keep \nchanging the code every time programming style changes, we may \nNEVER finish.\n\nSo for now, at least, I've determined to continue the classical \nstyle in Pascal, though we might indeed discuss objects and object \norientation as we go. Likewise, the target machine will remain \nthe Motorola 68000 family. Of all the decisions to be made here, \nthis one has been the easiest. Though I know that many of you \nwould like to see code for the 80x86, the 68000 has become, if \nanything, even more popular as a platform for embedded systems, \nand it's to that application that this whole effort began in the \nfirst place. Compiling for the PC, MSDOS platform, we'd have to \ndeal with all the issues of DOS system calls, DOS linker formats, \nthe PC file system and hardware, and all those other complications \nof a DOS environment. An embedded system, on the other hand, must \nrun standalone, and it's for this kind of application, as an \nalternative to assembly language, that I've always imagined that a \nlanguage like KISS would thrive. Anyway, who wants to deal with \nthe 80x86 architecture if they don't have to?\n\nThe one feature of Turbo Pascal that I'm going to be making heavy \nuse of is units. In the past, we've had to make compromises \nbetween code size and complexity, and program functionality. A \nlot of our work has been in the nature of computer \nexperimentation, looking at only one aspect of compiler technology \nat a time. We did this to avoid to avoid having to carry around \nlarge programs, just to investigate simple concepts. In the \nprocess, we've re-invented the wheel and re-programmed the same \nfunctions more times than I'd like to count. Turbo units provide \na wonderful way to get functionality and simplicity at the same \ntime: You write reusable code, and invoke it with a single line. \nYour test program stays small, but it can do powerful things.\n\nOne feature of Turbo Pascal units is their initialization block. \nAs with an Ada package, any code in the main begin-end block of a \nunit gets executed as the program is initialized. As you'll see \nlater, this sometimes gives us neat simplifications in the code. \nOur procedure Init, which has been with us since Installment 1, \ngoes away entirely when we use units. The various routines in the \nCradle, another key features of our approach, will get distributed \namong the units.\n\nThe concept of units, of course, is no different than that of C \nmodules. However, in C (and C++), the interface between modules \ncomes via preprocessor include statements and header files. As \nsomeone who's had to read a lot of other people's C programs, I've \nalways found this rather bewildering. It always seems that \nwhatever data structure you'd like to know about is in some other \nfile. Turbo units are simpler for the very reason that they're \ncriticized by some: The function interfaces and their \nimplementation are included in the same file. While this \norganization may create problems with code security, it also \nreduces the number of files by half, which isn't half bad. \nLinking of the object files is also easy, because the Turbo \ncompiler takes care of it without the need for make files or other \nmechanisms."},{title:"STARTING OVER?",path:"/starting-over",content:"Four years ago, in Installment 14, I promised you that our days of \nre-inventing the wheel, and recoding the same software over and \nover for each lesson, were over, and that from now on we'd stick \nto more complete programs that we would simply add new features \nto. I still intend to keep that promise; that's one of the main \npurposes for using units. However, because of the long time since \nInstallment 14, it's natural to want to at least do some review, \nand anyhow, we're going to have to make rather sweeping changes in \nthe code to make the transition to units. Besides, frankly, after \nall this time I can't remember all the neat ideas I had in my head \nfour years ago. The best way for me to recall them is to retrace \nsome of the steps we took to arrive at Installment 14. So I hope \nyou'll be understanding and bear with me as we go back to our \nroots, in a sense, and rebuild the core of the software, \ndistributing the routines among the various units, and \nbootstrapping ourselves back up to the point we were at lo, those \nmany moons ago. As has always been the case, you're going to get \nto see me make all the mistakes and execute changes of direction, \nin real time. Please bear with me ... we'll start getting to the \nnew stuff before you know it.\n\nSince we're going to be using multiple modules in our new \napproach, we have to address the issue of file management. If \nyou've followed all the other sections of this tutorial, you know \nthat, as our programs evolve, we're going to be replacing older, \nmore simple-minded units with more capable ones. This brings us to \nan issue of version control. There will almost certainly be times \nwhen we will overlay a simple file (unit), but later wish we had \nthe simple one again. A case in point is embodied in our \npredilection for using single-character variable names, keywords, \netc., to test concepts without getting bogged down in the details \nof a lexical scanner. Thanks to the use of units, we will be \ndoing much less of this in the future. Still, I not only suspect, \nbut am certain that we will need to save some older versions of \nfiles, for special purposes, even though they've been replaced by \nnewer, more capable ones.\n\nTo deal with this problem, I suggest that you create different \ndirectories, with different versions of the units as needed. If \nwe do this properly, the code in each directory will remain self-\nconsistent. I've tentatively created four directories: SINGLE \n(for single-character experimentation), MULTI (for, of course, \nmulti-character versions), TINY, and KISS.\n\nEnough said about philosophy and details. Let's get on with the \nresurrection of the software."},{title:"THE INPUT UNIT",path:"/the-input-unit",content:"A key concept that we've used since Day 1 has been the idea of an \ninput stream with one lookahead character. All the parsing \nroutines examine this character, without changing it, to decide \nwhat they should do next. (Compare this approach with the C/Unix \napproach using getchar and unget, and I think you'll agree that \nour approach is simpler). We'll begin our hike into the future by \ntranslating this concept into our new, unit-based organization. \nThe first unit, appropriately called Input, is shown below:\n\n```delphi\n{--------------------------------------------------------------}\nunit Input;\n{--------------------------------------------------------------}\ninterface\nvar Look: char; \t{ Lookahead character }\nprocedure GetChar; { Read new character }\n\n{--------------------------------------------------------------}\nimplementation\n\n{--------------------------------------------------------------}\n{ Read New Character From Input Stream }\n\nprocedure GetChar;\nbegin\n\tRead(Look);\nend;\n\n{--------------------------------------------------------------}\n{ Unit Initialization }\nbegin\n\tGetChar;\nend.\n{--------------------------------------------------------------}\n```\n\nAs you can see, there's nothing very profound, and certainly \nnothing complicated, about this unit, since it consists of only a \nsingle procedure. But already, we can see how the use of units \ngives us advantages. Note the executable code in the \ninitialization block. This code \"primes the pump\" of the input \nstream for us, something we've always had to do before, by \ninserting the call to GetChar in line, or in procedure Init. This \ntime, the call happens without any special reference to it on our \npart, except within the unit itself. As I predicted earlier, this \nmechanism is going to make our lives much simpler as we proceed.\nI consider it to be one of the most useful features of Turbo \nPascal, and I lean on it heavily. \n\nCopy this unit into your compiler's IDE, and compile it. To test \nthe software, of course, we always need a main program. I used \nthe following, really complex test program, which we'll later \nevolve into the Main for our compiler:\n\n```delphi\n{--------------------------------------------------------------}\nprogram Main;\nuses WinCRT, Input;\nbegin\n\tWriteLn(Look);\nend.\n{--------------------------------------------------------------}\n```\n\nNote the use of the Borland-supplied unit, WinCRT. This unit is \nnecessary if you intend to use the standard Pascal I/O routines, \nRead, ReadLn, Write, and WriteLn, which of course we intend to do.\nIf you forget to include this unit in the \"uses\" clause, you will \nget a really bizarre and indecipherable error message at run time.\n\nNote also that we can access the lookahead character, even though \nit's not declared in the main program. All variables declared \nwithin the interface section of a unit are global, but they're \nhidden from prying eyes; to that extent, we get a modicum of \ninformation hiding. Of course, if we were writing in an object-\noriented fashion, we should not allow outside modules to access \nthe units internal variables. But, although Turbo units have a \nlot in common with objects, we're not doing object-oriented design \nor code here, so our use of Look is appropriate.\n\nGo ahead and save the test program as Main.pas. To make life \neasier as we get more and more files, you might want to take this \nopportunity to declare this file as the compiler's Primary file. \nThat way, you can execute the program from any file. Otherwise, \nif you press Cntl-F9 to compile and run from one of the units, \nyou'll get an error message. You set the primary file using the \nmain submenu, \"Compile,\" in the Turbo IDE.\n\nI hasten to point out, as I've done before, that the function of \nunit Input is, and always has been, considered to be a dummy \nversion of the real thing. In a production version of a compiler, \nthe input stream will, of course, come from a file rather than \nfrom the keyboard. And it will almost certainly include line \nbuffering, at the very least, and more likely, a rather large text \nbuffer to support efficient disk I/O. The nice part about the \nunit approach is that, as with objects, we can modify the code in \nthe unit to be as simple or as sophisticated as we like. As long \nas the interface, as embodied in the public procedures and the \nlookahead character, don't change, the rest of the program is \ntotally unaffected. And since units are compiled, rather than \nmerely included, the time required to link with them is virtually \nnil. Again, the result is that we can get all the benefits of \nsophisticated implementations, without having to carry the code \naround as so much baggage.\n\nIn later installments, I intend to provide a full-blown IDE for \nthe KISS compiler, using a true Windows application generated by \nBorland's OWL applications framework. For now, though, we'll obey \nmy #1 rule to live by: Keep It Simple."},{title:"THE OUTPUT UNIT",path:"/the-output-unit",content:"Of course, every decent program should have output, and ours is no \nexception. Our output routines included the Emit functions. The \ncode for the corresponding output unit is shown next:\n\n```delphi\n{--------------------------------------------------------------}\nunit Output;\n{--------------------------------------------------------------}\ninterface\nprocedure Emit(s: string);\t\t\t{ Emit an instruction \t}\nprocedure EmitLn(s: string);\t\t{ Emit an instruction line }\n\n{--------------------------------------------------------------}\nimplementation\nconst TAB = ^I;\n\n{--------------------------------------------------------------}\n{ Emit an Instruction }\n\nprocedure Emit(s: string);\nbegin\n\tWrite(TAB, s);\nend;\n\n{--------------------------------------------------------------}\n{ Emit an Instruction, Followed By a Newline }\n\nprocedure EmitLn(s: string);\nbegin\n\tEmit(s);\n\tWriteLn;\nend;\n\nend.\n{--------------------------------------------------------------}\n```\n\n(Notice that this unit has no initialization clause, so it needs \nno begin-block.)\n \nTest this unit with the following main program:\n```delphi\n{--------------------------------------------------------------}\nprogram Test;\nuses WinCRT, Input, Output, Scanner, Parser;\nbegin\n\tWriteLn('MAIN:\");\n\tEmitLn('Hello, world!');\nend.\n{--------------------------------------------------------------}\n```\nDid you see anything that surprised you? You may have been \nsurprised to see that you needed to type something, even though \nthe main program requires no input. That's because of the \ninitialization in unit Input, which still requires something to \nput into the lookahead character. Sorry, there's no way out of \nthat box, or rather, we don't _WANT_ to get out. Except for simple \ntest cases such as this, we will always want a valid lookahead \ncharacter, so the right thing to do about this \"problem\" is ... \nnothing.\n\nPerhaps more surprisingly, notice that the TAB character had no \neffect; our line of \"instructions\" begins at column 1, same as the \nfake label. That's right: WinCRT doesn't support tabs. We have a \nproblem.\n\nThere are a few ways we can deal with this problem. The one thing \nwe can't do is to simply ignore it. Every assembler I've ever \nused reserves column 1 for labels, and will rebel to see \ninstructions starting there. So, at the very least, we must space \nthe instructions over one column to keep the assembler happy. . \nThat's easy enough to do: Simply change, in procedure Emit, the \nline:\n```delphi\n\tWrite(TAB, s);\n```\nby:\n```delphi\n\tWrite(' ', s);\n```\nI must admit that I've wrestled with this problem before, and find \nmyself changing my mind as often as a chameleon changes color. \nFor the purposes we're going to be using, 99% of which will be \nexamining the output code as it's displayed on a CRT, it would be \nnice to see neatly blocked out \"object\" code. The line:\n```\nSUB1:\t\tMOVE\t#4,D0\n```\njust plain looks neater than the different, but functionally \nidentical code,\n```\nSUB1:\n MOVE #4,D0\n```\nIn test versions of my code, I included a more sophisticated \nversion of the procedure PostLabel, that avoids having labels on \nseparate lines, but rather defers the printing of a label so it \ncan end up on the same line as the associated instruction. As \nrecently as an hour ago, my version of unit Output provided full \nsupport for tabs, using an internal column count variable and \nsoftware to manage it. I had, if I do say so myself, some rather \nelegant code to support the tab mechanism, with a minimum of code \nbloat. It was awfully tempting to show you the \"prettyprint\" \nversion, if for no other reason than to show off the elegance.\n\nNevertheless, the code of the \"elegant\" version was considerably \nmore complex and larger. Since then, I've had second thoughts. In \nspite of our desire to see pretty output, the inescapable fact is \nthat the two versions of the MAIN: code fragment shown above are \nfunctionally identical; the assembler, which is the ultimate \ndestination of the code, couldn't care less which version it gets, \nexcept that the prettier version will contain more characters, \ntherefore will use more disk space and take longer to assemble. \nbut the prettier one not only takes more code to generate, but \nwill create a larger output file, with many more space characters \nthan the minimum needed. When you look at it that way, it's not \nvery hard to decide which approach to use, is it?\n\nWhat finally clinched the issue for me was a reminder to consider \nmy own first commandment: KISS. Although I was pretty proud of \nall my elegant little tricks to implement tabbing, I had to remind \nmyself that, to paraphrase Senator Barry Goldwater, elegance in \nthe pursuit of complexity is no virtue. Another wise man once \nwrote, \"Any idiot can design a Rolls-Royce. It takes a genius to \ndesign a VW.\" So the elegant, tab-friendly version of Output is \nhistory, and what you see is the simple, compact, VW version."},{title:"THE ERROR UNIT",path:"/the-error-unit",content:"Our next set of routines are those that handle errors. To refresh \nyour memory, we take the approach, pioneered by Borland in Turbo \nPascal, of halting on the first error. Not only does this greatly \nsimplify our code, by completely avoiding the sticky issue of \nerror recovery, but it also makes much more sense, in my opinion, \nin an interactive environment. I know this may be an extreme \nposition, but I consider the practice of reporting all errors in a \nprogram to be an anachronism, a holdover from the days of batch \nprocessing. It's time to scuttle the practice. So there.\n\nIn our original Cradle, we had two error-handling procedures: \nError, which didn't halt, and Abort, which did. But I don't think \nwe ever found a use for the procedure that didn't halt, so in the \nnew, lean and mean unit Errors, shown next, procedure Error takes \nthe place of Abort.\n\n```delphi\n{--------------------------------------------------------------}\nunit Errors;\n{--------------------------------------------------------------}\ninterface\nprocedure Error(s: string);\nprocedure Expected(s: string);\n\n{--------------------------------------------------------------}\nimplementation\n\n{--------------------------------------------------------------}\n{ Write error Message and Halt }\n\nprocedure Error(s: string);\nbegin\n\tWriteLn;\n\tWriteLn(^G, 'Error: ', s, '.');\n\tHalt;\nend;\n\n{--------------------------------------------------------------}\n{ Write \"<something> Expected\" }\n\nprocedure Expected(s: string);\nbegin\n\tError(s + ' Expected');\nend;\n\nend.\n{--------------------------------------------------------------}\n```\n\nAs usual, here's a test program:\n\n\n\n```delphi\n{--------------------------------------------------------------}\nprogram Test;\nuses WinCRT, Input, Output, Errors;\n\nbegin\n\tExpected('Integer');\nend.\n{--------------------------------------------------------------}\n```\nHave you noticed that the \"uses\" line in our main program keeps \ngetting longer? That's OK. In the final version, the main program \nwill only call procedures in our parser, so its use clause will \nonly have a couple of entries. But for now, it's probably best to \ninclude all the units so we can test procedures in them."},{title:"SCANNING AND PARSING",path:"/scanning-and-parsing",content:"The classical compiler architecture consists of separate modules \nfor the lexical scanner, which supplies tokens in the language, \nand the parser, which tries to make sense of the tokens as syntax \nelements. If you can still remember what we did in earlier \ninstallments, you'll recall that we didn't do things that way. \nBecause we're using a predictive parser, we can almost always tell \nwhat language element is coming next, just by examining the \nlookahead character. Therefore, we found no need to prefetch \ntokens, as a scanner would do.\n\nBut, even though there is no functional procedure called \n\"Scanner,\" it still makes sense to separate the scanning functions \nfrom the parsing functions. So I've created two more units \ncalled, amazingly enough, Scanner and Parser. The Scanner unit \ncontains all of the routines known as recognizers. Some of these, \nsuch as IsAlpha, are pure boolean routines which operate on the \nlookahead character only. The other routines are those which \ncollect tokens, such as identifiers and numeric constants. The \nParser unit will contain all of the routines making up the \nrecursive-descent parser. The general rule should be that unit \nParser contains all of the information that is language-specific; \nin other words, the syntax of the language should be wholly \ncontained in Parser. In an ideal world, this rule should be true \nto the extent that we can change the compiler to compile a \ndifferent language, merely by replacing the single unit, Parser. \n\nIn practice, things are almost never this pure. There's always a \nsmall amount of \"leakage\" of syntax rules into the scanner as \nwell. For example, the rules concerning what makes up a legal \nidentifier or constant may vary from language to language. In \nsome languages, the rules concerning comments permit them to be \nfiltered by the scanner, while in others they do not. So in \npractice, both units are likely to end up having language-\ndependent components, but the changes required to the scanner \nshould be relatively trivial. \n\nNow, recall that we've used two versions of the scanner routines: \nOne that handled only single-character tokens, which we used for a \nnumber of our tests, and another that provided full support for \nmulti-character tokens. Now that we have our software separated \ninto units, I don't anticipate getting much use out of the single-\ncharacter version, but it doesn't cost us much to provide for \nboth. I've created two versions of the Scanner unit. The first \none, called Scanner1, contains the single-digit version of the \nrecognizers:\n\n```delphi\n{--------------------------------------------------------------}\nunit Scanner1;\n{--------------------------------------------------------------}\ninterface\nuses Input, Errors;\n\nfunction IsAlpha(c: char): boolean;\nfunction IsDigit(c: char): boolean;\nfunction IsAlNum(c: char): boolean;\nfunction IsAddop(c: char): boolean;\nfunction IsMulop(c: char): boolean;\n\nprocedure Match(x: char);\nfunction GetName: char;\nfunction GetNumber: char;\n\n{--------------------------------------------------------------}\nimplementation\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n\tIsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n{--------------------------------------------------------------}\n{ Recognize a Numeric Character }\n\nfunction IsDigit(c: char): boolean;\nbegin\n\tIsDigit := c in ['0'..'9'];\nend;\n\n{--------------------------------------------------------------}\n{ Recognize an Alphanumeric Character }\n\nfunction IsAlnum(c: char): boolean;\nbegin\n\tIsAlnum := IsAlpha(c) or IsDigit(c);\nend;\n\n{--------------------------------------------------------------}\n{ Recognize an Addition Operator }\n\nfunction IsAddop(c: char): boolean;\nbegin\n\tIsAddop := c in ['+','-'];\nend;\n\n{--------------------------------------------------------------}\n{ Recognize a Multiplication Operator }\n\nfunction IsMulop(c: char): boolean;\nbegin\n\tIsMulop := c in ['*','/'];\nend;\n\n{--------------------------------------------------------------}\n{ Match One Character }\n\nprocedure Match(x: char);\nbegin\n\tif Look = x then GetChar\n\telse Expected('''' + x + '''');\nend;\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: char;\nbegin\n\tif not IsAlpha(Look) then Expected('Name');\n\tGetName := UpCase(Look);\n\tGetChar;\nend;\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNumber: char;\nbegin\n\tif not IsDigit(Look) then Expected('Integer');\n\tGetNumber := Look;\n\tGetChar;\nend;\n\nend.\n{--------------------------------------------------------------}\n```\n\nThe following code fragment of the main program provides a good \ntest of the scanner. For brevity, I'll only include the \nexecutable code here; the rest remains the same. Don't forget, \nthough, to add the name Scanner1 to the \"uses\" clause.\n```delphi\n\tWrite(GetName);\n\tMatch('=');\n\tWrite(GetNumber);\n\tMatch('+');\n\tWriteLn(GetName);\n```\nThis code will recognize all sentences of the form:\n```\n\tx=0+y\n```\nwhere x and y can be any single-character variable names, and 0 \nany digit. The code should reject all other sentences, and give a \nmeaningful error message. If it did, you're in good shape and we \ncan proceed."},{title:"THE SCANNER UNIT",path:"/the-scanner-unit",content:"The next, and by far the most important, version of the scanner is \nthe one that handles the multi-character tokens that all real \nlanguages must have. Only the two functions, GetName and \nGetNumber, change between the two units, but just to be sure there \nare no mistakes, I've reproduced the entire unit here. This is \nunit Scanner:\n\n```delphi\n{--------------------------------------------------------------}\nunit Scanner;\n{--------------------------------------------------------------}\ninterface\nuses Input, Errors;\n\nfunction IsAlpha(c: char): boolean;\nfunction IsDigit(c: char): boolean;\nfunction IsAlNum(c: char): boolean;\nfunction IsAddop(c: char): boolean;\nfunction IsMulop(c: char): boolean;\n\nprocedure Match(x: char);\nfunction GetName: string;\nfunction GetNumber: longint;\n\n{--------------------------------------------------------------}\nimplementation\n\n{--------------------------------------------------------------}\n{ Recognize an Alpha Character }\n\nfunction IsAlpha(c: char): boolean;\nbegin\n\tIsAlpha := UpCase(c) in ['A'..'Z'];\nend;\n\n{--------------------------------------------------------------}\n{ Recognize a Numeric Character }\n\nfunction IsDigit(c: char): boolean;\nbegin\n\tIsDigit := c in ['0'..'9'];\nend;\n\n{--------------------------------------------------------------}\n{ Recognize an Alphanumeric Character }\n\nfunction IsAlnum(c: char): boolean;\nbegin\n\tIsAlnum := IsAlpha(c) or IsDigit(c);\nend;\n\n{--------------------------------------------------------------}\n{ Recognize an Addition Operator }\n\nfunction IsAddop(c: char): boolean;\nbegin\n\tIsAddop := c in ['+','-'];\nend;\n\n{--------------------------------------------------------------}\n{ Recognize a Multiplication Operator }\n\nfunction IsMulop(c: char): boolean;\nbegin\n\tIsMulop := c in ['*','/'];\nend;\n\n{--------------------------------------------------------------}\n{ Match One Character }\n\nprocedure Match(x: char);\nbegin\n\tif Look = x then GetChar\n\telse Expected('''' + x + '''');\nend;\n\n{--------------------------------------------------------------}\n{ Get an Identifier }\n\nfunction GetName: string;\nvar n: string;\nbegin\n\tn := '';\n\tif not IsAlpha(Look) then Expected('Name');\n\twhile IsAlnum(Look) do begin\n\t\tn := n + Look;\n\t\tGetChar;\n\tend;\n\tGetName := n;\nend;\n\n{--------------------------------------------------------------}\n{ Get a Number }\n\nfunction GetNumber: string;\nvar n: string;\nbegin\n\tn := '';\n\tif not IsDigit(Look) then Expected('Integer');\n\twhile IsDigit(Look) do begin\n\t\tn := n + Look;\n\t\tGetChar;\n\tend;\n\tGetNumber := n;\nend;\n\nend.\n{--------------------------------------------------------------}\n```\n\nThe same test program will test this scanner, also. Simply change \nthe \"uses\" clause to use Scanner instead of Scanner1. Now you \nshould be able to type multi-character names and numbers."},{title:"DECISIONS, DECISIONS",path:"/decisions-decisions",content:"In spite of the relative simplicity of both scanners, a lot of \nthought has gone into them, and a lot of decisions had to be made. \nI'd like to share those thoughts with you now so you can make your \nown educated decision, appropriate for your application. First, \nnote that both versions of GetName translate the input characters \nto upper case. Obviously, there was a design decision made here, \nand this is one of those cases where the language syntax splatters \nover into the scanner. In the C language, the case of characters \nin identifiers is significant. For such a language, we obviously \ncan't map the characters to upper case. The design I'm using \nassumes a language like Pascal, where the case of characters \ndoesn't matter. For such languages, it's easier to go ahead and \nmap all identifiers to upper case in the scanner, so we don't have \nto worry later on when we're comparing strings for equality.\n\nWe could have even gone a step further, and map the characters to \nupper case right as they come in, in GetChar. This approach works \ntoo, and I've used it in the past, but it's too confining. \nSpecifically, it will also map characters that may be part of \nquoted strings, which is not a good idea. So if you're going to \nmap to upper case at all, GetName is the proper place to do it.\n\nNote that the function GetNumber in this scanner returns a string, \njust as GetName does. This is another one of those things I've \noscillated about almost daily, and the last swing was all of ten \nminutes ago. The alternative approach, and one I've used many \ntimes in past installments, returns an integer result.\n\nBoth approaches have their good points. Since we're fetching a \nnumber, the approach that immediately comes to mind is to return \nit as an integer. But bear in mind that the eventual use of the \nnumber will be in a write statement that goes back to the outside \nworld. Someone -- either us or the code hidden inside the write \nstatement -- is going to have to convert the number back to a \nstring again. Turbo Pascal includes such string conversion \nroutines, but why use them if we don't have to? Why convert a \nnumber from string to integer form, only to convert it right back \nagain in the code generator, only a few statements later?\n\nFurthermore, as you'll soon see, we're going to need a temporary \nstorage spot for the value of the token we've fetched. If we treat \nthe number in its string form, we can store the value of either a \nvariable or a number in the same string. Otherwise, we'll have to \ncreate a second, integer variable.\n\nOn the other hand, we'll find that carrying the number as a string \nvirtually eliminates any chance of optimization later on. As we \nget to the point where we are beginning to concern ourselves with \ncode generation, we'll encounter cases in which we're doing \narithmetic on constants. For such cases, it's really foolish to \ngenerate code that performs the constant arithmetic at run time. \nFar better to let the parser do the arithmetic at compile time, \nand merely code the result. To do that, we'll wish we had the \nconstants stored as integers rather than strings.\n\nWhat finally swung me back over to the string approach was an \naggressive application of the KISS test, plus reminding myself \nthat we've studiously avoided issues of code efficiency. One of \nthe things that makes our simple-minded parsing work, without the \ncomplexities of a \"real\" compiler, is that we've said up front \nthat we aren't concerned about code efficiency. That gives us a \nlot of freedom to do things the easy way rather than the efficient \none, and it's a freedom we must be careful not to abandon \nvoluntarily, in spite of the urges for efficiency shouting in our \near. In addition to being a big believer in the KISS philosophy, \nI'm also an advocate of \"lazy programming,\" which in this context \nmeans, don't program anything until you need it. As P.J. Plauger \nsays, \"Never put off until tomorrow what you can put off \nindefinitely.\" Over the years, much code has been written to \nprovide for eventualities that never happened. I've learned that \nlesson myself, from bitter experience. So the bottom line is: We \nwon't convert to an integer here because we don't need to. It's \nas simple as that.\n\nFor those of you who still think we may need the integer version \n(and indeed we may), here it is:\n\n```delphi\n{--------------------------------------------------------------}\n{ Get a Number (integer version) }\n\nfunction GetNumber: longint;\nvar n: longint;\nbegin\n\tn := 0;\n\tif not IsDigit(Look) then Expected('Integer');\n\twhile IsDigit(Look) do begin\n\t\tn := 10 * n + (Ord(Look) - Ord('0'));\n\t\tGetChar;\n\tend;\n\tGetNumber := n;\nend;\n{--------------------------------------------------------------}\n```\nYou might file this one away, as I intend to, for a rainy day."},{title:"PARSING",path:"/parsing",content:"At this point, we have distributed all the routines that made up \nour Cradle into units that we can draw upon as we need them. \nObviously, they will evolve further as we continue the process of \nbootstrapping ourselves up again, but for the most part their \ncontent, and certainly the architecture that they imply, is \ndefined. What remains is to embody the language syntax into the \nparser unit. We won't do much of that in this installment, but I \ndo want to do a little, just to leave us with the good feeling \nthat we still know what we're doing. So before we go, let's \ngenerate just enough of a parser to process single factors in an \nexpression. In the process, we'll also, by necessity, find we \nhave created a code generator unit, as well.\n\nRemember the very first installment of this series? We read an \ninteger value, say n, and generated the code to load it into the \nD0 register via an immediate move:\n```\n\tMOVE #n,D0\n```\nShortly afterwards, we repeated the process for a variable, \n```\n\tMOVE X(PC),D0\n```\nand then for a factor that could be either constant or variable.\nFor old times sake, let's revisit that process. Define the \nfollowing new unit:\n\n```delphi\n{--------------------------------------------------------------}\nunit Parser;\n{--------------------------------------------------------------}\ninterface\nuses Input, Scanner, Errors, CodeGen;\nprocedure Factor;\n\n{--------------------------------------------------------------}\nimplementation\n\n{--------------------------------------------------------------}\n{ Parse and Translate a Factor }\n\nprocedure Factor;\nbegin\n\tLoadConstant(GetNumber);\nend;\n\nend.\n{--------------------------------------------------------------}\n```\n\nAs you can see, this unit calls a procedure, LoadConstant, which \nactually effects the output of the assembly-language code. The \nunit also uses a new unit, CodeGen. This step represents the last \nmajor change in our architecture, from earlier installments: The \nremoval of the machine-dependent code to a separate unit. If I \nhave my way, there will not be a single line of code, outside of \nCodeGen, that betrays the fact that we're targeting the 68000 CPU. \nAnd this is one place I think that having my way is quite \nfeasible. \n\nFor those of you who wish I were using the 80x86 architecture (or \nany other one) instead of the 68000, here's your answer: Merely \nreplace CodeGen with one suitable for your CPU of choice.\n\nSo far, our code generator has only one procedure in it. Here's \nthe unit:\n\n```delphi\n{--------------------------------------------------------------}\nunit CodeGen;\n\n{--------------------------------------------------------------}\ninterface\nuses Output;\nprocedure LoadConstant(n: string);\n\n{--------------------------------------------------------------}\nimplementation\n\n{--------------------------------------------------------------}\n{ Load the Primary Register with a Constant }\n\nprocedure LoadConstant(n: string);\nbegin\n\tEmitLn('MOVE #' + n + ',D0' );\nend;\n\nend.\n{--------------------------------------------------------------}\n\n\nCopy and compile this unit, and execute the following main \nprogram:\n\n{--------------------------------------------------------------}\nprogram Main;\nuses WinCRT, Input, Output, Errors, Scanner, Parser;\nbegin\n\tFactor;\nend.\n{--------------------------------------------------------------}\n```\n\nThere it is, the generated code, just as we hoped it would be.\n\nNow, I hope you can begin to see the advantage of the unit-based \narchitecture of our new design. Here we have a main program \nthat's all of five lines long. That's all of the program we need \nto see, unless we choose to see more. And yet, all those units \nare sitting there, patiently waiting to serve us. We can have our \ncake and eat it too, in that we have simple and short code, but \npowerful allies. What remains to be done is to flesh out the \nunits to match the capabilities of earlier installments. We'll do \nthat in the next installment, but before I close, let's finish out \nthe parsing of a factor, just to satisfy ourselves that we still \nknow how. The final version of CodeGen includes the new \nprocedure, LoadVariable:\n```delphi\n{--------------------------------------------------------------}\nunit CodeGen;\n\n{--------------------------------------------------------------}\ninterface\nuses Output;\nprocedure LoadConstant(n: string);\nprocedure LoadVariable(Name: string);\n\n{--------------------------------------------------------------}\nimplementation\n\n{--------------------------------------------------------------}\n{ Load the Primary Register with a Constant }\n\nprocedure LoadConstant(n: string);\nbegin\n\tEmitLn('MOVE #' + n + ',D0' );\nend;\n\n{--------------------------------------------------------------}\n{ Load a Variable to the Primary Register }\n\nprocedure LoadVariable(Name: string);\nbegin\n\tEmitLn('MOVE ' + Name + '(PC),D0');\nend;\n\n\nend.\n{--------------------------------------------------------------}\n```\n\nThe parser unit itself doesn't change, but we have a more complex \nversion of procedure Factor:\n```delphi\n{--------------------------------------------------------------}\n{ Parse and Translate a Factor }\n\nprocedure Factor;\nbegin\n\tif IsDigit(Look) then\n\t\tLoadConstant(GetNumber)\n\telse if IsAlpha(Look)then\n\t\tLoadVariable(GetName)\n\telse\n\t\tError('Unrecognized character ' + Look);\nend;\n{--------------------------------------------------------------}\n```\n\nNow, without altering the main program, you should find that our \nprogram will process either a variable or a constant factor. At \nthis point, our architecture is almost complete; we have units to \ndo all the dirty work, and enough code in the parser and code \ngenerator to demonstrate that everything works. What remains is \nto flesh out the units we've defined, particularly the parser and \ncode generator, to support the more complex syntax elements that \nmake up a real language. Since we've done this many times before \nin earlier installments, it shouldn't take long to get us back to \nwhere we were before the long hiatus. We'll continue this process \nin Installment 16, coming soon. See you then."},{title:"REFERENCES",path:"/references",content:'1. Crenshaw, J.W., "Object-Oriented Design of Assemblers and \nCompilers," Proc. Software Development \'91 Conference, Miller \nFreeman, San Francisco, CA, February 1991, pp. 143-155.\n\n2. Crenshaw, J.W., "A Perfect Marriage," Computer Language, Volume \n8, #6, June 1991, pp. 44-55.\n\n3. Crenshaw, J.W., "Syntax-Driven Object-Oriented Design," Proc. \n1991 Embedded Systems Conference, Miller Freeman, San \nFrancisco, CA, September 1991, pp. 45-60.'}]},{title:"Part 16: UNIT CONSTRUCTION - 29 May, 1995",path:"/part-16-unit-construction-29-may-1995",items:[{title:"INTRODUCTION ",path:"/introduction",content:" \nThis series of tutorials promises to be perhaps one of the longest-\nrunning mini-series in history, rivalled only by the delay in Volume IV \nof Knuth. Begun in 1988, the series ran into a four-year hiatus in 1990 \nwhen the \"cares of this world,\" changes in priorities and interests, and \nthe need to make a living seemed to stall it out after Installment 14. \nThose of you with loads of patience were finally rewarded, in the spring \nof last year, with the long-awaited Installment 15. In it, I began to \ntry to steer the series back on track, and in the process, to make it \neasier to continue on to the goal, which is to provide you with not only \nenough understanding of the difficult subject of compiler theory, but \nalso enough tools, in the form of canned subroutines and concepts, so \nthat you would be able to continue on your own and become proficient \nenough to build your own parsers and translators. Because of that long \nhiatus, I thought it appropriate to go back and review the concepts we \nhave covered so far, and to redo some of the software, as well. In the \npast, we've never concerned ourselves much with the development of \nproduction-quality software tools ... after all, I was trying to teach \n(and learn) concepts, not production practice. To do that, I tended to \ngive you, not complete compilers or parsers, but only those snippets of \ncode that illustrated the particular point we were considering at the \nmoment. \n \nI still believe that's a good way to learn any subject; no one wants to \nhave to make changes to 100,000 line programs just to try out a new \nidea. But the idea of just dealing with code snippets, rather than \ncomplete programs, also has its drawbacks in that we often seemed to be \nwriting the same code fragments over and over. Although repetition has \nbeen thoroughly proven to be a good way to learn new ideas, it's also \ntrue that one can have too much of a good thing. By the time I had \ncompleted Installment 14 I seemed to have reached the limits of my \nabilities to juggle multiple files and multiple versions of the same \nsoftware functions. Who knows, perhaps that's one reason I seemed to \nhave run out of gas at that point. \n \nFortunately, the later versions of Borland's Turbo Pascal allow us to \nhave our cake and eat it too. By using their concept of separately \ncompilable units, we can still write small subroutines and functions, \nand keep our main programs and test programs small and simple. But, \nonce written, the code in the Pascal units will always be there for us \nto use, and linking them in is totally painless and transparent. \n \nSince, by now, most of you are programming in either C or C++, I know \nwhat you're thinking: Borland, with their Turbo Pascal (TP), certainly \ndidn't invent the concept of separately compilable modules. And of \ncourse you're right. But if you've not used TP lately, or ever, you may \nnot realize just how painless the whole process is. Even in C or C++, \nyou still have to build a make file, either manually or by telling the \ncompiler how to do so. You must also list, using \"extern\" statements or \nheader files, the functions you want to import. In TP, you don't even \nhave to do that. You need only name the units you wish to use, and all \nof their procedures automatically become available. \n \n \nIt's not my intention to get into a language-war debate here, so I won't \npursue the subject any further. Even I no longer use Pascal on my job \n... I use C at work and C++ for my articles in Embedded Systems \nProgramming and other magazines. Believe me, when I set out to \nresurrect this series, I thought long and hard about switching both \nlanguages and target systems to the ones that we're all using these \ndays, C/C++ and PC architecture, and possibly object-oriented methods as \nwell. In the end, I felt it would cause more confusion than the hiatus \nitself has. And after all, Pascal still remains one of the best possible \nlanguages for teaching, not to mention production programming. Finally, \nTP still compiles at the speed of light, much faster than competing \nC/C++ compilers. And Borland's smart linker, used in TP but not in their \nC++ products, is second to none. Aside from being much faster than \nMicrosoft-compatible linkers, the Borland smart linker will cull unused \nprocedures and data items, even to the extent of trimming them out of \ndefined objects if they're not needed. For one of the few times in our \nlives, we don't have to compromise between completeness and efficiency. \nWhen we're writing a TP unit, we can make it as complete as we like, \nincluding any member functions and data items we may think we will ever \nneed, confident that doing so will not create unwanted bloat in the \ncompiled and linked executable. \n \nThe point, really, is simply this: By using TP's unit mechanism, we can \nhave all the advantages and convenience of writing small, seemingly \nstand-alone test programs, without having to constantly rewrite the \nsupport functions that we need. Once written, the TP units sit there, \nquietly waiting to do their duty and give us the support we need, when \nwe need it. \n \nUsing this principle, in Installment 15 I set out to minimize our \ntendency to re-invent the wheel by organizing our code into separate \nTurbo Pascal units, each containing different parts of the compiler. We \nended up with the following units: \n \n - Input \n - Output \n - Errors \n - Scanner \n - Parser \n - CodeGen \n \nEach of these units serves a different function, and encapsulates \nspecific areas of functionality. The Input and Output units, as their \nname implies, provide character stream I/O and the all-important \nlookahead character upon which our predictive parser is based. The \nErrors unit, of course, provides standard error handling. The Scanner \nunit contains all of our boolean functions such as IsAlpha, and the \nroutines GetName and GetNumber, which process multi-character tokens. \n \nThe two units we'll be working with the most, and the ones that most \nrepresent the personality of our compiler, are Parser and CodeGen. \nTheoretically, the Parser unit should encapsulate all aspects of the \ncompiler that depend on the syntax of the compiled language (though, as \nwe saw last time, a small amount of this syntax spills over into \nScanner). Similarly, the code generator unit, CodeGen, contains all of \nthe code dependent upon the target machine. In this installment, we'll \nbe continuing with the development of the functions in these two all-\nimportant units. \n \n "},{title:"JUST LIKE CLASSICAL? ",path:"/just-like-classical",content:" \nBefore we proceed, however, I think I should clarify the relationship \nbetween, and the functionality of these units. Those of you who are \nfamiliar with compiler theory as taught in universities will, of course, \nrecognize the names, Scanner, Parser, and CodeGen, all of which are \ncomponents of a classical compiler implementation. You may be thinking \nthat I've abandoned my commitment to the KISS philosophy, and drifted \ntowards a more conventional architecture than we once had. A closer \nlook, however, should convince you that, while the names are similar, \nthe functionalities are quite different. \n \nTogether, the scanner and parser of a classical implementation comprise \nthe so-called \"front end,\" and the code generator, the back end. The \nfront end routines process the language-dependent, syntax-related \naspects of the source language, while the code generator, or back end, \ndeals with the target machine-dependent parts of the problem. In \nclassical compilers, the two ends communicate via a file of instructions \nwritten in an intermediate language (IL). \n \nTypically, a classical scanner is a single procedure, operating as a co-\nprocedure with the parser. It \"tokenizes\" the source file, reading it \ncharacter by character, recognizing language elements, translating them \ninto tokens, and passing them along to the parser. You can think of the \nparser as an abstract machine, executing \"op codes,\" which are the \ntokens. Similarly, the parser generates op codes of a second abstract \nmachine, which mechanizes the IL. Typically, the IL file is written to \ndisk by the parser, and read back again by the code generator. \n \nOur organization is quite different. We have no lexical scanner, in the \nclassical sense; our unit Scanner, though it has a similar name, is not \na single procedure or co-procedure, but merely a set of separate \nsubroutines which are called by the parser as needed. \n \nSimilarly, the classical code generator, the back end, is a translator \nin its own right, reading an IL \"source\" file, and emitting an object \nfile. Our code generator doesn't work that way. In our compiler, there \nIS no intermediate language; every construct in the source language \nsyntax is converted into assembly language as it is recognized by the \nparser. Like Scanner, the unit CodeGen consists of individual \nprocedures which are called by the parser as needed. \n \nThis \"code 'em as you find 'em\" philosophy may not produce the world's \nmost efficient code -- for example, we haven't provided (yet!) a \nconvenient place for an optimizer to work its magic -- but it sure does \nsimplify the compiler, doesn't it? \n \nAnd that observation prompts me to reflect, once again, on how we have \nmanaged to reduce a compiler's functions to such comparatively simple \nterms. I've waxed eloquent on this subject in past installments, so I \nwon't belabor the point too much here. However, because of the time \nthat's elapsed since those last soliloquies, I hope you'll grant me just \na little time to remind myself, as well as you, how we got here. We got \nhere by applying several principles that writers of commercial compilers \nseldom have the luxury of using. These are: \n \n-\tThe KISS philosophy -- Never do things the hard way without a \nreason \n-\tLazy coding -- Never put off until tomorrow what you can put \nof forever (with credits to P.J. Plauger) \n-\tSkepticism -- Stubborn refusal to do something just because \nthat's the way it's always been done. \n-\tAcceptance of inefficient code \n-\tRejection of arbitrary constraints \n \nAs I've reviewed the history of compiler construction, I've learned that \nvirtually every production compiler in history has suffered from pre-\nimposed conditions that strongly influenced its design. The original \nFORTRAN compiler of John Backus, et al, had to compete with assembly \nlanguage, and therefore was constrained to produce extremely efficient \ncode. The IBM compilers for the minicomputers of the 70's had to run in \nthe very small RAM memories then available -- as small as 4k. The early \nAda compiler had to compile itself. Per Brinch Hansen decreed that his \nPascal compiler developed for the IBM PC must execute in a 64k machine. \nCompilers developed in Computer Science courses had to compile the \nwidest variety of languages, and therefore required LALR parsers. \n \nIn each of these cases, these preconceived constraints literally \ndominated the design of the compiler. \n \nA good example is Brinch Hansen's compiler, described in his excellent \nbook, \"Brinch Hansen on Pascal Compilers\" (highly recommended). Though \nhis compiler is one of the most clear and un-obscure compiler \nimplementations I've seen, that one decision, to compile large files in \na small RAM, totally drives the design, and he ends up with not just \none, but many intermediate files, together with the drivers to write and \nread them. \n \nIn time, the architectures resulting from such decisions have found \ntheir way into computer science lore as articles of faith. In this one \nman's opinion, it's time that they were re-examined critically. The \nconditions, environments, and requirements that led to classical \narchitectures are not the same as the ones we have today. There's no \nreason to believe the solutions should be the same, either. \n \nIn this tutorial, we've followed the leads of such pioneers in the world \nof small compilers for Pcs as Leor Zolman, Ron Cain, and James Hendrix, \nwho didn't know enough compiler theory to know that they \"couldn't do it \nthat way.\" We have resolutely refused to accept arbitrary constraints, \nbut rather have done whatever was easy. As a result, we have evolved an \narchitecture that, while quite different from the classical one, gets \nthe job done in very simple and straightforward fashion. \n \nI'll end this philosophizing with an observation re the notion of an \nintermediate language. While I've noted before that we don't have one \nin our compiler, that's not exactly true; we _DO_ have one, or at least \nare evolving one, in the sense that we are defining code generation \nfunctions for the parser to call. In essence, every call to a code \ngeneration procedure can be thought of as an instruction in an \nintermediate language. Should we ever find it necessary to formalize an \nintermediate language, this is the way we would do it: emit codes from \nthe parser, each representing a call to one of the code generator \nprocedures, and then process each code by calling those procedures in a \nseparate pass, implemented in a back end. Frankly, I don't see that \nwe'll ever find a need for this approach, but there is the connection, \nif you choose to follow it, between the classical and the current \napproaches. \n "},{title:"FLESHING OUT THE PARSER ",path:"/fleshing-out-the-parser",content:" \nThough I promised you, somewhere along about Installment 14, that we'd \nnever again write every single function from scratch, I ended up \nstarting to do just that in Installment 15. One reason: that long \nhiatus between the two installments made a review seem eminently \njustified ... even imperative, both for you and for me. More \nimportantly, the decision to collect the procedures into modules \n(units), forced us to look at each one yet again, whether we wanted to \nor not. And, finally and frankly, I've had some new ideas in the last \nfour years that warranted a fresh look at some old friends. When I \nfirst began this series, I was frankly amazed, and pleased, to learn \njust how simple parsing routines can be made. But this last time \naround, I've surprised myself yet again, and been able to make them just \nthat last little bit simpler, yet. \n \nStill, because of this total rewrite of the parsing modules, I was only \nable to include so much in the last installment. Because of this, our \nhero, the parser, when last seen, was a shadow of his former self, \nconsisting of only enough code to parse and process a factor consisting \nof either a variable or a constant. The main effort of this current \ninstallment will be to help flesh out the parser to its former glory. \nIn the process, I hope you'll bear with me if we sometimes cover ground \nwe've long since been over and dealt with. \n \nFirst, let's take care of a problem that we've addressed before: Our \ncurrent version of procedure Factor, as we left it in Installment 15, \ncan't handle negative arguments. To fix that, we'll introduce the \nprocedure SignedFactor: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate a Factor with Optional Sign } \n \nprocedure SignedFactor; \nvar Sign: char; \nbegin \n\tSign := Look; \n\tif IsAddop(Look) then \n\t\tGetChar; \n\tFactor; \n\tif Sign = '-' then Negate; \nend; \n{--------------------------------------------------------------} \n```\n \nNote that this procedure calls a new code generation routine, Negate: \n \n```delphi\n{--------------------------------------------------------------} \n{ Negate Primary } \n \nprocedure Negate; \nbegin \n\tEmitLn('NEG D0'); \nend; \n{--------------------------------------------------------------} \n```\n \n(Here, and elsewhere in this series, I'm only going to show you the new \nroutines. I'm counting on you to put them into the proper unit, which \nyou should normally have no trouble identifying. Don't forget to add \nthe procedure's prototype to the interface section of the unit.) \n \nIn the main program, simply change the procedure called from Factor to \nSignedFactor, and give the code a test. Isn't it neat how the Turbo \nlinker and make facility handle all the details? \n \nYes, I know, the code isn't very efficient. If we input a number, -3, \nthe generated code is: \n ```\n\tMOVE #3,D0 \n\tNEG D0 \n```\nwhich is really, really dumb. We can do better, of course, by simply \npre-appending a minus sign to the string passed to LoadConstant, but it \nadds a few lines of code to SignedFactor, and I'm applying the KISS \nphilosophy very aggressively here. What's more, to tell the truth, I \nthink I'm subconsciously enjoying generating \"really, really dumb\" code, \nso I can have the pleasure of watching it get dramatically better when \nwe get into optimization methods. \n \nMost of you have never heard of John Spray, so allow me to introduce him \nto you here. John's from New Zealand, and used to teach computer \nscience at one of its universities. John wrote a compiler for the \nMotorola 6809, based on a delightful, Pascal-like language of his own \ndesign called \"Whimsical.\" He later ported the compiler to the 68000, \nand for awhile it was the only compiler I had for my homebrewed 68000 \nsystem. \n \nFor the record, one of my standard tests for any new compiler is to see \nhow the compiler deals with a null program like: \n```delphi\n\tprogram main; \n\tbegin \n\tend. \n```\nMy test is to measure the time required to compile and link, and the \nsize of the object file generated. The undisputed _LOSER_ in the test \nis the DEC C compiler for the VAX, which took 60 seconds to compile, on \na VAX 11/780, and generated a 50k object file. John's compiler is the \nundisputed, once, future, and forever king in the code size department. \nGiven the null program, Whimsical generates precisely two bytes of code, \nimplementing the one instruction, \n```\n\tRET \n```\nBy setting a compiler option to generate an include file rather than a \nstandalone program, John can even cut this size, from two bytes to zero! \nSort of hard to beat a null object file, wouldn't you say? \n \nNeedless to say, I consider John to be something of an expert on code \noptimization, and I like what he has to say: \"The best way to optimize \nis not to have to optimize at all, but to produce good code in the first \nplace.\" Words to live by. When we get started on optimization, we'll \nfollow John's advice, and our first step will not be to add a peephole \noptimizer or other after-the-fact device, but to improve the quality of \nthe code emitted before optimization. So make a note of SignedFactor as \na good first candidate for attention, and for now we'll leave it be. \n "},{title:"TERMS AND EXPRESSIONS ",path:"/terms-and-expressions",content:" \nI'm sure you know what's coming next: We must, yet again, create the \nrest of the procedures that implement the recursive-descent parsing of \nan expression. We all know that the hierarchy of procedures for \narithmetic expressions is: \n```\nexpression \n\tterm \n\t\tfactor \n```\nHowever, for now let's continue to do things one step at a time, \nand consider only expressions with additive terms in them. The \ncode to implement expressions, including a possibly signed first \nterm, is shown next: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate an Expression } \n \nprocedure Expression; \nbegin \n\tSignedFactor; \n\twhile IsAddop(Look) do \n\t\tcase Look of \n\t\t\t'+': Add; \n\t\t\t'-': Subtract; \n\t\tend; \nend; \n{--------------------------------------------------------------} \n```\n \nThis procedure calls two other procedures to process the \noperations: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate an Addition Operation } \n \nprocedure Add; \nbegin \n\tMatch('+'); \n\tPush; \n\tFactor; \n\tPopAdd; \nend; \n \n \n{--------------------------------------------------------------} \n{ Parse and Translate a Subtraction Operation } \n \nprocedure Subtract; \nbegin \n\tMatch('-'); \n\tPush; \n\tFactor; \n\tPopSub; \nend; \n{--------------------------------------------------------------} \n```\n \nThe three procedures Push, PopAdd, and PopSub are new code generation \nroutines. As the name implies, procedure Push generates code to push \nthe primary register (D0, in our 68000 implementation) to the stack. \nPopAdd and PopSub pop the top of the stack again, and add it to, or \nsubtract it from, the primary register. The code is shown next: \n \n \n\n```delphi\n{--------------------------------------------------------------} \n{ Push Primary to Stack } \n \nprocedure Push; \nbegin \n\tEmitLn('MOVE D0,-(SP)'); \nend; \n \n{--------------------------------------------------------------} \n{ Add TOS to Primary } \n \nprocedure PopAdd; \nbegin \n\tEmitLn('ADD (SP)+,D0'); \nend; \n \n{--------------------------------------------------------------} \n{ Subtract TOS from Primary } \n \nprocedure PopSub; \nbegin \n\tEmitLn('SUB (SP)+,D0'); \n\tNegate; \nend; \n{--------------------------------------------------------------} \n```\n \nAdd these routines to Parser and CodeGen, and change the main program to \ncall Expression. Voila! \n \nThe next step, of course, is to add the capability for dealing with \nmultiplicative terms. To that end, we'll add a procedure Term, and code \ngeneration procedures PopMul and PopDiv. These code generation \nprocedures are shown next: \n \n```delphi\n{--------------------------------------------------------------} \n{ Multiply TOS by Primary } \n \nprocedure PopMul; \nbegin \n\tEmitLn('MULS (SP)+,D0'); \nend; \n \n{--------------------------------------------------------------} \n{ Divide Primary by TOS } \n \nprocedure PopDiv; \nbegin \n\tEmitLn('MOVE (SP)+,D7'); \n\tEmitLn('EXT.L D7'); \n\tEmitLn('DIVS D0,D7'); \n\tEmitLn('MOVE D7,D0'); \nend; \n{--------------------------------------------------------------} \n```\n\nI admit, the division routine is a little busy, but there's no help for \nit. Unfortunately, while the 68000 CPU allows a division using the top \nof stack (TOS), it wants the arguments in the wrong order, just as it \ndoes for subtraction. So our only recourse is to pop the stack to a \nscratch register (D7), perform the division there, and then move the \nresult back to our primary register, D0. Note the use of signed multiply \nand divide operations. This follows an implied, but unstated, \nassumption, that all our variables will be signed 16-bit integers. This \ndecision will come back to haunt us later, when we start looking at \nmultiple data types, type conversions, etc. \n \nOur procedure Term is virtually a clone of Expression, and looks like \nthis: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate a Term } \n \nprocedure Term; \nbegin \n\tFactor; \n\twhile IsMulop(Look) do \n\t\tcase Look of \n\t\t\t'*': Multiply; \n\t\t\t'/': Divide; \n\t\tend; \nend; \n{--------------------------------------------------------------} \n```\n \nOur next step is to change some names. SignedFactor now becomes \nSignedTerm, and the calls to Factor in Expression, Add, Subtract and \nSignedTerm get changed to call Term: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate a Term with Optional Leading Sign } \n \nprocedure SignedTerm; \nvar Sign: char; \nbegin \n\tSign := Look; \n\tif IsAddop(Look) then \n\t\tGetChar; \n\tTerm; \n\tif Sign = '-' then Negate; \nend; \n{--------------------------------------------------------------} \n... \n{--------------------------------------------------------------} \n{ Parse and Translate an Expression } \n \nprocedure Expression; \nbegin \n\tSignedTerm; \n\twhile IsAddop(Look) do \n\t\tcase Look of \n\t\t\t'+': Add; \n\t\t\t'-': Subtract; \n\t\tend; \nend; \n{--------------------------------------------------------------} \n```\n \nIf memory serves me correctly, we once had BOTH a procedure SignedFactor \nand a procedure SignedTerm. I had reasons for doing that at the time ... \nthey had to do with the handling of Boolean algebra and, in particular, \nthe Boolean \"not\" function. But certainly, for arithmetic operations, \nthat duplication isn't necessary. In an expression like: `-x*y`\n \nit's very apparent that the sign goes with the whole TERM, x*y, and not \njust the factor x, and that's the way Expression is coded. \n \nTest this new code by executing Main. It still calls Expression, so you \nshould now be able to deal with expressions containing any of the four \narithmetic operators. \n \nOur last bit of business, as far as expressions goes, is to modify \nprocedure Factor to allow for parenthetical expressions. By using a \nrecursive call to Expression, we can reduce the needed code to virtually \nnothing. Five lines added to Factor do the job: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate a Factor } \n \nprocedure Factor; \nbegin \n\tif Look ='(' then begin \n\t\tMatch('('); \n\t\tExpression; \n\t\tMatch(')'); \n\t\tend \n\telse if IsDigit(Look) then \n\t\tLoadConstant(GetNumber) \n\telse if IsAlpha(Look)then \n\t\tLoadVariable(GetName) \n\telse \n\t\tError('Unrecognized character ' + Look); \nend; \n{--------------------------------------------------------------} \n```\n \nAt this point, your \"compiler\" should be able to handle any legal \nexpression you can throw at it. Better yet, it should reject all \nillegal ones! \n "},{title:"ASSIGNMENTS ",path:"/assignments",content:" \nAs long as we're this close, we might as well create the code to deal \nwith an assignment statement. This code needs only to remember the name \nof the target variable where we are to store the result of an \nexpression, call Expression, then store the number. The procedure is \nshown next: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate an Assignment Statement } \n \nprocedure Assignment; \nvar Name: string; \nbegin \n\tName := GetName; \n\tMatch('='); \n\tExpression; \n\tStoreVariable(Name); \nend; \n{--------------------------------------------------------------} \n```\nThe assignment calls for yet another code generation routine: \n \n```delphi\n\n{--------------------------------------------------------------} \n{ Store the Primary Register to a Variable } \n \nprocedure StoreVariable(Name: string); \nbegin \n\tEmitLn('LEA ' + Name + '(PC),A0'); \n\tEmitLn('MOVE D0,(A0)'); \nend; \n{--------------------------------------------------------------} \n```\n \nNow, change the call in Main to call Assignment, and you should see a \nfull assignment statement being processed correctly. Pretty neat, eh? \nAnd painless, too. \n \nIn the past, we've always tried to show BNF relations to define the \nsyntax we're developing. I haven't done that here, and it's high time I \ndid. Here's the BNF: \n```\n<factor> ::= <variable> | <constant> | '(' <expression> ')'\t \n<signed_term> ::= [<addop>] <term> \n<term> ::= <factor> (<mulop> <factor>)*\t \n<expression> ::= <signed_term> (<addop> <term>)* \n<assignment> ::= <variable> '=' <expression> \n```"},{title:"BOOLEANS ",path:"/booleans",content:" \nThe next step, as we've learned several times before, is to add Boolean \nalgebra. In the past, this step has at least doubled the amount of code \nwe've had to write. As I've gone over this step in my mind, I've found \nmyself diverging more and more from what we did in previous \ninstallments. To refresh your memory, I noted that Pascal treats the \nBoolean operators pretty much identically to the way it treats \narithmetic ones. A Boolean \"and\" has the same precedence level as \nmultiplication, and the \"or\" as addition. C, on the other hand, sets \nthem at different precedence levels, and all told has a whopping 17 \nlevels. In our earlier work, I chose something in between, with seven \nlevels. As a result, we ended up with things called Boolean \nexpressions, paralleling in most details the arithmetic expressions, but \nat a different precedence level. All of this, as it turned out, came \nabout because I didn't like having to put parentheses around the Boolean \nexpressions in statements like: \n```delphi\nIF (c >= 'A') and (c <= 'Z') then ... \n```\nIn retrospect, that seems a pretty petty reason to add many layers of \ncomplexity to the parser. Perhaps more to the point, I'm not sure I was \neven able to avoid the parens. \n \nFor kicks, let's start anew, taking a more Pascal-ish approach, and just \ntreat the Boolean operators at the same precedence level as the \narithmetic ones. We'll see where it leads us. If it seems to be down \nthe garden path, we can always backtrack to the earlier approach. \n \nFor starters, we'll add the \"addition-level\" operators to Expression. \nThat's easily done; first, modify the function IsAddop in unit Scanner \nto include two extra operators: '|' for \"or,\" and '~' for \"exclusive \nor\": \n\n```delphi\n{--------------------------------------------------------------} \nfunction IsAddop(c: char): boolean; \nbegin \n\tIsAddop := c in ['+','-', '|', '~']; \nend; \n{--------------------------------------------------------------} \n```\n \nNext, we must include the parsing of the operators in procedure \nExpression: \n \n```delphi\n{--------------------------------------------------------------} \nprocedure Expression; \nbegin \n\tSignedTerm; \n\twhile IsAddop(Look) do \n\t\tcase Look of \n\t\t\t'+': Add; \n\t\t\t'-': Subtract; \n\t\t\t'|': _Or; \n\t\t\t'~': _Xor; \n\t\tend; \n{--------------------------------------------------------------} \nend; \n```\n \n(The underscores are needed, of course, because \"or\" and \"xor\" are \nreserved words in Turbo Pascal.) \n \nNext, the procedures _Or and _Xor: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate a Subtraction Operation } \n \nprocedure _Or; \nbegin \n\tMatch('|'); \n\tPush; \n\tTerm; \n\tPopOr; \nend; \n \n{--------------------------------------------------------------} \n{ Parse and Translate a Subtraction Operation } \n \nprocedure _Xor; \nbegin \n\tMatch('~'); \n\tPush; \n\tTerm; \n\tPopXor; \nend; \n{--------------------------------------------------------------} \n```\nAnd, finally, the new code generator procedures: \n \n```delphi\n{--------------------------------------------------------------} \n{ Or TOS with Primary } \n \nprocedure PopOr; \nbegin \n\tEmitLn('OR (SP)+,D0'); \nend; \n \n{--------------------------------------------------------------} \n{ Exclusive-Or TOS with Primary } \n \nprocedure PopXor; \nbegin \n\tEmitLn('EOR (SP)+,D0'); \nend; \n{--------------------------------------------------------------} \n```\nNow, let's test the translator (you might want to change the call \nin Main back to a call to Expression, just to avoid having to type \n\"x=\" for an assignment every time). \n \nSo far, so good. The parser nicely handles expressions of the \nform: `x|y~z`\n \nUnfortunately, it also does nothing to protect us from mixing \nBoolean and arithmetic algebra. It will merrily generate code \nfor: `(a+b)*(c~d)` \n \nWe've talked about this a bit, in the past. In general the rules \nfor what operations are legal or not cannot be enforced by the \nparser itself, because they are not part of the syntax of the \nlanguage, but rather its semantics. A compiler that doesn't allow \nmixed-mode expressions of this sort must recognize that c and d \nare Boolean variables, rather than numeric ones, and balk at \nmultiplying them in the next step. But this \"policing\" can't be \ndone by the parser; it must be handled somewhere between the \nparser and the code generator. We aren't in a position to enforce \nsuch rules yet, because we haven't got either a way of declaring \ntypes, or a symbol table to store the types in. So, for what \nwe've got to work with at the moment, the parser is doing \nprecisely what it's supposed to do. \n \nAnyway, are we sure that we DON'T want to allow mixed-type \noperations? We made the decision some time ago (or, at least, I \ndid) to adopt the value 0000 as a Boolean \"false,\" and -1, or \nFFFFh, as a Boolean \"true.\" The nice part about this choice is \nthat bitwise operations work exactly the same way as logical ones. \nIn other words, when we do an operation on one bit of a logical \nvariable, we do it on all of them. This means that we don't need \nto distinguish between logical and bitwise operations, as is done \nin C with the operators & and &&, and | and ||. Reducing the \nnumber of operators by half certainly doesn't seem all bad. \n \nFrom the point of view of the data in storage, of course, the \ncomputer and compiler couldn't care less whether the number FFFFh \nrepresents the logical TRUE, or the numeric -1. Should we? I \nsort of think not. I can think of many examples (though they \nmight be frowned upon as \"tricky\" code) where the ability to mix \nthe types might come in handy. Example, the Dirac delta function, \nwhich could be coded in one simple line: `-(x=0)`\n \nor the absolute value function (DEFINITELY tricky code!): `x*(1+2*(x<0))`\n \nPlease note, I'm not advocating coding like this as a way of life. \nI'd almost certainly write these functions in more readable form, \nusing IFs, just to keep from confusing later maintainers. Still, \na moral question arises: Do we have the right to ENFORCE our \nideas of good coding practice on the programmer, but writing the \nlanguage so he can't do anything else? That's what Nicklaus Wirth \ndid, in many places in Pascal, and Pascal has been criticized for \nit -- for not being as \"forgiving\" as C. \n \nAn interesting parallel presents itself in the example of the \nMotorola 68000 design. Though Motorola brags loudly about the \northogonality of their instruction set, the fact is that it's far \nfrom orthogonal. For example, you can read a variable from its \naddress:\n```\n\tMOVE X,D0 (where X is the name of a variable) \n```\nbut you can't write in the same way. To write, you must load an \naddress register with the address of X. The same is true for PC-\nrelative addressing:\n```\n\tMOVE X(PC),DO\t(legal) \n\tMOVE D0,X(PC)\t(illegal) \n```\nWhen you begin asking how such non-orthogonal behavior came about, \nyou find that someone in Motorola had some theories about how \nsoftware should be written. Specifically, in this case, they \ndecided that self-modifying code, which you can implement using \nPC-relative writes, is a Bad Thing. Therefore, they designed the \nprocessor to prohibit it. Unfortunately, in the process they also \nprohibited _ALL_ writes of the forms shown above, however benign. \nNote that this was not something done by default. Extra design \nwork had to be done, and extra gates added, to destroy the natural \northogonality of the instruction set. \n \nOne of the lessons I've learned from life: If you have two \nchoices, and can't decide which one to take, sometimes the best \nthing to do is nothing. Why add extra gates to a processor to \nenforce some stranger's idea of good programming practice? Leave \nthe instructions in, and let the programmers debate what good \nprogramming practice is. Similarly, why should we add extra code \nto our parser, to test for and prevent conditions that the user \nmight prefer to do, anyway? I'd rather leave the compiler simple, \nand let the software experts debate whether the practices should \nbe used or not. \n \nAll of which serves as rationalization for my decision as to how \nto prevent mixed-type arithmetic: I won't. For a language \nintended for systems programming, the fewer rules, the better. If \nyou don't agree, and want to test for such conditions, we can do \nit once we have a symbol table. \n "},{title:'BOOLEAN "AND" ',path:"/boolean-and",content:" \nWith that bit of philosophy out of the way, we can press on to the \n\"and\" operator, which goes into procedure Term. By now, you can \nprobably do this without me, but here's the code, anyway: \n \nIn Scanner, \n```delphi\n{--------------------------------------------------------------} \nfunction IsMulop(c: char): boolean; \nbegin \n\tIsMulop := c in ['*','/', '&']; \nend; \n{--------------------------------------------------------------} \n```\nIn Parser, \n \n```delphi\n{--------------------------------------------------------------} \nprocedure Term; \nbegin \n\tFactor; \n\twhile IsMulop(Look) do \n\t\tcase Look of \n\t\t\t'*': Multiply; \n\t\t\t'/': Divide; \n\t\t\t'&': _And; \n\t\tend; \nend; \n \n{--------------------------------------------------------------} \n{ Parse and Translate a Boolean And Operation } \n \nprocedure _And; \nbegin \n\tMatch('&'); \n\tPush; \n\tFactor; \n\tPopAnd; \nend; \n{--------------------------------------------------------------} \n```\nand in CodeGen, \n \n```delphi \n{--------------------------------------------------------------} \n{ And Primary with TOS } \n \nprocedure PopAnd; \nbegin \n\tEmitLn('AND (SP)+,D0'); \nend; \n{--------------------------------------------------------------} \n```\nYour parser should now be able to process almost any sort of logical \nexpression, and (should you be so inclined), mixed-mode expressions as \nwell. \n \nWhy not \"all sorts of logical expressions\"? Because, so far, we haven't \ndealt with the logical \"not\" operator, and this is where it gets tricky. \nThe logical \"not\" operator seems, at first glance, to be identical in \nits behavior to the unary minus, so my first thought was to let the \nexclusive or operator, '~', double as the unary \"not.\" That didn't \nwork. In my first attempt, procedure SignedTerm simply ate my '~', \nbecause the character passed the test for an addop, but SignedTerm \nignores all addops except '-'. It would have been easy enough to add \nanother line to SignedTerm, but that would still not solve the problem, \nbecause note that Expression only accepts a signed term for the _FIRST_ \nargument. \n \nMathematically, an expression like: `-a * -b`\n \nmakes little or no sense, and the parser should flag it as an error. \nBut the same expression, using a logical \"not,\" makes perfect sense: \n \n\t`not a and not b` \n \nIn the case of these unary operators, choosing to make them act the same \nway seems an artificial force fit, sacrificing reasonable behavior on \nthe altar of implementational ease. While I'm all for keeping the \nimplementation as simple as possible, I don't think we should do so at \nthe expense of reasonableness. Patching like this would be missing the \nmain point, which is that the logical \"not\" is simply NOT the same kind \nof animal as the unary minus. Consider the exclusive or, which is most \nnaturally written as: `a~b ::= (a and not b) or (not a and b)` \n \nIf we allow the \"not\" to modify the whole term, the last term in \nparentheses would be interpreted as: `not(a and b)` \n \nwhich is not the same thing at all. So it's clear that the logical \n\"not\" must be thought of as connected to the FACTOR, not the term. \n \nThe idea of overloading the '~' operator also makes no sense from a \nmathematical point of view. The implication of the unary minus is that \nit's equivalent to a subtraction from zero: `-x <=> 0-x `\n \nIn fact, in one of my more simple-minded versions of Expression, I \nreacted to a leading addop by simply preloading a zero, then processing \nthe operator as though it were a binary operator. But a \"not\" is not \nequivalent to an exclusive or with zero ... that would just give back \nthe original number. Instead, it's an exclusive or with FFFFh, or -1. \n \nIn short, the seeming parallel between the unary \"not\" and the unary \nminus falls apart under closer scrutiny. \"not\" modifies the factor, not \nthe term, and it is not related to either the unary minus nor the \nexclusive or. Therefore, it deserves a symbol to call its own. What \nbetter symbol than the obvious one, also used by C, the '!' character? \nUsing the rules about the way we think the \"not\" should behave, we \nshould be able to code the exclusive or (assuming we'd ever need to), in \nthe very natural form: `a & !b | !a & b` \n \nNote that no parentheses are required -- the precedence levels we've \nchosen automatically take care of things. \n \nIf you're keeping score on the precedence levels, this definition puts \nthe '!' at the top of the heap. The levels become: \n \n1.\t! \n2.\t`- (unary) `\n3.\t`*, /, &` \n4.\t`+, -, |, ~` \n \nLooking at this list, it's certainly not hard to see why we had trouble \nusing '~' as the \"not\" symbol! \n \nSo how do we mechanize the rules? In the same way as we did with \nSignedTerm, but at the factor level. We'll define a procedure \n`NotFactor`: \n \n```delphi\n{--------------------------------------------------------------} \n{ Parse and Translate a Factor with Optional \"Not\" } \n \nprocedure NotFactor; \nbegin \n\tif Look ='!' then begin \n\t\tMatch('!'); \n\t\tFactor; \n\t\tNotit; \n\t\tend \n\telse \n\t\tFactor; \nend; \n{--------------------------------------------------------------} \n```\n \nand call it from all the places where we formerly called Factor, i.e., \nfrom Term, Multiply, Divide, and _And. Note the new code generation \nprocedure: \n \n```delphi\n{--------------------------------------------------------------} \n{ Bitwise Not Primary } \n \nprocedure NotIt; \nbegin \n\tEmitLn('EOR #-1,D0'); \nend; \n \n{--------------------------------------------------------------} \n```\n \nTry this now, with a few simple cases. In fact, try that exclusive or \nexample, `a&!b|!a&b`\n \n \nYou should get the code (without the comments, of course): \n```\n MOVE A(PC),DO ; load a \n MOVE D0,-(SP)\t\t; push it \n MOVE B(PC),DO\t\t; load b \n EOR #-1,D0\t\t; not it \n AND (SP)+,D0\t\t; and with a \n MOVE D0,-(SP)\t\t; push result \n MOVE A(PC),DO\t\t; load a \n EOR #-1,D0\t\t; not it \n MOVE D0,-(SP)\t\t; push it \n MOVE B(PC),DO\t\t; load b \n AND (SP)+,D0\t\t; and with !a \n OR (SP)+,D0\t\t; or with first term \n```\nThat's precisely what we'd like to get. So, at least for both \narithmetic and logical operators, our new precedence and new, slimmer \nsyntax hang together. Even the peculiar, but legal, expression with \nleading addop: `~x` \n \nmakes sense. SignedTerm ignores the leading '~', as it should, since \nthe expression is equivalent to: `0~x`, \n \nwhich is equal to x. \n \nWhen we look at the BNF we've created, we find that our boolean algebra \nnow adds only one extra line: \n \n```\n<not_factor> \t::= [!] <factor> \n<factor> \t::= <variable> | <constant> | '(' <expression> ')'\t \n<signed_term> \t::= [<addop>] <term> \n<term> \t::= <not_factor> (<mulop> <not_factor>)*\t \n<expression> \t::= <signed_term> (<addop> <term>)* \n<assignment> \t::= <variable> '=' <expression> \n```\n \nThat's a big improvement over earlier efforts. Will our luck continue \nto hold when we get to relational operators? We'll find out soon, but \nit will have to wait for the next installment. We're at a good stopping \nplace, and I'm anxious to get this installment into your hands. It's \nalready been a year since the release of Installment 15. I blush to \nadmit that all of this current installment has been ready for almost as \nlong, with the exception of relational operators. But the information \ndoes you no good at all, sitting on my hard disk, and by holding it back \nuntil the relational operations were done, I've kept it out of your \nhands for that long. It's time for me to let go of it and get it out \nwhere you can get value from it. Besides, there are quite a number of \nserious philosophical questions associated with the relational \noperators, as well, and I'd rather save them for a separate installment \nwhere I can do them justice. \n \nHave fun with the new, leaner arithmetic and logical parsing, and I'll \nsee you soon with relationals. \n "}]}]},{}]},{},[165]);
  2. //# sourceMappingURL=bundle.react.js.map