@fink/larix
Version:
A parser for generating fink's AST.
455 lines (377 loc) • 8.9 kB
JavaScript
const {
curr_loc,
next_loc
} = require("@fink/prattler/parser.js");
const {
curr_value,
advance
} = require("@fink/prattler/parser.js");
const {
add_separator,
add_literal,
left_binding
} = require("@fink/prattler/expressions.js");
const {
add_error
} = require("@fink/prattler/errors.js");
const {
rx,
match_all,
replace_all
} = require("@fink/std-lib/regex.js");
const {
starts_with,
slice
} = require("@fink/std-lib/str.js");
const {
map,
filter,
flatten,
length
} = require("@fink/std-lib/iter.js");
const {
min
} = require("@fink/std-lib/math.js");
const {
max_int
} = require("@fink/std-lib/num.js");
const {
terminator
} = require("../expressions.js");
const {
terminated_block
} = require("../block/init.js");
const {
next_is_new_expr,
curr_indentation
} = require("../block/indentation.js");
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;
};
exports.curr_next_adjecent = curr_next_adjecent;
const get_str_empty = ctx => {
const {
start
} = curr_loc(ctx);
const end = start;
return {
type: `string:text`,
value: ``,
loc: {
start,
end
}
};
};
exports.get_str_empty = get_str_empty;
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];
};
exports.get_expr_part = get_expr_part;
const get_str_part = ctx => {
const value = curr_value(ctx);
const loc = curr_loc(ctx);
return [{
type: `string:text`,
value,
loc
}, ctx];
};
exports.get_str_part = get_str_part;
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];
}
}
};
exports.get_parts = get_parts;
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);
}
};
exports.get_str_ind = get_str_ind;
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`);
};
exports.get_unindent = get_unindent;
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];
};
exports.get_unindented_text = get_unindented_text;
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);
}
});
exports.string = string;
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);
};
exports.add_string = add_string;