UNPKG

@fink/larix

Version:

A parser for generating fink's AST.

474 lines (422 loc) 9.94 kB
import { curr_value, curr_loc } from "@fink/prattler/parser.js"; import { next_is } from "@fink/prattler/parser.js"; import { advance } from "@fink/prattler/parser.js"; import { add_error } from "@fink/prattler/errors.js"; import { prefix } from "../expressions.js"; import { terminated_block } from "../block/init.js"; import { single_expression } from "../block/expr.js"; import { get_unindented_text } from "../literals/string.js"; import { identifier } from "../identifier/init.js"; export const jsx_expr_container = ctx => { const [block, next_ctx] = terminated_block(ctx, `jsx-expr-end`); const expr = { ...block, type: `block` }; const { end } = curr_loc(next_ctx); return [{ type: `jsx:expr`, expr, loc: { start: expr.loc.start, end } }, next_ctx]; }; export const jsx_txt = ctx => { const { value, loc } = ctx.curr_token; return [{ type: `jsx:text`, value, loc }, ctx]; }; export const get_children = ctx => { const ˆvalue_1 = ctx.next_token; /* istanbul ignore else */ if (ˆvalue_1 != null) { const { type: ˆp_3 } = ˆvalue_1; /* istanbul ignore else */ if (ˆp_3 === `jsx-elem-close`) { return [[], ctx]; } } /* istanbul ignore else */ if (ˆvalue_1 != null) { const { type: ˆp_4 } = ˆvalue_1; /* istanbul ignore else */ if (ˆp_4 === `jsx-text`) { { const [expr, children_ctx] = jsx_txt(advance(ctx)); const [children, end_ctx] = get_children(children_ctx); return [[expr, ...children], end_ctx]; } } } /* istanbul ignore else */ if (ˆvalue_1 != null) { const { type: ˆp_5 } = ˆvalue_1; /* istanbul ignore else */ if (ˆp_5 === `jsx-frag-open`) { { const [expr, children_ctx] = jsx_frag(advance(ctx)); const [children, end_ctx] = get_children(children_ctx); return [[expr, ...children], end_ctx]; } } } /* istanbul ignore else */ if (ˆvalue_1 != null) { const { type: ˆp_6 } = ˆvalue_1; /* istanbul ignore else */ if (ˆp_6 === `jsx-elem-start`) { { const [expr, children_ctx] = jsx_elem(advance(ctx)); const [children, end_ctx] = get_children(children_ctx); return [[expr, ...children], end_ctx]; } } } /* istanbul ignore else */ if (ˆvalue_1 != null) { const { type: ˆp_7 } = ˆvalue_1; /* istanbul ignore else */ if (ˆp_7 === `jsx-expr-start`) { { const [expr, children_ctx] = jsx_expr_container(advance(ctx)); const [children, end_ctx] = get_children(children_ctx); return [[expr, ...children], end_ctx]; } } } { { const [err, err_ctx] = add_error(ctx, `Expected JSX element <...>, </...> or JSX text or JSX expr {...}.`, ctx.next_token); return [[err], err_ctx]; } } }; export const jsx_ident = ctx => identifier(advance(ctx)); export const jsx_body = (name, ctx) => { const [children, close_ctx] = get_children(ctx); let _do_result; ˆmatch_9: { const ˆvalue_8 = close_ctx.next_token; // TODO: name can also be a member expr, ... /* istanbul ignore else */ if (ˆvalue_8 != null) { const { type: ˆp_10, value: ˆp_11 } = ˆvalue_8; // TODO: name can also be a member expr, ... /* istanbul ignore else */ if (ˆp_10 === `jsx-elem-close`) // TODO: name can also be a member expr, ... /* istanbul ignore else */ if (ˆp_11 === `</${name.value}>`) { _do_result = advance(close_ctx); break ˆmatch_9; } } { { const [, err_ctx] = add_error(close_ctx, `Expected JSX closing element </${name.value}>.`, close_ctx.next_token); _do_result = err_ctx; } break ˆmatch_9; } } const end_ctx = _do_result; _do_result = undefined; return [children, end_ctx]; }; export const jsx_string_val = ctx => { const { start } = curr_loc(ctx); const op = curr_value(ctx); const [{ loc: { end }, ...str }, next_ctx] = get_unindented_text(ctx, op); return [{ ...str, loc: { start, end } }, next_ctx]; }; export const jsx_attr_value = ctx => { const ˆvalue_12 = ctx.next_token; /* istanbul ignore else */ if (ˆvalue_12 != null) { const { type: ˆp_14 } = ˆvalue_12; /* istanbul ignore else */ if (ˆp_14 === `jsx-expr-start`) { return jsx_expr_container(advance(ctx)); } } /* istanbul ignore else */ if (ˆvalue_12 != null) { const { type: ˆp_15 } = ˆvalue_12; /* istanbul ignore else */ if (ˆp_15 === `str-start`) { return jsx_string_val(advance(ctx)); } } { return single_expression({ ...ctx, jsx: true }, 0); } }; export const jsx_attr = ctx => { const [name, value_ctx] = jsx_ident(ctx); const { start } = name.loc; let _do_result2; ˆmatch_17: { const ˆvalue_16 = value_ctx; /* istanbul ignore else */ if (next_is(ˆvalue_16, `=`)) { _do_result2 = jsx_attr_value(advance(value_ctx)); break ˆmatch_17; } { _do_result2 = [false, value_ctx]; break ˆmatch_17; } } const [value, next_ctx] = _do_result2; _do_result2 = undefined; const { end } = curr_loc(next_ctx); return [{ type: `jsx:attr`, name, value, loc: { start, end } }, next_ctx]; }; export const jsx_spread_attr = ctx => { const { start } = curr_loc(ctx); const [right, next_ctx] = single_expression({ ...ctx, jsx: true }, 0); const { end } = curr_loc(next_ctx); return [{ type: `spread`, op: `...`, right, loc: { start, end } }, next_ctx]; }; export const jsx_props = ctx => { const ˆvalue_18 = ctx.next_token; /* istanbul ignore else */ if (ˆvalue_18 != null) { const { type: ˆp_20, value: ˆp_21 } = ˆvalue_18; /* istanbul ignore else */ if (ˆp_20 === `jsx-elem-close`) /* istanbul ignore else */ if (ˆp_21 === `/>`) { return [[], ctx]; } } /* istanbul ignore else */ if (ˆvalue_18 != null) { const { type: ˆp_22 } = ˆvalue_18; /* istanbul ignore else */ if (ˆp_22 === `jsx-elem-end`) { return [[], ctx]; } } /* istanbul ignore else */ if (ˆvalue_18 != null) { const { type: ˆp_23 } = ˆvalue_18; /* istanbul ignore else */ if (ˆp_23 === `...`) { { const [expr, next_ctx] = jsx_spread_attr(advance(ctx)); const [props, end_ctx] = jsx_props(next_ctx); return [[expr, ...props], end_ctx]; } } } /* istanbul ignore else */ if (ˆvalue_18 != null) { const { type: ˆp_24 } = ˆvalue_18; /* istanbul ignore else */ if (ˆp_24 === `ident`) { { const [expr, next_ctx] = jsx_attr(ctx); const [props, end_ctx] = jsx_props(next_ctx); return [[expr, ...props], end_ctx]; } } } { { const [err, err_ctx] = add_error(ctx, `Expected JSX prop identifier, spread expr, or end of tag.`, ctx.next_token); return [[err], err_ctx]; } } }; export const body_or_end_elem = (ctx, name) => { const ˆvalue_25 = ctx.next_token; /* istanbul ignore else */ if (ˆvalue_25 != null) { const { type: ˆp_27 } = ˆvalue_25; /* istanbul ignore else */ if (ˆp_27 === `jsx-elem-end`) { { const [children, next_ctx] = jsx_body(name, advance(ctx)); return [false, children, next_ctx]; } } } /* istanbul ignore else */ if (ˆvalue_25 != null) { const { type: ˆp_28 } = ˆvalue_25; /* istanbul ignore else */ if (ˆp_28 === `jsx-elem-close`) { return [true, [], advance(ctx)]; } } { { const [err, err_ctx] = add_error(ctx, `Expected end of JSX tag.`, ctx.next_token); return [false, [err], err_ctx]; } } }; export const jsx_elem = ctx => { const { start } = curr_loc(ctx); const [name, elem_ctx] = jsx_ident(ctx); const [props, body_ctx] = jsx_props(elem_ctx); const [self_closing, children, next_ctx] = body_or_end_elem(body_ctx, name); const { end } = curr_loc(next_ctx); return [{ type: `jsx:elem`, name, props, children, self_closing, loc: { start, end } }, next_ctx]; }; export const jsx_frag = ctx => { const { start } = curr_loc(ctx); const [children, close_ctx] = get_children(ctx); let _do_result3; ˆmatch_30: { const ˆvalue_29 = close_ctx.next_token; /* istanbul ignore else */ if (ˆvalue_29 != null) { const { type: ˆp_31, value: ˆp_32 } = ˆvalue_29; /* istanbul ignore else */ if (ˆp_31 === `jsx-elem-close`) /* istanbul ignore else */ if (ˆp_32 === `</>`) { _do_result3 = advance(close_ctx); break ˆmatch_30; } } { { const [, err_ctx] = add_error(ctx, `Expected closing JSX fragment </>.`, ctx.next_token); _do_result3 = err_ctx; } break ˆmatch_30; } } const end_ctx = _do_result3; _do_result3 = undefined; const { end } = curr_loc(end_ctx); return [{ type: `jsx:frag`, children, loc: { start, end } }, end_ctx]; }; export const jsx_elem_or_fragment = ctx => { const ˆvalue_33 = ctx.curr_token; /* istanbul ignore else */ if (ˆvalue_33 != null) { const { type: ˆp_35 } = ˆvalue_33; /* istanbul ignore else */ if (ˆp_35 === `jsx-frag-open`) { return jsx_frag(ctx); } } { return jsx_elem(ctx); } }; export const jsx = token_type => ({ ...prefix(token_type), nud: () => ctx => jsx_elem_or_fragment(ctx) });