fix: main, intrinsic, program typecheck

This commit is contained in:
2025-11-05 21:23:26 +08:00
parent 3352410941
commit b312898187
9 changed files with 193 additions and 87 deletions

2
.gitignore vendored
View File

@@ -2,3 +2,5 @@
target/
.mooncakes/
.moonagent/
*.zip

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "contest-2025-data"]
path = contest-2025-data
url = https://github.com/moonbitlang/contest-2025-data.git

39
batch_run.py Normal file
View File

@@ -0,0 +1,39 @@
import os
import asyncio
async def check_file(filepath):
proc = await asyncio.create_subprocess_exec(
"moon",
"run",
"src/bin/main.mbt",
"--",
"--typecheck",
filepath,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await proc.communicate()
if proc.returncode != 0:
print(f"""
[{proc.returncode}] Error in file {filepath}:
======== STDOUT ========
{stdout.decode()}
======== STDERR ========
{stderr.decode()}
""")
else:
print(f"File {filepath} compiled successfully.")
async def main():
tasks = []
for file in os.listdir("contest-2025-data/test_cases/mbt"):
if file.endswith(".mbt"):
filepath = os.path.join("contest-2025-data/test_cases/mbt", file)
tasks.append(check_file(filepath))
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())

1
contest-2025-data Submodule

Submodule contest-2025-data added at b296c16203

View File

@@ -1,6 +1,22 @@
///|
fn[T] println_panic(msg : String) -> T {
println("[FTL] \{msg}")
@sys.exit(1)
panic()
}
///|
fn println_debug(msg : String) -> Unit {
println("[DBG] \{msg}")
}
///|
fn main {
let argv = @sys.get_cli_args()
guard @sys.get_cli_args() is [_, .. argv] else {
println_panic("Failed to get CLI arguments")
}
let argv = argv.to_array()
println_debug("CLI arguments: \{argv}")
let typecheck_only = Ref::new(false)
let mut in_file = None
let out_file = Ref::new("a.s")
@@ -15,44 +31,55 @@ fn main {
(
"--output",
"-o",
@ArgParser.String(s => out_file.val = s),
@ArgParser.Set_string(out_file),
"Output file (default: a.s)",
),
],
fn(s) {
if !in_file.is_empty() {
abort("multiple files are given")
println_panic("multiple files are given")
}
in_file = Some(s)
},
(
#|Lilunar: Lil-Ran's experimental MiniMoonBit to RISC-V compiler for MGPIC-2025.
#|
#|Usage: lilunar [options] <input-file>
#|
#|Options:
#| --typecheck Only run type checking
#| -o, --output <file> Output file (default: a.s)
#| usage: lilunar [options] <input-file>
#|
),
argv,
) catch {
e => abort("Argument parsing error: \{e}")
@ArgParser.ErrorMsg(msg) => println_panic("Argument parsing error: \{msg}")
e => println_panic("Unknown error during argument parsing: \{e}")
}
let file = in_file.unwrap_or_else(abort("no input file provided"))
let file = in_file.unwrap_or_else(() => println_panic(
"no input file provided",
))
println_debug("Compiling file: \{file}")
let contents = @fs.read_file_to_string(file) catch {
e => abort("Failed to read file \{file}: \{e}")
@fs.IOError(msg) => println_panic("Failed to read file \{file}: \{msg}")
}
println_debug(
(
#|
#|================================
$|\{contents}
#|================================
),
)
let program = try
contents
|> @parser.tokenize()
|> @parser.parse_program()
|> @typecheck.typecheck()
catch {
e => abort("Parsing or type checking error: \{e}")
@parser.TokenizeError(msg) => println_panic("Tokenization error: \{msg}")
@parser.ParseError(msg) => println_panic("Parse error: \{msg}")
@typecheck.TypeCheckError(msg) => println_panic("Type check error: \{msg}")
_ => println_panic("Unknown error during compilation")
}
if typecheck_only.val {
println("Type checking passed.")
println_debug("Type checking passed.")
return
}

View File

@@ -98,56 +98,63 @@ pub fn Context::check_program(
self : Context,
program : @parser.Program,
) -> Program raise TypeCheckError {
self.collect_struct_names(program)
self.collect_function_types(program)
let struct_defs : Map[String, StructDef] = Map::new()
for name, struct_def in program.struct_defs {
let checked_struct_def = self.check_struct_def(struct_def)
struct_defs.set(name, checked_struct_def)
self.struct_defs.set(name, checked_struct_def)
}
let top_functions : Map[String, TopFunction] = Map::new()
for name, func in program.top_functions {
let checked_func = self.check_top_function(func)
top_functions.set(name, checked_func)
self.func_types.set(
name,
Function(checked_func.param_list.map(p => p.ty), checked_func.ret_ty),
)
}
let top_lets : Map[String, TopLet] = Map::new()
for name, let_def in program.top_lets {
let checked_let = self.check_top_let(let_def)
top_lets.set(name, checked_let)
self.type_env.set(name, checked_let.ty)
}
{ top_lets, top_functions, struct_defs }
}
///|
pub fn Context::collect_struct_names(
self : Context,
program : @parser.Program,
) -> Unit raise TypeCheckError {
// 收集结构体名称
for name, _ in program.struct_defs {
if self.struct_defs.contains(name) {
raise TypeCheckError("Duplicate struct definition: \{name}")
}
self.struct_defs.set(name, { name, fields: [] })
}
// 此时除泛型外,所有存在的类型都已确定
// 收集结构体定义
let struct_defs : Map[String, StructDef] = Map::new()
for name, struct_def in program.struct_defs {
let checked_struct_def = self.check_struct_def(struct_def)
struct_defs.set(name, checked_struct_def)
self.struct_defs.set(name, checked_struct_def)
}
// 收集顶层函数声明
for name, func in program.top_functions {
self.func_types.set(name, self.check_top_function_type_decl(func))
}
// 补充内建函数类型
self.add_intrinsic_functions()
// 此时顶层变量初始化表达式可引用的类型都已确定
// 收集顶层变量类型和表达式
let top_lets : Map[String, TopLet] = Map::new()
for name, let_def in program.top_lets {
let checked_let = self.check_top_let(let_def)
top_lets.set(name, checked_let)
self.type_env.set(name, checked_let.ty)
}
// 此时所有顶层标识符的类型都已确定
// 检查顶层函数定义
let top_functions : Map[String, TopFunction] = Map::new()
for name, func in program.top_functions {
top_functions.set(name, self.check_top_function_body(func))
}
{ top_lets, top_functions, struct_defs }
}
///|
pub fn Context::collect_function_types(
self : Context,
program : @parser.Program,
) -> Unit {
for name, _ in program.top_functions {
let func_type = self.new_type_var()
self.func_types.set(name, func_type)
}
for name, _ in program.top_lets {
let let_type = self.new_type_var()
self.type_env.set(name, { kind: let_type, mutable: false })
pub fn Context::add_intrinsic_functions(self : Context) -> Unit {
let map = Map::of([
("read_int", Function([], Int)),
("print_int", Function([Int], Unit)),
("read_char", Function([], Int)),
("print_char", Function([Int], Unit)),
("print_endline", Function([], Unit)),
("int_of_float", Function([Double], Int)),
("float_of_int", Function([Int], Double)),
("truncate", Function([Double], Int)),
("floor", Function([Double], Double)),
("abs_float", Function([Double], Double)),
("sqrt", Function([Double], Double)),
("sin", Function([Double], Double)),
("cos", Function([Double], Double)),
("atan", Function([Double], Double)),
])
for name, ty in map {
self.type_env.set(name, { kind: ty, mutable: false })
}
}

View File

@@ -13,51 +13,78 @@ pub(all) struct TopFunction {
} derive(Show)
///|
pub fn Context::check_top_function(
pub fn Context::check_top_function_body(
self : Context,
func : @parser.Function,
) -> TopFunction raise TypeCheckError {
// XXX: 目前的泛型不是真正的泛型,只是只能归一成单个类型的类型变量
if func.user_defined_type is Some(UserDefined(udt)) {
self.type_env.set("$Generic$\{udt}", {
kind: self.new_type_var(),
mutable: false,
})
}
//
let param_list : Array[Param] = func.params.map(param => {
let (param_name, param_type) = param
match param_type {
Some(ty) => { name: param_name, ty: self.check_parser_type(ty).kind }
None => { name: param_name, ty: self.new_type_var() }
guard self.type_env.get("$GenericFunc$\{func.id}$\{udt}") is Some(tv) else {
raise TypeCheckError(
"Internal error: generic type variable for function '\{func.id}' not found.",
)
}
})
let ret_ty = match func.return_type {
Some(ty) => self.check_parser_type(ty).kind
None => self.new_type_var()
self.type_env.set("$Generic$\{udt}", tv)
}
let func_type = Function(param_list.map(p => p.ty), ret_ty)
self.type_env.set(func.id, { kind: func_type, mutable: false })
//
let param_names = func.params.map(param => param.0)
let param_list = []
guard self.func_types.get(func.id) is Some(Function(param_types, ret_ty)) else {
raise TypeCheckError("Function type for '\{func.id}' not found.")
}
self.type_env.set(func.id, {
kind: Function(param_types, ret_ty),
mutable: false,
})
self.enter_scope()
self.current_func_ret_ty = Some(ret_ty)
//
for param in param_list {
let { name, ty } = param
self.type_env.set(name, { kind: ty, mutable: false })
for i in 0..<param_names.length() {
self.type_env.set(param_names[i], { kind: param_types[i], mutable: false })
param_list.push(Param::{ name: param_names[i], ty: param_types[i] })
}
//
let checked_body = self.check_block_expr(Block(func.body))
if !self.is_type_compatible(checked_body.ty, ret_ty) {
raise TypeCheckError(
"Function '\{func.id}' return type mismatch: expected \{ret_ty}, got \{checked_body.ty}",
)
}
//
self.exit_scope()
if func.user_defined_type is Some(UserDefined(udt)) {
self.type_env.local_.remove("$Generic$\{udt}")
}
//
{ fname: func.id, param_list, ret_ty, body: checked_body }
}
///|
pub fn Context::check_top_function_type_decl(
self : Context,
func : @parser.Function,
) -> TypeKind raise TypeCheckError {
// XXX: 目前的泛型不是真正的泛型,只是只能归一成单个类型的类型变量
if func.user_defined_type is Some(UserDefined(udt)) {
let generic_type = { kind: self.new_type_var(), mutable: false }
// 用于函数内类型查找
self.type_env.set("$Generic$\{udt}", generic_type)
// 用于稍后检查函数体时能使用同一个 type_var
self.type_env.set("$GenericFunc$\{func.id}$\{udt}", generic_type)
}
let param_list : Array[Param] = func.params.map(param => {
let (param_name, param_type) = param
let ty = param_type.or_error(
TypeCheckError(
"Parameter type annotation is required for top function; found none for '\{param_name}' in function '\{func.id}'.",
),
)
{ name: param_name, ty: self.check_parser_type(ty).kind }
})
let ret_ty = func.return_type.or_error(
TypeCheckError(
"Return type annotation is required for top function; found none in function '\{func.id}'.",
),
)
let ret_ty = self.check_parser_type(ret_ty).kind
let func_type = Function(param_list.map(p => p.ty), ret_ty)
if func.user_defined_type is Some(UserDefined(udt)) {
self.type_env.local_.remove("$Generic$\{udt}")
}
func_type
}

View File

@@ -759,7 +759,7 @@ test "Top Function TypeCheck Test" {
// parse
let tokens = @parser.tokenize(code)
let program = @parser.parse_program(tokens)
let _ = ctx.check_top_function(program.top_functions["fib"])
let _ = ctx.check_top_function_body(program.top_functions["fib"])
}

View File

@@ -3,10 +3,10 @@ pub fn typecheck(program : @parser.Program) -> Program raise TypeCheckError {
let ctx = Context::new()
let checked_program = ctx.check_program(program)
let result = ctx.substitute_type_var(checked_program)
// XXX: do not to_string
if result.to_string().contains("TypeVar") {
raise TypeCheckError("unsubstituted type variable remains")
for v in ctx.type_vars.values() {
if ctx.deref_type_var(v) is TypeVar(_) {
raise TypeCheckError("unsubstituted type variable remains")
}
}
result
}