diff --git a/README.md b/README.md
index 8e54d28..3748ae1 100644
--- a/README.md
+++ b/README.md
@@ -261,3 +261,39 @@ type res5 = Pipe<
- [x] `Extends`
- [x] `Equals`
- [x] `DoesNotExtend`
+- [ ] Parser
+ - [x] Parse
+ - [x] ToString
+ - [x] Literal
+ - [x] NotLiteral
+ - [x] Optional
+ - [x] Many
+ - [x] Many1
+ - [x] Sequence
+ - [x] EndOfInput
+ - [x] Map
+ - [x] MapError
+ - [x] Skip
+ - [x] Choice
+ - [x] Or
+ - [x] Not
+ - [x] Whitespace
+ - [x] Whitespaces
+ - [x] Trim
+ - [x] TrimLeft
+ - [x] TrimRight
+ - [x] Any
+ - [x] CharRange
+ - [x] Alpha
+ - [x] AlphaNum
+ - [x] Digit
+ - [x] Digits
+ - [x] Word
+ - [x] SepBy
+ - [x] Between
+ - [x] PrefixBy
+ - [x] SufixBy
+ - [x] SepByLiteral
+ - [x] BetweenLiterals
+ - [x] PrefixByLiteral
+ - [x] SufixByLiteral
diff --git a/src/index.ts b/src/index.ts
index c4f83d7..a8827fa 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -30,6 +30,7 @@ import { Tuples } from "./internals/tuples/Tuples";
import { Unions } from "./internals/unions/Unions";
import { Booleans } from "./internals/booleans/Booleans";
import { Match } from "./internals/match/Match";
+import { Parser } from "./internals/parser/Parser";
export {
_,
@@ -62,6 +63,7 @@ export {
Numbers,
Tuples,
Functions,
+ Parser,
Booleans as B,
Objects as O,
Unions as U,
@@ -69,4 +71,5 @@ export {
Numbers as N,
Tuples as T,
Functions as F,
+ Parser as P,
};
diff --git a/src/internals/objects/Objects.ts b/src/internals/objects/Objects.ts
index c3979b8..d15671c 100644
--- a/src/internals/objects/Objects.ts
+++ b/src/internals/objects/Objects.ts
@@ -18,6 +18,20 @@ export namespace Objects {
return: Impl.FromEntries>;
}
+ /**
+ * Create an object from an array like `[key1, value1, key2, value2, ...]`.
+ * @param arr - array to convert to an object
+ * @returns an object
+ *
+ * @example
+ * ```ts
+ * type T0 = Call; // { a: 1; b: true }
+ * ```
+ */
+ export interface FromArray extends Fn {
+ return: Impl.FromArray;
+ }
+
/**
* Turn an object into a union of entries
* @param obj - The object to transform to entries
diff --git a/src/internals/objects/impl/objects.ts b/src/internals/objects/impl/objects.ts
index 3a2125f..26647f8 100644
--- a/src/internals/objects/impl/objects.ts
+++ b/src/internals/objects/impl/objects.ts
@@ -26,6 +26,13 @@ export type FromEntries = {
[entry in entries as entry[0]]: entry[1];
};
+export type FromArray<
+ arr extends unknown[],
+ Acc extends Record = {}
+> = arr extends [infer key extends PropertyKey, infer value, ...infer rest]
+ ? FromArray>
+ : Acc;
+
export type Entries = Keys extends infer keys extends keyof T
? {
[K in keys]: [K, T[K]];
diff --git a/src/internals/parser/Parser.ts b/src/internals/parser/Parser.ts
new file mode 100644
index 0000000..cce3826
--- /dev/null
+++ b/src/internals/parser/Parser.ts
@@ -0,0 +1,914 @@
+import {
+ Call,
+ Compose,
+ Constant,
+ Eval,
+ Fn,
+ PartialApply,
+ Pipe,
+ unset,
+ _,
+} from "../core/Core";
+import { Match } from "../match/Match";
+import { CharToNumber } from "../strings/impl/chars";
+import { Strings } from "../strings/Strings";
+import { Tuples } from "../tuples/Tuples";
+import { GreaterThanOrEqual, LessThanOrEqual } from "../numbers/impl/compare";
+export namespace Parser {
+ /**
+ * A parser is a function that takes static parameters and a string input
+ * and returns a result or an error.
+ * @description to enable introspection, parsers augment the function type
+ * with a name and a list of parameters.
+ */
+ export interface ParserFn extends Fn {
+ name: string;
+ params: any;
+ }
+
+ /**
+ * Base functionnal Ok type to allow for advanced error handling in HOTScript.
+ */
+ export type Value = {
+ kind: "Ok";
+ value: value;
+ };
+
+ /**
+ * Base functionnal Error type to allow for advanced error handling in HOTScript.
+ */
+ type Error = {
+ kind: "Err";
+ error: error;
+ };
+
+ /**
+ * specialised Ok type for parsers.
+ */
+ export type Ok = Value<{
+ result: Result;
+ input: Input;
+ }>;
+
+ /**
+ * specialised Error type for parsers.
+ */
+ export type Err<
+ Parser,
+ Input extends string,
+ Cause extends unknown = ""
+ > = Error<{
+ message: `Expected '${Eval>}' - Received '${Input}'`;
+ input: Input;
+ cause: Eval>;
+ }>;
+
+ /**
+ * specialised Error type for parsers when the input is not a string.
+ */
+ export type InputError = Error<{
+ message: "Input must be a string";
+ cause: Input;
+ }>;
+
+ /**
+ * Extract the most appropriate error message from an error type.
+ */
+ export type ErrMsg = TError extends Error<{
+ message: infer Msg;
+ cause: infer Cause;
+ }>
+ ? Cause extends ""
+ ? Msg
+ : Cause
+ : never;
+
+ interface ToStringFn extends Fn {
+ return: this["arg0"] extends infer Parser extends ParserFn
+ ? `${Parser["name"]}(${Eval<
+ Tuples.Join<
+ ",",
+ Pipe<
+ Parser["params"],
+ [
+ Tuples.Map<
+ Match<
+ [
+ Match.With,
+ Match.With<
+ number | undefined | null | boolean,
+ Strings.ToString
+ >,
+ Match.With<
+ string,
+ Compose<[Strings.Append<"'">, Strings.Append<_, "'">]>
+ >
+ ]
+ >
+ >
+ ]
+ >
+ >
+ >})`
+ : never;
+ }
+
+ /**
+ * Introspetion function to convert a parser to a string for error messages.
+ * @param Parser - the parser to convert to a string
+ *
+ * @example
+ * ```ts
+ * type T0 = Eval>>;
+ * // ^? type T0 = "literal('a')"
+ * ```
+ */
+ export type ToString =
+ PartialApply;
+
+ /**
+ * Parser that matches a string.
+ * It can be a union of string literals or a string literal.
+ * in case of a union, the correct string literal is returned.
+ */
+ type LiteralImpl<
+ Self,
+ ExpectedLiteral extends string,
+ Input extends string
+ > = Input extends `${ExpectedLiteral}${infer Rest}`
+ ? Input extends `${infer Lit}${Rest}`
+ ? Ok
+ : Err
+ : Err;
+
+ /**
+ * Parser that matches a literal string or a union of literal strings.
+ * @param ExpectedLiteral - the literal string or a union of literal strings to match
+ * @returns an Ok type if the literal string is found, an Err type otherwise
+ *
+ * @example
+ * ```ts
+ * type T0 = Call, "a">;
+ * // ^? type T0 = Ok< "a", "" >
+ * type T1 = Call, "b">;
+ * // ^? type T1 = Error<{ message: "Expected 'literal('a')' - Received 'b'"; cause: "" }>
+ * type T2 = Call, "a">;
+ * // ^? type T2 = Ok< "a", "" >
+ * ```
+ */
+ export interface Literal extends ParserFn {
+ name: "literal";
+ params: [ExpectedLiteral];
+ return: LiteralImpl;
+ }
+
+ type ManyImpl<
+ Self,
+ Parser,
+ Input extends string,
+ Acc extends unknown[] = []
+ > = Input extends ""
+ ? Ok
+ : Parser extends infer F extends ParserFn
+ ? Call extends infer A
+ ? A extends Ok
+ ? ManyImpl
+ : A extends Ok
+ ? ManyImpl
+ : Ok
+ : Ok
+ : Err;
+
+ /**
+ * Parser that matches a parser 0 or more times. It returns an array of the matched parsers results.
+ * @param Parser - the parser to match
+ * @returns an Ok type if the parser matches 0 or more times and the rest of the input
+ *
+ * @example
+ * ```ts
+ * type T0 = Call>, "aaa">;
+ * // ^? type T0 = Ok< ["a", "a", "a"], "" >
+ * type T1 = Call>, "bbb">;
+ * // ^? type T1 = Ok< [], "bbb" >
+ * ```
+ */
+ export interface Many extends ParserFn {
+ name: "many";
+ params: [Parser];
+ return: this["arg0"] extends infer Input extends string
+ ? ManyImpl
+ : InputError;
+ }
+
+ type SequenceImpl<
+ Self,
+ Parsers,
+ Input extends string,
+ Acc extends unknown[] = []
+ > = Parsers extends [infer Head extends ParserFn, ...infer Tail]
+ ? Call extends infer A
+ ? A extends Ok
+ ? SequenceImpl
+ : A extends Ok
+ ? SequenceImpl
+ : A // forwards error
+ : never
+ : Ok;
+
+ /**
+ * Parser that matches a list of parsers in sequence.
+ * @param Parsers - the parsers to match
+ * @returns an Ok type with an array of all the parsers results or the error of the first parser that fails
+ *
+ * @example
+ * ```ts
+ * type T0 = Call, Literal<"b">]>, "ab">;
+ * // ^? type T0 = Ok< ["a", "b"], "" >
+ * type T1 = Call, Literal<"b">]>, "ac">;
+ * // ^? type T1 = Error<{ message: "Expected 'literal('b')' - Received 'c'"; cause: "" }>
+ * ```
+ */
+ export interface Sequence extends ParserFn {
+ name: "sequence";
+ params: Parsers;
+ return: this["arg0"] extends infer Input extends string
+ ? SequenceImpl
+ : InputError;
+ }
+
+ type CommaSep = Sequence<
+ [Trim, Optional, CommaSep]>>]
+ >;
+
+ type test = Call;
+ // ^?
+
+ /**
+ * Parser that fails if there is any input left.
+ * @returns an Ok type if there is no input left
+ *
+ * @example
+ * ```ts
+ * type T0 = Call;
+ * // ^? type T0 = Ok< [], "" >
+ * type T1 = Call;
+ * // ^? type T1 = Error<{ message: "Expected 'endOfInput()' - Received 'a'"; cause: "" }>
+ * ```
+ */
+ export interface EndOfInput extends ParserFn {
+ name: "endOfInput";
+ params: [];
+ return: this["arg0"] extends infer Input extends string
+ ? Input extends ""
+ ? Ok<[], Input>
+ : Err
+ : InputError;
+ }
+
+ /**
+ * Parser that transforms the result of another parser when it succeeds.
+ * @description The function `Map` is called with the result of `Parser` and the result of `Map` is returned.
+ * This allows you to transform the result of a parser to create an AST.
+ * @param Parser - the parser to match
+ * @param Map - the function to call with the result of `Parser`
+ * @returns an Ok type if the parser matches and the result of `Map` or the error of the parser
+ *
+ * @example
+ * ```ts
+ * type T0 = Call