diff --git a/.trae/rules/project_rules.md b/.trae/rules/project_rules.md index 04b37f1..2c4cfa4 100644 --- a/.trae/rules/project_rules.md +++ b/.trae/rules/project_rules.md @@ -47,3 +47,54 @@ Common Options: --dry-run Do not actually run the command --build-graph Generate build graph +# Project Agents.md Guide + +This is a [MoonBit](https://docs.moonbitlang.com) project. + +## Project Structure + +- MoonBit packages are organized per directory, for each directory, there is a + `package.json` file listing its dependencies. Each package has its files and + blackbox test files (common, ending in `_test.mbt`) and whitebox test files + (ending in `_wbtest.mbt`). + +- In the toplevel directory, this is a `moon.mod.json` file listing about the + module and some meta information. + +## Coding convention + +- MoonBit code is organized in block style, each block is separated by `///|`, + the order of each block is irrelevant. In some refactorings, you can process + block by block independently. + +- Try to keep deprecated blocks in file called `deprecated.mbt` in each + directory. + +## Tooling + +- `moon fmt` is used to format your code properly. + +- `moon info` is used to update the generated interface of the package, each + package has a generated interface file `.mbti`, it is a brief formal + description of the package. If nothing in `.mbti` changes, this means your + change does not bring the visible changes to the external package users, it is + typically a safe refactoring. + +- In the last step, run `moon info && moon fmt` to update the interface and + format the code. Check the diffs of `.mbti` file to see if the changes are + expected. + +- Run `moon test` to check the test is passed. MoonBit supports snapshot + testing, so when your changes indeed change the behavior of the code, you + should run `moon test --update` to update the snapshot. + +- You can run `moon check` to check the code is linted correctly. + +- When writing tests, you are encouraged to use `inspect` and run + `moon test --update` to update the snapshots, only use assertions like + `assert_eq` when you are in some loops where each snapshot may vary. You can + use `moon coverage analyze > uncovered.log` to see which parts of your code + are not covered by tests. + +- agent-todo.md has some small tasks that are easy for AI to pick up, agent is + welcome to finish the tasks and check the box when you are done diff --git a/moon.mod.json b/moon.mod.json index 3919046..6cbf4b5 100644 --- a/moon.mod.json +++ b/moon.mod.json @@ -1,9 +1,8 @@ { "name": "oboard/moonbit-eval", - "version": "0.8.11", + "version": "0.8.13", "deps": { "moonbitlang/parser": "0.1.11", - "oboard/mio": "0.4.3", "bobzhang/zip": "0.1.0" }, "readme": "README.md", diff --git a/src/example/main.mbt b/src/example/main.mbt new file mode 100644 index 0000000..e8e2b4d --- /dev/null +++ b/src/example/main.mbt @@ -0,0 +1,5 @@ +///| +fn main { + let result = @eval.MoonBitVM::new().eval("1 + 2") + println(result) +} diff --git a/src/example/moon.pkg.json b/src/example/moon.pkg.json new file mode 100644 index 0000000..b9ca184 --- /dev/null +++ b/src/example/moon.pkg.json @@ -0,0 +1,9 @@ +{ + "is-main": true, + "import": [ + { + "path": "oboard/moonbit-eval", + "alias": "eval" + } + ] +} \ No newline at end of file diff --git a/src/example/pkg.generated.mbti b/src/example/pkg.generated.mbti new file mode 100644 index 0000000..e68579c --- /dev/null +++ b/src/example/pkg.generated.mbti @@ -0,0 +1,13 @@ +// Generated using `moon info`, DON'T EDIT IT +package "oboard/moonbit-eval/example" + +// Values + +// Errors + +// Types and methods + +// Type aliases + +// Traits + diff --git a/src/export.mbt b/src/export.mbt index 70976b7..d843182 100644 --- a/src/export.mbt +++ b/src/export.mbt @@ -21,6 +21,14 @@ pub fn eval_result_to_string(result : EvalResult) -> String { } } +///| +pub fn code_to_ast(code : String) -> String { + match @interpreter.parse_code_to_impl(code) { + Ok(i) => i.to_json().stringify() + Err(msg) => msg + } +} + ///| pub fn add_extern_fn( vm : MoonBitVM, diff --git a/src/moon.pkg.json b/src/moon.pkg.json index 7961e60..aba1ba1 100644 --- a/src/moon.pkg.json +++ b/src/moon.pkg.json @@ -10,6 +10,7 @@ "create", "eval", "eval_result_to_string", + "code_to_ast", "add_extern_fn", "add_embedded_fn", "add_embedded_method", diff --git a/src/mooncakes/loader.mbt b/src/mooncakes/loader.mbt deleted file mode 100644 index 693c879..0000000 --- a/src/mooncakes/loader.mbt +++ /dev/null @@ -1,189 +0,0 @@ -///| -pub async fn load_meta( - mod_name : String, - version? : String, -) -> @interpreter.ModuleInfo { - ///| - let mod_name = mod_name + (if version is Some(v) { "@" + v } else { "" }) - // 获取模块元信息 - let url = "https://mooncakes.io/assets/\{mod_name}/resource.json" - let info = try? @mio.get(url).unwrap_json() - if info is Ok({ "meta_info": meta_info, .. }) { - @json.from_json(meta_info) - } else { - @interpreter.ModuleInfo::new("") - } -} - -///| -pub async fn load_module( - mod_name : String, - version? : String, -) -> Map[String, @interpreter.RuntimeModule] { - let mod_name = mod_name + (if version is Some(v) { "@" + v } else { "" }) - let modules : Map[String, @interpreter.RuntimeModule] = {} - - // 获取模块元信息 - let meta = load_meta(mod_name, version?) - - // 处理依赖模块 - if meta.deps is Some(deps) { - // for name, version in deps { - let deps = deps.to_array() - while deps.pop() is Some((name, version)) { - let dep_modules = load_module(name, version~) - for name, dep_module in dep_modules { - modules.set(name, dep_module) - } - } - } - let url = "https://mooncakes.io/assets/\{mod_name}/module_index.json" - let module_index = @mio.get(url).unwrap_json() - let pkgs : Map[String, @interpreter.RuntimePackage] = {} - - // Parse package information from module_index.json recursively - async fn parse_child( - child : Json, - pkgs : Map[String, @interpreter.RuntimePackage], - ) { - if child is { "childs": Array(childs), .. } { - while childs.pop() is Some(sub_child) { - parse_child(sub_child, pkgs) - } - } - if child is { "name": String(pkg_name), "package": package_info, .. } { - if package_info is { "path": String(path), .. } { - match load_package(path, pkgs) { - Some(pkg) => pkgs.set(pkg_name, pkg) - None => () - } - } - } - } - - parse_child(module_index, pkgs) - modules.set(mod_name, @interpreter.RuntimeModule::{ meta, pkgs }) - modules -} - -///| -pub async fn load_package( - pkg_name : String, - deps : Map[String, @interpreter.RuntimePackage], -) -> @interpreter.RuntimePackage? { - let url = "https://mooncakes.io/assets/\{pkg_name}/resource.json" - let info = @mio.get(url).unwrap_json() - let sources : Map[String, String] = {} - if info is { "source_files": Array(source_files), .. } { - while source_files.pop() is Some(file) { - if file is String(name) { - let source = @mio.get( - "https://mooncakes.io/assets/\{pkg_name}/\{name}.html", - ).text() catch { - _ => "" - } - let start = source.find("
")
- let end = source.rev_find("")
- if (start, end) is (Some(start), Some(end)) {
- sources.set(name, source[start:end].to_string())
- }
- }
- }
- Some({
- name: pkg_name,
- traits: Map::new(),
- fn_aliases: Map::new(),
- type_aliases: Map::new(),
- trait_aliases: Map::new(),
- stubs: Map::new(),
- trait_methods: Map::new(),
- type_definitions: Map::new(),
- type_derived_traits: Map::new(),
- constructors: Map::new(),
- struct_methods: Map::new(),
- values: Map::new(),
- env: @interpreter.RuntimeEnvironment::new(),
- deps,
- files: sources,
- loaded: false,
- })
- } else {
- None
- }
-}
-
-///|
-pub async fn load_module_zip(
- mod_name : String,
- version? : String,
-) -> @interpreter.RuntimeModule {
- let module_name = mod_name + (if version is Some(v) { "@" + v } else { "" })
- if (try? load_meta(module_name)) is Ok(a) {
- let res = try? @mio.get(a.get_zip_url())
- if res is Ok(res) {
- if (try? @zip.Archive::of_bytes(res.data)) is Ok(zip) {
- if zip.find(@fpath.Fpath("moon.mod.json")) is Some(m) {
- if m.kind() is File(data) {
- let module_info : @interpreter.ModuleInfo = @json.from_json(
- @json.parse(@encoding/utf8.decode(data.to_bytes())),
- ) catch {
- _ => abort("Failed to read moon.mod.json")
- }
- // println(module_info)
- let root = if module_info.source is Some(source) {
- source
- } else {
- ""
- }
- // println(root)
- fn collect_files(root : String) -> Map[String, String] {
- let files = {}
- for entry in zip.to_array() {
- if entry.kind() is File(file) {
- let path = entry.path().to_string()
- if path.has_prefix(root) && path.has_suffix(".mbt") {
- try {
- files[path] = @encoding/utf8.decode(file.to_bytes())
- } catch {
- _ => abort("Failed to decode file content")
- }
- }
- }
- }
- files
- }
- // 找出所有的 packages
- fn scan_packages() -> Array[@interpreter.RuntimePackage] {
- let pkgs = []
- for entry in zip.to_array() {
- if entry.is_dir() {
- let dir = entry.path().to_string()
- if zip.find(@fpath.Fpath(dir + "moon.pkg.json")) is Some(_) {
- let pkg_name = module_name +
- dir.strip_prefix(root).unwrap_or(dir).to_string()
- // println(pkg_name)
- // println(dir)
- pkgs.push(
- @interpreter.RuntimePackage::new(
- pkg_name,
- files=collect_files(dir),
- ),
- )
- }
- }
- }
- pkgs
- }
-
- let packages = scan_packages()
- return @interpreter.RuntimeModule::{
- meta: a,
- pkgs: Map::from_array(packages.map(pkg => (pkg.name, pkg))),
- }
- }
- }
- }
- }
- }
- abort("Failed to load module zip")
-}
diff --git a/src/mooncakes/moon.pkg.json b/src/mooncakes/moon.pkg.json
deleted file mode 100644
index 9d895a7..0000000
--- a/src/mooncakes/moon.pkg.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "import": [
- "oboard/moonbit-eval/interpreter",
- "oboard/mio",
- "bobzhang/zip",
- "bobzhang/zip/types/fpath",
- "bobzhang/zip/member",
- "bobzhang/zip/file"
- ]
-}
\ No newline at end of file
diff --git a/src/mooncakes/pkg.generated.mbti b/src/mooncakes/pkg.generated.mbti
deleted file mode 100644
index 7d76d53..0000000
--- a/src/mooncakes/pkg.generated.mbti
+++ /dev/null
@@ -1,24 +0,0 @@
-// Generated using `moon info`, DON'T EDIT IT
-package "oboard/moonbit-eval/mooncakes"
-
-import(
- "oboard/moonbit-eval/interpreter"
-)
-
-// Values
-async fn load_meta(String, version? : String) -> @interpreter.ModuleInfo
-
-async fn load_module(String, version? : String) -> Map[String, @interpreter.RuntimeModule]
-
-async fn load_module_zip(String, version? : String) -> @interpreter.RuntimeModule
-
-async fn load_package(String, Map[String, @interpreter.RuntimePackage]) -> @interpreter.RuntimePackage?
-
-// Errors
-
-// Types and methods
-
-// Type aliases
-
-// Traits
-
diff --git a/src/pkg.generated.mbti b/src/pkg.generated.mbti
index 973df8f..d26589f 100644
--- a/src/pkg.generated.mbti
+++ b/src/pkg.generated.mbti
@@ -13,6 +13,8 @@ fn add_embedded_method(MoonBitVM, String, String, (@interpreter.RuntimeFunctionC
fn add_extern_fn(MoonBitVM, String, (@interpreter.RuntimeFunctionContext) -> @interpreter.RuntimeValue raise @interpreter.ControlFlow) -> Unit
+fn code_to_ast(String) -> String
+
fn eval_result_to_string(EvalResult) -> String
fn expr_to_string(@syntax.Expr) -> String
diff --git a/src/test/moon.pkg.json b/src/test/moon.pkg.json
index 7749243..ee595af 100644
--- a/src/test/moon.pkg.json
+++ b/src/test/moon.pkg.json
@@ -4,8 +4,6 @@
"path": "oboard/moonbit-eval",
"alias": "eval"
},
- "oboard/moonbit-eval/interpreter",
- "oboard/mio",
- "oboard/moonbit-eval/mooncakes"
+ "oboard/moonbit-eval/interpreter"
]
}
\ No newline at end of file
diff --git a/src/test/packages.mbt b/src/test/packages.mbt
index f1ca664..86b4686 100644
--- a/src/test/packages.mbt
+++ b/src/test/packages.mbt
@@ -54,20 +54,20 @@ test "cross_package_method_calling" {
assert_eq(vm.eval("arr1.length()").to_string(), "5")
}
-///|
-test "online_package" {
- let vm = MoonBitVM::new()
- @mio.run(() => {
- let modules = try? @mooncakes.load_module("wangweigang/hello")
- if modules is Ok(modules) {
- vm.interpreter.main_pkg.deps.set(
- "hello",
- modules.get("wangweigang/hello").unwrap().pkgs["hello"],
- )
- println(vm.eval("@hello.hello()"))
- }
- })
-}
+// ///|
+// test "online_package" {
+// let vm = MoonBitVM::new()
+// @mio.run(() => {
+// let modules = try? @mooncakes.load_module("wangweigang/hello")
+// if modules is Ok(modules) {
+// vm.interpreter.main_pkg.deps.set(
+// "hello",
+// modules.get("wangweigang/hello").unwrap().pkgs["hello"],
+// )
+// println(vm.eval("@hello.hello()"))
+// }
+// })
+// }
// ///|
// test {