import { Token } from 'moo';
import Lexer from '@artemis-lang/lexer';

declare const NodeTypes: {
    readonly Program: "Program";
    readonly Binary: "Binary";
    readonly Hex: "Hex";
    readonly Number: "Number";
    readonly String: "String";
    readonly Boolean: "Boolean";
    readonly List: "List";
    readonly Map: "Map";
    readonly Set: "Set";
    readonly BinaryLiteral: "BinaryLiteral";
    readonly HexLiteral: "HexLiteral";
    readonly NumberLiteral: "NumberLiteral";
    readonly StringLiteral: "StringLiteral";
    readonly BooleanLiteral: "BooleanLiteral";
    readonly ListLiteral: "ListLiteral";
    readonly MapLiteral: "MapLiteral";
    readonly SetLiteral: "SetLiteral";
    readonly Fn: "Fn";
    readonly FnCall: "FnCall";
    readonly NativeFnCall: "NativeFnCall";
    readonly Result: "Result";
    readonly NativeFnResult: "NativeFnResult";
    readonly Reference: "Reference";
    readonly If: "If";
    readonly While: "While";
    readonly For: "For";
    readonly Match: "Match";
    readonly BinaryExpression: "BinaryExpression";
    readonly UnaryExpression: "UnaryExpression";
    readonly BinaryExpressionLiteral: "BinaryExpressionLiteral";
    readonly UnaryExpressionLiteral: "UnaryExpressionLiteral";
    readonly JsCode: "JsCode";
    readonly Return: "Return";
};

interface Node {
    type: keyof typeof NodeTypes;
}

interface IParserPlugin<T extends Node = Node> {
    matcher: (parser: Parser) => boolean;
    handler: (parser: Parser) => T;
}
type ParserPluginOptions = {};

interface Program extends Node {
    type: typeof NodeTypes.Program;
    body: Node[];
}

declare class Parser {
    tokens: Token[];
    current: number;
    plugins: IParserPlugin[];
    input: string;
    constructor(lexer: Lexer);
    init(tokens: moo.Token[]): Token[];
    isAtEnd(): boolean;
    isAtStart(): boolean;
    peek(): Token;
    previous(): Token;
    advance(): Token;
    match(type: string, value?: string): boolean;
    consume(type: string, message: string): Token;
    plugin(plugin: IParserPlugin): void;
    add(...plugins: IParserPlugin[]): void;
    next(): Token;
    nextBy(n: number): Token;
    nextByType(n: number): string | undefined;
    parse(): Program;
    parseExpression(): Node;
}

declare class ParserPlugin<T extends Node = Node> implements IParserPlugin<T> {
    matcher: (parser: Parser) => boolean;
    handler: (parser: Parser) => T;
    constructor(matcher: (parser: Parser) => boolean, handler: (parser: Parser) => T, _options?: ParserPluginOptions);
}

declare const plugins: (ParserPlugin<{
    type: "BinaryExpressionLiteral";
    operator: string;
    left: Node;
    right: Node;
}> | ParserPlugin<{
    type: "BinaryLiteral";
    value: string;
}> | ParserPlugin<{
    type: "BooleanLiteral";
    value: boolean;
}> | ParserPlugin<{
    type: "Binary";
    name: string;
    value: string;
}> | ParserPlugin<{
    type: "BinaryExpression";
    name: string;
    operator: string;
    left: Node;
    right: Node;
}> | ParserPlugin<{
    type: "Boolean";
    name: string;
    value: boolean;
}> | ParserPlugin<{
    type: "Number";
    name: string;
    value: string;
}> | ParserPlugin<{
    type: "Fn";
    name: string;
    args: string[];
    body: Node[];
}> | ParserPlugin<{
    type: "Hex";
    name: string;
    value: string;
}> | ParserPlugin<{
    type: "List";
    name: string;
    value: Node[];
}> | ParserPlugin<{
    type: "Map";
    name: string;
    value: Map<any, any>;
}> | ParserPlugin<{
    type: "Set";
    name: string;
    value: Set<unknown>;
}> | ParserPlugin<{
    type: "String";
    name: string;
    value: string;
}> | ParserPlugin<{
    type: "UnaryExpression";
    name: string;
    operator: string;
    operand: Node;
}> | ParserPlugin<{
    type: "For";
    index: string;
    start: Node;
    end: Node;
    step: Node | null;
    body: Node[];
}> | ParserPlugin<{
    type: "FnCall";
    name: string;
    params: Node[];
}> | ParserPlugin<{
    type: "NativeFnCall";
    name: string;
    params: Node[];
}> | ParserPlugin<{
    type: "HexLiteral";
    value: string;
}> | ParserPlugin<{
    type: "If";
    condition: Node;
    thenBranch: Node;
    elseBranch: Node;
} | {
    type: "If";
    condition: Node;
    thenBranch: Node;
    elseBranch: null;
}> | ParserPlugin<{
    type: "ListLiteral";
    value: Node[];
}> | ParserPlugin<{
    type: "MapLiteral";
    value: Map<any, any>;
}> | ParserPlugin<{
    type: "Match";
    value: Map<any, any>;
    condition: Node;
}> | ParserPlugin<{
    type: "NumberLiteral";
    value: string;
}> | ParserPlugin<{
    type: "Reference";
    value: string;
}> | ParserPlugin<{
    type: "Result";
    name: string;
    fnName: string;
    params: Node[];
}> | ParserPlugin<{
    type: "NativeFnResult";
    name: string;
    nativeFn: string;
    params: Node[];
}> | ParserPlugin<{
    type: "SetLiteral";
    value: Set<unknown>;
}> | ParserPlugin<{
    type: "StringLiteral";
    value: string;
}> | ParserPlugin<{
    type: "UnaryExpressionLiteral";
    operator: string;
    operand: Node;
}> | ParserPlugin<{
    type: "While";
    condition: Node;
    body: Node[];
}> | ParserPlugin<{
    type: "JsCode";
    value: string;
}> | ParserPlugin<{
    type: "Return";
    value: Node;
}>)[];

type NodeType<T extends keyof typeof NodeType> = typeof plugins extends IParserPlugin<infer R>[] ? Extract<R, {
    type: T;
}> : never;

export { IParserPlugin, Node, NodeType, ParserPluginOptions, Program, Parser as default };
