///|
fn diagnostics_to_string(
diagnostics : Array[@moonbitlang/parser/basic.Report],
) -> String {
diagnostics.fold(init="", (acc, cur) => "\{acc}\n\{cur}")
}
///|
pub fn parse_code_to_expr(code : String) -> Result[@syntax.Expr, String] {
let code = normalize_v092_syntax(code)
let (impls, diagnostics) = @moonbitlang/parser.parse_string(
"fn init{\n \{code}\n}",
parser=Handrolled,
)
match impls {
More(TopFuncDef(fun_decl=_, decl_body~, where_clause=_, loc=_), tail=Empty) =>
match decl_body {
DeclBody(expr~) => Ok(expr)
_ => Err("Invalid function declaration body")
}
_ => Err(diagnostics_to_string(diagnostics))
}
}
///|
pub fn parse_code_to_impl(code : String) -> Result[@syntax.Impl, String] {
let code = normalize_v092_syntax(code)
let (impls, diagnostics) = @moonbitlang/parser.parse_string(
code,
parser=Handrolled,
)
match impls {
More(top, tail=Empty) => Ok(top)
_ => Err(diagnostics_to_string(diagnostics))
}
}
///|
pub(all) enum EvalParseResult {
EvalExpr(@syntax.Expr)
EvalTop(@list.List[@syntax.Impl], run_main~ : Bool)
}
///|
fn parse_code_to_impls(
code : String,
) -> Result[@list.List[@syntax.Impl], String] {
let code = normalize_v092_syntax(code)
let (impls, diagnostics) = @moonbitlang/parser.parse_string(
code,
parser=Handrolled,
)
if diagnostics.length() == 0 {
Ok(impls)
} else {
Err(diagnostics_to_string(diagnostics))
}
}
///|
fn parse_code_to_impls_lenient(code : String) -> @list.List[@syntax.Impl] {
let code = normalize_v092_syntax(code)
let (impls, _) = @moonbitlang/parser.parse_string(code, parser=Handrolled)
impls
}
///|
fn impls_have_main(impls : @list.List[@syntax.Impl]) -> Bool {
match impls {
More(TopFuncDef(fun_decl={ name: { name: "main", .. }, .. }, ..), ..) =>
true
More(_, tail~) => impls_have_main(tail)
Empty => false
}
}
///|
fn impls_have_top_decl(impls : @list.List[@syntax.Impl]) -> Bool {
match impls {
More(TopExpr(_), tail~) => impls_have_top_decl(tail)
More(_, ..) => true
Empty => false
}
}
///|
fn impls_have_top_expr(impls : @list.List[@syntax.Impl]) -> Bool {
match impls {
More(TopExpr(_), ..) => true
More(_, tail~) => impls_have_top_expr(tail)
Empty => false
}
}
///|
fn top_item_requires_top(item : @syntax.Impl) -> Bool {
match item {
TopFuncDef(fun_decl~, decl_body~, ..) =>
fun_decl.type_name is Some(_) || decl_body is DeclStubs(_)
TopExpr(_) | TopLetDef(_) => false
_ => true
}
}
///|
fn impls_require_top(impls : @list.List[@syntax.Impl]) -> Bool {
match impls {
More(item, tail~) =>
if top_item_requires_top(item) {
true
} else {
impls_require_top(tail)
}
Empty => false
}
}
///|
fn repair_top_expr_hole(item : @syntax.Impl, code : String) -> @syntax.Impl {
match item {
TopExpr(expr=Hole(loc~, ..), is_main~, is_async~, loc=top_loc) => {
let start = loc.start.cnum.clamp(min=0, max=code.length())
let snippet = code[start:].to_owned()
match parse_code_to_expr(snippet) {
Ok(expr) => TopExpr(expr~, is_main~, is_async~, loc=top_loc)
Err(_) => item
}
}
_ => item
}
}
///|
fn repair_top_expr_holes(
impls : @list.List[@syntax.Impl],
code : String,
) -> @list.List[@syntax.Impl] {
match impls {
More(item, tail~) =>
@list.cons(
repair_top_expr_hole(item, code),
repair_top_expr_holes(tail, code),
)
Empty => @list.new()
}
}
///|
pub fn parse_eval_code(code : String) -> Result[EvalParseResult, String] {
let code = normalize_v092_syntax(code)
let top_result = parse_code_to_impls(code)
match top_result {
Ok(impls) if impls_have_main(impls) => Ok(EvalTop(impls, run_main=true))
Ok(impls) if impls_require_top(impls) =>
Ok(EvalTop(repair_top_expr_holes(impls, code), run_main=false))
_ => {
let lenient_impls = parse_code_to_impls_lenient(code)
if impls_require_top(lenient_impls) && !impls_have_top_expr(lenient_impls) {
return Ok(
EvalTop(repair_top_expr_holes(lenient_impls, code), run_main=false),
)
}
match parse_code_to_expr(code) {
Ok(expr) => Ok(EvalExpr(expr))
Err(expr_err) =>
match top_result {
Ok(impls) => Ok(EvalTop(impls, run_main=false))
Err(top_err) =>
if impls_have_top_decl(lenient_impls) {
Ok(
EvalTop(
repair_top_expr_holes(lenient_impls, code),
run_main=false,
),
)
} else {
Err("\{expr_err}\n\{top_err}")
}
}
}
}
}
}