@fink/larix
Version:
A parser for generating fink's AST.
379 lines (332 loc) • 8.46 kB
JavaScript
import { curr_loc, next_loc } from "@fink/prattler/parser.js";
import { curr_value, advance } from "@fink/prattler/parser.js";
import { add_separator, add_literal, left_binding } from "@fink/prattler/expressions.js";
import { add_error } from "@fink/prattler/errors.js";
import { rx, match_all, replace_all } from "@fink/std-lib/regex.js";
import { starts_with, slice } from "@fink/std-lib/str.js";
import { map, filter, flatten, length } from "@fink/std-lib/iter.js";
import { min } from "@fink/std-lib/math.js";
import { max_int } from "@fink/std-lib/num.js";
import { terminator } from "../expressions.js";
import { terminated_block } from "../block/init.js";
import { next_is_new_expr, curr_indentation } from "../block/indentation.js";
export const curr_next_adjecent = ctx => {
const curr = curr_loc(ctx);
const next = next_loc(ctx);
return curr.end.line === next.start.line && curr.end.column === next.start.column;
};
export const get_str_empty = ctx => {
const {
start
} = curr_loc(ctx);
const end = start;
return {
type: `string:text`,
value: ``,
loc: {
start,
end
}
};
};
export const get_expr_part = ctx => {
const [block, next_ctx] = terminated_block(ctx, `str-expr-end`);
let _do_result;
ˆmatch_2: {
const ˆvalue_1 = block;
/* istanbul ignore else */
if (ˆvalue_1 != null) {
const {
exprs: ˆp_3
} = ˆvalue_1;
/* istanbul ignore else */
if (1 < length(ˆp_3)) {
_do_result = { ...block,
type: `block`
};
break ˆmatch_2;
}
}
{
{
const {
exprs: [expr]
} = block;
_do_result = expr;
}
break ˆmatch_2;
}
}
const expr = _do_result;
_do_result = undefined;
return [expr, next_ctx];
};
export const get_str_part = ctx => {
const value = curr_value(ctx);
const loc = curr_loc(ctx);
return [{
type: `string:text`,
value,
loc
}, ctx];
};
export const get_parts = ctx => {
const ˆvalue_4 = ctx.next_token;
/* istanbul ignore else */
if (ˆvalue_4 != null) {
const {
type: ˆp_6
} = ˆvalue_4;
/* istanbul ignore else */
if (ˆp_6 === `str-end`) {
{
const expr = get_str_empty(ctx);
return [[expr], advance(ctx)];
}
}
}
/* istanbul ignore else */
if (ˆvalue_4 != null) {
const {
type: ˆp_7
} = ˆvalue_4;
/* istanbul ignore else */
if (ˆp_7 === `str-text`) {
{
const [expr, next_ctx] = get_str_part(advance(ctx));
{
const ˆvalue_8 = next_ctx.next_token;
/* istanbul ignore else */
if (ˆvalue_8 != null) {
const {
type: ˆp_10
} = ˆvalue_8;
/* istanbul ignore else */
if (ˆp_10 === `str-end`) {
return [[expr], advance(next_ctx)];
}
}
{
{
const [rest, end_ctx] = get_parts(next_ctx);
return [[expr, ...rest], end_ctx];
}
}
}
}
}
}
/* istanbul ignore else */
if (ˆvalue_4 != null) {
const {
type: ˆp_11
} = ˆvalue_4;
/* istanbul ignore else */
if (ˆp_11 === `str-expr-start`) {
{
const [expr, next_ctx] = get_expr_part(advance(ctx));
const [rest, end_ctx] = get_parts(next_ctx);
let _do_result2;
ˆmatch_13: {
const ˆvalue_12 = ctx.curr_token;
/* istanbul ignore else */
if (ˆvalue_12 != null) {
const {
type: ˆp_14
} = ˆvalue_12;
/* istanbul ignore else */
if (ˆp_14 === `str-expr-end`) {
_do_result2 = [get_str_empty(ctx), expr, ...rest];
break ˆmatch_13;
}
}
{
_do_result2 = [expr, ...rest];
break ˆmatch_13;
}
}
const exprs = _do_result2;
_do_result2 = undefined;
return [exprs, end_ctx];
}
}
}
{
{
const [, err_ctx] = add_error(ctx, `Unexpected end of code.`, ctx.next_token);
return [[], err_ctx];
}
}
};
export const get_str_ind = (str, min_ind) => {
const matched = match_all(str, rx`\n([ ]{${min_ind},})`);
{
let ˆpipe_result_15 = matched;
return ˆpipe_result_15 = map(([, spaces]) => length(spaces))(ˆpipe_result_15);
}
};
export const get_unindent = (ctx, parts) => {
const min_ind = 1 + curr_indentation(ctx);
let _do_result3;
{
let ˆpipe_result_16 = parts;
ˆpipe_result_16 = filter(part => {
const ˆvalue_17 = part;
/* istanbul ignore else */
if (ˆvalue_17 != null) {
const {
type: ˆp_19
} = ˆvalue_17;
/* istanbul ignore else */
if (ˆp_19 === `string:text`) {
return true;
}
}
{
return false;
}
})(ˆpipe_result_16);
ˆpipe_result_16 = map(part => get_str_ind(part.value, min_ind))(ˆpipe_result_16);
ˆpipe_result_16 = flatten(ˆpipe_result_16);
_do_result3 = ˆpipe_result_16 = min(max_int, ...ˆpipe_result_16);
}
const ind = _do_result3;
_do_result3 = undefined;
return str => replace_all(str, rx`\n[ ]{0,${ind}}`, `\n`);
};
export const get_unindented_text = (ctx, op) => {
const {
start
} = curr_loc(ctx);
const [[first_part, ...parsed_parts], next_ctx] = get_parts(ctx, op);
let _do_result4;
ˆmatch_21: {
const ˆvalue_20 = first_part;
/* istanbul ignore else */
if (ˆvalue_20 != null) {
const {
type: ˆp_22
} = ˆvalue_20;
/* istanbul ignore else */
if (ˆp_22 === `string:text`) {
_do_result4 = [first_part, ...parsed_parts];
break ˆmatch_21;
}
}
{
{
const expr = get_str_empty(ctx);
_do_result4 = [expr, first_part, ...parsed_parts];
}
break ˆmatch_21;
}
}
const parts = _do_result4;
_do_result4 = undefined;
const {
end
} = curr_loc(next_ctx);
const unindent = get_unindent(ctx, parts);
let _do_result5;
{
let ˆpipe_result_23 = parts;
_do_result5 = ˆpipe_result_23 = map(part => {
const ˆvalue_24 = part;
/* istanbul ignore else */
if (ˆvalue_24 != null) {
const {
type: ˆp_26
} = ˆvalue_24;
/* istanbul ignore else */
if (ˆp_26 === `string:text`) {
{
const value = unindent(part.value);
return { ...part,
value
};
}
}
}
{
return part;
}
})(ˆpipe_result_23);
}
const [first, ...rest] = _do_result5;
_do_result5 = undefined;
let _do_result6;
ˆmatch_28: {
const ˆvalue_27 = first.value;
/* istanbul ignore else */
if (starts_with(ˆvalue_27, `\n`)) {
_do_result6 = [{ ...first,
value: slice(first.value, 1)
}, ...rest];
break ˆmatch_28;
}
{
_do_result6 = [first, ...rest];
break ˆmatch_28;
}
}
// remove first empty line
const exprs = _do_result6;
_do_result6 = undefined;
return [{
type: `string`,
op,
exprs,
tag: false,
loc: {
start,
end
}
}, next_ctx];
};
export const string = token_type => ({ ...left_binding(token_type),
lbp: lbp => (ctx, left) => {
const ˆvalue_29 = true;
// default indentation behaviour
/* istanbul ignore else */
if (ˆvalue_29 === next_is_new_expr(ctx)) {
return 0;
}
/* istanbul ignore else */
if (ˆvalue_29 === (left.type === `ident` && curr_next_adjecent(ctx))) {
return lbp;
}
{
return 0;
}
},
led: () => (ctx, left) => {
const {
loc: {
start
}
} = left;
const op = curr_value(ctx);
const [{
loc: {
end
},
...str
}, next_ctx] = get_unindented_text(ctx, op);
return [{ ...str,
tag: left,
loc: {
start,
end
}
}, next_ctx];
},
nud: () => ctx => {
const op = curr_value(ctx);
return get_unindented_text(ctx, op);
}
});
export const add_string = ctx => {
let ˆpipe_result_31 = ctx;
ˆpipe_result_31 = add_literal(string(`str-start`))(ˆpipe_result_31);
return ˆpipe_result_31 = add_separator(terminator(`str-expr-end`))(ˆpipe_result_31);
};