@fink/prattler
Version:
A generic Pratt/top down operator precedence parser.
197 lines (161 loc) • 3.27 kB
JavaScript
const {
add_error
} = require("./errors.js");
const auto = {
auto: true
};
exports.auto = auto;
const error_nud = ctx => {
const {
curr_token
} = ctx;
const {
value
} = curr_token;
return add_error(ctx, `Unexpected token \`${value}\` at start of expression.`, curr_token);
};
exports.error_nud = error_nud;
const error_led = ctx => {
const {
curr_token
} = ctx;
const {
value
} = curr_token;
return add_error(ctx, `Cannot use \`${value}\` as an infix operator.`, curr_token);
};
exports.error_led = error_led;
const left_binding = token_type => ({
token_type,
nud: () => error_nud,
lbp: lbp => () => lbp,
led: () => error_led
});
exports.left_binding = left_binding;
const non_binding = token_type => ({
token_type,
nud: () => error_nud,
lbp: () => () => 0,
led: () => error_led
});
exports.non_binding = non_binding;
const add_expr = (expr_builder, lbp_value) => ({
expr_builders,
...ctx
}) => {
const {
next_lbp,
lbps,
nuds,
leds
} = expr_builders;
let _do_result;
ˆmatch_2: {
const ˆvalue_1 = lbp_value;
if (ˆvalue_1 === auto) {
_do_result = next_lbp;
break ˆmatch_2;
}
{
_do_result = lbp_value;
break ˆmatch_2;
}
}
const final_lbp_value = _do_result;
_do_result = undefined;
const {
token_type,
nud,
lbp,
led
} = expr_builder;
return { ...ctx,
expr_builders: {
next_lbp: next_lbp + 2,
nuds: { ...nuds,
[token_type]: nud(final_lbp_value)
},
lbps: { ...lbps,
[token_type]: lbp(final_lbp_value)
},
leds: { ...leds,
[token_type]: led(final_lbp_value)
}
}
};
};
exports.add_expr = add_expr;
const add_ignorable = token_type => ({
igns,
...ctx
}) => ({ ...ctx,
igns: { ...igns,
[token_type]: true
}
});
exports.add_ignorable = add_ignorable;
const add_separator = expr_builder => add_expr(expr_builder, 0);
exports.add_separator = add_separator;
const add_operator = expr_builder => add_expr(expr_builder, auto);
exports.add_operator = add_operator;
const add_identifier = expr_builder => add_expr(expr_builder, auto);
exports.add_identifier = add_identifier;
const add_literal = expr_builder => add_expr(expr_builder, auto);
exports.add_literal = add_literal;
const is_ignorable = (ctx, token) => ctx.igns[token.type] === true;
exports.is_ignorable = is_ignorable;
const nud = ctx => {
const {
curr_token: {
type
},
expr_builders: {
nuds
}
} = ctx;
const {
[type]: nud_fn
} = nuds;
return nud_fn(ctx);
};
exports.nud = nud;
const led = (ctx, left) => {
const {
curr_token: {
type
},
expr_builders: {
leds
}
} = ctx;
const {
[type]: led_fn
} = leds;
return led_fn(ctx, left);
};
exports.led = led;
const next_lbp = (ctx, left) => {
const {
next_token: {
type
},
expr_builders: {
lbps
}
} = ctx;
const {
[type]: lbp_fn
} = lbps;
return lbp_fn(ctx, left);
};
exports.next_lbp = next_lbp;
const init_expr_builders = () => ({
next_lbp: 2,
nuds: {},
lbps: {
end: () => 0
},
leds: {},
igns: {}
});
exports.init_expr_builders = init_expr_builders;