diff --git a/src/end-to-end.test.ts b/src/end-to-end.test.ts index 7152b31..7ff9fe7 100644 --- a/src/end-to-end.test.ts +++ b/src/end-to-end.test.ts @@ -63,6 +63,10 @@ testCases(endToEnd, code => code)('end-to-end tests', [ ['{ (a: :(")")), (")": (B)) }', either.makeRight({ a: 'B', ')': 'B' })], [':match({ a: A })({ tag: a, value: {} })', either.makeRight('A')], [':{string concatenate}(a)(b)', either.makeRight('ba')], + [ + ':flow({ :string.concatenate(a) :string.concatenate(b) })(z)', + either.makeRight('zab'), + ], [ `{ "static data":"blah blah blah" @@ -212,4 +216,12 @@ testCases(endToEnd, code => code)('end-to-end tests', [ value: 'true', }), ], + [ + `:integer.add( + :integer.subtract(1)(2) + )( + :integer.subtract(2)(4) + )`, + either.makeRight('3'), + ], ]) diff --git a/src/language/compiling/semantics/prelude.ts b/src/language/compiling/semantics/prelude.ts index cdf099e..bf954d4 100644 --- a/src/language/compiling/semantics/prelude.ts +++ b/src/language/compiling/semantics/prelude.ts @@ -308,8 +308,6 @@ export const prelude: ObjectNode = makeObjectNode({ ), boolean: makeObjectNode({ - true: 'true', - false: 'false', is: preludeFunction( ['boolean', 'is'], { diff --git a/src/language/parsing/molecule.ts b/src/language/parsing/molecule.ts index a642356..9119a3c 100644 --- a/src/language/parsing/molecule.ts +++ b/src/language/parsing/molecule.ts @@ -90,14 +90,16 @@ const sugaredApply: Parser = parser.map( parser.oneOrMore( parser.sequence([ parser.literal('('), + optional(omit(whitespace)), parser.lazy(() => propertyValue), + optional(omit(whitespace)), parser.literal(')'), ]), ), ]), ([f, multipleArguments]) => multipleArguments.reduce( - (expression, [_1, argument, _2]) => ({ + (expression, [_1, _2, argument, _3, _4]) => ({ 0: '@apply', function: expression, argument, diff --git a/src/language/semantics/type-system/subtyping.ts b/src/language/semantics/type-system/subtyping.ts index bf66e8a..c117516 100644 --- a/src/language/semantics/type-system/subtyping.ts +++ b/src/language/semantics/type-system/subtyping.ts @@ -232,12 +232,12 @@ const isNonUnionAssignableToUnion = ({ if (source.kind === 'opaque') { return source.isAssignableTo(target) } else { - // The strategy for this case is to check whether any of the target's members are - // assignable to the source type. However this alone is not sufficient—for example - // `{ a: 'a' | 'b' }` should be assignable to `{ a: 'a' } | { a: 'b' }` even though - // `{ a: 'a' | 'b' }` is not directly assignable to `{ a: 'a' }` nor `{ a: 'b' }`. To - // make things work the target type is first converted into a standard form (e.g. - // `{ a: 'a' } | { a: 'b' }` is translated into `{ a: 'a' | 'b' }`. + // The strategy for this case is to check whether any of the target's members are assignable to + // the source type. However this alone is not sufficient—for example `{ a: 'a' | 'b' }` should + // be assignable to `{ a: 'a' } | { a: 'b' }` even though `{ a: 'a' | 'b' }` is not directly + // assignable to `{ a: 'a' }` nor `{ a: 'b' }`. To make things work the target type is first + // converted into a standard form (e.g. `{ a: 'a' } | { a: 'b' }` is translated into + // `{ a: 'a' | 'b' }`. const preparedTarget = simplifyUnionType(target) @@ -290,7 +290,7 @@ export const simplifyUnionType = (typeToSimplify: UnionType): UnionType => { const reducibleSubsets: Map< string, { - readonly keys: string[] + readonly keys: readonly string[] readonly typesToMerge: Set } > = new Map() @@ -298,12 +298,10 @@ export const simplifyUnionType = (typeToSimplify: UnionType): UnionType => { if (typeof type !== 'string' && type.kind === 'object') { const keys = Object.keys(type.children) - // Object types with a single key are always mergeable with other object types - // containing the same single key. For example `{ a: 'a' } | { a: 'b' }` can become - // `{ a: 'a' | 'b' }`. - // TODO: Handle cases where there is more than one key but property types are - // compatible. For example `{ a: 'a', b: 'b' } | { a: 'b', b: 'b' }` can become - // `{ a: 'a' | 'b', b: 'b' }`. + // Object types with a single key are always mergeable with other object types containing the + // same single key. For example `{ a: 'a' } | { a: 'b' }` can become `{ a: 'a' | 'b' }`. + // TODO: Handle cases where there is more than one key but property types are compatible. For + // example `{ a: 'a', b: 'b' } | { a: 'b', b: 'b' }` can become `{ a: 'a' | 'b', b: 'b' }`. const fingerprint = keys[0] if (keys.length === 1 && fingerprint !== undefined) { const objectTypesWithThisFingerprint = reducibleSubsets.get( @@ -319,6 +317,7 @@ export const simplifyUnionType = (typeToSimplify: UnionType): UnionType => { const canonicalizedTargetMembers: Set> = new Set([...typeToSimplify.members]) + // Reduce `reducibleSubsets` by merging all candidate, updating `canonicalizedTargetMembers`. // Merge algorithm: // - for each reducible subset of object types: