Skip to content

0.4.0/polish#20

Merged
ItsMeBrianD merged 3 commits intomainfrom
0.4.0/polish
Oct 13, 2025
Merged

0.4.0/polish#20
ItsMeBrianD merged 3 commits intomainfrom
0.4.0/polish

Conversation

@ItsMeBrianD
Copy link
Copy Markdown
Contributor

@ItsMeBrianD ItsMeBrianD commented Oct 13, 2025

Summary by CodeRabbit

  • New Features

    • Restored functional lint command with input validation, file handling, and detailed diagnostics.
    • Added support for named references and serializable references to improve surface-rule resolution and list-based rule serialization.
    • Introduced JIT symbol initialization to enable runtime resolution of declared symbols for serialization.
  • Bug Fixes

    • Build now auto-creates missing output directories and reduces noisy traversal logs.
  • Examples

    • Updated surface rules example to showcase biome-based conditions and multiple block choices.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 13, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Activate lint flow, simplify build output-directory creation and logging, add a JIT symbol registry and reference-to-serializable resolution, introduce a generic JSON exporter, extend surface-rule serialization to resolve references and negation, expose ReferenceNode.ToSerializable, wire project symbols into lang, and update an example surface rules file.

Changes

Cohort / File(s) Summary
CLI commands
cmd/build.go, cmd/lint.go
Replace manual output-dir existence logic with mkdirIfNotExists; reduce noisy logging; remove unused import. Implement working mms lint flow: stat/read input, add file to project, parse and log diagnostics, handle errors, call BuildFsLike.
Surface rule serialization & refs
lang/SurfaceRules.go, lang/spec/Reference.go
Add named list serializer for surface rules, exported refs SurfaceConditionRef and SurfaceRuleRef, resolve reference nodes to serializable values during conditional serialization, add helpers for negation/conditional wrapping. Add ReferenceNode.ToSerializable() returning Ref().
JIT registry & project integration
lang/Registries.go, project/Project.go
Add exported var JitSymbols = map[string]*ast.Namespace{} and helpers to resolve symbols/values. Initialize lang.JitSymbols = p.Symbols() in BuildFsLike.
Generic exporter
lang/Dimensions.go
Add private generic helper exporter[T any](serializer func(node T) any, rootPath []string, extension string) func(fn T, name string) *lib.FileTreeLike to serialize nodes into indented JSON files under nested paths.
Minor spec and formatting
lang/spec/List.go
Small whitespace cleanup inside ListSpec.UsageStr (no logic change).
Examples
examples/test_files/surface_rules.mms
Replace Dimension block with Surface block, add InFriendlyBiome, InUnfriendlyBiome, HoneySurface, SlimeSurface, and MyStrangeSurface list with conditional entries and a magma_block.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as cmd/lint
  participant FS as Filesystem
  participant Project as project.Builder
  participant Lang as lang (JitSymbols)

  User->>CLI: mms lint <path>
  CLI->>FS: Stat(path)
  alt path is file
    CLI->>FS: ReadFile(path)
    CLI->>Project: AddFile(content)
  else path is dir
    CLI->>CLI: Log diagnostic (dir case)
  end
  CLI->>Project: BuildFsLike()
  Project->>Lang: lang.JitSymbols = p.Symbols()
  Project-->>CLI: build diagnostics
  CLI->>CLI: Log diagnostics
Loading
sequenceDiagram
  autonumber
  participant SR as SurfaceRules
  participant Reg as lang.JitSymbols
  participant Spec as ReferenceNode
  participant Export as exporter(...)

  SR->>Spec: SerializeConditional(node)
  alt node is Reference
    SR->>Reg: getSymbolValue(namespace,name)
    Reg-->>SR: Serializable value or nil
    SR->>SR: wrap resolved value in surface rule structure
  else direct node
    SR->>SR: serialize node directly
  end
  SR->>Export: exporter(serializer, rootPath, ext)
  Export-->>SR: *lib.FileTreeLike (JSON files)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • 0.4.0/polish #20 — Appears to modify many of the same files (cmd/, lang/, project/*) and is likely the same or a closely related change.

Poem

