87627cb8创建于 7月13日历史提交
// AST.fu - Fusion abstract syntax tree
//
// Copyright (C) 2011-2026  Piotr Fusik
//
// This file is part of Fusion Transpiler,
// see https://github.com/fusionlanguage/fut
//
// Fusion Transpiler is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Fusion Transpiler is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Fusion Transpiler.  If not, see http://www.gnu.org/licenses/

public enum FuVisibility
{
	Private,
	Internal,
	Protected,
	Public,
	NumericElementType,
	FinalValueType
}

public enum FuCallType
{
	Static,
	Normal,
	Abstract,
	Virtual,
	Override,
	Sealed
}

public enum FuPriority
{
	Statement,
	Argument,
	Assign,
	Select,
	SelectCond,
	CondOr,
	CondAnd,
	Or,
	Xor,
	And,
	Equality,
	Rel,
	Shift,
	Add,
	Mul,
	Primary
}

public enum FuId
{
	None,
	VoidType,
	NullType,
	BasePtr,
	TypeParam0,
	TypeParam0NotFinal,
	TypeParam0Predicate,
	TypeParam1,
	SByteRange,
	ByteRange,
	ShortRange,
	UShortRange,
	IntType,
	NIntType,
	LongType,
	FloatType,
	DoubleType,
	FloatIntType,
	FloatingType,
	NumericType,
	BoolType,
	StringClass,
	StringPtrType,
	StringStorageType,
	MainArgsType,
	ArrayPtrClass,
	ArrayStorageClass,
	ExceptionClass,
	ListClass,
	QueueClass,
	StackClass,
	PriorityQueueClass,
	HashSetClass,
	SortedSetClass,
	DictionaryClass,
	SortedDictionaryClass,
	OrderedDictionaryClass,
	TextWriterClass,
	StringWriterClass,
	RegexOptionsEnum,
	RegexClass,
	MatchClass,
	JsonElementClass,
	LockClass,
	StringLength,
	ArrayLength,
	ConsoleError,
	ConsoleOut,
	Main,
	ClassToString,
	MatchStart,
	MatchEnd,
	MatchLength,
	MatchValue,
	MathNaN,
	MathNegativeInfinity,
	MathPositiveInfinity,
	EnumFromInt,
	EnumHasFlag,
	EnumToInt,
	IntTryParse,
	NIntTryParse,
	LongTryParse,
	FloatTryParse,
	DoubleTryParse,
	StringContains,
	StringEndsWith,
	StringIndexOf,
	StringLastIndexOf,
	StringReplace,
	StringStartsWith,
	StringSubstring,
	StringToLower,
	StringToUpper,
	ArrayBinarySearchAll,
	ArrayBinarySearchPart,
	ArrayContains,
	ArrayCopyTo,
	ArrayFillAll,
	ArrayFillPart,
	ArraySortAll,
	ArraySortPart,
	ListAdd,
	ListAddRange,
	ListAll,
	ListAny,
	ListClear,
	ListContains,
	ListCopyTo,
	ListCount,
	ListIndexOf,
	ListInsert,
	ListLast,
	ListRemoveAt,
	ListRemoveRange,
	ListSortAll,
	ListSortPart,
	QueueClear,
	QueueCount,
	QueueDequeue,
	QueueEnqueue,
	QueuePeek,
	StackClear,
	StackCount,
	StackPeek,
	StackPush,
	StackPop,
	PriorityQueueClear,
	PriorityQueueCount,
	PriorityQueueDequeue,
	PriorityQueueEnqueue,
	PriorityQueuePeek,
	HashSetAdd,
	HashSetClear,
	HashSetContains,
	HashSetCount,
	HashSetRemove,
	SortedSetAdd,
	SortedSetClear,
	SortedSetContains,
	SortedSetCount,
	SortedSetRemove,
	DictionaryAdd,
	DictionaryClear,
	DictionaryContainsKey,
	DictionaryCount,
	DictionaryRemove,
	SortedDictionaryClear,
	SortedDictionaryContainsKey,
	SortedDictionaryCount,
	SortedDictionaryRemove,
	OrderedDictionaryClear,
	OrderedDictionaryContainsKey,
	OrderedDictionaryCount,
	OrderedDictionaryRemove,
	TextWriterFlush,
	TextWriterWrite,
	TextWriterWriteChar,
	TextWriterWriteCodePoint,
	TextWriterWriteLine,
	ConsoleReadLine,
	ConsoleWrite,
	ConsoleWriteLine,
	StringWriterClear,
	StringWriterToString,
	BitConverterInt32BitsToSingle,
	BitConverterInt64BitsToDouble,
	BitConverterSingleToInt32Bits,
	BitConverterDoubleToInt64Bits,
	ConvertToBase64String,
	UTF8GetByteCount,
	UTF8GetBytes,
	UTF8GetString,
	EnvironmentGetEnvironmentVariable,
	DateTimeOffsetUtcNowToUnixTimeMilliseconds,
	RegexCompile,
	RegexEscape,
	RegexIsMatchStr,
	RegexIsMatchRegex,
	MatchFindStr,
	MatchFindRegex,
	MatchGetCapture,
	JsonElementParse,
	JsonElementIsObject,
	JsonElementIsArray,
	JsonElementIsString,
	JsonElementIsNumber,
	JsonElementIsBoolean,
	JsonElementIsNull,
	JsonElementGetObject,
	JsonElementGetArray,
	JsonElementGetString,
	JsonElementGetDouble,
	JsonElementGetBoolean,
	MathMethod,
	MathAbs,
	MathCeiling,
	MathClamp,
	MathFusedMultiplyAdd,
	MathIsFinite,
	MathIsInfinity,
	MathIsNaN,
	MathLog2,
	MathMax,
	MathMin,
	MathRound,
	MathSqrt,
	MathTruncate
}

public abstract class FuDocInline
{
}

public class FuDocText : FuDocInline
{
	internal string() Text;
}

public class FuDocCode : FuDocInline
{
	internal string() Text;
}

public class FuDocLine : FuDocInline
{
}

public abstract class FuDocBlock
{
}

public class FuDocPara : FuDocBlock
{
	internal List<FuDocInline#>() Children;
}

public class FuDocList : FuDocBlock
{
	internal List<FuDocPara()>() Items;
}

public class FuCodeDoc
{
	internal FuDocPara() Summary;
	internal List<FuDocBlock#>() Details;
}

public abstract class FuVisitor
{
	protected void VisitOptionalStatement!(FuStatement? statement)
	{
		if (statement != null)
			statement.AcceptStatement(this);
	}

	internal abstract void VisitConst!(FuConst statement);
	internal abstract void VisitExpr!(FuExpr statement);
	internal abstract void VisitBlock!(FuBlock statement);
	internal abstract void VisitAssert!(FuAssert statement);
	internal abstract void VisitBreak!(FuBreak statement);
	internal abstract void VisitContinue!(FuContinue statement);
	internal abstract void VisitDoWhile!(FuDoWhile statement);
	internal abstract void VisitFor!(FuFor statement);
	internal abstract void VisitForeach!(FuForeach statement);
	internal abstract void VisitIf!(FuIf statement);
	internal abstract void VisitLock!(FuLock statement);
	internal abstract void VisitNative!(FuNative statement);
	internal abstract void VisitReturn!(FuReturn statement);
	internal abstract void VisitSwitch!(FuSwitch statement);
	internal abstract void VisitThrow!(FuThrow statement);
	internal abstract void VisitWhile!(FuWhile statement);
	internal abstract void VisitEnumValue!(FuConst konst, FuConst? previous);
	internal abstract void VisitLiteralNull!();
	internal abstract void VisitLiteralFalse!();
	internal abstract void VisitLiteralTrue!();
	internal abstract void VisitLiteralLong!(long value, FuPriority parent);
	internal abstract void VisitLiteralChar!(int value);
	internal abstract void VisitLiteralDouble!(double value);
	internal abstract void VisitLiteralString!(string value);
	internal abstract void VisitAggregateInitializer!(FuAggregateInitializer expr);
	internal abstract void VisitInterpolatedString!(FuInterpolatedString expr, FuPriority parent);
	internal abstract void VisitSymbolReference!(FuSymbolReference expr, FuPriority parent);
	internal abstract void VisitPrefixExpr!(FuPrefixExpr expr, FuPriority parent);
	internal abstract void VisitPostfixExpr!(FuPostfixExpr expr, FuPriority parent);
	internal abstract void VisitBinaryExpr!(FuBinaryExpr expr, FuPriority parent);
	internal abstract void VisitSelectExpr!(FuSelectExpr expr, FuPriority parent);
	internal abstract void VisitCallExpr!(FuCallExpr expr, FuPriority parent);
	internal abstract void VisitLambdaExpr!(FuLambdaExpr expr);
	internal abstract void VisitVar!(FuVar expr);
}

public abstract class FuStatement
{
	internal int Loc = 0;
	public virtual int GetLocLength() => 0;
	public abstract bool CompletesNormally();
	public abstract void AcceptStatement(FuVisitor! visitor);
}

public abstract class FuExpr : FuStatement
{
	internal FuType#? Type;
	public override bool CompletesNormally() => true;
	public virtual string() ToString() { assert false; }
	public virtual bool HasSideEffect() => false;
	public virtual bool IsIndexing() => false;
	public virtual bool IsLiteralZero() => false;
	public virtual int IntValue() { assert false; }
	public virtual bool IsConst(bool varIsConst) => false;
	public virtual void Accept(FuVisitor! visitor, FuPriority parent) { assert false; }
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitExpr(this); }
	public virtual bool IsReferenceTo(FuSymbol symbol) => false;
	public virtual bool IsNewString(bool substringOffset) => false;
	public virtual bool IsUnique() => false;
	public virtual void SetShared() { }
}

public abstract class FuName : FuExpr
{
	internal string() Name = "";
	public override int GetLocLength() => this.Name.Length;
	public abstract FuSymbol? GetSymbol();
}

public abstract class FuSymbol : FuName
{
	internal FuId Id = FuId.None;
	internal FuSymbol!? Next;
	internal FuScope!? Parent;
	internal FuCodeDoc#? Documentation = null;
	public override FuSymbol? GetSymbol() => this;
	public override string() ToString() => this.Name;
}

public abstract class FuScope : FuSymbol
{
	protected Dictionary<string, FuSymbol#>() Dict;
	internal FuSymbol!? First = null;
	internal FuSymbol!? Last = null;

	public int Count() => this.Dict.Count;

	public bool Contains(FuSymbol symbol) => this.Dict.ContainsKey(symbol.Name);

	public FuSymbol#? TryLookup(string name, bool global)
	{
		for (FuScope? scope = this; scope != null && (global || !(scope is FuProgram || scope is FuSystem)); scope = scope.Parent) {
			if (scope.Dict.ContainsKey(name))
				return scope.Dict[name];
		}
		return null;
	}

	protected void AddToList!(FuSymbol# symbol)
	{
		symbol.Next = null;
		symbol.Parent = this;
		if (this.First == null)
			this.First = symbol;
		else
			this.Last.Next = symbol;
		this.Last = symbol;
	}

	public void Add!(FuSymbol# symbol)
	{
		this.Dict[symbol.Name] = symbol;
		AddToList(symbol);
	}

	public bool Encloses(FuSymbol symbol)
	{
		for (FuScope? scope = symbol.Parent; scope != null; scope = scope.Parent) {
			if (scope == this)
				return true;
		}
		return false;
	}
}

public class FuAggregateInitializer : FuExpr
{
	internal List<FuExpr#>() Items;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitAggregateInitializer(this); }
}

public abstract class FuLiteral : FuExpr
{
	public override bool IsConst(bool varIsConst) => true;
	public abstract bool IsDefaultValue();
	public virtual string() GetLiteralString() { assert false; }
}

class FuLiteralNull : FuLiteral
{
	public override int GetLocLength() => 4;
	public override bool IsDefaultValue() => true;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitLiteralNull(); }
	public override bool IsUnique() => true;
	public override string() ToString() => "null";
}

class FuLiteralFalse : FuLiteral
{
	public override int GetLocLength() => 5;
	public override bool IsDefaultValue() => true;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitLiteralFalse(); }
	public override string() ToString() => "false";
}

class FuLiteralTrue : FuLiteral
{
	public override int GetLocLength() => 4;
	public override bool IsDefaultValue() => false;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitLiteralTrue(); }
	public override string() ToString() => "true";
}

class FuLiteralLong : FuLiteral
{
	internal long Value;
	public override bool IsLiteralZero() => this.Value == 0;
	public override int IntValue() => this.Value;
	public override bool IsDefaultValue() => this.Value == 0;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitLiteralLong(this.Value, parent); }
	public override string() GetLiteralString() => $"{this.Value}";
	public override string() ToString() => $"{this.Value}";
}

class FuLiteralChar : FuLiteralLong
{
	public static FuLiteralChar# New(int value, int loc) => new FuLiteralChar { Loc = loc, Type = FuRangeType.New(value, value), Value = value };
	public override int GetLocLength() => this.Value >= 0x10000 || this.Value == '\n' || this.Value == '\r' || this.Value == '\t' || this.Value == '\\' || this.Value == '\'' ? 4 : 3;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitLiteralChar(this.Value); }
//	public override string() ToString()
//	{
//		switch (this.Value) {
//		case '\n': return "'\\n'";
//		case '\r': return "'\\r'";
//		case '\t': return "'\\t'";
//		case '\\': return "'\\\\'";
//		case '\'': return "'\\''";
//		default: return $"'{(char) this.Value}'";
//		}
//	}
}

class FuLiteralDouble : FuLiteral
{
	internal double Value;
	public override bool IsDefaultValue() => this.Value == 0 && 1.0 / this.Value > 0; // rule out -0.0
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitLiteralDouble(this.Value); }
	public override string() GetLiteralString() => $"{this.Value}";
	public override string() ToString() => $"{this.Value}";
}

class FuLiteralString : FuLiteral
{
	internal string() Value;
	public override bool IsDefaultValue() => false;
	public override int GetLocLength() => this.Value.Length + 2;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitLiteralString(this.Value); }
	public override string() GetLiteralString() => this.Value;
	public override string() ToString() => $"\"{this.Value}\"";
	public int GetAsciiLength()
	{
		int length = 0;
		bool escaped = false;
		foreach (int c in this.Value) {
			if (c < 0 || c > 127)
				return -1;
			if (!escaped && c == '\\')
				escaped = true;
			else {
				length++;
				escaped = false;
			}
		}
		return length;
	}
	public int GetAsciiAt(int i)
	{
		bool escaped = false;
		foreach (int c in this.Value) {
			if (c < 0 || c > 127)
				return -1;
			if (!escaped && c == '\\')
				escaped = true;
			else if (i == 0)
				return escaped ? FuLexer.GetEscapedChar(c) : c;
			else {
				i--;
				escaped = false;
			}
		}
		return -1;
	}
	public int GetOneAscii()
	{
		switch (this.Value.Length) {
		case 1:
			int c = this.Value[0];
			return c >= 0 && c <= 127 ? c : -1;
		case 2:
			return this.Value[0] == '\\' ? FuLexer.GetEscapedChar(this.Value[1]) : -1;
		default:
			return -1;
		}
	}
}

public class FuInterpolatedPart
{
	internal string() Prefix;
	internal FuExpr# Argument;
	internal FuExpr#? WidthExpr;
	internal int Width;
	internal int Format;
	internal int Precision;
}

public class FuInterpolatedString : FuExpr
{
	internal List<FuInterpolatedPart()>() Parts;
	internal string() Suffix;
	public void AddPart!(string prefix, FuExpr# arg, FuExpr#? widthExpr = null, int format = ' ', int precision = -1)
	{
		this.Parts.Add();
		FuInterpolatedPart! part = this.Parts.Last();
		part.Prefix = prefix;
		part.Argument = arg;
		part.WidthExpr = widthExpr;
		part.Format = format;
		part.Precision = precision;
	}
	public override int GetLocLength() => 2; // FIXME
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitInterpolatedString(this, parent); }
//	public override bool IsConst(bool varIsConst) => this.Parts.All(part => part.Argument.IsConst(varIsConst));
	public override bool IsNewString(bool substringOffset) => true;
	public bool IsToString(int format)
	{
		return this.Suffix.Length == 0
			&& this.Parts.Count == 1
			&& this.Parts[0].Prefix.Length == 0
			&& this.Parts[0].WidthExpr == null
			&& (this.Parts[0].Format | 0x20) == format;
	}
//	public override string() ToString()
//	{
//		StringBuilder sb = new StringBuilder();
//		sb.Append("$\"");
//		foreach (FuInterpolatedPart part in this.Parts) {
//			sb.Append(part.Prefix.Replace("{", "{{"));
//			sb.Append('{');
//			sb.Append(part.Argument);
//			if (part.WidthExpr != null) {
//				sb.Append(',');
//				sb.Append(part.WidthExpr);
//			}
//			if (part.Format != ' ') {
//				sb.Append(':');
//				sb.Append((char) part.Format);
//				if (part.Precision >= 0)
//					sb.Append(part.Precision);
//			}
//			sb.Append('}');
//		}
//		sb.Append(this.Suffix.Replace("{", "{{"));
//		sb.Append('"');
//		return sb.ToString();
//	}
}

class FuImplicitEnumValue : FuExpr
{
	internal int Value;
	public override int IntValue() => this.Value;
}

public class FuSymbolReference : FuName
{
	internal FuExpr#? Left = null;
	internal FuSymbol!? Symbol;
	public override int IntValue()
	{
		assert this.Symbol is FuConst konst;
		return konst.Value.IntValue();
	}
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitSymbolReference(this, parent); }
	public override bool IsReferenceTo(FuSymbol symbol) => this.Symbol == symbol;
	public override bool IsConst(bool varIsConst) => this.Symbol.IsConst(varIsConst) && (this.Left == null || this.Left.IsConst(varIsConst));
	public override bool IsNewString(bool substringOffset) => this.Symbol.Id == FuId.MatchValue;
	public override void SetShared()
	{
		if (this.Symbol is FuNamedValue varOrField && varOrField.Type is FuDynamicPtrType! dynamic)
			dynamic.Unique = false;
	}
	public override FuSymbol? GetSymbol() => this.Symbol;
	public override string() ToString() => this.Left != null ? $"{this.Left}.{this.Name}" : this.Name;
}

public abstract class FuUnaryExpr : FuExpr
{
	internal FuToken Op;
	internal FuExpr#? Inner;
	public override int GetLocLength()
	{
		switch (this.Op) {
		case FuToken.Increment:
		case FuToken.Decrement:
			return 2;
		case FuToken.Minus:
		case FuToken.Tilde:
		case FuToken.ExclamationMark:
		case FuToken.Hash:
		case FuToken.QuestionMark:
			return 1;
		case FuToken.New:
			return 3;
		case FuToken.Resource:
			return 8;
		default:
			assert false;
		}
	}
}

public class FuPrefixExpr : FuUnaryExpr
{
	public override int IntValue()
	{
		assert this.Op == FuToken.Tilde;
		return ~this.Inner.IntValue();
	}
	public override bool HasSideEffect() => this.Op == FuToken.Increment || this.Op == FuToken.Decrement;
	public override bool IsConst(bool varIsConst) => (this.Op == FuToken.Minus || this.Op == FuToken.Tilde || this.Op == FuToken.ExclamationMark || this.Op == FuToken.Resource) && this.Inner.IsConst(varIsConst);
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitPrefixExpr(this, parent); }
	public override bool IsUnique() => this.Op == FuToken.New && !(this.Inner is FuAggregateInitializer);
}

public class FuPostfixExpr : FuUnaryExpr
{
	public override bool HasSideEffect() => true;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitPostfixExpr(this, parent); }
}

public class FuBinaryExpr : FuExpr
{
	internal FuExpr# Left;
	internal FuToken Op;
	internal FuExpr#? Right;
	public override int GetLocLength()
	{
		switch (this.Op) {
		case FuToken.LeftBracket:
		case FuToken.LeftBrace:
		case FuToken.Plus:
		case FuToken.Minus:
		case FuToken.Asterisk:
		case FuToken.Slash:
		case FuToken.Mod:
		case FuToken.And:
		case FuToken.Or:
		case FuToken.Xor:
		case FuToken.Less:
		case FuToken.Greater:
		case FuToken.Assign:
			return 1;
		case FuToken.ShiftLeft:
		case FuToken.ShiftRight:
		case FuToken.Equal:
		case FuToken.NotEqual:
		case FuToken.LessOrEqual:
		case FuToken.GreaterOrEqual:
		case FuToken.CondAnd:
		case FuToken.CondOr:
		case FuToken.AddAssign:
		case FuToken.SubAssign:
		case FuToken.MulAssign:
		case FuToken.DivAssign:
		case FuToken.ModAssign:
		case FuToken.AndAssign:
		case FuToken.OrAssign:
		case FuToken.XorAssign:
		case FuToken.Is:
			return 2;
		case FuToken.ShiftLeftAssign:
		case FuToken.ShiftRightAssign:
			return 3;
		case FuToken.When:
			return 0;
		default:
			assert false;
		}
	}
	public override bool IsIndexing() => this.Op == FuToken.LeftBracket;
	public override int IntValue()
	{
		switch (this.Op) {
		case FuToken.And:
			return this.Left.IntValue() & this.Right.IntValue();
		case FuToken.Or:
			return this.Left.IntValue() | this.Right.IntValue();
		case FuToken.Xor:
			return this.Left.IntValue() ^ this.Right.IntValue();
		default:
			assert false;
		}
	}
	public override bool IsConst(bool varIsConst) => this.Left.IsConst(varIsConst) && this.Right.IsConst(varIsConst);
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitBinaryExpr(this, parent); }
	public override bool IsNewString(bool substringOffset) => this.Op == FuToken.Plus && this.Type.Id == FuId.StringStorageType;
	public bool IsRel()
	{
		switch (this.Op) {
		case FuToken.Equal:
		case FuToken.NotEqual:
		case FuToken.Less:
		case FuToken.LessOrEqual:
		case FuToken.Greater:
		case FuToken.GreaterOrEqual:
			return true;
		default:
			return false;
		}
	}
	public override bool HasSideEffect()
	{
		switch (this.Op) {
		case FuToken.Assign:
		case FuToken.AddAssign:
		case FuToken.SubAssign:
		case FuToken.MulAssign:
		case FuToken.DivAssign:
		case FuToken.ModAssign:
		case FuToken.AndAssign:
		case FuToken.OrAssign:
		case FuToken.XorAssign:
		case FuToken.ShiftLeftAssign:
		case FuToken.ShiftRightAssign:
			return true;
		default:
			return false;
		}
	}

	public string GetOpString()
	{
		switch (this.Op) {
		case FuToken.Plus:
			return "+";
		case FuToken.Minus:
			return "-";
		case FuToken.Asterisk:
			return "*";
		case FuToken.Slash:
			return "/";
		case FuToken.Mod:
			return "%";
		case FuToken.ShiftLeft:
			return "<<";
		case FuToken.ShiftRight:
			return ">>";
		case FuToken.Less:
			return "<";
		case FuToken.LessOrEqual:
			return "<=";
		case FuToken.Greater:
			return ">";
		case FuToken.GreaterOrEqual:
			return ">=";
		case FuToken.Equal:
			return "==";
		case FuToken.NotEqual:
			return "!=";
		case FuToken.And:
			return "&";
		case FuToken.Or:
			return "|";
		case FuToken.Xor:
			return "^";
		case FuToken.CondAnd:
			return "&&";
		case FuToken.CondOr:
			return "||";
		case FuToken.Assign:
			return "=";
		case FuToken.AddAssign:
			return "+=";
		case FuToken.SubAssign:
			return "-=";
		case FuToken.MulAssign:
			return "*=";
		case FuToken.DivAssign:
			return "/=";
		case FuToken.ModAssign:
			return "%=";
		case FuToken.ShiftLeftAssign:
			return "<<=";
		case FuToken.ShiftRightAssign:
			return ">>=";
		case FuToken.AndAssign:
			return "&=";
		case FuToken.OrAssign:
			return "|=";
		case FuToken.XorAssign:
			return "^=";
		default:
			assert false;
		}
	}

	public override string() ToString() => this.Op == FuToken.LeftBracket ? $"{this.Left}[{this.Right}]" : $"({this.Left} {GetOpString()} {this.Right})";
}

public class FuSelectExpr : FuExpr
{
	internal FuExpr# Cond;
	internal FuExpr# OnTrue;
	internal FuExpr# OnFalse;
	public override int GetLocLength() => 1;
	public override bool IsConst(bool varIsConst) => this.Cond.IsConst(varIsConst) && this.OnTrue.IsConst(varIsConst) && this.OnFalse.IsConst(varIsConst);
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitSelectExpr(this, parent); }
	public override bool IsUnique() => this.OnTrue.IsUnique() && this.OnFalse.IsUnique();
	public override void SetShared()
	{
		this.OnTrue.SetShared();
		this.OnFalse.SetShared();
	}
	public override string() ToString() => $"({this.Cond} ? {this.OnTrue} : {this.OnFalse})";
}

public class FuCallExpr : FuExpr
{
	internal FuSymbolReference# Method;
	internal List<FuExpr#>() Arguments;
	public override bool HasSideEffect() => true;
	public override bool IsConst(bool varIsConst)
	{
		assert this.Method.Symbol is FuMethod method;
		return method.IsPure() && this.Arguments.All(arg => arg.IsConst(varIsConst));
	}
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitCallExpr(this, parent); }
	public override bool IsNewString(bool substringOffset)
		=> this.Type.Id == FuId.StringStorageType
			&& this.Method.Symbol.Id != FuId.ListLast && this.Method.Symbol.Id != FuId.QueuePeek && this.Method.Symbol.Id != FuId.StackPeek
			&& (substringOffset || this.Method.Symbol.Id != FuId.StringSubstring || this.Arguments.Count != 1);
	public override string() ToString() => this.Method.Name;
}

class FuLambdaExpr : FuScope
{
	internal FuExpr# Body;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitLambdaExpr(this); }
}

public abstract class FuCondCompletionStatement : FuScope
{
	bool CompletesNormallyValue;
	public override bool CompletesNormally() => this.CompletesNormallyValue;
	public void SetCompletesNormally!(bool value) { this.CompletesNormallyValue = value; }
}

public class FuBlock : FuCondCompletionStatement
{
	internal List<FuStatement#>() Statements;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitBlock(this); }
}

public class FuAssert : FuStatement
{
	internal FuExpr# Cond;
	internal FuExpr#? Message = null;
	public override int GetLocLength() => 6;
	public override bool CompletesNormally() => !(this.Cond is FuLiteralFalse);
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitAssert(this); }
}

public abstract class FuLoop : FuCondCompletionStatement
{
	internal FuExpr#? Cond;
	internal FuStatement# Body;
	internal bool HasBreak = false;
}

class FuBreak : FuStatement
{
	internal FuCondCompletionStatement! LoopOrSwitch;
	public override int GetLocLength() => 5;
	public override bool CompletesNormally() => false;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitBreak(this); }
}

class FuContinue : FuStatement
{
	internal FuLoop Loop;
	public override int GetLocLength() => 8;
	public override bool CompletesNormally() => false;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitContinue(this); }
}

class FuDoWhile : FuLoop
{
	public override int GetLocLength() => 2;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitDoWhile(this); }
}

class FuFor : FuLoop
{
	internal FuExpr#? Init;
	internal FuExpr#? Advance;
	internal bool IsRange = false;
	internal bool IsIndVarUsed;
	internal long RangeStep;
	public override int GetLocLength() => 3;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitFor(this); }
}

class FuForeach : FuLoop
{
	internal FuExpr# Collection;
	public override int GetLocLength() => 7;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitForeach(this); }
	public FuVar! GetVar()
	{
		assert this.First is FuVar! result;
		return result;
	}
	public FuVar! GetValueVar() => this.GetVar().NextVar();
}

class FuIf : FuCondCompletionStatement
{
	internal FuExpr# Cond;
	internal FuStatement# OnTrue;
	internal FuStatement#? OnFalse;
	public override int GetLocLength() => 2;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitIf(this); }
}

class FuLock : FuStatement
{
	internal FuExpr# Lock;
	internal FuStatement# Body;
	public override int GetLocLength() => 4;
	public override bool CompletesNormally() => this.Body.CompletesNormally();
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitLock(this); }
}

public class FuNative : FuSymbol
{
	internal string() Content;
	public override int GetLocLength() => 6;
	public override bool CompletesNormally() => true;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitNative(this); }
	public FuMember? GetFollowingMember()
	{
		for (FuSymbol? symbol = this.Next; symbol != null; symbol = symbol.Next) {
			if (symbol is FuMember member)
				return member;
		}
		return null;
	}
}

class FuReturn : FuScope
{
	internal FuExpr#? Value;
	public override int GetLocLength() => 6;
	public override bool CompletesNormally() => false;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitReturn(this); }
}

public class FuCase
{
	internal List<FuExpr#>() Values;
	internal List<FuStatement#>() Body;
}

public class FuSwitch : FuCondCompletionStatement
{
	internal FuExpr# Value;
	internal List<FuCase()>() Cases;
	internal List<FuStatement#>() DefaultBody;
	public override int GetLocLength() => 6;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitSwitch(this); }

	public bool IsTypeMatching() => this.Value.Type is FuClassType klass && klass.Class.Id != FuId.StringClass;

	public bool HasWhen() => this.Cases.Any(kase => kase.Values.Any(value => value is FuBinaryExpr when1 && when1.Op == FuToken.When));

	public static int LengthWithoutTrailingBreak(List<FuStatement#> body)
	{
		int length = body.Count;
		if (length > 0 && body[length - 1] is FuBreak)
			length--;
		return length;
	}

	public bool HasDefault() => LengthWithoutTrailingBreak(this.DefaultBody) > 0;

	static bool HasBreak(FuStatement statement)
	{
		switch (statement) {
		case FuBreak:
			return true;
		case FuIf ifStatement:
			return HasBreak(ifStatement.OnTrue) || (ifStatement.OnFalse != null && HasBreak(ifStatement.OnFalse));
		case FuBlock block:
			return block.Statements.Any(child => HasBreak(child));
		default:
			return false;
		}
	}

	public static bool HasEarlyBreak(List<FuStatement#> body)
	{
		int length = LengthWithoutTrailingBreak(body);
		for (int i = 0; i < length; i++) {
			if (HasBreak(body[i]))
				return true;
		}
		return false;
	}

	static bool ListHasContinue(List<FuStatement#> statements) => statements.Any(statement => HasContinue(statement));

	static bool HasContinue(FuStatement statement)
	{
		switch (statement) {
		case FuContinue:
			return true;
		case FuIf ifStatement:
			return HasContinue(ifStatement.OnTrue) || (ifStatement.OnFalse != null && HasContinue(ifStatement.OnFalse));
		case FuSwitch switchStatement:
			return switchStatement.Cases.Any(kase => ListHasContinue(kase.Body)) || ListHasContinue(switchStatement.DefaultBody);
		case FuBlock block:
			return ListHasContinue(block.Statements);
		default:
			return false;
		}
	}

	public static bool HasEarlyBreakAndContinue(List<FuStatement#> body) => HasEarlyBreak(body) && ListHasContinue(body);
}

public class FuThrow : FuStatement
{
	internal FuSymbolReference# Class;
	internal FuExpr#? Message;
	public override int GetLocLength() => 5;
	public override bool CompletesNormally() => false;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitThrow(this); }
}

class FuWhile : FuLoop
{
	public override int GetLocLength() => 5;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitWhile(this); }
}

public class FuParameters : FuScope
{
}

public class FuType : FuScope
{
	internal bool Nullable = false;
	public virtual string() GetArraySuffix() => "";
	public virtual bool IsAssignableFrom(FuType right) => this == right;
	public virtual bool EqualsType(FuType right) => this == right;
	public virtual bool IsArray() => false;
	public virtual bool IsFinal() => false;
	public virtual FuType GetBaseType() => this;
	public virtual FuType GetStorageType() => this;
	public FuClassType AsClassType()
	{
		assert this is FuClassType klass;
		return klass;
	}
}

class FuNumericType : FuType
{
}

class FuIntegerType : FuNumericType
{
	public override bool IsAssignableFrom(FuType right) => right is FuIntegerType || right.Id == FuId.FloatIntType;
}

class FuRangeType : FuIntegerType
{
	internal int Min;
	internal int Max;

	static void AddMinMaxValue(FuRangeType# target, string name, int value)
	{
		FuRangeType# type = target.Min == target.Max ? target : new FuRangeType { Min = value, Max = value };
		target.Add(new FuConst { Visibility = FuVisibility.Public, Name = name, Value = new FuLiteralLong { Type = type, Value = value }, VisitStatus = FuVisitStatus.Done });
	}

	public static FuRangeType# New(int min, int max)
	{
		assert min <= max;
		FuRangeType# result = new FuRangeType { Id =
			min >= 0 && max <= byte.MaxValue ? FuId.ByteRange :
			min >= -128 && max <= 127 ? FuId.SByteRange :
			min >= short.MinValue && max <= short.MaxValue ? FuId.ShortRange :
			min >= 0 && max <= ushort.MaxValue ? FuId.UShortRange :
			FuId.IntType, Min = min, Max = max };
		AddMinMaxValue(result, "MinValue", min);
		AddMinMaxValue(result, "MaxValue", max);
		return result;
	}

	public override string() ToString() => this.Min == this.Max ? $"{this.Min}" : $"({this.Min} .. {this.Max})";

	public override bool IsAssignableFrom(FuType right)
	{
		switch (right) {
		case FuRangeType range:
			return this.Min <= range.Max && this.Max >= range.Min;
		case FuIntegerType:
			return true;
		default:
			return right.Id == FuId.FloatIntType;
		}
	}

	public override bool EqualsType(FuType right) => right is FuRangeType that && this.Min == that.Min && this.Max == that.Max;

	public static int GetMask(int v)
	{
		// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
		v |= v >> 1;
		v |= v >> 2;
		v |= v >> 4;
		v |= v >> 8;
		v |= v >> 16;
		return v;
	}

	public int GetVariableBits() => GetMask(this.Min ^ this.Max);
}

class FuFloatingType : FuNumericType
{
	public override bool IsAssignableFrom(FuType right) => right is FuNumericType;
}

public abstract class FuNamedValue : FuSymbol
{
	internal FuExpr#? TypeExpr;
	internal FuExpr#? Value;
	public bool IsAssignableStorage() => this.Type is FuStorageType && !(this.Type is FuArrayStorageType)
		&& this.Value != null && !(this.Value is FuSymbolReference) && !(this.Value is FuAggregateInitializer);
}

public abstract class FuMember : FuNamedValue
{
	internal FuVisibility Visibility;
	internal int StartLine;
	internal int StartColumn;
	internal int EndLine;
	internal int EndColumn;
	protected FuMember()
	{
	}
	public abstract bool IsStatic();
}

public class FuVar : FuNamedValue
{
	internal bool IsAssigned = false;
	public static FuVar# New(FuType#? type, string name, FuExpr#? defaultValue = null)
		=> new FuVar { Type = type, Name = name, Value = defaultValue };
	public override bool IsConst(bool varIsConst) => varIsConst;
	public override void Accept(FuVisitor! visitor, FuPriority parent) { visitor.VisitVar(this); }
	public FuVar!? NextVar()
	{
		assert this.Next is FuVar! def; // FIXME: FuVar!?
		return def;
	}
}

enum FuVisitStatus
{
	NotYet,
	InProgress,
	Done
}

public class FuConst : FuMember
{
	internal FuMethodBase? InMethod;
	internal int InMethodIndex = 0;
	internal FuVisitStatus VisitStatus;
	public override bool IsConst(bool VarIsConst) => true;
	public override void AcceptStatement(FuVisitor! visitor) { visitor.VisitConst(this); }
	public override bool IsStatic() => true;
}

public class FuField : FuMember
{
	public override bool IsStatic() => false;
}

class FuProperty : FuMember
{
	public override bool IsStatic() => false;
	public override bool IsConst(bool varIsConst) => this.Id == FuId.StringLength || this.Id == FuId.ArrayLength;
	public static FuProperty# New(FuType# type, FuId id, string name)
		=> new FuProperty { Visibility = FuVisibility.Public, Type = type, Id = id, Name = name };
}

class FuStaticProperty : FuMember
{
	public override bool IsStatic() => true;
	public static FuStaticProperty# New(FuType# type, FuId id, string name)
		=> new FuStaticProperty { Visibility = FuVisibility.Public, Type = type, Id = id, Name = name };
}

public class FuThrowsDeclaration : FuSymbolReference
{
	internal FuCodeDoc#? Documentation;
}

public class FuMethodBase : FuMember
{
	internal FuParameters() Parameters;
	internal List<FuThrowsDeclaration#>() Throws;
	internal FuScope#? Body;
	internal bool IsLive = false;
	internal HashSet<FuMethod!>() Calls;
	public override bool IsStatic() => false; // constructor
	public void AddThis!(FuClass? klass, bool isMutator)
	{
		FuClassType# type = isMutator ? new FuReadWriteClassType() : new FuClassType();
		type.Class = klass;
		this.Parameters.Add(FuVar.New(type, "this"));
	}
	public bool IsMutator() => this.Parameters.First.Type is FuReadWriteClassType;
}

public class FuMethod : FuMethodBase
{
	internal FuCallType CallType;
	public static FuMethod# New(FuClass? klass, FuVisibility visibility, FuCallType callType, FuType# type, FuId id, string name, bool isMutator, FuVar#? param0 = null, FuVar#? param1 = null, FuVar#? param2 = null, FuVar#? param3 = null)
	{
		FuMethod# result = new FuMethod { Visibility = visibility, CallType = callType, Type = type, Id = id, Name = name, IsLive = true };
		if (callType != FuCallType.Static)
			result.AddThis(klass, isMutator);
		if (param0 != null) {
			result.Parameters.Add(param0);
			if (param1 != null) {
				result.Parameters.Add(param1);
				if (param2 != null) {
					result.Parameters.Add(param2);
					if (param3 != null)
						result.Parameters.Add(param3);
				}
			}
		}
		return result;
	}
	public override bool IsStatic() => this.CallType == FuCallType.Static;
	public bool IsAbstractOrVirtual() => this.CallType == FuCallType.Abstract || this.CallType == FuCallType.Virtual;
	public bool IsAbstractVirtualOrOverride() => this.CallType == FuCallType.Abstract || this.CallType == FuCallType.Virtual || this.CallType == FuCallType.Override;
	public static string CallTypeToString(FuCallType callType)
	{
		switch (callType) {
		case FuCallType.Static: return "static";
		case FuCallType.Abstract: return "abstract";
		case FuCallType.Virtual: return "virtual";
		case FuCallType.Override: return "override";
		case FuCallType.Sealed: return "sealed";
		default: assert false;
		}
	}
	public FuVar!? FirstParameter()
	{
		assert this.Parameters.First is FuVar! first; // FIXME: FuVar!?
		return IsStatic() ? first : first.NextVar();
	}
	public int GetParametersCount()
	{
		int c = this.Parameters.Count();
		return IsStatic() ? c : c - 1;
	}
	public FuMethod GetDeclaringMethod()
	{
		FuMethod method = this;
		while (method.CallType == FuCallType.Override) {
			assert method.Parent.Parent.TryLookup(method.Name, false) is FuMethod baseMethod;
			method = baseMethod;
		}
		return method;
	}
	public bool IsPure()
	{
		if (!IsStatic())
			return false;
		for (FuVar? param = this.FirstParameter(); param != null; param = param.NextVar()) {
			switch (param.Type) {
			case FuNumericType:
			case FuEnum:
			case FuStringType:
				break;
			default:
				return false;
			}
		}
		return this.Body is FuReturn ret && ret.Value.IsConst(true);
	}
}

class FuMethodGroup : FuMember
{
	internal FuMethod#?[2] Methods;
	FuMethodGroup()
	{
	}
	public override bool IsStatic() { assert false; }
	public static FuMethodGroup# New(FuMethod# method0, FuMethod# method1)
	{
		FuMethodGroup# result = new FuMethodGroup { Visibility = method0.Visibility, Name = method0.Name };
		result.Methods[0] = method0;
		result.Methods[1] = method1;
		return result;
	}
}

public abstract class FuContainerType : FuType
{
	internal bool IsPublic = false;
	internal int StartLine;
	internal int StartColumn;
	internal int EndLine;
	internal int EndColumn;
	public override bool IsConst(bool varIsConst) => true;
}

public class FuEnum : FuContainerType
{
	internal bool HasExplicitValue = false;
	public FuSymbol GetFirstValue()
	{
		FuSymbol symbol = this.First;
		while (!(symbol is FuConst))
			symbol = symbol.Next;
		return symbol;
	}
	public void AcceptValues(FuVisitor! visitor)
	{
		FuConst? previous = null;
		for (FuSymbol? symbol = this.First; symbol != null; symbol = symbol.Next) {
			if (symbol is FuConst konst) { // not HasFlag
				visitor.VisitEnumValue(konst, previous);
				previous = konst;
			}
		}
	}
}

class FuEnumFlags : FuEnum
{
}

public class FuClass : FuContainerType
{
	internal FuCallType CallType;
	internal int TypeParameterCount = 0;
	internal bool HasSubclasses = false;
	internal FuSymbolReference() BaseClass;
	internal FuMethodBase#? Constructor;
	internal List<FuConst!>() ConstArrays;
	internal SortedSet<string>() CppFriends;
	List<FuNative#>() Natives; // FIXME: avoid by moving the ownership from the dictionary to the linked list?
	public bool HasBaseClass() => this.BaseClass.Name.Length > 0;
	internal void SetBaseClass!(FuClass! baseClass)
	{
		this.Parent = baseClass;
		this.BaseClass.Name = baseClass.Name;
		this.BaseClass.Symbol = baseClass;
	}
	public bool AddsVirtualMethods()
	{
		for (FuSymbol? symbol = this.First; symbol != null; symbol = symbol.Next) {
			if (symbol is FuMethod method && method.IsAbstractOrVirtual())
				return true;
		}
		return false;
	}

	public static FuClass# New(FuCallType callType, FuId id, string name, int typeParameterCount = 0)
		=> new FuClass { CallType = callType, Id = id, Name = name, TypeParameterCount = typeParameterCount };

	public void AddMethod!(FuType# type, FuId id, string name, bool isMutator, FuVar#? param0 = null, FuVar#? param1 = null, FuVar#? param2 = null, FuVar#? param3 = null)
	{
		Add(FuMethod.New(this, FuVisibility.Public, FuCallType.Normal, type, id, name, isMutator, param0, param1, param2, param3));
	}

	public void AddStaticMethod!(FuType# type, FuId id, string name, FuVar#? param0 = null, FuVar#? param1 = null, FuVar#? param2 = null)
	{
		Add(FuMethod.New(this, FuVisibility.Public, FuCallType.Static, type, id, name, false, param0, param1, param2));
	}

	public void AddNative!(FuNative# nat)
	{
		AddToList(nat);
		this.Natives.Add(nat);
	}

	public bool IsSameOrBaseOf(FuClass derived)
	{
		while (derived != this) {
			if (derived.Parent is FuClass parent)
				derived = parent;
			else
				return false;
		}
		return true;
	}

	public FuClass? GetLowestCommonAncestor(FuClass other)
	{
		for (;;) {
			if (other.IsSameOrBaseOf(this))
				return other;
			if (other.Parent is FuClass parent)
				other = parent;
			else
				return null;
		}
	}

	public bool HasToString() => TryLookup("ToString", false) is FuMethod method && method.Id == FuId.ClassToString;

	public bool AddsToString() => this.Dict.ContainsKey("ToString") && this.Dict["ToString"] is FuMethod method && method.Id == FuId.ClassToString
		&& method.CallType != FuCallType.Override && method.CallType != FuCallType.Sealed;
}

public class FuClassType : FuType
{
	internal FuClass Class;
	internal FuType#? TypeArg0;
	internal FuType#? TypeArg1;
	public FuType# GetElementType() => this.TypeArg0;
	public FuType GetKeyType() => this.TypeArg0;
	public FuType# GetValueType() => this.TypeArg1;
	public override bool IsArray() => this.Class.Id == FuId.ArrayPtrClass || this.Class.Id == FuId.ArrayStorageClass;
	public override FuType GetBaseType() => IsArray() ? GetElementType().GetBaseType() : this;

	internal bool EqualTypeArguments(FuClassType right)
	{
		switch (this.Class.TypeParameterCount) {
		case 0: return true;
		case 1: return this.TypeArg0.EqualsType(right.TypeArg0);
		case 2: return this.TypeArg0.EqualsType(right.TypeArg0) && this.TypeArg1.EqualsType(right.TypeArg1);
		default: assert false;
		}
	}

	protected bool IsAssignableFromClass(FuClassType right) => this.Class.IsSameOrBaseOf(right.Class) && EqualTypeArguments(right);

	public override bool IsAssignableFrom(FuType right)
	{
		return (this.Nullable && right.Id == FuId.NullType)
			|| (right is FuClassType rightClass && IsAssignableFromClass(rightClass));
	}

	protected bool EqualsTypeInternal(FuClassType that) => this.Nullable == that.Nullable && this.Class == that.Class && EqualTypeArguments(that);

	public override bool EqualsType(FuType right)
		=> right is FuClassType that && !(right is FuReadWriteClassType) && EqualsTypeInternal(that);

	public override string() GetArraySuffix() => IsArray() ? "[]" : "";
	public virtual string GetClassSuffix() => "";
	string GetNullableSuffix() => this.Nullable ? "?" : "";

	public override string() ToString()
	{
		if (IsArray())
			return $"{GetElementType().GetBaseType()}{GetArraySuffix()}{GetNullableSuffix()}{GetElementType().GetArraySuffix()}";
		switch (this.Class.TypeParameterCount) {
		case 0: return $"{this.Class.Name}{GetClassSuffix()}{GetNullableSuffix()}";
		case 1: return $"{this.Class.Name}<{this.TypeArg0}>{GetClassSuffix()}{GetNullableSuffix()}";
		case 2: return $"{this.Class.Name}<{this.TypeArg0}, {this.TypeArg1}>{GetClassSuffix()}{GetNullableSuffix()}";
		default: assert false;
		}
	}
}

public class FuReadWriteClassType : FuClassType
{
	public override bool IsAssignableFrom(FuType right)
	{
		return (this.Nullable && right.Id == FuId.NullType)
			|| (right is FuReadWriteClassType rightClass && IsAssignableFromClass(rightClass));
	}

	public override bool EqualsType(FuType right)
		=> right is FuReadWriteClassType that && !(right is FuOwningType) && EqualsTypeInternal(that);

	public override string() GetArraySuffix() => IsArray() ? "[]!" : "";
	public override string GetClassSuffix() => "!";
}

public abstract class FuOwningType : FuReadWriteClassType
{
}

public class FuStorageType : FuOwningType
{
	public override bool IsFinal() => this.Class.Id != FuId.MatchClass;
	public override bool IsAssignableFrom(FuType right) => right is FuStorageType rightClass && this.Class == rightClass.Class && EqualTypeArguments(rightClass);
	public override bool EqualsType(FuType right) => right is FuStorageType that && EqualsTypeInternal(that);
	public override string GetClassSuffix() => "()";
}

class FuDynamicPtrType : FuOwningType
{
	internal bool Unique = false;
	public override bool IsAssignableFrom(FuType right)
	{
		return (this.Nullable && right.Id == FuId.NullType)
			|| (right is FuDynamicPtrType rightClass && IsAssignableFromClass(rightClass));
	}
	public override bool EqualsType(FuType right) => right is FuDynamicPtrType that && EqualsTypeInternal(that);

	public override string() GetArraySuffix() => IsArray() ? "[]#" : "";
	public override string GetClassSuffix() => "#";
}

public class FuArrayStorageType : FuStorageType
{
	internal FuExpr# LengthExpr;
	internal int Length;
	internal bool PtrTaken = false;

	public override FuType GetBaseType() => GetElementType().GetBaseType();
	public override bool IsArray() => true;
	public override string() GetArraySuffix() => $"[{this.Length}]";
	public override bool EqualsType(FuType right) => right is FuArrayStorageType that && GetElementType().EqualsType(that.GetElementType()) && this.Length == that.Length;
	public override FuType GetStorageType() => GetElementType().GetStorageType();
}

class FuStringType : FuClassType
{
	public override bool EqualsType(FuType right) => this == right;
}

class FuStringStorageType : FuStringType
{
	public override bool IsAssignableFrom(FuType right) => right is FuStringType;
	public override string GetClassSuffix() => "()";
}

class FuPrintableType : FuType
{
	public override bool IsAssignableFrom(FuType right)
	{
		switch (right) {
		case FuNumericType:
		case FuStringType:
			return true;
		case FuClassType klass:
			return klass.Class.HasToString();
		default:
			return false;
		}
	}
}

public class FuSystem : FuScope
{
	internal FuType# VoidType = new FuType { Id = FuId.VoidType, Name = "void" };
	internal FuType# NullType = new FuType { Id = FuId.NullType, Name = "null", Nullable = true };
	FuType# TypeParam0 = new FuType { Id = FuId.TypeParam0, Name = "T" };
	FuType# TypeParam1 = new FuType { Id = FuId.TypeParam1, Name = "T" };
	internal FuIntegerType# IntType = new FuIntegerType { Id = FuId.IntType, Name = "int" };
	FuRangeType# UIntType = FuRangeType.New(0, int.MaxValue);
	internal FuIntegerType# NIntType = new FuIntegerType { Id = FuId.NIntType, Name = "nint" };
	internal FuIntegerType# LongType = new FuIntegerType { Id = FuId.LongType, Name = "long" };
	internal FuRangeType# ByteType = FuRangeType.New(0, 0xff);
	internal FuFloatingType# FloatType = new FuFloatingType { Id = FuId.FloatType, Name = "float" };
	internal FuFloatingType# DoubleType = new FuFloatingType { Id = FuId.DoubleType, Name = "double" };
	internal FuRangeType# CharType = FuRangeType.New(-0x80, 0xffff);
	internal FuEnum# BoolType = new FuEnum { Id = FuId.BoolType, Name = "bool" };
	FuClass# StringClass = FuClass.New(FuCallType.Normal, FuId.StringClass, "string");
	internal FuStringType# StringPtrType = new FuStringType { Id = FuId.StringPtrType, Name = "string" };
	internal FuStringType# StringNullablePtrType = new FuStringType { Id = FuId.StringPtrType, Name = "string", Nullable = true };
	internal FuStringStorageType# StringStorageType = new FuStringStorageType { Id = FuId.StringStorageType };
	internal FuType# PrintableType = new FuPrintableType { Name = "printable" };
	internal FuClass# ArrayPtrClass = FuClass.New(FuCallType.Normal, FuId.ArrayPtrClass, "ArrayPtr", 1);
	internal FuClass# ArrayStorageClass = FuClass.New(FuCallType.Normal, FuId.ArrayStorageClass, "ArrayStorage", 1);
	internal FuClass# ExceptionClass = FuClass.New(FuCallType.Normal, FuId.ExceptionClass, "Exception");
	internal FuEnum# RegexOptionsEnum;
	internal FuReadWriteClassType# LockPtrType = new FuReadWriteClassType();
	internal FuClass[9] JsonClasses;

	internal FuLiteralLong# NewLiteralLong(long value, int loc = 0)
	{
		FuType# type = value >= int.MinValue && value <= int.MaxValue ? FuRangeType.New(value, value) : LongType;
		return new FuLiteralLong { Loc = loc, Type = type, Value = value };
	}

	internal FuLiteralString# NewLiteralString(string value, int loc = 0) => new FuLiteralString { Loc = loc, Type = StringPtrType, Value = value };

	internal FuType# PromoteIntegerTypes(FuType left, FuType right)
	{
		return left == LongType || right == LongType ? LongType
			: left == NIntType || right == NIntType ? NIntType
			: IntType;
	}

	internal FuType#? PromoteFloatingTypes(FuType left, FuType right)
	{
		if (left.Id == FuId.DoubleType || right.Id == FuId.DoubleType)
			return DoubleType;
		if (left.Id == FuId.FloatType || right.Id == FuId.FloatType
		 || left.Id == FuId.FloatIntType || right.Id == FuId.FloatIntType)
			return FloatType;
		return null;
	}

	internal FuType# PromoteNumericTypes(FuType# left, FuType# right)
	{
		// TODO: PromoteFloatingTypes(left, right) ?? PromoteIntegerTypes(left, right);
		FuType#? result = PromoteFloatingTypes(left, right);
		return result != null ? result : PromoteIntegerTypes(left, right);
	}

	internal FuEnum# NewEnum(bool flags)
	{
		FuEnum# enu = flags ? new FuEnumFlags() : new FuEnum();
		enu.Add(FuMethod.New(null, FuVisibility.Public, FuCallType.Static, enu, FuId.EnumFromInt, "FromInt", false, FuVar.New(IntType, "value")));
		enu.Add(FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, IntType, FuId.EnumToInt, "ToInt", false));
		if (flags)
			enu.Add(FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.EnumHasFlag, "HasFlag", false, FuVar.New(enu, "flag")));
		return enu;
	}

	FuClass! AddCollection!(FuId id, string name, int typeParameterCount, FuId clearId, FuId countId)
	{
		FuClass# result = FuClass.New(FuCallType.Normal, id, name, typeParameterCount);
		result.AddMethod(VoidType, clearId, "Clear", true);
		result.Add(FuProperty.New(NIntType, countId, "Count"));
		Add(result);
		return result;
	}

	void AddSet!(FuId id, string name, FuId addId, FuId clearId, FuId containsId, FuId countId, FuId removeId)
	{
		FuClass! set = AddCollection(id, name, 1, clearId, countId);
		set.AddMethod(VoidType, addId, "Add", true, FuVar.New(TypeParam0, "value"));
		set.AddMethod(BoolType, containsId, "Contains", false, FuVar.New(TypeParam0, "value"));
		set.AddMethod(VoidType, removeId, "Remove", true, FuVar.New(TypeParam0, "value"));
	}

	FuClass AddDictionary!(FuId id, string name, FuId clearId, FuId containsKeyId, FuId countId, FuId removeId)
	{
		FuClass! dict = AddCollection(id, name, 2, clearId, countId);
		dict.Add(FuMethod.New(dict, FuVisibility.FinalValueType, FuCallType.Normal, VoidType, FuId.DictionaryAdd, "Add", true, FuVar.New(TypeParam0, "key")));
		dict.AddMethod(BoolType, containsKeyId, "ContainsKey", false, FuVar.New(TypeParam0, "key"));
		dict.AddMethod(VoidType, removeId, "Remove", true, FuVar.New(TypeParam0, "key"));
		return dict;
	}

	static void AddEnumValue(FuEnum# enu, FuConst# value)
	{
		value.Type = enu;
		enu.Add(value);
	}

	FuConst# NewConstLong(string name, long value)
	{
		FuConst# result = new FuConst { Visibility = FuVisibility.Public, Name = name, Value = NewLiteralLong(value), VisitStatus = FuVisitStatus.Done };
		result.Type = result.Value.Type;
		return result;
	}

	FuConst# NewConstDouble(string name, double value)
		=> new FuConst { Visibility = FuVisibility.Public, Name = name, Value = new FuLiteralDouble { Value = value, Type = DoubleType }, Type = DoubleType, VisitStatus = FuVisitStatus.Done };

	void AddMinMaxValue(FuIntegerType! target, long min, long max)
	{
		target.Add(NewConstLong("MinValue", min));
		target.Add(NewConstLong("MaxValue", max));
	}

	FuReturn# NewReturnFalse() => new FuReturn { Value = new FuLiteralFalse { Type = BoolType } };

	FuReturn# NewReturnTrue() => new FuReturn { Value = new FuLiteralTrue { Type = BoolType } };

	FuReturn# NewReturnNull() => new FuReturn { Value = new FuLiteralNull { Type = NullType } };

	void AddFalseMethod(FuClass! klass, FuCallType callType, FuId id, string name)
	{
		FuMethod# method = FuMethod.New(klass, FuVisibility.Public, callType, BoolType, id, name, false);
		method.Body = NewReturnFalse();
		klass.Add(method);
	}

	void AddVirtualAssertFalseMethod(FuClass! klass, FuType# type, FuId id, string name)
	{
		FuMethod# method = FuMethod.New(null, FuVisibility.Public, FuCallType.Virtual, type, id, name, false);
		FuBlock# block = new FuBlock();
		block.Statements.Add(new FuAssert { Cond = new FuLiteralFalse { Type = BoolType } });
		method.Body = block;
		klass.Add(method);
	}

	void AddOverrideTrueMethod(FuClass! klass, string name)
	{
		FuMethod# method = FuMethod.New(klass, FuVisibility.Public, FuCallType.Override, BoolType, FuId.None, name, false);
		method.Body = NewReturnTrue();
		klass.Add(method);
	}

	static FuSymbolReference# NewSymbolReference(FuSymbol! symbol) => new FuSymbolReference { Type = symbol.Type, Name = symbol.Name, Symbol = symbol };

	static FuSymbolReference# NewMemberReference(FuSymbolReference# left, FuSymbol! member) => new FuSymbolReference { Left = left, Type = member.Type, Name = member.Name, Symbol = member };

	FuCallExpr# NewCall(FuMethod! method) => new FuCallExpr { Type = method.Type, Method = NewSymbolReference(method) };

	FuCallExpr# NewMethodCall(FuSymbol! obj, FuMethod! method) => new FuCallExpr { Type = method.Type, Method = NewMemberReference(NewSymbolReference(obj), method) };

	void AddJsonGetMethod(FuClass! klass, FuType# type, string name, FuField! valueField)
	{
		FuMethod# method = FuMethod.New(klass, FuVisibility.Public, FuCallType.Override, type, FuId.None, name, false);
		method.Body = new FuReturn { Value = NewSymbolReference(valueField) };
		klass.Add(method);
	}

	FuBinaryExpr# NewOffsetLessInputLength(FuSymbol! offsetField, FuSymbol! inputLengthField)
		=> new FuBinaryExpr { Type = BoolType, Left = NewSymbolReference(offsetField), Op = FuToken.Less, Right = NewSymbolReference(inputLengthField) };

	FuBinaryExpr# NewInputAtOffset(FuSymbol! inputField, FuSymbol! offsetField)
		=> new FuBinaryExpr { Type = IntType, Left = NewSymbolReference(inputField), Op = FuToken.LeftBracket, Right = NewSymbolReference(offsetField) };

	void AddCaseCharValue(FuCase! kase, int value)
	{
		kase.Values.Add(new FuLiteralChar { Type = IntType, Value = value });
	}

	FuVar# NewDynamicResult(FuClass klass)
	{
		FuDynamicPtrType# type = new FuDynamicPtrType { Class = klass };
		return FuVar.New(type, "result", new FuPrefixExpr { Type = type, Op = FuToken.New, Inner = null });
	}

	FuCallExpr# NewWriteJsonStringPart(FuField! inputField, FuMethod! stringSubstringMethod, FuVar! startOffsetVar, FuField! offsetField, FuVar! resultVar, FuMethod! textWriterWriteMethod)
	{
		FuCallExpr# substringCall = NewMethodCall(inputField, stringSubstringMethod);
		substringCall.Arguments.Add(NewSymbolReference(startOffsetVar));
		substringCall.Arguments.Add(new FuBinaryExpr { Type = NIntType, Left = new FuPostfixExpr { Type = NIntType, Inner = NewSymbolReference(offsetField), Op = FuToken.Increment },
			Op = FuToken.Minus, Right = NewSymbolReference(startOffsetVar) });
		FuCallExpr# writeCall = NewMethodCall(resultVar, textWriterWriteMethod);
		writeCall.Arguments.Add(substringCall);
		return writeCall;
	}

	void AddJsonEscape(FuSwitch! switch2, int c, FuVar! resultVar, FuMethod! textWriterWriteCharMethod, FuLiteralLong# literal)
	{
		switch2.Cases.Add();
		FuCase! kase = switch2.Cases.Last();
		AddCaseCharValue(kase, c);
		FuCallExpr# call = NewMethodCall(resultVar, textWriterWriteCharMethod);
		call.Arguments.Add(literal);
		kase.Body.Add(call);
		kase.Body.Add(new FuBreak { LoopOrSwitch = switch2 });
	}

	FuIf# NewIfNotSeeDigitReturnNull(FuMethod! seeDigitMethod)
		=> new FuIf { Cond = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = NewCall(seeDigitMethod) },
			OnTrue = NewReturnNull(), OnFalse = null };

	void AddParseJsonType(FuSwitch! switch_, int c, FuMethod! method)
	{
		switch_.Cases.Add();
		FuCase! kase = switch_.Cases.Last();
		AddCaseCharValue(kase, c);
		if (c == '-') {
			for (c = '0'; c <= '9'; c++)
				AddCaseCharValue(kase, c);
		}
		kase.Body.Add(new FuReturn { Value = NewCall(method) });
	}

	void AddParseJsonKeyword(FuSwitch! switch_, string keyword, FuMethod! parseKeywordMethod, FuClass klass)
	{
		switch_.Cases.Add();
		FuCase! kase = switch_.Cases.Last();
		AddCaseCharValue(kase, keyword[0]);
		FuCallExpr# call = NewCall(parseKeywordMethod);
		call.Arguments.Add(NewLiteralString(keyword.Substring(1)));
		kase.Body.Add(new FuReturn { Value = new FuSelectExpr { Cond = call,
			OnTrue = new FuPrefixExpr { Type = new FuDynamicPtrType { Class = klass }, Op = FuToken.New, Inner = null },
			OnFalse = new FuLiteralNull { Type = NullType } } });
	}

	FuSystem()
	{
		Parent = null;
		FuSymbol# basePtr = FuVar.New(null, "base");
		basePtr.Id = FuId.BasePtr;
		Add(basePtr);
		AddMinMaxValue(IntType, int.MinValue, int.MaxValue);
		FuMethod# intTryParseMethod = FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.IntTryParse, "TryParse", true, FuVar.New(StringPtrType, "value"), FuVar.New(IntType, "radix", NewLiteralLong(0)));
		IntType.Add(intTryParseMethod);
		Add(IntType);
		UIntType.Name = "uint";
		Add(UIntType);
		NIntType.Add(FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.NIntTryParse, "TryParse", true, FuVar.New(StringPtrType, "value"), FuVar.New(IntType, "radix", NewLiteralLong(0))));
		Add(NIntType);
		AddMinMaxValue(LongType, long.MinValue, long.MaxValue);
		LongType.Add(FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.LongTryParse, "TryParse", true, FuVar.New(StringPtrType, "value"), FuVar.New(IntType, "radix", NewLiteralLong(0))));
		Add(LongType);
		ByteType.Name = "byte";
		Add(ByteType);
		FuRangeType# sByteType = FuRangeType.New(-0x80, 0x7f);
		sByteType.Name = "sbyte";
		Add(sByteType);
		FuRangeType# shortType = FuRangeType.New(-0x8000, 0x7fff);
		shortType.Name = "short";
		Add(shortType);
		FuRangeType# ushortType = FuRangeType.New(0, 0xffff);
		ushortType.Name = "ushort";
		Add(ushortType);
		FloatType.Add(FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.FloatTryParse, "TryParse", true, FuVar.New(StringPtrType, "value")));
		Add(FloatType);
		FuMethod# doubleTryParseMethod = FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.DoubleTryParse, "TryParse", true, FuVar.New(StringPtrType, "value"));
		DoubleType.Add(doubleTryParseMethod);
		Add(DoubleType);
		Add(BoolType);
		StringClass.AddMethod(BoolType, FuId.StringContains, "Contains", false, FuVar.New(StringPtrType, "value"));
		StringClass.AddMethod(BoolType, FuId.StringEndsWith, "EndsWith", false, FuVar.New(StringPtrType, "value"));
		StringClass.AddMethod(NIntType, FuId.StringIndexOf, "IndexOf", false, FuVar.New(StringPtrType, "value"));
		StringClass.AddMethod(NIntType, FuId.StringLastIndexOf, "LastIndexOf", false, FuVar.New(StringPtrType, "value"));
		FuProperty# stringLengthProperty = FuProperty.New(NIntType, FuId.StringLength, "Length");
		StringClass.Add(stringLengthProperty);
		StringClass.AddMethod(StringStorageType, FuId.StringReplace, "Replace", false, FuVar.New(StringPtrType, "oldValue"), FuVar.New(StringPtrType, "newValue"));
		StringClass.AddMethod(BoolType, FuId.StringStartsWith, "StartsWith", false, FuVar.New(StringPtrType, "value"));
		FuMethod# stringSubstringMethod = FuMethod.New(StringClass, FuVisibility.Public, FuCallType.Normal, StringStorageType, FuId.StringSubstring, "Substring", false, FuVar.New(NIntType, "offset"), FuVar.New(NIntType, "length", NewLiteralLong(-1)));
		StringClass.Add(stringSubstringMethod);
		StringClass.AddMethod(StringStorageType, FuId.StringToLower, "ToLower", false);
		StringClass.AddMethod(StringStorageType, FuId.StringToUpper, "ToUpper", false);
		StringPtrType.Class = StringClass;
		Add(StringPtrType);
		StringNullablePtrType.Class = StringClass;
		StringStorageType.Class = StringClass;
		FuMethod# arrayBinarySearchPart = FuMethod.New(null, FuVisibility.NumericElementType, FuCallType.Normal, NIntType, FuId.ArrayBinarySearchPart, "BinarySearch", false,
			FuVar.New(TypeParam0, "value"), FuVar.New(NIntType, "startIndex"), FuVar.New(NIntType, "count"));
		ArrayPtrClass.Add(arrayBinarySearchPart);
		ArrayPtrClass.AddMethod(VoidType, FuId.ArrayCopyTo, "CopyTo", false, FuVar.New(NIntType, "sourceIndex"),
			FuVar.New(new FuReadWriteClassType { Class = ArrayPtrClass, TypeArg0 = TypeParam0 }, "destinationArray"), FuVar.New(NIntType, "destinationIndex"), FuVar.New(NIntType, "count"));
		FuMethod# arrayFillPart = FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, VoidType, FuId.ArrayFillPart, "Fill", true,
			FuVar.New(TypeParam0, "value"), FuVar.New(NIntType, "startIndex"), FuVar.New(NIntType, "count"));
		ArrayPtrClass.Add(arrayFillPart);
		FuMethod# arraySortPart = FuMethod.New(null, FuVisibility.NumericElementType, FuCallType.Normal, VoidType, FuId.ArraySortPart, "Sort", true, FuVar.New(NIntType, "startIndex"), FuVar.New(NIntType, "count"));
		ArrayPtrClass.Add(arraySortPart);
		ArrayStorageClass.Parent = ArrayPtrClass;
		ArrayStorageClass.Add(FuMethodGroup.New(FuMethod.New(ArrayStorageClass, FuVisibility.NumericElementType, FuCallType.Normal, NIntType, FuId.ArrayBinarySearchAll, "BinarySearch", false, FuVar.New(TypeParam0, "value")),
			arrayBinarySearchPart));
		ArrayStorageClass.AddMethod(BoolType, FuId.ArrayContains, "Contains", false, FuVar.New(TypeParam0, "value"));
		ArrayStorageClass.Add(FuMethodGroup.New(FuMethod.New(ArrayStorageClass, FuVisibility.Public, FuCallType.Normal, VoidType, FuId.ArrayFillAll, "Fill", true, FuVar.New(TypeParam0, "value")),
			arrayFillPart));
		ArrayStorageClass.Add(FuProperty.New(NIntType, FuId.ArrayLength, "Length"));
		ArrayStorageClass.Add(FuMethodGroup.New(
			FuMethod.New(ArrayStorageClass, FuVisibility.NumericElementType, FuCallType.Normal, VoidType, FuId.ArraySortAll, "Sort", true),
			arraySortPart));
		ExceptionClass.IsPublic = true;
		Add(ExceptionClass);

		FuType# typeParam0NotFinal = new FuType { Id = FuId.TypeParam0NotFinal, Name = "T" };
		FuType# typeParam0Predicate = new FuType { Id = FuId.TypeParam0Predicate, Name = "Predicate<T>" };
		FuClass! listClass = AddCollection(FuId.ListClass, "List", 1, FuId.ListClear, FuId.ListCount);
		FuMethod# listAddMethod = FuMethod.New(listClass, FuVisibility.Public, FuCallType.Normal, VoidType, FuId.ListAdd, "Add", true, FuVar.New(typeParam0NotFinal, "value"));
		listClass.Add(listAddMethod);
		listClass.AddMethod(VoidType, FuId.ListAddRange, "AddRange", true, FuVar.New(new FuClassType { Class = listClass, TypeArg0 = TypeParam0 }, "source"));
		listClass.AddMethod(BoolType, FuId.ListAll, "All", false, FuVar.New(typeParam0Predicate, "predicate"));
		listClass.AddMethod(BoolType, FuId.ListAny, "Any", false, FuVar.New(typeParam0Predicate, "predicate"));
		listClass.AddMethod(BoolType, FuId.ListContains, "Contains", false, FuVar.New(TypeParam0, "value"));
		listClass.AddMethod(VoidType, FuId.ListCopyTo, "CopyTo", false, FuVar.New(NIntType, "sourceIndex"),
			FuVar.New(new FuReadWriteClassType { Class = ArrayPtrClass, TypeArg0 = TypeParam0 }, "destinationArray"), FuVar.New(NIntType, "destinationIndex"), FuVar.New(NIntType, "count"));
		listClass.AddMethod(NIntType, FuId.ListIndexOf, "IndexOf", false, FuVar.New(TypeParam0, "value"));
		listClass.AddMethod(VoidType, FuId.ListInsert, "Insert", true, FuVar.New(NIntType, "index"), FuVar.New(typeParam0NotFinal, "value"));
		listClass.AddMethod(TypeParam0, FuId.ListLast, "Last", false);
		listClass.AddMethod(VoidType, FuId.ListRemoveAt, "RemoveAt", true, FuVar.New(NIntType, "index"));
		listClass.AddMethod(VoidType, FuId.ListRemoveRange, "RemoveRange", true, FuVar.New(NIntType, "index"), FuVar.New(NIntType, "count"));
		listClass.Add(FuMethodGroup.New(
			FuMethod.New(listClass, FuVisibility.NumericElementType, FuCallType.Normal, VoidType, FuId.ListSortAll, "Sort", true),
			FuMethod.New(listClass, FuVisibility.NumericElementType, FuCallType.Normal, VoidType, FuId.ListSortPart, "Sort", true, FuVar.New(NIntType, "startIndex"), FuVar.New(NIntType, "count"))));
		FuClass! queueClass = AddCollection(FuId.QueueClass, "Queue", 1, FuId.QueueClear, FuId.QueueCount);
		queueClass.AddMethod(TypeParam0, FuId.QueueDequeue, "Dequeue", true);
		queueClass.AddMethod(VoidType, FuId.QueueEnqueue, "Enqueue", true, FuVar.New(TypeParam0, "value"));
		queueClass.AddMethod(TypeParam0, FuId.QueuePeek, "Peek", false);
		FuClass! stackClass = AddCollection(FuId.StackClass, "Stack", 1, FuId.StackClear, FuId.StackCount);
		stackClass.AddMethod(TypeParam0, FuId.StackPeek, "Peek", false);
		stackClass.AddMethod(VoidType, FuId.StackPush, "Push", true, FuVar.New(TypeParam0, "value"));
		stackClass.AddMethod(TypeParam0, FuId.StackPop, "Pop", true);
		FuClass! priorityQueueClass = AddCollection(FuId.PriorityQueueClass, "PriorityQueue", 2, FuId.PriorityQueueClear, FuId.PriorityQueueCount);
		priorityQueueClass.AddMethod(TypeParam0, FuId.PriorityQueueDequeue, "Dequeue", true);
		priorityQueueClass.AddMethod(VoidType, FuId.PriorityQueueEnqueue, "Enqueue", true, FuVar.New(TypeParam0, "element"), FuVar.New(TypeParam1, "priority"));
		priorityQueueClass.AddMethod(TypeParam0, FuId.PriorityQueuePeek, "Peek", false);
		AddSet(FuId.HashSetClass, "HashSet", FuId.HashSetAdd, FuId.HashSetClear, FuId.HashSetContains, FuId.HashSetCount, FuId.HashSetRemove);
		AddSet(FuId.SortedSetClass, "SortedSet", FuId.SortedSetAdd, FuId.SortedSetClear, FuId.SortedSetContains, FuId.SortedSetCount, FuId.SortedSetRemove);
		FuClass dictionaryClass = AddDictionary(FuId.DictionaryClass, "Dictionary", FuId.DictionaryClear, FuId.DictionaryContainsKey, FuId.DictionaryCount, FuId.DictionaryRemove);
		AddDictionary(FuId.SortedDictionaryClass, "SortedDictionary", FuId.SortedDictionaryClear, FuId.SortedDictionaryContainsKey, FuId.SortedDictionaryCount, FuId.SortedDictionaryRemove);
		AddDictionary(FuId.OrderedDictionaryClass, "OrderedDictionary", FuId.OrderedDictionaryClear, FuId.OrderedDictionaryContainsKey, FuId.OrderedDictionaryCount, FuId.OrderedDictionaryRemove);

		FuClass# textWriterClass = FuClass.New(FuCallType.Normal, FuId.TextWriterClass, "TextWriter");
		textWriterClass.AddMethod(VoidType, FuId.TextWriterFlush, "Flush", true);
		FuMethod# textWriterWriteMethod = FuMethod.New(textWriterClass, FuVisibility.Public, FuCallType.Normal, VoidType, FuId.TextWriterWrite, "Write", true, FuVar.New(PrintableType, "value"));
		textWriterClass.Add(textWriterWriteMethod);
		FuMethod# textWriterWriteCharMethod = FuMethod.New(textWriterClass, FuVisibility.Public, FuCallType.Normal, VoidType, FuId.TextWriterWriteChar, "WriteChar", true, FuVar.New(IntType, "c"));
		textWriterClass.Add(textWriterWriteCharMethod);
		FuMethod# textWriterWriteCodePointMethod = FuMethod.New(textWriterClass, FuVisibility.Public, FuCallType.Normal, VoidType, FuId.TextWriterWriteCodePoint, "WriteCodePoint", true, FuVar.New(IntType, "c"));
		textWriterClass.Add(textWriterWriteCodePointMethod);
		textWriterClass.AddMethod(VoidType, FuId.TextWriterWriteLine, "WriteLine", true, FuVar.New(PrintableType, "value", NewLiteralString("")));
		Add(textWriterClass);
		FuClass# consoleClass = FuClass.New(FuCallType.Static, FuId.None, "Console");
		consoleClass.AddStaticMethod(StringStorageType, FuId.ConsoleReadLine, "ReadLine");
		consoleClass.AddStaticMethod(VoidType, FuId.ConsoleWrite, "Write", FuVar.New(PrintableType, "value"));
		consoleClass.AddStaticMethod(VoidType, FuId.ConsoleWriteLine, "WriteLine", FuVar.New(PrintableType, "value", NewLiteralString("")));
		FuStorageType# textWriterStorage = new FuStorageType { Class = textWriterClass };
		consoleClass.Add(FuStaticProperty.New(textWriterStorage, FuId.ConsoleError, "Error"));
		consoleClass.Add(FuStaticProperty.New(textWriterStorage, FuId.ConsoleOut, "Out"));
		Add(consoleClass);
		FuClass# stringWriterClass = FuClass.New(FuCallType.Sealed, FuId.StringWriterClass, "StringWriter");
		stringWriterClass.AddMethod(VoidType, FuId.StringWriterClear, "Clear", true);
		FuMethod# stringWriterToStringMethod = FuMethod.New(stringWriterClass, FuVisibility.Public, FuCallType.Normal, StringPtrType, FuId.StringWriterToString, "ToString", false);
		stringWriterClass.Add(stringWriterToStringMethod);
		Add(stringWriterClass);
		stringWriterClass.Parent = textWriterClass;
		FuClass# bitConverterClass = FuClass.New(FuCallType.Static, FuId.None, "BitConverter");
		bitConverterClass.AddStaticMethod(FloatType, FuId.BitConverterInt32BitsToSingle, "Int32BitsToSingle", FuVar.New(IntType, "value"));
		bitConverterClass.AddStaticMethod(DoubleType, FuId.BitConverterInt64BitsToDouble, "Int64BitsToDouble", FuVar.New(LongType, "value"));
		bitConverterClass.AddStaticMethod(IntType, FuId.BitConverterSingleToInt32Bits, "SingleToInt32Bits", FuVar.New(FloatType, "value"));
		bitConverterClass.AddStaticMethod(LongType, FuId.BitConverterDoubleToInt64Bits, "DoubleToInt64Bits", FuVar.New(DoubleType, "value"));
		Add(bitConverterClass);
		FuClass# convertClass = FuClass.New(FuCallType.Static, FuId.None, "Convert");
		convertClass.AddStaticMethod(StringStorageType, FuId.ConvertToBase64String, "ToBase64String",
			FuVar.New(new FuClassType { Class = ArrayPtrClass, TypeArg0 = ByteType }, "bytes"), FuVar.New(NIntType, "offset"), FuVar.New(NIntType, "length"));
		Add(convertClass);
		FuClass# utf8EncodingClass = FuClass.New(FuCallType.Sealed, FuId.None, "UTF8Encoding");
		utf8EncodingClass.AddMethod(NIntType, FuId.UTF8GetByteCount, "GetByteCount", false, FuVar.New(StringPtrType, "str"));
		utf8EncodingClass.AddMethod(VoidType, FuId.UTF8GetBytes, "GetBytes", false,
			FuVar.New(StringPtrType, "str"), FuVar.New(new FuReadWriteClassType { Class = ArrayPtrClass, TypeArg0 = ByteType }, "bytes"), FuVar.New(NIntType, "byteIndex"));
		utf8EncodingClass.AddMethod(StringStorageType, FuId.UTF8GetString, "GetString", false,
			FuVar.New(new FuClassType { Class = ArrayPtrClass, TypeArg0 = ByteType }, "bytes"), FuVar.New(NIntType, "offset"), FuVar.New(NIntType, "length"));
		FuClass# encodingClass = FuClass.New(FuCallType.Static, FuId.None, "Encoding");
		encodingClass.Add(FuStaticProperty.New(utf8EncodingClass, FuId.None, "UTF8"));
		Add(encodingClass);
		FuClass# environmentClass = FuClass.New(FuCallType.Static, FuId.None, "Environment");
		environmentClass.AddStaticMethod(StringNullablePtrType, FuId.EnvironmentGetEnvironmentVariable, "GetEnvironmentVariable", FuVar.New(StringPtrType, "name"));
		Add(environmentClass);
		FuClass# dateTimeOffsetClass = FuClass.New(FuCallType.Abstract, FuId.None, "DateTimeOffset");
		dateTimeOffsetClass.Add(FuStaticProperty.New(new FuClassType { Class = dateTimeOffsetClass }, FuId.None, "UtcNow"));
		dateTimeOffsetClass.AddMethod(LongType, FuId.DateTimeOffsetUtcNowToUnixTimeMilliseconds, "ToUnixTimeMilliseconds", false);
		Add(dateTimeOffsetClass);
		RegexOptionsEnum = NewEnum(true);
		RegexOptionsEnum.IsPublic = true;
		RegexOptionsEnum.Id = FuId.RegexOptionsEnum;
		RegexOptionsEnum.Name = "RegexOptions";
		FuConst# regexOptionsNone = NewConstLong("None", 0);
		AddEnumValue(RegexOptionsEnum, regexOptionsNone);
		AddEnumValue(RegexOptionsEnum, NewConstLong("IgnoreCase", 1));
		AddEnumValue(RegexOptionsEnum, NewConstLong("Multiline", 2));
		AddEnumValue(RegexOptionsEnum, NewConstLong("Singleline", 16));
		Add(RegexOptionsEnum);
		FuClass# regexClass = FuClass.New(FuCallType.Sealed, FuId.RegexClass, "Regex");
		regexClass.AddStaticMethod(StringStorageType, FuId.RegexEscape, "Escape", FuVar.New(StringPtrType, "str"));
		regexClass.Add(FuMethodGroup.New(
			FuMethod.New(null, FuVisibility.Public, FuCallType.Static, BoolType, FuId.RegexIsMatchStr, "IsMatch", false, FuVar.New(StringPtrType, "input"), FuVar.New(StringPtrType, "pattern"), FuVar.New(RegexOptionsEnum, "options", regexOptionsNone)),
			FuMethod.New(null, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.RegexIsMatchRegex, "IsMatch", false, FuVar.New(StringPtrType, "input"))));
		regexClass.AddStaticMethod(new FuDynamicPtrType { Class = regexClass }, FuId.RegexCompile, "Compile", FuVar.New(StringPtrType, "pattern"), FuVar.New(RegexOptionsEnum, "options", regexOptionsNone));
		Add(regexClass);
		FuClass# matchClass = FuClass.New(FuCallType.Sealed, FuId.MatchClass, "Match");
		matchClass.Add(FuMethodGroup.New(
			FuMethod.New(matchClass, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.MatchFindStr, "Find", true, FuVar.New(StringPtrType, "input"), FuVar.New(StringPtrType, "pattern"), FuVar.New(RegexOptionsEnum, "options", regexOptionsNone)),
			FuMethod.New(matchClass, FuVisibility.Public, FuCallType.Normal, BoolType, FuId.MatchFindRegex, "Find", true, FuVar.New(StringPtrType, "input"), FuVar.New(new FuClassType { Class = regexClass }, "pattern"))));
		matchClass.Add(FuProperty.New(NIntType, FuId.MatchStart, "Start"));
		matchClass.Add(FuProperty.New(NIntType, FuId.MatchEnd, "End"));
		matchClass.AddMethod(StringStorageType, FuId.MatchGetCapture, "GetCapture", false, FuVar.New(UIntType, "group"));
		matchClass.Add(FuProperty.New(NIntType, FuId.MatchLength, "Length"));
		matchClass.Add(FuProperty.New(StringStorageType, FuId.MatchValue, "Value"));
		Add(matchClass);

		FuClass# jsonElementClass = FuClass.New(FuCallType.Abstract, FuId.JsonElementClass, "JsonElement");
		FuDynamicPtrType# jsonElementPtr = new FuDynamicPtrType { Class = jsonElementClass };
		AddFalseMethod(jsonElementClass, FuCallType.Virtual, FuId.JsonElementIsObject, "IsObject");
		AddFalseMethod(jsonElementClass, FuCallType.Virtual, FuId.JsonElementIsArray, "IsArray");
		AddFalseMethod(jsonElementClass, FuCallType.Virtual, FuId.JsonElementIsString, "IsString");
		AddFalseMethod(jsonElementClass, FuCallType.Virtual, FuId.JsonElementIsNumber, "IsNumber");
		AddFalseMethod(jsonElementClass, FuCallType.Virtual, FuId.JsonElementIsBoolean, "IsBoolean");
		AddFalseMethod(jsonElementClass, FuCallType.Virtual, FuId.JsonElementIsNull, "IsNull");
		AddVirtualAssertFalseMethod(jsonElementClass, new FuClassType { Class = dictionaryClass, TypeArg0 = StringStorageType, TypeArg1 = jsonElementPtr }, FuId.JsonElementGetObject, "GetObject");
		AddVirtualAssertFalseMethod(jsonElementClass, new FuClassType { Class = listClass, TypeArg0 = jsonElementPtr }, FuId.JsonElementGetArray, "GetArray");
		AddVirtualAssertFalseMethod(jsonElementClass, StringPtrType, FuId.JsonElementGetString, "GetString");
		AddVirtualAssertFalseMethod(jsonElementClass, DoubleType, FuId.JsonElementGetDouble, "GetDouble");
		AddVirtualAssertFalseMethod(jsonElementClass, BoolType, FuId.JsonElementGetBoolean, "GetBoolean");
		FuMethod# jsonElementParseMethod = FuMethod.New(null, FuVisibility.Public, FuCallType.Static, jsonElementPtr, FuId.JsonElementParse, "Parse", false);
		FuClass# jsonParserClass = FuClass.New(FuCallType.Sealed, FuId.None, "JsonParser");
		FuMethod# jsonParserTryParseMethod = FuMethod.New(null, FuVisibility.Internal, FuCallType.Normal, new FuDynamicPtrType { Nullable = true, Class = jsonElementClass }, FuId.None, "TryParse", true);
		{
			FuVar# sParam = FuVar.New(StringPtrType, "s");
			jsonElementParseMethod.Parameters.Add(sParam);
			FuBlock# methodBlock = new FuBlock();
			FuVar# parserVar = FuVar.New(new FuStorageType { Class = jsonParserClass }, "parser");
			methodBlock.Statements.Add(parserVar);
			methodBlock.Add(parserVar);
			FuCallExpr# call = NewMethodCall(parserVar, jsonParserTryParseMethod);
			call.Arguments.Add(NewSymbolReference(sParam));
			methodBlock.Statements.Add(new FuReturn { Value = call });
			jsonElementParseMethod.Body = methodBlock;
		}
		jsonElementClass.Add(jsonElementParseMethod);
		Add(jsonElementClass);
		JsonClasses[0] = jsonElementClass;
		FuClass# jsonObjectClass = FuClass.New(FuCallType.Sealed, FuId.None, "JsonObject");
		FuField# jsonObjectValueField = new FuField { Visibility = FuVisibility.Internal, Type = new FuStorageType { Class = dictionaryClass, TypeArg0 = StringStorageType, TypeArg1 = jsonElementPtr }, Name = "Value" };
		jsonObjectClass.Add(jsonObjectValueField);
		AddOverrideTrueMethod(jsonObjectClass, "IsObject");
		AddJsonGetMethod(jsonObjectClass, new FuClassType { Class = dictionaryClass, TypeArg0 = StringStorageType, TypeArg1 = jsonElementPtr }, "GetObject", jsonObjectValueField);
		jsonObjectClass.CppFriends.Add("JsonParser");
		Add(jsonObjectClass);
		jsonObjectClass.SetBaseClass(jsonElementClass);
		JsonClasses[1] = jsonObjectClass;
		FuClass# jsonArrayClass = FuClass.New(FuCallType.Sealed, FuId.None, "JsonArray");
		FuField# jsonArrayValueField = new FuField { Visibility = FuVisibility.Internal, Type = new FuStorageType { Class = listClass, TypeArg0 = jsonElementPtr }, Name = "Value" };
		jsonArrayClass.Add(jsonArrayValueField);
		AddOverrideTrueMethod(jsonArrayClass, "IsArray");
		AddJsonGetMethod(jsonArrayClass, new FuClassType { Class = listClass, TypeArg0 = jsonElementPtr }, "GetArray", jsonArrayValueField);
		jsonArrayClass.CppFriends.Add("JsonParser");
		Add(jsonArrayClass);
		jsonArrayClass.SetBaseClass(jsonElementClass);
		JsonClasses[2] = jsonArrayClass;
		FuClass# jsonStringClass = FuClass.New(FuCallType.Sealed, FuId.None, "JsonString");
		FuField# jsonStringValueField = new FuField { Visibility = FuVisibility.Internal, Type = StringStorageType, Name = "Value" };
		jsonStringClass.Add(jsonStringValueField);
		AddOverrideTrueMethod(jsonStringClass, "IsString");
		AddJsonGetMethod(jsonStringClass, StringPtrType, "GetString", jsonStringValueField);
		jsonStringClass.CppFriends.Add("JsonParser");
		Add(jsonStringClass);
		jsonStringClass.SetBaseClass(jsonElementClass);
		JsonClasses[3] = jsonStringClass;
		FuClass# jsonNumberClass = FuClass.New(FuCallType.Sealed, FuId.None, "JsonNumber");
		FuField# jsonNumberValueField = new FuField { Visibility = FuVisibility.Internal, Type = DoubleType, Name = "Value" };
		jsonNumberClass.Add(jsonNumberValueField);
		AddOverrideTrueMethod(jsonNumberClass, "IsNumber");
		AddJsonGetMethod(jsonNumberClass, DoubleType, "GetDouble", jsonNumberValueField);
		jsonNumberClass.CppFriends.Add("JsonParser");
		Add(jsonNumberClass);
		jsonNumberClass.SetBaseClass(jsonElementClass);
		JsonClasses[4] = jsonNumberClass;
		FuClass# jsonBooleanClass = FuClass.New(FuCallType.Normal, FuId.None, "JsonBoolean");
		AddOverrideTrueMethod(jsonBooleanClass, "IsBoolean");
		AddFalseMethod(jsonBooleanClass, FuCallType.Override, FuId.None, "GetBoolean");
		Add(jsonBooleanClass);
		jsonBooleanClass.SetBaseClass(jsonElementClass);
		JsonClasses[5] = jsonBooleanClass;
		FuClass# jsonTrueClass = FuClass.New(FuCallType.Sealed, FuId.None, "JsonTrue");
		AddOverrideTrueMethod(jsonTrueClass, "GetBoolean");
		Add(jsonTrueClass);
		jsonTrueClass.SetBaseClass(jsonBooleanClass);
		JsonClasses[6] = jsonTrueClass;
		FuClass# jsonNullClass = FuClass.New(FuCallType.Sealed, FuId.None, "JsonNull");
		AddOverrideTrueMethod(jsonNullClass, "IsNull");
		Add(jsonNullClass);
		jsonNullClass.SetBaseClass(jsonElementClass);
		JsonClasses[7] = jsonNullClass;
		FuField# inputField = new FuField { Visibility = FuVisibility.Private, Type = StringPtrType, Name = "Input" };
		jsonParserClass.Add(inputField);
		FuField# offsetField = new FuField { Visibility = FuVisibility.Private, Type = NIntType, Name = "Offset" };
		jsonParserClass.Add(offsetField);
		FuField# inputLengthField = new FuField { Visibility = FuVisibility.Private, Type = NIntType, Name = "InputLength" };
		jsonParserClass.Add(inputLengthField);
		FuMethod# jsonParserSkipWhitespaceMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, BoolType, FuId.None, "SkipWhitespace", true);
		{
			FuBlock# methodBlock = new FuBlock();
			FuBlock# whileBlock = new FuBlock();
			methodBlock.Statements.Add(new FuWhile { Cond = NewOffsetLessInputLength(offsetField, inputLengthField), Body = whileBlock });
			FuSwitch# switch_ = new FuSwitch { Value = NewInputAtOffset(inputField, offsetField) };
			switch_.Cases.Add();
			FuCase! kase = switch_.Cases[0];
			AddCaseCharValue(kase, '\t');
			AddCaseCharValue(kase, '\n');
			AddCaseCharValue(kase, '\r');
			AddCaseCharValue(kase, ' ');
			kase.Body.Add(new FuBreak { LoopOrSwitch = switch_ });
			switch_.DefaultBody.Add(NewReturnTrue());
			whileBlock.Statements.Add(switch_);
			whileBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			methodBlock.Statements.Add(new FuReturn { Value = new FuLiteralFalse { Type = BoolType } });
			jsonParserSkipWhitespaceMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserSkipWhitespaceMethod);
		FuMethod# jsonParserParseObjectMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, new FuDynamicPtrType { Nullable = true, Class = jsonObjectClass }, FuId.None, "ParseObject", true);
		FuMethod# jsonParserParseStringMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, new FuDynamicPtrType { Nullable = true, Class = jsonStringClass }, FuId.None, "ParseString", true);
		FuMethod# jsonParserParseWhitespaceAndElementMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, new FuDynamicPtrType { Nullable = true, Class = jsonElementClass }, FuId.None, "ParseWhitespaceAndElement", true);
		{
			FuBlock# methodBlock = new FuBlock();
			methodBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			methodBlock.Statements.Add(new FuIf { Cond = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = NewCall(jsonParserSkipWhitespaceMethod) },
				OnTrue = NewReturnNull(), OnFalse = null });
			FuVar# resultVar = NewDynamicResult(jsonObjectClass);
			methodBlock.Statements.Add(resultVar);
			methodBlock.Add(resultVar);
			FuBlock# ifBlock = new FuBlock();
			methodBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.Equal, Right = new FuLiteralChar { Type = IntType, Value = '}' } },
				OnTrue = ifBlock, OnFalse = null });
			ifBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			ifBlock.Statements.Add(new FuReturn { Value = NewSymbolReference(resultVar) });
			FuBlock# whileBlock = new FuBlock();
			whileBlock.SetCompletesNormally(true);
			methodBlock.Statements.Add(new FuWhile { Cond = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.Equal, Right = new FuLiteralChar { Type = IntType, Value = '"' } }, Body = whileBlock });
			FuVar# keyVar = FuVar.New(new FuDynamicPtrType { Nullable = true, Class = jsonStringClass }, "key", NewCall(jsonParserParseStringMethod));
			whileBlock.Statements.Add(keyVar);
			whileBlock.Add(keyVar);
			whileBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType, Left = new FuBinaryExpr { Type = BoolType, Left = NewSymbolReference(keyVar), Op = FuToken.Equal, Right = new FuLiteralNull { Type = NullType } },
				Op = FuToken.CondOr, Right = new FuBinaryExpr { Type = BoolType, Left = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = NewCall(jsonParserSkipWhitespaceMethod) },
				Op = FuToken.CondOr, Right = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.NotEqual, Right = new FuLiteralChar { Type = IntType, Value = ':' } } } },
				OnTrue = NewReturnNull(), OnFalse = null });
			whileBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			FuVar# valueVar = FuVar.New(new FuDynamicPtrType { Nullable = true, Class = jsonElementClass }, "value", NewCall(jsonParserParseWhitespaceAndElementMethod));
			whileBlock.Statements.Add(valueVar);
			whileBlock.Add(valueVar);
			whileBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType, Left = new FuBinaryExpr { Type = BoolType, Left = NewSymbolReference(valueVar), Op = FuToken.Equal, Right = new FuLiteralNull { Type = NullType } },
				Op = FuToken.CondOr, Right = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = NewCall(jsonParserSkipWhitespaceMethod) } },
				OnTrue = NewReturnNull(), OnFalse = null });
			whileBlock.Statements.Add(new FuBinaryExpr { Type = jsonElementPtr, Left = new FuBinaryExpr { Type = jsonElementPtr, Left = NewMemberReference(NewSymbolReference(resultVar), jsonObjectValueField),
				Op = FuToken.LeftBracket, Right = NewMemberReference(NewSymbolReference(keyVar), jsonStringValueField) },
				Op = FuToken.Assign, Right = NewSymbolReference(valueVar) });
			FuSwitch# switch_ = new FuSwitch { Value = new FuBinaryExpr { Type = IntType, Left = NewSymbolReference(inputField), Op = FuToken.LeftBracket, Right = new FuPostfixExpr { Type = NIntType, Inner = NewSymbolReference(offsetField), Op = FuToken.Increment } } };
			switch_.Cases.Add();
			AddCaseCharValue(switch_.Cases[0], ',');
			switch_.Cases[0].Body.Add(new FuBreak { LoopOrSwitch = switch_ });
			switch_.Cases.Add();
			AddCaseCharValue(switch_.Cases[1], '}');
			switch_.Cases[1].Body.Add(new FuReturn { Value = NewSymbolReference(resultVar) });
			switch_.DefaultBody.Add(NewReturnNull());
			whileBlock.Statements.Add(switch_);
			whileBlock.Statements.Add(new FuIf { Cond = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = NewCall(jsonParserSkipWhitespaceMethod) },
				OnTrue = NewReturnNull(), OnFalse = null });
			methodBlock.Statements.Add(NewReturnNull());
			jsonParserParseObjectMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserParseObjectMethod);
		FuMethod# jsonParserParseArrayMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, new FuDynamicPtrType { Nullable = true, Class = jsonArrayClass }, FuId.None, "ParseArray", true);
		FuMethod# jsonParserParseElementMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, new FuDynamicPtrType { Nullable = true, Class = jsonElementClass }, FuId.None, "ParseElement", true);
		{
			FuBlock# methodBlock = new FuBlock();
			methodBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			methodBlock.Statements.Add(new FuIf { Cond = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = NewCall(jsonParserSkipWhitespaceMethod) },
				OnTrue = NewReturnNull(), OnFalse = null });
			FuVar# resultVar = NewDynamicResult(jsonArrayClass);
			methodBlock.Statements.Add(resultVar);
			methodBlock.Add(resultVar);
			FuBlock# ifBlock = new FuBlock();
			methodBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.Equal, Right = new FuLiteralChar { Type = IntType, Value = ']' } },
				OnTrue = ifBlock, OnFalse = null });
			ifBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			ifBlock.Statements.Add(new FuReturn { Value = NewSymbolReference(resultVar) });
			FuBlock# doWhileBlock = new FuBlock();
			doWhileBlock.SetCompletesNormally(true);
			methodBlock.Statements.Add(new FuDoWhile { Body = doWhileBlock, Cond = NewCall(jsonParserSkipWhitespaceMethod) });
			FuVar# elementVar = FuVar.New(new FuDynamicPtrType { Nullable = true, Class = jsonElementClass }, "element", NewCall(jsonParserParseElementMethod));
			doWhileBlock.Statements.Add(elementVar);
			doWhileBlock.Add(elementVar);
			doWhileBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType, Left = new FuBinaryExpr { Type = BoolType, Left = NewSymbolReference(elementVar), Op = FuToken.Equal, Right = new FuLiteralNull { Type = NullType } },
				Op = FuToken.CondOr, Right = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = NewCall(jsonParserSkipWhitespaceMethod) } },
				OnTrue = NewReturnNull(), OnFalse = null });
			FuCallExpr# addCall = new FuCallExpr { Type = VoidType, Method = NewMemberReference(NewMemberReference(NewSymbolReference(resultVar), jsonArrayValueField), listAddMethod) };
			addCall.Arguments.Add(NewSymbolReference(elementVar));
			doWhileBlock.Statements.Add(addCall);
			FuSwitch# switch_ = new FuSwitch { Value = new FuBinaryExpr { Type = IntType, Left = NewSymbolReference(inputField), Op = FuToken.LeftBracket, Right = new FuPostfixExpr { Type = NIntType, Inner = NewSymbolReference(offsetField), Op = FuToken.Increment } } };
			switch_.Cases.Add();
			AddCaseCharValue(switch_.Cases[0], ',');
			switch_.Cases[0].Body.Add(new FuBreak { LoopOrSwitch = switch_ });
			switch_.Cases.Add();
			AddCaseCharValue(switch_.Cases[1], ']');
			switch_.Cases[1].Body.Add(new FuReturn { Value = NewSymbolReference(resultVar) });
			switch_.DefaultBody.Add(NewReturnNull());
			doWhileBlock.Statements.Add(switch_);
			methodBlock.Statements.Add(NewReturnNull());
			jsonParserParseArrayMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserParseArrayMethod);
		{
			FuBlock# methodBlock = new FuBlock();
			methodBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			FuVar# resultVar = FuVar.New(new FuStorageType { Class = stringWriterClass }, "result");
			methodBlock.Statements.Add(resultVar);
			methodBlock.Add(resultVar);
			FuVar# startOffsetVar = FuVar.New(NIntType, "startOffset", NewSymbolReference(offsetField));
			methodBlock.Statements.Add(startOffsetVar);
			methodBlock.Add(startOffsetVar);
			FuBlock# whileBlock = new FuBlock();
			FuWhile# while_ = new FuWhile { Cond = NewOffsetLessInputLength(offsetField, inputLengthField), Body = whileBlock };
			FuSwitch# switch_ = new FuSwitch { Value = NewInputAtOffset(inputField, offsetField) };
			switch_.Cases.Add();
			for (int i = 0; i < 32; i++)
				switch_.Cases[0].Values.Add(NewLiteralLong(i));
			switch_.Cases[0].Body.Add(NewReturnNull());
			switch_.Cases.Add();
			AddCaseCharValue(switch_.Cases[1], '"');
			switch_.Cases[1].Body.Add(NewWriteJsonStringPart(inputField, stringSubstringMethod, startOffsetVar, offsetField, resultVar, textWriterWriteMethod));
			FuAggregateInitializer# init = new FuAggregateInitializer();
			init.Items.Add(new FuBinaryExpr { Left = NewSymbolReference(jsonStringValueField), Op = FuToken.Assign, Right = NewMethodCall(resultVar, stringWriterToStringMethod) });
			switch_.Cases[1].Body.Add(new FuReturn { Value = new FuPrefixExpr { Type = new FuDynamicPtrType { Class = jsonStringClass }, Op = FuToken.New, Inner = init } });
			switch_.Cases.Add();
			AddCaseCharValue(switch_.Cases[2], '\\');
			switch_.Cases[2].Body.Add(NewWriteJsonStringPart(inputField, stringSubstringMethod, startOffsetVar, offsetField, resultVar, textWriterWriteMethod));
			switch_.Cases[2].Body.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType, Left = NewSymbolReference(offsetField), Op = FuToken.GreaterOrEqual, Right = NewSymbolReference(inputLengthField) },
				OnTrue = NewReturnNull(), OnFalse = null });
			FuSwitch# switch2 = new FuSwitch { Value = NewInputAtOffset(inputField, offsetField) };
			switch2.Cases.Add();
			AddCaseCharValue(switch2.Cases[0], '"');
			AddCaseCharValue(switch2.Cases[0], '\\');
			AddCaseCharValue(switch2.Cases[0], '/');
			switch2.Cases[0].Body.Add(new FuBinaryExpr { Type = NIntType, Left = NewSymbolReference(startOffsetVar), Op = FuToken.Assign,
				Right = new FuPostfixExpr { Type = NIntType, Inner = NewSymbolReference(offsetField), Op = FuToken.Increment } });
			switch2.Cases[0].Body.Add(new FuContinue { Loop = while_ });
			AddJsonEscape(switch2, 'b', resultVar, textWriterWriteCharMethod, NewLiteralLong(8));
			AddJsonEscape(switch2, 'f', resultVar, textWriterWriteCharMethod, NewLiteralLong(12));
			AddJsonEscape(switch2, 'n', resultVar, textWriterWriteCharMethod, new FuLiteralChar { Type = IntType, Value = '\n' });
			AddJsonEscape(switch2, 'r', resultVar, textWriterWriteCharMethod, new FuLiteralChar { Type = IntType, Value = '\r' });
			AddJsonEscape(switch2, 't', resultVar, textWriterWriteCharMethod, new FuLiteralChar { Type = IntType, Value = '\t' });
			switch2.Cases.Add();
			AddCaseCharValue(switch2.Cases[6], 'u');
			switch2.Cases[6].Body.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType, Left = new FuBinaryExpr { Type = NIntType, Left = NewSymbolReference(offsetField), Op = FuToken.Plus, Right = NewLiteralLong(5) },
				Op = FuToken.GreaterOrEqual, Right = NewSymbolReference(inputLengthField) },
				OnTrue = NewReturnNull(), OnFalse = null });
			FuVar# cVar = FuVar.New(IntType, "c");
			switch2.Cases[6].Body.Add(cVar);
			FuCallExpr# substringCall = NewMethodCall(inputField, stringSubstringMethod);
			substringCall.Arguments.Add(new FuBinaryExpr { Type = NIntType, Left = NewSymbolReference(offsetField), Op = FuToken.Plus, Right = NewLiteralLong(1) });
			substringCall.Arguments.Add(NewLiteralLong(4));
			FuCallExpr# tryParseCall = NewMethodCall(cVar, intTryParseMethod);
			tryParseCall.Arguments.Add(substringCall);
			tryParseCall.Arguments.Add(NewLiteralLong(16));
			switch2.Cases[6].Body.Add(new FuIf { Cond = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = tryParseCall },
				OnTrue = NewReturnNull(), OnFalse = null });
			FuCallExpr# writeCodePointCall = NewMethodCall(resultVar, textWriterWriteCodePointMethod);
			writeCodePointCall.Arguments.Add(NewSymbolReference(cVar));
			switch2.Cases[6].Body.Add(writeCodePointCall);
			switch2.Cases[6].Body.Add(new FuBinaryExpr { Left = NewSymbolReference(offsetField), Op = FuToken.AddAssign, Right = NewLiteralLong(4) });
			switch2.Cases[6].Body.Add(new FuBreak { LoopOrSwitch = switch2 });
			switch2.DefaultBody.Add(NewReturnNull());
			switch_.Cases[2].Body.Add(switch2);
			switch_.Cases[2].Body.Add(new FuBinaryExpr { Type = NIntType, Left = NewSymbolReference(startOffsetVar), Op = FuToken.Assign,
				Right = new FuPrefixExpr { Type = NIntType, Op = FuToken.Increment, Inner = NewSymbolReference(offsetField) } });
			switch_.Cases[2].Body.Add(new FuBreak { LoopOrSwitch = switch_ });
			switch_.DefaultBody.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			switch_.DefaultBody.Add(new FuBreak { LoopOrSwitch = switch_ });
			whileBlock.Statements.Add(switch_);
			methodBlock.Statements.Add(while_);
			methodBlock.Statements.Add(NewReturnNull());
			jsonParserParseStringMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserParseStringMethod);
		FuMethod# jsonParserSeeDigitMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, BoolType, FuId.None, "SeeDigit", false);
		jsonParserSeeDigitMethod.Body = new FuReturn { Value = new FuBinaryExpr { Type = BoolType, Left = new FuBinaryExpr { Type = BoolType,
			Left = NewOffsetLessInputLength(offsetField, inputLengthField), Op = FuToken.CondAnd,
			Right = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.GreaterOrEqual, Right = new FuLiteralChar { Type = IntType, Value = '0' } } }, Op = FuToken.CondAnd,
			Right = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.LessOrEqual, Right = new FuLiteralChar { Type = IntType, Value = '9' } } } };
		jsonParserClass.Add(jsonParserSeeDigitMethod);
		FuMethod# jsonParserParseDigitsMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, VoidType, FuId.None, "ParseDigits", true);
		{
			FuBlock# methodBlock = new FuBlock();
			FuWhile# while_ = new FuWhile { Cond = NewCall(jsonParserSeeDigitMethod), Body = new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment } };
			methodBlock.Statements.Add(while_);
			jsonParserParseDigitsMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserParseDigitsMethod);
		FuMethod# jsonParserParseNumberMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, new FuDynamicPtrType { Nullable = true, Class = jsonNumberClass }, FuId.None, "ParseNumber", true);
		{
			FuBlock# methodBlock = new FuBlock();
			FuVar# startOffsetVar = FuVar.New(NIntType, "startOffset", NewSymbolReference(offsetField));
			methodBlock.Statements.Add(startOffsetVar);
			methodBlock.Add(startOffsetVar);
			methodBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.Equal, Right = new FuLiteralChar { Type = IntType, Value = '-' } },
				OnTrue = new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment }, OnFalse = null });
			methodBlock.Statements.Add(NewIfNotSeeDigitReturnNull(jsonParserSeeDigitMethod));
			methodBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType,
				Left = new FuBinaryExpr { Type = IntType, Left = NewSymbolReference(inputField), Op = FuToken.LeftBracket, Right = new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment } },
				Op = FuToken.Greater, Right = new FuLiteralChar { Type = IntType, Value = '0' } }, OnTrue = NewCall(jsonParserParseDigitsMethod) });
			FuBlock# ifBlock = new FuBlock();
			methodBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType,
				Left = NewOffsetLessInputLength(offsetField, inputLengthField), Op = FuToken.CondAnd,
				Right = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.Equal, Right = new FuLiteralChar { Type = IntType, Value = '.' } } },
				OnTrue = ifBlock, OnFalse = null });
			ifBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			ifBlock.Statements.Add(NewIfNotSeeDigitReturnNull(jsonParserSeeDigitMethod));
			ifBlock.Statements.Add(NewCall(jsonParserParseDigitsMethod));
			FuBlock# if2Block = new FuBlock();
			methodBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType,
				Left = NewOffsetLessInputLength(offsetField, inputLengthField), Op = FuToken.CondAnd,
				Right = new FuBinaryExpr { Type = BoolType, Left = new FuBinaryExpr { Type = IntType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.Or, Right = NewLiteralLong(0x20) },
					Op = FuToken.Equal, Right = new FuLiteralChar { Type = IntType, Value = 'e' } } },
				OnTrue = if2Block, OnFalse = null });
			if2Block.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType,
				Left = new FuBinaryExpr { Type = BoolType, Left = new FuPrefixExpr { Type = NIntType, Op = FuToken.Increment, Inner = NewSymbolReference(offsetField) }, Op = FuToken.Less, Right = NewSymbolReference(inputLengthField) },
				Op = FuToken.CondAnd, Right = new FuBinaryExpr { Type = BoolType,
					Left = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.Equal, Right = new FuLiteralChar { Type = IntType, Value = '+' } },
					Op = FuToken.CondOr, Right = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.Equal, Right = new FuLiteralChar { Type = IntType, Value = '-' } } } },
				OnTrue = new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment }, OnFalse = null });
			if2Block.Statements.Add(NewIfNotSeeDigitReturnNull(jsonParserSeeDigitMethod));
			if2Block.Statements.Add(NewCall(jsonParserParseDigitsMethod));
			FuVar# dVar = FuVar.New(DoubleType, "d");
			methodBlock.Statements.Add(dVar);
			methodBlock.Add(dVar);
			FuCallExpr# substringCall = NewMethodCall(inputField, stringSubstringMethod);
			substringCall.Arguments.Add(NewSymbolReference(startOffsetVar));
			substringCall.Arguments.Add(new FuBinaryExpr { Type = NIntType, Left = NewSymbolReference(offsetField), Op = FuToken.Minus, Right = NewSymbolReference(startOffsetVar) });
			FuCallExpr# tryParseCall = NewMethodCall(dVar, doubleTryParseMethod);
			tryParseCall.Arguments.Add(substringCall);
			methodBlock.Statements.Add(new FuIf { Cond = new FuPrefixExpr { Type = BoolType, Op = FuToken.ExclamationMark, Inner = tryParseCall },
				OnTrue = NewReturnNull(), OnFalse = null });
			FuAggregateInitializer# init = new FuAggregateInitializer();
			init.Items.Add(new FuBinaryExpr { Left = NewSymbolReference(jsonNumberValueField), Op = FuToken.Assign, Right = NewSymbolReference(dVar) });
			methodBlock.Statements.Add(new FuReturn { Value = new FuPrefixExpr { Type = new FuDynamicPtrType { Class = jsonNumberClass }, Op = FuToken.New, Inner = init } });
			jsonParserParseNumberMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserParseNumberMethod);
		FuVar# sParameter = FuVar.New(StringPtrType, "s");
		FuMethod# jsonParserParseKeywordMethod = FuMethod.New(jsonParserClass, FuVisibility.Private, FuCallType.Normal, BoolType, FuId.None, "ParseKeyword", true, sParameter);
		{
			FuBlock# methodBlock = new FuBlock();
			FuBlock# foreachBlock = new FuBlock();
			FuForeach# loop = new FuForeach { Collection = NewSymbolReference(sParameter), Body = foreachBlock };
			FuVar# cVar = FuVar.New(IntType, "c");
			loop.Add(cVar);
			foreachBlock.Statements.Add(new FuIf { Cond = new FuBinaryExpr { Type = BoolType,
				Left = new FuBinaryExpr { Type = BoolType, Left = new FuPrefixExpr { Type = NIntType, Op = FuToken.Increment, Inner = NewSymbolReference(offsetField) },
					Op = FuToken.GreaterOrEqual, Right = NewSymbolReference(inputLengthField) },
				Op = FuToken.CondOr, Right = new FuBinaryExpr { Type = BoolType, Left = NewInputAtOffset(inputField, offsetField), Op = FuToken.NotEqual, Right = NewSymbolReference(cVar) } },
				OnTrue = NewReturnFalse(), OnFalse = null });
			methodBlock.Statements.Add(loop);
			methodBlock.Statements.Add(new FuPostfixExpr { Inner = NewSymbolReference(offsetField), Op = FuToken.Increment });
			methodBlock.Statements.Add(NewReturnTrue());
			jsonParserParseKeywordMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserParseKeywordMethod);
		{
			FuBlock# methodBlock = new FuBlock();
			FuSwitch# switch_ = new FuSwitch { Value = NewInputAtOffset(inputField, offsetField) };
			AddParseJsonType(switch_, '{', jsonParserParseObjectMethod);
			AddParseJsonType(switch_, '[', jsonParserParseArrayMethod);
			AddParseJsonType(switch_, '"', jsonParserParseStringMethod);
			AddParseJsonType(switch_, '-', jsonParserParseNumberMethod);
			AddParseJsonKeyword(switch_, "true", jsonParserParseKeywordMethod, jsonTrueClass);
			AddParseJsonKeyword(switch_, "false", jsonParserParseKeywordMethod, jsonBooleanClass);
			AddParseJsonKeyword(switch_, "null", jsonParserParseKeywordMethod, jsonNullClass);
			switch_.DefaultBody.Add(NewReturnNull());
			methodBlock.Statements.Add(switch_);
			jsonParserParseElementMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserParseElementMethod);
		jsonParserParseWhitespaceAndElementMethod.Body = new FuReturn { Value = new FuSelectExpr { Cond = NewCall(jsonParserSkipWhitespaceMethod),
			OnTrue = NewCall(jsonParserParseElementMethod), OnFalse = new FuLiteralNull { Type = NullType } } };
		jsonParserClass.Add(jsonParserParseWhitespaceAndElementMethod);
		{
			FuVar# sParam = FuVar.New(StringPtrType, "s");
			jsonParserTryParseMethod.Parameters.Add(sParam);
			FuBlock# methodBlock = new FuBlock();
			methodBlock.Statements.Add(new FuBinaryExpr { Type = StringPtrType, Left = NewSymbolReference(inputField), Op = FuToken.Assign, Right = NewSymbolReference(sParam) });
			methodBlock.Statements.Add(new FuBinaryExpr { Type = NIntType, Left = NewSymbolReference(offsetField), Op = FuToken.Assign, Right = NewLiteralLong(0) });
			methodBlock.Statements.Add(new FuBinaryExpr { Type = NIntType, Left = NewSymbolReference(inputLengthField), Op = FuToken.Assign, Right = NewMemberReference(NewSymbolReference(sParam), stringLengthProperty) });
			FuVar# resultVar = FuVar.New(new FuDynamicPtrType { Nullable = true, Class = jsonElementClass }, "result", NewCall(jsonParserParseWhitespaceAndElementMethod));
			methodBlock.Statements.Add(resultVar);
			methodBlock.Statements.Add(new FuReturn { Value = new FuSelectExpr { Cond = NewCall(jsonParserSkipWhitespaceMethod),
				OnTrue = new FuLiteralNull { Type = NullType }, OnFalse = NewSymbolReference(resultVar) } });
			jsonParserTryParseMethod.Body = methodBlock;
		}
		jsonParserClass.Add(jsonParserTryParseMethod);
		jsonParserClass.CppFriends.Add("JsonElement");
		Add(jsonParserClass);
		JsonClasses[8] = jsonParserClass;

		FuNumericType# numericType = new FuNumericType { Id = FuId.NumericType, Name = "numeric" };
		FuFloatingType# floatingType = new FuFloatingType { Id = FuId.FloatingType, Name = "float" };
		FuFloatingType# floatIntType = new FuFloatingType { Id = FuId.FloatIntType, Name = "float" };
		FuClass# mathClass = FuClass.New(FuCallType.Static, FuId.None, "Math");
		mathClass.AddStaticMethod(numericType, FuId.MathAbs, "Abs", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Acos", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Asin", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Atan", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Atan2", FuVar.New(DoubleType, "y"), FuVar.New(DoubleType, "x"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Cbrt", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatIntType, FuId.MathCeiling, "Ceiling", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(numericType, FuId.MathClamp, "Clamp", FuVar.New(DoubleType, "value"), FuVar.New(DoubleType, "min"), FuVar.New(DoubleType, "max"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Cos", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Cosh", FuVar.New(DoubleType, "a"));
		mathClass.Add(NewConstDouble("E", Math.E));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Exp", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatIntType, FuId.MathMethod, "Floor", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathFusedMultiplyAdd, "FusedMultiplyAdd", FuVar.New(DoubleType, "x"), FuVar.New(DoubleType, "y"), FuVar.New(DoubleType, "z"));
		mathClass.AddStaticMethod(BoolType, FuId.MathIsFinite, "IsFinite", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(BoolType, FuId.MathIsInfinity, "IsInfinity", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(BoolType, FuId.MathIsNaN, "IsNaN", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Log", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathLog2, "Log2", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Log10", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(numericType, FuId.MathMax, "Max", FuVar.New(DoubleType, "a"), FuVar.New(DoubleType, "b"));
		mathClass.AddStaticMethod(numericType, FuId.MathMin, "Min", FuVar.New(DoubleType, "a"), FuVar.New(DoubleType, "b"));
		mathClass.Add(FuStaticProperty.New(FloatType, FuId.MathNaN, "NaN"));
		mathClass.Add(FuStaticProperty.New(FloatType, FuId.MathNegativeInfinity, "NegativeInfinity"));
		mathClass.Add(NewConstDouble("PI", Math.PI));
		mathClass.Add(FuStaticProperty.New(FloatType, FuId.MathPositiveInfinity, "PositiveInfinity"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Pow", FuVar.New(DoubleType, "x"), FuVar.New(DoubleType, "y"));
		mathClass.AddStaticMethod(floatIntType, FuId.MathRound, "Round", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Sin", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Sinh", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathSqrt, "Sqrt", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Tan", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatingType, FuId.MathMethod, "Tanh", FuVar.New(DoubleType, "a"));
		mathClass.AddStaticMethod(floatIntType, FuId.MathTruncate, "Truncate", FuVar.New(DoubleType, "a"));
		Add(mathClass);

		FuClass# lockClass = FuClass.New(FuCallType.Sealed, FuId.LockClass, "Lock");
		Add(lockClass);
		LockPtrType.Class = lockClass;
	}

	public static FuSystem# New() => new FuSystem();
}

class FuSourceFile
{
	internal string() Filename;
	internal int Line;
}

public class FuProgram : FuScope
{
	internal FuSystem System;
	internal List<string()>() TopLevelNatives;
	internal List<FuClass!>() Classes;
	internal FuMethod? Main = null;
	internal SortedDictionary<string(), List<byte>()>() Resources;
	internal bool RegexOptionsEnum = false;
	internal List<int>() LineLocs;
	internal List<FuSourceFile()>() SourceFiles;

	public void Init!(FuScope! parent, FuSystem system, FuParserHost! host)
	{
		this.Parent = parent;
		this.System = system;
		host.Program = this;
	}

	internal int GetLine(int loc)
	{
		int l = 0;
		int r = this.LineLocs.Count - 1;
		while (l < r) {
			int m = l + r + 1 >> 1;
			if (loc < this.LineLocs[m])
				r = m - 1;
			else
				l = m;
		}
		return l;
	}

	internal FuSourceFile GetSourceFile(int line)
	{
		int l = 0;
		int r = this.SourceFiles.Count - 1;
		while (l < r) {
			int m = l + r + 1 >> 1;
			if (line < this.SourceFiles[m].Line)
				r = m - 1;
			else
				l = m;
		}
		return this.SourceFiles[l];
	}
}