UNPKG

fable-compiler

Version:
349 lines 11 kB
import { anonRecord as makeAnonRecord, Record, Union } from "./Types"; import { compareArraysWith, equalArraysWith } from "./Util"; export class CaseInfo { constructor(declaringType, tag, name, fields) { this.declaringType = declaringType; this.tag = tag; this.name = name; this.fields = fields; } } export class TypeInfo { constructor(fullname, generics, construct, fields, cases, enumCases) { this.fullname = fullname; this.generics = generics; this.construct = construct; this.fields = fields; this.cases = cases; this.enumCases = enumCases; } toString() { return fullName(this); } Equals(other) { return equals(this, other); } CompareTo(other) { return compare(this, other); } } export function getGenerics(t) { return t.generics != null ? t.generics : []; } export function equals(t1, t2) { if (t1.fullname === "") { // Anonymous records return t2.fullname === "" && equalArraysWith(getRecordElements(t1), getRecordElements(t2), ([k1, v1], [k2, v2]) => k1 === k2 && equals(v1, v2)); } else { return t1.fullname === t2.fullname && equalArraysWith(getGenerics(t1), getGenerics(t2), equals); } } // System.Type is not comparable in .NET, but let's implement this // in case users want to create a dictionary with types as keys export function compare(t1, t2) { if (t1.fullname !== t2.fullname) { return t1.fullname < t2.fullname ? -1 : 1; } else { return compareArraysWith(getGenerics(t1), getGenerics(t2), compare); } } export function class_type(fullname, generics, construct) { return new TypeInfo(fullname, generics, construct); } export function record_type(fullname, generics, construct, fields) { return new TypeInfo(fullname, generics, construct, fields); } export function anonRecord_type(...fields) { return new TypeInfo("", undefined, undefined, () => fields); } export function union_type(fullname, generics, construct, cases) { const t = new TypeInfo(fullname, generics, construct, undefined, () => cases().map((x, i) => typeof x === "string" ? new CaseInfo(t, i, x) : new CaseInfo(t, i, x[0], x[1]))); return t; } export function tuple_type(...generics) { return new TypeInfo("System.Tuple`" + generics.length, generics); } export function delegate_type(...generics) { return new TypeInfo("System.Func`" + generics.length, generics); } export function lambda_type(argType, returnType) { return new TypeInfo("Microsoft.FSharp.Core.FSharpFunc`2", [argType, returnType]); } export function option_type(generic) { return new TypeInfo("Microsoft.FSharp.Core.FSharpOption`1", [generic]); } export function list_type(generic) { return new TypeInfo("Microsoft.FSharp.Collections.FSharpList`1", [generic]); } export function array_type(generic) { return new TypeInfo(generic.fullname + "[]", [generic]); } export function enum_type(fullname, underlyingType, enumCases) { return new TypeInfo(fullname, [underlyingType], undefined, undefined, undefined, enumCases); } export const obj_type = new TypeInfo("System.Object"); export const unit_type = new TypeInfo("Microsoft.FSharp.Core.Unit"); export const char_type = new TypeInfo("System.Char"); export const string_type = new TypeInfo("System.String"); export const bool_type = new TypeInfo("System.Boolean"); export const int8_type = new TypeInfo("System.SByte"); export const uint8_type = new TypeInfo("System.Byte"); export const int16_type = new TypeInfo("System.Int16"); export const uint16_type = new TypeInfo("System.UInt16"); export const int32_type = new TypeInfo("System.Int32"); export const uint32_type = new TypeInfo("System.UInt32"); export const float32_type = new TypeInfo("System.Single"); export const float64_type = new TypeInfo("System.Double"); export const decimal_type = new TypeInfo("System.Decimal"); export function name(info) { if (Array.isArray(info)) { return info[0]; } else if (info instanceof CaseInfo) { return info.name; } else { const i = info.fullname.lastIndexOf("."); return i === -1 ? info.fullname : info.fullname.substr(i + 1); } } export function fullName(t) { const gen = t.generics != null && !isArray(t) ? t.generics : []; if (gen.length > 0) { return t.fullname + "[" + gen.map((x) => fullName(x)).join(",") + "]"; } else { return t.fullname; } } export function namespace(t) { const i = t.fullname.lastIndexOf("."); return i === -1 ? "" : t.fullname.substr(0, i); } export function isArray(t) { return t.fullname.endsWith("[]"); } export function getElementType(t) { var _a; return isArray(t) ? (_a = t.generics) === null || _a === void 0 ? void 0 : _a[0] : undefined; } export function isGenericType(t) { return t.generics != null && t.generics.length > 0; } export function isEnum(t) { return t.enumCases != null && t.enumCases.length > 0; } /** * This doesn't replace types for fields (records) or cases (unions) * but it should be enough for type comparison purposes */ export function getGenericTypeDefinition(t) { return t.generics == null ? t : new TypeInfo(t.fullname, t.generics.map(() => obj_type)); } export function getEnumUnderlyingType(t) { var _a; return (_a = t.generics) === null || _a === void 0 ? void 0 : _a[0]; } export function getEnumValues(t) { if (isEnum(t) && t.enumCases != null) { return t.enumCases.map((kv) => kv[1]); } else { throw new Error(`${t.fullname} is not an enum type`); } } export function getEnumNames(t) { if (isEnum(t) && t.enumCases != null) { return t.enumCases.map((kv) => kv[0]); } else { throw new Error(`${t.fullname} is not an enum type`); } } function getEnumCase(t, v) { if (t.enumCases != null) { if (typeof v === "string") { for (const kv of t.enumCases) { if (kv[0] === v) { return kv; } } throw new Error(`'${v}' was not found in ${t.fullname}`); } else { for (const kv of t.enumCases) { if (kv[1] === v) { return kv; } } // .NET returns the number even if it doesn't match any of the cases return ["", v]; } } else { throw new Error(`${t.fullname} is not an enum type`); } } export function parseEnum(t, str) { // TODO: better int parsing here, parseInt ceils floats: "4.8" -> 4 const value = parseInt(str, 10); return getEnumCase(t, isNaN(value) ? str : value)[1]; } export function tryParseEnum(t, str) { try { const v = parseEnum(t, str); return [true, v]; } catch (_a) { // supress error } return [false, NaN]; } export function getEnumName(t, v) { return getEnumCase(t, v)[0]; } export function isEnumDefined(t, v) { try { const kv = getEnumCase(t, v); return kv[0] != null && kv[0] !== ""; } catch (_a) { // supress error } return false; } // FSharpType export function getUnionCases(t) { if (t.cases != null) { return t.cases(); } else { throw new Error(`${t.fullname} is not an F# union type`); } } export function getRecordElements(t) { if (t.fields != null) { return t.fields(); } else { throw new Error(`${t.fullname} is not an F# record type`); } } export function getTupleElements(t) { if (isTuple(t) && t.generics != null) { return t.generics; } else { throw new Error(`${t.fullname} is not a tuple type`); } } export function getFunctionElements(t) { if (isFunction(t) && t.generics != null) { const gen = t.generics; return [gen[0], gen[1]]; } else { throw new Error(`${t.fullname} is not an F# function type`); } } export function isUnion(t) { return t instanceof TypeInfo ? t.cases != null : t instanceof Union; } export function isRecord(t) { return t instanceof TypeInfo ? t.fields != null : t instanceof Record; } export function isTuple(t) { return t.fullname.startsWith("System.Tuple"); } // In .NET this is false for delegates export function isFunction(t) { return t.fullname === "Microsoft.FSharp.Core.FSharpFunc`2"; } // FSharpValue export function getUnionFields(v, t) { const cases = getUnionCases(t); const case_ = cases[v.tag]; if (case_ == null) { throw new Error(`Cannot find case ${v.name} in union type`); } return [case_, v.fields]; } export function getUnionCaseFields(uci) { return uci.fields == null ? [] : uci.fields; } export function getRecordFields(v) { return Object.keys(v).map((k) => v[k]); } export function getRecordField(v, field) { return v[field[0]]; } export function getTupleFields(v) { return v; } export function getTupleField(v, i) { return v[i]; } export function makeUnion(uci, values) { const expectedLength = (uci.fields || []).length; if (values.length !== expectedLength) { throw new Error(`Expected an array of length ${expectedLength} but got ${values.length}`); } return uci.declaringType.construct != null ? new uci.declaringType.construct(uci.tag, uci.name, ...values) : {}; } export function makeRecord(t, values) { const fields = getRecordElements(t); if (fields.length !== values.length) { throw new Error(`Expected an array of length ${fields.length} but got ${values.length}`); } return t.construct != null ? new t.construct(...values) : makeAnonRecord(fields.reduce((obj, [key, _t], i) => { obj[key] = values[i]; return obj; }, {})); } export function makeTuple(values, _t) { return values; } export function makeGenericType(t, generics) { return new TypeInfo(t.fullname, generics, t.construct, t.fields, t.cases); } export function createInstance(t, consArgs) { // TODO: Check if consArgs length is same as t.construct? // (Arg types can still be different) if (typeof t.construct === "function") { return new t.construct(...(consArgs !== null && consArgs !== void 0 ? consArgs : [])); } else { throw new Error(`Cannot access constructor of ${t.fullname}`); } } export function getValue(propertyInfo, v) { return v[propertyInfo[0]]; } // Fable.Core.Reflection function assertUnion(x) { if (!(x instanceof Union)) { throw new Error(`Value is not an F# union type`); } } export function getCaseTag(x) { assertUnion(x); return x.tag; } export function getCaseName(x) { assertUnion(x); return x.name; } export function getCaseFields(x) { assertUnion(x); return x.fields; } //# sourceMappingURL=Reflection.js.map