I nibble bytes and hop through trees,
Linting blades and JIT-lit leaves,
References bloom, exportors hum,
Surfaces sing where blocks will come.
Tiny paws push "build"—and then we run. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title “0.4.0/polish” is vague and only indicates a version bump and unspecified polish rather than summarizing the primary changes introduced by the pull request, making it unclear to reviewers what the main purpose of the update is. Please update the title to clearly summarize the key changes in this PR, for example by describing improvements to build and lint commands, the addition of generic exporter utilities, and the introduction of JitSymbols and surface rule serializers.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ed36b9 and 2e5eefd.

📒 Files selected for processing (1)
  • lang/Dimensions.go (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cmd/build.go (1)

68-72: Critical: Remove stale error check.

Lines 69-72 check err after calling BuildFsLike, but BuildFsLike doesn't return an error—only *lib.FileTreeLike. The err being checked here is stale state from the fs.Stat call on line 48, which is already handled on lines 50-53.

This creates two problems:

  1. The error message "Error exporting project" is misleading—it refers to a stat operation, not export
  2. If a stat error occurred, execution should have already returned at line 52

Apply this diff to remove the dead error check:

 	fsLike := project.BuildFsLike(outFile)
-	if err != nil {
-		log.Println("Error exporting project:", err)
-		return
-	}
 
 	if err = mkdirIfNotExists(outFile); err != nil {
cmd/lint.go (1)

56-81: Remove dead commented code.

Lines 56-81 contain a large block of commented-out code that duplicates the active implementation above. Dead code reduces maintainability and should be removed.

Apply this diff:

 	for _, diag := range project.AllDiagnostics() {
 		log.Println(diag)
 	}
 
-	project.BuildFsLike(".")
-
-	//
-	//stat, err := fs.Stat(os.DirFS("."), inFile)
-	//
-	//if err != nil {
-	//	log.Println("Error building project.", err)
-	//	return
-	//}
-	//if stat.IsDir() {
-	//	log.Println("Project is a directory")
-	//} else {
-	//	if content, err := os.ReadFile(inFile); err != nil {
-	//		log.Println("Error building project:")
-	//	} else {
-	//		f := project.AddFile(inFile, string(content))
-	//		f.Parse()
-	//	}
-	//}
-	//
-	//if len(project.Diagnostics()) > 0 {
-	//	for _, diag := range project.Diagnostics() {
-	//		log.Println(diag)
-	//	}
-	//} else {
-	//	log.Println("Project has no errors or warnings")
-	//}
-
 }
🧹 Nitpick comments (1)
cmd/lint.go (1)

54-54: Recommended: Remove unnecessary BuildFsLike call.

BuildFsLike is invoked but its result is discarded. For a lint command, building a filesystem-like structure is unnecessary—only parsing and diagnostic collection matters.

Additionally, this call triggers the side effect of setting lang.JitSymbols (see project/Project.go line 63), which isn't needed for linting.

Apply this diff to remove the unnecessary call:

 	for _, diag := range project.AllDiagnostics() {
 		log.Println(diag)
 	}
-
-	project.BuildFsLike(".")
-
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ed8787 and 1ed36b9.

📒 Files selected for processing (9)
  • cmd/build.go (1 hunks)
  • cmd/lint.go (2 hunks)
  • examples/test_files/surface_rules.mms (1 hunks)
  • lang/Dimensions.go (1 hunks)
  • lang/Registries.go (1 hunks)
  • lang/SurfaceRules.go (2 hunks)
  • lang/spec/List.go (0 hunks)
  • lang/spec/Reference.go (1 hunks)
  • project/Project.go (2 hunks)
💤 Files with no reviewable changes (1)
  • lang/spec/List.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (.windsurfrules)

**/*.go: Keep all imports at the top of Go files; never insert imports mid-file
Run gofmt/goimports and go vet before committing or proposing changes
Use Project.Diagnostics() method instead of accessing a Diagnostics field

Files:

  • lang/spec/Reference.go
  • cmd/build.go
  • project/Project.go
  • lang/Registries.go
  • cmd/lint.go
  • lang/SurfaceRules.go
  • lang/Dimensions.go
🧬 Code graph analysis (5)
project/Project.go (1)
lang/Registries.go (1)
  • JitSymbols (12-12)
lang/Registries.go (3)
lang/ast/Namespace.go (1)
  • Namespace (8-11)
lang/ast/Symbol.go (1)
  • Symbol (48-63)
lib/IsNilInterface.go (1)
  • IsNilInterface (5-11)
cmd/lint.go (1)
project/Project.go (1)
  • NewProject (18-23)
lang/SurfaceRules.go (6)
lang/spec/List.go (2)
  • ListNode (119-123)
  • NewListSpec (67-69)
lang/ast/Symbol.go (3)
  • Symbol (48-63)
  • SymbolSurfaceCondition (44-44)
  • SymbolSurfaceRule (43-43)
lib/IsNilInterface.go (1)
  • IsNilInterface (5-11)
lang/spec/Reference.go (2)
  • NewReferenceSpec (28-30)
  • ReferenceNode (142-147)
lang/spec/Condition.go (2)
  • NewConditionSpec (28-33)
  • ConditionalNode (250-257)
lang/ast/Namespace.go (1)
  • Namespace (8-11)
lang/Dimensions.go (2)
wasm/mms.d.ts (1)
  • FileTreeLike (24-27)
lib/FileTreeLike.go (1)
  • NewDirLike (140-147)
🔇 Additional comments (1)
cmd/build.go (1)

74-76: LGTM: Good refactoring of directory creation.

Consolidating the directory existence check into mkdirIfNotExists improves code clarity and reusability.

Comment thread cmd/lint.go
Comment on lines +37 to +49
if stat.IsDir() {
log.Println("Project is a directory")
} else {
if content, err := os.ReadFile(inFile); err != nil {
log.Println("Error reading project:", err)
} else {
_, err := project.AddFile(inFile, string(content))

if err != nil {
log.Println("Error parsing project:", err)
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Major: Implement directory traversal for lint.

When stat.IsDir() is true, the code logs "Project is a directory" but doesn't actually process any files within that directory. This means lint is effectively a no-op for directory inputs.

Consider implementing recursive directory traversal:

if stat.IsDir() {
	entries, err := os.ReadDir(inFile)
	if err != nil {
		log.Println("Error reading directory:", err)
		return
	}
	for _, entry := range entries {
		if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".mms") {
			filePath := path.Join(inFile, entry.Name())
			content, err := os.ReadFile(filePath)
			if err != nil {
				log.Println("Error reading file:", err)
				continue
			}
			_, err = project.AddFile(filePath, string(content))
			if err != nil {
				log.Println("Error parsing file:", err)
			}
		}
	}
} else {

Or use filepath.WalkDir for recursive traversal if subdirectories should also be processed.

🤖 Prompt for AI Agents
In cmd/lint.go around lines 37 to 49 the branch for stat.IsDir() only logs
"Project is a directory" and does not process any files; replace that no-op with
directory traversal that finds and reads .mms files (recursively if desired) and
calls project.AddFile for each file. Use filepath.WalkDir (or os.ReadDir for
non-recursive) to iterate entries, skip directories, filter by ".mms" suffix,
build the full path, read the file contents, handle and log read/parse errors
but continue processing other files, and ensure you pass the correct full file
path and file contents into project.AddFile.

Comment thread lang/Dimensions.go Outdated
Comment thread lang/SurfaceRules.go
Comment on lines 388 to +399
var val any = nil
if n.Value != nil && n.Value.ToSerializable() != nil {
val = n.Value.ToSerializable()
if !lib.IsNilInterface(n.Value) {
if ref, ok := n.Value.(*spec.ReferenceNode); ok {
val = getSymbolValue(ref.Namespace, ref.Name)
} else if !lib.IsNilInterface(n.Value.ToSerializable()) {
val = n.Value.ToSerializable()
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore fallback serialization when value references miss the registry.

If getSymbolValue can’t resolve the referenced symbol, val stays nil, dropping the rule body. Previously we still emitted the reference string, so this is a regression. Fall back to ref.ToSerializable() when resolution fails.

Apply this diff:

-			if ref, ok := n.Value.(*spec.ReferenceNode); ok {
-				val = getSymbolValue(ref.Namespace, ref.Name)
-			} else if !lib.IsNilInterface(n.Value.ToSerializable()) {
+			if ref, ok := n.Value.(*spec.ReferenceNode); ok {
+				if resolved := getSymbolValue(ref.Namespace, ref.Name); resolved != nil {
+					val = resolved
+				} else {
+					val = ref.ToSerializable()
+				}
+			} else if !lib.IsNilInterface(n.Value.ToSerializable()) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var val any = nil
if n.Value != nil && n.Value.ToSerializable() != nil {
val = n.Value.ToSerializable()
if !lib.IsNilInterface(n.Value) {
if ref, ok := n.Value.(*spec.ReferenceNode); ok {
val = getSymbolValue(ref.Namespace, ref.Name)
} else if !lib.IsNilInterface(n.Value.ToSerializable()) {
val = n.Value.ToSerializable()
}
var val any = nil
if !lib.IsNilInterface(n.Value) {
if ref, ok := n.Value.(*spec.ReferenceNode); ok {
if resolved := getSymbolValue(ref.Namespace, ref.Name); resolved != nil {
val = resolved
} else {
val = ref.ToSerializable()
}
} else if !lib.IsNilInterface(n.Value.ToSerializable()) {
val = n.Value.ToSerializable()
}
🤖 Prompt for AI Agents
In lang/SurfaceRules.go around lines 393 to 399, when handling n.Value as a
*spec.ReferenceNode the code calls getSymbolValue(ref.Namespace, ref.Name) but
if that fails val remains nil and the rule body is dropped; update the branch so
that if getSymbolValue cannot resolve the symbol (i.e., returns nil or not ok),
you fall back to using ref.ToSerializable() as the serialized value; ensure the
existing else-if that uses n.Value.ToSerializable() remains for non-reference
values.

Comment thread project/Project.go
func (p *Project) BuildFsLike(root string) *lib.FileTreeLike {
rootDir := lib.NewDirLike(root, nil)

lang.JitSymbols = p.Symbols()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Avoid global mutable state for symbols.

Setting lang.JitSymbols (a package-level global) introduces several risks:

  1. Race conditions: Concurrent calls to BuildFsLike on different projects will race, corrupting each other's symbol state
  2. Testing difficulties: Global state persists between test cases, causing non-deterministic failures
  3. Hidden dependencies: Downstream code relying on JitSymbols becomes implicitly coupled to BuildFsLike having been called first

Consider refactoring to pass symbols explicitly:

-func (p *Project) BuildFsLike(root string) *lib.FileTreeLike {
+func (p *Project) BuildFsLike(root string) *lib.FileTreeLike {
 	rootDir := lib.NewDirLike(root, nil)
 
-	lang.JitSymbols = p.Symbols()
-
 	for ns, decls := range p.symbols {
-		nsDir := rootDir.MkDir(ns, nil)
+		nsDir := rootDir.MkDir(ns, nil)
 		for name, decl := range decls.AllDecls() {
-			declFs := decl.ToFileTreeLike(name)
+			declFs := decl.ToFileTreeLike(name, p.Symbols())
 			if declFs != nil {
 				nsDir.AddChild(declFs)
 			}
 		}
 	}
 	return rootDir
 }

Then update ToFileTreeLike methods to accept symbols as a parameter rather than accessing the global. This eliminates the global state while preserving functionality.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
lang.JitSymbols = p.Symbols()
func (p *Project) BuildFsLike(root string) *lib.FileTreeLike {
rootDir := lib.NewDirLike(root, nil)
for ns, decls := range p.symbols {
nsDir := rootDir.MkDir(ns, nil)
for name, decl := range decls.AllDecls() {
declFs := decl.ToFileTreeLike(name, p.Symbols())
if declFs != nil {
nsDir.AddChild(declFs)
}
}
}
return rootDir
}
🤖 Prompt for AI Agents
In project/Project.go around line 63, remove the assignment to the package-level
lang.JitSymbols global and instead have BuildFsLike produce/return the symbols
(or a symbol container) as a value; change all ToFileTreeLike methods to accept
the symbols value as an explicit parameter and update their call sites to pass
the returned symbols through, eliminating reads/writes to lang.JitSymbols;
propagate the new parameter through callers, update tests to
construct/passthrough symbols per-test, and remove or mark lang.JitSymbols
deprecated so no code relies on mutable global state.

@ItsMeBrianD ItsMeBrianD merged commit ee4bb0c into main Oct 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant